blob: a8df76f5661c078878770c8cbc78fddc70d59929 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
Brian Harringb938c782012-02-29 15:14:38 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
Manoj Gupta7fad04d2019-06-14 20:12:25 -07004
Mike Frysinger2f95cfc2015-06-04 04:00:26 -04005"""Manage SDK chroots.
6
7This script is used for manipulating local chroot environments; creating,
8deleting, downloading, etc. If given --enter (or no args), it defaults
9to an interactive bash shell within the chroot.
10
11If given args those are passed to the chroot environment, and executed.
12"""
Brian Harringb938c782012-02-29 15:14:38 -080013
Mike Frysinger2f95cfc2015-06-04 04:00:26 -040014import argparse
Mike Frysinger12d055b2022-06-23 12:26:47 -040015import ast
Josh Triplett472a4182013-03-08 11:48:57 -080016import glob
Chris McDonaldb55b7032021-06-17 16:41:32 -060017import logging
Brian Harringb938c782012-02-29 15:14:38 -080018import os
Mike Frysinger23b5cf52021-06-16 23:18:00 -040019from pathlib import Path
Josh Triplett472a4182013-03-08 11:48:57 -080020import pwd
Benjamin Gordon2d7bf582017-07-12 10:11:26 -060021import random
Brian Norrisd37e2f72016-08-22 16:09:24 -070022import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070023import resource
Mike Frysinger5f5c70b2022-07-19 12:55:21 -040024import shlex
Sergey Frolov1cb46ec2020-12-09 21:46:16 -070025import subprocess
David James56e6c2c2012-10-24 23:54:41 -070026import sys
Mike Frysinger9a5124e2023-01-26 17:16:44 -050027from typing import List, Optional
Mike Frysingere852b072021-05-21 12:39:03 -040028import urllib.parse
Brian Harringb938c782012-02-29 15:14:38 -080029
Chris McDonaldb55b7032021-06-17 16:41:32 -060030from chromite.cbuildbot import cbuildbot_alerts
Brian Harringb6cf9142012-09-01 20:43:17 -070031from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060032from chromite.lib import constants
Brian Harringb938c782012-02-29 15:14:38 -080033from chromite.lib import cros_build_lib
Benjamin Gordon74645232018-05-04 17:40:42 -060034from chromite.lib import cros_sdk_lib
Mike Frysinger9a5124e2023-01-26 17:16:44 -050035from chromite.lib import goma_lib
Brian Harringb938c782012-02-29 15:14:38 -080036from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080037from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070038from chromite.lib import osutils
Yong Hong84ba9172018-02-07 01:37:42 +080039from chromite.lib import path_util
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040040from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080041from chromite.lib import retry_util
Michael Mortensenbf296fb2020-06-18 18:21:54 -060042from chromite.lib import timeout_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050043from chromite.lib import toolchain
Mike Frysingere652ba12019-09-08 00:57:43 -040044from chromite.utils import key_value_store
45
Brian Harringb938c782012-02-29 15:14:38 -080046
Mike Frysingerf744d032022-05-07 20:38:39 -040047# Which compression algos the SDK tarball uses. We've used xz since 2012.
Alex Klein1699fab2022-09-08 08:46:06 -060048COMPRESSION_PREFERENCE = ("xz",)
Zdenek Behanfd0efe42012-04-13 04:36:40 +020049
Brian Harringb938c782012-02-29 15:14:38 -080050# TODO(zbehan): Remove the dependency on these, reimplement them in python
Manoj Guptab12f7302019-06-03 16:40:14 -070051ENTER_CHROOT = [
Alex Klein1699fab2022-09-08 08:46:06 -060052 os.path.join(constants.SOURCE_ROOT, "src/scripts/sdk_lib/enter_chroot.sh")
Manoj Guptab12f7302019-06-03 16:40:14 -070053]
Brian Harringb938c782012-02-29 15:14:38 -080054
Josh Triplett472a4182013-03-08 11:48:57 -080055# Proxy simulator configuration.
Alex Klein1699fab2022-09-08 08:46:06 -060056PROXY_HOST_IP = "192.168.240.1"
Josh Triplett472a4182013-03-08 11:48:57 -080057PROXY_PORT = 8080
Alex Klein1699fab2022-09-08 08:46:06 -060058PROXY_GUEST_IP = "192.168.240.2"
Josh Triplett472a4182013-03-08 11:48:57 -080059PROXY_NETMASK = 30
Alex Klein1699fab2022-09-08 08:46:06 -060060PROXY_VETH_PREFIX = "veth"
Josh Triplett472a4182013-03-08 11:48:57 -080061PROXY_CONNECT_PORTS = (80, 443, 9418)
Alex Klein1699fab2022-09-08 08:46:06 -060062PROXY_APACHE_FALLBACK_USERS = ("www-data", "apache", "nobody")
63PROXY_APACHE_MPMS = ("event", "worker", "prefork")
64PROXY_APACHE_FALLBACK_PATH = ":".join(
65 "/usr/lib/apache2/mpm-%s" % mpm for mpm in PROXY_APACHE_MPMS
66)
67PROXY_APACHE_MODULE_GLOBS = ("/usr/lib*/apache2/modules", "/usr/lib*/apache2")
Josh Triplett472a4182013-03-08 11:48:57 -080068
Josh Triplett9a495f62013-03-15 18:06:55 -070069# We need these tools to run. Very common tools (tar,..) are omitted.
Alex Klein1699fab2022-09-08 08:46:06 -060070NEEDED_TOOLS = ("curl", "xz")
Brian Harringb938c782012-02-29 15:14:38 -080071
Josh Triplett472a4182013-03-08 11:48:57 -080072# Tools needed for --proxy-sim only.
Alex Klein1699fab2022-09-08 08:46:06 -060073PROXY_NEEDED_TOOLS = ("ip",)
Brian Harringb938c782012-02-29 15:14:38 -080074
Mike Frysingerc60141b2023-01-27 00:57:15 -050075# Tools needed when use_image is true.
Alex Klein1699fab2022-09-08 08:46:06 -060076IMAGE_NEEDED_TOOLS = (
77 "losetup",
78 "lvchange",
79 "lvcreate",
80 "lvs",
81 "mke2fs",
82 "pvscan",
83 "thin_check",
84 "vgchange",
85 "vgcreate",
86 "vgs",
87)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060088
Benjamin Gordone3d5bd12017-11-16 15:42:28 -070089# As space is used inside the chroot, the empty space in chroot.img is
90# allocated. Deleting files inside the chroot doesn't automatically return the
91# used space to the OS. Over time, this tends to make the sparse chroot.img
92# less sparse even if the chroot contents don't currently need much space. We
93# can recover most of this unused space with fstrim, but that takes too much
94# time to run it every time. Instead, check the used space against the image
95# size after mounting the chroot and only call fstrim if it looks like we could
96# recover at least this many GiB.
97MAX_UNUSED_IMAGE_GBS = 20
98
Mike Frysingercc838832014-05-24 13:10:30 -040099
Brian Harring1790ac42012-09-23 08:53:33 -0700100def GetArchStageTarballs(version):
Alex Klein1699fab2022-09-08 08:46:06 -0600101 """Returns the URL for a given arch/version"""
102 extension = {"xz": "tar.xz"}
103 return [
104 toolchain.GetSdkURL(
105 suburl="cros-sdk-%s.%s" % (version, extension[compressor])
106 )
107 for compressor in COMPRESSION_PREFERENCE
108 ]
Brian Harring1790ac42012-09-23 08:53:33 -0700109
110
Mike Frysingerf744d032022-05-07 20:38:39 -0400111def FetchRemoteTarballs(storage_dir, urls):
Alex Klein1699fab2022-09-08 08:46:06 -0600112 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200113
Alex Klein1699fab2022-09-08 08:46:06 -0600114 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700115 storage_dir: Path where to save the tarball.
116 urls: List of URLs to try to download. Download will stop on first
117 success.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200118
Alex Klein1699fab2022-09-08 08:46:06 -0600119 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700120 Full path to the downloaded file.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700121
Alex Klein1699fab2022-09-08 08:46:06 -0600122 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700123 ValueError: None of the URLs worked.
Alex Klein1699fab2022-09-08 08:46:06 -0600124 """
125 # Note we track content length ourselves since certain versions of curl
126 # fail if asked to resume a complete file.
127 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
128 status_re = re.compile(rb"^HTTP/[0-9]+(\.[0-9]+)? 200")
129 # pylint: disable=undefined-loop-variable
130 for url in urls:
131 logging.notice("Downloading tarball %s ...", urls[0].rsplit("/", 1)[-1])
132 parsed = urllib.parse.urlparse(url)
133 tarball_name = os.path.basename(parsed.path)
134 if parsed.scheme in ("", "file"):
135 if os.path.exists(parsed.path):
136 return parsed.path
137 continue
138 content_length = 0
139 logging.debug("Attempting download from %s", url)
140 result = retry_util.RunCurl(
141 ["-I", url],
142 print_cmd=False,
143 debug_level=logging.NOTICE,
144 capture_output=True,
145 )
146 successful = False
147 for header in result.stdout.splitlines():
148 # We must walk the output to find the 200 code for use cases where
149 # a proxy is involved and may have pushed down the actual header.
150 if status_re.match(header):
151 successful = True
152 elif header.lower().startswith(b"content-length:"):
153 content_length = int(header.split(b":", 1)[-1].strip())
154 if successful:
155 break
Brian Harring1790ac42012-09-23 08:53:33 -0700156 if successful:
Alex Klein1699fab2022-09-08 08:46:06 -0600157 break
158 else:
159 raise ValueError("No valid URLs found!")
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200160
Alex Klein1699fab2022-09-08 08:46:06 -0600161 tarball_dest = os.path.join(storage_dir, tarball_name)
162 current_size = 0
163 if os.path.exists(tarball_dest):
164 current_size = os.path.getsize(tarball_dest)
165 if current_size > content_length:
166 osutils.SafeUnlink(tarball_dest)
167 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100168
Alex Klein1699fab2022-09-08 08:46:06 -0600169 if current_size < content_length:
170 retry_util.RunCurl(
171 [
172 "--fail",
173 "-L",
174 "-y",
175 "30",
176 "-C",
177 "-",
178 "--output",
179 tarball_dest,
180 url,
181 ],
182 print_cmd=False,
183 debug_level=logging.NOTICE,
184 )
Brian Harringb938c782012-02-29 15:14:38 -0800185
Alex Klein1699fab2022-09-08 08:46:06 -0600186 # Cleanup old tarballs now since we've successfull fetched; only cleanup
187 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
188 # because we might have partial overlap between known prefixes.
189 for p in Path(storage_dir).glob("cros-sdk-*"):
190 if p.name == tarball_name:
191 continue
192 logging.info("Cleaning up old tarball: %s", p)
193 osutils.SafeUnlink(p)
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200194
Alex Klein1699fab2022-09-08 08:46:06 -0600195 return tarball_dest
Brian Harringb938c782012-02-29 15:14:38 -0800196
197
Alex Klein1699fab2022-09-08 08:46:06 -0600198def EnterChroot(
199 chroot_path,
200 cache_dir,
201 chrome_root,
202 chrome_root_mount,
Mike Frysinger9a5124e2023-01-26 17:16:44 -0500203 goma: Optional[goma_lib.Goma],
Alex Klein1699fab2022-09-08 08:46:06 -0600204 reclient_dir,
205 reproxy_cfg_file,
206 working_dir,
207 additional_args,
208):
209 """Enters an existing SDK chroot"""
210 st = os.statvfs(os.path.join(chroot_path, "usr", "bin", "sudo"))
211 if st.f_flag & os.ST_NOSUID:
212 cros_build_lib.Die("chroot cannot be in a nosuid mount")
Mike Frysingere5456972013-06-13 00:07:23 -0400213
Alex Klein1699fab2022-09-08 08:46:06 -0600214 cmd = ENTER_CHROOT + ["--chroot", chroot_path, "--cache_dir", cache_dir]
215 if chrome_root:
216 cmd.extend(["--chrome_root", chrome_root])
217 if chrome_root_mount:
218 cmd.extend(["--chrome_root_mount", chrome_root_mount])
Mike Frysinger9a5124e2023-01-26 17:16:44 -0500219 if goma:
220 cmd.extend(["--goma_dir", goma.linux_goma_dir])
Alex Klein1699fab2022-09-08 08:46:06 -0600221 if reclient_dir:
222 cmd.extend(["--reclient_dir", reclient_dir])
223 if reproxy_cfg_file:
224 cmd.extend(["--reproxy_cfg_file", reproxy_cfg_file])
225 if working_dir is not None:
226 cmd.extend(["--working_dir", working_dir])
Don Garrett230d1b22015-03-09 16:21:19 -0700227
Alex Klein1699fab2022-09-08 08:46:06 -0600228 if additional_args:
229 cmd.append("--")
230 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700231
Alex Klein1699fab2022-09-08 08:46:06 -0600232 if "CHROMEOS_SUDO_RLIMITS" in os.environ:
233 _SetRlimits(os.environ.pop("CHROMEOS_SUDO_RLIMITS"))
Mike Frysinger12d055b2022-06-23 12:26:47 -0400234
Alex Klein1699fab2022-09-08 08:46:06 -0600235 # Some systems set the soft limit too low. Bump it up to the hard limit.
236 # We don't override the hard limit because it's something the admins put
237 # in place and we want to respect such configs. http://b/234353695
238 soft, hard = resource.getrlimit(resource.RLIMIT_NPROC)
239 if soft != resource.RLIM_INFINITY and soft < 4096:
240 if soft < hard or hard == resource.RLIM_INFINITY:
241 resource.setrlimit(resource.RLIMIT_NPROC, (hard, hard))
Mike Frysingerebb23fc2022-06-06 21:17:39 -0400242
Alex Klein1699fab2022-09-08 08:46:06 -0600243 # ThinLTO opens lots of files at the same time.
244 # Set rlimit and vm.max_map_count to accommodate this.
245 file_limit = 262144
246 soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
247 resource.setrlimit(
248 resource.RLIMIT_NOFILE, (max(soft, file_limit), max(hard, file_limit))
249 )
250 max_map_count = int(osutils.ReadFile("/proc/sys/vm/max_map_count"))
251 if max_map_count < file_limit:
252 logging.notice(
253 "Raising vm.max_map_count from %s to %s", max_map_count, file_limit
254 )
255 osutils.WriteFile("/proc/sys/vm/max_map_count", str(file_limit))
256 return cros_build_lib.dbg_run(cmd, check=False)
Brian Harringb938c782012-02-29 15:14:38 -0800257
258
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600259def _ImageFileForChroot(chroot):
Alex Klein1699fab2022-09-08 08:46:06 -0600260 """Find the image file that should be associated with |chroot|.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600261
Alex Klein1699fab2022-09-08 08:46:06 -0600262 This function does not check if the image exists; it simply returns the
263 filename that would be used.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600264
Alex Klein1699fab2022-09-08 08:46:06 -0600265 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700266 chroot: Path to the chroot.
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600267
Alex Klein1699fab2022-09-08 08:46:06 -0600268 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700269 Path to an image file that would be associated with chroot.
Alex Klein1699fab2022-09-08 08:46:06 -0600270 """
271 return chroot.rstrip("/") + ".img"
Benjamin Gordonabb3e372017-08-09 10:21:05 -0600272
273
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600274def CreateChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600275 """Create a snapshot for the specified chroot VG/LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600276
Alex Klein1699fab2022-09-08 08:46:06 -0600277 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700278 snapshot_name: The name of the new snapshot.
279 chroot_vg: The name of the VG containing the origin LV.
280 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600281
Alex Klein1699fab2022-09-08 08:46:06 -0600282 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700283 True if the snapshot was created, or False if a snapshot with the same
284 name already exists.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600285
Alex Klein1699fab2022-09-08 08:46:06 -0600286 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700287 SystemExit: The lvcreate command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600288 """
289 if snapshot_name in ListChrootSnapshots(chroot_vg, chroot_lv):
290 logging.error(
291 "Cannot create snapshot %s: A volume with that name already "
292 "exists.",
293 snapshot_name,
294 )
295 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600296
Alex Klein1699fab2022-09-08 08:46:06 -0600297 cmd = [
298 "lvcreate",
299 "-s",
300 "--name",
301 snapshot_name,
302 "%s/%s" % (chroot_vg, chroot_lv),
303 ]
304 try:
305 logging.notice(
306 "Creating snapshot %s from %s in VG %s.",
307 snapshot_name,
308 chroot_lv,
309 chroot_vg,
310 )
311 cros_build_lib.dbg_run(cmd, capture_output=True)
312 return True
313 except cros_build_lib.RunCommandError as e:
314 cros_build_lib.Die("Creating snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600315
316
317def DeleteChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600318 """Delete the named snapshot from the specified chroot VG.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600319
Alex Klein1699fab2022-09-08 08:46:06 -0600320 If the requested snapshot is not found, nothing happens. The main chroot LV
321 and internal thinpool LV cannot be deleted with this function.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600322
Alex Klein1699fab2022-09-08 08:46:06 -0600323 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700324 snapshot_name: The name of the snapshot to delete.
325 chroot_vg: The name of the VG containing the origin LV.
326 chroot_lv: The name of the origin LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600327
Alex Klein1699fab2022-09-08 08:46:06 -0600328 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700329 SystemExit: The lvremove command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600330 """
331 if snapshot_name in (
332 cros_sdk_lib.CHROOT_LV_NAME,
333 cros_sdk_lib.CHROOT_THINPOOL_NAME,
334 ):
335 logging.error(
336 "Cannot remove LV %s as a snapshot. Use cros_sdk --delete "
337 "if you want to remove the whole chroot.",
338 snapshot_name,
339 )
340 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600341
Alex Klein1699fab2022-09-08 08:46:06 -0600342 if snapshot_name not in ListChrootSnapshots(chroot_vg, chroot_lv):
343 return
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600344
Alex Klein1699fab2022-09-08 08:46:06 -0600345 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, snapshot_name)]
346 try:
347 logging.notice(
348 "Deleting snapshot %s in VG %s.", snapshot_name, chroot_vg
349 )
350 cros_build_lib.dbg_run(cmd, capture_output=True)
351 except cros_build_lib.RunCommandError as e:
352 cros_build_lib.Die("Deleting snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600353
354
355def RestoreChrootSnapshot(snapshot_name, chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600356 """Restore the chroot to an existing snapshot.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600357
Alex Klein1699fab2022-09-08 08:46:06 -0600358 This is done by renaming the original |chroot_lv| LV to a temporary name,
359 renaming the snapshot named |snapshot_name| to |chroot_lv|, and deleting the
360 now unused LV. If an error occurs, attempts to rename the original snapshot
361 back to |chroot_lv| to leave the chroot unchanged.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600362
Alex Klein1699fab2022-09-08 08:46:06 -0600363 The chroot must be unmounted before calling this function, and will be left
364 unmounted after this function returns.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600365
Alex Klein1699fab2022-09-08 08:46:06 -0600366 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700367 snapshot_name: The name of the snapshot to restore. This snapshot will
368 no longer be accessible at its original name after this function
369 finishes.
370 chroot_vg: The VG containing the chroot LV and snapshot LV.
371 chroot_lv: The name of the original chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600372
Alex Klein1699fab2022-09-08 08:46:06 -0600373 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700374 True if the chroot was restored to the requested snapshot, or False if
375 the snapshot wasn't found or isn't valid.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600376
Alex Klein1699fab2022-09-08 08:46:06 -0600377 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700378 SystemExit: Any of the LVM commands failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600379 """
380 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
381 if (
382 snapshot_name
383 in (cros_sdk_lib.CHROOT_LV_NAME, cros_sdk_lib.CHROOT_THINPOOL_NAME)
384 or snapshot_name not in valid_snapshots
385 ):
386 logging.error(
387 "Chroot cannot be restored to %s. Valid snapshots: %s",
388 snapshot_name,
389 ", ".join(valid_snapshots),
390 )
391 return False
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600392
Alex Klein1699fab2022-09-08 08:46:06 -0600393 backup_chroot_name = "chroot-bak-%d" % random.randint(0, 1000)
394 cmd = ["lvrename", chroot_vg, chroot_lv, backup_chroot_name]
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600395 try:
Alex Klein1699fab2022-09-08 08:46:06 -0600396 cros_build_lib.dbg_run(cmd, capture_output=True)
Mike Frysinger75634e32020-02-22 23:48:12 -0500397 except cros_build_lib.RunCommandError as e:
Alex Klein1699fab2022-09-08 08:46:06 -0600398 cros_build_lib.Die("Restoring snapshot failed!\n%s", e)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600399
Alex Klein1699fab2022-09-08 08:46:06 -0600400 cmd = ["lvrename", chroot_vg, snapshot_name, chroot_lv]
401 try:
402 cros_build_lib.dbg_run(cmd, capture_output=True)
403 except cros_build_lib.RunCommandError as e:
404 cmd = ["lvrename", chroot_vg, backup_chroot_name, chroot_lv]
405 try:
406 cros_build_lib.dbg_run(cmd, capture_output=True)
407 except cros_build_lib.RunCommandError as e:
408 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -0700409 "Failed to rename %s to chroot and failed to restore %s back "
410 "to chroot!\n%s",
Alex Klein1699fab2022-09-08 08:46:06 -0600411 snapshot_name,
412 backup_chroot_name,
413 e,
414 )
415 cros_build_lib.Die(
416 "Failed to rename %s to chroot! Original chroot LV has "
417 "been restored.\n%s",
418 snapshot_name,
419 e,
420 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600421
Alex Klein1699fab2022-09-08 08:46:06 -0600422 # Some versions of LVM set snapshots to be skipped at auto-activate time.
423 # Other versions don't have this flag at all. We run lvchange to try
Alex Kleinef517832023-01-13 12:06:51 -0700424 # disabling auto-skip and activating the volume, but ignore errors. Versions
Alex Klein1699fab2022-09-08 08:46:06 -0600425 # that don't have the flag should be auto-activated.
426 chroot_lv_path = "%s/%s" % (chroot_vg, chroot_lv)
427 cmd = ["lvchange", "-kn", chroot_lv_path]
428 cros_build_lib.run(cmd, print_cmd=False, capture_output=True, check=False)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600429
Alex Klein1699fab2022-09-08 08:46:06 -0600430 # Activate the LV in case the lvchange above was needed. Activating an LV
Alex Kleinef517832023-01-13 12:06:51 -0700431 # that is already active shouldn't do anything, so this is safe to run even
432 # if the -kn wasn't needed.
Alex Klein1699fab2022-09-08 08:46:06 -0600433 cmd = ["lvchange", "-ay", chroot_lv_path]
Mike Frysinger3e8de442020-02-14 16:46:28 -0500434 cros_build_lib.dbg_run(cmd, capture_output=True)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600435
Alex Klein1699fab2022-09-08 08:46:06 -0600436 cmd = ["lvremove", "-f", "%s/%s" % (chroot_vg, backup_chroot_name)]
437 try:
438 cros_build_lib.dbg_run(cmd, capture_output=True)
439 except cros_build_lib.RunCommandError as e:
440 cros_build_lib.Die(
441 "Failed to remove backup LV %s/%s!\n%s",
442 chroot_vg,
443 backup_chroot_name,
444 e,
445 )
446
447 return True
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600448
449
450def ListChrootSnapshots(chroot_vg, chroot_lv):
Alex Klein1699fab2022-09-08 08:46:06 -0600451 """Return all snapshots in |chroot_vg| regardless of origin volume.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600452
Alex Klein1699fab2022-09-08 08:46:06 -0600453 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700454 chroot_vg: The name of the VG containing the chroot.
455 chroot_lv: The name of the chroot LV.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600456
Alex Klein1699fab2022-09-08 08:46:06 -0600457 Returns:
Alex Kleinef517832023-01-13 12:06:51 -0700458 A (possibly-empty) list of snapshot LVs found in |chroot_vg|.
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600459
Alex Klein1699fab2022-09-08 08:46:06 -0600460 Raises:
Alex Kleinef517832023-01-13 12:06:51 -0700461 SystemExit: The lvs command failed.
Alex Klein1699fab2022-09-08 08:46:06 -0600462 """
463 if not chroot_vg or not chroot_lv:
464 return []
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600465
Alex Klein1699fab2022-09-08 08:46:06 -0600466 cmd = [
467 "lvs",
468 "-o",
469 "lv_name,pool_lv,lv_attr",
470 "-O",
471 "lv_name",
472 "--noheadings",
473 "--separator",
474 "\t",
475 chroot_vg,
476 ]
477 try:
478 result = cros_build_lib.run(
479 cmd, print_cmd=False, stdout=True, encoding="utf-8"
480 )
481 except cros_build_lib.RunCommandError:
482 raise SystemExit("Running %r failed!" % cmd)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600483
Alex Klein1699fab2022-09-08 08:46:06 -0600484 # Once the thin origin volume has been deleted, there's no way to tell a
Alex Kleinef517832023-01-13 12:06:51 -0700485 # snapshot apart from any other volume. Since this VG is created and managed
486 # by cros_sdk, we'll assume that all volumes that share the same thin pool
487 # are valid snapshots.
Alex Klein1699fab2022-09-08 08:46:06 -0600488 snapshots = []
489 snapshot_attrs = re.compile(r"^V.....t.{2,}") # Matches a thin volume.
490 for line in result.stdout.splitlines():
491 lv_name, pool_lv, lv_attr = line.lstrip().split("\t")
492 if (
493 lv_name == chroot_lv
494 or lv_name == cros_sdk_lib.CHROOT_THINPOOL_NAME
495 or pool_lv != cros_sdk_lib.CHROOT_THINPOOL_NAME
496 or not snapshot_attrs.match(lv_attr)
497 ):
498 continue
499 snapshots.append(lv_name)
500 return snapshots
Benjamin Gordon2d7bf582017-07-12 10:11:26 -0600501
502
Mike Frysinger12d055b2022-06-23 12:26:47 -0400503# The rlimits we will lookup & pass down, in order.
504RLIMITS_TO_PASS = (
505 resource.RLIMIT_AS,
506 resource.RLIMIT_CORE,
507 resource.RLIMIT_CPU,
508 resource.RLIMIT_FSIZE,
509 resource.RLIMIT_MEMLOCK,
510 resource.RLIMIT_NICE,
511 resource.RLIMIT_NOFILE,
512 resource.RLIMIT_NPROC,
513 resource.RLIMIT_RSS,
514 resource.RLIMIT_STACK,
515)
516
517
518def _GetRlimits() -> str:
Alex Klein1699fab2022-09-08 08:46:06 -0600519 """Serialize current rlimits."""
520 return str(tuple(resource.getrlimit(x) for x in RLIMITS_TO_PASS))
Mike Frysinger12d055b2022-06-23 12:26:47 -0400521
522
523def _SetRlimits(limits: str) -> None:
Alex Klein1699fab2022-09-08 08:46:06 -0600524 """Deserialize rlimits."""
525 for rlim, limit in zip(RLIMITS_TO_PASS, ast.literal_eval(limits)):
526 cur_limit = resource.getrlimit(rlim)
527 if cur_limit != limit:
528 # Turn the number into a symbolic name for logging.
529 name = "RLIMIT_???"
530 for name, num in resource.__dict__.items():
531 if name.startswith("RLIMIT_") and num == rlim:
532 break
533 logging.debug(
534 "Restoring user rlimit %s from %r to %r", name, cur_limit, limit
535 )
Mike Frysinger12d055b2022-06-23 12:26:47 -0400536
Alex Klein1699fab2022-09-08 08:46:06 -0600537 resource.setrlimit(rlim, limit)
Mike Frysinger12d055b2022-06-23 12:26:47 -0400538
539
David James56e6c2c2012-10-24 23:54:41 -0700540def _SudoCommand():
Alex Klein1699fab2022-09-08 08:46:06 -0600541 """Get the 'sudo' command, along with all needed environment variables."""
David James56e6c2c2012-10-24 23:54:41 -0700542
Alex Klein1699fab2022-09-08 08:46:06 -0600543 # Pass in the ENVIRONMENT_ALLOWLIST and ENV_PASSTHRU variables so that
544 # scripts in the chroot know what variables to pass through.
545 cmd = ["sudo"]
546 for key in constants.CHROOT_ENVIRONMENT_ALLOWLIST + constants.ENV_PASSTHRU:
547 value = os.environ.get(key)
548 if value is not None:
549 cmd += ["%s=%s" % (key, value)]
David James56e6c2c2012-10-24 23:54:41 -0700550
Alex Kleinef517832023-01-13 12:06:51 -0700551 # We keep PATH not for the chroot but for the re-exec & for programs we
552 # might run before we chroot into the SDK. The process that enters the SDK
553 # itself will take care of initializing PATH to the right value then. But
554 # we can't override the system's default PATH for root as that will hide
555 # /sbin.
Alex Klein1699fab2022-09-08 08:46:06 -0600556 cmd += ["CHROMEOS_SUDO_PATH=%s" % os.environ.get("PATH", "")]
Mike Frysinger2bda4d12020-07-14 11:15:49 -0400557
Alex Klein1699fab2022-09-08 08:46:06 -0600558 # Pass along current rlimit settings so we can restore them.
559 cmd += [f"CHROMEOS_SUDO_RLIMITS={_GetRlimits()}"]
Mike Frysinger12d055b2022-06-23 12:26:47 -0400560
Alex Klein1699fab2022-09-08 08:46:06 -0600561 # Pass in the path to the depot_tools so that users can access them from
562 # within the chroot.
563 cmd += ["DEPOT_TOOLS=%s" % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500564
Alex Klein1699fab2022-09-08 08:46:06 -0600565 return cmd
David James56e6c2c2012-10-24 23:54:41 -0700566
567
Josh Triplett472a4182013-03-08 11:48:57 -0800568def _ReportMissing(missing):
Alex Klein1699fab2022-09-08 08:46:06 -0600569 """Report missing utilities, then exit.
Josh Triplett472a4182013-03-08 11:48:57 -0800570
Alex Klein1699fab2022-09-08 08:46:06 -0600571 Args:
Alex Kleinef517832023-01-13 12:06:51 -0700572 missing: List of missing utilities, as returned by
573 osutils.FindMissingBinaries. If non-empty, will not return.
Alex Klein1699fab2022-09-08 08:46:06 -0600574 """
Josh Triplett472a4182013-03-08 11:48:57 -0800575
Alex Klein1699fab2022-09-08 08:46:06 -0600576 if missing:
577 raise SystemExit(
578 "The tool(s) %s were not found.\n"
579 "Please install the appropriate package in your host.\n"
580 "Example(ubuntu):\n"
581 " sudo apt-get install <packagename>" % ", ".join(missing)
582 )
Josh Triplett472a4182013-03-08 11:48:57 -0800583
584
585def _ProxySimSetup(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600586 """Set up proxy simulator, and return only in the child environment.
Josh Triplett472a4182013-03-08 11:48:57 -0800587
Alex Klein1699fab2022-09-08 08:46:06 -0600588 TODO: Ideally, this should support multiple concurrent invocations of
589 cros_sdk --proxy-sim; currently, such invocations will conflict with each
590 other due to the veth device names and IP addresses. Either this code would
591 need to generate fresh, unused names for all of these before forking, or it
592 would need to support multiple concurrent cros_sdk invocations sharing one
593 proxy and allowing it to exit when unused (without counting on any local
594 service-management infrastructure on the host).
595 """
Josh Triplett472a4182013-03-08 11:48:57 -0800596
Alex Klein1699fab2022-09-08 08:46:06 -0600597 may_need_mpm = False
598 apache_bin = osutils.Which("apache2")
Josh Triplett472a4182013-03-08 11:48:57 -0800599 if apache_bin is None:
Alex Klein1699fab2022-09-08 08:46:06 -0600600 apache_bin = osutils.Which("apache2", PROXY_APACHE_FALLBACK_PATH)
601 if apache_bin is None:
602 _ReportMissing(("apache2",))
603 else:
604 may_need_mpm = True
Josh Triplett472a4182013-03-08 11:48:57 -0800605
Alex Klein1699fab2022-09-08 08:46:06 -0600606 # Module names and .so names included for ease of grepping.
607 apache_modules = [
608 ("proxy_module", "mod_proxy.so"),
609 ("proxy_connect_module", "mod_proxy_connect.so"),
610 ("proxy_http_module", "mod_proxy_http.so"),
611 ("proxy_ftp_module", "mod_proxy_ftp.so"),
612 ]
Josh Triplett472a4182013-03-08 11:48:57 -0800613
Alex Kleinef517832023-01-13 12:06:51 -0700614 # Find the apache module directory and make sure it has the modules we need.
Alex Klein1699fab2022-09-08 08:46:06 -0600615 module_dirs = {}
616 for g in PROXY_APACHE_MODULE_GLOBS:
617 for _, so in apache_modules:
618 for f in glob.glob(os.path.join(g, so)):
619 module_dirs.setdefault(os.path.dirname(f), []).append(so)
620 for apache_module_path, modules_found in module_dirs.items():
621 if len(modules_found) == len(apache_modules):
622 break
623 else:
Alex Kleinef517832023-01-13 12:06:51 -0700624 # Appease cros lint, which doesn't understand that this else block will
625 # not fall through to the subsequent code which relies on
626 # apache_module_path.
Alex Klein1699fab2022-09-08 08:46:06 -0600627 apache_module_path = None
628 raise SystemExit(
Alex Kleinef517832023-01-13 12:06:51 -0700629 "Could not find apache module path containing all required "
630 "modules: %s" % ", ".join(so for mod, so in apache_modules)
Alex Klein1699fab2022-09-08 08:46:06 -0600631 )
Josh Triplett472a4182013-03-08 11:48:57 -0800632
Alex Klein1699fab2022-09-08 08:46:06 -0600633 def check_add_module(name):
634 so = "mod_%s.so" % name
635 if os.access(os.path.join(apache_module_path, so), os.F_OK):
636 mod = "%s_module" % name
637 apache_modules.append((mod, so))
638 return True
639 return False
Josh Triplett472a4182013-03-08 11:48:57 -0800640
Alex Klein1699fab2022-09-08 08:46:06 -0600641 check_add_module("authz_core")
642 if may_need_mpm:
643 for mpm in PROXY_APACHE_MPMS:
644 if check_add_module("mpm_%s" % mpm):
645 break
Josh Triplett472a4182013-03-08 11:48:57 -0800646
Alex Klein1699fab2022-09-08 08:46:06 -0600647 veth_host = "%s-host" % PROXY_VETH_PREFIX
648 veth_guest = "%s-guest" % PROXY_VETH_PREFIX
Josh Triplett472a4182013-03-08 11:48:57 -0800649
Alex Klein1699fab2022-09-08 08:46:06 -0600650 # Set up locks to sync the net namespace setup. We need the child to create
Alex Kleinef517832023-01-13 12:06:51 -0700651 # the net ns first, and then have the parent assign the guest end of the
652 # veth interface to the child's new network namespace & bring up the proxy.
653 # Only then can the child move forward and rely on the network being up.
Alex Klein1699fab2022-09-08 08:46:06 -0600654 ns_create_lock = locking.PipeLock()
655 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800656
Alex Klein1699fab2022-09-08 08:46:06 -0600657 pid = os.fork()
658 if not pid:
659 # Create our new isolated net namespace.
660 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500661
Alex Klein1699fab2022-09-08 08:46:06 -0600662 # Signal the parent the ns is ready to be configured.
663 ns_create_lock.Post()
664 del ns_create_lock
665
666 # Wait for the parent to finish setting up the ns/proxy.
667 ns_setup_lock.Wait()
668 del ns_setup_lock
669
670 # Set up child side of the network.
671 commands = (
672 ("ip", "link", "set", "up", "lo"),
673 (
674 "ip",
675 "address",
676 "add",
677 "%s/%u" % (PROXY_GUEST_IP, PROXY_NETMASK),
678 "dev",
679 veth_guest,
680 ),
681 ("ip", "link", "set", veth_guest, "up"),
682 )
683 try:
684 for cmd in commands:
685 cros_build_lib.dbg_run(cmd)
686 except cros_build_lib.RunCommandError as e:
687 cros_build_lib.Die("Proxy setup failed!\n%s", e)
688
689 proxy_url = "http://%s:%u" % (PROXY_HOST_IP, PROXY_PORT)
690 for proto in ("http", "https", "ftp"):
691 os.environ[proto + "_proxy"] = proxy_url
692 for v in ("all_proxy", "RSYNC_PROXY", "no_proxy"):
693 os.environ.pop(v, None)
694 return
695
696 # Set up parent side of the network.
697 uid = int(os.environ.get("SUDO_UID", "0"))
698 gid = int(os.environ.get("SUDO_GID", "0"))
699 if uid == 0 or gid == 0:
700 for username in PROXY_APACHE_FALLBACK_USERS:
701 try:
702 pwnam = pwd.getpwnam(username)
703 uid, gid = pwnam.pw_uid, pwnam.pw_gid
704 break
705 except KeyError:
706 continue
707 if uid == 0 or gid == 0:
708 raise SystemExit("Could not find a non-root user to run Apache as")
709
710 chroot_parent, chroot_base = os.path.split(options.chroot)
711 pid_file = os.path.join(chroot_parent, ".%s-apache-proxy.pid" % chroot_base)
712 log_file = os.path.join(chroot_parent, ".%s-apache-proxy.log" % chroot_base)
713
714 # Wait for the child to create the net ns.
715 ns_create_lock.Wait()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500716 del ns_create_lock
717
Alex Klein1699fab2022-09-08 08:46:06 -0600718 apache_directives = [
719 "User #%u" % uid,
720 "Group #%u" % gid,
721 "PidFile %s" % pid_file,
722 "ErrorLog %s" % log_file,
723 "Listen %s:%u" % (PROXY_HOST_IP, PROXY_PORT),
724 "ServerName %s" % PROXY_HOST_IP,
725 "ProxyRequests On",
726 "AllowCONNECT %s" % " ".join(str(x) for x in PROXY_CONNECT_PORTS),
727 ] + [
728 "LoadModule %s %s" % (mod, os.path.join(apache_module_path, so))
729 for (mod, so) in apache_modules
730 ]
731 commands = (
732 (
733 "ip",
734 "link",
735 "add",
736 "name",
737 veth_host,
738 "type",
739 "veth",
740 "peer",
741 "name",
742 veth_guest,
743 ),
744 (
745 "ip",
746 "address",
747 "add",
748 "%s/%u" % (PROXY_HOST_IP, PROXY_NETMASK),
749 "dev",
750 veth_host,
751 ),
752 ("ip", "link", "set", veth_host, "up"),
753 (
754 [apache_bin, "-f", "/dev/null"]
755 + [arg for d in apache_directives for arg in ("-C", d)]
756 ),
757 ("ip", "link", "set", veth_guest, "netns", str(pid)),
758 )
759 cmd = None # Make cros lint happy.
760 try:
761 for cmd in commands:
762 cros_build_lib.dbg_run(cmd)
763 except cros_build_lib.RunCommandError as e:
764 # Clean up existing interfaces, if any.
765 cmd_cleanup = ("ip", "link", "del", veth_host)
766 try:
767 cros_build_lib.run(cmd_cleanup, print_cmd=False)
768 except cros_build_lib.RunCommandError:
769 logging.error("running %r failed", cmd_cleanup)
770 cros_build_lib.Die("Proxy network setup failed!\n%s", e)
771
772 # Signal the child that the net ns/proxy is fully configured now.
773 ns_setup_lock.Post()
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500774 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800775
Alex Klein1699fab2022-09-08 08:46:06 -0600776 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800777
778
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400779def _BuildReExecCommand(argv, opts) -> List[str]:
Alex Klein1699fab2022-09-08 08:46:06 -0600780 """Generate new command for self-reexec."""
781 # Make sure to preserve the active Python executable in case the version
782 # we're running as is not the default one found via the (new) $PATH.
783 cmd = _SudoCommand() + ["--"]
784 if opts.strace:
785 cmd += ["strace"] + shlex.split(opts.strace_arguments) + ["--"]
786 return cmd + [sys.executable] + argv
Mike Frysinger5f5c70b2022-07-19 12:55:21 -0400787
788
789def _ReExecuteIfNeeded(argv, opts):
Alex Klein1699fab2022-09-08 08:46:06 -0600790 """Re-execute cros_sdk as root.
David James56e6c2c2012-10-24 23:54:41 -0700791
Alex Klein1699fab2022-09-08 08:46:06 -0600792 Also unshare the mount namespace so as to ensure that processes outside
793 the chroot can't mess with our mounts.
794 """
795 if osutils.IsNonRootUser():
796 cmd = _BuildReExecCommand(argv, opts)
797 logging.debug(
798 "Reexecing self via sudo:\n%s", cros_build_lib.CmdToStr(cmd)
799 )
800 os.execvp(cmd[0], cmd)
David James56e6c2c2012-10-24 23:54:41 -0700801
802
Mike Frysinger34db8692013-11-11 14:54:08 -0500803def _CreateParser(sdk_latest_version, bootstrap_latest_version):
Alex Klein1699fab2022-09-08 08:46:06 -0600804 """Generate and return the parser with all the options."""
805 usage = (
806 "usage: %(prog)s [options] "
807 "[VAR1=val1 ... VAR2=val2] [--] [command [args]]"
808 )
809 parser = commandline.ArgumentParser(
810 usage=usage, description=__doc__, caching=True
811 )
Brian Harring218e13c2012-10-10 16:21:26 -0700812
Alex Klein1699fab2022-09-08 08:46:06 -0600813 # Global options.
814 default_chroot = os.path.join(
815 constants.SOURCE_ROOT, constants.DEFAULT_CHROOT_DIR
816 )
817 parser.add_argument(
818 "--chroot",
819 dest="chroot",
820 default=default_chroot,
821 type="path",
822 help=("SDK chroot dir name [%s]" % constants.DEFAULT_CHROOT_DIR),
823 )
824 parser.add_argument(
825 "--nouse-image",
826 dest="use_image",
827 action="store_false",
828 default=False,
829 help="Do not mount the chroot on a loopback image; "
830 "instead, create it directly in a directory.",
831 )
832 parser.add_argument(
833 "--use-image",
834 dest="use_image",
835 action="store_true",
836 default=False,
837 help="Mount the chroot on a loopback image "
838 "instead of creating it directly in a directory.",
839 )
Brian Harringb938c782012-02-29 15:14:38 -0800840
Alex Klein1699fab2022-09-08 08:46:06 -0600841 parser.add_argument(
842 "--chrome-root",
843 "--chrome_root",
844 type="path",
845 help="Mount this chrome root into the SDK chroot",
846 )
847 parser.add_argument(
848 "--chrome_root_mount",
849 type="path",
850 help="Mount chrome into this path inside SDK chroot",
851 )
852 parser.add_argument(
853 "--nousepkg",
854 action="store_true",
855 default=False,
856 help="Do not use binary packages when creating a chroot.",
857 )
858 parser.add_argument(
859 "-u",
860 "--url",
861 dest="sdk_url",
862 help="Use sdk tarball located at this url. Use file:// "
863 "for local files.",
864 )
865 parser.add_argument(
866 "--sdk-version",
867 help=(
868 "Use this sdk version. For prebuilt, current is %r"
869 ", for bootstrapping it is %r."
870 % (sdk_latest_version, bootstrap_latest_version)
871 ),
872 )
873 parser.add_argument(
Mike Frysinger85824562023-01-31 02:40:43 -0500874 "--goma-dir",
Alex Klein1699fab2022-09-08 08:46:06 -0600875 "--goma_dir",
876 type="path",
877 help="Goma installed directory to mount into the chroot.",
878 )
879 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600880 "--reclient-dir",
881 type="path",
882 help="Reclient installed directory to mount into the chroot.",
883 )
884 parser.add_argument(
885 "--reproxy-cfg-file",
886 type="path",
887 help="Config file for re-client's reproxy used for remoteexec.",
888 )
889 parser.add_argument(
890 "--skip-chroot-upgrade",
891 dest="chroot_upgrade",
892 action="store_false",
893 default=True,
Alex Kleinef517832023-01-13 12:06:51 -0700894 help="Skip automatic SDK and toolchain upgrade when entering the "
895 "chroot. Never guaranteed to work, especially as ToT moves forward.",
Alex Klein1699fab2022-09-08 08:46:06 -0600896 )
Yong Hong84ba9172018-02-07 01:37:42 +0800897
Alex Klein1699fab2022-09-08 08:46:06 -0600898 # Use type=str instead of type='path' to prevent the given path from being
Alex Kleinef517832023-01-13 12:06:51 -0700899 # transferred to absolute path automatically.
Alex Klein1699fab2022-09-08 08:46:06 -0600900 parser.add_argument(
901 "--working-dir",
902 type=str,
903 help="Run the command in specific working directory in "
904 "chroot. If the given directory is a relative "
905 "path, this program will transfer the path to "
906 "the corresponding one inside chroot.",
907 )
Yong Hong84ba9172018-02-07 01:37:42 +0800908
Alex Klein1699fab2022-09-08 08:46:06 -0600909 parser.add_argument("commands", nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500910
Alex Klein1699fab2022-09-08 08:46:06 -0600911 # Commands.
912 group = parser.add_argument_group("Commands")
Mike Frysinger79024a32021-04-05 03:28:35 -0400913 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600914 "--enter",
915 action="store_true",
916 default=False,
917 help="Enter the SDK chroot. Implies --create.",
918 )
Mike Frysinger79024a32021-04-05 03:28:35 -0400919 group.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600920 "--create",
921 action="store_true",
922 default=False,
Alex Klein22690a12022-11-17 10:56:09 -0700923 help="Create the chroot only if it does not already exist. Downloads "
924 "the SDK only if needed, even if --download explicitly passed.",
Alex Klein1699fab2022-09-08 08:46:06 -0600925 )
926 group.add_argument(
927 "--bootstrap",
928 action="store_true",
929 default=False,
930 help="Build everything from scratch, including the sdk. "
931 "Use this only if you need to validate a change "
932 "that affects SDK creation itself (toolchain and "
933 "build are typically the only folk who need this). "
934 "Note this will quite heavily slow down the build. "
935 "This option implies --create --nousepkg.",
936 )
937 group.add_argument(
938 "-r",
939 "--replace",
940 action="store_true",
941 default=False,
942 help="Replace an existing SDK chroot. Basically an alias "
943 "for --delete --create.",
944 )
945 group.add_argument(
946 "--delete",
947 action="store_true",
948 default=False,
949 help="Delete the current SDK chroot if it exists.",
950 )
951 group.add_argument(
952 "--force",
953 action="store_true",
954 default=False,
955 help="Force unmount/delete of the current SDK chroot even if "
956 "obtaining the write lock fails.",
957 )
958 group.add_argument(
959 "--unmount",
960 action="store_true",
961 default=False,
962 help="Unmount and clean up devices associated with the "
963 "SDK chroot if it exists. This does not delete the "
964 "backing image file, so the same chroot can be later "
965 "re-mounted for reuse. To fully delete the chroot, use "
966 "--delete. This is primarily useful for working on "
967 "cros_sdk or the chroot setup; you should not need it "
968 "under normal circumstances.",
969 )
970 group.add_argument(
971 "--download",
972 action="store_true",
973 default=False,
974 help="Download the sdk.",
975 )
976 group.add_argument(
977 "--snapshot-create",
978 metavar="SNAPSHOT_NAME",
979 help="Create a snapshot of the chroot. Requires that the chroot was "
980 "created without the --nouse-image option.",
981 )
982 group.add_argument(
983 "--snapshot-restore",
984 metavar="SNAPSHOT_NAME",
985 help="Restore the chroot to a previously created snapshot.",
986 )
987 group.add_argument(
988 "--snapshot-delete",
989 metavar="SNAPSHOT_NAME",
990 help="Delete a previously created snapshot. Deleting a snapshot that "
991 "does not exist is not an error.",
992 )
993 group.add_argument(
994 "--snapshot-list",
995 action="store_true",
996 default=False,
997 help="List existing snapshots of the chroot and exit.",
998 )
999 commands = group
Mike Frysinger80dfce92014-04-21 10:58:53 -04001000
Alex Klein1699fab2022-09-08 08:46:06 -06001001 # Namespace options.
1002 group = parser.add_argument_group("Namespaces")
1003 group.add_argument(
1004 "--proxy-sim",
1005 action="store_true",
1006 default=False,
1007 help="Simulate a restrictive network requiring an outbound" " proxy.",
1008 )
1009 for ns, default in (("pid", True), ("net", None)):
1010 group.add_argument(
1011 f"--ns-{ns}",
1012 default=default,
1013 action="store_true",
1014 help=f"Create a new {ns} namespace.",
1015 )
1016 group.add_argument(
1017 f"--no-ns-{ns}",
1018 dest=f"ns_{ns}",
1019 action="store_false",
1020 help=f"Do not create a new {ns} namespace.",
1021 )
Mike Frysinger5f5c70b2022-07-19 12:55:21 -04001022
Alex Klein1699fab2022-09-08 08:46:06 -06001023 # Debug options.
1024 group = parser.debug_group
1025 group.add_argument(
1026 "--strace",
1027 action="store_true",
1028 help="Run cros_sdk through strace after re-exec via sudo",
1029 )
1030 group.add_argument(
1031 "--strace-arguments",
1032 default="",
1033 help="Extra strace options (shell quoting permitted)",
1034 )
Mike Frysinger34db8692013-11-11 14:54:08 -05001035
Alex Klein1699fab2022-09-08 08:46:06 -06001036 # Internal options.
1037 group = parser.add_argument_group(
1038 "Internal Chromium OS Build Team Options",
1039 "Caution: these are for meant for the Chromium OS build team only",
1040 )
1041 group.add_argument(
1042 "--buildbot-log-version",
1043 default=False,
1044 action="store_true",
1045 help="Log SDK version for buildbot consumption",
1046 )
1047
1048 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -05001049
1050
1051def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -06001052 # Turn on strict sudo checks.
1053 cros_build_lib.STRICT_SUDO = True
1054 conf = key_value_store.LoadFile(
1055 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
1056 ignore_missing=True,
1057 )
1058 sdk_latest_version = conf.get("SDK_LATEST_VERSION", "<unknown>")
1059 bootstrap_frozen_version = conf.get("BOOTSTRAP_FROZEN_VERSION", "<unknown>")
Manoj Gupta55a63092019-06-13 11:47:13 -07001060
Alex Klein1699fab2022-09-08 08:46:06 -06001061 # Use latest SDK for bootstrapping if requested. Use a frozen version of SDK
1062 # for bootstrapping if BOOTSTRAP_FROZEN_VERSION is set.
1063 bootstrap_latest_version = (
1064 sdk_latest_version
1065 if bootstrap_frozen_version == "<unknown>"
1066 else bootstrap_frozen_version
1067 )
1068 parser, commands = _CreateParser(
1069 sdk_latest_version, bootstrap_latest_version
1070 )
1071 options = parser.parse_args(argv)
Brian Harringb938c782012-02-29 15:14:38 -08001072
Alex Klein1699fab2022-09-08 08:46:06 -06001073 # Some basic checks first, before we ask for sudo credentials.
1074 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -08001075
Alex Klein1699fab2022-09-08 08:46:06 -06001076 host = os.uname()[4]
1077 if host != "x86_64":
1078 cros_build_lib.Die(
1079 "cros_sdk is currently only supported on x86_64; you're running"
1080 " %s. Please find a x86_64 machine." % (host,)
1081 )
Brian Harring1790ac42012-09-23 08:53:33 -07001082
Mike Frysinger9a5124e2023-01-26 17:16:44 -05001083 goma = goma_lib.Goma(options.goma_dir) if options.goma_dir else None
1084
Alex Klein1699fab2022-09-08 08:46:06 -06001085 # Merge the outside PATH setting if we re-execed ourselves.
1086 if "CHROMEOS_SUDO_PATH" in os.environ:
1087 os.environ["PATH"] = "%s:%s" % (
1088 os.environ.pop("CHROMEOS_SUDO_PATH"),
1089 os.environ["PATH"],
1090 )
Mike Frysinger2bda4d12020-07-14 11:15:49 -04001091
Alex Klein1699fab2022-09-08 08:46:06 -06001092 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
1093 if options.proxy_sim:
1094 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
1095 missing_image_tools = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
Brian Harringb938c782012-02-29 15:14:38 -08001096
Alex Klein1699fab2022-09-08 08:46:06 -06001097 if (
1098 sdk_latest_version == "<unknown>"
1099 or bootstrap_latest_version == "<unknown>"
1100 ):
1101 cros_build_lib.Die(
1102 "No SDK version was found. "
1103 "Are you in a Chromium source tree instead of Chromium OS?\n\n"
1104 "Please change to a directory inside your Chromium OS source tree\n"
1105 "and retry. If you need to setup a Chromium OS source tree, see\n"
1106 " https://dev.chromium.org/chromium-os/developer-guide"
1107 )
Benjamin Gordon040a1162017-06-29 13:44:47 -06001108
Alex Klein1699fab2022-09-08 08:46:06 -06001109 any_snapshot_operation = (
1110 options.snapshot_create
1111 or options.snapshot_restore
1112 or options.snapshot_delete
1113 or options.snapshot_list
1114 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001115
Alex Klein1699fab2022-09-08 08:46:06 -06001116 if (
1117 options.snapshot_delete
1118 and options.snapshot_delete == options.snapshot_restore
1119 ):
1120 parser.error(
1121 "Cannot --snapshot_delete the same snapshot you are "
1122 "restoring with --snapshot_restore."
1123 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001124
Alex Klein1699fab2022-09-08 08:46:06 -06001125 _ReExecuteIfNeeded([sys.argv[0]] + argv, options)
David James471532c2013-01-21 10:23:31 -08001126
Alex Klein1699fab2022-09-08 08:46:06 -06001127 lock_path = os.path.dirname(options.chroot)
1128 lock_path = os.path.join(
1129 lock_path, ".%s_lock" % os.path.basename(options.chroot).lstrip(".")
1130 )
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001131
Alex Klein1699fab2022-09-08 08:46:06 -06001132 # Expand out the aliases...
1133 if options.replace:
1134 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001135
Alex Klein1699fab2022-09-08 08:46:06 -06001136 if options.bootstrap:
1137 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -08001138
Alex Klein1699fab2022-09-08 08:46:06 -06001139 # If a command is not given, default to enter.
1140 # pylint: disable=protected-access
1141 # This _group_actions access sucks, but upstream decided to not include an
1142 # alternative to optparse's option_list, and this is what they recommend.
1143 options.enter |= not any(
1144 getattr(options, x.dest) for x in commands._group_actions
1145 )
1146 # pylint: enable=protected-access
Mike Frysinger114a7b92023-01-26 19:08:57 -05001147 options.enter |= bool(options.commands)
Brian Harring218e13c2012-10-10 16:21:26 -07001148
Alex Klein1699fab2022-09-08 08:46:06 -06001149 if (
1150 options.delete
1151 and not options.create
1152 and (options.enter or any_snapshot_operation)
1153 ):
1154 parser.error(
1155 "Trying to enter or snapshot the chroot when --delete "
1156 "was specified makes no sense."
1157 )
Brian Harring218e13c2012-10-10 16:21:26 -07001158
Alex Klein1699fab2022-09-08 08:46:06 -06001159 if options.unmount and (
1160 options.create or options.enter or any_snapshot_operation
1161 ):
1162 parser.error("--unmount cannot be specified with other chroot actions.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001163
Alex Klein1699fab2022-09-08 08:46:06 -06001164 if options.working_dir is not None and not os.path.isabs(
1165 options.working_dir
1166 ):
1167 options.working_dir = path_util.ToChrootPath(options.working_dir)
Yong Hong84ba9172018-02-07 01:37:42 +08001168
Alex Klein1699fab2022-09-08 08:46:06 -06001169 # If there is an existing chroot image and we're not removing it then force
1170 # use_image on. This ensures that people don't have to remember to pass
Alex Kleinef517832023-01-13 12:06:51 -07001171 # --use-image after a reboot to avoid losing access to their existing
1172 # chroot.
Alex Klein1699fab2022-09-08 08:46:06 -06001173 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1174 img_path = _ImageFileForChroot(options.chroot)
1175 if (
1176 not options.use_image
1177 and not options.delete
1178 and not options.unmount
1179 and os.path.exists(img_path)
1180 ):
1181 if chroot_exists:
1182 # If the chroot is already populated, make sure it has something
1183 # mounted on it before we assume it came from an image.
1184 cmd = ["mountpoint", "-q", options.chroot]
1185 if cros_build_lib.dbg_run(cmd, check=False).returncode == 0:
1186 options.use_image = True
Benjamin Gordon832a4412020-12-08 10:39:16 -07001187
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001188 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001189 logging.notice(
1190 "Existing chroot image %s found. Forcing --use-image on.",
1191 img_path,
1192 )
1193 options.use_image = True
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001194
Alex Klein1699fab2022-09-08 08:46:06 -06001195 if any_snapshot_operation and not options.use_image:
1196 if os.path.exists(img_path):
1197 options.use_image = True
1198 else:
1199 cros_build_lib.Die(
1200 "Snapshot operations are not compatible with " "--nouse-image."
1201 )
Mike Frysingerc60141b2023-01-27 00:57:15 -05001202 if options.use_image:
1203 logging.warning("--use-image is deprecated and will be removed soon.")
1204 logging.warning("Please migrate, or create a new one with --delete.")
1205 logging.warning("See http://b/266878468 for details.")
Benjamin Gordon64a3f8d2018-06-08 10:34:39 -06001206
Alex Klein1699fab2022-09-08 08:46:06 -06001207 # Discern if we need to create the chroot.
1208 if (
1209 options.use_image
1210 and not chroot_exists
1211 and not options.delete
1212 and not options.unmount
1213 and not missing_image_tools
1214 and os.path.exists(img_path)
1215 ):
1216 # Try to re-mount an existing image in case the user has rebooted.
1217 with locking.FileLock(lock_path, "chroot lock") as lock:
1218 logging.debug("Checking if existing chroot image can be mounted.")
1219 lock.write_lock()
1220 cros_sdk_lib.MountChroot(options.chroot, create=False)
1221 chroot_exists = cros_sdk_lib.IsChrootReady(options.chroot)
1222 if chroot_exists:
1223 logging.notice("Mounted existing image %s on chroot", img_path)
1224
1225 # Finally, flip create if necessary.
1226 if options.enter or options.snapshot_create:
1227 options.create |= not chroot_exists
1228
1229 # Make sure we will download if we plan to create.
1230 options.download |= options.create
1231
Mike Frysinger6bbd3e92023-01-27 00:05:02 -05001232 options.Freeze()
1233
Alex Kleinef517832023-01-13 12:06:51 -07001234 # Anything that needs to manipulate the main chroot mount or communicate
1235 # with LVM needs to be done here before we enter the new namespaces.
Alex Klein1699fab2022-09-08 08:46:06 -06001236
1237 # If deleting, do it regardless of the use_image flag so that a
1238 # previously-created loopback chroot can also be cleaned up.
1239 if options.delete:
1240 # Set a timeout of 300 seconds when getting the lock.
1241 with locking.FileLock(
1242 lock_path, "chroot lock", blocking_timeout=300
1243 ) as lock:
1244 try:
1245 lock.write_lock()
1246 except timeout_util.TimeoutError as e:
1247 logging.error(
1248 "Acquiring write_lock on %s failed: %s", lock_path, e
1249 )
1250 if not options.force:
1251 cros_build_lib.Die(
1252 "Exiting; use --force to continue w/o lock."
1253 )
1254 else:
1255 logging.warning(
1256 "cros_sdk was invoked with force option, continuing."
1257 )
1258 logging.notice("Deleting chroot.")
1259 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=True)
1260
Alex Kleinef517832023-01-13 12:06:51 -07001261 # If cleanup was requested, we have to do it while we're still in the
1262 # original namespace. Since cleaning up the mount will interfere with any
1263 # other commands, we exit here. The check above should have made sure that
1264 # no other action was requested, anyway.
Alex Klein1699fab2022-09-08 08:46:06 -06001265 if options.unmount:
1266 # Set a timeout of 300 seconds when getting the lock.
1267 with locking.FileLock(
1268 lock_path, "chroot lock", blocking_timeout=300
1269 ) as lock:
1270 try:
1271 lock.write_lock()
1272 except timeout_util.TimeoutError as e:
1273 logging.error(
1274 "Acquiring write_lock on %s failed: %s", lock_path, e
1275 )
1276 logging.warning(
Alex Kleinef517832023-01-13 12:06:51 -07001277 "Continuing with CleanupChroot(%s), which will umount the "
1278 "tree.",
Alex Klein1699fab2022-09-08 08:46:06 -06001279 options.chroot,
1280 )
Alex Kleinef517832023-01-13 12:06:51 -07001281 # We can call CleanupChroot (which calls
1282 # cros_sdk_lib.CleanupChrootMount) even if we don't get the lock
1283 # because it will attempt to unmount the tree and will print
1284 # diagnostic information from 'fuser', 'lsof', and 'ps'.
Alex Klein1699fab2022-09-08 08:46:06 -06001285 cros_sdk_lib.CleanupChrootMount(options.chroot, delete=False)
1286 sys.exit(0)
1287
1288 # Make sure the main chroot mount is visible. Contents will be filled in
1289 # below if needed.
1290 if options.create and options.use_image:
1291 if missing_image_tools:
1292 raise SystemExit(
1293 """The tool(s) %s were not found.
Benjamin Gordonabb3e372017-08-09 10:21:05 -06001294Please make sure the lvm2 and thin-provisioning-tools packages
1295are installed on your host.
1296Example(ubuntu):
1297 sudo apt-get install lvm2 thin-provisioning-tools
1298
1299If you want to run without lvm2, pass --nouse-image (chroot
Alex Klein1699fab2022-09-08 08:46:06 -06001300snapshots will be unavailable)."""
1301 % ", ".join(missing_image_tools)
1302 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001303
Alex Klein1699fab2022-09-08 08:46:06 -06001304 logging.debug("Making sure chroot image is mounted.")
1305 with locking.FileLock(lock_path, "chroot lock") as lock:
1306 lock.write_lock()
1307 if not cros_sdk_lib.MountChroot(options.chroot, create=True):
1308 cros_build_lib.Die(
1309 "Unable to mount %s on chroot",
1310 _ImageFileForChroot(options.chroot),
1311 )
1312 logging.notice(
1313 "Mounted %s on chroot", _ImageFileForChroot(options.chroot)
1314 )
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001315
Alex Klein1699fab2022-09-08 08:46:06 -06001316 # Snapshot operations will always need the VG/LV, but other actions won't.
1317 if any_snapshot_operation:
1318 with locking.FileLock(lock_path, "chroot lock") as lock:
1319 chroot_vg, chroot_lv = cros_sdk_lib.FindChrootMountSource(
1320 options.chroot
1321 )
1322 if not chroot_vg or not chroot_lv:
1323 cros_build_lib.Die(
1324 "Unable to find VG/LV for chroot %s", options.chroot
1325 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001326
Alex Kleinef517832023-01-13 12:06:51 -07001327 # Delete snapshot before creating a new one. This allows the user to
1328 # throw out old state, create a new snapshot, and enter the chroot
1329 # in a single call to cros_sdk. Since restore involves deleting,
1330 # also do it before creating.
Alex Klein1699fab2022-09-08 08:46:06 -06001331 if options.snapshot_restore:
1332 lock.write_lock()
1333 valid_snapshots = ListChrootSnapshots(chroot_vg, chroot_lv)
1334 if options.snapshot_restore not in valid_snapshots:
1335 cros_build_lib.Die(
Alex Kleinef517832023-01-13 12:06:51 -07001336 "%s is not a valid snapshot to restore to. "
1337 "Valid snapshots: %s",
Alex Klein1699fab2022-09-08 08:46:06 -06001338 options.snapshot_restore,
1339 ", ".join(valid_snapshots),
1340 )
1341 osutils.UmountTree(options.chroot)
1342 if not RestoreChrootSnapshot(
1343 options.snapshot_restore, chroot_vg, chroot_lv
1344 ):
1345 cros_build_lib.Die("Unable to restore chroot to snapshot.")
1346 if not cros_sdk_lib.MountChroot(options.chroot, create=False):
1347 cros_build_lib.Die(
1348 "Unable to mount restored snapshot onto chroot."
1349 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001350
Alex Kleinef517832023-01-13 12:06:51 -07001351 # Use a read lock for snapshot delete and create even though they
1352 # modify the filesystem, because they don't modify the mounted
1353 # chroot itself. The underlying LVM commands take their own locks,
1354 # so conflicting concurrent operations here may crash cros_sdk, but
1355 # won't corrupt the chroot image. This tradeoff seems worth it to
1356 # allow snapshot operations on chroots that have a process inside.
Alex Klein1699fab2022-09-08 08:46:06 -06001357 if options.snapshot_delete:
1358 lock.read_lock()
1359 DeleteChrootSnapshot(
1360 options.snapshot_delete, chroot_vg, chroot_lv
1361 )
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001362
Alex Klein1699fab2022-09-08 08:46:06 -06001363 if options.snapshot_create:
1364 lock.read_lock()
1365 if not CreateChrootSnapshot(
1366 options.snapshot_create, chroot_vg, chroot_lv
1367 ):
1368 cros_build_lib.Die("Unable to create snapshot.")
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001369
Alex Klein1699fab2022-09-08 08:46:06 -06001370 img_path = _ImageFileForChroot(options.chroot)
1371 if (
1372 options.use_image
1373 and os.path.exists(options.chroot)
1374 and os.path.exists(img_path)
1375 ):
1376 img_stat = os.stat(img_path)
1377 img_used_bytes = img_stat.st_blocks * 512
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001378
Alex Klein1699fab2022-09-08 08:46:06 -06001379 mount_stat = os.statvfs(options.chroot)
1380 mount_used_bytes = mount_stat.f_frsize * (
1381 mount_stat.f_blocks - mount_stat.f_bfree
1382 )
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001383
Alex Klein1699fab2022-09-08 08:46:06 -06001384 extra_gbs = (img_used_bytes - mount_used_bytes) // 2**30
1385 if extra_gbs > MAX_UNUSED_IMAGE_GBS:
1386 logging.notice(
1387 "%s is using %s GiB more than needed. Running "
1388 "fstrim in background.",
1389 img_path,
1390 extra_gbs,
1391 )
1392 pid = os.fork()
1393 if pid == 0:
1394 try:
1395 # Directly call Popen to run fstrim concurrently.
1396 cmd = ["fstrim", options.chroot]
1397 subprocess.Popen(cmd, close_fds=True, shell=False)
1398 except subprocess.SubprocessError as e:
1399 logging.warning(
1400 "Running fstrim failed. Consider running fstrim on "
1401 "your chroot manually.\n%s",
1402 e,
1403 )
1404 os._exit(0) # pylint: disable=protected-access
1405 os.waitpid(pid, 0)
Benjamin Gordone3d5bd12017-11-16 15:42:28 -07001406
Alex Kleinef517832023-01-13 12:06:51 -07001407 # Enter a new set of namespaces. Everything after here cannot directly
1408 # affect the hosts's mounts or alter LVM volumes.
Alex Klein1699fab2022-09-08 08:46:06 -06001409 namespaces.SimpleUnshare(net=options.ns_net, pid=options.ns_pid)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -06001410
Alex Klein1699fab2022-09-08 08:46:06 -06001411 if options.snapshot_list:
1412 for snap in ListChrootSnapshots(chroot_vg, chroot_lv):
1413 print(snap)
1414 sys.exit(0)
Benjamin Gordon2d7bf582017-07-12 10:11:26 -06001415
Alex Klein1699fab2022-09-08 08:46:06 -06001416 if not options.sdk_version:
1417 sdk_version = (
1418 bootstrap_latest_version
1419 if options.bootstrap
1420 else sdk_latest_version
1421 )
Yong Hong4e29b622018-02-05 14:31:10 +08001422 else:
Alex Klein1699fab2022-09-08 08:46:06 -06001423 sdk_version = options.sdk_version
1424 if options.buildbot_log_version:
1425 cbuildbot_alerts.PrintBuildbotStepText(sdk_version)
Brian Harring1790ac42012-09-23 08:53:33 -07001426
Alex Klein1699fab2022-09-08 08:46:06 -06001427 # Based on selections, determine the tarball to fetch.
Alex Klein22690a12022-11-17 10:56:09 -07001428 urls = []
Mike Frysingerbf47cce2021-01-20 13:46:30 -05001429 if options.download:
Alex Klein1699fab2022-09-08 08:46:06 -06001430 if options.sdk_url:
1431 urls = [options.sdk_url]
1432 else:
1433 urls = GetArchStageTarballs(sdk_version)
Brian Harring218e13c2012-10-10 16:21:26 -07001434
Alex Klein1699fab2022-09-08 08:46:06 -06001435 with locking.FileLock(lock_path, "chroot lock") as lock:
1436 if options.proxy_sim:
1437 _ProxySimSetup(options)
Brian Harring1790ac42012-09-23 08:53:33 -07001438
Alex Klein1699fab2022-09-08 08:46:06 -06001439 sdk_cache = os.path.join(options.cache_dir, "sdks")
1440 distfiles_cache = os.path.join(options.cache_dir, "distfiles")
1441 osutils.SafeMakedirsNonRoot(options.cache_dir)
1442
1443 for target in (sdk_cache, distfiles_cache):
1444 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
1445 if not os.path.exists(src):
1446 osutils.SafeMakedirsNonRoot(target)
1447 continue
1448 lock.write_lock(
1449 "Upgrade to %r needed but chroot is locked; please exit "
1450 "all instances so this upgrade can finish." % src
1451 )
1452 if not os.path.exists(src):
Alex Kleinef517832023-01-13 12:06:51 -07001453 # Note that while waiting for the write lock, src may've
1454 # vanished; it's a rare race during the upgrade process that's a
1455 # byproduct of us avoiding taking a write lock to do the src
1456 # check. If we took a write lock for that check, it would
1457 # effectively limit all cros_sdk for a chroot to a single
1458 # instance.
Alex Klein1699fab2022-09-08 08:46:06 -06001459 osutils.SafeMakedirsNonRoot(target)
1460 elif not os.path.exists(target):
1461 # Upgrade occurred, but a reversion, or something whacky
1462 # occurred writing to the old location. Wipe and continue.
1463 os.rename(src, target)
1464 else:
1465 # Upgrade occurred once already, but either a reversion or
1466 # some before/after separate cros_sdk usage is at play.
1467 # Wipe and continue.
1468 osutils.RmDir(src)
1469
Alex Klein1699fab2022-09-08 08:46:06 -06001470 mounted = False
1471 if options.create:
1472 lock.write_lock()
Alex Kleinef517832023-01-13 12:06:51 -07001473 # Recheck if the chroot is set up here before creating to make sure
1474 # we account for whatever the various delete/unmount/remount steps
1475 # above have done.
Alex Klein1699fab2022-09-08 08:46:06 -06001476 if cros_sdk_lib.IsChrootReady(options.chroot):
1477 logging.debug("Chroot already exists. Skipping creation.")
1478 else:
Alex Klein22690a12022-11-17 10:56:09 -07001479 sdk_tarball = FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001480 cros_sdk_lib.CreateChroot(
1481 Path(options.chroot),
1482 Path(sdk_tarball),
1483 Path(options.cache_dir),
1484 usepkg=not options.bootstrap and not options.nousepkg,
1485 chroot_upgrade=options.chroot_upgrade,
1486 )
1487 mounted = True
Alex Klein22690a12022-11-17 10:56:09 -07001488 elif options.download:
1489 # Allow downloading only.
1490 lock.write_lock()
1491 FetchRemoteTarballs(sdk_cache, urls)
Alex Klein1699fab2022-09-08 08:46:06 -06001492
1493 if options.enter:
1494 lock.read_lock()
1495 if not mounted:
1496 cros_sdk_lib.MountChrootPaths(options.chroot)
1497 ret = EnterChroot(
1498 options.chroot,
1499 options.cache_dir,
1500 options.chrome_root,
1501 options.chrome_root_mount,
Mike Frysinger9a5124e2023-01-26 17:16:44 -05001502 goma,
Alex Klein1699fab2022-09-08 08:46:06 -06001503 options.reclient_dir,
1504 options.reproxy_cfg_file,
1505 options.working_dir,
Mike Frysinger114a7b92023-01-26 19:08:57 -05001506 options.commands,
Alex Klein1699fab2022-09-08 08:46:06 -06001507 )
1508 sys.exit(ret.returncode)