blob: 18f3f4a09e71732f97d82c0b114367af4eeaa77b [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
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.
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,
Peter Shihfe221582017-06-15 13:40:53 +0800113 enable_device, non_cros=False, system_root='/',
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800114 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')
Jon Salz4f3ade52014-02-20 17:55:09 +0800119
120 # Make sure we're on a CrOS device.
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800121 if not non_cros and not sys_utils.InCrOSDevice():
Jon Salz4f3ade52014-02-20 17:55:09 +0800122 sys.stderr.write(
Vic Yang196d5242014-08-05 13:51:35 +0800123 "ERROR: You're not on a CrOS device (for more details, please\n"
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800124 'check sys_utils.py:InCrOSDevice), so you must specify a test\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800125 'image or a mounted stateful partition on which to install the\n'
126 'factory toolkit. Please run\n'
127 '\n'
128 ' install_factory_toolkit.run -- --help\n'
129 '\n'
Vic Yang70fdae92015-02-17 19:21:08 -0800130 'for help.\n'
131 '\n'
132 'If you want to install the presenter on a non-CrOS host,\n'
133 'please run\n'
134 '\n'
135 ' install_factory_toolkit.run -- \\\n'
Hung-Te Lin0cba9d52016-03-22 11:45:35 +0800136 ' --non-cros --enable-presenter\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
Peter Ammon948b7172014-07-15 12:43:06 -0700153 self._enable_presenter = enable_presenter
154 self._presenter_tag_file = os.path.join(self._usr_local_dest, 'factory',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800155 'init', 'run_goofy_presenter')
Vic Yang7039f422014-07-07 15:38:13 -0700156
157 self._enable_device = enable_device
Ricky Liangd7716912014-07-10 11:52:24 +0800158 self._device_tag_file = os.path.join(self._usr_local_dest, 'factory',
159 'init', 'run_goofy_device')
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800160 self._apps = apps
Vic Yang7039f422014-07-07 15:38:13 -0700161
Hung-Te Lin4aeabe62016-10-21 15:45:20 +0800162 if not os.path.exists(self._usr_local_src):
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800163 raise Exception(
164 'This installer must be run from within the factory toolkit!')
165
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800166 def WarningMessage(self, target_test_image=None):
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800167 ret = file_utils.ReadFile(os.path.join(self._src, VERSION_PATH))
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800168 if target_test_image:
Jon Salz4f3ade52014-02-20 17:55:09 +0800169 ret += (
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800170 '\n'
171 '\n'
Jon Salz4f3ade52014-02-20 17:55:09 +0800172 '*** You are about to patch the factory toolkit into:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800173 '*** %s\n'
174 '***' % target_test_image)
175 else:
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 install the factory toolkit to:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800180 '*** %s\n'
181 '***' % self._dest)
182 if self._dest == self._system_root:
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800183 if self._no_enable:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800184 ret += ('\n*** Factory tests will be disabled after this process is '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800185 'done, but\n*** you can enable them by creating the factory '
186 'enabled tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800187 else:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800188 ret += ('\n*** After this process is done, your device will start '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800189 'factory\n*** tests on the next reboot.\n***\n*** Factory '
190 'tests can be disabled by deleting the factory enabled\n*** '
191 'tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800192 return ret
193
Vic Yang7039f422014-07-07 15:38:13 -0700194 def _SetTagFile(self, name, path, enabled):
195 """Install or remove a tag file."""
196 if enabled:
197 print '*** Installing %s enabled tag...' % name
Youcheng Syuac391772017-04-20 09:08:58 +0000198 Spawn(['touch', path], sudo=True, log=True, check_call=True)
199 Spawn(['chmod', 'go+r', path], sudo=True, log=True, check_call=True)
Vic Yang7039f422014-07-07 15:38:13 -0700200 else:
201 print '*** Removing %s enabled tag...' % name
Youcheng Syuac391772017-04-20 09:08:58 +0000202 Spawn(['rm', '-f', path], sudo=True, log=True, check_call=True)
Vic Yang7039f422014-07-07 15:38:13 -0700203
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800204 def _EnableApp(self, app, enabled):
205 """Enable / disable @app.
206
207 In factory/init/startup, a main app is considered disabled if and only:
208 1. file "factory/init/main.d/disable-@app" exists OR
209 2. file "factory/init/main.d/enable-@app" doesn't exist AND
210 file "factory/init/main.d/@app.sh" is not executable.
211
212 Therefore, we enable an app by removing file "disable-@app" and creating
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800213 file "enable-@app", and vice versa.
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800214 """
215 app_enable = os.path.join(self._usr_local_dest,
216 'factory', 'init', 'main.d', 'enable-' + app)
217 app_disable = os.path.join(self._usr_local_dest,
218 'factory', 'init', 'main.d', 'disable-' + app)
219 if enabled:
220 print '*** Enabling {app} ***'.format(app=app)
Youcheng Syuac391772017-04-20 09:08:58 +0000221 Spawn(['rm', '-f', app_disable], sudo=self._sudo, log=True,
222 check_call=True)
223 Spawn(['touch', app_enable], sudo=self._sudo, log=True, check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800224 else:
225 print '*** Disabling {app} ***'.format(app=app)
Youcheng Syuac391772017-04-20 09:08:58 +0000226 Spawn(['touch', app_disable], sudo=self._sudo, log=True, check_call=True)
227 Spawn(['rm', '-f', app_enable], sudo=self._sudo, log=True,
228 check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800229
230 def _EnableApps(self):
231 if not self._apps:
232 return
233
234 app_list = []
235 for app in self._apps:
236 if app[0] == '+':
237 app_list.append((app[1:], True))
238 elif app[0] == '-':
239 app_list.append((app[1:], False))
240 else:
241 raise ValueError(
242 'Use +{app} to enable and -{app} to disable'.format(app=app))
243
244 for app, enabled in app_list:
245 self._EnableApp(app, enabled)
246
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800247 def InstallFactorySubDir(self, sub_dirs):
248 """Install only the specified directories under factory folder."""
249
250 def _InstallOneSubDir(sub_dir_name):
251 sub_dir_dest = os.path.join(self._usr_local_dest, 'factory', sub_dir_name)
252 sub_dir_src = os.path.join(self._src, 'usr', 'local', 'factory',
253 sub_dir_name)
254 try:
Youcheng Syuac391772017-04-20 09:08:58 +0000255 Spawn(['mkdir', '-p', sub_dir_dest], sudo=True, log=True,
256 check_call=True)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800257 except OSError as e:
258 print str(e)
259 return
260
Youcheng Syuac391772017-04-20 09:08:58 +0000261 Spawn(['rsync', '-a', '--force', '-v',
262 sub_dir_src + '/', sub_dir_dest],
263 sudo=self._sudo, log=True, check_call=True)
264 Spawn(['chown', '-R', 'root', sub_dir_dest],
265 sudo=self._sudo, log=True, check_call=True)
266 Spawn(['chmod', '-R', 'go+rX', sub_dir_dest],
267 sudo=self._sudo, log=True, check_call=True)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800268
269 for sub_dir_name in sub_dirs:
270 _InstallOneSubDir(sub_dir_name)
271
272 self._SetTagFile('factory', self._tag_file, not self._no_enable)
273 self._EnableApps()
274
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800275 def Install(self):
276 print '*** Installing factory toolkit...'
Mao Huangf75aa3e2016-11-24 11:44:17 +0800277
278 # --no-owner and --no-group will set owner/group to the current user/group
279 # running the command. This is important if we're running with sudo, so
280 # the destination will be changed to root/root instead of the user/group
281 # before sudo (doesn't matter if sudo is not present). --force is also
282 # necessary to allow goofy directory from prior toolkit installations to
283 # be overwritten by the goofy symlink.
284 print '*** %s -> %s' % (self._usr_local_src, self._usr_local_dest)
Youcheng Syuac391772017-04-20 09:08:58 +0000285 Spawn(['rsync', '-a', '--no-owner', '--no-group', '--chmod=ugo+rX',
286 '--force'] + SERVER_FILE_MASK + [self._usr_local_src + '/',
287 self._usr_local_dest],
288 sudo=self._sudo, log=True, check_output=True, cwd=self._usr_local_src)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800289
Hung-Te Lineb7632b2016-07-29 15:38:34 +0800290 print '*** Ensure SSH keys file permission...'
291 sshkeys_dir = os.path.join(self._usr_local_dest, 'factory/misc/sshkeys')
292 sshkeys = glob.glob(os.path.join(sshkeys_dir, '*'))
293 ssh_public_keys = glob.glob(os.path.join(sshkeys_dir, '*.pub'))
294 ssh_private_keys = list(set(sshkeys) - set(ssh_public_keys))
295 if ssh_private_keys:
Youcheng Syuac391772017-04-20 09:08:58 +0000296 Spawn(['chmod', '600'] + ssh_private_keys, log=True, check_call=True,
297 sudo=self._sudo)
Hung-Te Lineb7632b2016-07-29 15:38:34 +0800298
Jon Salz25590302014-07-11 16:07:20 +0800299 print '*** Installing symlinks...'
300 install_symlinks.InstallSymlinks(
301 '../factory/bin',
302 os.path.join(self._usr_local_dest, 'bin'),
303 install_symlinks.MODE_FULL,
304 sudo=self._sudo)
305
Vic Yang7039f422014-07-07 15:38:13 -0700306 self._SetTagFile('factory', self._tag_file, not self._no_enable)
Peter Ammon948b7172014-07-15 12:43:06 -0700307 self._SetTagFile('presenter', self._presenter_tag_file,
308 self._enable_presenter)
Vic Yang7039f422014-07-07 15:38:13 -0700309 self._SetTagFile('device', self._device_tag_file, self._enable_device)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800310
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800311 self._EnableApps()
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800312
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800313 print '*** Installation completed.'
314
315
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800316@contextmanager
317def DummyContext(arg):
318 """A context manager that simply yields its argument."""
319 yield arg
320
321
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800322def PrintBuildInfo(src_root):
323 """Print build information."""
324 info_file = os.path.join(src_root, 'REPO_STATUS')
325 if not os.path.exists(info_file):
326 raise OSError('Build info file not found!')
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800327 print file_utils.ReadFile(info_file)
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800328
329
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800330def PackFactoryToolkit(src_root, output_path, initial_version,
331 enable_device, enable_presenter):
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800332 """Packs the files containing this script into a factory toolkit."""
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800333 if initial_version is None:
334 complete_version = '%s repacked by %s@%s at %s\n' % (
335 file_utils.ReadFile(os.path.join(src_root, VERSION_PATH)),
336 getpass.getuser(), os.uname()[1], time.strftime('%Y-%m-%d %H:%M:%S'))
337 initial_version = complete_version.splitlines()[0]
338 else:
339 complete_version = initial_version + '\n'
340 modified_times = len(complete_version.splitlines()) - 1
341 if modified_times == 0:
342 modified_msg = ''
343 else:
344 modified_msg = ' (modified %d times)' % modified_times
Jon Salz4f3ade52014-02-20 17:55:09 +0800345 with tempfile.NamedTemporaryFile() as help_header:
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800346 help_header.write(initial_version + '\n' +
347 HELP_HEADER + HELP_HEADER_MAKESELF)
Jon Salz4f3ade52014-02-20 17:55:09 +0800348 help_header.flush()
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800349 cmd = [os.path.join(src_root, 'makeself.sh'), '--bzip2', '--nox11',
Jon Salz4f3ade52014-02-20 17:55:09 +0800350 '--help-header', help_header.name,
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800351 src_root, output_path, initial_version + modified_msg,
352 INSTALLER_PATH, '--in-exe']
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800353 if not enable_device:
354 cmd.append('--no-enable-device')
355 if not enable_presenter:
356 cmd.append('--no-enable-presenter')
Youcheng Syuac391772017-04-20 09:08:58 +0000357 Spawn(cmd, check_call=True, log=True)
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800358 with file_utils.TempDirectory() as tmp_dir:
359 version_path = os.path.join(tmp_dir, VERSION_PATH)
360 os.makedirs(os.path.dirname(version_path))
361 file_utils.WriteFile(version_path, complete_version)
You-Cheng Syuc0682732017-05-10 12:17:05 +0800362 Spawn([cmd[0], '--lsm', version_path, '--append', tmp_dir, output_path],
363 check_call=True, log=True)
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800364 print ('\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800365 ' Factory toolkit generated at %s.\n'
366 '\n'
367 ' To install factory toolkit on a live device running a test image,\n'
368 ' copy this to the device and execute it as root.\n'
369 '\n'
370 ' Alternatively, the factory toolkit can be used to patch a test\n'
371 ' image. For more information, run:\n'
372 ' %s --help\n'
373 '\n' % (output_path, output_path))
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800374
375
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800376def ExtractOverlord(src_root, output_dir):
377 output_dir = os.path.join(output_dir, 'overlord')
378 try:
379 os.makedirs(output_dir)
380 except OSError as e:
381 print str(e)
382 return
383
384 # Copy overlord binary and resource files
385 shutil.copyfile(os.path.join(src_root, 'usr/bin/overlordd'),
386 os.path.join(output_dir, 'overlordd'))
387 shutil.copytree(os.path.join(src_root, 'usr/share/overlord/app'),
388 os.path.join(output_dir, 'app'))
389
390 # Give overlordd execution permission
391 os.chmod(os.path.join(output_dir, 'overlordd'), 0755)
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800392 print "Extracted overlord under '%s'" % output_dir
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800393
394
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800395def main():
Jon Salz4f3ade52014-02-20 17:55:09 +0800396 import logging
397 logging.basicConfig(level=logging.INFO)
398
399 # In order to determine which usage message to show, first determine
400 # whether we're in the self-extracting archive. Do this first
401 # because we need it to even parse the arguments.
402 if '--in-exe' in sys.argv:
403 sys.argv = [x for x in sys.argv if x != '--in-exe']
404 in_archive = True
405 else:
406 in_archive = False
407
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800408 parser = argparse.ArgumentParser(
Jon Salz4f3ade52014-02-20 17:55:09 +0800409 description=HELP_HEADER + HELP_HEADER_ADVANCED,
410 usage=('install_factory_toolkit.run -- [options]' if in_archive
411 else None),
412 formatter_class=argparse.RawDescriptionHelpFormatter)
Hung-Te Lin56b18402015-01-16 14:52:30 +0800413 parser.add_argument(
414 'dest', nargs='?', default='/',
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800415 help='A test image or the mount point of the stateful partition. '
416 "If omitted, install to live system, i.e. '/'.")
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800417 parser.add_argument('--no-enable', '-n', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800418 help="Don't enable factory tests after installing")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800419 parser.add_argument('--yes', '-y', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800420 help="Don't ask for confirmation")
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800421 parser.add_argument('--build-info', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800422 help='Print build information and exit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800423 parser.add_argument('--pack-into', metavar='NEW_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800424 help='Pack the files into a new factory toolkit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800425 parser.add_argument('--repack', metavar='UNPACKED_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800426 help='Repack from previously unpacked toolkit')
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800427 parser.add_argument('--version', metavar='VERSION',
428 help='String to write into TOOLKIT_VERSION when packing')
Vic Yang7039f422014-07-07 15:38:13 -0700429
Peter Ammon948b7172014-07-15 12:43:06 -0700430 parser.add_argument('--enable-presenter', dest='enable_presenter',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800431 action='store_true',
432 help='Run goofy in presenter mode on startup')
Peter Ammon948b7172014-07-15 12:43:06 -0700433 parser.add_argument('--no-enable-presenter', dest='enable_presenter',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800434 action='store_false', help=argparse.SUPPRESS)
Hung-Te Lin0cba9d52016-03-22 11:45:35 +0800435 parser.set_defaults(enable_presenter=False)
Vic Yang7039f422014-07-07 15:38:13 -0700436
Vic Yang70fdae92015-02-17 19:21:08 -0800437 parser.add_argument('--non-cros', dest='non_cros',
438 action='store_true',
439 help='Install on non-ChromeOS host.')
440
Vic Yang7039f422014-07-07 15:38:13 -0700441 parser.add_argument('--enable-device', dest='enable_device',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800442 action='store_true',
443 help='Run goofy in device mode on startup')
Vic Yang7039f422014-07-07 15:38:13 -0700444 parser.add_argument('--no-enable-device', dest='enable_device',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800445 action='store_false', help=argparse.SUPPRESS)
Hung-Te Lin0cba9d52016-03-22 11:45:35 +0800446 parser.set_defaults(enable_device=False)
Vic Yang423c2722014-09-24 16:05:06 +0800447
Rong Chang6d65fad2014-08-22 16:00:12 +0800448 parser.add_argument('--exe-path', dest='exe_path',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800449 nargs='?', default=None,
450 help='Current self-extracting archive pathname')
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800451 parser.add_argument('--extract-overlord', dest='extract_overlord',
452 metavar='OUTPUT_DIR', type=str, default=None,
453 help='Extract overlord from the toolkit')
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800454 parser.add_argument('--install-dirs', nargs='+', default=None,
455 help=('Install only the specified directories under '
456 'factory folder. Can be used with --apps to '
457 'enable / disable some apps. Defaults to install '
458 'all folders.'))
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800459 parser.add_argument('--apps', type=lambda s: s.split(','), default=None,
460 help=('Enable or disable some apps under '
461 'factory/init/main.d/. Use prefix "-" to disable, '
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800462 'prefix "+" to enable, and use "," to separate. '
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800463 'For example: --apps="-goofy,+whale_servo"'))
Vic Yang7039f422014-07-07 15:38:13 -0700464
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800465 args = parser.parse_args()
466
Peter Shihad166772017-05-31 11:36:17 +0800467 src_root = paths.FACTORY_DIR
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800468 for _ in xrange(3):
469 src_root = os.path.dirname(src_root)
470
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800471 if args.extract_overlord is not None:
472 ExtractOverlord(src_root, args.extract_overlord)
473 return
474
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800475 # --pack-into may be called directly so this must be done before changing
476 # working directory to OLDPWD.
477 if args.pack_into and args.repack is None:
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800478 PackFactoryToolkit(src_root, args.pack_into, args.version,
479 args.enable_device, args.enable_presenter)
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800480 return
481
Jon Salz4f3ade52014-02-20 17:55:09 +0800482 if not in_archive:
483 # If you're not in the self-extracting archive, you're not allowed to
484 # do anything except the above --pack-into call.
485 parser.error('Not running from install_factory_toolkit.run; '
486 'only --pack-into (without --repack) is allowed')
487
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800488 # Change to original working directory in case the user specifies
489 # a relative path.
490 # TODO: Use USER_PWD instead when makeself is upgraded
491 os.chdir(os.environ['OLDPWD'])
492
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800493 if args.repack:
494 if args.pack_into is None:
495 parser.error('Must specify --pack-into when using --repack.')
Youcheng Syuac391772017-04-20 09:08:58 +0000496 Spawn([os.path.join(args.repack, INSTALLER_PATH),
497 '--pack-into', args.pack_into], check_call=True, log=True)
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800498 return
499
500 if args.build_info:
501 PrintBuildInfo(src_root)
502 return
503
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800504 if not os.path.exists(args.dest):
505 parser.error('Destination %s does not exist!' % args.dest)
506
507 patch_test_image = os.path.isfile(args.dest)
508
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800509 with (sys_utils.MountPartition(args.dest, 1, rw=True) if patch_test_image
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800510 else DummyContext(args.dest)) as dest:
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800511
Hung-Te Lin56b18402015-01-16 14:52:30 +0800512 installer = FactoryToolkitInstaller(
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800513 src=src_root, dest=dest, no_enable=args.no_enable,
514 enable_presenter=args.enable_presenter,
515 enable_device=args.enable_device, non_cros=args.non_cros,
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800516 apps=args.apps)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800517
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800518 print installer.WarningMessage(args.dest if patch_test_image else None)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800519
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800520 if not args.yes:
521 answer = raw_input('*** Continue? [y/N] ')
522 if not answer or answer[0] not in 'yY':
523 sys.exit('Aborting.')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800524
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800525 if args.install_dirs:
526 installer.InstallFactorySubDir(args.install_dirs)
527 else:
528 installer.Install()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800529
530if __name__ == '__main__':
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800531 # makself interprets "LICENSE" environment variable string as license text and
Hung-Te Lin5a2a1c42016-11-10 12:43:40 +0800532 # will prompt user to accept before installation. For factory toolkit, we
533 # don't want any user interaction in installation and the license is already
534 # covered by ebuild or download platform like CPFE.
535 os.putenv('LICENSE', '')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800536 main()