blob: 715dca12313462e4f7e137ae7d67a28bb030b4e2 [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,
Joanna Wang1ec0c812021-11-17 17:41:27 -0800176 goma_dir, goma_client_json, reclient_dir, reproxy_cfg_file,
177 working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800178 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400179 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
Alex Klein875b30e2021-01-05 14:56:33 -0700180 if st.f_flag & os.ST_NOSUID:
Mike Frysingere5456972013-06-13 00:07:23 -0400181 cros_build_lib.Die('chroot cannot be in a nosuid mount')
182
Brian Harringae0a5322012-09-15 01:46:51 -0700183 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800184 if chrome_root:
185 cmd.extend(['--chrome_root', chrome_root])
186 if chrome_root_mount:
187 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900188 if goma_dir:
189 cmd.extend(['--goma_dir', goma_dir])
190 if goma_client_json:
191 cmd.extend(['--goma_client_json', goma_client_json])
Joanna Wang1ec0c812021-11-17 17:41:27 -0800192 if reclient_dir:
193 cmd.extend(['--reclient_dir', reclient_dir])
194 if reproxy_cfg_file:
195 cmd.extend(['--reproxy_cfg_file', reproxy_cfg_file])
Yong Hong84ba9172018-02-07 01:37:42 +0800196 if working_dir is not None:
197 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700198
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400199 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800200 cmd.append('--')
201 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700202
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700203 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000204 # Set rlimit and vm.max_map_count to accommodate this.
205 file_limit = 262144
206 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
207 resource.setrlimit(resource.RLIMIT_NOFILE,
208 (max(soft, file_limit), max(hard, file_limit)))
209 max_map_count = int(open('/proc/sys/vm/max_map_count').read())
210 if max_map_count < file_limit:
211 logging.notice(
212 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
213 open('/proc/sys/vm/max_map_count', 'w').write(f'{file_limit}\n')
Mike Frysingere1407f62021-10-30 01:56:40 -0400214 return cros_build_lib.dbg_run(cmd, check=False)
Brian Harringb938c782012-02-29 15:14:38 -0800215
216
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600217def _ImageFileForChroot(chroot):
218 """Find the image file that should be associated with |chroot|.
219
220 This function does not check if the image exists; it simply returns the
221 filename that would be used.
222
223 Args:
224 chroot: Path to the chroot.
225
226 Returns:
227 Path to an image file that would be associated with chroot.
228 """
229 return chroot.rstrip('/') + '.img'
230
231
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600232def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
233 """Create a snapshot for the specified chroot VG/LV.
234
235 Args:
236 snapshot_name: The name of the new snapshot.
237 chroot_vg: The name of the VG containing the origin LV.
238 chroot_lv: The name of the origin LV.
239
240 Returns:
241 True if the snapshot was created, or False if a snapshot with the same
242 name already exists.
243
244 Raises:
245 SystemExit: The lvcreate command failed.
246 """
247 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700248 logging.error(
249 'Cannot create snapshot %s: A volume with that name already '
250 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600251 return False
252
Manoj Guptab12f7302019-06-03 16:40:14 -0700253 cmd = [
254 'lvcreate', '-s', '--name', snapshot_name,
255 '%s/%s' % (chroot_vg, chroot_lv)
256 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600257 try:
258 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
259 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500260 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600261 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500262 except cros_build_lib.RunCommandError as e:
263 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600264
265
266def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
267 """Delete the named snapshot from the specified chroot VG.
268
269 If the requested snapshot is not found, nothing happens. The main chroot LV
270 and internal thinpool LV cannot be deleted with this function.
271
272 Args:
273 snapshot_name: The name of the snapshot to delete.
274 chroot_vg: The name of the VG containing the origin LV.
275 chroot_lv: The name of the origin LV.
276
277 Raises:
278 SystemExit: The lvremove command failed.
279 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600280 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
281 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700282 logging.error(
283 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
284 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600285 return
286
287 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
288 return
289
290 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
291 try:
292 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500293 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500294 except cros_build_lib.RunCommandError as e:
295 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600296
297
298def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
299 """Restore the chroot to an existing snapshot.
300
301 This is done by renaming the original |chroot_lv| LV to a temporary name,
302 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
303 now unused LV. If an error occurs, attempts to rename the original snapshot
304 back to |chroot_lv| to leave the chroot unchanged.
305
306 The chroot must be unmounted before calling this function, and will be left
307 unmounted after this function returns.
308
309 Args:
310 snapshot_name: The name of the snapshot to restore. This snapshot will no
311 longer be accessible at its original name after this function finishes.
312 chroot_vg: The VG containing the chroot LV and snapshot LV.
313 chroot_lv: The name of the original chroot LV.
314
315 Returns:
316 True if the chroot was restored to the requested snapshot, or False if
317 the snapshot wasn't found or isn't valid.
318
319 Raises:
320 SystemExit: Any of the LVM commands failed.
321 """
322 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600323 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
324 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600325 snapshot_name not in valid_snapshots):
326 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
327 snapshot_name, ', '.join(valid_snapshots))
328 return False
329
330 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
331 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
332 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500333 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500334 except cros_build_lib.RunCommandError as e:
335 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600336
337 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
338 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500339 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500340 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600341 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
342 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500343 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500344 except cros_build_lib.RunCommandError as e:
345 cros_build_lib.Die(
346 'Failed to rename %s to chroot and failed to restore %s back to '
347 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
348 cros_build_lib.Die(
349 'Failed to rename %s to chroot! Original chroot LV has '
350 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600351
352 # Some versions of LVM set snapshots to be skipped at auto-activate time.
353 # Other versions don't have this flag at all. We run lvchange to try
354 # disabling auto-skip and activating the volume, but ignore errors. Versions
355 # that don't have the flag should be auto-activated.
356 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
357 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400358 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500359 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600360
361 # Activate the LV in case the lvchange above was needed. Activating an LV
362 # that is already active shouldn't do anything, so this is safe to run even if
363 # the -kn wasn't needed.
364 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500365 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600366
367 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
368 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500369 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500370 except cros_build_lib.RunCommandError as e:
371 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
372 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600373
374 return True
375
376
377def ListChrootSnapshots(chroot_vg, chroot_lv):
378 """Return all snapshots in |chroot_vg| regardless of origin volume.
379
380 Args:
381 chroot_vg: The name of the VG containing the chroot.
382 chroot_lv: The name of the chroot LV.
383
384 Returns:
385 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
386
387 Raises:
388 SystemExit: The lvs command failed.
389 """
390 if not chroot_vg or not chroot_lv:
391 return []
392
Manoj Guptab12f7302019-06-03 16:40:14 -0700393 cmd = [
394 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
395 '--separator', '\t', chroot_vg
396 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600397 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400398 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600399 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600400 except cros_build_lib.RunCommandError:
401 raise SystemExit('Running %r failed!' % cmd)
402
403 # Once the thin origin volume has been deleted, there's no way to tell a
404 # snapshot apart from any other volume. Since this VG is created and managed
405 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
406 # valid snapshots.
407 snapshots = []
408 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
409 for line in result.output.splitlines():
410 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700411 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600412 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600413 not snapshot_attrs.match(lv_attr)):
414 continue
415 snapshots.append(lv_name)
416 return snapshots
417
418
David James56e6c2c2012-10-24 23:54:41 -0700419def _SudoCommand():
420 """Get the 'sudo' command, along with all needed environment variables."""
421
Cindy Lin81093092021-12-09 20:40:57 +0000422 # Pass in the ENVIRONMENT_ALLOWLIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400423 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700424 cmd = ['sudo']
Cindy Lin81093092021-12-09 20:40:57 +0000425 for key in constants.CHROOT_ENVIRONMENT_ALLOWLIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700426 value = os.environ.get(key)
427 if value is not None:
428 cmd += ['%s=%s' % (key, value)]
429
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400430 # We keep PATH not for the chroot but for the re-exec & for programs we might
431 # run before we chroot into the SDK. The process that enters the SDK itself
432 # will take care of initializing PATH to the right value then. But we can't
433 # override the system's default PATH for root as that will hide /sbin.
434 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
435
David James56e6c2c2012-10-24 23:54:41 -0700436 # Pass in the path to the depot_tools so that users can access them from
437 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400438 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500439
David James56e6c2c2012-10-24 23:54:41 -0700440 return cmd
441
442
Josh Triplett472a4182013-03-08 11:48:57 -0800443def _ReportMissing(missing):
444 """Report missing utilities, then exit.
445
446 Args:
447 missing: List of missing utilities, as returned by
448 osutils.FindMissingBinaries. If non-empty, will not return.
449 """
450
451 if missing:
452 raise SystemExit(
453 'The tool(s) %s were not found.\n'
454 'Please install the appropriate package in your host.\n'
455 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700456 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800457
458
459def _ProxySimSetup(options):
460 """Set up proxy simulator, and return only in the child environment.
461
462 TODO: Ideally, this should support multiple concurrent invocations of
463 cros_sdk --proxy-sim; currently, such invocations will conflict with each
464 other due to the veth device names and IP addresses. Either this code would
465 need to generate fresh, unused names for all of these before forking, or it
466 would need to support multiple concurrent cros_sdk invocations sharing one
467 proxy and allowing it to exit when unused (without counting on any local
468 service-management infrastructure on the host).
469 """
470
471 may_need_mpm = False
472 apache_bin = osutils.Which('apache2')
473 if apache_bin is None:
474 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
475 if apache_bin is None:
476 _ReportMissing(('apache2',))
477 else:
478 may_need_mpm = True
479
480 # Module names and .so names included for ease of grepping.
481 apache_modules = [('proxy_module', 'mod_proxy.so'),
482 ('proxy_connect_module', 'mod_proxy_connect.so'),
483 ('proxy_http_module', 'mod_proxy_http.so'),
484 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
485
486 # Find the apache module directory, and make sure it has the modules we need.
487 module_dirs = {}
488 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400489 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800490 for f in glob.glob(os.path.join(g, so)):
491 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400492 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800493 if len(modules_found) == len(apache_modules):
494 break
495 else:
496 # Appease cros lint, which doesn't understand that this else block will not
497 # fall through to the subsequent code which relies on apache_module_path.
498 apache_module_path = None
499 raise SystemExit(
500 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500501 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800502
503 def check_add_module(name):
504 so = 'mod_%s.so' % name
505 if os.access(os.path.join(apache_module_path, so), os.F_OK):
506 mod = '%s_module' % name
507 apache_modules.append((mod, so))
508 return True
509 return False
510
511 check_add_module('authz_core')
512 if may_need_mpm:
513 for mpm in PROXY_APACHE_MPMS:
514 if check_add_module('mpm_%s' % mpm):
515 break
516
517 veth_host = '%s-host' % PROXY_VETH_PREFIX
518 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
519
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500520 # Set up locks to sync the net namespace setup. We need the child to create
521 # the net ns first, and then have the parent assign the guest end of the veth
522 # interface to the child's new network namespace & bring up the proxy. Only
523 # then can the child move forward and rely on the network being up.
524 ns_create_lock = locking.PipeLock()
525 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800526
527 pid = os.fork()
528 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500529 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800530 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500531
532 # Signal the parent the ns is ready to be configured.
533 ns_create_lock.Post()
534 del ns_create_lock
535
536 # Wait for the parent to finish setting up the ns/proxy.
537 ns_setup_lock.Wait()
538 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800539
540 # Set up child side of the network.
541 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500542 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700543 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500544 'dev', veth_guest),
545 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800546 )
547 try:
548 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500549 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500550 except cros_build_lib.RunCommandError as e:
551 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800552
553 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
554 for proto in ('http', 'https', 'ftp'):
555 os.environ[proto + '_proxy'] = proxy_url
556 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
557 os.environ.pop(v, None)
558 return
559
Josh Triplett472a4182013-03-08 11:48:57 -0800560 # Set up parent side of the network.
561 uid = int(os.environ.get('SUDO_UID', '0'))
562 gid = int(os.environ.get('SUDO_GID', '0'))
563 if uid == 0 or gid == 0:
564 for username in PROXY_APACHE_FALLBACK_USERS:
565 try:
566 pwnam = pwd.getpwnam(username)
567 uid, gid = pwnam.pw_uid, pwnam.pw_gid
568 break
569 except KeyError:
570 continue
571 if uid == 0 or gid == 0:
572 raise SystemExit('Could not find a non-root user to run Apache as')
573
574 chroot_parent, chroot_base = os.path.split(options.chroot)
575 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
576 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
577
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500578 # Wait for the child to create the net ns.
579 ns_create_lock.Wait()
580 del ns_create_lock
581
Josh Triplett472a4182013-03-08 11:48:57 -0800582 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500583 'User #%u' % uid,
584 'Group #%u' % gid,
585 'PidFile %s' % pid_file,
586 'ErrorLog %s' % log_file,
587 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
588 'ServerName %s' % PROXY_HOST_IP,
589 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400590 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800591 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500592 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
593 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800594 ]
595 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700596 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
597 veth_guest),
598 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
599 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500600 ('ip', 'link', 'set', veth_host, 'up'),
601 ([apache_bin, '-f', '/dev/null'] +
602 [arg for d in apache_directives for arg in ('-C', d)]),
603 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800604 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700605 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800606 try:
607 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500608 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500609 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800610 # Clean up existing interfaces, if any.
611 cmd_cleanup = ('ip', 'link', 'del', veth_host)
612 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400613 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800614 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700615 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500616 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500617
618 # Signal the child that the net ns/proxy is fully configured now.
619 ns_setup_lock.Post()
620 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800621
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400622 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800623
624
Mike Frysingera78a56e2012-11-20 06:02:30 -0500625def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700626 """Re-execute cros_sdk as root.
627
628 Also unshare the mount namespace so as to ensure that processes outside
629 the chroot can't mess with our mounts.
630 """
Ram Chandrasekar69751282022-02-25 21:07:36 +0000631 if osutils.IsNonRootUser():
Mike Frysinger1f113512020-07-29 03:36:57 -0400632 # Make sure to preserve the active Python executable in case the version
633 # we're running as is not the default one found via the (new) $PATH.
634 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500635 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500636 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700637
638
Mike Frysinger34db8692013-11-11 14:54:08 -0500639def _CreateParser(sdk_latest_version, bootstrap_latest_version):
640 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400641 usage = ('usage: %(prog)s [options] '
642 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700643 parser = commandline.ArgumentParser(
644 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700645
Mike Frysinger34db8692013-11-11 14:54:08 -0500646 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500647 default_chroot = os.path.join(constants.SOURCE_ROOT,
648 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400649 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700650 '--chroot',
651 dest='chroot',
652 default=default_chroot,
653 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700654 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700655 parser.add_argument(
656 '--nouse-image',
657 dest='use_image',
658 action='store_false',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700659 default=False,
Manoj Guptab12f7302019-06-03 16:40:14 -0700660 help='Do not mount the chroot on a loopback image; '
661 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600662 parser.add_argument(
663 '--use-image',
664 dest='use_image',
665 action='store_true',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700666 default=False,
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600667 help='Mount the chroot on a loopback image '
668 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800669
Manoj Guptab12f7302019-06-03 16:40:14 -0700670 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600671 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700672 '--chrome_root',
673 type='path',
674 help='Mount this chrome root into the SDK chroot')
675 parser.add_argument(
676 '--chrome_root_mount',
677 type='path',
678 help='Mount chrome into this path inside SDK chroot')
679 parser.add_argument(
680 '--nousepkg',
681 action='store_true',
682 default=False,
683 help='Do not use binary packages when creating a chroot.')
684 parser.add_argument(
685 '-u',
686 '--url',
687 dest='sdk_url',
688 help='Use sdk tarball located at this url. Use file:// '
689 'for local files.')
690 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700691 '--sdk-version',
692 help=('Use this sdk version. For prebuilt, current is %r'
693 ', for bootstrapping it is %r.' % (sdk_latest_version,
694 bootstrap_latest_version)))
695 parser.add_argument(
696 '--goma_dir',
697 type='path',
698 help='Goma installed directory to mount into the chroot.')
699 parser.add_argument(
700 '--goma_client_json',
701 type='path',
702 help='Service account json file to use goma on bot. '
703 'Mounted into the chroot.')
Joanna Wang1ec0c812021-11-17 17:41:27 -0800704 parser.add_argument(
705 '--reclient-dir',
706 type='path',
707 help='Reclient installed directory to mount into the chroot.')
708 parser.add_argument(
709 '--reproxy-cfg-file',
710 type='path',
711 help="Config file for re-client's reproxy used for remoteexec.")
Yong Hong84ba9172018-02-07 01:37:42 +0800712
713 # Use type=str instead of type='path' to prevent the given path from being
714 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700715 parser.add_argument(
716 '--working-dir',
717 type=str,
718 help='Run the command in specific working directory in '
719 'chroot. If the given directory is a relative '
720 'path, this program will transfer the path to '
721 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800722
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400723 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500724
725 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400726 group = parser.add_argument_group('Commands')
727 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700728 '--enter',
729 action='store_true',
730 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500731 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400732 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700733 '--create',
734 action='store_true',
735 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500736 help='Create the chroot only if it does not already exist. '
737 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400738 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700739 '--bootstrap',
740 action='store_true',
741 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500742 help='Build everything from scratch, including the sdk. '
743 'Use this only if you need to validate a change '
744 'that affects SDK creation itself (toolchain and '
745 'build are typically the only folk who need this). '
746 'Note this will quite heavily slow down the build. '
747 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400748 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700749 '-r',
750 '--replace',
751 action='store_true',
752 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500753 help='Replace an existing SDK chroot. Basically an alias '
754 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400755 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700756 '--delete',
757 action='store_true',
758 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500759 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400760 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600761 '--force',
762 action='store_true',
763 default=False,
764 help='Force unmount/delete of the current SDK chroot even if '
765 'obtaining the write lock fails.')
766 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700767 '--unmount',
768 action='store_true',
769 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600770 help='Unmount and clean up devices associated with the '
771 'SDK chroot if it exists. This does not delete the '
772 'backing image file, so the same chroot can be later '
773 're-mounted for reuse. To fully delete the chroot, use '
774 '--delete. This is primarily useful for working on '
775 'cros_sdk or the chroot setup; you should not need it '
776 'under normal circumstances.')
777 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700778 '--download',
779 action='store_true',
780 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500781 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600782 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700783 '--snapshot-create',
784 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600785 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700786 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600787 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700788 '--snapshot-restore',
789 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600790 help='Restore the chroot to a previously created snapshot.')
791 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700792 '--snapshot-delete',
793 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600794 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700795 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600796 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700797 '--snapshot-list',
798 action='store_true',
799 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600800 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500801 commands = group
802
Mike Frysinger80dfce92014-04-21 10:58:53 -0400803 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400804 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700805 group.add_argument(
806 '--proxy-sim',
807 action='store_true',
808 default=False,
809 help='Simulate a restrictive network requiring an outbound'
810 ' proxy.')
Mike Frysinger79024a32021-04-05 03:28:35 -0400811 for ns, default in (('pid', True), ('net', None)):
812 group.add_argument(
813 f'--ns-{ns}',
814 default=default,
815 action='store_true',
816 help=f'Create a new {ns} namespace.')
817 group.add_argument(
818 f'--no-ns-{ns}',
819 dest=f'ns_{ns}',
820 action='store_false',
821 help=f'Do not create a new {ns} namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400822
Mike Frysinger34db8692013-11-11 14:54:08 -0500823 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400824 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500825 'Internal Chromium OS Build Team Options',
826 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700827 group.add_argument(
828 '--buildbot-log-version',
829 default=False,
830 action='store_true',
831 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500832
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400833 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500834
835
836def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600837 # Turn on strict sudo checks.
838 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400839 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500840 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
841 ignore_missing=True)
842 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700843 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700844
845 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
846 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
847 bootstrap_latest_version = (
848 sdk_latest_version
849 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500850 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400851 options = parser.parse_args(argv)
852 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800853
854 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500855 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800856
Brian Harring1790ac42012-09-23 08:53:33 -0700857 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700858 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600859 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700860 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400861 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700862
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400863 # Merge the outside PATH setting if we re-execed ourselves.
864 if 'CHROMEOS_SUDO_PATH' in os.environ:
865 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
866 os.environ['PATH'])
867
Josh Triplett472a4182013-03-08 11:48:57 -0800868 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
869 if options.proxy_sim:
870 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600871 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800872
Benjamin Gordon040a1162017-06-29 13:44:47 -0600873 if (sdk_latest_version == '<unknown>' or
874 bootstrap_latest_version == '<unknown>'):
875 cros_build_lib.Die(
876 'No SDK version was found. '
877 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
878 'Please change to a directory inside your Chromium OS source tree\n'
879 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400880 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600881
Manoj Guptab12f7302019-06-03 16:40:14 -0700882 any_snapshot_operation = (
883 options.snapshot_create or options.snapshot_restore or
884 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600885
Manoj Guptab12f7302019-06-03 16:40:14 -0700886 if (options.snapshot_delete and
887 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600888 parser.error('Cannot --snapshot_delete the same snapshot you are '
889 'restoring with --snapshot_restore.')
890
David James471532c2013-01-21 10:23:31 -0800891 _ReExecuteIfNeeded([sys.argv[0]] + argv)
892
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600893 lock_path = os.path.dirname(options.chroot)
894 lock_path = os.path.join(
895 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
896
Brian Harring218e13c2012-10-10 16:21:26 -0700897 # Expand out the aliases...
898 if options.replace:
899 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800900
Brian Harring218e13c2012-10-10 16:21:26 -0700901 if options.bootstrap:
902 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800903
Brian Harring218e13c2012-10-10 16:21:26 -0700904 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400905 # pylint: disable=protected-access
906 # This _group_actions access sucks, but upstream decided to not include an
907 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700908 options.enter |= not any(
909 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400910 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700911 options.enter |= bool(chroot_command)
912
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600913 if (options.delete and not options.create and
914 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400915 parser.error('Trying to enter or snapshot the chroot when --delete '
916 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700917
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600918 if (options.unmount and
919 (options.create or options.enter or any_snapshot_operation)):
920 parser.error('--unmount cannot be specified with other chroot actions.')
921
Yong Hong84ba9172018-02-07 01:37:42 +0800922 if options.working_dir is not None and not os.path.isabs(options.working_dir):
923 options.working_dir = path_util.ToChrootPath(options.working_dir)
924
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700925 # If there is an existing chroot image and we're not removing it then force
926 # use_image on. This ensures that people don't have to remember to pass
927 # --use-image after a reboot to avoid losing access to their existing chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600928 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700929 img_path = _ImageFileForChroot(options.chroot)
Benjamin Gordon832a4412020-12-08 10:39:16 -0700930 if (not options.use_image and not options.delete and not options.unmount
931 and os.path.exists(img_path)):
932 if chroot_exists:
933 # If the chroot is already populated, make sure it has something
934 # mounted on it before we assume it came from an image.
935 cmd = ['mountpoint', '-q', options.chroot]
936 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
937 options.use_image = True
938
939 else:
940 logging.notice('Existing chroot image %s found. Forcing --use-image on.',
941 img_path)
942 options.use_image = True
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700943
Benjamin Gordon9bce7032020-11-19 09:58:44 -0700944 if any_snapshot_operation and not options.use_image:
945 if os.path.exists(img_path):
946 options.use_image = True
947 else:
948 cros_build_lib.Die('Snapshot operations are not compatible with '
949 '--nouse-image.')
950
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700951 # Discern if we need to create the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600952 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600953 not options.unmount and not missing_image_tools and
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700954 os.path.exists(img_path)):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600955 # Try to re-mount an existing image in case the user has rebooted.
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500956 with locking.FileLock(lock_path, 'chroot lock') as lock:
957 logging.debug('Checking if existing chroot image can be mounted.')
958 lock.write_lock()
959 cros_sdk_lib.MountChroot(options.chroot, create=False)
960 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
961 if chroot_exists:
962 logging.notice('Mounted existing image %s on chroot', img_path)
Brian Harring218e13c2012-10-10 16:21:26 -0700963
964 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600965 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700966 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800967
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600968 # Make sure we will download if we plan to create.
969 options.download |= options.create
970
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600971 # Anything that needs to manipulate the main chroot mount or communicate with
972 # LVM needs to be done here before we enter the new namespaces.
973
974 # If deleting, do it regardless of the use_image flag so that a
975 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600976 if options.delete:
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500977 # Set a timeout of 300 seconds when getting the lock.
978 with locking.FileLock(lock_path, 'chroot lock',
979 blocking_timeout=300) as lock:
980 try:
981 lock.write_lock()
982 except timeout_util.TimeoutError as e:
983 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
984 if not options.force:
985 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600986 else:
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500987 logging.warning(
988 'cros_sdk was invoked with force option, continuing.')
Mike Frysingerf6fe6d02021-06-16 20:26:35 -0400989 logging.notice('Deleting chroot.')
990 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600991
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600992 # If cleanup was requested, we have to do it while we're still in the original
993 # namespace. Since cleaning up the mount will interfere with any other
994 # commands, we exit here. The check above should have made sure that no other
995 # action was requested, anyway.
996 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -0600997 # Set a timeout of 300 seconds when getting the lock.
998 with locking.FileLock(lock_path, 'chroot lock',
999 blocking_timeout=300) as lock:
1000 try:
1001 lock.write_lock()
1002 except timeout_util.TimeoutError as e:
1003 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -06001004 logging.warning(
1005 'Continuing with CleanupChroot(%s), which will umount the tree.',
1006 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001007 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1008 # even if we don't get the lock because it will attempt to unmount the
1009 # tree and will print diagnostic information from 'fuser', 'lsof', and
1010 # 'ps'.
Mike Frysingerf6fe6d02021-06-16 20:26:35 -04001011 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001012 sys.exit(0)
1013
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001014 # Make sure the main chroot mount is visible. Contents will be filled in
1015 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001016 if options.create and options.use_image:
1017 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001018 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001019Please make sure the lvm2 and thin-provisioning-tools packages
1020are installed on your host.
1021Example(ubuntu):
1022 sudo apt-get install lvm2 thin-provisioning-tools
1023
1024If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001025snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001026
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001027 logging.debug('Making sure chroot image is mounted.')
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001028 with locking.FileLock(lock_path, 'chroot lock') as lock:
1029 lock.write_lock()
1030 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1031 cros_build_lib.Die('Unable to mount %s on chroot',
1032 _ImageFileForChroot(options.chroot))
1033 logging.notice('Mounted %s on chroot',
1034 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001035
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001036 # Snapshot operations will always need the VG/LV, but other actions won't.
1037 if any_snapshot_operation:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001038 with locking.FileLock(lock_path, 'chroot lock') as lock:
1039 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(options.chroot)
1040 if not chroot_vg or not chroot_lv:
1041 cros_build_lib.Die('Unable to find VG/LV for chroot %s', options.chroot)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001042
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001043 # Delete snapshot before creating a new one. This allows the user to
1044 # throw out old state, create a new snapshot, and enter the chroot in a
1045 # single call to cros_sdk. Since restore involves deleting, also do it
1046 # before creating.
1047 if options.snapshot_restore:
1048 lock.write_lock()
1049 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1050 if options.snapshot_restore not in valid_snapshots:
1051 cros_build_lib.Die(
1052 '%s is not a valid snapshot to restore to. Valid snapshots: %s',
1053 options.snapshot_restore, ', '.join(valid_snapshots))
1054 osutils.UmountTree(options.chroot)
1055 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1056 chroot_lv):
1057 cros_build_lib.Die('Unable to restore chroot to snapshot.')
1058 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1059 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001060
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001061 # Use a read lock for snapshot delete and create even though they modify
1062 # the filesystem, because they don't modify the mounted chroot itself.
1063 # The underlying LVM commands take their own locks, so conflicting
1064 # concurrent operations here may crash cros_sdk, but won't corrupt the
1065 # chroot image. This tradeoff seems worth it to allow snapshot
1066 # operations on chroots that have a process inside.
1067 if options.snapshot_delete:
1068 lock.read_lock()
1069 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001070
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001071 if options.snapshot_create:
1072 lock.read_lock()
1073 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1074 chroot_lv):
1075 cros_build_lib.Die('Unable to create snapshot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001076
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001077 img_path = _ImageFileForChroot(options.chroot)
1078 if (options.use_image and os.path.exists(options.chroot) and
1079 os.path.exists(img_path)):
1080 img_stat = os.stat(img_path)
1081 img_used_bytes = img_stat.st_blocks * 512
1082
1083 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001084 mount_used_bytes = mount_stat.f_frsize * (
1085 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001086
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001087 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001088 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1089 logging.notice('%s is using %s GiB more than needed. Running '
Sergey Frolov1cb46ec2020-12-09 21:46:16 -07001090 'fstrim in background.', img_path, extra_gbs)
1091 pid = os.fork()
1092 if pid == 0:
1093 try:
1094 # Directly call Popen to run fstrim concurrently.
1095 cmd = ['fstrim', options.chroot]
1096 subprocess.Popen(cmd, close_fds=True, shell=False)
1097 except subprocess.SubprocessError as e:
1098 logging.warning(
1099 'Running fstrim failed. Consider running fstrim on '
1100 'your chroot manually.\n%s', e)
1101 os._exit(0) # pylint: disable=protected-access
1102 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001103
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001104 # Enter a new set of namespaces. Everything after here cannot directly affect
1105 # the hosts's mounts or alter LVM volumes.
Mike Frysinger79024a32021-04-05 03:28:35 -04001106 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001107
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001108 if options.snapshot_list:
1109 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1110 print(snap)
1111 sys.exit(0)
1112
Brian Harringb938c782012-02-29 15:14:38 -08001113 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001114 sdk_version = (
1115 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001116 else:
1117 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001118 if options.buildbot_log_version:
Chris McDonaldb55b7032021-06-17 16:41:32 -06001119 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001120
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001121 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001122 if options.download:
1123 if options.sdk_url:
1124 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001125 else:
1126 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001127
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001128 with locking.FileLock(lock_path, 'chroot lock') as lock:
1129 if options.proxy_sim:
1130 _ProxySimSetup(options)
Josh Triplett472a4182013-03-08 11:48:57 -08001131
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001132 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1133 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
1134 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001135
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001136 for target in (sdk_cache, distfiles_cache):
1137 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1138 if not os.path.exists(src):
1139 osutils.SafeMakedirsNonRoot(target)
1140 continue
1141 lock.write_lock(
1142 'Upgrade to %r needed but chroot is locked; please exit '
1143 'all instances so this upgrade can finish.' % src)
1144 if not os.path.exists(src):
1145 # Note that while waiting for the write lock, src may've vanished;
1146 # it's a rare race during the upgrade process that's a byproduct
1147 # of us avoiding taking a write lock to do the src check. If we
1148 # took a write lock for that check, it would effectively limit
1149 # all cros_sdk for a chroot to a single instance.
1150 osutils.SafeMakedirsNonRoot(target)
1151 elif not os.path.exists(target):
1152 # Upgrade occurred, but a reversion, or something whacky
1153 # occurred writing to the old location. Wipe and continue.
1154 os.rename(src, target)
1155 else:
1156 # Upgrade occurred once already, but either a reversion or
1157 # some before/after separate cros_sdk usage is at play.
1158 # Wipe and continue.
1159 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001160
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001161 if options.download:
1162 lock.write_lock()
1163 sdk_tarball = FetchRemoteTarballs(
1164 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001165
Mike Frysinger65b7b242021-06-17 21:11:25 -04001166 mounted = False
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001167 if options.create:
1168 lock.write_lock()
1169 # Recheck if the chroot is set up here before creating to make sure we
1170 # account for whatever the various delete/unmount/remount steps above
1171 # have done.
1172 if cros_sdk_lib.IsChrootReady(options.chroot):
1173 logging.debug('Chroot already exists. Skipping creation.')
1174 else:
Mike Frysinger23b5cf52021-06-16 23:18:00 -04001175 cros_sdk_lib.CreateChroot(
1176 Path(options.chroot),
1177 Path(sdk_tarball),
1178 Path(options.cache_dir),
1179 usepkg=not options.bootstrap and not options.nousepkg)
Mike Frysinger65b7b242021-06-17 21:11:25 -04001180 mounted = True
Brian Harring1790ac42012-09-23 08:53:33 -07001181
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001182 if options.enter:
1183 lock.read_lock()
Mike Frysinger65b7b242021-06-17 21:11:25 -04001184 if not mounted:
1185 cros_sdk_lib.MountChrootPaths(options.chroot)
Mike Frysingere1407f62021-10-30 01:56:40 -04001186 ret = EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
1187 options.chrome_root_mount, options.goma_dir,
Joanna Wang1ec0c812021-11-17 17:41:27 -08001188 options.goma_client_json, options.reclient_dir,
1189 options.reproxy_cfg_file, options.working_dir,
Mike Frysingere1407f62021-10-30 01:56:40 -04001190 chroot_command)
1191 sys.exit(ret.returncode)