blob: 519563c8561ce6ced7f21592d131d882c5f26859 [file] [log] [blame]
You-Cheng Syud5692942018-01-04 14:40:59 +08001#!/usr/bin/env python
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +08002#
Hung-Te Lin1990b742017-08-09 17:34:57 +08003# Copyright 2014 The Chromium OS Authors. All rights reserved.
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +08004# 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
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +080017import getpass
Hung-Te Lineb7632b2016-07-29 15:38:34 +080018import glob
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080019import os
Wei-Ning Huang4855e792015-06-11 15:33:39 +080020import shutil
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080021import sys
Jon Salz4f3ade52014-02-20 17:55:09 +080022import tempfile
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +080023import time
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080024
You-Cheng Syu70e99ad2017-03-09 17:15:00 +080025import factory_common # pylint: disable=unused-import
Wei-Han Chen2ebb92d2016-01-12 14:51:41 +080026from cros.factory.test.env import paths
Jon Salz25590302014-07-11 16:07:20 +080027from cros.factory.tools import install_symlinks
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +080028from cros.factory.utils import file_utils
Youcheng Syuac391772017-04-20 09:08:58 +000029from cros.factory.utils.process_utils import Spawn
Peter Shihfe221582017-06-15 13:40:53 +080030from cros.factory.utils import sys_utils
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080031
32
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +080033INSTALLER_PATH = 'usr/local/factory/py/toolkit/installer.py'
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +080034VERSION_PATH = 'usr/local/factory/TOOLKIT_VERSION'
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/**',
Peter Shihac904782017-06-14 15:24:09 +080092 '--exclude', 'bin/umpire',
93 '--exclude', 'bin/umpired',
Vic Yangdb1e20e2014-10-05 12:10:33 +080094]
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.
Peter Shih2f1f8c42017-06-15 15:05:55 +0800107 apps: The list of apps to enable/disable under factory/init/main.d/.
108 active_test_list: The id of active test list for Goofy.
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800109 """
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800110
Jon Salzb7e44262014-05-07 15:53:37 +0800111 # Whether to sudo when rsyncing; set to False for testing.
112 _sudo = True
113
Hung-Te Linfc162e62017-09-21 00:45:04 +0800114 def __init__(self, src, dest, no_enable, non_cros=False, system_root='/',
Peter Shih2f1f8c42017-06-15 15:05:55 +0800115 apps=None, active_test_list=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'
Hung-Te Linfc162e62017-09-21 00:45:04 +0800133 'If you want to install on a non-CrOS host,\n'
Vic Yang70fdae92015-02-17 19:21:08 -0800134 'please run\n'
135 '\n'
Hung-Te Linfc162e62017-09-21 00:45:04 +0800136 ' install_factory_toolkit.run -- --non-cros \n'
Vic Yang70fdae92015-02-17 19:21:08 -0800137 '\n')
Jon Salz4f3ade52014-02-20 17:55:09 +0800138 sys.exit(1)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800139 if os.getuid() != 0:
Jon Salz4f3ade52014-02-20 17:55:09 +0800140 raise Exception('You must be root to install the factory toolkit on a '
141 'CrOS device.')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800142 else:
143 self._usr_local_dest = os.path.join(dest, 'dev_image')
Hung-Te Lin4aeabe62016-10-21 15:45:20 +0800144 if not os.path.exists(self._usr_local_dest):
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800145 raise Exception(
146 'The destination path %s is not a stateful partition!' % dest)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800147
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800148 self._dest = dest
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800149 self._usr_local_src = os.path.join(src, 'usr', 'local')
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800150 self._no_enable = no_enable
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800151 self._tag_file = os.path.join(self._usr_local_dest, 'factory', 'enabled')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800152
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800153 self._apps = apps
Peter Shih2f1f8c42017-06-15 15:05:55 +0800154 self._active_test_list = active_test_list
Vic Yang7039f422014-07-07 15:38:13 -0700155
Hung-Te Lin4aeabe62016-10-21 15:45:20 +0800156 if not os.path.exists(self._usr_local_src):
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800157 raise Exception(
158 'This installer must be run from within the factory toolkit!')
159
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800160 def WarningMessage(self, target_test_image=None):
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800161 ret = file_utils.ReadFile(os.path.join(self._src, VERSION_PATH))
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800162 if target_test_image:
Jon Salz4f3ade52014-02-20 17:55:09 +0800163 ret += (
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800164 '\n'
165 '\n'
Jon Salz4f3ade52014-02-20 17:55:09 +0800166 '*** You are about to patch the factory toolkit into:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800167 '*** %s\n'
168 '***' % target_test_image)
169 else:
Jon Salz4f3ade52014-02-20 17:55:09 +0800170 ret += (
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800171 '\n'
172 '\n'
Jon Salz4f3ade52014-02-20 17:55:09 +0800173 '*** You are about to install the factory toolkit to:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800174 '*** %s\n'
175 '***' % self._dest)
176 if self._dest == self._system_root:
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800177 if self._no_enable:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800178 ret += ('\n*** Factory tests will be disabled after this process is '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800179 'done, but\n*** you can enable them by creating the factory '
180 'enabled tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800181 else:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800182 ret += ('\n*** After this process is done, your device will start '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800183 'factory\n*** tests on the next reboot.\n***\n*** Factory '
184 'tests can be disabled by deleting the factory enabled\n*** '
185 'tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800186 return ret
187
Vic Yang7039f422014-07-07 15:38:13 -0700188 def _SetTagFile(self, name, path, enabled):
189 """Install or remove a tag file."""
190 if enabled:
191 print '*** Installing %s enabled tag...' % name
Youcheng Syuac391772017-04-20 09:08:58 +0000192 Spawn(['touch', path], sudo=True, log=True, check_call=True)
193 Spawn(['chmod', 'go+r', path], sudo=True, log=True, check_call=True)
Vic Yang7039f422014-07-07 15:38:13 -0700194 else:
195 print '*** Removing %s enabled tag...' % name
Youcheng Syuac391772017-04-20 09:08:58 +0000196 Spawn(['rm', '-f', path], sudo=True, log=True, check_call=True)
Vic Yang7039f422014-07-07 15:38:13 -0700197
Peter Shih2f1f8c42017-06-15 15:05:55 +0800198 def _SetActiveTestList(self):
199 """Set the active test list for Goofy."""
200 if self._active_test_list is not None:
201 file_utils.WriteFile(
202 os.path.join(self._usr_local_dest, 'factory', 'py', 'test',
203 'test_lists', 'ACTIVE'), self._active_test_list)
204
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800205 def _EnableApp(self, app, enabled):
206 """Enable / disable @app.
207
208 In factory/init/startup, a main app is considered disabled if and only:
209 1. file "factory/init/main.d/disable-@app" exists OR
210 2. file "factory/init/main.d/enable-@app" doesn't exist AND
211 file "factory/init/main.d/@app.sh" is not executable.
212
213 Therefore, we enable an app by removing file "disable-@app" and creating
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800214 file "enable-@app", and vice versa.
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800215 """
216 app_enable = os.path.join(self._usr_local_dest,
217 'factory', 'init', 'main.d', 'enable-' + app)
218 app_disable = os.path.join(self._usr_local_dest,
219 'factory', 'init', 'main.d', 'disable-' + app)
220 if enabled:
221 print '*** Enabling {app} ***'.format(app=app)
Youcheng Syuac391772017-04-20 09:08:58 +0000222 Spawn(['rm', '-f', app_disable], sudo=self._sudo, log=True,
223 check_call=True)
224 Spawn(['touch', app_enable], sudo=self._sudo, log=True, check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800225 else:
226 print '*** Disabling {app} ***'.format(app=app)
Youcheng Syuac391772017-04-20 09:08:58 +0000227 Spawn(['touch', app_disable], sudo=self._sudo, log=True, check_call=True)
228 Spawn(['rm', '-f', app_enable], sudo=self._sudo, log=True,
229 check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800230
231 def _EnableApps(self):
232 if not self._apps:
233 return
234
235 app_list = []
236 for app in self._apps:
237 if app[0] == '+':
238 app_list.append((app[1:], True))
239 elif app[0] == '-':
240 app_list.append((app[1:], False))
241 else:
242 raise ValueError(
243 'Use +{app} to enable and -{app} to disable'.format(app=app))
244
245 for app, enabled in app_list:
246 self._EnableApp(app, enabled)
247
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800248 def InstallFactorySubDir(self, sub_dirs):
249 """Install only the specified directories under factory folder."""
250
251 def _InstallOneSubDir(sub_dir_name):
252 sub_dir_dest = os.path.join(self._usr_local_dest, 'factory', sub_dir_name)
253 sub_dir_src = os.path.join(self._src, 'usr', 'local', 'factory',
254 sub_dir_name)
255 try:
Youcheng Syuac391772017-04-20 09:08:58 +0000256 Spawn(['mkdir', '-p', sub_dir_dest], sudo=True, log=True,
257 check_call=True)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800258 except OSError as e:
259 print str(e)
260 return
261
Youcheng Syuac391772017-04-20 09:08:58 +0000262 Spawn(['rsync', '-a', '--force', '-v',
263 sub_dir_src + '/', sub_dir_dest],
264 sudo=self._sudo, log=True, check_call=True)
265 Spawn(['chown', '-R', 'root', sub_dir_dest],
266 sudo=self._sudo, log=True, check_call=True)
267 Spawn(['chmod', '-R', 'go+rX', sub_dir_dest],
268 sudo=self._sudo, log=True, check_call=True)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800269
270 for sub_dir_name in sub_dirs:
271 _InstallOneSubDir(sub_dir_name)
272
273 self._SetTagFile('factory', self._tag_file, not self._no_enable)
274 self._EnableApps()
275
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800276 def Install(self):
277 print '*** Installing factory toolkit...'
Mao Huangf75aa3e2016-11-24 11:44:17 +0800278
279 # --no-owner and --no-group will set owner/group to the current user/group
280 # running the command. This is important if we're running with sudo, so
281 # the destination will be changed to root/root instead of the user/group
282 # before sudo (doesn't matter if sudo is not present). --force is also
283 # necessary to allow goofy directory from prior toolkit installations to
284 # be overwritten by the goofy symlink.
285 print '*** %s -> %s' % (self._usr_local_src, self._usr_local_dest)
Youcheng Syuac391772017-04-20 09:08:58 +0000286 Spawn(['rsync', '-a', '--no-owner', '--no-group', '--chmod=ugo+rX',
287 '--force'] + SERVER_FILE_MASK + [self._usr_local_src + '/',
288 self._usr_local_dest],
289 sudo=self._sudo, log=True, check_output=True, cwd=self._usr_local_src)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800290
Hung-Te Lineb7632b2016-07-29 15:38:34 +0800291 print '*** Ensure SSH keys file permission...'
292 sshkeys_dir = os.path.join(self._usr_local_dest, 'factory/misc/sshkeys')
293 sshkeys = glob.glob(os.path.join(sshkeys_dir, '*'))
294 ssh_public_keys = glob.glob(os.path.join(sshkeys_dir, '*.pub'))
295 ssh_private_keys = list(set(sshkeys) - set(ssh_public_keys))
296 if ssh_private_keys:
Youcheng Syuac391772017-04-20 09:08:58 +0000297 Spawn(['chmod', '600'] + ssh_private_keys, log=True, check_call=True,
298 sudo=self._sudo)
Hung-Te Lineb7632b2016-07-29 15:38:34 +0800299
Jon Salz25590302014-07-11 16:07:20 +0800300 print '*** Installing symlinks...'
301 install_symlinks.InstallSymlinks(
302 '../factory/bin',
303 os.path.join(self._usr_local_dest, 'bin'),
304 install_symlinks.MODE_FULL,
305 sudo=self._sudo)
306
Vic Yang7039f422014-07-07 15:38:13 -0700307 self._SetTagFile('factory', self._tag_file, not self._no_enable)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800308
Peter Shih2f1f8c42017-06-15 15:05:55 +0800309 self._SetActiveTestList()
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800310 self._EnableApps()
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800311
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800312 print '*** Installation completed.'
313
314
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800315@contextmanager
316def DummyContext(arg):
317 """A context manager that simply yields its argument."""
318 yield arg
319
320
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800321def PrintBuildInfo(src_root):
322 """Print build information."""
323 info_file = os.path.join(src_root, 'REPO_STATUS')
324 if not os.path.exists(info_file):
325 raise OSError('Build info file not found!')
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800326 print file_utils.ReadFile(info_file)
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800327
328
Hung-Te Linfc162e62017-09-21 00:45:04 +0800329def PackFactoryToolkit(src_root, output_path, initial_version):
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800330 """Packs the files containing this script into a factory toolkit."""
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800331 if initial_version is None:
332 complete_version = '%s repacked by %s@%s at %s\n' % (
333 file_utils.ReadFile(os.path.join(src_root, VERSION_PATH)),
334 getpass.getuser(), os.uname()[1], time.strftime('%Y-%m-%d %H:%M:%S'))
335 initial_version = complete_version.splitlines()[0]
336 else:
337 complete_version = initial_version + '\n'
338 modified_times = len(complete_version.splitlines()) - 1
339 if modified_times == 0:
340 modified_msg = ''
341 else:
342 modified_msg = ' (modified %d times)' % modified_times
Jon Salz4f3ade52014-02-20 17:55:09 +0800343 with tempfile.NamedTemporaryFile() as help_header:
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800344 help_header.write(initial_version + '\n' +
345 HELP_HEADER + HELP_HEADER_MAKESELF)
Jon Salz4f3ade52014-02-20 17:55:09 +0800346 help_header.flush()
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800347 cmd = [os.path.join(src_root, 'makeself.sh'), '--bzip2', '--nox11',
Jon Salz4f3ade52014-02-20 17:55:09 +0800348 '--help-header', help_header.name,
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800349 src_root, output_path, initial_version + modified_msg,
350 INSTALLER_PATH, '--in-exe']
Youcheng Syuac391772017-04-20 09:08:58 +0000351 Spawn(cmd, check_call=True, log=True)
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800352 with file_utils.TempDirectory() as tmp_dir:
353 version_path = os.path.join(tmp_dir, VERSION_PATH)
354 os.makedirs(os.path.dirname(version_path))
355 file_utils.WriteFile(version_path, complete_version)
You-Cheng Syuc0682732017-05-10 12:17:05 +0800356 Spawn([cmd[0], '--lsm', version_path, '--append', tmp_dir, output_path],
357 check_call=True, log=True)
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800358 print ('\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800359 ' Factory toolkit generated at %s.\n'
360 '\n'
361 ' To install factory toolkit on a live device running a test image,\n'
362 ' copy this to the device and execute it as root.\n'
363 '\n'
364 ' Alternatively, the factory toolkit can be used to patch a test\n'
365 ' image. For more information, run:\n'
366 ' %s --help\n'
367 '\n' % (output_path, output_path))
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800368
369
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800370def ExtractOverlord(src_root, output_dir):
371 output_dir = os.path.join(output_dir, 'overlord')
372 try:
373 os.makedirs(output_dir)
374 except OSError as e:
375 print str(e)
376 return
377
378 # Copy overlord binary and resource files
379 shutil.copyfile(os.path.join(src_root, 'usr/bin/overlordd'),
380 os.path.join(output_dir, 'overlordd'))
381 shutil.copytree(os.path.join(src_root, 'usr/share/overlord/app'),
382 os.path.join(output_dir, 'app'))
383
384 # Give overlordd execution permission
Peter Shihe6afab32018-09-11 17:16:48 +0800385 os.chmod(os.path.join(output_dir, 'overlordd'), 0o755)
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800386 print "Extracted overlord under '%s'" % output_dir
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800387
388
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800389def main():
Jon Salz4f3ade52014-02-20 17:55:09 +0800390 import logging
391 logging.basicConfig(level=logging.INFO)
392
393 # In order to determine which usage message to show, first determine
394 # whether we're in the self-extracting archive. Do this first
395 # because we need it to even parse the arguments.
396 if '--in-exe' in sys.argv:
397 sys.argv = [x for x in sys.argv if x != '--in-exe']
398 in_archive = True
399 else:
400 in_archive = False
401
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800402 parser = argparse.ArgumentParser(
Jon Salz4f3ade52014-02-20 17:55:09 +0800403 description=HELP_HEADER + HELP_HEADER_ADVANCED,
404 usage=('install_factory_toolkit.run -- [options]' if in_archive
405 else None),
406 formatter_class=argparse.RawDescriptionHelpFormatter)
Hung-Te Lin56b18402015-01-16 14:52:30 +0800407 parser.add_argument(
408 'dest', nargs='?', default='/',
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800409 help='A test image or the mount point of the stateful partition. '
410 "If omitted, install to live system, i.e. '/'.")
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800411 parser.add_argument('--no-enable', '-n', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800412 help="Don't enable factory tests after installing")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800413 parser.add_argument('--yes', '-y', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800414 help="Don't ask for confirmation")
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800415 parser.add_argument('--build-info', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800416 help='Print build information and exit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800417 parser.add_argument('--pack-into', metavar='NEW_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800418 help='Pack the files into a new factory toolkit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800419 parser.add_argument('--repack', metavar='UNPACKED_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800420 help='Repack from previously unpacked toolkit')
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800421 parser.add_argument('--version', metavar='VERSION',
422 help='String to write into TOOLKIT_VERSION when packing')
Vic Yang7039f422014-07-07 15:38:13 -0700423
Vic Yang70fdae92015-02-17 19:21:08 -0800424 parser.add_argument('--non-cros', dest='non_cros',
425 action='store_true',
426 help='Install on non-ChromeOS host.')
427
Vic Yang423c2722014-09-24 16:05:06 +0800428
Rong Chang6d65fad2014-08-22 16:00:12 +0800429 parser.add_argument('--exe-path', dest='exe_path',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800430 nargs='?', default=None,
431 help='Current self-extracting archive pathname')
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800432 parser.add_argument('--extract-overlord', dest='extract_overlord',
433 metavar='OUTPUT_DIR', type=str, default=None,
434 help='Extract overlord from the toolkit')
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800435 parser.add_argument('--install-dirs', nargs='+', default=None,
436 help=('Install only the specified directories under '
437 'factory folder. Can be used with --apps to '
438 'enable / disable some apps. Defaults to install '
439 'all folders.'))
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800440 parser.add_argument('--apps', type=lambda s: s.split(','), default=None,
441 help=('Enable or disable some apps under '
442 'factory/init/main.d/. Use prefix "-" to disable, '
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800443 'prefix "+" to enable, and use "," to separate. '
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800444 'For example: --apps="-goofy,+whale_servo"'))
Peter Shih2f1f8c42017-06-15 15:05:55 +0800445 parser.add_argument('--active-test-list', dest='active_test_list',
446 default=None,
447 help='Set the id of active test list for Goofy.')
Vic Yang7039f422014-07-07 15:38:13 -0700448
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800449 args = parser.parse_args()
450
Peter Shihad166772017-05-31 11:36:17 +0800451 src_root = paths.FACTORY_DIR
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800452 for _ in xrange(3):
453 src_root = os.path.dirname(src_root)
454
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800455 if args.extract_overlord is not None:
456 ExtractOverlord(src_root, args.extract_overlord)
457 return
458
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800459 # --pack-into may be called directly so this must be done before changing
460 # working directory to OLDPWD.
461 if args.pack_into and args.repack is None:
Hung-Te Linfc162e62017-09-21 00:45:04 +0800462 PackFactoryToolkit(src_root, args.pack_into, args.version)
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800463 return
464
Jon Salz4f3ade52014-02-20 17:55:09 +0800465 if not in_archive:
466 # If you're not in the self-extracting archive, you're not allowed to
467 # do anything except the above --pack-into call.
468 parser.error('Not running from install_factory_toolkit.run; '
469 'only --pack-into (without --repack) is allowed')
470
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800471 # Change to original working directory in case the user specifies
472 # a relative path.
473 # TODO: Use USER_PWD instead when makeself is upgraded
474 os.chdir(os.environ['OLDPWD'])
475
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800476 if args.repack:
477 if args.pack_into is None:
478 parser.error('Must specify --pack-into when using --repack.')
Youcheng Syuac391772017-04-20 09:08:58 +0000479 Spawn([os.path.join(args.repack, INSTALLER_PATH),
480 '--pack-into', args.pack_into], check_call=True, log=True)
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800481 return
482
483 if args.build_info:
484 PrintBuildInfo(src_root)
485 return
486
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800487 if not os.path.exists(args.dest):
488 parser.error('Destination %s does not exist!' % args.dest)
489
490 patch_test_image = os.path.isfile(args.dest)
491
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800492 with (sys_utils.MountPartition(args.dest, 1, rw=True) if patch_test_image
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800493 else DummyContext(args.dest)) as dest:
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800494
Hung-Te Lin56b18402015-01-16 14:52:30 +0800495 installer = FactoryToolkitInstaller(
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800496 src=src_root, dest=dest, no_enable=args.no_enable,
Hung-Te Linfc162e62017-09-21 00:45:04 +0800497 non_cros=args.non_cros, apps=args.apps,
498 active_test_list=args.active_test_list)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800499
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800500 print installer.WarningMessage(args.dest if patch_test_image else None)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800501
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800502 if not args.yes:
503 answer = raw_input('*** Continue? [y/N] ')
504 if not answer or answer[0] not in 'yY':
505 sys.exit('Aborting.')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800506
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800507 if args.install_dirs:
508 installer.InstallFactorySubDir(args.install_dirs)
509 else:
510 installer.Install()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800511
512if __name__ == '__main__':
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800513 # makself interprets "LICENSE" environment variable string as license text and
Hung-Te Lin5a2a1c42016-11-10 12:43:40 +0800514 # will prompt user to accept before installation. For factory toolkit, we
515 # don't want any user interaction in installation and the license is already
516 # covered by ebuild or download platform like CPFE.
517 os.putenv('LICENSE', '')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800518 main()