blob: 7a4c9facee429541b9d4c0aa9f2c31958b9aaef9 [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
Mike Frysinger12d055b2022-06-23 12:26:47 -040015import ast
Josh Triplett472a4182013-03-08 11:48:57 -080016import glob
Chris McDonaldb55b7032021-06-17 16:41:32 -060017import logging
Brian Harringb938c782012-02-29 15:14:38 -080018import os
Mike Frysinger23b5cf52021-06-16 23:18:00 -040019from pathlib import Path
Josh Triplett472a4182013-03-08 11:48:57 -080020import pwd
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060021import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070022import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070023import resource
Sergey Frolov1cb46ec2020-12-09 21:46:16 -070024import subprocess
David James56e6c2c2012-10-24 23:54:41 -070025import sys
Mike Frysingere852b072021-05-21 12:39:03 -040026import urllib.parse
Brian Harringb938c782012-02-29 15:14:38 -080027
Chris McDonaldb55b7032021-06-17 16:41:32 -060028from chromite.cbuildbot import cbuildbot_alerts
Brian Harringb6cf9142012-09-01 20:43:17 -070029from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060030from chromite.lib import constants
Brian Harringb938c782012-02-29 15:14:38 -080031from chromite.lib import cros_build_lib
Benjamin Gordon74645232018-05-04 17:40:42 -060032from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080033from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080034from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070035from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080036from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040037from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080038from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060039from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050040from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040041from chromite.utils import key_value_store
42
Brian Harringb938c782012-02-29 15:14:38 -080043
Mike Frysingerf744d032022-05-07 20:38:39 -040044# Which compression algos the SDK tarball uses. We've used xz since 2012.
45COMPRESSION_PREFERENCE = ('xz',)
Zdenek Behanfd0efe42012-04-13 04:36:40 +020046
Brian Harringb938c782012-02-29 15:14:38 -080047# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070048ENTER_CHROOT = [
49 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
50]
Brian Harringb938c782012-02-29 15:14:38 -080051
Josh Triplett472a4182013-03-08 11:48:57 -080052# Proxy simulator configuration.
53PROXY_HOST_IP = '192.168.240.1'
54PROXY_PORT = 8080
55PROXY_GUEST_IP = '192.168.240.2'
56PROXY_NETMASK = 30
57PROXY_VETH_PREFIX = 'veth'
58PROXY_CONNECT_PORTS = (80, 443, 9418)
59PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
60PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
61PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070062 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080063PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
64
Josh Triplett9a495f62013-03-15 18:06:55 -070065# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080066NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080067
Josh Triplett472a4182013-03-08 11:48:57 -080068# Tools needed for --proxy-sim only.
69PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080070
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060071# Tools needed when use_image is true (the default).
72IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060073 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060074
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070075# As space is used inside the chroot, the empty space in chroot.img is
76# allocated. Deleting files inside the chroot doesn't automatically return the
77# used space to the OS. Over time, this tends to make the sparse chroot.img
78# less sparse even if the chroot contents don't currently need much space. We
79# can recover most of this unused space with fstrim, but that takes too much
80# time to run it every time. Instead, check the used space against the image
81# size after mounting the chroot and only call fstrim if it looks like we could
82# recover at least this many GiB.
83MAX_UNUSED_IMAGE_GBS = 20
84
Mike Frysingercc838832014-05-24 13:10:30 -040085
Brian Harring1790ac42012-09-23 08:53:33 -070086def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080087 """Returns the URL for a given arch/version"""
Mike Frysingerf744d032022-05-07 20:38:39 -040088 extension = {'xz': 'tar.xz'}
Manoj Guptab12f7302019-06-03 16:40:14 -070089 return [
90 toolchain.GetSdkURL(
91 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
92 for compressor in COMPRESSION_PREFERENCE
93 ]
Brian Harring1790ac42012-09-23 08:53:33 -070094
95
Mike Frysingerf744d032022-05-07 20:38:39 -040096def FetchRemoteTarballs(storage_dir, urls):
Mike Frysinger34db8692013-11-11 14:54:08 -050097 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +020098
99 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500100 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200101 urls: List of URLs to try to download. Download will stop on first success.
102
103 Returns:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500104 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700105
106 Raises:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500107 ValueError: None of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200108 """
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
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500112 status_re = re.compile(br'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400113 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200114 for url in urls:
Mike Frysingerf744d032022-05-07 20:38:39 -0400115 logging.notice('Downloading tarball %s ...', urls[0].rsplit('/', 1)[-1])
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
Mike Frysinger876a8e52022-06-23 18:07:30 -0400129 for header in result.stdout.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.
Mike Frysingerf744d032022-05-07 20:38:39 -0400160 for p in Path(storage_dir).glob('cros-sdk-*'):
161 if p.name == tarball_name:
Brian Harring1790ac42012-09-23 08:53:33 -0700162 continue
Mike Frysingerf744d032022-05-07 20:38:39 -0400163 logging.info('Cleaning up old tarball: %s', p)
164 osutils.SafeUnlink(p)
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200165
Brian Harringb938c782012-02-29 15:14:38 -0800166 return tarball_dest
167
168
Brian Harringae0a5322012-09-15 01:46:51 -0700169def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Joanna Wang1ec0c812021-11-17 17:41:27 -0800170 goma_dir, goma_client_json, reclient_dir, reproxy_cfg_file,
171 working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800172 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400173 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
Alex Klein875b30e2021-01-05 14:56:33 -0700174 if st.f_flag & os.ST_NOSUID:
Mike Frysingere5456972013-06-13 00:07:23 -0400175 cros_build_lib.Die('chroot cannot be in a nosuid mount')
176
Brian Harringae0a5322012-09-15 01:46:51 -0700177 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800178 if chrome_root:
179 cmd.extend(['--chrome_root', chrome_root])
180 if chrome_root_mount:
181 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900182 if goma_dir:
183 cmd.extend(['--goma_dir', goma_dir])
184 if goma_client_json:
185 cmd.extend(['--goma_client_json', goma_client_json])
Joanna Wang1ec0c812021-11-17 17:41:27 -0800186 if reclient_dir:
187 cmd.extend(['--reclient_dir', reclient_dir])
188 if reproxy_cfg_file:
189 cmd.extend(['--reproxy_cfg_file', reproxy_cfg_file])
Yong Hong84ba9172018-02-07 01:37:42 +0800190 if working_dir is not None:
191 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700192
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400193 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800194 cmd.append('--')
195 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700196
Mike Frysinger12d055b2022-06-23 12:26:47 -0400197 if 'CHROMEOS_SUDO_RLIMITS' in os.environ:
198 _SetRlimits(os.environ.pop('CHROMEOS_SUDO_RLIMITS'))
199
Mike Frysingerebb23fc2022-06-06 21:17:39 -0400200 # Some systems set the soft limit too low. Bump it up to the hard limit.
201 # We don't override the hard limit because it's something the admins put
202 # in place and we want to respect such configs. http://b/234353695
203 soft, hard = resource.getrlimit(resource.RLIMIT_NPROC)
204 if soft != resource.RLIM_INFINITY and soft < 4096:
205 if soft < hard or hard == resource.RLIM_INFINITY:
206 resource.setrlimit(resource.RLIMIT_NPROC, (hard, hard))
207
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700208 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000209 # Set rlimit and vm.max_map_count to accommodate this.
210 file_limit = 262144
211 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
212 resource.setrlimit(resource.RLIMIT_NOFILE,
213 (max(soft, file_limit), max(hard, file_limit)))
Mike Frysinger7201ec52022-06-23 11:12:41 -0400214 max_map_count = int(osutils.ReadFile('/proc/sys/vm/max_map_count'))
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000215 if max_map_count < file_limit:
216 logging.notice(
217 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
Mike Frysinger7201ec52022-06-23 11:12:41 -0400218 osutils.WriteFile('/proc/sys/vm/max_map_count', str(file_limit))
Mike Frysingere1407f62021-10-30 01:56:40 -0400219 return cros_build_lib.dbg_run(cmd, check=False)
Brian Harringb938c782012-02-29 15:14:38 -0800220
221
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600222def _ImageFileForChroot(chroot):
223 """Find the image file that should be associated with |chroot|.
224
225 This function does not check if the image exists; it simply returns the
226 filename that would be used.
227
228 Args:
229 chroot: Path to the chroot.
230
231 Returns:
232 Path to an image file that would be associated with chroot.
233 """
234 return chroot.rstrip('/') + '.img'
235
236
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600237def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
238 """Create a snapshot for the specified chroot VG/LV.
239
240 Args:
241 snapshot_name: The name of the new snapshot.
242 chroot_vg: The name of the VG containing the origin LV.
243 chroot_lv: The name of the origin LV.
244
245 Returns:
246 True if the snapshot was created, or False if a snapshot with the same
247 name already exists.
248
249 Raises:
250 SystemExit: The lvcreate command failed.
251 """
252 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700253 logging.error(
254 'Cannot create snapshot %s: A volume with that name already '
255 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600256 return False
257
Manoj Guptab12f7302019-06-03 16:40:14 -0700258 cmd = [
259 'lvcreate', '-s', '--name', snapshot_name,
260 '%s/%s' % (chroot_vg, chroot_lv)
261 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600262 try:
263 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
264 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500265 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600266 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500267 except cros_build_lib.RunCommandError as e:
268 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600269
270
271def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
272 """Delete the named snapshot from the specified chroot VG.
273
274 If the requested snapshot is not found, nothing happens. The main chroot LV
275 and internal thinpool LV cannot be deleted with this function.
276
277 Args:
278 snapshot_name: The name of the snapshot to delete.
279 chroot_vg: The name of the VG containing the origin LV.
280 chroot_lv: The name of the origin LV.
281
282 Raises:
283 SystemExit: The lvremove command failed.
284 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600285 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
286 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700287 logging.error(
288 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
289 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600290 return
291
292 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
293 return
294
295 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
296 try:
297 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500298 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500299 except cros_build_lib.RunCommandError as e:
300 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600301
302
303def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
304 """Restore the chroot to an existing snapshot.
305
306 This is done by renaming the original |chroot_lv| LV to a temporary name,
307 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
308 now unused LV. If an error occurs, attempts to rename the original snapshot
309 back to |chroot_lv| to leave the chroot unchanged.
310
311 The chroot must be unmounted before calling this function, and will be left
312 unmounted after this function returns.
313
314 Args:
315 snapshot_name: The name of the snapshot to restore. This snapshot will no
316 longer be accessible at its original name after this function finishes.
317 chroot_vg: The VG containing the chroot LV and snapshot LV.
318 chroot_lv: The name of the original chroot LV.
319
320 Returns:
321 True if the chroot was restored to the requested snapshot, or False if
322 the snapshot wasn't found or isn't valid.
323
324 Raises:
325 SystemExit: Any of the LVM commands failed.
326 """
327 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600328 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
329 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600330 snapshot_name not in valid_snapshots):
331 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
332 snapshot_name, ', '.join(valid_snapshots))
333 return False
334
335 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
336 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
337 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500338 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500339 except cros_build_lib.RunCommandError as e:
340 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600341
342 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
343 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500344 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500345 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600346 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
347 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500348 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500349 except cros_build_lib.RunCommandError as e:
350 cros_build_lib.Die(
351 'Failed to rename %s to chroot and failed to restore %s back to '
352 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
353 cros_build_lib.Die(
354 'Failed to rename %s to chroot! Original chroot LV has '
355 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600356
357 # Some versions of LVM set snapshots to be skipped at auto-activate time.
358 # Other versions don't have this flag at all. We run lvchange to try
359 # disabling auto-skip and activating the volume, but ignore errors. Versions
360 # that don't have the flag should be auto-activated.
361 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
362 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400363 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500364 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600365
366 # Activate the LV in case the lvchange above was needed. Activating an LV
367 # that is already active shouldn't do anything, so this is safe to run even if
368 # the -kn wasn't needed.
369 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500370 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600371
372 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
373 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500374 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500375 except cros_build_lib.RunCommandError as e:
376 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
377 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600378
379 return True
380
381
382def ListChrootSnapshots(chroot_vg, chroot_lv):
383 """Return all snapshots in |chroot_vg| regardless of origin volume.
384
385 Args:
386 chroot_vg: The name of the VG containing the chroot.
387 chroot_lv: The name of the chroot LV.
388
389 Returns:
390 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
391
392 Raises:
393 SystemExit: The lvs command failed.
394 """
395 if not chroot_vg or not chroot_lv:
396 return []
397
Manoj Guptab12f7302019-06-03 16:40:14 -0700398 cmd = [
399 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
400 '--separator', '\t', chroot_vg
401 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600402 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400403 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600404 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600405 except cros_build_lib.RunCommandError:
406 raise SystemExit('Running %r failed!' % cmd)
407
408 # Once the thin origin volume has been deleted, there's no way to tell a
409 # snapshot apart from any other volume. Since this VG is created and managed
410 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
411 # valid snapshots.
412 snapshots = []
413 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
Mike Frysinger876a8e52022-06-23 18:07:30 -0400414 for line in result.stdout.splitlines():
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600415 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700416 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600417 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600418 not snapshot_attrs.match(lv_attr)):
419 continue
420 snapshots.append(lv_name)
421 return snapshots
422
423
Mike Frysinger12d055b2022-06-23 12:26:47 -0400424# The rlimits we will lookup & pass down, in order.
425RLIMITS_TO_PASS = (
426 resource.RLIMIT_AS,
427 resource.RLIMIT_CORE,
428 resource.RLIMIT_CPU,
429 resource.RLIMIT_FSIZE,
430 resource.RLIMIT_MEMLOCK,
431 resource.RLIMIT_NICE,
432 resource.RLIMIT_NOFILE,
433 resource.RLIMIT_NPROC,
434 resource.RLIMIT_RSS,
435 resource.RLIMIT_STACK,
436)
437
438
439def _GetRlimits() -> str:
440 """Serialize current rlimits."""
441 return str(tuple(resource.getrlimit(x) for x in RLIMITS_TO_PASS))
442
443
444def _SetRlimits(limits: str) -> None:
445 """Deserialize rlimits."""
446 for rlim, limit in zip(RLIMITS_TO_PASS, ast.literal_eval(limits)):
447 cur_limit = resource.getrlimit(rlim)
448 if cur_limit != limit:
449 # Turn the number into a symbolic name for logging.
450 name = 'RLIMIT_???'
451 for name, num in resource.__dict__.items():
452 if name.startswith('RLIMIT_') and num == rlim:
453 break
454 logging.debug('Restoring user rlimit %s from %r to %r',
455 name, cur_limit, limit)
456
457 resource.setrlimit(rlim, limit)
458
459
David James56e6c2c2012-10-24 23:54:41 -0700460def _SudoCommand():
461 """Get the 'sudo' command, along with all needed environment variables."""
462
Cindy Lin81093092021-12-09 20:40:57 +0000463 # Pass in the ENVIRONMENT_ALLOWLIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400464 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700465 cmd = ['sudo']
Cindy Lin81093092021-12-09 20:40:57 +0000466 for key in constants.CHROOT_ENVIRONMENT_ALLOWLIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700467 value = os.environ.get(key)
468 if value is not None:
469 cmd += ['%s=%s' % (key, value)]
470
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400471 # We keep PATH not for the chroot but for the re-exec & for programs we might
472 # run before we chroot into the SDK. The process that enters the SDK itself
473 # will take care of initializing PATH to the right value then. But we can't
474 # override the system's default PATH for root as that will hide /sbin.
475 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
476
Mike Frysinger12d055b2022-06-23 12:26:47 -0400477 # Pass along current rlimit settings so we can restore them.
478 cmd += [f'CHROMEOS_SUDO_RLIMITS={_GetRlimits()}']
479
David James56e6c2c2012-10-24 23:54:41 -0700480 # Pass in the path to the depot_tools so that users can access them from
481 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400482 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500483
David James56e6c2c2012-10-24 23:54:41 -0700484 return cmd
485
486
Josh Triplett472a4182013-03-08 11:48:57 -0800487def _ReportMissing(missing):
488 """Report missing utilities, then exit.
489
490 Args:
491 missing: List of missing utilities, as returned by
492 osutils.FindMissingBinaries. If non-empty, will not return.
493 """
494
495 if missing:
496 raise SystemExit(
497 'The tool(s) %s were not found.\n'
498 'Please install the appropriate package in your host.\n'
499 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700500 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800501
502
503def _ProxySimSetup(options):
504 """Set up proxy simulator, and return only in the child environment.
505
506 TODO: Ideally, this should support multiple concurrent invocations of
507 cros_sdk --proxy-sim; currently, such invocations will conflict with each
508 other due to the veth device names and IP addresses. Either this code would
509 need to generate fresh, unused names for all of these before forking, or it
510 would need to support multiple concurrent cros_sdk invocations sharing one
511 proxy and allowing it to exit when unused (without counting on any local
512 service-management infrastructure on the host).
513 """
514
515 may_need_mpm = False
516 apache_bin = osutils.Which('apache2')
517 if apache_bin is None:
518 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
519 if apache_bin is None:
520 _ReportMissing(('apache2',))
521 else:
522 may_need_mpm = True
523
524 # Module names and .so names included for ease of grepping.
525 apache_modules = [('proxy_module', 'mod_proxy.so'),
526 ('proxy_connect_module', 'mod_proxy_connect.so'),
527 ('proxy_http_module', 'mod_proxy_http.so'),
528 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
529
530 # Find the apache module directory, and make sure it has the modules we need.
531 module_dirs = {}
532 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400533 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800534 for f in glob.glob(os.path.join(g, so)):
535 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400536 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800537 if len(modules_found) == len(apache_modules):
538 break
539 else:
540 # Appease cros lint, which doesn't understand that this else block will not
541 # fall through to the subsequent code which relies on apache_module_path.
542 apache_module_path = None
543 raise SystemExit(
544 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500545 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800546
547 def check_add_module(name):
548 so = 'mod_%s.so' % name
549 if os.access(os.path.join(apache_module_path, so), os.F_OK):
550 mod = '%s_module' % name
551 apache_modules.append((mod, so))
552 return True
553 return False
554
555 check_add_module('authz_core')
556 if may_need_mpm:
557 for mpm in PROXY_APACHE_MPMS:
558 if check_add_module('mpm_%s' % mpm):
559 break
560
561 veth_host = '%s-host' % PROXY_VETH_PREFIX
562 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
563
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500564 # Set up locks to sync the net namespace setup. We need the child to create
565 # the net ns first, and then have the parent assign the guest end of the veth
566 # interface to the child's new network namespace & bring up the proxy. Only
567 # then can the child move forward and rely on the network being up.
568 ns_create_lock = locking.PipeLock()
569 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800570
571 pid = os.fork()
572 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500573 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800574 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500575
576 # Signal the parent the ns is ready to be configured.
577 ns_create_lock.Post()
578 del ns_create_lock
579
580 # Wait for the parent to finish setting up the ns/proxy.
581 ns_setup_lock.Wait()
582 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800583
584 # Set up child side of the network.
585 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500586 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700587 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500588 'dev', veth_guest),
589 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800590 )
591 try:
592 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500593 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500594 except cros_build_lib.RunCommandError as e:
595 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800596
597 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
598 for proto in ('http', 'https', 'ftp'):
599 os.environ[proto + '_proxy'] = proxy_url
600 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
601 os.environ.pop(v, None)
602 return
603
Josh Triplett472a4182013-03-08 11:48:57 -0800604 # Set up parent side of the network.
605 uid = int(os.environ.get('SUDO_UID', '0'))
606 gid = int(os.environ.get('SUDO_GID', '0'))
607 if uid == 0 or gid == 0:
608 for username in PROXY_APACHE_FALLBACK_USERS:
609 try:
610 pwnam = pwd.getpwnam(username)
611 uid, gid = pwnam.pw_uid, pwnam.pw_gid
612 break
613 except KeyError:
614 continue
615 if uid == 0 or gid == 0:
616 raise SystemExit('Could not find a non-root user to run Apache as')
617
618 chroot_parent, chroot_base = os.path.split(options.chroot)
619 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
620 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
621
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500622 # Wait for the child to create the net ns.
623 ns_create_lock.Wait()
624 del ns_create_lock
625
Josh Triplett472a4182013-03-08 11:48:57 -0800626 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500627 'User #%u' % uid,
628 'Group #%u' % gid,
629 'PidFile %s' % pid_file,
630 'ErrorLog %s' % log_file,
631 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
632 'ServerName %s' % PROXY_HOST_IP,
633 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400634 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800635 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500636 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
637 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800638 ]
639 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700640 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
641 veth_guest),
642 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
643 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500644 ('ip', 'link', 'set', veth_host, 'up'),
645 ([apache_bin, '-f', '/dev/null'] +
646 [arg for d in apache_directives for arg in ('-C', d)]),
647 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800648 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700649 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800650 try:
651 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500652 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500653 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800654 # Clean up existing interfaces, if any.
655 cmd_cleanup = ('ip', 'link', 'del', veth_host)
656 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400657 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800658 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700659 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500660 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500661
662 # Signal the child that the net ns/proxy is fully configured now.
663 ns_setup_lock.Post()
664 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800665
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400666 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800667
668
Mike Frysingera78a56e2012-11-20 06:02:30 -0500669def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700670 """Re-execute cros_sdk as root.
671
672 Also unshare the mount namespace so as to ensure that processes outside
673 the chroot can't mess with our mounts.
674 """
Ram Chandrasekar69751282022-02-25 21:07:36 +0000675 if osutils.IsNonRootUser():
Mike Frysinger1f113512020-07-29 03:36:57 -0400676 # Make sure to preserve the active Python executable in case the version
677 # we're running as is not the default one found via the (new) $PATH.
678 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500679 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500680 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700681
682
Mike Frysinger34db8692013-11-11 14:54:08 -0500683def _CreateParser(sdk_latest_version, bootstrap_latest_version):
684 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400685 usage = ('usage: %(prog)s [options] '
686 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700687 parser = commandline.ArgumentParser(
688 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700689
Mike Frysinger34db8692013-11-11 14:54:08 -0500690 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500691 default_chroot = os.path.join(constants.SOURCE_ROOT,
692 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400693 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700694 '--chroot',
695 dest='chroot',
696 default=default_chroot,
697 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700698 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700699 parser.add_argument(
700 '--nouse-image',
701 dest='use_image',
702 action='store_false',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700703 default=False,
Manoj Guptab12f7302019-06-03 16:40:14 -0700704 help='Do not mount the chroot on a loopback image; '
705 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600706 parser.add_argument(
707 '--use-image',
708 dest='use_image',
709 action='store_true',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700710 default=False,
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600711 help='Mount the chroot on a loopback image '
712 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800713
Manoj Guptab12f7302019-06-03 16:40:14 -0700714 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600715 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700716 '--chrome_root',
717 type='path',
718 help='Mount this chrome root into the SDK chroot')
719 parser.add_argument(
720 '--chrome_root_mount',
721 type='path',
722 help='Mount chrome into this path inside SDK chroot')
723 parser.add_argument(
724 '--nousepkg',
725 action='store_true',
726 default=False,
727 help='Do not use binary packages when creating a chroot.')
728 parser.add_argument(
729 '-u',
730 '--url',
731 dest='sdk_url',
732 help='Use sdk tarball located at this url. Use file:// '
733 'for local files.')
734 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700735 '--sdk-version',
736 help=('Use this sdk version. For prebuilt, current is %r'
737 ', for bootstrapping it is %r.' % (sdk_latest_version,
738 bootstrap_latest_version)))
739 parser.add_argument(
740 '--goma_dir',
741 type='path',
742 help='Goma installed directory to mount into the chroot.')
743 parser.add_argument(
744 '--goma_client_json',
745 type='path',
746 help='Service account json file to use goma on bot. '
747 'Mounted into the chroot.')
Joanna Wang1ec0c812021-11-17 17:41:27 -0800748 parser.add_argument(
749 '--reclient-dir',
750 type='path',
751 help='Reclient installed directory to mount into the chroot.')
752 parser.add_argument(
753 '--reproxy-cfg-file',
754 type='path',
755 help="Config file for re-client's reproxy used for remoteexec.")
Sergey Frolov6038f612022-06-13 14:07:21 -0600756 parser.add_argument(
757 '--skip-chroot-upgrade',
758 dest='chroot_upgrade',
759 action='store_false',
760 default=True,
761 help='Skip automatic SDK and toolchain upgrade when entering the chroot. '
762 'Never guaranteed to work, especially as ToT moves forward.')
Yong Hong84ba9172018-02-07 01:37:42 +0800763
764 # Use type=str instead of type='path' to prevent the given path from being
765 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700766 parser.add_argument(
767 '--working-dir',
768 type=str,
769 help='Run the command in specific working directory in '
770 'chroot. If the given directory is a relative '
771 'path, this program will transfer the path to '
772 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800773
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400774 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500775
776 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400777 group = parser.add_argument_group('Commands')
778 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700779 '--enter',
780 action='store_true',
781 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500782 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400783 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700784 '--create',
785 action='store_true',
786 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500787 help='Create the chroot only if it does not already exist. '
788 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400789 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700790 '--bootstrap',
791 action='store_true',
792 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500793 help='Build everything from scratch, including the sdk. '
794 'Use this only if you need to validate a change '
795 'that affects SDK creation itself (toolchain and '
796 'build are typically the only folk who need this). '
797 'Note this will quite heavily slow down the build. '
798 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400799 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700800 '-r',
801 '--replace',
802 action='store_true',
803 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500804 help='Replace an existing SDK chroot. Basically an alias '
805 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400806 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700807 '--delete',
808 action='store_true',
809 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500810 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400811 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600812 '--force',
813 action='store_true',
814 default=False,
815 help='Force unmount/delete of the current SDK chroot even if '
816 'obtaining the write lock fails.')
817 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700818 '--unmount',
819 action='store_true',
820 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600821 help='Unmount and clean up devices associated with the '
822 'SDK chroot if it exists. This does not delete the '
823 'backing image file, so the same chroot can be later '
824 're-mounted for reuse. To fully delete the chroot, use '
825 '--delete. This is primarily useful for working on '
826 'cros_sdk or the chroot setup; you should not need it '
827 'under normal circumstances.')
828 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700829 '--download',
830 action='store_true',
831 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500832 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600833 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700834 '--snapshot-create',
835 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600836 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700837 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600838 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700839 '--snapshot-restore',
840 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600841 help='Restore the chroot to a previously created snapshot.')
842 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700843 '--snapshot-delete',
844 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600845 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700846 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600847 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700848 '--snapshot-list',
849 action='store_true',
850 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600851 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500852 commands = group
853
Mike Frysinger80dfce92014-04-21 10:58:53 -0400854 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400855 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700856 group.add_argument(
857 '--proxy-sim',
858 action='store_true',
859 default=False,
860 help='Simulate a restrictive network requiring an outbound'
861 ' proxy.')
Mike Frysinger79024a32021-04-05 03:28:35 -0400862 for ns, default in (('pid', True), ('net', None)):
863 group.add_argument(
864 f'--ns-{ns}',
865 default=default,
866 action='store_true',
867 help=f'Create a new {ns} namespace.')
868 group.add_argument(
869 f'--no-ns-{ns}',
870 dest=f'ns_{ns}',
871 action='store_false',
872 help=f'Do not create a new {ns} namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400873
Mike Frysinger34db8692013-11-11 14:54:08 -0500874 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400875 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500876 'Internal Chromium OS Build Team Options',
877 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700878 group.add_argument(
879 '--buildbot-log-version',
880 default=False,
881 action='store_true',
882 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500883
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400884 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500885
886
887def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600888 # Turn on strict sudo checks.
889 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400890 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500891 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
892 ignore_missing=True)
893 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700894 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700895
896 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
897 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
898 bootstrap_latest_version = (
899 sdk_latest_version
900 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500901 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400902 options = parser.parse_args(argv)
903 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800904
Sloan Johnsonc59e9262022-06-10 20:51:53 +0000905 # Some basic checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500906 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800907
Brian Harring1790ac42012-09-23 08:53:33 -0700908 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700909 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600910 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700911 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400912 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700913
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400914 # Merge the outside PATH setting if we re-execed ourselves.
915 if 'CHROMEOS_SUDO_PATH' in os.environ:
916 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
917 os.environ['PATH'])
918
Josh Triplett472a4182013-03-08 11:48:57 -0800919 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
920 if options.proxy_sim:
921 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600922 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800923
Benjamin Gordon040a1162017-06-29 13:44:47 -0600924 if (sdk_latest_version == '<unknown>' or
925 bootstrap_latest_version == '<unknown>'):
926 cros_build_lib.Die(
927 'No SDK version was found. '
928 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
929 'Please change to a directory inside your Chromium OS source tree\n'
930 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400931 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600932
Manoj Guptab12f7302019-06-03 16:40:14 -0700933 any_snapshot_operation = (
934 options.snapshot_create or options.snapshot_restore or
935 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600936
Manoj Guptab12f7302019-06-03 16:40:14 -0700937 if (options.snapshot_delete and
938 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600939 parser.error('Cannot --snapshot_delete the same snapshot you are '
940 'restoring with --snapshot_restore.')
941
David James471532c2013-01-21 10:23:31 -0800942 _ReExecuteIfNeeded([sys.argv[0]] + argv)
943
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600944 lock_path = os.path.dirname(options.chroot)
945 lock_path = os.path.join(
946 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
947
Brian Harring218e13c2012-10-10 16:21:26 -0700948 # Expand out the aliases...
949 if options.replace:
950 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800951
Brian Harring218e13c2012-10-10 16:21:26 -0700952 if options.bootstrap:
953 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800954
Brian Harring218e13c2012-10-10 16:21:26 -0700955 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400956 # pylint: disable=protected-access
957 # This _group_actions access sucks, but upstream decided to not include an
958 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700959 options.enter |= not any(
960 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400961 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700962 options.enter |= bool(chroot_command)
963
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600964 if (options.delete and not options.create and
965 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400966 parser.error('Trying to enter or snapshot the chroot when --delete '
967 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700968
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600969 if (options.unmount and
970 (options.create or options.enter or any_snapshot_operation)):
971 parser.error('--unmount cannot be specified with other chroot actions.')
972
Yong Hong84ba9172018-02-07 01:37:42 +0800973 if options.working_dir is not None and not os.path.isabs(options.working_dir):
974 options.working_dir = path_util.ToChrootPath(options.working_dir)
975
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700976 # If there is an existing chroot image and we're not removing it then force
977 # use_image on. This ensures that people don't have to remember to pass
978 # --use-image after a reboot to avoid losing access to their existing chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600979 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700980 img_path = _ImageFileForChroot(options.chroot)
Benjamin Gordon832a4412020-12-08 10:39:16 -0700981 if (not options.use_image and not options.delete and not options.unmount
982 and os.path.exists(img_path)):
983 if chroot_exists:
984 # If the chroot is already populated, make sure it has something
985 # mounted on it before we assume it came from an image.
986 cmd = ['mountpoint', '-q', options.chroot]
987 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
988 options.use_image = True
989
990 else:
991 logging.notice('Existing chroot image %s found. Forcing --use-image on.',
992 img_path)
993 options.use_image = True
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700994
Benjamin Gordon9bce7032020-11-19 09:58:44 -0700995 if any_snapshot_operation and not options.use_image:
996 if os.path.exists(img_path):
997 options.use_image = True
998 else:
999 cros_build_lib.Die('Snapshot operations are not compatible with '
1000 '--nouse-image.')
1001
Benjamin Gordon87b068a2020-11-02 11:22:16 -07001002 # Discern if we need to create the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001003 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001004 not options.unmount and not missing_image_tools and
Benjamin Gordon87b068a2020-11-02 11:22:16 -07001005 os.path.exists(img_path)):
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001006 # Try to re-mount an existing image in case the user has rebooted.
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001007 with locking.FileLock(lock_path, 'chroot lock') as lock:
1008 logging.debug('Checking if existing chroot image can be mounted.')
1009 lock.write_lock()
1010 cros_sdk_lib.MountChroot(options.chroot, create=False)
1011 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1012 if chroot_exists:
1013 logging.notice('Mounted existing image %s on chroot', img_path)
Brian Harring218e13c2012-10-10 16:21:26 -07001014
1015 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001016 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -07001017 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -08001018
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001019 # Make sure we will download if we plan to create.
1020 options.download |= options.create
1021
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001022 # Anything that needs to manipulate the main chroot mount or communicate with
1023 # LVM needs to be done here before we enter the new namespaces.
1024
1025 # If deleting, do it regardless of the use_image flag so that a
1026 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001027 if options.delete:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001028 # Set a timeout of 300 seconds when getting the lock.
1029 with locking.FileLock(lock_path, 'chroot lock',
1030 blocking_timeout=300) as lock:
1031 try:
1032 lock.write_lock()
1033 except timeout_util.TimeoutError as e:
1034 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
1035 if not options.force:
1036 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001037 else:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001038 logging.warning(
1039 'cros_sdk was invoked with force option, continuing.')
Mike Frysingerf6fe6d02021-06-16 20:26:35 -04001040 logging.notice('Deleting chroot.')
1041 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001042
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001043 # If cleanup was requested, we have to do it while we're still in the original
1044 # namespace. Since cleaning up the mount will interfere with any other
1045 # commands, we exit here. The check above should have made sure that no other
1046 # action was requested, anyway.
1047 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001048 # Set a timeout of 300 seconds when getting the lock.
1049 with locking.FileLock(lock_path, 'chroot lock',
1050 blocking_timeout=300) as lock:
1051 try:
1052 lock.write_lock()
1053 except timeout_util.TimeoutError as e:
1054 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -06001055 logging.warning(
1056 'Continuing with CleanupChroot(%s), which will umount the tree.',
1057 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001058 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1059 # even if we don't get the lock because it will attempt to unmount the
1060 # tree and will print diagnostic information from 'fuser', 'lsof', and
1061 # 'ps'.
Mike Frysingerf6fe6d02021-06-16 20:26:35 -04001062 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001063 sys.exit(0)
1064
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001065 # Make sure the main chroot mount is visible. Contents will be filled in
1066 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001067 if options.create and options.use_image:
1068 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001069 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001070Please make sure the lvm2 and thin-provisioning-tools packages
1071are installed on your host.
1072Example(ubuntu):
1073 sudo apt-get install lvm2 thin-provisioning-tools
1074
1075If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001076snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001077
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001078 logging.debug('Making sure chroot image is mounted.')
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001079 with locking.FileLock(lock_path, 'chroot lock') as lock:
1080 lock.write_lock()
1081 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1082 cros_build_lib.Die('Unable to mount %s on chroot',
1083 _ImageFileForChroot(options.chroot))
1084 logging.notice('Mounted %s on chroot',
1085 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001086
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001087 # Snapshot operations will always need the VG/LV, but other actions won't.
1088 if any_snapshot_operation:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001089 with locking.FileLock(lock_path, 'chroot lock') as lock:
1090 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(options.chroot)
1091 if not chroot_vg or not chroot_lv:
1092 cros_build_lib.Die('Unable to find VG/LV for chroot %s', options.chroot)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001093
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001094 # Delete snapshot before creating a new one. This allows the user to
1095 # throw out old state, create a new snapshot, and enter the chroot in a
1096 # single call to cros_sdk. Since restore involves deleting, also do it
1097 # before creating.
1098 if options.snapshot_restore:
1099 lock.write_lock()
1100 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1101 if options.snapshot_restore not in valid_snapshots:
1102 cros_build_lib.Die(
1103 '%s is not a valid snapshot to restore to. Valid snapshots: %s',
1104 options.snapshot_restore, ', '.join(valid_snapshots))
1105 osutils.UmountTree(options.chroot)
1106 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1107 chroot_lv):
1108 cros_build_lib.Die('Unable to restore chroot to snapshot.')
1109 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1110 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001111
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001112 # Use a read lock for snapshot delete and create even though they modify
1113 # the filesystem, because they don't modify the mounted chroot itself.
1114 # The underlying LVM commands take their own locks, so conflicting
1115 # concurrent operations here may crash cros_sdk, but won't corrupt the
1116 # chroot image. This tradeoff seems worth it to allow snapshot
1117 # operations on chroots that have a process inside.
1118 if options.snapshot_delete:
1119 lock.read_lock()
1120 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001121
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001122 if options.snapshot_create:
1123 lock.read_lock()
1124 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1125 chroot_lv):
1126 cros_build_lib.Die('Unable to create snapshot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001127
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001128 img_path = _ImageFileForChroot(options.chroot)
1129 if (options.use_image and os.path.exists(options.chroot) and
1130 os.path.exists(img_path)):
1131 img_stat = os.stat(img_path)
1132 img_used_bytes = img_stat.st_blocks * 512
1133
1134 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001135 mount_used_bytes = mount_stat.f_frsize * (
1136 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001137
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001138 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001139 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1140 logging.notice('%s is using %s GiB more than needed. Running '
Sergey Frolov1cb46ec2020-12-09 21:46:16 -07001141 'fstrim in background.', img_path, extra_gbs)
1142 pid = os.fork()
1143 if pid == 0:
1144 try:
1145 # Directly call Popen to run fstrim concurrently.
1146 cmd = ['fstrim', options.chroot]
1147 subprocess.Popen(cmd, close_fds=True, shell=False)
1148 except subprocess.SubprocessError as e:
1149 logging.warning(
1150 'Running fstrim failed. Consider running fstrim on '
1151 'your chroot manually.\n%s', e)
1152 os._exit(0) # pylint: disable=protected-access
1153 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001154
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001155 # Enter a new set of namespaces. Everything after here cannot directly affect
1156 # the hosts's mounts or alter LVM volumes.
Mike Frysinger79024a32021-04-05 03:28:35 -04001157 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001158
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001159 if options.snapshot_list:
1160 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1161 print(snap)
1162 sys.exit(0)
1163
Brian Harringb938c782012-02-29 15:14:38 -08001164 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001165 sdk_version = (
1166 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001167 else:
1168 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001169 if options.buildbot_log_version:
Chris McDonaldb55b7032021-06-17 16:41:32 -06001170 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001171
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001172 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001173 if options.download:
1174 if options.sdk_url:
1175 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001176 else:
1177 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001178
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001179 with locking.FileLock(lock_path, 'chroot lock') as lock:
1180 if options.proxy_sim:
1181 _ProxySimSetup(options)
Josh Triplett472a4182013-03-08 11:48:57 -08001182
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001183 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1184 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
1185 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001186
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001187 for target in (sdk_cache, distfiles_cache):
1188 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1189 if not os.path.exists(src):
1190 osutils.SafeMakedirsNonRoot(target)
1191 continue
1192 lock.write_lock(
1193 'Upgrade to %r needed but chroot is locked; please exit '
1194 'all instances so this upgrade can finish.' % src)
1195 if not os.path.exists(src):
1196 # Note that while waiting for the write lock, src may've vanished;
1197 # it's a rare race during the upgrade process that's a byproduct
1198 # of us avoiding taking a write lock to do the src check. If we
1199 # took a write lock for that check, it would effectively limit
1200 # all cros_sdk for a chroot to a single instance.
1201 osutils.SafeMakedirsNonRoot(target)
1202 elif not os.path.exists(target):
1203 # Upgrade occurred, but a reversion, or something whacky
1204 # occurred writing to the old location. Wipe and continue.
1205 os.rename(src, target)
1206 else:
1207 # Upgrade occurred once already, but either a reversion or
1208 # some before/after separate cros_sdk usage is at play.
1209 # Wipe and continue.
1210 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001211
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001212 if options.download:
1213 lock.write_lock()
Mike Frysingerf744d032022-05-07 20:38:39 -04001214 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Brian Harring218e13c2012-10-10 16:21:26 -07001215
Mike Frysinger65b7b242021-06-17 21:11:25 -04001216 mounted = False
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001217 if options.create:
1218 lock.write_lock()
1219 # Recheck if the chroot is set up here before creating to make sure we
1220 # account for whatever the various delete/unmount/remount steps above
1221 # have done.
1222 if cros_sdk_lib.IsChrootReady(options.chroot):
1223 logging.debug('Chroot already exists. Skipping creation.')
1224 else:
Mike Frysinger23b5cf52021-06-16 23:18:00 -04001225 cros_sdk_lib.CreateChroot(
1226 Path(options.chroot),
1227 Path(sdk_tarball),
1228 Path(options.cache_dir),
Sergey Frolov6038f612022-06-13 14:07:21 -06001229 usepkg=not options.bootstrap and not options.nousepkg,
1230 chroot_upgrade=options.chroot_upgrade)
Mike Frysinger65b7b242021-06-17 21:11:25 -04001231 mounted = True
Brian Harring1790ac42012-09-23 08:53:33 -07001232
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001233 if options.enter:
1234 lock.read_lock()
Mike Frysinger65b7b242021-06-17 21:11:25 -04001235 if not mounted:
1236 cros_sdk_lib.MountChrootPaths(options.chroot)
Mike Frysingere1407f62021-10-30 01:56:40 -04001237 ret = EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
1238 options.chrome_root_mount, options.goma_dir,
Joanna Wang1ec0c812021-11-17 17:41:27 -08001239 options.goma_client_json, options.reclient_dir,
1240 options.reproxy_cfg_file, options.working_dir,
Mike Frysingere1407f62021-10-30 01:56:40 -04001241 chroot_command)
1242 sys.exit(ret.returncode)