blob: b2ca946ddb95ee64906cc8705d17d2a3e9e8d4b7 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Mike Frysinger2de7f042012-07-10 04:45:03 -04002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Brian Harringb938c782012-02-29 15:14:38 -08003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
Manoj Gupta7fad04d2019-06-14 20:12:25 -07005
Mike Frysinger2f95cfc2015-06-04 04:00:26 -04006"""Manage SDK chroots.
7
8This script is used for manipulating local chroot environments; creating,
9deleting, downloading, etc. If given --enter (or no args), it defaults
10to an interactive bash shell within the chroot.
11
12If given args those are passed to the chroot environment, and executed.
13"""
Brian Harringb938c782012-02-29 15:14:38 -080014
Mike Frysinger383367e2014-09-16 15:06:17 -040015from __future__ import print_function
16
Mike Frysinger2f95cfc2015-06-04 04:00:26 -040017import argparse
Josh Triplett472a4182013-03-08 11:48:57 -080018import glob
Brian Harringb938c782012-02-29 15:14:38 -080019import os
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
David James56e6c2c2012-10-24 23:54:41 -070024import sys
Mike Frysinger3dcacee2019-08-23 17:09:11 -040025
26from six.moves import urllib
Brian Harringb938c782012-02-29 15:14:38 -080027
Aviv Keshetb7519e12016-10-04 00:50:00 -070028from chromite.lib import constants
Brian Harringcfe762a2012-02-29 13:03:53 -080029from chromite.lib import cgroups
Brian Harringb6cf9142012-09-01 20:43:17 -070030from chromite.lib import commandline
Brian Harringb938c782012-02-29 15:14:38 -080031from chromite.lib import cros_build_lib
Ralph Nathan59900422015-03-24 10:41:17 -070032from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060033from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080034from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080035from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070036from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080037from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040038from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080039from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060040from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050041from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040042from chromite.utils import key_value_store
43
Brian Harringb938c782012-02-29 15:14:38 -080044
Mike Frysinger3b1f8e22020-03-20 04:00:13 -040045assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
46
47
Zdenek Behanaa52cea2012-05-30 01:31:11 +020048COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020049
Brian Harringb938c782012-02-29 15:14:38 -080050# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070051MAKE_CHROOT = [
52 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/make_chroot.sh')
53]
54ENTER_CHROOT = [
55 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
56]
Brian Harringb938c782012-02-29 15:14:38 -080057
Josh Triplett472a4182013-03-08 11:48:57 -080058# Proxy simulator configuration.
59PROXY_HOST_IP = '192.168.240.1'
60PROXY_PORT = 8080
61PROXY_GUEST_IP = '192.168.240.2'
62PROXY_NETMASK = 30
63PROXY_VETH_PREFIX = 'veth'
64PROXY_CONNECT_PORTS = (80, 443, 9418)
65PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
66PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
67PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070068 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080069PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
70
Josh Triplett9a495f62013-03-15 18:06:55 -070071# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080072NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080073
Josh Triplett472a4182013-03-08 11:48:57 -080074# Tools needed for --proxy-sim only.
75PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080076
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060077# Tools needed when use_image is true (the default).
78IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060079 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060080
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070081# As space is used inside the chroot, the empty space in chroot.img is
82# allocated. Deleting files inside the chroot doesn't automatically return the
83# used space to the OS. Over time, this tends to make the sparse chroot.img
84# less sparse even if the chroot contents don't currently need much space. We
85# can recover most of this unused space with fstrim, but that takes too much
86# time to run it every time. Instead, check the used space against the image
87# size after mounting the chroot and only call fstrim if it looks like we could
88# recover at least this many GiB.
89MAX_UNUSED_IMAGE_GBS = 20
90
Mike Frysingercc838832014-05-24 13:10:30 -040091
Brian Harring1790ac42012-09-23 08:53:33 -070092def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080093 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070094 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
95 return [
96 toolchain.GetSdkURL(
97 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
98 for compressor in COMPRESSION_PREFERENCE
99 ]
Brian Harring1790ac42012-09-23 08:53:33 -0700100
101
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500102def FetchRemoteTarballs(storage_dir, urls, desc):
Mike Frysinger34db8692013-11-11 14:54:08 -0500103 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200104
105 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500106 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200107 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700108 desc: A string describing what tarball we're downloading (for logging).
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200109
110 Returns:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500111 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700112
113 Raises:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500114 ValueError: None of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200115 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200116
Brian Harring1790ac42012-09-23 08:53:33 -0700117 # Note we track content length ourselves since certain versions of curl
118 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700119 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700120 logging.notice('Downloading %s tarball...', desc)
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500121 status_re = re.compile(br'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400122 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200123 for url in urls:
Mike Frysinger3dcacee2019-08-23 17:09:11 -0400124 parsed = urllib.parse.urlparse(url)
Brian Harring1790ac42012-09-23 08:53:33 -0700125 tarball_name = os.path.basename(parsed.path)
126 if parsed.scheme in ('', 'file'):
127 if os.path.exists(parsed.path):
128 return parsed.path
129 continue
130 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700131 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700132 result = retry_util.RunCurl(['-I', url],
133 print_cmd=False,
134 debug_level=logging.NOTICE,
135 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700136 successful = False
137 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700138 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700139 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700140 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700141 successful = True
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500142 elif header.lower().startswith(b'content-length:'):
143 content_length = int(header.split(b':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700144 if successful:
145 break
146 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200147 break
148 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700149 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200150
Brian Harringae0a5322012-09-15 01:46:51 -0700151 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700152 current_size = 0
153 if os.path.exists(tarball_dest):
154 current_size = os.path.getsize(tarball_dest)
155 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700156 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700157 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100158
Brian Harring1790ac42012-09-23 08:53:33 -0700159 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800160 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900161 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700162 print_cmd=False,
163 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800164
Brian Harring1790ac42012-09-23 08:53:33 -0700165 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700166 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
167 # because we might have partial overlap between known prefixes.
168 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
169 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
170 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700171 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700172 if (filename == tarball_name or
173 any([(filename.startswith(p) and
174 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
175 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700176 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700177 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700178 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200179
Brian Harringb938c782012-02-29 15:14:38 -0800180 return tarball_dest
181
182
Benjamin Gordon589873b2018-05-31 14:30:56 -0600183def CreateChroot(chroot_path, sdk_tarball, cache_dir, nousepkg=False):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600184 """Creates a new chroot from a given SDK.
185
186 Args:
187 chroot_path: Path where the new chroot will be created.
188 sdk_tarball: Path to a downloaded Gentoo Stage3 or Chromium OS SDK tarball.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600189 cache_dir: Path to a directory that will be used for caching portage files,
190 etc.
191 nousepkg: If True, pass --nousepkg to cros_setup_toolchains inside the
192 chroot.
193 """
Brian Harringb938c782012-02-29 15:14:38 -0800194
Manoj Guptab12f7302019-06-03 16:40:14 -0700195 cmd = MAKE_CHROOT + [
196 '--stage3_path', sdk_tarball, '--chroot', chroot_path, '--cache_dir',
197 cache_dir
198 ]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700199
Mike Frysinger2de7f042012-07-10 04:45:03 -0400200 if nousepkg:
201 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800202
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700203 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800204 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500205 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500206 except cros_build_lib.RunCommandError as e:
207 cros_build_lib.Die('Creating chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800208
209
210def DeleteChroot(chroot_path):
211 """Deletes an existing chroot"""
Manoj Guptab12f7302019-06-03 16:40:14 -0700212 cmd = MAKE_CHROOT + ['--chroot', chroot_path, '--delete']
Brian Harringb938c782012-02-29 15:14:38 -0800213 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700214 logging.notice('Deleting chroot.')
Mike Frysinger3e8de442020-02-14 16:46:28 -0500215 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500216 except cros_build_lib.RunCommandError as e:
217 cros_build_lib.Die('Deleting chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800218
219
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600220def CleanupChroot(chroot_path):
221 """Unmounts a chroot and cleans up any associated devices."""
Don Garrett36650112018-06-28 15:54:34 -0700222 cros_sdk_lib.CleanupChrootMount(chroot_path, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600223
224
Brian Harringae0a5322012-09-15 01:46:51 -0700225def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500226 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800227 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400228 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
229 # The os.ST_NOSUID constant wasn't added until python-3.2.
230 if st.f_flag & 0x2:
231 cros_build_lib.Die('chroot cannot be in a nosuid mount')
232
Brian Harringae0a5322012-09-15 01:46:51 -0700233 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800234 if chrome_root:
235 cmd.extend(['--chrome_root', chrome_root])
236 if chrome_root_mount:
237 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900238 if goma_dir:
239 cmd.extend(['--goma_dir', goma_dir])
240 if goma_client_json:
241 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800242 if working_dir is not None:
243 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700244
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400245 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800246 cmd.append('--')
247 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700248
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700249 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000250 # Set rlimit and vm.max_map_count to accommodate this.
251 file_limit = 262144
252 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
253 resource.setrlimit(resource.RLIMIT_NOFILE,
254 (max(soft, file_limit), max(hard, file_limit)))
255 max_map_count = int(open('/proc/sys/vm/max_map_count').read())
256 if max_map_count < file_limit:
257 logging.notice(
258 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
259 open('/proc/sys/vm/max_map_count', 'w').write(f'{file_limit}\n')
Alex Klein1ec3e6a2020-04-03 11:13:50 -0600260 ret = cros_build_lib.dbg_run(cmd, check=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700261 # If we were in interactive mode, ignore the exit code; it'll be whatever
262 # they last ran w/in the chroot and won't matter to us one way or another.
263 # Note this does allow chroot entrance to fail and be ignored during
264 # interactive; this is however a rare case and the user will immediately
265 # see it (nor will they be checking the exit code manually).
266 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700267 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800268
269
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600270def _ImageFileForChroot(chroot):
271 """Find the image file that should be associated with |chroot|.
272
273 This function does not check if the image exists; it simply returns the
274 filename that would be used.
275
276 Args:
277 chroot: Path to the chroot.
278
279 Returns:
280 Path to an image file that would be associated with chroot.
281 """
282 return chroot.rstrip('/') + '.img'
283
284
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600285def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
286 """Create a snapshot for the specified chroot VG/LV.
287
288 Args:
289 snapshot_name: The name of the new snapshot.
290 chroot_vg: The name of the VG containing the origin LV.
291 chroot_lv: The name of the origin LV.
292
293 Returns:
294 True if the snapshot was created, or False if a snapshot with the same
295 name already exists.
296
297 Raises:
298 SystemExit: The lvcreate command failed.
299 """
300 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700301 logging.error(
302 'Cannot create snapshot %s: A volume with that name already '
303 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600304 return False
305
Manoj Guptab12f7302019-06-03 16:40:14 -0700306 cmd = [
307 'lvcreate', '-s', '--name', snapshot_name,
308 '%s/%s' % (chroot_vg, chroot_lv)
309 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600310 try:
311 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
312 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500313 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600314 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500315 except cros_build_lib.RunCommandError as e:
316 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600317
318
319def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
320 """Delete the named snapshot from the specified chroot VG.
321
322 If the requested snapshot is not found, nothing happens. The main chroot LV
323 and internal thinpool LV cannot be deleted with this function.
324
325 Args:
326 snapshot_name: The name of the snapshot to delete.
327 chroot_vg: The name of the VG containing the origin LV.
328 chroot_lv: The name of the origin LV.
329
330 Raises:
331 SystemExit: The lvremove command failed.
332 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600333 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
334 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700335 logging.error(
336 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
337 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600338 return
339
340 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
341 return
342
343 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
344 try:
345 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500346 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500347 except cros_build_lib.RunCommandError as e:
348 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600349
350
351def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
352 """Restore the chroot to an existing snapshot.
353
354 This is done by renaming the original |chroot_lv| LV to a temporary name,
355 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
356 now unused LV. If an error occurs, attempts to rename the original snapshot
357 back to |chroot_lv| to leave the chroot unchanged.
358
359 The chroot must be unmounted before calling this function, and will be left
360 unmounted after this function returns.
361
362 Args:
363 snapshot_name: The name of the snapshot to restore. This snapshot will no
364 longer be accessible at its original name after this function finishes.
365 chroot_vg: The VG containing the chroot LV and snapshot LV.
366 chroot_lv: The name of the original chroot LV.
367
368 Returns:
369 True if the chroot was restored to the requested snapshot, or False if
370 the snapshot wasn't found or isn't valid.
371
372 Raises:
373 SystemExit: Any of the LVM commands failed.
374 """
375 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600376 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
377 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600378 snapshot_name not in valid_snapshots):
379 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
380 snapshot_name, ', '.join(valid_snapshots))
381 return False
382
383 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
384 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
385 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500386 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500387 except cros_build_lib.RunCommandError as e:
388 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600389
390 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
391 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500392 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500393 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600394 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
395 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500396 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500397 except cros_build_lib.RunCommandError as e:
398 cros_build_lib.Die(
399 'Failed to rename %s to chroot and failed to restore %s back to '
400 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
401 cros_build_lib.Die(
402 'Failed to rename %s to chroot! Original chroot LV has '
403 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600404
405 # Some versions of LVM set snapshots to be skipped at auto-activate time.
406 # Other versions don't have this flag at all. We run lvchange to try
407 # disabling auto-skip and activating the volume, but ignore errors. Versions
408 # that don't have the flag should be auto-activated.
409 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
410 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400411 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500412 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600413
414 # Activate the LV in case the lvchange above was needed. Activating an LV
415 # that is already active shouldn't do anything, so this is safe to run even if
416 # the -kn wasn't needed.
417 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500418 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600419
420 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
421 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500422 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500423 except cros_build_lib.RunCommandError as e:
424 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
425 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600426
427 return True
428
429
430def ListChrootSnapshots(chroot_vg, chroot_lv):
431 """Return all snapshots in |chroot_vg| regardless of origin volume.
432
433 Args:
434 chroot_vg: The name of the VG containing the chroot.
435 chroot_lv: The name of the chroot LV.
436
437 Returns:
438 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
439
440 Raises:
441 SystemExit: The lvs command failed.
442 """
443 if not chroot_vg or not chroot_lv:
444 return []
445
Manoj Guptab12f7302019-06-03 16:40:14 -0700446 cmd = [
447 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
448 '--separator', '\t', chroot_vg
449 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600450 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400451 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600452 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600453 except cros_build_lib.RunCommandError:
454 raise SystemExit('Running %r failed!' % cmd)
455
456 # Once the thin origin volume has been deleted, there's no way to tell a
457 # snapshot apart from any other volume. Since this VG is created and managed
458 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
459 # valid snapshots.
460 snapshots = []
461 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
462 for line in result.output.splitlines():
463 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700464 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600465 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600466 not snapshot_attrs.match(lv_attr)):
467 continue
468 snapshots.append(lv_name)
469 return snapshots
470
471
David James56e6c2c2012-10-24 23:54:41 -0700472def _SudoCommand():
473 """Get the 'sudo' command, along with all needed environment variables."""
474
David James5a73b4d2013-03-07 10:23:40 -0800475 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400476 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700477 cmd = ['sudo']
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400478 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700479 value = os.environ.get(key)
480 if value is not None:
481 cmd += ['%s=%s' % (key, value)]
482
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400483 # We keep PATH not for the chroot but for the re-exec & for programs we might
484 # run before we chroot into the SDK. The process that enters the SDK itself
485 # will take care of initializing PATH to the right value then. But we can't
486 # override the system's default PATH for root as that will hide /sbin.
487 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
488
David James56e6c2c2012-10-24 23:54:41 -0700489 # Pass in the path to the depot_tools so that users can access them from
490 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400491 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500492
David James56e6c2c2012-10-24 23:54:41 -0700493 return cmd
494
495
Josh Triplett472a4182013-03-08 11:48:57 -0800496def _ReportMissing(missing):
497 """Report missing utilities, then exit.
498
499 Args:
500 missing: List of missing utilities, as returned by
501 osutils.FindMissingBinaries. If non-empty, will not return.
502 """
503
504 if missing:
505 raise SystemExit(
506 'The tool(s) %s were not found.\n'
507 'Please install the appropriate package in your host.\n'
508 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700509 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800510
511
512def _ProxySimSetup(options):
513 """Set up proxy simulator, and return only in the child environment.
514
515 TODO: Ideally, this should support multiple concurrent invocations of
516 cros_sdk --proxy-sim; currently, such invocations will conflict with each
517 other due to the veth device names and IP addresses. Either this code would
518 need to generate fresh, unused names for all of these before forking, or it
519 would need to support multiple concurrent cros_sdk invocations sharing one
520 proxy and allowing it to exit when unused (without counting on any local
521 service-management infrastructure on the host).
522 """
523
524 may_need_mpm = False
525 apache_bin = osutils.Which('apache2')
526 if apache_bin is None:
527 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
528 if apache_bin is None:
529 _ReportMissing(('apache2',))
530 else:
531 may_need_mpm = True
532
533 # Module names and .so names included for ease of grepping.
534 apache_modules = [('proxy_module', 'mod_proxy.so'),
535 ('proxy_connect_module', 'mod_proxy_connect.so'),
536 ('proxy_http_module', 'mod_proxy_http.so'),
537 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
538
539 # Find the apache module directory, and make sure it has the modules we need.
540 module_dirs = {}
541 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400542 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800543 for f in glob.glob(os.path.join(g, so)):
544 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400545 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800546 if len(modules_found) == len(apache_modules):
547 break
548 else:
549 # Appease cros lint, which doesn't understand that this else block will not
550 # fall through to the subsequent code which relies on apache_module_path.
551 apache_module_path = None
552 raise SystemExit(
553 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500554 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800555
556 def check_add_module(name):
557 so = 'mod_%s.so' % name
558 if os.access(os.path.join(apache_module_path, so), os.F_OK):
559 mod = '%s_module' % name
560 apache_modules.append((mod, so))
561 return True
562 return False
563
564 check_add_module('authz_core')
565 if may_need_mpm:
566 for mpm in PROXY_APACHE_MPMS:
567 if check_add_module('mpm_%s' % mpm):
568 break
569
570 veth_host = '%s-host' % PROXY_VETH_PREFIX
571 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
572
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500573 # Set up locks to sync the net namespace setup. We need the child to create
574 # the net ns first, and then have the parent assign the guest end of the veth
575 # interface to the child's new network namespace & bring up the proxy. Only
576 # then can the child move forward and rely on the network being up.
577 ns_create_lock = locking.PipeLock()
578 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800579
580 pid = os.fork()
581 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500582 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800583 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500584
585 # Signal the parent the ns is ready to be configured.
586 ns_create_lock.Post()
587 del ns_create_lock
588
589 # Wait for the parent to finish setting up the ns/proxy.
590 ns_setup_lock.Wait()
591 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800592
593 # Set up child side of the network.
594 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500595 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700596 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500597 'dev', veth_guest),
598 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800599 )
600 try:
601 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500602 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500603 except cros_build_lib.RunCommandError as e:
604 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800605
606 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
607 for proto in ('http', 'https', 'ftp'):
608 os.environ[proto + '_proxy'] = proxy_url
609 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
610 os.environ.pop(v, None)
611 return
612
Josh Triplett472a4182013-03-08 11:48:57 -0800613 # Set up parent side of the network.
614 uid = int(os.environ.get('SUDO_UID', '0'))
615 gid = int(os.environ.get('SUDO_GID', '0'))
616 if uid == 0 or gid == 0:
617 for username in PROXY_APACHE_FALLBACK_USERS:
618 try:
619 pwnam = pwd.getpwnam(username)
620 uid, gid = pwnam.pw_uid, pwnam.pw_gid
621 break
622 except KeyError:
623 continue
624 if uid == 0 or gid == 0:
625 raise SystemExit('Could not find a non-root user to run Apache as')
626
627 chroot_parent, chroot_base = os.path.split(options.chroot)
628 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
629 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
630
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500631 # Wait for the child to create the net ns.
632 ns_create_lock.Wait()
633 del ns_create_lock
634
Josh Triplett472a4182013-03-08 11:48:57 -0800635 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500636 'User #%u' % uid,
637 'Group #%u' % gid,
638 'PidFile %s' % pid_file,
639 'ErrorLog %s' % log_file,
640 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
641 'ServerName %s' % PROXY_HOST_IP,
642 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400643 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800644 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500645 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
646 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800647 ]
648 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700649 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
650 veth_guest),
651 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
652 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500653 ('ip', 'link', 'set', veth_host, 'up'),
654 ([apache_bin, '-f', '/dev/null'] +
655 [arg for d in apache_directives for arg in ('-C', d)]),
656 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800657 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700658 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800659 try:
660 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500661 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500662 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800663 # Clean up existing interfaces, if any.
664 cmd_cleanup = ('ip', 'link', 'del', veth_host)
665 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400666 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800667 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700668 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500669 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500670
671 # Signal the child that the net ns/proxy is fully configured now.
672 ns_setup_lock.Post()
673 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800674
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400675 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800676
677
Mike Frysingera78a56e2012-11-20 06:02:30 -0500678def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700679 """Re-execute cros_sdk as root.
680
681 Also unshare the mount namespace so as to ensure that processes outside
682 the chroot can't mess with our mounts.
683 """
684 if os.geteuid() != 0:
Mike Frysinger1f113512020-07-29 03:36:57 -0400685 # Make sure to preserve the active Python executable in case the version
686 # we're running as is not the default one found via the (new) $PATH.
687 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500688 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500689 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500690 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400691 # We must set up the cgroups mounts before we enter our own namespace.
692 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800693 cgroups.Cgroup.InitSystem()
David James56e6c2c2012-10-24 23:54:41 -0700694
695
Mike Frysinger34db8692013-11-11 14:54:08 -0500696def _CreateParser(sdk_latest_version, bootstrap_latest_version):
697 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400698 usage = ('usage: %(prog)s [options] '
699 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700700 parser = commandline.ArgumentParser(
701 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700702
Mike Frysinger34db8692013-11-11 14:54:08 -0500703 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500704 default_chroot = os.path.join(constants.SOURCE_ROOT,
705 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400706 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700707 '--chroot',
708 dest='chroot',
709 default=default_chroot,
710 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700711 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700712 parser.add_argument(
713 '--nouse-image',
714 dest='use_image',
715 action='store_false',
716 default=True,
717 help='Do not mount the chroot on a loopback image; '
718 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600719 parser.add_argument(
720 '--use-image',
721 dest='use_image',
722 action='store_true',
723 default=True,
724 help='Mount the chroot on a loopback image '
725 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800726
Manoj Guptab12f7302019-06-03 16:40:14 -0700727 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600728 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700729 '--chrome_root',
730 type='path',
731 help='Mount this chrome root into the SDK chroot')
732 parser.add_argument(
733 '--chrome_root_mount',
734 type='path',
735 help='Mount chrome into this path inside SDK chroot')
736 parser.add_argument(
737 '--nousepkg',
738 action='store_true',
739 default=False,
740 help='Do not use binary packages when creating a chroot.')
741 parser.add_argument(
742 '-u',
743 '--url',
744 dest='sdk_url',
745 help='Use sdk tarball located at this url. Use file:// '
746 'for local files.')
747 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700748 '--sdk-version',
749 help=('Use this sdk version. For prebuilt, current is %r'
750 ', for bootstrapping it is %r.' % (sdk_latest_version,
751 bootstrap_latest_version)))
752 parser.add_argument(
753 '--goma_dir',
754 type='path',
755 help='Goma installed directory to mount into the chroot.')
756 parser.add_argument(
757 '--goma_client_json',
758 type='path',
759 help='Service account json file to use goma on bot. '
760 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800761
762 # Use type=str instead of type='path' to prevent the given path from being
763 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700764 parser.add_argument(
765 '--working-dir',
766 type=str,
767 help='Run the command in specific working directory in '
768 'chroot. If the given directory is a relative '
769 'path, this program will transfer the path to '
770 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800771
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400772 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500773
774 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400775 group = parser.add_argument_group('Commands')
776 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700777 '--enter',
778 action='store_true',
779 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500780 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400781 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700782 '--create',
783 action='store_true',
784 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500785 help='Create the chroot only if it does not already exist. '
786 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400787 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700788 '--bootstrap',
789 action='store_true',
790 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500791 help='Build everything from scratch, including the sdk. '
792 'Use this only if you need to validate a change '
793 'that affects SDK creation itself (toolchain and '
794 'build are typically the only folk who need this). '
795 'Note this will quite heavily slow down the build. '
796 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400797 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700798 '-r',
799 '--replace',
800 action='store_true',
801 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500802 help='Replace an existing SDK chroot. Basically an alias '
803 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400804 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700805 '--delete',
806 action='store_true',
807 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500808 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400809 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600810 '--force',
811 action='store_true',
812 default=False,
813 help='Force unmount/delete of the current SDK chroot even if '
814 'obtaining the write lock fails.')
815 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700816 '--unmount',
817 action='store_true',
818 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600819 help='Unmount and clean up devices associated with the '
820 'SDK chroot if it exists. This does not delete the '
821 'backing image file, so the same chroot can be later '
822 're-mounted for reuse. To fully delete the chroot, use '
823 '--delete. This is primarily useful for working on '
824 'cros_sdk or the chroot setup; you should not need it '
825 'under normal circumstances.')
826 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700827 '--download',
828 action='store_true',
829 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500830 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600831 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700832 '--snapshot-create',
833 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600834 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700835 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600836 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700837 '--snapshot-restore',
838 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600839 help='Restore the chroot to a previously created snapshot.')
840 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700841 '--snapshot-delete',
842 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600843 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700844 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600845 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700846 '--snapshot-list',
847 action='store_true',
848 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600849 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500850 commands = group
851
Mike Frysinger80dfce92014-04-21 10:58:53 -0400852 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400853 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700854 group.add_argument(
855 '--proxy-sim',
856 action='store_true',
857 default=False,
858 help='Simulate a restrictive network requiring an outbound'
859 ' proxy.')
860 group.add_argument(
861 '--no-ns-pid',
862 dest='ns_pid',
863 default=True,
864 action='store_false',
865 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400866
Mike Frysinger34db8692013-11-11 14:54:08 -0500867 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400868 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500869 'Internal Chromium OS Build Team Options',
870 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700871 group.add_argument(
872 '--buildbot-log-version',
873 default=False,
874 action='store_true',
875 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500876
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400877 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500878
879
880def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600881 # Turn on strict sudo checks.
882 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400883 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500884 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
885 ignore_missing=True)
886 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700887 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700888
889 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
890 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
891 bootstrap_latest_version = (
892 sdk_latest_version
893 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500894 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400895 options = parser.parse_args(argv)
896 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800897
898 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500899 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800900
Brian Harring1790ac42012-09-23 08:53:33 -0700901 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700902 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600903 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700904 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400905 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700906
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400907 # Merge the outside PATH setting if we re-execed ourselves.
908 if 'CHROMEOS_SUDO_PATH' in os.environ:
909 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
910 os.environ['PATH'])
911
Josh Triplett472a4182013-03-08 11:48:57 -0800912 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
913 if options.proxy_sim:
914 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600915 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800916
Benjamin Gordon040a1162017-06-29 13:44:47 -0600917 if (sdk_latest_version == '<unknown>' or
918 bootstrap_latest_version == '<unknown>'):
919 cros_build_lib.Die(
920 'No SDK version was found. '
921 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
922 'Please change to a directory inside your Chromium OS source tree\n'
923 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400924 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600925
Manoj Guptab12f7302019-06-03 16:40:14 -0700926 any_snapshot_operation = (
927 options.snapshot_create or options.snapshot_restore or
928 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600929 if any_snapshot_operation and not options.use_image:
930 cros_build_lib.Die('Snapshot operations are not compatible with '
931 '--nouse-image.')
932
Manoj Guptab12f7302019-06-03 16:40:14 -0700933 if (options.snapshot_delete and
934 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600935 parser.error('Cannot --snapshot_delete the same snapshot you are '
936 'restoring with --snapshot_restore.')
937
David James471532c2013-01-21 10:23:31 -0800938 _ReExecuteIfNeeded([sys.argv[0]] + argv)
939
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600940 lock_path = os.path.dirname(options.chroot)
941 lock_path = os.path.join(
942 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
943
Brian Harring218e13c2012-10-10 16:21:26 -0700944 # Expand out the aliases...
945 if options.replace:
946 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800947
Brian Harring218e13c2012-10-10 16:21:26 -0700948 if options.bootstrap:
949 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800950
Brian Harring218e13c2012-10-10 16:21:26 -0700951 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400952 # pylint: disable=protected-access
953 # This _group_actions access sucks, but upstream decided to not include an
954 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700955 options.enter |= not any(
956 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400957 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700958 options.enter |= bool(chroot_command)
959
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600960 if (options.delete and not options.create and
961 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400962 parser.error('Trying to enter or snapshot the chroot when --delete '
963 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700964
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600965 if (options.unmount and
966 (options.create or options.enter or any_snapshot_operation)):
967 parser.error('--unmount cannot be specified with other chroot actions.')
968
Yong Hong84ba9172018-02-07 01:37:42 +0800969 if options.working_dir is not None and not os.path.isabs(options.working_dir):
970 options.working_dir = path_util.ToChrootPath(options.working_dir)
971
Benjamin Gordon35194f12017-07-19 10:26:22 -0600972 # Discern if we need to create the chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600973 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600974 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600975 not options.unmount and not missing_image_tools and
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600976 os.path.exists(_ImageFileForChroot(options.chroot))):
977 # Try to re-mount an existing image in case the user has rebooted.
978 with cgroups.SimpleContainChildren('cros_sdk'):
979 with locking.FileLock(lock_path, 'chroot lock') as lock:
980 logging.debug('Checking if existing chroot image can be mounted.')
981 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -0600982 cros_sdk_lib.MountChroot(options.chroot, create=False)
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600983 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600984 if chroot_exists:
985 logging.notice('Mounted existing image %s on chroot',
986 _ImageFileForChroot(options.chroot))
Brian Harring218e13c2012-10-10 16:21:26 -0700987
988 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600989 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700990 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800991
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600992 # Make sure we will download if we plan to create.
993 options.download |= options.create
994
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600995 # Anything that needs to manipulate the main chroot mount or communicate with
996 # LVM needs to be done here before we enter the new namespaces.
997
998 # If deleting, do it regardless of the use_image flag so that a
999 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001000 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
1001 # favor of this block.
1002 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001003 if options.delete:
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001004 with cgroups.SimpleContainChildren('cros_sdk'):
Michael Mortensene979a4d2020-06-24 13:09:42 -06001005 # Set a timeout of 300 seconds when getting the lock.
1006 with locking.FileLock(lock_path, 'chroot lock',
1007 blocking_timeout=300) as lock:
1008 try:
1009 lock.write_lock()
1010 except timeout_util.TimeoutError as e:
1011 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
1012 if not options.force:
Michael Mortensenc3a81f92020-07-06 14:28:57 -06001013 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Michael Mortensen1a176922020-07-14 20:53:35 -06001014 else:
1015 logging.warning(
1016 'cros_sdk was invoked with force option, continuing.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001017 if missing_image_tools:
1018 logging.notice('Unmounting chroot.')
1019 osutils.UmountTree(options.chroot)
1020 else:
1021 logging.notice('Deleting chroot.')
Don Garrett36650112018-06-28 15:54:34 -07001022 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001023 chroot_deleted = True
1024
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001025 # If cleanup was requested, we have to do it while we're still in the original
1026 # namespace. Since cleaning up the mount will interfere with any other
1027 # commands, we exit here. The check above should have made sure that no other
1028 # action was requested, anyway.
1029 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001030 # Set a timeout of 300 seconds when getting the lock.
1031 with locking.FileLock(lock_path, 'chroot lock',
1032 blocking_timeout=300) as lock:
1033 try:
1034 lock.write_lock()
1035 except timeout_util.TimeoutError as e:
1036 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -06001037 logging.warning(
1038 'Continuing with CleanupChroot(%s), which will umount the tree.',
1039 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001040 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1041 # even if we don't get the lock because it will attempt to unmount the
1042 # tree and will print diagnostic information from 'fuser', 'lsof', and
1043 # 'ps'.
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001044 CleanupChroot(options.chroot)
1045 sys.exit(0)
1046
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001047 # Make sure the main chroot mount is visible. Contents will be filled in
1048 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001049 if options.create and options.use_image:
1050 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001051 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001052Please make sure the lvm2 and thin-provisioning-tools packages
1053are installed on your host.
1054Example(ubuntu):
1055 sudo apt-get install lvm2 thin-provisioning-tools
1056
1057If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001058snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001059
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001060 logging.debug('Making sure chroot image is mounted.')
1061 with cgroups.SimpleContainChildren('cros_sdk'):
1062 with locking.FileLock(lock_path, 'chroot lock') as lock:
1063 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -06001064 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001065 cros_build_lib.Die('Unable to mount %s on chroot',
1066 _ImageFileForChroot(options.chroot))
1067 logging.notice('Mounted %s on chroot',
1068 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001069
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001070 # Snapshot operations will always need the VG/LV, but other actions won't.
1071 if any_snapshot_operation:
1072 with cgroups.SimpleContainChildren('cros_sdk'):
1073 with locking.FileLock(lock_path, 'chroot lock') as lock:
Benjamin Gordon74645232018-05-04 17:40:42 -06001074 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001075 options.chroot)
1076 if not chroot_vg or not chroot_lv:
1077 cros_build_lib.Die('Unable to find VG/LV for chroot %s',
1078 options.chroot)
1079
1080 # Delete snapshot before creating a new one. This allows the user to
1081 # throw out old state, create a new snapshot, and enter the chroot in a
1082 # single call to cros_sdk. Since restore involves deleting, also do it
1083 # before creating.
1084 if options.snapshot_restore:
1085 lock.write_lock()
1086 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1087 if options.snapshot_restore not in valid_snapshots:
Manoj Guptab12f7302019-06-03 16:40:14 -07001088 cros_build_lib.Die(
1089 '%s is not a valid snapshot to restore to. '
1090 'Valid snapshots: %s', options.snapshot_restore,
1091 ', '.join(valid_snapshots))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001092 osutils.UmountTree(options.chroot)
1093 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1094 chroot_lv):
1095 cros_build_lib.Die('Unable to restore chroot to snapshot.')
Benjamin Gordon74645232018-05-04 17:40:42 -06001096 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001097 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
1098
1099 # Use a read lock for snapshot delete and create even though they modify
1100 # the filesystem, because they don't modify the mounted chroot itself.
1101 # The underlying LVM commands take their own locks, so conflicting
1102 # concurrent operations here may crash cros_sdk, but won't corrupt the
1103 # chroot image. This tradeoff seems worth it to allow snapshot
1104 # operations on chroots that have a process inside.
1105 if options.snapshot_delete:
1106 lock.read_lock()
1107 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
1108
1109 if options.snapshot_create:
1110 lock.read_lock()
1111 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1112 chroot_lv):
1113 cros_build_lib.Die('Unable to create snapshot.')
1114
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001115 img_path = _ImageFileForChroot(options.chroot)
1116 if (options.use_image and os.path.exists(options.chroot) and
1117 os.path.exists(img_path)):
1118 img_stat = os.stat(img_path)
1119 img_used_bytes = img_stat.st_blocks * 512
1120
1121 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001122 mount_used_bytes = mount_stat.f_frsize * (
1123 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001124
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001125 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001126 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1127 logging.notice('%s is using %s GiB more than needed. Running '
1128 'fstrim.', img_path, extra_gbs)
1129 cmd = ['fstrim', options.chroot]
1130 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -05001131 cros_build_lib.dbg_run(cmd)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001132 except cros_build_lib.RunCommandError as e:
Manoj Guptab12f7302019-06-03 16:40:14 -07001133 logging.warning(
1134 'Running fstrim failed. Consider running fstrim on '
Mike Frysinger75634e32020-02-22 23:48:12 -05001135 'your chroot manually.\n%s', e)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001136
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001137 # Enter a new set of namespaces. Everything after here cannot directly affect
1138 # the hosts's mounts or alter LVM volumes.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001139 namespaces.SimpleUnshare()
1140 if options.ns_pid:
1141 first_pid = namespaces.CreatePidNs()
1142 else:
1143 first_pid = None
1144
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001145 if options.snapshot_list:
1146 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1147 print(snap)
1148 sys.exit(0)
1149
Brian Harringb938c782012-02-29 15:14:38 -08001150 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001151 sdk_version = (
1152 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001153 else:
1154 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001155 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001156 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001157
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001158 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001159 if options.download:
1160 if options.sdk_url:
1161 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001162 else:
1163 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001164
Mike Frysinger80dfce92014-04-21 10:58:53 -04001165 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -07001166 with locking.FileLock(lock_path, 'chroot lock') as lock:
Josh Triplett472a4182013-03-08 11:48:57 -08001167 if options.proxy_sim:
1168 _ProxySimSetup(options)
1169
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001170 if (options.delete and not chroot_deleted and
1171 (os.path.exists(options.chroot) or
1172 os.path.exists(_ImageFileForChroot(options.chroot)))):
David James56e6c2c2012-10-24 23:54:41 -07001173 lock.write_lock()
1174 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001175
David James56e6c2c2012-10-24 23:54:41 -07001176 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1177 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -07001178 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001179
David James56e6c2c2012-10-24 23:54:41 -07001180 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -05001181 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -07001182 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001183 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001184 continue
1185 lock.write_lock(
Mike Frysinger80de5012019-08-01 14:10:53 -04001186 'Upgrade to %r needed but chroot is locked; please exit '
1187 'all instances so this upgrade can finish.' % src)
David James56e6c2c2012-10-24 23:54:41 -07001188 if not os.path.exists(src):
1189 # Note that while waiting for the write lock, src may've vanished;
1190 # it's a rare race during the upgrade process that's a byproduct
1191 # of us avoiding taking a write lock to do the src check. If we
1192 # took a write lock for that check, it would effectively limit
1193 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001194 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001195 elif not os.path.exists(target):
1196 # Upgrade occurred, but a reversion, or something whacky
1197 # occurred writing to the old location. Wipe and continue.
1198 os.rename(src, target)
1199 else:
1200 # Upgrade occurred once already, but either a reversion or
1201 # some before/after separate cros_sdk usage is at play.
1202 # Wipe and continue.
1203 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001204
David James56e6c2c2012-10-24 23:54:41 -07001205 if options.download:
1206 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -07001207 sdk_tarball = FetchRemoteTarballs(
1208 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001209
David James56e6c2c2012-10-24 23:54:41 -07001210 if options.create:
1211 lock.write_lock()
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001212 # Recheck if the chroot is set up here before creating to make sure we
1213 # account for whatever the various delete/unmount/remount steps above
1214 # have done.
1215 if cros_sdk_lib.IsChrootReady(options.chroot):
1216 logging.debug('Chroot already exists. Skipping creation.')
1217 else:
Manoj Guptab12f7302019-06-03 16:40:14 -07001218 CreateChroot(
1219 options.chroot,
1220 sdk_tarball,
1221 options.cache_dir,
1222 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001223
David James56e6c2c2012-10-24 23:54:41 -07001224 if options.enter:
1225 lock.read_lock()
1226 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -05001227 options.chrome_root_mount, options.goma_dir,
1228 options.goma_client_json, options.working_dir,
1229 chroot_command)