blob: 3d3fe726c5eb78b1b1627bfd6c2738e46080bab2 [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 Frysinger3dcacee2019-08-23 17:09:11 -040023
24from six.moves import urllib
Brian Harringb938c782012-02-29 15:14:38 -080025
Aviv Keshetb7519e12016-10-04 00:50:00 -070026from chromite.lib import constants
Brian Harringb6cf9142012-09-01 20:43:17 -070027from chromite.lib import commandline
Brian Harringb938c782012-02-29 15:14:38 -080028from chromite.lib import cros_build_lib
Ralph Nathan59900422015-03-24 10:41:17 -070029from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060030from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080031from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080032from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070033from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080034from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040035from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080036from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060037from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050038from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040039from chromite.utils import key_value_store
40
Brian Harringb938c782012-02-29 15:14:38 -080041
Zdenek Behanaa52cea2012-05-30 01:31:11 +020042COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020043
Brian Harringb938c782012-02-29 15:14:38 -080044# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070045MAKE_CHROOT = [
46 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/make_chroot.sh')
47]
48ENTER_CHROOT = [
49 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
50]
Brian Harringb938c782012-02-29 15:14:38 -080051
Josh Triplett472a4182013-03-08 11:48:57 -080052# Proxy simulator configuration.
53PROXY_HOST_IP = '192.168.240.1'
54PROXY_PORT = 8080
55PROXY_GUEST_IP = '192.168.240.2'
56PROXY_NETMASK = 30
57PROXY_VETH_PREFIX = 'veth'
58PROXY_CONNECT_PORTS = (80, 443, 9418)
59PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
60PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
61PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070062 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080063PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
64
Josh Triplett9a495f62013-03-15 18:06:55 -070065# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080066NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080067
Josh Triplett472a4182013-03-08 11:48:57 -080068# Tools needed for --proxy-sim only.
69PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080070
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060071# Tools needed when use_image is true (the default).
72IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060073 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060074
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070075# As space is used inside the chroot, the empty space in chroot.img is
76# allocated. Deleting files inside the chroot doesn't automatically return the
77# used space to the OS. Over time, this tends to make the sparse chroot.img
78# less sparse even if the chroot contents don't currently need much space. We
79# can recover most of this unused space with fstrim, but that takes too much
80# time to run it every time. Instead, check the used space against the image
81# size after mounting the chroot and only call fstrim if it looks like we could
82# recover at least this many GiB.
83MAX_UNUSED_IMAGE_GBS = 20
84
Mike Frysingercc838832014-05-24 13:10:30 -040085
Brian Harring1790ac42012-09-23 08:53:33 -070086def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080087 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070088 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
89 return [
90 toolchain.GetSdkURL(
91 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
92 for compressor in COMPRESSION_PREFERENCE
93 ]
Brian Harring1790ac42012-09-23 08:53:33 -070094
95
Mike Frysingerdaf57b82019-11-23 17:26:51 -050096def FetchRemoteTarballs(storage_dir, urls, desc):
Mike Frysinger34db8692013-11-11 14:54:08 -050097 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +020098
99 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500100 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200101 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700102 desc: A string describing what tarball we're downloading (for logging).
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200103
104 Returns:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500105 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700106
107 Raises:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500108 ValueError: None of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200109 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200110
Brian Harring1790ac42012-09-23 08:53:33 -0700111 # Note we track content length ourselves since certain versions of curl
112 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700113 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700114 logging.notice('Downloading %s tarball...', desc)
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500115 status_re = re.compile(br'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400116 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200117 for url in urls:
Mike Frysinger3dcacee2019-08-23 17:09:11 -0400118 parsed = urllib.parse.urlparse(url)
Brian Harring1790ac42012-09-23 08:53:33 -0700119 tarball_name = os.path.basename(parsed.path)
120 if parsed.scheme in ('', 'file'):
121 if os.path.exists(parsed.path):
122 return parsed.path
123 continue
124 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700125 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700126 result = retry_util.RunCurl(['-I', url],
127 print_cmd=False,
128 debug_level=logging.NOTICE,
129 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700130 successful = False
131 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700132 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700133 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700134 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700135 successful = True
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500136 elif header.lower().startswith(b'content-length:'):
137 content_length = int(header.split(b':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700138 if successful:
139 break
140 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200141 break
142 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700143 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200144
Brian Harringae0a5322012-09-15 01:46:51 -0700145 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700146 current_size = 0
147 if os.path.exists(tarball_dest):
148 current_size = os.path.getsize(tarball_dest)
149 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700150 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700151 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100152
Brian Harring1790ac42012-09-23 08:53:33 -0700153 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800154 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900155 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700156 print_cmd=False,
157 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800158
Brian Harring1790ac42012-09-23 08:53:33 -0700159 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700160 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
161 # because we might have partial overlap between known prefixes.
162 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
163 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
164 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700165 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700166 if (filename == tarball_name or
167 any([(filename.startswith(p) and
168 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
169 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700170 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700171 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700172 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200173
Brian Harringb938c782012-02-29 15:14:38 -0800174 return tarball_dest
175
176
Benjamin Gordon589873b2018-05-31 14:30:56 -0600177def CreateChroot(chroot_path, sdk_tarball, cache_dir, nousepkg=False):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600178 """Creates a new chroot from a given SDK.
179
180 Args:
181 chroot_path: Path where the new chroot will be created.
182 sdk_tarball: Path to a downloaded Gentoo Stage3 or Chromium OS SDK tarball.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600183 cache_dir: Path to a directory that will be used for caching portage files,
184 etc.
185 nousepkg: If True, pass --nousepkg to cros_setup_toolchains inside the
186 chroot.
187 """
Brian Harringb938c782012-02-29 15:14:38 -0800188
Manoj Guptab12f7302019-06-03 16:40:14 -0700189 cmd = MAKE_CHROOT + [
190 '--stage3_path', sdk_tarball, '--chroot', chroot_path, '--cache_dir',
191 cache_dir
192 ]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700193
Mike Frysinger2de7f042012-07-10 04:45:03 -0400194 if nousepkg:
195 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800196
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700197 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800198 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500199 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500200 except cros_build_lib.RunCommandError as e:
201 cros_build_lib.Die('Creating chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800202
203
204def DeleteChroot(chroot_path):
205 """Deletes an existing chroot"""
Manoj Guptab12f7302019-06-03 16:40:14 -0700206 cmd = MAKE_CHROOT + ['--chroot', chroot_path, '--delete']
Brian Harringb938c782012-02-29 15:14:38 -0800207 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700208 logging.notice('Deleting chroot.')
Mike Frysinger3e8de442020-02-14 16:46:28 -0500209 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500210 except cros_build_lib.RunCommandError as e:
211 cros_build_lib.Die('Deleting chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800212
213
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600214def CleanupChroot(chroot_path):
215 """Unmounts a chroot and cleans up any associated devices."""
Don Garrett36650112018-06-28 15:54:34 -0700216 cros_sdk_lib.CleanupChrootMount(chroot_path, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600217
218
Brian Harringae0a5322012-09-15 01:46:51 -0700219def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500220 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800221 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400222 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
Alex Klein875b30e2021-01-05 14:56:33 -0700223 if st.f_flag & os.ST_NOSUID:
Mike Frysingere5456972013-06-13 00:07:23 -0400224 cros_build_lib.Die('chroot cannot be in a nosuid mount')
225
Brian Harringae0a5322012-09-15 01:46:51 -0700226 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800227 if chrome_root:
228 cmd.extend(['--chrome_root', chrome_root])
229 if chrome_root_mount:
230 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900231 if goma_dir:
232 cmd.extend(['--goma_dir', goma_dir])
233 if goma_client_json:
234 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800235 if working_dir is not None:
236 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700237
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400238 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800239 cmd.append('--')
240 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700241
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700242 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000243 # Set rlimit and vm.max_map_count to accommodate this.
244 file_limit = 262144
245 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
246 resource.setrlimit(resource.RLIMIT_NOFILE,
247 (max(soft, file_limit), max(hard, file_limit)))
248 max_map_count = int(open('/proc/sys/vm/max_map_count').read())
249 if max_map_count < file_limit:
250 logging.notice(
251 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
252 open('/proc/sys/vm/max_map_count', 'w').write(f'{file_limit}\n')
Alex Klein1ec3e6a2020-04-03 11:13:50 -0600253 ret = cros_build_lib.dbg_run(cmd, check=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700254 # If we were in interactive mode, ignore the exit code; it'll be whatever
255 # they last ran w/in the chroot and won't matter to us one way or another.
256 # Note this does allow chroot entrance to fail and be ignored during
257 # interactive; this is however a rare case and the user will immediately
258 # see it (nor will they be checking the exit code manually).
259 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700260 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800261
262
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600263def _ImageFileForChroot(chroot):
264 """Find the image file that should be associated with |chroot|.
265
266 This function does not check if the image exists; it simply returns the
267 filename that would be used.
268
269 Args:
270 chroot: Path to the chroot.
271
272 Returns:
273 Path to an image file that would be associated with chroot.
274 """
275 return chroot.rstrip('/') + '.img'
276
277
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600278def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
279 """Create a snapshot for the specified chroot VG/LV.
280
281 Args:
282 snapshot_name: The name of the new snapshot.
283 chroot_vg: The name of the VG containing the origin LV.
284 chroot_lv: The name of the origin LV.
285
286 Returns:
287 True if the snapshot was created, or False if a snapshot with the same
288 name already exists.
289
290 Raises:
291 SystemExit: The lvcreate command failed.
292 """
293 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700294 logging.error(
295 'Cannot create snapshot %s: A volume with that name already '
296 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600297 return False
298
Manoj Guptab12f7302019-06-03 16:40:14 -0700299 cmd = [
300 'lvcreate', '-s', '--name', snapshot_name,
301 '%s/%s' % (chroot_vg, chroot_lv)
302 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600303 try:
304 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
305 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500306 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600307 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500308 except cros_build_lib.RunCommandError as e:
309 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600310
311
312def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
313 """Delete the named snapshot from the specified chroot VG.
314
315 If the requested snapshot is not found, nothing happens. The main chroot LV
316 and internal thinpool LV cannot be deleted with this function.
317
318 Args:
319 snapshot_name: The name of the snapshot to delete.
320 chroot_vg: The name of the VG containing the origin LV.
321 chroot_lv: The name of the origin LV.
322
323 Raises:
324 SystemExit: The lvremove command failed.
325 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600326 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
327 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700328 logging.error(
329 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
330 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600331 return
332
333 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
334 return
335
336 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
337 try:
338 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500339 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500340 except cros_build_lib.RunCommandError as e:
341 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600342
343
344def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
345 """Restore the chroot to an existing snapshot.
346
347 This is done by renaming the original |chroot_lv| LV to a temporary name,
348 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
349 now unused LV. If an error occurs, attempts to rename the original snapshot
350 back to |chroot_lv| to leave the chroot unchanged.
351
352 The chroot must be unmounted before calling this function, and will be left
353 unmounted after this function returns.
354
355 Args:
356 snapshot_name: The name of the snapshot to restore. This snapshot will no
357 longer be accessible at its original name after this function finishes.
358 chroot_vg: The VG containing the chroot LV and snapshot LV.
359 chroot_lv: The name of the original chroot LV.
360
361 Returns:
362 True if the chroot was restored to the requested snapshot, or False if
363 the snapshot wasn't found or isn't valid.
364
365 Raises:
366 SystemExit: Any of the LVM commands failed.
367 """
368 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600369 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
370 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600371 snapshot_name not in valid_snapshots):
372 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
373 snapshot_name, ', '.join(valid_snapshots))
374 return False
375
376 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
377 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
378 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500379 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500380 except cros_build_lib.RunCommandError as e:
381 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600382
383 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
384 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500385 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500386 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600387 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
388 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500389 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500390 except cros_build_lib.RunCommandError as e:
391 cros_build_lib.Die(
392 'Failed to rename %s to chroot and failed to restore %s back to '
393 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
394 cros_build_lib.Die(
395 'Failed to rename %s to chroot! Original chroot LV has '
396 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600397
398 # Some versions of LVM set snapshots to be skipped at auto-activate time.
399 # Other versions don't have this flag at all. We run lvchange to try
400 # disabling auto-skip and activating the volume, but ignore errors. Versions
401 # that don't have the flag should be auto-activated.
402 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
403 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400404 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500405 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600406
407 # Activate the LV in case the lvchange above was needed. Activating an LV
408 # that is already active shouldn't do anything, so this is safe to run even if
409 # the -kn wasn't needed.
410 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500411 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600412
413 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
414 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500415 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500416 except cros_build_lib.RunCommandError as e:
417 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
418 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600419
420 return True
421
422
423def ListChrootSnapshots(chroot_vg, chroot_lv):
424 """Return all snapshots in |chroot_vg| regardless of origin volume.
425
426 Args:
427 chroot_vg: The name of the VG containing the chroot.
428 chroot_lv: The name of the chroot LV.
429
430 Returns:
431 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
432
433 Raises:
434 SystemExit: The lvs command failed.
435 """
436 if not chroot_vg or not chroot_lv:
437 return []
438
Manoj Guptab12f7302019-06-03 16:40:14 -0700439 cmd = [
440 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
441 '--separator', '\t', chroot_vg
442 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600443 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400444 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600445 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600446 except cros_build_lib.RunCommandError:
447 raise SystemExit('Running %r failed!' % cmd)
448
449 # Once the thin origin volume has been deleted, there's no way to tell a
450 # snapshot apart from any other volume. Since this VG is created and managed
451 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
452 # valid snapshots.
453 snapshots = []
454 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
455 for line in result.output.splitlines():
456 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700457 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600458 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600459 not snapshot_attrs.match(lv_attr)):
460 continue
461 snapshots.append(lv_name)
462 return snapshots
463
464
David James56e6c2c2012-10-24 23:54:41 -0700465def _SudoCommand():
466 """Get the 'sudo' command, along with all needed environment variables."""
467
David James5a73b4d2013-03-07 10:23:40 -0800468 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400469 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700470 cmd = ['sudo']
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400471 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700472 value = os.environ.get(key)
473 if value is not None:
474 cmd += ['%s=%s' % (key, value)]
475
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400476 # We keep PATH not for the chroot but for the re-exec & for programs we might
477 # run before we chroot into the SDK. The process that enters the SDK itself
478 # will take care of initializing PATH to the right value then. But we can't
479 # override the system's default PATH for root as that will hide /sbin.
480 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
481
David James56e6c2c2012-10-24 23:54:41 -0700482 # Pass in the path to the depot_tools so that users can access them from
483 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400484 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500485
David James56e6c2c2012-10-24 23:54:41 -0700486 return cmd
487
488
Josh Triplett472a4182013-03-08 11:48:57 -0800489def _ReportMissing(missing):
490 """Report missing utilities, then exit.
491
492 Args:
493 missing: List of missing utilities, as returned by
494 osutils.FindMissingBinaries. If non-empty, will not return.
495 """
496
497 if missing:
498 raise SystemExit(
499 'The tool(s) %s were not found.\n'
500 'Please install the appropriate package in your host.\n'
501 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700502 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800503
504
505def _ProxySimSetup(options):
506 """Set up proxy simulator, and return only in the child environment.
507
508 TODO: Ideally, this should support multiple concurrent invocations of
509 cros_sdk --proxy-sim; currently, such invocations will conflict with each
510 other due to the veth device names and IP addresses. Either this code would
511 need to generate fresh, unused names for all of these before forking, or it
512 would need to support multiple concurrent cros_sdk invocations sharing one
513 proxy and allowing it to exit when unused (without counting on any local
514 service-management infrastructure on the host).
515 """
516
517 may_need_mpm = False
518 apache_bin = osutils.Which('apache2')
519 if apache_bin is None:
520 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
521 if apache_bin is None:
522 _ReportMissing(('apache2',))
523 else:
524 may_need_mpm = True
525
526 # Module names and .so names included for ease of grepping.
527 apache_modules = [('proxy_module', 'mod_proxy.so'),
528 ('proxy_connect_module', 'mod_proxy_connect.so'),
529 ('proxy_http_module', 'mod_proxy_http.so'),
530 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
531
532 # Find the apache module directory, and make sure it has the modules we need.
533 module_dirs = {}
534 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400535 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800536 for f in glob.glob(os.path.join(g, so)):
537 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400538 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800539 if len(modules_found) == len(apache_modules):
540 break
541 else:
542 # Appease cros lint, which doesn't understand that this else block will not
543 # fall through to the subsequent code which relies on apache_module_path.
544 apache_module_path = None
545 raise SystemExit(
546 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500547 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800548
549 def check_add_module(name):
550 so = 'mod_%s.so' % name
551 if os.access(os.path.join(apache_module_path, so), os.F_OK):
552 mod = '%s_module' % name
553 apache_modules.append((mod, so))
554 return True
555 return False
556
557 check_add_module('authz_core')
558 if may_need_mpm:
559 for mpm in PROXY_APACHE_MPMS:
560 if check_add_module('mpm_%s' % mpm):
561 break
562
563 veth_host = '%s-host' % PROXY_VETH_PREFIX
564 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
565
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500566 # Set up locks to sync the net namespace setup. We need the child to create
567 # the net ns first, and then have the parent assign the guest end of the veth
568 # interface to the child's new network namespace & bring up the proxy. Only
569 # then can the child move forward and rely on the network being up.
570 ns_create_lock = locking.PipeLock()
571 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800572
573 pid = os.fork()
574 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500575 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800576 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500577
578 # Signal the parent the ns is ready to be configured.
579 ns_create_lock.Post()
580 del ns_create_lock
581
582 # Wait for the parent to finish setting up the ns/proxy.
583 ns_setup_lock.Wait()
584 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800585
586 # Set up child side of the network.
587 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500588 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700589 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500590 'dev', veth_guest),
591 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800592 )
593 try:
594 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500595 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500596 except cros_build_lib.RunCommandError as e:
597 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800598
599 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
600 for proto in ('http', 'https', 'ftp'):
601 os.environ[proto + '_proxy'] = proxy_url
602 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
603 os.environ.pop(v, None)
604 return
605
Josh Triplett472a4182013-03-08 11:48:57 -0800606 # Set up parent side of the network.
607 uid = int(os.environ.get('SUDO_UID', '0'))
608 gid = int(os.environ.get('SUDO_GID', '0'))
609 if uid == 0 or gid == 0:
610 for username in PROXY_APACHE_FALLBACK_USERS:
611 try:
612 pwnam = pwd.getpwnam(username)
613 uid, gid = pwnam.pw_uid, pwnam.pw_gid
614 break
615 except KeyError:
616 continue
617 if uid == 0 or gid == 0:
618 raise SystemExit('Could not find a non-root user to run Apache as')
619
620 chroot_parent, chroot_base = os.path.split(options.chroot)
621 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
622 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
623
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500624 # Wait for the child to create the net ns.
625 ns_create_lock.Wait()
626 del ns_create_lock
627
Josh Triplett472a4182013-03-08 11:48:57 -0800628 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500629 'User #%u' % uid,
630 'Group #%u' % gid,
631 'PidFile %s' % pid_file,
632 'ErrorLog %s' % log_file,
633 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
634 'ServerName %s' % PROXY_HOST_IP,
635 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400636 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800637 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500638 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
639 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800640 ]
641 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700642 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
643 veth_guest),
644 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
645 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500646 ('ip', 'link', 'set', veth_host, 'up'),
647 ([apache_bin, '-f', '/dev/null'] +
648 [arg for d in apache_directives for arg in ('-C', d)]),
649 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800650 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700651 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800652 try:
653 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500654 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500655 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800656 # Clean up existing interfaces, if any.
657 cmd_cleanup = ('ip', 'link', 'del', veth_host)
658 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400659 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800660 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700661 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500662 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500663
664 # Signal the child that the net ns/proxy is fully configured now.
665 ns_setup_lock.Post()
666 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800667
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400668 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800669
670
Mike Frysingera78a56e2012-11-20 06:02:30 -0500671def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700672 """Re-execute cros_sdk as root.
673
674 Also unshare the mount namespace so as to ensure that processes outside
675 the chroot can't mess with our mounts.
676 """
677 if os.geteuid() != 0:
Mike Frysinger1f113512020-07-29 03:36:57 -0400678 # Make sure to preserve the active Python executable in case the version
679 # we're running as is not the default one found via the (new) $PATH.
680 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500681 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500682 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700683
684
Mike Frysinger34db8692013-11-11 14:54:08 -0500685def _CreateParser(sdk_latest_version, bootstrap_latest_version):
686 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400687 usage = ('usage: %(prog)s [options] '
688 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700689 parser = commandline.ArgumentParser(
690 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700691
Mike Frysinger34db8692013-11-11 14:54:08 -0500692 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500693 default_chroot = os.path.join(constants.SOURCE_ROOT,
694 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400695 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700696 '--chroot',
697 dest='chroot',
698 default=default_chroot,
699 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700700 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700701 parser.add_argument(
702 '--nouse-image',
703 dest='use_image',
704 action='store_false',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700705 default=False,
Manoj Guptab12f7302019-06-03 16:40:14 -0700706 help='Do not mount the chroot on a loopback image; '
707 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600708 parser.add_argument(
709 '--use-image',
710 dest='use_image',
711 action='store_true',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700712 default=False,
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600713 help='Mount the chroot on a loopback image '
714 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800715
Manoj Guptab12f7302019-06-03 16:40:14 -0700716 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600717 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700718 '--chrome_root',
719 type='path',
720 help='Mount this chrome root into the SDK chroot')
721 parser.add_argument(
722 '--chrome_root_mount',
723 type='path',
724 help='Mount chrome into this path inside SDK chroot')
725 parser.add_argument(
726 '--nousepkg',
727 action='store_true',
728 default=False,
729 help='Do not use binary packages when creating a chroot.')
730 parser.add_argument(
731 '-u',
732 '--url',
733 dest='sdk_url',
734 help='Use sdk tarball located at this url. Use file:// '
735 'for local files.')
736 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700737 '--sdk-version',
738 help=('Use this sdk version. For prebuilt, current is %r'
739 ', for bootstrapping it is %r.' % (sdk_latest_version,
740 bootstrap_latest_version)))
741 parser.add_argument(
742 '--goma_dir',
743 type='path',
744 help='Goma installed directory to mount into the chroot.')
745 parser.add_argument(
746 '--goma_client_json',
747 type='path',
748 help='Service account json file to use goma on bot. '
749 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800750
751 # Use type=str instead of type='path' to prevent the given path from being
752 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700753 parser.add_argument(
754 '--working-dir',
755 type=str,
756 help='Run the command in specific working directory in '
757 'chroot. If the given directory is a relative '
758 'path, this program will transfer the path to '
759 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800760
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400761 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500762
763 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400764 group = parser.add_argument_group('Commands')
765 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700766 '--enter',
767 action='store_true',
768 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500769 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400770 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700771 '--create',
772 action='store_true',
773 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500774 help='Create the chroot only if it does not already exist. '
775 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400776 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700777 '--bootstrap',
778 action='store_true',
779 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500780 help='Build everything from scratch, including the sdk. '
781 'Use this only if you need to validate a change '
782 'that affects SDK creation itself (toolchain and '
783 'build are typically the only folk who need this). '
784 'Note this will quite heavily slow down the build. '
785 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400786 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700787 '-r',
788 '--replace',
789 action='store_true',
790 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500791 help='Replace an existing SDK chroot. Basically an alias '
792 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400793 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700794 '--delete',
795 action='store_true',
796 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500797 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400798 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600799 '--force',
800 action='store_true',
801 default=False,
802 help='Force unmount/delete of the current SDK chroot even if '
803 'obtaining the write lock fails.')
804 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700805 '--unmount',
806 action='store_true',
807 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600808 help='Unmount and clean up devices associated with the '
809 'SDK chroot if it exists. This does not delete the '
810 'backing image file, so the same chroot can be later '
811 're-mounted for reuse. To fully delete the chroot, use '
812 '--delete. This is primarily useful for working on '
813 'cros_sdk or the chroot setup; you should not need it '
814 'under normal circumstances.')
815 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700816 '--download',
817 action='store_true',
818 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500819 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600820 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700821 '--snapshot-create',
822 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600823 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700824 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600825 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700826 '--snapshot-restore',
827 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600828 help='Restore the chroot to a previously created snapshot.')
829 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700830 '--snapshot-delete',
831 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600832 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700833 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600834 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700835 '--snapshot-list',
836 action='store_true',
837 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600838 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500839 commands = group
840
Mike Frysinger80dfce92014-04-21 10:58:53 -0400841 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400842 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700843 group.add_argument(
844 '--proxy-sim',
845 action='store_true',
846 default=False,
847 help='Simulate a restrictive network requiring an outbound'
848 ' proxy.')
Mike Frysinger79024a32021-04-05 03:28:35 -0400849 for ns, default in (('pid', True), ('net', None)):
850 group.add_argument(
851 f'--ns-{ns}',
852 default=default,
853 action='store_true',
854 help=f'Create a new {ns} namespace.')
855 group.add_argument(
856 f'--no-ns-{ns}',
857 dest=f'ns_{ns}',
858 action='store_false',
859 help=f'Do not create a new {ns} namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400860
Mike Frysinger34db8692013-11-11 14:54:08 -0500861 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400862 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500863 'Internal Chromium OS Build Team Options',
864 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700865 group.add_argument(
866 '--buildbot-log-version',
867 default=False,
868 action='store_true',
869 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500870
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400871 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500872
873
874def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600875 # Turn on strict sudo checks.
876 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400877 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500878 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
879 ignore_missing=True)
880 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700881 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700882
883 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
884 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
885 bootstrap_latest_version = (
886 sdk_latest_version
887 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500888 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400889 options = parser.parse_args(argv)
890 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800891
892 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500893 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800894
Brian Harring1790ac42012-09-23 08:53:33 -0700895 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700896 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600897 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700898 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400899 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700900
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400901 # Merge the outside PATH setting if we re-execed ourselves.
902 if 'CHROMEOS_SUDO_PATH' in os.environ:
903 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
904 os.environ['PATH'])
905
Josh Triplett472a4182013-03-08 11:48:57 -0800906 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
907 if options.proxy_sim:
908 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600909 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800910
Benjamin Gordon040a1162017-06-29 13:44:47 -0600911 if (sdk_latest_version == '<unknown>' or
912 bootstrap_latest_version == '<unknown>'):
913 cros_build_lib.Die(
914 'No SDK version was found. '
915 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
916 'Please change to a directory inside your Chromium OS source tree\n'
917 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400918 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600919
Manoj Guptab12f7302019-06-03 16:40:14 -0700920 any_snapshot_operation = (
921 options.snapshot_create or options.snapshot_restore or
922 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600923
Manoj Guptab12f7302019-06-03 16:40:14 -0700924 if (options.snapshot_delete and
925 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600926 parser.error('Cannot --snapshot_delete the same snapshot you are '
927 'restoring with --snapshot_restore.')
928
David James471532c2013-01-21 10:23:31 -0800929 _ReExecuteIfNeeded([sys.argv[0]] + argv)
930
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600931 lock_path = os.path.dirname(options.chroot)
932 lock_path = os.path.join(
933 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
934
Brian Harring218e13c2012-10-10 16:21:26 -0700935 # Expand out the aliases...
936 if options.replace:
937 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800938
Brian Harring218e13c2012-10-10 16:21:26 -0700939 if options.bootstrap:
940 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800941
Brian Harring218e13c2012-10-10 16:21:26 -0700942 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400943 # pylint: disable=protected-access
944 # This _group_actions access sucks, but upstream decided to not include an
945 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700946 options.enter |= not any(
947 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400948 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700949 options.enter |= bool(chroot_command)
950
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600951 if (options.delete and not options.create and
952 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400953 parser.error('Trying to enter or snapshot the chroot when --delete '
954 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700955
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600956 if (options.unmount and
957 (options.create or options.enter or any_snapshot_operation)):
958 parser.error('--unmount cannot be specified with other chroot actions.')
959
Yong Hong84ba9172018-02-07 01:37:42 +0800960 if options.working_dir is not None and not os.path.isabs(options.working_dir):
961 options.working_dir = path_util.ToChrootPath(options.working_dir)
962
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700963 # If there is an existing chroot image and we're not removing it then force
964 # use_image on. This ensures that people don't have to remember to pass
965 # --use-image after a reboot to avoid losing access to their existing chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600966 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700967 img_path = _ImageFileForChroot(options.chroot)
Benjamin Gordon832a4412020-12-08 10:39:16 -0700968 if (not options.use_image and not options.delete and not options.unmount
969 and os.path.exists(img_path)):
970 if chroot_exists:
971 # If the chroot is already populated, make sure it has something
972 # mounted on it before we assume it came from an image.
973 cmd = ['mountpoint', '-q', options.chroot]
974 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
975 options.use_image = True
976
977 else:
978 logging.notice('Existing chroot image %s found. Forcing --use-image on.',
979 img_path)
980 options.use_image = True
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700981
Benjamin Gordon9bce7032020-11-19 09:58:44 -0700982 if any_snapshot_operation and not options.use_image:
983 if os.path.exists(img_path):
984 options.use_image = True
985 else:
986 cros_build_lib.Die('Snapshot operations are not compatible with '
987 '--nouse-image.')
988
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700989 # Discern if we need to create the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600990 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600991 not options.unmount and not missing_image_tools and
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700992 os.path.exists(img_path)):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600993 # Try to re-mount an existing image in case the user has rebooted.
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500994 with locking.FileLock(lock_path, 'chroot lock') as lock:
995 logging.debug('Checking if existing chroot image can be mounted.')
996 lock.write_lock()
997 cros_sdk_lib.MountChroot(options.chroot, create=False)
998 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
999 if chroot_exists:
1000 logging.notice('Mounted existing image %s on chroot', img_path)
Brian Harring218e13c2012-10-10 16:21:26 -07001001
1002 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001003 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -07001004 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -08001005
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001006 # Make sure we will download if we plan to create.
1007 options.download |= options.create
1008
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001009 # Anything that needs to manipulate the main chroot mount or communicate with
1010 # LVM needs to be done here before we enter the new namespaces.
1011
1012 # If deleting, do it regardless of the use_image flag so that a
1013 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001014 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
1015 # favor of this block.
1016 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001017 if options.delete:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001018 # Set a timeout of 300 seconds when getting the lock.
1019 with locking.FileLock(lock_path, 'chroot lock',
1020 blocking_timeout=300) as lock:
1021 try:
1022 lock.write_lock()
1023 except timeout_util.TimeoutError as e:
1024 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
1025 if not options.force:
1026 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001027 else:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001028 logging.warning(
1029 'cros_sdk was invoked with force option, continuing.')
1030 if missing_image_tools:
1031 logging.notice('Unmounting chroot.')
1032 osutils.UmountTree(options.chroot)
1033 else:
1034 logging.notice('Deleting chroot.')
1035 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
1036 chroot_deleted = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001037
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001038 # If cleanup was requested, we have to do it while we're still in the original
1039 # namespace. Since cleaning up the mount will interfere with any other
1040 # commands, we exit here. The check above should have made sure that no other
1041 # action was requested, anyway.
1042 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001043 # Set a timeout of 300 seconds when getting the lock.
1044 with locking.FileLock(lock_path, 'chroot lock',
1045 blocking_timeout=300) as lock:
1046 try:
1047 lock.write_lock()
1048 except timeout_util.TimeoutError as e:
1049 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -06001050 logging.warning(
1051 'Continuing with CleanupChroot(%s), which will umount the tree.',
1052 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001053 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1054 # even if we don't get the lock because it will attempt to unmount the
1055 # tree and will print diagnostic information from 'fuser', 'lsof', and
1056 # 'ps'.
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001057 CleanupChroot(options.chroot)
1058 sys.exit(0)
1059
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001060 # Make sure the main chroot mount is visible. Contents will be filled in
1061 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001062 if options.create and options.use_image:
1063 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001064 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001065Please make sure the lvm2 and thin-provisioning-tools packages
1066are installed on your host.
1067Example(ubuntu):
1068 sudo apt-get install lvm2 thin-provisioning-tools
1069
1070If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001071snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001072
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001073 logging.debug('Making sure chroot image is mounted.')
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001074 with locking.FileLock(lock_path, 'chroot lock') as lock:
1075 lock.write_lock()
1076 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1077 cros_build_lib.Die('Unable to mount %s on chroot',
1078 _ImageFileForChroot(options.chroot))
1079 logging.notice('Mounted %s on chroot',
1080 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001081
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001082 # Snapshot operations will always need the VG/LV, but other actions won't.
1083 if any_snapshot_operation:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001084 with locking.FileLock(lock_path, 'chroot lock') as lock:
1085 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(options.chroot)
1086 if not chroot_vg or not chroot_lv:
1087 cros_build_lib.Die('Unable to find VG/LV for chroot %s', options.chroot)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001088
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001089 # Delete snapshot before creating a new one. This allows the user to
1090 # throw out old state, create a new snapshot, and enter the chroot in a
1091 # single call to cros_sdk. Since restore involves deleting, also do it
1092 # before creating.
1093 if options.snapshot_restore:
1094 lock.write_lock()
1095 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1096 if options.snapshot_restore not in valid_snapshots:
1097 cros_build_lib.Die(
1098 '%s is not a valid snapshot to restore to. Valid snapshots: %s',
1099 options.snapshot_restore, ', '.join(valid_snapshots))
1100 osutils.UmountTree(options.chroot)
1101 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1102 chroot_lv):
1103 cros_build_lib.Die('Unable to restore chroot to snapshot.')
1104 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1105 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001106
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001107 # Use a read lock for snapshot delete and create even though they modify
1108 # the filesystem, because they don't modify the mounted chroot itself.
1109 # The underlying LVM commands take their own locks, so conflicting
1110 # concurrent operations here may crash cros_sdk, but won't corrupt the
1111 # chroot image. This tradeoff seems worth it to allow snapshot
1112 # operations on chroots that have a process inside.
1113 if options.snapshot_delete:
1114 lock.read_lock()
1115 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001116
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001117 if options.snapshot_create:
1118 lock.read_lock()
1119 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1120 chroot_lv):
1121 cros_build_lib.Die('Unable to create snapshot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001122
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001123 img_path = _ImageFileForChroot(options.chroot)
1124 if (options.use_image and os.path.exists(options.chroot) and
1125 os.path.exists(img_path)):
1126 img_stat = os.stat(img_path)
1127 img_used_bytes = img_stat.st_blocks * 512
1128
1129 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001130 mount_used_bytes = mount_stat.f_frsize * (
1131 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001132
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001133 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001134 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1135 logging.notice('%s is using %s GiB more than needed. Running '
Sergey Frolov1cb46ec2020-12-09 21:46:16 -07001136 'fstrim in background.', img_path, extra_gbs)
1137 pid = os.fork()
1138 if pid == 0:
1139 try:
1140 # Directly call Popen to run fstrim concurrently.
1141 cmd = ['fstrim', options.chroot]
1142 subprocess.Popen(cmd, close_fds=True, shell=False)
1143 except subprocess.SubprocessError as e:
1144 logging.warning(
1145 'Running fstrim failed. Consider running fstrim on '
1146 'your chroot manually.\n%s', e)
1147 os._exit(0) # pylint: disable=protected-access
1148 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001149
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001150 # Enter a new set of namespaces. Everything after here cannot directly affect
1151 # the hosts's mounts or alter LVM volumes.
Mike Frysinger79024a32021-04-05 03:28:35 -04001152 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001153
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001154 if options.snapshot_list:
1155 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1156 print(snap)
1157 sys.exit(0)
1158
Brian Harringb938c782012-02-29 15:14:38 -08001159 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001160 sdk_version = (
1161 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001162 else:
1163 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001164 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001165 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001166
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001167 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001168 if options.download:
1169 if options.sdk_url:
1170 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001171 else:
1172 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001173
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001174 with locking.FileLock(lock_path, 'chroot lock') as lock:
1175 if options.proxy_sim:
1176 _ProxySimSetup(options)
Josh Triplett472a4182013-03-08 11:48:57 -08001177
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001178 if (options.delete and not chroot_deleted and
1179 (os.path.exists(options.chroot) or
1180 os.path.exists(_ImageFileForChroot(options.chroot)))):
1181 lock.write_lock()
1182 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001183
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001184 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1185 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
1186 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001187
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001188 for target in (sdk_cache, distfiles_cache):
1189 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1190 if not os.path.exists(src):
1191 osutils.SafeMakedirsNonRoot(target)
1192 continue
1193 lock.write_lock(
1194 'Upgrade to %r needed but chroot is locked; please exit '
1195 'all instances so this upgrade can finish.' % src)
1196 if not os.path.exists(src):
1197 # Note that while waiting for the write lock, src may've vanished;
1198 # it's a rare race during the upgrade process that's a byproduct
1199 # of us avoiding taking a write lock to do the src check. If we
1200 # took a write lock for that check, it would effectively limit
1201 # all cros_sdk for a chroot to a single instance.
1202 osutils.SafeMakedirsNonRoot(target)
1203 elif not os.path.exists(target):
1204 # Upgrade occurred, but a reversion, or something whacky
1205 # occurred writing to the old location. Wipe and continue.
1206 os.rename(src, target)
1207 else:
1208 # Upgrade occurred once already, but either a reversion or
1209 # some before/after separate cros_sdk usage is at play.
1210 # Wipe and continue.
1211 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001212
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001213 if options.download:
1214 lock.write_lock()
1215 sdk_tarball = FetchRemoteTarballs(
1216 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001217
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001218 if options.create:
1219 lock.write_lock()
1220 # Recheck if the chroot is set up here before creating to make sure we
1221 # account for whatever the various delete/unmount/remount steps above
1222 # have done.
1223 if cros_sdk_lib.IsChrootReady(options.chroot):
1224 logging.debug('Chroot already exists. Skipping creation.')
1225 else:
1226 CreateChroot(
1227 options.chroot,
1228 sdk_tarball,
1229 options.cache_dir,
1230 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001231
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001232 if options.enter:
1233 lock.read_lock()
1234 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
1235 options.chrome_root_mount, options.goma_dir,
1236 options.goma_client_json, options.working_dir,
1237 chroot_command)