blob: 2851b0b3518aa261b48d2335ee390a8820c89103 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Mike Frysinger2de7f042012-07-10 04:45:03 -04002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Brian Harringb938c782012-02-29 15:14:38 -08003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
Manoj Gupta7fad04d2019-06-14 20:12:25 -07005
Mike Frysinger2f95cfc2015-06-04 04:00:26 -04006"""Manage SDK chroots.
7
8This script is used for manipulating local chroot environments; creating,
9deleting, downloading, etc. If given --enter (or no args), it defaults
10to an interactive bash shell within the chroot.
11
12If given args those are passed to the chroot environment, and executed.
13"""
Brian Harringb938c782012-02-29 15:14:38 -080014
Mike Frysinger383367e2014-09-16 15:06:17 -040015from __future__ import print_function
16
Mike Frysinger2f95cfc2015-06-04 04:00:26 -040017import argparse
Josh Triplett472a4182013-03-08 11:48:57 -080018import glob
Brian Harringb938c782012-02-29 15:14:38 -080019import os
Josh Triplett472a4182013-03-08 11:48:57 -080020import pwd
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060021import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070022import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070023import resource
David James56e6c2c2012-10-24 23:54:41 -070024import sys
Mike Frysinger3dcacee2019-08-23 17:09:11 -040025
26from six.moves import urllib
Brian Harringb938c782012-02-29 15:14:38 -080027
Aviv Keshetb7519e12016-10-04 00:50:00 -070028from chromite.lib import constants
Brian Harringcfe762a2012-02-29 13:03:53 -080029from chromite.lib import cgroups
Brian Harringb6cf9142012-09-01 20:43:17 -070030from chromite.lib import commandline
Brian Harringb938c782012-02-29 15:14:38 -080031from chromite.lib import cros_build_lib
Ralph Nathan59900422015-03-24 10:41:17 -070032from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060033from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080034from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080035from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070036from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080037from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040038from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080039from chromite.lib import retry_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050040from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040041from chromite.utils import key_value_store
42
Brian Harringb938c782012-02-29 15:14:38 -080043
Mike Frysinger3b1f8e22020-03-20 04:00:13 -040044assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
45
46
Zdenek Behanaa52cea2012-05-30 01:31:11 +020047COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020048
Brian Harringb938c782012-02-29 15:14:38 -080049# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070050MAKE_CHROOT = [
51 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/make_chroot.sh')
52]
53ENTER_CHROOT = [
54 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
55]
Brian Harringb938c782012-02-29 15:14:38 -080056
Josh Triplett472a4182013-03-08 11:48:57 -080057# Proxy simulator configuration.
58PROXY_HOST_IP = '192.168.240.1'
59PROXY_PORT = 8080
60PROXY_GUEST_IP = '192.168.240.2'
61PROXY_NETMASK = 30
62PROXY_VETH_PREFIX = 'veth'
63PROXY_CONNECT_PORTS = (80, 443, 9418)
64PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
65PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
66PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070067 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080068PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
69
Josh Triplett9a495f62013-03-15 18:06:55 -070070# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080071NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080072
Josh Triplett472a4182013-03-08 11:48:57 -080073# Tools needed for --proxy-sim only.
74PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080075
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060076# Tools needed when use_image is true (the default).
77IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060078 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060079
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070080# As space is used inside the chroot, the empty space in chroot.img is
81# allocated. Deleting files inside the chroot doesn't automatically return the
82# used space to the OS. Over time, this tends to make the sparse chroot.img
83# less sparse even if the chroot contents don't currently need much space. We
84# can recover most of this unused space with fstrim, but that takes too much
85# time to run it every time. Instead, check the used space against the image
86# size after mounting the chroot and only call fstrim if it looks like we could
87# recover at least this many GiB.
88MAX_UNUSED_IMAGE_GBS = 20
89
Mike Frysingercc838832014-05-24 13:10:30 -040090
Brian Harring1790ac42012-09-23 08:53:33 -070091def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080092 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070093 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
94 return [
95 toolchain.GetSdkURL(
96 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
97 for compressor in COMPRESSION_PREFERENCE
98 ]
Brian Harring1790ac42012-09-23 08:53:33 -070099
100
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500101def FetchRemoteTarballs(storage_dir, urls, desc):
Mike Frysinger34db8692013-11-11 14:54:08 -0500102 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200103
104 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500105 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200106 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700107 desc: A string describing what tarball we're downloading (for logging).
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200108
109 Returns:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500110 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700111
112 Raises:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500113 ValueError: None of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200114 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200115
Brian Harring1790ac42012-09-23 08:53:33 -0700116 # Note we track content length ourselves since certain versions of curl
117 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700118 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700119 logging.notice('Downloading %s tarball...', desc)
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500120 status_re = re.compile(br'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400121 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200122 for url in urls:
Mike Frysinger3dcacee2019-08-23 17:09:11 -0400123 parsed = urllib.parse.urlparse(url)
Brian Harring1790ac42012-09-23 08:53:33 -0700124 tarball_name = os.path.basename(parsed.path)
125 if parsed.scheme in ('', 'file'):
126 if os.path.exists(parsed.path):
127 return parsed.path
128 continue
129 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700130 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700131 result = retry_util.RunCurl(['-I', url],
132 print_cmd=False,
133 debug_level=logging.NOTICE,
134 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700135 successful = False
136 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700137 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700138 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700139 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700140 successful = True
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500141 elif header.lower().startswith(b'content-length:'):
142 content_length = int(header.split(b':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700143 if successful:
144 break
145 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200146 break
147 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700148 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200149
Brian Harringae0a5322012-09-15 01:46:51 -0700150 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700151 current_size = 0
152 if os.path.exists(tarball_dest):
153 current_size = os.path.getsize(tarball_dest)
154 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700155 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700156 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100157
Brian Harring1790ac42012-09-23 08:53:33 -0700158 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800159 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900160 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700161 print_cmd=False,
162 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800163
Brian Harring1790ac42012-09-23 08:53:33 -0700164 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700165 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
166 # because we might have partial overlap between known prefixes.
167 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
168 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
169 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700170 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700171 if (filename == tarball_name or
172 any([(filename.startswith(p) and
173 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
174 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700175 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700176 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700177 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200178
Brian Harringb938c782012-02-29 15:14:38 -0800179 return tarball_dest
180
181
Benjamin Gordon589873b2018-05-31 14:30:56 -0600182def CreateChroot(chroot_path, sdk_tarball, cache_dir, nousepkg=False):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600183 """Creates a new chroot from a given SDK.
184
185 Args:
186 chroot_path: Path where the new chroot will be created.
187 sdk_tarball: Path to a downloaded Gentoo Stage3 or Chromium OS SDK tarball.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600188 cache_dir: Path to a directory that will be used for caching portage files,
189 etc.
190 nousepkg: If True, pass --nousepkg to cros_setup_toolchains inside the
191 chroot.
192 """
Brian Harringb938c782012-02-29 15:14:38 -0800193
Manoj Guptab12f7302019-06-03 16:40:14 -0700194 cmd = MAKE_CHROOT + [
195 '--stage3_path', sdk_tarball, '--chroot', chroot_path, '--cache_dir',
196 cache_dir
197 ]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700198
Mike Frysinger2de7f042012-07-10 04:45:03 -0400199 if nousepkg:
200 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800201
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700202 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800203 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500204 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500205 except cros_build_lib.RunCommandError as e:
206 cros_build_lib.Die('Creating chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800207
208
209def DeleteChroot(chroot_path):
210 """Deletes an existing chroot"""
Manoj Guptab12f7302019-06-03 16:40:14 -0700211 cmd = MAKE_CHROOT + ['--chroot', chroot_path, '--delete']
Brian Harringb938c782012-02-29 15:14:38 -0800212 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700213 logging.notice('Deleting chroot.')
Mike Frysinger3e8de442020-02-14 16:46:28 -0500214 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500215 except cros_build_lib.RunCommandError as e:
216 cros_build_lib.Die('Deleting chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800217
218
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600219def CleanupChroot(chroot_path):
220 """Unmounts a chroot and cleans up any associated devices."""
Don Garrett36650112018-06-28 15:54:34 -0700221 cros_sdk_lib.CleanupChrootMount(chroot_path, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600222
223
Brian Harringae0a5322012-09-15 01:46:51 -0700224def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500225 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800226 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400227 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
228 # The os.ST_NOSUID constant wasn't added until python-3.2.
229 if st.f_flag & 0x2:
230 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.
249 resource.setrlimit(resource.RLIMIT_NOFILE, (32768, 32768))
Mike Frysinger3e8de442020-02-14 16:46:28 -0500250 ret = cros_build_lib.dbg_run(cmd, check=False, mute_output=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700251 # If we were in interactive mode, ignore the exit code; it'll be whatever
252 # they last ran w/in the chroot and won't matter to us one way or another.
253 # Note this does allow chroot entrance to fail and be ignored during
254 # interactive; this is however a rare case and the user will immediately
255 # see it (nor will they be checking the exit code manually).
256 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700257 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800258
259
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600260def _ImageFileForChroot(chroot):
261 """Find the image file that should be associated with |chroot|.
262
263 This function does not check if the image exists; it simply returns the
264 filename that would be used.
265
266 Args:
267 chroot: Path to the chroot.
268
269 Returns:
270 Path to an image file that would be associated with chroot.
271 """
272 return chroot.rstrip('/') + '.img'
273
274
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600275def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
276 """Create a snapshot for the specified chroot VG/LV.
277
278 Args:
279 snapshot_name: The name of the new snapshot.
280 chroot_vg: The name of the VG containing the origin LV.
281 chroot_lv: The name of the origin LV.
282
283 Returns:
284 True if the snapshot was created, or False if a snapshot with the same
285 name already exists.
286
287 Raises:
288 SystemExit: The lvcreate command failed.
289 """
290 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700291 logging.error(
292 'Cannot create snapshot %s: A volume with that name already '
293 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600294 return False
295
Manoj Guptab12f7302019-06-03 16:40:14 -0700296 cmd = [
297 'lvcreate', '-s', '--name', snapshot_name,
298 '%s/%s' % (chroot_vg, chroot_lv)
299 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600300 try:
301 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
302 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500303 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600304 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500305 except cros_build_lib.RunCommandError as e:
306 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600307
308
309def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
310 """Delete the named snapshot from the specified chroot VG.
311
312 If the requested snapshot is not found, nothing happens. The main chroot LV
313 and internal thinpool LV cannot be deleted with this function.
314
315 Args:
316 snapshot_name: The name of the snapshot to delete.
317 chroot_vg: The name of the VG containing the origin LV.
318 chroot_lv: The name of the origin LV.
319
320 Raises:
321 SystemExit: The lvremove command failed.
322 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600323 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
324 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700325 logging.error(
326 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
327 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600328 return
329
330 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
331 return
332
333 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
334 try:
335 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500336 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500337 except cros_build_lib.RunCommandError as e:
338 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600339
340
341def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
342 """Restore the chroot to an existing snapshot.
343
344 This is done by renaming the original |chroot_lv| LV to a temporary name,
345 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
346 now unused LV. If an error occurs, attempts to rename the original snapshot
347 back to |chroot_lv| to leave the chroot unchanged.
348
349 The chroot must be unmounted before calling this function, and will be left
350 unmounted after this function returns.
351
352 Args:
353 snapshot_name: The name of the snapshot to restore. This snapshot will no
354 longer be accessible at its original name after this function finishes.
355 chroot_vg: The VG containing the chroot LV and snapshot LV.
356 chroot_lv: The name of the original chroot LV.
357
358 Returns:
359 True if the chroot was restored to the requested snapshot, or False if
360 the snapshot wasn't found or isn't valid.
361
362 Raises:
363 SystemExit: Any of the LVM commands failed.
364 """
365 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600366 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
367 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600368 snapshot_name not in valid_snapshots):
369 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
370 snapshot_name, ', '.join(valid_snapshots))
371 return False
372
373 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
374 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
375 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500376 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500377 except cros_build_lib.RunCommandError as e:
378 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600379
380 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
381 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500382 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500383 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600384 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
385 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500386 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500387 except cros_build_lib.RunCommandError as e:
388 cros_build_lib.Die(
389 'Failed to rename %s to chroot and failed to restore %s back to '
390 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
391 cros_build_lib.Die(
392 'Failed to rename %s to chroot! Original chroot LV has '
393 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600394
395 # Some versions of LVM set snapshots to be skipped at auto-activate time.
396 # Other versions don't have this flag at all. We run lvchange to try
397 # disabling auto-skip and activating the volume, but ignore errors. Versions
398 # that don't have the flag should be auto-activated.
399 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
400 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400401 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500402 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600403
404 # Activate the LV in case the lvchange above was needed. Activating an LV
405 # that is already active shouldn't do anything, so this is safe to run even if
406 # the -kn wasn't needed.
407 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500408 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600409
410 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
411 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500412 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500413 except cros_build_lib.RunCommandError as e:
414 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
415 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600416
417 return True
418
419
420def ListChrootSnapshots(chroot_vg, chroot_lv):
421 """Return all snapshots in |chroot_vg| regardless of origin volume.
422
423 Args:
424 chroot_vg: The name of the VG containing the chroot.
425 chroot_lv: The name of the chroot LV.
426
427 Returns:
428 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
429
430 Raises:
431 SystemExit: The lvs command failed.
432 """
433 if not chroot_vg or not chroot_lv:
434 return []
435
Manoj Guptab12f7302019-06-03 16:40:14 -0700436 cmd = [
437 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
438 '--separator', '\t', chroot_vg
439 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600440 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400441 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500442 cmd, print_cmd=False, stdout=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600443 except cros_build_lib.RunCommandError:
444 raise SystemExit('Running %r failed!' % cmd)
445
446 # Once the thin origin volume has been deleted, there's no way to tell a
447 # snapshot apart from any other volume. Since this VG is created and managed
448 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
449 # valid snapshots.
450 snapshots = []
451 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
452 for line in result.output.splitlines():
453 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700454 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600455 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600456 not snapshot_attrs.match(lv_attr)):
457 continue
458 snapshots.append(lv_name)
459 return snapshots
460
461
David James56e6c2c2012-10-24 23:54:41 -0700462def _SudoCommand():
463 """Get the 'sudo' command, along with all needed environment variables."""
464
David James5a73b4d2013-03-07 10:23:40 -0800465 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger9dabf3d2020-02-14 01:00:08 -0500466 # scripts in the chroot know what variables to pass through. We keep PATH
467 # not for the chroot but for the re-exec & for programs we might run before
468 # we chroot into the SDK. The process that enters the SDK itself will take
469 # care of initializing PATH to the right value then.
David James56e6c2c2012-10-24 23:54:41 -0700470 cmd = ['sudo']
Mike Frysinger9dabf3d2020-02-14 01:00:08 -0500471 for key in (constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU +
472 ('PATH',)):
David James56e6c2c2012-10-24 23:54:41 -0700473 value = os.environ.get(key)
474 if value is not None:
475 cmd += ['%s=%s' % (key, value)]
476
477 # Pass in the path to the depot_tools so that users can access them from
478 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400479 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500480
David James56e6c2c2012-10-24 23:54:41 -0700481 return cmd
482
483
Josh Triplett472a4182013-03-08 11:48:57 -0800484def _ReportMissing(missing):
485 """Report missing utilities, then exit.
486
487 Args:
488 missing: List of missing utilities, as returned by
489 osutils.FindMissingBinaries. If non-empty, will not return.
490 """
491
492 if missing:
493 raise SystemExit(
494 'The tool(s) %s were not found.\n'
495 'Please install the appropriate package in your host.\n'
496 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700497 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800498
499
500def _ProxySimSetup(options):
501 """Set up proxy simulator, and return only in the child environment.
502
503 TODO: Ideally, this should support multiple concurrent invocations of
504 cros_sdk --proxy-sim; currently, such invocations will conflict with each
505 other due to the veth device names and IP addresses. Either this code would
506 need to generate fresh, unused names for all of these before forking, or it
507 would need to support multiple concurrent cros_sdk invocations sharing one
508 proxy and allowing it to exit when unused (without counting on any local
509 service-management infrastructure on the host).
510 """
511
512 may_need_mpm = False
513 apache_bin = osutils.Which('apache2')
514 if apache_bin is None:
515 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
516 if apache_bin is None:
517 _ReportMissing(('apache2',))
518 else:
519 may_need_mpm = True
520
521 # Module names and .so names included for ease of grepping.
522 apache_modules = [('proxy_module', 'mod_proxy.so'),
523 ('proxy_connect_module', 'mod_proxy_connect.so'),
524 ('proxy_http_module', 'mod_proxy_http.so'),
525 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
526
527 # Find the apache module directory, and make sure it has the modules we need.
528 module_dirs = {}
529 for g in PROXY_APACHE_MODULE_GLOBS:
530 for mod, so in apache_modules:
531 for f in glob.glob(os.path.join(g, so)):
532 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400533 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800534 if len(modules_found) == len(apache_modules):
535 break
536 else:
537 # Appease cros lint, which doesn't understand that this else block will not
538 # fall through to the subsequent code which relies on apache_module_path.
539 apache_module_path = None
540 raise SystemExit(
541 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500542 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800543
544 def check_add_module(name):
545 so = 'mod_%s.so' % name
546 if os.access(os.path.join(apache_module_path, so), os.F_OK):
547 mod = '%s_module' % name
548 apache_modules.append((mod, so))
549 return True
550 return False
551
552 check_add_module('authz_core')
553 if may_need_mpm:
554 for mpm in PROXY_APACHE_MPMS:
555 if check_add_module('mpm_%s' % mpm):
556 break
557
558 veth_host = '%s-host' % PROXY_VETH_PREFIX
559 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
560
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500561 # Set up locks to sync the net namespace setup. We need the child to create
562 # the net ns first, and then have the parent assign the guest end of the veth
563 # interface to the child's new network namespace & bring up the proxy. Only
564 # then can the child move forward and rely on the network being up.
565 ns_create_lock = locking.PipeLock()
566 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800567
568 pid = os.fork()
569 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500570 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800571 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500572
573 # Signal the parent the ns is ready to be configured.
574 ns_create_lock.Post()
575 del ns_create_lock
576
577 # Wait for the parent to finish setting up the ns/proxy.
578 ns_setup_lock.Wait()
579 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800580
581 # Set up child side of the network.
582 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500583 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700584 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500585 'dev', veth_guest),
586 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800587 )
588 try:
589 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500590 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500591 except cros_build_lib.RunCommandError as e:
592 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800593
594 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
595 for proto in ('http', 'https', 'ftp'):
596 os.environ[proto + '_proxy'] = proxy_url
597 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
598 os.environ.pop(v, None)
599 return
600
Josh Triplett472a4182013-03-08 11:48:57 -0800601 # Set up parent side of the network.
602 uid = int(os.environ.get('SUDO_UID', '0'))
603 gid = int(os.environ.get('SUDO_GID', '0'))
604 if uid == 0 or gid == 0:
605 for username in PROXY_APACHE_FALLBACK_USERS:
606 try:
607 pwnam = pwd.getpwnam(username)
608 uid, gid = pwnam.pw_uid, pwnam.pw_gid
609 break
610 except KeyError:
611 continue
612 if uid == 0 or gid == 0:
613 raise SystemExit('Could not find a non-root user to run Apache as')
614
615 chroot_parent, chroot_base = os.path.split(options.chroot)
616 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
617 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
618
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500619 # Wait for the child to create the net ns.
620 ns_create_lock.Wait()
621 del ns_create_lock
622
Josh Triplett472a4182013-03-08 11:48:57 -0800623 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500624 'User #%u' % uid,
625 'Group #%u' % gid,
626 'PidFile %s' % pid_file,
627 'ErrorLog %s' % log_file,
628 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
629 'ServerName %s' % PROXY_HOST_IP,
630 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400631 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800632 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500633 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
634 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800635 ]
636 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700637 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
638 veth_guest),
639 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
640 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500641 ('ip', 'link', 'set', veth_host, 'up'),
642 ([apache_bin, '-f', '/dev/null'] +
643 [arg for d in apache_directives for arg in ('-C', d)]),
644 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800645 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700646 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800647 try:
648 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500649 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500650 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800651 # Clean up existing interfaces, if any.
652 cmd_cleanup = ('ip', 'link', 'del', veth_host)
653 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400654 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800655 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700656 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500657 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500658
659 # Signal the child that the net ns/proxy is fully configured now.
660 ns_setup_lock.Post()
661 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800662
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400663 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800664
665
Mike Frysingera78a56e2012-11-20 06:02:30 -0500666def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700667 """Re-execute cros_sdk as root.
668
669 Also unshare the mount namespace so as to ensure that processes outside
670 the chroot can't mess with our mounts.
671 """
672 if os.geteuid() != 0:
Mike Frysingera78a56e2012-11-20 06:02:30 -0500673 cmd = _SudoCommand() + ['--'] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500674 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500675 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500676 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400677 # We must set up the cgroups mounts before we enter our own namespace.
678 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800679 cgroups.Cgroup.InitSystem()
David James56e6c2c2012-10-24 23:54:41 -0700680
681
Mike Frysinger34db8692013-11-11 14:54:08 -0500682def _CreateParser(sdk_latest_version, bootstrap_latest_version):
683 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400684 usage = ('usage: %(prog)s [options] '
685 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700686 parser = commandline.ArgumentParser(
687 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700688
Mike Frysinger34db8692013-11-11 14:54:08 -0500689 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500690 default_chroot = os.path.join(constants.SOURCE_ROOT,
691 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400692 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700693 '--chroot',
694 dest='chroot',
695 default=default_chroot,
696 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700697 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700698 parser.add_argument(
699 '--nouse-image',
700 dest='use_image',
701 action='store_false',
702 default=True,
703 help='Do not mount the chroot on a loopback image; '
704 'instead, create it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800705
Manoj Guptab12f7302019-06-03 16:40:14 -0700706 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600707 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700708 '--chrome_root',
709 type='path',
710 help='Mount this chrome root into the SDK chroot')
711 parser.add_argument(
712 '--chrome_root_mount',
713 type='path',
714 help='Mount chrome into this path inside SDK chroot')
715 parser.add_argument(
716 '--nousepkg',
717 action='store_true',
718 default=False,
719 help='Do not use binary packages when creating a chroot.')
720 parser.add_argument(
721 '-u',
722 '--url',
723 dest='sdk_url',
724 help='Use sdk tarball located at this url. Use file:// '
725 'for local files.')
726 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700727 '--sdk-version',
728 help=('Use this sdk version. For prebuilt, current is %r'
729 ', for bootstrapping it is %r.' % (sdk_latest_version,
730 bootstrap_latest_version)))
731 parser.add_argument(
732 '--goma_dir',
733 type='path',
734 help='Goma installed directory to mount into the chroot.')
735 parser.add_argument(
736 '--goma_client_json',
737 type='path',
738 help='Service account json file to use goma on bot. '
739 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800740
741 # Use type=str instead of type='path' to prevent the given path from being
742 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700743 parser.add_argument(
744 '--working-dir',
745 type=str,
746 help='Run the command in specific working directory in '
747 'chroot. If the given directory is a relative '
748 'path, this program will transfer the path to '
749 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800750
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400751 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500752
753 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400754 group = parser.add_argument_group('Commands')
755 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700756 '--enter',
757 action='store_true',
758 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500759 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400760 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700761 '--create',
762 action='store_true',
763 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500764 help='Create the chroot only if it does not already exist. '
765 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400766 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700767 '--bootstrap',
768 action='store_true',
769 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500770 help='Build everything from scratch, including the sdk. '
771 'Use this only if you need to validate a change '
772 'that affects SDK creation itself (toolchain and '
773 'build are typically the only folk who need this). '
774 'Note this will quite heavily slow down the build. '
775 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400776 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700777 '-r',
778 '--replace',
779 action='store_true',
780 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500781 help='Replace an existing SDK chroot. Basically an alias '
782 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400783 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700784 '--delete',
785 action='store_true',
786 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500787 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400788 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700789 '--unmount',
790 action='store_true',
791 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600792 help='Unmount and clean up devices associated with the '
793 'SDK chroot if it exists. This does not delete the '
794 'backing image file, so the same chroot can be later '
795 're-mounted for reuse. To fully delete the chroot, use '
796 '--delete. This is primarily useful for working on '
797 'cros_sdk or the chroot setup; you should not need it '
798 'under normal circumstances.')
799 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700800 '--download',
801 action='store_true',
802 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500803 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600804 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700805 '--snapshot-create',
806 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600807 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700808 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600809 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700810 '--snapshot-restore',
811 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600812 help='Restore the chroot to a previously created snapshot.')
813 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700814 '--snapshot-delete',
815 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600816 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700817 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600818 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700819 '--snapshot-list',
820 action='store_true',
821 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600822 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500823 commands = group
824
Mike Frysinger80dfce92014-04-21 10:58:53 -0400825 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400826 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700827 group.add_argument(
828 '--proxy-sim',
829 action='store_true',
830 default=False,
831 help='Simulate a restrictive network requiring an outbound'
832 ' proxy.')
833 group.add_argument(
834 '--no-ns-pid',
835 dest='ns_pid',
836 default=True,
837 action='store_false',
838 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400839
Mike Frysinger34db8692013-11-11 14:54:08 -0500840 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400841 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500842 'Internal Chromium OS Build Team Options',
843 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700844 group.add_argument(
845 '--buildbot-log-version',
846 default=False,
847 action='store_true',
848 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500849
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400850 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500851
852
853def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600854 # Turn on strict sudo checks.
855 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400856 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500857 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
858 ignore_missing=True)
859 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700860 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700861
862 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
863 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
864 bootstrap_latest_version = (
865 sdk_latest_version
866 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500867 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400868 options = parser.parse_args(argv)
869 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800870
871 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500872 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800873
Brian Harring1790ac42012-09-23 08:53:33 -0700874 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700875 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600876 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700877 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400878 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700879
Josh Triplett472a4182013-03-08 11:48:57 -0800880 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
881 if options.proxy_sim:
882 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600883 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800884
Benjamin Gordon040a1162017-06-29 13:44:47 -0600885 if (sdk_latest_version == '<unknown>' or
886 bootstrap_latest_version == '<unknown>'):
887 cros_build_lib.Die(
888 'No SDK version was found. '
889 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
890 'Please change to a directory inside your Chromium OS source tree\n'
891 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400892 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600893
Manoj Guptab12f7302019-06-03 16:40:14 -0700894 any_snapshot_operation = (
895 options.snapshot_create or options.snapshot_restore or
896 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600897 if any_snapshot_operation and not options.use_image:
898 cros_build_lib.Die('Snapshot operations are not compatible with '
899 '--nouse-image.')
900
Manoj Guptab12f7302019-06-03 16:40:14 -0700901 if (options.snapshot_delete and
902 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600903 parser.error('Cannot --snapshot_delete the same snapshot you are '
904 'restoring with --snapshot_restore.')
905
David James471532c2013-01-21 10:23:31 -0800906 _ReExecuteIfNeeded([sys.argv[0]] + argv)
907
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600908 lock_path = os.path.dirname(options.chroot)
909 lock_path = os.path.join(
910 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
911
Brian Harring218e13c2012-10-10 16:21:26 -0700912 # Expand out the aliases...
913 if options.replace:
914 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800915
Brian Harring218e13c2012-10-10 16:21:26 -0700916 if options.bootstrap:
917 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800918
Brian Harring218e13c2012-10-10 16:21:26 -0700919 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400920 # pylint: disable=protected-access
921 # This _group_actions access sucks, but upstream decided to not include an
922 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700923 options.enter |= not any(
924 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400925 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700926 options.enter |= bool(chroot_command)
927
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600928 if (options.delete and not options.create and
929 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400930 parser.error('Trying to enter or snapshot the chroot when --delete '
931 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700932
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600933 if (options.unmount and
934 (options.create or options.enter or any_snapshot_operation)):
935 parser.error('--unmount cannot be specified with other chroot actions.')
936
Yong Hong84ba9172018-02-07 01:37:42 +0800937 if options.working_dir is not None and not os.path.isabs(options.working_dir):
938 options.working_dir = path_util.ToChrootPath(options.working_dir)
939
Benjamin Gordon35194f12017-07-19 10:26:22 -0600940 # Discern if we need to create the chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600941 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600942 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600943 not options.unmount and not missing_image_tools and
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600944 os.path.exists(_ImageFileForChroot(options.chroot))):
945 # Try to re-mount an existing image in case the user has rebooted.
946 with cgroups.SimpleContainChildren('cros_sdk'):
947 with locking.FileLock(lock_path, 'chroot lock') as lock:
948 logging.debug('Checking if existing chroot image can be mounted.')
949 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -0600950 cros_sdk_lib.MountChroot(options.chroot, create=False)
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600951 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600952 if chroot_exists:
953 logging.notice('Mounted existing image %s on chroot',
954 _ImageFileForChroot(options.chroot))
Brian Harring218e13c2012-10-10 16:21:26 -0700955
956 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600957 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700958 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800959
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600960 # Make sure we will download if we plan to create.
961 options.download |= options.create
962
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600963 # Anything that needs to manipulate the main chroot mount or communicate with
964 # LVM needs to be done here before we enter the new namespaces.
965
966 # If deleting, do it regardless of the use_image flag so that a
967 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600968 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
969 # favor of this block.
970 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600971 if options.delete:
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600972 with cgroups.SimpleContainChildren('cros_sdk'):
973 with locking.FileLock(lock_path, 'chroot lock') as lock:
974 lock.write_lock()
975 if missing_image_tools:
976 logging.notice('Unmounting chroot.')
977 osutils.UmountTree(options.chroot)
978 else:
979 logging.notice('Deleting chroot.')
Don Garrett36650112018-06-28 15:54:34 -0700980 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600981 chroot_deleted = True
982
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600983 # If cleanup was requested, we have to do it while we're still in the original
984 # namespace. Since cleaning up the mount will interfere with any other
985 # commands, we exit here. The check above should have made sure that no other
986 # action was requested, anyway.
987 if options.unmount:
988 with locking.FileLock(lock_path, 'chroot lock') as lock:
989 lock.write_lock()
990 CleanupChroot(options.chroot)
991 sys.exit(0)
992
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600993 # Make sure the main chroot mount is visible. Contents will be filled in
994 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600995 if options.create and options.use_image:
996 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -0400997 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600998Please make sure the lvm2 and thin-provisioning-tools packages
999are installed on your host.
1000Example(ubuntu):
1001 sudo apt-get install lvm2 thin-provisioning-tools
1002
1003If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001004snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001005
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001006 logging.debug('Making sure chroot image is mounted.')
1007 with cgroups.SimpleContainChildren('cros_sdk'):
1008 with locking.FileLock(lock_path, 'chroot lock') as lock:
1009 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -06001010 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001011 cros_build_lib.Die('Unable to mount %s on chroot',
1012 _ImageFileForChroot(options.chroot))
1013 logging.notice('Mounted %s on chroot',
1014 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001015
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001016 # Snapshot operations will always need the VG/LV, but other actions won't.
1017 if any_snapshot_operation:
1018 with cgroups.SimpleContainChildren('cros_sdk'):
1019 with locking.FileLock(lock_path, 'chroot lock') as lock:
Benjamin Gordon74645232018-05-04 17:40:42 -06001020 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001021 options.chroot)
1022 if not chroot_vg or not chroot_lv:
1023 cros_build_lib.Die('Unable to find VG/LV for chroot %s',
1024 options.chroot)
1025
1026 # Delete snapshot before creating a new one. This allows the user to
1027 # throw out old state, create a new snapshot, and enter the chroot in a
1028 # single call to cros_sdk. Since restore involves deleting, also do it
1029 # before creating.
1030 if options.snapshot_restore:
1031 lock.write_lock()
1032 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1033 if options.snapshot_restore not in valid_snapshots:
Manoj Guptab12f7302019-06-03 16:40:14 -07001034 cros_build_lib.Die(
1035 '%s is not a valid snapshot to restore to. '
1036 'Valid snapshots: %s', options.snapshot_restore,
1037 ', '.join(valid_snapshots))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001038 osutils.UmountTree(options.chroot)
1039 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1040 chroot_lv):
1041 cros_build_lib.Die('Unable to restore chroot to snapshot.')
Benjamin Gordon74645232018-05-04 17:40:42 -06001042 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001043 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
1044
1045 # Use a read lock for snapshot delete and create even though they modify
1046 # the filesystem, because they don't modify the mounted chroot itself.
1047 # The underlying LVM commands take their own locks, so conflicting
1048 # concurrent operations here may crash cros_sdk, but won't corrupt the
1049 # chroot image. This tradeoff seems worth it to allow snapshot
1050 # operations on chroots that have a process inside.
1051 if options.snapshot_delete:
1052 lock.read_lock()
1053 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
1054
1055 if options.snapshot_create:
1056 lock.read_lock()
1057 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1058 chroot_lv):
1059 cros_build_lib.Die('Unable to create snapshot.')
1060
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001061 img_path = _ImageFileForChroot(options.chroot)
1062 if (options.use_image and os.path.exists(options.chroot) and
1063 os.path.exists(img_path)):
1064 img_stat = os.stat(img_path)
1065 img_used_bytes = img_stat.st_blocks * 512
1066
1067 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001068 mount_used_bytes = mount_stat.f_frsize * (
1069 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001070
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001071 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001072 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1073 logging.notice('%s is using %s GiB more than needed. Running '
1074 'fstrim.', img_path, extra_gbs)
1075 cmd = ['fstrim', options.chroot]
1076 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -05001077 cros_build_lib.dbg_run(cmd)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001078 except cros_build_lib.RunCommandError as e:
Manoj Guptab12f7302019-06-03 16:40:14 -07001079 logging.warning(
1080 'Running fstrim failed. Consider running fstrim on '
Mike Frysinger75634e32020-02-22 23:48:12 -05001081 'your chroot manually.\n%s', e)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001082
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001083 # Enter a new set of namespaces. Everything after here cannot directly affect
1084 # the hosts's mounts or alter LVM volumes.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001085 namespaces.SimpleUnshare()
1086 if options.ns_pid:
1087 first_pid = namespaces.CreatePidNs()
1088 else:
1089 first_pid = None
1090
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001091 if options.snapshot_list:
1092 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1093 print(snap)
1094 sys.exit(0)
1095
Brian Harringb938c782012-02-29 15:14:38 -08001096 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001097 sdk_version = (
1098 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001099 else:
1100 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001101 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001102 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001103
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001104 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001105 if options.download:
1106 if options.sdk_url:
1107 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001108 else:
1109 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001110
Mike Frysinger80dfce92014-04-21 10:58:53 -04001111 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -07001112 with locking.FileLock(lock_path, 'chroot lock') as lock:
Josh Triplett472a4182013-03-08 11:48:57 -08001113 if options.proxy_sim:
1114 _ProxySimSetup(options)
1115
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001116 if (options.delete and not chroot_deleted and
1117 (os.path.exists(options.chroot) or
1118 os.path.exists(_ImageFileForChroot(options.chroot)))):
David James56e6c2c2012-10-24 23:54:41 -07001119 lock.write_lock()
1120 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001121
David James56e6c2c2012-10-24 23:54:41 -07001122 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1123 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -07001124 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001125
David James56e6c2c2012-10-24 23:54:41 -07001126 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -05001127 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -07001128 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001129 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001130 continue
1131 lock.write_lock(
Mike Frysinger80de5012019-08-01 14:10:53 -04001132 'Upgrade to %r needed but chroot is locked; please exit '
1133 'all instances so this upgrade can finish.' % src)
David James56e6c2c2012-10-24 23:54:41 -07001134 if not os.path.exists(src):
1135 # Note that while waiting for the write lock, src may've vanished;
1136 # it's a rare race during the upgrade process that's a byproduct
1137 # of us avoiding taking a write lock to do the src check. If we
1138 # took a write lock for that check, it would effectively limit
1139 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001140 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001141 elif not os.path.exists(target):
1142 # Upgrade occurred, but a reversion, or something whacky
1143 # occurred writing to the old location. Wipe and continue.
1144 os.rename(src, target)
1145 else:
1146 # Upgrade occurred once already, but either a reversion or
1147 # some before/after separate cros_sdk usage is at play.
1148 # Wipe and continue.
1149 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001150
David James56e6c2c2012-10-24 23:54:41 -07001151 if options.download:
1152 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -07001153 sdk_tarball = FetchRemoteTarballs(
1154 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001155
David James56e6c2c2012-10-24 23:54:41 -07001156 if options.create:
1157 lock.write_lock()
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001158 # Recheck if the chroot is set up here before creating to make sure we
1159 # account for whatever the various delete/unmount/remount steps above
1160 # have done.
1161 if cros_sdk_lib.IsChrootReady(options.chroot):
1162 logging.debug('Chroot already exists. Skipping creation.')
1163 else:
Manoj Guptab12f7302019-06-03 16:40:14 -07001164 CreateChroot(
1165 options.chroot,
1166 sdk_tarball,
1167 options.cache_dir,
1168 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001169
David James56e6c2c2012-10-24 23:54:41 -07001170 if options.enter:
1171 lock.read_lock()
1172 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -05001173 options.chrome_root_mount, options.goma_dir,
1174 options.goma_client_json, options.working_dir,
1175 chroot_command)