blob: 27eb251737fda4d01245f2da39e4e9f7de95ebf2 [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
Hung-Te Lineb7632b2016-07-29 15:38:34 +080017import glob
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080018import os
Wei-Ning Huang4855e792015-06-11 15:33:39 +080019import shutil
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080020import sys
Jon Salz4f3ade52014-02-20 17:55:09 +080021import tempfile
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080022
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +080023import factory_common # pylint: disable=W0611
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +080024from cros.factory.test import event_log
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080025from cros.factory.test.env import paths
Jon Salz25590302014-07-11 16:07:20 +080026from cros.factory.tools import install_symlinks
Rong Chang6d65fad2014-08-22 16:00:12 +080027from cros.factory.utils import file_utils
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +080028from cros.factory.utils import sys_utils
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080029from cros.factory.utils.process_utils import Spawn
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080030
31
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +080032INSTALLER_PATH = 'usr/local/factory/py/toolkit/installer.py'
Rong Chang6d65fad2014-08-22 16:00:12 +080033MAKESELF_SHELL = '/bin/sh'
34TOOLKIT_NAME = 'install_factory_toolkit.run'
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +080035
Jon Salz4f3ade52014-02-20 17:55:09 +080036# Short and sweet help header for the executable generated by makeself.
37HELP_HEADER = """
38Installs the factory toolkit, transforming a test image into a factory test
39image. You can:
40
41- Install the factory toolkit on a CrOS device that is running a test
42 image. To do this, copy install_factory_toolkit.run to the device and
43 run it. The factory tests will then come up on the next boot.
44
45 rsync -a install_factory_toolkit.run crosdevice:/tmp
46 ssh crosdevice '/tmp/install_factory_toolkit.run && sync && reboot'
47
48- Modify a test image, turning it into a factory test image. When you
49 use the image on a device, the factory tests will come up.
50
51 install_factory_toolkit.run chromiumos_test_image.bin
52"""
53
54HELP_HEADER_ADVANCED = """
55- (advanced) Modify a mounted stateful partition, turning it into a factory
56 test image. This is equivalent to the previous command:
57
58 mount_partition -rw chromiumos_test_image.bin 1 /mnt/stateful
59 install_factory_toolkit.run /mnt/stateful
60 umount /mnt/stateful
61
62- (advanced) Unpack the factory toolkit, modify a file, and then repack it.
63
64 # Unpack but don't actually install
65 install_factory_toolkit.run --target /tmp/toolkit --noexec
66 # Edit some files in /tmp/toolkit
67 emacs /tmp/toolkit/whatever
68 # Repack
69 install_factory_toolkit.run -- --repack /tmp/toolkit \\
70 --pack-into /path/to/new/install_factory_toolkit.run
71"""
72
73# The makeself-generated header comes next. This is a little confusing,
74# so explain.
75HELP_HEADER_MAKESELF = """
76For complete usage information and advanced operations, run
77"install_factory_toolkit.run -- --help" (note the extra "--").
78
79Following is the help message from makeself, which was used to create
80this self-extracting archive.
81
82-----
83"""
84
Vic Yangdb1e20e2014-10-05 12:10:33 +080085SERVER_FILE_MASK = [
86 # Exclude Umpire server but keep Umpire client
87 '--include', 'py/umpire/__init__.*',
88 '--include', 'py/umpire/common.*',
89 '--include', 'py/umpire/client',
90 '--include', 'py/umpire/client/**',
91 '--exclude', 'py/umpire/**',
92
93 # Lumberjack is only used on Umpire server
94 '--exclude', 'py/lumberjack'
95]
96
Jon Salz4f3ade52014-02-20 17:55:09 +080097
Hung-Te Lin7b596ff2015-01-16 20:19:15 +080098class FactoryToolkitInstaller(object):
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080099 """Factory toolkit installer.
100
101 Args:
102 src: Source path containing usr/ and var/.
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800103 dest: Installation destination path. Set this to the mount point of the
104 stateful partition if patching a test image.
105 no_enable: True to not install the tag file.
106 system_root: The path to the root of the file system. This must be left
107 as its default value except for unit testing.
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800108 """
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800109
Jon Salzb7e44262014-05-07 15:53:37 +0800110 # Whether to sudo when rsyncing; set to False for testing.
111 _sudo = True
112
Peter Ammon948b7172014-07-15 12:43:06 -0700113 def __init__(self, src, dest, no_enable, enable_presenter,
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800114 enable_device, non_cros=False, device_id=None, system_root='/',
115 apps=None):
Jon Salz4f3ade52014-02-20 17:55:09 +0800116 self._src = src
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800117 self._system_root = system_root
118 if dest == self._system_root:
119 self._usr_local_dest = os.path.join(dest, 'usr', 'local')
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')
Hung-Te Lin4aeabe62016-10-21 15:45:20 +0800145 if not os.path.exists(self._usr_local_dest):
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800146 raise Exception(
147 'The destination path %s is not a stateful partition!' % dest)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800148
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800149 self._dest = dest
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800150 self._usr_local_src = os.path.join(src, 'usr', 'local')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800151 self._no_enable = no_enable
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800152 self._tag_file = os.path.join(self._usr_local_dest, 'factory', 'enabled')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800153
Peter Ammon948b7172014-07-15 12:43:06 -0700154 self._enable_presenter = enable_presenter
155 self._presenter_tag_file = os.path.join(self._usr_local_dest, 'factory',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800156 'init', 'run_goofy_presenter')
Vic Yang7039f422014-07-07 15:38:13 -0700157
158 self._enable_device = enable_device
Ricky Liangd7716912014-07-10 11:52:24 +0800159 self._device_tag_file = os.path.join(self._usr_local_dest, 'factory',
160 'init', 'run_goofy_device')
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800161 self._device_id = device_id
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800162 self._apps = apps
Vic Yang7039f422014-07-07 15:38:13 -0700163
Hung-Te Lin4aeabe62016-10-21 15:45:20 +0800164 if not os.path.exists(self._usr_local_src):
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800165 raise Exception(
166 'This installer must be run from within the factory toolkit!')
167
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800168 def WarningMessage(self, target_test_image=None):
Jon Salz4f3ade52014-02-20 17:55:09 +0800169 with open(os.path.join(self._src, 'VERSION')) as f:
170 ret = f.read()
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800171 if target_test_image:
Jon Salz4f3ade52014-02-20 17:55:09 +0800172 ret += (
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800173 '\n'
174 '\n'
Jon Salz4f3ade52014-02-20 17:55:09 +0800175 '*** You are about to patch the factory toolkit into:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800176 '*** %s\n'
177 '***' % target_test_image)
178 else:
Jon Salz4f3ade52014-02-20 17:55:09 +0800179 ret += (
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800180 '\n'
181 '\n'
Jon Salz4f3ade52014-02-20 17:55:09 +0800182 '*** You are about to install the factory toolkit to:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800183 '*** %s\n'
184 '***' % self._dest)
185 if self._dest == self._system_root:
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800186 if self._no_enable:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800187 ret += ('\n*** Factory tests will be disabled after this process is '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800188 'done, but\n*** you can enable them by creating the factory '
189 'enabled tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800190 else:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800191 ret += ('\n*** After this process is done, your device will start '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800192 'factory\n*** tests on the next reboot.\n***\n*** Factory '
193 'tests can be disabled by deleting the factory enabled\n*** '
194 'tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800195 return ret
196
Vic Yang7039f422014-07-07 15:38:13 -0700197 def _SetTagFile(self, name, path, enabled):
198 """Install or remove a tag file."""
199 if enabled:
200 print '*** Installing %s enabled tag...' % name
201 Spawn(['touch', path], sudo=True, log=True, check_call=True)
Ricky Liang8c88a122014-07-11 21:21:22 +0800202 Spawn(['chmod', 'go+r', path], sudo=True, log=True, check_call=True)
Vic Yang7039f422014-07-07 15:38:13 -0700203 else:
204 print '*** Removing %s enabled tag...' % name
205 Spawn(['rm', '-f', path], sudo=True, log=True, check_call=True)
206
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800207 def _SetDeviceID(self):
208 if self._device_id is not None:
209 with open(os.path.join(event_log.DEVICE_ID_PATH), 'w') as f:
210 f.write(self._device_id)
211
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800212 def _EnableApp(self, app, enabled):
213 """Enable / disable @app.
214
215 In factory/init/startup, a main app is considered disabled if and only:
216 1. file "factory/init/main.d/disable-@app" exists OR
217 2. file "factory/init/main.d/enable-@app" doesn't exist AND
218 file "factory/init/main.d/@app.sh" is not executable.
219
220 Therefore, we enable an app by removing file "disable-@app" and creating
221 file "enable-@app", and vise versa.
222 """
223 app_enable = os.path.join(self._usr_local_dest,
224 'factory', 'init', 'main.d', 'enable-' + app)
225 app_disable = os.path.join(self._usr_local_dest,
226 'factory', 'init', 'main.d', 'disable-' + app)
227 if enabled:
228 print '*** Enabling {app} ***'.format(app=app)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800229 Spawn(['rm', '-f', app_disable], sudo=self._sudo, log=True,
230 check_call=True)
231 Spawn(['touch', app_enable], sudo=self._sudo, log=True, check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800232 else:
233 print '*** Disabling {app} ***'.format(app=app)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800234 Spawn(['touch', app_disable], sudo=self._sudo, log=True, check_call=True)
235 Spawn(['rm', '-f', app_enable], sudo=self._sudo, log=True,
236 check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800237
238 def _EnableApps(self):
239 if not self._apps:
240 return
241
242 app_list = []
243 for app in self._apps:
244 if app[0] == '+':
245 app_list.append((app[1:], True))
246 elif app[0] == '-':
247 app_list.append((app[1:], False))
248 else:
249 raise ValueError(
250 'Use +{app} to enable and -{app} to disable'.format(app=app))
251
252 for app, enabled in app_list:
253 self._EnableApp(app, enabled)
254
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800255 def InstallFactorySubDir(self, sub_dirs):
256 """Install only the specified directories under factory folder."""
257
258 def _InstallOneSubDir(sub_dir_name):
259 sub_dir_dest = os.path.join(self._usr_local_dest, 'factory', sub_dir_name)
260 sub_dir_src = os.path.join(self._src, 'usr', 'local', 'factory',
261 sub_dir_name)
262 try:
263 Spawn(['mkdir', '-p', sub_dir_dest], sudo=True, log=True,
264 check_call=True)
265 except OSError as e:
266 print str(e)
267 return
268
269 Spawn(['rsync', '-a', '--force', '-v',
270 sub_dir_src + '/', sub_dir_dest],
271 sudo=self._sudo, log=True, check_call=True)
272 Spawn(['chown', '-R', 'root', sub_dir_dest],
273 sudo=self._sudo, log=True, check_call=True)
274 Spawn(['chmod', '-R', 'go+rX', sub_dir_dest],
275 sudo=self._sudo, log=True, check_call=True)
276
277 for sub_dir_name in sub_dirs:
278 _InstallOneSubDir(sub_dir_name)
279
280 self._SetTagFile('factory', self._tag_file, not self._no_enable)
281 self._EnableApps()
282
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800283 def Install(self):
284 print '*** Installing factory toolkit...'
Hung-Te Lin4aeabe62016-10-21 15:45:20 +0800285 for src, dest in ((self._usr_local_src, self._usr_local_dest),):
Jon Salzb7e44262014-05-07 15:53:37 +0800286 # Change the source directory to root, and add group/world read
287 # permissions. This is necessary because when the toolkit was
288 # unpacked, the user may not have been root so the permessions
289 # may be hosed. This is skipped for testing.
Peter Ammon5ac58422014-06-09 14:45:50 -0700290 # --force is necessary to allow goofy directory from prior
291 # toolkit installations to be overwritten by the goofy symlink.
Ricky Liang5e95be22014-07-09 12:52:07 +0800292 try:
293 if self._sudo:
294 Spawn(['chown', '-R', 'root', src],
295 sudo=True, log=True, check_call=True)
296 Spawn(['chmod', '-R', 'go+rX', src],
297 sudo=True, log=True, check_call=True)
298 print '*** %s -> %s' % (src, dest)
Hung-Te Lin56b18402015-01-16 14:52:30 +0800299 Spawn(['rsync', '-a', '--force'] + SERVER_FILE_MASK +
Vic Yangdb1e20e2014-10-05 12:10:33 +0800300 [src + '/', dest], sudo=self._sudo, log=True,
301 check_output=True, cwd=src)
Ricky Liang5e95be22014-07-09 12:52:07 +0800302 finally:
303 # Need to change the source directory back to the original user, or the
304 # script in makeself will fail to remove the temporary source directory.
305 if self._sudo:
306 myuser = os.environ.get('USER')
307 Spawn(['chown', '-R', myuser, src],
308 sudo=True, log=True, check_call=True)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800309
Hung-Te Lineb7632b2016-07-29 15:38:34 +0800310 print '*** Ensure SSH keys file permission...'
311 sshkeys_dir = os.path.join(self._usr_local_dest, 'factory/misc/sshkeys')
312 sshkeys = glob.glob(os.path.join(sshkeys_dir, '*'))
313 ssh_public_keys = glob.glob(os.path.join(sshkeys_dir, '*.pub'))
314 ssh_private_keys = list(set(sshkeys) - set(ssh_public_keys))
315 if ssh_private_keys:
316 Spawn(['chmod', '600'] + ssh_private_keys, log=True, check_call=True,
317 sudo=self._sudo)
318
Jon Salz25590302014-07-11 16:07:20 +0800319 print '*** Installing symlinks...'
320 install_symlinks.InstallSymlinks(
321 '../factory/bin',
322 os.path.join(self._usr_local_dest, 'bin'),
323 install_symlinks.MODE_FULL,
324 sudo=self._sudo)
325
Vic Yang7039f422014-07-07 15:38:13 -0700326 self._SetTagFile('factory', self._tag_file, not self._no_enable)
Peter Ammon948b7172014-07-15 12:43:06 -0700327 self._SetTagFile('presenter', self._presenter_tag_file,
328 self._enable_presenter)
Vic Yang7039f422014-07-07 15:38:13 -0700329 self._SetTagFile('device', self._device_tag_file, self._enable_device)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800330
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800331 self._SetDeviceID()
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800332 self._EnableApps()
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800333
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800334 print '*** Installation completed.'
335
336
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800337@contextmanager
338def DummyContext(arg):
339 """A context manager that simply yields its argument."""
340 yield arg
341
342
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800343def PrintBuildInfo(src_root):
344 """Print build information."""
345 info_file = os.path.join(src_root, 'REPO_STATUS')
346 if not os.path.exists(info_file):
347 raise OSError('Build info file not found!')
348 with open(info_file, 'r') as f:
349 print f.read()
350
351
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800352def PackFactoryToolkit(src_root, output_path, enable_device, enable_presenter):
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800353 """Packs the files containing this script into a factory toolkit."""
354 with open(os.path.join(src_root, 'VERSION'), 'r') as f:
355 version = f.read().strip()
Jon Salz4f3ade52014-02-20 17:55:09 +0800356 with tempfile.NamedTemporaryFile() as help_header:
Hung-Te Lin56b18402015-01-16 14:52:30 +0800357 help_header.write(version + '\n' + HELP_HEADER + HELP_HEADER_MAKESELF)
Jon Salz4f3ade52014-02-20 17:55:09 +0800358 help_header.flush()
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800359 cmd = [os.path.join(src_root, 'makeself.sh'), '--bzip2', '--nox11',
Jon Salz4f3ade52014-02-20 17:55:09 +0800360 '--help-header', help_header.name,
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800361 src_root, output_path, version, INSTALLER_PATH, '--in-exe']
362 if not enable_device:
363 cmd.append('--no-enable-device')
364 if not enable_presenter:
365 cmd.append('--no-enable-presenter')
366 Spawn(cmd, check_call=True, log=True)
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800367 print ('\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800368 ' Factory toolkit generated at %s.\n'
369 '\n'
370 ' To install factory toolkit on a live device running a test image,\n'
371 ' copy this to the device and execute it as root.\n'
372 '\n'
373 ' Alternatively, the factory toolkit can be used to patch a test\n'
374 ' image. For more information, run:\n'
375 ' %s --help\n'
376 '\n' % (output_path, output_path))
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800377
378
Rong Chang6d65fad2014-08-22 16:00:12 +0800379def InitUmpire(exe_path, src_root, target_board):
380 """Inits Umpire server environment."""
381 if exe_path is None:
382 parent_cmdline = open('/proc/%s/cmdline' % os.getppid(),
383 'r').read().rstrip('\0').split('\0')
384
You-Cheng Syufd644432016-10-04 13:40:51 +0800385 if len(parent_cmdline) > 1 and parent_cmdline[0] == MAKESELF_SHELL:
Rong Chang6d65fad2014-08-22 16:00:12 +0800386 # Get parent script name from parent process.
387 exe_path = parent_cmdline[1]
388 else:
389 # Set to default.
390 exe_path = TOOLKIT_NAME
391
392 if not exe_path.startswith('/'):
393 exe_path = os.path.join(os.environ.get('OLDPWD'), exe_path)
394
395 with file_utils.TempDirectory() as nano_bundle:
396 bundle_toolkit_dir = os.path.join(nano_bundle, 'factory_toolkit')
397 os.mkdir(bundle_toolkit_dir)
You-Cheng Syufd644432016-10-04 13:40:51 +0800398 os.symlink(exe_path, os.path.join(bundle_toolkit_dir, TOOLKIT_NAME))
Rong Chang6d65fad2014-08-22 16:00:12 +0800399 umpire_bin = os.path.join(src_root, 'usr', 'local', 'factory', 'bin',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800400 'umpire')
Rong Chang6d65fad2014-08-22 16:00:12 +0800401 Spawn([umpire_bin, 'init', '--board', target_board, nano_bundle],
402 check_call=True, log=True)
403 print ('\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800404 ' Umpire initialized successfully. Upstart service is running:\n'
405 ' umpire BOARD=%(board)s.\n'
406 ' For more information, please check umpire command line:\n'
407 '\n'
408 ' umpire-%(board)s --help (if your id is in umpire group)\n'
409 ' or sudo umpire-%(board)s --help\n'
410 '\n' % {'board': target_board})
Rong Chang6d65fad2014-08-22 16:00:12 +0800411
412
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800413def ExtractOverlord(src_root, output_dir):
414 output_dir = os.path.join(output_dir, 'overlord')
415 try:
416 os.makedirs(output_dir)
417 except OSError as e:
418 print str(e)
419 return
420
421 # Copy overlord binary and resource files
422 shutil.copyfile(os.path.join(src_root, 'usr/bin/overlordd'),
423 os.path.join(output_dir, 'overlordd'))
424 shutil.copytree(os.path.join(src_root, 'usr/share/overlord/app'),
425 os.path.join(output_dir, 'app'))
426
427 # Give overlordd execution permission
428 os.chmod(os.path.join(output_dir, 'overlordd'), 0755)
429 print "Extarcted overlord under '%s'" % output_dir
430
431
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800432def main():
Jon Salz4f3ade52014-02-20 17:55:09 +0800433 import logging
434 logging.basicConfig(level=logging.INFO)
435
436 # In order to determine which usage message to show, first determine
437 # whether we're in the self-extracting archive. Do this first
438 # because we need it to even parse the arguments.
439 if '--in-exe' in sys.argv:
440 sys.argv = [x for x in sys.argv if x != '--in-exe']
441 in_archive = True
442 else:
443 in_archive = False
444
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800445 parser = argparse.ArgumentParser(
Jon Salz4f3ade52014-02-20 17:55:09 +0800446 description=HELP_HEADER + HELP_HEADER_ADVANCED,
447 usage=('install_factory_toolkit.run -- [options]' if in_archive
448 else None),
449 formatter_class=argparse.RawDescriptionHelpFormatter)
Hung-Te Lin56b18402015-01-16 14:52:30 +0800450 parser.add_argument(
451 'dest', nargs='?', default='/',
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800452 help='A test image or the mount point of the stateful partition. '
453 "If omitted, install to live system, i.e. '/'.")
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800454 parser.add_argument('--no-enable', '-n', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800455 help="Don't enable factory tests after installing")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800456 parser.add_argument('--yes', '-y', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800457 help="Don't ask for confirmation")
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800458 parser.add_argument('--build-info', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800459 help='Print build information and exit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800460 parser.add_argument('--pack-into', metavar='NEW_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800461 help='Pack the files into a new factory toolkit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800462 parser.add_argument('--repack', metavar='UNPACKED_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800463 help='Repack from previously unpacked toolkit')
Vic Yang7039f422014-07-07 15:38:13 -0700464
Peter Ammon948b7172014-07-15 12:43:06 -0700465 parser.add_argument('--enable-presenter', dest='enable_presenter',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800466 action='store_true',
467 help='Run goofy in presenter mode on startup')
Peter Ammon948b7172014-07-15 12:43:06 -0700468 parser.add_argument('--no-enable-presenter', dest='enable_presenter',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800469 action='store_false', help=argparse.SUPPRESS)
Hung-Te Lin0cba9d52016-03-22 11:45:35 +0800470 parser.set_defaults(enable_presenter=False)
Vic Yang7039f422014-07-07 15:38:13 -0700471
Vic Yang70fdae92015-02-17 19:21:08 -0800472 parser.add_argument('--non-cros', dest='non_cros',
473 action='store_true',
474 help='Install on non-ChromeOS host.')
475
Vic Yang7039f422014-07-07 15:38:13 -0700476 parser.add_argument('--enable-device', dest='enable_device',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800477 action='store_true',
478 help='Run goofy in device mode on startup')
Vic Yang7039f422014-07-07 15:38:13 -0700479 parser.add_argument('--no-enable-device', dest='enable_device',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800480 action='store_false', help=argparse.SUPPRESS)
Hung-Te Lin0cba9d52016-03-22 11:45:35 +0800481 parser.set_defaults(enable_device=False)
Vic Yang423c2722014-09-24 16:05:06 +0800482
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800483 parser.add_argument('--device-id', dest='device_id', type=str, default=None,
484 help='Set device ID for this device')
485
Rong Chang6d65fad2014-08-22 16:00:12 +0800486 parser.add_argument('--init-umpire-board', dest='umpire_board',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800487 nargs='?', default=None,
488 help='Locally install Umpire server for specific board')
Rong Chang6d65fad2014-08-22 16:00:12 +0800489 parser.add_argument('--exe-path', dest='exe_path',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800490 nargs='?', default=None,
491 help='Current self-extracting archive pathname')
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800492 parser.add_argument('--extract-overlord', dest='extract_overlord',
493 metavar='OUTPUT_DIR', type=str, default=None,
494 help='Extract overlord from the toolkit')
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800495 parser.add_argument('--install-dirs', nargs='+', default=None,
496 help=('Install only the specified directories under '
497 'factory folder. Can be used with --apps to '
498 'enable / disable some apps. Defaults to install '
499 'all folders.'))
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800500 parser.add_argument('--apps', type=lambda s: s.split(','), default=None,
501 help=('Enable or disable some apps under '
502 'factory/init/main.d/. Use prefix "-" to disable, '
503 'prefix "+" to enable, and use "," to seperate. '
504 'For example: --apps="-goofy,+whale_servo"'))
Vic Yang7039f422014-07-07 15:38:13 -0700505
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800506 args = parser.parse_args()
507
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +0800508 src_root = paths.FACTORY_PATH
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800509 for _ in xrange(3):
510 src_root = os.path.dirname(src_root)
511
Rong Chang6d65fad2014-08-22 16:00:12 +0800512 # --init-umpire-board creates a nano bundle, then calls umpire command
513 # line utility to install the server code and upstart configurations.
514 if args.umpire_board:
515 InitUmpire(args.exe_path, src_root, args.umpire_board)
516 return
517
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800518 if args.extract_overlord is not None:
519 ExtractOverlord(src_root, args.extract_overlord)
520 return
521
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800522 # --pack-into may be called directly so this must be done before changing
523 # working directory to OLDPWD.
524 if args.pack_into and args.repack is None:
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800525 PackFactoryToolkit(src_root, args.pack_into, args.enable_device,
526 args.enable_presenter)
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800527 return
528
Jon Salz4f3ade52014-02-20 17:55:09 +0800529 if not in_archive:
530 # If you're not in the self-extracting archive, you're not allowed to
531 # do anything except the above --pack-into call.
532 parser.error('Not running from install_factory_toolkit.run; '
533 'only --pack-into (without --repack) is allowed')
534
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800535 # Change to original working directory in case the user specifies
536 # a relative path.
537 # TODO: Use USER_PWD instead when makeself is upgraded
538 os.chdir(os.environ['OLDPWD'])
539
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800540 if args.repack:
541 if args.pack_into is None:
542 parser.error('Must specify --pack-into when using --repack.')
543 Spawn([os.path.join(args.repack, INSTALLER_PATH),
544 '--pack-into', args.pack_into], check_call=True, log=True)
545 return
546
547 if args.build_info:
548 PrintBuildInfo(src_root)
549 return
550
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800551 if not os.path.exists(args.dest):
552 parser.error('Destination %s does not exist!' % args.dest)
553
554 patch_test_image = os.path.isfile(args.dest)
555
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800556 with (sys_utils.MountPartition(args.dest, 1, rw=True) if patch_test_image
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800557 else DummyContext(args.dest)) as dest:
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800558
Hung-Te Lin56b18402015-01-16 14:52:30 +0800559 installer = FactoryToolkitInstaller(
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800560 src=src_root, dest=dest, no_enable=args.no_enable,
561 enable_presenter=args.enable_presenter,
562 enable_device=args.enable_device, non_cros=args.non_cros,
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800563 device_id=args.device_id,
564 apps=args.apps)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800565
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800566 print installer.WarningMessage(args.dest if patch_test_image else None)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800567
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800568 if not args.yes:
569 answer = raw_input('*** Continue? [y/N] ')
570 if not answer or answer[0] not in 'yY':
571 sys.exit('Aborting.')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800572
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800573 if args.install_dirs:
574 installer.InstallFactorySubDir(args.install_dirs)
575 else:
576 installer.Install()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800577
578if __name__ == '__main__':
Hung-Te Lin5a2a1c42016-11-10 12:43:40 +0800579 # makself inteprets "LICENSE" environment variable string as license text and
580 # will prompt user to accept before installation. For factory toolkit, we
581 # don't want any user interaction in installation and the license is already
582 # covered by ebuild or download platform like CPFE.
583 os.putenv('LICENSE', '')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800584 main()