blob: d31939593022927a4144f89dd30bc5b4ed2e9283 [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
Sergey Frolov1cb46ec2020-12-09 21:46:16 -070024import subprocess
David James56e6c2c2012-10-24 23:54:41 -070025import sys
Mike Frysinger3dcacee2019-08-23 17:09:11 -040026
27from six.moves import urllib
Brian Harringb938c782012-02-29 15:14:38 -080028
Aviv Keshetb7519e12016-10-04 00:50:00 -070029from chromite.lib import constants
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'))
Alex Klein875b30e2021-01-05 14:56:33 -0700229 if st.f_flag & os.ST_NOSUID:
Mike Frysingere5456972013-06-13 00:07:23 -0400230 cros_build_lib.Die('chroot cannot be in a nosuid mount')
231
Brian Harringae0a5322012-09-15 01:46:51 -0700232 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800233 if chrome_root:
234 cmd.extend(['--chrome_root', chrome_root])
235 if chrome_root_mount:
236 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900237 if goma_dir:
238 cmd.extend(['--goma_dir', goma_dir])
239 if goma_client_json:
240 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800241 if working_dir is not None:
242 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700243
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400244 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800245 cmd.append('--')
246 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700247
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700248 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000249 # Set rlimit and vm.max_map_count to accommodate this.
250 file_limit = 262144
251 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
252 resource.setrlimit(resource.RLIMIT_NOFILE,
253 (max(soft, file_limit), max(hard, file_limit)))
254 max_map_count = int(open('/proc/sys/vm/max_map_count').read())
255 if max_map_count < file_limit:
256 logging.notice(
257 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
258 open('/proc/sys/vm/max_map_count', 'w').write(f'{file_limit}\n')
Alex Klein1ec3e6a2020-04-03 11:13:50 -0600259 ret = cros_build_lib.dbg_run(cmd, check=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700260 # If we were in interactive mode, ignore the exit code; it'll be whatever
261 # they last ran w/in the chroot and won't matter to us one way or another.
262 # Note this does allow chroot entrance to fail and be ignored during
263 # interactive; this is however a rare case and the user will immediately
264 # see it (nor will they be checking the exit code manually).
265 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700266 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800267
268
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600269def _ImageFileForChroot(chroot):
270 """Find the image file that should be associated with |chroot|.
271
272 This function does not check if the image exists; it simply returns the
273 filename that would be used.
274
275 Args:
276 chroot: Path to the chroot.
277
278 Returns:
279 Path to an image file that would be associated with chroot.
280 """
281 return chroot.rstrip('/') + '.img'
282
283
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600284def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
285 """Create a snapshot for the specified chroot VG/LV.
286
287 Args:
288 snapshot_name: The name of the new snapshot.
289 chroot_vg: The name of the VG containing the origin LV.
290 chroot_lv: The name of the origin LV.
291
292 Returns:
293 True if the snapshot was created, or False if a snapshot with the same
294 name already exists.
295
296 Raises:
297 SystemExit: The lvcreate command failed.
298 """
299 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700300 logging.error(
301 'Cannot create snapshot %s: A volume with that name already '
302 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600303 return False
304
Manoj Guptab12f7302019-06-03 16:40:14 -0700305 cmd = [
306 'lvcreate', '-s', '--name', snapshot_name,
307 '%s/%s' % (chroot_vg, chroot_lv)
308 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600309 try:
310 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
311 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500312 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600313 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500314 except cros_build_lib.RunCommandError as e:
315 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600316
317
318def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
319 """Delete the named snapshot from the specified chroot VG.
320
321 If the requested snapshot is not found, nothing happens. The main chroot LV
322 and internal thinpool LV cannot be deleted with this function.
323
324 Args:
325 snapshot_name: The name of the snapshot to delete.
326 chroot_vg: The name of the VG containing the origin LV.
327 chroot_lv: The name of the origin LV.
328
329 Raises:
330 SystemExit: The lvremove command failed.
331 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600332 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
333 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700334 logging.error(
335 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
336 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600337 return
338
339 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
340 return
341
342 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
343 try:
344 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500345 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500346 except cros_build_lib.RunCommandError as e:
347 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600348
349
350def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
351 """Restore the chroot to an existing snapshot.
352
353 This is done by renaming the original |chroot_lv| LV to a temporary name,
354 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
355 now unused LV. If an error occurs, attempts to rename the original snapshot
356 back to |chroot_lv| to leave the chroot unchanged.
357
358 The chroot must be unmounted before calling this function, and will be left
359 unmounted after this function returns.
360
361 Args:
362 snapshot_name: The name of the snapshot to restore. This snapshot will no
363 longer be accessible at its original name after this function finishes.
364 chroot_vg: The VG containing the chroot LV and snapshot LV.
365 chroot_lv: The name of the original chroot LV.
366
367 Returns:
368 True if the chroot was restored to the requested snapshot, or False if
369 the snapshot wasn't found or isn't valid.
370
371 Raises:
372 SystemExit: Any of the LVM commands failed.
373 """
374 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600375 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
376 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600377 snapshot_name not in valid_snapshots):
378 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
379 snapshot_name, ', '.join(valid_snapshots))
380 return False
381
382 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
383 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
384 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500385 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500386 except cros_build_lib.RunCommandError as e:
387 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600388
389 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
390 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500391 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500392 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600393 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
394 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500395 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500396 except cros_build_lib.RunCommandError as e:
397 cros_build_lib.Die(
398 'Failed to rename %s to chroot and failed to restore %s back to '
399 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
400 cros_build_lib.Die(
401 'Failed to rename %s to chroot! Original chroot LV has '
402 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600403
404 # Some versions of LVM set snapshots to be skipped at auto-activate time.
405 # Other versions don't have this flag at all. We run lvchange to try
406 # disabling auto-skip and activating the volume, but ignore errors. Versions
407 # that don't have the flag should be auto-activated.
408 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
409 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400410 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500411 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600412
413 # Activate the LV in case the lvchange above was needed. Activating an LV
414 # that is already active shouldn't do anything, so this is safe to run even if
415 # the -kn wasn't needed.
416 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500417 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600418
419 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
420 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500421 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500422 except cros_build_lib.RunCommandError as e:
423 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
424 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600425
426 return True
427
428
429def ListChrootSnapshots(chroot_vg, chroot_lv):
430 """Return all snapshots in |chroot_vg| regardless of origin volume.
431
432 Args:
433 chroot_vg: The name of the VG containing the chroot.
434 chroot_lv: The name of the chroot LV.
435
436 Returns:
437 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
438
439 Raises:
440 SystemExit: The lvs command failed.
441 """
442 if not chroot_vg or not chroot_lv:
443 return []
444
Manoj Guptab12f7302019-06-03 16:40:14 -0700445 cmd = [
446 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
447 '--separator', '\t', chroot_vg
448 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600449 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400450 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600451 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600452 except cros_build_lib.RunCommandError:
453 raise SystemExit('Running %r failed!' % cmd)
454
455 # Once the thin origin volume has been deleted, there's no way to tell a
456 # snapshot apart from any other volume. Since this VG is created and managed
457 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
458 # valid snapshots.
459 snapshots = []
460 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
461 for line in result.output.splitlines():
462 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700463 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600464 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600465 not snapshot_attrs.match(lv_attr)):
466 continue
467 snapshots.append(lv_name)
468 return snapshots
469
470
David James56e6c2c2012-10-24 23:54:41 -0700471def _SudoCommand():
472 """Get the 'sudo' command, along with all needed environment variables."""
473
David James5a73b4d2013-03-07 10:23:40 -0800474 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400475 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700476 cmd = ['sudo']
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400477 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700478 value = os.environ.get(key)
479 if value is not None:
480 cmd += ['%s=%s' % (key, value)]
481
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400482 # We keep PATH not for the chroot but for the re-exec & for programs we might
483 # run before we chroot into the SDK. The process that enters the SDK itself
484 # will take care of initializing PATH to the right value then. But we can't
485 # override the system's default PATH for root as that will hide /sbin.
486 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
487
David James56e6c2c2012-10-24 23:54:41 -0700488 # Pass in the path to the depot_tools so that users can access them from
489 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400490 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500491
David James56e6c2c2012-10-24 23:54:41 -0700492 return cmd
493
494
Josh Triplett472a4182013-03-08 11:48:57 -0800495def _ReportMissing(missing):
496 """Report missing utilities, then exit.
497
498 Args:
499 missing: List of missing utilities, as returned by
500 osutils.FindMissingBinaries. If non-empty, will not return.
501 """
502
503 if missing:
504 raise SystemExit(
505 'The tool(s) %s were not found.\n'
506 'Please install the appropriate package in your host.\n'
507 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700508 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800509
510
511def _ProxySimSetup(options):
512 """Set up proxy simulator, and return only in the child environment.
513
514 TODO: Ideally, this should support multiple concurrent invocations of
515 cros_sdk --proxy-sim; currently, such invocations will conflict with each
516 other due to the veth device names and IP addresses. Either this code would
517 need to generate fresh, unused names for all of these before forking, or it
518 would need to support multiple concurrent cros_sdk invocations sharing one
519 proxy and allowing it to exit when unused (without counting on any local
520 service-management infrastructure on the host).
521 """
522
523 may_need_mpm = False
524 apache_bin = osutils.Which('apache2')
525 if apache_bin is None:
526 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
527 if apache_bin is None:
528 _ReportMissing(('apache2',))
529 else:
530 may_need_mpm = True
531
532 # Module names and .so names included for ease of grepping.
533 apache_modules = [('proxy_module', 'mod_proxy.so'),
534 ('proxy_connect_module', 'mod_proxy_connect.so'),
535 ('proxy_http_module', 'mod_proxy_http.so'),
536 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
537
538 # Find the apache module directory, and make sure it has the modules we need.
539 module_dirs = {}
540 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400541 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800542 for f in glob.glob(os.path.join(g, so)):
543 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400544 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800545 if len(modules_found) == len(apache_modules):
546 break
547 else:
548 # Appease cros lint, which doesn't understand that this else block will not
549 # fall through to the subsequent code which relies on apache_module_path.
550 apache_module_path = None
551 raise SystemExit(
552 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500553 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800554
555 def check_add_module(name):
556 so = 'mod_%s.so' % name
557 if os.access(os.path.join(apache_module_path, so), os.F_OK):
558 mod = '%s_module' % name
559 apache_modules.append((mod, so))
560 return True
561 return False
562
563 check_add_module('authz_core')
564 if may_need_mpm:
565 for mpm in PROXY_APACHE_MPMS:
566 if check_add_module('mpm_%s' % mpm):
567 break
568
569 veth_host = '%s-host' % PROXY_VETH_PREFIX
570 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
571
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500572 # Set up locks to sync the net namespace setup. We need the child to create
573 # the net ns first, and then have the parent assign the guest end of the veth
574 # interface to the child's new network namespace & bring up the proxy. Only
575 # then can the child move forward and rely on the network being up.
576 ns_create_lock = locking.PipeLock()
577 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800578
579 pid = os.fork()
580 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500581 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800582 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500583
584 # Signal the parent the ns is ready to be configured.
585 ns_create_lock.Post()
586 del ns_create_lock
587
588 # Wait for the parent to finish setting up the ns/proxy.
589 ns_setup_lock.Wait()
590 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800591
592 # Set up child side of the network.
593 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500594 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700595 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500596 'dev', veth_guest),
597 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800598 )
599 try:
600 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500601 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500602 except cros_build_lib.RunCommandError as e:
603 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800604
605 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
606 for proto in ('http', 'https', 'ftp'):
607 os.environ[proto + '_proxy'] = proxy_url
608 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
609 os.environ.pop(v, None)
610 return
611
Josh Triplett472a4182013-03-08 11:48:57 -0800612 # Set up parent side of the network.
613 uid = int(os.environ.get('SUDO_UID', '0'))
614 gid = int(os.environ.get('SUDO_GID', '0'))
615 if uid == 0 or gid == 0:
616 for username in PROXY_APACHE_FALLBACK_USERS:
617 try:
618 pwnam = pwd.getpwnam(username)
619 uid, gid = pwnam.pw_uid, pwnam.pw_gid
620 break
621 except KeyError:
622 continue
623 if uid == 0 or gid == 0:
624 raise SystemExit('Could not find a non-root user to run Apache as')
625
626 chroot_parent, chroot_base = os.path.split(options.chroot)
627 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
628 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
629
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500630 # Wait for the child to create the net ns.
631 ns_create_lock.Wait()
632 del ns_create_lock
633
Josh Triplett472a4182013-03-08 11:48:57 -0800634 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500635 'User #%u' % uid,
636 'Group #%u' % gid,
637 'PidFile %s' % pid_file,
638 'ErrorLog %s' % log_file,
639 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
640 'ServerName %s' % PROXY_HOST_IP,
641 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400642 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800643 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500644 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
645 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800646 ]
647 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700648 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
649 veth_guest),
650 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
651 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500652 ('ip', 'link', 'set', veth_host, 'up'),
653 ([apache_bin, '-f', '/dev/null'] +
654 [arg for d in apache_directives for arg in ('-C', d)]),
655 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800656 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700657 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800658 try:
659 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500660 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500661 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800662 # Clean up existing interfaces, if any.
663 cmd_cleanup = ('ip', 'link', 'del', veth_host)
664 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400665 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800666 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700667 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500668 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500669
670 # Signal the child that the net ns/proxy is fully configured now.
671 ns_setup_lock.Post()
672 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800673
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400674 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800675
676
Mike Frysingera78a56e2012-11-20 06:02:30 -0500677def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700678 """Re-execute cros_sdk as root.
679
680 Also unshare the mount namespace so as to ensure that processes outside
681 the chroot can't mess with our mounts.
682 """
683 if os.geteuid() != 0:
Mike Frysinger1f113512020-07-29 03:36:57 -0400684 # Make sure to preserve the active Python executable in case the version
685 # we're running as is not the default one found via the (new) $PATH.
686 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500687 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500688 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700689
690
Mike Frysinger34db8692013-11-11 14:54:08 -0500691def _CreateParser(sdk_latest_version, bootstrap_latest_version):
692 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400693 usage = ('usage: %(prog)s [options] '
694 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700695 parser = commandline.ArgumentParser(
696 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700697
Mike Frysinger34db8692013-11-11 14:54:08 -0500698 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500699 default_chroot = os.path.join(constants.SOURCE_ROOT,
700 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400701 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700702 '--chroot',
703 dest='chroot',
704 default=default_chroot,
705 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700706 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700707 parser.add_argument(
708 '--nouse-image',
709 dest='use_image',
710 action='store_false',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700711 default=False,
Manoj Guptab12f7302019-06-03 16:40:14 -0700712 help='Do not mount the chroot on a loopback image; '
713 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600714 parser.add_argument(
715 '--use-image',
716 dest='use_image',
717 action='store_true',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700718 default=False,
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600719 help='Mount the chroot on a loopback image '
720 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800721
Manoj Guptab12f7302019-06-03 16:40:14 -0700722 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600723 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700724 '--chrome_root',
725 type='path',
726 help='Mount this chrome root into the SDK chroot')
727 parser.add_argument(
728 '--chrome_root_mount',
729 type='path',
730 help='Mount chrome into this path inside SDK chroot')
731 parser.add_argument(
732 '--nousepkg',
733 action='store_true',
734 default=False,
735 help='Do not use binary packages when creating a chroot.')
736 parser.add_argument(
737 '-u',
738 '--url',
739 dest='sdk_url',
740 help='Use sdk tarball located at this url. Use file:// '
741 'for local files.')
742 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700743 '--sdk-version',
744 help=('Use this sdk version. For prebuilt, current is %r'
745 ', for bootstrapping it is %r.' % (sdk_latest_version,
746 bootstrap_latest_version)))
747 parser.add_argument(
748 '--goma_dir',
749 type='path',
750 help='Goma installed directory to mount into the chroot.')
751 parser.add_argument(
752 '--goma_client_json',
753 type='path',
754 help='Service account json file to use goma on bot. '
755 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800756
757 # Use type=str instead of type='path' to prevent the given path from being
758 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700759 parser.add_argument(
760 '--working-dir',
761 type=str,
762 help='Run the command in specific working directory in '
763 'chroot. If the given directory is a relative '
764 'path, this program will transfer the path to '
765 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800766
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400767 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500768
769 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400770 group = parser.add_argument_group('Commands')
771 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700772 '--enter',
773 action='store_true',
774 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500775 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400776 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700777 '--create',
778 action='store_true',
779 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500780 help='Create the chroot only if it does not already exist. '
781 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400782 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700783 '--bootstrap',
784 action='store_true',
785 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500786 help='Build everything from scratch, including the sdk. '
787 'Use this only if you need to validate a change '
788 'that affects SDK creation itself (toolchain and '
789 'build are typically the only folk who need this). '
790 'Note this will quite heavily slow down the build. '
791 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400792 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700793 '-r',
794 '--replace',
795 action='store_true',
796 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500797 help='Replace an existing SDK chroot. Basically an alias '
798 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400799 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700800 '--delete',
801 action='store_true',
802 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500803 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400804 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600805 '--force',
806 action='store_true',
807 default=False,
808 help='Force unmount/delete of the current SDK chroot even if '
809 'obtaining the write lock fails.')
810 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700811 '--unmount',
812 action='store_true',
813 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600814 help='Unmount and clean up devices associated with the '
815 'SDK chroot if it exists. This does not delete the '
816 'backing image file, so the same chroot can be later '
817 're-mounted for reuse. To fully delete the chroot, use '
818 '--delete. This is primarily useful for working on '
819 'cros_sdk or the chroot setup; you should not need it '
820 'under normal circumstances.')
821 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700822 '--download',
823 action='store_true',
824 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500825 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600826 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700827 '--snapshot-create',
828 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600829 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700830 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600831 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700832 '--snapshot-restore',
833 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600834 help='Restore the chroot to a previously created snapshot.')
835 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700836 '--snapshot-delete',
837 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600838 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700839 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600840 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700841 '--snapshot-list',
842 action='store_true',
843 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600844 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500845 commands = group
846
Mike Frysinger80dfce92014-04-21 10:58:53 -0400847 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400848 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700849 group.add_argument(
850 '--proxy-sim',
851 action='store_true',
852 default=False,
853 help='Simulate a restrictive network requiring an outbound'
854 ' proxy.')
855 group.add_argument(
856 '--no-ns-pid',
857 dest='ns_pid',
858 default=True,
859 action='store_false',
860 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400861
Mike Frysinger34db8692013-11-11 14:54:08 -0500862 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400863 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500864 'Internal Chromium OS Build Team Options',
865 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700866 group.add_argument(
867 '--buildbot-log-version',
868 default=False,
869 action='store_true',
870 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500871
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400872 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500873
874
875def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600876 # Turn on strict sudo checks.
877 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400878 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500879 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
880 ignore_missing=True)
881 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700882 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700883
884 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
885 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
886 bootstrap_latest_version = (
887 sdk_latest_version
888 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500889 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400890 options = parser.parse_args(argv)
891 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800892
893 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500894 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800895
Brian Harring1790ac42012-09-23 08:53:33 -0700896 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700897 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600898 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700899 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400900 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700901
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400902 # Merge the outside PATH setting if we re-execed ourselves.
903 if 'CHROMEOS_SUDO_PATH' in os.environ:
904 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
905 os.environ['PATH'])
906
Josh Triplett472a4182013-03-08 11:48:57 -0800907 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
908 if options.proxy_sim:
909 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600910 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800911
Benjamin Gordon040a1162017-06-29 13:44:47 -0600912 if (sdk_latest_version == '<unknown>' or
913 bootstrap_latest_version == '<unknown>'):
914 cros_build_lib.Die(
915 'No SDK version was found. '
916 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
917 'Please change to a directory inside your Chromium OS source tree\n'
918 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400919 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600920
Manoj Guptab12f7302019-06-03 16:40:14 -0700921 any_snapshot_operation = (
922 options.snapshot_create or options.snapshot_restore or
923 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600924
Manoj Guptab12f7302019-06-03 16:40:14 -0700925 if (options.snapshot_delete and
926 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600927 parser.error('Cannot --snapshot_delete the same snapshot you are '
928 'restoring with --snapshot_restore.')
929
David James471532c2013-01-21 10:23:31 -0800930 _ReExecuteIfNeeded([sys.argv[0]] + argv)
931
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600932 lock_path = os.path.dirname(options.chroot)
933 lock_path = os.path.join(
934 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
935
Brian Harring218e13c2012-10-10 16:21:26 -0700936 # Expand out the aliases...
937 if options.replace:
938 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800939
Brian Harring218e13c2012-10-10 16:21:26 -0700940 if options.bootstrap:
941 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800942
Brian Harring218e13c2012-10-10 16:21:26 -0700943 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400944 # pylint: disable=protected-access
945 # This _group_actions access sucks, but upstream decided to not include an
946 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700947 options.enter |= not any(
948 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400949 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700950 options.enter |= bool(chroot_command)
951
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600952 if (options.delete and not options.create and
953 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400954 parser.error('Trying to enter or snapshot the chroot when --delete '
955 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700956
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600957 if (options.unmount and
958 (options.create or options.enter or any_snapshot_operation)):
959 parser.error('--unmount cannot be specified with other chroot actions.')
960
Yong Hong84ba9172018-02-07 01:37:42 +0800961 if options.working_dir is not None and not os.path.isabs(options.working_dir):
962 options.working_dir = path_util.ToChrootPath(options.working_dir)
963
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700964 # If there is an existing chroot image and we're not removing it then force
965 # use_image on. This ensures that people don't have to remember to pass
966 # --use-image after a reboot to avoid losing access to their existing chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600967 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700968 img_path = _ImageFileForChroot(options.chroot)
Benjamin Gordon832a4412020-12-08 10:39:16 -0700969 if (not options.use_image and not options.delete and not options.unmount
970 and os.path.exists(img_path)):
971 if chroot_exists:
972 # If the chroot is already populated, make sure it has something
973 # mounted on it before we assume it came from an image.
974 cmd = ['mountpoint', '-q', options.chroot]
975 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
976 options.use_image = True
977
978 else:
979 logging.notice('Existing chroot image %s found. Forcing --use-image on.',
980 img_path)
981 options.use_image = True
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700982
Benjamin Gordon9bce7032020-11-19 09:58:44 -0700983 if any_snapshot_operation and not options.use_image:
984 if os.path.exists(img_path):
985 options.use_image = True
986 else:
987 cros_build_lib.Die('Snapshot operations are not compatible with '
988 '--nouse-image.')
989
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700990 # Discern if we need to create the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600991 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600992 not options.unmount and not missing_image_tools and
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700993 os.path.exists(img_path)):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600994 # Try to re-mount an existing image in case the user has rebooted.
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500995 with locking.FileLock(lock_path, 'chroot lock') as lock:
996 logging.debug('Checking if existing chroot image can be mounted.')
997 lock.write_lock()
998 cros_sdk_lib.MountChroot(options.chroot, create=False)
999 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1000 if chroot_exists:
1001 logging.notice('Mounted existing image %s on chroot', img_path)
Brian Harring218e13c2012-10-10 16:21:26 -07001002
1003 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001004 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -07001005 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -08001006
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001007 # Make sure we will download if we plan to create.
1008 options.download |= options.create
1009
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001010 # Anything that needs to manipulate the main chroot mount or communicate with
1011 # LVM needs to be done here before we enter the new namespaces.
1012
1013 # If deleting, do it regardless of the use_image flag so that a
1014 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001015 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
1016 # favor of this block.
1017 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001018 if options.delete:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001019 # Set a timeout of 300 seconds when getting the lock.
1020 with locking.FileLock(lock_path, 'chroot lock',
1021 blocking_timeout=300) as lock:
1022 try:
1023 lock.write_lock()
1024 except timeout_util.TimeoutError as e:
1025 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
1026 if not options.force:
1027 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001028 else:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001029 logging.warning(
1030 'cros_sdk was invoked with force option, continuing.')
1031 if missing_image_tools:
1032 logging.notice('Unmounting chroot.')
1033 osutils.UmountTree(options.chroot)
1034 else:
1035 logging.notice('Deleting chroot.')
1036 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
1037 chroot_deleted = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001038
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001039 # If cleanup was requested, we have to do it while we're still in the original
1040 # namespace. Since cleaning up the mount will interfere with any other
1041 # commands, we exit here. The check above should have made sure that no other
1042 # action was requested, anyway.
1043 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001044 # Set a timeout of 300 seconds when getting the lock.
1045 with locking.FileLock(lock_path, 'chroot lock',
1046 blocking_timeout=300) as lock:
1047 try:
1048 lock.write_lock()
1049 except timeout_util.TimeoutError as e:
1050 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -06001051 logging.warning(
1052 'Continuing with CleanupChroot(%s), which will umount the tree.',
1053 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001054 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1055 # even if we don't get the lock because it will attempt to unmount the
1056 # tree and will print diagnostic information from 'fuser', 'lsof', and
1057 # 'ps'.
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001058 CleanupChroot(options.chroot)
1059 sys.exit(0)
1060
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001061 # Make sure the main chroot mount is visible. Contents will be filled in
1062 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001063 if options.create and options.use_image:
1064 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001065 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001066Please make sure the lvm2 and thin-provisioning-tools packages
1067are installed on your host.
1068Example(ubuntu):
1069 sudo apt-get install lvm2 thin-provisioning-tools
1070
1071If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001072snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001073
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001074 logging.debug('Making sure chroot image is mounted.')
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001075 with locking.FileLock(lock_path, 'chroot lock') as lock:
1076 lock.write_lock()
1077 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1078 cros_build_lib.Die('Unable to mount %s on chroot',
1079 _ImageFileForChroot(options.chroot))
1080 logging.notice('Mounted %s on chroot',
1081 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001082
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001083 # Snapshot operations will always need the VG/LV, but other actions won't.
1084 if any_snapshot_operation:
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001085 with locking.FileLock(lock_path, 'chroot lock') as lock:
1086 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(options.chroot)
1087 if not chroot_vg or not chroot_lv:
1088 cros_build_lib.Die('Unable to find VG/LV for chroot %s', options.chroot)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001089
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001090 # Delete snapshot before creating a new one. This allows the user to
1091 # throw out old state, create a new snapshot, and enter the chroot in a
1092 # single call to cros_sdk. Since restore involves deleting, also do it
1093 # before creating.
1094 if options.snapshot_restore:
1095 lock.write_lock()
1096 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1097 if options.snapshot_restore not in valid_snapshots:
1098 cros_build_lib.Die(
1099 '%s is not a valid snapshot to restore to. Valid snapshots: %s',
1100 options.snapshot_restore, ', '.join(valid_snapshots))
1101 osutils.UmountTree(options.chroot)
1102 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1103 chroot_lv):
1104 cros_build_lib.Die('Unable to restore chroot to snapshot.')
1105 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1106 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001107
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001108 # Use a read lock for snapshot delete and create even though they modify
1109 # the filesystem, because they don't modify the mounted chroot itself.
1110 # The underlying LVM commands take their own locks, so conflicting
1111 # concurrent operations here may crash cros_sdk, but won't corrupt the
1112 # chroot image. This tradeoff seems worth it to allow snapshot
1113 # operations on chroots that have a process inside.
1114 if options.snapshot_delete:
1115 lock.read_lock()
1116 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001117
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001118 if options.snapshot_create:
1119 lock.read_lock()
1120 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1121 chroot_lv):
1122 cros_build_lib.Die('Unable to create snapshot.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001123
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001124 img_path = _ImageFileForChroot(options.chroot)
1125 if (options.use_image and os.path.exists(options.chroot) and
1126 os.path.exists(img_path)):
1127 img_stat = os.stat(img_path)
1128 img_used_bytes = img_stat.st_blocks * 512
1129
1130 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001131 mount_used_bytes = mount_stat.f_frsize * (
1132 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001133
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001134 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001135 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1136 logging.notice('%s is using %s GiB more than needed. Running '
Sergey Frolov1cb46ec2020-12-09 21:46:16 -07001137 'fstrim in background.', img_path, extra_gbs)
1138 pid = os.fork()
1139 if pid == 0:
1140 try:
1141 # Directly call Popen to run fstrim concurrently.
1142 cmd = ['fstrim', options.chroot]
1143 subprocess.Popen(cmd, close_fds=True, shell=False)
1144 except subprocess.SubprocessError as e:
1145 logging.warning(
1146 'Running fstrim failed. Consider running fstrim on '
1147 'your chroot manually.\n%s', e)
1148 os._exit(0) # pylint: disable=protected-access
1149 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001150
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001151 # Enter a new set of namespaces. Everything after here cannot directly affect
1152 # the hosts's mounts or alter LVM volumes.
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001153 namespaces.SimpleUnshare(pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001154
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001155 if options.snapshot_list:
1156 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1157 print(snap)
1158 sys.exit(0)
1159
Brian Harringb938c782012-02-29 15:14:38 -08001160 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001161 sdk_version = (
1162 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001163 else:
1164 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001165 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001166 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001167
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001168 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001169 if options.download:
1170 if options.sdk_url:
1171 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001172 else:
1173 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001174
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001175 with locking.FileLock(lock_path, 'chroot lock') as lock:
1176 if options.proxy_sim:
1177 _ProxySimSetup(options)
Josh Triplett472a4182013-03-08 11:48:57 -08001178
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001179 if (options.delete and not chroot_deleted and
1180 (os.path.exists(options.chroot) or
1181 os.path.exists(_ImageFileForChroot(options.chroot)))):
1182 lock.write_lock()
1183 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001184
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001185 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1186 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
1187 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001188
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001189 for target in (sdk_cache, distfiles_cache):
1190 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1191 if not os.path.exists(src):
1192 osutils.SafeMakedirsNonRoot(target)
1193 continue
1194 lock.write_lock(
1195 'Upgrade to %r needed but chroot is locked; please exit '
1196 'all instances so this upgrade can finish.' % src)
1197 if not os.path.exists(src):
1198 # Note that while waiting for the write lock, src may've vanished;
1199 # it's a rare race during the upgrade process that's a byproduct
1200 # of us avoiding taking a write lock to do the src check. If we
1201 # took a write lock for that check, it would effectively limit
1202 # all cros_sdk for a chroot to a single instance.
1203 osutils.SafeMakedirsNonRoot(target)
1204 elif not os.path.exists(target):
1205 # Upgrade occurred, but a reversion, or something whacky
1206 # occurred writing to the old location. Wipe and continue.
1207 os.rename(src, target)
1208 else:
1209 # Upgrade occurred once already, but either a reversion or
1210 # some before/after separate cros_sdk usage is at play.
1211 # Wipe and continue.
1212 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001213
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001214 if options.download:
1215 lock.write_lock()
1216 sdk_tarball = FetchRemoteTarballs(
1217 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001218
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001219 if options.create:
1220 lock.write_lock()
1221 # Recheck if the chroot is set up here before creating to make sure we
1222 # account for whatever the various delete/unmount/remount steps above
1223 # have done.
1224 if cros_sdk_lib.IsChrootReady(options.chroot):
1225 logging.debug('Chroot already exists. Skipping creation.')
1226 else:
1227 CreateChroot(
1228 options.chroot,
1229 sdk_tarball,
1230 options.cache_dir,
1231 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001232
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001233 if options.enter:
1234 lock.read_lock()
1235 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
1236 options.chrome_root_mount, options.goma_dir,
1237 options.goma_client_json, options.working_dir,
1238 chroot_command)