blob: b228bf00ce9fd41f54dffa8685e19c35553b8129 [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
Yong Hong89938e62018-10-26 11:59:21 +080029from cros.factory.utils import json_utils
Youcheng Syuac391772017-04-20 09:08:58 +000030from cros.factory.utils.process_utils import Spawn
Peter Shihfe221582017-06-15 13:40:53 +080031from cros.factory.utils import sys_utils
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +080032
33
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +080034INSTALLER_PATH = 'usr/local/factory/py/toolkit/installer.py'
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +080035VERSION_PATH = 'usr/local/factory/TOOLKIT_VERSION'
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +080036
Jon Salz4f3ade52014-02-20 17:55:09 +080037# Short and sweet help header for the executable generated by makeself.
38HELP_HEADER = """
39Installs the factory toolkit, transforming a test image into a factory test
40image. You can:
41
42- Install the factory toolkit on a CrOS device that is running a test
43 image. To do this, copy install_factory_toolkit.run to the device and
44 run it. The factory tests will then come up on the next boot.
45
46 rsync -a install_factory_toolkit.run crosdevice:/tmp
47 ssh crosdevice '/tmp/install_factory_toolkit.run && sync && reboot'
48
49- Modify a test image, turning it into a factory test image. When you
50 use the image on a device, the factory tests will come up.
51
52 install_factory_toolkit.run chromiumos_test_image.bin
53"""
54
55HELP_HEADER_ADVANCED = """
56- (advanced) Modify a mounted stateful partition, turning it into a factory
57 test image. This is equivalent to the previous command:
58
59 mount_partition -rw chromiumos_test_image.bin 1 /mnt/stateful
60 install_factory_toolkit.run /mnt/stateful
61 umount /mnt/stateful
62
63- (advanced) Unpack the factory toolkit, modify a file, and then repack it.
64
65 # Unpack but don't actually install
66 install_factory_toolkit.run --target /tmp/toolkit --noexec
67 # Edit some files in /tmp/toolkit
68 emacs /tmp/toolkit/whatever
69 # Repack
70 install_factory_toolkit.run -- --repack /tmp/toolkit \\
71 --pack-into /path/to/new/install_factory_toolkit.run
72"""
73
74# The makeself-generated header comes next. This is a little confusing,
75# so explain.
76HELP_HEADER_MAKESELF = """
77For complete usage information and advanced operations, run
78"install_factory_toolkit.run -- --help" (note the extra "--").
79
80Following is the help message from makeself, which was used to create
81this self-extracting archive.
82
83-----
84"""
85
Vic Yangdb1e20e2014-10-05 12:10:33 +080086SERVER_FILE_MASK = [
87 # Exclude Umpire server but keep Umpire client
88 '--include', 'py/umpire/__init__.*',
89 '--include', 'py/umpire/common.*',
90 '--include', 'py/umpire/client',
91 '--include', 'py/umpire/client/**',
92 '--exclude', 'py/umpire/**',
Peter Shihac904782017-06-14 15:24:09 +080093 '--exclude', 'bin/umpire',
94 '--exclude', 'bin/umpired',
Vic Yangdb1e20e2014-10-05 12:10:33 +080095]
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.
Peter Shih2f1f8c42017-06-15 15:05:55 +0800108 apps: The list of apps to enable/disable under factory/init/main.d/.
109 active_test_list: The id of active test list for Goofy.
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800110 """
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800111
Jon Salzb7e44262014-05-07 15:53:37 +0800112 # Whether to sudo when rsyncing; set to False for testing.
113 _sudo = True
114
Hung-Te Linfc162e62017-09-21 00:45:04 +0800115 def __init__(self, src, dest, no_enable, non_cros=False, system_root='/',
Peter Shih2f1f8c42017-06-15 15:05:55 +0800116 apps=None, active_test_list=None):
Jon Salz4f3ade52014-02-20 17:55:09 +0800117 self._src = src
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800118 self._system_root = system_root
119 if dest == self._system_root:
120 self._usr_local_dest = os.path.join(dest, 'usr', 'local')
Jon Salz4f3ade52014-02-20 17:55:09 +0800121
122 # Make sure we're on a CrOS device.
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800123 if not non_cros and not sys_utils.InCrOSDevice():
Jon Salz4f3ade52014-02-20 17:55:09 +0800124 sys.stderr.write(
Vic Yang196d5242014-08-05 13:51:35 +0800125 "ERROR: You're not on a CrOS device (for more details, please\n"
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800126 'check sys_utils.py:InCrOSDevice), so you must specify a test\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800127 'image or a mounted stateful partition on which to install the\n'
128 'factory toolkit. Please run\n'
129 '\n'
130 ' install_factory_toolkit.run -- --help\n'
131 '\n'
Vic Yang70fdae92015-02-17 19:21:08 -0800132 'for help.\n'
133 '\n'
Hung-Te Linfc162e62017-09-21 00:45:04 +0800134 'If you want to install on a non-CrOS host,\n'
Vic Yang70fdae92015-02-17 19:21:08 -0800135 'please run\n'
136 '\n'
Hung-Te Linfc162e62017-09-21 00:45:04 +0800137 ' install_factory_toolkit.run -- --non-cros \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
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800154 self._apps = apps
Peter Shih2f1f8c42017-06-15 15:05:55 +0800155 self._active_test_list = active_test_list
Vic Yang7039f422014-07-07 15:38:13 -0700156
Hung-Te Lin4aeabe62016-10-21 15:45:20 +0800157 if not os.path.exists(self._usr_local_src):
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800158 raise Exception(
159 'This installer must be run from within the factory toolkit!')
160
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800161 def WarningMessage(self, target_test_image=None):
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800162 ret = file_utils.ReadFile(os.path.join(self._src, VERSION_PATH))
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800163 if target_test_image:
Jon Salz4f3ade52014-02-20 17:55:09 +0800164 ret += (
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800165 '\n'
166 '\n'
Jon Salz4f3ade52014-02-20 17:55:09 +0800167 '*** You are about to patch the factory toolkit into:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800168 '*** %s\n'
169 '***' % target_test_image)
170 else:
Jon Salz4f3ade52014-02-20 17:55:09 +0800171 ret += (
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800172 '\n'
173 '\n'
Jon Salz4f3ade52014-02-20 17:55:09 +0800174 '*** You are about to install the factory toolkit to:\n'
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800175 '*** %s\n'
176 '***' % self._dest)
177 if self._dest == self._system_root:
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800178 if self._no_enable:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800179 ret += ('\n*** Factory tests will be disabled after this process is '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800180 'done, but\n*** you can enable them by creating the factory '
181 'enabled tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800182 else:
Hung-Te Lin7b596ff2015-01-16 20:19:15 +0800183 ret += ('\n*** After this process is done, your device will start '
Hung-Te Lin56b18402015-01-16 14:52:30 +0800184 'factory\n*** tests on the next reboot.\n***\n*** Factory '
185 'tests can be disabled by deleting the factory enabled\n*** '
186 'tag:\n*** %s\n***' % self._tag_file)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800187 return ret
188
Vic Yang7039f422014-07-07 15:38:13 -0700189 def _SetTagFile(self, name, path, enabled):
190 """Install or remove a tag file."""
191 if enabled:
192 print '*** Installing %s enabled tag...' % name
Youcheng Syuac391772017-04-20 09:08:58 +0000193 Spawn(['touch', path], sudo=True, log=True, check_call=True)
194 Spawn(['chmod', 'go+r', path], sudo=True, log=True, check_call=True)
Vic Yang7039f422014-07-07 15:38:13 -0700195 else:
196 print '*** Removing %s enabled tag...' % name
Youcheng Syuac391772017-04-20 09:08:58 +0000197 Spawn(['rm', '-f', path], sudo=True, log=True, check_call=True)
Vic Yang7039f422014-07-07 15:38:13 -0700198
Peter Shih2f1f8c42017-06-15 15:05:55 +0800199 def _SetActiveTestList(self):
200 """Set the active test list for Goofy."""
201 if self._active_test_list is not None:
Yong Hong89938e62018-10-26 11:59:21 +0800202 path = os.path.join(self._usr_local_dest, 'factory', 'py', 'config',
203 'active_test_list.json')
204 json_utils.DumpFile(path, {'id': self._active_test_list})
Peter Shih2f1f8c42017-06-15 15:05:55 +0800205
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800206 def _EnableApp(self, app, enabled):
207 """Enable / disable @app.
208
209 In factory/init/startup, a main app is considered disabled if and only:
210 1. file "factory/init/main.d/disable-@app" exists OR
211 2. file "factory/init/main.d/enable-@app" doesn't exist AND
212 file "factory/init/main.d/@app.sh" is not executable.
213
214 Therefore, we enable an app by removing file "disable-@app" and creating
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800215 file "enable-@app", and vice versa.
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800216 """
217 app_enable = os.path.join(self._usr_local_dest,
218 'factory', 'init', 'main.d', 'enable-' + app)
219 app_disable = os.path.join(self._usr_local_dest,
220 'factory', 'init', 'main.d', 'disable-' + app)
221 if enabled:
222 print '*** Enabling {app} ***'.format(app=app)
Youcheng Syuac391772017-04-20 09:08:58 +0000223 Spawn(['rm', '-f', app_disable], sudo=self._sudo, log=True,
224 check_call=True)
225 Spawn(['touch', app_enable], sudo=self._sudo, log=True, check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800226 else:
227 print '*** Disabling {app} ***'.format(app=app)
Youcheng Syuac391772017-04-20 09:08:58 +0000228 Spawn(['touch', app_disable], sudo=self._sudo, log=True, check_call=True)
229 Spawn(['rm', '-f', app_enable], sudo=self._sudo, log=True,
230 check_call=True)
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800231
232 def _EnableApps(self):
233 if not self._apps:
234 return
235
236 app_list = []
237 for app in self._apps:
238 if app[0] == '+':
239 app_list.append((app[1:], True))
240 elif app[0] == '-':
241 app_list.append((app[1:], False))
242 else:
243 raise ValueError(
244 'Use +{app} to enable and -{app} to disable'.format(app=app))
245
246 for app, enabled in app_list:
247 self._EnableApp(app, enabled)
248
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800249 def InstallFactorySubDir(self, sub_dirs):
250 """Install only the specified directories under factory folder."""
251
252 def _InstallOneSubDir(sub_dir_name):
253 sub_dir_dest = os.path.join(self._usr_local_dest, 'factory', sub_dir_name)
254 sub_dir_src = os.path.join(self._src, 'usr', 'local', 'factory',
255 sub_dir_name)
256 try:
Youcheng Syuac391772017-04-20 09:08:58 +0000257 Spawn(['mkdir', '-p', sub_dir_dest], sudo=True, log=True,
258 check_call=True)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800259 except OSError as e:
260 print str(e)
261 return
262
Youcheng Syuac391772017-04-20 09:08:58 +0000263 Spawn(['rsync', '-a', '--force', '-v',
264 sub_dir_src + '/', sub_dir_dest],
265 sudo=self._sudo, log=True, check_call=True)
266 Spawn(['chown', '-R', 'root', sub_dir_dest],
267 sudo=self._sudo, log=True, check_call=True)
268 Spawn(['chmod', '-R', 'go+rX', sub_dir_dest],
269 sudo=self._sudo, log=True, check_call=True)
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800270
271 for sub_dir_name in sub_dirs:
272 _InstallOneSubDir(sub_dir_name)
273
274 self._SetTagFile('factory', self._tag_file, not self._no_enable)
275 self._EnableApps()
276
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800277 def Install(self):
278 print '*** Installing factory toolkit...'
Mao Huangf75aa3e2016-11-24 11:44:17 +0800279
280 # --no-owner and --no-group will set owner/group to the current user/group
281 # running the command. This is important if we're running with sudo, so
282 # the destination will be changed to root/root instead of the user/group
283 # before sudo (doesn't matter if sudo is not present). --force is also
284 # necessary to allow goofy directory from prior toolkit installations to
285 # be overwritten by the goofy symlink.
286 print '*** %s -> %s' % (self._usr_local_src, self._usr_local_dest)
Youcheng Syuac391772017-04-20 09:08:58 +0000287 Spawn(['rsync', '-a', '--no-owner', '--no-group', '--chmod=ugo+rX',
288 '--force'] + SERVER_FILE_MASK + [self._usr_local_src + '/',
289 self._usr_local_dest],
290 sudo=self._sudo, log=True, check_output=True, cwd=self._usr_local_src)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800291
Hung-Te Lineb7632b2016-07-29 15:38:34 +0800292 print '*** Ensure SSH keys file permission...'
293 sshkeys_dir = os.path.join(self._usr_local_dest, 'factory/misc/sshkeys')
294 sshkeys = glob.glob(os.path.join(sshkeys_dir, '*'))
295 ssh_public_keys = glob.glob(os.path.join(sshkeys_dir, '*.pub'))
296 ssh_private_keys = list(set(sshkeys) - set(ssh_public_keys))
297 if ssh_private_keys:
Youcheng Syuac391772017-04-20 09:08:58 +0000298 Spawn(['chmod', '600'] + ssh_private_keys, log=True, check_call=True,
299 sudo=self._sudo)
Hung-Te Lineb7632b2016-07-29 15:38:34 +0800300
Jon Salz25590302014-07-11 16:07:20 +0800301 print '*** Installing symlinks...'
302 install_symlinks.InstallSymlinks(
303 '../factory/bin',
304 os.path.join(self._usr_local_dest, 'bin'),
305 install_symlinks.MODE_FULL,
306 sudo=self._sudo)
307
Vic Yang7039f422014-07-07 15:38:13 -0700308 self._SetTagFile('factory', self._tag_file, not self._no_enable)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800309
Peter Shih2f1f8c42017-06-15 15:05:55 +0800310 self._SetActiveTestList()
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
Hung-Te Linfc162e62017-09-21 00:45:04 +0800330def PackFactoryToolkit(src_root, output_path, initial_version):
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800331 """Packs the files containing this script into a factory toolkit."""
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800332 if initial_version is None:
333 complete_version = '%s repacked by %s@%s at %s\n' % (
334 file_utils.ReadFile(os.path.join(src_root, VERSION_PATH)),
335 getpass.getuser(), os.uname()[1], time.strftime('%Y-%m-%d %H:%M:%S'))
336 initial_version = complete_version.splitlines()[0]
337 else:
338 complete_version = initial_version + '\n'
339 modified_times = len(complete_version.splitlines()) - 1
340 if modified_times == 0:
341 modified_msg = ''
342 else:
343 modified_msg = ' (modified %d times)' % modified_times
Jon Salz4f3ade52014-02-20 17:55:09 +0800344 with tempfile.NamedTemporaryFile() as help_header:
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800345 help_header.write(initial_version + '\n' +
346 HELP_HEADER + HELP_HEADER_MAKESELF)
Jon Salz4f3ade52014-02-20 17:55:09 +0800347 help_header.flush()
Wei-Ning Huangb723d7f2014-11-17 17:26:32 +0800348 cmd = [os.path.join(src_root, 'makeself.sh'), '--bzip2', '--nox11',
Jon Salz4f3ade52014-02-20 17:55:09 +0800349 '--help-header', help_header.name,
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800350 src_root, output_path, initial_version + modified_msg,
351 INSTALLER_PATH, '--in-exe']
Youcheng Syuac391772017-04-20 09:08:58 +0000352 Spawn(cmd, check_call=True, log=True)
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800353 with file_utils.TempDirectory() as tmp_dir:
354 version_path = os.path.join(tmp_dir, VERSION_PATH)
355 os.makedirs(os.path.dirname(version_path))
356 file_utils.WriteFile(version_path, complete_version)
You-Cheng Syuc0682732017-05-10 12:17:05 +0800357 Spawn([cmd[0], '--lsm', version_path, '--append', tmp_dir, output_path],
358 check_call=True, log=True)
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800359 print ('\n'
Hung-Te Lin56b18402015-01-16 14:52:30 +0800360 ' Factory toolkit generated at %s.\n'
361 '\n'
362 ' To install factory toolkit on a live device running a test image,\n'
363 ' copy this to the device and execute it as root.\n'
364 '\n'
365 ' Alternatively, the factory toolkit can be used to patch a test\n'
366 ' image. For more information, run:\n'
367 ' %s --help\n'
368 '\n' % (output_path, output_path))
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800369
370
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800371def ExtractOverlord(src_root, output_dir):
372 output_dir = os.path.join(output_dir, 'overlord')
373 try:
374 os.makedirs(output_dir)
375 except OSError as e:
376 print str(e)
377 return
378
379 # Copy overlord binary and resource files
380 shutil.copyfile(os.path.join(src_root, 'usr/bin/overlordd'),
381 os.path.join(output_dir, 'overlordd'))
382 shutil.copytree(os.path.join(src_root, 'usr/share/overlord/app'),
383 os.path.join(output_dir, 'app'))
384
385 # Give overlordd execution permission
Peter Shihe6afab32018-09-11 17:16:48 +0800386 os.chmod(os.path.join(output_dir, 'overlordd'), 0o755)
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800387 print "Extracted overlord under '%s'" % output_dir
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800388
389
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800390def main():
Jon Salz4f3ade52014-02-20 17:55:09 +0800391 import logging
392 logging.basicConfig(level=logging.INFO)
393
394 # In order to determine which usage message to show, first determine
395 # whether we're in the self-extracting archive. Do this first
396 # because we need it to even parse the arguments.
397 if '--in-exe' in sys.argv:
398 sys.argv = [x for x in sys.argv if x != '--in-exe']
399 in_archive = True
400 else:
401 in_archive = False
402
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800403 parser = argparse.ArgumentParser(
Jon Salz4f3ade52014-02-20 17:55:09 +0800404 description=HELP_HEADER + HELP_HEADER_ADVANCED,
405 usage=('install_factory_toolkit.run -- [options]' if in_archive
406 else None),
407 formatter_class=argparse.RawDescriptionHelpFormatter)
Hung-Te Lin56b18402015-01-16 14:52:30 +0800408 parser.add_argument(
409 'dest', nargs='?', default='/',
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800410 help='A test image or the mount point of the stateful partition. '
411 "If omitted, install to live system, i.e. '/'.")
Vic (Chun-Ju) Yang7cc3e672014-01-20 14:06:39 +0800412 parser.add_argument('--no-enable', '-n', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800413 help="Don't enable factory tests after installing")
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800414 parser.add_argument('--yes', '-y', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800415 help="Don't ask for confirmation")
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800416 parser.add_argument('--build-info', action='store_true',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800417 help='Print build information and exit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800418 parser.add_argument('--pack-into', metavar='NEW_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800419 help='Pack the files into a new factory toolkit')
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800420 parser.add_argument('--repack', metavar='UNPACKED_TOOLKIT',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800421 help='Repack from previously unpacked toolkit')
You-Cheng Syu53f4a0c2017-04-20 17:46:12 +0800422 parser.add_argument('--version', metavar='VERSION',
423 help='String to write into TOOLKIT_VERSION when packing')
Vic Yang7039f422014-07-07 15:38:13 -0700424
Vic Yang70fdae92015-02-17 19:21:08 -0800425 parser.add_argument('--non-cros', dest='non_cros',
426 action='store_true',
427 help='Install on non-ChromeOS host.')
428
Vic Yang423c2722014-09-24 16:05:06 +0800429
Rong Chang6d65fad2014-08-22 16:00:12 +0800430 parser.add_argument('--exe-path', dest='exe_path',
Hung-Te Lin56b18402015-01-16 14:52:30 +0800431 nargs='?', default=None,
432 help='Current self-extracting archive pathname')
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800433 parser.add_argument('--extract-overlord', dest='extract_overlord',
434 metavar='OUTPUT_DIR', type=str, default=None,
435 help='Extract overlord from the toolkit')
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800436 parser.add_argument('--install-dirs', nargs='+', default=None,
437 help=('Install only the specified directories under '
438 'factory folder. Can be used with --apps to '
439 'enable / disable some apps. Defaults to install '
440 'all folders.'))
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800441 parser.add_argument('--apps', type=lambda s: s.split(','), default=None,
442 help=('Enable or disable some apps under '
443 'factory/init/main.d/. Use prefix "-" to disable, '
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800444 'prefix "+" to enable, and use "," to separate. '
Wei-Han Chene6fe3872016-06-30 14:29:06 +0800445 'For example: --apps="-goofy,+whale_servo"'))
Peter Shih2f1f8c42017-06-15 15:05:55 +0800446 parser.add_argument('--active-test-list', dest='active_test_list',
447 default=None,
448 help='Set the id of active test list for Goofy.')
Vic Yang7039f422014-07-07 15:38:13 -0700449
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800450 args = parser.parse_args()
451
Peter Shihad166772017-05-31 11:36:17 +0800452 src_root = paths.FACTORY_DIR
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800453 for _ in xrange(3):
454 src_root = os.path.dirname(src_root)
455
Wei-Ning Huang4855e792015-06-11 15:33:39 +0800456 if args.extract_overlord is not None:
457 ExtractOverlord(src_root, args.extract_overlord)
458 return
459
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800460 # --pack-into may be called directly so this must be done before changing
461 # working directory to OLDPWD.
462 if args.pack_into and args.repack is None:
Hung-Te Linfc162e62017-09-21 00:45:04 +0800463 PackFactoryToolkit(src_root, args.pack_into, args.version)
Vic (Chun-Ju) Yang98b4fbc2014-02-18 19:32:32 +0800464 return
465
Jon Salz4f3ade52014-02-20 17:55:09 +0800466 if not in_archive:
467 # If you're not in the self-extracting archive, you're not allowed to
468 # do anything except the above --pack-into call.
469 parser.error('Not running from install_factory_toolkit.run; '
470 'only --pack-into (without --repack) is allowed')
471
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800472 # Change to original working directory in case the user specifies
473 # a relative path.
474 # TODO: Use USER_PWD instead when makeself is upgraded
475 os.chdir(os.environ['OLDPWD'])
476
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800477 if args.repack:
478 if args.pack_into is None:
479 parser.error('Must specify --pack-into when using --repack.')
Youcheng Syuac391772017-04-20 09:08:58 +0000480 Spawn([os.path.join(args.repack, INSTALLER_PATH),
481 '--pack-into', args.pack_into], check_call=True, log=True)
Vic (Chun-Ju) Yangb7388f72014-02-19 15:22:58 +0800482 return
483
484 if args.build_info:
485 PrintBuildInfo(src_root)
486 return
487
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800488 if not os.path.exists(args.dest):
489 parser.error('Destination %s does not exist!' % args.dest)
490
491 patch_test_image = os.path.isfile(args.dest)
492
Hung-Te Linf5f2d7f2016-01-08 17:12:46 +0800493 with (sys_utils.MountPartition(args.dest, 1, rw=True) if patch_test_image
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800494 else DummyContext(args.dest)) as dest:
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800495
Hung-Te Lin56b18402015-01-16 14:52:30 +0800496 installer = FactoryToolkitInstaller(
Wei-Ning Huang5135b7e2015-07-03 17:31:15 +0800497 src=src_root, dest=dest, no_enable=args.no_enable,
Hung-Te Linfc162e62017-09-21 00:45:04 +0800498 non_cros=args.non_cros, apps=args.apps,
499 active_test_list=args.active_test_list)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800500
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800501 print installer.WarningMessage(args.dest if patch_test_image else None)
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800502
Vic (Chun-Ju) Yang469592b2014-02-18 19:15:41 +0800503 if not args.yes:
504 answer = raw_input('*** Continue? [y/N] ')
505 if not answer or answer[0] not in 'yY':
506 sys.exit('Aborting.')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800507
Wei-Han Chen7137dcf2016-08-03 15:43:22 +0800508 if args.install_dirs:
509 installer.InstallFactorySubDir(args.install_dirs)
510 else:
511 installer.Install()
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800512
513if __name__ == '__main__':
You-Cheng Syu70e99ad2017-03-09 17:15:00 +0800514 # makself interprets "LICENSE" environment variable string as license text and
Hung-Te Lin5a2a1c42016-11-10 12:43:40 +0800515 # will prompt user to accept before installation. For factory toolkit, we
516 # don't want any user interaction in installation and the license is already
517 # covered by ebuild or download platform like CPFE.
518 os.putenv('LICENSE', '')
Vic (Chun-Ju) Yang296871a2014-01-13 12:05:18 +0800519 main()