blob: 0ef3a3011c13c441afc4c47455bc9eccf09fcdae [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(
832 "--nouse-image",
833 dest="use_image",
834 action="store_false",
835 default=False,
836 help="Do not mount the chroot on a loopback image; "
837 "instead, create it directly in a directory.",
838 )
839 parser.add_argument(
840 "--use-image",
841 dest="use_image",
842 action="store_true",
843 default=False,
844 help="Mount the chroot on a loopback image "
845 "instead of creating it directly in a directory.",
846 )
Brian Harringb938c782012-02-29 15:14:38 -0800847
Alex Klein1699fab2022-09-08 08:46:06 -0600848 parser.add_argument(
849 "--chrome-root",
850 "--chrome_root",
851 type="path",
852 help="Mount this chrome root into the SDK chroot",
853 )
854 parser.add_argument(
855 "--chrome_root_mount",
856 type="path",
857 help="Mount chrome into this path inside SDK chroot",
858 )
859 parser.add_argument(
860 "--nousepkg",
861 action="store_true",
862 default=False,
863 help="Do not use binary packages when creating a chroot.",
864 )
865 parser.add_argument(
866 "-u",
867 "--url",
868 dest="sdk_url",
869 help="Use sdk tarball located at this url. Use file:// "
870 "for local files.",
871 )
872 parser.add_argument(
873 "--sdk-version",
874 help=(
875 "Use this sdk version. For prebuilt, current is %r"
876 ", for bootstrapping it is %r."
877 % (sdk_latest_version, bootstrap_latest_version)
878 ),
879 )
880 parser.add_argument(
Mike Frysinger85824562023-01-31 02:40:43 -0500881 "--goma-dir",
Alex Klein1699fab2022-09-08 08:46:06 -0600882 "--goma_dir",
883 type="path",
884 help="Goma installed directory to mount into the chroot.",
885 )
886 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600887 "--reclient-dir",
888 type="path",
889 help="Reclient installed directory to mount into the chroot.",
890 )
891 parser.add_argument(
892 "--reproxy-cfg-file",
893 type="path",
894 help="Config file for re-client's reproxy used for remoteexec.",
895 )
896 parser.add_argument(
897 "--skip-chroot-upgrade",
898 dest="chroot_upgrade",
899 action="store_false",
900 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700901 help="Skip automatic SDK and toolchain upgrade when entering the "
902 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600903 )
Yong Hong84ba9172018-02-07 01:37:42 +0800904
Alex Klein1699fab2022-09-08 08:46:06 -0600905 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700906 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600907 parser.add_argument(
908 "--working-dir",
909 type=str,
910 help="Run the command in specific working directory in "
911 "chroot. If the given directory is a relative "
912 "path, this program will transfer the path to "
913 "the corresponding one inside chroot.",
914 )
Yong Hong84ba9172018-02-07 01:37:42 +0800915
Alex Klein1699fab2022-09-08 08:46:06 -0600916 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500917
Alex Klein1699fab2022-09-08 08:46:06 -0600918 # Commands.
919 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400920 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600921 "--enter",
922 action="store_true",
923 default=False,
924 help="Enter the SDK chroot. Implies --create.",
925 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400926 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600927 "--create",
928 action="store_true",
929 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700930 help="Create the chroot only if it does not already exist. Downloads "
931 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600932 )
933 group.add_argument(
934 "--bootstrap",
935 action="store_true",
936 default=False,
937 help="Build everything from scratch, including the sdk. "
938 "Use this only if you need to validate a change "
939 "that affects SDK creation itself (toolchain and "
940 "build are typically the only folk who need this). "
941 "Note this will quite heavily slow down the build. "
942 "This option implies --create --nousepkg.",
943 )
944 group.add_argument(
945 "-r",
946 "--replace",
947 action="store_true",
948 default=False,
949 help="Replace an existing SDK chroot. Basically an alias "
950 "for --delete --create.",
951 )
952 group.add_argument(
953 "--delete",
954 action="store_true",
955 default=False,
956 help="Delete the current SDK chroot if it exists.",
957 )
958 group.add_argument(
959 "--force",
960 action="store_true",
961 default=False,
962 help="Force unmount/delete of the current SDK chroot even if "
963 "obtaining the write lock fails.",
964 )
965 group.add_argument(
966 "--unmount",
967 action="store_true",
968 default=False,
969 help="Unmount and clean up devices associated with the "
970 "SDK chroot if it exists. This does not delete the "
971 "backing image file, so the same chroot can be later "
972 "re-mounted for reuse. To fully delete the chroot, use "
973 "--delete. This is primarily useful for working on "
974 "cros_sdk or the chroot setup; you should not need it "
975 "under normal circumstances.",
976 )
977 group.add_argument(
978 "--download",
979 action="store_true",
980 default=False,
981 help="Download the sdk.",
982 )
983 group.add_argument(
984 "--snapshot-create",
985 metavar="SNAPSHOT_NAME",
986 help="Create a snapshot of the chroot. Requires that the chroot was "
987 "created without the --nouse-image option.",
988 )
989 group.add_argument(
990 "--snapshot-restore",
991 metavar="SNAPSHOT_NAME",
992 help="Restore the chroot to a previously created snapshot.",
993 )
994 group.add_argument(
995 "--snapshot-delete",
996 metavar="SNAPSHOT_NAME",
997 help="Delete a previously created snapshot. Deleting a snapshot that "
998 "does not exist is not an error.",
999 )
1000 group.add_argument(
1001 "--snapshot-list",
1002 action="store_true",
1003 default=False,
1004 help="List existing snapshots of the chroot and exit.",
1005 )
1006 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -04001007
Alex Klein1699fab2022-09-08 08:46:06 -06001008 # Namespace options.
1009 group = parser.add_argument_group("Namespaces")
1010 group.add_argument(
1011 "--proxy-sim",
1012 action="store_true",
1013 default=False,
1014 help="Simulate a restrictive network requiring an outbound" " proxy.",
1015 )
1016 for ns, default in (("pid", True), ("net", None)):
1017 group.add_argument(
1018 f"--ns-{ns}",
1019 default=default,
1020 action="store_true",
1021 help=f"Create a new {ns} namespace.",
1022 )
1023 group.add_argument(
1024 f"--no-ns-{ns}",
1025 dest=f"ns_{ns}",
1026 action="store_false",
1027 help=f"Do not create a new {ns} namespace.",
1028 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -04001029
Alex Klein1699fab2022-09-08 08:46:06 -06001030 # Debug options.
1031 group = parser.debug_group
1032 group.add_argument(
1033 "--strace",
1034 action="store_true",
1035 help="Run cros_sdk through strace after re-exec via sudo",
1036 )
1037 group.add_argument(
1038 "--strace-arguments",
1039 default="",
1040 help="Extra strace options (shell quoting permitted)",
1041 )
Mike Frysinger34db8692013-11-11 14:54:08 -05001042
Alex Klein1699fab2022-09-08 08:46:06 -06001043 # Internal options.
1044 group = parser.add_argument_group(
1045 "Internal Chromium OS Build Team Options",
1046 "Caution: these are for meant for the Chromium OS build team only",
1047 )
1048 group.add_argument(
1049 "--buildbot-log-version",
1050 default=False,
1051 action="store_true",
1052 help="Log SDK version for buildbot consumption",
1053 )
1054
1055 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -05001056
1057
1058def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -06001059 # Turn on strict sudo checks.
1060 cros_build_lib.STRICT_SUDO = True
1061 conf = key_value_store.LoadFile(
1062 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
1063 ignore_missing=True,
1064 )
1065 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
1066 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -07001067
Alex Klein1699fab2022-09-08 08:46:06 -06001068 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
1069 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
1070 bootstrap_latest_version = (
1071 sdk_latest_version
1072 if bootstrap_frozen_version == "<unknown>"
1073 else bootstrap_frozen_version
1074 )
1075 parser, commands = _CreateParser(
1076 sdk_latest_version, bootstrap_latest_version
1077 )
1078 options = parser.parse_args(argv)
Brian Harringb938c782012-02-29 15:14:38 -08001079
Alex Klein1699fab2022-09-08 08:46:06 -06001080 # Some basic checks first, before we ask for sudo credentials.
1081 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -08001082
Alex Klein1699fab2022-09-08 08:46:06 -06001083 host = os.uname()[4]
1084 if host != "x86_64":
1085 cros_build_lib.Die(
1086 "cros_sdk is currently only supported on x86_64; you're running"
1087 " %s. Please find a x86_64 machine." % (host,)
1088 )
Brian Harring1790ac42012-09-23 08:53:33 -07001089
Mike Frysinger9a5124e2023-01-26 17:16:44 -05001090 goma = goma_lib.Goma(options.goma_dir) if options.goma_dir else None
1091
Alex Klein1699fab2022-09-08 08:46:06 -06001092 # Merge the outside PATH setting if we re-execed ourselves.
1093 if "CHROMEOS_SUDO_PATH" in os.environ:
1094 os.environ["PATH"] = "%s:%s" % (
1095 os.environ.pop("CHROMEOS_SUDO_PATH"),
1096 os.environ["PATH"],
1097 )
Mike Frysinger2bda4d12020-07-14 11:15:49 -04001098
Alex Klein1699fab2022-09-08 08:46:06 -06001099 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
1100 if options.proxy_sim:
1101 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
1102 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -08001103
Alex Klein1699fab2022-09-08 08:46:06 -06001104 if (
1105 sdk_latest_version == "<unknown>"
1106 or bootstrap_latest_version == "<unknown>"
1107 ):
1108 cros_build_lib.Die(
1109 "No SDK version was found. "
1110 "Are you in a Chromium source tree instead of Chromium OS?\n\n"
1111 "Please change to a directory inside your Chromium OS source tree\n"
1112 "and retry. If you need to setup a Chromium OS source tree, see\n"
1113 " https://dev.chromium.org/chromium-os/developer-guide"
1114 )
Benjamin Gordon040a1162017-06-29 13:44:47 -06001115
Alex Klein1699fab2022-09-08 08:46:06 -06001116 any_snapshot_operation = (
1117 options.snapshot_create
1118 or options.snapshot_restore
1119 or options.snapshot_delete
1120 or options.snapshot_list
1121 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001122
Alex Klein1699fab2022-09-08 08:46:06 -06001123 if (
1124 options.snapshot_delete
1125 and options.snapshot_delete == options.snapshot_restore
1126 ):
1127 parser.error(
1128 "Cannot --snapshot_delete the same snapshot you are "
1129 "restoring with --snapshot_restore."
1130 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001131
Alex Klein1699fab2022-09-08 08:46:06 -06001132 _ReExecuteIfNeeded([sys.argv[0]] + argv, options)
David James471532c2013-01-21 10:23:31 -08001133
Alex Klein1699fab2022-09-08 08:46:06 -06001134 lock_path = os.path.dirname(options.chroot)
1135 lock_path = os.path.join(
1136 lock_path, ".%s_lock" % os.path.basename(options.chroot).lstrip(".")
1137 )
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001138
Alex Klein1699fab2022-09-08 08:46:06 -06001139 # Expand out the aliases...
1140 if options.replace:
1141 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001142
Alex Klein1699fab2022-09-08 08:46:06 -06001143 if options.bootstrap:
1144 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001145
Alex Klein1699fab2022-09-08 08:46:06 -06001146 # If a command is not given, default to enter.
1147 # pylint: disable=protected-access
1148 # This _group_actions access sucks, but upstream decided to not include an
1149 # alternative to optparse's option_list, and this is what they recommend.
1150 options.enter |= not any(
1151 getattr(options, x.dest) for x in commands._group_actions
1152 )
1153 # pylint: enable=protected-access
Mike Frysinger114a7b92023-01-26 19:08:57 -05001154 options.enter |= bool(options.commands)
Brian Harring218e13c2012-10-10 16:21:26 -07001155
Alex Klein1699fab2022-09-08 08:46:06 -06001156 if (
1157 options.delete
1158 and not options.create
1159 and (options.enter or any_snapshot_operation)
1160 ):
1161 parser.error(
1162 "Trying to enter or snapshot the chroot when --delete "
1163 "was specified makes no sense."
1164 )
Brian Harring218e13c2012-10-10 16:21:26 -07001165
Alex Klein1699fab2022-09-08 08:46:06 -06001166 if options.unmount and (
1167 options.create or options.enter or any_snapshot_operation
1168 ):
1169 parser.error("--unmount cannot be specified with other chroot actions.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001170
Alex Klein1699fab2022-09-08 08:46:06 -06001171 if options.working_dir is not None and not os.path.isabs(
1172 options.working_dir
1173 ):
1174 options.working_dir = path_util.ToChrootPath(options.working_dir)
Yong Hong84ba9172018-02-07 01:37:42 +08001175
Alex Klein1699fab2022-09-08 08:46:06 -06001176 # If there is an existing chroot image and we're not removing it then force
1177 # use_image on. This ensures that people don't have to remember to pass
Alex Kleinef517832023-01-13 12:06:51 -07001178 # --use-image after a reboot to avoid losing access to their existing
1179 # chroot.
Alex Klein1699fab2022-09-08 08:46:06 -06001180 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1181 img_path = _ImageFileForChroot(options.chroot)
1182 if (
1183 not options.use_image
1184 and not options.delete
1185 and not options.unmount
1186 and os.path.exists(img_path)
1187 ):
1188 if chroot_exists:
1189 # If the chroot is already populated, make sure it has something
1190 # mounted on it before we assume it came from an image.
1191 cmd = ["mountpoint", "-q", options.chroot]
1192 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
1193 options.use_image = True
Benjamin Gordon832a4412020-12-08 10:39:16 -07001194
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001195 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001196 logging.notice(
1197 "Existing chroot image %s found. Forcing --use-image on.",
1198 img_path,
1199 )
1200 options.use_image = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001201
Alex Klein1699fab2022-09-08 08:46:06 -06001202 if any_snapshot_operation and not options.use_image:
1203 if os.path.exists(img_path):
1204 options.use_image = True
1205 else:
1206 cros_build_lib.Die(
1207 "Snapshot operations are not compatible with " "--nouse-image."
1208 )
Mike Frysingerc60141b2023-01-27 00:57:15 -05001209 if options.use_image:
1210 logging.warning("--use-image is deprecated and will be removed soon.")
1211 logging.warning("Please migrate, or create a new one with --delete.")
1212 logging.warning("See http://b/266878468 for details.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001213
Alex Klein1699fab2022-09-08 08:46:06 -06001214 # Discern if we need to create the chroot.
1215 if (
1216 options.use_image
1217 and not chroot_exists
1218 and not options.delete
1219 and not options.unmount
1220 and not missing_image_tools
1221 and os.path.exists(img_path)
1222 ):
1223 # Try to re-mount an existing image in case the user has rebooted.
1224 with locking.FileLock(lock_path, "chroot lock") as lock:
1225 logging.debug("Checking if existing chroot image can be mounted.")
1226 lock.write_lock()
1227 cros_sdk_lib.MountChroot(options.chroot, create=False)
1228 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1229 if chroot_exists:
1230 logging.notice("Mounted existing image %s on chroot", img_path)
1231
1232 # Finally, flip create if necessary.
1233 if options.enter or options.snapshot_create:
1234 options.create |= not chroot_exists
1235
1236 # Make sure we will download if we plan to create.
1237 options.download |= options.create
1238
Mike Frysinger6bbd3e92023-01-27 00:05:02 -05001239 options.Freeze()
1240
Mike Frysinger253c8392023-01-27 01:12:21 -05001241 remoteexec = (
1242 remoteexec_util.Remoteexec(
1243 options.reclient_dir, options.reproxy_cfg_file
1244 )
1245 if (options.reclient_dir and options.reproxy_cfg_file)
1246 else None
1247 )
1248
Mike Frysingerec32bea2023-01-27 01:20:48 -05001249 chroot = chroot_lib.Chroot(
1250 path=options.chroot,
1251 cache_dir=options.cache_dir,
1252 chrome_root=options.chrome_root,
1253 goma=goma,
1254 remoteexec=remoteexec,
1255 )
1256
Alex Kleinef517832023-01-13 12:06:51 -07001257 # Anything that needs to manipulate the main chroot mount or communicate
1258 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -06001259
1260 # If deleting, do it regardless of the use_image flag so that a
1261 # previously-created loopback chroot can also be cleaned up.
1262 if options.delete:
1263 # Set a timeout of 300 seconds when getting the lock.
1264 with locking.FileLock(
1265 lock_path, "chroot lock", blocking_timeout=300
1266 ) as lock:
1267 try:
1268 lock.write_lock()
1269 except timeout_util.TimeoutError as e:
1270 logging.error(
1271 "Acquiring write_lock on %s failed: %s", lock_path, e
1272 )
1273 if not options.force:
1274 cros_build_lib.Die(
1275 "Exiting; use --force to continue w/o lock."
1276 )
1277 else:
1278 logging.warning(
1279 "cros_sdk was invoked with force option, continuing."
1280 )
1281 logging.notice("Deleting chroot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001282 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=True)
Alex Klein1699fab2022-09-08 08:46:06 -06001283
Alex Kleinef517832023-01-13 12:06:51 -07001284 # If cleanup was requested, we have to do it while we're still in the
1285 # original namespace. Since cleaning up the mount will interfere with any
1286 # other commands, we exit here. The check above should have made sure that
1287 # no other action was requested, anyway.
Alex Klein1699fab2022-09-08 08:46:06 -06001288 if options.unmount:
1289 # Set a timeout of 300 seconds when getting the lock.
1290 with locking.FileLock(
1291 lock_path, "chroot lock", blocking_timeout=300
1292 ) as lock:
1293 try:
1294 lock.write_lock()
1295 except timeout_util.TimeoutError as e:
1296 logging.error(
1297 "Acquiring write_lock on %s failed: %s", lock_path, e
1298 )
1299 logging.warning(
Alex Kleinef517832023-01-13 12:06:51 -07001300 "Continuing with CleanupChroot(%s), which will umount the "
1301 "tree.",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001302 chroot.path,
Alex Klein1699fab2022-09-08 08:46:06 -06001303 )
Alex Kleinef517832023-01-13 12:06:51 -07001304 # We can call CleanupChroot (which calls
1305 # cros_sdk_lib.CleanupChrootMount) even if we don't get the lock
1306 # because it will attempt to unmount the tree and will print
1307 # diagnostic information from 'fuser', 'lsof', and 'ps'.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001308 cros_sdk_lib.CleanupChrootMount(chroot.path, delete=False)
Alex Klein1699fab2022-09-08 08:46:06 -06001309 sys.exit(0)
1310
1311 # Make sure the main chroot mount is visible. Contents will be filled in
1312 # below if needed.
1313 if options.create and options.use_image:
1314 if missing_image_tools:
1315 raise SystemExit(
1316 """The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001317Please make sure the lvm2 and thin-provisioning-tools packages
1318are installed on your host.
1319Example(ubuntu):
1320 sudo apt-get install lvm2 thin-provisioning-tools
1321
1322If you want to run without lvm2, pass --nouse-image (chroot
Alex Klein1699fab2022-09-08 08:46:06 -06001323snapshots will be unavailable)."""
1324 % ", ".join(missing_image_tools)
1325 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001326
Alex Klein1699fab2022-09-08 08:46:06 -06001327 logging.debug("Making sure chroot image is mounted.")
1328 with locking.FileLock(lock_path, "chroot lock") as lock:
1329 lock.write_lock()
Mike Frysingerec32bea2023-01-27 01:20:48 -05001330 if not cros_sdk_lib.MountChroot(chroot.path, create=True):
Alex Klein1699fab2022-09-08 08:46:06 -06001331 cros_build_lib.Die(
1332 "Unable to mount %s on chroot",
Mike Frysingerec32bea2023-01-27 01:20:48 -05001333 _ImageFileForChroot(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001334 )
1335 logging.notice(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001336 "Mounted %s on chroot", _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001337 )
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001338
Alex Klein1699fab2022-09-08 08:46:06 -06001339 # Snapshot operations will always need the VG/LV, but other actions won't.
1340 if any_snapshot_operation:
1341 with locking.FileLock(lock_path, "chroot lock") as lock:
1342 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001343 chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001344 )
1345 if not chroot_vg or not chroot_lv:
1346 cros_build_lib.Die(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001347 "Unable to find VG/LV for chroot %s", chroot.path
Alex Klein1699fab2022-09-08 08:46:06 -06001348 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001349
Alex Kleinef517832023-01-13 12:06:51 -07001350 # Delete snapshot before creating a new one. This allows the user to
1351 # throw out old state, create a new snapshot, and enter the chroot
1352 # in a single call to cros_sdk. Since restore involves deleting,
1353 # also do it before creating.
Alex Klein1699fab2022-09-08 08:46:06 -06001354 if options.snapshot_restore:
1355 lock.write_lock()
1356 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1357 if options.snapshot_restore not in valid_snapshots:
1358 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -07001359 "%s is not a valid snapshot to restore to. "
1360 "Valid snapshots: %s",
Alex Klein1699fab2022-09-08 08:46:06 -06001361 options.snapshot_restore,
1362 ", ".join(valid_snapshots),
1363 )
Mike Frysingerec32bea2023-01-27 01:20:48 -05001364 osutils.UmountTree(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001365 if not RestoreChrootSnapshot(
1366 options.snapshot_restore, chroot_vg, chroot_lv
1367 ):
1368 cros_build_lib.Die("Unable to restore chroot to snapshot.")
Mike Frysingerec32bea2023-01-27 01:20:48 -05001369 if not cros_sdk_lib.MountChroot(chroot.path, create=False):
Alex Klein1699fab2022-09-08 08:46:06 -06001370 cros_build_lib.Die(
1371 "Unable to mount restored snapshot onto chroot."
1372 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001373
Alex Kleinef517832023-01-13 12:06:51 -07001374 # Use a read lock for snapshot delete and create even though they
1375 # modify the filesystem, because they don't modify the mounted
1376 # chroot itself. The underlying LVM commands take their own locks,
1377 # so conflicting concurrent operations here may crash cros_sdk, but
1378 # won't corrupt the chroot image. This tradeoff seems worth it to
1379 # allow snapshot operations on chroots that have a process inside.
Alex Klein1699fab2022-09-08 08:46:06 -06001380 if options.snapshot_delete:
1381 lock.read_lock()
1382 DeleteChrootSnapshot(
1383 options.snapshot_delete, chroot_vg, chroot_lv
1384 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001385
Alex Klein1699fab2022-09-08 08:46:06 -06001386 if options.snapshot_create:
1387 lock.read_lock()
1388 if not CreateChrootSnapshot(
1389 options.snapshot_create, chroot_vg, chroot_lv
1390 ):
1391 cros_build_lib.Die("Unable to create snapshot.")
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001392
Mike Frysingerec32bea2023-01-27 01:20:48 -05001393 img_path = _ImageFileForChroot(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001394 if (
1395 options.use_image
Mike Frysingerec32bea2023-01-27 01:20:48 -05001396 and os.path.exists(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001397 and os.path.exists(img_path)
1398 ):
1399 img_stat = os.stat(img_path)
1400 img_used_bytes = img_stat.st_blocks * 512
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001401
Mike Frysingerec32bea2023-01-27 01:20:48 -05001402 mount_stat = os.statvfs(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001403 mount_used_bytes = mount_stat.f_frsize * (
1404 mount_stat.f_blocks - mount_stat.f_bfree
1405 )
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001406
Alex Klein1699fab2022-09-08 08:46:06 -06001407 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
1408 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1409 logging.notice(
1410 "%s is using %s GiB more than needed. Running "
1411 "fstrim in background.",
1412 img_path,
1413 extra_gbs,
1414 )
1415 pid = os.fork()
1416 if pid == 0:
1417 try:
1418 # Directly call Popen to run fstrim concurrently.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001419 cmd = ["fstrim", chroot.path]
Alex Klein1699fab2022-09-08 08:46:06 -06001420 subprocess.Popen(cmd, close_fds=True, shell=False)
1421 except subprocess.SubprocessError as e:
1422 logging.warning(
1423 "Running fstrim failed. Consider running fstrim on "
1424 "your chroot manually.\n%s",
1425 e,
1426 )
1427 os._exit(0) # pylint: disable=protected-access
1428 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001429
Alex Kleinef517832023-01-13 12:06:51 -07001430 # Enter a new set of namespaces. Everything after here cannot directly
1431 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -06001432 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001433
Alex Klein1699fab2022-09-08 08:46:06 -06001434 if options.snapshot_list:
1435 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1436 print(snap)
1437 sys.exit(0)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001438
Alex Klein1699fab2022-09-08 08:46:06 -06001439 if not options.sdk_version:
1440 sdk_version = (
1441 bootstrap_latest_version
1442 if options.bootstrap
1443 else sdk_latest_version
1444 )
Yong Hong4e29b622018-02-05 14:31:10 +08001445 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001446 sdk_version = options.sdk_version
1447 if options.buildbot_log_version:
1448 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001449
Alex Klein1699fab2022-09-08 08:46:06 -06001450 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -07001451 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001452 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -06001453 if options.sdk_url:
1454 urls = [options.sdk_url]
1455 else:
1456 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -07001457
Alex Klein1699fab2022-09-08 08:46:06 -06001458 with locking.FileLock(lock_path, "chroot lock") as lock:
1459 if options.proxy_sim:
1460 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -07001461
Mike Frysingerec32bea2023-01-27 01:20:48 -05001462 sdk_cache = os.path.join(chroot.cache_dir, "sdks")
1463 distfiles_cache = os.path.join(chroot.cache_dir, "distfiles")
1464 osutils.SafeMakedirsNonRoot(chroot.cache_dir)
Alex Klein1699fab2022-09-08 08:46:06 -06001465
1466 for target in (sdk_cache, distfiles_cache):
1467 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1468 if not os.path.exists(src):
1469 osutils.SafeMakedirsNonRoot(target)
1470 continue
1471 lock.write_lock(
1472 "Upgrade to %r needed but chroot is locked; please exit "
1473 "all instances so this upgrade can finish." % src
1474 )
1475 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -07001476 # Note that while waiting for the write lock, src may've
1477 # vanished; it's a rare race during the upgrade process that's a
1478 # byproduct of us avoiding taking a write lock to do the src
1479 # check. If we took a write lock for that check, it would
1480 # effectively limit all cros_sdk for a chroot to a single
1481 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -06001482 osutils.SafeMakedirsNonRoot(target)
1483 elif not os.path.exists(target):
1484 # Upgrade occurred, but a reversion, or something whacky
1485 # occurred writing to the old location. Wipe and continue.
1486 os.rename(src, target)
1487 else:
1488 # Upgrade occurred once already, but either a reversion or
1489 # some before/after separate cros_sdk usage is at play.
1490 # Wipe and continue.
1491 osutils.RmDir(src)
1492
Alex Klein1699fab2022-09-08 08:46:06 -06001493 mounted = False
1494 if options.create:
1495 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -07001496 # Recheck if the chroot is set up here before creating to make sure
1497 # we account for whatever the various delete/unmount/remount steps
1498 # above have done.
Mike Frysingerec32bea2023-01-27 01:20:48 -05001499 if cros_sdk_lib.IsChrootReady(chroot.path):
Alex Klein1699fab2022-09-08 08:46:06 -06001500 logging.debug("Chroot already exists. Skipping creation.")
1501 else:
Alex Klein22690a12022-11-17 10:56:09 -07001502 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001503 cros_sdk_lib.CreateChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001504 Path(chroot.path),
Alex Klein1699fab2022-09-08 08:46:06 -06001505 Path(sdk_tarball),
Mike Frysingerec32bea2023-01-27 01:20:48 -05001506 Path(chroot.cache_dir),
Alex Klein1699fab2022-09-08 08:46:06 -06001507 usepkg=not options.bootstrap and not options.nousepkg,
1508 chroot_upgrade=options.chroot_upgrade,
1509 )
1510 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -07001511 elif options.download:
1512 # Allow downloading only.
1513 lock.write_lock()
1514 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001515
1516 if options.enter:
1517 lock.read_lock()
1518 if not mounted:
Mike Frysingerec32bea2023-01-27 01:20:48 -05001519 cros_sdk_lib.MountChrootPaths(chroot.path)
Alex Klein1699fab2022-09-08 08:46:06 -06001520 ret = EnterChroot(
Mike Frysingerec32bea2023-01-27 01:20:48 -05001521 chroot,
Alex Klein1699fab2022-09-08 08:46:06 -06001522 options.chrome_root_mount,
Alex Klein1699fab2022-09-08 08:46:06 -06001523 options.working_dir,
Mike Frysinger114a7b92023-01-26 19:08:57 -05001524 options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -06001525 )
1526 sys.exit(ret.returncode)