blob: 11cfb21fe0b94fd6a52b8461d535b81bdf8e6ce3 [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 Harringcfe762a2012-02-29 13:03:53 -080030from chromite.lib import cgroups
Brian Harringb6cf9142012-09-01 20:43:17 -070031from chromite.lib import commandline
Brian Harringb938c782012-02-29 15:14:38 -080032from chromite.lib import cros_build_lib
Ralph Nathan59900422015-03-24 10:41:17 -070033from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060034from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080035from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080036from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070037from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080038from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040039from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080040from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060041from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050042from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040043from chromite.utils import key_value_store
44
Brian Harringb938c782012-02-29 15:14:38 -080045
Mike Frysinger3b1f8e22020-03-20 04:00:13 -040046assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
47
48
Zdenek Behanaa52cea2012-05-30 01:31:11 +020049COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020050
Brian Harringb938c782012-02-29 15:14:38 -080051# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070052MAKE_CHROOT = [
53 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/make_chroot.sh')
54]
55ENTER_CHROOT = [
56 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
57]
Brian Harringb938c782012-02-29 15:14:38 -080058
Josh Triplett472a4182013-03-08 11:48:57 -080059# Proxy simulator configuration.
60PROXY_HOST_IP = '192.168.240.1'
61PROXY_PORT = 8080
62PROXY_GUEST_IP = '192.168.240.2'
63PROXY_NETMASK = 30
64PROXY_VETH_PREFIX = 'veth'
65PROXY_CONNECT_PORTS = (80, 443, 9418)
66PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
67PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
68PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070069 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080070PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
71
Josh Triplett9a495f62013-03-15 18:06:55 -070072# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080073NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080074
Josh Triplett472a4182013-03-08 11:48:57 -080075# Tools needed for --proxy-sim only.
76PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080077
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060078# Tools needed when use_image is true (the default).
79IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060080 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060081
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070082# As space is used inside the chroot, the empty space in chroot.img is
83# allocated. Deleting files inside the chroot doesn't automatically return the
84# used space to the OS. Over time, this tends to make the sparse chroot.img
85# less sparse even if the chroot contents don't currently need much space. We
86# can recover most of this unused space with fstrim, but that takes too much
87# time to run it every time. Instead, check the used space against the image
88# size after mounting the chroot and only call fstrim if it looks like we could
89# recover at least this many GiB.
90MAX_UNUSED_IMAGE_GBS = 20
91
Mike Frysingercc838832014-05-24 13:10:30 -040092
Brian Harring1790ac42012-09-23 08:53:33 -070093def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080094 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070095 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
96 return [
97 toolchain.GetSdkURL(
98 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
99 for compressor in COMPRESSION_PREFERENCE
100 ]
Brian Harring1790ac42012-09-23 08:53:33 -0700101
102
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500103def FetchRemoteTarballs(storage_dir, urls, desc):
Mike Frysinger34db8692013-11-11 14:54:08 -0500104 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200105
106 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500107 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200108 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700109 desc: A string describing what tarball we're downloading (for logging).
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200110
111 Returns:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500112 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700113
114 Raises:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500115 ValueError: None of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200116 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200117
Brian Harring1790ac42012-09-23 08:53:33 -0700118 # Note we track content length ourselves since certain versions of curl
119 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700120 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700121 logging.notice('Downloading %s tarball...', desc)
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500122 status_re = re.compile(br'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400123 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200124 for url in urls:
Mike Frysinger3dcacee2019-08-23 17:09:11 -0400125 parsed = urllib.parse.urlparse(url)
Brian Harring1790ac42012-09-23 08:53:33 -0700126 tarball_name = os.path.basename(parsed.path)
127 if parsed.scheme in ('', 'file'):
128 if os.path.exists(parsed.path):
129 return parsed.path
130 continue
131 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700132 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700133 result = retry_util.RunCurl(['-I', url],
134 print_cmd=False,
135 debug_level=logging.NOTICE,
136 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700137 successful = False
138 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700139 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700140 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700141 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700142 successful = True
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500143 elif header.lower().startswith(b'content-length:'):
144 content_length = int(header.split(b':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700145 if successful:
146 break
147 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200148 break
149 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700150 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200151
Brian Harringae0a5322012-09-15 01:46:51 -0700152 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700153 current_size = 0
154 if os.path.exists(tarball_dest):
155 current_size = os.path.getsize(tarball_dest)
156 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700157 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700158 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100159
Brian Harring1790ac42012-09-23 08:53:33 -0700160 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800161 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900162 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700163 print_cmd=False,
164 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800165
Brian Harring1790ac42012-09-23 08:53:33 -0700166 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700167 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
168 # because we might have partial overlap between known prefixes.
169 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
170 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
171 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700172 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700173 if (filename == tarball_name or
174 any([(filename.startswith(p) and
175 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
176 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700177 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700178 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700179 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200180
Brian Harringb938c782012-02-29 15:14:38 -0800181 return tarball_dest
182
183
Benjamin Gordon589873b2018-05-31 14:30:56 -0600184def CreateChroot(chroot_path, sdk_tarball, cache_dir, nousepkg=False):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600185 """Creates a new chroot from a given SDK.
186
187 Args:
188 chroot_path: Path where the new chroot will be created.
189 sdk_tarball: Path to a downloaded Gentoo Stage3 or Chromium OS SDK tarball.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600190 cache_dir: Path to a directory that will be used for caching portage files,
191 etc.
192 nousepkg: If True, pass --nousepkg to cros_setup_toolchains inside the
193 chroot.
194 """
Brian Harringb938c782012-02-29 15:14:38 -0800195
Manoj Guptab12f7302019-06-03 16:40:14 -0700196 cmd = MAKE_CHROOT + [
197 '--stage3_path', sdk_tarball, '--chroot', chroot_path, '--cache_dir',
198 cache_dir
199 ]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700200
Mike Frysinger2de7f042012-07-10 04:45:03 -0400201 if nousepkg:
202 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800203
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700204 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800205 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500206 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500207 except cros_build_lib.RunCommandError as e:
208 cros_build_lib.Die('Creating chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800209
210
211def DeleteChroot(chroot_path):
212 """Deletes an existing chroot"""
Manoj Guptab12f7302019-06-03 16:40:14 -0700213 cmd = MAKE_CHROOT + ['--chroot', chroot_path, '--delete']
Brian Harringb938c782012-02-29 15:14:38 -0800214 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700215 logging.notice('Deleting chroot.')
Mike Frysinger3e8de442020-02-14 16:46:28 -0500216 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500217 except cros_build_lib.RunCommandError as e:
218 cros_build_lib.Die('Deleting chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800219
220
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600221def CleanupChroot(chroot_path):
222 """Unmounts a chroot and cleans up any associated devices."""
Don Garrett36650112018-06-28 15:54:34 -0700223 cros_sdk_lib.CleanupChrootMount(chroot_path, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600224
225
Brian Harringae0a5322012-09-15 01:46:51 -0700226def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500227 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800228 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400229 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
230 # The os.ST_NOSUID constant wasn't added until python-3.2.
231 if st.f_flag & 0x2:
232 cros_build_lib.Die('chroot cannot be in a nosuid mount')
233
Brian Harringae0a5322012-09-15 01:46:51 -0700234 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800235 if chrome_root:
236 cmd.extend(['--chrome_root', chrome_root])
237 if chrome_root_mount:
238 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900239 if goma_dir:
240 cmd.extend(['--goma_dir', goma_dir])
241 if goma_client_json:
242 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800243 if working_dir is not None:
244 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700245
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400246 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800247 cmd.append('--')
248 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700249
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700250 # ThinLTO opens lots of files at the same time.
Bob Haarman7c9f31b2020-10-12 19:08:51 +0000251 # Set rlimit and vm.max_map_count to accommodate this.
252 file_limit = 262144
253 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
254 resource.setrlimit(resource.RLIMIT_NOFILE,
255 (max(soft, file_limit), max(hard, file_limit)))
256 max_map_count = int(open('/proc/sys/vm/max_map_count').read())
257 if max_map_count < file_limit:
258 logging.notice(
259 'Raising vm.max_map_count from %s to %s', max_map_count, file_limit)
260 open('/proc/sys/vm/max_map_count', 'w').write(f'{file_limit}\n')
Alex Klein1ec3e6a2020-04-03 11:13:50 -0600261 ret = cros_build_lib.dbg_run(cmd, check=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700262 # If we were in interactive mode, ignore the exit code; it'll be whatever
263 # they last ran w/in the chroot and won't matter to us one way or another.
264 # Note this does allow chroot entrance to fail and be ignored during
265 # interactive; this is however a rare case and the user will immediately
266 # see it (nor will they be checking the exit code manually).
267 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700268 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800269
270
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600271def _ImageFileForChroot(chroot):
272 """Find the image file that should be associated with |chroot|.
273
274 This function does not check if the image exists; it simply returns the
275 filename that would be used.
276
277 Args:
278 chroot: Path to the chroot.
279
280 Returns:
281 Path to an image file that would be associated with chroot.
282 """
283 return chroot.rstrip('/') + '.img'
284
285
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600286def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
287 """Create a snapshot for the specified chroot VG/LV.
288
289 Args:
290 snapshot_name: The name of the new snapshot.
291 chroot_vg: The name of the VG containing the origin LV.
292 chroot_lv: The name of the origin LV.
293
294 Returns:
295 True if the snapshot was created, or False if a snapshot with the same
296 name already exists.
297
298 Raises:
299 SystemExit: The lvcreate command failed.
300 """
301 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700302 logging.error(
303 'Cannot create snapshot %s: A volume with that name already '
304 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600305 return False
306
Manoj Guptab12f7302019-06-03 16:40:14 -0700307 cmd = [
308 'lvcreate', '-s', '--name', snapshot_name,
309 '%s/%s' % (chroot_vg, chroot_lv)
310 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600311 try:
312 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
313 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500314 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600315 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500316 except cros_build_lib.RunCommandError as e:
317 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600318
319
320def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
321 """Delete the named snapshot from the specified chroot VG.
322
323 If the requested snapshot is not found, nothing happens. The main chroot LV
324 and internal thinpool LV cannot be deleted with this function.
325
326 Args:
327 snapshot_name: The name of the snapshot to delete.
328 chroot_vg: The name of the VG containing the origin LV.
329 chroot_lv: The name of the origin LV.
330
331 Raises:
332 SystemExit: The lvremove command failed.
333 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600334 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
335 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700336 logging.error(
337 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
338 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600339 return
340
341 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
342 return
343
344 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
345 try:
346 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500347 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500348 except cros_build_lib.RunCommandError as e:
349 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600350
351
352def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
353 """Restore the chroot to an existing snapshot.
354
355 This is done by renaming the original |chroot_lv| LV to a temporary name,
356 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
357 now unused LV. If an error occurs, attempts to rename the original snapshot
358 back to |chroot_lv| to leave the chroot unchanged.
359
360 The chroot must be unmounted before calling this function, and will be left
361 unmounted after this function returns.
362
363 Args:
364 snapshot_name: The name of the snapshot to restore. This snapshot will no
365 longer be accessible at its original name after this function finishes.
366 chroot_vg: The VG containing the chroot LV and snapshot LV.
367 chroot_lv: The name of the original chroot LV.
368
369 Returns:
370 True if the chroot was restored to the requested snapshot, or False if
371 the snapshot wasn't found or isn't valid.
372
373 Raises:
374 SystemExit: Any of the LVM commands failed.
375 """
376 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600377 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
378 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600379 snapshot_name not in valid_snapshots):
380 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
381 snapshot_name, ', '.join(valid_snapshots))
382 return False
383
384 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
385 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
386 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500387 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500388 except cros_build_lib.RunCommandError as e:
389 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600390
391 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
392 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500393 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500394 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600395 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
396 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500397 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500398 except cros_build_lib.RunCommandError as e:
399 cros_build_lib.Die(
400 'Failed to rename %s to chroot and failed to restore %s back to '
401 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
402 cros_build_lib.Die(
403 'Failed to rename %s to chroot! Original chroot LV has '
404 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600405
406 # Some versions of LVM set snapshots to be skipped at auto-activate time.
407 # Other versions don't have this flag at all. We run lvchange to try
408 # disabling auto-skip and activating the volume, but ignore errors. Versions
409 # that don't have the flag should be auto-activated.
410 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
411 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400412 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500413 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600414
415 # Activate the LV in case the lvchange above was needed. Activating an LV
416 # that is already active shouldn't do anything, so this is safe to run even if
417 # the -kn wasn't needed.
418 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500419 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600420
421 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
422 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500423 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500424 except cros_build_lib.RunCommandError as e:
425 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
426 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600427
428 return True
429
430
431def ListChrootSnapshots(chroot_vg, chroot_lv):
432 """Return all snapshots in |chroot_vg| regardless of origin volume.
433
434 Args:
435 chroot_vg: The name of the VG containing the chroot.
436 chroot_lv: The name of the chroot LV.
437
438 Returns:
439 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
440
441 Raises:
442 SystemExit: The lvs command failed.
443 """
444 if not chroot_vg or not chroot_lv:
445 return []
446
Manoj Guptab12f7302019-06-03 16:40:14 -0700447 cmd = [
448 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
449 '--separator', '\t', chroot_vg
450 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600451 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400452 result = cros_build_lib.run(
Chris McDonaldffdf5aa2020-04-07 16:28:45 -0600453 cmd, print_cmd=False, stdout=True, encoding='utf-8')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600454 except cros_build_lib.RunCommandError:
455 raise SystemExit('Running %r failed!' % cmd)
456
457 # Once the thin origin volume has been deleted, there's no way to tell a
458 # snapshot apart from any other volume. Since this VG is created and managed
459 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
460 # valid snapshots.
461 snapshots = []
462 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
463 for line in result.output.splitlines():
464 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700465 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600466 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600467 not snapshot_attrs.match(lv_attr)):
468 continue
469 snapshots.append(lv_name)
470 return snapshots
471
472
David James56e6c2c2012-10-24 23:54:41 -0700473def _SudoCommand():
474 """Get the 'sudo' command, along with all needed environment variables."""
475
David James5a73b4d2013-03-07 10:23:40 -0800476 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400477 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700478 cmd = ['sudo']
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400479 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700480 value = os.environ.get(key)
481 if value is not None:
482 cmd += ['%s=%s' % (key, value)]
483
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400484 # We keep PATH not for the chroot but for the re-exec & for programs we might
485 # run before we chroot into the SDK. The process that enters the SDK itself
486 # will take care of initializing PATH to the right value then. But we can't
487 # override the system's default PATH for root as that will hide /sbin.
488 cmd += ['CHROMEOS_SUDO_PATH=%s' % os.environ.get('PATH', '')]
489
David James56e6c2c2012-10-24 23:54:41 -0700490 # Pass in the path to the depot_tools so that users can access them from
491 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400492 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500493
David James56e6c2c2012-10-24 23:54:41 -0700494 return cmd
495
496
Josh Triplett472a4182013-03-08 11:48:57 -0800497def _ReportMissing(missing):
498 """Report missing utilities, then exit.
499
500 Args:
501 missing: List of missing utilities, as returned by
502 osutils.FindMissingBinaries. If non-empty, will not return.
503 """
504
505 if missing:
506 raise SystemExit(
507 'The tool(s) %s were not found.\n'
508 'Please install the appropriate package in your host.\n'
509 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700510 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800511
512
513def _ProxySimSetup(options):
514 """Set up proxy simulator, and return only in the child environment.
515
516 TODO: Ideally, this should support multiple concurrent invocations of
517 cros_sdk --proxy-sim; currently, such invocations will conflict with each
518 other due to the veth device names and IP addresses. Either this code would
519 need to generate fresh, unused names for all of these before forking, or it
520 would need to support multiple concurrent cros_sdk invocations sharing one
521 proxy and allowing it to exit when unused (without counting on any local
522 service-management infrastructure on the host).
523 """
524
525 may_need_mpm = False
526 apache_bin = osutils.Which('apache2')
527 if apache_bin is None:
528 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
529 if apache_bin is None:
530 _ReportMissing(('apache2',))
531 else:
532 may_need_mpm = True
533
534 # Module names and .so names included for ease of grepping.
535 apache_modules = [('proxy_module', 'mod_proxy.so'),
536 ('proxy_connect_module', 'mod_proxy_connect.so'),
537 ('proxy_http_module', 'mod_proxy_http.so'),
538 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
539
540 # Find the apache module directory, and make sure it has the modules we need.
541 module_dirs = {}
542 for g in PROXY_APACHE_MODULE_GLOBS:
Mike Frysinger336f6b02020-05-09 00:03:28 -0400543 for _, so in apache_modules:
Josh Triplett472a4182013-03-08 11:48:57 -0800544 for f in glob.glob(os.path.join(g, so)):
545 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400546 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800547 if len(modules_found) == len(apache_modules):
548 break
549 else:
550 # Appease cros lint, which doesn't understand that this else block will not
551 # fall through to the subsequent code which relies on apache_module_path.
552 apache_module_path = None
553 raise SystemExit(
554 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500555 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800556
557 def check_add_module(name):
558 so = 'mod_%s.so' % name
559 if os.access(os.path.join(apache_module_path, so), os.F_OK):
560 mod = '%s_module' % name
561 apache_modules.append((mod, so))
562 return True
563 return False
564
565 check_add_module('authz_core')
566 if may_need_mpm:
567 for mpm in PROXY_APACHE_MPMS:
568 if check_add_module('mpm_%s' % mpm):
569 break
570
571 veth_host = '%s-host' % PROXY_VETH_PREFIX
572 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
573
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500574 # Set up locks to sync the net namespace setup. We need the child to create
575 # the net ns first, and then have the parent assign the guest end of the veth
576 # interface to the child's new network namespace & bring up the proxy. Only
577 # then can the child move forward and rely on the network being up.
578 ns_create_lock = locking.PipeLock()
579 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800580
581 pid = os.fork()
582 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500583 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800584 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500585
586 # Signal the parent the ns is ready to be configured.
587 ns_create_lock.Post()
588 del ns_create_lock
589
590 # Wait for the parent to finish setting up the ns/proxy.
591 ns_setup_lock.Wait()
592 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800593
594 # Set up child side of the network.
595 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500596 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700597 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500598 'dev', veth_guest),
599 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800600 )
601 try:
602 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500603 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500604 except cros_build_lib.RunCommandError as e:
605 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800606
607 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
608 for proto in ('http', 'https', 'ftp'):
609 os.environ[proto + '_proxy'] = proxy_url
610 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
611 os.environ.pop(v, None)
612 return
613
Josh Triplett472a4182013-03-08 11:48:57 -0800614 # Set up parent side of the network.
615 uid = int(os.environ.get('SUDO_UID', '0'))
616 gid = int(os.environ.get('SUDO_GID', '0'))
617 if uid == 0 or gid == 0:
618 for username in PROXY_APACHE_FALLBACK_USERS:
619 try:
620 pwnam = pwd.getpwnam(username)
621 uid, gid = pwnam.pw_uid, pwnam.pw_gid
622 break
623 except KeyError:
624 continue
625 if uid == 0 or gid == 0:
626 raise SystemExit('Could not find a non-root user to run Apache as')
627
628 chroot_parent, chroot_base = os.path.split(options.chroot)
629 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
630 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
631
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500632 # Wait for the child to create the net ns.
633 ns_create_lock.Wait()
634 del ns_create_lock
635
Josh Triplett472a4182013-03-08 11:48:57 -0800636 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500637 'User #%u' % uid,
638 'Group #%u' % gid,
639 'PidFile %s' % pid_file,
640 'ErrorLog %s' % log_file,
641 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
642 'ServerName %s' % PROXY_HOST_IP,
643 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400644 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800645 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500646 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
647 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800648 ]
649 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700650 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
651 veth_guest),
652 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
653 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500654 ('ip', 'link', 'set', veth_host, 'up'),
655 ([apache_bin, '-f', '/dev/null'] +
656 [arg for d in apache_directives for arg in ('-C', d)]),
657 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800658 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700659 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800660 try:
661 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500662 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500663 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800664 # Clean up existing interfaces, if any.
665 cmd_cleanup = ('ip', 'link', 'del', veth_host)
666 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400667 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800668 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700669 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500670 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500671
672 # Signal the child that the net ns/proxy is fully configured now.
673 ns_setup_lock.Post()
674 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800675
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400676 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800677
678
Mike Frysingera78a56e2012-11-20 06:02:30 -0500679def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700680 """Re-execute cros_sdk as root.
681
682 Also unshare the mount namespace so as to ensure that processes outside
683 the chroot can't mess with our mounts.
684 """
685 if os.geteuid() != 0:
Mike Frysinger1f113512020-07-29 03:36:57 -0400686 # Make sure to preserve the active Python executable in case the version
687 # we're running as is not the default one found via the (new) $PATH.
688 cmd = _SudoCommand() + ['--'] + [sys.executable] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500689 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500690 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500691 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400692 # We must set up the cgroups mounts before we enter our own namespace.
693 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800694 cgroups.Cgroup.InitSystem()
David James56e6c2c2012-10-24 23:54:41 -0700695
696
Mike Frysinger34db8692013-11-11 14:54:08 -0500697def _CreateParser(sdk_latest_version, bootstrap_latest_version):
698 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400699 usage = ('usage: %(prog)s [options] '
700 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700701 parser = commandline.ArgumentParser(
702 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700703
Mike Frysinger34db8692013-11-11 14:54:08 -0500704 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500705 default_chroot = os.path.join(constants.SOURCE_ROOT,
706 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400707 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700708 '--chroot',
709 dest='chroot',
710 default=default_chroot,
711 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700712 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700713 parser.add_argument(
714 '--nouse-image',
715 dest='use_image',
716 action='store_false',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700717 default=False,
Manoj Guptab12f7302019-06-03 16:40:14 -0700718 help='Do not mount the chroot on a loopback image; '
719 'instead, create it directly in a directory.')
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600720 parser.add_argument(
721 '--use-image',
722 dest='use_image',
723 action='store_true',
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700724 default=False,
Benjamin Gordonacbaac22020-09-25 12:59:33 -0600725 help='Mount the chroot on a loopback image '
726 'instead of creating it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800727
Manoj Guptab12f7302019-06-03 16:40:14 -0700728 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600729 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700730 '--chrome_root',
731 type='path',
732 help='Mount this chrome root into the SDK chroot')
733 parser.add_argument(
734 '--chrome_root_mount',
735 type='path',
736 help='Mount chrome into this path inside SDK chroot')
737 parser.add_argument(
738 '--nousepkg',
739 action='store_true',
740 default=False,
741 help='Do not use binary packages when creating a chroot.')
742 parser.add_argument(
743 '-u',
744 '--url',
745 dest='sdk_url',
746 help='Use sdk tarball located at this url. Use file:// '
747 'for local files.')
748 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700749 '--sdk-version',
750 help=('Use this sdk version. For prebuilt, current is %r'
751 ', for bootstrapping it is %r.' % (sdk_latest_version,
752 bootstrap_latest_version)))
753 parser.add_argument(
754 '--goma_dir',
755 type='path',
756 help='Goma installed directory to mount into the chroot.')
757 parser.add_argument(
758 '--goma_client_json',
759 type='path',
760 help='Service account json file to use goma on bot. '
761 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800762
763 # Use type=str instead of type='path' to prevent the given path from being
764 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700765 parser.add_argument(
766 '--working-dir',
767 type=str,
768 help='Run the command in specific working directory in '
769 'chroot. If the given directory is a relative '
770 'path, this program will transfer the path to '
771 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800772
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400773 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500774
775 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400776 group = parser.add_argument_group('Commands')
777 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700778 '--enter',
779 action='store_true',
780 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500781 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400782 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700783 '--create',
784 action='store_true',
785 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500786 help='Create the chroot only if it does not already exist. '
787 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400788 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700789 '--bootstrap',
790 action='store_true',
791 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500792 help='Build everything from scratch, including the sdk. '
793 'Use this only if you need to validate a change '
794 'that affects SDK creation itself (toolchain and '
795 'build are typically the only folk who need this). '
796 'Note this will quite heavily slow down the build. '
797 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400798 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700799 '-r',
800 '--replace',
801 action='store_true',
802 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500803 help='Replace an existing SDK chroot. Basically an alias '
804 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400805 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700806 '--delete',
807 action='store_true',
808 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500809 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400810 group.add_argument(
Michael Mortensene979a4d2020-06-24 13:09:42 -0600811 '--force',
812 action='store_true',
813 default=False,
814 help='Force unmount/delete of the current SDK chroot even if '
815 'obtaining the write lock fails.')
816 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700817 '--unmount',
818 action='store_true',
819 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600820 help='Unmount and clean up devices associated with the '
821 'SDK chroot if it exists. This does not delete the '
822 'backing image file, so the same chroot can be later '
823 're-mounted for reuse. To fully delete the chroot, use '
824 '--delete. This is primarily useful for working on '
825 'cros_sdk or the chroot setup; you should not need it '
826 'under normal circumstances.')
827 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700828 '--download',
829 action='store_true',
830 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500831 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600832 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700833 '--snapshot-create',
834 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600835 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700836 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600837 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700838 '--snapshot-restore',
839 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600840 help='Restore the chroot to a previously created snapshot.')
841 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700842 '--snapshot-delete',
843 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600844 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700845 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600846 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700847 '--snapshot-list',
848 action='store_true',
849 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600850 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500851 commands = group
852
Mike Frysinger80dfce92014-04-21 10:58:53 -0400853 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400854 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700855 group.add_argument(
856 '--proxy-sim',
857 action='store_true',
858 default=False,
859 help='Simulate a restrictive network requiring an outbound'
860 ' proxy.')
861 group.add_argument(
862 '--no-ns-pid',
863 dest='ns_pid',
864 default=True,
865 action='store_false',
866 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400867
Mike Frysinger34db8692013-11-11 14:54:08 -0500868 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400869 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500870 'Internal Chromium OS Build Team Options',
871 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700872 group.add_argument(
873 '--buildbot-log-version',
874 default=False,
875 action='store_true',
876 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500877
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400878 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500879
880
881def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600882 # Turn on strict sudo checks.
883 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400884 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500885 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
886 ignore_missing=True)
887 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700888 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700889
890 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
891 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
892 bootstrap_latest_version = (
893 sdk_latest_version
894 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500895 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400896 options = parser.parse_args(argv)
897 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800898
899 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500900 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800901
Brian Harring1790ac42012-09-23 08:53:33 -0700902 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700903 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600904 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700905 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400906 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700907
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400908 # Merge the outside PATH setting if we re-execed ourselves.
909 if 'CHROMEOS_SUDO_PATH' in os.environ:
910 os.environ['PATH'] = '%s:%s' % (os.environ.pop('CHROMEOS_SUDO_PATH'),
911 os.environ['PATH'])
912
Josh Triplett472a4182013-03-08 11:48:57 -0800913 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
914 if options.proxy_sim:
915 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600916 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800917
Benjamin Gordon040a1162017-06-29 13:44:47 -0600918 if (sdk_latest_version == '<unknown>' or
919 bootstrap_latest_version == '<unknown>'):
920 cros_build_lib.Die(
921 'No SDK version was found. '
922 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
923 'Please change to a directory inside your Chromium OS source tree\n'
924 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400925 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600926
Manoj Guptab12f7302019-06-03 16:40:14 -0700927 any_snapshot_operation = (
928 options.snapshot_create or options.snapshot_restore or
929 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600930
Manoj Guptab12f7302019-06-03 16:40:14 -0700931 if (options.snapshot_delete and
932 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600933 parser.error('Cannot --snapshot_delete the same snapshot you are '
934 'restoring with --snapshot_restore.')
935
David James471532c2013-01-21 10:23:31 -0800936 _ReExecuteIfNeeded([sys.argv[0]] + argv)
937
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600938 lock_path = os.path.dirname(options.chroot)
939 lock_path = os.path.join(
940 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
941
Brian Harring218e13c2012-10-10 16:21:26 -0700942 # Expand out the aliases...
943 if options.replace:
944 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800945
Brian Harring218e13c2012-10-10 16:21:26 -0700946 if options.bootstrap:
947 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800948
Brian Harring218e13c2012-10-10 16:21:26 -0700949 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400950 # pylint: disable=protected-access
951 # This _group_actions access sucks, but upstream decided to not include an
952 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700953 options.enter |= not any(
954 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400955 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700956 options.enter |= bool(chroot_command)
957
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600958 if (options.delete and not options.create and
959 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400960 parser.error('Trying to enter or snapshot the chroot when --delete '
961 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700962
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600963 if (options.unmount and
964 (options.create or options.enter or any_snapshot_operation)):
965 parser.error('--unmount cannot be specified with other chroot actions.')
966
Yong Hong84ba9172018-02-07 01:37:42 +0800967 if options.working_dir is not None and not os.path.isabs(options.working_dir):
968 options.working_dir = path_util.ToChrootPath(options.working_dir)
969
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700970 # If there is an existing chroot image and we're not removing it then force
971 # use_image on. This ensures that people don't have to remember to pass
972 # --use-image after a reboot to avoid losing access to their existing chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600973 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700974 img_path = _ImageFileForChroot(options.chroot)
Benjamin Gordon832a4412020-12-08 10:39:16 -0700975 if (not options.use_image and not options.delete and not options.unmount
976 and os.path.exists(img_path)):
977 if chroot_exists:
978 # If the chroot is already populated, make sure it has something
979 # mounted on it before we assume it came from an image.
980 cmd = ['mountpoint', '-q', options.chroot]
981 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
982 options.use_image = True
983
984 else:
985 logging.notice('Existing chroot image %s found. Forcing --use-image on.',
986 img_path)
987 options.use_image = True
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700988
Benjamin Gordon9bce7032020-11-19 09:58:44 -0700989 if any_snapshot_operation and not options.use_image:
990 if os.path.exists(img_path):
991 options.use_image = True
992 else:
993 cros_build_lib.Die('Snapshot operations are not compatible with '
994 '--nouse-image.')
995
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700996 # Discern if we need to create the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600997 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600998 not options.unmount and not missing_image_tools and
Benjamin Gordon87b068a2020-11-02 11:22:16 -0700999 os.path.exists(img_path)):
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001000 # Try to re-mount an existing image in case the user has rebooted.
1001 with cgroups.SimpleContainChildren('cros_sdk'):
1002 with locking.FileLock(lock_path, 'chroot lock') as lock:
1003 logging.debug('Checking if existing chroot image can be mounted.')
1004 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -06001005 cros_sdk_lib.MountChroot(options.chroot, create=False)
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001006 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001007 if chroot_exists:
Benjamin Gordon87b068a2020-11-02 11:22:16 -07001008 logging.notice('Mounted existing image %s on chroot', img_path)
Brian Harring218e13c2012-10-10 16:21:26 -07001009
1010 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001011 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -07001012 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -08001013
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001014 # Make sure we will download if we plan to create.
1015 options.download |= options.create
1016
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001017 # Anything that needs to manipulate the main chroot mount or communicate with
1018 # LVM needs to be done here before we enter the new namespaces.
1019
1020 # If deleting, do it regardless of the use_image flag so that a
1021 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001022 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
1023 # favor of this block.
1024 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001025 if options.delete:
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001026 with cgroups.SimpleContainChildren('cros_sdk'):
Michael Mortensene979a4d2020-06-24 13:09:42 -06001027 # Set a timeout of 300 seconds when getting the lock.
1028 with locking.FileLock(lock_path, 'chroot lock',
1029 blocking_timeout=300) as lock:
1030 try:
1031 lock.write_lock()
1032 except timeout_util.TimeoutError as e:
1033 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
1034 if not options.force:
Michael Mortensenc3a81f92020-07-06 14:28:57 -06001035 cros_build_lib.Die('Exiting; use --force to continue w/o lock.')
Michael Mortensen1a176922020-07-14 20:53:35 -06001036 else:
1037 logging.warning(
1038 'cros_sdk was invoked with force option, continuing.')
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001039 if missing_image_tools:
1040 logging.notice('Unmounting chroot.')
1041 osutils.UmountTree(options.chroot)
1042 else:
1043 logging.notice('Deleting chroot.')
Don Garrett36650112018-06-28 15:54:34 -07001044 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001045 chroot_deleted = True
1046
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001047 # If cleanup was requested, we have to do it while we're still in the original
1048 # namespace. Since cleaning up the mount will interfere with any other
1049 # commands, we exit here. The check above should have made sure that no other
1050 # action was requested, anyway.
1051 if options.unmount:
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001052 # Set a timeout of 300 seconds when getting the lock.
1053 with locking.FileLock(lock_path, 'chroot lock',
1054 blocking_timeout=300) as lock:
1055 try:
1056 lock.write_lock()
1057 except timeout_util.TimeoutError as e:
1058 logging.error('Acquiring write_lock on %s failed: %s', lock_path, e)
Michael Mortensen1a176922020-07-14 20:53:35 -06001059 logging.warning(
1060 'Continuing with CleanupChroot(%s), which will umount the tree.',
1061 options.chroot)
Michael Mortensenbf296fb2020-06-18 18:21:54 -06001062 # We can call CleanupChroot (which calls cros_sdk_lib.CleanupChrootMount)
1063 # even if we don't get the lock because it will attempt to unmount the
1064 # tree and will print diagnostic information from 'fuser', 'lsof', and
1065 # 'ps'.
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001066 CleanupChroot(options.chroot)
1067 sys.exit(0)
1068
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001069 # Make sure the main chroot mount is visible. Contents will be filled in
1070 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001071 if options.create and options.use_image:
1072 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -04001073 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001074Please make sure the lvm2 and thin-provisioning-tools packages
1075are installed on your host.
1076Example(ubuntu):
1077 sudo apt-get install lvm2 thin-provisioning-tools
1078
1079If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001080snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001081
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001082 logging.debug('Making sure chroot image is mounted.')
1083 with cgroups.SimpleContainChildren('cros_sdk'):
1084 with locking.FileLock(lock_path, 'chroot lock') as lock:
1085 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -06001086 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001087 cros_build_lib.Die('Unable to mount %s on chroot',
1088 _ImageFileForChroot(options.chroot))
1089 logging.notice('Mounted %s on chroot',
1090 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001091
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001092 # Snapshot operations will always need the VG/LV, but other actions won't.
1093 if any_snapshot_operation:
1094 with cgroups.SimpleContainChildren('cros_sdk'):
1095 with locking.FileLock(lock_path, 'chroot lock') as lock:
Benjamin Gordon74645232018-05-04 17:40:42 -06001096 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001097 options.chroot)
1098 if not chroot_vg or not chroot_lv:
1099 cros_build_lib.Die('Unable to find VG/LV for chroot %s',
1100 options.chroot)
1101
1102 # Delete snapshot before creating a new one. This allows the user to
1103 # throw out old state, create a new snapshot, and enter the chroot in a
1104 # single call to cros_sdk. Since restore involves deleting, also do it
1105 # before creating.
1106 if options.snapshot_restore:
1107 lock.write_lock()
1108 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1109 if options.snapshot_restore not in valid_snapshots:
Manoj Guptab12f7302019-06-03 16:40:14 -07001110 cros_build_lib.Die(
1111 '%s is not a valid snapshot to restore to. '
1112 'Valid snapshots: %s', options.snapshot_restore,
1113 ', '.join(valid_snapshots))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001114 osutils.UmountTree(options.chroot)
1115 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1116 chroot_lv):
1117 cros_build_lib.Die('Unable to restore chroot to snapshot.')
Benjamin Gordon74645232018-05-04 17:40:42 -06001118 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001119 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
1120
1121 # Use a read lock for snapshot delete and create even though they modify
1122 # the filesystem, because they don't modify the mounted chroot itself.
1123 # The underlying LVM commands take their own locks, so conflicting
1124 # concurrent operations here may crash cros_sdk, but won't corrupt the
1125 # chroot image. This tradeoff seems worth it to allow snapshot
1126 # operations on chroots that have a process inside.
1127 if options.snapshot_delete:
1128 lock.read_lock()
1129 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
1130
1131 if options.snapshot_create:
1132 lock.read_lock()
1133 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1134 chroot_lv):
1135 cros_build_lib.Die('Unable to create snapshot.')
1136
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001137 img_path = _ImageFileForChroot(options.chroot)
1138 if (options.use_image and os.path.exists(options.chroot) and
1139 os.path.exists(img_path)):
1140 img_stat = os.stat(img_path)
1141 img_used_bytes = img_stat.st_blocks * 512
1142
1143 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001144 mount_used_bytes = mount_stat.f_frsize * (
1145 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001146
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001147 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001148 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1149 logging.notice('%s is using %s GiB more than needed. Running '
Sergey Frolov1cb46ec2020-12-09 21:46:16 -07001150 'fstrim in background.', img_path, extra_gbs)
1151 pid = os.fork()
1152 if pid == 0:
1153 try:
1154 # Directly call Popen to run fstrim concurrently.
1155 cmd = ['fstrim', options.chroot]
1156 subprocess.Popen(cmd, close_fds=True, shell=False)
1157 except subprocess.SubprocessError as e:
1158 logging.warning(
1159 'Running fstrim failed. Consider running fstrim on '
1160 'your chroot manually.\n%s', e)
1161 os._exit(0) # pylint: disable=protected-access
1162 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001163
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001164 # Enter a new set of namespaces. Everything after here cannot directly affect
1165 # the hosts's mounts or alter LVM volumes.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001166 namespaces.SimpleUnshare()
1167 if options.ns_pid:
1168 first_pid = namespaces.CreatePidNs()
1169 else:
1170 first_pid = None
1171
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001172 if options.snapshot_list:
1173 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1174 print(snap)
1175 sys.exit(0)
1176
Brian Harringb938c782012-02-29 15:14:38 -08001177 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001178 sdk_version = (
1179 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001180 else:
1181 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001182 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001183 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001184
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001185 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001186 if options.download:
1187 if options.sdk_url:
1188 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001189 else:
1190 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001191
Mike Frysinger80dfce92014-04-21 10:58:53 -04001192 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -07001193 with locking.FileLock(lock_path, 'chroot lock') as lock:
Josh Triplett472a4182013-03-08 11:48:57 -08001194 if options.proxy_sim:
1195 _ProxySimSetup(options)
1196
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001197 if (options.delete and not chroot_deleted and
1198 (os.path.exists(options.chroot) or
1199 os.path.exists(_ImageFileForChroot(options.chroot)))):
David James56e6c2c2012-10-24 23:54:41 -07001200 lock.write_lock()
1201 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001202
David James56e6c2c2012-10-24 23:54:41 -07001203 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1204 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -07001205 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001206
David James56e6c2c2012-10-24 23:54:41 -07001207 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -05001208 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -07001209 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001210 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001211 continue
1212 lock.write_lock(
Mike Frysinger80de5012019-08-01 14:10:53 -04001213 'Upgrade to %r needed but chroot is locked; please exit '
1214 'all instances so this upgrade can finish.' % src)
David James56e6c2c2012-10-24 23:54:41 -07001215 if not os.path.exists(src):
1216 # Note that while waiting for the write lock, src may've vanished;
1217 # it's a rare race during the upgrade process that's a byproduct
1218 # of us avoiding taking a write lock to do the src check. If we
1219 # took a write lock for that check, it would effectively limit
1220 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001221 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001222 elif not os.path.exists(target):
1223 # Upgrade occurred, but a reversion, or something whacky
1224 # occurred writing to the old location. Wipe and continue.
1225 os.rename(src, target)
1226 else:
1227 # Upgrade occurred once already, but either a reversion or
1228 # some before/after separate cros_sdk usage is at play.
1229 # Wipe and continue.
1230 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001231
David James56e6c2c2012-10-24 23:54:41 -07001232 if options.download:
1233 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -07001234 sdk_tarball = FetchRemoteTarballs(
1235 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001236
David James56e6c2c2012-10-24 23:54:41 -07001237 if options.create:
1238 lock.write_lock()
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001239 # Recheck if the chroot is set up here before creating to make sure we
1240 # account for whatever the various delete/unmount/remount steps above
1241 # have done.
1242 if cros_sdk_lib.IsChrootReady(options.chroot):
1243 logging.debug('Chroot already exists. Skipping creation.')
1244 else:
Manoj Guptab12f7302019-06-03 16:40:14 -07001245 CreateChroot(
1246 options.chroot,
1247 sdk_tarball,
1248 options.cache_dir,
1249 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001250
David James56e6c2c2012-10-24 23:54:41 -07001251 if options.enter:
1252 lock.read_lock()
1253 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -05001254 options.chrome_root_mount, options.goma_dir,
1255 options.goma_client_json, options.working_dir,
1256 chroot_command)