blob: 3234547badb2a87e161e025cf16265b9542e2356 [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.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -04005"""Manage SDK chroots.
6
7This script is used for manipulating local chroot environments; creating,
8deleting, downloading, etc. If given --enter (or no args), it defaults
9to an interactive bash shell within the chroot.
10
11If given args those are passed to the chroot environment, and executed.
12"""
Brian Harringb938c782012-02-29 15:14:38 -080013
Mike Frysinger383367e2014-09-16 15:06:17 -040014from __future__ import print_function
15
Mike Frysinger2f95cfc2015-06-04 04:00:26 -040016import argparse
Josh Triplett472a4182013-03-08 11:48:57 -080017import glob
Brian Harringb938c782012-02-29 15:14:38 -080018import os
Josh Triplett472a4182013-03-08 11:48:57 -080019import pwd
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060020import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070021import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070022import resource
David James56e6c2c2012-10-24 23:54:41 -070023import sys
Brian Harringb938c782012-02-29 15:14:38 -080024import urlparse
25
Aviv Keshetb7519e12016-10-04 00:50:00 -070026from chromite.lib import constants
Brian Harringcfe762a2012-02-29 13:03:53 -080027from chromite.lib import cgroups
Brian Harringb6cf9142012-09-01 20:43:17 -070028from chromite.lib import commandline
Brian Harringb938c782012-02-29 15:14:38 -080029from chromite.lib import cros_build_lib
Ralph Nathan59900422015-03-24 10:41:17 -070030from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060031from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080032from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080033from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070034from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080035from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040036from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080037from chromite.lib import retry_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050038from chromite.lib import toolchain
Brian Harringb938c782012-02-29 15:14:38 -080039
40cros_build_lib.STRICT_SUDO = True
41
Zdenek Behanaa52cea2012-05-30 01:31:11 +020042COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020043
Brian Harringb938c782012-02-29 15:14:38 -080044# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070045MAKE_CHROOT = [
46 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/make_chroot.sh')
47]
48ENTER_CHROOT = [
49 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
50]
Brian Harringb938c782012-02-29 15:14:38 -080051
Josh Triplett472a4182013-03-08 11:48:57 -080052# Proxy simulator configuration.
53PROXY_HOST_IP = '192.168.240.1'
54PROXY_PORT = 8080
55PROXY_GUEST_IP = '192.168.240.2'
56PROXY_NETMASK = 30
57PROXY_VETH_PREFIX = 'veth'
58PROXY_CONNECT_PORTS = (80, 443, 9418)
59PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
60PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
61PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070062 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080063PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
64
Josh Triplett9a495f62013-03-15 18:06:55 -070065# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080066NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080067
Josh Triplett472a4182013-03-08 11:48:57 -080068# Tools needed for --proxy-sim only.
69PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080070
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060071# Tools needed when use_image is true (the default).
72IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060073 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060074
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070075# As space is used inside the chroot, the empty space in chroot.img is
76# allocated. Deleting files inside the chroot doesn't automatically return the
77# used space to the OS. Over time, this tends to make the sparse chroot.img
78# less sparse even if the chroot contents don't currently need much space. We
79# can recover most of this unused space with fstrim, but that takes too much
80# time to run it every time. Instead, check the used space against the image
81# size after mounting the chroot and only call fstrim if it looks like we could
82# recover at least this many GiB.
83MAX_UNUSED_IMAGE_GBS = 20
84
Mike Frysingercc838832014-05-24 13:10:30 -040085
Brian Harring1790ac42012-09-23 08:53:33 -070086def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080087 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070088 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
89 return [
90 toolchain.GetSdkURL(
91 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
92 for compressor in COMPRESSION_PREFERENCE
93 ]
Brian Harring1790ac42012-09-23 08:53:33 -070094
95
96def GetStage3Urls(version):
Manoj Guptab12f7302019-06-03 16:40:14 -070097 return [
98 toolchain.GetSdkURL(suburl='stage3-amd64-%s.tar.%s' % (version, ext))
99 for ext in COMPRESSION_PREFERENCE
100 ]
Brian Harringb938c782012-02-29 15:14:38 -0800101
102
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700103def FetchRemoteTarballs(storage_dir, urls, desc, allow_none=False):
Mike Frysinger34db8692013-11-11 14:54:08 -0500104 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200105
106 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500107 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200108 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700109 desc: A string describing what tarball we're downloading (for logging).
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700110 allow_none: Don't fail if none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200111
112 Returns:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700113 Full path to the downloaded file, or None if |allow_none| and no URL worked.
114
115 Raises:
116 ValueError: If |allow_none| is False and none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200117 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200118
Brian Harring1790ac42012-09-23 08:53:33 -0700119 # Note we track content length ourselves since certain versions of curl
120 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700121 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700122 logging.notice('Downloading %s tarball...', desc)
Brian Norriscf8aef42016-09-27 10:43:39 -0700123 status_re = re.compile(r'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400124 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200125 for url in urls:
Brian Harring1790ac42012-09-23 08:53:33 -0700126 parsed = urlparse.urlparse(url)
127 tarball_name = os.path.basename(parsed.path)
128 if parsed.scheme in ('', 'file'):
129 if os.path.exists(parsed.path):
130 return parsed.path
131 continue
132 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700133 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700134 result = retry_util.RunCurl(['-I', url],
135 print_cmd=False,
136 debug_level=logging.NOTICE,
137 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700138 successful = False
139 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700140 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700141 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700142 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700143 successful = True
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500144 elif header.lower().startswith('content-length:'):
145 content_length = int(header.split(':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700146 if successful:
147 break
148 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200149 break
150 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700151 if allow_none:
152 return None
153 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200154
Brian Harringae0a5322012-09-15 01:46:51 -0700155 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700156 current_size = 0
157 if os.path.exists(tarball_dest):
158 current_size = os.path.getsize(tarball_dest)
159 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700160 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700161 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100162
Brian Harring1790ac42012-09-23 08:53:33 -0700163 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800164 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900165 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700166 print_cmd=False,
167 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800168
Brian Harring1790ac42012-09-23 08:53:33 -0700169 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700170 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
171 # because we might have partial overlap between known prefixes.
172 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
173 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
174 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700175 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700176 if (filename == tarball_name or
177 any([(filename.startswith(p) and
178 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
179 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700180 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700181 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700182 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200183
Brian Harringb938c782012-02-29 15:14:38 -0800184 return tarball_dest
185
186
Benjamin Gordon589873b2018-05-31 14:30:56 -0600187def CreateChroot(chroot_path, sdk_tarball, cache_dir, nousepkg=False):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600188 """Creates a new chroot from a given SDK.
189
190 Args:
191 chroot_path: Path where the new chroot will be created.
192 sdk_tarball: Path to a downloaded Gentoo Stage3 or Chromium OS SDK tarball.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600193 cache_dir: Path to a directory that will be used for caching portage files,
194 etc.
195 nousepkg: If True, pass --nousepkg to cros_setup_toolchains inside the
196 chroot.
197 """
Brian Harringb938c782012-02-29 15:14:38 -0800198
Manoj Guptab12f7302019-06-03 16:40:14 -0700199 cmd = MAKE_CHROOT + [
200 '--stage3_path', sdk_tarball, '--chroot', chroot_path, '--cache_dir',
201 cache_dir
202 ]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700203
Mike Frysinger2de7f042012-07-10 04:45:03 -0400204 if nousepkg:
205 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800206
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700207 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800208 try:
209 cros_build_lib.RunCommand(cmd, print_cmd=False)
210 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700211 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800212
213
214def DeleteChroot(chroot_path):
215 """Deletes an existing chroot"""
Manoj Guptab12f7302019-06-03 16:40:14 -0700216 cmd = MAKE_CHROOT + ['--chroot', chroot_path, '--delete']
Brian Harringb938c782012-02-29 15:14:38 -0800217 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700218 logging.notice('Deleting chroot.')
Brian Harringb938c782012-02-29 15:14:38 -0800219 cros_build_lib.RunCommand(cmd, print_cmd=False)
220 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700221 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800222
223
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600224def CleanupChroot(chroot_path):
225 """Unmounts a chroot and cleans up any associated devices."""
Don Garrett36650112018-06-28 15:54:34 -0700226 cros_sdk_lib.CleanupChrootMount(chroot_path, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600227
228
Brian Harringae0a5322012-09-15 01:46:51 -0700229def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500230 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800231 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400232 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
233 # The os.ST_NOSUID constant wasn't added until python-3.2.
234 if st.f_flag & 0x2:
235 cros_build_lib.Die('chroot cannot be in a nosuid mount')
236
Brian Harringae0a5322012-09-15 01:46:51 -0700237 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800238 if chrome_root:
239 cmd.extend(['--chrome_root', chrome_root])
240 if chrome_root_mount:
241 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900242 if goma_dir:
243 cmd.extend(['--goma_dir', goma_dir])
244 if goma_client_json:
245 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800246 if working_dir is not None:
247 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700248
Brian Harringb938c782012-02-29 15:14:38 -0800249 if len(additional_args) > 0:
250 cmd.append('--')
251 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700252
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700253 # ThinLTO opens lots of files at the same time.
254 resource.setrlimit(resource.RLIMIT_NOFILE, (32768, 32768))
Manoj Guptab12f7302019-06-03 16:40:14 -0700255 ret = cros_build_lib.RunCommand(
256 cmd, print_cmd=False, error_code_ok=True, mute_output=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700257 # If we were in interactive mode, ignore the exit code; it'll be whatever
258 # they last ran w/in the chroot and won't matter to us one way or another.
259 # Note this does allow chroot entrance to fail and be ignored during
260 # interactive; this is however a rare case and the user will immediately
261 # see it (nor will they be checking the exit code manually).
262 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700263 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800264
265
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600266def _ImageFileForChroot(chroot):
267 """Find the image file that should be associated with |chroot|.
268
269 This function does not check if the image exists; it simply returns the
270 filename that would be used.
271
272 Args:
273 chroot: Path to the chroot.
274
275 Returns:
276 Path to an image file that would be associated with chroot.
277 """
278 return chroot.rstrip('/') + '.img'
279
280
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600281def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
282 """Create a snapshot for the specified chroot VG/LV.
283
284 Args:
285 snapshot_name: The name of the new snapshot.
286 chroot_vg: The name of the VG containing the origin LV.
287 chroot_lv: The name of the origin LV.
288
289 Returns:
290 True if the snapshot was created, or False if a snapshot with the same
291 name already exists.
292
293 Raises:
294 SystemExit: The lvcreate command failed.
295 """
296 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700297 logging.error(
298 'Cannot create snapshot %s: A volume with that name already '
299 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600300 return False
301
Manoj Guptab12f7302019-06-03 16:40:14 -0700302 cmd = [
303 'lvcreate', '-s', '--name', snapshot_name,
304 '%s/%s' % (chroot_vg, chroot_lv)
305 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600306 try:
307 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
308 chroot_lv, chroot_vg)
309 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
310 return True
311 except cros_build_lib.RunCommandError:
312 raise SystemExit('Running %r failed!' % cmd)
313
314
315def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
316 """Delete the named snapshot from the specified chroot VG.
317
318 If the requested snapshot is not found, nothing happens. The main chroot LV
319 and internal thinpool LV cannot be deleted with this function.
320
321 Args:
322 snapshot_name: The name of the snapshot to delete.
323 chroot_vg: The name of the VG containing the origin LV.
324 chroot_lv: The name of the origin LV.
325
326 Raises:
327 SystemExit: The lvremove command failed.
328 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600329 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
330 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700331 logging.error(
332 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
333 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600334 return
335
336 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
337 return
338
339 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
340 try:
341 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
342 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
343 except cros_build_lib.RunCommandError:
344 raise SystemExit('Running %r failed!' % cmd)
345
346
347def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
348 """Restore the chroot to an existing snapshot.
349
350 This is done by renaming the original |chroot_lv| LV to a temporary name,
351 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
352 now unused LV. If an error occurs, attempts to rename the original snapshot
353 back to |chroot_lv| to leave the chroot unchanged.
354
355 The chroot must be unmounted before calling this function, and will be left
356 unmounted after this function returns.
357
358 Args:
359 snapshot_name: The name of the snapshot to restore. This snapshot will no
360 longer be accessible at its original name after this function finishes.
361 chroot_vg: The VG containing the chroot LV and snapshot LV.
362 chroot_lv: The name of the original chroot LV.
363
364 Returns:
365 True if the chroot was restored to the requested snapshot, or False if
366 the snapshot wasn't found or isn't valid.
367
368 Raises:
369 SystemExit: Any of the LVM commands failed.
370 """
371 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600372 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
373 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600374 snapshot_name not in valid_snapshots):
375 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
376 snapshot_name, ', '.join(valid_snapshots))
377 return False
378
379 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
380 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
381 try:
382 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
383 except cros_build_lib.RunCommandError:
384 raise SystemExit('Running %r failed!' % cmd)
385
386 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
387 try:
388 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
389 except cros_build_lib.RunCommandError:
390 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
391 try:
392 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
393 except cros_build_lib.RunCommandError:
394 raise SystemExit('Failed to rename %s to chroot and failed to restore '
395 '%s back to chroot. Failed command: %r' %
396 (snapshot_name, backup_chroot_name, cmd))
Manoj Guptab12f7302019-06-03 16:40:14 -0700397 raise SystemExit(
398 'Failed to rename %s to chroot. Original chroot LV has '
399 'been restored. Failed command: %r' % (snapshot_name, cmd))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600400
401 # Some versions of LVM set snapshots to be skipped at auto-activate time.
402 # Other versions don't have this flag at all. We run lvchange to try
403 # disabling auto-skip and activating the volume, but ignore errors. Versions
404 # that don't have the flag should be auto-activated.
405 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
406 cmd = ['lvchange', '-kn', chroot_lv_path]
Manoj Guptab12f7302019-06-03 16:40:14 -0700407 cros_build_lib.RunCommand(
408 cmd, print_cmd=False, capture_output=True, error_code_ok=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600409
410 # Activate the LV in case the lvchange above was needed. Activating an LV
411 # that is already active shouldn't do anything, so this is safe to run even if
412 # the -kn wasn't needed.
413 cmd = ['lvchange', '-ay', chroot_lv_path]
414 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
415
416 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
417 try:
418 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
419 except cros_build_lib.RunCommandError:
420 raise SystemExit('Failed to remove backup LV %s/%s. Failed command: %r' %
421 (chroot_vg, backup_chroot_name, cmd))
422
423 return True
424
425
426def ListChrootSnapshots(chroot_vg, chroot_lv):
427 """Return all snapshots in |chroot_vg| regardless of origin volume.
428
429 Args:
430 chroot_vg: The name of the VG containing the chroot.
431 chroot_lv: The name of the chroot LV.
432
433 Returns:
434 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
435
436 Raises:
437 SystemExit: The lvs command failed.
438 """
439 if not chroot_vg or not chroot_lv:
440 return []
441
Manoj Guptab12f7302019-06-03 16:40:14 -0700442 cmd = [
443 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
444 '--separator', '\t', chroot_vg
445 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600446 try:
Manoj Guptab12f7302019-06-03 16:40:14 -0700447 result = cros_build_lib.RunCommand(
448 cmd, print_cmd=False, redirect_stdout=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600449 except cros_build_lib.RunCommandError:
450 raise SystemExit('Running %r failed!' % cmd)
451
452 # Once the thin origin volume has been deleted, there's no way to tell a
453 # snapshot apart from any other volume. Since this VG is created and managed
454 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
455 # valid snapshots.
456 snapshots = []
457 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
458 for line in result.output.splitlines():
459 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700460 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600461 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600462 not snapshot_attrs.match(lv_attr)):
463 continue
464 snapshots.append(lv_name)
465 return snapshots
466
467
David James56e6c2c2012-10-24 23:54:41 -0700468def _SudoCommand():
469 """Get the 'sudo' command, along with all needed environment variables."""
470
David James5a73b4d2013-03-07 10:23:40 -0800471 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
472 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700473 cmd = ['sudo']
David James5a73b4d2013-03-07 10:23:40 -0800474 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700475 value = os.environ.get(key)
476 if value is not None:
477 cmd += ['%s=%s' % (key, value)]
478
479 # Pass in the path to the depot_tools so that users can access them from
480 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400481 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500482
David James56e6c2c2012-10-24 23:54:41 -0700483 return cmd
484
485
Josh Triplett472a4182013-03-08 11:48:57 -0800486def _ReportMissing(missing):
487 """Report missing utilities, then exit.
488
489 Args:
490 missing: List of missing utilities, as returned by
491 osutils.FindMissingBinaries. If non-empty, will not return.
492 """
493
494 if missing:
495 raise SystemExit(
496 'The tool(s) %s were not found.\n'
497 'Please install the appropriate package in your host.\n'
498 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700499 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800500
501
502def _ProxySimSetup(options):
503 """Set up proxy simulator, and return only in the child environment.
504
505 TODO: Ideally, this should support multiple concurrent invocations of
506 cros_sdk --proxy-sim; currently, such invocations will conflict with each
507 other due to the veth device names and IP addresses. Either this code would
508 need to generate fresh, unused names for all of these before forking, or it
509 would need to support multiple concurrent cros_sdk invocations sharing one
510 proxy and allowing it to exit when unused (without counting on any local
511 service-management infrastructure on the host).
512 """
513
514 may_need_mpm = False
515 apache_bin = osutils.Which('apache2')
516 if apache_bin is None:
517 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
518 if apache_bin is None:
519 _ReportMissing(('apache2',))
520 else:
521 may_need_mpm = True
522
523 # Module names and .so names included for ease of grepping.
524 apache_modules = [('proxy_module', 'mod_proxy.so'),
525 ('proxy_connect_module', 'mod_proxy_connect.so'),
526 ('proxy_http_module', 'mod_proxy_http.so'),
527 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
528
529 # Find the apache module directory, and make sure it has the modules we need.
530 module_dirs = {}
531 for g in PROXY_APACHE_MODULE_GLOBS:
532 for mod, so in apache_modules:
533 for f in glob.glob(os.path.join(g, so)):
534 module_dirs.setdefault(os.path.dirname(f), []).append(so)
535 for apache_module_path, modules_found in module_dirs.iteritems():
536 if len(modules_found) == len(apache_modules):
537 break
538 else:
539 # Appease cros lint, which doesn't understand that this else block will not
540 # fall through to the subsequent code which relies on apache_module_path.
541 apache_module_path = None
542 raise SystemExit(
543 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500544 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800545
546 def check_add_module(name):
547 so = 'mod_%s.so' % name
548 if os.access(os.path.join(apache_module_path, so), os.F_OK):
549 mod = '%s_module' % name
550 apache_modules.append((mod, so))
551 return True
552 return False
553
554 check_add_module('authz_core')
555 if may_need_mpm:
556 for mpm in PROXY_APACHE_MPMS:
557 if check_add_module('mpm_%s' % mpm):
558 break
559
560 veth_host = '%s-host' % PROXY_VETH_PREFIX
561 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
562
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500563 # Set up locks to sync the net namespace setup. We need the child to create
564 # the net ns first, and then have the parent assign the guest end of the veth
565 # interface to the child's new network namespace & bring up the proxy. Only
566 # then can the child move forward and rely on the network being up.
567 ns_create_lock = locking.PipeLock()
568 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800569
570 pid = os.fork()
571 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500572 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800573 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500574
575 # Signal the parent the ns is ready to be configured.
576 ns_create_lock.Post()
577 del ns_create_lock
578
579 # Wait for the parent to finish setting up the ns/proxy.
580 ns_setup_lock.Wait()
581 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800582
583 # Set up child side of the network.
584 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500585 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700586 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500587 'dev', veth_guest),
588 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800589 )
590 try:
591 for cmd in commands:
592 cros_build_lib.RunCommand(cmd, print_cmd=False)
593 except cros_build_lib.RunCommandError:
594 raise SystemExit('Running %r failed!' % (cmd,))
595
596 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
597 for proto in ('http', 'https', 'ftp'):
598 os.environ[proto + '_proxy'] = proxy_url
599 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
600 os.environ.pop(v, None)
601 return
602
Josh Triplett472a4182013-03-08 11:48:57 -0800603 # Set up parent side of the network.
604 uid = int(os.environ.get('SUDO_UID', '0'))
605 gid = int(os.environ.get('SUDO_GID', '0'))
606 if uid == 0 or gid == 0:
607 for username in PROXY_APACHE_FALLBACK_USERS:
608 try:
609 pwnam = pwd.getpwnam(username)
610 uid, gid = pwnam.pw_uid, pwnam.pw_gid
611 break
612 except KeyError:
613 continue
614 if uid == 0 or gid == 0:
615 raise SystemExit('Could not find a non-root user to run Apache as')
616
617 chroot_parent, chroot_base = os.path.split(options.chroot)
618 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
619 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
620
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500621 # Wait for the child to create the net ns.
622 ns_create_lock.Wait()
623 del ns_create_lock
624
Josh Triplett472a4182013-03-08 11:48:57 -0800625 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500626 'User #%u' % uid,
627 'Group #%u' % gid,
628 'PidFile %s' % pid_file,
629 'ErrorLog %s' % log_file,
630 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
631 'ServerName %s' % PROXY_HOST_IP,
632 'ProxyRequests On',
633 'AllowCONNECT %s' % ' '.join(map(str, PROXY_CONNECT_PORTS)),
Josh Triplett472a4182013-03-08 11:48:57 -0800634 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500635 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
636 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800637 ]
638 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700639 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
640 veth_guest),
641 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
642 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500643 ('ip', 'link', 'set', veth_host, 'up'),
644 ([apache_bin, '-f', '/dev/null'] +
645 [arg for d in apache_directives for arg in ('-C', d)]),
646 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800647 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700648 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800649 try:
650 for cmd in commands:
651 cros_build_lib.RunCommand(cmd, print_cmd=False)
652 except cros_build_lib.RunCommandError:
653 # Clean up existing interfaces, if any.
654 cmd_cleanup = ('ip', 'link', 'del', veth_host)
655 try:
656 cros_build_lib.RunCommand(cmd_cleanup, print_cmd=False)
657 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700658 logging.error('running %r failed', cmd_cleanup)
Josh Triplett472a4182013-03-08 11:48:57 -0800659 raise SystemExit('Running %r failed!' % (cmd,))
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500660
661 # Signal the child that the net ns/proxy is fully configured now.
662 ns_setup_lock.Post()
663 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800664
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400665 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800666
667
Mike Frysingera78a56e2012-11-20 06:02:30 -0500668def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700669 """Re-execute cros_sdk as root.
670
671 Also unshare the mount namespace so as to ensure that processes outside
672 the chroot can't mess with our mounts.
673 """
674 if os.geteuid() != 0:
Mike Frysingera78a56e2012-11-20 06:02:30 -0500675 cmd = _SudoCommand() + ['--'] + argv
676 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500677 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400678 # We must set up the cgroups mounts before we enter our own namespace.
679 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800680 cgroups.Cgroup.InitSystem()
David James56e6c2c2012-10-24 23:54:41 -0700681
682
Mike Frysinger34db8692013-11-11 14:54:08 -0500683def _CreateParser(sdk_latest_version, bootstrap_latest_version):
684 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400685 usage = ('usage: %(prog)s [options] '
686 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700687 parser = commandline.ArgumentParser(
688 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700689
Mike Frysinger34db8692013-11-11 14:54:08 -0500690 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500691 default_chroot = os.path.join(constants.SOURCE_ROOT,
692 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400693 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700694 '--chroot',
695 dest='chroot',
696 default=default_chroot,
697 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700698 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700699 parser.add_argument(
700 '--nouse-image',
701 dest='use_image',
702 action='store_false',
703 default=True,
704 help='Do not mount the chroot on a loopback image; '
705 'instead, create it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800706
Manoj Guptab12f7302019-06-03 16:40:14 -0700707 parser.add_argument(
708 '--chrome_root',
709 type='path',
710 help='Mount this chrome root into the SDK chroot')
711 parser.add_argument(
712 '--chrome_root_mount',
713 type='path',
714 help='Mount chrome into this path inside SDK chroot')
715 parser.add_argument(
716 '--nousepkg',
717 action='store_true',
718 default=False,
719 help='Do not use binary packages when creating a chroot.')
720 parser.add_argument(
721 '-u',
722 '--url',
723 dest='sdk_url',
724 help='Use sdk tarball located at this url. Use file:// '
725 'for local files.')
726 parser.add_argument(
727 '--self-bootstrap',
728 dest='self_bootstrap',
729 action='store_true',
730 default=False,
731 help=('Use previously build sdk for bootstrapping.'))
732 parser.add_argument(
733 '--sdk-version',
734 help=('Use this sdk version. For prebuilt, current is %r'
735 ', for bootstrapping it is %r.' % (sdk_latest_version,
736 bootstrap_latest_version)))
737 parser.add_argument(
738 '--goma_dir',
739 type='path',
740 help='Goma installed directory to mount into the chroot.')
741 parser.add_argument(
742 '--goma_client_json',
743 type='path',
744 help='Service account json file to use goma on bot. '
745 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800746
747 # Use type=str instead of type='path' to prevent the given path from being
748 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700749 parser.add_argument(
750 '--working-dir',
751 type=str,
752 help='Run the command in specific working directory in '
753 'chroot. If the given directory is a relative '
754 'path, this program will transfer the path to '
755 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800756
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400757 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500758
759 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400760 group = parser.add_argument_group('Commands')
761 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700762 '--enter',
763 action='store_true',
764 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500765 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400766 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700767 '--create',
768 action='store_true',
769 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500770 help='Create the chroot only if it does not already exist. '
771 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400772 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700773 '--bootstrap',
774 action='store_true',
775 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500776 help='Build everything from scratch, including the sdk. '
777 'Use this only if you need to validate a change '
778 'that affects SDK creation itself (toolchain and '
779 'build are typically the only folk who need this). '
780 'Note this will quite heavily slow down the build. '
781 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400782 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700783 '-r',
784 '--replace',
785 action='store_true',
786 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500787 help='Replace an existing SDK chroot. Basically an alias '
788 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400789 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700790 '--delete',
791 action='store_true',
792 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500793 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400794 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700795 '--unmount',
796 action='store_true',
797 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600798 help='Unmount and clean up devices associated with the '
799 'SDK chroot if it exists. This does not delete the '
800 'backing image file, so the same chroot can be later '
801 're-mounted for reuse. To fully delete the chroot, use '
802 '--delete. This is primarily useful for working on '
803 'cros_sdk or the chroot setup; you should not need it '
804 'under normal circumstances.')
805 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700806 '--download',
807 action='store_true',
808 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500809 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600810 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700811 '--snapshot-create',
812 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600813 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700814 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600815 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700816 '--snapshot-restore',
817 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600818 help='Restore the chroot to a previously created snapshot.')
819 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700820 '--snapshot-delete',
821 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600822 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700823 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600824 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700825 '--snapshot-list',
826 action='store_true',
827 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600828 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500829 commands = group
830
Mike Frysinger80dfce92014-04-21 10:58:53 -0400831 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400832 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700833 group.add_argument(
834 '--proxy-sim',
835 action='store_true',
836 default=False,
837 help='Simulate a restrictive network requiring an outbound'
838 ' proxy.')
839 group.add_argument(
840 '--no-ns-pid',
841 dest='ns_pid',
842 default=True,
843 action='store_false',
844 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400845
Mike Frysinger34db8692013-11-11 14:54:08 -0500846 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400847 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500848 'Internal Chromium OS Build Team Options',
849 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700850 group.add_argument(
851 '--buildbot-log-version',
852 default=False,
853 action='store_true',
854 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500855
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400856 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500857
858
859def main(argv):
860 conf = cros_build_lib.LoadKeyValueFile(
861 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
862 ignore_missing=True)
863 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
864 bootstrap_latest_version = conf.get('BOOTSTRAP_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700865 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Mike Frysinger34db8692013-11-11 14:54:08 -0500866 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400867 options = parser.parse_args(argv)
868 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800869
870 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500871 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800872
Brian Harring1790ac42012-09-23 08:53:33 -0700873 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700874 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600875 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700876 "cros_sdk is currently only supported on x86_64; you're running"
877 " %s. Please find a x86_64 machine." % (host,))
878
Josh Triplett472a4182013-03-08 11:48:57 -0800879 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
880 if options.proxy_sim:
881 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600882 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800883
Manoj Gupta01927c12019-05-13 17:33:14 -0700884 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
885 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
Manoj Guptaa0cf99f2019-04-15 20:14:35 -0700886 if options.self_bootstrap:
Manoj Gupta01927c12019-05-13 17:33:14 -0700887 bootstrap_latest_version = (
888 sdk_latest_version if bootstrap_frozen_version == '<unknown>' else
889 bootstrap_frozen_version)
Manoj Guptaa0cf99f2019-04-15 20:14:35 -0700890
Benjamin Gordon040a1162017-06-29 13:44:47 -0600891 if (sdk_latest_version == '<unknown>' or
892 bootstrap_latest_version == '<unknown>'):
893 cros_build_lib.Die(
894 'No SDK version was found. '
895 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
896 'Please change to a directory inside your Chromium OS source tree\n'
897 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400898 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600899
Manoj Guptab12f7302019-06-03 16:40:14 -0700900 any_snapshot_operation = (
901 options.snapshot_create or options.snapshot_restore or
902 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600903 if any_snapshot_operation and not options.use_image:
904 cros_build_lib.Die('Snapshot operations are not compatible with '
905 '--nouse-image.')
906
Manoj Guptab12f7302019-06-03 16:40:14 -0700907 if (options.snapshot_delete and
908 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600909 parser.error('Cannot --snapshot_delete the same snapshot you are '
910 'restoring with --snapshot_restore.')
911
David James471532c2013-01-21 10:23:31 -0800912 _ReExecuteIfNeeded([sys.argv[0]] + argv)
913
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600914 lock_path = os.path.dirname(options.chroot)
915 lock_path = os.path.join(
916 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
917
Brian Harring218e13c2012-10-10 16:21:26 -0700918 # Expand out the aliases...
919 if options.replace:
920 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800921
Brian Harring218e13c2012-10-10 16:21:26 -0700922 if options.bootstrap:
923 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800924
Brian Harring218e13c2012-10-10 16:21:26 -0700925 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400926 # pylint: disable=protected-access
927 # This _group_actions access sucks, but upstream decided to not include an
928 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700929 options.enter |= not any(
930 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400931 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700932 options.enter |= bool(chroot_command)
933
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600934 if (options.delete and not options.create and
935 (options.enter or any_snapshot_operation)):
936 parser.error("Trying to enter or snapshot the chroot when --delete "
Brian Harring218e13c2012-10-10 16:21:26 -0700937 "was specified makes no sense.")
938
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600939 if (options.unmount and
940 (options.create or options.enter or any_snapshot_operation)):
941 parser.error('--unmount cannot be specified with other chroot actions.')
942
Yong Hong84ba9172018-02-07 01:37:42 +0800943 if options.working_dir is not None and not os.path.isabs(options.working_dir):
944 options.working_dir = path_util.ToChrootPath(options.working_dir)
945
Benjamin Gordon35194f12017-07-19 10:26:22 -0600946 # Discern if we need to create the chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600947 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600948 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600949 not options.unmount and not missing_image_tools and
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600950 os.path.exists(_ImageFileForChroot(options.chroot))):
951 # Try to re-mount an existing image in case the user has rebooted.
952 with cgroups.SimpleContainChildren('cros_sdk'):
953 with locking.FileLock(lock_path, 'chroot lock') as lock:
954 logging.debug('Checking if existing chroot image can be mounted.')
955 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -0600956 cros_sdk_lib.MountChroot(options.chroot, create=False)
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600957 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600958 if chroot_exists:
959 logging.notice('Mounted existing image %s on chroot',
960 _ImageFileForChroot(options.chroot))
Brian Harring218e13c2012-10-10 16:21:26 -0700961
962 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600963 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700964 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800965
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600966 # Make sure we will download if we plan to create.
967 options.download |= options.create
968
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600969 # Anything that needs to manipulate the main chroot mount or communicate with
970 # LVM needs to be done here before we enter the new namespaces.
971
972 # If deleting, do it regardless of the use_image flag so that a
973 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600974 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
975 # favor of this block.
976 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600977 if options.delete:
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600978 with cgroups.SimpleContainChildren('cros_sdk'):
979 with locking.FileLock(lock_path, 'chroot lock') as lock:
980 lock.write_lock()
981 if missing_image_tools:
982 logging.notice('Unmounting chroot.')
983 osutils.UmountTree(options.chroot)
984 else:
985 logging.notice('Deleting chroot.')
Don Garrett36650112018-06-28 15:54:34 -0700986 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600987 chroot_deleted = True
988
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600989 # If cleanup was requested, we have to do it while we're still in the original
990 # namespace. Since cleaning up the mount will interfere with any other
991 # commands, we exit here. The check above should have made sure that no other
992 # action was requested, anyway.
993 if options.unmount:
994 with locking.FileLock(lock_path, 'chroot lock') as lock:
995 lock.write_lock()
996 CleanupChroot(options.chroot)
997 sys.exit(0)
998
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600999 # Make sure the main chroot mount is visible. Contents will be filled in
1000 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001001 if options.create and options.use_image:
1002 if missing_image_tools:
Manoj Guptab12f7302019-06-03 16:40:14 -07001003 raise SystemExit('''The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001004Please make sure the lvm2 and thin-provisioning-tools packages
1005are installed on your host.
1006Example(ubuntu):
1007 sudo apt-get install lvm2 thin-provisioning-tools
1008
1009If you want to run without lvm2, pass --nouse-image (chroot
1010snapshots will be unavailable).''' % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001011
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001012 logging.debug('Making sure chroot image is mounted.')
1013 with cgroups.SimpleContainChildren('cros_sdk'):
1014 with locking.FileLock(lock_path, 'chroot lock') as lock:
1015 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -06001016 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001017 cros_build_lib.Die('Unable to mount %s on chroot',
1018 _ImageFileForChroot(options.chroot))
1019 logging.notice('Mounted %s on chroot',
1020 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001021
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001022 # Snapshot operations will always need the VG/LV, but other actions won't.
1023 if any_snapshot_operation:
1024 with cgroups.SimpleContainChildren('cros_sdk'):
1025 with locking.FileLock(lock_path, 'chroot lock') as lock:
Benjamin Gordon74645232018-05-04 17:40:42 -06001026 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001027 options.chroot)
1028 if not chroot_vg or not chroot_lv:
1029 cros_build_lib.Die('Unable to find VG/LV for chroot %s',
1030 options.chroot)
1031
1032 # Delete snapshot before creating a new one. This allows the user to
1033 # throw out old state, create a new snapshot, and enter the chroot in a
1034 # single call to cros_sdk. Since restore involves deleting, also do it
1035 # before creating.
1036 if options.snapshot_restore:
1037 lock.write_lock()
1038 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1039 if options.snapshot_restore not in valid_snapshots:
Manoj Guptab12f7302019-06-03 16:40:14 -07001040 cros_build_lib.Die(
1041 '%s is not a valid snapshot to restore to. '
1042 'Valid snapshots: %s', options.snapshot_restore,
1043 ', '.join(valid_snapshots))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001044 osutils.UmountTree(options.chroot)
1045 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1046 chroot_lv):
1047 cros_build_lib.Die('Unable to restore chroot to snapshot.')
Benjamin Gordon74645232018-05-04 17:40:42 -06001048 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001049 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
1050
1051 # Use a read lock for snapshot delete and create even though they modify
1052 # the filesystem, because they don't modify the mounted chroot itself.
1053 # The underlying LVM commands take their own locks, so conflicting
1054 # concurrent operations here may crash cros_sdk, but won't corrupt the
1055 # chroot image. This tradeoff seems worth it to allow snapshot
1056 # operations on chroots that have a process inside.
1057 if options.snapshot_delete:
1058 lock.read_lock()
1059 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
1060
1061 if options.snapshot_create:
1062 lock.read_lock()
1063 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1064 chroot_lv):
1065 cros_build_lib.Die('Unable to create snapshot.')
1066
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001067 img_path = _ImageFileForChroot(options.chroot)
1068 if (options.use_image and os.path.exists(options.chroot) and
1069 os.path.exists(img_path)):
1070 img_stat = os.stat(img_path)
1071 img_used_bytes = img_stat.st_blocks * 512
1072
1073 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001074 mount_used_bytes = mount_stat.f_frsize * (
1075 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001076
1077 extra_gbs = (img_used_bytes - mount_used_bytes) / 2**30
1078 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1079 logging.notice('%s is using %s GiB more than needed. Running '
1080 'fstrim.', img_path, extra_gbs)
1081 cmd = ['fstrim', options.chroot]
1082 try:
1083 cros_build_lib.RunCommand(cmd, print_cmd=False)
1084 except cros_build_lib.RunCommandError as e:
Manoj Guptab12f7302019-06-03 16:40:14 -07001085 logging.warning(
1086 'Running fstrim failed. Consider running fstrim on '
1087 'your chroot manually.\nError: %s', e)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001088
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001089 # Enter a new set of namespaces. Everything after here cannot directly affect
1090 # the hosts's mounts or alter LVM volumes.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001091 namespaces.SimpleUnshare()
1092 if options.ns_pid:
1093 first_pid = namespaces.CreatePidNs()
1094 else:
1095 first_pid = None
1096
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001097 if options.snapshot_list:
1098 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1099 print(snap)
1100 sys.exit(0)
1101
Brian Harringb938c782012-02-29 15:14:38 -08001102 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001103 sdk_version = (
1104 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001105 else:
1106 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001107 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001108 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001109
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001110 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001111 if options.download:
1112 if options.sdk_url:
1113 urls = [options.sdk_url]
1114 elif options.bootstrap:
1115 urls = GetStage3Urls(sdk_version)
Manoj Guptaa0cf99f2019-04-15 20:14:35 -07001116 if options.self_bootstrap:
1117 urls = GetArchStageTarballs(sdk_version)
Yong Hong4e29b622018-02-05 14:31:10 +08001118 else:
1119 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001120
Mike Frysinger80dfce92014-04-21 10:58:53 -04001121 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -07001122 with locking.FileLock(lock_path, 'chroot lock') as lock:
Josh Triplett472a4182013-03-08 11:48:57 -08001123 if options.proxy_sim:
1124 _ProxySimSetup(options)
1125
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001126 if (options.delete and not chroot_deleted and
1127 (os.path.exists(options.chroot) or
1128 os.path.exists(_ImageFileForChroot(options.chroot)))):
David James56e6c2c2012-10-24 23:54:41 -07001129 lock.write_lock()
1130 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001131
David James56e6c2c2012-10-24 23:54:41 -07001132 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1133 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -07001134 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001135
David James56e6c2c2012-10-24 23:54:41 -07001136 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -05001137 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -07001138 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001139 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001140 continue
1141 lock.write_lock(
1142 "Upgrade to %r needed but chroot is locked; please exit "
1143 "all instances so this upgrade can finish." % src)
1144 if not os.path.exists(src):
1145 # Note that while waiting for the write lock, src may've vanished;
1146 # it's a rare race during the upgrade process that's a byproduct
1147 # of us avoiding taking a write lock to do the src check. If we
1148 # took a write lock for that check, it would effectively limit
1149 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001150 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001151 elif not os.path.exists(target):
1152 # Upgrade occurred, but a reversion, or something whacky
1153 # occurred writing to the old location. Wipe and continue.
1154 os.rename(src, target)
1155 else:
1156 # Upgrade occurred once already, but either a reversion or
1157 # some before/after separate cros_sdk usage is at play.
1158 # Wipe and continue.
1159 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001160
David James56e6c2c2012-10-24 23:54:41 -07001161 if options.download:
1162 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -07001163 sdk_tarball = FetchRemoteTarballs(
1164 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001165
David James56e6c2c2012-10-24 23:54:41 -07001166 if options.create:
1167 lock.write_lock()
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001168 # Recheck if the chroot is set up here before creating to make sure we
1169 # account for whatever the various delete/unmount/remount steps above
1170 # have done.
1171 if cros_sdk_lib.IsChrootReady(options.chroot):
1172 logging.debug('Chroot already exists. Skipping creation.')
1173 else:
Manoj Guptab12f7302019-06-03 16:40:14 -07001174 CreateChroot(
1175 options.chroot,
1176 sdk_tarball,
1177 options.cache_dir,
1178 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001179
David James56e6c2c2012-10-24 23:54:41 -07001180 if options.enter:
1181 lock.read_lock()
1182 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -05001183 options.chrome_root_mount, options.goma_dir,
1184 options.goma_client_json, options.working_dir,
1185 chroot_command)