blob: afccc18911d492666e31242bf1516f78e0149815 [file] [log] [blame]
Wei-Han Chene97d3532016-03-31 19:22:01 +08001# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Joel Kitching679a00b2016-08-03 11:42:58 +08005"""Transition to release state directly without reboot."""
Wei-Han Chene97d3532016-03-31 19:22:01 +08006
Wei-Han Chenbe1355a2016-04-24 19:31:03 +08007import json
Wei-Han Chene97d3532016-03-31 19:22:01 +08008import logging
Wei-Han Chene97d3532016-03-31 19:22:01 +08009import os
Shun-Hsing Oub5724832016-07-21 11:45:58 +080010import re
Wei-Han Chene97d3532016-03-31 19:22:01 +080011import resource
12import shutil
13import signal
Wei-Han Chenbe1355a2016-04-24 19:31:03 +080014import socket
Wei-Han Chene97d3532016-03-31 19:22:01 +080015import tempfile
16import textwrap
17import time
18
19import factory_common # pylint: disable=unused-import
20from cros.factory.gooftool import chroot
Wei-Han Chen0a3320e2016-04-23 01:32:07 +080021from cros.factory.gooftool.common import ExecFactoryPar
Shen-En Shih502b3102018-04-24 11:12:01 +080022from cros.factory.gooftool.common import Shell
Wei-Han Chen9adf9de2016-04-01 19:35:41 +080023from cros.factory.gooftool.common import Util
Wei-Han Chen0a3320e2016-04-23 01:32:07 +080024from cros.factory.test.env import paths
Wei-Han Chenb05699a2017-07-12 16:37:47 +080025from cros.factory.utils import file_utils
Wei-Han Chene97d3532016-03-31 19:22:01 +080026from cros.factory.utils import process_utils
27from cros.factory.utils import sync_utils
28from cros.factory.utils import sys_utils
29
30
Hung-Te Lina3195462016-10-14 15:48:29 +080031CUTOFF_SCRIPT_DIR = '/usr/local/factory/sh/cutoff'
Peter Shih18898302018-03-05 15:32:58 +080032"""Directory of scripts for device cut-off"""
Wei-Han Chen9adf9de2016-04-01 19:35:41 +080033
Wei-Han Chenbe1355a2016-04-24 19:31:03 +080034WIPE_IN_TMPFS_LOG = 'wipe_in_tmpfs.log'
Wei-Han Chene97d3532016-03-31 19:22:01 +080035
Wei-Han Chenb05699a2017-07-12 16:37:47 +080036STATEFUL_PARTITION_PATH = '/mnt/stateful_partition/'
37
38WIPE_MARK_FILE = 'wipe_mark_file'
39
Hung-Te Lindd3425d2017-07-12 20:10:52 +080040CRX_CACHE_PAYLOAD_NAME = 'cros_payloads/release_image.crx_cache'
41CRX_CACHE_TAR_PATH = '/tmp/crx_cache.tar'
Wei-Han Chenb05699a2017-07-12 16:37:47 +080042
Peter Shihe6afab32018-09-11 17:16:48 +080043class WipeError(Exception):
Wei-Han Chenb05699a2017-07-12 16:37:47 +080044 """Failed to complete wiping."""
45
Joel Kitching679a00b2016-08-03 11:42:58 +080046
Wei-Han Chenbe1355a2016-04-24 19:31:03 +080047def _CopyLogFileToStateDev(state_dev, logfile):
Wei-Han Chene97d3532016-03-31 19:22:01 +080048 with sys_utils.MountPartition(state_dev,
49 rw=True,
50 fstype='ext4') as mount_point:
51 shutil.copyfile(logfile,
52 os.path.join(mount_point, os.path.basename(logfile)))
53
54
Wei-Han Chenbe1355a2016-04-24 19:31:03 +080055def _OnError(ip, port, token, state_dev, wipe_in_tmpfs_log=None,
56 wipe_init_log=None):
57 if wipe_in_tmpfs_log:
58 _CopyLogFileToStateDev(state_dev, wipe_in_tmpfs_log)
59 if wipe_init_log:
60 _CopyLogFileToStateDev(state_dev, wipe_init_log)
61 _InformStation(ip, port, token,
62 wipe_in_tmpfs_log=wipe_in_tmpfs_log,
63 wipe_init_log=wipe_init_log,
64 success=False)
65
66
Wei-Han Chene97d3532016-03-31 19:22:01 +080067def Daemonize(logfile=None):
68 """Starts a daemon process and terminates current process.
69
You-Cheng Syu461ec032017-03-06 15:56:58 +080070 A daemon process will be started, and continue executing the following codes.
Wei-Han Chene97d3532016-03-31 19:22:01 +080071 The original process that calls this function will be terminated.
72
73 Example::
74
75 def DaemonFunc():
76 Daemonize()
77 # the process calling DaemonFunc is terminated.
78 # the following codes will be executed in a daemon process
79 ...
80
81 If you would like to keep the original process alive, you could fork a child
82 process and let child process start the daemon.
83 """
84 # fork from parent process
85 if os.fork():
86 # stop parent process
87 os._exit(0) # pylint: disable=protected-access
88
89 # decouple from parent process
90 os.chdir('/')
91 os.umask(0)
92 os.setsid()
93
94 # fork again
95 if os.fork():
96 os._exit(0) # pylint: disable=protected-access
97
98 maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
99 if maxfd == resource.RLIM_INFINITY:
100 maxfd = 1024
101
102 for fd in xrange(maxfd):
103 try:
104 os.close(fd)
105 except OSError:
106 pass
107
108 # Reopen fd 0 (stdin), 1 (stdout), 2 (stderr) to prevent errors from reading
109 # or writing to these files.
110 # Since we have closed all file descriptors, os.open should open a file with
111 # file descriptor equals to 0
112 os.open('/dev/null', os.O_RDWR)
113 if logfile is None:
114 os.dup2(0, 1) # stdout
115 os.dup2(0, 2) # stderr
116 else:
117 os.open(logfile, os.O_RDWR | os.O_CREAT)
118 os.dup2(1, 2) # stderr
119
120
121def ResetLog(logfile=None):
Peter Shih19a938f2018-02-26 14:26:16 +0800122 if logging.getLogger().handlers:
Wei-Han Chene97d3532016-03-31 19:22:01 +0800123 for handler in logging.getLogger().handlers:
124 logging.getLogger().removeHandler(handler)
125 logging.basicConfig(filename=logfile, level=logging.NOTSET)
126
127
Hung-Te Lin7b27f0c2016-10-18 18:41:29 +0800128def WipeInTmpFs(is_fast=None, shopfloor_url=None, station_ip=None,
129 station_port=None, wipe_finish_token=None):
You-Cheng Syu461ec032017-03-06 15:56:58 +0800130 """prepare to wipe by pivot root to tmpfs and unmount stateful partition.
Wei-Han Chene97d3532016-03-31 19:22:01 +0800131
132 Args:
133 is_fast: whether or not to apply fast wipe.
Wei-Han Chene97d3532016-03-31 19:22:01 +0800134 shopfloor_url: for inform_shopfloor.sh
135 """
136
Shen-En Shih502b3102018-04-24 11:12:01 +0800137 def _CheckBug78323428():
138 # b/78323428: Check if dhcpcd is locking /var/run. If dhcpcd is locking
139 # /var/run, unmount will fail. Need CL:1021611 to use /run instead.
140 for pid in Shell('pgrep dhcpcd').stdout.splitlines():
141 lock_result = Shell('ls -al /proc/%s/fd | grep /var/run' % pid)
142 if lock_result.stdout:
143 raise WipeError('dhcpcd is still locking on /var/run. Please use a '
144 'newer ChromeOS image with CL:1021611 included. '
145 'Lock info: "%s"' % lock_result.stdout)
146 _CheckBug78323428()
147
Wei-Han Chene97d3532016-03-31 19:22:01 +0800148 Daemonize()
149
You-Cheng Syu461ec032017-03-06 15:56:58 +0800150 # Set the default umask.
Peter Shihe6afab32018-09-11 17:16:48 +0800151 os.umask(0o022)
Shun-Hsing Oub5724832016-07-21 11:45:58 +0800152
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800153 logfile = os.path.join('/tmp', WIPE_IN_TMPFS_LOG)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800154 ResetLog(logfile)
155
Wei-Han Chen0a3320e2016-04-23 01:32:07 +0800156 factory_par = paths.GetFactoryPythonArchivePath()
Wei-Han Chene97d3532016-03-31 19:22:01 +0800157
158 new_root = tempfile.mkdtemp(prefix='tmpfs.')
159 binary_deps = [
160 'activate_date', 'backlight_tool', 'busybox', 'cgpt', 'cgpt.bin',
161 'clobber-log', 'clobber-state', 'coreutils', 'crossystem', 'dd',
162 'display_boot_message', 'dumpe2fs', 'ectool', 'flashrom', 'halt',
163 'initctl', 'mkfs.ext4', 'mktemp', 'mosys', 'mount', 'mount-encrypted',
164 'od', 'pango-view', 'pkill', 'pv', 'python', 'reboot', 'setterm', 'sh',
Cheng-Han Yang6f12dc42017-11-30 15:28:38 +0800165 'shutdown', 'stop', 'umount', 'vpd', 'curl', 'lsof', 'jq', '/sbin/frecon',
166 'stressapptest']
Wei-Han Chene97d3532016-03-31 19:22:01 +0800167
168 etc_issue = textwrap.dedent("""
169 You are now in tmp file system created for in-place wiping.
170
171 For debugging wiping fails, see log files under
172 /tmp
173 /mnt/stateful_partition/unencrypted
174
175 The log file name should be
176 - wipe_in_tmpfs.log
177 - wipe_init.log
178
179 You can also run scripts under /usr/local/factory/sh for wiping process.
180 """)
181
Wei-Han Chen0a3320e2016-04-23 01:32:07 +0800182 util = Util()
183
184 root_disk = util.GetPrimaryDevicePath()
185 release_rootfs = util.GetReleaseRootPartitionPath()
186 state_dev = util.GetPrimaryDevicePath(1)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800187 wipe_args = 'factory' + (' fast' if is_fast else '')
188
189 logging.debug('state_dev: %s', state_dev)
190 logging.debug('factory_par: %s', factory_par)
191
192 old_root = 'old_root'
193
194 try:
Shun-Hsing Oub5724832016-07-21 11:45:58 +0800195 # pango load library module dynamically. Therefore we need to query it
196 # first.
197 pango_query_output = process_utils.SpawnOutput(
198 ['pango-querymodules', '--system'])
199 m = re.search(r'^# ModulesPath = (.+)$', pango_query_output, re.M)
Peter Shihe6afab32018-09-11 17:16:48 +0800200 assert m is not None, 'Failed to find pango module path.'
Shun-Hsing Oub5724832016-07-21 11:45:58 +0800201 pango_module = m.group(1)
202
Wei-Han Chene97d3532016-03-31 19:22:01 +0800203 with chroot.TmpChroot(
204 new_root,
205 file_dir_list=[
Shun-Hsing Oub5724832016-07-21 11:45:58 +0800206 # Basic rootfs.
Wei-Ning Huang71f94e12016-07-17 23:21:41 +0800207 '/bin', '/etc', '/lib', '/lib64', '/root', '/sbin',
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800208 '/usr/sbin', '/usr/bin',
Shun-Hsing Oub5724832016-07-21 11:45:58 +0800209 # Factory related scripts.
210 factory_par,
211 '/usr/local/factory/sh',
Wei-Han Chen85ace052017-06-24 15:39:50 +0800212 # Factory config files
213 '/usr/local/factory/py/config',
Shun-Hsing Oub5724832016-07-21 11:45:58 +0800214 # Fonts and assets required for showing message.
215 pango_module,
Wei-Han Chene97d3532016-03-31 19:22:01 +0800216 '/usr/share/fonts/notocjk',
217 '/usr/share/cache/fontconfig',
218 '/usr/share/chromeos-assets/images',
219 '/usr/share/chromeos-assets/text/boot_messages',
220 '/usr/share/misc/chromeos-common.sh',
Shun-Hsing Oub5724832016-07-21 11:45:58 +0800221 # File required for enable ssh connection.
222 '/mnt/stateful_partition/etc/ssh',
223 '/root/.ssh',
224 '/usr/share/chromeos-ssh-config',
225 # /var/empty is required by openssh server.
226 '/var/empty'],
Wei-Han Chene97d3532016-03-31 19:22:01 +0800227 binary_list=binary_deps, etc_issue=etc_issue).PivotRoot(old_root):
228 logging.debug(
229 'lsof: %s',
230 process_utils.SpawnOutput('lsof -p %d' % os.getpid(), shell=True))
231
Hung-Te Lin6ce54bd2017-06-27 16:20:36 +0800232 # Modify display_wipe_message so we have shells in VT2.
233 # --dev-mode provides shell with etc-issue.
234 # --enable-vt1 allows drawing escapes (OSC) on VT1 but it'll also display
235 # etc-issue and login prompt.
236 # For now we only want login prompts on VT2+.
237 process_utils.Spawn(['sed', '-i',
238 's/--no-login/--dev-mode/g;s/--enable-vt1//g',
239 '/usr/sbin/display_boot_message'],
240 call=True)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800241
242 # Restart gooftool under new root. Since current gooftool might be using
243 # some resource under stateful partition, restarting gooftool ensures that
244 # everything new gooftool is using comes from tmpfs and we can safely
245 # unmount stateful partition.
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800246 args = []
247 if wipe_args:
248 args += ['--wipe_args', wipe_args]
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800249 if shopfloor_url:
250 args += ['--shopfloor_url', shopfloor_url]
251 if station_ip:
252 args += ['--station_ip', station_ip]
253 if station_port:
254 args += ['--station_port', station_port]
255 if wipe_finish_token:
256 args += ['--wipe_finish_token', wipe_finish_token]
257 args += ['--state_dev', state_dev]
258 args += ['--release_rootfs', release_rootfs]
259 args += ['--root_disk', root_disk]
260 args += ['--old_root', old_root]
261
262 ExecFactoryPar('gooftool', 'wipe_init', *args)
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800263 raise WipeError('Should not reach here')
Hung-Te Linc8174b52017-06-02 11:11:45 +0800264 except Exception:
Wei-Han Chene97d3532016-03-31 19:22:01 +0800265 logging.exception('wipe_in_place failed')
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800266 _OnError(station_ip, station_port, wipe_finish_token, state_dev,
267 wipe_in_tmpfs_log=logfile, wipe_init_log=None)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800268 raise
269
270
271def _StopAllUpstartJobs(exclude_list=None):
272 logging.debug('stopping upstart jobs')
273
274 # Try three times to stop running services because some service will respawn
275 # one time after being stopped, e.g. shill_respawn. Two times should be enough
276 # to stop shill. Adding one more try for safety.
277
278 if exclude_list is None:
279 exclude_list = []
280
281 for unused_tries in xrange(3):
282 service_list = process_utils.SpawnOutput(['initctl', 'list']).splitlines()
283 service_list = [
284 line.split()[0] for line in service_list if 'start/running' in line]
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800285 logging.info('Going to stop: services: %r', service_list)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800286 for service in service_list:
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800287 if service in exclude_list or service.startswith('console-'):
Wei-Han Chene97d3532016-03-31 19:22:01 +0800288 continue
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800289 process_utils.Spawn(['stop', service], call=True, log=True)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800290
291
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800292def _UnmountStatefulPartition(root, state_dev):
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800293 logging.debug('Unmount stateful partition.')
Wei-Han Chene97d3532016-03-31 19:22:01 +0800294
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800295 # Expected stateful partition mount point.
296 state_dir = os.path.join(root, STATEFUL_PARTITION_PATH.strip(os.path.sep))
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800297
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800298 # Touch a mark file so we can check if the stateful partition is wiped
299 # successfully.
300 file_utils.WriteFile(os.path.join(state_dir, WIPE_MARK_FILE), '')
301
302 # Backup extension cache (crx_cache) if available (will be restored after
303 # wiping by clobber-state).
304 crx_cache_path = os.path.join(state_dir, CRX_CACHE_PAYLOAD_NAME)
305 if os.path.exists(crx_cache_path):
306 shutil.copyfile(crx_cache_path, CRX_CACHE_TAR_PATH)
307
308 # Find mount points on stateful partition.
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800309 mount_output = process_utils.SpawnOutput(['mount'], log=True)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800310
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800311 mount_point_list = []
312 for line in mount_output.splitlines():
313 fields = line.split()
314 if fields[0] == state_dev:
315 mount_point_list.append(fields[2])
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800316 logging.debug('stateful partitions mounted on: %s', mount_point_list)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800317
Wei-Han Chen0a3320e2016-04-23 01:32:07 +0800318 def _ListProcOpening(path_list):
319 lsof_cmd = ['lsof', '-t'] + path_list
Wei-Han Chene97d3532016-03-31 19:22:01 +0800320 return [int(line)
321 for line in process_utils.SpawnOutput(lsof_cmd).splitlines()]
322
Hung-Te Lin09226ef2017-01-11 18:00:19 +0800323 def _ListMinijail():
324 # Not sure why, but if we use 'minijail0', then we can't find processes that
325 # starts with /sbin/minijail0.
326 list_cmd = ['pgrep', 'minijail']
327 return [int(line)
328 for line in process_utils.SpawnOutput(list_cmd).splitlines()]
329
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800330 # Find processes that are using stateful partitions.
Wei-Han Chene97d3532016-03-31 19:22:01 +0800331 proc_list = _ListProcOpening(mount_point_list)
332
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800333 if os.getpid() in proc_list:
Wei-Han Chene97d3532016-03-31 19:22:01 +0800334 logging.error('wipe_init itself is using stateful partition')
335 logging.error(
336 'lsof: %s',
337 process_utils.SpawnOutput('lsof -p %d' % os.getpid(), shell=True))
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800338 raise WipeError('wipe_init itself is using stateful partition')
Wei-Han Chene97d3532016-03-31 19:22:01 +0800339
340 def _KillOpeningBySignal(sig):
341 proc_list = _ListProcOpening(mount_point_list)
342 if not proc_list:
343 return True # we are done
344 for pid in proc_list:
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800345 try:
346 os.kill(pid, sig)
Hung-Te Linc8174b52017-06-02 11:11:45 +0800347 except Exception:
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800348 logging.exception('killing process %d failed', pid)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800349 return False # need to check again
350
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800351 # Try to kill processes using stateful partition gracefully.
Wei-Han Chene97d3532016-03-31 19:22:01 +0800352 sync_utils.Retry(10, 0.1, None, _KillOpeningBySignal, signal.SIGTERM)
353 sync_utils.Retry(10, 0.1, None, _KillOpeningBySignal, signal.SIGKILL)
354
355 proc_list = _ListProcOpening(mount_point_list)
356 assert not proc_list, "processes using stateful partition: %s" % proc_list
357
You-Cheng Syu2ea26dd2016-12-06 20:50:05 +0800358 def _Unmount(mount_point, critical):
Hung-Te Lin09226ef2017-01-11 18:00:19 +0800359 logging.info('try to unmount %s', mount_point)
You-Cheng Syu2ea26dd2016-12-06 20:50:05 +0800360 for unused_i in xrange(10):
361 output = process_utils.Spawn(['umount', '-n', '-R', mount_point],
362 read_stderr=True, log=True).stderr_data
363 # some mount points need to be unmounted multiple times.
364 if (output.endswith(': not mounted\n') or
365 output.endswith(': not found\n')):
366 return
367 time.sleep(0.5)
Hung-Te Lin09226ef2017-01-11 18:00:19 +0800368 logging.error('failed to unmount %s', mount_point)
You-Cheng Syu2ea26dd2016-12-06 20:50:05 +0800369 if critical:
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800370 raise WipeError('Unmounting %s is critical. Stop.' % mount_point)
You-Cheng Syu2ea26dd2016-12-06 20:50:05 +0800371
Wei-Han Chene97d3532016-03-31 19:22:01 +0800372 if os.path.exists(os.path.join(root, 'dev', 'mapper', 'encstateful')):
Hung-Te Lin09226ef2017-01-11 18:00:19 +0800373
374 # minijail will make encstateful busy, but usually we can't just kill them.
375 # Need to list the processes and solve each-by-each.
376 proc_list = _ListMinijail()
377 assert not proc_list, "processes still using minijail: %s" % proc_list
378
You-Cheng Syuf0990462016-09-07 14:56:19 +0800379 # Doing what 'mount-encrypted umount' should do.
380 for mount_point in mount_point_list:
You-Cheng Syu2ea26dd2016-12-06 20:50:05 +0800381 _Unmount(mount_point, False)
382 _Unmount(os.path.join(root, 'var'), True)
Jeffy Chend5b08e12017-03-06 10:22:59 +0800383 process_utils.Spawn(['dmsetup', 'remove', 'encstateful',
384 '--noudevrules', '--noudevsync'], check_call=True)
You-Cheng Syuf0990462016-09-07 14:56:19 +0800385 process_utils.Spawn(['losetup', '-D'], check_call=True)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800386
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800387 # Try to unmount all known mount points.
Wei-Han Chene97d3532016-03-31 19:22:01 +0800388 for mount_point in mount_point_list:
You-Cheng Syu2ea26dd2016-12-06 20:50:05 +0800389 _Unmount(mount_point, True)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800390 process_utils.Spawn(['sync'], call=True)
391
You-Cheng Syuf0990462016-09-07 14:56:19 +0800392 # Check if the stateful partition is unmounted successfully.
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800393 if _IsStateDevMounted(state_dev):
394 raise WipeError('Failed to unmount stateful_partition')
395
396
397def _IsStateDevMounted(state_dev):
398 try:
399 output = process_utils.CheckOutput(['df', state_dev])
400 return output.splitlines()[-1].split()[0] == state_dev
401 except Exception:
402 return False
You-Cheng Syuf0990462016-09-07 14:56:19 +0800403
Wei-Han Chene97d3532016-03-31 19:22:01 +0800404
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800405def _InformStation(ip, port, token, wipe_init_log=None,
406 wipe_in_tmpfs_log=None, success=True):
407 if not ip:
408 return
409 port = int(port)
410
411 logging.debug('inform station %s:%d', ip, port)
412
413 try:
414 sync_utils.WaitFor(
Peter Shih14458732018-02-26 14:40:15 +0800415 lambda: process_utils.Spawn(['ping', '-w1', '-c1', ip],
416 call=True).returncode == 0,
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800417 timeout_secs=180, poll_interval=1)
Hung-Te Linc8174b52017-06-02 11:11:45 +0800418 except Exception:
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800419 logging.exception('cannot get network connection...')
420 else:
421 sock = socket.socket()
422 sock.connect((ip, port))
423
424 response = dict(token=token, success=success)
425
426 if wipe_init_log:
427 with open(wipe_init_log) as f:
428 response['wipe_init_log'] = f.read()
429
430 if wipe_in_tmpfs_log:
431 with open(wipe_in_tmpfs_log) as f:
432 response['wipe_in_tmpfs_log'] = f.read()
433
434 sock.sendall(json.dumps(response) + '\n')
435 sock.close()
436
437
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800438def _WipeStateDev(release_rootfs, root_disk, wipe_args, state_dev):
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800439 clobber_state_env = os.environ.copy()
Wei-Han Chen0a3320e2016-04-23 01:32:07 +0800440 clobber_state_env.update(ROOT_DEV=release_rootfs,
Earl Oueeb289d2016-11-04 14:36:40 +0800441 ROOT_DISK=root_disk)
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800442 logging.debug('clobber-state: root_dev=%s, root_disk=%s',
Wei-Han Chen0a3320e2016-04-23 01:32:07 +0800443 release_rootfs, root_disk)
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800444 process_utils.Spawn(
445 ['clobber-state', wipe_args], env=clobber_state_env, check_call=True)
446
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800447 logging.info('Checking if stateful partition is mounted...')
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800448 # Check if the stateful partition is wiped.
449 if not _IsStateDevMounted(state_dev):
450 process_utils.Spawn(['mount', state_dev, STATEFUL_PARTITION_PATH],
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800451 check_call=True, log=True)
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800452
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800453 logging.info('Checking wipe mark file %s...', WIPE_MARK_FILE)
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800454 if os.path.exists(
455 os.path.join(STATEFUL_PARTITION_PATH, WIPE_MARK_FILE)):
456 raise WipeError(WIPE_MARK_FILE + ' still exists')
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800457
458 # Restore CRX cache.
459 logging.info('Checking CRX cache %s...', CRX_CACHE_TAR_PATH)
460 if os.path.exists(CRX_CACHE_TAR_PATH):
461 process_utils.Spawn(['tar', '-xpvf', CRX_CACHE_TAR_PATH, '-C',
462 STATEFUL_PARTITION_PATH], check_call=True, log=True)
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800463
464 # Remove developer flag, which is created by clobber-state after wiping.
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800465 try:
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800466 # TODO(hungte) Unlink or create developer flag according to gooftool
467 # execution results.
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800468 os.unlink(os.path.join(STATEFUL_PARTITION_PATH, '.developer_mode'))
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800469 except OSError:
470 pass
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800471
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800472 process_utils.Spawn(['umount', STATEFUL_PARTITION_PATH], call=True)
473 # Make sure that everything is synced.
474 process_utils.Spawn(['sync'], call=True)
475 time.sleep(3)
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800476
Joel Kitching679a00b2016-08-03 11:42:58 +0800477
Earl Ou564a7872016-10-05 10:22:00 +0800478def EnableReleasePartition(release_rootfs):
479 """Enables a release image partition on disk."""
Wei-Han Chen0a3320e2016-04-23 01:32:07 +0800480 logging.debug('enable release partition: %s', release_rootfs)
481 Util().EnableReleasePartition(release_rootfs)
Earl Ou564a7872016-10-05 10:22:00 +0800482 logging.debug('Device will boot from %s after reboot.', release_rootfs)
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800483
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800484
485def _InformShopfloor(shopfloor_url):
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800486 if shopfloor_url:
487 logging.debug('inform shopfloor %s', shopfloor_url)
Hung-Te Lina3195462016-10-14 15:48:29 +0800488 proc = process_utils.Spawn(
Yilun Lindbb8af72018-01-31 16:01:17 +0800489 [
490 os.path.join(CUTOFF_SCRIPT_DIR, 'inform_shopfloor.sh'),
491 shopfloor_url, 'factory_wipe'
492 ],
493 read_stdout=True,
494 read_stderr=True)
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800495 logging.debug('stdout: %s', proc.stdout_data)
496 logging.debug('stderr: %s', proc.stderr_data)
Yilun Lindbb8af72018-01-31 16:01:17 +0800497 if proc.returncode != 0:
Peter Shihbf6f22b2018-02-26 14:05:28 +0800498 raise RuntimeError('InformShopfloor failed.')
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800499
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800500
Hung-Te Lin7b27f0c2016-10-18 18:41:29 +0800501def _Cutoff():
502 logging.debug('cutoff')
Hung-Te Lina3195462016-10-14 15:48:29 +0800503 cutoff_script = os.path.join(CUTOFF_SCRIPT_DIR, 'cutoff.sh')
You-Cheng Syue6844172017-11-28 16:39:32 +0800504 process_utils.Spawn([cutoff_script], check_call=True)
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800505
506
Hung-Te Lin7b27f0c2016-10-18 18:41:29 +0800507def WipeInit(wipe_args, shopfloor_url, state_dev, release_rootfs,
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800508 root_disk, old_root, station_ip, station_port, finish_token):
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800509 Daemonize()
Wei-Han Chene97d3532016-03-31 19:22:01 +0800510 logfile = '/tmp/wipe_init.log'
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800511 wipe_in_tmpfs_log = os.path.join(old_root, 'tmp', WIPE_IN_TMPFS_LOG)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800512 ResetLog(logfile)
513
Wei-Han Chen0a3320e2016-04-23 01:32:07 +0800514 logging.debug('wipe_args: %s', wipe_args)
Wei-Han Chen0a3320e2016-04-23 01:32:07 +0800515 logging.debug('shopfloor_url: %s', shopfloor_url)
516 logging.debug('state_dev: %s', state_dev)
517 logging.debug('release_rootfs: %s', release_rootfs)
518 logging.debug('root_disk: %s', root_disk)
519 logging.debug('old_root: %s', old_root)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800520
Wei-Han Chene97d3532016-03-31 19:22:01 +0800521 try:
Shun-Hsing Oub5724832016-07-21 11:45:58 +0800522 _StopAllUpstartJobs(exclude_list=[
523 # Milestone marker that use to determine the running of other services.
524 'boot-services',
525 'system-services',
526 'failsafe',
527 # Keep dbus to make sure we can shutdown the device.
528 'dbus',
529 # Keep shill for connecting to shopfloor or stations.
530 'shill',
531 # Keep openssh-server for debugging purpose.
532 'openssh-server',
533 # sslh is a service in ARC++ for muxing between ssh and adb.
534 'sslh'
Peter Shihe6afab32018-09-11 17:16:48 +0800535 ])
Wei-Han Chenc8f24562016-04-23 19:42:42 +0800536 _UnmountStatefulPartition(old_root, state_dev)
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800537
Hung-Te Lina3195462016-10-14 15:48:29 +0800538 process_utils.Spawn(
539 [os.path.join(CUTOFF_SCRIPT_DIR, 'display_wipe_message.sh'), 'wipe'],
540 call=True)
Wei-Han Chen9adf9de2016-04-01 19:35:41 +0800541
Wei-Han Chenb05699a2017-07-12 16:37:47 +0800542 try:
543 _WipeStateDev(release_rootfs, root_disk, wipe_args, state_dev)
544 except Exception:
545 process_utils.Spawn(
546 [os.path.join(CUTOFF_SCRIPT_DIR, 'display_wipe_message.sh'),
547 'wipe_failed'], call=True)
548 raise
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800549
Earl Ou564a7872016-10-05 10:22:00 +0800550 EnableReleasePartition(release_rootfs)
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800551
552 _InformShopfloor(shopfloor_url)
553
554 _InformStation(station_ip, station_port, finish_token,
555 wipe_init_log=logfile,
556 wipe_in_tmpfs_log=wipe_in_tmpfs_log,
557 success=True)
558
Hung-Te Lin7b27f0c2016-10-18 18:41:29 +0800559 _Cutoff()
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800560
561 # should not reach here
Hung-Te Lindd3425d2017-07-12 20:10:52 +0800562 logging.info('Going to sleep forever!')
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800563 time.sleep(1e8)
Hung-Te Linc8174b52017-06-02 11:11:45 +0800564 except Exception:
Wei-Han Chene97d3532016-03-31 19:22:01 +0800565 logging.exception('wipe_init failed')
Wei-Han Chenbe1355a2016-04-24 19:31:03 +0800566 _OnError(station_ip, station_port, finish_token, state_dev,
567 wipe_in_tmpfs_log=wipe_in_tmpfs_log, wipe_init_log=logfile)
Wei-Han Chene97d3532016-03-31 19:22:01 +0800568 raise