blob: 170725a65540e89d0f5d7357fd903b629f03564f [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.
710 default_chroot = os.path.join(
711 constants.SOURCE_ROOT, constants.DEFAULT_CHROOT_DIR
712 )
713 parser.add_argument(
714 "--chroot",
715 dest="chroot",
716 default=default_chroot,
717 type="path",
718 help=("SDK chroot dir name [%s]" % constants.DEFAULT_CHROOT_DIR),
719 )
720 parser.add_argument(
Dan Callaghanada9e032023-01-30 14:28:46 +1100721 "--out-dir",
722 metavar="DIR",
723 default=constants.DEFAULT_OUT_DIR,
724 type=Path,
725 help="Use DIR for build state and output files",
726 )
727 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600728 "--nouse-image",
729 dest="use_image",
730 action="store_false",
731 default=False,
732 help="Do not mount the chroot on a loopback image; "
733 "instead, create it directly in a directory.",
734 )
735 parser.add_argument(
736 "--use-image",
737 dest="use_image",
738 action="store_true",
739 default=False,
740 help="Mount the chroot on a loopback image "
741 "instead of creating it directly in a directory.",
742 )
Brian Harringb938c782012-02-29 15:14:38 -0800743
Alex Klein1699fab2022-09-08 08:46:06 -0600744 parser.add_argument(
745 "--chrome-root",
746 "--chrome_root",
747 type="path",
748 help="Mount this chrome root into the SDK chroot",
749 )
750 parser.add_argument(
751 "--chrome_root_mount",
752 type="path",
753 help="Mount chrome into this path inside SDK chroot",
754 )
755 parser.add_argument(
756 "--nousepkg",
757 action="store_true",
758 default=False,
759 help="Do not use binary packages when creating a chroot.",
760 )
761 parser.add_argument(
762 "-u",
763 "--url",
764 dest="sdk_url",
765 help="Use sdk tarball located at this url. Use file:// "
766 "for local files.",
767 )
768 parser.add_argument(
769 "--sdk-version",
770 help=(
771 "Use this sdk version. For prebuilt, current is %r"
772 ", for bootstrapping it is %r."
773 % (sdk_latest_version, bootstrap_latest_version)
774 ),
775 )
776 parser.add_argument(
Mike Frysinger85824562023-01-31 02:40:43 -0500777 "--goma-dir",
Alex Klein1699fab2022-09-08 08:46:06 -0600778 "--goma_dir",
Mike Frysinger8fc83772023-02-01 15:30:47 -0500779 type="dir_exists",
Alex Klein1699fab2022-09-08 08:46:06 -0600780 help="Goma installed directory to mount into the chroot.",
781 )
782 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600783 "--reclient-dir",
Mike Frysinger8fc83772023-02-01 15:30:47 -0500784 type="dir_exists",
Alex Klein1699fab2022-09-08 08:46:06 -0600785 help="Reclient installed directory to mount into the chroot.",
786 )
787 parser.add_argument(
788 "--reproxy-cfg-file",
Mike Frysinger8fc83772023-02-01 15:30:47 -0500789 type="file_exists",
Alex Klein1699fab2022-09-08 08:46:06 -0600790 help="Config file for re-client's reproxy used for remoteexec.",
791 )
792 parser.add_argument(
793 "--skip-chroot-upgrade",
794 dest="chroot_upgrade",
795 action="store_false",
796 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700797 help="Skip automatic SDK and toolchain upgrade when entering the "
798 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600799 )
Yong Hong84ba9172018-02-07 01:37:42 +0800800
Alex Klein1699fab2022-09-08 08:46:06 -0600801 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700802 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600803 parser.add_argument(
804 "--working-dir",
Mike Frysinger695f2fd2023-02-03 20:45:14 -0500805 type=Path,
Alex Klein1699fab2022-09-08 08:46:06 -0600806 help="Run the command in specific working directory in "
807 "chroot. If the given directory is a relative "
808 "path, this program will transfer the path to "
809 "the corresponding one inside chroot.",
810 )
Yong Hong84ba9172018-02-07 01:37:42 +0800811
Alex Klein1699fab2022-09-08 08:46:06 -0600812 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500813
Alex Klein1699fab2022-09-08 08:46:06 -0600814 # Commands.
815 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400816 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600817 "--enter",
818 action="store_true",
819 default=False,
820 help="Enter the SDK chroot. Implies --create.",
821 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400822 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600823 "--create",
824 action="store_true",
825 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700826 help="Create the chroot only if it does not already exist. Downloads "
827 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600828 )
829 group.add_argument(
830 "--bootstrap",
831 action="store_true",
832 default=False,
833 help="Build everything from scratch, including the sdk. "
834 "Use this only if you need to validate a change "
835 "that affects SDK creation itself (toolchain and "
836 "build are typically the only folk who need this). "
837 "Note this will quite heavily slow down the build. "
838 "This option implies --create --nousepkg.",
839 )
840 group.add_argument(
841 "-r",
842 "--replace",
843 action="store_true",
844 default=False,
845 help="Replace an existing SDK chroot. Basically an alias "
846 "for --delete --create.",
847 )
848 group.add_argument(
849 "--delete",
850 action="store_true",
851 default=False,
852 help="Delete the current SDK chroot if it exists.",
853 )
854 group.add_argument(
855 "--force",
856 action="store_true",
857 default=False,
858 help="Force unmount/delete of the current SDK chroot even if "
859 "obtaining the write lock fails.",
860 )
861 group.add_argument(
862 "--unmount",
863 action="store_true",
864 default=False,
865 help="Unmount and clean up devices associated with the "
866 "SDK chroot if it exists. This does not delete the "
867 "backing image file, so the same chroot can be later "
868 "re-mounted for reuse. To fully delete the chroot, use "
869 "--delete. This is primarily useful for working on "
870 "cros_sdk or the chroot setup; you should not need it "
871 "under normal circumstances.",
872 )
873 group.add_argument(
874 "--download",
875 action="store_true",
876 default=False,
877 help="Download the sdk.",
878 )
879 group.add_argument(
880 "--snapshot-create",
881 metavar="SNAPSHOT_NAME",
882 help="Create a snapshot of the chroot. Requires that the chroot was "
883 "created without the --nouse-image option.",
884 )
885 group.add_argument(
886 "--snapshot-restore",
887 metavar="SNAPSHOT_NAME",
888 help="Restore the chroot to a previously created snapshot.",
889 )
890 group.add_argument(
891 "--snapshot-delete",
892 metavar="SNAPSHOT_NAME",
893 help="Delete a previously created snapshot. Deleting a snapshot that "
894 "does not exist is not an error.",
895 )
896 group.add_argument(
897 "--snapshot-list",
898 action="store_true",
899 default=False,
900 help="List existing snapshots of the chroot and exit.",
901 )
902 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -0400903
Alex Klein1699fab2022-09-08 08:46:06 -0600904 # Namespace options.
905 group = parser.add_argument_group("Namespaces")
906 group.add_argument(
907 "--proxy-sim",
908 action="store_true",
909 default=False,
910 help="Simulate a restrictive network requiring an outbound" " proxy.",
911 )
912 for ns, default in (("pid", True), ("net", None)):
913 group.add_argument(
914 f"--ns-{ns}",
915 default=default,
916 action="store_true",
917 help=f"Create a new {ns} namespace.",
918 )
919 group.add_argument(
920 f"--no-ns-{ns}",
921 dest=f"ns_{ns}",
922 action="store_false",
923 help=f"Do not create a new {ns} namespace.",
924 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400925
Alex Klein1699fab2022-09-08 08:46:06 -0600926 # Debug options.
927 group = parser.debug_group
928 group.add_argument(
929 "--strace",
930 action="store_true",
931 help="Run cros_sdk through strace after re-exec via sudo",
932 )
933 group.add_argument(
934 "--strace-arguments",
935 default="",
936 help="Extra strace options (shell quoting permitted)",
937 )
Mike Frysinger34db8692013-11-11 14:54:08 -0500938
Alex Klein1699fab2022-09-08 08:46:06 -0600939 # Internal options.
940 group = parser.add_argument_group(
941 "Internal Chromium OS Build Team Options",
942 "Caution: these are for meant for the Chromium OS build team only",
943 )
944 group.add_argument(
945 "--buildbot-log-version",
946 default=False,
947 action="store_true",
948 help="Log SDK version for buildbot consumption",
949 )
950
951 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500952
953
954def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600955 # Turn on strict sudo checks.
956 cros_build_lib.STRICT_SUDO = True
957 conf = key_value_store.LoadFile(
958 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
959 ignore_missing=True,
960 )
961 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
962 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -0700963
Alex Klein1699fab2022-09-08 08:46:06 -0600964 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
965 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
966 bootstrap_latest_version = (
967 sdk_latest_version
968 if bootstrap_frozen_version == "<unknown>"
969 else bootstrap_frozen_version
970 )
971 parser, commands = _CreateParser(
972 sdk_latest_version, bootstrap_latest_version
973 )
974 options = parser.parse_args(argv)
Brian Harringb938c782012-02-29 15:14:38 -0800975
Alex Klein1699fab2022-09-08 08:46:06 -0600976 # Some basic checks first, before we ask for sudo credentials.
977 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800978
Alex Klein1699fab2022-09-08 08:46:06 -0600979 host = os.uname()[4]
980 if host != "x86_64":
981 cros_build_lib.Die(
982 "cros_sdk is currently only supported on x86_64; you're running"
983 " %s. Please find a x86_64 machine." % (host,)
984 )
Brian Harring1790ac42012-09-23 08:53:33 -0700985
Mike Frysinger9a5124e2023-01-26 17:16:44 -0500986 goma = goma_lib.Goma(options.goma_dir) if options.goma_dir else None
987
Alex Klein1699fab2022-09-08 08:46:06 -0600988 # Merge the outside PATH setting if we re-execed ourselves.
989 if "CHROMEOS_SUDO_PATH" in os.environ:
990 os.environ["PATH"] = "%s:%s" % (
991 os.environ.pop("CHROMEOS_SUDO_PATH"),
992 os.environ["PATH"],
993 )
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400994
Alex Klein1699fab2022-09-08 08:46:06 -0600995 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
996 if options.proxy_sim:
997 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
998 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -0800999
Alex Klein1699fab2022-09-08 08:46:06 -06001000 if (
1001 sdk_latest_version == "<unknown>"
1002 or bootstrap_latest_version == "<unknown>"
1003 ):
1004 cros_build_lib.Die(
1005 "No SDK version was found. "
1006 "Are you in a Chromium source tree instead of Chromium OS?\n\n"
1007 "Please change to a directory inside your Chromium OS source tree\n"
1008 "and retry. If you need to setup a Chromium OS source tree, see\n"
1009 " https://dev.chromium.org/chromium-os/developer-guide"
1010 )
Benjamin Gordon040a1162017-06-29 13:44:47 -06001011
Alex Klein1699fab2022-09-08 08:46:06 -06001012 any_snapshot_operation = (
1013 options.snapshot_create
1014 or options.snapshot_restore
1015 or options.snapshot_delete
1016 or options.snapshot_list
1017 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001018
Alex Klein1699fab2022-09-08 08:46:06 -06001019 if (
1020 options.snapshot_delete
1021 and options.snapshot_delete == options.snapshot_restore
1022 ):
1023 parser.error(
1024 "Cannot --snapshot_delete the same snapshot you are "
1025 "restoring with --snapshot_restore."
1026 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001027
Alex Klein1699fab2022-09-08 08:46:06 -06001028 _ReExecuteIfNeeded([sys.argv[0]] + argv, options)
David James471532c2013-01-21 10:23:31 -08001029
Alex Klein1699fab2022-09-08 08:46:06 -06001030 lock_path = os.path.dirname(options.chroot)
1031 lock_path = os.path.join(
1032 lock_path, ".%s_lock" % os.path.basename(options.chroot).lstrip(".")
1033 )
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001034
Alex Klein1699fab2022-09-08 08:46:06 -06001035 # Expand out the aliases...
1036 if options.replace:
1037 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001038
Alex Klein1699fab2022-09-08 08:46:06 -06001039 if options.bootstrap:
1040 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001041
Alex Klein1699fab2022-09-08 08:46:06 -06001042 # If a command is not given, default to enter.
1043 # pylint: disable=protected-access
1044 # This _group_actions access sucks, but upstream decided to not include an
1045 # alternative to optparse's option_list, and this is what they recommend.
1046 options.enter |= not any(
1047 getattr(options, x.dest) for x in commands._group_actions
1048 )
1049 # pylint: enable=protected-access
Mike Frysinger114a7b92023-01-26 19:08:57 -05001050 options.enter |= bool(options.commands)
Brian Harring218e13c2012-10-10 16:21:26 -07001051
Alex Klein1699fab2022-09-08 08:46:06 -06001052 if (
1053 options.delete
1054 and not options.create
1055 and (options.enter or any_snapshot_operation)
1056 ):
1057 parser.error(
1058 "Trying to enter or snapshot the chroot when --delete "
1059 "was specified makes no sense."
1060 )
Brian Harring218e13c2012-10-10 16:21:26 -07001061
Alex Klein1699fab2022-09-08 08:46:06 -06001062 if options.unmount and (
1063 options.create or options.enter or any_snapshot_operation
1064 ):
1065 parser.error("--unmount cannot be specified with other chroot actions.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001066
Alex Klein1699fab2022-09-08 08:46:06 -06001067 # If there is an existing chroot image and we're not removing it then force
1068 # use_image on. This ensures that people don't have to remember to pass
Alex Kleinef517832023-01-13 12:06:51 -07001069 # --use-image after a reboot to avoid losing access to their existing
1070 # chroot.
Alex Klein1699fab2022-09-08 08:46:06 -06001071 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1072 img_path = _ImageFileForChroot(options.chroot)
1073 if (
1074 not options.use_image
1075 and not options.delete
1076 and not options.unmount
1077 and os.path.exists(img_path)
1078 ):
1079 if chroot_exists:
1080 # If the chroot is already populated, make sure it has something
1081 # mounted on it before we assume it came from an image.
1082 cmd = ["mountpoint", "-q", options.chroot]
1083 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
1084 options.use_image = True
Benjamin Gordon832a4412020-12-08 10:39:16 -07001085
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001086 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001087 logging.notice(
1088 "Existing chroot image %s found. Forcing --use-image on.",
1089 img_path,
1090 )
1091 options.use_image = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001092
Alex Klein1699fab2022-09-08 08:46:06 -06001093 if any_snapshot_operation and not options.use_image:
1094 if os.path.exists(img_path):
1095 options.use_image = True
1096 else:
1097 cros_build_lib.Die(
1098 "Snapshot operations are not compatible with " "--nouse-image."
1099 )
Mike Frysingerc60141b2023-01-27 00:57:15 -05001100 if options.use_image:
1101 logging.warning("--use-image is deprecated and will be removed soon.")
1102 logging.warning("Please migrate, or create a new one with --delete.")
1103 logging.warning("See http://b/266878468 for details.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001104
Alex Klein1699fab2022-09-08 08:46:06 -06001105 # Discern if we need to create the chroot.
1106 if (
1107 options.use_image
1108 and not chroot_exists
1109 and not options.delete
1110 and not options.unmount
1111 and not missing_image_tools
1112 and os.path.exists(img_path)
1113 ):
1114 # Try to re-mount an existing image in case the user has rebooted.
1115 with locking.FileLock(lock_path, "chroot lock") as lock:
1116 logging.debug("Checking if existing chroot image can be mounted.")
1117 lock.write_lock()
1118 cros_sdk_lib.MountChroot(options.chroot, create=False)
1119 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1120 if chroot_exists:
1121 logging.notice("Mounted existing image %s on chroot", img_path)
1122
1123 # Finally, flip create if necessary.
1124 if options.enter or options.snapshot_create:
1125 options.create |= not chroot_exists
1126
1127 # Make sure we will download if we plan to create.
1128 options.download |= options.create
1129
Mike Frysinger6bbd3e92023-01-27 00:05:02 -05001130 options.Freeze()
1131
Mike Frysingerded13922023-02-01 15:31:59 -05001132 if options.reclient_dir and not options.reproxy_cfg_file:
1133 cros_build_lib.Die("--reclient-dir requires --reproxy-cfg-file")
1134 if not options.reclient_dir and options.reproxy_cfg_file:
1135 cros_build_lib.Die(
1136 "--reproxy-cfg-file only makes sense with --reclient-dir"
1137 )
1138
Mike Frysinger253c8392023-01-27 01:12:21 -05001139 remoteexec = (
1140 remoteexec_util.Remoteexec(
1141 options.reclient_dir, options.reproxy_cfg_file
1142 )
1143 if (options.reclient_dir and options.reproxy_cfg_file)
1144 else None
1145 )
1146
Mike Frysingerec32bea2023-01-27 01:20:48 -05001147 chroot = chroot_lib.Chroot(
1148 path=options.chroot,
1149 cache_dir=options.cache_dir,
1150 chrome_root=options.chrome_root,
1151 goma=goma,
1152 remoteexec=remoteexec,
1153 )
1154
Alex Kleinef517832023-01-13 12:06:51 -07001155 # Anything that needs to manipulate the main chroot mount or communicate
1156 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -06001157
1158 # If deleting, do it regardless of the use_image flag so that a
1159 # previously-created loopback chroot can also be cleaned up.
1160 if options.delete:
1161 # Set a timeout of 300 seconds when getting the lock.
1162 with locking.FileLock(
1163 lock_path, "chroot lock", blocking_timeout=300
1164 ) as lock:
1165 try:
1166 lock.write_lock()
1167 except timeout_util.TimeoutError as e:
1168 logging.error(
1169 "Acquiring write_lock on %s failed: %s", lock_path, e
1170 )
1171 if not options.force:
1172 cros_build_lib.Die(
1173 "Exiting; use --force to continue w/o lock."
1174 )
1175 else:
1176 logging.warning(
1177 "cros_sdk was invoked with force option, continuing."
1178 )
1179 logging.notice("Deleting chroot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001180 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=True)
Alex Klein1699fab2022-09-08 08:46:06 -06001181
Alex Kleinef517832023-01-13 12:06:51 -07001182 # If cleanup was requested, we have to do it while we're still in the
1183 # original namespace. Since cleaning up the mount will interfere with any
1184 # other commands, we exit here. The check above should have made sure that
1185 # no other action was requested, anyway.
Alex Klein1699fab2022-09-08 08:46:06 -06001186 if options.unmount:
1187 # Set a timeout of 300 seconds when getting the lock.
1188 with locking.FileLock(
1189 lock_path, "chroot lock", blocking_timeout=300
1190 ) as lock:
1191 try:
1192 lock.write_lock()
1193 except timeout_util.TimeoutError as e:
1194 logging.error(
1195 "Acquiring write_lock on %s failed: %s", lock_path, e
1196 )
1197 logging.warning(
Alex Kleinef517832023-01-13 12:06:51 -07001198 "Continuing with CleanupChroot(%s), which will umount the "
1199 "tree.",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001200 chroot.path,
Alex Klein1699fab2022-09-08 08:46:06 -06001201 )
Alex Kleinef517832023-01-13 12:06:51 -07001202 # We can call CleanupChroot (which calls
1203 # cros_sdk_lib.CleanupChrootMount) even if we don't get the lock
1204 # because it will attempt to unmount the tree and will print
1205 # diagnostic information from 'fuser', 'lsof', and 'ps'.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001206 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=False)
Alex Klein1699fab2022-09-08 08:46:06 -06001207 sys.exit(0)
1208
1209 # Make sure the main chroot mount is visible. Contents will be filled in
1210 # below if needed.
1211 if options.create and options.use_image:
1212 if missing_image_tools:
1213 raise SystemExit(
1214 """The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001215Please make sure the lvm2 and thin-provisioning-tools packages
1216are installed on your host.
1217Example(ubuntu):
1218 sudo apt-get install lvm2 thin-provisioning-tools
1219
1220If you want to run without lvm2, pass --nouse-image (chroot
Alex Klein1699fab2022-09-08 08:46:06 -06001221snapshots will be unavailable)."""
1222 % ", ".join(missing_image_tools)
1223 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001224
Alex Klein1699fab2022-09-08 08:46:06 -06001225 logging.debug("Making sure chroot image is mounted.")
1226 with locking.FileLock(lock_path, "chroot lock") as lock:
1227 lock.write_lock()
Mike Frysingerec32bea2023-01-27 01:20:48 -05001228 if not cros_sdk_lib.MountChroot(chroot.path, create=True):
Alex Klein1699fab2022-09-08 08:46:06 -06001229 cros_build_lib.Die(
1230 "Unable to mount %s on chroot",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001231 _ImageFileForChroot(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001232 )
1233 logging.notice(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001234 "Mounted %s on chroot", _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001235 )
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001236
Alex Klein1699fab2022-09-08 08:46:06 -06001237 # Snapshot operations will always need the VG/LV, but other actions won't.
1238 if any_snapshot_operation:
1239 with locking.FileLock(lock_path, "chroot lock") as lock:
1240 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001241 chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001242 )
1243 if not chroot_vg or not chroot_lv:
1244 cros_build_lib.Die(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001245 "Unable to find VG/LV for chroot %s", chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001246 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001247
Alex Kleinef517832023-01-13 12:06:51 -07001248 # Delete snapshot before creating a new one. This allows the user to
1249 # throw out old state, create a new snapshot, and enter the chroot
1250 # in a single call to cros_sdk. Since restore involves deleting,
1251 # also do it before creating.
Alex Klein1699fab2022-09-08 08:46:06 -06001252 if options.snapshot_restore:
1253 lock.write_lock()
1254 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1255 if options.snapshot_restore not in valid_snapshots:
1256 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -07001257 "%s is not a valid snapshot to restore to. "
1258 "Valid snapshots: %s",
Alex Klein1699fab2022-09-08 08:46:06 -06001259 options.snapshot_restore,
1260 ", ".join(valid_snapshots),
1261 )
Mike Frysingerec32bea2023-01-27 01:20:48 -05001262 osutils.UmountTree(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001263 if not RestoreChrootSnapshot(
1264 options.snapshot_restore, chroot_vg, chroot_lv
1265 ):
1266 cros_build_lib.Die("Unable to restore chroot to snapshot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001267 if not cros_sdk_lib.MountChroot(chroot.path, create=False):
Alex Klein1699fab2022-09-08 08:46:06 -06001268 cros_build_lib.Die(
1269 "Unable to mount restored snapshot onto chroot."
1270 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001271
Alex Kleinef517832023-01-13 12:06:51 -07001272 # Use a read lock for snapshot delete and create even though they
1273 # modify the filesystem, because they don't modify the mounted
1274 # chroot itself. The underlying LVM commands take their own locks,
1275 # so conflicting concurrent operations here may crash cros_sdk, but
1276 # won't corrupt the chroot image. This tradeoff seems worth it to
1277 # allow snapshot operations on chroots that have a process inside.
Alex Klein1699fab2022-09-08 08:46:06 -06001278 if options.snapshot_delete:
1279 lock.read_lock()
1280 DeleteChrootSnapshot(
1281 options.snapshot_delete, chroot_vg, chroot_lv
1282 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001283
Alex Klein1699fab2022-09-08 08:46:06 -06001284 if options.snapshot_create:
1285 lock.read_lock()
1286 if not CreateChrootSnapshot(
1287 options.snapshot_create, chroot_vg, chroot_lv
1288 ):
1289 cros_build_lib.Die("Unable to create snapshot.")
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001290
Mike Frysingerec32bea2023-01-27 01:20:48 -05001291 img_path = _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001292 if (
1293 options.use_image
Mike Frysingerec32bea2023-01-27 01:20:48 -05001294 and os.path.exists(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001295 and os.path.exists(img_path)
1296 ):
1297 img_stat = os.stat(img_path)
1298 img_used_bytes = img_stat.st_blocks * 512
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001299
Mike Frysingerec32bea2023-01-27 01:20:48 -05001300 mount_stat = os.statvfs(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001301 mount_used_bytes = mount_stat.f_frsize * (
1302 mount_stat.f_blocks - mount_stat.f_bfree
1303 )
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001304
Alex Klein1699fab2022-09-08 08:46:06 -06001305 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
1306 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1307 logging.notice(
1308 "%s is using %s GiB more than needed. Running "
1309 "fstrim in background.",
1310 img_path,
1311 extra_gbs,
1312 )
1313 pid = os.fork()
1314 if pid == 0:
1315 try:
1316 # Directly call Popen to run fstrim concurrently.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001317 cmd = ["fstrim", chroot.path]
Alex Klein1699fab2022-09-08 08:46:06 -06001318 subprocess.Popen(cmd, close_fds=True, shell=False)
1319 except subprocess.SubprocessError as e:
1320 logging.warning(
1321 "Running fstrim failed. Consider running fstrim on "
1322 "your chroot manually.\n%s",
1323 e,
1324 )
1325 os._exit(0) # pylint: disable=protected-access
1326 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001327
Alex Kleinef517832023-01-13 12:06:51 -07001328 # Enter a new set of namespaces. Everything after here cannot directly
1329 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -06001330 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001331
Alex Klein1699fab2022-09-08 08:46:06 -06001332 if options.snapshot_list:
1333 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1334 print(snap)
1335 sys.exit(0)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001336
Alex Klein1699fab2022-09-08 08:46:06 -06001337 if not options.sdk_version:
1338 sdk_version = (
1339 bootstrap_latest_version
1340 if options.bootstrap
1341 else sdk_latest_version
1342 )
Yong Hong4e29b622018-02-05 14:31:10 +08001343 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001344 sdk_version = options.sdk_version
1345 if options.buildbot_log_version:
1346 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001347
Alex Klein1699fab2022-09-08 08:46:06 -06001348 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -07001349 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001350 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -06001351 if options.sdk_url:
1352 urls = [options.sdk_url]
1353 else:
1354 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -07001355
Alex Klein1699fab2022-09-08 08:46:06 -06001356 with locking.FileLock(lock_path, "chroot lock") as lock:
1357 if options.proxy_sim:
1358 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -07001359
Mike Frysingerec32bea2023-01-27 01:20:48 -05001360 sdk_cache = os.path.join(chroot.cache_dir, "sdks")
1361 distfiles_cache = os.path.join(chroot.cache_dir, "distfiles")
1362 osutils.SafeMakedirsNonRoot(chroot.cache_dir)
Dan Callaghanada9e032023-01-30 14:28:46 +11001363 osutils.SafeMakedirsNonRoot(options.out_dir)
Alex Klein1699fab2022-09-08 08:46:06 -06001364
1365 for target in (sdk_cache, distfiles_cache):
1366 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1367 if not os.path.exists(src):
1368 osutils.SafeMakedirsNonRoot(target)
1369 continue
1370 lock.write_lock(
1371 "Upgrade to %r needed but chroot is locked; please exit "
1372 "all instances so this upgrade can finish." % src
1373 )
1374 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -07001375 # Note that while waiting for the write lock, src may've
1376 # vanished; it's a rare race during the upgrade process that's a
1377 # byproduct of us avoiding taking a write lock to do the src
1378 # check. If we took a write lock for that check, it would
1379 # effectively limit all cros_sdk for a chroot to a single
1380 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -06001381 osutils.SafeMakedirsNonRoot(target)
1382 elif not os.path.exists(target):
1383 # Upgrade occurred, but a reversion, or something whacky
1384 # occurred writing to the old location. Wipe and continue.
1385 os.rename(src, target)
1386 else:
1387 # Upgrade occurred once already, but either a reversion or
1388 # some before/after separate cros_sdk usage is at play.
1389 # Wipe and continue.
1390 osutils.RmDir(src)
1391
Alex Klein1699fab2022-09-08 08:46:06 -06001392 mounted = False
1393 if options.create:
1394 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -07001395 # Recheck if the chroot is set up here before creating to make sure
1396 # we account for whatever the various delete/unmount/remount steps
1397 # above have done.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001398 if cros_sdk_lib.IsChrootReady(chroot.path):
Alex Klein1699fab2022-09-08 08:46:06 -06001399 logging.debug("Chroot already exists. Skipping creation.")
1400 else:
Alex Klein22690a12022-11-17 10:56:09 -07001401 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001402 cros_sdk_lib.CreateChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001403 Path(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001404 Path(sdk_tarball),
Dan Callaghanada9e032023-01-30 14:28:46 +11001405 options.out_dir,
Mike Frysingerec32bea2023-01-27 01:20:48 -05001406 Path(chroot.cache_dir),
Alex Klein1699fab2022-09-08 08:46:06 -06001407 usepkg=not options.bootstrap and not options.nousepkg,
1408 chroot_upgrade=options.chroot_upgrade,
1409 )
1410 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -07001411 elif options.download:
1412 # Allow downloading only.
1413 lock.write_lock()
1414 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001415
1416 if options.enter:
1417 lock.read_lock()
1418 if not mounted:
Dan Callaghanada9e032023-01-30 14:28:46 +11001419 cros_sdk_lib.MountChrootPaths(chroot.path, options.out_dir)
Mike Frysinger53baf882021-06-24 23:16:50 -04001420 ret = cros_sdk_lib.EnterChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001421 chroot,
Mike Frysinger53baf882021-06-24 23:16:50 -04001422 chrome_root_mount=options.chrome_root_mount,
1423 cwd=options.working_dir,
1424 cmd=options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -06001425 )
1426 sys.exit(ret.returncode)