blob: 49db71c028a37e647d4e6f33320b4117120db82c [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
Mike Frysinger23b5cf52021-06-16 23:18:00 -040017from pathlib import Path
Josh Triplett472a4182013-03-08 11:48:57 -080018import pwd
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060019import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070020import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070021import resource
Sergey Frolov1cb46ec2020-12-09 21:46:16 -070022import subprocess
David James56e6c2c2012-10-24 23:54:41 -070023import sys
Mike Frysingere852b072021-05-21 12:39:03 -040024import urllib.parse
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 -070045ENTER_CHROOT = [
46 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
47]
Brian Harringb938c782012-02-29 15:14:38 -080048
Josh Triplett472a4182013-03-08 11:48:57 -080049# Proxy simulator configuration.
50PROXY_HOST_IP = '192.168.240.1'
51PROXY_PORT = 8080
52PROXY_GUEST_IP = '192.168.240.2'
53PROXY_NETMASK = 30
54PROXY_VETH_PREFIX = 'veth'
55PROXY_CONNECT_PORTS = (80, 443, 9418)
56PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
57PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
58PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070059 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080060PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
61
Josh Triplett9a495f62013-03-15 18:06:55 -070062# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080063NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080064
Josh Triplett472a4182013-03-08 11:48:57 -080065# Tools needed for --proxy-sim only.
66PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080067
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060068# Tools needed when use_image is true (the default).
69IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060070 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060071
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070072# As space is used inside the chroot, the empty space in chroot.img is
73# allocated. Deleting files inside the chroot doesn't automatically return the
74# used space to the OS. Over time, this tends to make the sparse chroot.img
75# less sparse even if the chroot contents don't currently need much space. We
76# can recover most of this unused space with fstrim, but that takes too much
77# time to run it every time. Instead, check the used space against the image
78# size after mounting the chroot and only call fstrim if it looks like we could
79# recover at least this many GiB.
80MAX_UNUSED_IMAGE_GBS = 20
81
Mike Frysingercc838832014-05-24 13:10:30 -040082
Brian Harring1790ac42012-09-23 08:53:33 -070083def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080084 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070085 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
86 return [
87 toolchain.GetSdkURL(
88 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
89 for compressor in COMPRESSION_PREFERENCE
90 ]
Brian Harring1790ac42012-09-23 08:53:33 -070091
92
Mike Frysingerdaf57b82019-11-23 17:26:51 -050093def FetchRemoteTarballs(storage_dir, urls, desc):
Mike Frysinger34db8692013-11-11 14:54:08 -050094 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +020095
96 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -050097 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +020098 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -070099 desc: A string describing what tarball we're downloading (for logging).
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200100
101 Returns:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500102 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700103
104 Raises:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500105 ValueError: None of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200106 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200107
Brian Harring1790ac42012-09-23 08:53:33 -0700108 # Note we track content length ourselves since certain versions of curl
109 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700110 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700111 logging.notice('Downloading %s tarball...', desc)
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500112 status_re = re.compile(br'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400113 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200114 for url in urls:
Mike Frysinger3dcacee2019-08-23 17:09:11 -0400115 parsed = urllib.parse.urlparse(url)
Brian Harring1790ac42012-09-23 08:53:33 -0700116 tarball_name = os.path.basename(parsed.path)
117 if parsed.scheme in ('', 'file'):
118 if os.path.exists(parsed.path):
119 return parsed.path
120 continue
121 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700122 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700123 result = retry_util.RunCurl(['-I', url],
124 print_cmd=False,
125 debug_level=logging.NOTICE,
126 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700127 successful = False
128 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700129 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700130 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700131 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700132 successful = True
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500133 elif header.lower().startswith(b'content-length:'):
134 content_length = int(header.split(b':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700135 if successful:
136 break
137 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200138 break
139 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700140 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200141
Brian Harringae0a5322012-09-15 01:46:51 -0700142 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700143 current_size = 0
144 if os.path.exists(tarball_dest):
145 current_size = os.path.getsize(tarball_dest)
146 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700147 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700148 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100149
Brian Harring1790ac42012-09-23 08:53:33 -0700150 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800151 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900152 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700153 print_cmd=False,
154 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800155
Brian Harring1790ac42012-09-23 08:53:33 -0700156 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700157 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
158 # because we might have partial overlap between known prefixes.
159 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
160 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
161 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700162 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700163 if (filename == tarball_name or
164 any([(filename.startswith(p) and
165 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
166 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700167 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700168 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700169 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200170
Brian Harringb938c782012-02-29 15:14:38 -0800171 return tarball_dest
172
173
Brian Harringae0a5322012-09-15 01:46:51 -0700174def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500175 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800176 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400177 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
Alex Klein875b30e2021-01-05 14:56:33 -0700178 if st.f_flag & os.ST_NOSUID:
Mike Frysingere5456972013-06-13 00:07:23 -0400179 cros_build_lib.Die('chroot cannot be in a nosuid mount')
180
Brian Harringae0a5322012-09-15 01:46:51 -0700181 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800182 if chrome_root:
183 cmd.extend(['--chrome_root', chrome_root])
184 if chrome_root_mount:
185 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900186 if goma_dir:
187 cmd.extend(['--goma_dir', goma_dir])
188 if goma_client_json:
189 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800190 if working_dir is not None:
191 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700192
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400193 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800194 cmd.append('--')
195 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700196
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700197 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000198 # Set rlimit and vm.max_map_count to accommodate this.
199 file_limit = 262144
200 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
201 resource.setrlimit(resource.RLIMIT_NOFILE,
202 (max(soft, file_limit), max(hard, file_limit)))
203 max_map_count = int(open('/proc/sys/vm/max_map_count').read())
204 if max_map_count < file_limit:
205 logging.notice(
206 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
207 open('/proc/sys/vm/max_map_count', 'w').write(f'{file_limit}\n')
Alex Klein1ec3e6a2020-04-03 11:13:50 -0600208 ret = cros_build_lib.dbg_run(cmd, check=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700209 # If we were in interactive mode, ignore the exit code; it'll be whatever
210 # they last ran w/in the chroot and won't matter to us one way or another.
211 # Note this does allow chroot entrance to fail and be ignored during
212 # interactive; this is however a rare case and the user will immediately
213 # see it (nor will they be checking the exit code manually).
214 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700215 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800216
217
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600218def _ImageFileForChroot(chroot):
219 """Find the image file that should be associated with |chroot|.
220
221 This function does not check if the image exists; it simply returns the
222 filename that would be used.
223
224 Args:
225 chroot: Path to the chroot.
226
227 Returns:
228 Path to an image file that would be associated with chroot.
229 """
230 return chroot.rstrip('/') + '.img'
231
232
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600233def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
234 """Create a snapshot for the specified chroot VG/LV.
235
236 Args:
237 snapshot_name: The name of the new snapshot.
238 chroot_vg: The name of the VG containing the origin LV.
239 chroot_lv: The name of the origin LV.
240
241 Returns:
242 True if the snapshot was created, or False if a snapshot with the same
243 name already exists.
244
245 Raises:
246 SystemExit: The lvcreate command failed.
247 """
248 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700249 logging.error(
250 'Cannot create snapshot %s: A volume with that name already '
251 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600252 return False
253
Manoj Guptab12f7302019-06-03 16:40:14 -0700254 cmd = [
255 'lvcreate', '-s', '--name', snapshot_name,
256 '%s/%s' % (chroot_vg, chroot_lv)
257 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600258 try:
259 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
260 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500261 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600262 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500263 except cros_build_lib.RunCommandError as e:
264 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600265
266
267def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
268 """Delete the named snapshot from the specified chroot VG.
269
270 If the requested snapshot is not found, nothing happens. The main chroot LV
271 and internal thinpool LV cannot be deleted with this function.
272
273 Args:
274 snapshot_name: The name of the snapshot to delete.
275 chroot_vg: The name of the VG containing the origin LV.
276 chroot_lv: The name of the origin LV.
277
278 Raises:
279 SystemExit: The lvremove command failed.
280 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600281 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
282 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700283 logging.error(
284 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
285 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600286 return
287
288 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
289 return
290
291 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
292 try:
293 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500294 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500295 except cros_build_lib.RunCommandError as e:
296 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600297
298
299def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
300 """Restore the chroot to an existing snapshot.
301
302 This is done by renaming the original |chroot_lv| LV to a temporary name,
303 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
304 now unused LV. If an error occurs, attempts to rename the original snapshot
305 back to |chroot_lv| to leave the chroot unchanged.
306
307 The chroot must be unmounted before calling this function, and will be left
308 unmounted after this function returns.
309
310 Args:
311 snapshot_name: The name of the snapshot to restore. This snapshot will no
312 longer be accessible at its original name after this function finishes.
313 chroot_vg: The VG containing the chroot LV and snapshot LV.
314 chroot_lv: The name of the original chroot LV.
315
316 Returns:
317 True if the chroot was restored to the requested snapshot, or False if
318 the snapshot wasn't found or isn't valid.
319
320 Raises:
321 SystemExit: Any of the LVM commands failed.
322 """
323 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600324 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
325 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600326 snapshot_name not in valid_snapshots):
327 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
328 snapshot_name, ', '.join(valid_snapshots))
329 return False
330
331 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
332 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
333 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500334 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500335 except cros_build_lib.RunCommandError as e:
336 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600337
338 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
339 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500340 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500341 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600342 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
343 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500344 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500345 except cros_build_lib.RunCommandError as e:
346 cros_build_lib.Die(
347 'Failed to rename %s to chroot and failed to restore %s back to '
348 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
349 cros_build_lib.Die(
350 'Failed to rename %s to chroot! Original chroot LV has '
351 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600352
353 # Some versions of LVM set snapshots to be skipped at auto-activate time.
354 # Other versions don't have this flag at all. We run lvchange to try
355 # disabling auto-skip and activating the volume, but ignore errors. Versions
356 # that don't have the flag should be auto-activated.
357 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
358 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400359 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500360 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600361
362 # Activate the LV in case the lvchange above was needed. Activating an LV
363 # that is already active shouldn't do anything, so this is safe to run even if
364 # the -kn wasn't needed.
365 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500366 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600367
368 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
369 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500370 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500371 except cros_build_lib.RunCommandError as e:
372 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
373 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600374
375 return True
376
377
378def ListChrootSnapshots(chroot_vg, chroot_lv):
379 """Return all snapshots in |chroot_vg| regardless of origin volume.
380
381 Args:
382 chroot_vg: The name of the VG containing the chroot.
383 chroot_lv: The name of the chroot LV.
384
385 Returns:
386 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
387
388 Raises:
389 SystemExit: The lvs command failed.
390 """
391 if not chroot_vg or not chroot_lv:
392 return []
393
Manoj Guptab12f7302019-06-03 16:40:14 -0700394 cmd = [
395 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
396 '--separator', '\t', chroot_vg
397 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600398 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400399 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600400 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600401 except cros_build_lib.RunCommandError:
402 raise SystemExit('Running %r failed!' % cmd)
403
404 # Once the thin origin volume has been deleted, there's no way to tell a
405 # snapshot apart from any other volume. Since this VG is created and managed
406 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
407 # valid snapshots.
408 snapshots = []
409 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
410 for line in result.output.splitlines():
411 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700412 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600413 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600414 not snapshot_attrs.match(lv_attr)):
415 continue
416 snapshots.append(lv_name)
417 return snapshots
418
419
David James56e6c2c2012-10-24 23:54:41 -0700420def _SudoCommand():
421 """Get the 'sudo' command, along with all needed environment variables."""
422
David James5a73b4d2013-03-07 10:23:40 -0800423 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400424 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700425 cmd = ['sudo']
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400426 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700427 value = os.environ.get(key)
428 if value is not None:
429 cmd += ['%s=%s' % (key, value)]
430
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400431 # We keep PATH not for the chroot but for the re-exec & for programs we might
432 # run before we chroot into the SDK. The process that enters the SDK itself
433 # will take care of initializing PATH to the right value then. But we can't
434 # override the system's default PATH for root as that will hide /sbin.
435 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
436
David James56e6c2c2012-10-24 23:54:41 -0700437 # Pass in the path to the depot_tools so that users can access them from
438 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400439 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500440
David James56e6c2c2012-10-24 23:54:41 -0700441 return cmd
442
443
Josh Triplett472a4182013-03-08 11:48:57 -0800444def _ReportMissing(missing):
445 """Report missing utilities, then exit.
446
447 Args:
448 missing: List of missing utilities, as returned by
449 osutils.FindMissingBinaries. If non-empty, will not return.
450 """
451
452 if missing:
453 raise SystemExit(
454 'The tool(s) %s were not found.\n'
455 'Please install the appropriate package in your host.\n'
456 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700457 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800458
459
460def _ProxySimSetup(options):
461 """Set up proxy simulator, and return only in the child environment.
462
463 TODO: Ideally, this should support multiple concurrent invocations of
464 cros_sdk --proxy-sim; currently, such invocations will conflict with each
465 other due to the veth device names and IP addresses. Either this code would
466 need to generate fresh, unused names for all of these before forking, or it
467 would need to support multiple concurrent cros_sdk invocations sharing one
468 proxy and allowing it to exit when unused (without counting on any local
469 service-management infrastructure on the host).
470 """
471
472 may_need_mpm = False
473 apache_bin = osutils.Which('apache2')
474 if apache_bin is None:
475 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
476 if apache_bin is None:
477 _ReportMissing(('apache2',))
478 else:
479 may_need_mpm = True
480
481 # Module names and .so names included for ease of grepping.
482 apache_modules = [('proxy_module', 'mod_proxy.so'),
483 ('proxy_connect_module', 'mod_proxy_connect.so'),
484 ('proxy_http_module', 'mod_proxy_http.so'),
485 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
486
487 # Find the apache module directory, and make sure it has the modules we need.
488 module_dirs = {}
489 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400490 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800491 for f in glob.glob(os.path.join(g, so)):
492 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400493 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800494 if len(modules_found) == len(apache_modules):
495 break
496 else:
497 # Appease cros lint, which doesn't understand that this else block will not
498 # fall through to the subsequent code which relies on apache_module_path.
499 apache_module_path = None
500 raise SystemExit(
501 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500502 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800503
504 def check_add_module(name):
505 so = 'mod_%s.so' % name
506 if os.access(os.path.join(apache_module_path, so), os.F_OK):
507 mod = '%s_module' % name
508 apache_modules.append((mod, so))
509 return True
510 return False
511
512 check_add_module('authz_core')
513 if may_need_mpm:
514 for mpm in PROXY_APACHE_MPMS:
515 if check_add_module('mpm_%s' % mpm):
516 break
517
518 veth_host = '%s-host' % PROXY_VETH_PREFIX
519 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
520
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500521 # Set up locks to sync the net namespace setup. We need the child to create
522 # the net ns first, and then have the parent assign the guest end of the veth
523 # interface to the child's new network namespace & bring up the proxy. Only
524 # then can the child move forward and rely on the network being up.
525 ns_create_lock = locking.PipeLock()
526 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800527
528 pid = os.fork()
529 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500530 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800531 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500532
533 # Signal the parent the ns is ready to be configured.
534 ns_create_lock.Post()
535 del ns_create_lock
536
537 # Wait for the parent to finish setting up the ns/proxy.
538 ns_setup_lock.Wait()
539 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800540
541 # Set up child side of the network.
542 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500543 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700544 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500545 'dev', veth_guest),
546 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800547 )
548 try:
549 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500550 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500551 except cros_build_lib.RunCommandError as e:
552 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800553
554 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
555 for proto in ('http', 'https', 'ftp'):
556 os.environ[proto + '_proxy'] = proxy_url
557 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
558 os.environ.pop(v, None)
559 return
560
Josh Triplett472a4182013-03-08 11:48:57 -0800561 # Set up parent side of the network.
562 uid = int(os.environ.get('SUDO_UID', '0'))
563 gid = int(os.environ.get('SUDO_GID', '0'))
564 if uid == 0 or gid == 0:
565 for username in PROXY_APACHE_FALLBACK_USERS:
566 try:
567 pwnam = pwd.getpwnam(username)
568 uid, gid = pwnam.pw_uid, pwnam.pw_gid
569 break
570 except KeyError:
571 continue
572 if uid == 0 or gid == 0:
573 raise SystemExit('Could not find a non-root user to run Apache as')
574
575 chroot_parent, chroot_base = os.path.split(options.chroot)
576 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
577 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
578
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500579 # Wait for the child to create the net ns.
580 ns_create_lock.Wait()
581 del ns_create_lock
582
Josh Triplett472a4182013-03-08 11:48:57 -0800583 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500584 'User #%u' % uid,
585 'Group #%u' % gid,
586 'PidFile %s' % pid_file,
587 'ErrorLog %s' % log_file,
588 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
589 'ServerName %s' % PROXY_HOST_IP,
590 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400591 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800592 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500593 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
594 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800595 ]
596 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700597 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
598 veth_guest),
599 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
600 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500601 ('ip', 'link', 'set', veth_host, 'up'),
602 ([apache_bin, '-f', '/dev/null'] +
603 [arg for d in apache_directives for arg in ('-C', d)]),
604 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800605 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700606 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800607 try:
608 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500609 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500610 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800611 # Clean up existing interfaces, if any.
612 cmd_cleanup = ('ip', 'link', 'del', veth_host)
613 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400614 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800615 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700616 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500617 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500618
619 # Signal the child that the net ns/proxy is fully configured now.
620 ns_setup_lock.Post()
621 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800622
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400623 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800624
625
Mike Frysingera78a56e2012-11-20 06:02:30 -0500626def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700627 """Re-execute cros_sdk as root.
628
629 Also unshare the mount namespace so as to ensure that processes outside
630 the chroot can't mess with our mounts.
631 """
632 if os.geteuid() != 0:
Mike Frysinger1f113512020-07-29 03:36:57 -0400633 # Make sure to preserve the active Python executable in case the version
634 # we're running as is not the default one found via the (new) $PATH.
635 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500636 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500637 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700638
639
Mike Frysinger34db8692013-11-11 14:54:08 -0500640def _CreateParser(sdk_latest_version, bootstrap_latest_version):
641 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400642 usage = ('usage: %(prog)s [options] '
643 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700644 parser = commandline.ArgumentParser(
645 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700646
Mike Frysinger34db8692013-11-11 14:54:08 -0500647 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500648 default_chroot = os.path.join(constants.SOURCE_ROOT,
649 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400650 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700651 '--chroot',
652 dest='chroot',
653 default=default_chroot,
654 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700655 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700656 parser.add_argument(
657 '--nouse-image',
658 dest='use_image',
659 action='store_false',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700660 default=False,
Manoj Guptab12f7302019-06-03 16:40:14 -0700661 help='Do not mount the chroot on a loopback image; '
662 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600663 parser.add_argument(
664 '--use-image',
665 dest='use_image',
666 action='store_true',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700667 default=False,
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600668 help='Mount the chroot on a loopback image '
669 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800670
Manoj Guptab12f7302019-06-03 16:40:14 -0700671 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600672 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700673 '--chrome_root',
674 type='path',
675 help='Mount this chrome root into the SDK chroot')
676 parser.add_argument(
677 '--chrome_root_mount',
678 type='path',
679 help='Mount chrome into this path inside SDK chroot')
680 parser.add_argument(
681 '--nousepkg',
682 action='store_true',
683 default=False,
684 help='Do not use binary packages when creating a chroot.')
685 parser.add_argument(
686 '-u',
687 '--url',
688 dest='sdk_url',
689 help='Use sdk tarball located at this url. Use file:// '
690 'for local files.')
691 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700692 '--sdk-version',
693 help=('Use this sdk version. For prebuilt, current is %r'
694 ', for bootstrapping it is %r.' % (sdk_latest_version,
695 bootstrap_latest_version)))
696 parser.add_argument(
697 '--goma_dir',
698 type='path',
699 help='Goma installed directory to mount into the chroot.')
700 parser.add_argument(
701 '--goma_client_json',
702 type='path',
703 help='Service account json file to use goma on bot. '
704 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800705
706 # Use type=str instead of type='path' to prevent the given path from being
707 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700708 parser.add_argument(
709 '--working-dir',
710 type=str,
711 help='Run the command in specific working directory in '
712 'chroot. If the given directory is a relative '
713 'path, this program will transfer the path to '
714 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800715
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400716 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500717
718 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400719 group = parser.add_argument_group('Commands')
720 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700721 '--enter',
722 action='store_true',
723 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500724 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400725 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700726 '--create',
727 action='store_true',
728 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500729 help='Create the chroot only if it does not already exist. '
730 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400731 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700732 '--bootstrap',
733 action='store_true',
734 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500735 help='Build everything from scratch, including the sdk. '
736 'Use this only if you need to validate a change '
737 'that affects SDK creation itself (toolchain and '
738 'build are typically the only folk who need this). '
739 'Note this will quite heavily slow down the build. '
740 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400741 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700742 '-r',
743 '--replace',
744 action='store_true',
745 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500746 help='Replace an existing SDK chroot. Basically an alias '
747 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400748 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700749 '--delete',
750 action='store_true',
751 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500752 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400753 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600754 '--force',
755 action='store_true',
756 default=False,
757 help='Force unmount/delete of the current SDK chroot even if '
758 'obtaining the write lock fails.')
759 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700760 '--unmount',
761 action='store_true',
762 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600763 help='Unmount and clean up devices associated with the '
764 'SDK chroot if it exists. This does not delete the '
765 'backing image file, so the same chroot can be later '
766 're-mounted for reuse. To fully delete the chroot, use '
767 '--delete. This is primarily useful for working on '
768 'cros_sdk or the chroot setup; you should not need it '
769 'under normal circumstances.')
770 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700771 '--download',
772 action='store_true',
773 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500774 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600775 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700776 '--snapshot-create',
777 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600778 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700779 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600780 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700781 '--snapshot-restore',
782 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600783 help='Restore the chroot to a previously created snapshot.')
784 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700785 '--snapshot-delete',
786 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600787 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700788 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600789 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700790 '--snapshot-list',
791 action='store_true',
792 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600793 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500794 commands = group
795
Mike Frysinger80dfce92014-04-21 10:58:53 -0400796 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400797 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700798 group.add_argument(
799 '--proxy-sim',
800 action='store_true',
801 default=False,
802 help='Simulate a restrictive network requiring an outbound'
803 ' proxy.')
Mike Frysinger79024a32021-04-05 03:28:35 -0400804 for ns, default in (('pid', True), ('net', None)):
805 group.add_argument(
806 f'--ns-{ns}',
807 default=default,
808 action='store_true',
809 help=f'Create a new {ns} namespace.')
810 group.add_argument(
811 f'--no-ns-{ns}',
812 dest=f'ns_{ns}',
813 action='store_false',
814 help=f'Do not create a new {ns} namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400815
Mike Frysinger34db8692013-11-11 14:54:08 -0500816 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400817 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500818 'Internal Chromium OS Build Team Options',
819 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700820 group.add_argument(
821 '--buildbot-log-version',
822 default=False,
823 action='store_true',
824 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500825
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400826 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500827
828
829def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600830 # Turn on strict sudo checks.
831 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400832 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500833 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
834 ignore_missing=True)
835 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700836 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700837
838 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
839 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
840 bootstrap_latest_version = (
841 sdk_latest_version
842 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500843 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400844 options = parser.parse_args(argv)
845 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800846
847 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500848 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800849
Brian Harring1790ac42012-09-23 08:53:33 -0700850 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700851 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600852 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700853 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400854 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700855
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400856 # Merge the outside PATH setting if we re-execed ourselves.
857 if 'CHROMEOS_SUDO_PATH' in os.environ:
858 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
859 os.environ['PATH'])
860
Josh Triplett472a4182013-03-08 11:48:57 -0800861 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
862 if options.proxy_sim:
863 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600864 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800865
Benjamin Gordon040a1162017-06-29 13:44:47 -0600866 if (sdk_latest_version == '<unknown>' or
867 bootstrap_latest_version == '<unknown>'):
868 cros_build_lib.Die(
869 'No SDK version was found. '
870 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
871 'Please change to a directory inside your Chromium OS source tree\n'
872 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400873 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600874
Manoj Guptab12f7302019-06-03 16:40:14 -0700875 any_snapshot_operation = (
876 options.snapshot_create or options.snapshot_restore or
877 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600878
Manoj Guptab12f7302019-06-03 16:40:14 -0700879 if (options.snapshot_delete and
880 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600881 parser.error('Cannot --snapshot_delete the same snapshot you are '
882 'restoring with --snapshot_restore.')
883
David James471532c2013-01-21 10:23:31 -0800884 _ReExecuteIfNeeded([sys.argv[0]] + argv)
885
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600886 lock_path = os.path.dirname(options.chroot)
887 lock_path = os.path.join(
888 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
889
Brian Harring218e13c2012-10-10 16:21:26 -0700890 # Expand out the aliases...
891 if options.replace:
892 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800893
Brian Harring218e13c2012-10-10 16:21:26 -0700894 if options.bootstrap:
895 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800896
Brian Harring218e13c2012-10-10 16:21:26 -0700897 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400898 # pylint: disable=protected-access
899 # This _group_actions access sucks, but upstream decided to not include an
900 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700901 options.enter |= not any(
902 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400903 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700904 options.enter |= bool(chroot_command)
905
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600906 if (options.delete and not options.create and
907 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400908 parser.error('Trying to enter or snapshot the chroot when --delete '
909 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700910
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600911 if (options.unmount and
912 (options.create or options.enter or any_snapshot_operation)):
913 parser.error('--unmount cannot be specified with other chroot actions.')
914
Yong Hong84ba9172018-02-07 01:37:42 +0800915 if options.working_dir is not None and not os.path.isabs(options.working_dir):
916 options.working_dir = path_util.ToChrootPath(options.working_dir)
917
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700918 # If there is an existing chroot image and we're not removing it then force
919 # use_image on. This ensures that people don't have to remember to pass
920 # --use-image after a reboot to avoid losing access to their existing chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600921 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700922 img_path = _ImageFileForChroot(options.chroot)
Benjamin Gordon832a4412020-12-08 10:39:16 -0700923 if (not options.use_image and not options.delete and not options.unmount
924 and os.path.exists(img_path)):
925 if chroot_exists:
926 # If the chroot is already populated, make sure it has something
927 # mounted on it before we assume it came from an image.
928 cmd = ['mountpoint', '-q', options.chroot]
929 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
930 options.use_image = True
931
932 else:
933 logging.notice('Existing chroot image %s found. Forcing --use-image on.',
934 img_path)
935 options.use_image = True
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700936
Benjamin Gordon9bce7032020-11-19 09:58:44 -0700937 if any_snapshot_operation and not options.use_image:
938 if os.path.exists(img_path):
939 options.use_image = True
940 else:
941 cros_build_lib.Die('Snapshot operations are not compatible with '
942 '--nouse-image.')
943
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700944 # Discern if we need to create the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600945 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600946 not options.unmount and not missing_image_tools and
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700947 os.path.exists(img_path)):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600948 # Try to re-mount an existing image in case the user has rebooted.
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500949 with locking.FileLock(lock_path, 'chroot lock') as lock:
950 logging.debug('Checking if existing chroot image can be mounted.')
951 lock.write_lock()
952 cros_sdk_lib.MountChroot(options.chroot, create=False)
953 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
954 if chroot_exists:
955 logging.notice('Mounted existing image %s on chroot', img_path)
Brian Harring218e13c2012-10-10 16:21:26 -0700956
957 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600958 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700959 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800960
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600961 # Make sure we will download if we plan to create.
962 options.download |= options.create
963
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600964 # Anything that needs to manipulate the main chroot mount or communicate with
965 # LVM needs to be done here before we enter the new namespaces.
966
967 # If deleting, do it regardless of the use_image flag so that a
968 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600969 if options.delete:
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500970 # Set a timeout of 300 seconds when getting the lock.
971 with locking.FileLock(lock_path, 'chroot lock',
972 blocking_timeout=300) as lock:
973 try:
974 lock.write_lock()
975 except timeout_util.TimeoutError as e:
976 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
977 if not options.force:
978 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600979 else:
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500980 logging.warning(
981 'cros_sdk was invoked with force option, continuing.')
Mike Frysingerf6fe6d02021-06-16 20:26:35 -0400982 logging.notice('Deleting chroot.')
983 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600984
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600985 # If cleanup was requested, we have to do it while we're still in the original
986 # namespace. Since cleaning up the mount will interfere with any other
987 # commands, we exit here. The check above should have made sure that no other
988 # action was requested, anyway.
989 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -0600990 # Set a timeout of 300 seconds when getting the lock.
991 with locking.FileLock(lock_path, 'chroot lock',
992 blocking_timeout=300) as lock:
993 try:
994 lock.write_lock()
995 except timeout_util.TimeoutError as e:
996 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -0600997 logging.warning(
998 'Continuing with CleanupChroot(%s), which will umount the tree.',
999 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001000 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1001 # even if we don't get the lock because it will attempt to unmount the
1002 # tree and will print diagnostic information from 'fuser', 'lsof', and
1003 # 'ps'.
Mike Frysingerf6fe6d02021-06-16 20:26:35 -04001004 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001005 sys.exit(0)
1006
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001007 # Make sure the main chroot mount is visible. Contents will be filled in
1008 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001009 if options.create and options.use_image:
1010 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001011 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001012Please make sure the lvm2 and thin-provisioning-tools packages
1013are installed on your host.
1014Example(ubuntu):
1015 sudo apt-get install lvm2 thin-provisioning-tools
1016
1017If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001018snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001019
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001020 logging.debug('Making sure chroot image is mounted.')
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001021 with locking.FileLock(lock_path, 'chroot lock') as lock:
1022 lock.write_lock()
1023 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1024 cros_build_lib.Die('Unable to mount %s on chroot',
1025 _ImageFileForChroot(options.chroot))
1026 logging.notice('Mounted %s on chroot',
1027 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001028
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001029 # Snapshot operations will always need the VG/LV, but other actions won't.
1030 if any_snapshot_operation:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001031 with locking.FileLock(lock_path, 'chroot lock') as lock:
1032 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(options.chroot)
1033 if not chroot_vg or not chroot_lv:
1034 cros_build_lib.Die('Unable to find VG/LV for chroot %s', options.chroot)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001035
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001036 # Delete snapshot before creating a new one. This allows the user to
1037 # throw out old state, create a new snapshot, and enter the chroot in a
1038 # single call to cros_sdk. Since restore involves deleting, also do it
1039 # before creating.
1040 if options.snapshot_restore:
1041 lock.write_lock()
1042 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1043 if options.snapshot_restore not in valid_snapshots:
1044 cros_build_lib.Die(
1045 '%s is not a valid snapshot to restore to. Valid snapshots: %s',
1046 options.snapshot_restore, ', '.join(valid_snapshots))
1047 osutils.UmountTree(options.chroot)
1048 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1049 chroot_lv):
1050 cros_build_lib.Die('Unable to restore chroot to snapshot.')
1051 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1052 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001053
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001054 # Use a read lock for snapshot delete and create even though they modify
1055 # the filesystem, because they don't modify the mounted chroot itself.
1056 # The underlying LVM commands take their own locks, so conflicting
1057 # concurrent operations here may crash cros_sdk, but won't corrupt the
1058 # chroot image. This tradeoff seems worth it to allow snapshot
1059 # operations on chroots that have a process inside.
1060 if options.snapshot_delete:
1061 lock.read_lock()
1062 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001063
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001064 if options.snapshot_create:
1065 lock.read_lock()
1066 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1067 chroot_lv):
1068 cros_build_lib.Die('Unable to create snapshot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001069
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001070 img_path = _ImageFileForChroot(options.chroot)
1071 if (options.use_image and os.path.exists(options.chroot) and
1072 os.path.exists(img_path)):
1073 img_stat = os.stat(img_path)
1074 img_used_bytes = img_stat.st_blocks * 512
1075
1076 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001077 mount_used_bytes = mount_stat.f_frsize * (
1078 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001079
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001080 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001081 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1082 logging.notice('%s is using %s GiB more than needed. Running '
Sergey Frolov1cb46ec2020-12-09 21:46:16 -07001083 'fstrim in background.', img_path, extra_gbs)
1084 pid = os.fork()
1085 if pid == 0:
1086 try:
1087 # Directly call Popen to run fstrim concurrently.
1088 cmd = ['fstrim', options.chroot]
1089 subprocess.Popen(cmd, close_fds=True, shell=False)
1090 except subprocess.SubprocessError as e:
1091 logging.warning(
1092 'Running fstrim failed. Consider running fstrim on '
1093 'your chroot manually.\n%s', e)
1094 os._exit(0) # pylint: disable=protected-access
1095 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001096
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001097 # Enter a new set of namespaces. Everything after here cannot directly affect
1098 # the hosts's mounts or alter LVM volumes.
Mike Frysinger79024a32021-04-05 03:28:35 -04001099 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001100
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001101 if options.snapshot_list:
1102 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1103 print(snap)
1104 sys.exit(0)
1105
Brian Harringb938c782012-02-29 15:14:38 -08001106 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001107 sdk_version = (
1108 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001109 else:
1110 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001111 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001112 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001113
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001114 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001115 if options.download:
1116 if options.sdk_url:
1117 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001118 else:
1119 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001120
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001121 with locking.FileLock(lock_path, 'chroot lock') as lock:
1122 if options.proxy_sim:
1123 _ProxySimSetup(options)
Josh Triplett472a4182013-03-08 11:48:57 -08001124
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001125 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1126 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
1127 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001128
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001129 for target in (sdk_cache, distfiles_cache):
1130 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1131 if not os.path.exists(src):
1132 osutils.SafeMakedirsNonRoot(target)
1133 continue
1134 lock.write_lock(
1135 'Upgrade to %r needed but chroot is locked; please exit '
1136 'all instances so this upgrade can finish.' % src)
1137 if not os.path.exists(src):
1138 # Note that while waiting for the write lock, src may've vanished;
1139 # it's a rare race during the upgrade process that's a byproduct
1140 # of us avoiding taking a write lock to do the src check. If we
1141 # took a write lock for that check, it would effectively limit
1142 # all cros_sdk for a chroot to a single instance.
1143 osutils.SafeMakedirsNonRoot(target)
1144 elif not os.path.exists(target):
1145 # Upgrade occurred, but a reversion, or something whacky
1146 # occurred writing to the old location. Wipe and continue.
1147 os.rename(src, target)
1148 else:
1149 # Upgrade occurred once already, but either a reversion or
1150 # some before/after separate cros_sdk usage is at play.
1151 # Wipe and continue.
1152 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001153
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001154 if options.download:
1155 lock.write_lock()
1156 sdk_tarball = FetchRemoteTarballs(
1157 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001158
Mike Frysinger65b7b242021-06-17 21:11:25 -04001159 mounted = False
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001160 if options.create:
1161 lock.write_lock()
1162 # Recheck if the chroot is set up here before creating to make sure we
1163 # account for whatever the various delete/unmount/remount steps above
1164 # have done.
1165 if cros_sdk_lib.IsChrootReady(options.chroot):
1166 logging.debug('Chroot already exists. Skipping creation.')
1167 else:
Mike Frysinger23b5cf52021-06-16 23:18:00 -04001168 cros_sdk_lib.CreateChroot(
1169 Path(options.chroot),
1170 Path(sdk_tarball),
1171 Path(options.cache_dir),
1172 usepkg=not options.bootstrap and not options.nousepkg)
Mike Frysinger65b7b242021-06-17 21:11:25 -04001173 mounted = True
Brian Harring1790ac42012-09-23 08:53:33 -07001174
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001175 if options.enter:
1176 lock.read_lock()
Mike Frysinger65b7b242021-06-17 21:11:25 -04001177 if not mounted:
1178 cros_sdk_lib.MountChrootPaths(options.chroot)
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001179 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
1180 options.chrome_root_mount, options.goma_dir,
1181 options.goma_client_json, options.working_dir,
1182 chroot_command)