blob: 4742bcf7f8678fc1366372259f0a42802b799535 [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 Frysinger9a5124e2023-01-26 17:16:44 -050027from typing import List, Optional
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
Mike Frysinger9a5124e2023-01-26 17:16:44 -050035from chromite.lib import goma_lib
Brian Harringb938c782012-02-29 15:14:38 -080036from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080037from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070038from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080039from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040040from chromite.lib import process_util
Mike Frysinger253c8392023-01-27 01:12:21 -050041from chromite.lib import remoteexec_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080042from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060043from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050044from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040045from chromite.utils import key_value_store
46
Brian Harringb938c782012-02-29 15:14:38 -080047
Mike Frysingerf744d032022-05-07 20:38:39 -040048# Which compression algos the SDK tarball uses. We've used xz since 2012.
Alex Klein1699fab2022-09-08 08:46:06 -060049COMPRESSION_PREFERENCE = ("xz",)
Zdenek Behanfd0efe42012-04-13 04:36:40 +020050
Brian Harringb938c782012-02-29 15:14:38 -080051# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070052ENTER_CHROOT = [
Alex Klein1699fab2022-09-08 08:46:06 -060053 os.path.join(constants.SOURCE_ROOT, "src/scripts/sdk_lib/enter_chroot.sh")
Manoj Guptab12f7302019-06-03 16:40:14 -070054]
Brian Harringb938c782012-02-29 15:14:38 -080055
Josh Triplett472a4182013-03-08 11:48:57 -080056# Proxy simulator configuration.
Alex Klein1699fab2022-09-08 08:46:06 -060057PROXY_HOST_IP = "192.168.240.1"
Josh Triplett472a4182013-03-08 11:48:57 -080058PROXY_PORT = 8080
Alex Klein1699fab2022-09-08 08:46:06 -060059PROXY_GUEST_IP = "192.168.240.2"
Josh Triplett472a4182013-03-08 11:48:57 -080060PROXY_NETMASK = 30
Alex Klein1699fab2022-09-08 08:46:06 -060061PROXY_VETH_PREFIX = "veth"
Josh Triplett472a4182013-03-08 11:48:57 -080062PROXY_CONNECT_PORTS = (80, 443, 9418)
Alex Klein1699fab2022-09-08 08:46:06 -060063PROXY_APACHE_FALLBACK_USERS = ("www-data", "apache", "nobody")
64PROXY_APACHE_MPMS = ("event", "worker", "prefork")
65PROXY_APACHE_FALLBACK_PATH = ":".join(
66 "/usr/lib/apache2/mpm-%s" % mpm for mpm in PROXY_APACHE_MPMS
67)
68PROXY_APACHE_MODULE_GLOBS = ("/usr/lib*/apache2/modules", "/usr/lib*/apache2")
Josh Triplett472a4182013-03-08 11:48:57 -080069
Josh Triplett9a495f62013-03-15 18:06:55 -070070# We need these tools to run. Very common tools (tar,..) are omitted.
Alex Klein1699fab2022-09-08 08:46:06 -060071NEEDED_TOOLS = ("curl", "xz")
Brian Harringb938c782012-02-29 15:14:38 -080072
Josh Triplett472a4182013-03-08 11:48:57 -080073# Tools needed for --proxy-sim only.
Alex Klein1699fab2022-09-08 08:46:06 -060074PROXY_NEEDED_TOOLS = ("ip",)
Brian Harringb938c782012-02-29 15:14:38 -080075
Mike Frysingerc60141b2023-01-27 00:57:15 -050076# Tools needed when use_image is true.
Alex Klein1699fab2022-09-08 08:46:06 -060077IMAGE_NEEDED_TOOLS = (
78 "losetup",
79 "lvchange",
80 "lvcreate",
81 "lvs",
82 "mke2fs",
83 "pvscan",
84 "thin_check",
85 "vgchange",
86 "vgcreate",
87 "vgs",
88)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060089
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070090# As space is used inside the chroot, the empty space in chroot.img is
91# allocated. Deleting files inside the chroot doesn't automatically return the
92# used space to the OS. Over time, this tends to make the sparse chroot.img
93# less sparse even if the chroot contents don't currently need much space. We
94# can recover most of this unused space with fstrim, but that takes too much
95# time to run it every time. Instead, check the used space against the image
96# size after mounting the chroot and only call fstrim if it looks like we could
97# recover at least this many GiB.
98MAX_UNUSED_IMAGE_GBS = 20
99
Mike Frysingercc838832014-05-24 13:10:30 -0400100
Brian Harring1790ac42012-09-23 08:53:33 -0700101def GetArchStageTarballs(version):
Alex Klein1699fab2022-09-08 08:46:06 -0600102 """Returns the URL for a given arch/version"""
103 extension = {"xz": "tar.xz"}
104 return [
105 toolchain.GetSdkURL(
106 suburl="cros-sdk-%s.%s" % (version, extension[compressor])
107 )
108 for compressor in COMPRESSION_PREFERENCE
109 ]
Brian Harring1790ac42012-09-23 08:53:33 -0700110
111
Mike Frysingerf744d032022-05-07 20:38:39 -0400112def FetchRemoteTarballs(storage_dir, urls):
Alex Klein1699fab2022-09-08 08:46:06 -0600113 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700116 storage_dir: Path where to save the tarball.
117 urls: List of URLs to try to download. Download will stop on first
118 success.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200119
Alex Klein1699fab2022-09-08 08:46:06 -0600120 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700121 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700122
Alex Klein1699fab2022-09-08 08:46:06 -0600123 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700124 ValueError: None of the URLs worked.
Alex Klein1699fab2022-09-08 08:46:06 -0600125 """
126 # Note we track content length ourselves since certain versions of curl
127 # fail if asked to resume a complete file.
128 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
129 status_re = re.compile(rb"^HTTP/[0-9]+(\.[0-9]+)? 200")
130 # pylint: disable=undefined-loop-variable
131 for url in urls:
132 logging.notice("Downloading tarball %s ...", urls[0].rsplit("/", 1)[-1])
133 parsed = urllib.parse.urlparse(url)
134 tarball_name = os.path.basename(parsed.path)
135 if parsed.scheme in ("", "file"):
136 if os.path.exists(parsed.path):
137 return parsed.path
138 continue
139 content_length = 0
140 logging.debug("Attempting download from %s", url)
141 result = retry_util.RunCurl(
142 ["-I", url],
143 print_cmd=False,
144 debug_level=logging.NOTICE,
145 capture_output=True,
146 )
147 successful = False
148 for header in result.stdout.splitlines():
149 # We must walk the output to find the 200 code for use cases where
150 # a proxy is involved and may have pushed down the actual header.
151 if status_re.match(header):
152 successful = True
153 elif header.lower().startswith(b"content-length:"):
154 content_length = int(header.split(b":", 1)[-1].strip())
155 if successful:
156 break
Brian Harring1790ac42012-09-23 08:53:33 -0700157 if successful:
Alex Klein1699fab2022-09-08 08:46:06 -0600158 break
159 else:
160 raise ValueError("No valid URLs found!")
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200161
Alex Klein1699fab2022-09-08 08:46:06 -0600162 tarball_dest = os.path.join(storage_dir, tarball_name)
163 current_size = 0
164 if os.path.exists(tarball_dest):
165 current_size = os.path.getsize(tarball_dest)
166 if current_size > content_length:
167 osutils.SafeUnlink(tarball_dest)
168 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100169
Alex Klein1699fab2022-09-08 08:46:06 -0600170 if current_size < content_length:
171 retry_util.RunCurl(
172 [
173 "--fail",
174 "-L",
175 "-y",
176 "30",
177 "-C",
178 "-",
179 "--output",
180 tarball_dest,
181 url,
182 ],
183 print_cmd=False,
184 debug_level=logging.NOTICE,
185 )
Brian Harringb938c782012-02-29 15:14:38 -0800186
Alex Klein1699fab2022-09-08 08:46:06 -0600187 # Cleanup old tarballs now since we've successfull fetched; only cleanup
188 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
189 # because we might have partial overlap between known prefixes.
190 for p in Path(storage_dir).glob("cros-sdk-*"):
191 if p.name == tarball_name:
192 continue
193 logging.info("Cleaning up old tarball: %s", p)
194 osutils.SafeUnlink(p)
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200195
Alex Klein1699fab2022-09-08 08:46:06 -0600196 return tarball_dest
Brian Harringb938c782012-02-29 15:14:38 -0800197
198
Alex Klein1699fab2022-09-08 08:46:06 -0600199def EnterChroot(
200 chroot_path,
201 cache_dir,
202 chrome_root,
203 chrome_root_mount,
Mike Frysinger9a5124e2023-01-26 17:16:44 -0500204 goma: Optional[goma_lib.Goma],
Mike Frysinger253c8392023-01-27 01:12:21 -0500205 remoteexec: Optional[remoteexec_util.Remoteexec],
Alex Klein1699fab2022-09-08 08:46:06 -0600206 working_dir,
207 additional_args,
208):
209 """Enters an existing SDK chroot"""
210 st = os.statvfs(os.path.join(chroot_path, "usr", "bin", "sudo"))
211 if st.f_flag & os.ST_NOSUID:
212 cros_build_lib.Die("chroot cannot be in a nosuid mount")
Mike Frysingere5456972013-06-13 00:07:23 -0400213
Alex Klein1699fab2022-09-08 08:46:06 -0600214 cmd = ENTER_CHROOT + ["--chroot", chroot_path, "--cache_dir", cache_dir]
215 if chrome_root:
216 cmd.extend(["--chrome_root", chrome_root])
217 if chrome_root_mount:
218 cmd.extend(["--chrome_root_mount", chrome_root_mount])
Mike Frysinger9a5124e2023-01-26 17:16:44 -0500219 if goma:
220 cmd.extend(["--goma_dir", goma.linux_goma_dir])
Mike Frysinger253c8392023-01-27 01:12:21 -0500221 if remoteexec:
222 cmd.extend(
223 [
224 "--reclient_dir",
225 remoteexec.reclient_dir,
226 "--reproxy_cfg_file",
227 remoteexec.reproxy_cfg_file,
228 ]
229 )
Alex Klein1699fab2022-09-08 08:46:06 -0600230 if working_dir is not None:
231 cmd.extend(["--working_dir", working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700232
Alex Klein1699fab2022-09-08 08:46:06 -0600233 if additional_args:
234 cmd.append("--")
235 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700236
Alex Klein1699fab2022-09-08 08:46:06 -0600237 if "CHROMEOS_SUDO_RLIMITS" in os.environ:
238 _SetRlimits(os.environ.pop("CHROMEOS_SUDO_RLIMITS"))
Mike Frysinger12d055b2022-06-23 12:26:47 -0400239
Alex Klein1699fab2022-09-08 08:46:06 -0600240 # Some systems set the soft limit too low. Bump it up to the hard limit.
241 # We don't override the hard limit because it's something the admins put
242 # in place and we want to respect such configs. http://b/234353695
243 soft, hard = resource.getrlimit(resource.RLIMIT_NPROC)
244 if soft != resource.RLIM_INFINITY and soft < 4096:
245 if soft < hard or hard == resource.RLIM_INFINITY:
246 resource.setrlimit(resource.RLIMIT_NPROC, (hard, hard))
Mike Frysingerebb23fc2022-06-06 21:17:39 -0400247
Alex Klein1699fab2022-09-08 08:46:06 -0600248 # ThinLTO opens lots of files at the same time.
249 # Set rlimit and vm.max_map_count to accommodate this.
250 file_limit = 262144
251 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
252 resource.setrlimit(
253 resource.RLIMIT_NOFILE, (max(soft, file_limit), max(hard, file_limit))
254 )
255 max_map_count = int(osutils.ReadFile("/proc/sys/vm/max_map_count"))
256 if max_map_count < file_limit:
257 logging.notice(
258 "Raising vm.max_map_count from %s to %s", max_map_count, file_limit
259 )
260 osutils.WriteFile("/proc/sys/vm/max_map_count", str(file_limit))
261 return cros_build_lib.dbg_run(cmd, check=False)
Brian Harringb938c782012-02-29 15:14:38 -0800262
263
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600264def _ImageFileForChroot(chroot):
Alex Klein1699fab2022-09-08 08:46:06 -0600265 """Find the image file that should be associated with |chroot|.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600266
Alex Klein1699fab2022-09-08 08:46:06 -0600267 This function does not check if the image exists; it simply returns the
268 filename that would be used.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600269
Alex Klein1699fab2022-09-08 08:46:06 -0600270 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700271 chroot: Path to the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600272
Alex Klein1699fab2022-09-08 08:46:06 -0600273 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700274 Path to an image file that would be associated with chroot.
Alex Klein1699fab2022-09-08 08:46:06 -0600275 """
276 return chroot.rstrip("/") + ".img"
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600277
278
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600279def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600280 """Create a snapshot for the specified chroot VG/LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600281
Alex Klein1699fab2022-09-08 08:46:06 -0600282 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700283 snapshot_name: The name of the new snapshot.
284 chroot_vg: The name of the VG containing the origin LV.
285 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600286
Alex Klein1699fab2022-09-08 08:46:06 -0600287 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700288 True if the snapshot was created, or False if a snapshot with the same
289 name already exists.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700292 SystemExit: The lvcreate command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600293 """
294 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
295 logging.error(
296 "Cannot create snapshot %s: A volume with that name already "
297 "exists.",
298 snapshot_name,
299 )
300 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600301
Alex Klein1699fab2022-09-08 08:46:06 -0600302 cmd = [
303 "lvcreate",
304 "-s",
305 "--name",
306 snapshot_name,
307 "%s/%s" % (chroot_vg, chroot_lv),
308 ]
309 try:
310 logging.notice(
311 "Creating snapshot %s from %s in VG %s.",
312 snapshot_name,
313 chroot_lv,
314 chroot_vg,
315 )
316 cros_build_lib.dbg_run(cmd, capture_output=True)
317 return True
318 except cros_build_lib.RunCommandError as e:
319 cros_build_lib.Die("Creating snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600320
321
322def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600323 """Delete the named snapshot from the specified chroot VG.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600324
Alex Klein1699fab2022-09-08 08:46:06 -0600325 If the requested snapshot is not found, nothing happens. The main chroot LV
326 and internal thinpool LV cannot be deleted with this function.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600327
Alex Klein1699fab2022-09-08 08:46:06 -0600328 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700329 snapshot_name: The name of the snapshot to delete.
330 chroot_vg: The name of the VG containing the origin LV.
331 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600332
Alex Klein1699fab2022-09-08 08:46:06 -0600333 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700334 SystemExit: The lvremove command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600335 """
336 if snapshot_name in (
337 cros_sdk_lib.CHROOT_LV_NAME,
338 cros_sdk_lib.CHROOT_THINPOOL_NAME,
339 ):
340 logging.error(
341 "Cannot remove LV %s as a snapshot. Use cros_sdk --delete "
342 "if you want to remove the whole chroot.",
343 snapshot_name,
344 )
345 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600346
Alex Klein1699fab2022-09-08 08:46:06 -0600347 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
348 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600349
Alex Klein1699fab2022-09-08 08:46:06 -0600350 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, snapshot_name)]
351 try:
352 logging.notice(
353 "Deleting snapshot %s in VG %s.", snapshot_name, chroot_vg
354 )
355 cros_build_lib.dbg_run(cmd, capture_output=True)
356 except cros_build_lib.RunCommandError as e:
357 cros_build_lib.Die("Deleting snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600358
359
360def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600361 """Restore the chroot to an existing snapshot.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600362
Alex Klein1699fab2022-09-08 08:46:06 -0600363 This is done by renaming the original |chroot_lv| LV to a temporary name,
364 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
365 now unused LV. If an error occurs, attempts to rename the original snapshot
366 back to |chroot_lv| to leave the chroot unchanged.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600367
Alex Klein1699fab2022-09-08 08:46:06 -0600368 The chroot must be unmounted before calling this function, and will be left
369 unmounted after this function returns.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600370
Alex Klein1699fab2022-09-08 08:46:06 -0600371 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700372 snapshot_name: The name of the snapshot to restore. This snapshot will
373 no longer be accessible at its original name after this function
374 finishes.
375 chroot_vg: The VG containing the chroot LV and snapshot LV.
376 chroot_lv: The name of the original chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600377
Alex Klein1699fab2022-09-08 08:46:06 -0600378 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700379 True if the chroot was restored to the requested snapshot, or False if
380 the snapshot wasn't found or isn't valid.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600381
Alex Klein1699fab2022-09-08 08:46:06 -0600382 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700383 SystemExit: Any of the LVM commands failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600384 """
385 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
386 if (
387 snapshot_name
388 in (cros_sdk_lib.CHROOT_LV_NAME, cros_sdk_lib.CHROOT_THINPOOL_NAME)
389 or snapshot_name not in valid_snapshots
390 ):
391 logging.error(
392 "Chroot cannot be restored to %s. Valid snapshots: %s",
393 snapshot_name,
394 ", ".join(valid_snapshots),
395 )
396 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600397
Alex Klein1699fab2022-09-08 08:46:06 -0600398 backup_chroot_name = "chroot-bak-%d" % random.randint(0, 1000)
399 cmd = ["lvrename", chroot_vg, chroot_lv, backup_chroot_name]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600400 try:
Alex Klein1699fab2022-09-08 08:46:06 -0600401 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500402 except cros_build_lib.RunCommandError as e:
Alex Klein1699fab2022-09-08 08:46:06 -0600403 cros_build_lib.Die("Restoring snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600404
Alex Klein1699fab2022-09-08 08:46:06 -0600405 cmd = ["lvrename", chroot_vg, snapshot_name, chroot_lv]
406 try:
407 cros_build_lib.dbg_run(cmd, capture_output=True)
408 except cros_build_lib.RunCommandError as e:
409 cmd = ["lvrename", chroot_vg, backup_chroot_name, chroot_lv]
410 try:
411 cros_build_lib.dbg_run(cmd, capture_output=True)
412 except cros_build_lib.RunCommandError as e:
413 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -0700414 "Failed to rename %s to chroot and failed to restore %s back "
415 "to chroot!\n%s",
Alex Klein1699fab2022-09-08 08:46:06 -0600416 snapshot_name,
417 backup_chroot_name,
418 e,
419 )
420 cros_build_lib.Die(
421 "Failed to rename %s to chroot! Original chroot LV has "
422 "been restored.\n%s",
423 snapshot_name,
424 e,
425 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600426
Alex Klein1699fab2022-09-08 08:46:06 -0600427 # Some versions of LVM set snapshots to be skipped at auto-activate time.
428 # Other versions don't have this flag at all. We run lvchange to try
Alex Kleinef517832023-01-13 12:06:51 -0700429 # disabling auto-skip and activating the volume, but ignore errors. Versions
Alex Klein1699fab2022-09-08 08:46:06 -0600430 # that don't have the flag should be auto-activated.
431 chroot_lv_path = "%s/%s" % (chroot_vg, chroot_lv)
432 cmd = ["lvchange", "-kn", chroot_lv_path]
433 cros_build_lib.run(cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600434
Alex Klein1699fab2022-09-08 08:46:06 -0600435 # Activate the LV in case the lvchange above was needed. Activating an LV
Alex Kleinef517832023-01-13 12:06:51 -0700436 # that is already active shouldn't do anything, so this is safe to run even
437 # if the -kn wasn't needed.
Alex Klein1699fab2022-09-08 08:46:06 -0600438 cmd = ["lvchange", "-ay", chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500439 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600440
Alex Klein1699fab2022-09-08 08:46:06 -0600441 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, backup_chroot_name)]
442 try:
443 cros_build_lib.dbg_run(cmd, capture_output=True)
444 except cros_build_lib.RunCommandError as e:
445 cros_build_lib.Die(
446 "Failed to remove backup LV %s/%s!\n%s",
447 chroot_vg,
448 backup_chroot_name,
449 e,
450 )
451
452 return True
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600453
454
455def ListChrootSnapshots(chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600456 """Return all snapshots in |chroot_vg| regardless of origin volume.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700459 chroot_vg: The name of the VG containing the chroot.
460 chroot_lv: The name of the chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600461
Alex Klein1699fab2022-09-08 08:46:06 -0600462 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700463 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600464
Alex Klein1699fab2022-09-08 08:46:06 -0600465 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700466 SystemExit: The lvs command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600467 """
468 if not chroot_vg or not chroot_lv:
469 return []
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600470
Alex Klein1699fab2022-09-08 08:46:06 -0600471 cmd = [
472 "lvs",
473 "-o",
474 "lv_name,pool_lv,lv_attr",
475 "-O",
476 "lv_name",
477 "--noheadings",
478 "--separator",
479 "\t",
480 chroot_vg,
481 ]
482 try:
483 result = cros_build_lib.run(
484 cmd, print_cmd=False, stdout=True, encoding="utf-8"
485 )
486 except cros_build_lib.RunCommandError:
487 raise SystemExit("Running %r failed!" % cmd)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600488
Alex Klein1699fab2022-09-08 08:46:06 -0600489 # Once the thin origin volume has been deleted, there's no way to tell a
Alex Kleinef517832023-01-13 12:06:51 -0700490 # snapshot apart from any other volume. Since this VG is created and managed
491 # by cros_sdk, we'll assume that all volumes that share the same thin pool
492 # are valid snapshots.
Alex Klein1699fab2022-09-08 08:46:06 -0600493 snapshots = []
494 snapshot_attrs = re.compile(r"^V.....t.{2,}") # Matches a thin volume.
495 for line in result.stdout.splitlines():
496 lv_name, pool_lv, lv_attr = line.lstrip().split("\t")
497 if (
498 lv_name == chroot_lv
499 or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME
500 or pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME
501 or not snapshot_attrs.match(lv_attr)
502 ):
503 continue
504 snapshots.append(lv_name)
505 return snapshots
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600506
507
Mike Frysinger12d055b2022-06-23 12:26:47 -0400508# The rlimits we will lookup & pass down, in order.
509RLIMITS_TO_PASS = (
510 resource.RLIMIT_AS,
511 resource.RLIMIT_CORE,
512 resource.RLIMIT_CPU,
513 resource.RLIMIT_FSIZE,
514 resource.RLIMIT_MEMLOCK,
515 resource.RLIMIT_NICE,
516 resource.RLIMIT_NOFILE,
517 resource.RLIMIT_NPROC,
518 resource.RLIMIT_RSS,
519 resource.RLIMIT_STACK,
520)
521
522
523def _GetRlimits() -> str:
Alex Klein1699fab2022-09-08 08:46:06 -0600524 """Serialize current rlimits."""
525 return str(tuple(resource.getrlimit(x) for x in RLIMITS_TO_PASS))
Mike Frysinger12d055b2022-06-23 12:26:47 -0400526
527
528def _SetRlimits(limits: str) -> None:
Alex Klein1699fab2022-09-08 08:46:06 -0600529 """Deserialize rlimits."""
530 for rlim, limit in zip(RLIMITS_TO_PASS, ast.literal_eval(limits)):
531 cur_limit = resource.getrlimit(rlim)
532 if cur_limit != limit:
533 # Turn the number into a symbolic name for logging.
534 name = "RLIMIT_???"
535 for name, num in resource.__dict__.items():
536 if name.startswith("RLIMIT_") and num == rlim:
537 break
538 logging.debug(
539 "Restoring user rlimit %s from %r to %r", name, cur_limit, limit
540 )
Mike Frysinger12d055b2022-06-23 12:26:47 -0400541
Alex Klein1699fab2022-09-08 08:46:06 -0600542 resource.setrlimit(rlim, limit)
Mike Frysinger12d055b2022-06-23 12:26:47 -0400543
544
David James56e6c2c2012-10-24 23:54:41 -0700545def _SudoCommand():
Alex Klein1699fab2022-09-08 08:46:06 -0600546 """Get the 'sudo' command, along with all needed environment variables."""
David James56e6c2c2012-10-24 23:54:41 -0700547
Alex Klein1699fab2022-09-08 08:46:06 -0600548 # Pass in the ENVIRONMENT_ALLOWLIST and ENV_PASSTHRU variables so that
549 # scripts in the chroot know what variables to pass through.
550 cmd = ["sudo"]
551 for key in constants.CHROOT_ENVIRONMENT_ALLOWLIST + constants.ENV_PASSTHRU:
552 value = os.environ.get(key)
553 if value is not None:
554 cmd += ["%s=%s" % (key, value)]
David James56e6c2c2012-10-24 23:54:41 -0700555
Alex Kleinef517832023-01-13 12:06:51 -0700556 # We keep PATH not for the chroot but for the re-exec & for programs we
557 # might run before we chroot into the SDK. The process that enters the SDK
558 # itself will take care of initializing PATH to the right value then. But
559 # we can't override the system's default PATH for root as that will hide
560 # /sbin.
Alex Klein1699fab2022-09-08 08:46:06 -0600561 cmd += ["CHROMEOS_SUDO_PATH=%s" % os.environ.get("PATH", "")]
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400562
Alex Klein1699fab2022-09-08 08:46:06 -0600563 # Pass along current rlimit settings so we can restore them.
564 cmd += [f"CHROMEOS_SUDO_RLIMITS={_GetRlimits()}"]
Mike Frysinger12d055b2022-06-23 12:26:47 -0400565
Alex Klein1699fab2022-09-08 08:46:06 -0600566 # Pass in the path to the depot_tools so that users can access them from
567 # within the chroot.
568 cmd += ["DEPOT_TOOLS=%s" % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500569
Alex Klein1699fab2022-09-08 08:46:06 -0600570 return cmd
David James56e6c2c2012-10-24 23:54:41 -0700571
572
Josh Triplett472a4182013-03-08 11:48:57 -0800573def _ReportMissing(missing):
Alex Klein1699fab2022-09-08 08:46:06 -0600574 """Report missing utilities, then exit.
Josh Triplett472a4182013-03-08 11:48:57 -0800575
Alex Klein1699fab2022-09-08 08:46:06 -0600576 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700577 missing: List of missing utilities, as returned by
578 osutils.FindMissingBinaries. If non-empty, will not return.
Alex Klein1699fab2022-09-08 08:46:06 -0600579 """
Josh Triplett472a4182013-03-08 11:48:57 -0800580
Alex Klein1699fab2022-09-08 08:46:06 -0600581 if missing:
582 raise SystemExit(
583 "The tool(s) %s were not found.\n"
584 "Please install the appropriate package in your host.\n"
585 "Example(ubuntu):\n"
586 " sudo apt-get install <packagename>" % ", ".join(missing)
587 )
Josh Triplett472a4182013-03-08 11:48:57 -0800588
589
590def _ProxySimSetup(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600591 """Set up proxy simulator, and return only in the child environment.
Josh Triplett472a4182013-03-08 11:48:57 -0800592
Alex Klein1699fab2022-09-08 08:46:06 -0600593 TODO: Ideally, this should support multiple concurrent invocations of
594 cros_sdk --proxy-sim; currently, such invocations will conflict with each
595 other due to the veth device names and IP addresses. Either this code would
596 need to generate fresh, unused names for all of these before forking, or it
597 would need to support multiple concurrent cros_sdk invocations sharing one
598 proxy and allowing it to exit when unused (without counting on any local
599 service-management infrastructure on the host).
600 """
Josh Triplett472a4182013-03-08 11:48:57 -0800601
Alex Klein1699fab2022-09-08 08:46:06 -0600602 may_need_mpm = False
603 apache_bin = osutils.Which("apache2")
Josh Triplett472a4182013-03-08 11:48:57 -0800604 if apache_bin is None:
Alex Klein1699fab2022-09-08 08:46:06 -0600605 apache_bin = osutils.Which("apache2", PROXY_APACHE_FALLBACK_PATH)
606 if apache_bin is None:
607 _ReportMissing(("apache2",))
608 else:
609 may_need_mpm = True
Josh Triplett472a4182013-03-08 11:48:57 -0800610
Alex Klein1699fab2022-09-08 08:46:06 -0600611 # Module names and .so names included for ease of grepping.
612 apache_modules = [
613 ("proxy_module", "mod_proxy.so"),
614 ("proxy_connect_module", "mod_proxy_connect.so"),
615 ("proxy_http_module", "mod_proxy_http.so"),
616 ("proxy_ftp_module", "mod_proxy_ftp.so"),
617 ]
Josh Triplett472a4182013-03-08 11:48:57 -0800618
Alex Kleinef517832023-01-13 12:06:51 -0700619 # Find the apache module directory and make sure it has the modules we need.
Alex Klein1699fab2022-09-08 08:46:06 -0600620 module_dirs = {}
621 for g in PROXY_APACHE_MODULE_GLOBS:
622 for _, so in apache_modules:
623 for f in glob.glob(os.path.join(g, so)):
624 module_dirs.setdefault(os.path.dirname(f), []).append(so)
625 for apache_module_path, modules_found in module_dirs.items():
626 if len(modules_found) == len(apache_modules):
627 break
628 else:
Alex Kleinef517832023-01-13 12:06:51 -0700629 # Appease cros lint, which doesn't understand that this else block will
630 # not fall through to the subsequent code which relies on
631 # apache_module_path.
Alex Klein1699fab2022-09-08 08:46:06 -0600632 apache_module_path = None
633 raise SystemExit(
Alex Kleinef517832023-01-13 12:06:51 -0700634 "Could not find apache module path containing all required "
635 "modules: %s" % ", ".join(so for mod, so in apache_modules)
Alex Klein1699fab2022-09-08 08:46:06 -0600636 )
Josh Triplett472a4182013-03-08 11:48:57 -0800637
Alex Klein1699fab2022-09-08 08:46:06 -0600638 def check_add_module(name):
639 so = "mod_%s.so" % name
640 if os.access(os.path.join(apache_module_path, so), os.F_OK):
641 mod = "%s_module" % name
642 apache_modules.append((mod, so))
643 return True
644 return False
Josh Triplett472a4182013-03-08 11:48:57 -0800645
Alex Klein1699fab2022-09-08 08:46:06 -0600646 check_add_module("authz_core")
647 if may_need_mpm:
648 for mpm in PROXY_APACHE_MPMS:
649 if check_add_module("mpm_%s" % mpm):
650 break
Josh Triplett472a4182013-03-08 11:48:57 -0800651
Alex Klein1699fab2022-09-08 08:46:06 -0600652 veth_host = "%s-host" % PROXY_VETH_PREFIX
653 veth_guest = "%s-guest" % PROXY_VETH_PREFIX
Josh Triplett472a4182013-03-08 11:48:57 -0800654
Alex Klein1699fab2022-09-08 08:46:06 -0600655 # Set up locks to sync the net namespace setup. We need the child to create
Alex Kleinef517832023-01-13 12:06:51 -0700656 # the net ns first, and then have the parent assign the guest end of the
657 # veth interface to the child's new network namespace & bring up the proxy.
658 # Only then can the child move forward and rely on the network being up.
Alex Klein1699fab2022-09-08 08:46:06 -0600659 ns_create_lock = locking.PipeLock()
660 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800661
Alex Klein1699fab2022-09-08 08:46:06 -0600662 pid = os.fork()
663 if not pid:
664 # Create our new isolated net namespace.
665 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500666
Alex Klein1699fab2022-09-08 08:46:06 -0600667 # Signal the parent the ns is ready to be configured.
668 ns_create_lock.Post()
669 del ns_create_lock
670
671 # Wait for the parent to finish setting up the ns/proxy.
672 ns_setup_lock.Wait()
673 del ns_setup_lock
674
675 # Set up child side of the network.
676 commands = (
677 ("ip", "link", "set", "up", "lo"),
678 (
679 "ip",
680 "address",
681 "add",
682 "%s/%u" % (PROXY_GUEST_IP, PROXY_NETMASK),
683 "dev",
684 veth_guest,
685 ),
686 ("ip", "link", "set", veth_guest, "up"),
687 )
688 try:
689 for cmd in commands:
690 cros_build_lib.dbg_run(cmd)
691 except cros_build_lib.RunCommandError as e:
692 cros_build_lib.Die("Proxy setup failed!\n%s", e)
693
694 proxy_url = "http://%s:%u" % (PROXY_HOST_IP, PROXY_PORT)
695 for proto in ("http", "https", "ftp"):
696 os.environ[proto + "_proxy"] = proxy_url
697 for v in ("all_proxy", "RSYNC_PROXY", "no_proxy"):
698 os.environ.pop(v, None)
699 return
700
701 # Set up parent side of the network.
702 uid = int(os.environ.get("SUDO_UID", "0"))
703 gid = int(os.environ.get("SUDO_GID", "0"))
704 if uid == 0 or gid == 0:
705 for username in PROXY_APACHE_FALLBACK_USERS:
706 try:
707 pwnam = pwd.getpwnam(username)
708 uid, gid = pwnam.pw_uid, pwnam.pw_gid
709 break
710 except KeyError:
711 continue
712 if uid == 0 or gid == 0:
713 raise SystemExit("Could not find a non-root user to run Apache as")
714
715 chroot_parent, chroot_base = os.path.split(options.chroot)
716 pid_file = os.path.join(chroot_parent, ".%s-apache-proxy.pid" % chroot_base)
717 log_file = os.path.join(chroot_parent, ".%s-apache-proxy.log" % chroot_base)
718
719 # Wait for the child to create the net ns.
720 ns_create_lock.Wait()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500721 del ns_create_lock
722
Alex Klein1699fab2022-09-08 08:46:06 -0600723 apache_directives = [
724 "User #%u" % uid,
725 "Group #%u" % gid,
726 "PidFile %s" % pid_file,
727 "ErrorLog %s" % log_file,
728 "Listen %s:%u" % (PROXY_HOST_IP, PROXY_PORT),
729 "ServerName %s" % PROXY_HOST_IP,
730 "ProxyRequests On",
731 "AllowCONNECT %s" % " ".join(str(x) for x in PROXY_CONNECT_PORTS),
732 ] + [
733 "LoadModule %s %s" % (mod, os.path.join(apache_module_path, so))
734 for (mod, so) in apache_modules
735 ]
736 commands = (
737 (
738 "ip",
739 "link",
740 "add",
741 "name",
742 veth_host,
743 "type",
744 "veth",
745 "peer",
746 "name",
747 veth_guest,
748 ),
749 (
750 "ip",
751 "address",
752 "add",
753 "%s/%u" % (PROXY_HOST_IP, PROXY_NETMASK),
754 "dev",
755 veth_host,
756 ),
757 ("ip", "link", "set", veth_host, "up"),
758 (
759 [apache_bin, "-f", "/dev/null"]
760 + [arg for d in apache_directives for arg in ("-C", d)]
761 ),
762 ("ip", "link", "set", veth_guest, "netns", str(pid)),
763 )
764 cmd = None # Make cros lint happy.
765 try:
766 for cmd in commands:
767 cros_build_lib.dbg_run(cmd)
768 except cros_build_lib.RunCommandError as e:
769 # Clean up existing interfaces, if any.
770 cmd_cleanup = ("ip", "link", "del", veth_host)
771 try:
772 cros_build_lib.run(cmd_cleanup, print_cmd=False)
773 except cros_build_lib.RunCommandError:
774 logging.error("running %r failed", cmd_cleanup)
775 cros_build_lib.Die("Proxy network setup failed!\n%s", e)
776
777 # Signal the child that the net ns/proxy is fully configured now.
778 ns_setup_lock.Post()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500779 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800780
Alex Klein1699fab2022-09-08 08:46:06 -0600781 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800782
783
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400784def _BuildReExecCommand(argv, opts) -> List[str]:
Alex Klein1699fab2022-09-08 08:46:06 -0600785 """Generate new command for self-reexec."""
786 # Make sure to preserve the active Python executable in case the version
787 # we're running as is not the default one found via the (new) $PATH.
788 cmd = _SudoCommand() + ["--"]
789 if opts.strace:
790 cmd += ["strace"] + shlex.split(opts.strace_arguments) + ["--"]
791 return cmd + [sys.executable] + argv
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400792
793
794def _ReExecuteIfNeeded(argv, opts):
Alex Klein1699fab2022-09-08 08:46:06 -0600795 """Re-execute cros_sdk as root.
David James56e6c2c2012-10-24 23:54:41 -0700796
Alex Klein1699fab2022-09-08 08:46:06 -0600797 Also unshare the mount namespace so as to ensure that processes outside
798 the chroot can't mess with our mounts.
799 """
800 if osutils.IsNonRootUser():
801 cmd = _BuildReExecCommand(argv, opts)
802 logging.debug(
803 "Reexecing self via sudo:\n%s", cros_build_lib.CmdToStr(cmd)
804 )
805 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700806
807
Mike Frysinger34db8692013-11-11 14:54:08 -0500808def _CreateParser(sdk_latest_version, bootstrap_latest_version):
Alex Klein1699fab2022-09-08 08:46:06 -0600809 """Generate and return the parser with all the options."""
810 usage = (
811 "usage: %(prog)s [options] "
812 "[VAR1=val1 ... VAR2=val2] [--] [command [args]]"
813 )
814 parser = commandline.ArgumentParser(
815 usage=usage, description=__doc__, caching=True
816 )
Brian Harring218e13c2012-10-10 16:21:26 -0700817
Alex Klein1699fab2022-09-08 08:46:06 -0600818 # Global options.
819 default_chroot = os.path.join(
820 constants.SOURCE_ROOT, constants.DEFAULT_CHROOT_DIR
821 )
822 parser.add_argument(
823 "--chroot",
824 dest="chroot",
825 default=default_chroot,
826 type="path",
827 help=("SDK chroot dir name [%s]" % constants.DEFAULT_CHROOT_DIR),
828 )
829 parser.add_argument(
830 "--nouse-image",
831 dest="use_image",
832 action="store_false",
833 default=False,
834 help="Do not mount the chroot on a loopback image; "
835 "instead, create it directly in a directory.",
836 )
837 parser.add_argument(
838 "--use-image",
839 dest="use_image",
840 action="store_true",
841 default=False,
842 help="Mount the chroot on a loopback image "
843 "instead of creating it directly in a directory.",
844 )
Brian Harringb938c782012-02-29 15:14:38 -0800845
Alex Klein1699fab2022-09-08 08:46:06 -0600846 parser.add_argument(
847 "--chrome-root",
848 "--chrome_root",
849 type="path",
850 help="Mount this chrome root into the SDK chroot",
851 )
852 parser.add_argument(
853 "--chrome_root_mount",
854 type="path",
855 help="Mount chrome into this path inside SDK chroot",
856 )
857 parser.add_argument(
858 "--nousepkg",
859 action="store_true",
860 default=False,
861 help="Do not use binary packages when creating a chroot.",
862 )
863 parser.add_argument(
864 "-u",
865 "--url",
866 dest="sdk_url",
867 help="Use sdk tarball located at this url. Use file:// "
868 "for local files.",
869 )
870 parser.add_argument(
871 "--sdk-version",
872 help=(
873 "Use this sdk version. For prebuilt, current is %r"
874 ", for bootstrapping it is %r."
875 % (sdk_latest_version, bootstrap_latest_version)
876 ),
877 )
878 parser.add_argument(
Mike Frysinger85824562023-01-31 02:40:43 -0500879 "--goma-dir",
Alex Klein1699fab2022-09-08 08:46:06 -0600880 "--goma_dir",
881 type="path",
882 help="Goma installed directory to mount into the chroot.",
883 )
884 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600885 "--reclient-dir",
886 type="path",
887 help="Reclient installed directory to mount into the chroot.",
888 )
889 parser.add_argument(
890 "--reproxy-cfg-file",
891 type="path",
892 help="Config file for re-client's reproxy used for remoteexec.",
893 )
894 parser.add_argument(
895 "--skip-chroot-upgrade",
896 dest="chroot_upgrade",
897 action="store_false",
898 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700899 help="Skip automatic SDK and toolchain upgrade when entering the "
900 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600901 )
Yong Hong84ba9172018-02-07 01:37:42 +0800902
Alex Klein1699fab2022-09-08 08:46:06 -0600903 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700904 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600905 parser.add_argument(
906 "--working-dir",
907 type=str,
908 help="Run the command in specific working directory in "
909 "chroot. If the given directory is a relative "
910 "path, this program will transfer the path to "
911 "the corresponding one inside chroot.",
912 )
Yong Hong84ba9172018-02-07 01:37:42 +0800913
Alex Klein1699fab2022-09-08 08:46:06 -0600914 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500915
Alex Klein1699fab2022-09-08 08:46:06 -0600916 # Commands.
917 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400918 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600919 "--enter",
920 action="store_true",
921 default=False,
922 help="Enter the SDK chroot. Implies --create.",
923 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400924 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600925 "--create",
926 action="store_true",
927 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700928 help="Create the chroot only if it does not already exist. Downloads "
929 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600930 )
931 group.add_argument(
932 "--bootstrap",
933 action="store_true",
934 default=False,
935 help="Build everything from scratch, including the sdk. "
936 "Use this only if you need to validate a change "
937 "that affects SDK creation itself (toolchain and "
938 "build are typically the only folk who need this). "
939 "Note this will quite heavily slow down the build. "
940 "This option implies --create --nousepkg.",
941 )
942 group.add_argument(
943 "-r",
944 "--replace",
945 action="store_true",
946 default=False,
947 help="Replace an existing SDK chroot. Basically an alias "
948 "for --delete --create.",
949 )
950 group.add_argument(
951 "--delete",
952 action="store_true",
953 default=False,
954 help="Delete the current SDK chroot if it exists.",
955 )
956 group.add_argument(
957 "--force",
958 action="store_true",
959 default=False,
960 help="Force unmount/delete of the current SDK chroot even if "
961 "obtaining the write lock fails.",
962 )
963 group.add_argument(
964 "--unmount",
965 action="store_true",
966 default=False,
967 help="Unmount and clean up devices associated with the "
968 "SDK chroot if it exists. This does not delete the "
969 "backing image file, so the same chroot can be later "
970 "re-mounted for reuse. To fully delete the chroot, use "
971 "--delete. This is primarily useful for working on "
972 "cros_sdk or the chroot setup; you should not need it "
973 "under normal circumstances.",
974 )
975 group.add_argument(
976 "--download",
977 action="store_true",
978 default=False,
979 help="Download the sdk.",
980 )
981 group.add_argument(
982 "--snapshot-create",
983 metavar="SNAPSHOT_NAME",
984 help="Create a snapshot of the chroot. Requires that the chroot was "
985 "created without the --nouse-image option.",
986 )
987 group.add_argument(
988 "--snapshot-restore",
989 metavar="SNAPSHOT_NAME",
990 help="Restore the chroot to a previously created snapshot.",
991 )
992 group.add_argument(
993 "--snapshot-delete",
994 metavar="SNAPSHOT_NAME",
995 help="Delete a previously created snapshot. Deleting a snapshot that "
996 "does not exist is not an error.",
997 )
998 group.add_argument(
999 "--snapshot-list",
1000 action="store_true",
1001 default=False,
1002 help="List existing snapshots of the chroot and exit.",
1003 )
1004 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -04001005
Alex Klein1699fab2022-09-08 08:46:06 -06001006 # Namespace options.
1007 group = parser.add_argument_group("Namespaces")
1008 group.add_argument(
1009 "--proxy-sim",
1010 action="store_true",
1011 default=False,
1012 help="Simulate a restrictive network requiring an outbound" " proxy.",
1013 )
1014 for ns, default in (("pid", True), ("net", None)):
1015 group.add_argument(
1016 f"--ns-{ns}",
1017 default=default,
1018 action="store_true",
1019 help=f"Create a new {ns} namespace.",
1020 )
1021 group.add_argument(
1022 f"--no-ns-{ns}",
1023 dest=f"ns_{ns}",
1024 action="store_false",
1025 help=f"Do not create a new {ns} namespace.",
1026 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -04001027
Alex Klein1699fab2022-09-08 08:46:06 -06001028 # Debug options.
1029 group = parser.debug_group
1030 group.add_argument(
1031 "--strace",
1032 action="store_true",
1033 help="Run cros_sdk through strace after re-exec via sudo",
1034 )
1035 group.add_argument(
1036 "--strace-arguments",
1037 default="",
1038 help="Extra strace options (shell quoting permitted)",
1039 )
Mike Frysinger34db8692013-11-11 14:54:08 -05001040
Alex Klein1699fab2022-09-08 08:46:06 -06001041 # Internal options.
1042 group = parser.add_argument_group(
1043 "Internal Chromium OS Build Team Options",
1044 "Caution: these are for meant for the Chromium OS build team only",
1045 )
1046 group.add_argument(
1047 "--buildbot-log-version",
1048 default=False,
1049 action="store_true",
1050 help="Log SDK version for buildbot consumption",
1051 )
1052
1053 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -05001054
1055
1056def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -06001057 # Turn on strict sudo checks.
1058 cros_build_lib.STRICT_SUDO = True
1059 conf = key_value_store.LoadFile(
1060 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
1061 ignore_missing=True,
1062 )
1063 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
1064 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -07001065
Alex Klein1699fab2022-09-08 08:46:06 -06001066 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
1067 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
1068 bootstrap_latest_version = (
1069 sdk_latest_version
1070 if bootstrap_frozen_version == "<unknown>"
1071 else bootstrap_frozen_version
1072 )
1073 parser, commands = _CreateParser(
1074 sdk_latest_version, bootstrap_latest_version
1075 )
1076 options = parser.parse_args(argv)
Brian Harringb938c782012-02-29 15:14:38 -08001077
Alex Klein1699fab2022-09-08 08:46:06 -06001078 # Some basic checks first, before we ask for sudo credentials.
1079 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -08001080
Alex Klein1699fab2022-09-08 08:46:06 -06001081 host = os.uname()[4]
1082 if host != "x86_64":
1083 cros_build_lib.Die(
1084 "cros_sdk is currently only supported on x86_64; you're running"
1085 " %s. Please find a x86_64 machine." % (host,)
1086 )
Brian Harring1790ac42012-09-23 08:53:33 -07001087
Mike Frysinger9a5124e2023-01-26 17:16:44 -05001088 goma = goma_lib.Goma(options.goma_dir) if options.goma_dir else None
1089
Alex Klein1699fab2022-09-08 08:46:06 -06001090 # Merge the outside PATH setting if we re-execed ourselves.
1091 if "CHROMEOS_SUDO_PATH" in os.environ:
1092 os.environ["PATH"] = "%s:%s" % (
1093 os.environ.pop("CHROMEOS_SUDO_PATH"),
1094 os.environ["PATH"],
1095 )
Mike Frysinger2bda4d12020-07-14 11:15:49 -04001096
Alex Klein1699fab2022-09-08 08:46:06 -06001097 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
1098 if options.proxy_sim:
1099 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
1100 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -08001101
Alex Klein1699fab2022-09-08 08:46:06 -06001102 if (
1103 sdk_latest_version == "<unknown>"
1104 or bootstrap_latest_version == "<unknown>"
1105 ):
1106 cros_build_lib.Die(
1107 "No SDK version was found. "
1108 "Are you in a Chromium source tree instead of Chromium OS?\n\n"
1109 "Please change to a directory inside your Chromium OS source tree\n"
1110 "and retry. If you need to setup a Chromium OS source tree, see\n"
1111 " https://dev.chromium.org/chromium-os/developer-guide"
1112 )
Benjamin Gordon040a1162017-06-29 13:44:47 -06001113
Alex Klein1699fab2022-09-08 08:46:06 -06001114 any_snapshot_operation = (
1115 options.snapshot_create
1116 or options.snapshot_restore
1117 or options.snapshot_delete
1118 or options.snapshot_list
1119 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001120
Alex Klein1699fab2022-09-08 08:46:06 -06001121 if (
1122 options.snapshot_delete
1123 and options.snapshot_delete == options.snapshot_restore
1124 ):
1125 parser.error(
1126 "Cannot --snapshot_delete the same snapshot you are "
1127 "restoring with --snapshot_restore."
1128 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001129
Alex Klein1699fab2022-09-08 08:46:06 -06001130 _ReExecuteIfNeeded([sys.argv[0]] + argv, options)
David James471532c2013-01-21 10:23:31 -08001131
Alex Klein1699fab2022-09-08 08:46:06 -06001132 lock_path = os.path.dirname(options.chroot)
1133 lock_path = os.path.join(
1134 lock_path, ".%s_lock" % os.path.basename(options.chroot).lstrip(".")
1135 )
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001136
Alex Klein1699fab2022-09-08 08:46:06 -06001137 # Expand out the aliases...
1138 if options.replace:
1139 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001140
Alex Klein1699fab2022-09-08 08:46:06 -06001141 if options.bootstrap:
1142 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001143
Alex Klein1699fab2022-09-08 08:46:06 -06001144 # If a command is not given, default to enter.
1145 # pylint: disable=protected-access
1146 # This _group_actions access sucks, but upstream decided to not include an
1147 # alternative to optparse's option_list, and this is what they recommend.
1148 options.enter |= not any(
1149 getattr(options, x.dest) for x in commands._group_actions
1150 )
1151 # pylint: enable=protected-access
Mike Frysinger114a7b92023-01-26 19:08:57 -05001152 options.enter |= bool(options.commands)
Brian Harring218e13c2012-10-10 16:21:26 -07001153
Alex Klein1699fab2022-09-08 08:46:06 -06001154 if (
1155 options.delete
1156 and not options.create
1157 and (options.enter or any_snapshot_operation)
1158 ):
1159 parser.error(
1160 "Trying to enter or snapshot the chroot when --delete "
1161 "was specified makes no sense."
1162 )
Brian Harring218e13c2012-10-10 16:21:26 -07001163
Alex Klein1699fab2022-09-08 08:46:06 -06001164 if options.unmount and (
1165 options.create or options.enter or any_snapshot_operation
1166 ):
1167 parser.error("--unmount cannot be specified with other chroot actions.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001168
Alex Klein1699fab2022-09-08 08:46:06 -06001169 if options.working_dir is not None and not os.path.isabs(
1170 options.working_dir
1171 ):
1172 options.working_dir = path_util.ToChrootPath(options.working_dir)
Yong Hong84ba9172018-02-07 01:37:42 +08001173
Alex Klein1699fab2022-09-08 08:46:06 -06001174 # If there is an existing chroot image and we're not removing it then force
1175 # use_image on. This ensures that people don't have to remember to pass
Alex Kleinef517832023-01-13 12:06:51 -07001176 # --use-image after a reboot to avoid losing access to their existing
1177 # chroot.
Alex Klein1699fab2022-09-08 08:46:06 -06001178 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1179 img_path = _ImageFileForChroot(options.chroot)
1180 if (
1181 not options.use_image
1182 and not options.delete
1183 and not options.unmount
1184 and os.path.exists(img_path)
1185 ):
1186 if chroot_exists:
1187 # If the chroot is already populated, make sure it has something
1188 # mounted on it before we assume it came from an image.
1189 cmd = ["mountpoint", "-q", options.chroot]
1190 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
1191 options.use_image = True
Benjamin Gordon832a4412020-12-08 10:39:16 -07001192
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001193 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001194 logging.notice(
1195 "Existing chroot image %s found. Forcing --use-image on.",
1196 img_path,
1197 )
1198 options.use_image = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001199
Alex Klein1699fab2022-09-08 08:46:06 -06001200 if any_snapshot_operation and not options.use_image:
1201 if os.path.exists(img_path):
1202 options.use_image = True
1203 else:
1204 cros_build_lib.Die(
1205 "Snapshot operations are not compatible with " "--nouse-image."
1206 )
Mike Frysingerc60141b2023-01-27 00:57:15 -05001207 if options.use_image:
1208 logging.warning("--use-image is deprecated and will be removed soon.")
1209 logging.warning("Please migrate, or create a new one with --delete.")
1210 logging.warning("See http://b/266878468 for details.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001211
Alex Klein1699fab2022-09-08 08:46:06 -06001212 # Discern if we need to create the chroot.
1213 if (
1214 options.use_image
1215 and not chroot_exists
1216 and not options.delete
1217 and not options.unmount
1218 and not missing_image_tools
1219 and os.path.exists(img_path)
1220 ):
1221 # Try to re-mount an existing image in case the user has rebooted.
1222 with locking.FileLock(lock_path, "chroot lock") as lock:
1223 logging.debug("Checking if existing chroot image can be mounted.")
1224 lock.write_lock()
1225 cros_sdk_lib.MountChroot(options.chroot, create=False)
1226 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1227 if chroot_exists:
1228 logging.notice("Mounted existing image %s on chroot", img_path)
1229
1230 # Finally, flip create if necessary.
1231 if options.enter or options.snapshot_create:
1232 options.create |= not chroot_exists
1233
1234 # Make sure we will download if we plan to create.
1235 options.download |= options.create
1236
Mike Frysinger6bbd3e92023-01-27 00:05:02 -05001237 options.Freeze()
1238
Mike Frysinger253c8392023-01-27 01:12:21 -05001239 remoteexec = (
1240 remoteexec_util.Remoteexec(
1241 options.reclient_dir, options.reproxy_cfg_file
1242 )
1243 if (options.reclient_dir and options.reproxy_cfg_file)
1244 else None
1245 )
1246
Alex Kleinef517832023-01-13 12:06:51 -07001247 # Anything that needs to manipulate the main chroot mount or communicate
1248 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -06001249
1250 # If deleting, do it regardless of the use_image flag so that a
1251 # previously-created loopback chroot can also be cleaned up.
1252 if options.delete:
1253 # Set a timeout of 300 seconds when getting the lock.
1254 with locking.FileLock(
1255 lock_path, "chroot lock", blocking_timeout=300
1256 ) as lock:
1257 try:
1258 lock.write_lock()
1259 except timeout_util.TimeoutError as e:
1260 logging.error(
1261 "Acquiring write_lock on %s failed: %s", lock_path, e
1262 )
1263 if not options.force:
1264 cros_build_lib.Die(
1265 "Exiting; use --force to continue w/o lock."
1266 )
1267 else:
1268 logging.warning(
1269 "cros_sdk was invoked with force option, continuing."
1270 )
1271 logging.notice("Deleting chroot.")
1272 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
1273
Alex Kleinef517832023-01-13 12:06:51 -07001274 # If cleanup was requested, we have to do it while we're still in the
1275 # original namespace. Since cleaning up the mount will interfere with any
1276 # other commands, we exit here. The check above should have made sure that
1277 # no other action was requested, anyway.
Alex Klein1699fab2022-09-08 08:46:06 -06001278 if options.unmount:
1279 # Set a timeout of 300 seconds when getting the lock.
1280 with locking.FileLock(
1281 lock_path, "chroot lock", blocking_timeout=300
1282 ) as lock:
1283 try:
1284 lock.write_lock()
1285 except timeout_util.TimeoutError as e:
1286 logging.error(
1287 "Acquiring write_lock on %s failed: %s", lock_path, e
1288 )
1289 logging.warning(
Alex Kleinef517832023-01-13 12:06:51 -07001290 "Continuing with CleanupChroot(%s), which will umount the "
1291 "tree.",
Alex Klein1699fab2022-09-08 08:46:06 -06001292 options.chroot,
1293 )
Alex Kleinef517832023-01-13 12:06:51 -07001294 # We can call CleanupChroot (which calls
1295 # cros_sdk_lib.CleanupChrootMount) even if we don't get the lock
1296 # because it will attempt to unmount the tree and will print
1297 # diagnostic information from 'fuser', 'lsof', and 'ps'.
Alex Klein1699fab2022-09-08 08:46:06 -06001298 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=False)
1299 sys.exit(0)
1300
1301 # Make sure the main chroot mount is visible. Contents will be filled in
1302 # below if needed.
1303 if options.create and options.use_image:
1304 if missing_image_tools:
1305 raise SystemExit(
1306 """The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001307Please make sure the lvm2 and thin-provisioning-tools packages
1308are installed on your host.
1309Example(ubuntu):
1310 sudo apt-get install lvm2 thin-provisioning-tools
1311
1312If you want to run without lvm2, pass --nouse-image (chroot
Alex Klein1699fab2022-09-08 08:46:06 -06001313snapshots will be unavailable)."""
1314 % ", ".join(missing_image_tools)
1315 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001316
Alex Klein1699fab2022-09-08 08:46:06 -06001317 logging.debug("Making sure chroot image is mounted.")
1318 with locking.FileLock(lock_path, "chroot lock") as lock:
1319 lock.write_lock()
1320 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1321 cros_build_lib.Die(
1322 "Unable to mount %s on chroot",
1323 _ImageFileForChroot(options.chroot),
1324 )
1325 logging.notice(
1326 "Mounted %s on chroot", _ImageFileForChroot(options.chroot)
1327 )
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001328
Alex Klein1699fab2022-09-08 08:46:06 -06001329 # Snapshot operations will always need the VG/LV, but other actions won't.
1330 if any_snapshot_operation:
1331 with locking.FileLock(lock_path, "chroot lock") as lock:
1332 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
1333 options.chroot
1334 )
1335 if not chroot_vg or not chroot_lv:
1336 cros_build_lib.Die(
1337 "Unable to find VG/LV for chroot %s", options.chroot
1338 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001339
Alex Kleinef517832023-01-13 12:06:51 -07001340 # Delete snapshot before creating a new one. This allows the user to
1341 # throw out old state, create a new snapshot, and enter the chroot
1342 # in a single call to cros_sdk. Since restore involves deleting,
1343 # also do it before creating.
Alex Klein1699fab2022-09-08 08:46:06 -06001344 if options.snapshot_restore:
1345 lock.write_lock()
1346 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1347 if options.snapshot_restore not in valid_snapshots:
1348 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -07001349 "%s is not a valid snapshot to restore to. "
1350 "Valid snapshots: %s",
Alex Klein1699fab2022-09-08 08:46:06 -06001351 options.snapshot_restore,
1352 ", ".join(valid_snapshots),
1353 )
1354 osutils.UmountTree(options.chroot)
1355 if not RestoreChrootSnapshot(
1356 options.snapshot_restore, chroot_vg, chroot_lv
1357 ):
1358 cros_build_lib.Die("Unable to restore chroot to snapshot.")
1359 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1360 cros_build_lib.Die(
1361 "Unable to mount restored snapshot onto chroot."
1362 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001363
Alex Kleinef517832023-01-13 12:06:51 -07001364 # Use a read lock for snapshot delete and create even though they
1365 # modify the filesystem, because they don't modify the mounted
1366 # chroot itself. The underlying LVM commands take their own locks,
1367 # so conflicting concurrent operations here may crash cros_sdk, but
1368 # won't corrupt the chroot image. This tradeoff seems worth it to
1369 # allow snapshot operations on chroots that have a process inside.
Alex Klein1699fab2022-09-08 08:46:06 -06001370 if options.snapshot_delete:
1371 lock.read_lock()
1372 DeleteChrootSnapshot(
1373 options.snapshot_delete, chroot_vg, chroot_lv
1374 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001375
Alex Klein1699fab2022-09-08 08:46:06 -06001376 if options.snapshot_create:
1377 lock.read_lock()
1378 if not CreateChrootSnapshot(
1379 options.snapshot_create, chroot_vg, chroot_lv
1380 ):
1381 cros_build_lib.Die("Unable to create snapshot.")
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001382
Alex Klein1699fab2022-09-08 08:46:06 -06001383 img_path = _ImageFileForChroot(options.chroot)
1384 if (
1385 options.use_image
1386 and os.path.exists(options.chroot)
1387 and os.path.exists(img_path)
1388 ):
1389 img_stat = os.stat(img_path)
1390 img_used_bytes = img_stat.st_blocks * 512
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001391
Alex Klein1699fab2022-09-08 08:46:06 -06001392 mount_stat = os.statvfs(options.chroot)
1393 mount_used_bytes = mount_stat.f_frsize * (
1394 mount_stat.f_blocks - mount_stat.f_bfree
1395 )
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001396
Alex Klein1699fab2022-09-08 08:46:06 -06001397 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
1398 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1399 logging.notice(
1400 "%s is using %s GiB more than needed. Running "
1401 "fstrim in background.",
1402 img_path,
1403 extra_gbs,
1404 )
1405 pid = os.fork()
1406 if pid == 0:
1407 try:
1408 # Directly call Popen to run fstrim concurrently.
1409 cmd = ["fstrim", options.chroot]
1410 subprocess.Popen(cmd, close_fds=True, shell=False)
1411 except subprocess.SubprocessError as e:
1412 logging.warning(
1413 "Running fstrim failed. Consider running fstrim on "
1414 "your chroot manually.\n%s",
1415 e,
1416 )
1417 os._exit(0) # pylint: disable=protected-access
1418 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001419
Alex Kleinef517832023-01-13 12:06:51 -07001420 # Enter a new set of namespaces. Everything after here cannot directly
1421 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -06001422 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001423
Alex Klein1699fab2022-09-08 08:46:06 -06001424 if options.snapshot_list:
1425 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1426 print(snap)
1427 sys.exit(0)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001428
Alex Klein1699fab2022-09-08 08:46:06 -06001429 if not options.sdk_version:
1430 sdk_version = (
1431 bootstrap_latest_version
1432 if options.bootstrap
1433 else sdk_latest_version
1434 )
Yong Hong4e29b622018-02-05 14:31:10 +08001435 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001436 sdk_version = options.sdk_version
1437 if options.buildbot_log_version:
1438 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001439
Alex Klein1699fab2022-09-08 08:46:06 -06001440 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -07001441 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001442 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -06001443 if options.sdk_url:
1444 urls = [options.sdk_url]
1445 else:
1446 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -07001447
Alex Klein1699fab2022-09-08 08:46:06 -06001448 with locking.FileLock(lock_path, "chroot lock") as lock:
1449 if options.proxy_sim:
1450 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -07001451
Alex Klein1699fab2022-09-08 08:46:06 -06001452 sdk_cache = os.path.join(options.cache_dir, "sdks")
1453 distfiles_cache = os.path.join(options.cache_dir, "distfiles")
1454 osutils.SafeMakedirsNonRoot(options.cache_dir)
1455
1456 for target in (sdk_cache, distfiles_cache):
1457 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1458 if not os.path.exists(src):
1459 osutils.SafeMakedirsNonRoot(target)
1460 continue
1461 lock.write_lock(
1462 "Upgrade to %r needed but chroot is locked; please exit "
1463 "all instances so this upgrade can finish." % src
1464 )
1465 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -07001466 # Note that while waiting for the write lock, src may've
1467 # vanished; it's a rare race during the upgrade process that's a
1468 # byproduct of us avoiding taking a write lock to do the src
1469 # check. If we took a write lock for that check, it would
1470 # effectively limit all cros_sdk for a chroot to a single
1471 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -06001472 osutils.SafeMakedirsNonRoot(target)
1473 elif not os.path.exists(target):
1474 # Upgrade occurred, but a reversion, or something whacky
1475 # occurred writing to the old location. Wipe and continue.
1476 os.rename(src, target)
1477 else:
1478 # Upgrade occurred once already, but either a reversion or
1479 # some before/after separate cros_sdk usage is at play.
1480 # Wipe and continue.
1481 osutils.RmDir(src)
1482
Alex Klein1699fab2022-09-08 08:46:06 -06001483 mounted = False
1484 if options.create:
1485 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -07001486 # Recheck if the chroot is set up here before creating to make sure
1487 # we account for whatever the various delete/unmount/remount steps
1488 # above have done.
Alex Klein1699fab2022-09-08 08:46:06 -06001489 if cros_sdk_lib.IsChrootReady(options.chroot):
1490 logging.debug("Chroot already exists. Skipping creation.")
1491 else:
Alex Klein22690a12022-11-17 10:56:09 -07001492 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001493 cros_sdk_lib.CreateChroot(
1494 Path(options.chroot),
1495 Path(sdk_tarball),
1496 Path(options.cache_dir),
1497 usepkg=not options.bootstrap and not options.nousepkg,
1498 chroot_upgrade=options.chroot_upgrade,
1499 )
1500 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -07001501 elif options.download:
1502 # Allow downloading only.
1503 lock.write_lock()
1504 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001505
1506 if options.enter:
1507 lock.read_lock()
1508 if not mounted:
1509 cros_sdk_lib.MountChrootPaths(options.chroot)
1510 ret = EnterChroot(
1511 options.chroot,
1512 options.cache_dir,
1513 options.chrome_root,
1514 options.chrome_root_mount,
Mike Frysinger9a5124e2023-01-26 17:16:44 -05001515 goma,
Mike Frysinger253c8392023-01-27 01:12:21 -05001516 remoteexec,
Alex Klein1699fab2022-09-08 08:46:06 -06001517 options.working_dir,
Mike Frysinger114a7b92023-01-26 19:08:57 -05001518 options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -06001519 )
1520 sys.exit(ret.returncode)