blob: f662912ab5c23c8153a3048ba009c4dd85df8725 [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
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060020import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070021import re
Mike Frysinger5f5c70b2022-07-19 12:55:21 -040022import shlex
Sergey Frolov1cb46ec2020-12-09 21:46:16 -070023import subprocess
David James56e6c2c2012-10-24 23:54:41 -070024import sys
Mike Frysingerec32bea2023-01-27 01:20:48 -050025from typing import List
Mike Frysingere852b072021-05-21 12:39:03 -040026import urllib.parse
Brian Harringb938c782012-02-29 15:14:38 -080027
Chris McDonaldb55b7032021-06-17 16:41:32 -060028from chromite.cbuildbot import cbuildbot_alerts
Mike Frysingerec32bea2023-01-27 01:20:48 -050029from chromite.lib import chroot_lib
Brian Harringb6cf9142012-09-01 20:43:17 -070030from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060031from chromite.lib import constants
Brian Harringb938c782012-02-29 15:14:38 -080032from chromite.lib import cros_build_lib
Benjamin Gordon74645232018-05-04 17:40:42 -060033from chromite.lib import cros_sdk_lib
Mike Frysinger9a5124e2023-01-26 17:16:44 -050034from chromite.lib import goma_lib
Brian Harringb938c782012-02-29 15:14:38 -080035from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080036from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070037from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080038from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040039from chromite.lib import process_util
Mike Frysinger253c8392023-01-27 01:12:21 -050040from chromite.lib import remoteexec_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080041from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060042from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050043from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040044from chromite.utils import key_value_store
45
Brian Harringb938c782012-02-29 15:14:38 -080046
Mike Frysingerf744d032022-05-07 20:38:39 -040047# Which compression algos the SDK tarball uses. We've used xz since 2012.
Alex Klein1699fab2022-09-08 08:46:06 -060048COMPRESSION_PREFERENCE = ("xz",)
Zdenek Behanfd0efe42012-04-13 04:36:40 +020049
Josh Triplett472a4182013-03-08 11:48:57 -080050# Proxy simulator configuration.
Alex Klein1699fab2022-09-08 08:46:06 -060051PROXY_HOST_IP = "192.168.240.1"
Josh Triplett472a4182013-03-08 11:48:57 -080052PROXY_PORT = 8080
Alex Klein1699fab2022-09-08 08:46:06 -060053PROXY_GUEST_IP = "192.168.240.2"
Josh Triplett472a4182013-03-08 11:48:57 -080054PROXY_NETMASK = 30
Alex Klein1699fab2022-09-08 08:46:06 -060055PROXY_VETH_PREFIX = "veth"
Josh Triplett472a4182013-03-08 11:48:57 -080056PROXY_CONNECT_PORTS = (80, 443, 9418)
Alex Klein1699fab2022-09-08 08:46:06 -060057PROXY_APACHE_FALLBACK_USERS = ("www-data", "apache", "nobody")
58PROXY_APACHE_MPMS = ("event", "worker", "prefork")
59PROXY_APACHE_FALLBACK_PATH = ":".join(
60 "/usr/lib/apache2/mpm-%s" % mpm for mpm in PROXY_APACHE_MPMS
61)
62PROXY_APACHE_MODULE_GLOBS = ("/usr/lib*/apache2/modules", "/usr/lib*/apache2")
Josh Triplett472a4182013-03-08 11:48:57 -080063
Josh Triplett9a495f62013-03-15 18:06:55 -070064# We need these tools to run. Very common tools (tar,..) are omitted.
Alex Klein1699fab2022-09-08 08:46:06 -060065NEEDED_TOOLS = ("curl", "xz")
Brian Harringb938c782012-02-29 15:14:38 -080066
Josh Triplett472a4182013-03-08 11:48:57 -080067# Tools needed for --proxy-sim only.
Alex Klein1699fab2022-09-08 08:46:06 -060068PROXY_NEEDED_TOOLS = ("ip",)
Brian Harringb938c782012-02-29 15:14:38 -080069
Mike Frysingerc60141b2023-01-27 00:57:15 -050070# Tools needed when use_image is true.
Alex Klein1699fab2022-09-08 08:46:06 -060071IMAGE_NEEDED_TOOLS = (
72 "losetup",
73 "lvchange",
74 "lvcreate",
75 "lvs",
76 "mke2fs",
77 "pvscan",
78 "thin_check",
79 "vgchange",
80 "vgcreate",
81 "vgs",
82)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060083
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070084# As space is used inside the chroot, the empty space in chroot.img is
85# allocated. Deleting files inside the chroot doesn't automatically return the
86# used space to the OS. Over time, this tends to make the sparse chroot.img
87# less sparse even if the chroot contents don't currently need much space. We
88# can recover most of this unused space with fstrim, but that takes too much
89# time to run it every time. Instead, check the used space against the image
90# size after mounting the chroot and only call fstrim if it looks like we could
91# recover at least this many GiB.
92MAX_UNUSED_IMAGE_GBS = 20
93
Mike Frysingercc838832014-05-24 13:10:30 -040094
Brian Harring1790ac42012-09-23 08:53:33 -070095def GetArchStageTarballs(version):
Alex Klein1699fab2022-09-08 08:46:06 -060096 """Returns the URL for a given arch/version"""
97 extension = {"xz": "tar.xz"}
98 return [
99 toolchain.GetSdkURL(
100 suburl="cros-sdk-%s.%s" % (version, extension[compressor])
101 )
102 for compressor in COMPRESSION_PREFERENCE
103 ]
Brian Harring1790ac42012-09-23 08:53:33 -0700104
105
Mike Frysingerf744d032022-05-07 20:38:39 -0400106def FetchRemoteTarballs(storage_dir, urls):
Alex Klein1699fab2022-09-08 08:46:06 -0600107 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200108
Alex Klein1699fab2022-09-08 08:46:06 -0600109 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700110 storage_dir: Path where to save the tarball.
111 urls: List of URLs to try to download. Download will stop on first
112 success.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200113
Alex Klein1699fab2022-09-08 08:46:06 -0600114 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700115 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700116
Alex Klein1699fab2022-09-08 08:46:06 -0600117 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700118 ValueError: None of the URLs worked.
Alex Klein1699fab2022-09-08 08:46:06 -0600119 """
120 # Note we track content length ourselves since certain versions of curl
121 # fail if asked to resume a complete file.
122 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
123 status_re = re.compile(rb"^HTTP/[0-9]+(\.[0-9]+)? 200")
124 # pylint: disable=undefined-loop-variable
125 for url in urls:
126 logging.notice("Downloading tarball %s ...", urls[0].rsplit("/", 1)[-1])
127 parsed = urllib.parse.urlparse(url)
128 tarball_name = os.path.basename(parsed.path)
129 if parsed.scheme in ("", "file"):
130 if os.path.exists(parsed.path):
131 return parsed.path
132 continue
133 content_length = 0
134 logging.debug("Attempting download from %s", url)
135 result = retry_util.RunCurl(
136 ["-I", url],
137 print_cmd=False,
138 debug_level=logging.NOTICE,
139 capture_output=True,
140 )
141 successful = False
142 for header in result.stdout.splitlines():
143 # We must walk the output to find the 200 code for use cases where
144 # a proxy is involved and may have pushed down the actual header.
145 if status_re.match(header):
146 successful = True
147 elif header.lower().startswith(b"content-length:"):
148 content_length = int(header.split(b":", 1)[-1].strip())
149 if successful:
150 break
Brian Harring1790ac42012-09-23 08:53:33 -0700151 if successful:
Alex Klein1699fab2022-09-08 08:46:06 -0600152 break
153 else:
154 raise ValueError("No valid URLs found!")
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200155
Alex Klein1699fab2022-09-08 08:46:06 -0600156 tarball_dest = os.path.join(storage_dir, tarball_name)
157 current_size = 0
158 if os.path.exists(tarball_dest):
159 current_size = os.path.getsize(tarball_dest)
160 if current_size > content_length:
161 osutils.SafeUnlink(tarball_dest)
162 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100163
Alex Klein1699fab2022-09-08 08:46:06 -0600164 if current_size < content_length:
165 retry_util.RunCurl(
166 [
167 "--fail",
168 "-L",
169 "-y",
170 "30",
171 "-C",
172 "-",
173 "--output",
174 tarball_dest,
175 url,
176 ],
177 print_cmd=False,
178 debug_level=logging.NOTICE,
179 )
Brian Harringb938c782012-02-29 15:14:38 -0800180
Alex Klein1699fab2022-09-08 08:46:06 -0600181 # Cleanup old tarballs now since we've successfull fetched; only cleanup
182 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
183 # because we might have partial overlap between known prefixes.
184 for p in Path(storage_dir).glob("cros-sdk-*"):
185 if p.name == tarball_name:
186 continue
187 logging.info("Cleaning up old tarball: %s", p)
188 osutils.SafeUnlink(p)
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200189
Alex Klein1699fab2022-09-08 08:46:06 -0600190 return tarball_dest
Brian Harringb938c782012-02-29 15:14:38 -0800191
192
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600193def _ImageFileForChroot(chroot):
Alex Klein1699fab2022-09-08 08:46:06 -0600194 """Find the image file that should be associated with |chroot|.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600195
Alex Klein1699fab2022-09-08 08:46:06 -0600196 This function does not check if the image exists; it simply returns the
197 filename that would be used.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600198
Alex Klein1699fab2022-09-08 08:46:06 -0600199 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700200 chroot: Path to the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600201
Alex Klein1699fab2022-09-08 08:46:06 -0600202 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700203 Path to an image file that would be associated with chroot.
Alex Klein1699fab2022-09-08 08:46:06 -0600204 """
205 return chroot.rstrip("/") + ".img"
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600206
207
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600208def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600209 """Create a snapshot for the specified chroot VG/LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600210
Alex Klein1699fab2022-09-08 08:46:06 -0600211 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700212 snapshot_name: The name of the new snapshot.
213 chroot_vg: The name of the VG containing the origin LV.
214 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600215
Alex Klein1699fab2022-09-08 08:46:06 -0600216 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700217 True if the snapshot was created, or False if a snapshot with the same
218 name already exists.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600219
Alex Klein1699fab2022-09-08 08:46:06 -0600220 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700221 SystemExit: The lvcreate command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600222 """
223 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
224 logging.error(
225 "Cannot create snapshot %s: A volume with that name already "
226 "exists.",
227 snapshot_name,
228 )
229 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600230
Alex Klein1699fab2022-09-08 08:46:06 -0600231 cmd = [
232 "lvcreate",
233 "-s",
234 "--name",
235 snapshot_name,
236 "%s/%s" % (chroot_vg, chroot_lv),
237 ]
238 try:
239 logging.notice(
240 "Creating snapshot %s from %s in VG %s.",
241 snapshot_name,
242 chroot_lv,
243 chroot_vg,
244 )
245 cros_build_lib.dbg_run(cmd, capture_output=True)
246 return True
247 except cros_build_lib.RunCommandError as e:
248 cros_build_lib.Die("Creating snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600249
250
251def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600252 """Delete the named snapshot from the specified chroot VG.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600253
Alex Klein1699fab2022-09-08 08:46:06 -0600254 If the requested snapshot is not found, nothing happens. The main chroot LV
255 and internal thinpool LV cannot be deleted with this function.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600256
Alex Klein1699fab2022-09-08 08:46:06 -0600257 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700258 snapshot_name: The name of the snapshot to delete.
259 chroot_vg: The name of the VG containing the origin LV.
260 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600261
Alex Klein1699fab2022-09-08 08:46:06 -0600262 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700263 SystemExit: The lvremove command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600264 """
265 if snapshot_name in (
266 cros_sdk_lib.CHROOT_LV_NAME,
267 cros_sdk_lib.CHROOT_THINPOOL_NAME,
268 ):
269 logging.error(
270 "Cannot remove LV %s as a snapshot. Use cros_sdk --delete "
271 "if you want to remove the whole chroot.",
272 snapshot_name,
273 )
274 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600275
Alex Klein1699fab2022-09-08 08:46:06 -0600276 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
277 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600278
Alex Klein1699fab2022-09-08 08:46:06 -0600279 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, snapshot_name)]
280 try:
281 logging.notice(
282 "Deleting snapshot %s in VG %s.", snapshot_name, chroot_vg
283 )
284 cros_build_lib.dbg_run(cmd, capture_output=True)
285 except cros_build_lib.RunCommandError as e:
286 cros_build_lib.Die("Deleting snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600287
288
289def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600290 """Restore the chroot to an existing snapshot.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600291
Alex Klein1699fab2022-09-08 08:46:06 -0600292 This is done by renaming the original |chroot_lv| LV to a temporary name,
293 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
294 now unused LV. If an error occurs, attempts to rename the original snapshot
295 back to |chroot_lv| to leave the chroot unchanged.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600296
Alex Klein1699fab2022-09-08 08:46:06 -0600297 The chroot must be unmounted before calling this function, and will be left
298 unmounted after this function returns.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600299
Alex Klein1699fab2022-09-08 08:46:06 -0600300 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700301 snapshot_name: The name of the snapshot to restore. This snapshot will
302 no longer be accessible at its original name after this function
303 finishes.
304 chroot_vg: The VG containing the chroot LV and snapshot LV.
305 chroot_lv: The name of the original chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600306
Alex Klein1699fab2022-09-08 08:46:06 -0600307 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700308 True if the chroot was restored to the requested snapshot, or False if
309 the snapshot wasn't found or isn't valid.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600310
Alex Klein1699fab2022-09-08 08:46:06 -0600311 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700312 SystemExit: Any of the LVM commands failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600313 """
314 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
315 if (
316 snapshot_name
317 in (cros_sdk_lib.CHROOT_LV_NAME, cros_sdk_lib.CHROOT_THINPOOL_NAME)
318 or snapshot_name not in valid_snapshots
319 ):
320 logging.error(
321 "Chroot cannot be restored to %s. Valid snapshots: %s",
322 snapshot_name,
323 ", ".join(valid_snapshots),
324 )
325 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 backup_chroot_name = "chroot-bak-%d" % random.randint(0, 1000)
328 cmd = ["lvrename", chroot_vg, chroot_lv, backup_chroot_name]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600329 try:
Alex Klein1699fab2022-09-08 08:46:06 -0600330 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500331 except cros_build_lib.RunCommandError as e:
Alex Klein1699fab2022-09-08 08:46:06 -0600332 cros_build_lib.Die("Restoring snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600333
Alex Klein1699fab2022-09-08 08:46:06 -0600334 cmd = ["lvrename", chroot_vg, snapshot_name, chroot_lv]
335 try:
336 cros_build_lib.dbg_run(cmd, capture_output=True)
337 except cros_build_lib.RunCommandError as e:
338 cmd = ["lvrename", chroot_vg, backup_chroot_name, chroot_lv]
339 try:
340 cros_build_lib.dbg_run(cmd, capture_output=True)
341 except cros_build_lib.RunCommandError as e:
342 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -0700343 "Failed to rename %s to chroot and failed to restore %s back "
344 "to chroot!\n%s",
Alex Klein1699fab2022-09-08 08:46:06 -0600345 snapshot_name,
346 backup_chroot_name,
347 e,
348 )
349 cros_build_lib.Die(
350 "Failed to rename %s to chroot! Original chroot LV has "
351 "been restored.\n%s",
352 snapshot_name,
353 e,
354 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600355
Alex Klein1699fab2022-09-08 08:46:06 -0600356 # Some versions of LVM set snapshots to be skipped at auto-activate time.
357 # Other versions don't have this flag at all. We run lvchange to try
Alex Kleinef517832023-01-13 12:06:51 -0700358 # disabling auto-skip and activating the volume, but ignore errors. Versions
Alex Klein1699fab2022-09-08 08:46:06 -0600359 # that don't have the flag should be auto-activated.
360 chroot_lv_path = "%s/%s" % (chroot_vg, chroot_lv)
361 cmd = ["lvchange", "-kn", chroot_lv_path]
362 cros_build_lib.run(cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600363
Alex Klein1699fab2022-09-08 08:46:06 -0600364 # Activate the LV in case the lvchange above was needed. Activating an LV
Alex Kleinef517832023-01-13 12:06:51 -0700365 # that is already active shouldn't do anything, so this is safe to run even
366 # if the -kn wasn't needed.
Alex Klein1699fab2022-09-08 08:46:06 -0600367 cmd = ["lvchange", "-ay", chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500368 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600369
Alex Klein1699fab2022-09-08 08:46:06 -0600370 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, backup_chroot_name)]
371 try:
372 cros_build_lib.dbg_run(cmd, capture_output=True)
373 except cros_build_lib.RunCommandError as e:
374 cros_build_lib.Die(
375 "Failed to remove backup LV %s/%s!\n%s",
376 chroot_vg,
377 backup_chroot_name,
378 e,
379 )
380
381 return True
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600382
383
384def ListChrootSnapshots(chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600385 """Return all snapshots in |chroot_vg| regardless of origin volume.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600386
Alex Klein1699fab2022-09-08 08:46:06 -0600387 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700388 chroot_vg: The name of the VG containing the chroot.
389 chroot_lv: The name of the chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600390
Alex Klein1699fab2022-09-08 08:46:06 -0600391 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700392 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600393
Alex Klein1699fab2022-09-08 08:46:06 -0600394 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700395 SystemExit: The lvs command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600396 """
397 if not chroot_vg or not chroot_lv:
398 return []
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600399
Alex Klein1699fab2022-09-08 08:46:06 -0600400 cmd = [
401 "lvs",
402 "-o",
403 "lv_name,pool_lv,lv_attr",
404 "-O",
405 "lv_name",
406 "--noheadings",
407 "--separator",
408 "\t",
409 chroot_vg,
410 ]
411 try:
412 result = cros_build_lib.run(
413 cmd, print_cmd=False, stdout=True, encoding="utf-8"
414 )
415 except cros_build_lib.RunCommandError:
416 raise SystemExit("Running %r failed!" % cmd)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600417
Alex Klein1699fab2022-09-08 08:46:06 -0600418 # Once the thin origin volume has been deleted, there's no way to tell a
Alex Kleinef517832023-01-13 12:06:51 -0700419 # snapshot apart from any other volume. Since this VG is created and managed
420 # by cros_sdk, we'll assume that all volumes that share the same thin pool
421 # are valid snapshots.
Alex Klein1699fab2022-09-08 08:46:06 -0600422 snapshots = []
423 snapshot_attrs = re.compile(r"^V.....t.{2,}") # Matches a thin volume.
424 for line in result.stdout.splitlines():
425 lv_name, pool_lv, lv_attr = line.lstrip().split("\t")
426 if (
427 lv_name == chroot_lv
428 or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME
429 or pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME
430 or not snapshot_attrs.match(lv_attr)
431 ):
432 continue
433 snapshots.append(lv_name)
434 return snapshots
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600435
436
David James56e6c2c2012-10-24 23:54:41 -0700437def _SudoCommand():
Alex Klein1699fab2022-09-08 08:46:06 -0600438 """Get the 'sudo' command, along with all needed environment variables."""
David James56e6c2c2012-10-24 23:54:41 -0700439
Alex Klein1699fab2022-09-08 08:46:06 -0600440 # Pass in the ENVIRONMENT_ALLOWLIST and ENV_PASSTHRU variables so that
441 # scripts in the chroot know what variables to pass through.
442 cmd = ["sudo"]
443 for key in constants.CHROOT_ENVIRONMENT_ALLOWLIST + constants.ENV_PASSTHRU:
444 value = os.environ.get(key)
445 if value is not None:
446 cmd += ["%s=%s" % (key, value)]
David James56e6c2c2012-10-24 23:54:41 -0700447
Alex Kleinef517832023-01-13 12:06:51 -0700448 # We keep PATH not for the chroot but for the re-exec & for programs we
449 # might run before we chroot into the SDK. The process that enters the SDK
450 # itself will take care of initializing PATH to the right value then. But
451 # we can't override the system's default PATH for root as that will hide
452 # /sbin.
Alex Klein1699fab2022-09-08 08:46:06 -0600453 cmd += ["CHROMEOS_SUDO_PATH=%s" % os.environ.get("PATH", "")]
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400454
Alex Klein1699fab2022-09-08 08:46:06 -0600455 # Pass along current rlimit settings so we can restore them.
Mike Frysinger53baf882021-06-24 23:16:50 -0400456 cmd += [f"CHROMEOS_SUDO_RLIMITS={cros_sdk_lib.ChrootEnteror.get_rlimits()}"]
Mike Frysinger12d055b2022-06-23 12:26:47 -0400457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 # Pass in the path to the depot_tools so that users can access them from
459 # within the chroot.
460 cmd += ["DEPOT_TOOLS=%s" % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500461
Alex Klein1699fab2022-09-08 08:46:06 -0600462 return cmd
David James56e6c2c2012-10-24 23:54:41 -0700463
464
Josh Triplett472a4182013-03-08 11:48:57 -0800465def _ReportMissing(missing):
Alex Klein1699fab2022-09-08 08:46:06 -0600466 """Report missing utilities, then exit.
Josh Triplett472a4182013-03-08 11:48:57 -0800467
Alex Klein1699fab2022-09-08 08:46:06 -0600468 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700469 missing: List of missing utilities, as returned by
470 osutils.FindMissingBinaries. If non-empty, will not return.
Alex Klein1699fab2022-09-08 08:46:06 -0600471 """
Josh Triplett472a4182013-03-08 11:48:57 -0800472
Alex Klein1699fab2022-09-08 08:46:06 -0600473 if missing:
474 raise SystemExit(
475 "The tool(s) %s were not found.\n"
476 "Please install the appropriate package in your host.\n"
477 "Example(ubuntu):\n"
478 " sudo apt-get install <packagename>" % ", ".join(missing)
479 )
Josh Triplett472a4182013-03-08 11:48:57 -0800480
481
482def _ProxySimSetup(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600483 """Set up proxy simulator, and return only in the child environment.
Josh Triplett472a4182013-03-08 11:48:57 -0800484
Alex Klein1699fab2022-09-08 08:46:06 -0600485 TODO: Ideally, this should support multiple concurrent invocations of
486 cros_sdk --proxy-sim; currently, such invocations will conflict with each
487 other due to the veth device names and IP addresses. Either this code would
488 need to generate fresh, unused names for all of these before forking, or it
489 would need to support multiple concurrent cros_sdk invocations sharing one
490 proxy and allowing it to exit when unused (without counting on any local
491 service-management infrastructure on the host).
492 """
Josh Triplett472a4182013-03-08 11:48:57 -0800493
Alex Klein1699fab2022-09-08 08:46:06 -0600494 may_need_mpm = False
495 apache_bin = osutils.Which("apache2")
Josh Triplett472a4182013-03-08 11:48:57 -0800496 if apache_bin is None:
Alex Klein1699fab2022-09-08 08:46:06 -0600497 apache_bin = osutils.Which("apache2", PROXY_APACHE_FALLBACK_PATH)
498 if apache_bin is None:
499 _ReportMissing(("apache2",))
500 else:
501 may_need_mpm = True
Josh Triplett472a4182013-03-08 11:48:57 -0800502
Alex Klein1699fab2022-09-08 08:46:06 -0600503 # Module names and .so names included for ease of grepping.
504 apache_modules = [
505 ("proxy_module", "mod_proxy.so"),
506 ("proxy_connect_module", "mod_proxy_connect.so"),
507 ("proxy_http_module", "mod_proxy_http.so"),
508 ("proxy_ftp_module", "mod_proxy_ftp.so"),
509 ]
Josh Triplett472a4182013-03-08 11:48:57 -0800510
Alex Kleinef517832023-01-13 12:06:51 -0700511 # Find the apache module directory and make sure it has the modules we need.
Alex Klein1699fab2022-09-08 08:46:06 -0600512 module_dirs = {}
513 for g in PROXY_APACHE_MODULE_GLOBS:
514 for _, so in apache_modules:
515 for f in glob.glob(os.path.join(g, so)):
516 module_dirs.setdefault(os.path.dirname(f), []).append(so)
517 for apache_module_path, modules_found in module_dirs.items():
518 if len(modules_found) == len(apache_modules):
519 break
520 else:
Alex Kleinef517832023-01-13 12:06:51 -0700521 # Appease cros lint, which doesn't understand that this else block will
522 # not fall through to the subsequent code which relies on
523 # apache_module_path.
Alex Klein1699fab2022-09-08 08:46:06 -0600524 apache_module_path = None
525 raise SystemExit(
Alex Kleinef517832023-01-13 12:06:51 -0700526 "Could not find apache module path containing all required "
527 "modules: %s" % ", ".join(so for mod, so in apache_modules)
Alex Klein1699fab2022-09-08 08:46:06 -0600528 )
Josh Triplett472a4182013-03-08 11:48:57 -0800529
Alex Klein1699fab2022-09-08 08:46:06 -0600530 def check_add_module(name):
531 so = "mod_%s.so" % name
532 if os.access(os.path.join(apache_module_path, so), os.F_OK):
533 mod = "%s_module" % name
534 apache_modules.append((mod, so))
535 return True
536 return False
Josh Triplett472a4182013-03-08 11:48:57 -0800537
Alex Klein1699fab2022-09-08 08:46:06 -0600538 check_add_module("authz_core")
539 if may_need_mpm:
540 for mpm in PROXY_APACHE_MPMS:
541 if check_add_module("mpm_%s" % mpm):
542 break
Josh Triplett472a4182013-03-08 11:48:57 -0800543
Alex Klein1699fab2022-09-08 08:46:06 -0600544 veth_host = "%s-host" % PROXY_VETH_PREFIX
545 veth_guest = "%s-guest" % PROXY_VETH_PREFIX
Josh Triplett472a4182013-03-08 11:48:57 -0800546
Alex Klein1699fab2022-09-08 08:46:06 -0600547 # Set up locks to sync the net namespace setup. We need the child to create
Alex Kleinef517832023-01-13 12:06:51 -0700548 # the net ns first, and then have the parent assign the guest end of the
549 # veth interface to the child's new network namespace & bring up the proxy.
550 # Only then can the child move forward and rely on the network being up.
Alex Klein1699fab2022-09-08 08:46:06 -0600551 ns_create_lock = locking.PipeLock()
552 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800553
Alex Klein1699fab2022-09-08 08:46:06 -0600554 pid = os.fork()
555 if not pid:
556 # Create our new isolated net namespace.
557 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500558
Alex Klein1699fab2022-09-08 08:46:06 -0600559 # Signal the parent the ns is ready to be configured.
560 ns_create_lock.Post()
561 del ns_create_lock
562
563 # Wait for the parent to finish setting up the ns/proxy.
564 ns_setup_lock.Wait()
565 del ns_setup_lock
566
567 # Set up child side of the network.
568 commands = (
569 ("ip", "link", "set", "up", "lo"),
570 (
571 "ip",
572 "address",
573 "add",
574 "%s/%u" % (PROXY_GUEST_IP, PROXY_NETMASK),
575 "dev",
576 veth_guest,
577 ),
578 ("ip", "link", "set", veth_guest, "up"),
579 )
580 try:
581 for cmd in commands:
582 cros_build_lib.dbg_run(cmd)
583 except cros_build_lib.RunCommandError as e:
584 cros_build_lib.Die("Proxy setup failed!\n%s", e)
585
586 proxy_url = "http://%s:%u" % (PROXY_HOST_IP, PROXY_PORT)
587 for proto in ("http", "https", "ftp"):
588 os.environ[proto + "_proxy"] = proxy_url
589 for v in ("all_proxy", "RSYNC_PROXY", "no_proxy"):
590 os.environ.pop(v, None)
591 return
592
593 # Set up parent side of the network.
594 uid = int(os.environ.get("SUDO_UID", "0"))
595 gid = int(os.environ.get("SUDO_GID", "0"))
596 if uid == 0 or gid == 0:
597 for username in PROXY_APACHE_FALLBACK_USERS:
598 try:
599 pwnam = pwd.getpwnam(username)
600 uid, gid = pwnam.pw_uid, pwnam.pw_gid
601 break
602 except KeyError:
603 continue
604 if uid == 0 or gid == 0:
605 raise SystemExit("Could not find a non-root user to run Apache as")
606
607 chroot_parent, chroot_base = os.path.split(options.chroot)
608 pid_file = os.path.join(chroot_parent, ".%s-apache-proxy.pid" % chroot_base)
609 log_file = os.path.join(chroot_parent, ".%s-apache-proxy.log" % chroot_base)
610
611 # Wait for the child to create the net ns.
612 ns_create_lock.Wait()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500613 del ns_create_lock
614
Alex Klein1699fab2022-09-08 08:46:06 -0600615 apache_directives = [
616 "User #%u" % uid,
617 "Group #%u" % gid,
618 "PidFile %s" % pid_file,
619 "ErrorLog %s" % log_file,
620 "Listen %s:%u" % (PROXY_HOST_IP, PROXY_PORT),
621 "ServerName %s" % PROXY_HOST_IP,
622 "ProxyRequests On",
623 "AllowCONNECT %s" % " ".join(str(x) for x in PROXY_CONNECT_PORTS),
624 ] + [
625 "LoadModule %s %s" % (mod, os.path.join(apache_module_path, so))
626 for (mod, so) in apache_modules
627 ]
628 commands = (
629 (
630 "ip",
631 "link",
632 "add",
633 "name",
634 veth_host,
635 "type",
636 "veth",
637 "peer",
638 "name",
639 veth_guest,
640 ),
641 (
642 "ip",
643 "address",
644 "add",
645 "%s/%u" % (PROXY_HOST_IP, PROXY_NETMASK),
646 "dev",
647 veth_host,
648 ),
649 ("ip", "link", "set", veth_host, "up"),
650 (
651 [apache_bin, "-f", "/dev/null"]
652 + [arg for d in apache_directives for arg in ("-C", d)]
653 ),
654 ("ip", "link", "set", veth_guest, "netns", str(pid)),
655 )
656 cmd = None # Make cros lint happy.
657 try:
658 for cmd in commands:
659 cros_build_lib.dbg_run(cmd)
660 except cros_build_lib.RunCommandError as e:
661 # Clean up existing interfaces, if any.
662 cmd_cleanup = ("ip", "link", "del", veth_host)
663 try:
664 cros_build_lib.run(cmd_cleanup, print_cmd=False)
665 except cros_build_lib.RunCommandError:
666 logging.error("running %r failed", cmd_cleanup)
667 cros_build_lib.Die("Proxy network setup failed!\n%s", e)
668
669 # Signal the child that the net ns/proxy is fully configured now.
670 ns_setup_lock.Post()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500671 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800672
Alex Klein1699fab2022-09-08 08:46:06 -0600673 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800674
675
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400676def _BuildReExecCommand(argv, opts) -> List[str]:
Alex Klein1699fab2022-09-08 08:46:06 -0600677 """Generate new command for self-reexec."""
678 # Make sure to preserve the active Python executable in case the version
679 # we're running as is not the default one found via the (new) $PATH.
680 cmd = _SudoCommand() + ["--"]
681 if opts.strace:
682 cmd += ["strace"] + shlex.split(opts.strace_arguments) + ["--"]
683 return cmd + [sys.executable] + argv
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400684
685
686def _ReExecuteIfNeeded(argv, opts):
Alex Klein1699fab2022-09-08 08:46:06 -0600687 """Re-execute cros_sdk as root.
David James56e6c2c2012-10-24 23:54:41 -0700688
Alex Klein1699fab2022-09-08 08:46:06 -0600689 Also unshare the mount namespace so as to ensure that processes outside
690 the chroot can't mess with our mounts.
691 """
692 if osutils.IsNonRootUser():
693 cmd = _BuildReExecCommand(argv, opts)
694 logging.debug(
695 "Reexecing self via sudo:\n%s", cros_build_lib.CmdToStr(cmd)
696 )
697 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700698
699
Mike Frysinger34db8692013-11-11 14:54:08 -0500700def _CreateParser(sdk_latest_version, bootstrap_latest_version):
Alex Klein1699fab2022-09-08 08:46:06 -0600701 """Generate and return the parser with all the options."""
702 usage = (
703 "usage: %(prog)s [options] "
704 "[VAR1=val1 ... VAR2=val2] [--] [command [args]]"
705 )
706 parser = commandline.ArgumentParser(
707 usage=usage, description=__doc__, caching=True
708 )
Brian Harring218e13c2012-10-10 16:21:26 -0700709
Alex Klein1699fab2022-09-08 08:46:06 -0600710 # Global options.
711 default_chroot = os.path.join(
712 constants.SOURCE_ROOT, constants.DEFAULT_CHROOT_DIR
713 )
714 parser.add_argument(
715 "--chroot",
716 dest="chroot",
717 default=default_chroot,
718 type="path",
719 help=("SDK chroot dir name [%s]" % constants.DEFAULT_CHROOT_DIR),
720 )
721 parser.add_argument(
Dan Callaghanada9e032023-01-30 14:28:46 +1100722 "--out-dir",
723 metavar="DIR",
724 default=constants.DEFAULT_OUT_DIR,
725 type=Path,
726 help="Use DIR for build state and output files",
727 )
728 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600729 "--nouse-image",
730 dest="use_image",
731 action="store_false",
732 default=False,
733 help="Do not mount the chroot on a loopback image; "
734 "instead, create it directly in a directory.",
735 )
736 parser.add_argument(
737 "--use-image",
738 dest="use_image",
739 action="store_true",
740 default=False,
741 help="Mount the chroot on a loopback image "
742 "instead of creating it directly in a directory.",
743 )
Brian Harringb938c782012-02-29 15:14:38 -0800744
Alex Klein1699fab2022-09-08 08:46:06 -0600745 parser.add_argument(
746 "--chrome-root",
747 "--chrome_root",
748 type="path",
749 help="Mount this chrome root into the SDK chroot",
750 )
751 parser.add_argument(
752 "--chrome_root_mount",
753 type="path",
754 help="Mount chrome into this path inside SDK chroot",
755 )
756 parser.add_argument(
757 "--nousepkg",
758 action="store_true",
759 default=False,
760 help="Do not use binary packages when creating a chroot.",
761 )
762 parser.add_argument(
763 "-u",
764 "--url",
765 dest="sdk_url",
766 help="Use sdk tarball located at this url. Use file:// "
767 "for local files.",
768 )
769 parser.add_argument(
770 "--sdk-version",
771 help=(
772 "Use this sdk version. For prebuilt, current is %r"
773 ", for bootstrapping it is %r."
774 % (sdk_latest_version, bootstrap_latest_version)
775 ),
776 )
777 parser.add_argument(
Mike Frysinger85824562023-01-31 02:40:43 -0500778 "--goma-dir",
Alex Klein1699fab2022-09-08 08:46:06 -0600779 "--goma_dir",
780 type="path",
781 help="Goma installed directory to mount into the chroot.",
782 )
783 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600784 "--reclient-dir",
785 type="path",
786 help="Reclient installed directory to mount into the chroot.",
787 )
788 parser.add_argument(
789 "--reproxy-cfg-file",
790 type="path",
791 help="Config file for re-client's reproxy used for remoteexec.",
792 )
793 parser.add_argument(
794 "--skip-chroot-upgrade",
795 dest="chroot_upgrade",
796 action="store_false",
797 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700798 help="Skip automatic SDK and toolchain upgrade when entering the "
799 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600800 )
Yong Hong84ba9172018-02-07 01:37:42 +0800801
Alex Klein1699fab2022-09-08 08:46:06 -0600802 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700803 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600804 parser.add_argument(
805 "--working-dir",
806 type=str,
807 help="Run the command in specific working directory in "
808 "chroot. If the given directory is a relative "
809 "path, this program will transfer the path to "
810 "the corresponding one inside chroot.",
811 )
Yong Hong84ba9172018-02-07 01:37:42 +0800812
Alex Klein1699fab2022-09-08 08:46:06 -0600813 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500814
Alex Klein1699fab2022-09-08 08:46:06 -0600815 # Commands.
816 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400817 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600818 "--enter",
819 action="store_true",
820 default=False,
821 help="Enter the SDK chroot. Implies --create.",
822 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400823 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600824 "--create",
825 action="store_true",
826 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700827 help="Create the chroot only if it does not already exist. Downloads "
828 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600829 )
830 group.add_argument(
831 "--bootstrap",
832 action="store_true",
833 default=False,
834 help="Build everything from scratch, including the sdk. "
835 "Use this only if you need to validate a change "
836 "that affects SDK creation itself (toolchain and "
837 "build are typically the only folk who need this). "
838 "Note this will quite heavily slow down the build. "
839 "This option implies --create --nousepkg.",
840 )
841 group.add_argument(
842 "-r",
843 "--replace",
844 action="store_true",
845 default=False,
846 help="Replace an existing SDK chroot. Basically an alias "
847 "for --delete --create.",
848 )
849 group.add_argument(
850 "--delete",
851 action="store_true",
852 default=False,
853 help="Delete the current SDK chroot if it exists.",
854 )
855 group.add_argument(
856 "--force",
857 action="store_true",
858 default=False,
859 help="Force unmount/delete of the current SDK chroot even if "
860 "obtaining the write lock fails.",
861 )
862 group.add_argument(
863 "--unmount",
864 action="store_true",
865 default=False,
866 help="Unmount and clean up devices associated with the "
867 "SDK chroot if it exists. This does not delete the "
868 "backing image file, so the same chroot can be later "
869 "re-mounted for reuse. To fully delete the chroot, use "
870 "--delete. This is primarily useful for working on "
871 "cros_sdk or the chroot setup; you should not need it "
872 "under normal circumstances.",
873 )
874 group.add_argument(
875 "--download",
876 action="store_true",
877 default=False,
878 help="Download the sdk.",
879 )
880 group.add_argument(
881 "--snapshot-create",
882 metavar="SNAPSHOT_NAME",
883 help="Create a snapshot of the chroot. Requires that the chroot was "
884 "created without the --nouse-image option.",
885 )
886 group.add_argument(
887 "--snapshot-restore",
888 metavar="SNAPSHOT_NAME",
889 help="Restore the chroot to a previously created snapshot.",
890 )
891 group.add_argument(
892 "--snapshot-delete",
893 metavar="SNAPSHOT_NAME",
894 help="Delete a previously created snapshot. Deleting a snapshot that "
895 "does not exist is not an error.",
896 )
897 group.add_argument(
898 "--snapshot-list",
899 action="store_true",
900 default=False,
901 help="List existing snapshots of the chroot and exit.",
902 )
903 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -0400904
Alex Klein1699fab2022-09-08 08:46:06 -0600905 # Namespace options.
906 group = parser.add_argument_group("Namespaces")
907 group.add_argument(
908 "--proxy-sim",
909 action="store_true",
910 default=False,
911 help="Simulate a restrictive network requiring an outbound" " proxy.",
912 )
913 for ns, default in (("pid", True), ("net", None)):
914 group.add_argument(
915 f"--ns-{ns}",
916 default=default,
917 action="store_true",
918 help=f"Create a new {ns} namespace.",
919 )
920 group.add_argument(
921 f"--no-ns-{ns}",
922 dest=f"ns_{ns}",
923 action="store_false",
924 help=f"Do not create a new {ns} namespace.",
925 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400926
Alex Klein1699fab2022-09-08 08:46:06 -0600927 # Debug options.
928 group = parser.debug_group
929 group.add_argument(
930 "--strace",
931 action="store_true",
932 help="Run cros_sdk through strace after re-exec via sudo",
933 )
934 group.add_argument(
935 "--strace-arguments",
936 default="",
937 help="Extra strace options (shell quoting permitted)",
938 )
Mike Frysinger34db8692013-11-11 14:54:08 -0500939
Alex Klein1699fab2022-09-08 08:46:06 -0600940 # Internal options.
941 group = parser.add_argument_group(
942 "Internal Chromium OS Build Team Options",
943 "Caution: these are for meant for the Chromium OS build team only",
944 )
945 group.add_argument(
946 "--buildbot-log-version",
947 default=False,
948 action="store_true",
949 help="Log SDK version for buildbot consumption",
950 )
951
952 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500953
954
955def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600956 # Turn on strict sudo checks.
957 cros_build_lib.STRICT_SUDO = True
958 conf = key_value_store.LoadFile(
959 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
960 ignore_missing=True,
961 )
962 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
963 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -0700964
Alex Klein1699fab2022-09-08 08:46:06 -0600965 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
966 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
967 bootstrap_latest_version = (
968 sdk_latest_version
969 if bootstrap_frozen_version == "<unknown>"
970 else bootstrap_frozen_version
971 )
972 parser, commands = _CreateParser(
973 sdk_latest_version, bootstrap_latest_version
974 )
975 options = parser.parse_args(argv)
Brian Harringb938c782012-02-29 15:14:38 -0800976
Alex Klein1699fab2022-09-08 08:46:06 -0600977 # Some basic checks first, before we ask for sudo credentials.
978 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800979
Alex Klein1699fab2022-09-08 08:46:06 -0600980 host = os.uname()[4]
981 if host != "x86_64":
982 cros_build_lib.Die(
983 "cros_sdk is currently only supported on x86_64; you're running"
984 " %s. Please find a x86_64 machine." % (host,)
985 )
Brian Harring1790ac42012-09-23 08:53:33 -0700986
Mike Frysinger9a5124e2023-01-26 17:16:44 -0500987 goma = goma_lib.Goma(options.goma_dir) if options.goma_dir else None
988
Alex Klein1699fab2022-09-08 08:46:06 -0600989 # Merge the outside PATH setting if we re-execed ourselves.
990 if "CHROMEOS_SUDO_PATH" in os.environ:
991 os.environ["PATH"] = "%s:%s" % (
992 os.environ.pop("CHROMEOS_SUDO_PATH"),
993 os.environ["PATH"],
994 )
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400995
Alex Klein1699fab2022-09-08 08:46:06 -0600996 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
997 if options.proxy_sim:
998 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
999 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -08001000
Alex Klein1699fab2022-09-08 08:46:06 -06001001 if (
1002 sdk_latest_version == "<unknown>"
1003 or bootstrap_latest_version == "<unknown>"
1004 ):
1005 cros_build_lib.Die(
1006 "No SDK version was found. "
1007 "Are you in a Chromium source tree instead of Chromium OS?\n\n"
1008 "Please change to a directory inside your Chromium OS source tree\n"
1009 "and retry. If you need to setup a Chromium OS source tree, see\n"
1010 " https://dev.chromium.org/chromium-os/developer-guide"
1011 )
Benjamin Gordon040a1162017-06-29 13:44:47 -06001012
Alex Klein1699fab2022-09-08 08:46:06 -06001013 any_snapshot_operation = (
1014 options.snapshot_create
1015 or options.snapshot_restore
1016 or options.snapshot_delete
1017 or options.snapshot_list
1018 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001019
Alex Klein1699fab2022-09-08 08:46:06 -06001020 if (
1021 options.snapshot_delete
1022 and options.snapshot_delete == options.snapshot_restore
1023 ):
1024 parser.error(
1025 "Cannot --snapshot_delete the same snapshot you are "
1026 "restoring with --snapshot_restore."
1027 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001028
Alex Klein1699fab2022-09-08 08:46:06 -06001029 _ReExecuteIfNeeded([sys.argv[0]] + argv, options)
David James471532c2013-01-21 10:23:31 -08001030
Alex Klein1699fab2022-09-08 08:46:06 -06001031 lock_path = os.path.dirname(options.chroot)
1032 lock_path = os.path.join(
1033 lock_path, ".%s_lock" % os.path.basename(options.chroot).lstrip(".")
1034 )
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001035
Alex Klein1699fab2022-09-08 08:46:06 -06001036 # Expand out the aliases...
1037 if options.replace:
1038 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001039
Alex Klein1699fab2022-09-08 08:46:06 -06001040 if options.bootstrap:
1041 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001042
Alex Klein1699fab2022-09-08 08:46:06 -06001043 # If a command is not given, default to enter.
1044 # pylint: disable=protected-access
1045 # This _group_actions access sucks, but upstream decided to not include an
1046 # alternative to optparse's option_list, and this is what they recommend.
1047 options.enter |= not any(
1048 getattr(options, x.dest) for x in commands._group_actions
1049 )
1050 # pylint: enable=protected-access
Mike Frysinger114a7b92023-01-26 19:08:57 -05001051 options.enter |= bool(options.commands)
Brian Harring218e13c2012-10-10 16:21:26 -07001052
Alex Klein1699fab2022-09-08 08:46:06 -06001053 if (
1054 options.delete
1055 and not options.create
1056 and (options.enter or any_snapshot_operation)
1057 ):
1058 parser.error(
1059 "Trying to enter or snapshot the chroot when --delete "
1060 "was specified makes no sense."
1061 )
Brian Harring218e13c2012-10-10 16:21:26 -07001062
Alex Klein1699fab2022-09-08 08:46:06 -06001063 if options.unmount and (
1064 options.create or options.enter or any_snapshot_operation
1065 ):
1066 parser.error("--unmount cannot be specified with other chroot actions.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001067
Alex Klein1699fab2022-09-08 08:46:06 -06001068 if options.working_dir is not None and not os.path.isabs(
1069 options.working_dir
1070 ):
1071 options.working_dir = path_util.ToChrootPath(options.working_dir)
Yong Hong84ba9172018-02-07 01:37:42 +08001072
Alex Klein1699fab2022-09-08 08:46:06 -06001073 # If there is an existing chroot image and we're not removing it then force
1074 # use_image on. This ensures that people don't have to remember to pass
Alex Kleinef517832023-01-13 12:06:51 -07001075 # --use-image after a reboot to avoid losing access to their existing
1076 # chroot.
Alex Klein1699fab2022-09-08 08:46:06 -06001077 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1078 img_path = _ImageFileForChroot(options.chroot)
1079 if (
1080 not options.use_image
1081 and not options.delete
1082 and not options.unmount
1083 and os.path.exists(img_path)
1084 ):
1085 if chroot_exists:
1086 # If the chroot is already populated, make sure it has something
1087 # mounted on it before we assume it came from an image.
1088 cmd = ["mountpoint", "-q", options.chroot]
1089 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
1090 options.use_image = True
Benjamin Gordon832a4412020-12-08 10:39:16 -07001091
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001092 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001093 logging.notice(
1094 "Existing chroot image %s found. Forcing --use-image on.",
1095 img_path,
1096 )
1097 options.use_image = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001098
Alex Klein1699fab2022-09-08 08:46:06 -06001099 if any_snapshot_operation and not options.use_image:
1100 if os.path.exists(img_path):
1101 options.use_image = True
1102 else:
1103 cros_build_lib.Die(
1104 "Snapshot operations are not compatible with " "--nouse-image."
1105 )
Mike Frysingerc60141b2023-01-27 00:57:15 -05001106 if options.use_image:
1107 logging.warning("--use-image is deprecated and will be removed soon.")
1108 logging.warning("Please migrate, or create a new one with --delete.")
1109 logging.warning("See http://b/266878468 for details.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001110
Alex Klein1699fab2022-09-08 08:46:06 -06001111 # Discern if we need to create the chroot.
1112 if (
1113 options.use_image
1114 and not chroot_exists
1115 and not options.delete
1116 and not options.unmount
1117 and not missing_image_tools
1118 and os.path.exists(img_path)
1119 ):
1120 # Try to re-mount an existing image in case the user has rebooted.
1121 with locking.FileLock(lock_path, "chroot lock") as lock:
1122 logging.debug("Checking if existing chroot image can be mounted.")
1123 lock.write_lock()
1124 cros_sdk_lib.MountChroot(options.chroot, create=False)
1125 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1126 if chroot_exists:
1127 logging.notice("Mounted existing image %s on chroot", img_path)
1128
1129 # Finally, flip create if necessary.
1130 if options.enter or options.snapshot_create:
1131 options.create |= not chroot_exists
1132
1133 # Make sure we will download if we plan to create.
1134 options.download |= options.create
1135
Mike Frysinger6bbd3e92023-01-27 00:05:02 -05001136 options.Freeze()
1137
Mike Frysinger253c8392023-01-27 01:12:21 -05001138 remoteexec = (
1139 remoteexec_util.Remoteexec(
1140 options.reclient_dir, options.reproxy_cfg_file
1141 )
1142 if (options.reclient_dir and options.reproxy_cfg_file)
1143 else None
1144 )
1145
Mike Frysingerec32bea2023-01-27 01:20:48 -05001146 chroot = chroot_lib.Chroot(
1147 path=options.chroot,
1148 cache_dir=options.cache_dir,
1149 chrome_root=options.chrome_root,
1150 goma=goma,
1151 remoteexec=remoteexec,
1152 )
1153
Alex Kleinef517832023-01-13 12:06:51 -07001154 # Anything that needs to manipulate the main chroot mount or communicate
1155 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -06001156
1157 # If deleting, do it regardless of the use_image flag so that a
1158 # previously-created loopback chroot can also be cleaned up.
1159 if options.delete:
1160 # Set a timeout of 300 seconds when getting the lock.
1161 with locking.FileLock(
1162 lock_path, "chroot lock", blocking_timeout=300
1163 ) as lock:
1164 try:
1165 lock.write_lock()
1166 except timeout_util.TimeoutError as e:
1167 logging.error(
1168 "Acquiring write_lock on %s failed: %s", lock_path, e
1169 )
1170 if not options.force:
1171 cros_build_lib.Die(
1172 "Exiting; use --force to continue w/o lock."
1173 )
1174 else:
1175 logging.warning(
1176 "cros_sdk was invoked with force option, continuing."
1177 )
1178 logging.notice("Deleting chroot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001179 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=True)
Alex Klein1699fab2022-09-08 08:46:06 -06001180
Alex Kleinef517832023-01-13 12:06:51 -07001181 # If cleanup was requested, we have to do it while we're still in the
1182 # original namespace. Since cleaning up the mount will interfere with any
1183 # other commands, we exit here. The check above should have made sure that
1184 # no other action was requested, anyway.
Alex Klein1699fab2022-09-08 08:46:06 -06001185 if options.unmount:
1186 # Set a timeout of 300 seconds when getting the lock.
1187 with locking.FileLock(
1188 lock_path, "chroot lock", blocking_timeout=300
1189 ) as lock:
1190 try:
1191 lock.write_lock()
1192 except timeout_util.TimeoutError as e:
1193 logging.error(
1194 "Acquiring write_lock on %s failed: %s", lock_path, e
1195 )
1196 logging.warning(
Alex Kleinef517832023-01-13 12:06:51 -07001197 "Continuing with CleanupChroot(%s), which will umount the "
1198 "tree.",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001199 chroot.path,
Alex Klein1699fab2022-09-08 08:46:06 -06001200 )
Alex Kleinef517832023-01-13 12:06:51 -07001201 # We can call CleanupChroot (which calls
1202 # cros_sdk_lib.CleanupChrootMount) even if we don't get the lock
1203 # because it will attempt to unmount the tree and will print
1204 # diagnostic information from 'fuser', 'lsof', and 'ps'.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001205 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=False)
Alex Klein1699fab2022-09-08 08:46:06 -06001206 sys.exit(0)
1207
1208 # Make sure the main chroot mount is visible. Contents will be filled in
1209 # below if needed.
1210 if options.create and options.use_image:
1211 if missing_image_tools:
1212 raise SystemExit(
1213 """The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001214Please make sure the lvm2 and thin-provisioning-tools packages
1215are installed on your host.
1216Example(ubuntu):
1217 sudo apt-get install lvm2 thin-provisioning-tools
1218
1219If you want to run without lvm2, pass --nouse-image (chroot
Alex Klein1699fab2022-09-08 08:46:06 -06001220snapshots will be unavailable)."""
1221 % ", ".join(missing_image_tools)
1222 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001223
Alex Klein1699fab2022-09-08 08:46:06 -06001224 logging.debug("Making sure chroot image is mounted.")
1225 with locking.FileLock(lock_path, "chroot lock") as lock:
1226 lock.write_lock()
Mike Frysingerec32bea2023-01-27 01:20:48 -05001227 if not cros_sdk_lib.MountChroot(chroot.path, create=True):
Alex Klein1699fab2022-09-08 08:46:06 -06001228 cros_build_lib.Die(
1229 "Unable to mount %s on chroot",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001230 _ImageFileForChroot(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001231 )
1232 logging.notice(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001233 "Mounted %s on chroot", _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001234 )
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001235
Alex Klein1699fab2022-09-08 08:46:06 -06001236 # Snapshot operations will always need the VG/LV, but other actions won't.
1237 if any_snapshot_operation:
1238 with locking.FileLock(lock_path, "chroot lock") as lock:
1239 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001240 chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001241 )
1242 if not chroot_vg or not chroot_lv:
1243 cros_build_lib.Die(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001244 "Unable to find VG/LV for chroot %s", chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001245 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001246
Alex Kleinef517832023-01-13 12:06:51 -07001247 # Delete snapshot before creating a new one. This allows the user to
1248 # throw out old state, create a new snapshot, and enter the chroot
1249 # in a single call to cros_sdk. Since restore involves deleting,
1250 # also do it before creating.
Alex Klein1699fab2022-09-08 08:46:06 -06001251 if options.snapshot_restore:
1252 lock.write_lock()
1253 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1254 if options.snapshot_restore not in valid_snapshots:
1255 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -07001256 "%s is not a valid snapshot to restore to. "
1257 "Valid snapshots: %s",
Alex Klein1699fab2022-09-08 08:46:06 -06001258 options.snapshot_restore,
1259 ", ".join(valid_snapshots),
1260 )
Mike Frysingerec32bea2023-01-27 01:20:48 -05001261 osutils.UmountTree(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001262 if not RestoreChrootSnapshot(
1263 options.snapshot_restore, chroot_vg, chroot_lv
1264 ):
1265 cros_build_lib.Die("Unable to restore chroot to snapshot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001266 if not cros_sdk_lib.MountChroot(chroot.path, create=False):
Alex Klein1699fab2022-09-08 08:46:06 -06001267 cros_build_lib.Die(
1268 "Unable to mount restored snapshot onto chroot."
1269 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001270
Alex Kleinef517832023-01-13 12:06:51 -07001271 # Use a read lock for snapshot delete and create even though they
1272 # modify the filesystem, because they don't modify the mounted
1273 # chroot itself. The underlying LVM commands take their own locks,
1274 # so conflicting concurrent operations here may crash cros_sdk, but
1275 # won't corrupt the chroot image. This tradeoff seems worth it to
1276 # allow snapshot operations on chroots that have a process inside.
Alex Klein1699fab2022-09-08 08:46:06 -06001277 if options.snapshot_delete:
1278 lock.read_lock()
1279 DeleteChrootSnapshot(
1280 options.snapshot_delete, chroot_vg, chroot_lv
1281 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001282
Alex Klein1699fab2022-09-08 08:46:06 -06001283 if options.snapshot_create:
1284 lock.read_lock()
1285 if not CreateChrootSnapshot(
1286 options.snapshot_create, chroot_vg, chroot_lv
1287 ):
1288 cros_build_lib.Die("Unable to create snapshot.")
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001289
Mike Frysingerec32bea2023-01-27 01:20:48 -05001290 img_path = _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001291 if (
1292 options.use_image
Mike Frysingerec32bea2023-01-27 01:20:48 -05001293 and os.path.exists(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001294 and os.path.exists(img_path)
1295 ):
1296 img_stat = os.stat(img_path)
1297 img_used_bytes = img_stat.st_blocks * 512
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001298
Mike Frysingerec32bea2023-01-27 01:20:48 -05001299 mount_stat = os.statvfs(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001300 mount_used_bytes = mount_stat.f_frsize * (
1301 mount_stat.f_blocks - mount_stat.f_bfree
1302 )
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001303
Alex Klein1699fab2022-09-08 08:46:06 -06001304 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
1305 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1306 logging.notice(
1307 "%s is using %s GiB more than needed. Running "
1308 "fstrim in background.",
1309 img_path,
1310 extra_gbs,
1311 )
1312 pid = os.fork()
1313 if pid == 0:
1314 try:
1315 # Directly call Popen to run fstrim concurrently.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001316 cmd = ["fstrim", chroot.path]
Alex Klein1699fab2022-09-08 08:46:06 -06001317 subprocess.Popen(cmd, close_fds=True, shell=False)
1318 except subprocess.SubprocessError as e:
1319 logging.warning(
1320 "Running fstrim failed. Consider running fstrim on "
1321 "your chroot manually.\n%s",
1322 e,
1323 )
1324 os._exit(0) # pylint: disable=protected-access
1325 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001326
Alex Kleinef517832023-01-13 12:06:51 -07001327 # Enter a new set of namespaces. Everything after here cannot directly
1328 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -06001329 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001330
Alex Klein1699fab2022-09-08 08:46:06 -06001331 if options.snapshot_list:
1332 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1333 print(snap)
1334 sys.exit(0)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001335
Alex Klein1699fab2022-09-08 08:46:06 -06001336 if not options.sdk_version:
1337 sdk_version = (
1338 bootstrap_latest_version
1339 if options.bootstrap
1340 else sdk_latest_version
1341 )
Yong Hong4e29b622018-02-05 14:31:10 +08001342 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001343 sdk_version = options.sdk_version
1344 if options.buildbot_log_version:
1345 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001346
Alex Klein1699fab2022-09-08 08:46:06 -06001347 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -07001348 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001349 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -06001350 if options.sdk_url:
1351 urls = [options.sdk_url]
1352 else:
1353 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -07001354
Alex Klein1699fab2022-09-08 08:46:06 -06001355 with locking.FileLock(lock_path, "chroot lock") as lock:
1356 if options.proxy_sim:
1357 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -07001358
Mike Frysingerec32bea2023-01-27 01:20:48 -05001359 sdk_cache = os.path.join(chroot.cache_dir, "sdks")
1360 distfiles_cache = os.path.join(chroot.cache_dir, "distfiles")
1361 osutils.SafeMakedirsNonRoot(chroot.cache_dir)
Dan Callaghanada9e032023-01-30 14:28:46 +11001362 osutils.SafeMakedirsNonRoot(options.out_dir)
Alex Klein1699fab2022-09-08 08:46:06 -06001363
1364 for target in (sdk_cache, distfiles_cache):
1365 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1366 if not os.path.exists(src):
1367 osutils.SafeMakedirsNonRoot(target)
1368 continue
1369 lock.write_lock(
1370 "Upgrade to %r needed but chroot is locked; please exit "
1371 "all instances so this upgrade can finish." % src
1372 )
1373 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -07001374 # Note that while waiting for the write lock, src may've
1375 # vanished; it's a rare race during the upgrade process that's a
1376 # byproduct of us avoiding taking a write lock to do the src
1377 # check. If we took a write lock for that check, it would
1378 # effectively limit all cros_sdk for a chroot to a single
1379 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -06001380 osutils.SafeMakedirsNonRoot(target)
1381 elif not os.path.exists(target):
1382 # Upgrade occurred, but a reversion, or something whacky
1383 # occurred writing to the old location. Wipe and continue.
1384 os.rename(src, target)
1385 else:
1386 # Upgrade occurred once already, but either a reversion or
1387 # some before/after separate cros_sdk usage is at play.
1388 # Wipe and continue.
1389 osutils.RmDir(src)
1390
Alex Klein1699fab2022-09-08 08:46:06 -06001391 mounted = False
1392 if options.create:
1393 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -07001394 # Recheck if the chroot is set up here before creating to make sure
1395 # we account for whatever the various delete/unmount/remount steps
1396 # above have done.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001397 if cros_sdk_lib.IsChrootReady(chroot.path):
Alex Klein1699fab2022-09-08 08:46:06 -06001398 logging.debug("Chroot already exists. Skipping creation.")
1399 else:
Alex Klein22690a12022-11-17 10:56:09 -07001400 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001401 cros_sdk_lib.CreateChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001402 Path(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001403 Path(sdk_tarball),
Dan Callaghanada9e032023-01-30 14:28:46 +11001404 options.out_dir,
Mike Frysingerec32bea2023-01-27 01:20:48 -05001405 Path(chroot.cache_dir),
Alex Klein1699fab2022-09-08 08:46:06 -06001406 usepkg=not options.bootstrap and not options.nousepkg,
1407 chroot_upgrade=options.chroot_upgrade,
1408 )
1409 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -07001410 elif options.download:
1411 # Allow downloading only.
1412 lock.write_lock()
1413 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001414
1415 if options.enter:
1416 lock.read_lock()
1417 if not mounted:
Dan Callaghanada9e032023-01-30 14:28:46 +11001418 cros_sdk_lib.MountChrootPaths(chroot.path, options.out_dir)
Mike Frysinger53baf882021-06-24 23:16:50 -04001419 ret = cros_sdk_lib.EnterChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001420 chroot,
Mike Frysinger53baf882021-06-24 23:16:50 -04001421 chrome_root_mount=options.chrome_root_mount,
1422 cwd=options.working_dir,
1423 cmd=options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -06001424 )
1425 sys.exit(ret.returncode)