blob: edb87b410325b9dd8904798823c417b1d2852b56 [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
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040038from chromite.lib import process_util
Mike Frysinger253c8392023-01-27 01:12:21 -050039from chromite.lib import remoteexec_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080040from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060041from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050042from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040043from chromite.utils import key_value_store
44
Brian Harringb938c782012-02-29 15:14:38 -080045
Mike Frysingerf744d032022-05-07 20:38:39 -040046# Which compression algos the SDK tarball uses. We've used xz since 2012.
Alex Klein1699fab2022-09-08 08:46:06 -060047COMPRESSION_PREFERENCE = ("xz",)
Zdenek Behanfd0efe42012-04-13 04:36:40 +020048
Josh Triplett472a4182013-03-08 11:48:57 -080049# Proxy simulator configuration.
Alex Klein1699fab2022-09-08 08:46:06 -060050PROXY_HOST_IP = "192.168.240.1"
Josh Triplett472a4182013-03-08 11:48:57 -080051PROXY_PORT = 8080
Alex Klein1699fab2022-09-08 08:46:06 -060052PROXY_GUEST_IP = "192.168.240.2"
Josh Triplett472a4182013-03-08 11:48:57 -080053PROXY_NETMASK = 30
Alex Klein1699fab2022-09-08 08:46:06 -060054PROXY_VETH_PREFIX = "veth"
Josh Triplett472a4182013-03-08 11:48:57 -080055PROXY_CONNECT_PORTS = (80, 443, 9418)
Alex Klein1699fab2022-09-08 08:46:06 -060056PROXY_APACHE_FALLBACK_USERS = ("www-data", "apache", "nobody")
57PROXY_APACHE_MPMS = ("event", "worker", "prefork")
58PROXY_APACHE_FALLBACK_PATH = ":".join(
59 "/usr/lib/apache2/mpm-%s" % mpm for mpm in PROXY_APACHE_MPMS
60)
61PROXY_APACHE_MODULE_GLOBS = ("/usr/lib*/apache2/modules", "/usr/lib*/apache2")
Josh Triplett472a4182013-03-08 11:48:57 -080062
Josh Triplett9a495f62013-03-15 18:06:55 -070063# We need these tools to run. Very common tools (tar,..) are omitted.
Alex Klein1699fab2022-09-08 08:46:06 -060064NEEDED_TOOLS = ("curl", "xz")
Brian Harringb938c782012-02-29 15:14:38 -080065
Josh Triplett472a4182013-03-08 11:48:57 -080066# Tools needed for --proxy-sim only.
Alex Klein1699fab2022-09-08 08:46:06 -060067PROXY_NEEDED_TOOLS = ("ip",)
Brian Harringb938c782012-02-29 15:14:38 -080068
Mike Frysingerc60141b2023-01-27 00:57:15 -050069# Tools needed when use_image is true.
Alex Klein1699fab2022-09-08 08:46:06 -060070IMAGE_NEEDED_TOOLS = (
71 "losetup",
72 "lvchange",
73 "lvcreate",
74 "lvs",
75 "mke2fs",
76 "pvscan",
77 "thin_check",
78 "vgchange",
79 "vgcreate",
80 "vgs",
81)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060082
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070083# As space is used inside the chroot, the empty space in chroot.img is
84# allocated. Deleting files inside the chroot doesn't automatically return the
85# used space to the OS. Over time, this tends to make the sparse chroot.img
86# less sparse even if the chroot contents don't currently need much space. We
87# can recover most of this unused space with fstrim, but that takes too much
88# time to run it every time. Instead, check the used space against the image
89# size after mounting the chroot and only call fstrim if it looks like we could
90# recover at least this many GiB.
91MAX_UNUSED_IMAGE_GBS = 20
92
Mike Frysingercc838832014-05-24 13:10:30 -040093
Brian Harring1790ac42012-09-23 08:53:33 -070094def GetArchStageTarballs(version):
Alex Klein1699fab2022-09-08 08:46:06 -060095 """Returns the URL for a given arch/version"""
96 extension = {"xz": "tar.xz"}
97 return [
98 toolchain.GetSdkURL(
99 suburl="cros-sdk-%s.%s" % (version, extension[compressor])
100 )
101 for compressor in COMPRESSION_PREFERENCE
102 ]
Brian Harring1790ac42012-09-23 08:53:33 -0700103
104
Mike Frysingerf744d032022-05-07 20:38:39 -0400105def FetchRemoteTarballs(storage_dir, urls):
Alex Klein1699fab2022-09-08 08:46:06 -0600106 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200107
Alex Klein1699fab2022-09-08 08:46:06 -0600108 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700109 storage_dir: Path where to save the tarball.
110 urls: List of URLs to try to download. Download will stop on first
111 success.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200112
Alex Klein1699fab2022-09-08 08:46:06 -0600113 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700114 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700115
Alex Klein1699fab2022-09-08 08:46:06 -0600116 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700117 ValueError: None of the URLs worked.
Alex Klein1699fab2022-09-08 08:46:06 -0600118 """
119 # Note we track content length ourselves since certain versions of curl
120 # fail if asked to resume a complete file.
121 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
122 status_re = re.compile(rb"^HTTP/[0-9]+(\.[0-9]+)? 200")
123 # pylint: disable=undefined-loop-variable
124 for url in urls:
125 logging.notice("Downloading tarball %s ...", urls[0].rsplit("/", 1)[-1])
126 parsed = urllib.parse.urlparse(url)
127 tarball_name = os.path.basename(parsed.path)
128 if parsed.scheme in ("", "file"):
129 if os.path.exists(parsed.path):
130 return parsed.path
131 continue
132 content_length = 0
133 logging.debug("Attempting download from %s", url)
134 result = retry_util.RunCurl(
135 ["-I", url],
136 print_cmd=False,
137 debug_level=logging.NOTICE,
138 capture_output=True,
139 )
140 successful = False
141 for header in result.stdout.splitlines():
142 # We must walk the output to find the 200 code for use cases where
143 # a proxy is involved and may have pushed down the actual header.
144 if status_re.match(header):
145 successful = True
146 elif header.lower().startswith(b"content-length:"):
147 content_length = int(header.split(b":", 1)[-1].strip())
148 if successful:
149 break
Brian Harring1790ac42012-09-23 08:53:33 -0700150 if successful:
Alex Klein1699fab2022-09-08 08:46:06 -0600151 break
152 else:
153 raise ValueError("No valid URLs found!")
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200154
Alex Klein1699fab2022-09-08 08:46:06 -0600155 tarball_dest = os.path.join(storage_dir, tarball_name)
156 current_size = 0
157 if os.path.exists(tarball_dest):
158 current_size = os.path.getsize(tarball_dest)
159 if current_size > content_length:
160 osutils.SafeUnlink(tarball_dest)
161 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 if current_size < content_length:
164 retry_util.RunCurl(
165 [
166 "--fail",
167 "-L",
168 "-y",
169 "30",
170 "-C",
171 "-",
172 "--output",
173 tarball_dest,
174 url,
175 ],
176 print_cmd=False,
177 debug_level=logging.NOTICE,
178 )
Brian Harringb938c782012-02-29 15:14:38 -0800179
Alex Klein1699fab2022-09-08 08:46:06 -0600180 # Cleanup old tarballs now since we've successfull fetched; only cleanup
181 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
182 # because we might have partial overlap between known prefixes.
183 for p in Path(storage_dir).glob("cros-sdk-*"):
184 if p.name == tarball_name:
185 continue
186 logging.info("Cleaning up old tarball: %s", p)
187 osutils.SafeUnlink(p)
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200188
Alex Klein1699fab2022-09-08 08:46:06 -0600189 return tarball_dest
Brian Harringb938c782012-02-29 15:14:38 -0800190
191
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600192def _ImageFileForChroot(chroot):
Alex Klein1699fab2022-09-08 08:46:06 -0600193 """Find the image file that should be associated with |chroot|.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600194
Alex Klein1699fab2022-09-08 08:46:06 -0600195 This function does not check if the image exists; it simply returns the
196 filename that would be used.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600197
Alex Klein1699fab2022-09-08 08:46:06 -0600198 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700199 chroot: Path to the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600200
Alex Klein1699fab2022-09-08 08:46:06 -0600201 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700202 Path to an image file that would be associated with chroot.
Alex Klein1699fab2022-09-08 08:46:06 -0600203 """
204 return chroot.rstrip("/") + ".img"
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600205
206
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600207def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600208 """Create a snapshot for the specified chroot VG/LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600209
Alex Klein1699fab2022-09-08 08:46:06 -0600210 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700211 snapshot_name: The name of the new snapshot.
212 chroot_vg: The name of the VG containing the origin LV.
213 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600214
Alex Klein1699fab2022-09-08 08:46:06 -0600215 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700216 True if the snapshot was created, or False if a snapshot with the same
217 name already exists.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600218
Alex Klein1699fab2022-09-08 08:46:06 -0600219 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700220 SystemExit: The lvcreate command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600221 """
222 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
223 logging.error(
224 "Cannot create snapshot %s: A volume with that name already "
225 "exists.",
226 snapshot_name,
227 )
228 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600229
Alex Klein1699fab2022-09-08 08:46:06 -0600230 cmd = [
231 "lvcreate",
232 "-s",
233 "--name",
234 snapshot_name,
235 "%s/%s" % (chroot_vg, chroot_lv),
236 ]
237 try:
238 logging.notice(
239 "Creating snapshot %s from %s in VG %s.",
240 snapshot_name,
241 chroot_lv,
242 chroot_vg,
243 )
244 cros_build_lib.dbg_run(cmd, capture_output=True)
245 return True
246 except cros_build_lib.RunCommandError as e:
247 cros_build_lib.Die("Creating snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600248
249
250def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600251 """Delete the named snapshot from the specified chroot VG.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600252
Alex Klein1699fab2022-09-08 08:46:06 -0600253 If the requested snapshot is not found, nothing happens. The main chroot LV
254 and internal thinpool LV cannot be deleted with this function.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600255
Alex Klein1699fab2022-09-08 08:46:06 -0600256 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700257 snapshot_name: The name of the snapshot to delete.
258 chroot_vg: The name of the VG containing the origin LV.
259 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600260
Alex Klein1699fab2022-09-08 08:46:06 -0600261 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700262 SystemExit: The lvremove command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600263 """
264 if snapshot_name in (
265 cros_sdk_lib.CHROOT_LV_NAME,
266 cros_sdk_lib.CHROOT_THINPOOL_NAME,
267 ):
268 logging.error(
269 "Cannot remove LV %s as a snapshot. Use cros_sdk --delete "
270 "if you want to remove the whole chroot.",
271 snapshot_name,
272 )
273 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600274
Alex Klein1699fab2022-09-08 08:46:06 -0600275 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
276 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600277
Alex Klein1699fab2022-09-08 08:46:06 -0600278 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, snapshot_name)]
279 try:
280 logging.notice(
281 "Deleting snapshot %s in VG %s.", snapshot_name, chroot_vg
282 )
283 cros_build_lib.dbg_run(cmd, capture_output=True)
284 except cros_build_lib.RunCommandError as e:
285 cros_build_lib.Die("Deleting snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600286
287
288def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600289 """Restore the chroot to an existing snapshot.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 This is done by renaming the original |chroot_lv| LV to a temporary name,
292 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
293 now unused LV. If an error occurs, attempts to rename the original snapshot
294 back to |chroot_lv| to leave the chroot unchanged.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600295
Alex Klein1699fab2022-09-08 08:46:06 -0600296 The chroot must be unmounted before calling this function, and will be left
297 unmounted after this function returns.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600298
Alex Klein1699fab2022-09-08 08:46:06 -0600299 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700300 snapshot_name: The name of the snapshot to restore. This snapshot will
301 no longer be accessible at its original name after this function
302 finishes.
303 chroot_vg: The VG containing the chroot LV and snapshot LV.
304 chroot_lv: The name of the original chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600305
Alex Klein1699fab2022-09-08 08:46:06 -0600306 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700307 True if the chroot was restored to the requested snapshot, or False if
308 the snapshot wasn't found or isn't valid.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600309
Alex Klein1699fab2022-09-08 08:46:06 -0600310 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700311 SystemExit: Any of the LVM commands failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600312 """
313 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
314 if (
315 snapshot_name
316 in (cros_sdk_lib.CHROOT_LV_NAME, cros_sdk_lib.CHROOT_THINPOOL_NAME)
317 or snapshot_name not in valid_snapshots
318 ):
319 logging.error(
320 "Chroot cannot be restored to %s. Valid snapshots: %s",
321 snapshot_name,
322 ", ".join(valid_snapshots),
323 )
324 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600325
Alex Klein1699fab2022-09-08 08:46:06 -0600326 backup_chroot_name = "chroot-bak-%d" % random.randint(0, 1000)
327 cmd = ["lvrename", chroot_vg, chroot_lv, backup_chroot_name]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600328 try:
Alex Klein1699fab2022-09-08 08:46:06 -0600329 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500330 except cros_build_lib.RunCommandError as e:
Alex Klein1699fab2022-09-08 08:46:06 -0600331 cros_build_lib.Die("Restoring snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600332
Alex Klein1699fab2022-09-08 08:46:06 -0600333 cmd = ["lvrename", chroot_vg, snapshot_name, chroot_lv]
334 try:
335 cros_build_lib.dbg_run(cmd, capture_output=True)
336 except cros_build_lib.RunCommandError as e:
337 cmd = ["lvrename", chroot_vg, backup_chroot_name, chroot_lv]
338 try:
339 cros_build_lib.dbg_run(cmd, capture_output=True)
340 except cros_build_lib.RunCommandError as e:
341 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -0700342 "Failed to rename %s to chroot and failed to restore %s back "
343 "to chroot!\n%s",
Alex Klein1699fab2022-09-08 08:46:06 -0600344 snapshot_name,
345 backup_chroot_name,
346 e,
347 )
348 cros_build_lib.Die(
349 "Failed to rename %s to chroot! Original chroot LV has "
350 "been restored.\n%s",
351 snapshot_name,
352 e,
353 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600354
Alex Klein1699fab2022-09-08 08:46:06 -0600355 # Some versions of LVM set snapshots to be skipped at auto-activate time.
356 # Other versions don't have this flag at all. We run lvchange to try
Alex Kleinef517832023-01-13 12:06:51 -0700357 # disabling auto-skip and activating the volume, but ignore errors. Versions
Alex Klein1699fab2022-09-08 08:46:06 -0600358 # that don't have the flag should be auto-activated.
359 chroot_lv_path = "%s/%s" % (chroot_vg, chroot_lv)
360 cmd = ["lvchange", "-kn", chroot_lv_path]
361 cros_build_lib.run(cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600362
Alex Klein1699fab2022-09-08 08:46:06 -0600363 # Activate the LV in case the lvchange above was needed. Activating an LV
Alex Kleinef517832023-01-13 12:06:51 -0700364 # that is already active shouldn't do anything, so this is safe to run even
365 # if the -kn wasn't needed.
Alex Klein1699fab2022-09-08 08:46:06 -0600366 cmd = ["lvchange", "-ay", chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500367 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600368
Alex Klein1699fab2022-09-08 08:46:06 -0600369 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, backup_chroot_name)]
370 try:
371 cros_build_lib.dbg_run(cmd, capture_output=True)
372 except cros_build_lib.RunCommandError as e:
373 cros_build_lib.Die(
374 "Failed to remove backup LV %s/%s!\n%s",
375 chroot_vg,
376 backup_chroot_name,
377 e,
378 )
379
380 return True
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600381
382
383def ListChrootSnapshots(chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600384 """Return all snapshots in |chroot_vg| regardless of origin volume.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600385
Alex Klein1699fab2022-09-08 08:46:06 -0600386 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700387 chroot_vg: The name of the VG containing the chroot.
388 chroot_lv: The name of the chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600389
Alex Klein1699fab2022-09-08 08:46:06 -0600390 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700391 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600392
Alex Klein1699fab2022-09-08 08:46:06 -0600393 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700394 SystemExit: The lvs command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600395 """
396 if not chroot_vg or not chroot_lv:
397 return []
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600398
Alex Klein1699fab2022-09-08 08:46:06 -0600399 cmd = [
400 "lvs",
401 "-o",
402 "lv_name,pool_lv,lv_attr",
403 "-O",
404 "lv_name",
405 "--noheadings",
406 "--separator",
407 "\t",
408 chroot_vg,
409 ]
410 try:
411 result = cros_build_lib.run(
412 cmd, print_cmd=False, stdout=True, encoding="utf-8"
413 )
414 except cros_build_lib.RunCommandError:
415 raise SystemExit("Running %r failed!" % cmd)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 # Once the thin origin volume has been deleted, there's no way to tell a
Alex Kleinef517832023-01-13 12:06:51 -0700418 # snapshot apart from any other volume. Since this VG is created and managed
419 # by cros_sdk, we'll assume that all volumes that share the same thin pool
420 # are valid snapshots.
Alex Klein1699fab2022-09-08 08:46:06 -0600421 snapshots = []
422 snapshot_attrs = re.compile(r"^V.....t.{2,}") # Matches a thin volume.
423 for line in result.stdout.splitlines():
424 lv_name, pool_lv, lv_attr = line.lstrip().split("\t")
425 if (
426 lv_name == chroot_lv
427 or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME
428 or pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME
429 or not snapshot_attrs.match(lv_attr)
430 ):
431 continue
432 snapshots.append(lv_name)
433 return snapshots
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600434
435
David James56e6c2c2012-10-24 23:54:41 -0700436def _SudoCommand():
Alex Klein1699fab2022-09-08 08:46:06 -0600437 """Get the 'sudo' command, along with all needed environment variables."""
David James56e6c2c2012-10-24 23:54:41 -0700438
Alex Klein1699fab2022-09-08 08:46:06 -0600439 # Pass in the ENVIRONMENT_ALLOWLIST and ENV_PASSTHRU variables so that
440 # scripts in the chroot know what variables to pass through.
441 cmd = ["sudo"]
442 for key in constants.CHROOT_ENVIRONMENT_ALLOWLIST + constants.ENV_PASSTHRU:
443 value = os.environ.get(key)
444 if value is not None:
445 cmd += ["%s=%s" % (key, value)]
David James56e6c2c2012-10-24 23:54:41 -0700446
Alex Kleinef517832023-01-13 12:06:51 -0700447 # We keep PATH not for the chroot but for the re-exec & for programs we
448 # might run before we chroot into the SDK. The process that enters the SDK
449 # itself will take care of initializing PATH to the right value then. But
450 # we can't override the system's default PATH for root as that will hide
451 # /sbin.
Alex Klein1699fab2022-09-08 08:46:06 -0600452 cmd += ["CHROMEOS_SUDO_PATH=%s" % os.environ.get("PATH", "")]
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400453
Alex Klein1699fab2022-09-08 08:46:06 -0600454 # Pass along current rlimit settings so we can restore them.
Mike Frysinger53baf882021-06-24 23:16:50 -0400455 cmd += [f"CHROMEOS_SUDO_RLIMITS={cros_sdk_lib.ChrootEnteror.get_rlimits()}"]
Mike Frysinger12d055b2022-06-23 12:26:47 -0400456
Alex Klein1699fab2022-09-08 08:46:06 -0600457 # Pass in the path to the depot_tools so that users can access them from
458 # within the chroot.
459 cmd += ["DEPOT_TOOLS=%s" % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500460
Alex Klein1699fab2022-09-08 08:46:06 -0600461 return cmd
David James56e6c2c2012-10-24 23:54:41 -0700462
463
Josh Triplett472a4182013-03-08 11:48:57 -0800464def _ReportMissing(missing):
Alex Klein1699fab2022-09-08 08:46:06 -0600465 """Report missing utilities, then exit.
Josh Triplett472a4182013-03-08 11:48:57 -0800466
Alex Klein1699fab2022-09-08 08:46:06 -0600467 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700468 missing: List of missing utilities, as returned by
469 osutils.FindMissingBinaries. If non-empty, will not return.
Alex Klein1699fab2022-09-08 08:46:06 -0600470 """
Josh Triplett472a4182013-03-08 11:48:57 -0800471
Alex Klein1699fab2022-09-08 08:46:06 -0600472 if missing:
473 raise SystemExit(
474 "The tool(s) %s were not found.\n"
475 "Please install the appropriate package in your host.\n"
476 "Example(ubuntu):\n"
477 " sudo apt-get install <packagename>" % ", ".join(missing)
478 )
Josh Triplett472a4182013-03-08 11:48:57 -0800479
480
481def _ProxySimSetup(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600482 """Set up proxy simulator, and return only in the child environment.
Josh Triplett472a4182013-03-08 11:48:57 -0800483
Alex Klein1699fab2022-09-08 08:46:06 -0600484 TODO: Ideally, this should support multiple concurrent invocations of
485 cros_sdk --proxy-sim; currently, such invocations will conflict with each
486 other due to the veth device names and IP addresses. Either this code would
487 need to generate fresh, unused names for all of these before forking, or it
488 would need to support multiple concurrent cros_sdk invocations sharing one
489 proxy and allowing it to exit when unused (without counting on any local
490 service-management infrastructure on the host).
491 """
Josh Triplett472a4182013-03-08 11:48:57 -0800492
Alex Klein1699fab2022-09-08 08:46:06 -0600493 may_need_mpm = False
494 apache_bin = osutils.Which("apache2")
Josh Triplett472a4182013-03-08 11:48:57 -0800495 if apache_bin is None:
Alex Klein1699fab2022-09-08 08:46:06 -0600496 apache_bin = osutils.Which("apache2", PROXY_APACHE_FALLBACK_PATH)
497 if apache_bin is None:
498 _ReportMissing(("apache2",))
499 else:
500 may_need_mpm = True
Josh Triplett472a4182013-03-08 11:48:57 -0800501
Alex Klein1699fab2022-09-08 08:46:06 -0600502 # Module names and .so names included for ease of grepping.
503 apache_modules = [
504 ("proxy_module", "mod_proxy.so"),
505 ("proxy_connect_module", "mod_proxy_connect.so"),
506 ("proxy_http_module", "mod_proxy_http.so"),
507 ("proxy_ftp_module", "mod_proxy_ftp.so"),
508 ]
Josh Triplett472a4182013-03-08 11:48:57 -0800509
Alex Kleinef517832023-01-13 12:06:51 -0700510 # Find the apache module directory and make sure it has the modules we need.
Alex Klein1699fab2022-09-08 08:46:06 -0600511 module_dirs = {}
512 for g in PROXY_APACHE_MODULE_GLOBS:
513 for _, so in apache_modules:
514 for f in glob.glob(os.path.join(g, so)):
515 module_dirs.setdefault(os.path.dirname(f), []).append(so)
516 for apache_module_path, modules_found in module_dirs.items():
517 if len(modules_found) == len(apache_modules):
518 break
519 else:
Alex Kleinef517832023-01-13 12:06:51 -0700520 # Appease cros lint, which doesn't understand that this else block will
521 # not fall through to the subsequent code which relies on
522 # apache_module_path.
Alex Klein1699fab2022-09-08 08:46:06 -0600523 apache_module_path = None
524 raise SystemExit(
Alex Kleinef517832023-01-13 12:06:51 -0700525 "Could not find apache module path containing all required "
526 "modules: %s" % ", ".join(so for mod, so in apache_modules)
Alex Klein1699fab2022-09-08 08:46:06 -0600527 )
Josh Triplett472a4182013-03-08 11:48:57 -0800528
Alex Klein1699fab2022-09-08 08:46:06 -0600529 def check_add_module(name):
530 so = "mod_%s.so" % name
531 if os.access(os.path.join(apache_module_path, so), os.F_OK):
532 mod = "%s_module" % name
533 apache_modules.append((mod, so))
534 return True
535 return False
Josh Triplett472a4182013-03-08 11:48:57 -0800536
Alex Klein1699fab2022-09-08 08:46:06 -0600537 check_add_module("authz_core")
538 if may_need_mpm:
539 for mpm in PROXY_APACHE_MPMS:
540 if check_add_module("mpm_%s" % mpm):
541 break
Josh Triplett472a4182013-03-08 11:48:57 -0800542
Alex Klein1699fab2022-09-08 08:46:06 -0600543 veth_host = "%s-host" % PROXY_VETH_PREFIX
544 veth_guest = "%s-guest" % PROXY_VETH_PREFIX
Josh Triplett472a4182013-03-08 11:48:57 -0800545
Alex Klein1699fab2022-09-08 08:46:06 -0600546 # Set up locks to sync the net namespace setup. We need the child to create
Alex Kleinef517832023-01-13 12:06:51 -0700547 # the net ns first, and then have the parent assign the guest end of the
548 # veth interface to the child's new network namespace & bring up the proxy.
549 # Only then can the child move forward and rely on the network being up.
Alex Klein1699fab2022-09-08 08:46:06 -0600550 ns_create_lock = locking.PipeLock()
551 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800552
Alex Klein1699fab2022-09-08 08:46:06 -0600553 pid = os.fork()
554 if not pid:
555 # Create our new isolated net namespace.
556 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500557
Alex Klein1699fab2022-09-08 08:46:06 -0600558 # Signal the parent the ns is ready to be configured.
559 ns_create_lock.Post()
560 del ns_create_lock
561
562 # Wait for the parent to finish setting up the ns/proxy.
563 ns_setup_lock.Wait()
564 del ns_setup_lock
565
566 # Set up child side of the network.
567 commands = (
568 ("ip", "link", "set", "up", "lo"),
569 (
570 "ip",
571 "address",
572 "add",
573 "%s/%u" % (PROXY_GUEST_IP, PROXY_NETMASK),
574 "dev",
575 veth_guest,
576 ),
577 ("ip", "link", "set", veth_guest, "up"),
578 )
579 try:
580 for cmd in commands:
581 cros_build_lib.dbg_run(cmd)
582 except cros_build_lib.RunCommandError as e:
583 cros_build_lib.Die("Proxy setup failed!\n%s", e)
584
585 proxy_url = "http://%s:%u" % (PROXY_HOST_IP, PROXY_PORT)
586 for proto in ("http", "https", "ftp"):
587 os.environ[proto + "_proxy"] = proxy_url
588 for v in ("all_proxy", "RSYNC_PROXY", "no_proxy"):
589 os.environ.pop(v, None)
590 return
591
592 # Set up parent side of the network.
593 uid = int(os.environ.get("SUDO_UID", "0"))
594 gid = int(os.environ.get("SUDO_GID", "0"))
595 if uid == 0 or gid == 0:
596 for username in PROXY_APACHE_FALLBACK_USERS:
597 try:
598 pwnam = pwd.getpwnam(username)
599 uid, gid = pwnam.pw_uid, pwnam.pw_gid
600 break
601 except KeyError:
602 continue
603 if uid == 0 or gid == 0:
604 raise SystemExit("Could not find a non-root user to run Apache as")
605
606 chroot_parent, chroot_base = os.path.split(options.chroot)
607 pid_file = os.path.join(chroot_parent, ".%s-apache-proxy.pid" % chroot_base)
608 log_file = os.path.join(chroot_parent, ".%s-apache-proxy.log" % chroot_base)
609
610 # Wait for the child to create the net ns.
611 ns_create_lock.Wait()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500612 del ns_create_lock
613
Alex Klein1699fab2022-09-08 08:46:06 -0600614 apache_directives = [
615 "User #%u" % uid,
616 "Group #%u" % gid,
617 "PidFile %s" % pid_file,
618 "ErrorLog %s" % log_file,
619 "Listen %s:%u" % (PROXY_HOST_IP, PROXY_PORT),
620 "ServerName %s" % PROXY_HOST_IP,
621 "ProxyRequests On",
622 "AllowCONNECT %s" % " ".join(str(x) for x in PROXY_CONNECT_PORTS),
623 ] + [
624 "LoadModule %s %s" % (mod, os.path.join(apache_module_path, so))
625 for (mod, so) in apache_modules
626 ]
627 commands = (
628 (
629 "ip",
630 "link",
631 "add",
632 "name",
633 veth_host,
634 "type",
635 "veth",
636 "peer",
637 "name",
638 veth_guest,
639 ),
640 (
641 "ip",
642 "address",
643 "add",
644 "%s/%u" % (PROXY_HOST_IP, PROXY_NETMASK),
645 "dev",
646 veth_host,
647 ),
648 ("ip", "link", "set", veth_host, "up"),
649 (
650 [apache_bin, "-f", "/dev/null"]
651 + [arg for d in apache_directives for arg in ("-C", d)]
652 ),
653 ("ip", "link", "set", veth_guest, "netns", str(pid)),
654 )
655 cmd = None # Make cros lint happy.
656 try:
657 for cmd in commands:
658 cros_build_lib.dbg_run(cmd)
659 except cros_build_lib.RunCommandError as e:
660 # Clean up existing interfaces, if any.
661 cmd_cleanup = ("ip", "link", "del", veth_host)
662 try:
663 cros_build_lib.run(cmd_cleanup, print_cmd=False)
664 except cros_build_lib.RunCommandError:
665 logging.error("running %r failed", cmd_cleanup)
666 cros_build_lib.Die("Proxy network setup failed!\n%s", e)
667
668 # Signal the child that the net ns/proxy is fully configured now.
669 ns_setup_lock.Post()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500670 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800671
Alex Klein1699fab2022-09-08 08:46:06 -0600672 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800673
674
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400675def _BuildReExecCommand(argv, opts) -> List[str]:
Alex Klein1699fab2022-09-08 08:46:06 -0600676 """Generate new command for self-reexec."""
677 # Make sure to preserve the active Python executable in case the version
678 # we're running as is not the default one found via the (new) $PATH.
679 cmd = _SudoCommand() + ["--"]
680 if opts.strace:
681 cmd += ["strace"] + shlex.split(opts.strace_arguments) + ["--"]
682 return cmd + [sys.executable] + argv
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400683
684
685def _ReExecuteIfNeeded(argv, opts):
Alex Klein1699fab2022-09-08 08:46:06 -0600686 """Re-execute cros_sdk as root.
David James56e6c2c2012-10-24 23:54:41 -0700687
Alex Klein1699fab2022-09-08 08:46:06 -0600688 Also unshare the mount namespace so as to ensure that processes outside
689 the chroot can't mess with our mounts.
690 """
691 if osutils.IsNonRootUser():
692 cmd = _BuildReExecCommand(argv, opts)
693 logging.debug(
694 "Reexecing self via sudo:\n%s", cros_build_lib.CmdToStr(cmd)
695 )
696 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700697
698
Mike Frysinger34db8692013-11-11 14:54:08 -0500699def _CreateParser(sdk_latest_version, bootstrap_latest_version):
Alex Klein1699fab2022-09-08 08:46:06 -0600700 """Generate and return the parser with all the options."""
701 usage = (
702 "usage: %(prog)s [options] "
703 "[VAR1=val1 ... VAR2=val2] [--] [command [args]]"
704 )
705 parser = commandline.ArgumentParser(
706 usage=usage, description=__doc__, caching=True
707 )
Brian Harring218e13c2012-10-10 16:21:26 -0700708
Alex Klein1699fab2022-09-08 08:46:06 -0600709 # Global options.
Alex Klein1699fab2022-09-08 08:46:06 -0600710 parser.add_argument(
711 "--chroot",
712 dest="chroot",
Brian Norrise1760452023-02-23 15:54:40 -0800713 default=constants.DEFAULT_CHROOT_PATH,
Alex Klein1699fab2022-09-08 08:46:06 -0600714 type="path",
715 help=("SDK chroot dir name [%s]" % constants.DEFAULT_CHROOT_DIR),
716 )
717 parser.add_argument(
Dan Callaghanada9e032023-01-30 14:28:46 +1100718 "--out-dir",
719 metavar="DIR",
Brian Norrise1760452023-02-23 15:54:40 -0800720 default=constants.DEFAULT_OUT_PATH,
Dan Callaghanada9e032023-01-30 14:28:46 +1100721 type=Path,
722 help="Use DIR for build state and output files",
723 )
724 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600725 "--nouse-image",
726 dest="use_image",
727 action="store_false",
728 default=False,
729 help="Do not mount the chroot on a loopback image; "
730 "instead, create it directly in a directory.",
731 )
732 parser.add_argument(
733 "--use-image",
734 dest="use_image",
735 action="store_true",
736 default=False,
737 help="Mount the chroot on a loopback image "
738 "instead of creating it directly in a directory.",
739 )
Brian Harringb938c782012-02-29 15:14:38 -0800740
Alex Klein1699fab2022-09-08 08:46:06 -0600741 parser.add_argument(
742 "--chrome-root",
743 "--chrome_root",
744 type="path",
745 help="Mount this chrome root into the SDK chroot",
746 )
747 parser.add_argument(
748 "--chrome_root_mount",
749 type="path",
750 help="Mount chrome into this path inside SDK chroot",
751 )
752 parser.add_argument(
753 "--nousepkg",
754 action="store_true",
755 default=False,
756 help="Do not use binary packages when creating a chroot.",
757 )
758 parser.add_argument(
759 "-u",
760 "--url",
761 dest="sdk_url",
762 help="Use sdk tarball located at this url. Use file:// "
763 "for local files.",
764 )
765 parser.add_argument(
766 "--sdk-version",
767 help=(
768 "Use this sdk version. For prebuilt, current is %r"
769 ", for bootstrapping it is %r."
770 % (sdk_latest_version, bootstrap_latest_version)
771 ),
772 )
773 parser.add_argument(
Mike Frysinger85824562023-01-31 02:40:43 -0500774 "--goma-dir",
Alex Klein1699fab2022-09-08 08:46:06 -0600775 "--goma_dir",
Mike Frysinger8fc83772023-02-01 15:30:47 -0500776 type="dir_exists",
Alex Klein1699fab2022-09-08 08:46:06 -0600777 help="Goma installed directory to mount into the chroot.",
778 )
779 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600780 "--reclient-dir",
Mike Frysinger8fc83772023-02-01 15:30:47 -0500781 type="dir_exists",
Alex Klein1699fab2022-09-08 08:46:06 -0600782 help="Reclient installed directory to mount into the chroot.",
783 )
784 parser.add_argument(
785 "--reproxy-cfg-file",
Mike Frysinger8fc83772023-02-01 15:30:47 -0500786 type="file_exists",
Alex Klein1699fab2022-09-08 08:46:06 -0600787 help="Config file for re-client's reproxy used for remoteexec.",
788 )
789 parser.add_argument(
790 "--skip-chroot-upgrade",
791 dest="chroot_upgrade",
792 action="store_false",
793 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700794 help="Skip automatic SDK and toolchain upgrade when entering the "
795 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600796 )
Yong Hong84ba9172018-02-07 01:37:42 +0800797
Alex Klein1699fab2022-09-08 08:46:06 -0600798 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700799 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600800 parser.add_argument(
801 "--working-dir",
Mike Frysinger695f2fd2023-02-03 20:45:14 -0500802 type=Path,
Alex Klein1699fab2022-09-08 08:46:06 -0600803 help="Run the command in specific working directory in "
804 "chroot. If the given directory is a relative "
805 "path, this program will transfer the path to "
806 "the corresponding one inside chroot.",
807 )
Yong Hong84ba9172018-02-07 01:37:42 +0800808
Alex Klein1699fab2022-09-08 08:46:06 -0600809 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500810
Alex Klein1699fab2022-09-08 08:46:06 -0600811 # Commands.
812 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400813 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600814 "--enter",
815 action="store_true",
816 default=False,
817 help="Enter the SDK chroot. Implies --create.",
818 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400819 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600820 "--create",
821 action="store_true",
822 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700823 help="Create the chroot only if it does not already exist. Downloads "
824 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600825 )
826 group.add_argument(
827 "--bootstrap",
828 action="store_true",
829 default=False,
830 help="Build everything from scratch, including the sdk. "
831 "Use this only if you need to validate a change "
832 "that affects SDK creation itself (toolchain and "
833 "build are typically the only folk who need this). "
834 "Note this will quite heavily slow down the build. "
835 "This option implies --create --nousepkg.",
836 )
837 group.add_argument(
838 "-r",
839 "--replace",
840 action="store_true",
841 default=False,
842 help="Replace an existing SDK chroot. Basically an alias "
843 "for --delete --create.",
844 )
845 group.add_argument(
846 "--delete",
847 action="store_true",
848 default=False,
849 help="Delete the current SDK chroot if it exists.",
850 )
851 group.add_argument(
852 "--force",
853 action="store_true",
854 default=False,
855 help="Force unmount/delete of the current SDK chroot even if "
856 "obtaining the write lock fails.",
857 )
858 group.add_argument(
859 "--unmount",
860 action="store_true",
861 default=False,
862 help="Unmount and clean up devices associated with the "
863 "SDK chroot if it exists. This does not delete the "
864 "backing image file, so the same chroot can be later "
865 "re-mounted for reuse. To fully delete the chroot, use "
866 "--delete. This is primarily useful for working on "
867 "cros_sdk or the chroot setup; you should not need it "
868 "under normal circumstances.",
869 )
870 group.add_argument(
871 "--download",
872 action="store_true",
873 default=False,
874 help="Download the sdk.",
875 )
876 group.add_argument(
877 "--snapshot-create",
878 metavar="SNAPSHOT_NAME",
879 help="Create a snapshot of the chroot. Requires that the chroot was "
880 "created without the --nouse-image option.",
881 )
882 group.add_argument(
883 "--snapshot-restore",
884 metavar="SNAPSHOT_NAME",
885 help="Restore the chroot to a previously created snapshot.",
886 )
887 group.add_argument(
888 "--snapshot-delete",
889 metavar="SNAPSHOT_NAME",
890 help="Delete a previously created snapshot. Deleting a snapshot that "
891 "does not exist is not an error.",
892 )
893 group.add_argument(
894 "--snapshot-list",
895 action="store_true",
896 default=False,
897 help="List existing snapshots of the chroot and exit.",
898 )
899 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -0400900
Alex Klein1699fab2022-09-08 08:46:06 -0600901 # Namespace options.
902 group = parser.add_argument_group("Namespaces")
903 group.add_argument(
904 "--proxy-sim",
905 action="store_true",
906 default=False,
907 help="Simulate a restrictive network requiring an outbound" " proxy.",
908 )
909 for ns, default in (("pid", True), ("net", None)):
910 group.add_argument(
911 f"--ns-{ns}",
912 default=default,
913 action="store_true",
914 help=f"Create a new {ns} namespace.",
915 )
916 group.add_argument(
917 f"--no-ns-{ns}",
918 dest=f"ns_{ns}",
919 action="store_false",
920 help=f"Do not create a new {ns} namespace.",
921 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400922
Alex Klein1699fab2022-09-08 08:46:06 -0600923 # Debug options.
924 group = parser.debug_group
925 group.add_argument(
926 "--strace",
927 action="store_true",
928 help="Run cros_sdk through strace after re-exec via sudo",
929 )
930 group.add_argument(
931 "--strace-arguments",
932 default="",
933 help="Extra strace options (shell quoting permitted)",
934 )
Mike Frysinger34db8692013-11-11 14:54:08 -0500935
Alex Klein1699fab2022-09-08 08:46:06 -0600936 # Internal options.
937 group = parser.add_argument_group(
938 "Internal Chromium OS Build Team Options",
939 "Caution: these are for meant for the Chromium OS build team only",
940 )
941 group.add_argument(
942 "--buildbot-log-version",
943 default=False,
944 action="store_true",
945 help="Log SDK version for buildbot consumption",
946 )
947
948 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500949
950
951def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600952 # Turn on strict sudo checks.
953 cros_build_lib.STRICT_SUDO = True
954 conf = key_value_store.LoadFile(
955 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
956 ignore_missing=True,
957 )
958 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
959 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -0700960
Alex Klein1699fab2022-09-08 08:46:06 -0600961 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
962 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
963 bootstrap_latest_version = (
964 sdk_latest_version
965 if bootstrap_frozen_version == "<unknown>"
966 else bootstrap_frozen_version
967 )
968 parser, commands = _CreateParser(
969 sdk_latest_version, bootstrap_latest_version
970 )
971 options = parser.parse_args(argv)
Brian Harringb938c782012-02-29 15:14:38 -0800972
Alex Klein1699fab2022-09-08 08:46:06 -0600973 # Some basic checks first, before we ask for sudo credentials.
974 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800975
Alex Klein1699fab2022-09-08 08:46:06 -0600976 host = os.uname()[4]
977 if host != "x86_64":
978 cros_build_lib.Die(
979 "cros_sdk is currently only supported on x86_64; you're running"
980 " %s. Please find a x86_64 machine." % (host,)
981 )
Brian Harring1790ac42012-09-23 08:53:33 -0700982
Brian Norriscf031912023-02-21 15:04:27 -0800983 goma = (
984 goma_lib.Goma(options.goma_dir, chroot_dir=options.chroot)
985 if options.goma_dir
986 else None
987 )
Mike Frysinger9a5124e2023-01-26 17:16:44 -0500988
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 there is an existing chroot image and we're not removing it then force
1069 # use_image on. This ensures that people don't have to remember to pass
Alex Kleinef517832023-01-13 12:06:51 -07001070 # --use-image after a reboot to avoid losing access to their existing
1071 # chroot.
Alex Klein1699fab2022-09-08 08:46:06 -06001072 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1073 img_path = _ImageFileForChroot(options.chroot)
1074 if (
1075 not options.use_image
1076 and not options.delete
1077 and not options.unmount
1078 and os.path.exists(img_path)
1079 ):
1080 if chroot_exists:
1081 # If the chroot is already populated, make sure it has something
1082 # mounted on it before we assume it came from an image.
1083 cmd = ["mountpoint", "-q", options.chroot]
1084 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
1085 options.use_image = True
Benjamin Gordon832a4412020-12-08 10:39:16 -07001086
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001087 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001088 logging.notice(
1089 "Existing chroot image %s found. Forcing --use-image on.",
1090 img_path,
1091 )
1092 options.use_image = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001093
Alex Klein1699fab2022-09-08 08:46:06 -06001094 if any_snapshot_operation and not options.use_image:
1095 if os.path.exists(img_path):
1096 options.use_image = True
1097 else:
1098 cros_build_lib.Die(
1099 "Snapshot operations are not compatible with " "--nouse-image."
1100 )
Mike Frysingerc60141b2023-01-27 00:57:15 -05001101 if options.use_image:
1102 logging.warning("--use-image is deprecated and will be removed soon.")
1103 logging.warning("Please migrate, or create a new one with --delete.")
1104 logging.warning("See http://b/266878468 for details.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001105
Alex Klein1699fab2022-09-08 08:46:06 -06001106 # Discern if we need to create the chroot.
1107 if (
1108 options.use_image
1109 and not chroot_exists
1110 and not options.delete
1111 and not options.unmount
1112 and not missing_image_tools
1113 and os.path.exists(img_path)
1114 ):
1115 # Try to re-mount an existing image in case the user has rebooted.
1116 with locking.FileLock(lock_path, "chroot lock") as lock:
1117 logging.debug("Checking if existing chroot image can be mounted.")
1118 lock.write_lock()
1119 cros_sdk_lib.MountChroot(options.chroot, create=False)
1120 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1121 if chroot_exists:
1122 logging.notice("Mounted existing image %s on chroot", img_path)
1123
1124 # Finally, flip create if necessary.
1125 if options.enter or options.snapshot_create:
1126 options.create |= not chroot_exists
1127
1128 # Make sure we will download if we plan to create.
1129 options.download |= options.create
1130
Mike Frysinger6bbd3e92023-01-27 00:05:02 -05001131 options.Freeze()
1132
Mike Frysingerded13922023-02-01 15:31:59 -05001133 if options.reclient_dir and not options.reproxy_cfg_file:
1134 cros_build_lib.Die("--reclient-dir requires --reproxy-cfg-file")
1135 if not options.reclient_dir and options.reproxy_cfg_file:
1136 cros_build_lib.Die(
1137 "--reproxy-cfg-file only makes sense with --reclient-dir"
1138 )
1139
Mike Frysinger253c8392023-01-27 01:12:21 -05001140 remoteexec = (
1141 remoteexec_util.Remoteexec(
1142 options.reclient_dir, options.reproxy_cfg_file
1143 )
1144 if (options.reclient_dir and options.reproxy_cfg_file)
1145 else None
1146 )
1147
Mike Frysingerec32bea2023-01-27 01:20:48 -05001148 chroot = chroot_lib.Chroot(
1149 path=options.chroot,
1150 cache_dir=options.cache_dir,
1151 chrome_root=options.chrome_root,
1152 goma=goma,
1153 remoteexec=remoteexec,
1154 )
1155
Alex Kleinef517832023-01-13 12:06:51 -07001156 # Anything that needs to manipulate the main chroot mount or communicate
1157 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -06001158
1159 # If deleting, do it regardless of the use_image flag so that a
1160 # previously-created loopback chroot can also be cleaned up.
1161 if options.delete:
1162 # Set a timeout of 300 seconds when getting the lock.
1163 with locking.FileLock(
1164 lock_path, "chroot lock", blocking_timeout=300
1165 ) as lock:
1166 try:
1167 lock.write_lock()
1168 except timeout_util.TimeoutError as e:
1169 logging.error(
1170 "Acquiring write_lock on %s failed: %s", lock_path, e
1171 )
1172 if not options.force:
1173 cros_build_lib.Die(
1174 "Exiting; use --force to continue w/o lock."
1175 )
1176 else:
1177 logging.warning(
1178 "cros_sdk was invoked with force option, continuing."
1179 )
1180 logging.notice("Deleting chroot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001181 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=True)
Alex Klein1699fab2022-09-08 08:46:06 -06001182
Alex Kleinef517832023-01-13 12:06:51 -07001183 # If cleanup was requested, we have to do it while we're still in the
1184 # original namespace. Since cleaning up the mount will interfere with any
1185 # other commands, we exit here. The check above should have made sure that
1186 # no other action was requested, anyway.
Alex Klein1699fab2022-09-08 08:46:06 -06001187 if options.unmount:
1188 # Set a timeout of 300 seconds when getting the lock.
1189 with locking.FileLock(
1190 lock_path, "chroot lock", blocking_timeout=300
1191 ) as lock:
1192 try:
1193 lock.write_lock()
1194 except timeout_util.TimeoutError as e:
1195 logging.error(
1196 "Acquiring write_lock on %s failed: %s", lock_path, e
1197 )
1198 logging.warning(
Alex Kleinef517832023-01-13 12:06:51 -07001199 "Continuing with CleanupChroot(%s), which will umount the "
1200 "tree.",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001201 chroot.path,
Alex Klein1699fab2022-09-08 08:46:06 -06001202 )
Alex Kleinef517832023-01-13 12:06:51 -07001203 # We can call CleanupChroot (which calls
1204 # cros_sdk_lib.CleanupChrootMount) even if we don't get the lock
1205 # because it will attempt to unmount the tree and will print
1206 # diagnostic information from 'fuser', 'lsof', and 'ps'.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001207 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=False)
Alex Klein1699fab2022-09-08 08:46:06 -06001208 sys.exit(0)
1209
1210 # Make sure the main chroot mount is visible. Contents will be filled in
1211 # below if needed.
1212 if options.create and options.use_image:
1213 if missing_image_tools:
1214 raise SystemExit(
1215 """The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001216Please make sure the lvm2 and thin-provisioning-tools packages
1217are installed on your host.
1218Example(ubuntu):
1219 sudo apt-get install lvm2 thin-provisioning-tools
1220
1221If you want to run without lvm2, pass --nouse-image (chroot
Alex Klein1699fab2022-09-08 08:46:06 -06001222snapshots will be unavailable)."""
1223 % ", ".join(missing_image_tools)
1224 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001225
Alex Klein1699fab2022-09-08 08:46:06 -06001226 logging.debug("Making sure chroot image is mounted.")
1227 with locking.FileLock(lock_path, "chroot lock") as lock:
1228 lock.write_lock()
Mike Frysingerec32bea2023-01-27 01:20:48 -05001229 if not cros_sdk_lib.MountChroot(chroot.path, create=True):
Alex Klein1699fab2022-09-08 08:46:06 -06001230 cros_build_lib.Die(
1231 "Unable to mount %s on chroot",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001232 _ImageFileForChroot(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001233 )
1234 logging.notice(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001235 "Mounted %s on chroot", _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001236 )
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001237
Alex Klein1699fab2022-09-08 08:46:06 -06001238 # Snapshot operations will always need the VG/LV, but other actions won't.
1239 if any_snapshot_operation:
1240 with locking.FileLock(lock_path, "chroot lock") as lock:
1241 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001242 chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001243 )
1244 if not chroot_vg or not chroot_lv:
1245 cros_build_lib.Die(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001246 "Unable to find VG/LV for chroot %s", chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001247 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001248
Alex Kleinef517832023-01-13 12:06:51 -07001249 # Delete snapshot before creating a new one. This allows the user to
1250 # throw out old state, create a new snapshot, and enter the chroot
1251 # in a single call to cros_sdk. Since restore involves deleting,
1252 # also do it before creating.
Alex Klein1699fab2022-09-08 08:46:06 -06001253 if options.snapshot_restore:
1254 lock.write_lock()
1255 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1256 if options.snapshot_restore not in valid_snapshots:
1257 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -07001258 "%s is not a valid snapshot to restore to. "
1259 "Valid snapshots: %s",
Alex Klein1699fab2022-09-08 08:46:06 -06001260 options.snapshot_restore,
1261 ", ".join(valid_snapshots),
1262 )
Mike Frysingerec32bea2023-01-27 01:20:48 -05001263 osutils.UmountTree(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001264 if not RestoreChrootSnapshot(
1265 options.snapshot_restore, chroot_vg, chroot_lv
1266 ):
1267 cros_build_lib.Die("Unable to restore chroot to snapshot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001268 if not cros_sdk_lib.MountChroot(chroot.path, create=False):
Alex Klein1699fab2022-09-08 08:46:06 -06001269 cros_build_lib.Die(
1270 "Unable to mount restored snapshot onto chroot."
1271 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001272
Alex Kleinef517832023-01-13 12:06:51 -07001273 # Use a read lock for snapshot delete and create even though they
1274 # modify the filesystem, because they don't modify the mounted
1275 # chroot itself. The underlying LVM commands take their own locks,
1276 # so conflicting concurrent operations here may crash cros_sdk, but
1277 # won't corrupt the chroot image. This tradeoff seems worth it to
1278 # allow snapshot operations on chroots that have a process inside.
Alex Klein1699fab2022-09-08 08:46:06 -06001279 if options.snapshot_delete:
1280 lock.read_lock()
1281 DeleteChrootSnapshot(
1282 options.snapshot_delete, chroot_vg, chroot_lv
1283 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001284
Alex Klein1699fab2022-09-08 08:46:06 -06001285 if options.snapshot_create:
1286 lock.read_lock()
1287 if not CreateChrootSnapshot(
1288 options.snapshot_create, chroot_vg, chroot_lv
1289 ):
1290 cros_build_lib.Die("Unable to create snapshot.")
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001291
Mike Frysingerec32bea2023-01-27 01:20:48 -05001292 img_path = _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001293 if (
1294 options.use_image
Mike Frysingerec32bea2023-01-27 01:20:48 -05001295 and os.path.exists(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001296 and os.path.exists(img_path)
1297 ):
1298 img_stat = os.stat(img_path)
1299 img_used_bytes = img_stat.st_blocks * 512
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001300
Mike Frysingerec32bea2023-01-27 01:20:48 -05001301 mount_stat = os.statvfs(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001302 mount_used_bytes = mount_stat.f_frsize * (
1303 mount_stat.f_blocks - mount_stat.f_bfree
1304 )
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001305
Alex Klein1699fab2022-09-08 08:46:06 -06001306 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
1307 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1308 logging.notice(
1309 "%s is using %s GiB more than needed. Running "
1310 "fstrim in background.",
1311 img_path,
1312 extra_gbs,
1313 )
1314 pid = os.fork()
1315 if pid == 0:
1316 try:
1317 # Directly call Popen to run fstrim concurrently.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001318 cmd = ["fstrim", chroot.path]
Alex Klein1699fab2022-09-08 08:46:06 -06001319 subprocess.Popen(cmd, close_fds=True, shell=False)
1320 except subprocess.SubprocessError as e:
1321 logging.warning(
1322 "Running fstrim failed. Consider running fstrim on "
1323 "your chroot manually.\n%s",
1324 e,
1325 )
1326 os._exit(0) # pylint: disable=protected-access
1327 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001328
Alex Kleinef517832023-01-13 12:06:51 -07001329 # Enter a new set of namespaces. Everything after here cannot directly
1330 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -06001331 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001332
Alex Klein1699fab2022-09-08 08:46:06 -06001333 if options.snapshot_list:
1334 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1335 print(snap)
1336 sys.exit(0)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001337
Alex Klein1699fab2022-09-08 08:46:06 -06001338 if not options.sdk_version:
1339 sdk_version = (
1340 bootstrap_latest_version
1341 if options.bootstrap
1342 else sdk_latest_version
1343 )
Yong Hong4e29b622018-02-05 14:31:10 +08001344 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001345 sdk_version = options.sdk_version
1346 if options.buildbot_log_version:
1347 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001348
Alex Klein1699fab2022-09-08 08:46:06 -06001349 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -07001350 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001351 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -06001352 if options.sdk_url:
1353 urls = [options.sdk_url]
1354 else:
1355 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -07001356
Alex Klein1699fab2022-09-08 08:46:06 -06001357 with locking.FileLock(lock_path, "chroot lock") as lock:
1358 if options.proxy_sim:
1359 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -07001360
Mike Frysingerec32bea2023-01-27 01:20:48 -05001361 sdk_cache = os.path.join(chroot.cache_dir, "sdks")
1362 distfiles_cache = os.path.join(chroot.cache_dir, "distfiles")
1363 osutils.SafeMakedirsNonRoot(chroot.cache_dir)
Dan Callaghanada9e032023-01-30 14:28:46 +11001364 osutils.SafeMakedirsNonRoot(options.out_dir)
Alex Klein1699fab2022-09-08 08:46:06 -06001365
1366 for target in (sdk_cache, distfiles_cache):
1367 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1368 if not os.path.exists(src):
1369 osutils.SafeMakedirsNonRoot(target)
1370 continue
1371 lock.write_lock(
1372 "Upgrade to %r needed but chroot is locked; please exit "
1373 "all instances so this upgrade can finish." % src
1374 )
1375 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -07001376 # Note that while waiting for the write lock, src may've
1377 # vanished; it's a rare race during the upgrade process that's a
1378 # byproduct of us avoiding taking a write lock to do the src
1379 # check. If we took a write lock for that check, it would
1380 # effectively limit all cros_sdk for a chroot to a single
1381 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -06001382 osutils.SafeMakedirsNonRoot(target)
1383 elif not os.path.exists(target):
1384 # Upgrade occurred, but a reversion, or something whacky
1385 # occurred writing to the old location. Wipe and continue.
1386 os.rename(src, target)
1387 else:
1388 # Upgrade occurred once already, but either a reversion or
1389 # some before/after separate cros_sdk usage is at play.
1390 # Wipe and continue.
1391 osutils.RmDir(src)
1392
Alex Klein1699fab2022-09-08 08:46:06 -06001393 mounted = False
1394 if options.create:
1395 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -07001396 # Recheck if the chroot is set up here before creating to make sure
1397 # we account for whatever the various delete/unmount/remount steps
1398 # above have done.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001399 if cros_sdk_lib.IsChrootReady(chroot.path):
Alex Klein1699fab2022-09-08 08:46:06 -06001400 logging.debug("Chroot already exists. Skipping creation.")
1401 else:
Alex Klein22690a12022-11-17 10:56:09 -07001402 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001403 cros_sdk_lib.CreateChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001404 Path(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001405 Path(sdk_tarball),
Dan Callaghanada9e032023-01-30 14:28:46 +11001406 options.out_dir,
Mike Frysingerec32bea2023-01-27 01:20:48 -05001407 Path(chroot.cache_dir),
Alex Klein1699fab2022-09-08 08:46:06 -06001408 usepkg=not options.bootstrap and not options.nousepkg,
1409 chroot_upgrade=options.chroot_upgrade,
1410 )
1411 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -07001412 elif options.download:
1413 # Allow downloading only.
1414 lock.write_lock()
1415 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001416
1417 if options.enter:
1418 lock.read_lock()
1419 if not mounted:
Dan Callaghanada9e032023-01-30 14:28:46 +11001420 cros_sdk_lib.MountChrootPaths(chroot.path, options.out_dir)
Mike Frysinger53baf882021-06-24 23:16:50 -04001421 ret = cros_sdk_lib.EnterChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001422 chroot,
Mike Frysinger53baf882021-06-24 23:16:50 -04001423 chrome_root_mount=options.chrome_root_mount,
1424 cwd=options.working_dir,
1425 cmd=options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -06001426 )
1427 sys.exit(ret.returncode)