blob: 2445d52a96054597dd6b7e1f745c73036ab72ca7 [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 Frysingerec32bea2023-01-27 01:20:48 -050027from 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
Mike Frysingerec32bea2023-01-27 01:20:48 -050031from chromite.lib import chroot_lib
Brian Harringb6cf9142012-09-01 20:43:17 -070032from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060033from chromite.lib import constants
Brian Harringb938c782012-02-29 15:14:38 -080034from chromite.lib import cros_build_lib
Benjamin Gordon74645232018-05-04 17:40:42 -060035from chromite.lib import cros_sdk_lib
Mike Frysinger9a5124e2023-01-26 17:16:44 -050036from chromite.lib import goma_lib
Brian Harringb938c782012-02-29 15:14:38 -080037from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080038from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070039from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080040from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040041from chromite.lib import process_util
Mike Frysinger253c8392023-01-27 01:12:21 -050042from chromite.lib import remoteexec_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080043from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060044from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050045from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040046from chromite.utils import key_value_store
47
Brian Harringb938c782012-02-29 15:14:38 -080048
Mike Frysingerf744d032022-05-07 20:38:39 -040049# Which compression algos the SDK tarball uses. We've used xz since 2012.
Alex Klein1699fab2022-09-08 08:46:06 -060050COMPRESSION_PREFERENCE = ("xz",)
Zdenek Behanfd0efe42012-04-13 04:36:40 +020051
Brian Harringb938c782012-02-29 15:14:38 -080052# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070053ENTER_CHROOT = [
Alex Klein1699fab2022-09-08 08:46:06 -060054 os.path.join(constants.SOURCE_ROOT, "src/scripts/sdk_lib/enter_chroot.sh")
Manoj Guptab12f7302019-06-03 16:40:14 -070055]
Brian Harringb938c782012-02-29 15:14:38 -080056
Josh Triplett472a4182013-03-08 11:48:57 -080057# Proxy simulator configuration.
Alex Klein1699fab2022-09-08 08:46:06 -060058PROXY_HOST_IP = "192.168.240.1"
Josh Triplett472a4182013-03-08 11:48:57 -080059PROXY_PORT = 8080
Alex Klein1699fab2022-09-08 08:46:06 -060060PROXY_GUEST_IP = "192.168.240.2"
Josh Triplett472a4182013-03-08 11:48:57 -080061PROXY_NETMASK = 30
Alex Klein1699fab2022-09-08 08:46:06 -060062PROXY_VETH_PREFIX = "veth"
Josh Triplett472a4182013-03-08 11:48:57 -080063PROXY_CONNECT_PORTS = (80, 443, 9418)
Alex Klein1699fab2022-09-08 08:46:06 -060064PROXY_APACHE_FALLBACK_USERS = ("www-data", "apache", "nobody")
65PROXY_APACHE_MPMS = ("event", "worker", "prefork")
66PROXY_APACHE_FALLBACK_PATH = ":".join(
67 "/usr/lib/apache2/mpm-%s" % mpm for mpm in PROXY_APACHE_MPMS
68)
69PROXY_APACHE_MODULE_GLOBS = ("/usr/lib*/apache2/modules", "/usr/lib*/apache2")
Josh Triplett472a4182013-03-08 11:48:57 -080070
Josh Triplett9a495f62013-03-15 18:06:55 -070071# We need these tools to run. Very common tools (tar,..) are omitted.
Alex Klein1699fab2022-09-08 08:46:06 -060072NEEDED_TOOLS = ("curl", "xz")
Brian Harringb938c782012-02-29 15:14:38 -080073
Josh Triplett472a4182013-03-08 11:48:57 -080074# Tools needed for --proxy-sim only.
Alex Klein1699fab2022-09-08 08:46:06 -060075PROXY_NEEDED_TOOLS = ("ip",)
Brian Harringb938c782012-02-29 15:14:38 -080076
Mike Frysingerc60141b2023-01-27 00:57:15 -050077# Tools needed when use_image is true.
Alex Klein1699fab2022-09-08 08:46:06 -060078IMAGE_NEEDED_TOOLS = (
79 "losetup",
80 "lvchange",
81 "lvcreate",
82 "lvs",
83 "mke2fs",
84 "pvscan",
85 "thin_check",
86 "vgchange",
87 "vgcreate",
88 "vgs",
89)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060090
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070091# As space is used inside the chroot, the empty space in chroot.img is
92# allocated. Deleting files inside the chroot doesn't automatically return the
93# used space to the OS. Over time, this tends to make the sparse chroot.img
94# less sparse even if the chroot contents don't currently need much space. We
95# can recover most of this unused space with fstrim, but that takes too much
96# time to run it every time. Instead, check the used space against the image
97# size after mounting the chroot and only call fstrim if it looks like we could
98# recover at least this many GiB.
99MAX_UNUSED_IMAGE_GBS = 20
100
Mike Frysingercc838832014-05-24 13:10:30 -0400101
Brian Harring1790ac42012-09-23 08:53:33 -0700102def GetArchStageTarballs(version):
Alex Klein1699fab2022-09-08 08:46:06 -0600103 """Returns the URL for a given arch/version"""
104 extension = {"xz": "tar.xz"}
105 return [
106 toolchain.GetSdkURL(
107 suburl="cros-sdk-%s.%s" % (version, extension[compressor])
108 )
109 for compressor in COMPRESSION_PREFERENCE
110 ]
Brian Harring1790ac42012-09-23 08:53:33 -0700111
112
Mike Frysingerf744d032022-05-07 20:38:39 -0400113def FetchRemoteTarballs(storage_dir, urls):
Alex Klein1699fab2022-09-08 08:46:06 -0600114 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200115
Alex Klein1699fab2022-09-08 08:46:06 -0600116 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700117 storage_dir: Path where to save the tarball.
118 urls: List of URLs to try to download. Download will stop on first
119 success.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200120
Alex Klein1699fab2022-09-08 08:46:06 -0600121 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700122 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700123
Alex Klein1699fab2022-09-08 08:46:06 -0600124 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700125 ValueError: None of the URLs worked.
Alex Klein1699fab2022-09-08 08:46:06 -0600126 """
127 # Note we track content length ourselves since certain versions of curl
128 # fail if asked to resume a complete file.
129 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
130 status_re = re.compile(rb"^HTTP/[0-9]+(\.[0-9]+)? 200")
131 # pylint: disable=undefined-loop-variable
132 for url in urls:
133 logging.notice("Downloading tarball %s ...", urls[0].rsplit("/", 1)[-1])
134 parsed = urllib.parse.urlparse(url)
135 tarball_name = os.path.basename(parsed.path)
136 if parsed.scheme in ("", "file"):
137 if os.path.exists(parsed.path):
138 return parsed.path
139 continue
140 content_length = 0
141 logging.debug("Attempting download from %s", url)
142 result = retry_util.RunCurl(
143 ["-I", url],
144 print_cmd=False,
145 debug_level=logging.NOTICE,
146 capture_output=True,
147 )
148 successful = False
149 for header in result.stdout.splitlines():
150 # We must walk the output to find the 200 code for use cases where
151 # a proxy is involved and may have pushed down the actual header.
152 if status_re.match(header):
153 successful = True
154 elif header.lower().startswith(b"content-length:"):
155 content_length = int(header.split(b":", 1)[-1].strip())
156 if successful:
157 break
Brian Harring1790ac42012-09-23 08:53:33 -0700158 if successful:
Alex Klein1699fab2022-09-08 08:46:06 -0600159 break
160 else:
161 raise ValueError("No valid URLs found!")
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 tarball_dest = os.path.join(storage_dir, tarball_name)
164 current_size = 0
165 if os.path.exists(tarball_dest):
166 current_size = os.path.getsize(tarball_dest)
167 if current_size > content_length:
168 osutils.SafeUnlink(tarball_dest)
169 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100170
Alex Klein1699fab2022-09-08 08:46:06 -0600171 if current_size < content_length:
172 retry_util.RunCurl(
173 [
174 "--fail",
175 "-L",
176 "-y",
177 "30",
178 "-C",
179 "-",
180 "--output",
181 tarball_dest,
182 url,
183 ],
184 print_cmd=False,
185 debug_level=logging.NOTICE,
186 )
Brian Harringb938c782012-02-29 15:14:38 -0800187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 # Cleanup old tarballs now since we've successfull fetched; only cleanup
189 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
190 # because we might have partial overlap between known prefixes.
191 for p in Path(storage_dir).glob("cros-sdk-*"):
192 if p.name == tarball_name:
193 continue
194 logging.info("Cleaning up old tarball: %s", p)
195 osutils.SafeUnlink(p)
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200196
Alex Klein1699fab2022-09-08 08:46:06 -0600197 return tarball_dest
Brian Harringb938c782012-02-29 15:14:38 -0800198
199
Alex Klein1699fab2022-09-08 08:46:06 -0600200def EnterChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -0500201 chroot: chroot_lib.Chroot,
Alex Klein1699fab2022-09-08 08:46:06 -0600202 chrome_root_mount,
Alex Klein1699fab2022-09-08 08:46:06 -0600203 working_dir,
204 additional_args,
205):
206 """Enters an existing SDK chroot"""
Mike Frysingerec32bea2023-01-27 01:20:48 -0500207 st = os.statvfs(os.path.join(chroot.path, "usr", "bin", "sudo"))
Alex Klein1699fab2022-09-08 08:46:06 -0600208 if st.f_flag & os.ST_NOSUID:
209 cros_build_lib.Die("chroot cannot be in a nosuid mount")
Mike Frysingere5456972013-06-13 00:07:23 -0400210
Mike Frysingerec32bea2023-01-27 01:20:48 -0500211 cmd = ENTER_CHROOT + [
212 "--chroot",
213 chroot.path,
214 "--cache_dir",
215 chroot.cache_dir,
216 ]
217 if chroot.chrome_root:
218 cmd.extend(["--chrome_root", chroot.chrome_root])
Alex Klein1699fab2022-09-08 08:46:06 -0600219 if chrome_root_mount:
220 cmd.extend(["--chrome_root_mount", chrome_root_mount])
Mike Frysingerec32bea2023-01-27 01:20:48 -0500221 if chroot.goma:
222 cmd.extend(["--goma_dir", chroot.goma.linux_goma_dir])
223 if chroot.remoteexec:
Mike Frysinger253c8392023-01-27 01:12:21 -0500224 cmd.extend(
225 [
226 "--reclient_dir",
Mike Frysingerec32bea2023-01-27 01:20:48 -0500227 chroot.remoteexec.reclient_dir,
Mike Frysinger253c8392023-01-27 01:12:21 -0500228 "--reproxy_cfg_file",
Mike Frysingerec32bea2023-01-27 01:20:48 -0500229 chroot.remoteexec.reproxy_cfg_file,
Mike Frysinger253c8392023-01-27 01:12:21 -0500230 ]
231 )
Alex Klein1699fab2022-09-08 08:46:06 -0600232 if working_dir is not None:
233 cmd.extend(["--working_dir", working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700234
Alex Klein1699fab2022-09-08 08:46:06 -0600235 if additional_args:
236 cmd.append("--")
237 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700238
Alex Klein1699fab2022-09-08 08:46:06 -0600239 if "CHROMEOS_SUDO_RLIMITS" in os.environ:
240 _SetRlimits(os.environ.pop("CHROMEOS_SUDO_RLIMITS"))
Mike Frysinger12d055b2022-06-23 12:26:47 -0400241
Alex Klein1699fab2022-09-08 08:46:06 -0600242 # Some systems set the soft limit too low. Bump it up to the hard limit.
243 # We don't override the hard limit because it's something the admins put
244 # in place and we want to respect such configs. http://b/234353695
245 soft, hard = resource.getrlimit(resource.RLIMIT_NPROC)
246 if soft != resource.RLIM_INFINITY and soft < 4096:
247 if soft < hard or hard == resource.RLIM_INFINITY:
248 resource.setrlimit(resource.RLIMIT_NPROC, (hard, hard))
Mike Frysingerebb23fc2022-06-06 21:17:39 -0400249
Alex Klein1699fab2022-09-08 08:46:06 -0600250 # ThinLTO opens lots of files at the same time.
251 # Set rlimit and vm.max_map_count to accommodate this.
252 file_limit = 262144
253 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
254 resource.setrlimit(
255 resource.RLIMIT_NOFILE, (max(soft, file_limit), max(hard, file_limit))
256 )
257 max_map_count = int(osutils.ReadFile("/proc/sys/vm/max_map_count"))
258 if max_map_count < file_limit:
259 logging.notice(
260 "Raising vm.max_map_count from %s to %s", max_map_count, file_limit
261 )
262 osutils.WriteFile("/proc/sys/vm/max_map_count", str(file_limit))
263 return cros_build_lib.dbg_run(cmd, check=False)
Brian Harringb938c782012-02-29 15:14:38 -0800264
265
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600266def _ImageFileForChroot(chroot):
Alex Klein1699fab2022-09-08 08:46:06 -0600267 """Find the image file that should be associated with |chroot|.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600268
Alex Klein1699fab2022-09-08 08:46:06 -0600269 This function does not check if the image exists; it simply returns the
270 filename that would be used.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600271
Alex Klein1699fab2022-09-08 08:46:06 -0600272 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700273 chroot: Path to the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600274
Alex Klein1699fab2022-09-08 08:46:06 -0600275 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700276 Path to an image file that would be associated with chroot.
Alex Klein1699fab2022-09-08 08:46:06 -0600277 """
278 return chroot.rstrip("/") + ".img"
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600279
280
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600281def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600282 """Create a snapshot for the specified chroot VG/LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600283
Alex Klein1699fab2022-09-08 08:46:06 -0600284 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700285 snapshot_name: The name of the new snapshot.
286 chroot_vg: The name of the VG containing the origin LV.
287 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600288
Alex Klein1699fab2022-09-08 08:46:06 -0600289 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700290 True if the snapshot was created, or False if a snapshot with the same
291 name already exists.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600292
Alex Klein1699fab2022-09-08 08:46:06 -0600293 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700294 SystemExit: The lvcreate command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600295 """
296 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
297 logging.error(
298 "Cannot create snapshot %s: A volume with that name already "
299 "exists.",
300 snapshot_name,
301 )
302 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600303
Alex Klein1699fab2022-09-08 08:46:06 -0600304 cmd = [
305 "lvcreate",
306 "-s",
307 "--name",
308 snapshot_name,
309 "%s/%s" % (chroot_vg, chroot_lv),
310 ]
311 try:
312 logging.notice(
313 "Creating snapshot %s from %s in VG %s.",
314 snapshot_name,
315 chroot_lv,
316 chroot_vg,
317 )
318 cros_build_lib.dbg_run(cmd, capture_output=True)
319 return True
320 except cros_build_lib.RunCommandError as e:
321 cros_build_lib.Die("Creating snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600322
323
324def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600325 """Delete the named snapshot from the specified chroot VG.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 If the requested snapshot is not found, nothing happens. The main chroot LV
328 and internal thinpool LV cannot be deleted with this function.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600329
Alex Klein1699fab2022-09-08 08:46:06 -0600330 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700331 snapshot_name: The name of the snapshot to delete.
332 chroot_vg: The name of the VG containing the origin LV.
333 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600334
Alex Klein1699fab2022-09-08 08:46:06 -0600335 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700336 SystemExit: The lvremove command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600337 """
338 if snapshot_name in (
339 cros_sdk_lib.CHROOT_LV_NAME,
340 cros_sdk_lib.CHROOT_THINPOOL_NAME,
341 ):
342 logging.error(
343 "Cannot remove LV %s as a snapshot. Use cros_sdk --delete "
344 "if you want to remove the whole chroot.",
345 snapshot_name,
346 )
347 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600348
Alex Klein1699fab2022-09-08 08:46:06 -0600349 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
350 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600351
Alex Klein1699fab2022-09-08 08:46:06 -0600352 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, snapshot_name)]
353 try:
354 logging.notice(
355 "Deleting snapshot %s in VG %s.", snapshot_name, chroot_vg
356 )
357 cros_build_lib.dbg_run(cmd, capture_output=True)
358 except cros_build_lib.RunCommandError as e:
359 cros_build_lib.Die("Deleting snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600360
361
362def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600363 """Restore the chroot to an existing snapshot.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600364
Alex Klein1699fab2022-09-08 08:46:06 -0600365 This is done by renaming the original |chroot_lv| LV to a temporary name,
366 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
367 now unused LV. If an error occurs, attempts to rename the original snapshot
368 back to |chroot_lv| to leave the chroot unchanged.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600369
Alex Klein1699fab2022-09-08 08:46:06 -0600370 The chroot must be unmounted before calling this function, and will be left
371 unmounted after this function returns.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600372
Alex Klein1699fab2022-09-08 08:46:06 -0600373 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700374 snapshot_name: The name of the snapshot to restore. This snapshot will
375 no longer be accessible at its original name after this function
376 finishes.
377 chroot_vg: The VG containing the chroot LV and snapshot LV.
378 chroot_lv: The name of the original chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600379
Alex Klein1699fab2022-09-08 08:46:06 -0600380 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700381 True if the chroot was restored to the requested snapshot, or False if
382 the snapshot wasn't found or isn't valid.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600383
Alex Klein1699fab2022-09-08 08:46:06 -0600384 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700385 SystemExit: Any of the LVM commands failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600386 """
387 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
388 if (
389 snapshot_name
390 in (cros_sdk_lib.CHROOT_LV_NAME, cros_sdk_lib.CHROOT_THINPOOL_NAME)
391 or snapshot_name not in valid_snapshots
392 ):
393 logging.error(
394 "Chroot cannot be restored to %s. Valid snapshots: %s",
395 snapshot_name,
396 ", ".join(valid_snapshots),
397 )
398 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600399
Alex Klein1699fab2022-09-08 08:46:06 -0600400 backup_chroot_name = "chroot-bak-%d" % random.randint(0, 1000)
401 cmd = ["lvrename", chroot_vg, chroot_lv, backup_chroot_name]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600402 try:
Alex Klein1699fab2022-09-08 08:46:06 -0600403 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500404 except cros_build_lib.RunCommandError as e:
Alex Klein1699fab2022-09-08 08:46:06 -0600405 cros_build_lib.Die("Restoring snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600406
Alex Klein1699fab2022-09-08 08:46:06 -0600407 cmd = ["lvrename", chroot_vg, snapshot_name, chroot_lv]
408 try:
409 cros_build_lib.dbg_run(cmd, capture_output=True)
410 except cros_build_lib.RunCommandError as e:
411 cmd = ["lvrename", chroot_vg, backup_chroot_name, chroot_lv]
412 try:
413 cros_build_lib.dbg_run(cmd, capture_output=True)
414 except cros_build_lib.RunCommandError as e:
415 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -0700416 "Failed to rename %s to chroot and failed to restore %s back "
417 "to chroot!\n%s",
Alex Klein1699fab2022-09-08 08:46:06 -0600418 snapshot_name,
419 backup_chroot_name,
420 e,
421 )
422 cros_build_lib.Die(
423 "Failed to rename %s to chroot! Original chroot LV has "
424 "been restored.\n%s",
425 snapshot_name,
426 e,
427 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600428
Alex Klein1699fab2022-09-08 08:46:06 -0600429 # Some versions of LVM set snapshots to be skipped at auto-activate time.
430 # Other versions don't have this flag at all. We run lvchange to try
Alex Kleinef517832023-01-13 12:06:51 -0700431 # disabling auto-skip and activating the volume, but ignore errors. Versions
Alex Klein1699fab2022-09-08 08:46:06 -0600432 # that don't have the flag should be auto-activated.
433 chroot_lv_path = "%s/%s" % (chroot_vg, chroot_lv)
434 cmd = ["lvchange", "-kn", chroot_lv_path]
435 cros_build_lib.run(cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600436
Alex Klein1699fab2022-09-08 08:46:06 -0600437 # Activate the LV in case the lvchange above was needed. Activating an LV
Alex Kleinef517832023-01-13 12:06:51 -0700438 # that is already active shouldn't do anything, so this is safe to run even
439 # if the -kn wasn't needed.
Alex Klein1699fab2022-09-08 08:46:06 -0600440 cmd = ["lvchange", "-ay", chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500441 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600442
Alex Klein1699fab2022-09-08 08:46:06 -0600443 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, backup_chroot_name)]
444 try:
445 cros_build_lib.dbg_run(cmd, capture_output=True)
446 except cros_build_lib.RunCommandError as e:
447 cros_build_lib.Die(
448 "Failed to remove backup LV %s/%s!\n%s",
449 chroot_vg,
450 backup_chroot_name,
451 e,
452 )
453
454 return True
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600455
456
457def ListChrootSnapshots(chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600458 """Return all snapshots in |chroot_vg| regardless of origin volume.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600459
Alex Klein1699fab2022-09-08 08:46:06 -0600460 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700461 chroot_vg: The name of the VG containing the chroot.
462 chroot_lv: The name of the chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600463
Alex Klein1699fab2022-09-08 08:46:06 -0600464 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700465 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600466
Alex Klein1699fab2022-09-08 08:46:06 -0600467 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700468 SystemExit: The lvs command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600469 """
470 if not chroot_vg or not chroot_lv:
471 return []
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600472
Alex Klein1699fab2022-09-08 08:46:06 -0600473 cmd = [
474 "lvs",
475 "-o",
476 "lv_name,pool_lv,lv_attr",
477 "-O",
478 "lv_name",
479 "--noheadings",
480 "--separator",
481 "\t",
482 chroot_vg,
483 ]
484 try:
485 result = cros_build_lib.run(
486 cmd, print_cmd=False, stdout=True, encoding="utf-8"
487 )
488 except cros_build_lib.RunCommandError:
489 raise SystemExit("Running %r failed!" % cmd)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600490
Alex Klein1699fab2022-09-08 08:46:06 -0600491 # Once the thin origin volume has been deleted, there's no way to tell a
Alex Kleinef517832023-01-13 12:06:51 -0700492 # snapshot apart from any other volume. Since this VG is created and managed
493 # by cros_sdk, we'll assume that all volumes that share the same thin pool
494 # are valid snapshots.
Alex Klein1699fab2022-09-08 08:46:06 -0600495 snapshots = []
496 snapshot_attrs = re.compile(r"^V.....t.{2,}") # Matches a thin volume.
497 for line in result.stdout.splitlines():
498 lv_name, pool_lv, lv_attr = line.lstrip().split("\t")
499 if (
500 lv_name == chroot_lv
501 or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME
502 or pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME
503 or not snapshot_attrs.match(lv_attr)
504 ):
505 continue
506 snapshots.append(lv_name)
507 return snapshots
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600508
509
Mike Frysinger12d055b2022-06-23 12:26:47 -0400510# The rlimits we will lookup & pass down, in order.
511RLIMITS_TO_PASS = (
512 resource.RLIMIT_AS,
513 resource.RLIMIT_CORE,
514 resource.RLIMIT_CPU,
515 resource.RLIMIT_FSIZE,
516 resource.RLIMIT_MEMLOCK,
517 resource.RLIMIT_NICE,
518 resource.RLIMIT_NOFILE,
519 resource.RLIMIT_NPROC,
520 resource.RLIMIT_RSS,
521 resource.RLIMIT_STACK,
522)
523
524
525def _GetRlimits() -> str:
Alex Klein1699fab2022-09-08 08:46:06 -0600526 """Serialize current rlimits."""
527 return str(tuple(resource.getrlimit(x) for x in RLIMITS_TO_PASS))
Mike Frysinger12d055b2022-06-23 12:26:47 -0400528
529
530def _SetRlimits(limits: str) -> None:
Alex Klein1699fab2022-09-08 08:46:06 -0600531 """Deserialize rlimits."""
532 for rlim, limit in zip(RLIMITS_TO_PASS, ast.literal_eval(limits)):
533 cur_limit = resource.getrlimit(rlim)
534 if cur_limit != limit:
535 # Turn the number into a symbolic name for logging.
536 name = "RLIMIT_???"
537 for name, num in resource.__dict__.items():
538 if name.startswith("RLIMIT_") and num == rlim:
539 break
540 logging.debug(
541 "Restoring user rlimit %s from %r to %r", name, cur_limit, limit
542 )
Mike Frysinger12d055b2022-06-23 12:26:47 -0400543
Alex Klein1699fab2022-09-08 08:46:06 -0600544 resource.setrlimit(rlim, limit)
Mike Frysinger12d055b2022-06-23 12:26:47 -0400545
546
David James56e6c2c2012-10-24 23:54:41 -0700547def _SudoCommand():
Alex Klein1699fab2022-09-08 08:46:06 -0600548 """Get the 'sudo' command, along with all needed environment variables."""
David James56e6c2c2012-10-24 23:54:41 -0700549
Alex Klein1699fab2022-09-08 08:46:06 -0600550 # Pass in the ENVIRONMENT_ALLOWLIST and ENV_PASSTHRU variables so that
551 # scripts in the chroot know what variables to pass through.
552 cmd = ["sudo"]
553 for key in constants.CHROOT_ENVIRONMENT_ALLOWLIST + constants.ENV_PASSTHRU:
554 value = os.environ.get(key)
555 if value is not None:
556 cmd += ["%s=%s" % (key, value)]
David James56e6c2c2012-10-24 23:54:41 -0700557
Alex Kleinef517832023-01-13 12:06:51 -0700558 # We keep PATH not for the chroot but for the re-exec & for programs we
559 # might run before we chroot into the SDK. The process that enters the SDK
560 # itself will take care of initializing PATH to the right value then. But
561 # we can't override the system's default PATH for root as that will hide
562 # /sbin.
Alex Klein1699fab2022-09-08 08:46:06 -0600563 cmd += ["CHROMEOS_SUDO_PATH=%s" % os.environ.get("PATH", "")]
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400564
Alex Klein1699fab2022-09-08 08:46:06 -0600565 # Pass along current rlimit settings so we can restore them.
566 cmd += [f"CHROMEOS_SUDO_RLIMITS={_GetRlimits()}"]
Mike Frysinger12d055b2022-06-23 12:26:47 -0400567
Alex Klein1699fab2022-09-08 08:46:06 -0600568 # Pass in the path to the depot_tools so that users can access them from
569 # within the chroot.
570 cmd += ["DEPOT_TOOLS=%s" % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500571
Alex Klein1699fab2022-09-08 08:46:06 -0600572 return cmd
David James56e6c2c2012-10-24 23:54:41 -0700573
574
Josh Triplett472a4182013-03-08 11:48:57 -0800575def _ReportMissing(missing):
Alex Klein1699fab2022-09-08 08:46:06 -0600576 """Report missing utilities, then exit.
Josh Triplett472a4182013-03-08 11:48:57 -0800577
Alex Klein1699fab2022-09-08 08:46:06 -0600578 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700579 missing: List of missing utilities, as returned by
580 osutils.FindMissingBinaries. If non-empty, will not return.
Alex Klein1699fab2022-09-08 08:46:06 -0600581 """
Josh Triplett472a4182013-03-08 11:48:57 -0800582
Alex Klein1699fab2022-09-08 08:46:06 -0600583 if missing:
584 raise SystemExit(
585 "The tool(s) %s were not found.\n"
586 "Please install the appropriate package in your host.\n"
587 "Example(ubuntu):\n"
588 " sudo apt-get install <packagename>" % ", ".join(missing)
589 )
Josh Triplett472a4182013-03-08 11:48:57 -0800590
591
592def _ProxySimSetup(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600593 """Set up proxy simulator, and return only in the child environment.
Josh Triplett472a4182013-03-08 11:48:57 -0800594
Alex Klein1699fab2022-09-08 08:46:06 -0600595 TODO: Ideally, this should support multiple concurrent invocations of
596 cros_sdk --proxy-sim; currently, such invocations will conflict with each
597 other due to the veth device names and IP addresses. Either this code would
598 need to generate fresh, unused names for all of these before forking, or it
599 would need to support multiple concurrent cros_sdk invocations sharing one
600 proxy and allowing it to exit when unused (without counting on any local
601 service-management infrastructure on the host).
602 """
Josh Triplett472a4182013-03-08 11:48:57 -0800603
Alex Klein1699fab2022-09-08 08:46:06 -0600604 may_need_mpm = False
605 apache_bin = osutils.Which("apache2")
Josh Triplett472a4182013-03-08 11:48:57 -0800606 if apache_bin is None:
Alex Klein1699fab2022-09-08 08:46:06 -0600607 apache_bin = osutils.Which("apache2", PROXY_APACHE_FALLBACK_PATH)
608 if apache_bin is None:
609 _ReportMissing(("apache2",))
610 else:
611 may_need_mpm = True
Josh Triplett472a4182013-03-08 11:48:57 -0800612
Alex Klein1699fab2022-09-08 08:46:06 -0600613 # Module names and .so names included for ease of grepping.
614 apache_modules = [
615 ("proxy_module", "mod_proxy.so"),
616 ("proxy_connect_module", "mod_proxy_connect.so"),
617 ("proxy_http_module", "mod_proxy_http.so"),
618 ("proxy_ftp_module", "mod_proxy_ftp.so"),
619 ]
Josh Triplett472a4182013-03-08 11:48:57 -0800620
Alex Kleinef517832023-01-13 12:06:51 -0700621 # Find the apache module directory and make sure it has the modules we need.
Alex Klein1699fab2022-09-08 08:46:06 -0600622 module_dirs = {}
623 for g in PROXY_APACHE_MODULE_GLOBS:
624 for _, so in apache_modules:
625 for f in glob.glob(os.path.join(g, so)):
626 module_dirs.setdefault(os.path.dirname(f), []).append(so)
627 for apache_module_path, modules_found in module_dirs.items():
628 if len(modules_found) == len(apache_modules):
629 break
630 else:
Alex Kleinef517832023-01-13 12:06:51 -0700631 # Appease cros lint, which doesn't understand that this else block will
632 # not fall through to the subsequent code which relies on
633 # apache_module_path.
Alex Klein1699fab2022-09-08 08:46:06 -0600634 apache_module_path = None
635 raise SystemExit(
Alex Kleinef517832023-01-13 12:06:51 -0700636 "Could not find apache module path containing all required "
637 "modules: %s" % ", ".join(so for mod, so in apache_modules)
Alex Klein1699fab2022-09-08 08:46:06 -0600638 )
Josh Triplett472a4182013-03-08 11:48:57 -0800639
Alex Klein1699fab2022-09-08 08:46:06 -0600640 def check_add_module(name):
641 so = "mod_%s.so" % name
642 if os.access(os.path.join(apache_module_path, so), os.F_OK):
643 mod = "%s_module" % name
644 apache_modules.append((mod, so))
645 return True
646 return False
Josh Triplett472a4182013-03-08 11:48:57 -0800647
Alex Klein1699fab2022-09-08 08:46:06 -0600648 check_add_module("authz_core")
649 if may_need_mpm:
650 for mpm in PROXY_APACHE_MPMS:
651 if check_add_module("mpm_%s" % mpm):
652 break
Josh Triplett472a4182013-03-08 11:48:57 -0800653
Alex Klein1699fab2022-09-08 08:46:06 -0600654 veth_host = "%s-host" % PROXY_VETH_PREFIX
655 veth_guest = "%s-guest" % PROXY_VETH_PREFIX
Josh Triplett472a4182013-03-08 11:48:57 -0800656
Alex Klein1699fab2022-09-08 08:46:06 -0600657 # Set up locks to sync the net namespace setup. We need the child to create
Alex Kleinef517832023-01-13 12:06:51 -0700658 # the net ns first, and then have the parent assign the guest end of the
659 # veth interface to the child's new network namespace & bring up the proxy.
660 # Only then can the child move forward and rely on the network being up.
Alex Klein1699fab2022-09-08 08:46:06 -0600661 ns_create_lock = locking.PipeLock()
662 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800663
Alex Klein1699fab2022-09-08 08:46:06 -0600664 pid = os.fork()
665 if not pid:
666 # Create our new isolated net namespace.
667 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500668
Alex Klein1699fab2022-09-08 08:46:06 -0600669 # Signal the parent the ns is ready to be configured.
670 ns_create_lock.Post()
671 del ns_create_lock
672
673 # Wait for the parent to finish setting up the ns/proxy.
674 ns_setup_lock.Wait()
675 del ns_setup_lock
676
677 # Set up child side of the network.
678 commands = (
679 ("ip", "link", "set", "up", "lo"),
680 (
681 "ip",
682 "address",
683 "add",
684 "%s/%u" % (PROXY_GUEST_IP, PROXY_NETMASK),
685 "dev",
686 veth_guest,
687 ),
688 ("ip", "link", "set", veth_guest, "up"),
689 )
690 try:
691 for cmd in commands:
692 cros_build_lib.dbg_run(cmd)
693 except cros_build_lib.RunCommandError as e:
694 cros_build_lib.Die("Proxy setup failed!\n%s", e)
695
696 proxy_url = "http://%s:%u" % (PROXY_HOST_IP, PROXY_PORT)
697 for proto in ("http", "https", "ftp"):
698 os.environ[proto + "_proxy"] = proxy_url
699 for v in ("all_proxy", "RSYNC_PROXY", "no_proxy"):
700 os.environ.pop(v, None)
701 return
702
703 # Set up parent side of the network.
704 uid = int(os.environ.get("SUDO_UID", "0"))
705 gid = int(os.environ.get("SUDO_GID", "0"))
706 if uid == 0 or gid == 0:
707 for username in PROXY_APACHE_FALLBACK_USERS:
708 try:
709 pwnam = pwd.getpwnam(username)
710 uid, gid = pwnam.pw_uid, pwnam.pw_gid
711 break
712 except KeyError:
713 continue
714 if uid == 0 or gid == 0:
715 raise SystemExit("Could not find a non-root user to run Apache as")
716
717 chroot_parent, chroot_base = os.path.split(options.chroot)
718 pid_file = os.path.join(chroot_parent, ".%s-apache-proxy.pid" % chroot_base)
719 log_file = os.path.join(chroot_parent, ".%s-apache-proxy.log" % chroot_base)
720
721 # Wait for the child to create the net ns.
722 ns_create_lock.Wait()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500723 del ns_create_lock
724
Alex Klein1699fab2022-09-08 08:46:06 -0600725 apache_directives = [
726 "User #%u" % uid,
727 "Group #%u" % gid,
728 "PidFile %s" % pid_file,
729 "ErrorLog %s" % log_file,
730 "Listen %s:%u" % (PROXY_HOST_IP, PROXY_PORT),
731 "ServerName %s" % PROXY_HOST_IP,
732 "ProxyRequests On",
733 "AllowCONNECT %s" % " ".join(str(x) for x in PROXY_CONNECT_PORTS),
734 ] + [
735 "LoadModule %s %s" % (mod, os.path.join(apache_module_path, so))
736 for (mod, so) in apache_modules
737 ]
738 commands = (
739 (
740 "ip",
741 "link",
742 "add",
743 "name",
744 veth_host,
745 "type",
746 "veth",
747 "peer",
748 "name",
749 veth_guest,
750 ),
751 (
752 "ip",
753 "address",
754 "add",
755 "%s/%u" % (PROXY_HOST_IP, PROXY_NETMASK),
756 "dev",
757 veth_host,
758 ),
759 ("ip", "link", "set", veth_host, "up"),
760 (
761 [apache_bin, "-f", "/dev/null"]
762 + [arg for d in apache_directives for arg in ("-C", d)]
763 ),
764 ("ip", "link", "set", veth_guest, "netns", str(pid)),
765 )
766 cmd = None # Make cros lint happy.
767 try:
768 for cmd in commands:
769 cros_build_lib.dbg_run(cmd)
770 except cros_build_lib.RunCommandError as e:
771 # Clean up existing interfaces, if any.
772 cmd_cleanup = ("ip", "link", "del", veth_host)
773 try:
774 cros_build_lib.run(cmd_cleanup, print_cmd=False)
775 except cros_build_lib.RunCommandError:
776 logging.error("running %r failed", cmd_cleanup)
777 cros_build_lib.Die("Proxy network setup failed!\n%s", e)
778
779 # Signal the child that the net ns/proxy is fully configured now.
780 ns_setup_lock.Post()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500781 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800782
Alex Klein1699fab2022-09-08 08:46:06 -0600783 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800784
785
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400786def _BuildReExecCommand(argv, opts) -> List[str]:
Alex Klein1699fab2022-09-08 08:46:06 -0600787 """Generate new command for self-reexec."""
788 # Make sure to preserve the active Python executable in case the version
789 # we're running as is not the default one found via the (new) $PATH.
790 cmd = _SudoCommand() + ["--"]
791 if opts.strace:
792 cmd += ["strace"] + shlex.split(opts.strace_arguments) + ["--"]
793 return cmd + [sys.executable] + argv
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400794
795
796def _ReExecuteIfNeeded(argv, opts):
Alex Klein1699fab2022-09-08 08:46:06 -0600797 """Re-execute cros_sdk as root.
David James56e6c2c2012-10-24 23:54:41 -0700798
Alex Klein1699fab2022-09-08 08:46:06 -0600799 Also unshare the mount namespace so as to ensure that processes outside
800 the chroot can't mess with our mounts.
801 """
802 if osutils.IsNonRootUser():
803 cmd = _BuildReExecCommand(argv, opts)
804 logging.debug(
805 "Reexecing self via sudo:\n%s", cros_build_lib.CmdToStr(cmd)
806 )
807 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700808
809
Mike Frysinger34db8692013-11-11 14:54:08 -0500810def _CreateParser(sdk_latest_version, bootstrap_latest_version):
Alex Klein1699fab2022-09-08 08:46:06 -0600811 """Generate and return the parser with all the options."""
812 usage = (
813 "usage: %(prog)s [options] "
814 "[VAR1=val1 ... VAR2=val2] [--] [command [args]]"
815 )
816 parser = commandline.ArgumentParser(
817 usage=usage, description=__doc__, caching=True
818 )
Brian Harring218e13c2012-10-10 16:21:26 -0700819
Alex Klein1699fab2022-09-08 08:46:06 -0600820 # Global options.
821 default_chroot = os.path.join(
822 constants.SOURCE_ROOT, constants.DEFAULT_CHROOT_DIR
823 )
824 parser.add_argument(
825 "--chroot",
826 dest="chroot",
827 default=default_chroot,
828 type="path",
829 help=("SDK chroot dir name [%s]" % constants.DEFAULT_CHROOT_DIR),
830 )
831 parser.add_argument(
Dan Callaghanada9e032023-01-30 14:28:46 +1100832 "--out-dir",
833 metavar="DIR",
834 default=constants.DEFAULT_OUT_DIR,
835 type=Path,
836 help="Use DIR for build state and output files",
837 )
838 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600839 "--nouse-image",
840 dest="use_image",
841 action="store_false",
842 default=False,
843 help="Do not mount the chroot on a loopback image; "
844 "instead, create it directly in a directory.",
845 )
846 parser.add_argument(
847 "--use-image",
848 dest="use_image",
849 action="store_true",
850 default=False,
851 help="Mount the chroot on a loopback image "
852 "instead of creating it directly in a directory.",
853 )
Brian Harringb938c782012-02-29 15:14:38 -0800854
Alex Klein1699fab2022-09-08 08:46:06 -0600855 parser.add_argument(
856 "--chrome-root",
857 "--chrome_root",
858 type="path",
859 help="Mount this chrome root into the SDK chroot",
860 )
861 parser.add_argument(
862 "--chrome_root_mount",
863 type="path",
864 help="Mount chrome into this path inside SDK chroot",
865 )
866 parser.add_argument(
867 "--nousepkg",
868 action="store_true",
869 default=False,
870 help="Do not use binary packages when creating a chroot.",
871 )
872 parser.add_argument(
873 "-u",
874 "--url",
875 dest="sdk_url",
876 help="Use sdk tarball located at this url. Use file:// "
877 "for local files.",
878 )
879 parser.add_argument(
880 "--sdk-version",
881 help=(
882 "Use this sdk version. For prebuilt, current is %r"
883 ", for bootstrapping it is %r."
884 % (sdk_latest_version, bootstrap_latest_version)
885 ),
886 )
887 parser.add_argument(
Mike Frysinger85824562023-01-31 02:40:43 -0500888 "--goma-dir",
Alex Klein1699fab2022-09-08 08:46:06 -0600889 "--goma_dir",
890 type="path",
891 help="Goma installed directory to mount into the chroot.",
892 )
893 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600894 "--reclient-dir",
895 type="path",
896 help="Reclient installed directory to mount into the chroot.",
897 )
898 parser.add_argument(
899 "--reproxy-cfg-file",
900 type="path",
901 help="Config file for re-client's reproxy used for remoteexec.",
902 )
903 parser.add_argument(
904 "--skip-chroot-upgrade",
905 dest="chroot_upgrade",
906 action="store_false",
907 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700908 help="Skip automatic SDK and toolchain upgrade when entering the "
909 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600910 )
Yong Hong84ba9172018-02-07 01:37:42 +0800911
Alex Klein1699fab2022-09-08 08:46:06 -0600912 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700913 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600914 parser.add_argument(
915 "--working-dir",
916 type=str,
917 help="Run the command in specific working directory in "
918 "chroot. If the given directory is a relative "
919 "path, this program will transfer the path to "
920 "the corresponding one inside chroot.",
921 )
Yong Hong84ba9172018-02-07 01:37:42 +0800922
Alex Klein1699fab2022-09-08 08:46:06 -0600923 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500924
Alex Klein1699fab2022-09-08 08:46:06 -0600925 # Commands.
926 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400927 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600928 "--enter",
929 action="store_true",
930 default=False,
931 help="Enter the SDK chroot. Implies --create.",
932 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400933 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600934 "--create",
935 action="store_true",
936 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700937 help="Create the chroot only if it does not already exist. Downloads "
938 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600939 )
940 group.add_argument(
941 "--bootstrap",
942 action="store_true",
943 default=False,
944 help="Build everything from scratch, including the sdk. "
945 "Use this only if you need to validate a change "
946 "that affects SDK creation itself (toolchain and "
947 "build are typically the only folk who need this). "
948 "Note this will quite heavily slow down the build. "
949 "This option implies --create --nousepkg.",
950 )
951 group.add_argument(
952 "-r",
953 "--replace",
954 action="store_true",
955 default=False,
956 help="Replace an existing SDK chroot. Basically an alias "
957 "for --delete --create.",
958 )
959 group.add_argument(
960 "--delete",
961 action="store_true",
962 default=False,
963 help="Delete the current SDK chroot if it exists.",
964 )
965 group.add_argument(
966 "--force",
967 action="store_true",
968 default=False,
969 help="Force unmount/delete of the current SDK chroot even if "
970 "obtaining the write lock fails.",
971 )
972 group.add_argument(
973 "--unmount",
974 action="store_true",
975 default=False,
976 help="Unmount and clean up devices associated with the "
977 "SDK chroot if it exists. This does not delete the "
978 "backing image file, so the same chroot can be later "
979 "re-mounted for reuse. To fully delete the chroot, use "
980 "--delete. This is primarily useful for working on "
981 "cros_sdk or the chroot setup; you should not need it "
982 "under normal circumstances.",
983 )
984 group.add_argument(
985 "--download",
986 action="store_true",
987 default=False,
988 help="Download the sdk.",
989 )
990 group.add_argument(
991 "--snapshot-create",
992 metavar="SNAPSHOT_NAME",
993 help="Create a snapshot of the chroot. Requires that the chroot was "
994 "created without the --nouse-image option.",
995 )
996 group.add_argument(
997 "--snapshot-restore",
998 metavar="SNAPSHOT_NAME",
999 help="Restore the chroot to a previously created snapshot.",
1000 )
1001 group.add_argument(
1002 "--snapshot-delete",
1003 metavar="SNAPSHOT_NAME",
1004 help="Delete a previously created snapshot. Deleting a snapshot that "
1005 "does not exist is not an error.",
1006 )
1007 group.add_argument(
1008 "--snapshot-list",
1009 action="store_true",
1010 default=False,
1011 help="List existing snapshots of the chroot and exit.",
1012 )
1013 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -04001014
Alex Klein1699fab2022-09-08 08:46:06 -06001015 # Namespace options.
1016 group = parser.add_argument_group("Namespaces")
1017 group.add_argument(
1018 "--proxy-sim",
1019 action="store_true",
1020 default=False,
1021 help="Simulate a restrictive network requiring an outbound" " proxy.",
1022 )
1023 for ns, default in (("pid", True), ("net", None)):
1024 group.add_argument(
1025 f"--ns-{ns}",
1026 default=default,
1027 action="store_true",
1028 help=f"Create a new {ns} namespace.",
1029 )
1030 group.add_argument(
1031 f"--no-ns-{ns}",
1032 dest=f"ns_{ns}",
1033 action="store_false",
1034 help=f"Do not create a new {ns} namespace.",
1035 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -04001036
Alex Klein1699fab2022-09-08 08:46:06 -06001037 # Debug options.
1038 group = parser.debug_group
1039 group.add_argument(
1040 "--strace",
1041 action="store_true",
1042 help="Run cros_sdk through strace after re-exec via sudo",
1043 )
1044 group.add_argument(
1045 "--strace-arguments",
1046 default="",
1047 help="Extra strace options (shell quoting permitted)",
1048 )
Mike Frysinger34db8692013-11-11 14:54:08 -05001049
Alex Klein1699fab2022-09-08 08:46:06 -06001050 # Internal options.
1051 group = parser.add_argument_group(
1052 "Internal Chromium OS Build Team Options",
1053 "Caution: these are for meant for the Chromium OS build team only",
1054 )
1055 group.add_argument(
1056 "--buildbot-log-version",
1057 default=False,
1058 action="store_true",
1059 help="Log SDK version for buildbot consumption",
1060 )
1061
1062 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -05001063
1064
1065def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -06001066 # Turn on strict sudo checks.
1067 cros_build_lib.STRICT_SUDO = True
1068 conf = key_value_store.LoadFile(
1069 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
1070 ignore_missing=True,
1071 )
1072 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
1073 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -07001074
Alex Klein1699fab2022-09-08 08:46:06 -06001075 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
1076 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
1077 bootstrap_latest_version = (
1078 sdk_latest_version
1079 if bootstrap_frozen_version == "<unknown>"
1080 else bootstrap_frozen_version
1081 )
1082 parser, commands = _CreateParser(
1083 sdk_latest_version, bootstrap_latest_version
1084 )
1085 options = parser.parse_args(argv)
Brian Harringb938c782012-02-29 15:14:38 -08001086
Alex Klein1699fab2022-09-08 08:46:06 -06001087 # Some basic checks first, before we ask for sudo credentials.
1088 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -08001089
Alex Klein1699fab2022-09-08 08:46:06 -06001090 host = os.uname()[4]
1091 if host != "x86_64":
1092 cros_build_lib.Die(
1093 "cros_sdk is currently only supported on x86_64; you're running"
1094 " %s. Please find a x86_64 machine." % (host,)
1095 )
Brian Harring1790ac42012-09-23 08:53:33 -07001096
Mike Frysinger9a5124e2023-01-26 17:16:44 -05001097 goma = goma_lib.Goma(options.goma_dir) if options.goma_dir else None
1098
Alex Klein1699fab2022-09-08 08:46:06 -06001099 # Merge the outside PATH setting if we re-execed ourselves.
1100 if "CHROMEOS_SUDO_PATH" in os.environ:
1101 os.environ["PATH"] = "%s:%s" % (
1102 os.environ.pop("CHROMEOS_SUDO_PATH"),
1103 os.environ["PATH"],
1104 )
Mike Frysinger2bda4d12020-07-14 11:15:49 -04001105
Alex Klein1699fab2022-09-08 08:46:06 -06001106 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
1107 if options.proxy_sim:
1108 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
1109 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -08001110
Alex Klein1699fab2022-09-08 08:46:06 -06001111 if (
1112 sdk_latest_version == "<unknown>"
1113 or bootstrap_latest_version == "<unknown>"
1114 ):
1115 cros_build_lib.Die(
1116 "No SDK version was found. "
1117 "Are you in a Chromium source tree instead of Chromium OS?\n\n"
1118 "Please change to a directory inside your Chromium OS source tree\n"
1119 "and retry. If you need to setup a Chromium OS source tree, see\n"
1120 " https://dev.chromium.org/chromium-os/developer-guide"
1121 )
Benjamin Gordon040a1162017-06-29 13:44:47 -06001122
Alex Klein1699fab2022-09-08 08:46:06 -06001123 any_snapshot_operation = (
1124 options.snapshot_create
1125 or options.snapshot_restore
1126 or options.snapshot_delete
1127 or options.snapshot_list
1128 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001129
Alex Klein1699fab2022-09-08 08:46:06 -06001130 if (
1131 options.snapshot_delete
1132 and options.snapshot_delete == options.snapshot_restore
1133 ):
1134 parser.error(
1135 "Cannot --snapshot_delete the same snapshot you are "
1136 "restoring with --snapshot_restore."
1137 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001138
Alex Klein1699fab2022-09-08 08:46:06 -06001139 _ReExecuteIfNeeded([sys.argv[0]] + argv, options)
David James471532c2013-01-21 10:23:31 -08001140
Alex Klein1699fab2022-09-08 08:46:06 -06001141 lock_path = os.path.dirname(options.chroot)
1142 lock_path = os.path.join(
1143 lock_path, ".%s_lock" % os.path.basename(options.chroot).lstrip(".")
1144 )
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001145
Alex Klein1699fab2022-09-08 08:46:06 -06001146 # Expand out the aliases...
1147 if options.replace:
1148 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001149
Alex Klein1699fab2022-09-08 08:46:06 -06001150 if options.bootstrap:
1151 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001152
Alex Klein1699fab2022-09-08 08:46:06 -06001153 # If a command is not given, default to enter.
1154 # pylint: disable=protected-access
1155 # This _group_actions access sucks, but upstream decided to not include an
1156 # alternative to optparse's option_list, and this is what they recommend.
1157 options.enter |= not any(
1158 getattr(options, x.dest) for x in commands._group_actions
1159 )
1160 # pylint: enable=protected-access
Mike Frysinger114a7b92023-01-26 19:08:57 -05001161 options.enter |= bool(options.commands)
Brian Harring218e13c2012-10-10 16:21:26 -07001162
Alex Klein1699fab2022-09-08 08:46:06 -06001163 if (
1164 options.delete
1165 and not options.create
1166 and (options.enter or any_snapshot_operation)
1167 ):
1168 parser.error(
1169 "Trying to enter or snapshot the chroot when --delete "
1170 "was specified makes no sense."
1171 )
Brian Harring218e13c2012-10-10 16:21:26 -07001172
Alex Klein1699fab2022-09-08 08:46:06 -06001173 if options.unmount and (
1174 options.create or options.enter or any_snapshot_operation
1175 ):
1176 parser.error("--unmount cannot be specified with other chroot actions.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001177
Alex Klein1699fab2022-09-08 08:46:06 -06001178 if options.working_dir is not None and not os.path.isabs(
1179 options.working_dir
1180 ):
1181 options.working_dir = path_util.ToChrootPath(options.working_dir)
Yong Hong84ba9172018-02-07 01:37:42 +08001182
Alex Klein1699fab2022-09-08 08:46:06 -06001183 # If there is an existing chroot image and we're not removing it then force
1184 # use_image on. This ensures that people don't have to remember to pass
Alex Kleinef517832023-01-13 12:06:51 -07001185 # --use-image after a reboot to avoid losing access to their existing
1186 # chroot.
Alex Klein1699fab2022-09-08 08:46:06 -06001187 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1188 img_path = _ImageFileForChroot(options.chroot)
1189 if (
1190 not options.use_image
1191 and not options.delete
1192 and not options.unmount
1193 and os.path.exists(img_path)
1194 ):
1195 if chroot_exists:
1196 # If the chroot is already populated, make sure it has something
1197 # mounted on it before we assume it came from an image.
1198 cmd = ["mountpoint", "-q", options.chroot]
1199 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
1200 options.use_image = True
Benjamin Gordon832a4412020-12-08 10:39:16 -07001201
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001202 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001203 logging.notice(
1204 "Existing chroot image %s found. Forcing --use-image on.",
1205 img_path,
1206 )
1207 options.use_image = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001208
Alex Klein1699fab2022-09-08 08:46:06 -06001209 if any_snapshot_operation and not options.use_image:
1210 if os.path.exists(img_path):
1211 options.use_image = True
1212 else:
1213 cros_build_lib.Die(
1214 "Snapshot operations are not compatible with " "--nouse-image."
1215 )
Mike Frysingerc60141b2023-01-27 00:57:15 -05001216 if options.use_image:
1217 logging.warning("--use-image is deprecated and will be removed soon.")
1218 logging.warning("Please migrate, or create a new one with --delete.")
1219 logging.warning("See http://b/266878468 for details.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001220
Alex Klein1699fab2022-09-08 08:46:06 -06001221 # Discern if we need to create the chroot.
1222 if (
1223 options.use_image
1224 and not chroot_exists
1225 and not options.delete
1226 and not options.unmount
1227 and not missing_image_tools
1228 and os.path.exists(img_path)
1229 ):
1230 # Try to re-mount an existing image in case the user has rebooted.
1231 with locking.FileLock(lock_path, "chroot lock") as lock:
1232 logging.debug("Checking if existing chroot image can be mounted.")
1233 lock.write_lock()
1234 cros_sdk_lib.MountChroot(options.chroot, create=False)
1235 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1236 if chroot_exists:
1237 logging.notice("Mounted existing image %s on chroot", img_path)
1238
1239 # Finally, flip create if necessary.
1240 if options.enter or options.snapshot_create:
1241 options.create |= not chroot_exists
1242
1243 # Make sure we will download if we plan to create.
1244 options.download |= options.create
1245
Mike Frysinger6bbd3e92023-01-27 00:05:02 -05001246 options.Freeze()
1247
Mike Frysinger253c8392023-01-27 01:12:21 -05001248 remoteexec = (
1249 remoteexec_util.Remoteexec(
1250 options.reclient_dir, options.reproxy_cfg_file
1251 )
1252 if (options.reclient_dir and options.reproxy_cfg_file)
1253 else None
1254 )
1255
Mike Frysingerec32bea2023-01-27 01:20:48 -05001256 chroot = chroot_lib.Chroot(
1257 path=options.chroot,
1258 cache_dir=options.cache_dir,
1259 chrome_root=options.chrome_root,
1260 goma=goma,
1261 remoteexec=remoteexec,
1262 )
1263
Alex Kleinef517832023-01-13 12:06:51 -07001264 # Anything that needs to manipulate the main chroot mount or communicate
1265 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -06001266
1267 # If deleting, do it regardless of the use_image flag so that a
1268 # previously-created loopback chroot can also be cleaned up.
1269 if options.delete:
1270 # Set a timeout of 300 seconds when getting the lock.
1271 with locking.FileLock(
1272 lock_path, "chroot lock", blocking_timeout=300
1273 ) as lock:
1274 try:
1275 lock.write_lock()
1276 except timeout_util.TimeoutError as e:
1277 logging.error(
1278 "Acquiring write_lock on %s failed: %s", lock_path, e
1279 )
1280 if not options.force:
1281 cros_build_lib.Die(
1282 "Exiting; use --force to continue w/o lock."
1283 )
1284 else:
1285 logging.warning(
1286 "cros_sdk was invoked with force option, continuing."
1287 )
1288 logging.notice("Deleting chroot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001289 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=True)
Alex Klein1699fab2022-09-08 08:46:06 -06001290
Alex Kleinef517832023-01-13 12:06:51 -07001291 # If cleanup was requested, we have to do it while we're still in the
1292 # original namespace. Since cleaning up the mount will interfere with any
1293 # other commands, we exit here. The check above should have made sure that
1294 # no other action was requested, anyway.
Alex Klein1699fab2022-09-08 08:46:06 -06001295 if options.unmount:
1296 # Set a timeout of 300 seconds when getting the lock.
1297 with locking.FileLock(
1298 lock_path, "chroot lock", blocking_timeout=300
1299 ) as lock:
1300 try:
1301 lock.write_lock()
1302 except timeout_util.TimeoutError as e:
1303 logging.error(
1304 "Acquiring write_lock on %s failed: %s", lock_path, e
1305 )
1306 logging.warning(
Alex Kleinef517832023-01-13 12:06:51 -07001307 "Continuing with CleanupChroot(%s), which will umount the "
1308 "tree.",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001309 chroot.path,
Alex Klein1699fab2022-09-08 08:46:06 -06001310 )
Alex Kleinef517832023-01-13 12:06:51 -07001311 # We can call CleanupChroot (which calls
1312 # cros_sdk_lib.CleanupChrootMount) even if we don't get the lock
1313 # because it will attempt to unmount the tree and will print
1314 # diagnostic information from 'fuser', 'lsof', and 'ps'.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001315 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=False)
Alex Klein1699fab2022-09-08 08:46:06 -06001316 sys.exit(0)
1317
1318 # Make sure the main chroot mount is visible. Contents will be filled in
1319 # below if needed.
1320 if options.create and options.use_image:
1321 if missing_image_tools:
1322 raise SystemExit(
1323 """The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001324Please make sure the lvm2 and thin-provisioning-tools packages
1325are installed on your host.
1326Example(ubuntu):
1327 sudo apt-get install lvm2 thin-provisioning-tools
1328
1329If you want to run without lvm2, pass --nouse-image (chroot
Alex Klein1699fab2022-09-08 08:46:06 -06001330snapshots will be unavailable)."""
1331 % ", ".join(missing_image_tools)
1332 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001333
Alex Klein1699fab2022-09-08 08:46:06 -06001334 logging.debug("Making sure chroot image is mounted.")
1335 with locking.FileLock(lock_path, "chroot lock") as lock:
1336 lock.write_lock()
Mike Frysingerec32bea2023-01-27 01:20:48 -05001337 if not cros_sdk_lib.MountChroot(chroot.path, create=True):
Alex Klein1699fab2022-09-08 08:46:06 -06001338 cros_build_lib.Die(
1339 "Unable to mount %s on chroot",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001340 _ImageFileForChroot(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001341 )
1342 logging.notice(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001343 "Mounted %s on chroot", _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001344 )
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001345
Alex Klein1699fab2022-09-08 08:46:06 -06001346 # Snapshot operations will always need the VG/LV, but other actions won't.
1347 if any_snapshot_operation:
1348 with locking.FileLock(lock_path, "chroot lock") as lock:
1349 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001350 chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001351 )
1352 if not chroot_vg or not chroot_lv:
1353 cros_build_lib.Die(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001354 "Unable to find VG/LV for chroot %s", chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001355 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001356
Alex Kleinef517832023-01-13 12:06:51 -07001357 # Delete snapshot before creating a new one. This allows the user to
1358 # throw out old state, create a new snapshot, and enter the chroot
1359 # in a single call to cros_sdk. Since restore involves deleting,
1360 # also do it before creating.
Alex Klein1699fab2022-09-08 08:46:06 -06001361 if options.snapshot_restore:
1362 lock.write_lock()
1363 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1364 if options.snapshot_restore not in valid_snapshots:
1365 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -07001366 "%s is not a valid snapshot to restore to. "
1367 "Valid snapshots: %s",
Alex Klein1699fab2022-09-08 08:46:06 -06001368 options.snapshot_restore,
1369 ", ".join(valid_snapshots),
1370 )
Mike Frysingerec32bea2023-01-27 01:20:48 -05001371 osutils.UmountTree(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001372 if not RestoreChrootSnapshot(
1373 options.snapshot_restore, chroot_vg, chroot_lv
1374 ):
1375 cros_build_lib.Die("Unable to restore chroot to snapshot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001376 if not cros_sdk_lib.MountChroot(chroot.path, create=False):
Alex Klein1699fab2022-09-08 08:46:06 -06001377 cros_build_lib.Die(
1378 "Unable to mount restored snapshot onto chroot."
1379 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001380
Alex Kleinef517832023-01-13 12:06:51 -07001381 # Use a read lock for snapshot delete and create even though they
1382 # modify the filesystem, because they don't modify the mounted
1383 # chroot itself. The underlying LVM commands take their own locks,
1384 # so conflicting concurrent operations here may crash cros_sdk, but
1385 # won't corrupt the chroot image. This tradeoff seems worth it to
1386 # allow snapshot operations on chroots that have a process inside.
Alex Klein1699fab2022-09-08 08:46:06 -06001387 if options.snapshot_delete:
1388 lock.read_lock()
1389 DeleteChrootSnapshot(
1390 options.snapshot_delete, chroot_vg, chroot_lv
1391 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001392
Alex Klein1699fab2022-09-08 08:46:06 -06001393 if options.snapshot_create:
1394 lock.read_lock()
1395 if not CreateChrootSnapshot(
1396 options.snapshot_create, chroot_vg, chroot_lv
1397 ):
1398 cros_build_lib.Die("Unable to create snapshot.")
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001399
Mike Frysingerec32bea2023-01-27 01:20:48 -05001400 img_path = _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001401 if (
1402 options.use_image
Mike Frysingerec32bea2023-01-27 01:20:48 -05001403 and os.path.exists(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001404 and os.path.exists(img_path)
1405 ):
1406 img_stat = os.stat(img_path)
1407 img_used_bytes = img_stat.st_blocks * 512
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001408
Mike Frysingerec32bea2023-01-27 01:20:48 -05001409 mount_stat = os.statvfs(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001410 mount_used_bytes = mount_stat.f_frsize * (
1411 mount_stat.f_blocks - mount_stat.f_bfree
1412 )
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001413
Alex Klein1699fab2022-09-08 08:46:06 -06001414 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
1415 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1416 logging.notice(
1417 "%s is using %s GiB more than needed. Running "
1418 "fstrim in background.",
1419 img_path,
1420 extra_gbs,
1421 )
1422 pid = os.fork()
1423 if pid == 0:
1424 try:
1425 # Directly call Popen to run fstrim concurrently.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001426 cmd = ["fstrim", chroot.path]
Alex Klein1699fab2022-09-08 08:46:06 -06001427 subprocess.Popen(cmd, close_fds=True, shell=False)
1428 except subprocess.SubprocessError as e:
1429 logging.warning(
1430 "Running fstrim failed. Consider running fstrim on "
1431 "your chroot manually.\n%s",
1432 e,
1433 )
1434 os._exit(0) # pylint: disable=protected-access
1435 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001436
Alex Kleinef517832023-01-13 12:06:51 -07001437 # Enter a new set of namespaces. Everything after here cannot directly
1438 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -06001439 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001440
Alex Klein1699fab2022-09-08 08:46:06 -06001441 if options.snapshot_list:
1442 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1443 print(snap)
1444 sys.exit(0)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001445
Alex Klein1699fab2022-09-08 08:46:06 -06001446 if not options.sdk_version:
1447 sdk_version = (
1448 bootstrap_latest_version
1449 if options.bootstrap
1450 else sdk_latest_version
1451 )
Yong Hong4e29b622018-02-05 14:31:10 +08001452 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001453 sdk_version = options.sdk_version
1454 if options.buildbot_log_version:
1455 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001456
Alex Klein1699fab2022-09-08 08:46:06 -06001457 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -07001458 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001459 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -06001460 if options.sdk_url:
1461 urls = [options.sdk_url]
1462 else:
1463 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -07001464
Alex Klein1699fab2022-09-08 08:46:06 -06001465 with locking.FileLock(lock_path, "chroot lock") as lock:
1466 if options.proxy_sim:
1467 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -07001468
Mike Frysingerec32bea2023-01-27 01:20:48 -05001469 sdk_cache = os.path.join(chroot.cache_dir, "sdks")
1470 distfiles_cache = os.path.join(chroot.cache_dir, "distfiles")
1471 osutils.SafeMakedirsNonRoot(chroot.cache_dir)
Dan Callaghanada9e032023-01-30 14:28:46 +11001472 osutils.SafeMakedirsNonRoot(options.out_dir)
Alex Klein1699fab2022-09-08 08:46:06 -06001473
1474 for target in (sdk_cache, distfiles_cache):
1475 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1476 if not os.path.exists(src):
1477 osutils.SafeMakedirsNonRoot(target)
1478 continue
1479 lock.write_lock(
1480 "Upgrade to %r needed but chroot is locked; please exit "
1481 "all instances so this upgrade can finish." % src
1482 )
1483 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -07001484 # Note that while waiting for the write lock, src may've
1485 # vanished; it's a rare race during the upgrade process that's a
1486 # byproduct of us avoiding taking a write lock to do the src
1487 # check. If we took a write lock for that check, it would
1488 # effectively limit all cros_sdk for a chroot to a single
1489 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -06001490 osutils.SafeMakedirsNonRoot(target)
1491 elif not os.path.exists(target):
1492 # Upgrade occurred, but a reversion, or something whacky
1493 # occurred writing to the old location. Wipe and continue.
1494 os.rename(src, target)
1495 else:
1496 # Upgrade occurred once already, but either a reversion or
1497 # some before/after separate cros_sdk usage is at play.
1498 # Wipe and continue.
1499 osutils.RmDir(src)
1500
Alex Klein1699fab2022-09-08 08:46:06 -06001501 mounted = False
1502 if options.create:
1503 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -07001504 # Recheck if the chroot is set up here before creating to make sure
1505 # we account for whatever the various delete/unmount/remount steps
1506 # above have done.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001507 if cros_sdk_lib.IsChrootReady(chroot.path):
Alex Klein1699fab2022-09-08 08:46:06 -06001508 logging.debug("Chroot already exists. Skipping creation.")
1509 else:
Alex Klein22690a12022-11-17 10:56:09 -07001510 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001511 cros_sdk_lib.CreateChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001512 Path(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001513 Path(sdk_tarball),
Dan Callaghanada9e032023-01-30 14:28:46 +11001514 options.out_dir,
Mike Frysingerec32bea2023-01-27 01:20:48 -05001515 Path(chroot.cache_dir),
Alex Klein1699fab2022-09-08 08:46:06 -06001516 usepkg=not options.bootstrap and not options.nousepkg,
1517 chroot_upgrade=options.chroot_upgrade,
1518 )
1519 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -07001520 elif options.download:
1521 # Allow downloading only.
1522 lock.write_lock()
1523 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001524
1525 if options.enter:
1526 lock.read_lock()
1527 if not mounted:
Dan Callaghanada9e032023-01-30 14:28:46 +11001528 cros_sdk_lib.MountChrootPaths(chroot.path, options.out_dir)
Alex Klein1699fab2022-09-08 08:46:06 -06001529 ret = EnterChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001530 chroot,
Alex Klein1699fab2022-09-08 08:46:06 -06001531 options.chrome_root_mount,
Alex Klein1699fab2022-09-08 08:46:06 -06001532 options.working_dir,
Mike Frysinger114a7b92023-01-26 19:08:57 -05001533 options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -06001534 )
1535 sys.exit(ret.returncode)