blob: 286354d8c809d324c6672507f164238ece496e04 [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
Chris McDonaldb55b7032021-06-17 16:41:32 -060016import logging
Brian Harringb938c782012-02-29 15:14:38 -080017import os
Mike Frysinger23b5cf52021-06-16 23:18:00 -040018from pathlib import Path
Josh Triplett472a4182013-03-08 11:48:57 -080019import pwd
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060020import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070021import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070022import resource
Sergey Frolov1cb46ec2020-12-09 21:46:16 -070023import subprocess
David James56e6c2c2012-10-24 23:54:41 -070024import sys
Mike Frysingere852b072021-05-21 12:39:03 -040025import urllib.parse
Brian Harringb938c782012-02-29 15:14:38 -080026
Chris McDonaldb55b7032021-06-17 16:41:32 -060027from chromite.cbuildbot import cbuildbot_alerts
Brian Harringb6cf9142012-09-01 20:43:17 -070028from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060029from chromite.lib import constants
Brian Harringb938c782012-02-29 15:14:38 -080030from chromite.lib import cros_build_lib
Benjamin Gordon74645232018-05-04 17:40:42 -060031from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080032from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080033from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070034from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080035from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040036from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080037from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060038from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050039from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040040from chromite.utils import key_value_store
41
Brian Harringb938c782012-02-29 15:14:38 -080042
Zdenek Behanaa52cea2012-05-30 01:31:11 +020043COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020044
Brian Harringb938c782012-02-29 15:14:38 -080045# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070046ENTER_CHROOT = [
47 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
48]
Brian Harringb938c782012-02-29 15:14:38 -080049
Josh Triplett472a4182013-03-08 11:48:57 -080050# Proxy simulator configuration.
51PROXY_HOST_IP = '192.168.240.1'
52PROXY_PORT = 8080
53PROXY_GUEST_IP = '192.168.240.2'
54PROXY_NETMASK = 30
55PROXY_VETH_PREFIX = 'veth'
56PROXY_CONNECT_PORTS = (80, 443, 9418)
57PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
58PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
59PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070060 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080061PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
62
Josh Triplett9a495f62013-03-15 18:06:55 -070063# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080064NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080065
Josh Triplett472a4182013-03-08 11:48:57 -080066# Tools needed for --proxy-sim only.
67PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080068
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060069# Tools needed when use_image is true (the default).
70IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060071 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060072
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070073# As space is used inside the chroot, the empty space in chroot.img is
74# allocated. Deleting files inside the chroot doesn't automatically return the
75# used space to the OS. Over time, this tends to make the sparse chroot.img
76# less sparse even if the chroot contents don't currently need much space. We
77# can recover most of this unused space with fstrim, but that takes too much
78# time to run it every time. Instead, check the used space against the image
79# size after mounting the chroot and only call fstrim if it looks like we could
80# recover at least this many GiB.
81MAX_UNUSED_IMAGE_GBS = 20
82
Mike Frysingercc838832014-05-24 13:10:30 -040083
Brian Harring1790ac42012-09-23 08:53:33 -070084def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080085 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070086 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
87 return [
88 toolchain.GetSdkURL(
89 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
90 for compressor in COMPRESSION_PREFERENCE
91 ]
Brian Harring1790ac42012-09-23 08:53:33 -070092
93
Mike Frysingerdaf57b82019-11-23 17:26:51 -050094def FetchRemoteTarballs(storage_dir, urls, desc):
Mike Frysinger34db8692013-11-11 14:54:08 -050095 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +020096
97 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -050098 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +020099 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700100 desc: A string describing what tarball we're downloading (for logging).
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200101
102 Returns:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500103 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700104
105 Raises:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500106 ValueError: None of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200107 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200108
Brian Harring1790ac42012-09-23 08:53:33 -0700109 # Note we track content length ourselves since certain versions of curl
110 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700111 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700112 logging.notice('Downloading %s tarball...', desc)
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500113 status_re = re.compile(br'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400114 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200115 for url in urls:
Mike Frysinger3dcacee2019-08-23 17:09:11 -0400116 parsed = urllib.parse.urlparse(url)
Brian Harring1790ac42012-09-23 08:53:33 -0700117 tarball_name = os.path.basename(parsed.path)
118 if parsed.scheme in ('', 'file'):
119 if os.path.exists(parsed.path):
120 return parsed.path
121 continue
122 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700123 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700124 result = retry_util.RunCurl(['-I', url],
125 print_cmd=False,
126 debug_level=logging.NOTICE,
127 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700128 successful = False
129 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700130 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700131 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700132 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700133 successful = True
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500134 elif header.lower().startswith(b'content-length:'):
135 content_length = int(header.split(b':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700136 if successful:
137 break
138 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200139 break
140 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700141 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200142
Brian Harringae0a5322012-09-15 01:46:51 -0700143 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700144 current_size = 0
145 if os.path.exists(tarball_dest):
146 current_size = os.path.getsize(tarball_dest)
147 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700148 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700149 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100150
Brian Harring1790ac42012-09-23 08:53:33 -0700151 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800152 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900153 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700154 print_cmd=False,
155 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800156
Brian Harring1790ac42012-09-23 08:53:33 -0700157 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700158 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
159 # because we might have partial overlap between known prefixes.
160 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
161 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
162 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700163 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700164 if (filename == tarball_name or
165 any([(filename.startswith(p) and
166 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
167 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700168 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700169 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700170 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200171
Brian Harringb938c782012-02-29 15:14:38 -0800172 return tarball_dest
173
174
Brian Harringae0a5322012-09-15 01:46:51 -0700175def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500176 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800177 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400178 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
Alex Klein875b30e2021-01-05 14:56:33 -0700179 if st.f_flag & os.ST_NOSUID:
Mike Frysingere5456972013-06-13 00:07:23 -0400180 cros_build_lib.Die('chroot cannot be in a nosuid mount')
181
Brian Harringae0a5322012-09-15 01:46:51 -0700182 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800183 if chrome_root:
184 cmd.extend(['--chrome_root', chrome_root])
185 if chrome_root_mount:
186 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900187 if goma_dir:
188 cmd.extend(['--goma_dir', goma_dir])
189 if goma_client_json:
190 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800191 if working_dir is not None:
192 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700193
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400194 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800195 cmd.append('--')
196 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700197
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700198 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000199 # Set rlimit and vm.max_map_count to accommodate this.
200 file_limit = 262144
201 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
202 resource.setrlimit(resource.RLIMIT_NOFILE,
203 (max(soft, file_limit), max(hard, file_limit)))
204 max_map_count = int(open('/proc/sys/vm/max_map_count').read())
205 if max_map_count < file_limit:
206 logging.notice(
207 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
208 open('/proc/sys/vm/max_map_count', 'w').write(f'{file_limit}\n')
Alex Klein1ec3e6a2020-04-03 11:13:50 -0600209 ret = cros_build_lib.dbg_run(cmd, check=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700210 # If we were in interactive mode, ignore the exit code; it'll be whatever
211 # they last ran w/in the chroot and won't matter to us one way or another.
212 # Note this does allow chroot entrance to fail and be ignored during
213 # interactive; this is however a rare case and the user will immediately
214 # see it (nor will they be checking the exit code manually).
215 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700216 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800217
218
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600219def _ImageFileForChroot(chroot):
220 """Find the image file that should be associated with |chroot|.
221
222 This function does not check if the image exists; it simply returns the
223 filename that would be used.
224
225 Args:
226 chroot: Path to the chroot.
227
228 Returns:
229 Path to an image file that would be associated with chroot.
230 """
231 return chroot.rstrip('/') + '.img'
232
233
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600234def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
235 """Create a snapshot for the specified chroot VG/LV.
236
237 Args:
238 snapshot_name: The name of the new snapshot.
239 chroot_vg: The name of the VG containing the origin LV.
240 chroot_lv: The name of the origin LV.
241
242 Returns:
243 True if the snapshot was created, or False if a snapshot with the same
244 name already exists.
245
246 Raises:
247 SystemExit: The lvcreate command failed.
248 """
249 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700250 logging.error(
251 'Cannot create snapshot %s: A volume with that name already '
252 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600253 return False
254
Manoj Guptab12f7302019-06-03 16:40:14 -0700255 cmd = [
256 'lvcreate', '-s', '--name', snapshot_name,
257 '%s/%s' % (chroot_vg, chroot_lv)
258 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600259 try:
260 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
261 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500262 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600263 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500264 except cros_build_lib.RunCommandError as e:
265 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600266
267
268def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
269 """Delete the named snapshot from the specified chroot VG.
270
271 If the requested snapshot is not found, nothing happens. The main chroot LV
272 and internal thinpool LV cannot be deleted with this function.
273
274 Args:
275 snapshot_name: The name of the snapshot to delete.
276 chroot_vg: The name of the VG containing the origin LV.
277 chroot_lv: The name of the origin LV.
278
279 Raises:
280 SystemExit: The lvremove command failed.
281 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600282 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
283 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700284 logging.error(
285 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
286 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600287 return
288
289 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
290 return
291
292 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
293 try:
294 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500295 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500296 except cros_build_lib.RunCommandError as e:
297 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600298
299
300def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
301 """Restore the chroot to an existing snapshot.
302
303 This is done by renaming the original |chroot_lv| LV to a temporary name,
304 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
305 now unused LV. If an error occurs, attempts to rename the original snapshot
306 back to |chroot_lv| to leave the chroot unchanged.
307
308 The chroot must be unmounted before calling this function, and will be left
309 unmounted after this function returns.
310
311 Args:
312 snapshot_name: The name of the snapshot to restore. This snapshot will no
313 longer be accessible at its original name after this function finishes.
314 chroot_vg: The VG containing the chroot LV and snapshot LV.
315 chroot_lv: The name of the original chroot LV.
316
317 Returns:
318 True if the chroot was restored to the requested snapshot, or False if
319 the snapshot wasn't found or isn't valid.
320
321 Raises:
322 SystemExit: Any of the LVM commands failed.
323 """
324 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600325 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
326 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600327 snapshot_name not in valid_snapshots):
328 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
329 snapshot_name, ', '.join(valid_snapshots))
330 return False
331
332 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
333 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
334 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500335 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500336 except cros_build_lib.RunCommandError as e:
337 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600338
339 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
340 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500341 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500342 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600343 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
344 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500345 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500346 except cros_build_lib.RunCommandError as e:
347 cros_build_lib.Die(
348 'Failed to rename %s to chroot and failed to restore %s back to '
349 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
350 cros_build_lib.Die(
351 'Failed to rename %s to chroot! Original chroot LV has '
352 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600353
354 # Some versions of LVM set snapshots to be skipped at auto-activate time.
355 # Other versions don't have this flag at all. We run lvchange to try
356 # disabling auto-skip and activating the volume, but ignore errors. Versions
357 # that don't have the flag should be auto-activated.
358 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
359 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400360 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500361 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600362
363 # Activate the LV in case the lvchange above was needed. Activating an LV
364 # that is already active shouldn't do anything, so this is safe to run even if
365 # the -kn wasn't needed.
366 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500367 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600368
369 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
370 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500371 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500372 except cros_build_lib.RunCommandError as e:
373 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
374 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600375
376 return True
377
378
379def ListChrootSnapshots(chroot_vg, chroot_lv):
380 """Return all snapshots in |chroot_vg| regardless of origin volume.
381
382 Args:
383 chroot_vg: The name of the VG containing the chroot.
384 chroot_lv: The name of the chroot LV.
385
386 Returns:
387 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
388
389 Raises:
390 SystemExit: The lvs command failed.
391 """
392 if not chroot_vg or not chroot_lv:
393 return []
394
Manoj Guptab12f7302019-06-03 16:40:14 -0700395 cmd = [
396 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
397 '--separator', '\t', chroot_vg
398 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600399 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400400 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600401 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600402 except cros_build_lib.RunCommandError:
403 raise SystemExit('Running %r failed!' % cmd)
404
405 # Once the thin origin volume has been deleted, there's no way to tell a
406 # snapshot apart from any other volume. Since this VG is created and managed
407 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
408 # valid snapshots.
409 snapshots = []
410 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
411 for line in result.output.splitlines():
412 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700413 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600414 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600415 not snapshot_attrs.match(lv_attr)):
416 continue
417 snapshots.append(lv_name)
418 return snapshots
419
420
David James56e6c2c2012-10-24 23:54:41 -0700421def _SudoCommand():
422 """Get the 'sudo' command, along with all needed environment variables."""
423
David James5a73b4d2013-03-07 10:23:40 -0800424 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400425 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700426 cmd = ['sudo']
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400427 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700428 value = os.environ.get(key)
429 if value is not None:
430 cmd += ['%s=%s' % (key, value)]
431
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400432 # We keep PATH not for the chroot but for the re-exec & for programs we might
433 # run before we chroot into the SDK. The process that enters the SDK itself
434 # will take care of initializing PATH to the right value then. But we can't
435 # override the system's default PATH for root as that will hide /sbin.
436 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
437
David James56e6c2c2012-10-24 23:54:41 -0700438 # Pass in the path to the depot_tools so that users can access them from
439 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400440 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500441
David James56e6c2c2012-10-24 23:54:41 -0700442 return cmd
443
444
Josh Triplett472a4182013-03-08 11:48:57 -0800445def _ReportMissing(missing):
446 """Report missing utilities, then exit.
447
448 Args:
449 missing: List of missing utilities, as returned by
450 osutils.FindMissingBinaries. If non-empty, will not return.
451 """
452
453 if missing:
454 raise SystemExit(
455 'The tool(s) %s were not found.\n'
456 'Please install the appropriate package in your host.\n'
457 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700458 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800459
460
461def _ProxySimSetup(options):
462 """Set up proxy simulator, and return only in the child environment.
463
464 TODO: Ideally, this should support multiple concurrent invocations of
465 cros_sdk --proxy-sim; currently, such invocations will conflict with each
466 other due to the veth device names and IP addresses. Either this code would
467 need to generate fresh, unused names for all of these before forking, or it
468 would need to support multiple concurrent cros_sdk invocations sharing one
469 proxy and allowing it to exit when unused (without counting on any local
470 service-management infrastructure on the host).
471 """
472
473 may_need_mpm = False
474 apache_bin = osutils.Which('apache2')
475 if apache_bin is None:
476 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
477 if apache_bin is None:
478 _ReportMissing(('apache2',))
479 else:
480 may_need_mpm = True
481
482 # Module names and .so names included for ease of grepping.
483 apache_modules = [('proxy_module', 'mod_proxy.so'),
484 ('proxy_connect_module', 'mod_proxy_connect.so'),
485 ('proxy_http_module', 'mod_proxy_http.so'),
486 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
487
488 # Find the apache module directory, and make sure it has the modules we need.
489 module_dirs = {}
490 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400491 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800492 for f in glob.glob(os.path.join(g, so)):
493 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400494 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800495 if len(modules_found) == len(apache_modules):
496 break
497 else:
498 # Appease cros lint, which doesn't understand that this else block will not
499 # fall through to the subsequent code which relies on apache_module_path.
500 apache_module_path = None
501 raise SystemExit(
502 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500503 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800504
505 def check_add_module(name):
506 so = 'mod_%s.so' % name
507 if os.access(os.path.join(apache_module_path, so), os.F_OK):
508 mod = '%s_module' % name
509 apache_modules.append((mod, so))
510 return True
511 return False
512
513 check_add_module('authz_core')
514 if may_need_mpm:
515 for mpm in PROXY_APACHE_MPMS:
516 if check_add_module('mpm_%s' % mpm):
517 break
518
519 veth_host = '%s-host' % PROXY_VETH_PREFIX
520 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
521
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500522 # Set up locks to sync the net namespace setup. We need the child to create
523 # the net ns first, and then have the parent assign the guest end of the veth
524 # interface to the child's new network namespace & bring up the proxy. Only
525 # then can the child move forward and rely on the network being up.
526 ns_create_lock = locking.PipeLock()
527 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800528
529 pid = os.fork()
530 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500531 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800532 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500533
534 # Signal the parent the ns is ready to be configured.
535 ns_create_lock.Post()
536 del ns_create_lock
537
538 # Wait for the parent to finish setting up the ns/proxy.
539 ns_setup_lock.Wait()
540 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800541
542 # Set up child side of the network.
543 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500544 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700545 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500546 'dev', veth_guest),
547 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800548 )
549 try:
550 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500551 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500552 except cros_build_lib.RunCommandError as e:
553 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800554
555 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
556 for proto in ('http', 'https', 'ftp'):
557 os.environ[proto + '_proxy'] = proxy_url
558 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
559 os.environ.pop(v, None)
560 return
561
Josh Triplett472a4182013-03-08 11:48:57 -0800562 # Set up parent side of the network.
563 uid = int(os.environ.get('SUDO_UID', '0'))
564 gid = int(os.environ.get('SUDO_GID', '0'))
565 if uid == 0 or gid == 0:
566 for username in PROXY_APACHE_FALLBACK_USERS:
567 try:
568 pwnam = pwd.getpwnam(username)
569 uid, gid = pwnam.pw_uid, pwnam.pw_gid
570 break
571 except KeyError:
572 continue
573 if uid == 0 or gid == 0:
574 raise SystemExit('Could not find a non-root user to run Apache as')
575
576 chroot_parent, chroot_base = os.path.split(options.chroot)
577 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
578 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
579
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500580 # Wait for the child to create the net ns.
581 ns_create_lock.Wait()
582 del ns_create_lock
583
Josh Triplett472a4182013-03-08 11:48:57 -0800584 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500585 'User #%u' % uid,
586 'Group #%u' % gid,
587 'PidFile %s' % pid_file,
588 'ErrorLog %s' % log_file,
589 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
590 'ServerName %s' % PROXY_HOST_IP,
591 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400592 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800593 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500594 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
595 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800596 ]
597 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700598 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
599 veth_guest),
600 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
601 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500602 ('ip', 'link', 'set', veth_host, 'up'),
603 ([apache_bin, '-f', '/dev/null'] +
604 [arg for d in apache_directives for arg in ('-C', d)]),
605 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800606 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700607 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800608 try:
609 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500610 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500611 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800612 # Clean up existing interfaces, if any.
613 cmd_cleanup = ('ip', 'link', 'del', veth_host)
614 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400615 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800616 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700617 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500618 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500619
620 # Signal the child that the net ns/proxy is fully configured now.
621 ns_setup_lock.Post()
622 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800623
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400624 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800625
626
Mike Frysingera78a56e2012-11-20 06:02:30 -0500627def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700628 """Re-execute cros_sdk as root.
629
630 Also unshare the mount namespace so as to ensure that processes outside
631 the chroot can't mess with our mounts.
632 """
633 if os.geteuid() != 0:
Mike Frysinger1f113512020-07-29 03:36:57 -0400634 # Make sure to preserve the active Python executable in case the version
635 # we're running as is not the default one found via the (new) $PATH.
636 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500637 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500638 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700639
640
Mike Frysinger34db8692013-11-11 14:54:08 -0500641def _CreateParser(sdk_latest_version, bootstrap_latest_version):
642 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400643 usage = ('usage: %(prog)s [options] '
644 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700645 parser = commandline.ArgumentParser(
646 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700647
Mike Frysinger34db8692013-11-11 14:54:08 -0500648 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500649 default_chroot = os.path.join(constants.SOURCE_ROOT,
650 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400651 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700652 '--chroot',
653 dest='chroot',
654 default=default_chroot,
655 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700656 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700657 parser.add_argument(
658 '--nouse-image',
659 dest='use_image',
660 action='store_false',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700661 default=False,
Manoj Guptab12f7302019-06-03 16:40:14 -0700662 help='Do not mount the chroot on a loopback image; '
663 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600664 parser.add_argument(
665 '--use-image',
666 dest='use_image',
667 action='store_true',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700668 default=False,
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600669 help='Mount the chroot on a loopback image '
670 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800671
Manoj Guptab12f7302019-06-03 16:40:14 -0700672 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600673 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700674 '--chrome_root',
675 type='path',
676 help='Mount this chrome root into the SDK chroot')
677 parser.add_argument(
678 '--chrome_root_mount',
679 type='path',
680 help='Mount chrome into this path inside SDK chroot')
681 parser.add_argument(
682 '--nousepkg',
683 action='store_true',
684 default=False,
685 help='Do not use binary packages when creating a chroot.')
686 parser.add_argument(
687 '-u',
688 '--url',
689 dest='sdk_url',
690 help='Use sdk tarball located at this url. Use file:// '
691 'for local files.')
692 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700693 '--sdk-version',
694 help=('Use this sdk version. For prebuilt, current is %r'
695 ', for bootstrapping it is %r.' % (sdk_latest_version,
696 bootstrap_latest_version)))
697 parser.add_argument(
698 '--goma_dir',
699 type='path',
700 help='Goma installed directory to mount into the chroot.')
701 parser.add_argument(
702 '--goma_client_json',
703 type='path',
704 help='Service account json file to use goma on bot. '
705 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800706
707 # Use type=str instead of type='path' to prevent the given path from being
708 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700709 parser.add_argument(
710 '--working-dir',
711 type=str,
712 help='Run the command in specific working directory in '
713 'chroot. If the given directory is a relative '
714 'path, this program will transfer the path to '
715 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800716
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400717 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500718
719 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400720 group = parser.add_argument_group('Commands')
721 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700722 '--enter',
723 action='store_true',
724 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500725 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400726 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700727 '--create',
728 action='store_true',
729 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500730 help='Create the chroot only if it does not already exist. '
731 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400732 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700733 '--bootstrap',
734 action='store_true',
735 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500736 help='Build everything from scratch, including the sdk. '
737 'Use this only if you need to validate a change '
738 'that affects SDK creation itself (toolchain and '
739 'build are typically the only folk who need this). '
740 'Note this will quite heavily slow down the build. '
741 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400742 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700743 '-r',
744 '--replace',
745 action='store_true',
746 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500747 help='Replace an existing SDK chroot. Basically an alias '
748 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400749 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700750 '--delete',
751 action='store_true',
752 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500753 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400754 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600755 '--force',
756 action='store_true',
757 default=False,
758 help='Force unmount/delete of the current SDK chroot even if '
759 'obtaining the write lock fails.')
760 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700761 '--unmount',
762 action='store_true',
763 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600764 help='Unmount and clean up devices associated with the '
765 'SDK chroot if it exists. This does not delete the '
766 'backing image file, so the same chroot can be later '
767 're-mounted for reuse. To fully delete the chroot, use '
768 '--delete. This is primarily useful for working on '
769 'cros_sdk or the chroot setup; you should not need it '
770 'under normal circumstances.')
771 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700772 '--download',
773 action='store_true',
774 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500775 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600776 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700777 '--snapshot-create',
778 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600779 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700780 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600781 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700782 '--snapshot-restore',
783 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600784 help='Restore the chroot to a previously created snapshot.')
785 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700786 '--snapshot-delete',
787 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600788 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700789 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600790 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700791 '--snapshot-list',
792 action='store_true',
793 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600794 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500795 commands = group
796
Mike Frysinger80dfce92014-04-21 10:58:53 -0400797 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400798 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700799 group.add_argument(
800 '--proxy-sim',
801 action='store_true',
802 default=False,
803 help='Simulate a restrictive network requiring an outbound'
804 ' proxy.')
Mike Frysinger79024a32021-04-05 03:28:35 -0400805 for ns, default in (('pid', True), ('net', None)):
806 group.add_argument(
807 f'--ns-{ns}',
808 default=default,
809 action='store_true',
810 help=f'Create a new {ns} namespace.')
811 group.add_argument(
812 f'--no-ns-{ns}',
813 dest=f'ns_{ns}',
814 action='store_false',
815 help=f'Do not create a new {ns} namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400816
Mike Frysinger34db8692013-11-11 14:54:08 -0500817 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400818 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500819 'Internal Chromium OS Build Team Options',
820 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700821 group.add_argument(
822 '--buildbot-log-version',
823 default=False,
824 action='store_true',
825 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500826
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400827 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500828
829
830def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600831 # Turn on strict sudo checks.
832 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400833 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500834 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
835 ignore_missing=True)
836 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700837 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700838
839 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
840 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
841 bootstrap_latest_version = (
842 sdk_latest_version
843 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500844 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400845 options = parser.parse_args(argv)
846 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800847
848 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500849 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800850
Brian Harring1790ac42012-09-23 08:53:33 -0700851 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700852 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600853 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700854 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400855 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700856
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400857 # Merge the outside PATH setting if we re-execed ourselves.
858 if 'CHROMEOS_SUDO_PATH' in os.environ:
859 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
860 os.environ['PATH'])
861
Josh Triplett472a4182013-03-08 11:48:57 -0800862 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
863 if options.proxy_sim:
864 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600865 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800866
Benjamin Gordon040a1162017-06-29 13:44:47 -0600867 if (sdk_latest_version == '<unknown>' or
868 bootstrap_latest_version == '<unknown>'):
869 cros_build_lib.Die(
870 'No SDK version was found. '
871 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
872 'Please change to a directory inside your Chromium OS source tree\n'
873 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400874 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600875
Manoj Guptab12f7302019-06-03 16:40:14 -0700876 any_snapshot_operation = (
877 options.snapshot_create or options.snapshot_restore or
878 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600879
Manoj Guptab12f7302019-06-03 16:40:14 -0700880 if (options.snapshot_delete and
881 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600882 parser.error('Cannot --snapshot_delete the same snapshot you are '
883 'restoring with --snapshot_restore.')
884
David James471532c2013-01-21 10:23:31 -0800885 _ReExecuteIfNeeded([sys.argv[0]] + argv)
886
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600887 lock_path = os.path.dirname(options.chroot)
888 lock_path = os.path.join(
889 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
890
Brian Harring218e13c2012-10-10 16:21:26 -0700891 # Expand out the aliases...
892 if options.replace:
893 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800894
Brian Harring218e13c2012-10-10 16:21:26 -0700895 if options.bootstrap:
896 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800897
Brian Harring218e13c2012-10-10 16:21:26 -0700898 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400899 # pylint: disable=protected-access
900 # This _group_actions access sucks, but upstream decided to not include an
901 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700902 options.enter |= not any(
903 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400904 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700905 options.enter |= bool(chroot_command)
906
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600907 if (options.delete and not options.create and
908 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400909 parser.error('Trying to enter or snapshot the chroot when --delete '
910 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700911
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600912 if (options.unmount and
913 (options.create or options.enter or any_snapshot_operation)):
914 parser.error('--unmount cannot be specified with other chroot actions.')
915
Yong Hong84ba9172018-02-07 01:37:42 +0800916 if options.working_dir is not None and not os.path.isabs(options.working_dir):
917 options.working_dir = path_util.ToChrootPath(options.working_dir)
918
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700919 # If there is an existing chroot image and we're not removing it then force
920 # use_image on. This ensures that people don't have to remember to pass
921 # --use-image after a reboot to avoid losing access to their existing chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600922 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700923 img_path = _ImageFileForChroot(options.chroot)
Benjamin Gordon832a4412020-12-08 10:39:16 -0700924 if (not options.use_image and not options.delete and not options.unmount
925 and os.path.exists(img_path)):
926 if chroot_exists:
927 # If the chroot is already populated, make sure it has something
928 # mounted on it before we assume it came from an image.
929 cmd = ['mountpoint', '-q', options.chroot]
930 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
931 options.use_image = True
932
933 else:
934 logging.notice('Existing chroot image %s found. Forcing --use-image on.',
935 img_path)
936 options.use_image = True
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700937
Benjamin Gordon9bce7032020-11-19 09:58:44 -0700938 if any_snapshot_operation and not options.use_image:
939 if os.path.exists(img_path):
940 options.use_image = True
941 else:
942 cros_build_lib.Die('Snapshot operations are not compatible with '
943 '--nouse-image.')
944
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700945 # Discern if we need to create the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600946 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600947 not options.unmount and not missing_image_tools and
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700948 os.path.exists(img_path)):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600949 # Try to re-mount an existing image in case the user has rebooted.
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500950 with locking.FileLock(lock_path, 'chroot lock') as lock:
951 logging.debug('Checking if existing chroot image can be mounted.')
952 lock.write_lock()
953 cros_sdk_lib.MountChroot(options.chroot, create=False)
954 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
955 if chroot_exists:
956 logging.notice('Mounted existing image %s on chroot', img_path)
Brian Harring218e13c2012-10-10 16:21:26 -0700957
958 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600959 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700960 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800961
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600962 # Make sure we will download if we plan to create.
963 options.download |= options.create
964
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600965 # Anything that needs to manipulate the main chroot mount or communicate with
966 # LVM needs to be done here before we enter the new namespaces.
967
968 # If deleting, do it regardless of the use_image flag so that a
969 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600970 if options.delete:
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500971 # Set a timeout of 300 seconds when getting the lock.
972 with locking.FileLock(lock_path, 'chroot lock',
973 blocking_timeout=300) as lock:
974 try:
975 lock.write_lock()
976 except timeout_util.TimeoutError as e:
977 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
978 if not options.force:
979 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600980 else:
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500981 logging.warning(
982 'cros_sdk was invoked with force option, continuing.')
Mike Frysingerf6fe6d02021-06-16 20:26:35 -0400983 logging.notice('Deleting chroot.')
984 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600985
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600986 # If cleanup was requested, we have to do it while we're still in the original
987 # namespace. Since cleaning up the mount will interfere with any other
988 # commands, we exit here. The check above should have made sure that no other
989 # action was requested, anyway.
990 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -0600991 # Set a timeout of 300 seconds when getting the lock.
992 with locking.FileLock(lock_path, 'chroot lock',
993 blocking_timeout=300) as lock:
994 try:
995 lock.write_lock()
996 except timeout_util.TimeoutError as e:
997 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -0600998 logging.warning(
999 'Continuing with CleanupChroot(%s), which will umount the tree.',
1000 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001001 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1002 # even if we don't get the lock because it will attempt to unmount the
1003 # tree and will print diagnostic information from 'fuser', 'lsof', and
1004 # 'ps'.
Mike Frysingerf6fe6d02021-06-16 20:26:35 -04001005 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001006 sys.exit(0)
1007
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001008 # Make sure the main chroot mount is visible. Contents will be filled in
1009 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001010 if options.create and options.use_image:
1011 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001012 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001013Please make sure the lvm2 and thin-provisioning-tools packages
1014are installed on your host.
1015Example(ubuntu):
1016 sudo apt-get install lvm2 thin-provisioning-tools
1017
1018If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001019snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001020
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001021 logging.debug('Making sure chroot image is mounted.')
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001022 with locking.FileLock(lock_path, 'chroot lock') as lock:
1023 lock.write_lock()
1024 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1025 cros_build_lib.Die('Unable to mount %s on chroot',
1026 _ImageFileForChroot(options.chroot))
1027 logging.notice('Mounted %s on chroot',
1028 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001029
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001030 # Snapshot operations will always need the VG/LV, but other actions won't.
1031 if any_snapshot_operation:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001032 with locking.FileLock(lock_path, 'chroot lock') as lock:
1033 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(options.chroot)
1034 if not chroot_vg or not chroot_lv:
1035 cros_build_lib.Die('Unable to find VG/LV for chroot %s', options.chroot)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001036
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001037 # Delete snapshot before creating a new one. This allows the user to
1038 # throw out old state, create a new snapshot, and enter the chroot in a
1039 # single call to cros_sdk. Since restore involves deleting, also do it
1040 # before creating.
1041 if options.snapshot_restore:
1042 lock.write_lock()
1043 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1044 if options.snapshot_restore not in valid_snapshots:
1045 cros_build_lib.Die(
1046 '%s is not a valid snapshot to restore to. Valid snapshots: %s',
1047 options.snapshot_restore, ', '.join(valid_snapshots))
1048 osutils.UmountTree(options.chroot)
1049 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1050 chroot_lv):
1051 cros_build_lib.Die('Unable to restore chroot to snapshot.')
1052 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1053 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001054
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001055 # Use a read lock for snapshot delete and create even though they modify
1056 # the filesystem, because they don't modify the mounted chroot itself.
1057 # The underlying LVM commands take their own locks, so conflicting
1058 # concurrent operations here may crash cros_sdk, but won't corrupt the
1059 # chroot image. This tradeoff seems worth it to allow snapshot
1060 # operations on chroots that have a process inside.
1061 if options.snapshot_delete:
1062 lock.read_lock()
1063 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001064
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001065 if options.snapshot_create:
1066 lock.read_lock()
1067 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1068 chroot_lv):
1069 cros_build_lib.Die('Unable to create snapshot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001070
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001071 img_path = _ImageFileForChroot(options.chroot)
1072 if (options.use_image and os.path.exists(options.chroot) and
1073 os.path.exists(img_path)):
1074 img_stat = os.stat(img_path)
1075 img_used_bytes = img_stat.st_blocks * 512
1076
1077 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001078 mount_used_bytes = mount_stat.f_frsize * (
1079 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001080
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001081 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001082 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1083 logging.notice('%s is using %s GiB more than needed. Running '
Sergey Frolov1cb46ec2020-12-09 21:46:16 -07001084 'fstrim in background.', img_path, extra_gbs)
1085 pid = os.fork()
1086 if pid == 0:
1087 try:
1088 # Directly call Popen to run fstrim concurrently.
1089 cmd = ['fstrim', options.chroot]
1090 subprocess.Popen(cmd, close_fds=True, shell=False)
1091 except subprocess.SubprocessError as e:
1092 logging.warning(
1093 'Running fstrim failed. Consider running fstrim on '
1094 'your chroot manually.\n%s', e)
1095 os._exit(0) # pylint: disable=protected-access
1096 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001097
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001098 # Enter a new set of namespaces. Everything after here cannot directly affect
1099 # the hosts's mounts or alter LVM volumes.
Mike Frysinger79024a32021-04-05 03:28:35 -04001100 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001101
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001102 if options.snapshot_list:
1103 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1104 print(snap)
1105 sys.exit(0)
1106
Brian Harringb938c782012-02-29 15:14:38 -08001107 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001108 sdk_version = (
1109 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001110 else:
1111 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001112 if options.buildbot_log_version:
Chris McDonaldb55b7032021-06-17 16:41:32 -06001113 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001114
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001115 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001116 if options.download:
1117 if options.sdk_url:
1118 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001119 else:
1120 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001121
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001122 with locking.FileLock(lock_path, 'chroot lock') as lock:
1123 if options.proxy_sim:
1124 _ProxySimSetup(options)
Josh Triplett472a4182013-03-08 11:48:57 -08001125
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001126 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1127 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
1128 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001129
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001130 for target in (sdk_cache, distfiles_cache):
1131 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1132 if not os.path.exists(src):
1133 osutils.SafeMakedirsNonRoot(target)
1134 continue
1135 lock.write_lock(
1136 'Upgrade to %r needed but chroot is locked; please exit '
1137 'all instances so this upgrade can finish.' % src)
1138 if not os.path.exists(src):
1139 # Note that while waiting for the write lock, src may've vanished;
1140 # it's a rare race during the upgrade process that's a byproduct
1141 # of us avoiding taking a write lock to do the src check. If we
1142 # took a write lock for that check, it would effectively limit
1143 # all cros_sdk for a chroot to a single instance.
1144 osutils.SafeMakedirsNonRoot(target)
1145 elif not os.path.exists(target):
1146 # Upgrade occurred, but a reversion, or something whacky
1147 # occurred writing to the old location. Wipe and continue.
1148 os.rename(src, target)
1149 else:
1150 # Upgrade occurred once already, but either a reversion or
1151 # some before/after separate cros_sdk usage is at play.
1152 # Wipe and continue.
1153 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001154
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001155 if options.download:
1156 lock.write_lock()
1157 sdk_tarball = FetchRemoteTarballs(
1158 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001159
Mike Frysinger65b7b242021-06-17 21:11:25 -04001160 mounted = False
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001161 if options.create:
1162 lock.write_lock()
1163 # Recheck if the chroot is set up here before creating to make sure we
1164 # account for whatever the various delete/unmount/remount steps above
1165 # have done.
1166 if cros_sdk_lib.IsChrootReady(options.chroot):
1167 logging.debug('Chroot already exists. Skipping creation.')
1168 else:
Mike Frysinger23b5cf52021-06-16 23:18:00 -04001169 cros_sdk_lib.CreateChroot(
1170 Path(options.chroot),
1171 Path(sdk_tarball),
1172 Path(options.cache_dir),
1173 usepkg=not options.bootstrap and not options.nousepkg)
Mike Frysinger65b7b242021-06-17 21:11:25 -04001174 mounted = True
Brian Harring1790ac42012-09-23 08:53:33 -07001175
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001176 if options.enter:
1177 lock.read_lock()
Mike Frysinger65b7b242021-06-17 21:11:25 -04001178 if not mounted:
1179 cros_sdk_lib.MountChrootPaths(options.chroot)
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001180 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
1181 options.chrome_root_mount, options.goma_dir,
1182 options.goma_client_json, options.working_dir,
1183 chroot_command)