blob: bafb74d9bcb407e34b6b361b55fd3f0a010c64ed [file] [log] [blame]
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +08001#!/usr/bin/python -Bu
2#
3# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Factory toolkit installer.
8
9The factory toolkit is a self-extracting shellball containing factory test
10related files and this installer. This installer is invoked when the toolkit
11is deployed and is responsible for installing files.
12"""
13
14
15import argparse
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080016from contextlib import contextmanager
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080017import os
Wei-Ning Huang4855e792015-06-11 15:33:39 +080018import shutil
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080019import sys
Jon Salz4f3ade52014-02-20 17:55:09 +080020import tempfile
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080021
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080022import factory_common # pylint: disable=W0611
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +080023from cros.factory.test import event_log
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080024from cros.factory.test.env import paths
Jon Salz25590302014-07-11 16:07:20 +080025from cros.factory.tools import install_symlinks
Rong Chang6d65fad2014-08-22 16:00:12 +080026from cros.factory.utils import file_utils
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +080027from cros.factory.utils import sys_utils
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080028from cros.factory.utils.process_utils import Spawn
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080029
30
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +080031INSTALLER_PATH = 'usr/local/factory/py/toolkit/installer.py'
Rong Chang6d65fad2014-08-22 16:00:12 +080032MAKESELF_SHELL = '/bin/sh'
33TOOLKIT_NAME = 'install_factory_toolkit.run'
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +080034
Jon Salz4f3ade52014-02-20 17:55:09 +080035# Short and sweet help header for the executable generated by makeself.
36HELP_HEADER = """
37Installs the factory toolkit, transforming a test image into a factory test
38image. You can:
39
40- Install the factory toolkit on a CrOS device that is running a test
41 image. To do this, copy install_factory_toolkit.run to the device and
42 run it. The factory tests will then come up on the next boot.
43
44 rsync -a install_factory_toolkit.run crosdevice:/tmp
45 ssh crosdevice '/tmp/install_factory_toolkit.run && sync && reboot'
46
47- Modify a test image, turning it into a factory test image. When you
48 use the image on a device, the factory tests will come up.
49
50 install_factory_toolkit.run chromiumos_test_image.bin
51"""
52
53HELP_HEADER_ADVANCED = """
54- (advanced) Modify a mounted stateful partition, turning it into a factory
55 test image. This is equivalent to the previous command:
56
57 mount_partition -rw chromiumos_test_image.bin 1 /mnt/stateful
58 install_factory_toolkit.run /mnt/stateful
59 umount /mnt/stateful
60
61- (advanced) Unpack the factory toolkit, modify a file, and then repack it.
62
63 # Unpack but don't actually install
64 install_factory_toolkit.run --target /tmp/toolkit --noexec
65 # Edit some files in /tmp/toolkit
66 emacs /tmp/toolkit/whatever
67 # Repack
68 install_factory_toolkit.run -- --repack /tmp/toolkit \\
69 --pack-into /path/to/new/install_factory_toolkit.run
70"""
71
72# The makeself-generated header comes next. This is a little confusing,
73# so explain.
74HELP_HEADER_MAKESELF = """
75For complete usage information and advanced operations, run
76"install_factory_toolkit.run -- --help" (note the extra "--").
77
78Following is the help message from makeself, which was used to create
79this self-extracting archive.
80
81-----
82"""
83
Vic Yangdb1e20e2014-10-05 12:10:33 +080084SERVER_FILE_MASK = [
85 # Exclude Umpire server but keep Umpire client
86 '--include', 'py/umpire/__init__.*',
87 '--include', 'py/umpire/common.*',
88 '--include', 'py/umpire/client',
89 '--include', 'py/umpire/client/**',
90 '--exclude', 'py/umpire/**',
91
92 # Lumberjack is only used on Umpire server
93 '--exclude', 'py/lumberjack'
94]
95
Jon Salz4f3ade52014-02-20 17:55:09 +080096
Hung-Te Lin7b596ff2015-01-16 20:19:15 +080097class FactoryToolkitInstaller(object):
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080098 """Factory toolkit installer.
99
100 Args:
101 src: Source path containing usr/ and var/.
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800102 dest: Installation destination path. Set this to the mount point of the
103 stateful partition if patching a test image.
104 no_enable: True to not install the tag file.
105 system_root: The path to the root of the file system. This must be left
106 as its default value except for unit testing.
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800107 """
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800108
Jon Salzb7e44262014-05-07 15:53:37 +0800109 # Whether to sudo when rsyncing; set to False for testing.
110 _sudo = True
111
Peter Ammon948b7172014-07-15 12:43:06 -0700112 def __init__(self, src, dest, no_enable, enable_presenter,
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800113 enable_device, non_cros=False, device_id=None, system_root='/',
114 apps=None):
Jon Salz4f3ade52014-02-20 17:55:09 +0800115 self._src = src
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800116 self._system_root = system_root
117 if dest == self._system_root:
118 self._usr_local_dest = os.path.join(dest, 'usr', 'local')
119 self._var_dest = os.path.join(dest, 'var')
Jon Salz4f3ade52014-02-20 17:55:09 +0800120
121 # Make sure we're on a CrOS device.
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800122 if not non_cros and not sys_utils.InCrOSDevice():
Jon Salz4f3ade52014-02-20 17:55:09 +0800123 sys.stderr.write(
Vic Yang196d5242014-08-05 13:51:35 +0800124 "ERROR: You're not on a CrOS device (for more details, please\n"
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800125 'check sys_utils.py:InCrOSDevice), so you must specify a test\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800126 'image or a mounted stateful partition on which to install the\n'
127 'factory toolkit. Please run\n'
128 '\n'
129 ' install_factory_toolkit.run -- --help\n'
130 '\n'
Vic Yang70fdae92015-02-17 19:21:08 -0800131 'for help.\n'
132 '\n'
133 'If you want to install the presenter on a non-CrOS host,\n'
134 'please run\n'
135 '\n'
136 ' install_factory_toolkit.run -- \\\n'
Hung-Te Lin0cba9d52016-03-22 11:45:35 +0800137 ' --non-cros --enable-presenter\n'
Vic Yang70fdae92015-02-17 19:21:08 -0800138 '\n')
Jon Salz4f3ade52014-02-20 17:55:09 +0800139 sys.exit(1)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800140 if os.getuid() != 0:
Jon Salz4f3ade52014-02-20 17:55:09 +0800141 raise Exception('You must be root to install the factory toolkit on a '
142 'CrOS device.')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800143 else:
144 self._usr_local_dest = os.path.join(dest, 'dev_image')
145 self._var_dest = os.path.join(dest, 'var_overlay')
146 if (not os.path.exists(self._usr_local_dest) or
147 not os.path.exists(self._var_dest)):
148 raise Exception(
149 'The destination path %s is not a stateful partition!' % dest)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800150
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800151 self._dest = dest
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800152 self._usr_local_src = os.path.join(src, 'usr', 'local')
153 self._var_src = os.path.join(src, 'var')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800154 self._no_enable = no_enable
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800155 self._tag_file = os.path.join(self._usr_local_dest, 'factory', 'enabled')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800156
Peter Ammon948b7172014-07-15 12:43:06 -0700157 self._enable_presenter = enable_presenter
158 self._presenter_tag_file = os.path.join(self._usr_local_dest, 'factory',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800159 'init', 'run_goofy_presenter')
Vic Yang7039f422014-07-07 15:38:13 -0700160
161 self._enable_device = enable_device
Ricky Liangd7716912014-07-10 11:52:24 +0800162 self._device_tag_file = os.path.join(self._usr_local_dest, 'factory',
163 'init', 'run_goofy_device')
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800164 self._device_id = device_id
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800165 self._apps = apps
Vic Yang7039f422014-07-07 15:38:13 -0700166
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800167 if (not os.path.exists(self._usr_local_src) or
168 not os.path.exists(self._var_src)):
169 raise Exception(
170 'This installer must be run from within the factory toolkit!')
171
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800172 def WarningMessage(self, target_test_image=None):
Jon Salz4f3ade52014-02-20 17:55:09 +0800173 with open(os.path.join(self._src, 'VERSION')) as f:
174 ret = f.read()
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800175 if target_test_image:
Jon Salz4f3ade52014-02-20 17:55:09 +0800176 ret += (
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800177 '\n'
178 '\n'
Jon Salz4f3ade52014-02-20 17:55:09 +0800179 '*** You are about to patch the factory toolkit into:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800180 '*** %s\n'
181 '***' % target_test_image)
182 else:
Jon Salz4f3ade52014-02-20 17:55:09 +0800183 ret += (
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800184 '\n'
185 '\n'
Jon Salz4f3ade52014-02-20 17:55:09 +0800186 '*** You are about to install the factory toolkit to:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800187 '*** %s\n'
188 '***' % self._dest)
189 if self._dest == self._system_root:
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800190 if self._no_enable:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800191 ret += ('\n*** Factory tests will be disabled after this process is '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800192 'done, but\n*** you can enable them by creating the factory '
193 'enabled tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800194 else:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800195 ret += ('\n*** After this process is done, your device will start '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800196 'factory\n*** tests on the next reboot.\n***\n*** Factory '
197 'tests can be disabled by deleting the factory enabled\n*** '
198 'tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800199 return ret
200
Vic Yang7039f422014-07-07 15:38:13 -0700201 def _SetTagFile(self, name, path, enabled):
202 """Install or remove a tag file."""
203 if enabled:
204 print '*** Installing %s enabled tag...' % name
205 Spawn(['touch', path], sudo=True, log=True, check_call=True)
Ricky Liang8c88a122014-07-11 21:21:22 +0800206 Spawn(['chmod', 'go+r', path], sudo=True, log=True, check_call=True)
Vic Yang7039f422014-07-07 15:38:13 -0700207 else:
208 print '*** Removing %s enabled tag...' % name
209 Spawn(['rm', '-f', path], sudo=True, log=True, check_call=True)
210
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800211 def _SetDeviceID(self):
212 if self._device_id is not None:
213 with open(os.path.join(event_log.DEVICE_ID_PATH), 'w') as f:
214 f.write(self._device_id)
215
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800216 def _EnableApp(self, app, enabled):
217 """Enable / disable @app.
218
219 In factory/init/startup, a main app is considered disabled if and only:
220 1. file "factory/init/main.d/disable-@app" exists OR
221 2. file "factory/init/main.d/enable-@app" doesn't exist AND
222 file "factory/init/main.d/@app.sh" is not executable.
223
224 Therefore, we enable an app by removing file "disable-@app" and creating
225 file "enable-@app", and vise versa.
226 """
227 app_enable = os.path.join(self._usr_local_dest,
228 'factory', 'init', 'main.d', 'enable-' + app)
229 app_disable = os.path.join(self._usr_local_dest,
230 'factory', 'init', 'main.d', 'disable-' + app)
231 if enabled:
232 print '*** Enabling {app} ***'.format(app=app)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800233 Spawn(['rm', '-f', app_disable], sudo=self._sudo, log=True,
234 check_call=True)
235 Spawn(['touch', app_enable], sudo=self._sudo, log=True, check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800236 else:
237 print '*** Disabling {app} ***'.format(app=app)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800238 Spawn(['touch', app_disable], sudo=self._sudo, log=True, check_call=True)
239 Spawn(['rm', '-f', app_enable], sudo=self._sudo, log=True,
240 check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800241
242 def _EnableApps(self):
243 if not self._apps:
244 return
245
246 app_list = []
247 for app in self._apps:
248 if app[0] == '+':
249 app_list.append((app[1:], True))
250 elif app[0] == '-':
251 app_list.append((app[1:], False))
252 else:
253 raise ValueError(
254 'Use +{app} to enable and -{app} to disable'.format(app=app))
255
256 for app, enabled in app_list:
257 self._EnableApp(app, enabled)
258
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800259 def InstallFactorySubDir(self, sub_dirs):
260 """Install only the specified directories under factory folder."""
261
262 def _InstallOneSubDir(sub_dir_name):
263 sub_dir_dest = os.path.join(self._usr_local_dest, 'factory', sub_dir_name)
264 sub_dir_src = os.path.join(self._src, 'usr', 'local', 'factory',
265 sub_dir_name)
266 try:
267 Spawn(['mkdir', '-p', sub_dir_dest], sudo=True, log=True,
268 check_call=True)
269 except OSError as e:
270 print str(e)
271 return
272
273 Spawn(['rsync', '-a', '--force', '-v',
274 sub_dir_src + '/', sub_dir_dest],
275 sudo=self._sudo, log=True, check_call=True)
276 Spawn(['chown', '-R', 'root', sub_dir_dest],
277 sudo=self._sudo, log=True, check_call=True)
278 Spawn(['chmod', '-R', 'go+rX', sub_dir_dest],
279 sudo=self._sudo, log=True, check_call=True)
280
281 for sub_dir_name in sub_dirs:
282 _InstallOneSubDir(sub_dir_name)
283
284 self._SetTagFile('factory', self._tag_file, not self._no_enable)
285 self._EnableApps()
286
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800287 def Install(self):
288 print '*** Installing factory toolkit...'
Jon Salzb7e44262014-05-07 15:53:37 +0800289 for src, dest in ((self._usr_local_src, self._usr_local_dest),
290 (self._var_src, self._var_dest)):
291 # Change the source directory to root, and add group/world read
292 # permissions. This is necessary because when the toolkit was
293 # unpacked, the user may not have been root so the permessions
294 # may be hosed. This is skipped for testing.
Peter Ammon5ac58422014-06-09 14:45:50 -0700295 # --force is necessary to allow goofy directory from prior
296 # toolkit installations to be overwritten by the goofy symlink.
Ricky Liang5e95be22014-07-09 12:52:07 +0800297 try:
298 if self._sudo:
299 Spawn(['chown', '-R', 'root', src],
300 sudo=True, log=True, check_call=True)
301 Spawn(['chmod', '-R', 'go+rX', src],
302 sudo=True, log=True, check_call=True)
303 print '*** %s -> %s' % (src, dest)
Hung-Te Lin56b18402015-01-16 14:52:30 +0800304 Spawn(['rsync', '-a', '--force'] + SERVER_FILE_MASK +
Vic Yangdb1e20e2014-10-05 12:10:33 +0800305 [src + '/', dest], sudo=self._sudo, log=True,
306 check_output=True, cwd=src)
Ricky Liang5e95be22014-07-09 12:52:07 +0800307 finally:
308 # Need to change the source directory back to the original user, or the
309 # script in makeself will fail to remove the temporary source directory.
310 if self._sudo:
311 myuser = os.environ.get('USER')
312 Spawn(['chown', '-R', myuser, src],
313 sudo=True, log=True, check_call=True)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800314
Jon Salz25590302014-07-11 16:07:20 +0800315 print '*** Installing symlinks...'
316 install_symlinks.InstallSymlinks(
317 '../factory/bin',
318 os.path.join(self._usr_local_dest, 'bin'),
319 install_symlinks.MODE_FULL,
320 sudo=self._sudo)
321
Vic Yang7039f422014-07-07 15:38:13 -0700322 self._SetTagFile('factory', self._tag_file, not self._no_enable)
Peter Ammon948b7172014-07-15 12:43:06 -0700323 self._SetTagFile('presenter', self._presenter_tag_file,
324 self._enable_presenter)
Vic Yang7039f422014-07-07 15:38:13 -0700325 self._SetTagFile('device', self._device_tag_file, self._enable_device)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800326
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800327 self._SetDeviceID()
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800328 self._EnableApps()
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800329
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800330 print '*** Installation completed.'
331
332
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800333@contextmanager
334def DummyContext(arg):
335 """A context manager that simply yields its argument."""
336 yield arg
337
338
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800339def PrintBuildInfo(src_root):
340 """Print build information."""
341 info_file = os.path.join(src_root, 'REPO_STATUS')
342 if not os.path.exists(info_file):
343 raise OSError('Build info file not found!')
344 with open(info_file, 'r') as f:
345 print f.read()
346
347
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800348def PackFactoryToolkit(src_root, output_path, enable_device, enable_presenter):
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800349 """Packs the files containing this script into a factory toolkit."""
350 with open(os.path.join(src_root, 'VERSION'), 'r') as f:
351 version = f.read().strip()
Jon Salz4f3ade52014-02-20 17:55:09 +0800352 with tempfile.NamedTemporaryFile() as help_header:
Hung-Te Lin56b18402015-01-16 14:52:30 +0800353 help_header.write(version + '\n' + HELP_HEADER + HELP_HEADER_MAKESELF)
Jon Salz4f3ade52014-02-20 17:55:09 +0800354 help_header.flush()
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800355 cmd = [os.path.join(src_root, 'makeself.sh'), '--bzip2', '--nox11',
Jon Salz4f3ade52014-02-20 17:55:09 +0800356 '--help-header', help_header.name,
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800357 src_root, output_path, version, INSTALLER_PATH, '--in-exe']
358 if not enable_device:
359 cmd.append('--no-enable-device')
360 if not enable_presenter:
361 cmd.append('--no-enable-presenter')
362 Spawn(cmd, check_call=True, log=True)
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800363 print ('\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800364 ' Factory toolkit generated at %s.\n'
365 '\n'
366 ' To install factory toolkit on a live device running a test image,\n'
367 ' copy this to the device and execute it as root.\n'
368 '\n'
369 ' Alternatively, the factory toolkit can be used to patch a test\n'
370 ' image. For more information, run:\n'
371 ' %s --help\n'
372 '\n' % (output_path, output_path))
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800373
374
Rong Chang6d65fad2014-08-22 16:00:12 +0800375def InitUmpire(exe_path, src_root, target_board):
376 """Inits Umpire server environment."""
377 if exe_path is None:
378 parent_cmdline = open('/proc/%s/cmdline' % os.getppid(),
379 'r').read().rstrip('\0').split('\0')
380
381 if parent_cmdline > 1 and parent_cmdline[0] == MAKESELF_SHELL:
382 # Get parent script name from parent process.
383 exe_path = parent_cmdline[1]
384 else:
385 # Set to default.
386 exe_path = TOOLKIT_NAME
387
388 if not exe_path.startswith('/'):
389 exe_path = os.path.join(os.environ.get('OLDPWD'), exe_path)
390
391 with file_utils.TempDirectory() as nano_bundle:
392 bundle_toolkit_dir = os.path.join(nano_bundle, 'factory_toolkit')
393 os.mkdir(bundle_toolkit_dir)
394 os.symlink(exe_path, os.path.join(bundle_toolkit_dir,
395 os.path.basename(exe_path)))
396 umpire_bin = os.path.join(src_root, 'usr', 'local', 'factory', 'bin',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800397 'umpire')
Rong Chang6d65fad2014-08-22 16:00:12 +0800398 Spawn([umpire_bin, 'init', '--board', target_board, nano_bundle],
399 check_call=True, log=True)
400 print ('\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800401 ' Umpire initialized successfully. Upstart service is running:\n'
402 ' umpire BOARD=%(board)s.\n'
403 ' For more information, please check umpire command line:\n'
404 '\n'
405 ' umpire-%(board)s --help (if your id is in umpire group)\n'
406 ' or sudo umpire-%(board)s --help\n'
407 '\n' % {'board': target_board})
Rong Chang6d65fad2014-08-22 16:00:12 +0800408
409
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800410def ExtractOverlord(src_root, output_dir):
411 output_dir = os.path.join(output_dir, 'overlord')
412 try:
413 os.makedirs(output_dir)
414 except OSError as e:
415 print str(e)
416 return
417
418 # Copy overlord binary and resource files
419 shutil.copyfile(os.path.join(src_root, 'usr/bin/overlordd'),
420 os.path.join(output_dir, 'overlordd'))
421 shutil.copytree(os.path.join(src_root, 'usr/share/overlord/app'),
422 os.path.join(output_dir, 'app'))
423
424 # Give overlordd execution permission
425 os.chmod(os.path.join(output_dir, 'overlordd'), 0755)
426 print "Extarcted overlord under '%s'" % output_dir
427
428
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800429def main():
Jon Salz4f3ade52014-02-20 17:55:09 +0800430 import logging
431 logging.basicConfig(level=logging.INFO)
432
433 # In order to determine which usage message to show, first determine
434 # whether we're in the self-extracting archive. Do this first
435 # because we need it to even parse the arguments.
436 if '--in-exe' in sys.argv:
437 sys.argv = [x for x in sys.argv if x != '--in-exe']
438 in_archive = True
439 else:
440 in_archive = False
441
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800442 parser = argparse.ArgumentParser(
Jon Salz4f3ade52014-02-20 17:55:09 +0800443 description=HELP_HEADER + HELP_HEADER_ADVANCED,
444 usage=('install_factory_toolkit.run -- [options]' if in_archive
445 else None),
446 formatter_class=argparse.RawDescriptionHelpFormatter)
Hung-Te Lin56b18402015-01-16 14:52:30 +0800447 parser.add_argument(
448 'dest', nargs='?', default='/',
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800449 help='A test image or the mount point of the stateful partition. '
450 "If omitted, install to live system, i.e. '/'.")
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800451 parser.add_argument('--no-enable', '-n', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800452 help="Don't enable factory tests after installing")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800453 parser.add_argument('--yes', '-y', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800454 help="Don't ask for confirmation")
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800455 parser.add_argument('--build-info', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800456 help='Print build information and exit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800457 parser.add_argument('--pack-into', metavar='NEW_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800458 help='Pack the files into a new factory toolkit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800459 parser.add_argument('--repack', metavar='UNPACKED_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800460 help='Repack from previously unpacked toolkit')
Vic Yang7039f422014-07-07 15:38:13 -0700461
Peter Ammon948b7172014-07-15 12:43:06 -0700462 parser.add_argument('--enable-presenter', dest='enable_presenter',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800463 action='store_true',
464 help='Run goofy in presenter mode on startup')
Peter Ammon948b7172014-07-15 12:43:06 -0700465 parser.add_argument('--no-enable-presenter', dest='enable_presenter',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800466 action='store_false', help=argparse.SUPPRESS)
Hung-Te Lin0cba9d52016-03-22 11:45:35 +0800467 parser.set_defaults(enable_presenter=False)
Vic Yang7039f422014-07-07 15:38:13 -0700468
Vic Yang70fdae92015-02-17 19:21:08 -0800469 parser.add_argument('--non-cros', dest='non_cros',
470 action='store_true',
471 help='Install on non-ChromeOS host.')
472
Vic Yang7039f422014-07-07 15:38:13 -0700473 parser.add_argument('--enable-device', dest='enable_device',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800474 action='store_true',
475 help='Run goofy in device mode on startup')
Vic Yang7039f422014-07-07 15:38:13 -0700476 parser.add_argument('--no-enable-device', dest='enable_device',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800477 action='store_false', help=argparse.SUPPRESS)
Hung-Te Lin0cba9d52016-03-22 11:45:35 +0800478 parser.set_defaults(enable_device=False)
Vic Yang423c2722014-09-24 16:05:06 +0800479
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800480 parser.add_argument('--device-id', dest='device_id', type=str, default=None,
481 help='Set device ID for this device')
482
Rong Chang6d65fad2014-08-22 16:00:12 +0800483 parser.add_argument('--init-umpire-board', dest='umpire_board',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800484 nargs='?', default=None,
485 help='Locally install Umpire server for specific board')
Rong Chang6d65fad2014-08-22 16:00:12 +0800486 parser.add_argument('--exe-path', dest='exe_path',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800487 nargs='?', default=None,
488 help='Current self-extracting archive pathname')
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800489 parser.add_argument('--extract-overlord', dest='extract_overlord',
490 metavar='OUTPUT_DIR', type=str, default=None,
491 help='Extract overlord from the toolkit')
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800492 parser.add_argument('--install-dirs', nargs='+', default=None,
493 help=('Install only the specified directories under '
494 'factory folder. Can be used with --apps to '
495 'enable / disable some apps. Defaults to install '
496 'all folders.'))
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800497 parser.add_argument('--apps', type=lambda s: s.split(','), default=None,
498 help=('Enable or disable some apps under '
499 'factory/init/main.d/. Use prefix "-" to disable, '
500 'prefix "+" to enable, and use "," to seperate. '
501 'For example: --apps="-goofy,+whale_servo"'))
Vic Yang7039f422014-07-07 15:38:13 -0700502
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800503 args = parser.parse_args()
504
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +0800505 src_root = paths.FACTORY_PATH
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800506 for _ in xrange(3):
507 src_root = os.path.dirname(src_root)
508
Rong Chang6d65fad2014-08-22 16:00:12 +0800509 # --init-umpire-board creates a nano bundle, then calls umpire command
510 # line utility to install the server code and upstart configurations.
511 if args.umpire_board:
512 InitUmpire(args.exe_path, src_root, args.umpire_board)
513 return
514
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800515 if args.extract_overlord is not None:
516 ExtractOverlord(src_root, args.extract_overlord)
517 return
518
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800519 # --pack-into may be called directly so this must be done before changing
520 # working directory to OLDPWD.
521 if args.pack_into and args.repack is None:
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800522 PackFactoryToolkit(src_root, args.pack_into, args.enable_device,
523 args.enable_presenter)
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800524 return
525
Jon Salz4f3ade52014-02-20 17:55:09 +0800526 if not in_archive:
527 # If you're not in the self-extracting archive, you're not allowed to
528 # do anything except the above --pack-into call.
529 parser.error('Not running from install_factory_toolkit.run; '
530 'only --pack-into (without --repack) is allowed')
531
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800532 # Change to original working directory in case the user specifies
533 # a relative path.
534 # TODO: Use USER_PWD instead when makeself is upgraded
535 os.chdir(os.environ['OLDPWD'])
536
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800537 if args.repack:
538 if args.pack_into is None:
539 parser.error('Must specify --pack-into when using --repack.')
540 Spawn([os.path.join(args.repack, INSTALLER_PATH),
541 '--pack-into', args.pack_into], check_call=True, log=True)
542 return
543
544 if args.build_info:
545 PrintBuildInfo(src_root)
546 return
547
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800548 if not os.path.exists(args.dest):
549 parser.error('Destination %s does not exist!' % args.dest)
550
551 patch_test_image = os.path.isfile(args.dest)
552
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800553 with (sys_utils.MountPartition(args.dest, 1, rw=True) if patch_test_image
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800554 else DummyContext(args.dest)) as dest:
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800555
Hung-Te Lin56b18402015-01-16 14:52:30 +0800556 installer = FactoryToolkitInstaller(
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800557 src=src_root, dest=dest, no_enable=args.no_enable,
558 enable_presenter=args.enable_presenter,
559 enable_device=args.enable_device, non_cros=args.non_cros,
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800560 device_id=args.device_id,
561 apps=args.apps)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800562
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800563 print installer.WarningMessage(args.dest if patch_test_image else None)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800564
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800565 if not args.yes:
566 answer = raw_input('*** Continue? [y/N] ')
567 if not answer or answer[0] not in 'yY':
568 sys.exit('Aborting.')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800569
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800570 if args.install_dirs:
571 installer.InstallFactorySubDir(args.install_dirs)
572 else:
573 installer.Install()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800574
575if __name__ == '__main__':
576 main()