blob: f6fe417deedad21306fbe458bb2ee3b09f9717d0 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
Brian Harringb938c782012-02-29 15:14:38 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Manoj Gupta7fad04d2019-06-14 20:12:25 -07004
Mike Frysinger2f95cfc2015-06-04 04:00:26 -04005"""Manage SDK chroots.
6
7This script is used for manipulating local chroot environments; creating,
8deleting, downloading, etc. If given --enter (or no args), it defaults
9to an interactive bash shell within the chroot.
10
11If given args those are passed to the chroot environment, and executed.
12"""
Brian Harringb938c782012-02-29 15:14:38 -080013
Mike Frysinger2f95cfc2015-06-04 04:00:26 -040014import argparse
Josh Triplett472a4182013-03-08 11:48:57 -080015import glob
Chris McDonaldb55b7032021-06-17 16:41:32 -060016import logging
Brian Harringb938c782012-02-29 15:14:38 -080017import os
Mike Frysinger23b5cf52021-06-16 23:18:00 -040018from pathlib import Path
Josh Triplett472a4182013-03-08 11:48:57 -080019import pwd
Brian Norrisd37e2f72016-08-22 16:09:24 -070020import re
Mike Frysinger5f5c70b2022-07-19 12:55:21 -040021import shlex
David James56e6c2c2012-10-24 23:54:41 -070022import sys
Mike Frysingerec32bea2023-01-27 01:20:48 -050023from typing import List
Mike Frysingere852b072021-05-21 12:39:03 -040024import urllib.parse
Brian Harringb938c782012-02-29 15:14:38 -080025
Chris McDonaldb55b7032021-06-17 16:41:32 -060026from chromite.cbuildbot import cbuildbot_alerts
Mike Frysingerec32bea2023-01-27 01:20:48 -050027from chromite.lib import chroot_lib
Brian Harringb6cf9142012-09-01 20:43:17 -070028from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060029from chromite.lib import constants
Brian Harringb938c782012-02-29 15:14:38 -080030from chromite.lib import cros_build_lib
Benjamin Gordon74645232018-05-04 17:40:42 -060031from chromite.lib import cros_sdk_lib
Mike Frysinger9a5124e2023-01-26 17:16:44 -050032from chromite.lib import goma_lib
Brian Harringb938c782012-02-29 15:14:38 -080033from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080034from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070035from chromite.lib import osutils
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040036from chromite.lib import process_util
Mike Frysinger253c8392023-01-27 01:12:21 -050037from chromite.lib import remoteexec_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080038from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060039from chromite.lib import timeout_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 Frysingerf744d032022-05-07 20:38:39 -040044# Which compression algos the SDK tarball uses. We've used xz since 2012.
Alex Klein1699fab2022-09-08 08:46:06 -060045COMPRESSION_PREFERENCE = ("xz",)
Zdenek Behanfd0efe42012-04-13 04:36:40 +020046
Josh Triplett472a4182013-03-08 11:48:57 -080047# Proxy simulator configuration.
Alex Klein1699fab2022-09-08 08:46:06 -060048PROXY_HOST_IP = "192.168.240.1"
Josh Triplett472a4182013-03-08 11:48:57 -080049PROXY_PORT = 8080
Alex Klein1699fab2022-09-08 08:46:06 -060050PROXY_GUEST_IP = "192.168.240.2"
Josh Triplett472a4182013-03-08 11:48:57 -080051PROXY_NETMASK = 30
Alex Klein1699fab2022-09-08 08:46:06 -060052PROXY_VETH_PREFIX = "veth"
Josh Triplett472a4182013-03-08 11:48:57 -080053PROXY_CONNECT_PORTS = (80, 443, 9418)
Alex Klein1699fab2022-09-08 08:46:06 -060054PROXY_APACHE_FALLBACK_USERS = ("www-data", "apache", "nobody")
55PROXY_APACHE_MPMS = ("event", "worker", "prefork")
56PROXY_APACHE_FALLBACK_PATH = ":".join(
57 "/usr/lib/apache2/mpm-%s" % mpm for mpm in PROXY_APACHE_MPMS
58)
59PROXY_APACHE_MODULE_GLOBS = ("/usr/lib*/apache2/modules", "/usr/lib*/apache2")
Josh Triplett472a4182013-03-08 11:48:57 -080060
Josh Triplett9a495f62013-03-15 18:06:55 -070061# We need these tools to run. Very common tools (tar,..) are omitted.
Alex Klein1699fab2022-09-08 08:46:06 -060062NEEDED_TOOLS = ("curl", "xz")
Brian Harringb938c782012-02-29 15:14:38 -080063
Josh Triplett472a4182013-03-08 11:48:57 -080064# Tools needed for --proxy-sim only.
Alex Klein1699fab2022-09-08 08:46:06 -060065PROXY_NEEDED_TOOLS = ("ip",)
Brian Harringb938c782012-02-29 15:14:38 -080066
Mike Frysingercc838832014-05-24 13:10:30 -040067
Brian Harring1790ac42012-09-23 08:53:33 -070068def GetArchStageTarballs(version):
Alex Klein1699fab2022-09-08 08:46:06 -060069 """Returns the URL for a given arch/version"""
70 extension = {"xz": "tar.xz"}
71 return [
72 toolchain.GetSdkURL(
73 suburl="cros-sdk-%s.%s" % (version, extension[compressor])
74 )
75 for compressor in COMPRESSION_PREFERENCE
76 ]
Brian Harring1790ac42012-09-23 08:53:33 -070077
78
Mike Frysingerf744d032022-05-07 20:38:39 -040079def FetchRemoteTarballs(storage_dir, urls):
Alex Klein1699fab2022-09-08 08:46:06 -060080 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +020081
Alex Klein1699fab2022-09-08 08:46:06 -060082 Args:
Alex Kleinef517832023-01-13 12:06:51 -070083 storage_dir: Path where to save the tarball.
84 urls: List of URLs to try to download. Download will stop on first
85 success.
Zdenek Behanfd0efe42012-04-13 04:36:40 +020086
Alex Klein1699fab2022-09-08 08:46:06 -060087 Returns:
Alex Kleinef517832023-01-13 12:06:51 -070088 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -070089
Alex Klein1699fab2022-09-08 08:46:06 -060090 Raises:
Alex Kleinef517832023-01-13 12:06:51 -070091 ValueError: None of the URLs worked.
Alex Klein1699fab2022-09-08 08:46:06 -060092 """
93 # Note we track content length ourselves since certain versions of curl
94 # fail if asked to resume a complete file.
95 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
96 status_re = re.compile(rb"^HTTP/[0-9]+(\.[0-9]+)? 200")
97 # pylint: disable=undefined-loop-variable
98 for url in urls:
99 logging.notice("Downloading tarball %s ...", urls[0].rsplit("/", 1)[-1])
100 parsed = urllib.parse.urlparse(url)
101 tarball_name = os.path.basename(parsed.path)
102 if parsed.scheme in ("", "file"):
103 if os.path.exists(parsed.path):
104 return parsed.path
105 continue
106 content_length = 0
107 logging.debug("Attempting download from %s", url)
108 result = retry_util.RunCurl(
109 ["-I", url],
110 print_cmd=False,
111 debug_level=logging.NOTICE,
112 capture_output=True,
113 )
114 successful = False
115 for header in result.stdout.splitlines():
116 # We must walk the output to find the 200 code for use cases where
117 # a proxy is involved and may have pushed down the actual header.
118 if status_re.match(header):
119 successful = True
120 elif header.lower().startswith(b"content-length:"):
121 content_length = int(header.split(b":", 1)[-1].strip())
122 if successful:
123 break
Brian Harring1790ac42012-09-23 08:53:33 -0700124 if successful:
Alex Klein1699fab2022-09-08 08:46:06 -0600125 break
126 else:
127 raise ValueError("No valid URLs found!")
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200128
Alex Klein1699fab2022-09-08 08:46:06 -0600129 tarball_dest = os.path.join(storage_dir, tarball_name)
130 current_size = 0
131 if os.path.exists(tarball_dest):
132 current_size = os.path.getsize(tarball_dest)
133 if current_size > content_length:
134 osutils.SafeUnlink(tarball_dest)
135 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100136
Alex Klein1699fab2022-09-08 08:46:06 -0600137 if current_size < content_length:
138 retry_util.RunCurl(
139 [
140 "--fail",
141 "-L",
142 "-y",
143 "30",
144 "-C",
145 "-",
146 "--output",
147 tarball_dest,
148 url,
149 ],
150 print_cmd=False,
151 debug_level=logging.NOTICE,
152 )
Brian Harringb938c782012-02-29 15:14:38 -0800153
Alex Klein1699fab2022-09-08 08:46:06 -0600154 # Cleanup old tarballs now since we've successfull fetched; only cleanup
155 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
156 # because we might have partial overlap between known prefixes.
157 for p in Path(storage_dir).glob("cros-sdk-*"):
158 if p.name == tarball_name:
159 continue
160 logging.info("Cleaning up old tarball: %s", p)
161 osutils.SafeUnlink(p)
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 return tarball_dest
Brian Harringb938c782012-02-29 15:14:38 -0800164
165
David James56e6c2c2012-10-24 23:54:41 -0700166def _SudoCommand():
Alex Klein1699fab2022-09-08 08:46:06 -0600167 """Get the 'sudo' command, along with all needed environment variables."""
David James56e6c2c2012-10-24 23:54:41 -0700168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 # Pass in the ENVIRONMENT_ALLOWLIST and ENV_PASSTHRU variables so that
170 # scripts in the chroot know what variables to pass through.
171 cmd = ["sudo"]
172 for key in constants.CHROOT_ENVIRONMENT_ALLOWLIST + constants.ENV_PASSTHRU:
173 value = os.environ.get(key)
174 if value is not None:
175 cmd += ["%s=%s" % (key, value)]
David James56e6c2c2012-10-24 23:54:41 -0700176
Alex Kleinef517832023-01-13 12:06:51 -0700177 # We keep PATH not for the chroot but for the re-exec & for programs we
178 # might run before we chroot into the SDK. The process that enters the SDK
179 # itself will take care of initializing PATH to the right value then. But
180 # we can't override the system's default PATH for root as that will hide
181 # /sbin.
Alex Klein1699fab2022-09-08 08:46:06 -0600182 cmd += ["CHROMEOS_SUDO_PATH=%s" % os.environ.get("PATH", "")]
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400183
Alex Klein1699fab2022-09-08 08:46:06 -0600184 # Pass along current rlimit settings so we can restore them.
Mike Frysinger53baf882021-06-24 23:16:50 -0400185 cmd += [f"CHROMEOS_SUDO_RLIMITS={cros_sdk_lib.ChrootEnteror.get_rlimits()}"]
Mike Frysinger12d055b2022-06-23 12:26:47 -0400186
Alex Klein1699fab2022-09-08 08:46:06 -0600187 # Pass in the path to the depot_tools so that users can access them from
188 # within the chroot.
Mike Frysingera95870e2023-03-27 15:45:34 -0400189 cmd += [f"DEPOT_TOOLS={constants.DEPOT_TOOLS_DIR}"]
Mike Frysinger749251e2014-01-29 05:04:27 -0500190
Alex Klein1699fab2022-09-08 08:46:06 -0600191 return cmd
David James56e6c2c2012-10-24 23:54:41 -0700192
193
Josh Triplett472a4182013-03-08 11:48:57 -0800194def _ReportMissing(missing):
Alex Klein1699fab2022-09-08 08:46:06 -0600195 """Report missing utilities, then exit.
Josh Triplett472a4182013-03-08 11:48:57 -0800196
Alex Klein1699fab2022-09-08 08:46:06 -0600197 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700198 missing: List of missing utilities, as returned by
199 osutils.FindMissingBinaries. If non-empty, will not return.
Alex Klein1699fab2022-09-08 08:46:06 -0600200 """
Josh Triplett472a4182013-03-08 11:48:57 -0800201
Alex Klein1699fab2022-09-08 08:46:06 -0600202 if missing:
203 raise SystemExit(
204 "The tool(s) %s were not found.\n"
205 "Please install the appropriate package in your host.\n"
206 "Example(ubuntu):\n"
207 " sudo apt-get install <packagename>" % ", ".join(missing)
208 )
Josh Triplett472a4182013-03-08 11:48:57 -0800209
210
211def _ProxySimSetup(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600212 """Set up proxy simulator, and return only in the child environment.
Josh Triplett472a4182013-03-08 11:48:57 -0800213
Alex Klein1699fab2022-09-08 08:46:06 -0600214 TODO: Ideally, this should support multiple concurrent invocations of
215 cros_sdk --proxy-sim; currently, such invocations will conflict with each
216 other due to the veth device names and IP addresses. Either this code would
217 need to generate fresh, unused names for all of these before forking, or it
218 would need to support multiple concurrent cros_sdk invocations sharing one
219 proxy and allowing it to exit when unused (without counting on any local
220 service-management infrastructure on the host).
221 """
Josh Triplett472a4182013-03-08 11:48:57 -0800222
Alex Klein1699fab2022-09-08 08:46:06 -0600223 may_need_mpm = False
224 apache_bin = osutils.Which("apache2")
Josh Triplett472a4182013-03-08 11:48:57 -0800225 if apache_bin is None:
Alex Klein1699fab2022-09-08 08:46:06 -0600226 apache_bin = osutils.Which("apache2", PROXY_APACHE_FALLBACK_PATH)
227 if apache_bin is None:
228 _ReportMissing(("apache2",))
229 else:
230 may_need_mpm = True
Josh Triplett472a4182013-03-08 11:48:57 -0800231
Alex Klein1699fab2022-09-08 08:46:06 -0600232 # Module names and .so names included for ease of grepping.
233 apache_modules = [
234 ("proxy_module", "mod_proxy.so"),
235 ("proxy_connect_module", "mod_proxy_connect.so"),
236 ("proxy_http_module", "mod_proxy_http.so"),
237 ("proxy_ftp_module", "mod_proxy_ftp.so"),
238 ]
Josh Triplett472a4182013-03-08 11:48:57 -0800239
Alex Kleinef517832023-01-13 12:06:51 -0700240 # Find the apache module directory and make sure it has the modules we need.
Alex Klein1699fab2022-09-08 08:46:06 -0600241 module_dirs = {}
242 for g in PROXY_APACHE_MODULE_GLOBS:
243 for _, so in apache_modules:
244 for f in glob.glob(os.path.join(g, so)):
245 module_dirs.setdefault(os.path.dirname(f), []).append(so)
246 for apache_module_path, modules_found in module_dirs.items():
247 if len(modules_found) == len(apache_modules):
248 break
249 else:
Alex Kleinef517832023-01-13 12:06:51 -0700250 # Appease cros lint, which doesn't understand that this else block will
251 # not fall through to the subsequent code which relies on
252 # apache_module_path.
Alex Klein1699fab2022-09-08 08:46:06 -0600253 apache_module_path = None
254 raise SystemExit(
Alex Kleinef517832023-01-13 12:06:51 -0700255 "Could not find apache module path containing all required "
256 "modules: %s" % ", ".join(so for mod, so in apache_modules)
Alex Klein1699fab2022-09-08 08:46:06 -0600257 )
Josh Triplett472a4182013-03-08 11:48:57 -0800258
Alex Klein1699fab2022-09-08 08:46:06 -0600259 def check_add_module(name):
260 so = "mod_%s.so" % name
261 if os.access(os.path.join(apache_module_path, so), os.F_OK):
262 mod = "%s_module" % name
263 apache_modules.append((mod, so))
264 return True
265 return False
Josh Triplett472a4182013-03-08 11:48:57 -0800266
Alex Klein1699fab2022-09-08 08:46:06 -0600267 check_add_module("authz_core")
268 if may_need_mpm:
269 for mpm in PROXY_APACHE_MPMS:
270 if check_add_module("mpm_%s" % mpm):
271 break
Josh Triplett472a4182013-03-08 11:48:57 -0800272
Alex Klein1699fab2022-09-08 08:46:06 -0600273 veth_host = "%s-host" % PROXY_VETH_PREFIX
274 veth_guest = "%s-guest" % PROXY_VETH_PREFIX
Josh Triplett472a4182013-03-08 11:48:57 -0800275
Alex Klein1699fab2022-09-08 08:46:06 -0600276 # Set up locks to sync the net namespace setup. We need the child to create
Alex Kleinef517832023-01-13 12:06:51 -0700277 # the net ns first, and then have the parent assign the guest end of the
278 # veth interface to the child's new network namespace & bring up the proxy.
279 # Only then can the child move forward and rely on the network being up.
Alex Klein1699fab2022-09-08 08:46:06 -0600280 ns_create_lock = locking.PipeLock()
281 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800282
Alex Klein1699fab2022-09-08 08:46:06 -0600283 pid = os.fork()
284 if not pid:
285 # Create our new isolated net namespace.
286 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500287
Alex Klein1699fab2022-09-08 08:46:06 -0600288 # Signal the parent the ns is ready to be configured.
289 ns_create_lock.Post()
290 del ns_create_lock
291
292 # Wait for the parent to finish setting up the ns/proxy.
293 ns_setup_lock.Wait()
294 del ns_setup_lock
295
296 # Set up child side of the network.
297 commands = (
298 ("ip", "link", "set", "up", "lo"),
299 (
300 "ip",
301 "address",
302 "add",
303 "%s/%u" % (PROXY_GUEST_IP, PROXY_NETMASK),
304 "dev",
305 veth_guest,
306 ),
307 ("ip", "link", "set", veth_guest, "up"),
308 )
309 try:
310 for cmd in commands:
311 cros_build_lib.dbg_run(cmd)
312 except cros_build_lib.RunCommandError as e:
313 cros_build_lib.Die("Proxy setup failed!\n%s", e)
314
315 proxy_url = "http://%s:%u" % (PROXY_HOST_IP, PROXY_PORT)
316 for proto in ("http", "https", "ftp"):
317 os.environ[proto + "_proxy"] = proxy_url
318 for v in ("all_proxy", "RSYNC_PROXY", "no_proxy"):
319 os.environ.pop(v, None)
320 return
321
322 # Set up parent side of the network.
323 uid = int(os.environ.get("SUDO_UID", "0"))
324 gid = int(os.environ.get("SUDO_GID", "0"))
325 if uid == 0 or gid == 0:
326 for username in PROXY_APACHE_FALLBACK_USERS:
327 try:
328 pwnam = pwd.getpwnam(username)
329 uid, gid = pwnam.pw_uid, pwnam.pw_gid
330 break
331 except KeyError:
332 continue
333 if uid == 0 or gid == 0:
334 raise SystemExit("Could not find a non-root user to run Apache as")
335
336 chroot_parent, chroot_base = os.path.split(options.chroot)
337 pid_file = os.path.join(chroot_parent, ".%s-apache-proxy.pid" % chroot_base)
338 log_file = os.path.join(chroot_parent, ".%s-apache-proxy.log" % chroot_base)
339
340 # Wait for the child to create the net ns.
341 ns_create_lock.Wait()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500342 del ns_create_lock
343
Alex Klein1699fab2022-09-08 08:46:06 -0600344 apache_directives = [
345 "User #%u" % uid,
346 "Group #%u" % gid,
347 "PidFile %s" % pid_file,
348 "ErrorLog %s" % log_file,
349 "Listen %s:%u" % (PROXY_HOST_IP, PROXY_PORT),
350 "ServerName %s" % PROXY_HOST_IP,
351 "ProxyRequests On",
352 "AllowCONNECT %s" % " ".join(str(x) for x in PROXY_CONNECT_PORTS),
353 ] + [
354 "LoadModule %s %s" % (mod, os.path.join(apache_module_path, so))
355 for (mod, so) in apache_modules
356 ]
357 commands = (
358 (
359 "ip",
360 "link",
361 "add",
362 "name",
363 veth_host,
364 "type",
365 "veth",
366 "peer",
367 "name",
368 veth_guest,
369 ),
370 (
371 "ip",
372 "address",
373 "add",
374 "%s/%u" % (PROXY_HOST_IP, PROXY_NETMASK),
375 "dev",
376 veth_host,
377 ),
378 ("ip", "link", "set", veth_host, "up"),
379 (
380 [apache_bin, "-f", "/dev/null"]
381 + [arg for d in apache_directives for arg in ("-C", d)]
382 ),
383 ("ip", "link", "set", veth_guest, "netns", str(pid)),
384 )
385 cmd = None # Make cros lint happy.
386 try:
387 for cmd in commands:
388 cros_build_lib.dbg_run(cmd)
389 except cros_build_lib.RunCommandError as e:
390 # Clean up existing interfaces, if any.
391 cmd_cleanup = ("ip", "link", "del", veth_host)
392 try:
393 cros_build_lib.run(cmd_cleanup, print_cmd=False)
394 except cros_build_lib.RunCommandError:
395 logging.error("running %r failed", cmd_cleanup)
396 cros_build_lib.Die("Proxy network setup failed!\n%s", e)
397
398 # Signal the child that the net ns/proxy is fully configured now.
399 ns_setup_lock.Post()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500400 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800401
Alex Klein1699fab2022-09-08 08:46:06 -0600402 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800403
404
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400405def _BuildReExecCommand(argv, opts) -> List[str]:
Alex Klein1699fab2022-09-08 08:46:06 -0600406 """Generate new command for self-reexec."""
407 # Make sure to preserve the active Python executable in case the version
408 # we're running as is not the default one found via the (new) $PATH.
409 cmd = _SudoCommand() + ["--"]
410 if opts.strace:
411 cmd += ["strace"] + shlex.split(opts.strace_arguments) + ["--"]
412 return cmd + [sys.executable] + argv
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400413
414
415def _ReExecuteIfNeeded(argv, opts):
Alex Klein1699fab2022-09-08 08:46:06 -0600416 """Re-execute cros_sdk as root.
David James56e6c2c2012-10-24 23:54:41 -0700417
Alex Klein1699fab2022-09-08 08:46:06 -0600418 Also unshare the mount namespace so as to ensure that processes outside
419 the chroot can't mess with our mounts.
420 """
421 if osutils.IsNonRootUser():
422 cmd = _BuildReExecCommand(argv, opts)
423 logging.debug(
424 "Reexecing self via sudo:\n%s", cros_build_lib.CmdToStr(cmd)
425 )
426 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700427
428
Mike Frysinger34db8692013-11-11 14:54:08 -0500429def _CreateParser(sdk_latest_version, bootstrap_latest_version):
Alex Klein1699fab2022-09-08 08:46:06 -0600430 """Generate and return the parser with all the options."""
431 usage = (
432 "usage: %(prog)s [options] "
433 "[VAR1=val1 ... VAR2=val2] [--] [command [args]]"
434 )
435 parser = commandline.ArgumentParser(
436 usage=usage, description=__doc__, caching=True
437 )
Brian Harring218e13c2012-10-10 16:21:26 -0700438
Alex Klein1699fab2022-09-08 08:46:06 -0600439 # Global options.
Alex Klein1699fab2022-09-08 08:46:06 -0600440 parser.add_argument(
441 "--chroot",
442 dest="chroot",
Brian Norrise1760452023-02-23 15:54:40 -0800443 default=constants.DEFAULT_CHROOT_PATH,
Alex Klein1699fab2022-09-08 08:46:06 -0600444 type="path",
445 help=("SDK chroot dir name [%s]" % constants.DEFAULT_CHROOT_DIR),
446 )
447 parser.add_argument(
Dan Callaghanada9e032023-01-30 14:28:46 +1100448 "--out-dir",
449 metavar="DIR",
Brian Norrise1760452023-02-23 15:54:40 -0800450 default=constants.DEFAULT_OUT_PATH,
Dan Callaghanada9e032023-01-30 14:28:46 +1100451 type=Path,
452 help="Use DIR for build state and output files",
453 )
454 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600455 "--nouse-image",
456 dest="use_image",
457 action="store_false",
458 default=False,
Brian Norris35a7ed02023-02-23 12:50:14 -0800459 deprecated="--[no]use-image is no longer supported (b/266878468).",
460 help=argparse.SUPPRESS,
Alex Klein1699fab2022-09-08 08:46:06 -0600461 )
462 parser.add_argument(
463 "--use-image",
464 dest="use_image",
465 action="store_true",
466 default=False,
Brian Norris35a7ed02023-02-23 12:50:14 -0800467 deprecated="--[no]use-image is no longer supported (b/266878468).",
468 help=argparse.SUPPRESS,
Alex Klein1699fab2022-09-08 08:46:06 -0600469 )
Brian Harringb938c782012-02-29 15:14:38 -0800470
Alex Klein1699fab2022-09-08 08:46:06 -0600471 parser.add_argument(
472 "--chrome-root",
473 "--chrome_root",
474 type="path",
475 help="Mount this chrome root into the SDK chroot",
476 )
477 parser.add_argument(
478 "--chrome_root_mount",
479 type="path",
480 help="Mount chrome into this path inside SDK chroot",
481 )
482 parser.add_argument(
483 "--nousepkg",
484 action="store_true",
485 default=False,
486 help="Do not use binary packages when creating a chroot.",
487 )
488 parser.add_argument(
489 "-u",
490 "--url",
491 dest="sdk_url",
492 help="Use sdk tarball located at this url. Use file:// "
493 "for local files.",
494 )
495 parser.add_argument(
496 "--sdk-version",
497 help=(
498 "Use this sdk version. For prebuilt, current is %r"
499 ", for bootstrapping it is %r."
500 % (sdk_latest_version, bootstrap_latest_version)
501 ),
502 )
503 parser.add_argument(
Mike Frysinger85824562023-01-31 02:40:43 -0500504 "--goma-dir",
Alex Klein1699fab2022-09-08 08:46:06 -0600505 "--goma_dir",
Mike Frysinger8fc83772023-02-01 15:30:47 -0500506 type="dir_exists",
Alex Klein1699fab2022-09-08 08:46:06 -0600507 help="Goma installed directory to mount into the chroot.",
508 )
509 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600510 "--reclient-dir",
Mike Frysinger8fc83772023-02-01 15:30:47 -0500511 type="dir_exists",
Alex Klein1699fab2022-09-08 08:46:06 -0600512 help="Reclient installed directory to mount into the chroot.",
513 )
514 parser.add_argument(
515 "--reproxy-cfg-file",
Mike Frysinger8fc83772023-02-01 15:30:47 -0500516 type="file_exists",
Alex Klein1699fab2022-09-08 08:46:06 -0600517 help="Config file for re-client's reproxy used for remoteexec.",
518 )
519 parser.add_argument(
520 "--skip-chroot-upgrade",
521 dest="chroot_upgrade",
522 action="store_false",
523 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700524 help="Skip automatic SDK and toolchain upgrade when entering the "
525 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600526 )
Brian Norris872684f2023-08-22 15:20:20 -0700527 parser.add_bool_argument(
528 "--delete-out-dir",
529 default=True,
530 enabled_desc="Delete the SDK build state along with the chroot. "
531 "Applies to --delete or --replace.",
532 disabled_desc="Don't delete the SDK build state along with the chroot. "
533 "Applies to --delete or --replace.",
534 )
Yong Hong84ba9172018-02-07 01:37:42 +0800535
Alex Klein1699fab2022-09-08 08:46:06 -0600536 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700537 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600538 parser.add_argument(
539 "--working-dir",
Mike Frysinger695f2fd2023-02-03 20:45:14 -0500540 type=Path,
Alex Klein1699fab2022-09-08 08:46:06 -0600541 help="Run the command in specific working directory in "
542 "chroot. If the given directory is a relative "
543 "path, this program will transfer the path to "
544 "the corresponding one inside chroot.",
545 )
Yong Hong84ba9172018-02-07 01:37:42 +0800546
Alex Klein1699fab2022-09-08 08:46:06 -0600547 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500548
Alex Klein1699fab2022-09-08 08:46:06 -0600549 # Commands.
550 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400551 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600552 "--enter",
553 action="store_true",
554 default=False,
555 help="Enter the SDK chroot. Implies --create.",
556 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400557 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600558 "--create",
559 action="store_true",
560 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700561 help="Create the chroot only if it does not already exist. Downloads "
562 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600563 )
564 group.add_argument(
565 "--bootstrap",
566 action="store_true",
567 default=False,
568 help="Build everything from scratch, including the sdk. "
569 "Use this only if you need to validate a change "
570 "that affects SDK creation itself (toolchain and "
571 "build are typically the only folk who need this). "
572 "Note this will quite heavily slow down the build. "
573 "This option implies --create --nousepkg.",
574 )
575 group.add_argument(
576 "-r",
577 "--replace",
578 action="store_true",
579 default=False,
580 help="Replace an existing SDK chroot. Basically an alias "
581 "for --delete --create.",
582 )
583 group.add_argument(
584 "--delete",
585 action="store_true",
586 default=False,
Brian Norris872684f2023-08-22 15:20:20 -0700587 help="Delete the current SDK chroot and build state if they exist.",
Alex Klein1699fab2022-09-08 08:46:06 -0600588 )
589 group.add_argument(
590 "--force",
591 action="store_true",
592 default=False,
Brian Norrisf624bf42023-03-02 12:57:49 -0800593 help="Force delete of the current SDK chroot even if "
Alex Klein1699fab2022-09-08 08:46:06 -0600594 "obtaining the write lock fails.",
595 )
596 group.add_argument(
597 "--unmount",
598 action="store_true",
599 default=False,
Brian Norrisf624bf42023-03-02 12:57:49 -0800600 deprecated="loopback-image (--use-image) is no longer supported "
601 "(b/266878468). If needed, consider `cros unmount /path/to/chroot`.",
602 help=argparse.SUPPRESS,
Alex Klein1699fab2022-09-08 08:46:06 -0600603 )
604 group.add_argument(
605 "--download",
606 action="store_true",
607 default=False,
608 help="Download the sdk.",
609 )
Alex Klein1699fab2022-09-08 08:46:06 -0600610 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -0400611
Alex Klein1699fab2022-09-08 08:46:06 -0600612 # Namespace options.
613 group = parser.add_argument_group("Namespaces")
614 group.add_argument(
615 "--proxy-sim",
616 action="store_true",
617 default=False,
618 help="Simulate a restrictive network requiring an outbound" " proxy.",
619 )
620 for ns, default in (("pid", True), ("net", None)):
621 group.add_argument(
622 f"--ns-{ns}",
623 default=default,
624 action="store_true",
625 help=f"Create a new {ns} namespace.",
626 )
627 group.add_argument(
628 f"--no-ns-{ns}",
629 dest=f"ns_{ns}",
630 action="store_false",
631 help=f"Do not create a new {ns} namespace.",
632 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400633
Alex Klein1699fab2022-09-08 08:46:06 -0600634 # Debug options.
635 group = parser.debug_group
636 group.add_argument(
637 "--strace",
638 action="store_true",
639 help="Run cros_sdk through strace after re-exec via sudo",
640 )
641 group.add_argument(
642 "--strace-arguments",
643 default="",
644 help="Extra strace options (shell quoting permitted)",
645 )
Mike Frysinger34db8692013-11-11 14:54:08 -0500646
Alex Klein1699fab2022-09-08 08:46:06 -0600647 # Internal options.
648 group = parser.add_argument_group(
649 "Internal Chromium OS Build Team Options",
650 "Caution: these are for meant for the Chromium OS build team only",
651 )
652 group.add_argument(
653 "--buildbot-log-version",
654 default=False,
655 action="store_true",
656 help="Log SDK version for buildbot consumption",
657 )
658
659 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500660
661
662def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600663 # Turn on strict sudo checks.
664 cros_build_lib.STRICT_SUDO = True
665 conf = key_value_store.LoadFile(
Greg Edelston0382ca42023-05-04 14:00:15 -0600666 constants.SDK_VERSION_FILE_FULL_PATH,
Alex Klein1699fab2022-09-08 08:46:06 -0600667 ignore_missing=True,
668 )
669 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
670 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -0700671
Alex Klein1699fab2022-09-08 08:46:06 -0600672 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
673 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
674 bootstrap_latest_version = (
675 sdk_latest_version
676 if bootstrap_frozen_version == "<unknown>"
677 else bootstrap_frozen_version
678 )
679 parser, commands = _CreateParser(
680 sdk_latest_version, bootstrap_latest_version
681 )
682 options = parser.parse_args(argv)
Mike Frysingerce3d78f2023-08-30 10:41:33 -0400683 options.out_dir = options.out_dir.resolve()
Brian Harringb938c782012-02-29 15:14:38 -0800684
Alex Klein1699fab2022-09-08 08:46:06 -0600685 # Some basic checks first, before we ask for sudo credentials.
686 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800687
Alex Klein1699fab2022-09-08 08:46:06 -0600688 host = os.uname()[4]
689 if host != "x86_64":
690 cros_build_lib.Die(
691 "cros_sdk is currently only supported on x86_64; you're running"
692 " %s. Please find a x86_64 machine." % (host,)
693 )
Brian Harring1790ac42012-09-23 08:53:33 -0700694
Brian Norriscf031912023-02-21 15:04:27 -0800695 goma = (
Brian Norris4f251e42023-03-09 15:53:26 -0800696 goma_lib.Goma(
697 options.goma_dir, chroot_dir=options.chroot, out_dir=options.out_dir
698 )
Brian Norriscf031912023-02-21 15:04:27 -0800699 if options.goma_dir
700 else None
701 )
Mike Frysinger9a5124e2023-01-26 17:16:44 -0500702
Alex Klein1699fab2022-09-08 08:46:06 -0600703 # Merge the outside PATH setting if we re-execed ourselves.
704 if "CHROMEOS_SUDO_PATH" in os.environ:
705 os.environ["PATH"] = "%s:%s" % (
706 os.environ.pop("CHROMEOS_SUDO_PATH"),
707 os.environ["PATH"],
708 )
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400709
Alex Klein1699fab2022-09-08 08:46:06 -0600710 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
711 if options.proxy_sim:
712 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Brian Harringb938c782012-02-29 15:14:38 -0800713
Alex Klein1699fab2022-09-08 08:46:06 -0600714 if (
715 sdk_latest_version == "<unknown>"
716 or bootstrap_latest_version == "<unknown>"
717 ):
718 cros_build_lib.Die(
719 "No SDK version was found. "
720 "Are you in a Chromium source tree instead of Chromium OS?\n\n"
721 "Please change to a directory inside your Chromium OS source tree\n"
722 "and retry. If you need to setup a Chromium OS source tree, see\n"
723 " https://dev.chromium.org/chromium-os/developer-guide"
724 )
Benjamin Gordon040a1162017-06-29 13:44:47 -0600725
Alex Klein1699fab2022-09-08 08:46:06 -0600726 _ReExecuteIfNeeded([sys.argv[0]] + argv, options)
David James471532c2013-01-21 10:23:31 -0800727
Alex Klein1699fab2022-09-08 08:46:06 -0600728 lock_path = os.path.dirname(options.chroot)
729 lock_path = os.path.join(
730 lock_path, ".%s_lock" % os.path.basename(options.chroot).lstrip(".")
731 )
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600732
Alex Klein1699fab2022-09-08 08:46:06 -0600733 # Expand out the aliases...
734 if options.replace:
735 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800736
Alex Klein1699fab2022-09-08 08:46:06 -0600737 if options.bootstrap:
738 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800739
Alex Klein1699fab2022-09-08 08:46:06 -0600740 # If a command is not given, default to enter.
741 # pylint: disable=protected-access
742 # This _group_actions access sucks, but upstream decided to not include an
743 # alternative to optparse's option_list, and this is what they recommend.
744 options.enter |= not any(
745 getattr(options, x.dest) for x in commands._group_actions
746 )
747 # pylint: enable=protected-access
Mike Frysinger114a7b92023-01-26 19:08:57 -0500748 options.enter |= bool(options.commands)
Brian Harring218e13c2012-10-10 16:21:26 -0700749
Brian Norris35a7ed02023-02-23 12:50:14 -0800750 if options.delete and not options.create and options.enter:
Alex Klein1699fab2022-09-08 08:46:06 -0600751 parser.error(
Brian Norris35a7ed02023-02-23 12:50:14 -0800752 "Trying to enter the chroot when --delete "
Alex Klein1699fab2022-09-08 08:46:06 -0600753 "was specified makes no sense."
754 )
Brian Harring218e13c2012-10-10 16:21:26 -0700755
Alex Klein1699fab2022-09-08 08:46:06 -0600756 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
Alex Klein1699fab2022-09-08 08:46:06 -0600757 # Finally, flip create if necessary.
Brian Norris35a7ed02023-02-23 12:50:14 -0800758 if options.enter:
Alex Klein1699fab2022-09-08 08:46:06 -0600759 options.create |= not chroot_exists
760
761 # Make sure we will download if we plan to create.
762 options.download |= options.create
763
Mike Frysinger6bbd3e92023-01-27 00:05:02 -0500764 options.Freeze()
765
Mike Frysingerded13922023-02-01 15:31:59 -0500766 if options.reclient_dir and not options.reproxy_cfg_file:
767 cros_build_lib.Die("--reclient-dir requires --reproxy-cfg-file")
768 if not options.reclient_dir and options.reproxy_cfg_file:
769 cros_build_lib.Die(
770 "--reproxy-cfg-file only makes sense with --reclient-dir"
771 )
772
Mike Frysinger253c8392023-01-27 01:12:21 -0500773 remoteexec = (
774 remoteexec_util.Remoteexec(
775 options.reclient_dir, options.reproxy_cfg_file
776 )
777 if (options.reclient_dir and options.reproxy_cfg_file)
778 else None
779 )
780
Mike Frysingerec32bea2023-01-27 01:20:48 -0500781 chroot = chroot_lib.Chroot(
782 path=options.chroot,
Brian Norris2adb74c2023-03-29 12:20:44 -0700783 out_path=options.out_dir,
Mike Frysingerec32bea2023-01-27 01:20:48 -0500784 cache_dir=options.cache_dir,
785 chrome_root=options.chrome_root,
786 goma=goma,
787 remoteexec=remoteexec,
788 )
789
Alex Kleinef517832023-01-13 12:06:51 -0700790 # Anything that needs to manipulate the main chroot mount or communicate
791 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -0600792
Alex Klein1699fab2022-09-08 08:46:06 -0600793 if options.delete:
794 # Set a timeout of 300 seconds when getting the lock.
795 with locking.FileLock(
796 lock_path, "chroot lock", blocking_timeout=300
797 ) as lock:
798 try:
799 lock.write_lock()
800 except timeout_util.TimeoutError as e:
801 logging.error(
802 "Acquiring write_lock on %s failed: %s", lock_path, e
803 )
804 if not options.force:
805 cros_build_lib.Die(
806 "Exiting; use --force to continue w/o lock."
807 )
808 else:
809 logging.warning(
810 "cros_sdk was invoked with force option, continuing."
811 )
Mike Frysingerbeb40d82023-08-30 10:42:22 -0400812 logging.notice("Deleting chroot: %s", chroot.path)
813 logging.notice(
814 "%s output dir: %s",
815 "Deleting" if options.delete_out_dir else "Keeping",
816 chroot.out_path,
817 )
Brian Norris872684f2023-08-22 15:20:20 -0700818 cros_sdk_lib.CleanupChrootMount(
819 chroot, delete=True, delete_out=options.delete_out_dir
820 )
Alex Klein1699fab2022-09-08 08:46:06 -0600821
Alex Kleinef517832023-01-13 12:06:51 -0700822 # Enter a new set of namespaces. Everything after here cannot directly
823 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -0600824 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600825
Alex Klein1699fab2022-09-08 08:46:06 -0600826 if not options.sdk_version:
827 sdk_version = (
828 bootstrap_latest_version
829 if options.bootstrap
830 else sdk_latest_version
831 )
Yong Hong4e29b622018-02-05 14:31:10 +0800832 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600833 sdk_version = options.sdk_version
834 if options.buildbot_log_version:
835 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -0700836
Alex Klein1699fab2022-09-08 08:46:06 -0600837 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -0700838 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -0500839 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -0600840 if options.sdk_url:
841 urls = [options.sdk_url]
842 else:
843 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -0700844
Alex Klein1699fab2022-09-08 08:46:06 -0600845 with locking.FileLock(lock_path, "chroot lock") as lock:
846 if options.proxy_sim:
847 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -0700848
Mike Frysingerec32bea2023-01-27 01:20:48 -0500849 sdk_cache = os.path.join(chroot.cache_dir, "sdks")
850 distfiles_cache = os.path.join(chroot.cache_dir, "distfiles")
851 osutils.SafeMakedirsNonRoot(chroot.cache_dir)
Dan Callaghanada9e032023-01-30 14:28:46 +1100852 osutils.SafeMakedirsNonRoot(options.out_dir)
Brian Norris5e4e37b2023-04-03 14:38:33 -0700853 # Create here (and not, say, in cros_sdk_lib.MountChrootPaths())
854 # because some usages want to create tmp files here even before we've
855 # fully mounted the SDK.
856 osutils.SafeMakedirsNonRoot(options.out_dir / "tmp", mode=0o777)
Alex Klein1699fab2022-09-08 08:46:06 -0600857
858 for target in (sdk_cache, distfiles_cache):
859 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
860 if not os.path.exists(src):
861 osutils.SafeMakedirsNonRoot(target)
862 continue
863 lock.write_lock(
864 "Upgrade to %r needed but chroot is locked; please exit "
865 "all instances so this upgrade can finish." % src
866 )
867 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -0700868 # Note that while waiting for the write lock, src may've
869 # vanished; it's a rare race during the upgrade process that's a
870 # byproduct of us avoiding taking a write lock to do the src
871 # check. If we took a write lock for that check, it would
872 # effectively limit all cros_sdk for a chroot to a single
873 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -0600874 osutils.SafeMakedirsNonRoot(target)
875 elif not os.path.exists(target):
876 # Upgrade occurred, but a reversion, or something whacky
877 # occurred writing to the old location. Wipe and continue.
878 os.rename(src, target)
879 else:
880 # Upgrade occurred once already, but either a reversion or
881 # some before/after separate cros_sdk usage is at play.
882 # Wipe and continue.
883 osutils.RmDir(src)
884
Brian Norrisc2b71e42023-03-30 15:27:36 -0700885 cros_sdk_lib.MigrateStatePaths(chroot, lock)
886
Alex Klein1699fab2022-09-08 08:46:06 -0600887 mounted = False
888 if options.create:
889 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -0700890 # Recheck if the chroot is set up here before creating to make sure
Brian Norrisf624bf42023-03-02 12:57:49 -0800891 # we account for whatever the various delete/cleanup steps above
892 # have done.
Mike Frysingerec32bea2023-01-27 01:20:48 -0500893 if cros_sdk_lib.IsChrootReady(chroot.path):
Alex Klein1699fab2022-09-08 08:46:06 -0600894 logging.debug("Chroot already exists. Skipping creation.")
895 else:
Alex Klein22690a12022-11-17 10:56:09 -0700896 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -0600897 cros_sdk_lib.CreateChroot(
Brian Norris7f10df82023-08-22 13:38:54 -0700898 chroot,
Alex Klein1699fab2022-09-08 08:46:06 -0600899 Path(sdk_tarball),
Alex Klein1699fab2022-09-08 08:46:06 -0600900 usepkg=not options.bootstrap and not options.nousepkg,
901 chroot_upgrade=options.chroot_upgrade,
902 )
903 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -0700904 elif options.download:
905 # Allow downloading only.
906 lock.write_lock()
907 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -0600908
909 if options.enter:
910 lock.read_lock()
911 if not mounted:
Brian Norris7f10df82023-08-22 13:38:54 -0700912 cros_sdk_lib.MountChrootPaths(chroot)
Mike Frysinger53baf882021-06-24 23:16:50 -0400913 ret = cros_sdk_lib.EnterChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -0500914 chroot,
Mike Frysinger53baf882021-06-24 23:16:50 -0400915 chrome_root_mount=options.chrome_root_mount,
916 cwd=options.working_dir,
917 cmd=options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -0600918 )
919 sys.exit(ret.returncode)