blob: ac1393fa90efa3979a358ff30cbfcb3676d172af [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
Brian Harringb938c782012-02-29 15:14:38 -080025import urlparse
26
Aviv Keshetb7519e12016-10-04 00:50:00 -070027from chromite.lib import constants
Brian Harringcfe762a2012-02-29 13:03:53 -080028from chromite.lib import cgroups
Brian Harringb6cf9142012-09-01 20:43:17 -070029from chromite.lib import commandline
Brian Harringb938c782012-02-29 15:14:38 -080030from chromite.lib import cros_build_lib
Ralph Nathan59900422015-03-24 10:41:17 -070031from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060032from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080033from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080034from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070035from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080036from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040037from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080038from chromite.lib import retry_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050039from chromite.lib import toolchain
Brian Harringb938c782012-02-29 15:14:38 -080040
41cros_build_lib.STRICT_SUDO = True
42
Zdenek Behanaa52cea2012-05-30 01:31:11 +020043COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020044
Brian Harringb938c782012-02-29 15:14:38 -080045# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070046MAKE_CHROOT = [
47 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/make_chroot.sh')
48]
49ENTER_CHROOT = [
50 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
51]
Brian Harringb938c782012-02-29 15:14:38 -080052
Josh Triplett472a4182013-03-08 11:48:57 -080053# Proxy simulator configuration.
54PROXY_HOST_IP = '192.168.240.1'
55PROXY_PORT = 8080
56PROXY_GUEST_IP = '192.168.240.2'
57PROXY_NETMASK = 30
58PROXY_VETH_PREFIX = 'veth'
59PROXY_CONNECT_PORTS = (80, 443, 9418)
60PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
61PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
62PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070063 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080064PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
65
Josh Triplett9a495f62013-03-15 18:06:55 -070066# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080067NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080068
Josh Triplett472a4182013-03-08 11:48:57 -080069# Tools needed for --proxy-sim only.
70PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080071
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060072# Tools needed when use_image is true (the default).
73IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060074 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060075
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070076# As space is used inside the chroot, the empty space in chroot.img is
77# allocated. Deleting files inside the chroot doesn't automatically return the
78# used space to the OS. Over time, this tends to make the sparse chroot.img
79# less sparse even if the chroot contents don't currently need much space. We
80# can recover most of this unused space with fstrim, but that takes too much
81# time to run it every time. Instead, check the used space against the image
82# size after mounting the chroot and only call fstrim if it looks like we could
83# recover at least this many GiB.
84MAX_UNUSED_IMAGE_GBS = 20
85
Mike Frysingercc838832014-05-24 13:10:30 -040086
Brian Harring1790ac42012-09-23 08:53:33 -070087def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080088 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070089 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
90 return [
91 toolchain.GetSdkURL(
92 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
93 for compressor in COMPRESSION_PREFERENCE
94 ]
Brian Harring1790ac42012-09-23 08:53:33 -070095
96
97def GetStage3Urls(version):
Manoj Guptab12f7302019-06-03 16:40:14 -070098 return [
99 toolchain.GetSdkURL(suburl='stage3-amd64-%s.tar.%s' % (version, ext))
100 for ext in COMPRESSION_PREFERENCE
101 ]
Brian Harringb938c782012-02-29 15:14:38 -0800102
103
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700104def FetchRemoteTarballs(storage_dir, urls, desc, allow_none=False):
Mike Frysinger34db8692013-11-11 14:54:08 -0500105 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200106
107 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500108 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200109 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700110 desc: A string describing what tarball we're downloading (for logging).
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700111 allow_none: Don't fail if none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200112
113 Returns:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700114 Full path to the downloaded file, or None if |allow_none| and no URL worked.
115
116 Raises:
117 ValueError: If |allow_none| is False and none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200118 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200119
Brian Harring1790ac42012-09-23 08:53:33 -0700120 # Note we track content length ourselves since certain versions of curl
121 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700122 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700123 logging.notice('Downloading %s tarball...', desc)
Brian Norriscf8aef42016-09-27 10:43:39 -0700124 status_re = re.compile(r'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400125 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200126 for url in urls:
Brian Harring1790ac42012-09-23 08:53:33 -0700127 parsed = urlparse.urlparse(url)
128 tarball_name = os.path.basename(parsed.path)
129 if parsed.scheme in ('', 'file'):
130 if os.path.exists(parsed.path):
131 return parsed.path
132 continue
133 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700134 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700135 result = retry_util.RunCurl(['-I', url],
136 print_cmd=False,
137 debug_level=logging.NOTICE,
138 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700139 successful = False
140 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700141 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700142 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700143 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700144 successful = True
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500145 elif header.lower().startswith('content-length:'):
146 content_length = int(header.split(':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700147 if successful:
148 break
149 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200150 break
151 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700152 if allow_none:
153 return None
154 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200155
Brian Harringae0a5322012-09-15 01:46:51 -0700156 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700157 current_size = 0
158 if os.path.exists(tarball_dest):
159 current_size = os.path.getsize(tarball_dest)
160 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700161 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700162 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100163
Brian Harring1790ac42012-09-23 08:53:33 -0700164 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800165 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900166 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700167 print_cmd=False,
168 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800169
Brian Harring1790ac42012-09-23 08:53:33 -0700170 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700171 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
172 # because we might have partial overlap between known prefixes.
173 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
174 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
175 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700176 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700177 if (filename == tarball_name or
178 any([(filename.startswith(p) and
179 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
180 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700181 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700182 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700183 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200184
Brian Harringb938c782012-02-29 15:14:38 -0800185 return tarball_dest
186
187
Benjamin Gordon589873b2018-05-31 14:30:56 -0600188def CreateChroot(chroot_path, sdk_tarball, cache_dir, nousepkg=False):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600189 """Creates a new chroot from a given SDK.
190
191 Args:
192 chroot_path: Path where the new chroot will be created.
193 sdk_tarball: Path to a downloaded Gentoo Stage3 or Chromium OS SDK tarball.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600194 cache_dir: Path to a directory that will be used for caching portage files,
195 etc.
196 nousepkg: If True, pass --nousepkg to cros_setup_toolchains inside the
197 chroot.
198 """
Brian Harringb938c782012-02-29 15:14:38 -0800199
Manoj Guptab12f7302019-06-03 16:40:14 -0700200 cmd = MAKE_CHROOT + [
201 '--stage3_path', sdk_tarball, '--chroot', chroot_path, '--cache_dir',
202 cache_dir
203 ]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700204
Mike Frysinger2de7f042012-07-10 04:45:03 -0400205 if nousepkg:
206 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800207
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700208 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800209 try:
210 cros_build_lib.RunCommand(cmd, print_cmd=False)
211 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700212 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800213
214
215def DeleteChroot(chroot_path):
216 """Deletes an existing chroot"""
Manoj Guptab12f7302019-06-03 16:40:14 -0700217 cmd = MAKE_CHROOT + ['--chroot', chroot_path, '--delete']
Brian Harringb938c782012-02-29 15:14:38 -0800218 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700219 logging.notice('Deleting chroot.')
Brian Harringb938c782012-02-29 15:14:38 -0800220 cros_build_lib.RunCommand(cmd, print_cmd=False)
221 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700222 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800223
224
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600225def CleanupChroot(chroot_path):
226 """Unmounts a chroot and cleans up any associated devices."""
Don Garrett36650112018-06-28 15:54:34 -0700227 cros_sdk_lib.CleanupChrootMount(chroot_path, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600228
229
Brian Harringae0a5322012-09-15 01:46:51 -0700230def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500231 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800232 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400233 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
234 # The os.ST_NOSUID constant wasn't added until python-3.2.
235 if st.f_flag & 0x2:
236 cros_build_lib.Die('chroot cannot be in a nosuid mount')
237
Brian Harringae0a5322012-09-15 01:46:51 -0700238 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800239 if chrome_root:
240 cmd.extend(['--chrome_root', chrome_root])
241 if chrome_root_mount:
242 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900243 if goma_dir:
244 cmd.extend(['--goma_dir', goma_dir])
245 if goma_client_json:
246 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800247 if working_dir is not None:
248 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700249
Brian Harringb938c782012-02-29 15:14:38 -0800250 if len(additional_args) > 0:
251 cmd.append('--')
252 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700253
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700254 # ThinLTO opens lots of files at the same time.
255 resource.setrlimit(resource.RLIMIT_NOFILE, (32768, 32768))
Manoj Guptab12f7302019-06-03 16:40:14 -0700256 ret = cros_build_lib.RunCommand(
257 cmd, print_cmd=False, error_code_ok=True, mute_output=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700258 # If we were in interactive mode, ignore the exit code; it'll be whatever
259 # they last ran w/in the chroot and won't matter to us one way or another.
260 # Note this does allow chroot entrance to fail and be ignored during
261 # interactive; this is however a rare case and the user will immediately
262 # see it (nor will they be checking the exit code manually).
263 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700264 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800265
266
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600267def _ImageFileForChroot(chroot):
268 """Find the image file that should be associated with |chroot|.
269
270 This function does not check if the image exists; it simply returns the
271 filename that would be used.
272
273 Args:
274 chroot: Path to the chroot.
275
276 Returns:
277 Path to an image file that would be associated with chroot.
278 """
279 return chroot.rstrip('/') + '.img'
280
281
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600282def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
283 """Create a snapshot for the specified chroot VG/LV.
284
285 Args:
286 snapshot_name: The name of the new snapshot.
287 chroot_vg: The name of the VG containing the origin LV.
288 chroot_lv: The name of the origin LV.
289
290 Returns:
291 True if the snapshot was created, or False if a snapshot with the same
292 name already exists.
293
294 Raises:
295 SystemExit: The lvcreate command failed.
296 """
297 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700298 logging.error(
299 'Cannot create snapshot %s: A volume with that name already '
300 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600301 return False
302
Manoj Guptab12f7302019-06-03 16:40:14 -0700303 cmd = [
304 'lvcreate', '-s', '--name', snapshot_name,
305 '%s/%s' % (chroot_vg, chroot_lv)
306 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600307 try:
308 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
309 chroot_lv, chroot_vg)
310 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
311 return True
312 except cros_build_lib.RunCommandError:
313 raise SystemExit('Running %r failed!' % cmd)
314
315
316def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
317 """Delete the named snapshot from the specified chroot VG.
318
319 If the requested snapshot is not found, nothing happens. The main chroot LV
320 and internal thinpool LV cannot be deleted with this function.
321
322 Args:
323 snapshot_name: The name of the snapshot to delete.
324 chroot_vg: The name of the VG containing the origin LV.
325 chroot_lv: The name of the origin LV.
326
327 Raises:
328 SystemExit: The lvremove command failed.
329 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600330 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
331 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700332 logging.error(
333 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
334 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600335 return
336
337 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
338 return
339
340 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
341 try:
342 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
343 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
344 except cros_build_lib.RunCommandError:
345 raise SystemExit('Running %r failed!' % cmd)
346
347
348def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
349 """Restore the chroot to an existing snapshot.
350
351 This is done by renaming the original |chroot_lv| LV to a temporary name,
352 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
353 now unused LV. If an error occurs, attempts to rename the original snapshot
354 back to |chroot_lv| to leave the chroot unchanged.
355
356 The chroot must be unmounted before calling this function, and will be left
357 unmounted after this function returns.
358
359 Args:
360 snapshot_name: The name of the snapshot to restore. This snapshot will no
361 longer be accessible at its original name after this function finishes.
362 chroot_vg: The VG containing the chroot LV and snapshot LV.
363 chroot_lv: The name of the original chroot LV.
364
365 Returns:
366 True if the chroot was restored to the requested snapshot, or False if
367 the snapshot wasn't found or isn't valid.
368
369 Raises:
370 SystemExit: Any of the LVM commands failed.
371 """
372 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600373 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
374 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600375 snapshot_name not in valid_snapshots):
376 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
377 snapshot_name, ', '.join(valid_snapshots))
378 return False
379
380 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
381 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
382 try:
383 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
384 except cros_build_lib.RunCommandError:
385 raise SystemExit('Running %r failed!' % cmd)
386
387 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
388 try:
389 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
390 except cros_build_lib.RunCommandError:
391 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
392 try:
393 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
394 except cros_build_lib.RunCommandError:
395 raise SystemExit('Failed to rename %s to chroot and failed to restore '
396 '%s back to chroot. Failed command: %r' %
397 (snapshot_name, backup_chroot_name, cmd))
Manoj Guptab12f7302019-06-03 16:40:14 -0700398 raise SystemExit(
399 'Failed to rename %s to chroot. Original chroot LV has '
400 'been restored. Failed command: %r' % (snapshot_name, cmd))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600401
402 # Some versions of LVM set snapshots to be skipped at auto-activate time.
403 # Other versions don't have this flag at all. We run lvchange to try
404 # disabling auto-skip and activating the volume, but ignore errors. Versions
405 # that don't have the flag should be auto-activated.
406 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
407 cmd = ['lvchange', '-kn', chroot_lv_path]
Manoj Guptab12f7302019-06-03 16:40:14 -0700408 cros_build_lib.RunCommand(
409 cmd, print_cmd=False, capture_output=True, error_code_ok=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600410
411 # Activate the LV in case the lvchange above was needed. Activating an LV
412 # that is already active shouldn't do anything, so this is safe to run even if
413 # the -kn wasn't needed.
414 cmd = ['lvchange', '-ay', chroot_lv_path]
415 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
416
417 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
418 try:
419 cros_build_lib.RunCommand(cmd, print_cmd=False, capture_output=True)
420 except cros_build_lib.RunCommandError:
421 raise SystemExit('Failed to remove backup LV %s/%s. Failed command: %r' %
422 (chroot_vg, backup_chroot_name, cmd))
423
424 return True
425
426
427def ListChrootSnapshots(chroot_vg, chroot_lv):
428 """Return all snapshots in |chroot_vg| regardless of origin volume.
429
430 Args:
431 chroot_vg: The name of the VG containing the chroot.
432 chroot_lv: The name of the chroot LV.
433
434 Returns:
435 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
436
437 Raises:
438 SystemExit: The lvs command failed.
439 """
440 if not chroot_vg or not chroot_lv:
441 return []
442
Manoj Guptab12f7302019-06-03 16:40:14 -0700443 cmd = [
444 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
445 '--separator', '\t', chroot_vg
446 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600447 try:
Manoj Guptab12f7302019-06-03 16:40:14 -0700448 result = cros_build_lib.RunCommand(
449 cmd, print_cmd=False, redirect_stdout=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600450 except cros_build_lib.RunCommandError:
451 raise SystemExit('Running %r failed!' % cmd)
452
453 # Once the thin origin volume has been deleted, there's no way to tell a
454 # snapshot apart from any other volume. Since this VG is created and managed
455 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
456 # valid snapshots.
457 snapshots = []
458 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
459 for line in result.output.splitlines():
460 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700461 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600462 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600463 not snapshot_attrs.match(lv_attr)):
464 continue
465 snapshots.append(lv_name)
466 return snapshots
467
468
David James56e6c2c2012-10-24 23:54:41 -0700469def _SudoCommand():
470 """Get the 'sudo' command, along with all needed environment variables."""
471
David James5a73b4d2013-03-07 10:23:40 -0800472 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
473 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700474 cmd = ['sudo']
David James5a73b4d2013-03-07 10:23:40 -0800475 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700476 value = os.environ.get(key)
477 if value is not None:
478 cmd += ['%s=%s' % (key, value)]
479
480 # Pass in the path to the depot_tools so that users can access them from
481 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400482 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500483
David James56e6c2c2012-10-24 23:54:41 -0700484 return cmd
485
486
Josh Triplett472a4182013-03-08 11:48:57 -0800487def _ReportMissing(missing):
488 """Report missing utilities, then exit.
489
490 Args:
491 missing: List of missing utilities, as returned by
492 osutils.FindMissingBinaries. If non-empty, will not return.
493 """
494
495 if missing:
496 raise SystemExit(
497 'The tool(s) %s were not found.\n'
498 'Please install the appropriate package in your host.\n'
499 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700500 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800501
502
503def _ProxySimSetup(options):
504 """Set up proxy simulator, and return only in the child environment.
505
506 TODO: Ideally, this should support multiple concurrent invocations of
507 cros_sdk --proxy-sim; currently, such invocations will conflict with each
508 other due to the veth device names and IP addresses. Either this code would
509 need to generate fresh, unused names for all of these before forking, or it
510 would need to support multiple concurrent cros_sdk invocations sharing one
511 proxy and allowing it to exit when unused (without counting on any local
512 service-management infrastructure on the host).
513 """
514
515 may_need_mpm = False
516 apache_bin = osutils.Which('apache2')
517 if apache_bin is None:
518 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
519 if apache_bin is None:
520 _ReportMissing(('apache2',))
521 else:
522 may_need_mpm = True
523
524 # Module names and .so names included for ease of grepping.
525 apache_modules = [('proxy_module', 'mod_proxy.so'),
526 ('proxy_connect_module', 'mod_proxy_connect.so'),
527 ('proxy_http_module', 'mod_proxy_http.so'),
528 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
529
530 # Find the apache module directory, and make sure it has the modules we need.
531 module_dirs = {}
532 for g in PROXY_APACHE_MODULE_GLOBS:
533 for mod, so in apache_modules:
534 for f in glob.glob(os.path.join(g, so)):
535 module_dirs.setdefault(os.path.dirname(f), []).append(so)
536 for apache_module_path, modules_found in module_dirs.iteritems():
537 if len(modules_found) == len(apache_modules):
538 break
539 else:
540 # Appease cros lint, which doesn't understand that this else block will not
541 # fall through to the subsequent code which relies on apache_module_path.
542 apache_module_path = None
543 raise SystemExit(
544 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500545 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800546
547 def check_add_module(name):
548 so = 'mod_%s.so' % name
549 if os.access(os.path.join(apache_module_path, so), os.F_OK):
550 mod = '%s_module' % name
551 apache_modules.append((mod, so))
552 return True
553 return False
554
555 check_add_module('authz_core')
556 if may_need_mpm:
557 for mpm in PROXY_APACHE_MPMS:
558 if check_add_module('mpm_%s' % mpm):
559 break
560
561 veth_host = '%s-host' % PROXY_VETH_PREFIX
562 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
563
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500564 # Set up locks to sync the net namespace setup. We need the child to create
565 # the net ns first, and then have the parent assign the guest end of the veth
566 # interface to the child's new network namespace & bring up the proxy. Only
567 # then can the child move forward and rely on the network being up.
568 ns_create_lock = locking.PipeLock()
569 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800570
571 pid = os.fork()
572 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500573 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800574 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500575
576 # Signal the parent the ns is ready to be configured.
577 ns_create_lock.Post()
578 del ns_create_lock
579
580 # Wait for the parent to finish setting up the ns/proxy.
581 ns_setup_lock.Wait()
582 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800583
584 # Set up child side of the network.
585 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500586 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700587 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500588 'dev', veth_guest),
589 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800590 )
591 try:
592 for cmd in commands:
593 cros_build_lib.RunCommand(cmd, print_cmd=False)
594 except cros_build_lib.RunCommandError:
595 raise SystemExit('Running %r failed!' % (cmd,))
596
597 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
598 for proto in ('http', 'https', 'ftp'):
599 os.environ[proto + '_proxy'] = proxy_url
600 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
601 os.environ.pop(v, None)
602 return
603
Josh Triplett472a4182013-03-08 11:48:57 -0800604 # Set up parent side of the network.
605 uid = int(os.environ.get('SUDO_UID', '0'))
606 gid = int(os.environ.get('SUDO_GID', '0'))
607 if uid == 0 or gid == 0:
608 for username in PROXY_APACHE_FALLBACK_USERS:
609 try:
610 pwnam = pwd.getpwnam(username)
611 uid, gid = pwnam.pw_uid, pwnam.pw_gid
612 break
613 except KeyError:
614 continue
615 if uid == 0 or gid == 0:
616 raise SystemExit('Could not find a non-root user to run Apache as')
617
618 chroot_parent, chroot_base = os.path.split(options.chroot)
619 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
620 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
621
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500622 # Wait for the child to create the net ns.
623 ns_create_lock.Wait()
624 del ns_create_lock
625
Josh Triplett472a4182013-03-08 11:48:57 -0800626 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500627 'User #%u' % uid,
628 'Group #%u' % gid,
629 'PidFile %s' % pid_file,
630 'ErrorLog %s' % log_file,
631 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
632 'ServerName %s' % PROXY_HOST_IP,
633 'ProxyRequests On',
634 'AllowCONNECT %s' % ' '.join(map(str, PROXY_CONNECT_PORTS)),
Josh Triplett472a4182013-03-08 11:48:57 -0800635 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500636 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
637 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800638 ]
639 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700640 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
641 veth_guest),
642 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
643 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500644 ('ip', 'link', 'set', veth_host, 'up'),
645 ([apache_bin, '-f', '/dev/null'] +
646 [arg for d in apache_directives for arg in ('-C', d)]),
647 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800648 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700649 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800650 try:
651 for cmd in commands:
652 cros_build_lib.RunCommand(cmd, print_cmd=False)
653 except cros_build_lib.RunCommandError:
654 # Clean up existing interfaces, if any.
655 cmd_cleanup = ('ip', 'link', 'del', veth_host)
656 try:
657 cros_build_lib.RunCommand(cmd_cleanup, print_cmd=False)
658 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700659 logging.error('running %r failed', cmd_cleanup)
Josh Triplett472a4182013-03-08 11:48:57 -0800660 raise SystemExit('Running %r failed!' % (cmd,))
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500661
662 # Signal the child that the net ns/proxy is fully configured now.
663 ns_setup_lock.Post()
664 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800665
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400666 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800667
668
Mike Frysingera78a56e2012-11-20 06:02:30 -0500669def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700670 """Re-execute cros_sdk as root.
671
672 Also unshare the mount namespace so as to ensure that processes outside
673 the chroot can't mess with our mounts.
674 """
675 if os.geteuid() != 0:
Mike Frysingera78a56e2012-11-20 06:02:30 -0500676 cmd = _SudoCommand() + ['--'] + argv
677 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500678 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400679 # We must set up the cgroups mounts before we enter our own namespace.
680 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800681 cgroups.Cgroup.InitSystem()
David James56e6c2c2012-10-24 23:54:41 -0700682
683
Mike Frysinger34db8692013-11-11 14:54:08 -0500684def _CreateParser(sdk_latest_version, bootstrap_latest_version):
685 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400686 usage = ('usage: %(prog)s [options] '
687 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700688 parser = commandline.ArgumentParser(
689 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700690
Mike Frysinger34db8692013-11-11 14:54:08 -0500691 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500692 default_chroot = os.path.join(constants.SOURCE_ROOT,
693 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400694 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700695 '--chroot',
696 dest='chroot',
697 default=default_chroot,
698 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700699 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700700 parser.add_argument(
701 '--nouse-image',
702 dest='use_image',
703 action='store_false',
704 default=True,
705 help='Do not mount the chroot on a loopback image; '
706 'instead, create it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800707
Manoj Guptab12f7302019-06-03 16:40:14 -0700708 parser.add_argument(
709 '--chrome_root',
710 type='path',
711 help='Mount this chrome root into the SDK chroot')
712 parser.add_argument(
713 '--chrome_root_mount',
714 type='path',
715 help='Mount chrome into this path inside SDK chroot')
716 parser.add_argument(
717 '--nousepkg',
718 action='store_true',
719 default=False,
720 help='Do not use binary packages when creating a chroot.')
721 parser.add_argument(
722 '-u',
723 '--url',
724 dest='sdk_url',
725 help='Use sdk tarball located at this url. Use file:// '
726 'for local files.')
727 parser.add_argument(
728 '--self-bootstrap',
729 dest='self_bootstrap',
730 action='store_true',
731 default=False,
732 help=('Use previously build sdk for bootstrapping.'))
733 parser.add_argument(
734 '--sdk-version',
735 help=('Use this sdk version. For prebuilt, current is %r'
736 ', for bootstrapping it is %r.' % (sdk_latest_version,
737 bootstrap_latest_version)))
738 parser.add_argument(
739 '--goma_dir',
740 type='path',
741 help='Goma installed directory to mount into the chroot.')
742 parser.add_argument(
743 '--goma_client_json',
744 type='path',
745 help='Service account json file to use goma on bot. '
746 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800747
748 # Use type=str instead of type='path' to prevent the given path from being
749 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700750 parser.add_argument(
751 '--working-dir',
752 type=str,
753 help='Run the command in specific working directory in '
754 'chroot. If the given directory is a relative '
755 'path, this program will transfer the path to '
756 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800757
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400758 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500759
760 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400761 group = parser.add_argument_group('Commands')
762 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700763 '--enter',
764 action='store_true',
765 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500766 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400767 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700768 '--create',
769 action='store_true',
770 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500771 help='Create the chroot only if it does not already exist. '
772 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400773 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700774 '--bootstrap',
775 action='store_true',
776 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500777 help='Build everything from scratch, including the sdk. '
778 'Use this only if you need to validate a change '
779 'that affects SDK creation itself (toolchain and '
780 'build are typically the only folk who need this). '
781 'Note this will quite heavily slow down the build. '
782 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400783 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700784 '-r',
785 '--replace',
786 action='store_true',
787 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500788 help='Replace an existing SDK chroot. Basically an alias '
789 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400790 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700791 '--delete',
792 action='store_true',
793 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500794 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400795 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700796 '--unmount',
797 action='store_true',
798 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600799 help='Unmount and clean up devices associated with the '
800 'SDK chroot if it exists. This does not delete the '
801 'backing image file, so the same chroot can be later '
802 're-mounted for reuse. To fully delete the chroot, use '
803 '--delete. This is primarily useful for working on '
804 'cros_sdk or the chroot setup; you should not need it '
805 'under normal circumstances.')
806 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700807 '--download',
808 action='store_true',
809 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500810 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600811 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700812 '--snapshot-create',
813 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600814 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700815 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600816 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700817 '--snapshot-restore',
818 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600819 help='Restore the chroot to a previously created snapshot.')
820 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700821 '--snapshot-delete',
822 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600823 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700824 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600825 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700826 '--snapshot-list',
827 action='store_true',
828 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600829 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500830 commands = group
831
Mike Frysinger80dfce92014-04-21 10:58:53 -0400832 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400833 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700834 group.add_argument(
835 '--proxy-sim',
836 action='store_true',
837 default=False,
838 help='Simulate a restrictive network requiring an outbound'
839 ' proxy.')
840 group.add_argument(
841 '--no-ns-pid',
842 dest='ns_pid',
843 default=True,
844 action='store_false',
845 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400846
Mike Frysinger34db8692013-11-11 14:54:08 -0500847 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400848 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500849 'Internal Chromium OS Build Team Options',
850 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700851 group.add_argument(
852 '--buildbot-log-version',
853 default=False,
854 action='store_true',
855 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500856
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400857 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500858
859
860def main(argv):
861 conf = cros_build_lib.LoadKeyValueFile(
862 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
863 ignore_missing=True)
864 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
865 bootstrap_latest_version = conf.get('BOOTSTRAP_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700866 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Mike Frysinger34db8692013-11-11 14:54:08 -0500867 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400868 options = parser.parse_args(argv)
869 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800870
871 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500872 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800873
Brian Harring1790ac42012-09-23 08:53:33 -0700874 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700875 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600876 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700877 "cros_sdk is currently only supported on x86_64; you're running"
878 " %s. Please find a x86_64 machine." % (host,))
879
Josh Triplett472a4182013-03-08 11:48:57 -0800880 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
881 if options.proxy_sim:
882 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600883 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800884
Manoj Gupta01927c12019-05-13 17:33:14 -0700885 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
886 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
Manoj Guptaa0cf99f2019-04-15 20:14:35 -0700887 if options.self_bootstrap:
Manoj Gupta01927c12019-05-13 17:33:14 -0700888 bootstrap_latest_version = (
889 sdk_latest_version if bootstrap_frozen_version == '<unknown>' else
890 bootstrap_frozen_version)
Manoj Guptaa0cf99f2019-04-15 20:14:35 -0700891
Benjamin Gordon040a1162017-06-29 13:44:47 -0600892 if (sdk_latest_version == '<unknown>' or
893 bootstrap_latest_version == '<unknown>'):
894 cros_build_lib.Die(
895 'No SDK version was found. '
896 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
897 'Please change to a directory inside your Chromium OS source tree\n'
898 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400899 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600900
Manoj Guptab12f7302019-06-03 16:40:14 -0700901 any_snapshot_operation = (
902 options.snapshot_create or options.snapshot_restore or
903 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600904 if any_snapshot_operation and not options.use_image:
905 cros_build_lib.Die('Snapshot operations are not compatible with '
906 '--nouse-image.')
907
Manoj Guptab12f7302019-06-03 16:40:14 -0700908 if (options.snapshot_delete and
909 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600910 parser.error('Cannot --snapshot_delete the same snapshot you are '
911 'restoring with --snapshot_restore.')
912
David James471532c2013-01-21 10:23:31 -0800913 _ReExecuteIfNeeded([sys.argv[0]] + argv)
914
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600915 lock_path = os.path.dirname(options.chroot)
916 lock_path = os.path.join(
917 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
918
Brian Harring218e13c2012-10-10 16:21:26 -0700919 # Expand out the aliases...
920 if options.replace:
921 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800922
Brian Harring218e13c2012-10-10 16:21:26 -0700923 if options.bootstrap:
924 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800925
Brian Harring218e13c2012-10-10 16:21:26 -0700926 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400927 # pylint: disable=protected-access
928 # This _group_actions access sucks, but upstream decided to not include an
929 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700930 options.enter |= not any(
931 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400932 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700933 options.enter |= bool(chroot_command)
934
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600935 if (options.delete and not options.create and
936 (options.enter or any_snapshot_operation)):
937 parser.error("Trying to enter or snapshot the chroot when --delete "
Brian Harring218e13c2012-10-10 16:21:26 -0700938 "was specified makes no sense.")
939
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600940 if (options.unmount and
941 (options.create or options.enter or any_snapshot_operation)):
942 parser.error('--unmount cannot be specified with other chroot actions.')
943
Yong Hong84ba9172018-02-07 01:37:42 +0800944 if options.working_dir is not None and not os.path.isabs(options.working_dir):
945 options.working_dir = path_util.ToChrootPath(options.working_dir)
946
Benjamin Gordon35194f12017-07-19 10:26:22 -0600947 # Discern if we need to create the chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600948 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600949 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600950 not options.unmount and not missing_image_tools and
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600951 os.path.exists(_ImageFileForChroot(options.chroot))):
952 # Try to re-mount an existing image in case the user has rebooted.
953 with cgroups.SimpleContainChildren('cros_sdk'):
954 with locking.FileLock(lock_path, 'chroot lock') as lock:
955 logging.debug('Checking if existing chroot image can be mounted.')
956 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -0600957 cros_sdk_lib.MountChroot(options.chroot, create=False)
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600958 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600959 if chroot_exists:
960 logging.notice('Mounted existing image %s on chroot',
961 _ImageFileForChroot(options.chroot))
Brian Harring218e13c2012-10-10 16:21:26 -0700962
963 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600964 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700965 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800966
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600967 # Make sure we will download if we plan to create.
968 options.download |= options.create
969
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600970 # Anything that needs to manipulate the main chroot mount or communicate with
971 # LVM needs to be done here before we enter the new namespaces.
972
973 # If deleting, do it regardless of the use_image flag so that a
974 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600975 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
976 # favor of this block.
977 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600978 if options.delete:
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600979 with cgroups.SimpleContainChildren('cros_sdk'):
980 with locking.FileLock(lock_path, 'chroot lock') as lock:
981 lock.write_lock()
982 if missing_image_tools:
983 logging.notice('Unmounting chroot.')
984 osutils.UmountTree(options.chroot)
985 else:
986 logging.notice('Deleting chroot.')
Don Garrett36650112018-06-28 15:54:34 -0700987 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600988 chroot_deleted = True
989
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600990 # If cleanup was requested, we have to do it while we're still in the original
991 # namespace. Since cleaning up the mount will interfere with any other
992 # commands, we exit here. The check above should have made sure that no other
993 # action was requested, anyway.
994 if options.unmount:
995 with locking.FileLock(lock_path, 'chroot lock') as lock:
996 lock.write_lock()
997 CleanupChroot(options.chroot)
998 sys.exit(0)
999
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001000 # Make sure the main chroot mount is visible. Contents will be filled in
1001 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001002 if options.create and options.use_image:
1003 if missing_image_tools:
Manoj Guptab12f7302019-06-03 16:40:14 -07001004 raise SystemExit('''The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001005Please make sure the lvm2 and thin-provisioning-tools packages
1006are installed on your host.
1007Example(ubuntu):
1008 sudo apt-get install lvm2 thin-provisioning-tools
1009
1010If you want to run without lvm2, pass --nouse-image (chroot
1011snapshots will be unavailable).''' % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001012
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001013 logging.debug('Making sure chroot image is mounted.')
1014 with cgroups.SimpleContainChildren('cros_sdk'):
1015 with locking.FileLock(lock_path, 'chroot lock') as lock:
1016 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -06001017 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001018 cros_build_lib.Die('Unable to mount %s on chroot',
1019 _ImageFileForChroot(options.chroot))
1020 logging.notice('Mounted %s on chroot',
1021 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001022
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001023 # Snapshot operations will always need the VG/LV, but other actions won't.
1024 if any_snapshot_operation:
1025 with cgroups.SimpleContainChildren('cros_sdk'):
1026 with locking.FileLock(lock_path, 'chroot lock') as lock:
Benjamin Gordon74645232018-05-04 17:40:42 -06001027 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001028 options.chroot)
1029 if not chroot_vg or not chroot_lv:
1030 cros_build_lib.Die('Unable to find VG/LV for chroot %s',
1031 options.chroot)
1032
1033 # Delete snapshot before creating a new one. This allows the user to
1034 # throw out old state, create a new snapshot, and enter the chroot in a
1035 # single call to cros_sdk. Since restore involves deleting, also do it
1036 # before creating.
1037 if options.snapshot_restore:
1038 lock.write_lock()
1039 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1040 if options.snapshot_restore not in valid_snapshots:
Manoj Guptab12f7302019-06-03 16:40:14 -07001041 cros_build_lib.Die(
1042 '%s is not a valid snapshot to restore to. '
1043 'Valid snapshots: %s', options.snapshot_restore,
1044 ', '.join(valid_snapshots))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001045 osutils.UmountTree(options.chroot)
1046 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1047 chroot_lv):
1048 cros_build_lib.Die('Unable to restore chroot to snapshot.')
Benjamin Gordon74645232018-05-04 17:40:42 -06001049 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001050 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
1051
1052 # Use a read lock for snapshot delete and create even though they modify
1053 # the filesystem, because they don't modify the mounted chroot itself.
1054 # The underlying LVM commands take their own locks, so conflicting
1055 # concurrent operations here may crash cros_sdk, but won't corrupt the
1056 # chroot image. This tradeoff seems worth it to allow snapshot
1057 # operations on chroots that have a process inside.
1058 if options.snapshot_delete:
1059 lock.read_lock()
1060 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
1061
1062 if options.snapshot_create:
1063 lock.read_lock()
1064 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1065 chroot_lv):
1066 cros_build_lib.Die('Unable to create snapshot.')
1067
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001068 img_path = _ImageFileForChroot(options.chroot)
1069 if (options.use_image and os.path.exists(options.chroot) and
1070 os.path.exists(img_path)):
1071 img_stat = os.stat(img_path)
1072 img_used_bytes = img_stat.st_blocks * 512
1073
1074 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001075 mount_used_bytes = mount_stat.f_frsize * (
1076 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001077
1078 extra_gbs = (img_used_bytes - mount_used_bytes) / 2**30
1079 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1080 logging.notice('%s is using %s GiB more than needed. Running '
1081 'fstrim.', img_path, extra_gbs)
1082 cmd = ['fstrim', options.chroot]
1083 try:
1084 cros_build_lib.RunCommand(cmd, print_cmd=False)
1085 except cros_build_lib.RunCommandError as e:
Manoj Guptab12f7302019-06-03 16:40:14 -07001086 logging.warning(
1087 'Running fstrim failed. Consider running fstrim on '
1088 'your chroot manually.\nError: %s', e)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001089
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001090 # Enter a new set of namespaces. Everything after here cannot directly affect
1091 # the hosts's mounts or alter LVM volumes.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001092 namespaces.SimpleUnshare()
1093 if options.ns_pid:
1094 first_pid = namespaces.CreatePidNs()
1095 else:
1096 first_pid = None
1097
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001098 if options.snapshot_list:
1099 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1100 print(snap)
1101 sys.exit(0)
1102
Brian Harringb938c782012-02-29 15:14:38 -08001103 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001104 sdk_version = (
1105 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001106 else:
1107 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001108 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001109 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001110
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001111 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001112 if options.download:
1113 if options.sdk_url:
1114 urls = [options.sdk_url]
1115 elif options.bootstrap:
1116 urls = GetStage3Urls(sdk_version)
Manoj Guptaa0cf99f2019-04-15 20:14:35 -07001117 if options.self_bootstrap:
1118 urls = GetArchStageTarballs(sdk_version)
Yong Hong4e29b622018-02-05 14:31:10 +08001119 else:
1120 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001121
Mike Frysinger80dfce92014-04-21 10:58:53 -04001122 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -07001123 with locking.FileLock(lock_path, 'chroot lock') as lock:
Josh Triplett472a4182013-03-08 11:48:57 -08001124 if options.proxy_sim:
1125 _ProxySimSetup(options)
1126
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001127 if (options.delete and not chroot_deleted and
1128 (os.path.exists(options.chroot) or
1129 os.path.exists(_ImageFileForChroot(options.chroot)))):
David James56e6c2c2012-10-24 23:54:41 -07001130 lock.write_lock()
1131 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001132
David James56e6c2c2012-10-24 23:54:41 -07001133 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1134 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -07001135 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001136
David James56e6c2c2012-10-24 23:54:41 -07001137 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -05001138 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -07001139 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001140 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001141 continue
1142 lock.write_lock(
1143 "Upgrade to %r needed but chroot is locked; please exit "
1144 "all instances so this upgrade can finish." % src)
1145 if not os.path.exists(src):
1146 # Note that while waiting for the write lock, src may've vanished;
1147 # it's a rare race during the upgrade process that's a byproduct
1148 # of us avoiding taking a write lock to do the src check. If we
1149 # took a write lock for that check, it would effectively limit
1150 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001151 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001152 elif not os.path.exists(target):
1153 # Upgrade occurred, but a reversion, or something whacky
1154 # occurred writing to the old location. Wipe and continue.
1155 os.rename(src, target)
1156 else:
1157 # Upgrade occurred once already, but either a reversion or
1158 # some before/after separate cros_sdk usage is at play.
1159 # Wipe and continue.
1160 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001161
David James56e6c2c2012-10-24 23:54:41 -07001162 if options.download:
1163 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -07001164 sdk_tarball = FetchRemoteTarballs(
1165 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001166
David James56e6c2c2012-10-24 23:54:41 -07001167 if options.create:
1168 lock.write_lock()
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001169 # Recheck if the chroot is set up here before creating to make sure we
1170 # account for whatever the various delete/unmount/remount steps above
1171 # have done.
1172 if cros_sdk_lib.IsChrootReady(options.chroot):
1173 logging.debug('Chroot already exists. Skipping creation.')
1174 else:
Manoj Guptab12f7302019-06-03 16:40:14 -07001175 CreateChroot(
1176 options.chroot,
1177 sdk_tarball,
1178 options.cache_dir,
1179 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001180
David James56e6c2c2012-10-24 23:54:41 -07001181 if options.enter:
1182 lock.read_lock()
1183 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -05001184 options.chrome_root_mount, options.goma_dir,
1185 options.goma_client_json, options.working_dir,
1186 chroot_command)