blob: 9a7ddcf702d6e6d7ce2e259a8c0a6b12dd07e38e [file] [log] [blame]
Mike Frysinger2de7f042012-07-10 04:45:03 -04001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
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.
4
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 Frysinger383367e2014-09-16 15:06:17 -040014from __future__ import print_function
15
Mike Frysinger2f95cfc2015-06-04 04:00:26 -040016import argparse
Josh Triplett472a4182013-03-08 11:48:57 -080017import glob
Brian Harringb938c782012-02-29 15:14:38 -080018import os
Josh Triplett472a4182013-03-08 11:48:57 -080019import pwd
Brian Norrisd37e2f72016-08-22 16:09:24 -070020import re
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -070021import resource
David James56e6c2c2012-10-24 23:54:41 -070022import sys
Brian Harringb938c782012-02-29 15:14:38 -080023import urlparse
24
Aviv Keshetb7519e12016-10-04 00:50:00 -070025from chromite.lib import constants
Brian Harringcfe762a2012-02-29 13:03:53 -080026from chromite.lib import cgroups
Brian Harringb6cf9142012-09-01 20:43:17 -070027from chromite.lib import commandline
Brian Harringb938c782012-02-29 15:14:38 -080028from chromite.lib import cros_build_lib
Ralph Nathan59900422015-03-24 10:41:17 -070029from chromite.lib import cros_logging as logging
Brian Harringb938c782012-02-29 15:14:38 -080030from chromite.lib import locking
Josh Triplette759b232013-03-08 13:03:43 -080031from chromite.lib import namespaces
Brian Harringae0a5322012-09-15 01:46:51 -070032from chromite.lib import osutils
Mike Frysingere2d8f0d2014-11-01 13:09:26 -040033from chromite.lib import process_util
David Jamesc93e6a4d2014-01-13 11:37:36 -080034from chromite.lib import retry_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050035from chromite.lib import toolchain
Brian Harringb938c782012-02-29 15:14:38 -080036
37cros_build_lib.STRICT_SUDO = True
38
39
Zdenek Behanaa52cea2012-05-30 01:31:11 +020040COMPRESSION_PREFERENCE = ('xz', 'bz2')
Zdenek Behanfd0efe42012-04-13 04:36:40 +020041
Brian Harringb938c782012-02-29 15:14:38 -080042# TODO(zbehan): Remove the dependency on these, reimplement them in python
Mike Frysinger648ba2d2013-01-08 14:19:34 -050043MAKE_CHROOT = [os.path.join(constants.SOURCE_ROOT,
44 'src/scripts/sdk_lib/make_chroot.sh')]
45ENTER_CHROOT = [os.path.join(constants.SOURCE_ROOT,
46 'src/scripts/sdk_lib/enter_chroot.sh')]
Brian Harringb938c782012-02-29 15:14:38 -080047
Josh Triplett472a4182013-03-08 11:48:57 -080048# Proxy simulator configuration.
49PROXY_HOST_IP = '192.168.240.1'
50PROXY_PORT = 8080
51PROXY_GUEST_IP = '192.168.240.2'
52PROXY_NETMASK = 30
53PROXY_VETH_PREFIX = 'veth'
54PROXY_CONNECT_PORTS = (80, 443, 9418)
55PROXY_APACHE_FALLBACK_USERS = ('www-data', 'apache', 'nobody')
56PROXY_APACHE_MPMS = ('event', 'worker', 'prefork')
57PROXY_APACHE_FALLBACK_PATH = ':'.join(
58 '/usr/lib/apache2/mpm-%s' % mpm for mpm in PROXY_APACHE_MPMS
59)
60PROXY_APACHE_MODULE_GLOBS = ('/usr/lib*/apache2/modules', '/usr/lib*/apache2')
61
Josh Triplett9a495f62013-03-15 18:06:55 -070062# We need these tools to run. Very common tools (tar,..) are omitted.
Josh Triplette759b232013-03-08 13:03:43 -080063NEEDED_TOOLS = ('curl', 'xz')
Brian Harringb938c782012-02-29 15:14:38 -080064
Josh Triplett472a4182013-03-08 11:48:57 -080065# Tools needed for --proxy-sim only.
66PROXY_NEEDED_TOOLS = ('ip',)
Brian Harringb938c782012-02-29 15:14:38 -080067
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060068# Tools needed when use_image is true (the default).
69IMAGE_NEEDED_TOOLS = ('losetup', 'lvchange', 'lvcreate', 'lvs', 'mke2fs',
Benjamin Gordoncfa9c162017-08-03 13:49:29 -060070 'pvscan', 'thin_check', 'vgchange', 'vgcreate', 'vgs')
Benjamin Gordon386b9eb2017-07-20 09:21:33 -060071
Mike Frysingercc838832014-05-24 13:10:30 -040072
Brian Harring1790ac42012-09-23 08:53:33 -070073def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080074 """Returns the URL for a given arch/version"""
Brian Harring1790ac42012-09-23 08:53:33 -070075 extension = {'bz2':'tbz2', 'xz':'tar.xz'}
Mike Frysinger8e727a32013-01-16 16:57:53 -050076 return [toolchain.GetSdkURL(suburl='cros-sdk-%s.%s'
77 % (version, extension[compressor]))
Brian Harring1790ac42012-09-23 08:53:33 -070078 for compressor in COMPRESSION_PREFERENCE]
79
80
81def GetStage3Urls(version):
Mike Frysinger8e727a32013-01-16 16:57:53 -050082 return [toolchain.GetSdkURL(suburl='stage3-amd64-%s.tar.%s' % (version, ext))
Brian Harring1790ac42012-09-23 08:53:33 -070083 for ext in COMPRESSION_PREFERENCE]
Brian Harringb938c782012-02-29 15:14:38 -080084
85
Gilad Arnold6a8f0452015-06-04 11:25:18 -070086def GetToolchainsOverlayUrls(version, toolchains):
87 """Returns the URL(s) for a toolchains SDK overlay.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -070088
89 Args:
90 version: The SDK version used, e.g. 2015.05.27.145939. We use the year and
91 month components to point to a subdirectory on the SDK bucket where
Gilad Arnold6a8f0452015-06-04 11:25:18 -070092 overlays are stored (.../2015/05/ in this case).
93 toolchains: Iterable of toolchain target strings (e.g. 'i686-pc-linux-gnu').
Gilad Arnoldecc86fa2015-05-22 12:06:04 -070094
95 Returns:
Gilad Arnold6a8f0452015-06-04 11:25:18 -070096 List of alternative download URLs for an SDK overlay tarball that contains
97 the given toolchains.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -070098 """
Gilad Arnold6a8f0452015-06-04 11:25:18 -070099 toolchains_desc = '-'.join(sorted(toolchains))
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700100 suburl_template = os.path.join(
101 *(version.split('.')[:2] +
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700102 ['cros-sdk-overlay-toolchains-%s-%s.tar.%%s' %
103 (toolchains_desc, version)]))
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700104 return [toolchain.GetSdkURL(suburl=suburl_template % ext)
105 for ext in COMPRESSION_PREFERENCE]
106
107
108def FetchRemoteTarballs(storage_dir, urls, desc, allow_none=False):
Mike Frysinger34db8692013-11-11 14:54:08 -0500109 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200110
111 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500112 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200113 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700114 desc: A string describing what tarball we're downloading (for logging).
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700115 allow_none: Don't fail if none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200116
117 Returns:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700118 Full path to the downloaded file, or None if |allow_none| and no URL worked.
119
120 Raises:
121 ValueError: If |allow_none| is False and none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200122 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200123
Brian Harring1790ac42012-09-23 08:53:33 -0700124 # Note we track content length ourselves since certain versions of curl
125 # fail if asked to resume a complete file.
126 # pylint: disable=C0301,W0631
127 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700128 logging.notice('Downloading %s tarball...', desc)
Brian Norriscf8aef42016-09-27 10:43:39 -0700129 status_re = re.compile(r'^HTTP/[0-9]+(\.[0-9]+)? 200')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200130 for url in urls:
Brian Harring1790ac42012-09-23 08:53:33 -0700131 # http://www.logilab.org/ticket/8766
132 # pylint: disable=E1101
133 parsed = urlparse.urlparse(url)
134 tarball_name = os.path.basename(parsed.path)
135 if parsed.scheme in ('', 'file'):
136 if os.path.exists(parsed.path):
137 return parsed.path
138 continue
139 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700140 logging.debug('Attempting download from %s', url)
David Jamesc93e6a4d2014-01-13 11:37:36 -0800141 result = retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900142 ['-I', url], print_cmd=False, debug_level=logging.NOTICE,
143 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700144 successful = False
145 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700146 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700147 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700148 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700149 successful = True
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500150 elif header.lower().startswith('content-length:'):
151 content_length = int(header.split(':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700152 if successful:
153 break
154 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200155 break
156 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700157 if allow_none:
158 return None
159 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200160
Brian Harringae0a5322012-09-15 01:46:51 -0700161 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700162 current_size = 0
163 if os.path.exists(tarball_dest):
164 current_size = os.path.getsize(tarball_dest)
165 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700166 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700167 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100168
Brian Harring1790ac42012-09-23 08:53:33 -0700169 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800170 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900171 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
172 print_cmd=False, debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800173
Brian Harring1790ac42012-09-23 08:53:33 -0700174 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700175 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
176 # because we might have partial overlap between known prefixes.
177 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
178 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
179 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700180 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700181 if (filename == tarball_name or
182 any([(filename.startswith(p) and
183 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
184 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700185 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700186 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700187 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200188
Brian Harringb938c782012-02-29 15:14:38 -0800189 return tarball_dest
190
191
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700192def CreateChroot(chroot_path, sdk_tarball, toolchains_overlay_tarball,
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600193 cache_dir, nousepkg=False, useimage=False):
Brian Harringb938c782012-02-29 15:14:38 -0800194 """Creates a new chroot from a given SDK"""
Brian Harringb938c782012-02-29 15:14:38 -0800195
Brian Harring1790ac42012-09-23 08:53:33 -0700196 cmd = MAKE_CHROOT + ['--stage3_path', sdk_tarball,
Brian Harringae0a5322012-09-15 01:46:51 -0700197 '--chroot', chroot_path,
198 '--cache_dir', cache_dir]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700199
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700200 if toolchains_overlay_tarball:
201 cmd.extend(['--toolchains_overlay_path', toolchains_overlay_tarball])
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700202
Mike Frysinger2de7f042012-07-10 04:45:03 -0400203 if nousepkg:
204 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800205
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600206 if useimage:
207 cmd.append('--useimage')
208
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700209 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800210 try:
211 cros_build_lib.RunCommand(cmd, print_cmd=False)
212 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700213 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800214
215
216def DeleteChroot(chroot_path):
217 """Deletes an existing chroot"""
218 cmd = MAKE_CHROOT + ['--chroot', chroot_path,
219 '--delete']
220 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700221 logging.notice('Deleting chroot.')
Brian Harringb938c782012-02-29 15:14:38 -0800222 cros_build_lib.RunCommand(cmd, print_cmd=False)
223 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700224 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800225
226
Brian Harringae0a5322012-09-15 01:46:51 -0700227def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900228 workspace, goma_dir, goma_client_json, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800229 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400230 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
231 # The os.ST_NOSUID constant wasn't added until python-3.2.
232 if st.f_flag & 0x2:
233 cros_build_lib.Die('chroot cannot be in a nosuid mount')
234
Brian Harringae0a5322012-09-15 01:46:51 -0700235 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800236 if chrome_root:
237 cmd.extend(['--chrome_root', chrome_root])
238 if chrome_root_mount:
239 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Don Garrett230d1b22015-03-09 16:21:19 -0700240 if workspace:
241 cmd.extend(['--workspace_root', workspace])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900242 if goma_dir:
243 cmd.extend(['--goma_dir', goma_dir])
244 if goma_client_json:
245 cmd.extend(['--goma_client_json', goma_client_json])
Don Garrett230d1b22015-03-09 16:21:19 -0700246
Brian Harringb938c782012-02-29 15:14:38 -0800247 if len(additional_args) > 0:
248 cmd.append('--')
249 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700250
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700251 # ThinLTO opens lots of files at the same time.
252 resource.setrlimit(resource.RLIMIT_NOFILE, (32768, 32768))
Ralph Nathan549d3502015-03-26 17:38:42 -0700253 ret = cros_build_lib.RunCommand(cmd, print_cmd=False, error_code_ok=True,
254 mute_output=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700255 # If we were in interactive mode, ignore the exit code; it'll be whatever
256 # they last ran w/in the chroot and won't matter to us one way or another.
257 # Note this does allow chroot entrance to fail and be ignored during
258 # interactive; this is however a rare case and the user will immediately
259 # see it (nor will they be checking the exit code manually).
260 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700261 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800262
263
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600264def _FindChrootDevice(chroot_path):
265 """Find the VG and LV mounted on chroot_path.
266
267 Returns:
268 A tuple containing the VG and LV names, or (None, None) if an appropriately-
269 name device mounted on |chroot_path| isn't found.
270 """
271
272 mount = [m for m in osutils.IterateMountPoints()
273 if m.destination == chroot_path]
274 if not mount:
275 return (None, None)
276
277 # Take the last mount entry because it's the one currently visible.
278 mount_source = mount[-1].source
279 match = re.match(r'/dev.*/(cros[^-]*)-(.*)', mount_source)
280 if not match:
281 return (None, None)
282
283 return (match.group(1), match.group(2))
284
285
286def _FindSubmounts(*args):
287 """Find all mounts matching each of the paths in |args| and any submounts.
288
289 Returns:
290 A list of all matching mounts in the order found in /proc/mounts.
291 """
292 mounts = []
293 paths = [p.rstrip('/') for p in args]
294 for mtab in osutils.IterateMountPoints():
295 for path in paths:
296 if mtab.destination == path or mtab.destination.startswith(path + '/'):
297 mounts.append(mtab.destination)
298 break
299
300 return mounts
301
302
David James56e6c2c2012-10-24 23:54:41 -0700303def _SudoCommand():
304 """Get the 'sudo' command, along with all needed environment variables."""
305
David James5a73b4d2013-03-07 10:23:40 -0800306 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
307 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700308 cmd = ['sudo']
David James5a73b4d2013-03-07 10:23:40 -0800309 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700310 value = os.environ.get(key)
311 if value is not None:
312 cmd += ['%s=%s' % (key, value)]
313
314 # Pass in the path to the depot_tools so that users can access them from
315 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400316 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500317
David James56e6c2c2012-10-24 23:54:41 -0700318 return cmd
319
320
Josh Triplett472a4182013-03-08 11:48:57 -0800321def _ReportMissing(missing):
322 """Report missing utilities, then exit.
323
324 Args:
325 missing: List of missing utilities, as returned by
326 osutils.FindMissingBinaries. If non-empty, will not return.
327 """
328
329 if missing:
330 raise SystemExit(
331 'The tool(s) %s were not found.\n'
332 'Please install the appropriate package in your host.\n'
333 'Example(ubuntu):\n'
334 ' sudo apt-get install <packagename>'
335 % ', '.join(missing))
336
337
338def _ProxySimSetup(options):
339 """Set up proxy simulator, and return only in the child environment.
340
341 TODO: Ideally, this should support multiple concurrent invocations of
342 cros_sdk --proxy-sim; currently, such invocations will conflict with each
343 other due to the veth device names and IP addresses. Either this code would
344 need to generate fresh, unused names for all of these before forking, or it
345 would need to support multiple concurrent cros_sdk invocations sharing one
346 proxy and allowing it to exit when unused (without counting on any local
347 service-management infrastructure on the host).
348 """
349
350 may_need_mpm = False
351 apache_bin = osutils.Which('apache2')
352 if apache_bin is None:
353 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
354 if apache_bin is None:
355 _ReportMissing(('apache2',))
356 else:
357 may_need_mpm = True
358
359 # Module names and .so names included for ease of grepping.
360 apache_modules = [('proxy_module', 'mod_proxy.so'),
361 ('proxy_connect_module', 'mod_proxy_connect.so'),
362 ('proxy_http_module', 'mod_proxy_http.so'),
363 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
364
365 # Find the apache module directory, and make sure it has the modules we need.
366 module_dirs = {}
367 for g in PROXY_APACHE_MODULE_GLOBS:
368 for mod, so in apache_modules:
369 for f in glob.glob(os.path.join(g, so)):
370 module_dirs.setdefault(os.path.dirname(f), []).append(so)
371 for apache_module_path, modules_found in module_dirs.iteritems():
372 if len(modules_found) == len(apache_modules):
373 break
374 else:
375 # Appease cros lint, which doesn't understand that this else block will not
376 # fall through to the subsequent code which relies on apache_module_path.
377 apache_module_path = None
378 raise SystemExit(
379 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500380 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800381
382 def check_add_module(name):
383 so = 'mod_%s.so' % name
384 if os.access(os.path.join(apache_module_path, so), os.F_OK):
385 mod = '%s_module' % name
386 apache_modules.append((mod, so))
387 return True
388 return False
389
390 check_add_module('authz_core')
391 if may_need_mpm:
392 for mpm in PROXY_APACHE_MPMS:
393 if check_add_module('mpm_%s' % mpm):
394 break
395
396 veth_host = '%s-host' % PROXY_VETH_PREFIX
397 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
398
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500399 # Set up locks to sync the net namespace setup. We need the child to create
400 # the net ns first, and then have the parent assign the guest end of the veth
401 # interface to the child's new network namespace & bring up the proxy. Only
402 # then can the child move forward and rely on the network being up.
403 ns_create_lock = locking.PipeLock()
404 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800405
406 pid = os.fork()
407 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500408 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800409 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500410
411 # Signal the parent the ns is ready to be configured.
412 ns_create_lock.Post()
413 del ns_create_lock
414
415 # Wait for the parent to finish setting up the ns/proxy.
416 ns_setup_lock.Wait()
417 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800418
419 # Set up child side of the network.
420 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500421 ('ip', 'link', 'set', 'up', 'lo'),
422 ('ip', 'address', 'add',
423 '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
424 'dev', veth_guest),
425 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800426 )
427 try:
428 for cmd in commands:
429 cros_build_lib.RunCommand(cmd, print_cmd=False)
430 except cros_build_lib.RunCommandError:
431 raise SystemExit('Running %r failed!' % (cmd,))
432
433 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
434 for proto in ('http', 'https', 'ftp'):
435 os.environ[proto + '_proxy'] = proxy_url
436 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
437 os.environ.pop(v, None)
438 return
439
Josh Triplett472a4182013-03-08 11:48:57 -0800440 # Set up parent side of the network.
441 uid = int(os.environ.get('SUDO_UID', '0'))
442 gid = int(os.environ.get('SUDO_GID', '0'))
443 if uid == 0 or gid == 0:
444 for username in PROXY_APACHE_FALLBACK_USERS:
445 try:
446 pwnam = pwd.getpwnam(username)
447 uid, gid = pwnam.pw_uid, pwnam.pw_gid
448 break
449 except KeyError:
450 continue
451 if uid == 0 or gid == 0:
452 raise SystemExit('Could not find a non-root user to run Apache as')
453
454 chroot_parent, chroot_base = os.path.split(options.chroot)
455 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
456 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
457
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500458 # Wait for the child to create the net ns.
459 ns_create_lock.Wait()
460 del ns_create_lock
461
Josh Triplett472a4182013-03-08 11:48:57 -0800462 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500463 'User #%u' % uid,
464 'Group #%u' % gid,
465 'PidFile %s' % pid_file,
466 'ErrorLog %s' % log_file,
467 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
468 'ServerName %s' % PROXY_HOST_IP,
469 'ProxyRequests On',
470 'AllowCONNECT %s' % ' '.join(map(str, PROXY_CONNECT_PORTS)),
Josh Triplett472a4182013-03-08 11:48:57 -0800471 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500472 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
473 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800474 ]
475 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500476 ('ip', 'link', 'add', 'name', veth_host,
477 'type', 'veth', 'peer', 'name', veth_guest),
478 ('ip', 'address', 'add',
479 '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK),
480 'dev', veth_host),
481 ('ip', 'link', 'set', veth_host, 'up'),
482 ([apache_bin, '-f', '/dev/null'] +
483 [arg for d in apache_directives for arg in ('-C', d)]),
484 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800485 )
486 cmd = None # Make cros lint happy.
487 try:
488 for cmd in commands:
489 cros_build_lib.RunCommand(cmd, print_cmd=False)
490 except cros_build_lib.RunCommandError:
491 # Clean up existing interfaces, if any.
492 cmd_cleanup = ('ip', 'link', 'del', veth_host)
493 try:
494 cros_build_lib.RunCommand(cmd_cleanup, print_cmd=False)
495 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700496 logging.error('running %r failed', cmd_cleanup)
Josh Triplett472a4182013-03-08 11:48:57 -0800497 raise SystemExit('Running %r failed!' % (cmd,))
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500498
499 # Signal the child that the net ns/proxy is fully configured now.
500 ns_setup_lock.Post()
501 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800502
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400503 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800504
505
Mike Frysingera78a56e2012-11-20 06:02:30 -0500506def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700507 """Re-execute cros_sdk as root.
508
509 Also unshare the mount namespace so as to ensure that processes outside
510 the chroot can't mess with our mounts.
511 """
512 if os.geteuid() != 0:
Mike Frysingera78a56e2012-11-20 06:02:30 -0500513 cmd = _SudoCommand() + ['--'] + argv
514 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500515 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400516 # We must set up the cgroups mounts before we enter our own namespace.
517 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800518 cgroups.Cgroup.InitSystem()
David James56e6c2c2012-10-24 23:54:41 -0700519
520
Mike Frysinger34db8692013-11-11 14:54:08 -0500521def _CreateParser(sdk_latest_version, bootstrap_latest_version):
522 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400523 usage = ('usage: %(prog)s [options] '
524 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
525 parser = commandline.ArgumentParser(usage=usage, description=__doc__,
526 caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700527
Mike Frysinger34db8692013-11-11 14:54:08 -0500528 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500529 default_chroot = os.path.join(constants.SOURCE_ROOT,
530 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400531 parser.add_argument(
Brian Harring218e13c2012-10-10 16:21:26 -0700532 '--chroot', dest='chroot', default=default_chroot, type='path',
533 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600534 parser.add_argument('--nouse-image', dest='use_image', action='store_false',
535 default=True,
536 help='Do not mount the chroot on a loopback image; '
537 'instead, create it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800538
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400539 parser.add_argument('--chrome_root', type='path',
540 help='Mount this chrome root into the SDK chroot')
541 parser.add_argument('--chrome_root_mount', type='path',
542 help='Mount chrome into this path inside SDK chroot')
543 parser.add_argument('--nousepkg', action='store_true', default=False,
544 help='Do not use binary packages when creating a chroot.')
545 parser.add_argument('-u', '--url', dest='sdk_url',
546 help='Use sdk tarball located at this url. Use file:// '
547 'for local files.')
548 parser.add_argument('--sdk-version',
549 help=('Use this sdk version. For prebuilt, current is %r'
550 ', for bootstrapping it is %r.'
551 % (sdk_latest_version, bootstrap_latest_version)))
552 parser.add_argument('--workspace',
553 help='Workspace directory to mount into the chroot.')
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900554 parser.add_argument('--goma_dir', type='path',
555 help='Goma installed directory to mount into the chroot.')
556 parser.add_argument('--goma_client_json', type='path',
557 help='Service account json file to use goma on bot. '
558 'Mounted into the chroot.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400559 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500560
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700561 # SDK overlay tarball options (mutually exclusive).
562 group = parser.add_mutually_exclusive_group()
563 group.add_argument('--toolchains',
564 help=('Comma-separated list of toolchains we expect to be '
565 'using on the chroot. Used for downloading a '
566 'corresponding SDK toolchains group (if one is '
567 'found), which may speed up chroot initialization '
568 'when building for the first time. Otherwise this '
569 'has no effect and will not restrict the chroot in '
570 'any way. Ignored if using --bootstrap.'))
571 group.add_argument('--board',
572 help=('The board we intend to be building in the chroot. '
573 'Used for deriving the list of required toolchains '
574 '(see --toolchains).'))
575
Mike Frysinger34db8692013-11-11 14:54:08 -0500576 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400577 group = parser.add_argument_group('Commands')
578 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500579 '--enter', action='store_true', default=False,
580 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400581 group.add_argument(
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500582 '--create', action='store_true', default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500583 help='Create the chroot only if it does not already exist. '
584 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400585 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500586 '--bootstrap', action='store_true', default=False,
587 help='Build everything from scratch, including the sdk. '
588 'Use this only if you need to validate a change '
589 'that affects SDK creation itself (toolchain and '
590 'build are typically the only folk who need this). '
591 'Note this will quite heavily slow down the build. '
592 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400593 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500594 '-r', '--replace', action='store_true', default=False,
595 help='Replace an existing SDK chroot. Basically an alias '
596 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400597 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500598 '--delete', action='store_true', default=False,
599 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400600 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500601 '--download', action='store_true', default=False,
602 help='Download the sdk.')
603 commands = group
604
Mike Frysinger80dfce92014-04-21 10:58:53 -0400605 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400606 group = parser.add_argument_group('Namespaces')
607 group.add_argument('--proxy-sim', action='store_true', default=False,
608 help='Simulate a restrictive network requiring an outbound'
609 ' proxy.')
610 group.add_argument('--no-ns-pid', dest='ns_pid',
611 default=True, action='store_false',
612 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400613
Mike Frysinger34db8692013-11-11 14:54:08 -0500614 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400615 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500616 'Internal Chromium OS Build Team Options',
617 'Caution: these are for meant for the Chromium OS build team only')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400618 group.add_argument('--buildbot-log-version', default=False,
619 action='store_true',
620 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500621
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400622 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500623
624
625def main(argv):
626 conf = cros_build_lib.LoadKeyValueFile(
627 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
628 ignore_missing=True)
629 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
630 bootstrap_latest_version = conf.get('BOOTSTRAP_LATEST_VERSION', '<unknown>')
631 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400632 options = parser.parse_args(argv)
633 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800634
635 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500636 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800637
Brian Harring1790ac42012-09-23 08:53:33 -0700638 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700639 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600640 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700641 "cros_sdk is currently only supported on x86_64; you're running"
642 " %s. Please find a x86_64 machine." % (host,))
643
Josh Triplett472a4182013-03-08 11:48:57 -0800644 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
645 if options.proxy_sim:
646 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Brian Harringb938c782012-02-29 15:14:38 -0800647
Benjamin Gordon040a1162017-06-29 13:44:47 -0600648 if (sdk_latest_version == '<unknown>' or
649 bootstrap_latest_version == '<unknown>'):
650 cros_build_lib.Die(
651 'No SDK version was found. '
652 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
653 'Please change to a directory inside your Chromium OS source tree\n'
654 'and retry. If you need to setup a Chromium OS source tree, see\n'
655 ' http://www.chromium.org/chromium-os/developer-guide')
656
David James471532c2013-01-21 10:23:31 -0800657 _ReExecuteIfNeeded([sys.argv[0]] + argv)
658
Brian Harring218e13c2012-10-10 16:21:26 -0700659 # Expand out the aliases...
660 if options.replace:
661 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800662
Brian Harring218e13c2012-10-10 16:21:26 -0700663 if options.bootstrap:
664 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800665
Brian Harring218e13c2012-10-10 16:21:26 -0700666 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400667 # pylint: disable=protected-access
668 # This _group_actions access sucks, but upstream decided to not include an
669 # alternative to optparse's option_list, and this is what they recommend.
Brian Harring218e13c2012-10-10 16:21:26 -0700670 options.enter |= not any(getattr(options, x.dest)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400671 for x in commands._group_actions)
672 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700673 options.enter |= bool(chroot_command)
674
675 if options.enter and options.delete and not options.create:
676 parser.error("Trying to enter the chroot when --delete "
677 "was specified makes no sense.")
678
Benjamin Gordon35194f12017-07-19 10:26:22 -0600679 # Discern if we need to create the chroot.
680 chroot_exists = os.path.exists(os.path.join(
681 options.chroot, 'etc', 'cros_chroot_version'))
Brian Harring218e13c2012-10-10 16:21:26 -0700682 if options.create or options.enter:
683 # Only create if it's being wiped, or if it doesn't exist.
684 if not options.delete and chroot_exists:
685 options.create = False
686 else:
687 options.download = True
688
689 # Finally, flip create if necessary.
690 if options.enter:
691 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800692
Benjamin Gordoncfa9c162017-08-03 13:49:29 -0600693 # If we're going to be using a loopback image for the chroot, we need
694 # LVM tools installed. We can skip the check if something that looks
695 # like a chroot is already visible in the expected place, since we won't
696 # actually call any LVM commands in that case.
697 if options.use_image and options.create:
698 missing = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
699 if missing:
700 raise SystemExit(
701 'The tool(s) %s were not found.\n'
702 'Please make sure the lvm2 and thin-provisioning-tools packages '
703 'are installed on your host.\n'
704 'Example(ubuntu):\n'
705 ' sudo apt-get install lvm2 thin-provisioning-tools\n\n'
706 'If you want to run without lvm2, pass --nouse-image (chroot '
707 'snapshots will be unavailable).'
708 % ', '.join(missing))
709
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600710 # This dance is to support mounting the chroot inside a separate mount
711 # namespace. While we're here in the original namespace, we set up a
712 # temporary shared subtree
713 # (https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) and
714 # then let CreateChroot operate inside there. Since it's shared, we can see
715 # the mount that gets created. make_chroot.sh marks its mount private inside
716 # the namespace, so none of the inner mounts leak out. After the chroot has
717 # been created, we will return to this mount namespace (using an fd that we
718 # open here and save) and move the mount from the temporary spot to the
719 # requested final location. Once this is done, future uses of the same chroot
720 # don't have to jump through any of these hoops until it gets unmounted. If
721 # we're running on a system where things are mounted shared by default then
722 # all this isn't necessary, but it seems safer to assume we need this setup
723 # rather than try to detect it.
724 if options.use_image:
725 chroot_temp_parent = options.chroot + '.build'
726 chroot_temp_mount = os.path.join(chroot_temp_parent, 'chroot')
727 already_mounted = [m for m in osutils.IterateMountPoints()
728 if m.destination == chroot_temp_parent]
729 if not already_mounted and options.create:
730 osutils.SafeMakedirsNonRoot(chroot_temp_mount)
731 osutils.Mount(chroot_temp_parent, chroot_temp_parent, '', osutils.MS_BIND)
732 osutils.Mount('', chroot_temp_parent, '', osutils.MS_SHARED)
733 parent_ns_file = open('/proc/%d/ns/mnt' % (os.getppid(),))
734 parent_ns = parent_ns_file.fileno()
735
736 # If we're going to delete, also make sure the chroot isn't mounted
737 # before we enter the new mount namespace.
738 if options.delete:
739 osutils.UmountTree(options.chroot)
740
741 namespaces.SimpleUnshare()
742 if options.ns_pid:
743 first_pid = namespaces.CreatePidNs()
744 else:
745 first_pid = None
746
Brian Harringb938c782012-02-29 15:14:38 -0800747 if not options.sdk_version:
Brian Harring1790ac42012-09-23 08:53:33 -0700748 sdk_version = (bootstrap_latest_version if options.bootstrap
749 else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -0800750 else:
751 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -0500752 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -0700753 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -0800754
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700755 # Based on selections, determine the tarball to fetch.
Brian Harring1790ac42012-09-23 08:53:33 -0700756 if options.sdk_url:
757 urls = [options.sdk_url]
758 elif options.bootstrap:
759 urls = GetStage3Urls(sdk_version)
760 else:
761 urls = GetArchStageTarballs(sdk_version)
762
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700763 # Get URLs for the toolchains overlay, if one is to be used.
764 toolchains_overlay_urls = None
765 if not options.bootstrap:
766 toolchains = None
767 if options.toolchains:
768 toolchains = options.toolchains.split(',')
769 elif options.board:
770 toolchains = toolchain.GetToolchainsForBoard(options.board).keys()
771
772 if toolchains:
773 toolchains_overlay_urls = GetToolchainsOverlayUrls(sdk_version,
774 toolchains)
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700775
Brian Harringb6cf9142012-09-01 20:43:17 -0700776 lock_path = os.path.dirname(options.chroot)
Gilad Arnoldfbe40e22015-03-18 14:52:16 -0700777 lock_path = os.path.join(
778 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
Mike Frysinger80dfce92014-04-21 10:58:53 -0400779 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -0700780 with locking.FileLock(lock_path, 'chroot lock') as lock:
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700781 toolchains_overlay_tarball = None
Brian Harring1790ac42012-09-23 08:53:33 -0700782
Josh Triplett472a4182013-03-08 11:48:57 -0800783 if options.proxy_sim:
784 _ProxySimSetup(options)
785
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600786 if options.delete and (os.path.exists(options.chroot) or
787 os.path.exists(options.chroot + '.img')):
David James56e6c2c2012-10-24 23:54:41 -0700788 lock.write_lock()
789 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -0800790
David James56e6c2c2012-10-24 23:54:41 -0700791 sdk_cache = os.path.join(options.cache_dir, 'sdks')
792 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -0700793 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -0700794
David James56e6c2c2012-10-24 23:54:41 -0700795 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500796 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -0700797 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -0700798 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -0700799 continue
800 lock.write_lock(
801 "Upgrade to %r needed but chroot is locked; please exit "
802 "all instances so this upgrade can finish." % src)
803 if not os.path.exists(src):
804 # Note that while waiting for the write lock, src may've vanished;
805 # it's a rare race during the upgrade process that's a byproduct
806 # of us avoiding taking a write lock to do the src check. If we
807 # took a write lock for that check, it would effectively limit
808 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -0700809 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -0700810 elif not os.path.exists(target):
811 # Upgrade occurred, but a reversion, or something whacky
812 # occurred writing to the old location. Wipe and continue.
813 os.rename(src, target)
814 else:
815 # Upgrade occurred once already, but either a reversion or
816 # some before/after separate cros_sdk usage is at play.
817 # Wipe and continue.
818 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -0700819
David James56e6c2c2012-10-24 23:54:41 -0700820 if options.download:
821 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700822 sdk_tarball = FetchRemoteTarballs(
823 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
824 if toolchains_overlay_urls:
825 toolchains_overlay_tarball = FetchRemoteTarballs(
826 sdk_cache, toolchains_overlay_urls, 'SDK toolchains overlay',
827 allow_none=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700828
David James56e6c2c2012-10-24 23:54:41 -0700829 if options.create:
830 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700831 CreateChroot(options.chroot, sdk_tarball, toolchains_overlay_tarball,
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700832 options.cache_dir,
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600833 nousepkg=(options.bootstrap or options.nousepkg),
834 useimage=options.use_image)
Brian Harring1790ac42012-09-23 08:53:33 -0700835
David James56e6c2c2012-10-24 23:54:41 -0700836 if options.enter:
837 lock.read_lock()
838 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Don Garrett230d1b22015-03-09 16:21:19 -0700839 options.chrome_root_mount, options.workspace,
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900840 options.goma_dir, options.goma_client_json,
Don Garrett230d1b22015-03-09 16:21:19 -0700841 chroot_command)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600842
843 # Remount the inner chroot mount back up to the original namespace. See above
844 # for details.
845 if options.use_image and options.create:
846 vg, lv = _FindChrootDevice(chroot_temp_mount)
847
848 # Clean up inside the child mount namespace. Normally these will disappear
849 # as soon as the last process exits the mount namespace, but we want to be
850 # able to clean up the underlying directories without waiting for the forked
851 # "init" copes of cros_sdk to exit the namespace. This is safe to do even
852 # with multiple processes in the same chroot because the other cros_sdk
853 # copies will have their own mount namespace.
854 chroot_mounts = _FindSubmounts(chroot_temp_parent, options.chroot)
855 osutils.UmountTree(chroot_temp_parent)
856 osutils.UmountTree(options.chroot)
857
858 namespaces.SetNS(parent_ns, 0)
859 chroot_mounts = _FindSubmounts(chroot_temp_parent, options.chroot)
860 if not options.chroot in chroot_mounts:
861 if not vg or not lv:
862 cros_build_lib.Die('Unable to find VG/LV mounted on %s after building '
863 'chroot.' % chroot_temp_mount)
864 osutils.UmountTree(chroot_temp_parent)
865 osutils.RmDir(chroot_temp_parent, ignore_missing=True)
866
867 chroot_dev_path = '/dev/mapper/%s-%s' % (vg, lv)
868 osutils.Mount(chroot_dev_path, options.chroot, 'ext4', osutils.MS_NOATIME)