blob: 307e900c2669ebcb39af89bd974108bfad773b24 [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
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060074# Tools needed when use_image is true (the default).
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(
873 "--goma_dir",
874 type="path",
875 help="Goma installed directory to mount into the chroot.",
876 )
877 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600878 "--reclient-dir",
879 type="path",
880 help="Reclient installed directory to mount into the chroot.",
881 )
882 parser.add_argument(
883 "--reproxy-cfg-file",
884 type="path",
885 help="Config file for re-client's reproxy used for remoteexec.",
886 )
887 parser.add_argument(
888 "--skip-chroot-upgrade",
889 dest="chroot_upgrade",
890 action="store_false",
891 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700892 help="Skip automatic SDK and toolchain upgrade when entering the "
893 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600894 )
Yong Hong84ba9172018-02-07 01:37:42 +0800895
Alex Klein1699fab2022-09-08 08:46:06 -0600896 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700897 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600898 parser.add_argument(
899 "--working-dir",
900 type=str,
901 help="Run the command in specific working directory in "
902 "chroot. If the given directory is a relative "
903 "path, this program will transfer the path to "
904 "the corresponding one inside chroot.",
905 )
Yong Hong84ba9172018-02-07 01:37:42 +0800906
Alex Klein1699fab2022-09-08 08:46:06 -0600907 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500908
Alex Klein1699fab2022-09-08 08:46:06 -0600909 # Commands.
910 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400911 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600912 "--enter",
913 action="store_true",
914 default=False,
915 help="Enter the SDK chroot. Implies --create.",
916 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400917 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600918 "--create",
919 action="store_true",
920 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700921 help="Create the chroot only if it does not already exist. Downloads "
922 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600923 )
924 group.add_argument(
925 "--bootstrap",
926 action="store_true",
927 default=False,
928 help="Build everything from scratch, including the sdk. "
929 "Use this only if you need to validate a change "
930 "that affects SDK creation itself (toolchain and "
931 "build are typically the only folk who need this). "
932 "Note this will quite heavily slow down the build. "
933 "This option implies --create --nousepkg.",
934 )
935 group.add_argument(
936 "-r",
937 "--replace",
938 action="store_true",
939 default=False,
940 help="Replace an existing SDK chroot. Basically an alias "
941 "for --delete --create.",
942 )
943 group.add_argument(
944 "--delete",
945 action="store_true",
946 default=False,
947 help="Delete the current SDK chroot if it exists.",
948 )
949 group.add_argument(
950 "--force",
951 action="store_true",
952 default=False,
953 help="Force unmount/delete of the current SDK chroot even if "
954 "obtaining the write lock fails.",
955 )
956 group.add_argument(
957 "--unmount",
958 action="store_true",
959 default=False,
960 help="Unmount and clean up devices associated with the "
961 "SDK chroot if it exists. This does not delete the "
962 "backing image file, so the same chroot can be later "
963 "re-mounted for reuse. To fully delete the chroot, use "
964 "--delete. This is primarily useful for working on "
965 "cros_sdk or the chroot setup; you should not need it "
966 "under normal circumstances.",
967 )
968 group.add_argument(
969 "--download",
970 action="store_true",
971 default=False,
972 help="Download the sdk.",
973 )
974 group.add_argument(
975 "--snapshot-create",
976 metavar="SNAPSHOT_NAME",
977 help="Create a snapshot of the chroot. Requires that the chroot was "
978 "created without the --nouse-image option.",
979 )
980 group.add_argument(
981 "--snapshot-restore",
982 metavar="SNAPSHOT_NAME",
983 help="Restore the chroot to a previously created snapshot.",
984 )
985 group.add_argument(
986 "--snapshot-delete",
987 metavar="SNAPSHOT_NAME",
988 help="Delete a previously created snapshot. Deleting a snapshot that "
989 "does not exist is not an error.",
990 )
991 group.add_argument(
992 "--snapshot-list",
993 action="store_true",
994 default=False,
995 help="List existing snapshots of the chroot and exit.",
996 )
997 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -0400998
Alex Klein1699fab2022-09-08 08:46:06 -0600999 # Namespace options.
1000 group = parser.add_argument_group("Namespaces")
1001 group.add_argument(
1002 "--proxy-sim",
1003 action="store_true",
1004 default=False,
1005 help="Simulate a restrictive network requiring an outbound" " proxy.",
1006 )
1007 for ns, default in (("pid", True), ("net", None)):
1008 group.add_argument(
1009 f"--ns-{ns}",
1010 default=default,
1011 action="store_true",
1012 help=f"Create a new {ns} namespace.",
1013 )
1014 group.add_argument(
1015 f"--no-ns-{ns}",
1016 dest=f"ns_{ns}",
1017 action="store_false",
1018 help=f"Do not create a new {ns} namespace.",
1019 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -04001020
Alex Klein1699fab2022-09-08 08:46:06 -06001021 # Debug options.
1022 group = parser.debug_group
1023 group.add_argument(
1024 "--strace",
1025 action="store_true",
1026 help="Run cros_sdk through strace after re-exec via sudo",
1027 )
1028 group.add_argument(
1029 "--strace-arguments",
1030 default="",
1031 help="Extra strace options (shell quoting permitted)",
1032 )
Mike Frysinger34db8692013-11-11 14:54:08 -05001033
Alex Klein1699fab2022-09-08 08:46:06 -06001034 # Internal options.
1035 group = parser.add_argument_group(
1036 "Internal Chromium OS Build Team Options",
1037 "Caution: these are for meant for the Chromium OS build team only",
1038 )
1039 group.add_argument(
1040 "--buildbot-log-version",
1041 default=False,
1042 action="store_true",
1043 help="Log SDK version for buildbot consumption",
1044 )
1045
1046 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -05001047
1048
1049def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -06001050 # Turn on strict sudo checks.
1051 cros_build_lib.STRICT_SUDO = True
1052 conf = key_value_store.LoadFile(
1053 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
1054 ignore_missing=True,
1055 )
1056 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
1057 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -07001058
Alex Klein1699fab2022-09-08 08:46:06 -06001059 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
1060 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
1061 bootstrap_latest_version = (
1062 sdk_latest_version
1063 if bootstrap_frozen_version == "<unknown>"
1064 else bootstrap_frozen_version
1065 )
1066 parser, commands = _CreateParser(
1067 sdk_latest_version, bootstrap_latest_version
1068 )
1069 options = parser.parse_args(argv)
Brian Harringb938c782012-02-29 15:14:38 -08001070
Alex Klein1699fab2022-09-08 08:46:06 -06001071 # Some basic checks first, before we ask for sudo credentials.
1072 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -08001073
Alex Klein1699fab2022-09-08 08:46:06 -06001074 host = os.uname()[4]
1075 if host != "x86_64":
1076 cros_build_lib.Die(
1077 "cros_sdk is currently only supported on x86_64; you're running"
1078 " %s. Please find a x86_64 machine." % (host,)
1079 )
Brian Harring1790ac42012-09-23 08:53:33 -07001080
Alex Klein1699fab2022-09-08 08:46:06 -06001081 # Merge the outside PATH setting if we re-execed ourselves.
1082 if "CHROMEOS_SUDO_PATH" in os.environ:
1083 os.environ["PATH"] = "%s:%s" % (
1084 os.environ.pop("CHROMEOS_SUDO_PATH"),
1085 os.environ["PATH"],
1086 )
Mike Frysinger2bda4d12020-07-14 11:15:49 -04001087
Alex Klein1699fab2022-09-08 08:46:06 -06001088 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
1089 if options.proxy_sim:
1090 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
1091 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -08001092
Alex Klein1699fab2022-09-08 08:46:06 -06001093 if (
1094 sdk_latest_version == "<unknown>"
1095 or bootstrap_latest_version == "<unknown>"
1096 ):
1097 cros_build_lib.Die(
1098 "No SDK version was found. "
1099 "Are you in a Chromium source tree instead of Chromium OS?\n\n"
1100 "Please change to a directory inside your Chromium OS source tree\n"
1101 "and retry. If you need to setup a Chromium OS source tree, see\n"
1102 " https://dev.chromium.org/chromium-os/developer-guide"
1103 )
Benjamin Gordon040a1162017-06-29 13:44:47 -06001104
Alex Klein1699fab2022-09-08 08:46:06 -06001105 any_snapshot_operation = (
1106 options.snapshot_create
1107 or options.snapshot_restore
1108 or options.snapshot_delete
1109 or options.snapshot_list
1110 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001111
Alex Klein1699fab2022-09-08 08:46:06 -06001112 if (
1113 options.snapshot_delete
1114 and options.snapshot_delete == options.snapshot_restore
1115 ):
1116 parser.error(
1117 "Cannot --snapshot_delete the same snapshot you are "
1118 "restoring with --snapshot_restore."
1119 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001120
Alex Klein1699fab2022-09-08 08:46:06 -06001121 _ReExecuteIfNeeded([sys.argv[0]] + argv, options)
David James471532c2013-01-21 10:23:31 -08001122
Alex Klein1699fab2022-09-08 08:46:06 -06001123 lock_path = os.path.dirname(options.chroot)
1124 lock_path = os.path.join(
1125 lock_path, ".%s_lock" % os.path.basename(options.chroot).lstrip(".")
1126 )
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001127
Alex Klein1699fab2022-09-08 08:46:06 -06001128 # Expand out the aliases...
1129 if options.replace:
1130 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001131
Alex Klein1699fab2022-09-08 08:46:06 -06001132 if options.bootstrap:
1133 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001134
Alex Klein1699fab2022-09-08 08:46:06 -06001135 # If a command is not given, default to enter.
1136 # pylint: disable=protected-access
1137 # This _group_actions access sucks, but upstream decided to not include an
1138 # alternative to optparse's option_list, and this is what they recommend.
1139 options.enter |= not any(
1140 getattr(options, x.dest) for x in commands._group_actions
1141 )
1142 # pylint: enable=protected-access
Mike Frysinger114a7b92023-01-26 19:08:57 -05001143 options.enter |= bool(options.commands)
Brian Harring218e13c2012-10-10 16:21:26 -07001144
Alex Klein1699fab2022-09-08 08:46:06 -06001145 if (
1146 options.delete
1147 and not options.create
1148 and (options.enter or any_snapshot_operation)
1149 ):
1150 parser.error(
1151 "Trying to enter or snapshot the chroot when --delete "
1152 "was specified makes no sense."
1153 )
Brian Harring218e13c2012-10-10 16:21:26 -07001154
Alex Klein1699fab2022-09-08 08:46:06 -06001155 if options.unmount and (
1156 options.create or options.enter or any_snapshot_operation
1157 ):
1158 parser.error("--unmount cannot be specified with other chroot actions.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001159
Alex Klein1699fab2022-09-08 08:46:06 -06001160 if options.working_dir is not None and not os.path.isabs(
1161 options.working_dir
1162 ):
1163 options.working_dir = path_util.ToChrootPath(options.working_dir)
Yong Hong84ba9172018-02-07 01:37:42 +08001164
Alex Klein1699fab2022-09-08 08:46:06 -06001165 # If there is an existing chroot image and we're not removing it then force
1166 # use_image on. This ensures that people don't have to remember to pass
Alex Kleinef517832023-01-13 12:06:51 -07001167 # --use-image after a reboot to avoid losing access to their existing
1168 # chroot.
Alex Klein1699fab2022-09-08 08:46:06 -06001169 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1170 img_path = _ImageFileForChroot(options.chroot)
1171 if (
1172 not options.use_image
1173 and not options.delete
1174 and not options.unmount
1175 and os.path.exists(img_path)
1176 ):
1177 if chroot_exists:
1178 # If the chroot is already populated, make sure it has something
1179 # mounted on it before we assume it came from an image.
1180 cmd = ["mountpoint", "-q", options.chroot]
1181 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
1182 options.use_image = True
Benjamin Gordon832a4412020-12-08 10:39:16 -07001183
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001184 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001185 logging.notice(
1186 "Existing chroot image %s found. Forcing --use-image on.",
1187 img_path,
1188 )
1189 options.use_image = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001190
Alex Klein1699fab2022-09-08 08:46:06 -06001191 if any_snapshot_operation and not options.use_image:
1192 if os.path.exists(img_path):
1193 options.use_image = True
1194 else:
1195 cros_build_lib.Die(
1196 "Snapshot operations are not compatible with " "--nouse-image."
1197 )
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001198
Alex Klein1699fab2022-09-08 08:46:06 -06001199 # Discern if we need to create the chroot.
1200 if (
1201 options.use_image
1202 and not chroot_exists
1203 and not options.delete
1204 and not options.unmount
1205 and not missing_image_tools
1206 and os.path.exists(img_path)
1207 ):
1208 # Try to re-mount an existing image in case the user has rebooted.
1209 with locking.FileLock(lock_path, "chroot lock") as lock:
1210 logging.debug("Checking if existing chroot image can be mounted.")
1211 lock.write_lock()
1212 cros_sdk_lib.MountChroot(options.chroot, create=False)
1213 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1214 if chroot_exists:
1215 logging.notice("Mounted existing image %s on chroot", img_path)
1216
1217 # Finally, flip create if necessary.
1218 if options.enter or options.snapshot_create:
1219 options.create |= not chroot_exists
1220
1221 # Make sure we will download if we plan to create.
1222 options.download |= options.create
1223
Alex Kleinef517832023-01-13 12:06:51 -07001224 # Anything that needs to manipulate the main chroot mount or communicate
1225 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -06001226
1227 # If deleting, do it regardless of the use_image flag so that a
1228 # previously-created loopback chroot can also be cleaned up.
1229 if options.delete:
1230 # Set a timeout of 300 seconds when getting the lock.
1231 with locking.FileLock(
1232 lock_path, "chroot lock", blocking_timeout=300
1233 ) as lock:
1234 try:
1235 lock.write_lock()
1236 except timeout_util.TimeoutError as e:
1237 logging.error(
1238 "Acquiring write_lock on %s failed: %s", lock_path, e
1239 )
1240 if not options.force:
1241 cros_build_lib.Die(
1242 "Exiting; use --force to continue w/o lock."
1243 )
1244 else:
1245 logging.warning(
1246 "cros_sdk was invoked with force option, continuing."
1247 )
1248 logging.notice("Deleting chroot.")
1249 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
1250
Alex Kleinef517832023-01-13 12:06:51 -07001251 # If cleanup was requested, we have to do it while we're still in the
1252 # original namespace. Since cleaning up the mount will interfere with any
1253 # other commands, we exit here. The check above should have made sure that
1254 # no other action was requested, anyway.
Alex Klein1699fab2022-09-08 08:46:06 -06001255 if options.unmount:
1256 # Set a timeout of 300 seconds when getting the lock.
1257 with locking.FileLock(
1258 lock_path, "chroot lock", blocking_timeout=300
1259 ) as lock:
1260 try:
1261 lock.write_lock()
1262 except timeout_util.TimeoutError as e:
1263 logging.error(
1264 "Acquiring write_lock on %s failed: %s", lock_path, e
1265 )
1266 logging.warning(
Alex Kleinef517832023-01-13 12:06:51 -07001267 "Continuing with CleanupChroot(%s), which will umount the "
1268 "tree.",
Alex Klein1699fab2022-09-08 08:46:06 -06001269 options.chroot,
1270 )
Alex Kleinef517832023-01-13 12:06:51 -07001271 # We can call CleanupChroot (which calls
1272 # cros_sdk_lib.CleanupChrootMount) even if we don't get the lock
1273 # because it will attempt to unmount the tree and will print
1274 # diagnostic information from 'fuser', 'lsof', and 'ps'.
Alex Klein1699fab2022-09-08 08:46:06 -06001275 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=False)
1276 sys.exit(0)
1277
1278 # Make sure the main chroot mount is visible. Contents will be filled in
1279 # below if needed.
1280 if options.create and options.use_image:
1281 if missing_image_tools:
1282 raise SystemExit(
1283 """The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001284Please make sure the lvm2 and thin-provisioning-tools packages
1285are installed on your host.
1286Example(ubuntu):
1287 sudo apt-get install lvm2 thin-provisioning-tools
1288
1289If you want to run without lvm2, pass --nouse-image (chroot
Alex Klein1699fab2022-09-08 08:46:06 -06001290snapshots will be unavailable)."""
1291 % ", ".join(missing_image_tools)
1292 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001293
Alex Klein1699fab2022-09-08 08:46:06 -06001294 logging.debug("Making sure chroot image is mounted.")
1295 with locking.FileLock(lock_path, "chroot lock") as lock:
1296 lock.write_lock()
1297 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1298 cros_build_lib.Die(
1299 "Unable to mount %s on chroot",
1300 _ImageFileForChroot(options.chroot),
1301 )
1302 logging.notice(
1303 "Mounted %s on chroot", _ImageFileForChroot(options.chroot)
1304 )
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001305
Alex Klein1699fab2022-09-08 08:46:06 -06001306 # Snapshot operations will always need the VG/LV, but other actions won't.
1307 if any_snapshot_operation:
1308 with locking.FileLock(lock_path, "chroot lock") as lock:
1309 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
1310 options.chroot
1311 )
1312 if not chroot_vg or not chroot_lv:
1313 cros_build_lib.Die(
1314 "Unable to find VG/LV for chroot %s", options.chroot
1315 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001316
Alex Kleinef517832023-01-13 12:06:51 -07001317 # Delete snapshot before creating a new one. This allows the user to
1318 # throw out old state, create a new snapshot, and enter the chroot
1319 # in a single call to cros_sdk. Since restore involves deleting,
1320 # also do it before creating.
Alex Klein1699fab2022-09-08 08:46:06 -06001321 if options.snapshot_restore:
1322 lock.write_lock()
1323 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1324 if options.snapshot_restore not in valid_snapshots:
1325 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -07001326 "%s is not a valid snapshot to restore to. "
1327 "Valid snapshots: %s",
Alex Klein1699fab2022-09-08 08:46:06 -06001328 options.snapshot_restore,
1329 ", ".join(valid_snapshots),
1330 )
1331 osutils.UmountTree(options.chroot)
1332 if not RestoreChrootSnapshot(
1333 options.snapshot_restore, chroot_vg, chroot_lv
1334 ):
1335 cros_build_lib.Die("Unable to restore chroot to snapshot.")
1336 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1337 cros_build_lib.Die(
1338 "Unable to mount restored snapshot onto chroot."
1339 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001340
Alex Kleinef517832023-01-13 12:06:51 -07001341 # Use a read lock for snapshot delete and create even though they
1342 # modify the filesystem, because they don't modify the mounted
1343 # chroot itself. The underlying LVM commands take their own locks,
1344 # so conflicting concurrent operations here may crash cros_sdk, but
1345 # won't corrupt the chroot image. This tradeoff seems worth it to
1346 # allow snapshot operations on chroots that have a process inside.
Alex Klein1699fab2022-09-08 08:46:06 -06001347 if options.snapshot_delete:
1348 lock.read_lock()
1349 DeleteChrootSnapshot(
1350 options.snapshot_delete, chroot_vg, chroot_lv
1351 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001352
Alex Klein1699fab2022-09-08 08:46:06 -06001353 if options.snapshot_create:
1354 lock.read_lock()
1355 if not CreateChrootSnapshot(
1356 options.snapshot_create, chroot_vg, chroot_lv
1357 ):
1358 cros_build_lib.Die("Unable to create snapshot.")
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001359
Alex Klein1699fab2022-09-08 08:46:06 -06001360 img_path = _ImageFileForChroot(options.chroot)
1361 if (
1362 options.use_image
1363 and os.path.exists(options.chroot)
1364 and os.path.exists(img_path)
1365 ):
1366 img_stat = os.stat(img_path)
1367 img_used_bytes = img_stat.st_blocks * 512
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001368
Alex Klein1699fab2022-09-08 08:46:06 -06001369 mount_stat = os.statvfs(options.chroot)
1370 mount_used_bytes = mount_stat.f_frsize * (
1371 mount_stat.f_blocks - mount_stat.f_bfree
1372 )
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001373
Alex Klein1699fab2022-09-08 08:46:06 -06001374 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
1375 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1376 logging.notice(
1377 "%s is using %s GiB more than needed. Running "
1378 "fstrim in background.",
1379 img_path,
1380 extra_gbs,
1381 )
1382 pid = os.fork()
1383 if pid == 0:
1384 try:
1385 # Directly call Popen to run fstrim concurrently.
1386 cmd = ["fstrim", options.chroot]
1387 subprocess.Popen(cmd, close_fds=True, shell=False)
1388 except subprocess.SubprocessError as e:
1389 logging.warning(
1390 "Running fstrim failed. Consider running fstrim on "
1391 "your chroot manually.\n%s",
1392 e,
1393 )
1394 os._exit(0) # pylint: disable=protected-access
1395 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001396
Alex Kleinef517832023-01-13 12:06:51 -07001397 # Enter a new set of namespaces. Everything after here cannot directly
1398 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -06001399 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001400
Alex Klein1699fab2022-09-08 08:46:06 -06001401 if options.snapshot_list:
1402 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1403 print(snap)
1404 sys.exit(0)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001405
Alex Klein1699fab2022-09-08 08:46:06 -06001406 if not options.sdk_version:
1407 sdk_version = (
1408 bootstrap_latest_version
1409 if options.bootstrap
1410 else sdk_latest_version
1411 )
Yong Hong4e29b622018-02-05 14:31:10 +08001412 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001413 sdk_version = options.sdk_version
1414 if options.buildbot_log_version:
1415 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001416
Alex Klein1699fab2022-09-08 08:46:06 -06001417 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -07001418 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001419 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -06001420 if options.sdk_url:
1421 urls = [options.sdk_url]
1422 else:
1423 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -07001424
Alex Klein1699fab2022-09-08 08:46:06 -06001425 with locking.FileLock(lock_path, "chroot lock") as lock:
1426 if options.proxy_sim:
1427 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -07001428
Alex Klein1699fab2022-09-08 08:46:06 -06001429 sdk_cache = os.path.join(options.cache_dir, "sdks")
1430 distfiles_cache = os.path.join(options.cache_dir, "distfiles")
1431 osutils.SafeMakedirsNonRoot(options.cache_dir)
1432
1433 for target in (sdk_cache, distfiles_cache):
1434 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1435 if not os.path.exists(src):
1436 osutils.SafeMakedirsNonRoot(target)
1437 continue
1438 lock.write_lock(
1439 "Upgrade to %r needed but chroot is locked; please exit "
1440 "all instances so this upgrade can finish." % src
1441 )
1442 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -07001443 # Note that while waiting for the write lock, src may've
1444 # vanished; it's a rare race during the upgrade process that's a
1445 # byproduct of us avoiding taking a write lock to do the src
1446 # check. If we took a write lock for that check, it would
1447 # effectively limit all cros_sdk for a chroot to a single
1448 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -06001449 osutils.SafeMakedirsNonRoot(target)
1450 elif not os.path.exists(target):
1451 # Upgrade occurred, but a reversion, or something whacky
1452 # occurred writing to the old location. Wipe and continue.
1453 os.rename(src, target)
1454 else:
1455 # Upgrade occurred once already, but either a reversion or
1456 # some before/after separate cros_sdk usage is at play.
1457 # Wipe and continue.
1458 osutils.RmDir(src)
1459
Alex Klein1699fab2022-09-08 08:46:06 -06001460 mounted = False
1461 if options.create:
1462 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -07001463 # Recheck if the chroot is set up here before creating to make sure
1464 # we account for whatever the various delete/unmount/remount steps
1465 # above have done.
Alex Klein1699fab2022-09-08 08:46:06 -06001466 if cros_sdk_lib.IsChrootReady(options.chroot):
1467 logging.debug("Chroot already exists. Skipping creation.")
1468 else:
Alex Klein22690a12022-11-17 10:56:09 -07001469 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001470 cros_sdk_lib.CreateChroot(
1471 Path(options.chroot),
1472 Path(sdk_tarball),
1473 Path(options.cache_dir),
1474 usepkg=not options.bootstrap and not options.nousepkg,
1475 chroot_upgrade=options.chroot_upgrade,
1476 )
1477 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -07001478 elif options.download:
1479 # Allow downloading only.
1480 lock.write_lock()
1481 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001482
1483 if options.enter:
1484 lock.read_lock()
1485 if not mounted:
1486 cros_sdk_lib.MountChrootPaths(options.chroot)
1487 ret = EnterChroot(
1488 options.chroot,
1489 options.cache_dir,
1490 options.chrome_root,
1491 options.chrome_root_mount,
1492 options.goma_dir,
Alex Klein1699fab2022-09-08 08:46:06 -06001493 options.reclient_dir,
1494 options.reproxy_cfg_file,
1495 options.working_dir,
Mike Frysinger114a7b92023-01-26 19:08:57 -05001496 options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -06001497 )
1498 sys.exit(ret.returncode)