blob: d94a194d358b2ad8d564f4e25f71425ef614f36e [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
Mike Frysinger12d055b2022-06-23 12:26:47 -040015import ast
Josh Triplett472a4182013-03-08 11:48:57 -080016import glob
Chris McDonaldb55b7032021-06-17 16:41:32 -060017import logging
Brian Harringb938c782012-02-29 15:14:38 -080018import os
Mike Frysinger23b5cf52021-06-16 23:18:00 -040019from pathlib import Path
Josh Triplett472a4182013-03-08 11:48:57 -080020import pwd
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060021import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070022import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070023import resource
Mike Frysinger5f5c70b2022-07-19 12:55:21 -040024import shlex
Sergey Frolov1cb46ec2020-12-09 21:46:16 -070025import subprocess
David James56e6c2c2012-10-24 23:54:41 -070026import sys
Mike Frysinger5f5c70b2022-07-19 12:55:21 -040027from typing import List
Mike Frysingere852b072021-05-21 12:39:03 -040028import urllib.parse
Brian Harringb938c782012-02-29 15:14:38 -080029
Chris McDonaldb55b7032021-06-17 16:41:32 -060030from chromite.cbuildbot import cbuildbot_alerts
Brian Harringb6cf9142012-09-01 20:43:17 -070031from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060032from chromite.lib import constants
Brian Harringb938c782012-02-29 15:14:38 -080033from chromite.lib import cros_build_lib
Benjamin Gordon74645232018-05-04 17:40:42 -060034from chromite.lib import cros_sdk_lib
Brian Harringb938c782012-02-29 15:14:38 -080035from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080036from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070037from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080038from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040039from chromite.lib import process_util
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
Brian Harringb938c782012-02-29 15:14:38 -080049# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070050ENTER_CHROOT = [
Alex Klein1699fab2022-09-08 08:46:06 -060051 os.path.join(constants.SOURCE_ROOT, "src/scripts/sdk_lib/enter_chroot.sh")
Manoj Guptab12f7302019-06-03 16:40:14 -070052]
Brian Harringb938c782012-02-29 15:14:38 -080053
Josh Triplett472a4182013-03-08 11:48:57 -080054# Proxy simulator configuration.
Alex Klein1699fab2022-09-08 08:46:06 -060055PROXY_HOST_IP = "192.168.240.1"
Josh Triplett472a4182013-03-08 11:48:57 -080056PROXY_PORT = 8080
Alex Klein1699fab2022-09-08 08:46:06 -060057PROXY_GUEST_IP = "192.168.240.2"
Josh Triplett472a4182013-03-08 11:48:57 -080058PROXY_NETMASK = 30
Alex Klein1699fab2022-09-08 08:46:06 -060059PROXY_VETH_PREFIX = "veth"
Josh Triplett472a4182013-03-08 11:48:57 -080060PROXY_CONNECT_PORTS = (80, 443, 9418)
Alex Klein1699fab2022-09-08 08:46:06 -060061PROXY_APACHE_FALLBACK_USERS = ("www-data", "apache", "nobody")
62PROXY_APACHE_MPMS = ("event", "worker", "prefork")
63PROXY_APACHE_FALLBACK_PATH = ":".join(
64 "/usr/lib/apache2/mpm-%s" % mpm for mpm in PROXY_APACHE_MPMS
65)
66PROXY_APACHE_MODULE_GLOBS = ("/usr/lib*/apache2/modules", "/usr/lib*/apache2")
Josh Triplett472a4182013-03-08 11:48:57 -080067
Josh Triplett9a495f62013-03-15 18:06:55 -070068# We need these tools to run. Very common tools (tar,..) are omitted.
Alex Klein1699fab2022-09-08 08:46:06 -060069NEEDED_TOOLS = ("curl", "xz")
Brian Harringb938c782012-02-29 15:14:38 -080070
Josh Triplett472a4182013-03-08 11:48:57 -080071# Tools needed for --proxy-sim only.
Alex Klein1699fab2022-09-08 08:46:06 -060072PROXY_NEEDED_TOOLS = ("ip",)
Brian Harringb938c782012-02-29 15:14:38 -080073
Mike Frysingerc60141b2023-01-27 00:57:15 -050074# Tools needed when use_image is true.
Alex Klein1699fab2022-09-08 08:46:06 -060075IMAGE_NEEDED_TOOLS = (
76 "losetup",
77 "lvchange",
78 "lvcreate",
79 "lvs",
80 "mke2fs",
81 "pvscan",
82 "thin_check",
83 "vgchange",
84 "vgcreate",
85 "vgs",
86)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060087
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070088# As space is used inside the chroot, the empty space in chroot.img is
89# allocated. Deleting files inside the chroot doesn't automatically return the
90# used space to the OS. Over time, this tends to make the sparse chroot.img
91# less sparse even if the chroot contents don't currently need much space. We
92# can recover most of this unused space with fstrim, but that takes too much
93# time to run it every time. Instead, check the used space against the image
94# size after mounting the chroot and only call fstrim if it looks like we could
95# recover at least this many GiB.
96MAX_UNUSED_IMAGE_GBS = 20
97
Mike Frysingercc838832014-05-24 13:10:30 -040098
Brian Harring1790ac42012-09-23 08:53:33 -070099def GetArchStageTarballs(version):
Alex Klein1699fab2022-09-08 08:46:06 -0600100 """Returns the URL for a given arch/version"""
101 extension = {"xz": "tar.xz"}
102 return [
103 toolchain.GetSdkURL(
104 suburl="cros-sdk-%s.%s" % (version, extension[compressor])
105 )
106 for compressor in COMPRESSION_PREFERENCE
107 ]
Brian Harring1790ac42012-09-23 08:53:33 -0700108
109
Mike Frysingerf744d032022-05-07 20:38:39 -0400110def FetchRemoteTarballs(storage_dir, urls):
Alex Klein1699fab2022-09-08 08:46:06 -0600111 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200112
Alex Klein1699fab2022-09-08 08:46:06 -0600113 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700114 storage_dir: Path where to save the tarball.
115 urls: List of URLs to try to download. Download will stop on first
116 success.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200117
Alex Klein1699fab2022-09-08 08:46:06 -0600118 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700119 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700120
Alex Klein1699fab2022-09-08 08:46:06 -0600121 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700122 ValueError: None of the URLs worked.
Alex Klein1699fab2022-09-08 08:46:06 -0600123 """
124 # Note we track content length ourselves since certain versions of curl
125 # fail if asked to resume a complete file.
126 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
127 status_re = re.compile(rb"^HTTP/[0-9]+(\.[0-9]+)? 200")
128 # pylint: disable=undefined-loop-variable
129 for url in urls:
130 logging.notice("Downloading tarball %s ...", urls[0].rsplit("/", 1)[-1])
131 parsed = urllib.parse.urlparse(url)
132 tarball_name = os.path.basename(parsed.path)
133 if parsed.scheme in ("", "file"):
134 if os.path.exists(parsed.path):
135 return parsed.path
136 continue
137 content_length = 0
138 logging.debug("Attempting download from %s", url)
139 result = retry_util.RunCurl(
140 ["-I", url],
141 print_cmd=False,
142 debug_level=logging.NOTICE,
143 capture_output=True,
144 )
145 successful = False
146 for header in result.stdout.splitlines():
147 # We must walk the output to find the 200 code for use cases where
148 # a proxy is involved and may have pushed down the actual header.
149 if status_re.match(header):
150 successful = True
151 elif header.lower().startswith(b"content-length:"):
152 content_length = int(header.split(b":", 1)[-1].strip())
153 if successful:
154 break
Brian Harring1790ac42012-09-23 08:53:33 -0700155 if successful:
Alex Klein1699fab2022-09-08 08:46:06 -0600156 break
157 else:
158 raise ValueError("No valid URLs found!")
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200159
Alex Klein1699fab2022-09-08 08:46:06 -0600160 tarball_dest = os.path.join(storage_dir, tarball_name)
161 current_size = 0
162 if os.path.exists(tarball_dest):
163 current_size = os.path.getsize(tarball_dest)
164 if current_size > content_length:
165 osutils.SafeUnlink(tarball_dest)
166 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100167
Alex Klein1699fab2022-09-08 08:46:06 -0600168 if current_size < content_length:
169 retry_util.RunCurl(
170 [
171 "--fail",
172 "-L",
173 "-y",
174 "30",
175 "-C",
176 "-",
177 "--output",
178 tarball_dest,
179 url,
180 ],
181 print_cmd=False,
182 debug_level=logging.NOTICE,
183 )
Brian Harringb938c782012-02-29 15:14:38 -0800184
Alex Klein1699fab2022-09-08 08:46:06 -0600185 # Cleanup old tarballs now since we've successfull fetched; only cleanup
186 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
187 # because we might have partial overlap between known prefixes.
188 for p in Path(storage_dir).glob("cros-sdk-*"):
189 if p.name == tarball_name:
190 continue
191 logging.info("Cleaning up old tarball: %s", p)
192 osutils.SafeUnlink(p)
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200193
Alex Klein1699fab2022-09-08 08:46:06 -0600194 return tarball_dest
Brian Harringb938c782012-02-29 15:14:38 -0800195
196
Alex Klein1699fab2022-09-08 08:46:06 -0600197def EnterChroot(
198 chroot_path,
199 cache_dir,
200 chrome_root,
201 chrome_root_mount,
202 goma_dir,
Alex Klein1699fab2022-09-08 08:46:06 -0600203 reclient_dir,
204 reproxy_cfg_file,
205 working_dir,
206 additional_args,
207):
208 """Enters an existing SDK chroot"""
209 st = os.statvfs(os.path.join(chroot_path, "usr", "bin", "sudo"))
210 if st.f_flag & os.ST_NOSUID:
211 cros_build_lib.Die("chroot cannot be in a nosuid mount")
Mike Frysingere5456972013-06-13 00:07:23 -0400212
Alex Klein1699fab2022-09-08 08:46:06 -0600213 cmd = ENTER_CHROOT + ["--chroot", chroot_path, "--cache_dir", cache_dir]
214 if chrome_root:
215 cmd.extend(["--chrome_root", chrome_root])
216 if chrome_root_mount:
217 cmd.extend(["--chrome_root_mount", chrome_root_mount])
218 if goma_dir:
219 cmd.extend(["--goma_dir", goma_dir])
Alex Klein1699fab2022-09-08 08:46:06 -0600220 if reclient_dir:
221 cmd.extend(["--reclient_dir", reclient_dir])
222 if reproxy_cfg_file:
223 cmd.extend(["--reproxy_cfg_file", reproxy_cfg_file])
224 if working_dir is not None:
225 cmd.extend(["--working_dir", working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700226
Alex Klein1699fab2022-09-08 08:46:06 -0600227 if additional_args:
228 cmd.append("--")
229 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700230
Alex Klein1699fab2022-09-08 08:46:06 -0600231 if "CHROMEOS_SUDO_RLIMITS" in os.environ:
232 _SetRlimits(os.environ.pop("CHROMEOS_SUDO_RLIMITS"))
Mike Frysinger12d055b2022-06-23 12:26:47 -0400233
Alex Klein1699fab2022-09-08 08:46:06 -0600234 # Some systems set the soft limit too low. Bump it up to the hard limit.
235 # We don't override the hard limit because it's something the admins put
236 # in place and we want to respect such configs. http://b/234353695
237 soft, hard = resource.getrlimit(resource.RLIMIT_NPROC)
238 if soft != resource.RLIM_INFINITY and soft < 4096:
239 if soft < hard or hard == resource.RLIM_INFINITY:
240 resource.setrlimit(resource.RLIMIT_NPROC, (hard, hard))
Mike Frysingerebb23fc2022-06-06 21:17:39 -0400241
Alex Klein1699fab2022-09-08 08:46:06 -0600242 # ThinLTO opens lots of files at the same time.
243 # Set rlimit and vm.max_map_count to accommodate this.
244 file_limit = 262144
245 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
246 resource.setrlimit(
247 resource.RLIMIT_NOFILE, (max(soft, file_limit), max(hard, file_limit))
248 )
249 max_map_count = int(osutils.ReadFile("/proc/sys/vm/max_map_count"))
250 if max_map_count < file_limit:
251 logging.notice(
252 "Raising vm.max_map_count from %s to %s", max_map_count, file_limit
253 )
254 osutils.WriteFile("/proc/sys/vm/max_map_count", str(file_limit))
255 return cros_build_lib.dbg_run(cmd, check=False)
Brian Harringb938c782012-02-29 15:14:38 -0800256
257
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600258def _ImageFileForChroot(chroot):
Alex Klein1699fab2022-09-08 08:46:06 -0600259 """Find the image file that should be associated with |chroot|.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600260
Alex Klein1699fab2022-09-08 08:46:06 -0600261 This function does not check if the image exists; it simply returns the
262 filename that would be used.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600263
Alex Klein1699fab2022-09-08 08:46:06 -0600264 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700265 chroot: Path to the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600266
Alex Klein1699fab2022-09-08 08:46:06 -0600267 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700268 Path to an image file that would be associated with chroot.
Alex Klein1699fab2022-09-08 08:46:06 -0600269 """
270 return chroot.rstrip("/") + ".img"
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600271
272
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600273def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600274 """Create a snapshot for the specified chroot VG/LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600275
Alex Klein1699fab2022-09-08 08:46:06 -0600276 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700277 snapshot_name: The name of the new snapshot.
278 chroot_vg: The name of the VG containing the origin LV.
279 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600280
Alex Klein1699fab2022-09-08 08:46:06 -0600281 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700282 True if the snapshot was created, or False if a snapshot with the same
283 name already exists.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600284
Alex Klein1699fab2022-09-08 08:46:06 -0600285 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700286 SystemExit: The lvcreate command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600287 """
288 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
289 logging.error(
290 "Cannot create snapshot %s: A volume with that name already "
291 "exists.",
292 snapshot_name,
293 )
294 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600295
Alex Klein1699fab2022-09-08 08:46:06 -0600296 cmd = [
297 "lvcreate",
298 "-s",
299 "--name",
300 snapshot_name,
301 "%s/%s" % (chroot_vg, chroot_lv),
302 ]
303 try:
304 logging.notice(
305 "Creating snapshot %s from %s in VG %s.",
306 snapshot_name,
307 chroot_lv,
308 chroot_vg,
309 )
310 cros_build_lib.dbg_run(cmd, capture_output=True)
311 return True
312 except cros_build_lib.RunCommandError as e:
313 cros_build_lib.Die("Creating snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600314
315
316def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600317 """Delete the named snapshot from the specified chroot VG.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600318
Alex Klein1699fab2022-09-08 08:46:06 -0600319 If the requested snapshot is not found, nothing happens. The main chroot LV
320 and internal thinpool LV cannot be deleted with this function.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600321
Alex Klein1699fab2022-09-08 08:46:06 -0600322 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700323 snapshot_name: The name of the snapshot to delete.
324 chroot_vg: The name of the VG containing the origin LV.
325 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700328 SystemExit: The lvremove command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600329 """
330 if snapshot_name in (
331 cros_sdk_lib.CHROOT_LV_NAME,
332 cros_sdk_lib.CHROOT_THINPOOL_NAME,
333 ):
334 logging.error(
335 "Cannot remove LV %s as a snapshot. Use cros_sdk --delete "
336 "if you want to remove the whole chroot.",
337 snapshot_name,
338 )
339 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600340
Alex Klein1699fab2022-09-08 08:46:06 -0600341 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
342 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600343
Alex Klein1699fab2022-09-08 08:46:06 -0600344 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, snapshot_name)]
345 try:
346 logging.notice(
347 "Deleting snapshot %s in VG %s.", snapshot_name, chroot_vg
348 )
349 cros_build_lib.dbg_run(cmd, capture_output=True)
350 except cros_build_lib.RunCommandError as e:
351 cros_build_lib.Die("Deleting snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600352
353
354def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600355 """Restore the chroot to an existing snapshot.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600356
Alex Klein1699fab2022-09-08 08:46:06 -0600357 This is done by renaming the original |chroot_lv| LV to a temporary name,
358 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
359 now unused LV. If an error occurs, attempts to rename the original snapshot
360 back to |chroot_lv| to leave the chroot unchanged.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600361
Alex Klein1699fab2022-09-08 08:46:06 -0600362 The chroot must be unmounted before calling this function, and will be left
363 unmounted after this function returns.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600364
Alex Klein1699fab2022-09-08 08:46:06 -0600365 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700366 snapshot_name: The name of the snapshot to restore. This snapshot will
367 no longer be accessible at its original name after this function
368 finishes.
369 chroot_vg: The VG containing the chroot LV and snapshot LV.
370 chroot_lv: The name of the original chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600371
Alex Klein1699fab2022-09-08 08:46:06 -0600372 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700373 True if the chroot was restored to the requested snapshot, or False if
374 the snapshot wasn't found or isn't valid.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600375
Alex Klein1699fab2022-09-08 08:46:06 -0600376 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700377 SystemExit: Any of the LVM commands failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600378 """
379 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
380 if (
381 snapshot_name
382 in (cros_sdk_lib.CHROOT_LV_NAME, cros_sdk_lib.CHROOT_THINPOOL_NAME)
383 or snapshot_name not in valid_snapshots
384 ):
385 logging.error(
386 "Chroot cannot be restored to %s. Valid snapshots: %s",
387 snapshot_name,
388 ", ".join(valid_snapshots),
389 )
390 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600391
Alex Klein1699fab2022-09-08 08:46:06 -0600392 backup_chroot_name = "chroot-bak-%d" % random.randint(0, 1000)
393 cmd = ["lvrename", chroot_vg, chroot_lv, backup_chroot_name]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600394 try:
Alex Klein1699fab2022-09-08 08:46:06 -0600395 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500396 except cros_build_lib.RunCommandError as e:
Alex Klein1699fab2022-09-08 08:46:06 -0600397 cros_build_lib.Die("Restoring snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600398
Alex Klein1699fab2022-09-08 08:46:06 -0600399 cmd = ["lvrename", chroot_vg, snapshot_name, chroot_lv]
400 try:
401 cros_build_lib.dbg_run(cmd, capture_output=True)
402 except cros_build_lib.RunCommandError as e:
403 cmd = ["lvrename", chroot_vg, backup_chroot_name, chroot_lv]
404 try:
405 cros_build_lib.dbg_run(cmd, capture_output=True)
406 except cros_build_lib.RunCommandError as e:
407 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -0700408 "Failed to rename %s to chroot and failed to restore %s back "
409 "to chroot!\n%s",
Alex Klein1699fab2022-09-08 08:46:06 -0600410 snapshot_name,
411 backup_chroot_name,
412 e,
413 )
414 cros_build_lib.Die(
415 "Failed to rename %s to chroot! Original chroot LV has "
416 "been restored.\n%s",
417 snapshot_name,
418 e,
419 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600420
Alex Klein1699fab2022-09-08 08:46:06 -0600421 # Some versions of LVM set snapshots to be skipped at auto-activate time.
422 # Other versions don't have this flag at all. We run lvchange to try
Alex Kleinef517832023-01-13 12:06:51 -0700423 # disabling auto-skip and activating the volume, but ignore errors. Versions
Alex Klein1699fab2022-09-08 08:46:06 -0600424 # that don't have the flag should be auto-activated.
425 chroot_lv_path = "%s/%s" % (chroot_vg, chroot_lv)
426 cmd = ["lvchange", "-kn", chroot_lv_path]
427 cros_build_lib.run(cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600428
Alex Klein1699fab2022-09-08 08:46:06 -0600429 # Activate the LV in case the lvchange above was needed. Activating an LV
Alex Kleinef517832023-01-13 12:06:51 -0700430 # that is already active shouldn't do anything, so this is safe to run even
431 # if the -kn wasn't needed.
Alex Klein1699fab2022-09-08 08:46:06 -0600432 cmd = ["lvchange", "-ay", chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500433 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600434
Alex Klein1699fab2022-09-08 08:46:06 -0600435 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, backup_chroot_name)]
436 try:
437 cros_build_lib.dbg_run(cmd, capture_output=True)
438 except cros_build_lib.RunCommandError as e:
439 cros_build_lib.Die(
440 "Failed to remove backup LV %s/%s!\n%s",
441 chroot_vg,
442 backup_chroot_name,
443 e,
444 )
445
446 return True
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600447
448
449def ListChrootSnapshots(chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600450 """Return all snapshots in |chroot_vg| regardless of origin volume.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600451
Alex Klein1699fab2022-09-08 08:46:06 -0600452 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700453 chroot_vg: The name of the VG containing the chroot.
454 chroot_lv: The name of the chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600455
Alex Klein1699fab2022-09-08 08:46:06 -0600456 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700457 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600458
Alex Klein1699fab2022-09-08 08:46:06 -0600459 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700460 SystemExit: The lvs command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600461 """
462 if not chroot_vg or not chroot_lv:
463 return []
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600464
Alex Klein1699fab2022-09-08 08:46:06 -0600465 cmd = [
466 "lvs",
467 "-o",
468 "lv_name,pool_lv,lv_attr",
469 "-O",
470 "lv_name",
471 "--noheadings",
472 "--separator",
473 "\t",
474 chroot_vg,
475 ]
476 try:
477 result = cros_build_lib.run(
478 cmd, print_cmd=False, stdout=True, encoding="utf-8"
479 )
480 except cros_build_lib.RunCommandError:
481 raise SystemExit("Running %r failed!" % cmd)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600482
Alex Klein1699fab2022-09-08 08:46:06 -0600483 # Once the thin origin volume has been deleted, there's no way to tell a
Alex Kleinef517832023-01-13 12:06:51 -0700484 # snapshot apart from any other volume. Since this VG is created and managed
485 # by cros_sdk, we'll assume that all volumes that share the same thin pool
486 # are valid snapshots.
Alex Klein1699fab2022-09-08 08:46:06 -0600487 snapshots = []
488 snapshot_attrs = re.compile(r"^V.....t.{2,}") # Matches a thin volume.
489 for line in result.stdout.splitlines():
490 lv_name, pool_lv, lv_attr = line.lstrip().split("\t")
491 if (
492 lv_name == chroot_lv
493 or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME
494 or pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME
495 or not snapshot_attrs.match(lv_attr)
496 ):
497 continue
498 snapshots.append(lv_name)
499 return snapshots
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600500
501
Mike Frysinger12d055b2022-06-23 12:26:47 -0400502# The rlimits we will lookup & pass down, in order.
503RLIMITS_TO_PASS = (
504 resource.RLIMIT_AS,
505 resource.RLIMIT_CORE,
506 resource.RLIMIT_CPU,
507 resource.RLIMIT_FSIZE,
508 resource.RLIMIT_MEMLOCK,
509 resource.RLIMIT_NICE,
510 resource.RLIMIT_NOFILE,
511 resource.RLIMIT_NPROC,
512 resource.RLIMIT_RSS,
513 resource.RLIMIT_STACK,
514)
515
516
517def _GetRlimits() -> str:
Alex Klein1699fab2022-09-08 08:46:06 -0600518 """Serialize current rlimits."""
519 return str(tuple(resource.getrlimit(x) for x in RLIMITS_TO_PASS))
Mike Frysinger12d055b2022-06-23 12:26:47 -0400520
521
522def _SetRlimits(limits: str) -> None:
Alex Klein1699fab2022-09-08 08:46:06 -0600523 """Deserialize rlimits."""
524 for rlim, limit in zip(RLIMITS_TO_PASS, ast.literal_eval(limits)):
525 cur_limit = resource.getrlimit(rlim)
526 if cur_limit != limit:
527 # Turn the number into a symbolic name for logging.
528 name = "RLIMIT_???"
529 for name, num in resource.__dict__.items():
530 if name.startswith("RLIMIT_") and num == rlim:
531 break
532 logging.debug(
533 "Restoring user rlimit %s from %r to %r", name, cur_limit, limit
534 )
Mike Frysinger12d055b2022-06-23 12:26:47 -0400535
Alex Klein1699fab2022-09-08 08:46:06 -0600536 resource.setrlimit(rlim, limit)
Mike Frysinger12d055b2022-06-23 12:26:47 -0400537
538
David James56e6c2c2012-10-24 23:54:41 -0700539def _SudoCommand():
Alex Klein1699fab2022-09-08 08:46:06 -0600540 """Get the 'sudo' command, along with all needed environment variables."""
David James56e6c2c2012-10-24 23:54:41 -0700541
Alex Klein1699fab2022-09-08 08:46:06 -0600542 # Pass in the ENVIRONMENT_ALLOWLIST and ENV_PASSTHRU variables so that
543 # scripts in the chroot know what variables to pass through.
544 cmd = ["sudo"]
545 for key in constants.CHROOT_ENVIRONMENT_ALLOWLIST + constants.ENV_PASSTHRU:
546 value = os.environ.get(key)
547 if value is not None:
548 cmd += ["%s=%s" % (key, value)]
David James56e6c2c2012-10-24 23:54:41 -0700549
Alex Kleinef517832023-01-13 12:06:51 -0700550 # We keep PATH not for the chroot but for the re-exec & for programs we
551 # might run before we chroot into the SDK. The process that enters the SDK
552 # itself will take care of initializing PATH to the right value then. But
553 # we can't override the system's default PATH for root as that will hide
554 # /sbin.
Alex Klein1699fab2022-09-08 08:46:06 -0600555 cmd += ["CHROMEOS_SUDO_PATH=%s" % os.environ.get("PATH", "")]
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400556
Alex Klein1699fab2022-09-08 08:46:06 -0600557 # Pass along current rlimit settings so we can restore them.
558 cmd += [f"CHROMEOS_SUDO_RLIMITS={_GetRlimits()}"]
Mike Frysinger12d055b2022-06-23 12:26:47 -0400559
Alex Klein1699fab2022-09-08 08:46:06 -0600560 # Pass in the path to the depot_tools so that users can access them from
561 # within the chroot.
562 cmd += ["DEPOT_TOOLS=%s" % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500563
Alex Klein1699fab2022-09-08 08:46:06 -0600564 return cmd
David James56e6c2c2012-10-24 23:54:41 -0700565
566
Josh Triplett472a4182013-03-08 11:48:57 -0800567def _ReportMissing(missing):
Alex Klein1699fab2022-09-08 08:46:06 -0600568 """Report missing utilities, then exit.
Josh Triplett472a4182013-03-08 11:48:57 -0800569
Alex Klein1699fab2022-09-08 08:46:06 -0600570 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700571 missing: List of missing utilities, as returned by
572 osutils.FindMissingBinaries. If non-empty, will not return.
Alex Klein1699fab2022-09-08 08:46:06 -0600573 """
Josh Triplett472a4182013-03-08 11:48:57 -0800574
Alex Klein1699fab2022-09-08 08:46:06 -0600575 if missing:
576 raise SystemExit(
577 "The tool(s) %s were not found.\n"
578 "Please install the appropriate package in your host.\n"
579 "Example(ubuntu):\n"
580 " sudo apt-get install <packagename>" % ", ".join(missing)
581 )
Josh Triplett472a4182013-03-08 11:48:57 -0800582
583
584def _ProxySimSetup(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600585 """Set up proxy simulator, and return only in the child environment.
Josh Triplett472a4182013-03-08 11:48:57 -0800586
Alex Klein1699fab2022-09-08 08:46:06 -0600587 TODO: Ideally, this should support multiple concurrent invocations of
588 cros_sdk --proxy-sim; currently, such invocations will conflict with each
589 other due to the veth device names and IP addresses. Either this code would
590 need to generate fresh, unused names for all of these before forking, or it
591 would need to support multiple concurrent cros_sdk invocations sharing one
592 proxy and allowing it to exit when unused (without counting on any local
593 service-management infrastructure on the host).
594 """
Josh Triplett472a4182013-03-08 11:48:57 -0800595
Alex Klein1699fab2022-09-08 08:46:06 -0600596 may_need_mpm = False
597 apache_bin = osutils.Which("apache2")
Josh Triplett472a4182013-03-08 11:48:57 -0800598 if apache_bin is None:
Alex Klein1699fab2022-09-08 08:46:06 -0600599 apache_bin = osutils.Which("apache2", PROXY_APACHE_FALLBACK_PATH)
600 if apache_bin is None:
601 _ReportMissing(("apache2",))
602 else:
603 may_need_mpm = True
Josh Triplett472a4182013-03-08 11:48:57 -0800604
Alex Klein1699fab2022-09-08 08:46:06 -0600605 # Module names and .so names included for ease of grepping.
606 apache_modules = [
607 ("proxy_module", "mod_proxy.so"),
608 ("proxy_connect_module", "mod_proxy_connect.so"),
609 ("proxy_http_module", "mod_proxy_http.so"),
610 ("proxy_ftp_module", "mod_proxy_ftp.so"),
611 ]
Josh Triplett472a4182013-03-08 11:48:57 -0800612
Alex Kleinef517832023-01-13 12:06:51 -0700613 # Find the apache module directory and make sure it has the modules we need.
Alex Klein1699fab2022-09-08 08:46:06 -0600614 module_dirs = {}
615 for g in PROXY_APACHE_MODULE_GLOBS:
616 for _, so in apache_modules:
617 for f in glob.glob(os.path.join(g, so)):
618 module_dirs.setdefault(os.path.dirname(f), []).append(so)
619 for apache_module_path, modules_found in module_dirs.items():
620 if len(modules_found) == len(apache_modules):
621 break
622 else:
Alex Kleinef517832023-01-13 12:06:51 -0700623 # Appease cros lint, which doesn't understand that this else block will
624 # not fall through to the subsequent code which relies on
625 # apache_module_path.
Alex Klein1699fab2022-09-08 08:46:06 -0600626 apache_module_path = None
627 raise SystemExit(
Alex Kleinef517832023-01-13 12:06:51 -0700628 "Could not find apache module path containing all required "
629 "modules: %s" % ", ".join(so for mod, so in apache_modules)
Alex Klein1699fab2022-09-08 08:46:06 -0600630 )
Josh Triplett472a4182013-03-08 11:48:57 -0800631
Alex Klein1699fab2022-09-08 08:46:06 -0600632 def check_add_module(name):
633 so = "mod_%s.so" % name
634 if os.access(os.path.join(apache_module_path, so), os.F_OK):
635 mod = "%s_module" % name
636 apache_modules.append((mod, so))
637 return True
638 return False
Josh Triplett472a4182013-03-08 11:48:57 -0800639
Alex Klein1699fab2022-09-08 08:46:06 -0600640 check_add_module("authz_core")
641 if may_need_mpm:
642 for mpm in PROXY_APACHE_MPMS:
643 if check_add_module("mpm_%s" % mpm):
644 break
Josh Triplett472a4182013-03-08 11:48:57 -0800645
Alex Klein1699fab2022-09-08 08:46:06 -0600646 veth_host = "%s-host" % PROXY_VETH_PREFIX
647 veth_guest = "%s-guest" % PROXY_VETH_PREFIX
Josh Triplett472a4182013-03-08 11:48:57 -0800648
Alex Klein1699fab2022-09-08 08:46:06 -0600649 # Set up locks to sync the net namespace setup. We need the child to create
Alex Kleinef517832023-01-13 12:06:51 -0700650 # the net ns first, and then have the parent assign the guest end of the
651 # veth interface to the child's new network namespace & bring up the proxy.
652 # Only then can the child move forward and rely on the network being up.
Alex Klein1699fab2022-09-08 08:46:06 -0600653 ns_create_lock = locking.PipeLock()
654 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800655
Alex Klein1699fab2022-09-08 08:46:06 -0600656 pid = os.fork()
657 if not pid:
658 # Create our new isolated net namespace.
659 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500660
Alex Klein1699fab2022-09-08 08:46:06 -0600661 # Signal the parent the ns is ready to be configured.
662 ns_create_lock.Post()
663 del ns_create_lock
664
665 # Wait for the parent to finish setting up the ns/proxy.
666 ns_setup_lock.Wait()
667 del ns_setup_lock
668
669 # Set up child side of the network.
670 commands = (
671 ("ip", "link", "set", "up", "lo"),
672 (
673 "ip",
674 "address",
675 "add",
676 "%s/%u" % (PROXY_GUEST_IP, PROXY_NETMASK),
677 "dev",
678 veth_guest,
679 ),
680 ("ip", "link", "set", veth_guest, "up"),
681 )
682 try:
683 for cmd in commands:
684 cros_build_lib.dbg_run(cmd)
685 except cros_build_lib.RunCommandError as e:
686 cros_build_lib.Die("Proxy setup failed!\n%s", e)
687
688 proxy_url = "http://%s:%u" % (PROXY_HOST_IP, PROXY_PORT)
689 for proto in ("http", "https", "ftp"):
690 os.environ[proto + "_proxy"] = proxy_url
691 for v in ("all_proxy", "RSYNC_PROXY", "no_proxy"):
692 os.environ.pop(v, None)
693 return
694
695 # Set up parent side of the network.
696 uid = int(os.environ.get("SUDO_UID", "0"))
697 gid = int(os.environ.get("SUDO_GID", "0"))
698 if uid == 0 or gid == 0:
699 for username in PROXY_APACHE_FALLBACK_USERS:
700 try:
701 pwnam = pwd.getpwnam(username)
702 uid, gid = pwnam.pw_uid, pwnam.pw_gid
703 break
704 except KeyError:
705 continue
706 if uid == 0 or gid == 0:
707 raise SystemExit("Could not find a non-root user to run Apache as")
708
709 chroot_parent, chroot_base = os.path.split(options.chroot)
710 pid_file = os.path.join(chroot_parent, ".%s-apache-proxy.pid" % chroot_base)
711 log_file = os.path.join(chroot_parent, ".%s-apache-proxy.log" % chroot_base)
712
713 # Wait for the child to create the net ns.
714 ns_create_lock.Wait()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500715 del ns_create_lock
716
Alex Klein1699fab2022-09-08 08:46:06 -0600717 apache_directives = [
718 "User #%u" % uid,
719 "Group #%u" % gid,
720 "PidFile %s" % pid_file,
721 "ErrorLog %s" % log_file,
722 "Listen %s:%u" % (PROXY_HOST_IP, PROXY_PORT),
723 "ServerName %s" % PROXY_HOST_IP,
724 "ProxyRequests On",
725 "AllowCONNECT %s" % " ".join(str(x) for x in PROXY_CONNECT_PORTS),
726 ] + [
727 "LoadModule %s %s" % (mod, os.path.join(apache_module_path, so))
728 for (mod, so) in apache_modules
729 ]
730 commands = (
731 (
732 "ip",
733 "link",
734 "add",
735 "name",
736 veth_host,
737 "type",
738 "veth",
739 "peer",
740 "name",
741 veth_guest,
742 ),
743 (
744 "ip",
745 "address",
746 "add",
747 "%s/%u" % (PROXY_HOST_IP, PROXY_NETMASK),
748 "dev",
749 veth_host,
750 ),
751 ("ip", "link", "set", veth_host, "up"),
752 (
753 [apache_bin, "-f", "/dev/null"]
754 + [arg for d in apache_directives for arg in ("-C", d)]
755 ),
756 ("ip", "link", "set", veth_guest, "netns", str(pid)),
757 )
758 cmd = None # Make cros lint happy.
759 try:
760 for cmd in commands:
761 cros_build_lib.dbg_run(cmd)
762 except cros_build_lib.RunCommandError as e:
763 # Clean up existing interfaces, if any.
764 cmd_cleanup = ("ip", "link", "del", veth_host)
765 try:
766 cros_build_lib.run(cmd_cleanup, print_cmd=False)
767 except cros_build_lib.RunCommandError:
768 logging.error("running %r failed", cmd_cleanup)
769 cros_build_lib.Die("Proxy network setup failed!\n%s", e)
770
771 # Signal the child that the net ns/proxy is fully configured now.
772 ns_setup_lock.Post()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500773 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800774
Alex Klein1699fab2022-09-08 08:46:06 -0600775 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800776
777
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400778def _BuildReExecCommand(argv, opts) -> List[str]:
Alex Klein1699fab2022-09-08 08:46:06 -0600779 """Generate new command for self-reexec."""
780 # Make sure to preserve the active Python executable in case the version
781 # we're running as is not the default one found via the (new) $PATH.
782 cmd = _SudoCommand() + ["--"]
783 if opts.strace:
784 cmd += ["strace"] + shlex.split(opts.strace_arguments) + ["--"]
785 return cmd + [sys.executable] + argv
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400786
787
788def _ReExecuteIfNeeded(argv, opts):
Alex Klein1699fab2022-09-08 08:46:06 -0600789 """Re-execute cros_sdk as root.
David James56e6c2c2012-10-24 23:54:41 -0700790
Alex Klein1699fab2022-09-08 08:46:06 -0600791 Also unshare the mount namespace so as to ensure that processes outside
792 the chroot can't mess with our mounts.
793 """
794 if osutils.IsNonRootUser():
795 cmd = _BuildReExecCommand(argv, opts)
796 logging.debug(
797 "Reexecing self via sudo:\n%s", cros_build_lib.CmdToStr(cmd)
798 )
799 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700800
801
Mike Frysinger34db8692013-11-11 14:54:08 -0500802def _CreateParser(sdk_latest_version, bootstrap_latest_version):
Alex Klein1699fab2022-09-08 08:46:06 -0600803 """Generate and return the parser with all the options."""
804 usage = (
805 "usage: %(prog)s [options] "
806 "[VAR1=val1 ... VAR2=val2] [--] [command [args]]"
807 )
808 parser = commandline.ArgumentParser(
809 usage=usage, description=__doc__, caching=True
810 )
Brian Harring218e13c2012-10-10 16:21:26 -0700811
Alex Klein1699fab2022-09-08 08:46:06 -0600812 # Global options.
813 default_chroot = os.path.join(
814 constants.SOURCE_ROOT, constants.DEFAULT_CHROOT_DIR
815 )
816 parser.add_argument(
817 "--chroot",
818 dest="chroot",
819 default=default_chroot,
820 type="path",
821 help=("SDK chroot dir name [%s]" % constants.DEFAULT_CHROOT_DIR),
822 )
823 parser.add_argument(
824 "--nouse-image",
825 dest="use_image",
826 action="store_false",
827 default=False,
828 help="Do not mount the chroot on a loopback image; "
829 "instead, create it directly in a directory.",
830 )
831 parser.add_argument(
832 "--use-image",
833 dest="use_image",
834 action="store_true",
835 default=False,
836 help="Mount the chroot on a loopback image "
837 "instead of creating it directly in a directory.",
838 )
Brian Harringb938c782012-02-29 15:14:38 -0800839
Alex Klein1699fab2022-09-08 08:46:06 -0600840 parser.add_argument(
841 "--chrome-root",
842 "--chrome_root",
843 type="path",
844 help="Mount this chrome root into the SDK chroot",
845 )
846 parser.add_argument(
847 "--chrome_root_mount",
848 type="path",
849 help="Mount chrome into this path inside SDK chroot",
850 )
851 parser.add_argument(
852 "--nousepkg",
853 action="store_true",
854 default=False,
855 help="Do not use binary packages when creating a chroot.",
856 )
857 parser.add_argument(
858 "-u",
859 "--url",
860 dest="sdk_url",
861 help="Use sdk tarball located at this url. Use file:// "
862 "for local files.",
863 )
864 parser.add_argument(
865 "--sdk-version",
866 help=(
867 "Use this sdk version. For prebuilt, current is %r"
868 ", for bootstrapping it is %r."
869 % (sdk_latest_version, bootstrap_latest_version)
870 ),
871 )
872 parser.add_argument(
Mike Frysinger85824562023-01-31 02:40:43 -0500873 "--goma-dir",
Alex Klein1699fab2022-09-08 08:46:06 -0600874 "--goma_dir",
875 type="path",
876 help="Goma installed directory to mount into the chroot.",
877 )
878 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600879 "--reclient-dir",
880 type="path",
881 help="Reclient installed directory to mount into the chroot.",
882 )
883 parser.add_argument(
884 "--reproxy-cfg-file",
885 type="path",
886 help="Config file for re-client's reproxy used for remoteexec.",
887 )
888 parser.add_argument(
889 "--skip-chroot-upgrade",
890 dest="chroot_upgrade",
891 action="store_false",
892 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700893 help="Skip automatic SDK and toolchain upgrade when entering the "
894 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600895 )
Yong Hong84ba9172018-02-07 01:37:42 +0800896
Alex Klein1699fab2022-09-08 08:46:06 -0600897 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700898 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600899 parser.add_argument(
900 "--working-dir",
901 type=str,
902 help="Run the command in specific working directory in "
903 "chroot. If the given directory is a relative "
904 "path, this program will transfer the path to "
905 "the corresponding one inside chroot.",
906 )
Yong Hong84ba9172018-02-07 01:37:42 +0800907
Alex Klein1699fab2022-09-08 08:46:06 -0600908 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500909
Alex Klein1699fab2022-09-08 08:46:06 -0600910 # Commands.
911 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400912 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600913 "--enter",
914 action="store_true",
915 default=False,
916 help="Enter the SDK chroot. Implies --create.",
917 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400918 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600919 "--create",
920 action="store_true",
921 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700922 help="Create the chroot only if it does not already exist. Downloads "
923 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600924 )
925 group.add_argument(
926 "--bootstrap",
927 action="store_true",
928 default=False,
929 help="Build everything from scratch, including the sdk. "
930 "Use this only if you need to validate a change "
931 "that affects SDK creation itself (toolchain and "
932 "build are typically the only folk who need this). "
933 "Note this will quite heavily slow down the build. "
934 "This option implies --create --nousepkg.",
935 )
936 group.add_argument(
937 "-r",
938 "--replace",
939 action="store_true",
940 default=False,
941 help="Replace an existing SDK chroot. Basically an alias "
942 "for --delete --create.",
943 )
944 group.add_argument(
945 "--delete",
946 action="store_true",
947 default=False,
948 help="Delete the current SDK chroot if it exists.",
949 )
950 group.add_argument(
951 "--force",
952 action="store_true",
953 default=False,
954 help="Force unmount/delete of the current SDK chroot even if "
955 "obtaining the write lock fails.",
956 )
957 group.add_argument(
958 "--unmount",
959 action="store_true",
960 default=False,
961 help="Unmount and clean up devices associated with the "
962 "SDK chroot if it exists. This does not delete the "
963 "backing image file, so the same chroot can be later "
964 "re-mounted for reuse. To fully delete the chroot, use "
965 "--delete. This is primarily useful for working on "
966 "cros_sdk or the chroot setup; you should not need it "
967 "under normal circumstances.",
968 )
969 group.add_argument(
970 "--download",
971 action="store_true",
972 default=False,
973 help="Download the sdk.",
974 )
975 group.add_argument(
976 "--snapshot-create",
977 metavar="SNAPSHOT_NAME",
978 help="Create a snapshot of the chroot. Requires that the chroot was "
979 "created without the --nouse-image option.",
980 )
981 group.add_argument(
982 "--snapshot-restore",
983 metavar="SNAPSHOT_NAME",
984 help="Restore the chroot to a previously created snapshot.",
985 )
986 group.add_argument(
987 "--snapshot-delete",
988 metavar="SNAPSHOT_NAME",
989 help="Delete a previously created snapshot. Deleting a snapshot that "
990 "does not exist is not an error.",
991 )
992 group.add_argument(
993 "--snapshot-list",
994 action="store_true",
995 default=False,
996 help="List existing snapshots of the chroot and exit.",
997 )
998 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -0400999
Alex Klein1699fab2022-09-08 08:46:06 -06001000 # Namespace options.
1001 group = parser.add_argument_group("Namespaces")
1002 group.add_argument(
1003 "--proxy-sim",
1004 action="store_true",
1005 default=False,
1006 help="Simulate a restrictive network requiring an outbound" " proxy.",
1007 )
1008 for ns, default in (("pid", True), ("net", None)):
1009 group.add_argument(
1010 f"--ns-{ns}",
1011 default=default,
1012 action="store_true",
1013 help=f"Create a new {ns} namespace.",
1014 )
1015 group.add_argument(
1016 f"--no-ns-{ns}",
1017 dest=f"ns_{ns}",
1018 action="store_false",
1019 help=f"Do not create a new {ns} namespace.",
1020 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -04001021
Alex Klein1699fab2022-09-08 08:46:06 -06001022 # Debug options.
1023 group = parser.debug_group
1024 group.add_argument(
1025 "--strace",
1026 action="store_true",
1027 help="Run cros_sdk through strace after re-exec via sudo",
1028 )
1029 group.add_argument(
1030 "--strace-arguments",
1031 default="",
1032 help="Extra strace options (shell quoting permitted)",
1033 )
Mike Frysinger34db8692013-11-11 14:54:08 -05001034
Alex Klein1699fab2022-09-08 08:46:06 -06001035 # Internal options.
1036 group = parser.add_argument_group(
1037 "Internal Chromium OS Build Team Options",
1038 "Caution: these are for meant for the Chromium OS build team only",
1039 )
1040 group.add_argument(
1041 "--buildbot-log-version",
1042 default=False,
1043 action="store_true",
1044 help="Log SDK version for buildbot consumption",
1045 )
1046
1047 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -05001048
1049
1050def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -06001051 # Turn on strict sudo checks.
1052 cros_build_lib.STRICT_SUDO = True
1053 conf = key_value_store.LoadFile(
1054 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
1055 ignore_missing=True,
1056 )
1057 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
1058 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -07001059
Alex Klein1699fab2022-09-08 08:46:06 -06001060 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
1061 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
1062 bootstrap_latest_version = (
1063 sdk_latest_version
1064 if bootstrap_frozen_version == "<unknown>"
1065 else bootstrap_frozen_version
1066 )
1067 parser, commands = _CreateParser(
1068 sdk_latest_version, bootstrap_latest_version
1069 )
1070 options = parser.parse_args(argv)
Brian Harringb938c782012-02-29 15:14:38 -08001071
Alex Klein1699fab2022-09-08 08:46:06 -06001072 # Some basic checks first, before we ask for sudo credentials.
1073 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -08001074
Alex Klein1699fab2022-09-08 08:46:06 -06001075 host = os.uname()[4]
1076 if host != "x86_64":
1077 cros_build_lib.Die(
1078 "cros_sdk is currently only supported on x86_64; you're running"
1079 " %s. Please find a x86_64 machine." % (host,)
1080 )
Brian Harring1790ac42012-09-23 08:53:33 -07001081
Alex Klein1699fab2022-09-08 08:46:06 -06001082 # Merge the outside PATH setting if we re-execed ourselves.
1083 if "CHROMEOS_SUDO_PATH" in os.environ:
1084 os.environ["PATH"] = "%s:%s" % (
1085 os.environ.pop("CHROMEOS_SUDO_PATH"),
1086 os.environ["PATH"],
1087 )
Mike Frysinger2bda4d12020-07-14 11:15:49 -04001088
Alex Klein1699fab2022-09-08 08:46:06 -06001089 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
1090 if options.proxy_sim:
1091 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
1092 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -08001093
Alex Klein1699fab2022-09-08 08:46:06 -06001094 if (
1095 sdk_latest_version == "<unknown>"
1096 or bootstrap_latest_version == "<unknown>"
1097 ):
1098 cros_build_lib.Die(
1099 "No SDK version was found. "
1100 "Are you in a Chromium source tree instead of Chromium OS?\n\n"
1101 "Please change to a directory inside your Chromium OS source tree\n"
1102 "and retry. If you need to setup a Chromium OS source tree, see\n"
1103 " https://dev.chromium.org/chromium-os/developer-guide"
1104 )
Benjamin Gordon040a1162017-06-29 13:44:47 -06001105
Alex Klein1699fab2022-09-08 08:46:06 -06001106 any_snapshot_operation = (
1107 options.snapshot_create
1108 or options.snapshot_restore
1109 or options.snapshot_delete
1110 or options.snapshot_list
1111 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001112
Alex Klein1699fab2022-09-08 08:46:06 -06001113 if (
1114 options.snapshot_delete
1115 and options.snapshot_delete == options.snapshot_restore
1116 ):
1117 parser.error(
1118 "Cannot --snapshot_delete the same snapshot you are "
1119 "restoring with --snapshot_restore."
1120 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001121
Alex Klein1699fab2022-09-08 08:46:06 -06001122 _ReExecuteIfNeeded([sys.argv[0]] + argv, options)
David James471532c2013-01-21 10:23:31 -08001123
Alex Klein1699fab2022-09-08 08:46:06 -06001124 lock_path = os.path.dirname(options.chroot)
1125 lock_path = os.path.join(
1126 lock_path, ".%s_lock" % os.path.basename(options.chroot).lstrip(".")
1127 )
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001128
Alex Klein1699fab2022-09-08 08:46:06 -06001129 # Expand out the aliases...
1130 if options.replace:
1131 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001132
Alex Klein1699fab2022-09-08 08:46:06 -06001133 if options.bootstrap:
1134 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001135
Alex Klein1699fab2022-09-08 08:46:06 -06001136 # If a command is not given, default to enter.
1137 # pylint: disable=protected-access
1138 # This _group_actions access sucks, but upstream decided to not include an
1139 # alternative to optparse's option_list, and this is what they recommend.
1140 options.enter |= not any(
1141 getattr(options, x.dest) for x in commands._group_actions
1142 )
1143 # pylint: enable=protected-access
Mike Frysinger114a7b92023-01-26 19:08:57 -05001144 options.enter |= bool(options.commands)
Brian Harring218e13c2012-10-10 16:21:26 -07001145
Alex Klein1699fab2022-09-08 08:46:06 -06001146 if (
1147 options.delete
1148 and not options.create
1149 and (options.enter or any_snapshot_operation)
1150 ):
1151 parser.error(
1152 "Trying to enter or snapshot the chroot when --delete "
1153 "was specified makes no sense."
1154 )
Brian Harring218e13c2012-10-10 16:21:26 -07001155
Alex Klein1699fab2022-09-08 08:46:06 -06001156 if options.unmount and (
1157 options.create or options.enter or any_snapshot_operation
1158 ):
1159 parser.error("--unmount cannot be specified with other chroot actions.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001160
Alex Klein1699fab2022-09-08 08:46:06 -06001161 if options.working_dir is not None and not os.path.isabs(
1162 options.working_dir
1163 ):
1164 options.working_dir = path_util.ToChrootPath(options.working_dir)
Yong Hong84ba9172018-02-07 01:37:42 +08001165
Alex Klein1699fab2022-09-08 08:46:06 -06001166 # If there is an existing chroot image and we're not removing it then force
1167 # use_image on. This ensures that people don't have to remember to pass
Alex Kleinef517832023-01-13 12:06:51 -07001168 # --use-image after a reboot to avoid losing access to their existing
1169 # chroot.
Alex Klein1699fab2022-09-08 08:46:06 -06001170 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1171 img_path = _ImageFileForChroot(options.chroot)
1172 if (
1173 not options.use_image
1174 and not options.delete
1175 and not options.unmount
1176 and os.path.exists(img_path)
1177 ):
1178 if chroot_exists:
1179 # If the chroot is already populated, make sure it has something
1180 # mounted on it before we assume it came from an image.
1181 cmd = ["mountpoint", "-q", options.chroot]
1182 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
1183 options.use_image = True
Benjamin Gordon832a4412020-12-08 10:39:16 -07001184
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001185 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001186 logging.notice(
1187 "Existing chroot image %s found. Forcing --use-image on.",
1188 img_path,
1189 )
1190 options.use_image = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001191
Alex Klein1699fab2022-09-08 08:46:06 -06001192 if any_snapshot_operation and not options.use_image:
1193 if os.path.exists(img_path):
1194 options.use_image = True
1195 else:
1196 cros_build_lib.Die(
1197 "Snapshot operations are not compatible with " "--nouse-image."
1198 )
Mike Frysingerc60141b2023-01-27 00:57:15 -05001199 if options.use_image:
1200 logging.warning("--use-image is deprecated and will be removed soon.")
1201 logging.warning("Please migrate, or create a new one with --delete.")
1202 logging.warning("See http://b/266878468 for details.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001203
Alex Klein1699fab2022-09-08 08:46:06 -06001204 # Discern if we need to create the chroot.
1205 if (
1206 options.use_image
1207 and not chroot_exists
1208 and not options.delete
1209 and not options.unmount
1210 and not missing_image_tools
1211 and os.path.exists(img_path)
1212 ):
1213 # Try to re-mount an existing image in case the user has rebooted.
1214 with locking.FileLock(lock_path, "chroot lock") as lock:
1215 logging.debug("Checking if existing chroot image can be mounted.")
1216 lock.write_lock()
1217 cros_sdk_lib.MountChroot(options.chroot, create=False)
1218 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1219 if chroot_exists:
1220 logging.notice("Mounted existing image %s on chroot", img_path)
1221
1222 # Finally, flip create if necessary.
1223 if options.enter or options.snapshot_create:
1224 options.create |= not chroot_exists
1225
1226 # Make sure we will download if we plan to create.
1227 options.download |= options.create
1228
Mike Frysinger6bbd3e92023-01-27 00:05:02 -05001229 options.Freeze()
1230
Alex Kleinef517832023-01-13 12:06:51 -07001231 # Anything that needs to manipulate the main chroot mount or communicate
1232 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -06001233
1234 # If deleting, do it regardless of the use_image flag so that a
1235 # previously-created loopback chroot can also be cleaned up.
1236 if options.delete:
1237 # Set a timeout of 300 seconds when getting the lock.
1238 with locking.FileLock(
1239 lock_path, "chroot lock", blocking_timeout=300
1240 ) as lock:
1241 try:
1242 lock.write_lock()
1243 except timeout_util.TimeoutError as e:
1244 logging.error(
1245 "Acquiring write_lock on %s failed: %s", lock_path, e
1246 )
1247 if not options.force:
1248 cros_build_lib.Die(
1249 "Exiting; use --force to continue w/o lock."
1250 )
1251 else:
1252 logging.warning(
1253 "cros_sdk was invoked with force option, continuing."
1254 )
1255 logging.notice("Deleting chroot.")
1256 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
1257
Alex Kleinef517832023-01-13 12:06:51 -07001258 # If cleanup was requested, we have to do it while we're still in the
1259 # original namespace. Since cleaning up the mount will interfere with any
1260 # other commands, we exit here. The check above should have made sure that
1261 # no other action was requested, anyway.
Alex Klein1699fab2022-09-08 08:46:06 -06001262 if options.unmount:
1263 # Set a timeout of 300 seconds when getting the lock.
1264 with locking.FileLock(
1265 lock_path, "chroot lock", blocking_timeout=300
1266 ) as lock:
1267 try:
1268 lock.write_lock()
1269 except timeout_util.TimeoutError as e:
1270 logging.error(
1271 "Acquiring write_lock on %s failed: %s", lock_path, e
1272 )
1273 logging.warning(
Alex Kleinef517832023-01-13 12:06:51 -07001274 "Continuing with CleanupChroot(%s), which will umount the "
1275 "tree.",
Alex Klein1699fab2022-09-08 08:46:06 -06001276 options.chroot,
1277 )
Alex Kleinef517832023-01-13 12:06:51 -07001278 # We can call CleanupChroot (which calls
1279 # cros_sdk_lib.CleanupChrootMount) even if we don't get the lock
1280 # because it will attempt to unmount the tree and will print
1281 # diagnostic information from 'fuser', 'lsof', and 'ps'.
Alex Klein1699fab2022-09-08 08:46:06 -06001282 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=False)
1283 sys.exit(0)
1284
1285 # Make sure the main chroot mount is visible. Contents will be filled in
1286 # below if needed.
1287 if options.create and options.use_image:
1288 if missing_image_tools:
1289 raise SystemExit(
1290 """The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001291Please make sure the lvm2 and thin-provisioning-tools packages
1292are installed on your host.
1293Example(ubuntu):
1294 sudo apt-get install lvm2 thin-provisioning-tools
1295
1296If you want to run without lvm2, pass --nouse-image (chroot
Alex Klein1699fab2022-09-08 08:46:06 -06001297snapshots will be unavailable)."""
1298 % ", ".join(missing_image_tools)
1299 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001300
Alex Klein1699fab2022-09-08 08:46:06 -06001301 logging.debug("Making sure chroot image is mounted.")
1302 with locking.FileLock(lock_path, "chroot lock") as lock:
1303 lock.write_lock()
1304 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1305 cros_build_lib.Die(
1306 "Unable to mount %s on chroot",
1307 _ImageFileForChroot(options.chroot),
1308 )
1309 logging.notice(
1310 "Mounted %s on chroot", _ImageFileForChroot(options.chroot)
1311 )
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001312
Alex Klein1699fab2022-09-08 08:46:06 -06001313 # Snapshot operations will always need the VG/LV, but other actions won't.
1314 if any_snapshot_operation:
1315 with locking.FileLock(lock_path, "chroot lock") as lock:
1316 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
1317 options.chroot
1318 )
1319 if not chroot_vg or not chroot_lv:
1320 cros_build_lib.Die(
1321 "Unable to find VG/LV for chroot %s", options.chroot
1322 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001323
Alex Kleinef517832023-01-13 12:06:51 -07001324 # Delete snapshot before creating a new one. This allows the user to
1325 # throw out old state, create a new snapshot, and enter the chroot
1326 # in a single call to cros_sdk. Since restore involves deleting,
1327 # also do it before creating.
Alex Klein1699fab2022-09-08 08:46:06 -06001328 if options.snapshot_restore:
1329 lock.write_lock()
1330 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1331 if options.snapshot_restore not in valid_snapshots:
1332 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -07001333 "%s is not a valid snapshot to restore to. "
1334 "Valid snapshots: %s",
Alex Klein1699fab2022-09-08 08:46:06 -06001335 options.snapshot_restore,
1336 ", ".join(valid_snapshots),
1337 )
1338 osutils.UmountTree(options.chroot)
1339 if not RestoreChrootSnapshot(
1340 options.snapshot_restore, chroot_vg, chroot_lv
1341 ):
1342 cros_build_lib.Die("Unable to restore chroot to snapshot.")
1343 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1344 cros_build_lib.Die(
1345 "Unable to mount restored snapshot onto chroot."
1346 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001347
Alex Kleinef517832023-01-13 12:06:51 -07001348 # Use a read lock for snapshot delete and create even though they
1349 # modify the filesystem, because they don't modify the mounted
1350 # chroot itself. The underlying LVM commands take their own locks,
1351 # so conflicting concurrent operations here may crash cros_sdk, but
1352 # won't corrupt the chroot image. This tradeoff seems worth it to
1353 # allow snapshot operations on chroots that have a process inside.
Alex Klein1699fab2022-09-08 08:46:06 -06001354 if options.snapshot_delete:
1355 lock.read_lock()
1356 DeleteChrootSnapshot(
1357 options.snapshot_delete, chroot_vg, chroot_lv
1358 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001359
Alex Klein1699fab2022-09-08 08:46:06 -06001360 if options.snapshot_create:
1361 lock.read_lock()
1362 if not CreateChrootSnapshot(
1363 options.snapshot_create, chroot_vg, chroot_lv
1364 ):
1365 cros_build_lib.Die("Unable to create snapshot.")
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001366
Alex Klein1699fab2022-09-08 08:46:06 -06001367 img_path = _ImageFileForChroot(options.chroot)
1368 if (
1369 options.use_image
1370 and os.path.exists(options.chroot)
1371 and os.path.exists(img_path)
1372 ):
1373 img_stat = os.stat(img_path)
1374 img_used_bytes = img_stat.st_blocks * 512
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001375
Alex Klein1699fab2022-09-08 08:46:06 -06001376 mount_stat = os.statvfs(options.chroot)
1377 mount_used_bytes = mount_stat.f_frsize * (
1378 mount_stat.f_blocks - mount_stat.f_bfree
1379 )
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001380
Alex Klein1699fab2022-09-08 08:46:06 -06001381 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
1382 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1383 logging.notice(
1384 "%s is using %s GiB more than needed. Running "
1385 "fstrim in background.",
1386 img_path,
1387 extra_gbs,
1388 )
1389 pid = os.fork()
1390 if pid == 0:
1391 try:
1392 # Directly call Popen to run fstrim concurrently.
1393 cmd = ["fstrim", options.chroot]
1394 subprocess.Popen(cmd, close_fds=True, shell=False)
1395 except subprocess.SubprocessError as e:
1396 logging.warning(
1397 "Running fstrim failed. Consider running fstrim on "
1398 "your chroot manually.\n%s",
1399 e,
1400 )
1401 os._exit(0) # pylint: disable=protected-access
1402 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001403
Alex Kleinef517832023-01-13 12:06:51 -07001404 # Enter a new set of namespaces. Everything after here cannot directly
1405 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -06001406 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001407
Alex Klein1699fab2022-09-08 08:46:06 -06001408 if options.snapshot_list:
1409 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1410 print(snap)
1411 sys.exit(0)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001412
Alex Klein1699fab2022-09-08 08:46:06 -06001413 if not options.sdk_version:
1414 sdk_version = (
1415 bootstrap_latest_version
1416 if options.bootstrap
1417 else sdk_latest_version
1418 )
Yong Hong4e29b622018-02-05 14:31:10 +08001419 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001420 sdk_version = options.sdk_version
1421 if options.buildbot_log_version:
1422 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001423
Alex Klein1699fab2022-09-08 08:46:06 -06001424 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -07001425 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001426 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -06001427 if options.sdk_url:
1428 urls = [options.sdk_url]
1429 else:
1430 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -07001431
Alex Klein1699fab2022-09-08 08:46:06 -06001432 with locking.FileLock(lock_path, "chroot lock") as lock:
1433 if options.proxy_sim:
1434 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -07001435
Alex Klein1699fab2022-09-08 08:46:06 -06001436 sdk_cache = os.path.join(options.cache_dir, "sdks")
1437 distfiles_cache = os.path.join(options.cache_dir, "distfiles")
1438 osutils.SafeMakedirsNonRoot(options.cache_dir)
1439
1440 for target in (sdk_cache, distfiles_cache):
1441 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1442 if not os.path.exists(src):
1443 osutils.SafeMakedirsNonRoot(target)
1444 continue
1445 lock.write_lock(
1446 "Upgrade to %r needed but chroot is locked; please exit "
1447 "all instances so this upgrade can finish." % src
1448 )
1449 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -07001450 # Note that while waiting for the write lock, src may've
1451 # vanished; it's a rare race during the upgrade process that's a
1452 # byproduct of us avoiding taking a write lock to do the src
1453 # check. If we took a write lock for that check, it would
1454 # effectively limit all cros_sdk for a chroot to a single
1455 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -06001456 osutils.SafeMakedirsNonRoot(target)
1457 elif not os.path.exists(target):
1458 # Upgrade occurred, but a reversion, or something whacky
1459 # occurred writing to the old location. Wipe and continue.
1460 os.rename(src, target)
1461 else:
1462 # Upgrade occurred once already, but either a reversion or
1463 # some before/after separate cros_sdk usage is at play.
1464 # Wipe and continue.
1465 osutils.RmDir(src)
1466
Alex Klein1699fab2022-09-08 08:46:06 -06001467 mounted = False
1468 if options.create:
1469 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -07001470 # Recheck if the chroot is set up here before creating to make sure
1471 # we account for whatever the various delete/unmount/remount steps
1472 # above have done.
Alex Klein1699fab2022-09-08 08:46:06 -06001473 if cros_sdk_lib.IsChrootReady(options.chroot):
1474 logging.debug("Chroot already exists. Skipping creation.")
1475 else:
Alex Klein22690a12022-11-17 10:56:09 -07001476 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001477 cros_sdk_lib.CreateChroot(
1478 Path(options.chroot),
1479 Path(sdk_tarball),
1480 Path(options.cache_dir),
1481 usepkg=not options.bootstrap and not options.nousepkg,
1482 chroot_upgrade=options.chroot_upgrade,
1483 )
1484 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -07001485 elif options.download:
1486 # Allow downloading only.
1487 lock.write_lock()
1488 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001489
1490 if options.enter:
1491 lock.read_lock()
1492 if not mounted:
1493 cros_sdk_lib.MountChrootPaths(options.chroot)
1494 ret = EnterChroot(
1495 options.chroot,
1496 options.cache_dir,
1497 options.chrome_root,
1498 options.chrome_root_mount,
1499 options.goma_dir,
Alex Klein1699fab2022-09-08 08:46:06 -06001500 options.reclient_dir,
1501 options.reproxy_cfg_file,
1502 options.working_dir,
Mike Frysinger114a7b92023-01-26 19:08:57 -05001503 options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -06001504 )
1505 sys.exit(ret.returncode)