blob: 9b22a1c265426f21ed6ef6eff081d931cb36852c [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
203def DeleteChroot(chroot_path):
204 """Deletes an existing chroot"""
Manoj Guptab12f7302019-06-03 16:40:14 -0700205 cmd = MAKE_CHROOT + ['--chroot', chroot_path, '--delete']
Brian Harringb938c782012-02-29 15:14:38 -0800206 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700207 logging.notice('Deleting chroot.')
Mike Frysinger3e8de442020-02-14 16:46:28 -0500208 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500209 except cros_build_lib.RunCommandError as e:
210 cros_build_lib.Die('Deleting chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800211
212
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600213def CleanupChroot(chroot_path):
214 """Unmounts a chroot and cleans up any associated devices."""
Don Garrett36650112018-06-28 15:54:34 -0700215 cros_sdk_lib.CleanupChrootMount(chroot_path, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600216
217
Brian Harringae0a5322012-09-15 01:46:51 -0700218def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500219 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800220 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400221 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
Alex Klein875b30e2021-01-05 14:56:33 -0700222 if st.f_flag & os.ST_NOSUID:
Mike Frysingere5456972013-06-13 00:07:23 -0400223 cros_build_lib.Die('chroot cannot be in a nosuid mount')
224
Brian Harringae0a5322012-09-15 01:46:51 -0700225 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800226 if chrome_root:
227 cmd.extend(['--chrome_root', chrome_root])
228 if chrome_root_mount:
229 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900230 if goma_dir:
231 cmd.extend(['--goma_dir', goma_dir])
232 if goma_client_json:
233 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800234 if working_dir is not None:
235 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700236
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400237 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800238 cmd.append('--')
239 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700240
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700241 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000242 # Set rlimit and vm.max_map_count to accommodate this.
243 file_limit = 262144
244 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
245 resource.setrlimit(resource.RLIMIT_NOFILE,
246 (max(soft, file_limit), max(hard, file_limit)))
247 max_map_count = int(open('/proc/sys/vm/max_map_count').read())
248 if max_map_count < file_limit:
249 logging.notice(
250 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
251 open('/proc/sys/vm/max_map_count', 'w').write(f'{file_limit}\n')
Alex Klein1ec3e6a2020-04-03 11:13:50 -0600252 ret = cros_build_lib.dbg_run(cmd, check=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700253 # If we were in interactive mode, ignore the exit code; it'll be whatever
254 # they last ran w/in the chroot and won't matter to us one way or another.
255 # Note this does allow chroot entrance to fail and be ignored during
256 # interactive; this is however a rare case and the user will immediately
257 # see it (nor will they be checking the exit code manually).
258 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700259 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800260
261
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600262def _ImageFileForChroot(chroot):
263 """Find the image file that should be associated with |chroot|.
264
265 This function does not check if the image exists; it simply returns the
266 filename that would be used.
267
268 Args:
269 chroot: Path to the chroot.
270
271 Returns:
272 Path to an image file that would be associated with chroot.
273 """
274 return chroot.rstrip('/') + '.img'
275
276
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600277def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
278 """Create a snapshot for the specified chroot VG/LV.
279
280 Args:
281 snapshot_name: The name of the new snapshot.
282 chroot_vg: The name of the VG containing the origin LV.
283 chroot_lv: The name of the origin LV.
284
285 Returns:
286 True if the snapshot was created, or False if a snapshot with the same
287 name already exists.
288
289 Raises:
290 SystemExit: The lvcreate command failed.
291 """
292 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700293 logging.error(
294 'Cannot create snapshot %s: A volume with that name already '
295 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600296 return False
297
Manoj Guptab12f7302019-06-03 16:40:14 -0700298 cmd = [
299 'lvcreate', '-s', '--name', snapshot_name,
300 '%s/%s' % (chroot_vg, chroot_lv)
301 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600302 try:
303 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
304 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500305 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600306 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500307 except cros_build_lib.RunCommandError as e:
308 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600309
310
311def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
312 """Delete the named snapshot from the specified chroot VG.
313
314 If the requested snapshot is not found, nothing happens. The main chroot LV
315 and internal thinpool LV cannot be deleted with this function.
316
317 Args:
318 snapshot_name: The name of the snapshot to delete.
319 chroot_vg: The name of the VG containing the origin LV.
320 chroot_lv: The name of the origin LV.
321
322 Raises:
323 SystemExit: The lvremove command failed.
324 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600325 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
326 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700327 logging.error(
328 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
329 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600330 return
331
332 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
333 return
334
335 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
336 try:
337 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500338 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500339 except cros_build_lib.RunCommandError as e:
340 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600341
342
343def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
344 """Restore the chroot to an existing snapshot.
345
346 This is done by renaming the original |chroot_lv| LV to a temporary name,
347 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
348 now unused LV. If an error occurs, attempts to rename the original snapshot
349 back to |chroot_lv| to leave the chroot unchanged.
350
351 The chroot must be unmounted before calling this function, and will be left
352 unmounted after this function returns.
353
354 Args:
355 snapshot_name: The name of the snapshot to restore. This snapshot will no
356 longer be accessible at its original name after this function finishes.
357 chroot_vg: The VG containing the chroot LV and snapshot LV.
358 chroot_lv: The name of the original chroot LV.
359
360 Returns:
361 True if the chroot was restored to the requested snapshot, or False if
362 the snapshot wasn't found or isn't valid.
363
364 Raises:
365 SystemExit: Any of the LVM commands failed.
366 """
367 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600368 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
369 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600370 snapshot_name not in valid_snapshots):
371 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
372 snapshot_name, ', '.join(valid_snapshots))
373 return False
374
375 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
376 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
377 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500378 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500379 except cros_build_lib.RunCommandError as e:
380 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600381
382 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
383 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500384 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500385 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600386 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
387 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500388 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500389 except cros_build_lib.RunCommandError as e:
390 cros_build_lib.Die(
391 'Failed to rename %s to chroot and failed to restore %s back to '
392 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
393 cros_build_lib.Die(
394 'Failed to rename %s to chroot! Original chroot LV has '
395 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600396
397 # Some versions of LVM set snapshots to be skipped at auto-activate time.
398 # Other versions don't have this flag at all. We run lvchange to try
399 # disabling auto-skip and activating the volume, but ignore errors. Versions
400 # that don't have the flag should be auto-activated.
401 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
402 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400403 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500404 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600405
406 # Activate the LV in case the lvchange above was needed. Activating an LV
407 # that is already active shouldn't do anything, so this is safe to run even if
408 # the -kn wasn't needed.
409 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500410 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600411
412 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
413 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500414 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500415 except cros_build_lib.RunCommandError as e:
416 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
417 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600418
419 return True
420
421
422def ListChrootSnapshots(chroot_vg, chroot_lv):
423 """Return all snapshots in |chroot_vg| regardless of origin volume.
424
425 Args:
426 chroot_vg: The name of the VG containing the chroot.
427 chroot_lv: The name of the chroot LV.
428
429 Returns:
430 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
431
432 Raises:
433 SystemExit: The lvs command failed.
434 """
435 if not chroot_vg or not chroot_lv:
436 return []
437
Manoj Guptab12f7302019-06-03 16:40:14 -0700438 cmd = [
439 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
440 '--separator', '\t', chroot_vg
441 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600442 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400443 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600444 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600445 except cros_build_lib.RunCommandError:
446 raise SystemExit('Running %r failed!' % cmd)
447
448 # Once the thin origin volume has been deleted, there's no way to tell a
449 # snapshot apart from any other volume. Since this VG is created and managed
450 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
451 # valid snapshots.
452 snapshots = []
453 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
454 for line in result.output.splitlines():
455 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700456 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600457 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600458 not snapshot_attrs.match(lv_attr)):
459 continue
460 snapshots.append(lv_name)
461 return snapshots
462
463
David James56e6c2c2012-10-24 23:54:41 -0700464def _SudoCommand():
465 """Get the 'sudo' command, along with all needed environment variables."""
466
David James5a73b4d2013-03-07 10:23:40 -0800467 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400468 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700469 cmd = ['sudo']
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400470 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700471 value = os.environ.get(key)
472 if value is not None:
473 cmd += ['%s=%s' % (key, value)]
474
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400475 # We keep PATH not for the chroot but for the re-exec & for programs we might
476 # run before we chroot into the SDK. The process that enters the SDK itself
477 # will take care of initializing PATH to the right value then. But we can't
478 # override the system's default PATH for root as that will hide /sbin.
479 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
480
David James56e6c2c2012-10-24 23:54:41 -0700481 # Pass in the path to the depot_tools so that users can access them from
482 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400483 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500484
David James56e6c2c2012-10-24 23:54:41 -0700485 return cmd
486
487
Josh Triplett472a4182013-03-08 11:48:57 -0800488def _ReportMissing(missing):
489 """Report missing utilities, then exit.
490
491 Args:
492 missing: List of missing utilities, as returned by
493 osutils.FindMissingBinaries. If non-empty, will not return.
494 """
495
496 if missing:
497 raise SystemExit(
498 'The tool(s) %s were not found.\n'
499 'Please install the appropriate package in your host.\n'
500 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700501 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800502
503
504def _ProxySimSetup(options):
505 """Set up proxy simulator, and return only in the child environment.
506
507 TODO: Ideally, this should support multiple concurrent invocations of
508 cros_sdk --proxy-sim; currently, such invocations will conflict with each
509 other due to the veth device names and IP addresses. Either this code would
510 need to generate fresh, unused names for all of these before forking, or it
511 would need to support multiple concurrent cros_sdk invocations sharing one
512 proxy and allowing it to exit when unused (without counting on any local
513 service-management infrastructure on the host).
514 """
515
516 may_need_mpm = False
517 apache_bin = osutils.Which('apache2')
518 if apache_bin is None:
519 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
520 if apache_bin is None:
521 _ReportMissing(('apache2',))
522 else:
523 may_need_mpm = True
524
525 # Module names and .so names included for ease of grepping.
526 apache_modules = [('proxy_module', 'mod_proxy.so'),
527 ('proxy_connect_module', 'mod_proxy_connect.so'),
528 ('proxy_http_module', 'mod_proxy_http.so'),
529 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
530
531 # Find the apache module directory, and make sure it has the modules we need.
532 module_dirs = {}
533 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400534 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800535 for f in glob.glob(os.path.join(g, so)):
536 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400537 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800538 if len(modules_found) == len(apache_modules):
539 break
540 else:
541 # Appease cros lint, which doesn't understand that this else block will not
542 # fall through to the subsequent code which relies on apache_module_path.
543 apache_module_path = None
544 raise SystemExit(
545 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500546 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800547
548 def check_add_module(name):
549 so = 'mod_%s.so' % name
550 if os.access(os.path.join(apache_module_path, so), os.F_OK):
551 mod = '%s_module' % name
552 apache_modules.append((mod, so))
553 return True
554 return False
555
556 check_add_module('authz_core')
557 if may_need_mpm:
558 for mpm in PROXY_APACHE_MPMS:
559 if check_add_module('mpm_%s' % mpm):
560 break
561
562 veth_host = '%s-host' % PROXY_VETH_PREFIX
563 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
564
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500565 # Set up locks to sync the net namespace setup. We need the child to create
566 # the net ns first, and then have the parent assign the guest end of the veth
567 # interface to the child's new network namespace & bring up the proxy. Only
568 # then can the child move forward and rely on the network being up.
569 ns_create_lock = locking.PipeLock()
570 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800571
572 pid = os.fork()
573 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500574 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800575 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500576
577 # Signal the parent the ns is ready to be configured.
578 ns_create_lock.Post()
579 del ns_create_lock
580
581 # Wait for the parent to finish setting up the ns/proxy.
582 ns_setup_lock.Wait()
583 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800584
585 # Set up child side of the network.
586 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500587 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700588 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500589 'dev', veth_guest),
590 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800591 )
592 try:
593 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500594 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500595 except cros_build_lib.RunCommandError as e:
596 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800597
598 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
599 for proto in ('http', 'https', 'ftp'):
600 os.environ[proto + '_proxy'] = proxy_url
601 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
602 os.environ.pop(v, None)
603 return
604
Josh Triplett472a4182013-03-08 11:48:57 -0800605 # Set up parent side of the network.
606 uid = int(os.environ.get('SUDO_UID', '0'))
607 gid = int(os.environ.get('SUDO_GID', '0'))
608 if uid == 0 or gid == 0:
609 for username in PROXY_APACHE_FALLBACK_USERS:
610 try:
611 pwnam = pwd.getpwnam(username)
612 uid, gid = pwnam.pw_uid, pwnam.pw_gid
613 break
614 except KeyError:
615 continue
616 if uid == 0 or gid == 0:
617 raise SystemExit('Could not find a non-root user to run Apache as')
618
619 chroot_parent, chroot_base = os.path.split(options.chroot)
620 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
621 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
622
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500623 # Wait for the child to create the net ns.
624 ns_create_lock.Wait()
625 del ns_create_lock
626
Josh Triplett472a4182013-03-08 11:48:57 -0800627 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500628 'User #%u' % uid,
629 'Group #%u' % gid,
630 'PidFile %s' % pid_file,
631 'ErrorLog %s' % log_file,
632 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
633 'ServerName %s' % PROXY_HOST_IP,
634 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400635 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800636 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500637 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
638 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800639 ]
640 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700641 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
642 veth_guest),
643 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
644 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500645 ('ip', 'link', 'set', veth_host, 'up'),
646 ([apache_bin, '-f', '/dev/null'] +
647 [arg for d in apache_directives for arg in ('-C', d)]),
648 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800649 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700650 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800651 try:
652 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500653 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500654 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800655 # Clean up existing interfaces, if any.
656 cmd_cleanup = ('ip', 'link', 'del', veth_host)
657 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400658 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800659 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700660 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500661 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500662
663 # Signal the child that the net ns/proxy is fully configured now.
664 ns_setup_lock.Post()
665 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800666
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400667 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800668
669
Mike Frysingera78a56e2012-11-20 06:02:30 -0500670def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700671 """Re-execute cros_sdk as root.
672
673 Also unshare the mount namespace so as to ensure that processes outside
674 the chroot can't mess with our mounts.
675 """
676 if os.geteuid() != 0:
Mike Frysinger1f113512020-07-29 03:36:57 -0400677 # Make sure to preserve the active Python executable in case the version
678 # we're running as is not the default one found via the (new) $PATH.
679 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500680 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500681 os.execvp(cmd[0], cmd)
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',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700704 default=False,
Manoj Guptab12f7302019-06-03 16:40:14 -0700705 help='Do not mount the chroot on a loopback image; '
706 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600707 parser.add_argument(
708 '--use-image',
709 dest='use_image',
710 action='store_true',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700711 default=False,
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600712 help='Mount the chroot on a loopback image '
713 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800714
Manoj Guptab12f7302019-06-03 16:40:14 -0700715 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600716 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700717 '--chrome_root',
718 type='path',
719 help='Mount this chrome root into the SDK chroot')
720 parser.add_argument(
721 '--chrome_root_mount',
722 type='path',
723 help='Mount chrome into this path inside SDK chroot')
724 parser.add_argument(
725 '--nousepkg',
726 action='store_true',
727 default=False,
728 help='Do not use binary packages when creating a chroot.')
729 parser.add_argument(
730 '-u',
731 '--url',
732 dest='sdk_url',
733 help='Use sdk tarball located at this url. Use file:// '
734 'for local files.')
735 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700736 '--sdk-version',
737 help=('Use this sdk version. For prebuilt, current is %r'
738 ', for bootstrapping it is %r.' % (sdk_latest_version,
739 bootstrap_latest_version)))
740 parser.add_argument(
741 '--goma_dir',
742 type='path',
743 help='Goma installed directory to mount into the chroot.')
744 parser.add_argument(
745 '--goma_client_json',
746 type='path',
747 help='Service account json file to use goma on bot. '
748 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800749
750 # Use type=str instead of type='path' to prevent the given path from being
751 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700752 parser.add_argument(
753 '--working-dir',
754 type=str,
755 help='Run the command in specific working directory in '
756 'chroot. If the given directory is a relative '
757 'path, this program will transfer the path to '
758 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800759
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400760 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500761
762 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400763 group = parser.add_argument_group('Commands')
764 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700765 '--enter',
766 action='store_true',
767 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500768 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400769 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700770 '--create',
771 action='store_true',
772 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500773 help='Create the chroot only if it does not already exist. '
774 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400775 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700776 '--bootstrap',
777 action='store_true',
778 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500779 help='Build everything from scratch, including the sdk. '
780 'Use this only if you need to validate a change '
781 'that affects SDK creation itself (toolchain and '
782 'build are typically the only folk who need this). '
783 'Note this will quite heavily slow down the build. '
784 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400785 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700786 '-r',
787 '--replace',
788 action='store_true',
789 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500790 help='Replace an existing SDK chroot. Basically an alias '
791 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400792 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700793 '--delete',
794 action='store_true',
795 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500796 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400797 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600798 '--force',
799 action='store_true',
800 default=False,
801 help='Force unmount/delete of the current SDK chroot even if '
802 'obtaining the write lock fails.')
803 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700804 '--unmount',
805 action='store_true',
806 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600807 help='Unmount and clean up devices associated with the '
808 'SDK chroot if it exists. This does not delete the '
809 'backing image file, so the same chroot can be later '
810 're-mounted for reuse. To fully delete the chroot, use '
811 '--delete. This is primarily useful for working on '
812 'cros_sdk or the chroot setup; you should not need it '
813 'under normal circumstances.')
814 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700815 '--download',
816 action='store_true',
817 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500818 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600819 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700820 '--snapshot-create',
821 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600822 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700823 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600824 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700825 '--snapshot-restore',
826 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600827 help='Restore the chroot to a previously created snapshot.')
828 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700829 '--snapshot-delete',
830 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600831 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700832 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600833 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700834 '--snapshot-list',
835 action='store_true',
836 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600837 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500838 commands = group
839
Mike Frysinger80dfce92014-04-21 10:58:53 -0400840 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400841 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700842 group.add_argument(
843 '--proxy-sim',
844 action='store_true',
845 default=False,
846 help='Simulate a restrictive network requiring an outbound'
847 ' proxy.')
Mike Frysinger79024a32021-04-05 03:28:35 -0400848 for ns, default in (('pid', True), ('net', None)):
849 group.add_argument(
850 f'--ns-{ns}',
851 default=default,
852 action='store_true',
853 help=f'Create a new {ns} namespace.')
854 group.add_argument(
855 f'--no-ns-{ns}',
856 dest=f'ns_{ns}',
857 action='store_false',
858 help=f'Do not create a new {ns} namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400859
Mike Frysinger34db8692013-11-11 14:54:08 -0500860 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400861 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500862 'Internal Chromium OS Build Team Options',
863 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700864 group.add_argument(
865 '--buildbot-log-version',
866 default=False,
867 action='store_true',
868 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500869
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400870 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500871
872
873def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600874 # Turn on strict sudo checks.
875 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400876 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500877 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
878 ignore_missing=True)
879 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700880 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700881
882 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
883 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
884 bootstrap_latest_version = (
885 sdk_latest_version
886 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500887 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400888 options = parser.parse_args(argv)
889 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800890
891 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500892 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800893
Brian Harring1790ac42012-09-23 08:53:33 -0700894 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700895 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600896 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700897 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400898 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700899
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400900 # Merge the outside PATH setting if we re-execed ourselves.
901 if 'CHROMEOS_SUDO_PATH' in os.environ:
902 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
903 os.environ['PATH'])
904
Josh Triplett472a4182013-03-08 11:48:57 -0800905 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
906 if options.proxy_sim:
907 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600908 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800909
Benjamin Gordon040a1162017-06-29 13:44:47 -0600910 if (sdk_latest_version == '<unknown>' or
911 bootstrap_latest_version == '<unknown>'):
912 cros_build_lib.Die(
913 'No SDK version was found. '
914 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
915 'Please change to a directory inside your Chromium OS source tree\n'
916 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400917 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600918
Manoj Guptab12f7302019-06-03 16:40:14 -0700919 any_snapshot_operation = (
920 options.snapshot_create or options.snapshot_restore or
921 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600922
Manoj Guptab12f7302019-06-03 16:40:14 -0700923 if (options.snapshot_delete and
924 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600925 parser.error('Cannot --snapshot_delete the same snapshot you are '
926 'restoring with --snapshot_restore.')
927
David James471532c2013-01-21 10:23:31 -0800928 _ReExecuteIfNeeded([sys.argv[0]] + argv)
929
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600930 lock_path = os.path.dirname(options.chroot)
931 lock_path = os.path.join(
932 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
933
Brian Harring218e13c2012-10-10 16:21:26 -0700934 # Expand out the aliases...
935 if options.replace:
936 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800937
Brian Harring218e13c2012-10-10 16:21:26 -0700938 if options.bootstrap:
939 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800940
Brian Harring218e13c2012-10-10 16:21:26 -0700941 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400942 # pylint: disable=protected-access
943 # This _group_actions access sucks, but upstream decided to not include an
944 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700945 options.enter |= not any(
946 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400947 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700948 options.enter |= bool(chroot_command)
949
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600950 if (options.delete and not options.create and
951 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400952 parser.error('Trying to enter or snapshot the chroot when --delete '
953 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700954
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600955 if (options.unmount and
956 (options.create or options.enter or any_snapshot_operation)):
957 parser.error('--unmount cannot be specified with other chroot actions.')
958
Yong Hong84ba9172018-02-07 01:37:42 +0800959 if options.working_dir is not None and not os.path.isabs(options.working_dir):
960 options.working_dir = path_util.ToChrootPath(options.working_dir)
961
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700962 # If there is an existing chroot image and we're not removing it then force
963 # use_image on. This ensures that people don't have to remember to pass
964 # --use-image after a reboot to avoid losing access to their existing chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600965 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700966 img_path = _ImageFileForChroot(options.chroot)
Benjamin Gordon832a4412020-12-08 10:39:16 -0700967 if (not options.use_image and not options.delete and not options.unmount
968 and os.path.exists(img_path)):
969 if chroot_exists:
970 # If the chroot is already populated, make sure it has something
971 # mounted on it before we assume it came from an image.
972 cmd = ['mountpoint', '-q', options.chroot]
973 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
974 options.use_image = True
975
976 else:
977 logging.notice('Existing chroot image %s found. Forcing --use-image on.',
978 img_path)
979 options.use_image = True
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700980
Benjamin Gordon9bce7032020-11-19 09:58:44 -0700981 if any_snapshot_operation and not options.use_image:
982 if os.path.exists(img_path):
983 options.use_image = True
984 else:
985 cros_build_lib.Die('Snapshot operations are not compatible with '
986 '--nouse-image.')
987
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700988 # Discern if we need to create the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600989 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600990 not options.unmount and not missing_image_tools and
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700991 os.path.exists(img_path)):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600992 # Try to re-mount an existing image in case the user has rebooted.
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500993 with locking.FileLock(lock_path, 'chroot lock') as lock:
994 logging.debug('Checking if existing chroot image can be mounted.')
995 lock.write_lock()
996 cros_sdk_lib.MountChroot(options.chroot, create=False)
997 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
998 if chroot_exists:
999 logging.notice('Mounted existing image %s on chroot', img_path)
Brian Harring218e13c2012-10-10 16:21:26 -07001000
1001 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001002 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -07001003 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -08001004
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001005 # Make sure we will download if we plan to create.
1006 options.download |= options.create
1007
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001008 # Anything that needs to manipulate the main chroot mount or communicate with
1009 # LVM needs to be done here before we enter the new namespaces.
1010
1011 # If deleting, do it regardless of the use_image flag so that a
1012 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001013 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
1014 # favor of this block.
1015 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001016 if options.delete:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001017 # Set a timeout of 300 seconds when getting the lock.
1018 with locking.FileLock(lock_path, 'chroot lock',
1019 blocking_timeout=300) as lock:
1020 try:
1021 lock.write_lock()
1022 except timeout_util.TimeoutError as e:
1023 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
1024 if not options.force:
1025 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001026 else:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001027 logging.warning(
1028 'cros_sdk was invoked with force option, continuing.')
1029 if missing_image_tools:
1030 logging.notice('Unmounting chroot.')
1031 osutils.UmountTree(options.chroot)
1032 else:
1033 logging.notice('Deleting chroot.')
1034 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
1035 chroot_deleted = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001036
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001037 # If cleanup was requested, we have to do it while we're still in the original
1038 # namespace. Since cleaning up the mount will interfere with any other
1039 # commands, we exit here. The check above should have made sure that no other
1040 # action was requested, anyway.
1041 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001042 # Set a timeout of 300 seconds when getting the lock.
1043 with locking.FileLock(lock_path, 'chroot lock',
1044 blocking_timeout=300) as lock:
1045 try:
1046 lock.write_lock()
1047 except timeout_util.TimeoutError as e:
1048 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -06001049 logging.warning(
1050 'Continuing with CleanupChroot(%s), which will umount the tree.',
1051 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001052 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1053 # even if we don't get the lock because it will attempt to unmount the
1054 # tree and will print diagnostic information from 'fuser', 'lsof', and
1055 # 'ps'.
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001056 CleanupChroot(options.chroot)
1057 sys.exit(0)
1058
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001059 # Make sure the main chroot mount is visible. Contents will be filled in
1060 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001061 if options.create and options.use_image:
1062 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001063 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001064Please make sure the lvm2 and thin-provisioning-tools packages
1065are installed on your host.
1066Example(ubuntu):
1067 sudo apt-get install lvm2 thin-provisioning-tools
1068
1069If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001070snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001071
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001072 logging.debug('Making sure chroot image is mounted.')
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001073 with locking.FileLock(lock_path, 'chroot lock') as lock:
1074 lock.write_lock()
1075 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1076 cros_build_lib.Die('Unable to mount %s on chroot',
1077 _ImageFileForChroot(options.chroot))
1078 logging.notice('Mounted %s on chroot',
1079 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001080
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001081 # Snapshot operations will always need the VG/LV, but other actions won't.
1082 if any_snapshot_operation:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001083 with locking.FileLock(lock_path, 'chroot lock') as lock:
1084 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(options.chroot)
1085 if not chroot_vg or not chroot_lv:
1086 cros_build_lib.Die('Unable to find VG/LV for chroot %s', options.chroot)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001087
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001088 # Delete snapshot before creating a new one. This allows the user to
1089 # throw out old state, create a new snapshot, and enter the chroot in a
1090 # single call to cros_sdk. Since restore involves deleting, also do it
1091 # before creating.
1092 if options.snapshot_restore:
1093 lock.write_lock()
1094 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1095 if options.snapshot_restore not in valid_snapshots:
1096 cros_build_lib.Die(
1097 '%s is not a valid snapshot to restore to. Valid snapshots: %s',
1098 options.snapshot_restore, ', '.join(valid_snapshots))
1099 osutils.UmountTree(options.chroot)
1100 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1101 chroot_lv):
1102 cros_build_lib.Die('Unable to restore chroot to snapshot.')
1103 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1104 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001105
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001106 # Use a read lock for snapshot delete and create even though they modify
1107 # the filesystem, because they don't modify the mounted chroot itself.
1108 # The underlying LVM commands take their own locks, so conflicting
1109 # concurrent operations here may crash cros_sdk, but won't corrupt the
1110 # chroot image. This tradeoff seems worth it to allow snapshot
1111 # operations on chroots that have a process inside.
1112 if options.snapshot_delete:
1113 lock.read_lock()
1114 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001115
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001116 if options.snapshot_create:
1117 lock.read_lock()
1118 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1119 chroot_lv):
1120 cros_build_lib.Die('Unable to create snapshot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001121
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001122 img_path = _ImageFileForChroot(options.chroot)
1123 if (options.use_image and os.path.exists(options.chroot) and
1124 os.path.exists(img_path)):
1125 img_stat = os.stat(img_path)
1126 img_used_bytes = img_stat.st_blocks * 512
1127
1128 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001129 mount_used_bytes = mount_stat.f_frsize * (
1130 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001131
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001132 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001133 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1134 logging.notice('%s is using %s GiB more than needed. Running '
Sergey Frolov1cb46ec2020-12-09 21:46:16 -07001135 'fstrim in background.', img_path, extra_gbs)
1136 pid = os.fork()
1137 if pid == 0:
1138 try:
1139 # Directly call Popen to run fstrim concurrently.
1140 cmd = ['fstrim', options.chroot]
1141 subprocess.Popen(cmd, close_fds=True, shell=False)
1142 except subprocess.SubprocessError as e:
1143 logging.warning(
1144 'Running fstrim failed. Consider running fstrim on '
1145 'your chroot manually.\n%s', e)
1146 os._exit(0) # pylint: disable=protected-access
1147 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001148
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001149 # Enter a new set of namespaces. Everything after here cannot directly affect
1150 # the hosts's mounts or alter LVM volumes.
Mike Frysinger79024a32021-04-05 03:28:35 -04001151 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001152
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001153 if options.snapshot_list:
1154 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1155 print(snap)
1156 sys.exit(0)
1157
Brian Harringb938c782012-02-29 15:14:38 -08001158 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001159 sdk_version = (
1160 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001161 else:
1162 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001163 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001164 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001165
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001166 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001167 if options.download:
1168 if options.sdk_url:
1169 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001170 else:
1171 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001172
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001173 with locking.FileLock(lock_path, 'chroot lock') as lock:
1174 if options.proxy_sim:
1175 _ProxySimSetup(options)
Josh Triplett472a4182013-03-08 11:48:57 -08001176
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001177 if (options.delete and not chroot_deleted and
1178 (os.path.exists(options.chroot) or
1179 os.path.exists(_ImageFileForChroot(options.chroot)))):
1180 lock.write_lock()
1181 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001182
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001183 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1184 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
1185 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001186
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001187 for target in (sdk_cache, distfiles_cache):
1188 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1189 if not os.path.exists(src):
1190 osutils.SafeMakedirsNonRoot(target)
1191 continue
1192 lock.write_lock(
1193 'Upgrade to %r needed but chroot is locked; please exit '
1194 'all instances so this upgrade can finish.' % src)
1195 if not os.path.exists(src):
1196 # Note that while waiting for the write lock, src may've vanished;
1197 # it's a rare race during the upgrade process that's a byproduct
1198 # of us avoiding taking a write lock to do the src check. If we
1199 # took a write lock for that check, it would effectively limit
1200 # all cros_sdk for a chroot to a single instance.
1201 osutils.SafeMakedirsNonRoot(target)
1202 elif not os.path.exists(target):
1203 # Upgrade occurred, but a reversion, or something whacky
1204 # occurred writing to the old location. Wipe and continue.
1205 os.rename(src, target)
1206 else:
1207 # Upgrade occurred once already, but either a reversion or
1208 # some before/after separate cros_sdk usage is at play.
1209 # Wipe and continue.
1210 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001211
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001212 if options.download:
1213 lock.write_lock()
1214 sdk_tarball = FetchRemoteTarballs(
1215 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001216
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001217 if options.create:
1218 lock.write_lock()
1219 # Recheck if the chroot is set up here before creating to make sure we
1220 # account for whatever the various delete/unmount/remount steps above
1221 # have done.
1222 if cros_sdk_lib.IsChrootReady(options.chroot):
1223 logging.debug('Chroot already exists. Skipping creation.')
1224 else:
1225 CreateChroot(
1226 options.chroot,
1227 sdk_tarball,
1228 options.cache_dir,
1229 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001230
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001231 if options.enter:
1232 lock.read_lock()
1233 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
1234 options.chrome_root_mount, options.goma_dir,
1235 options.goma_client_json, options.working_dir,
1236 chroot_command)