blob: 715ca0a5d87073903bcbce393adb6c15dc66c896 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Mike Frysinger2de7f042012-07-10 04:45:03 -04002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Brian Harringb938c782012-02-29 15:14:38 -08003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
Manoj Gupta7fad04d2019-06-14 20:12:25 -07005
Mike Frysinger2f95cfc2015-06-04 04:00:26 -04006"""Manage SDK chroots.
7
8This script is used for manipulating local chroot environments; creating,
9deleting, downloading, etc. If given --enter (or no args), it defaults
10to an interactive bash shell within the chroot.
11
12If given args those are passed to the chroot environment, and executed.
13"""
Brian Harringb938c782012-02-29 15:14:38 -080014
Mike Frysinger383367e2014-09-16 15:06:17 -040015from __future__ import print_function
16
Mike Frysinger2f95cfc2015-06-04 04:00:26 -040017import argparse
Josh Triplett472a4182013-03-08 11:48:57 -080018import glob
Brian Harringb938c782012-02-29 15:14:38 -080019import os
Josh Triplett472a4182013-03-08 11:48:57 -080020import pwd
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060021import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070022import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070023import resource
David James56e6c2c2012-10-24 23:54:41 -070024import sys
Mike Frysinger3dcacee2019-08-23 17:09:11 -040025
26from six.moves import urllib
Brian Harringb938c782012-02-29 15:14:38 -080027
Aviv Keshetb7519e12016-10-04 00:50:00 -070028from chromite.lib import constants
Brian Harringcfe762a2012-02-29 13:03:53 -080029from chromite.lib import cgroups
Brian Harringb6cf9142012-09-01 20:43:17 -070030from chromite.lib import commandline
Brian Harringb938c782012-02-29 15:14:38 -080031from chromite.lib import cros_build_lib
Ralph Nathan59900422015-03-24 10:41:17 -070032from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060033from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080034from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080035from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070036from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080037from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040038from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080039from chromite.lib import retry_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050040from chromite.lib import toolchain
Brian Harringb938c782012-02-29 15:14:38 -080041
42cros_build_lib.STRICT_SUDO = True
43
Zdenek Behanaa52cea2012-05-30 01:31:11 +020044COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020045
Brian Harringb938c782012-02-29 15:14:38 -080046# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070047MAKE_CHROOT = [
48 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/make_chroot.sh')
49]
50ENTER_CHROOT = [
51 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
52]
Brian Harringb938c782012-02-29 15:14:38 -080053
Josh Triplett472a4182013-03-08 11:48:57 -080054# Proxy simulator configuration.
55PROXY_HOST_IP = '192.168.240.1'
56PROXY_PORT = 8080
57PROXY_GUEST_IP = '192.168.240.2'
58PROXY_NETMASK = 30
59PROXY_VETH_PREFIX = 'veth'
60PROXY_CONNECT_PORTS = (80, 443, 9418)
61PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
62PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
63PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070064 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080065PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
66
Josh Triplett9a495f62013-03-15 18:06:55 -070067# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080068NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080069
Josh Triplett472a4182013-03-08 11:48:57 -080070# Tools needed for --proxy-sim only.
71PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080072
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060073# Tools needed when use_image is true (the default).
74IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060075 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060076
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070077# As space is used inside the chroot, the empty space in chroot.img is
78# allocated. Deleting files inside the chroot doesn't automatically return the
79# used space to the OS. Over time, this tends to make the sparse chroot.img
80# less sparse even if the chroot contents don't currently need much space. We
81# can recover most of this unused space with fstrim, but that takes too much
82# time to run it every time. Instead, check the used space against the image
83# size after mounting the chroot and only call fstrim if it looks like we could
84# recover at least this many GiB.
85MAX_UNUSED_IMAGE_GBS = 20
86
Mike Frysingercc838832014-05-24 13:10:30 -040087
Brian Harring1790ac42012-09-23 08:53:33 -070088def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080089 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070090 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
91 return [
92 toolchain.GetSdkURL(
93 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
94 for compressor in COMPRESSION_PREFERENCE
95 ]
Brian Harring1790ac42012-09-23 08:53:33 -070096
97
Gilad Arnoldecc86fa2015-05-22 12:06:04 -070098def FetchRemoteTarballs(storage_dir, urls, desc, allow_none=False):
Mike Frysinger34db8692013-11-11 14:54:08 -050099 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200100
101 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500102 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200103 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700104 desc: A string describing what tarball we're downloading (for logging).
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700105 allow_none: Don't fail if none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200106
107 Returns:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700108 Full path to the downloaded file, or None if |allow_none| and no URL worked.
109
110 Raises:
111 ValueError: If |allow_none| is False and none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200112 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200113
Brian Harring1790ac42012-09-23 08:53:33 -0700114 # Note we track content length ourselves since certain versions of curl
115 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700116 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700117 logging.notice('Downloading %s tarball...', desc)
Brian Norriscf8aef42016-09-27 10:43:39 -0700118 status_re = re.compile(r'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400119 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200120 for url in urls:
Mike Frysinger3dcacee2019-08-23 17:09:11 -0400121 parsed = urllib.parse.urlparse(url)
Brian Harring1790ac42012-09-23 08:53:33 -0700122 tarball_name = os.path.basename(parsed.path)
123 if parsed.scheme in ('', 'file'):
124 if os.path.exists(parsed.path):
125 return parsed.path
126 continue
127 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700128 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700129 result = retry_util.RunCurl(['-I', url],
130 print_cmd=False,
131 debug_level=logging.NOTICE,
132 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700133 successful = False
134 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700135 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700136 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700137 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700138 successful = True
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500139 elif header.lower().startswith('content-length:'):
140 content_length = int(header.split(':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700141 if successful:
142 break
143 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200144 break
145 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700146 if allow_none:
147 return None
148 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200149
Brian Harringae0a5322012-09-15 01:46:51 -0700150 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700151 current_size = 0
152 if os.path.exists(tarball_dest):
153 current_size = os.path.getsize(tarball_dest)
154 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700155 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700156 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100157
Brian Harring1790ac42012-09-23 08:53:33 -0700158 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800159 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900160 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700161 print_cmd=False,
162 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800163
Brian Harring1790ac42012-09-23 08:53:33 -0700164 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700165 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
166 # because we might have partial overlap between known prefixes.
167 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
168 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
169 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700170 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700171 if (filename == tarball_name or
172 any([(filename.startswith(p) and
173 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
174 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700175 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700176 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700177 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200178
Brian Harringb938c782012-02-29 15:14:38 -0800179 return tarball_dest
180
181
Benjamin Gordon589873b2018-05-31 14:30:56 -0600182def CreateChroot(chroot_path, sdk_tarball, cache_dir, nousepkg=False):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600183 """Creates a new chroot from a given SDK.
184
185 Args:
186 chroot_path: Path where the new chroot will be created.
187 sdk_tarball: Path to a downloaded Gentoo Stage3 or Chromium OS SDK tarball.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600188 cache_dir: Path to a directory that will be used for caching portage files,
189 etc.
190 nousepkg: If True, pass --nousepkg to cros_setup_toolchains inside the
191 chroot.
192 """
Brian Harringb938c782012-02-29 15:14:38 -0800193
Manoj Guptab12f7302019-06-03 16:40:14 -0700194 cmd = MAKE_CHROOT + [
195 '--stage3_path', sdk_tarball, '--chroot', chroot_path, '--cache_dir',
196 cache_dir
197 ]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700198
Mike Frysinger2de7f042012-07-10 04:45:03 -0400199 if nousepkg:
200 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800201
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700202 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800203 try:
204 cros_build_lib.RunCommand(cmd, print_cmd=False)
205 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700206 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800207
208
209def DeleteChroot(chroot_path):
210 """Deletes an existing chroot"""
Manoj Guptab12f7302019-06-03 16:40:14 -0700211 cmd = MAKE_CHROOT + ['--chroot', chroot_path, '--delete']
Brian Harringb938c782012-02-29 15:14:38 -0800212 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700213 logging.notice('Deleting chroot.')
Brian Harringb938c782012-02-29 15:14:38 -0800214 cros_build_lib.RunCommand(cmd, print_cmd=False)
215 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700216 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800217
218
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600219def CleanupChroot(chroot_path):
220 """Unmounts a chroot and cleans up any associated devices."""
Don Garrett36650112018-06-28 15:54:34 -0700221 cros_sdk_lib.CleanupChrootMount(chroot_path, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600222
223
Brian Harringae0a5322012-09-15 01:46:51 -0700224def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500225 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800226 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400227 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
228 # The os.ST_NOSUID constant wasn't added until python-3.2.
229 if st.f_flag & 0x2:
230 cros_build_lib.Die('chroot cannot be in a nosuid mount')
231
Brian Harringae0a5322012-09-15 01:46:51 -0700232 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800233 if chrome_root:
234 cmd.extend(['--chrome_root', chrome_root])
235 if chrome_root_mount:
236 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900237 if goma_dir:
238 cmd.extend(['--goma_dir', goma_dir])
239 if goma_client_json:
240 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800241 if working_dir is not None:
242 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700243
Brian Harringb938c782012-02-29 15:14:38 -0800244 if len(additional_args) > 0:
245 cmd.append('--')
246 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700247
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700248 # ThinLTO opens lots of files at the same time.
249 resource.setrlimit(resource.RLIMIT_NOFILE, (32768, 32768))
Manoj Guptab12f7302019-06-03 16:40:14 -0700250 ret = cros_build_lib.RunCommand(
251 cmd, print_cmd=False, error_code_ok=True, mute_output=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700252 # If we were in interactive mode, ignore the exit code; it'll be whatever
253 # they last ran w/in the chroot and won't matter to us one way or another.
254 # Note this does allow chroot entrance to fail and be ignored during
255 # interactive; this is however a rare case and the user will immediately
256 # see it (nor will they be checking the exit code manually).
257 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700258 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800259
260
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600261def _ImageFileForChroot(chroot):
262 """Find the image file that should be associated with |chroot|.
263
264 This function does not check if the image exists; it simply returns the
265 filename that would be used.
266
267 Args:
268 chroot: Path to the chroot.
269
270 Returns:
271 Path to an image file that would be associated with chroot.
272 """
273 return chroot.rstrip('/') + '.img'
274
275
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600276def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
277 """Create a snapshot for the specified chroot VG/LV.
278
279 Args:
280 snapshot_name: The name of the new snapshot.
281 chroot_vg: The name of the VG containing the origin LV.
282 chroot_lv: The name of the origin LV.
283
284 Returns:
285 True if the snapshot was created, or False if a snapshot with the same
286 name already exists.
287
288 Raises:
289 SystemExit: The lvcreate command failed.
290 """
291 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700292 logging.error(
293 'Cannot create snapshot %s: A volume with that name already '
294 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600295 return False
296
Manoj Guptab12f7302019-06-03 16:40:14 -0700297 cmd = [
298 'lvcreate', '-s', '--name', snapshot_name,
299 '%s/%s' % (chroot_vg, chroot_lv)
300 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600301 try:
302 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
303 chroot_lv, chroot_vg)
304 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
305 return True
306 except cros_build_lib.RunCommandError:
307 raise SystemExit('Running %r failed!' % cmd)
308
309
310def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
311 """Delete the named snapshot from the specified chroot VG.
312
313 If the requested snapshot is not found, nothing happens. The main chroot LV
314 and internal thinpool LV cannot be deleted with this function.
315
316 Args:
317 snapshot_name: The name of the snapshot to delete.
318 chroot_vg: The name of the VG containing the origin LV.
319 chroot_lv: The name of the origin LV.
320
321 Raises:
322 SystemExit: The lvremove command failed.
323 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600324 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
325 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700326 logging.error(
327 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
328 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600329 return
330
331 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
332 return
333
334 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
335 try:
336 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
337 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
338 except cros_build_lib.RunCommandError:
339 raise SystemExit('Running %r failed!' % cmd)
340
341
342def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
343 """Restore the chroot to an existing snapshot.
344
345 This is done by renaming the original |chroot_lv| LV to a temporary name,
346 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
347 now unused LV. If an error occurs, attempts to rename the original snapshot
348 back to |chroot_lv| to leave the chroot unchanged.
349
350 The chroot must be unmounted before calling this function, and will be left
351 unmounted after this function returns.
352
353 Args:
354 snapshot_name: The name of the snapshot to restore. This snapshot will no
355 longer be accessible at its original name after this function finishes.
356 chroot_vg: The VG containing the chroot LV and snapshot LV.
357 chroot_lv: The name of the original chroot LV.
358
359 Returns:
360 True if the chroot was restored to the requested snapshot, or False if
361 the snapshot wasn't found or isn't valid.
362
363 Raises:
364 SystemExit: Any of the LVM commands failed.
365 """
366 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600367 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
368 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600369 snapshot_name not in valid_snapshots):
370 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
371 snapshot_name, ', '.join(valid_snapshots))
372 return False
373
374 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
375 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
376 try:
377 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
378 except cros_build_lib.RunCommandError:
379 raise SystemExit('Running %r failed!' % cmd)
380
381 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
382 try:
383 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
384 except cros_build_lib.RunCommandError:
385 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
386 try:
387 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
388 except cros_build_lib.RunCommandError:
389 raise SystemExit('Failed to rename %s to chroot and failed to restore '
390 '%s back to chroot. Failed command: %r' %
391 (snapshot_name, backup_chroot_name, cmd))
Manoj Guptab12f7302019-06-03 16:40:14 -0700392 raise SystemExit(
393 'Failed to rename %s to chroot. Original chroot LV has '
394 'been restored. Failed command: %r' % (snapshot_name, cmd))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600395
396 # Some versions of LVM set snapshots to be skipped at auto-activate time.
397 # Other versions don't have this flag at all. We run lvchange to try
398 # disabling auto-skip and activating the volume, but ignore errors. Versions
399 # that don't have the flag should be auto-activated.
400 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
401 cmd = ['lvchange', '-kn', chroot_lv_path]
Manoj Guptab12f7302019-06-03 16:40:14 -0700402 cros_build_lib.RunCommand(
403 cmd, print_cmd=False, capture_output=True, error_code_ok=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600404
405 # Activate the LV in case the lvchange above was needed. Activating an LV
406 # that is already active shouldn't do anything, so this is safe to run even if
407 # the -kn wasn't needed.
408 cmd = ['lvchange', '-ay', chroot_lv_path]
409 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
410
411 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
412 try:
413 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
414 except cros_build_lib.RunCommandError:
415 raise SystemExit('Failed to remove backup LV %s/%s. Failed command: %r' %
416 (chroot_vg, backup_chroot_name, cmd))
417
418 return True
419
420
421def ListChrootSnapshots(chroot_vg, chroot_lv):
422 """Return all snapshots in |chroot_vg| regardless of origin volume.
423
424 Args:
425 chroot_vg: The name of the VG containing the chroot.
426 chroot_lv: The name of the chroot LV.
427
428 Returns:
429 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
430
431 Raises:
432 SystemExit: The lvs command failed.
433 """
434 if not chroot_vg or not chroot_lv:
435 return []
436
Manoj Guptab12f7302019-06-03 16:40:14 -0700437 cmd = [
438 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
439 '--separator', '\t', chroot_vg
440 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600441 try:
Manoj Guptab12f7302019-06-03 16:40:14 -0700442 result = cros_build_lib.RunCommand(
443 cmd, print_cmd=False, redirect_stdout=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600444 except cros_build_lib.RunCommandError:
445 raise SystemExit('Running %r failed!' % cmd)
446
447 # Once the thin origin volume has been deleted, there's no way to tell a
448 # snapshot apart from any other volume. Since this VG is created and managed
449 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
450 # valid snapshots.
451 snapshots = []
452 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
453 for line in result.output.splitlines():
454 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700455 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600456 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600457 not snapshot_attrs.match(lv_attr)):
458 continue
459 snapshots.append(lv_name)
460 return snapshots
461
462
David James56e6c2c2012-10-24 23:54:41 -0700463def _SudoCommand():
464 """Get the 'sudo' command, along with all needed environment variables."""
465
David James5a73b4d2013-03-07 10:23:40 -0800466 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
467 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700468 cmd = ['sudo']
David James5a73b4d2013-03-07 10:23:40 -0800469 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700470 value = os.environ.get(key)
471 if value is not None:
472 cmd += ['%s=%s' % (key, value)]
473
474 # Pass in the path to the depot_tools so that users can access them from
475 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400476 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500477
David James56e6c2c2012-10-24 23:54:41 -0700478 return cmd
479
480
Josh Triplett472a4182013-03-08 11:48:57 -0800481def _ReportMissing(missing):
482 """Report missing utilities, then exit.
483
484 Args:
485 missing: List of missing utilities, as returned by
486 osutils.FindMissingBinaries. If non-empty, will not return.
487 """
488
489 if missing:
490 raise SystemExit(
491 'The tool(s) %s were not found.\n'
492 'Please install the appropriate package in your host.\n'
493 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700494 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800495
496
497def _ProxySimSetup(options):
498 """Set up proxy simulator, and return only in the child environment.
499
500 TODO: Ideally, this should support multiple concurrent invocations of
501 cros_sdk --proxy-sim; currently, such invocations will conflict with each
502 other due to the veth device names and IP addresses. Either this code would
503 need to generate fresh, unused names for all of these before forking, or it
504 would need to support multiple concurrent cros_sdk invocations sharing one
505 proxy and allowing it to exit when unused (without counting on any local
506 service-management infrastructure on the host).
507 """
508
509 may_need_mpm = False
510 apache_bin = osutils.Which('apache2')
511 if apache_bin is None:
512 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
513 if apache_bin is None:
514 _ReportMissing(('apache2',))
515 else:
516 may_need_mpm = True
517
518 # Module names and .so names included for ease of grepping.
519 apache_modules = [('proxy_module', 'mod_proxy.so'),
520 ('proxy_connect_module', 'mod_proxy_connect.so'),
521 ('proxy_http_module', 'mod_proxy_http.so'),
522 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
523
524 # Find the apache module directory, and make sure it has the modules we need.
525 module_dirs = {}
526 for g in PROXY_APACHE_MODULE_GLOBS:
527 for mod, so in apache_modules:
528 for f in glob.glob(os.path.join(g, so)):
529 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400530 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800531 if len(modules_found) == len(apache_modules):
532 break
533 else:
534 # Appease cros lint, which doesn't understand that this else block will not
535 # fall through to the subsequent code which relies on apache_module_path.
536 apache_module_path = None
537 raise SystemExit(
538 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500539 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800540
541 def check_add_module(name):
542 so = 'mod_%s.so' % name
543 if os.access(os.path.join(apache_module_path, so), os.F_OK):
544 mod = '%s_module' % name
545 apache_modules.append((mod, so))
546 return True
547 return False
548
549 check_add_module('authz_core')
550 if may_need_mpm:
551 for mpm in PROXY_APACHE_MPMS:
552 if check_add_module('mpm_%s' % mpm):
553 break
554
555 veth_host = '%s-host' % PROXY_VETH_PREFIX
556 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
557
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500558 # Set up locks to sync the net namespace setup. We need the child to create
559 # the net ns first, and then have the parent assign the guest end of the veth
560 # interface to the child's new network namespace & bring up the proxy. Only
561 # then can the child move forward and rely on the network being up.
562 ns_create_lock = locking.PipeLock()
563 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800564
565 pid = os.fork()
566 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500567 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800568 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500569
570 # Signal the parent the ns is ready to be configured.
571 ns_create_lock.Post()
572 del ns_create_lock
573
574 # Wait for the parent to finish setting up the ns/proxy.
575 ns_setup_lock.Wait()
576 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800577
578 # Set up child side of the network.
579 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500580 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700581 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500582 'dev', veth_guest),
583 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800584 )
585 try:
586 for cmd in commands:
587 cros_build_lib.RunCommand(cmd, print_cmd=False)
588 except cros_build_lib.RunCommandError:
589 raise SystemExit('Running %r failed!' % (cmd,))
590
591 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
592 for proto in ('http', 'https', 'ftp'):
593 os.environ[proto + '_proxy'] = proxy_url
594 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
595 os.environ.pop(v, None)
596 return
597
Josh Triplett472a4182013-03-08 11:48:57 -0800598 # Set up parent side of the network.
599 uid = int(os.environ.get('SUDO_UID', '0'))
600 gid = int(os.environ.get('SUDO_GID', '0'))
601 if uid == 0 or gid == 0:
602 for username in PROXY_APACHE_FALLBACK_USERS:
603 try:
604 pwnam = pwd.getpwnam(username)
605 uid, gid = pwnam.pw_uid, pwnam.pw_gid
606 break
607 except KeyError:
608 continue
609 if uid == 0 or gid == 0:
610 raise SystemExit('Could not find a non-root user to run Apache as')
611
612 chroot_parent, chroot_base = os.path.split(options.chroot)
613 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
614 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
615
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500616 # Wait for the child to create the net ns.
617 ns_create_lock.Wait()
618 del ns_create_lock
619
Josh Triplett472a4182013-03-08 11:48:57 -0800620 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500621 'User #%u' % uid,
622 'Group #%u' % gid,
623 'PidFile %s' % pid_file,
624 'ErrorLog %s' % log_file,
625 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
626 'ServerName %s' % PROXY_HOST_IP,
627 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400628 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800629 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500630 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
631 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800632 ]
633 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700634 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
635 veth_guest),
636 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
637 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500638 ('ip', 'link', 'set', veth_host, 'up'),
639 ([apache_bin, '-f', '/dev/null'] +
640 [arg for d in apache_directives for arg in ('-C', d)]),
641 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800642 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700643 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800644 try:
645 for cmd in commands:
646 cros_build_lib.RunCommand(cmd, print_cmd=False)
647 except cros_build_lib.RunCommandError:
648 # Clean up existing interfaces, if any.
649 cmd_cleanup = ('ip', 'link', 'del', veth_host)
650 try:
651 cros_build_lib.RunCommand(cmd_cleanup, print_cmd=False)
652 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700653 logging.error('running %r failed', cmd_cleanup)
Josh Triplett472a4182013-03-08 11:48:57 -0800654 raise SystemExit('Running %r failed!' % (cmd,))
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500655
656 # Signal the child that the net ns/proxy is fully configured now.
657 ns_setup_lock.Post()
658 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800659
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400660 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800661
662
Mike Frysingera78a56e2012-11-20 06:02:30 -0500663def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700664 """Re-execute cros_sdk as root.
665
666 Also unshare the mount namespace so as to ensure that processes outside
667 the chroot can't mess with our mounts.
668 """
669 if os.geteuid() != 0:
Mike Frysingera78a56e2012-11-20 06:02:30 -0500670 cmd = _SudoCommand() + ['--'] + argv
671 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500672 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400673 # We must set up the cgroups mounts before we enter our own namespace.
674 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800675 cgroups.Cgroup.InitSystem()
David James56e6c2c2012-10-24 23:54:41 -0700676
677
Mike Frysinger34db8692013-11-11 14:54:08 -0500678def _CreateParser(sdk_latest_version, bootstrap_latest_version):
679 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400680 usage = ('usage: %(prog)s [options] '
681 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700682 parser = commandline.ArgumentParser(
683 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700684
Mike Frysinger34db8692013-11-11 14:54:08 -0500685 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500686 default_chroot = os.path.join(constants.SOURCE_ROOT,
687 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400688 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700689 '--chroot',
690 dest='chroot',
691 default=default_chroot,
692 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700693 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700694 parser.add_argument(
695 '--nouse-image',
696 dest='use_image',
697 action='store_false',
698 default=True,
699 help='Do not mount the chroot on a loopback image; '
700 'instead, create it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800701
Manoj Guptab12f7302019-06-03 16:40:14 -0700702 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600703 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700704 '--chrome_root',
705 type='path',
706 help='Mount this chrome root into the SDK chroot')
707 parser.add_argument(
708 '--chrome_root_mount',
709 type='path',
710 help='Mount chrome into this path inside SDK chroot')
711 parser.add_argument(
712 '--nousepkg',
713 action='store_true',
714 default=False,
715 help='Do not use binary packages when creating a chroot.')
716 parser.add_argument(
717 '-u',
718 '--url',
719 dest='sdk_url',
720 help='Use sdk tarball located at this url. Use file:// '
721 'for local files.')
722 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700723 '--sdk-version',
724 help=('Use this sdk version. For prebuilt, current is %r'
725 ', for bootstrapping it is %r.' % (sdk_latest_version,
726 bootstrap_latest_version)))
727 parser.add_argument(
728 '--goma_dir',
729 type='path',
730 help='Goma installed directory to mount into the chroot.')
731 parser.add_argument(
732 '--goma_client_json',
733 type='path',
734 help='Service account json file to use goma on bot. '
735 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800736
737 # Use type=str instead of type='path' to prevent the given path from being
738 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700739 parser.add_argument(
740 '--working-dir',
741 type=str,
742 help='Run the command in specific working directory in '
743 'chroot. If the given directory is a relative '
744 'path, this program will transfer the path to '
745 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800746
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400747 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500748
749 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400750 group = parser.add_argument_group('Commands')
751 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700752 '--enter',
753 action='store_true',
754 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500755 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400756 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700757 '--create',
758 action='store_true',
759 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500760 help='Create the chroot only if it does not already exist. '
761 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400762 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700763 '--bootstrap',
764 action='store_true',
765 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500766 help='Build everything from scratch, including the sdk. '
767 'Use this only if you need to validate a change '
768 'that affects SDK creation itself (toolchain and '
769 'build are typically the only folk who need this). '
770 'Note this will quite heavily slow down the build. '
771 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400772 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700773 '-r',
774 '--replace',
775 action='store_true',
776 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500777 help='Replace an existing SDK chroot. Basically an alias '
778 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400779 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700780 '--delete',
781 action='store_true',
782 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500783 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400784 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700785 '--unmount',
786 action='store_true',
787 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600788 help='Unmount and clean up devices associated with the '
789 'SDK chroot if it exists. This does not delete the '
790 'backing image file, so the same chroot can be later '
791 're-mounted for reuse. To fully delete the chroot, use '
792 '--delete. This is primarily useful for working on '
793 'cros_sdk or the chroot setup; you should not need it '
794 'under normal circumstances.')
795 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700796 '--download',
797 action='store_true',
798 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500799 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600800 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700801 '--snapshot-create',
802 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600803 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700804 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600805 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700806 '--snapshot-restore',
807 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600808 help='Restore the chroot to a previously created snapshot.')
809 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700810 '--snapshot-delete',
811 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600812 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700813 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600814 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700815 '--snapshot-list',
816 action='store_true',
817 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600818 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500819 commands = group
820
Mike Frysinger80dfce92014-04-21 10:58:53 -0400821 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400822 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700823 group.add_argument(
824 '--proxy-sim',
825 action='store_true',
826 default=False,
827 help='Simulate a restrictive network requiring an outbound'
828 ' proxy.')
829 group.add_argument(
830 '--no-ns-pid',
831 dest='ns_pid',
832 default=True,
833 action='store_false',
834 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400835
Mike Frysinger34db8692013-11-11 14:54:08 -0500836 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400837 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500838 'Internal Chromium OS Build Team Options',
839 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700840 group.add_argument(
841 '--buildbot-log-version',
842 default=False,
843 action='store_true',
844 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500845
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400846 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500847
848
849def main(argv):
850 conf = cros_build_lib.LoadKeyValueFile(
851 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
852 ignore_missing=True)
853 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700854 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700855
856 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
857 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
858 bootstrap_latest_version = (
859 sdk_latest_version
860 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500861 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400862 options = parser.parse_args(argv)
863 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800864
865 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500866 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800867
Brian Harring1790ac42012-09-23 08:53:33 -0700868 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700869 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600870 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700871 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400872 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700873
Josh Triplett472a4182013-03-08 11:48:57 -0800874 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
875 if options.proxy_sim:
876 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600877 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800878
Benjamin Gordon040a1162017-06-29 13:44:47 -0600879 if (sdk_latest_version == '<unknown>' or
880 bootstrap_latest_version == '<unknown>'):
881 cros_build_lib.Die(
882 'No SDK version was found. '
883 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
884 'Please change to a directory inside your Chromium OS source tree\n'
885 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400886 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600887
Manoj Guptab12f7302019-06-03 16:40:14 -0700888 any_snapshot_operation = (
889 options.snapshot_create or options.snapshot_restore or
890 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600891 if any_snapshot_operation and not options.use_image:
892 cros_build_lib.Die('Snapshot operations are not compatible with '
893 '--nouse-image.')
894
Manoj Guptab12f7302019-06-03 16:40:14 -0700895 if (options.snapshot_delete and
896 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600897 parser.error('Cannot --snapshot_delete the same snapshot you are '
898 'restoring with --snapshot_restore.')
899
David James471532c2013-01-21 10:23:31 -0800900 _ReExecuteIfNeeded([sys.argv[0]] + argv)
901
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600902 lock_path = os.path.dirname(options.chroot)
903 lock_path = os.path.join(
904 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
905
Brian Harring218e13c2012-10-10 16:21:26 -0700906 # Expand out the aliases...
907 if options.replace:
908 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800909
Brian Harring218e13c2012-10-10 16:21:26 -0700910 if options.bootstrap:
911 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800912
Brian Harring218e13c2012-10-10 16:21:26 -0700913 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400914 # pylint: disable=protected-access
915 # This _group_actions access sucks, but upstream decided to not include an
916 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700917 options.enter |= not any(
918 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400919 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700920 options.enter |= bool(chroot_command)
921
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600922 if (options.delete and not options.create and
923 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400924 parser.error('Trying to enter or snapshot the chroot when --delete '
925 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700926
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600927 if (options.unmount and
928 (options.create or options.enter or any_snapshot_operation)):
929 parser.error('--unmount cannot be specified with other chroot actions.')
930
Yong Hong84ba9172018-02-07 01:37:42 +0800931 if options.working_dir is not None and not os.path.isabs(options.working_dir):
932 options.working_dir = path_util.ToChrootPath(options.working_dir)
933
Benjamin Gordon35194f12017-07-19 10:26:22 -0600934 # Discern if we need to create the chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600935 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600936 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600937 not options.unmount and not missing_image_tools and
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600938 os.path.exists(_ImageFileForChroot(options.chroot))):
939 # Try to re-mount an existing image in case the user has rebooted.
940 with cgroups.SimpleContainChildren('cros_sdk'):
941 with locking.FileLock(lock_path, 'chroot lock') as lock:
942 logging.debug('Checking if existing chroot image can be mounted.')
943 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -0600944 cros_sdk_lib.MountChroot(options.chroot, create=False)
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600945 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600946 if chroot_exists:
947 logging.notice('Mounted existing image %s on chroot',
948 _ImageFileForChroot(options.chroot))
Brian Harring218e13c2012-10-10 16:21:26 -0700949
950 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600951 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700952 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800953
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600954 # Make sure we will download if we plan to create.
955 options.download |= options.create
956
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600957 # Anything that needs to manipulate the main chroot mount or communicate with
958 # LVM needs to be done here before we enter the new namespaces.
959
960 # If deleting, do it regardless of the use_image flag so that a
961 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600962 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
963 # favor of this block.
964 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600965 if options.delete:
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600966 with cgroups.SimpleContainChildren('cros_sdk'):
967 with locking.FileLock(lock_path, 'chroot lock') as lock:
968 lock.write_lock()
969 if missing_image_tools:
970 logging.notice('Unmounting chroot.')
971 osutils.UmountTree(options.chroot)
972 else:
973 logging.notice('Deleting chroot.')
Don Garrett36650112018-06-28 15:54:34 -0700974 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600975 chroot_deleted = True
976
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600977 # If cleanup was requested, we have to do it while we're still in the original
978 # namespace. Since cleaning up the mount will interfere with any other
979 # commands, we exit here. The check above should have made sure that no other
980 # action was requested, anyway.
981 if options.unmount:
982 with locking.FileLock(lock_path, 'chroot lock') as lock:
983 lock.write_lock()
984 CleanupChroot(options.chroot)
985 sys.exit(0)
986
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600987 # Make sure the main chroot mount is visible. Contents will be filled in
988 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600989 if options.create and options.use_image:
990 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -0400991 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600992Please make sure the lvm2 and thin-provisioning-tools packages
993are installed on your host.
994Example(ubuntu):
995 sudo apt-get install lvm2 thin-provisioning-tools
996
997If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -0400998snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600999
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001000 logging.debug('Making sure chroot image is mounted.')
1001 with cgroups.SimpleContainChildren('cros_sdk'):
1002 with locking.FileLock(lock_path, 'chroot lock') as lock:
1003 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -06001004 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001005 cros_build_lib.Die('Unable to mount %s on chroot',
1006 _ImageFileForChroot(options.chroot))
1007 logging.notice('Mounted %s on chroot',
1008 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001009
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001010 # Snapshot operations will always need the VG/LV, but other actions won't.
1011 if any_snapshot_operation:
1012 with cgroups.SimpleContainChildren('cros_sdk'):
1013 with locking.FileLock(lock_path, 'chroot lock') as lock:
Benjamin Gordon74645232018-05-04 17:40:42 -06001014 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001015 options.chroot)
1016 if not chroot_vg or not chroot_lv:
1017 cros_build_lib.Die('Unable to find VG/LV for chroot %s',
1018 options.chroot)
1019
1020 # Delete snapshot before creating a new one. This allows the user to
1021 # throw out old state, create a new snapshot, and enter the chroot in a
1022 # single call to cros_sdk. Since restore involves deleting, also do it
1023 # before creating.
1024 if options.snapshot_restore:
1025 lock.write_lock()
1026 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1027 if options.snapshot_restore not in valid_snapshots:
Manoj Guptab12f7302019-06-03 16:40:14 -07001028 cros_build_lib.Die(
1029 '%s is not a valid snapshot to restore to. '
1030 'Valid snapshots: %s', options.snapshot_restore,
1031 ', '.join(valid_snapshots))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001032 osutils.UmountTree(options.chroot)
1033 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1034 chroot_lv):
1035 cros_build_lib.Die('Unable to restore chroot to snapshot.')
Benjamin Gordon74645232018-05-04 17:40:42 -06001036 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001037 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
1038
1039 # Use a read lock for snapshot delete and create even though they modify
1040 # the filesystem, because they don't modify the mounted chroot itself.
1041 # The underlying LVM commands take their own locks, so conflicting
1042 # concurrent operations here may crash cros_sdk, but won't corrupt the
1043 # chroot image. This tradeoff seems worth it to allow snapshot
1044 # operations on chroots that have a process inside.
1045 if options.snapshot_delete:
1046 lock.read_lock()
1047 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
1048
1049 if options.snapshot_create:
1050 lock.read_lock()
1051 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1052 chroot_lv):
1053 cros_build_lib.Die('Unable to create snapshot.')
1054
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001055 img_path = _ImageFileForChroot(options.chroot)
1056 if (options.use_image and os.path.exists(options.chroot) and
1057 os.path.exists(img_path)):
1058 img_stat = os.stat(img_path)
1059 img_used_bytes = img_stat.st_blocks * 512
1060
1061 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001062 mount_used_bytes = mount_stat.f_frsize * (
1063 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001064
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001065 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001066 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1067 logging.notice('%s is using %s GiB more than needed. Running '
1068 'fstrim.', img_path, extra_gbs)
1069 cmd = ['fstrim', options.chroot]
1070 try:
1071 cros_build_lib.RunCommand(cmd, print_cmd=False)
1072 except cros_build_lib.RunCommandError as e:
Manoj Guptab12f7302019-06-03 16:40:14 -07001073 logging.warning(
1074 'Running fstrim failed. Consider running fstrim on '
1075 'your chroot manually.\nError: %s', e)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001076
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001077 # Enter a new set of namespaces. Everything after here cannot directly affect
1078 # the hosts's mounts or alter LVM volumes.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001079 namespaces.SimpleUnshare()
1080 if options.ns_pid:
1081 first_pid = namespaces.CreatePidNs()
1082 else:
1083 first_pid = None
1084
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001085 if options.snapshot_list:
1086 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1087 print(snap)
1088 sys.exit(0)
1089
Brian Harringb938c782012-02-29 15:14:38 -08001090 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001091 sdk_version = (
1092 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001093 else:
1094 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001095 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001096 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001097
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001098 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001099 if options.download:
1100 if options.sdk_url:
1101 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001102 else:
1103 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001104
Mike Frysinger80dfce92014-04-21 10:58:53 -04001105 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -07001106 with locking.FileLock(lock_path, 'chroot lock') as lock:
Josh Triplett472a4182013-03-08 11:48:57 -08001107 if options.proxy_sim:
1108 _ProxySimSetup(options)
1109
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001110 if (options.delete and not chroot_deleted and
1111 (os.path.exists(options.chroot) or
1112 os.path.exists(_ImageFileForChroot(options.chroot)))):
David James56e6c2c2012-10-24 23:54:41 -07001113 lock.write_lock()
1114 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001115
David James56e6c2c2012-10-24 23:54:41 -07001116 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1117 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -07001118 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001119
David James56e6c2c2012-10-24 23:54:41 -07001120 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -05001121 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -07001122 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001123 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001124 continue
1125 lock.write_lock(
Mike Frysinger80de5012019-08-01 14:10:53 -04001126 'Upgrade to %r needed but chroot is locked; please exit '
1127 'all instances so this upgrade can finish.' % src)
David James56e6c2c2012-10-24 23:54:41 -07001128 if not os.path.exists(src):
1129 # Note that while waiting for the write lock, src may've vanished;
1130 # it's a rare race during the upgrade process that's a byproduct
1131 # of us avoiding taking a write lock to do the src check. If we
1132 # took a write lock for that check, it would effectively limit
1133 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001134 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001135 elif not os.path.exists(target):
1136 # Upgrade occurred, but a reversion, or something whacky
1137 # occurred writing to the old location. Wipe and continue.
1138 os.rename(src, target)
1139 else:
1140 # Upgrade occurred once already, but either a reversion or
1141 # some before/after separate cros_sdk usage is at play.
1142 # Wipe and continue.
1143 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001144
David James56e6c2c2012-10-24 23:54:41 -07001145 if options.download:
1146 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -07001147 sdk_tarball = FetchRemoteTarballs(
1148 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001149
David James56e6c2c2012-10-24 23:54:41 -07001150 if options.create:
1151 lock.write_lock()
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001152 # Recheck if the chroot is set up here before creating to make sure we
1153 # account for whatever the various delete/unmount/remount steps above
1154 # have done.
1155 if cros_sdk_lib.IsChrootReady(options.chroot):
1156 logging.debug('Chroot already exists. Skipping creation.')
1157 else:
Manoj Guptab12f7302019-06-03 16:40:14 -07001158 CreateChroot(
1159 options.chroot,
1160 sdk_tarball,
1161 options.cache_dir,
1162 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001163
David James56e6c2c2012-10-24 23:54:41 -07001164 if options.enter:
1165 lock.read_lock()
1166 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -05001167 options.chrome_root_mount, options.goma_dir,
1168 options.goma_client_json, options.working_dir,
1169 chroot_command)