blob: 3e120dec36f40491da048d15c20be79591db2380 [file] [log] [blame]
Mike Frysinger2de7f042012-07-10 04:45:03 -04001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Brian Harringb938c782012-02-29 15:14:38 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Manoj Gupta7fad04d2019-06-14 20:12:25 -07004
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 Frysinger2f95cfc2015-06-04 04:00:26 -040014import argparse
Josh Triplett472a4182013-03-08 11:48:57 -080015import glob
Brian Harringb938c782012-02-29 15:14:38 -080016import os
Josh Triplett472a4182013-03-08 11:48:57 -080017import pwd
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060018import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070019import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070020import resource
Sergey Frolov1cb46ec2020-12-09 21:46:16 -070021import subprocess
David James56e6c2c2012-10-24 23:54:41 -070022import sys
Mike Frysingere852b072021-05-21 12:39:03 -040023import urllib.parse
Brian Harringb938c782012-02-29 15:14:38 -080024
Aviv Keshetb7519e12016-10-04 00:50:00 -070025from chromite.lib import constants
Brian Harringb6cf9142012-09-01 20:43:17 -070026from chromite.lib import commandline
Brian Harringb938c782012-02-29 15:14:38 -080027from chromite.lib import cros_build_lib
Ralph Nathan59900422015-03-24 10:41:17 -070028from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060029from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080030from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080031from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070032from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080033from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040034from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080035from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060036from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050037from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040038from chromite.utils import key_value_store
39
Brian Harringb938c782012-02-29 15:14:38 -080040
Zdenek Behanaa52cea2012-05-30 01:31:11 +020041COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020042
Brian Harringb938c782012-02-29 15:14:38 -080043# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070044MAKE_CHROOT = [
45 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/make_chroot.sh')
46]
47ENTER_CHROOT = [
48 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
49]
Brian Harringb938c782012-02-29 15:14:38 -080050
Josh Triplett472a4182013-03-08 11:48:57 -080051# Proxy simulator configuration.
52PROXY_HOST_IP = '192.168.240.1'
53PROXY_PORT = 8080
54PROXY_GUEST_IP = '192.168.240.2'
55PROXY_NETMASK = 30
56PROXY_VETH_PREFIX = 'veth'
57PROXY_CONNECT_PORTS = (80, 443, 9418)
58PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
59PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
60PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070061 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080062PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
63
Josh Triplett9a495f62013-03-15 18:06:55 -070064# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080065NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080066
Josh Triplett472a4182013-03-08 11:48:57 -080067# Tools needed for --proxy-sim only.
68PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080069
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060070# Tools needed when use_image is true (the default).
71IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060072 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060073
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070074# As space is used inside the chroot, the empty space in chroot.img is
75# allocated. Deleting files inside the chroot doesn't automatically return the
76# used space to the OS. Over time, this tends to make the sparse chroot.img
77# less sparse even if the chroot contents don't currently need much space. We
78# can recover most of this unused space with fstrim, but that takes too much
79# time to run it every time. Instead, check the used space against the image
80# size after mounting the chroot and only call fstrim if it looks like we could
81# recover at least this many GiB.
82MAX_UNUSED_IMAGE_GBS = 20
83
Mike Frysingercc838832014-05-24 13:10:30 -040084
Brian Harring1790ac42012-09-23 08:53:33 -070085def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080086 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070087 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
88 return [
89 toolchain.GetSdkURL(
90 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
91 for compressor in COMPRESSION_PREFERENCE
92 ]
Brian Harring1790ac42012-09-23 08:53:33 -070093
94
Mike Frysingerdaf57b82019-11-23 17:26:51 -050095def FetchRemoteTarballs(storage_dir, urls, desc):
Mike Frysinger34db8692013-11-11 14:54:08 -050096 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +020097
98 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -050099 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200100 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700101 desc: A string describing what tarball we're downloading (for logging).
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200102
103 Returns:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500104 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700105
106 Raises:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500107 ValueError: None of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200108 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200109
Brian Harring1790ac42012-09-23 08:53:33 -0700110 # Note we track content length ourselves since certain versions of curl
111 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700112 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700113 logging.notice('Downloading %s tarball...', desc)
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500114 status_re = re.compile(br'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400115 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200116 for url in urls:
Mike Frysinger3dcacee2019-08-23 17:09:11 -0400117 parsed = urllib.parse.urlparse(url)
Brian Harring1790ac42012-09-23 08:53:33 -0700118 tarball_name = os.path.basename(parsed.path)
119 if parsed.scheme in ('', 'file'):
120 if os.path.exists(parsed.path):
121 return parsed.path
122 continue
123 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700124 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700125 result = retry_util.RunCurl(['-I', url],
126 print_cmd=False,
127 debug_level=logging.NOTICE,
128 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700129 successful = False
130 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700131 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700132 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700133 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700134 successful = True
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500135 elif header.lower().startswith(b'content-length:'):
136 content_length = int(header.split(b':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700137 if successful:
138 break
139 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200140 break
141 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700142 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200143
Brian Harringae0a5322012-09-15 01:46:51 -0700144 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700145 current_size = 0
146 if os.path.exists(tarball_dest):
147 current_size = os.path.getsize(tarball_dest)
148 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700149 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700150 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100151
Brian Harring1790ac42012-09-23 08:53:33 -0700152 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800153 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900154 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700155 print_cmd=False,
156 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800157
Brian Harring1790ac42012-09-23 08:53:33 -0700158 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700159 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
160 # because we might have partial overlap between known prefixes.
161 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
162 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
163 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700164 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700165 if (filename == tarball_name or
166 any([(filename.startswith(p) and
167 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
168 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700169 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700170 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700171 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200172
Brian Harringb938c782012-02-29 15:14:38 -0800173 return tarball_dest
174
175
Benjamin Gordon589873b2018-05-31 14:30:56 -0600176def CreateChroot(chroot_path, sdk_tarball, cache_dir, nousepkg=False):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600177 """Creates a new chroot from a given SDK.
178
179 Args:
180 chroot_path: Path where the new chroot will be created.
181 sdk_tarball: Path to a downloaded Gentoo Stage3 or Chromium OS SDK tarball.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600182 cache_dir: Path to a directory that will be used for caching portage files,
183 etc.
184 nousepkg: If True, pass --nousepkg to cros_setup_toolchains inside the
185 chroot.
186 """
Brian Harringb938c782012-02-29 15:14:38 -0800187
Manoj Guptab12f7302019-06-03 16:40:14 -0700188 cmd = MAKE_CHROOT + [
189 '--stage3_path', sdk_tarball, '--chroot', chroot_path, '--cache_dir',
190 cache_dir
191 ]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700192
Mike Frysinger2de7f042012-07-10 04:45:03 -0400193 if nousepkg:
194 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800195
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700196 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800197 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500198 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500199 except cros_build_lib.RunCommandError as e:
200 cros_build_lib.Die('Creating chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800201
202
Brian Harringae0a5322012-09-15 01:46:51 -0700203def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500204 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800205 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400206 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
Alex Klein875b30e2021-01-05 14:56:33 -0700207 if st.f_flag & os.ST_NOSUID:
Mike Frysingere5456972013-06-13 00:07:23 -0400208 cros_build_lib.Die('chroot cannot be in a nosuid mount')
209
Brian Harringae0a5322012-09-15 01:46:51 -0700210 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800211 if chrome_root:
212 cmd.extend(['--chrome_root', chrome_root])
213 if chrome_root_mount:
214 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900215 if goma_dir:
216 cmd.extend(['--goma_dir', goma_dir])
217 if goma_client_json:
218 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800219 if working_dir is not None:
220 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700221
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400222 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800223 cmd.append('--')
224 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700225
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700226 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000227 # Set rlimit and vm.max_map_count to accommodate this.
228 file_limit = 262144
229 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
230 resource.setrlimit(resource.RLIMIT_NOFILE,
231 (max(soft, file_limit), max(hard, file_limit)))
232 max_map_count = int(open('/proc/sys/vm/max_map_count').read())
233 if max_map_count < file_limit:
234 logging.notice(
235 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
236 open('/proc/sys/vm/max_map_count', 'w').write(f'{file_limit}\n')
Alex Klein1ec3e6a2020-04-03 11:13:50 -0600237 ret = cros_build_lib.dbg_run(cmd, check=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700238 # If we were in interactive mode, ignore the exit code; it'll be whatever
239 # they last ran w/in the chroot and won't matter to us one way or another.
240 # Note this does allow chroot entrance to fail and be ignored during
241 # interactive; this is however a rare case and the user will immediately
242 # see it (nor will they be checking the exit code manually).
243 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700244 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800245
246
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600247def _ImageFileForChroot(chroot):
248 """Find the image file that should be associated with |chroot|.
249
250 This function does not check if the image exists; it simply returns the
251 filename that would be used.
252
253 Args:
254 chroot: Path to the chroot.
255
256 Returns:
257 Path to an image file that would be associated with chroot.
258 """
259 return chroot.rstrip('/') + '.img'
260
261
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600262def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
263 """Create a snapshot for the specified chroot VG/LV.
264
265 Args:
266 snapshot_name: The name of the new snapshot.
267 chroot_vg: The name of the VG containing the origin LV.
268 chroot_lv: The name of the origin LV.
269
270 Returns:
271 True if the snapshot was created, or False if a snapshot with the same
272 name already exists.
273
274 Raises:
275 SystemExit: The lvcreate command failed.
276 """
277 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700278 logging.error(
279 'Cannot create snapshot %s: A volume with that name already '
280 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600281 return False
282
Manoj Guptab12f7302019-06-03 16:40:14 -0700283 cmd = [
284 'lvcreate', '-s', '--name', snapshot_name,
285 '%s/%s' % (chroot_vg, chroot_lv)
286 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600287 try:
288 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
289 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500290 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600291 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500292 except cros_build_lib.RunCommandError as e:
293 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600294
295
296def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
297 """Delete the named snapshot from the specified chroot VG.
298
299 If the requested snapshot is not found, nothing happens. The main chroot LV
300 and internal thinpool LV cannot be deleted with this function.
301
302 Args:
303 snapshot_name: The name of the snapshot to delete.
304 chroot_vg: The name of the VG containing the origin LV.
305 chroot_lv: The name of the origin LV.
306
307 Raises:
308 SystemExit: The lvremove command failed.
309 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600310 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
311 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700312 logging.error(
313 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
314 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600315 return
316
317 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
318 return
319
320 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
321 try:
322 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500323 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500324 except cros_build_lib.RunCommandError as e:
325 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600326
327
328def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
329 """Restore the chroot to an existing snapshot.
330
331 This is done by renaming the original |chroot_lv| LV to a temporary name,
332 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
333 now unused LV. If an error occurs, attempts to rename the original snapshot
334 back to |chroot_lv| to leave the chroot unchanged.
335
336 The chroot must be unmounted before calling this function, and will be left
337 unmounted after this function returns.
338
339 Args:
340 snapshot_name: The name of the snapshot to restore. This snapshot will no
341 longer be accessible at its original name after this function finishes.
342 chroot_vg: The VG containing the chroot LV and snapshot LV.
343 chroot_lv: The name of the original chroot LV.
344
345 Returns:
346 True if the chroot was restored to the requested snapshot, or False if
347 the snapshot wasn't found or isn't valid.
348
349 Raises:
350 SystemExit: Any of the LVM commands failed.
351 """
352 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600353 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
354 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600355 snapshot_name not in valid_snapshots):
356 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
357 snapshot_name, ', '.join(valid_snapshots))
358 return False
359
360 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
361 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
362 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500363 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500364 except cros_build_lib.RunCommandError as e:
365 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600366
367 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
368 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500369 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500370 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600371 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
372 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500373 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500374 except cros_build_lib.RunCommandError as e:
375 cros_build_lib.Die(
376 'Failed to rename %s to chroot and failed to restore %s back to '
377 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
378 cros_build_lib.Die(
379 'Failed to rename %s to chroot! Original chroot LV has '
380 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600381
382 # Some versions of LVM set snapshots to be skipped at auto-activate time.
383 # Other versions don't have this flag at all. We run lvchange to try
384 # disabling auto-skip and activating the volume, but ignore errors. Versions
385 # that don't have the flag should be auto-activated.
386 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
387 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400388 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500389 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600390
391 # Activate the LV in case the lvchange above was needed. Activating an LV
392 # that is already active shouldn't do anything, so this is safe to run even if
393 # the -kn wasn't needed.
394 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500395 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600396
397 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
398 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500399 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500400 except cros_build_lib.RunCommandError as e:
401 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
402 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600403
404 return True
405
406
407def ListChrootSnapshots(chroot_vg, chroot_lv):
408 """Return all snapshots in |chroot_vg| regardless of origin volume.
409
410 Args:
411 chroot_vg: The name of the VG containing the chroot.
412 chroot_lv: The name of the chroot LV.
413
414 Returns:
415 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
416
417 Raises:
418 SystemExit: The lvs command failed.
419 """
420 if not chroot_vg or not chroot_lv:
421 return []
422
Manoj Guptab12f7302019-06-03 16:40:14 -0700423 cmd = [
424 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
425 '--separator', '\t', chroot_vg
426 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600427 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400428 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600429 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600430 except cros_build_lib.RunCommandError:
431 raise SystemExit('Running %r failed!' % cmd)
432
433 # Once the thin origin volume has been deleted, there's no way to tell a
434 # snapshot apart from any other volume. Since this VG is created and managed
435 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
436 # valid snapshots.
437 snapshots = []
438 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
439 for line in result.output.splitlines():
440 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700441 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600442 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600443 not snapshot_attrs.match(lv_attr)):
444 continue
445 snapshots.append(lv_name)
446 return snapshots
447
448
David James56e6c2c2012-10-24 23:54:41 -0700449def _SudoCommand():
450 """Get the 'sudo' command, along with all needed environment variables."""
451
David James5a73b4d2013-03-07 10:23:40 -0800452 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400453 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700454 cmd = ['sudo']
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400455 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700456 value = os.environ.get(key)
457 if value is not None:
458 cmd += ['%s=%s' % (key, value)]
459
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400460 # We keep PATH not for the chroot but for the re-exec & for programs we might
461 # run before we chroot into the SDK. The process that enters the SDK itself
462 # will take care of initializing PATH to the right value then. But we can't
463 # override the system's default PATH for root as that will hide /sbin.
464 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
465
David James56e6c2c2012-10-24 23:54:41 -0700466 # Pass in the path to the depot_tools so that users can access them from
467 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400468 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500469
David James56e6c2c2012-10-24 23:54:41 -0700470 return cmd
471
472
Josh Triplett472a4182013-03-08 11:48:57 -0800473def _ReportMissing(missing):
474 """Report missing utilities, then exit.
475
476 Args:
477 missing: List of missing utilities, as returned by
478 osutils.FindMissingBinaries. If non-empty, will not return.
479 """
480
481 if missing:
482 raise SystemExit(
483 'The tool(s) %s were not found.\n'
484 'Please install the appropriate package in your host.\n'
485 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700486 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800487
488
489def _ProxySimSetup(options):
490 """Set up proxy simulator, and return only in the child environment.
491
492 TODO: Ideally, this should support multiple concurrent invocations of
493 cros_sdk --proxy-sim; currently, such invocations will conflict with each
494 other due to the veth device names and IP addresses. Either this code would
495 need to generate fresh, unused names for all of these before forking, or it
496 would need to support multiple concurrent cros_sdk invocations sharing one
497 proxy and allowing it to exit when unused (without counting on any local
498 service-management infrastructure on the host).
499 """
500
501 may_need_mpm = False
502 apache_bin = osutils.Which('apache2')
503 if apache_bin is None:
504 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
505 if apache_bin is None:
506 _ReportMissing(('apache2',))
507 else:
508 may_need_mpm = True
509
510 # Module names and .so names included for ease of grepping.
511 apache_modules = [('proxy_module', 'mod_proxy.so'),
512 ('proxy_connect_module', 'mod_proxy_connect.so'),
513 ('proxy_http_module', 'mod_proxy_http.so'),
514 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
515
516 # Find the apache module directory, and make sure it has the modules we need.
517 module_dirs = {}
518 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400519 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800520 for f in glob.glob(os.path.join(g, so)):
521 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400522 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800523 if len(modules_found) == len(apache_modules):
524 break
525 else:
526 # Appease cros lint, which doesn't understand that this else block will not
527 # fall through to the subsequent code which relies on apache_module_path.
528 apache_module_path = None
529 raise SystemExit(
530 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500531 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800532
533 def check_add_module(name):
534 so = 'mod_%s.so' % name
535 if os.access(os.path.join(apache_module_path, so), os.F_OK):
536 mod = '%s_module' % name
537 apache_modules.append((mod, so))
538 return True
539 return False
540
541 check_add_module('authz_core')
542 if may_need_mpm:
543 for mpm in PROXY_APACHE_MPMS:
544 if check_add_module('mpm_%s' % mpm):
545 break
546
547 veth_host = '%s-host' % PROXY_VETH_PREFIX
548 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
549
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500550 # Set up locks to sync the net namespace setup. We need the child to create
551 # the net ns first, and then have the parent assign the guest end of the veth
552 # interface to the child's new network namespace & bring up the proxy. Only
553 # then can the child move forward and rely on the network being up.
554 ns_create_lock = locking.PipeLock()
555 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800556
557 pid = os.fork()
558 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500559 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800560 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500561
562 # Signal the parent the ns is ready to be configured.
563 ns_create_lock.Post()
564 del ns_create_lock
565
566 # Wait for the parent to finish setting up the ns/proxy.
567 ns_setup_lock.Wait()
568 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800569
570 # Set up child side of the network.
571 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500572 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700573 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500574 'dev', veth_guest),
575 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800576 )
577 try:
578 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500579 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500580 except cros_build_lib.RunCommandError as e:
581 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800582
583 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
584 for proto in ('http', 'https', 'ftp'):
585 os.environ[proto + '_proxy'] = proxy_url
586 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
587 os.environ.pop(v, None)
588 return
589
Josh Triplett472a4182013-03-08 11:48:57 -0800590 # Set up parent side of the network.
591 uid = int(os.environ.get('SUDO_UID', '0'))
592 gid = int(os.environ.get('SUDO_GID', '0'))
593 if uid == 0 or gid == 0:
594 for username in PROXY_APACHE_FALLBACK_USERS:
595 try:
596 pwnam = pwd.getpwnam(username)
597 uid, gid = pwnam.pw_uid, pwnam.pw_gid
598 break
599 except KeyError:
600 continue
601 if uid == 0 or gid == 0:
602 raise SystemExit('Could not find a non-root user to run Apache as')
603
604 chroot_parent, chroot_base = os.path.split(options.chroot)
605 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
606 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
607
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500608 # Wait for the child to create the net ns.
609 ns_create_lock.Wait()
610 del ns_create_lock
611
Josh Triplett472a4182013-03-08 11:48:57 -0800612 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500613 'User #%u' % uid,
614 'Group #%u' % gid,
615 'PidFile %s' % pid_file,
616 'ErrorLog %s' % log_file,
617 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
618 'ServerName %s' % PROXY_HOST_IP,
619 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400620 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800621 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500622 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
623 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800624 ]
625 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700626 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
627 veth_guest),
628 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
629 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500630 ('ip', 'link', 'set', veth_host, 'up'),
631 ([apache_bin, '-f', '/dev/null'] +
632 [arg for d in apache_directives for arg in ('-C', d)]),
633 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800634 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700635 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800636 try:
637 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500638 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500639 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800640 # Clean up existing interfaces, if any.
641 cmd_cleanup = ('ip', 'link', 'del', veth_host)
642 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400643 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800644 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700645 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500646 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500647
648 # Signal the child that the net ns/proxy is fully configured now.
649 ns_setup_lock.Post()
650 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800651
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400652 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800653
654
Mike Frysingera78a56e2012-11-20 06:02:30 -0500655def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700656 """Re-execute cros_sdk as root.
657
658 Also unshare the mount namespace so as to ensure that processes outside
659 the chroot can't mess with our mounts.
660 """
661 if os.geteuid() != 0:
Mike Frysinger1f113512020-07-29 03:36:57 -0400662 # Make sure to preserve the active Python executable in case the version
663 # we're running as is not the default one found via the (new) $PATH.
664 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500665 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500666 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700667
668
Mike Frysinger34db8692013-11-11 14:54:08 -0500669def _CreateParser(sdk_latest_version, bootstrap_latest_version):
670 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400671 usage = ('usage: %(prog)s [options] '
672 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700673 parser = commandline.ArgumentParser(
674 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700675
Mike Frysinger34db8692013-11-11 14:54:08 -0500676 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500677 default_chroot = os.path.join(constants.SOURCE_ROOT,
678 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400679 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700680 '--chroot',
681 dest='chroot',
682 default=default_chroot,
683 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700684 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700685 parser.add_argument(
686 '--nouse-image',
687 dest='use_image',
688 action='store_false',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700689 default=False,
Manoj Guptab12f7302019-06-03 16:40:14 -0700690 help='Do not mount the chroot on a loopback image; '
691 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600692 parser.add_argument(
693 '--use-image',
694 dest='use_image',
695 action='store_true',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700696 default=False,
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600697 help='Mount the chroot on a loopback image '
698 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800699
Manoj Guptab12f7302019-06-03 16:40:14 -0700700 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600701 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700702 '--chrome_root',
703 type='path',
704 help='Mount this chrome root into the SDK chroot')
705 parser.add_argument(
706 '--chrome_root_mount',
707 type='path',
708 help='Mount chrome into this path inside SDK chroot')
709 parser.add_argument(
710 '--nousepkg',
711 action='store_true',
712 default=False,
713 help='Do not use binary packages when creating a chroot.')
714 parser.add_argument(
715 '-u',
716 '--url',
717 dest='sdk_url',
718 help='Use sdk tarball located at this url. Use file:// '
719 'for local files.')
720 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700721 '--sdk-version',
722 help=('Use this sdk version. For prebuilt, current is %r'
723 ', for bootstrapping it is %r.' % (sdk_latest_version,
724 bootstrap_latest_version)))
725 parser.add_argument(
726 '--goma_dir',
727 type='path',
728 help='Goma installed directory to mount into the chroot.')
729 parser.add_argument(
730 '--goma_client_json',
731 type='path',
732 help='Service account json file to use goma on bot. '
733 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800734
735 # Use type=str instead of type='path' to prevent the given path from being
736 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700737 parser.add_argument(
738 '--working-dir',
739 type=str,
740 help='Run the command in specific working directory in '
741 'chroot. If the given directory is a relative '
742 'path, this program will transfer the path to '
743 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800744
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400745 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500746
747 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400748 group = parser.add_argument_group('Commands')
749 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700750 '--enter',
751 action='store_true',
752 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500753 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400754 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700755 '--create',
756 action='store_true',
757 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500758 help='Create the chroot only if it does not already exist. '
759 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400760 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700761 '--bootstrap',
762 action='store_true',
763 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500764 help='Build everything from scratch, including the sdk. '
765 'Use this only if you need to validate a change '
766 'that affects SDK creation itself (toolchain and '
767 'build are typically the only folk who need this). '
768 'Note this will quite heavily slow down the build. '
769 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400770 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700771 '-r',
772 '--replace',
773 action='store_true',
774 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500775 help='Replace an existing SDK chroot. Basically an alias '
776 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400777 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700778 '--delete',
779 action='store_true',
780 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500781 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400782 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600783 '--force',
784 action='store_true',
785 default=False,
786 help='Force unmount/delete of the current SDK chroot even if '
787 'obtaining the write lock fails.')
788 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700789 '--unmount',
790 action='store_true',
791 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600792 help='Unmount and clean up devices associated with the '
793 'SDK chroot if it exists. This does not delete the '
794 'backing image file, so the same chroot can be later '
795 're-mounted for reuse. To fully delete the chroot, use '
796 '--delete. This is primarily useful for working on '
797 'cros_sdk or the chroot setup; you should not need it '
798 'under normal circumstances.')
799 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700800 '--download',
801 action='store_true',
802 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500803 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600804 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700805 '--snapshot-create',
806 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600807 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700808 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600809 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700810 '--snapshot-restore',
811 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600812 help='Restore the chroot to a previously created snapshot.')
813 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700814 '--snapshot-delete',
815 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600816 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700817 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600818 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700819 '--snapshot-list',
820 action='store_true',
821 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600822 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500823 commands = group
824
Mike Frysinger80dfce92014-04-21 10:58:53 -0400825 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400826 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700827 group.add_argument(
828 '--proxy-sim',
829 action='store_true',
830 default=False,
831 help='Simulate a restrictive network requiring an outbound'
832 ' proxy.')
Mike Frysinger79024a32021-04-05 03:28:35 -0400833 for ns, default in (('pid', True), ('net', None)):
834 group.add_argument(
835 f'--ns-{ns}',
836 default=default,
837 action='store_true',
838 help=f'Create a new {ns} namespace.')
839 group.add_argument(
840 f'--no-ns-{ns}',
841 dest=f'ns_{ns}',
842 action='store_false',
843 help=f'Do not create a new {ns} namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400844
Mike Frysinger34db8692013-11-11 14:54:08 -0500845 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400846 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500847 'Internal Chromium OS Build Team Options',
848 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700849 group.add_argument(
850 '--buildbot-log-version',
851 default=False,
852 action='store_true',
853 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500854
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400855 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500856
857
858def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600859 # Turn on strict sudo checks.
860 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400861 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500862 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
863 ignore_missing=True)
864 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700865 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700866
867 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
868 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
869 bootstrap_latest_version = (
870 sdk_latest_version
871 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500872 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400873 options = parser.parse_args(argv)
874 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800875
876 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500877 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800878
Brian Harring1790ac42012-09-23 08:53:33 -0700879 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700880 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600881 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700882 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400883 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700884
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400885 # Merge the outside PATH setting if we re-execed ourselves.
886 if 'CHROMEOS_SUDO_PATH' in os.environ:
887 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
888 os.environ['PATH'])
889
Josh Triplett472a4182013-03-08 11:48:57 -0800890 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
891 if options.proxy_sim:
892 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600893 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800894
Benjamin Gordon040a1162017-06-29 13:44:47 -0600895 if (sdk_latest_version == '<unknown>' or
896 bootstrap_latest_version == '<unknown>'):
897 cros_build_lib.Die(
898 'No SDK version was found. '
899 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
900 'Please change to a directory inside your Chromium OS source tree\n'
901 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400902 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600903
Manoj Guptab12f7302019-06-03 16:40:14 -0700904 any_snapshot_operation = (
905 options.snapshot_create or options.snapshot_restore or
906 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600907
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)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400937 parser.error('Trying to enter or snapshot the chroot when --delete '
938 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700939
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 Gordon87b068a2020-11-02 11:22:16 -0700947 # If there is an existing chroot image and we're not removing it then force
948 # use_image on. This ensures that people don't have to remember to pass
949 # --use-image after a reboot to avoid losing access to their existing chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600950 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700951 img_path = _ImageFileForChroot(options.chroot)
Benjamin Gordon832a4412020-12-08 10:39:16 -0700952 if (not options.use_image and not options.delete and not options.unmount
953 and os.path.exists(img_path)):
954 if chroot_exists:
955 # If the chroot is already populated, make sure it has something
956 # mounted on it before we assume it came from an image.
957 cmd = ['mountpoint', '-q', options.chroot]
958 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
959 options.use_image = True
960
961 else:
962 logging.notice('Existing chroot image %s found. Forcing --use-image on.',
963 img_path)
964 options.use_image = True
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700965
Benjamin Gordon9bce7032020-11-19 09:58:44 -0700966 if any_snapshot_operation and not options.use_image:
967 if os.path.exists(img_path):
968 options.use_image = True
969 else:
970 cros_build_lib.Die('Snapshot operations are not compatible with '
971 '--nouse-image.')
972
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700973 # Discern if we need to create the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600974 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600975 not options.unmount and not missing_image_tools and
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700976 os.path.exists(img_path)):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600977 # Try to re-mount an existing image in case the user has rebooted.
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500978 with locking.FileLock(lock_path, 'chroot lock') as lock:
979 logging.debug('Checking if existing chroot image can be mounted.')
980 lock.write_lock()
981 cros_sdk_lib.MountChroot(options.chroot, create=False)
982 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
983 if chroot_exists:
984 logging.notice('Mounted existing image %s on chroot', img_path)
Brian Harring218e13c2012-10-10 16:21:26 -0700985
986 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600987 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700988 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800989
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600990 # Make sure we will download if we plan to create.
991 options.download |= options.create
992
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600993 # Anything that needs to manipulate the main chroot mount or communicate with
994 # LVM needs to be done here before we enter the new namespaces.
995
996 # If deleting, do it regardless of the use_image flag so that a
997 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600998 if options.delete:
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500999 # Set a timeout of 300 seconds when getting the lock.
1000 with locking.FileLock(lock_path, 'chroot lock',
1001 blocking_timeout=300) as lock:
1002 try:
1003 lock.write_lock()
1004 except timeout_util.TimeoutError as e:
1005 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
1006 if not options.force:
1007 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001008 else:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001009 logging.warning(
1010 'cros_sdk was invoked with force option, continuing.')
Mike Frysingerf6fe6d02021-06-16 20:26:35 -04001011 logging.notice('Deleting chroot.')
1012 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001013
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001014 # If cleanup was requested, we have to do it while we're still in the original
1015 # namespace. Since cleaning up the mount will interfere with any other
1016 # commands, we exit here. The check above should have made sure that no other
1017 # action was requested, anyway.
1018 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001019 # Set a timeout of 300 seconds when getting the lock.
1020 with locking.FileLock(lock_path, 'chroot lock',
1021 blocking_timeout=300) as lock:
1022 try:
1023 lock.write_lock()
1024 except timeout_util.TimeoutError as e:
1025 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -06001026 logging.warning(
1027 'Continuing with CleanupChroot(%s), which will umount the tree.',
1028 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001029 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1030 # even if we don't get the lock because it will attempt to unmount the
1031 # tree and will print diagnostic information from 'fuser', 'lsof', and
1032 # 'ps'.
Mike Frysingerf6fe6d02021-06-16 20:26:35 -04001033 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001034 sys.exit(0)
1035
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001036 # Make sure the main chroot mount is visible. Contents will be filled in
1037 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001038 if options.create and options.use_image:
1039 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001040 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001041Please make sure the lvm2 and thin-provisioning-tools packages
1042are installed on your host.
1043Example(ubuntu):
1044 sudo apt-get install lvm2 thin-provisioning-tools
1045
1046If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001047snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001048
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001049 logging.debug('Making sure chroot image is mounted.')
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001050 with locking.FileLock(lock_path, 'chroot lock') as lock:
1051 lock.write_lock()
1052 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1053 cros_build_lib.Die('Unable to mount %s on chroot',
1054 _ImageFileForChroot(options.chroot))
1055 logging.notice('Mounted %s on chroot',
1056 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001057
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001058 # Snapshot operations will always need the VG/LV, but other actions won't.
1059 if any_snapshot_operation:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001060 with locking.FileLock(lock_path, 'chroot lock') as lock:
1061 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(options.chroot)
1062 if not chroot_vg or not chroot_lv:
1063 cros_build_lib.Die('Unable to find VG/LV for chroot %s', options.chroot)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001064
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001065 # Delete snapshot before creating a new one. This allows the user to
1066 # throw out old state, create a new snapshot, and enter the chroot in a
1067 # single call to cros_sdk. Since restore involves deleting, also do it
1068 # before creating.
1069 if options.snapshot_restore:
1070 lock.write_lock()
1071 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1072 if options.snapshot_restore not in valid_snapshots:
1073 cros_build_lib.Die(
1074 '%s is not a valid snapshot to restore to. Valid snapshots: %s',
1075 options.snapshot_restore, ', '.join(valid_snapshots))
1076 osutils.UmountTree(options.chroot)
1077 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1078 chroot_lv):
1079 cros_build_lib.Die('Unable to restore chroot to snapshot.')
1080 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1081 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001082
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001083 # Use a read lock for snapshot delete and create even though they modify
1084 # the filesystem, because they don't modify the mounted chroot itself.
1085 # The underlying LVM commands take their own locks, so conflicting
1086 # concurrent operations here may crash cros_sdk, but won't corrupt the
1087 # chroot image. This tradeoff seems worth it to allow snapshot
1088 # operations on chroots that have a process inside.
1089 if options.snapshot_delete:
1090 lock.read_lock()
1091 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001092
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001093 if options.snapshot_create:
1094 lock.read_lock()
1095 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1096 chroot_lv):
1097 cros_build_lib.Die('Unable to create snapshot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001098
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001099 img_path = _ImageFileForChroot(options.chroot)
1100 if (options.use_image and os.path.exists(options.chroot) and
1101 os.path.exists(img_path)):
1102 img_stat = os.stat(img_path)
1103 img_used_bytes = img_stat.st_blocks * 512
1104
1105 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001106 mount_used_bytes = mount_stat.f_frsize * (
1107 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001108
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001109 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001110 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1111 logging.notice('%s is using %s GiB more than needed. Running '
Sergey Frolov1cb46ec2020-12-09 21:46:16 -07001112 'fstrim in background.', img_path, extra_gbs)
1113 pid = os.fork()
1114 if pid == 0:
1115 try:
1116 # Directly call Popen to run fstrim concurrently.
1117 cmd = ['fstrim', options.chroot]
1118 subprocess.Popen(cmd, close_fds=True, shell=False)
1119 except subprocess.SubprocessError as e:
1120 logging.warning(
1121 'Running fstrim failed. Consider running fstrim on '
1122 'your chroot manually.\n%s', e)
1123 os._exit(0) # pylint: disable=protected-access
1124 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001125
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001126 # Enter a new set of namespaces. Everything after here cannot directly affect
1127 # the hosts's mounts or alter LVM volumes.
Mike Frysinger79024a32021-04-05 03:28:35 -04001128 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001129
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001130 if options.snapshot_list:
1131 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1132 print(snap)
1133 sys.exit(0)
1134
Brian Harringb938c782012-02-29 15:14:38 -08001135 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001136 sdk_version = (
1137 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001138 else:
1139 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001140 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001141 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001142
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001143 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001144 if options.download:
1145 if options.sdk_url:
1146 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001147 else:
1148 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001149
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001150 with locking.FileLock(lock_path, 'chroot lock') as lock:
1151 if options.proxy_sim:
1152 _ProxySimSetup(options)
Josh Triplett472a4182013-03-08 11:48:57 -08001153
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001154 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1155 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
1156 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001157
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001158 for target in (sdk_cache, distfiles_cache):
1159 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1160 if not os.path.exists(src):
1161 osutils.SafeMakedirsNonRoot(target)
1162 continue
1163 lock.write_lock(
1164 'Upgrade to %r needed but chroot is locked; please exit '
1165 'all instances so this upgrade can finish.' % src)
1166 if not os.path.exists(src):
1167 # Note that while waiting for the write lock, src may've vanished;
1168 # it's a rare race during the upgrade process that's a byproduct
1169 # of us avoiding taking a write lock to do the src check. If we
1170 # took a write lock for that check, it would effectively limit
1171 # all cros_sdk for a chroot to a single instance.
1172 osutils.SafeMakedirsNonRoot(target)
1173 elif not os.path.exists(target):
1174 # Upgrade occurred, but a reversion, or something whacky
1175 # occurred writing to the old location. Wipe and continue.
1176 os.rename(src, target)
1177 else:
1178 # Upgrade occurred once already, but either a reversion or
1179 # some before/after separate cros_sdk usage is at play.
1180 # Wipe and continue.
1181 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001182
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001183 if options.download:
1184 lock.write_lock()
1185 sdk_tarball = FetchRemoteTarballs(
1186 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001187
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001188 if options.create:
1189 lock.write_lock()
1190 # Recheck if the chroot is set up here before creating to make sure we
1191 # account for whatever the various delete/unmount/remount steps above
1192 # have done.
1193 if cros_sdk_lib.IsChrootReady(options.chroot):
1194 logging.debug('Chroot already exists. Skipping creation.')
1195 else:
1196 CreateChroot(
1197 options.chroot,
1198 sdk_tarball,
1199 options.cache_dir,
1200 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001201
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001202 if options.enter:
1203 lock.read_lock()
1204 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
1205 options.chrome_root_mount, options.goma_dir,
1206 options.goma_client_json, options.working_dir,
1207 chroot_command)