blob: 238176094e546a5aa08f0d2d208079b39b761086 [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
Zdenek Behanaa52cea2012-05-30 01:31:11 +020044COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020045
Brian Harringb938c782012-02-29 15:14:38 -080046# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070047MAKE_CHROOT = [
48 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/make_chroot.sh')
49]
50ENTER_CHROOT = [
51 os.path.join(constants.SOURCE_ROOT, 'src/scripts/sdk_lib/enter_chroot.sh')
52]
Brian Harringb938c782012-02-29 15:14:38 -080053
Josh Triplett472a4182013-03-08 11:48:57 -080054# Proxy simulator configuration.
55PROXY_HOST_IP = '192.168.240.1'
56PROXY_PORT = 8080
57PROXY_GUEST_IP = '192.168.240.2'
58PROXY_NETMASK = 30
59PROXY_VETH_PREFIX = 'veth'
60PROXY_CONNECT_PORTS = (80, 443, 9418)
61PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
62PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
63PROXY_APACHE_FALLBACK_PATH = ':'.join(
Manoj Guptab12f7302019-06-03 16:40:14 -070064 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS)
Josh Triplett472a4182013-03-08 11:48:57 -080065PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
66
Josh Triplett9a495f62013-03-15 18:06:55 -070067# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080068NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080069
Josh Triplett472a4182013-03-08 11:48:57 -080070# Tools needed for --proxy-sim only.
71PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080072
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060073# Tools needed when use_image is true (the default).
74IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060075 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060076
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070077# As space is used inside the chroot, the empty space in chroot.img is
78# allocated. Deleting files inside the chroot doesn't automatically return the
79# used space to the OS. Over time, this tends to make the sparse chroot.img
80# less sparse even if the chroot contents don't currently need much space. We
81# can recover most of this unused space with fstrim, but that takes too much
82# time to run it every time. Instead, check the used space against the image
83# size after mounting the chroot and only call fstrim if it looks like we could
84# recover at least this many GiB.
85MAX_UNUSED_IMAGE_GBS = 20
86
Mike Frysingercc838832014-05-24 13:10:30 -040087
Brian Harring1790ac42012-09-23 08:53:33 -070088def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080089 """Returns the URL for a given arch/version"""
Manoj Guptab12f7302019-06-03 16:40:14 -070090 extension = {'bz2': 'tbz2', 'xz': 'tar.xz'}
91 return [
92 toolchain.GetSdkURL(
93 suburl='cros-sdk-%s.%s' % (version, extension[compressor]))
94 for compressor in COMPRESSION_PREFERENCE
95 ]
Brian Harring1790ac42012-09-23 08:53:33 -070096
97
Mike Frysingerdaf57b82019-11-23 17:26:51 -050098def FetchRemoteTarballs(storage_dir, urls, desc):
Mike Frysinger34db8692013-11-11 14:54:08 -050099 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200100
101 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500102 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200103 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700104 desc: A string describing what tarball we're downloading (for logging).
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200105
106 Returns:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500107 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700108
109 Raises:
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500110 ValueError: None of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200111 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200112
Brian Harring1790ac42012-09-23 08:53:33 -0700113 # Note we track content length ourselves since certain versions of curl
114 # fail if asked to resume a complete file.
Brian Harring1790ac42012-09-23 08:53:33 -0700115 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700116 logging.notice('Downloading %s tarball...', desc)
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500117 status_re = re.compile(br'^HTTP/[0-9]+(\.[0-9]+)? 200')
Mike Frysinger27e21b72018-07-12 14:20:21 -0400118 # pylint: disable=undefined-loop-variable
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200119 for url in urls:
Mike Frysinger3dcacee2019-08-23 17:09:11 -0400120 parsed = urllib.parse.urlparse(url)
Brian Harring1790ac42012-09-23 08:53:33 -0700121 tarball_name = os.path.basename(parsed.path)
122 if parsed.scheme in ('', 'file'):
123 if os.path.exists(parsed.path):
124 return parsed.path
125 continue
126 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700127 logging.debug('Attempting download from %s', url)
Manoj Guptab12f7302019-06-03 16:40:14 -0700128 result = retry_util.RunCurl(['-I', url],
129 print_cmd=False,
130 debug_level=logging.NOTICE,
131 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700132 successful = False
133 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700134 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700135 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700136 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700137 successful = True
Mike Frysingerdaf57b82019-11-23 17:26:51 -0500138 elif header.lower().startswith(b'content-length:'):
139 content_length = int(header.split(b':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700140 if successful:
141 break
142 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200143 break
144 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700145 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200146
Brian Harringae0a5322012-09-15 01:46:51 -0700147 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700148 current_size = 0
149 if os.path.exists(tarball_dest):
150 current_size = os.path.getsize(tarball_dest)
151 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700152 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700153 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100154
Brian Harring1790ac42012-09-23 08:53:33 -0700155 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800156 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900157 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
Manoj Guptab12f7302019-06-03 16:40:14 -0700158 print_cmd=False,
159 debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800160
Brian Harring1790ac42012-09-23 08:53:33 -0700161 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700162 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
163 # because we might have partial overlap between known prefixes.
164 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
165 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
166 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700167 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700168 if (filename == tarball_name or
169 any([(filename.startswith(p) and
170 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
171 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700172 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700173 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700174 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200175
Brian Harringb938c782012-02-29 15:14:38 -0800176 return tarball_dest
177
178
Benjamin Gordon589873b2018-05-31 14:30:56 -0600179def CreateChroot(chroot_path, sdk_tarball, cache_dir, nousepkg=False):
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600180 """Creates a new chroot from a given SDK.
181
182 Args:
183 chroot_path: Path where the new chroot will be created.
184 sdk_tarball: Path to a downloaded Gentoo Stage3 or Chromium OS SDK tarball.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600185 cache_dir: Path to a directory that will be used for caching portage files,
186 etc.
187 nousepkg: If True, pass --nousepkg to cros_setup_toolchains inside the
188 chroot.
189 """
Brian Harringb938c782012-02-29 15:14:38 -0800190
Manoj Guptab12f7302019-06-03 16:40:14 -0700191 cmd = MAKE_CHROOT + [
192 '--stage3_path', sdk_tarball, '--chroot', chroot_path, '--cache_dir',
193 cache_dir
194 ]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700195
Mike Frysinger2de7f042012-07-10 04:45:03 -0400196 if nousepkg:
197 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800198
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700199 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800200 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500201 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500202 except cros_build_lib.RunCommandError as e:
203 cros_build_lib.Die('Creating chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800204
205
206def DeleteChroot(chroot_path):
207 """Deletes an existing chroot"""
Manoj Guptab12f7302019-06-03 16:40:14 -0700208 cmd = MAKE_CHROOT + ['--chroot', chroot_path, '--delete']
Brian Harringb938c782012-02-29 15:14:38 -0800209 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700210 logging.notice('Deleting chroot.')
Mike Frysinger3e8de442020-02-14 16:46:28 -0500211 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500212 except cros_build_lib.RunCommandError as e:
213 cros_build_lib.Die('Deleting chroot failed!\n%s', e)
Brian Harringb938c782012-02-29 15:14:38 -0800214
215
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600216def CleanupChroot(chroot_path):
217 """Unmounts a chroot and cleans up any associated devices."""
Don Garrett36650112018-06-28 15:54:34 -0700218 cros_sdk_lib.CleanupChrootMount(chroot_path, delete=False)
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600219
220
Brian Harringae0a5322012-09-15 01:46:51 -0700221def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -0500222 goma_dir, goma_client_json, working_dir, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800223 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400224 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
225 # The os.ST_NOSUID constant wasn't added until python-3.2.
226 if st.f_flag & 0x2:
227 cros_build_lib.Die('chroot cannot be in a nosuid mount')
228
Brian Harringae0a5322012-09-15 01:46:51 -0700229 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800230 if chrome_root:
231 cmd.extend(['--chrome_root', chrome_root])
232 if chrome_root_mount:
233 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900234 if goma_dir:
235 cmd.extend(['--goma_dir', goma_dir])
236 if goma_client_json:
237 cmd.extend(['--goma_client_json', goma_client_json])
Yong Hong84ba9172018-02-07 01:37:42 +0800238 if working_dir is not None:
239 cmd.extend(['--working_dir', working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700240
Mike Frysinger53ffaae2019-08-27 16:30:27 -0400241 if additional_args:
Brian Harringb938c782012-02-29 15:14:38 -0800242 cmd.append('--')
243 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700244
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700245 # ThinLTO opens lots of files at the same time.
246 resource.setrlimit(resource.RLIMIT_NOFILE, (32768, 32768))
Mike Frysinger3e8de442020-02-14 16:46:28 -0500247 ret = cros_build_lib.dbg_run(cmd, check=False, mute_output=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700248 # If we were in interactive mode, ignore the exit code; it'll be whatever
249 # they last ran w/in the chroot and won't matter to us one way or another.
250 # Note this does allow chroot entrance to fail and be ignored during
251 # interactive; this is however a rare case and the user will immediately
252 # see it (nor will they be checking the exit code manually).
253 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700254 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800255
256
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600257def _ImageFileForChroot(chroot):
258 """Find the image file that should be associated with |chroot|.
259
260 This function does not check if the image exists; it simply returns the
261 filename that would be used.
262
263 Args:
264 chroot: Path to the chroot.
265
266 Returns:
267 Path to an image file that would be associated with chroot.
268 """
269 return chroot.rstrip('/') + '.img'
270
271
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600272def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
273 """Create a snapshot for the specified chroot VG/LV.
274
275 Args:
276 snapshot_name: The name of the new snapshot.
277 chroot_vg: The name of the VG containing the origin LV.
278 chroot_lv: The name of the origin LV.
279
280 Returns:
281 True if the snapshot was created, or False if a snapshot with the same
282 name already exists.
283
284 Raises:
285 SystemExit: The lvcreate command failed.
286 """
287 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
Manoj Guptab12f7302019-06-03 16:40:14 -0700288 logging.error(
289 'Cannot create snapshot %s: A volume with that name already '
290 'exists.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600291 return False
292
Manoj Guptab12f7302019-06-03 16:40:14 -0700293 cmd = [
294 'lvcreate', '-s', '--name', snapshot_name,
295 '%s/%s' % (chroot_vg, chroot_lv)
296 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600297 try:
298 logging.notice('Creating snapshot %s from %s in VG %s.', snapshot_name,
299 chroot_lv, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500300 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600301 return True
Mike Frysinger75634e32020-02-22 23:48:12 -0500302 except cros_build_lib.RunCommandError as e:
303 cros_build_lib.Die('Creating snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600304
305
306def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
307 """Delete the named snapshot from the specified chroot VG.
308
309 If the requested snapshot is not found, nothing happens. The main chroot LV
310 and internal thinpool LV cannot be deleted with this function.
311
312 Args:
313 snapshot_name: The name of the snapshot to delete.
314 chroot_vg: The name of the VG containing the origin LV.
315 chroot_lv: The name of the origin LV.
316
317 Raises:
318 SystemExit: The lvremove command failed.
319 """
Benjamin Gordon74645232018-05-04 17:40:42 -0600320 if snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
321 cros_sdk_lib.CHROOT_THINPOOL_NAME):
Manoj Guptab12f7302019-06-03 16:40:14 -0700322 logging.error(
323 'Cannot remove LV %s as a snapshot. Use cros_sdk --delete '
324 'if you want to remove the whole chroot.', snapshot_name)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600325 return
326
327 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
328 return
329
330 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, snapshot_name)]
331 try:
332 logging.notice('Deleting snapshot %s in VG %s.', snapshot_name, chroot_vg)
Mike Frysinger3e8de442020-02-14 16:46:28 -0500333 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500334 except cros_build_lib.RunCommandError as e:
335 cros_build_lib.Die('Deleting snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600336
337
338def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
339 """Restore the chroot to an existing snapshot.
340
341 This is done by renaming the original |chroot_lv| LV to a temporary name,
342 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
343 now unused LV. If an error occurs, attempts to rename the original snapshot
344 back to |chroot_lv| to leave the chroot unchanged.
345
346 The chroot must be unmounted before calling this function, and will be left
347 unmounted after this function returns.
348
349 Args:
350 snapshot_name: The name of the snapshot to restore. This snapshot will no
351 longer be accessible at its original name after this function finishes.
352 chroot_vg: The VG containing the chroot LV and snapshot LV.
353 chroot_lv: The name of the original chroot LV.
354
355 Returns:
356 True if the chroot was restored to the requested snapshot, or False if
357 the snapshot wasn't found or isn't valid.
358
359 Raises:
360 SystemExit: Any of the LVM commands failed.
361 """
362 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
Benjamin Gordon74645232018-05-04 17:40:42 -0600363 if (snapshot_name in (cros_sdk_lib.CHROOT_LV_NAME,
364 cros_sdk_lib.CHROOT_THINPOOL_NAME) or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600365 snapshot_name not in valid_snapshots):
366 logging.error('Chroot cannot be restored to %s. Valid snapshots: %s',
367 snapshot_name, ', '.join(valid_snapshots))
368 return False
369
370 backup_chroot_name = 'chroot-bak-%d' % random.randint(0, 1000)
371 cmd = ['lvrename', chroot_vg, chroot_lv, backup_chroot_name]
372 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500373 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500374 except cros_build_lib.RunCommandError as e:
375 cros_build_lib.Die('Restoring snapshot failed!\n%s', e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600376
377 cmd = ['lvrename', chroot_vg, snapshot_name, chroot_lv]
378 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500379 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500380 except cros_build_lib.RunCommandError as e:
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600381 cmd = ['lvrename', chroot_vg, backup_chroot_name, chroot_lv]
382 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500383 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500384 except cros_build_lib.RunCommandError as e:
385 cros_build_lib.Die(
386 'Failed to rename %s to chroot and failed to restore %s back to '
387 'chroot!\n%s', snapshot_name, backup_chroot_name, e)
388 cros_build_lib.Die(
389 'Failed to rename %s to chroot! Original chroot LV has '
390 'been restored.\n%s', snapshot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600391
392 # Some versions of LVM set snapshots to be skipped at auto-activate time.
393 # Other versions don't have this flag at all. We run lvchange to try
394 # disabling auto-skip and activating the volume, but ignore errors. Versions
395 # that don't have the flag should be auto-activated.
396 chroot_lv_path = '%s/%s' % (chroot_vg, chroot_lv)
397 cmd = ['lvchange', '-kn', chroot_lv_path]
Mike Frysinger45602c72019-09-22 02:15:11 -0400398 cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500399 cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600400
401 # Activate the LV in case the lvchange above was needed. Activating an LV
402 # that is already active shouldn't do anything, so this is safe to run even if
403 # the -kn wasn't needed.
404 cmd = ['lvchange', '-ay', chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500405 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600406
407 cmd = ['lvremove', '-f', '%s/%s' % (chroot_vg, backup_chroot_name)]
408 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500409 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500410 except cros_build_lib.RunCommandError as e:
411 cros_build_lib.Die('Failed to remove backup LV %s/%s!\n%s',
412 chroot_vg, backup_chroot_name, e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600413
414 return True
415
416
417def ListChrootSnapshots(chroot_vg, chroot_lv):
418 """Return all snapshots in |chroot_vg| regardless of origin volume.
419
420 Args:
421 chroot_vg: The name of the VG containing the chroot.
422 chroot_lv: The name of the chroot LV.
423
424 Returns:
425 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
426
427 Raises:
428 SystemExit: The lvs command failed.
429 """
430 if not chroot_vg or not chroot_lv:
431 return []
432
Manoj Guptab12f7302019-06-03 16:40:14 -0700433 cmd = [
434 'lvs', '-o', 'lv_name,pool_lv,lv_attr', '-O', 'lv_name', '--noheadings',
435 '--separator', '\t', chroot_vg
436 ]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600437 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400438 result = cros_build_lib.run(
Mike Frysinger0282d222019-12-17 17:15:48 -0500439 cmd, print_cmd=False, stdout=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600440 except cros_build_lib.RunCommandError:
441 raise SystemExit('Running %r failed!' % cmd)
442
443 # Once the thin origin volume has been deleted, there's no way to tell a
444 # snapshot apart from any other volume. Since this VG is created and managed
445 # by cros_sdk, we'll assume that all volumes that share the same thin pool are
446 # valid snapshots.
447 snapshots = []
448 snapshot_attrs = re.compile(r'^V.....t.{2,}') # Matches a thin volume.
449 for line in result.output.splitlines():
450 lv_name, pool_lv, lv_attr = line.lstrip().split('\t')
Manoj Guptab12f7302019-06-03 16:40:14 -0700451 if (lv_name == chroot_lv or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon74645232018-05-04 17:40:42 -0600452 pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME or
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600453 not snapshot_attrs.match(lv_attr)):
454 continue
455 snapshots.append(lv_name)
456 return snapshots
457
458
David James56e6c2c2012-10-24 23:54:41 -0700459def _SudoCommand():
460 """Get the 'sudo' command, along with all needed environment variables."""
461
David James5a73b4d2013-03-07 10:23:40 -0800462 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
Mike Frysinger9dabf3d2020-02-14 01:00:08 -0500463 # scripts in the chroot know what variables to pass through. We keep PATH
464 # not for the chroot but for the re-exec & for programs we might run before
465 # we chroot into the SDK. The process that enters the SDK itself will take
466 # care of initializing PATH to the right value then.
David James56e6c2c2012-10-24 23:54:41 -0700467 cmd = ['sudo']
Mike Frysinger9dabf3d2020-02-14 01:00:08 -0500468 for key in (constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU +
469 ('PATH',)):
David James56e6c2c2012-10-24 23:54:41 -0700470 value = os.environ.get(key)
471 if value is not None:
472 cmd += ['%s=%s' % (key, value)]
473
474 # Pass in the path to the depot_tools so that users can access them from
475 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400476 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500477
David James56e6c2c2012-10-24 23:54:41 -0700478 return cmd
479
480
Josh Triplett472a4182013-03-08 11:48:57 -0800481def _ReportMissing(missing):
482 """Report missing utilities, then exit.
483
484 Args:
485 missing: List of missing utilities, as returned by
486 osutils.FindMissingBinaries. If non-empty, will not return.
487 """
488
489 if missing:
490 raise SystemExit(
491 'The tool(s) %s were not found.\n'
492 'Please install the appropriate package in your host.\n'
493 'Example(ubuntu):\n'
Manoj Guptab12f7302019-06-03 16:40:14 -0700494 ' sudo apt-get install <packagename>' % ', '.join(missing))
Josh Triplett472a4182013-03-08 11:48:57 -0800495
496
497def _ProxySimSetup(options):
498 """Set up proxy simulator, and return only in the child environment.
499
500 TODO: Ideally, this should support multiple concurrent invocations of
501 cros_sdk --proxy-sim; currently, such invocations will conflict with each
502 other due to the veth device names and IP addresses. Either this code would
503 need to generate fresh, unused names for all of these before forking, or it
504 would need to support multiple concurrent cros_sdk invocations sharing one
505 proxy and allowing it to exit when unused (without counting on any local
506 service-management infrastructure on the host).
507 """
508
509 may_need_mpm = False
510 apache_bin = osutils.Which('apache2')
511 if apache_bin is None:
512 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
513 if apache_bin is None:
514 _ReportMissing(('apache2',))
515 else:
516 may_need_mpm = True
517
518 # Module names and .so names included for ease of grepping.
519 apache_modules = [('proxy_module', 'mod_proxy.so'),
520 ('proxy_connect_module', 'mod_proxy_connect.so'),
521 ('proxy_http_module', 'mod_proxy_http.so'),
522 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
523
524 # Find the apache module directory, and make sure it has the modules we need.
525 module_dirs = {}
526 for g in PROXY_APACHE_MODULE_GLOBS:
527 for mod, so in apache_modules:
528 for f in glob.glob(os.path.join(g, so)):
529 module_dirs.setdefault(os.path.dirname(f), []).append(so)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400530 for apache_module_path, modules_found in module_dirs.items():
Josh Triplett472a4182013-03-08 11:48:57 -0800531 if len(modules_found) == len(apache_modules):
532 break
533 else:
534 # Appease cros lint, which doesn't understand that this else block will not
535 # fall through to the subsequent code which relies on apache_module_path.
536 apache_module_path = None
537 raise SystemExit(
538 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500539 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800540
541 def check_add_module(name):
542 so = 'mod_%s.so' % name
543 if os.access(os.path.join(apache_module_path, so), os.F_OK):
544 mod = '%s_module' % name
545 apache_modules.append((mod, so))
546 return True
547 return False
548
549 check_add_module('authz_core')
550 if may_need_mpm:
551 for mpm in PROXY_APACHE_MPMS:
552 if check_add_module('mpm_%s' % mpm):
553 break
554
555 veth_host = '%s-host' % PROXY_VETH_PREFIX
556 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
557
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500558 # Set up locks to sync the net namespace setup. We need the child to create
559 # the net ns first, and then have the parent assign the guest end of the veth
560 # interface to the child's new network namespace & bring up the proxy. Only
561 # then can the child move forward and rely on the network being up.
562 ns_create_lock = locking.PipeLock()
563 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800564
565 pid = os.fork()
566 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500567 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800568 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500569
570 # Signal the parent the ns is ready to be configured.
571 ns_create_lock.Post()
572 del ns_create_lock
573
574 # Wait for the parent to finish setting up the ns/proxy.
575 ns_setup_lock.Wait()
576 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800577
578 # Set up child side of the network.
579 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500580 ('ip', 'link', 'set', 'up', 'lo'),
Manoj Guptab12f7302019-06-03 16:40:14 -0700581 ('ip', 'address', 'add', '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500582 'dev', veth_guest),
583 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800584 )
585 try:
586 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500587 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500588 except cros_build_lib.RunCommandError as e:
589 cros_build_lib.Die('Proxy setup failed!\n%s', e)
Josh Triplett472a4182013-03-08 11:48:57 -0800590
591 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
592 for proto in ('http', 'https', 'ftp'):
593 os.environ[proto + '_proxy'] = proxy_url
594 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
595 os.environ.pop(v, None)
596 return
597
Josh Triplett472a4182013-03-08 11:48:57 -0800598 # Set up parent side of the network.
599 uid = int(os.environ.get('SUDO_UID', '0'))
600 gid = int(os.environ.get('SUDO_GID', '0'))
601 if uid == 0 or gid == 0:
602 for username in PROXY_APACHE_FALLBACK_USERS:
603 try:
604 pwnam = pwd.getpwnam(username)
605 uid, gid = pwnam.pw_uid, pwnam.pw_gid
606 break
607 except KeyError:
608 continue
609 if uid == 0 or gid == 0:
610 raise SystemExit('Could not find a non-root user to run Apache as')
611
612 chroot_parent, chroot_base = os.path.split(options.chroot)
613 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
614 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
615
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500616 # Wait for the child to create the net ns.
617 ns_create_lock.Wait()
618 del ns_create_lock
619
Josh Triplett472a4182013-03-08 11:48:57 -0800620 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500621 'User #%u' % uid,
622 'Group #%u' % gid,
623 'PidFile %s' % pid_file,
624 'ErrorLog %s' % log_file,
625 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
626 'ServerName %s' % PROXY_HOST_IP,
627 'ProxyRequests On',
Mike Frysinger66ce4132019-07-17 22:52:52 -0400628 'AllowCONNECT %s' % ' '.join(str(x) for x in PROXY_CONNECT_PORTS),
Josh Triplett472a4182013-03-08 11:48:57 -0800629 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500630 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
631 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800632 ]
633 commands = (
Manoj Guptab12f7302019-06-03 16:40:14 -0700634 ('ip', 'link', 'add', 'name', veth_host, 'type', 'veth', 'peer', 'name',
635 veth_guest),
636 ('ip', 'address', 'add', '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK), 'dev',
637 veth_host),
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500638 ('ip', 'link', 'set', veth_host, 'up'),
639 ([apache_bin, '-f', '/dev/null'] +
640 [arg for d in apache_directives for arg in ('-C', d)]),
641 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800642 )
Manoj Guptab12f7302019-06-03 16:40:14 -0700643 cmd = None # Make cros lint happy.
Josh Triplett472a4182013-03-08 11:48:57 -0800644 try:
645 for cmd in commands:
Mike Frysinger3e8de442020-02-14 16:46:28 -0500646 cros_build_lib.dbg_run(cmd)
Mike Frysinger75634e32020-02-22 23:48:12 -0500647 except cros_build_lib.RunCommandError as e:
Josh Triplett472a4182013-03-08 11:48:57 -0800648 # Clean up existing interfaces, if any.
649 cmd_cleanup = ('ip', 'link', 'del', veth_host)
650 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400651 cros_build_lib.run(cmd_cleanup, print_cmd=False)
Josh Triplett472a4182013-03-08 11:48:57 -0800652 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700653 logging.error('running %r failed', cmd_cleanup)
Mike Frysinger75634e32020-02-22 23:48:12 -0500654 cros_build_lib.Die('Proxy network setup failed!\n%s', e)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500655
656 # Signal the child that the net ns/proxy is fully configured now.
657 ns_setup_lock.Post()
658 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800659
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400660 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800661
662
Mike Frysingera78a56e2012-11-20 06:02:30 -0500663def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700664 """Re-execute cros_sdk as root.
665
666 Also unshare the mount namespace so as to ensure that processes outside
667 the chroot can't mess with our mounts.
668 """
669 if os.geteuid() != 0:
Mike Frysingera78a56e2012-11-20 06:02:30 -0500670 cmd = _SudoCommand() + ['--'] + argv
Mike Frysinger3e8de442020-02-14 16:46:28 -0500671 logging.debug('Reexecing self via sudo:\n%s', cros_build_lib.CmdToStr(cmd))
Mike Frysingera78a56e2012-11-20 06:02:30 -0500672 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500673 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400674 # We must set up the cgroups mounts before we enter our own namespace.
675 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800676 cgroups.Cgroup.InitSystem()
David James56e6c2c2012-10-24 23:54:41 -0700677
678
Mike Frysinger34db8692013-11-11 14:54:08 -0500679def _CreateParser(sdk_latest_version, bootstrap_latest_version):
680 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400681 usage = ('usage: %(prog)s [options] '
682 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
Manoj Guptab12f7302019-06-03 16:40:14 -0700683 parser = commandline.ArgumentParser(
684 usage=usage, description=__doc__, caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700685
Mike Frysinger34db8692013-11-11 14:54:08 -0500686 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500687 default_chroot = os.path.join(constants.SOURCE_ROOT,
688 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400689 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700690 '--chroot',
691 dest='chroot',
692 default=default_chroot,
693 type='path',
Brian Harring218e13c2012-10-10 16:21:26 -0700694 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Manoj Guptab12f7302019-06-03 16:40:14 -0700695 parser.add_argument(
696 '--nouse-image',
697 dest='use_image',
698 action='store_false',
699 default=True,
700 help='Do not mount the chroot on a loopback image; '
701 'instead, create it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800702
Manoj Guptab12f7302019-06-03 16:40:14 -0700703 parser.add_argument(
Alex Klein5e4b1bc2019-07-02 12:27:06 -0600704 '--chrome-root',
Manoj Guptab12f7302019-06-03 16:40:14 -0700705 '--chrome_root',
706 type='path',
707 help='Mount this chrome root into the SDK chroot')
708 parser.add_argument(
709 '--chrome_root_mount',
710 type='path',
711 help='Mount chrome into this path inside SDK chroot')
712 parser.add_argument(
713 '--nousepkg',
714 action='store_true',
715 default=False,
716 help='Do not use binary packages when creating a chroot.')
717 parser.add_argument(
718 '-u',
719 '--url',
720 dest='sdk_url',
721 help='Use sdk tarball located at this url. Use file:// '
722 'for local files.')
723 parser.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700724 '--sdk-version',
725 help=('Use this sdk version. For prebuilt, current is %r'
726 ', for bootstrapping it is %r.' % (sdk_latest_version,
727 bootstrap_latest_version)))
728 parser.add_argument(
729 '--goma_dir',
730 type='path',
731 help='Goma installed directory to mount into the chroot.')
732 parser.add_argument(
733 '--goma_client_json',
734 type='path',
735 help='Service account json file to use goma on bot. '
736 'Mounted into the chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800737
738 # Use type=str instead of type='path' to prevent the given path from being
739 # transfered to absolute path automatically.
Manoj Guptab12f7302019-06-03 16:40:14 -0700740 parser.add_argument(
741 '--working-dir',
742 type=str,
743 help='Run the command in specific working directory in '
744 'chroot. If the given directory is a relative '
745 'path, this program will transfer the path to '
746 'the corresponding one inside chroot.')
Yong Hong84ba9172018-02-07 01:37:42 +0800747
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400748 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500749
750 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400751 group = parser.add_argument_group('Commands')
752 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700753 '--enter',
754 action='store_true',
755 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500756 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400757 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700758 '--create',
759 action='store_true',
760 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500761 help='Create the chroot only if it does not already exist. '
762 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400763 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700764 '--bootstrap',
765 action='store_true',
766 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500767 help='Build everything from scratch, including the sdk. '
768 'Use this only if you need to validate a change '
769 'that affects SDK creation itself (toolchain and '
770 'build are typically the only folk who need this). '
771 'Note this will quite heavily slow down the build. '
772 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400773 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700774 '-r',
775 '--replace',
776 action='store_true',
777 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500778 help='Replace an existing SDK chroot. Basically an alias '
779 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400780 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700781 '--delete',
782 action='store_true',
783 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500784 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400785 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700786 '--unmount',
787 action='store_true',
788 default=False,
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600789 help='Unmount and clean up devices associated with the '
790 'SDK chroot if it exists. This does not delete the '
791 'backing image file, so the same chroot can be later '
792 're-mounted for reuse. To fully delete the chroot, use '
793 '--delete. This is primarily useful for working on '
794 'cros_sdk or the chroot setup; you should not need it '
795 'under normal circumstances.')
796 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700797 '--download',
798 action='store_true',
799 default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500800 help='Download the sdk.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600801 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700802 '--snapshot-create',
803 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600804 help='Create a snapshot of the chroot. Requires that the chroot was '
Manoj Guptab12f7302019-06-03 16:40:14 -0700805 'created without the --nouse-image option.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600806 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700807 '--snapshot-restore',
808 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600809 help='Restore the chroot to a previously created snapshot.')
810 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700811 '--snapshot-delete',
812 metavar='SNAPSHOT_NAME',
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600813 help='Delete a previously created snapshot. Deleting a snapshot that '
Manoj Guptab12f7302019-06-03 16:40:14 -0700814 'does not exist is not an error.')
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600815 group.add_argument(
Manoj Guptab12f7302019-06-03 16:40:14 -0700816 '--snapshot-list',
817 action='store_true',
818 default=False,
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600819 help='List existing snapshots of the chroot and exit.')
Mike Frysinger34db8692013-11-11 14:54:08 -0500820 commands = group
821
Mike Frysinger80dfce92014-04-21 10:58:53 -0400822 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400823 group = parser.add_argument_group('Namespaces')
Manoj Guptab12f7302019-06-03 16:40:14 -0700824 group.add_argument(
825 '--proxy-sim',
826 action='store_true',
827 default=False,
828 help='Simulate a restrictive network requiring an outbound'
829 ' proxy.')
830 group.add_argument(
831 '--no-ns-pid',
832 dest='ns_pid',
833 default=True,
834 action='store_false',
835 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400836
Mike Frysinger34db8692013-11-11 14:54:08 -0500837 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400838 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500839 'Internal Chromium OS Build Team Options',
840 'Caution: these are for meant for the Chromium OS build team only')
Manoj Guptab12f7302019-06-03 16:40:14 -0700841 group.add_argument(
842 '--buildbot-log-version',
843 default=False,
844 action='store_true',
845 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500846
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400847 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500848
849
850def main(argv):
Greg Edelston97f4e302020-03-13 14:01:23 -0600851 # Turn on strict sudo checks.
852 cros_build_lib.STRICT_SUDO = True
Mike Frysingere652ba12019-09-08 00:57:43 -0400853 conf = key_value_store.LoadFile(
Mike Frysinger34db8692013-11-11 14:54:08 -0500854 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
855 ignore_missing=True)
856 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
Manoj Gupta01927c12019-05-13 17:33:14 -0700857 bootstrap_frozen_version = conf.get('BOOTSTRAP_FROZEN_VERSION', '<unknown>')
Manoj Gupta55a63092019-06-13 11:47:13 -0700858
859 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
860 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
861 bootstrap_latest_version = (
862 sdk_latest_version
863 if bootstrap_frozen_version == '<unknown>' else bootstrap_frozen_version)
Mike Frysinger34db8692013-11-11 14:54:08 -0500864 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400865 options = parser.parse_args(argv)
866 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800867
868 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500869 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800870
Brian Harring1790ac42012-09-23 08:53:33 -0700871 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700872 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600873 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700874 "cros_sdk is currently only supported on x86_64; you're running"
Mike Frysinger80de5012019-08-01 14:10:53 -0400875 ' %s. Please find a x86_64 machine.' % (host,))
Brian Harring1790ac42012-09-23 08:53:33 -0700876
Josh Triplett472a4182013-03-08 11:48:57 -0800877 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
878 if options.proxy_sim:
879 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600880 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800881
Benjamin Gordon040a1162017-06-29 13:44:47 -0600882 if (sdk_latest_version == '<unknown>' or
883 bootstrap_latest_version == '<unknown>'):
884 cros_build_lib.Die(
885 'No SDK version was found. '
886 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
887 'Please change to a directory inside your Chromium OS source tree\n'
888 'and retry. If you need to setup a Chromium OS source tree, see\n'
Mike Frysingerdcad4e02018-08-03 16:20:02 -0400889 ' https://dev.chromium.org/chromium-os/developer-guide')
Benjamin Gordon040a1162017-06-29 13:44:47 -0600890
Manoj Guptab12f7302019-06-03 16:40:14 -0700891 any_snapshot_operation = (
892 options.snapshot_create or options.snapshot_restore or
893 options.snapshot_delete or options.snapshot_list)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600894 if any_snapshot_operation and not options.use_image:
895 cros_build_lib.Die('Snapshot operations are not compatible with '
896 '--nouse-image.')
897
Manoj Guptab12f7302019-06-03 16:40:14 -0700898 if (options.snapshot_delete and
899 options.snapshot_delete == options.snapshot_restore):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600900 parser.error('Cannot --snapshot_delete the same snapshot you are '
901 'restoring with --snapshot_restore.')
902
David James471532c2013-01-21 10:23:31 -0800903 _ReExecuteIfNeeded([sys.argv[0]] + argv)
904
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600905 lock_path = os.path.dirname(options.chroot)
906 lock_path = os.path.join(
907 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
908
Brian Harring218e13c2012-10-10 16:21:26 -0700909 # Expand out the aliases...
910 if options.replace:
911 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800912
Brian Harring218e13c2012-10-10 16:21:26 -0700913 if options.bootstrap:
914 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800915
Brian Harring218e13c2012-10-10 16:21:26 -0700916 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400917 # pylint: disable=protected-access
918 # This _group_actions access sucks, but upstream decided to not include an
919 # alternative to optparse's option_list, and this is what they recommend.
Manoj Guptab12f7302019-06-03 16:40:14 -0700920 options.enter |= not any(
921 getattr(options, x.dest) for x in commands._group_actions)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400922 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700923 options.enter |= bool(chroot_command)
924
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600925 if (options.delete and not options.create and
926 (options.enter or any_snapshot_operation)):
Mike Frysinger80de5012019-08-01 14:10:53 -0400927 parser.error('Trying to enter or snapshot the chroot when --delete '
928 'was specified makes no sense.')
Brian Harring218e13c2012-10-10 16:21:26 -0700929
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600930 if (options.unmount and
931 (options.create or options.enter or any_snapshot_operation)):
932 parser.error('--unmount cannot be specified with other chroot actions.')
933
Yong Hong84ba9172018-02-07 01:37:42 +0800934 if options.working_dir is not None and not os.path.isabs(options.working_dir):
935 options.working_dir = path_util.ToChrootPath(options.working_dir)
936
Benjamin Gordon35194f12017-07-19 10:26:22 -0600937 # Discern if we need to create the chroot.
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600938 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600939 if (options.use_image and not chroot_exists and not options.delete and
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600940 not options.unmount and not missing_image_tools and
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600941 os.path.exists(_ImageFileForChroot(options.chroot))):
942 # Try to re-mount an existing image in case the user has rebooted.
943 with cgroups.SimpleContainChildren('cros_sdk'):
944 with locking.FileLock(lock_path, 'chroot lock') as lock:
945 logging.debug('Checking if existing chroot image can be mounted.')
946 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -0600947 cros_sdk_lib.MountChroot(options.chroot, create=False)
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600948 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600949 if chroot_exists:
950 logging.notice('Mounted existing image %s on chroot',
951 _ImageFileForChroot(options.chroot))
Brian Harring218e13c2012-10-10 16:21:26 -0700952
953 # Finally, flip create if necessary.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600954 if options.enter or options.snapshot_create:
Brian Harring218e13c2012-10-10 16:21:26 -0700955 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800956
Benjamin Gordon7b44bef2018-06-08 08:13:59 -0600957 # Make sure we will download if we plan to create.
958 options.download |= options.create
959
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600960 # Anything that needs to manipulate the main chroot mount or communicate with
961 # LVM needs to be done here before we enter the new namespaces.
962
963 # If deleting, do it regardless of the use_image flag so that a
964 # previously-created loopback chroot can also be cleaned up.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600965 # TODO(bmgordon): See if the DeleteChroot call below can be removed in
966 # favor of this block.
967 chroot_deleted = False
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600968 if options.delete:
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600969 with cgroups.SimpleContainChildren('cros_sdk'):
970 with locking.FileLock(lock_path, 'chroot lock') as lock:
971 lock.write_lock()
972 if missing_image_tools:
973 logging.notice('Unmounting chroot.')
974 osutils.UmountTree(options.chroot)
975 else:
976 logging.notice('Deleting chroot.')
Don Garrett36650112018-06-28 15:54:34 -0700977 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600978 chroot_deleted = True
979
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -0600980 # If cleanup was requested, we have to do it while we're still in the original
981 # namespace. Since cleaning up the mount will interfere with any other
982 # commands, we exit here. The check above should have made sure that no other
983 # action was requested, anyway.
984 if options.unmount:
985 with locking.FileLock(lock_path, 'chroot lock') as lock:
986 lock.write_lock()
987 CleanupChroot(options.chroot)
988 sys.exit(0)
989
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600990 # Make sure the main chroot mount is visible. Contents will be filled in
991 # below if needed.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600992 if options.create and options.use_image:
993 if missing_image_tools:
Mike Frysinger80de5012019-08-01 14:10:53 -0400994 raise SystemExit("""The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600995Please make sure the lvm2 and thin-provisioning-tools packages
996are installed on your host.
997Example(ubuntu):
998 sudo apt-get install lvm2 thin-provisioning-tools
999
1000If you want to run without lvm2, pass --nouse-image (chroot
Mike Frysinger80de5012019-08-01 14:10:53 -04001001snapshots will be unavailable).""" % ', '.join(missing_image_tools))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001002
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001003 logging.debug('Making sure chroot image is mounted.')
1004 with cgroups.SimpleContainChildren('cros_sdk'):
1005 with locking.FileLock(lock_path, 'chroot lock') as lock:
1006 lock.write_lock()
Benjamin Gordon74645232018-05-04 17:40:42 -06001007 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001008 cros_build_lib.Die('Unable to mount %s on chroot',
1009 _ImageFileForChroot(options.chroot))
1010 logging.notice('Mounted %s on chroot',
1011 _ImageFileForChroot(options.chroot))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001012
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001013 # Snapshot operations will always need the VG/LV, but other actions won't.
1014 if any_snapshot_operation:
1015 with cgroups.SimpleContainChildren('cros_sdk'):
1016 with locking.FileLock(lock_path, 'chroot lock') as lock:
Benjamin Gordon74645232018-05-04 17:40:42 -06001017 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001018 options.chroot)
1019 if not chroot_vg or not chroot_lv:
1020 cros_build_lib.Die('Unable to find VG/LV for chroot %s',
1021 options.chroot)
1022
1023 # Delete snapshot before creating a new one. This allows the user to
1024 # throw out old state, create a new snapshot, and enter the chroot in a
1025 # single call to cros_sdk. Since restore involves deleting, also do it
1026 # before creating.
1027 if options.snapshot_restore:
1028 lock.write_lock()
1029 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1030 if options.snapshot_restore not in valid_snapshots:
Manoj Guptab12f7302019-06-03 16:40:14 -07001031 cros_build_lib.Die(
1032 '%s is not a valid snapshot to restore to. '
1033 'Valid snapshots: %s', options.snapshot_restore,
1034 ', '.join(valid_snapshots))
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001035 osutils.UmountTree(options.chroot)
1036 if not RestoreChrootSnapshot(options.snapshot_restore, chroot_vg,
1037 chroot_lv):
1038 cros_build_lib.Die('Unable to restore chroot to snapshot.')
Benjamin Gordon74645232018-05-04 17:40:42 -06001039 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001040 cros_build_lib.Die('Unable to mount restored snapshot onto chroot.')
1041
1042 # Use a read lock for snapshot delete and create even though they modify
1043 # the filesystem, because they don't modify the mounted chroot itself.
1044 # The underlying LVM commands take their own locks, so conflicting
1045 # concurrent operations here may crash cros_sdk, but won't corrupt the
1046 # chroot image. This tradeoff seems worth it to allow snapshot
1047 # operations on chroots that have a process inside.
1048 if options.snapshot_delete:
1049 lock.read_lock()
1050 DeleteChrootSnapshot(options.snapshot_delete, chroot_vg, chroot_lv)
1051
1052 if options.snapshot_create:
1053 lock.read_lock()
1054 if not CreateChrootSnapshot(options.snapshot_create, chroot_vg,
1055 chroot_lv):
1056 cros_build_lib.Die('Unable to create snapshot.')
1057
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001058 img_path = _ImageFileForChroot(options.chroot)
1059 if (options.use_image and os.path.exists(options.chroot) and
1060 os.path.exists(img_path)):
1061 img_stat = os.stat(img_path)
1062 img_used_bytes = img_stat.st_blocks * 512
1063
1064 mount_stat = os.statvfs(options.chroot)
Manoj Guptab12f7302019-06-03 16:40:14 -07001065 mount_used_bytes = mount_stat.f_frsize * (
1066 mount_stat.f_blocks - mount_stat.f_bfree)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001067
Mike Frysinger93e8ffa2019-07-03 20:24:18 -04001068 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001069 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1070 logging.notice('%s is using %s GiB more than needed. Running '
1071 'fstrim.', img_path, extra_gbs)
1072 cmd = ['fstrim', options.chroot]
1073 try:
Mike Frysinger3e8de442020-02-14 16:46:28 -05001074 cros_build_lib.dbg_run(cmd)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001075 except cros_build_lib.RunCommandError as e:
Manoj Guptab12f7302019-06-03 16:40:14 -07001076 logging.warning(
1077 'Running fstrim failed. Consider running fstrim on '
Mike Frysinger75634e32020-02-22 23:48:12 -05001078 'your chroot manually.\n%s', e)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001079
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001080 # Enter a new set of namespaces. Everything after here cannot directly affect
1081 # the hosts's mounts or alter LVM volumes.
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001082 namespaces.SimpleUnshare()
1083 if options.ns_pid:
1084 first_pid = namespaces.CreatePidNs()
1085 else:
1086 first_pid = None
1087
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001088 if options.snapshot_list:
1089 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1090 print(snap)
1091 sys.exit(0)
1092
Brian Harringb938c782012-02-29 15:14:38 -08001093 if not options.sdk_version:
Manoj Guptab12f7302019-06-03 16:40:14 -07001094 sdk_version = (
1095 bootstrap_latest_version if options.bootstrap else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -08001096 else:
1097 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -05001098 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -07001099 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -08001100
Gilad Arnoldecc86fa2015-05-22 12:06:04 -07001101 # Based on selections, determine the tarball to fetch.
Yong Hong4e29b622018-02-05 14:31:10 +08001102 if options.download:
1103 if options.sdk_url:
1104 urls = [options.sdk_url]
Yong Hong4e29b622018-02-05 14:31:10 +08001105 else:
1106 urls = GetArchStageTarballs(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001107
Mike Frysinger80dfce92014-04-21 10:58:53 -04001108 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -07001109 with locking.FileLock(lock_path, 'chroot lock') as lock:
Josh Triplett472a4182013-03-08 11:48:57 -08001110 if options.proxy_sim:
1111 _ProxySimSetup(options)
1112
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001113 if (options.delete and not chroot_deleted and
1114 (os.path.exists(options.chroot) or
1115 os.path.exists(_ImageFileForChroot(options.chroot)))):
David James56e6c2c2012-10-24 23:54:41 -07001116 lock.write_lock()
1117 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -08001118
David James56e6c2c2012-10-24 23:54:41 -07001119 sdk_cache = os.path.join(options.cache_dir, 'sdks')
1120 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -07001121 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -07001122
David James56e6c2c2012-10-24 23:54:41 -07001123 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -05001124 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -07001125 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001126 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001127 continue
1128 lock.write_lock(
Mike Frysinger80de5012019-08-01 14:10:53 -04001129 'Upgrade to %r needed but chroot is locked; please exit '
1130 'all instances so this upgrade can finish.' % src)
David James56e6c2c2012-10-24 23:54:41 -07001131 if not os.path.exists(src):
1132 # Note that while waiting for the write lock, src may've vanished;
1133 # it's a rare race during the upgrade process that's a byproduct
1134 # of us avoiding taking a write lock to do the src check. If we
1135 # took a write lock for that check, it would effectively limit
1136 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -07001137 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -07001138 elif not os.path.exists(target):
1139 # Upgrade occurred, but a reversion, or something whacky
1140 # occurred writing to the old location. Wipe and continue.
1141 os.rename(src, target)
1142 else:
1143 # Upgrade occurred once already, but either a reversion or
1144 # some before/after separate cros_sdk usage is at play.
1145 # Wipe and continue.
1146 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -07001147
David James56e6c2c2012-10-24 23:54:41 -07001148 if options.download:
1149 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -07001150 sdk_tarball = FetchRemoteTarballs(
1151 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
Brian Harring218e13c2012-10-10 16:21:26 -07001152
David James56e6c2c2012-10-24 23:54:41 -07001153 if options.create:
1154 lock.write_lock()
Benjamin Gordon7b44bef2018-06-08 08:13:59 -06001155 # Recheck if the chroot is set up here before creating to make sure we
1156 # account for whatever the various delete/unmount/remount steps above
1157 # have done.
1158 if cros_sdk_lib.IsChrootReady(options.chroot):
1159 logging.debug('Chroot already exists. Skipping creation.')
1160 else:
Manoj Guptab12f7302019-06-03 16:40:14 -07001161 CreateChroot(
1162 options.chroot,
1163 sdk_tarball,
1164 options.cache_dir,
1165 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -07001166
David James56e6c2c2012-10-24 23:54:41 -07001167 if options.enter:
1168 lock.read_lock()
1169 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Mike Frysinger0b2d9ee2019-02-28 17:05:47 -05001170 options.chrome_root_mount, options.goma_dir,
1171 options.goma_client_json, options.working_dir,
1172 chroot_command)