blob: ce757686c8be084ac94610e3fc196f8ff8e8fe3a [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 _FindSubmounts(*args):
265 """Find all mounts matching each of the paths in |args| and any submounts.
266
267 Returns:
268 A list of all matching mounts in the order found in /proc/mounts.
269 """
270 mounts = []
271 paths = [p.rstrip('/') for p in args]
272 for mtab in osutils.IterateMountPoints():
273 for path in paths:
274 if mtab.destination == path or mtab.destination.startswith(path + '/'):
275 mounts.append(mtab.destination)
276 break
277
278 return mounts
279
280
David James56e6c2c2012-10-24 23:54:41 -0700281def _SudoCommand():
282 """Get the 'sudo' command, along with all needed environment variables."""
283
David James5a73b4d2013-03-07 10:23:40 -0800284 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
285 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700286 cmd = ['sudo']
David James5a73b4d2013-03-07 10:23:40 -0800287 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700288 value = os.environ.get(key)
289 if value is not None:
290 cmd += ['%s=%s' % (key, value)]
291
292 # Pass in the path to the depot_tools so that users can access them from
293 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400294 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500295
David James56e6c2c2012-10-24 23:54:41 -0700296 return cmd
297
298
Josh Triplett472a4182013-03-08 11:48:57 -0800299def _ReportMissing(missing):
300 """Report missing utilities, then exit.
301
302 Args:
303 missing: List of missing utilities, as returned by
304 osutils.FindMissingBinaries. If non-empty, will not return.
305 """
306
307 if missing:
308 raise SystemExit(
309 'The tool(s) %s were not found.\n'
310 'Please install the appropriate package in your host.\n'
311 'Example(ubuntu):\n'
312 ' sudo apt-get install <packagename>'
313 % ', '.join(missing))
314
315
316def _ProxySimSetup(options):
317 """Set up proxy simulator, and return only in the child environment.
318
319 TODO: Ideally, this should support multiple concurrent invocations of
320 cros_sdk --proxy-sim; currently, such invocations will conflict with each
321 other due to the veth device names and IP addresses. Either this code would
322 need to generate fresh, unused names for all of these before forking, or it
323 would need to support multiple concurrent cros_sdk invocations sharing one
324 proxy and allowing it to exit when unused (without counting on any local
325 service-management infrastructure on the host).
326 """
327
328 may_need_mpm = False
329 apache_bin = osutils.Which('apache2')
330 if apache_bin is None:
331 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
332 if apache_bin is None:
333 _ReportMissing(('apache2',))
334 else:
335 may_need_mpm = True
336
337 # Module names and .so names included for ease of grepping.
338 apache_modules = [('proxy_module', 'mod_proxy.so'),
339 ('proxy_connect_module', 'mod_proxy_connect.so'),
340 ('proxy_http_module', 'mod_proxy_http.so'),
341 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
342
343 # Find the apache module directory, and make sure it has the modules we need.
344 module_dirs = {}
345 for g in PROXY_APACHE_MODULE_GLOBS:
346 for mod, so in apache_modules:
347 for f in glob.glob(os.path.join(g, so)):
348 module_dirs.setdefault(os.path.dirname(f), []).append(so)
349 for apache_module_path, modules_found in module_dirs.iteritems():
350 if len(modules_found) == len(apache_modules):
351 break
352 else:
353 # Appease cros lint, which doesn't understand that this else block will not
354 # fall through to the subsequent code which relies on apache_module_path.
355 apache_module_path = None
356 raise SystemExit(
357 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500358 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800359
360 def check_add_module(name):
361 so = 'mod_%s.so' % name
362 if os.access(os.path.join(apache_module_path, so), os.F_OK):
363 mod = '%s_module' % name
364 apache_modules.append((mod, so))
365 return True
366 return False
367
368 check_add_module('authz_core')
369 if may_need_mpm:
370 for mpm in PROXY_APACHE_MPMS:
371 if check_add_module('mpm_%s' % mpm):
372 break
373
374 veth_host = '%s-host' % PROXY_VETH_PREFIX
375 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
376
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500377 # Set up locks to sync the net namespace setup. We need the child to create
378 # the net ns first, and then have the parent assign the guest end of the veth
379 # interface to the child's new network namespace & bring up the proxy. Only
380 # then can the child move forward and rely on the network being up.
381 ns_create_lock = locking.PipeLock()
382 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800383
384 pid = os.fork()
385 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500386 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800387 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500388
389 # Signal the parent the ns is ready to be configured.
390 ns_create_lock.Post()
391 del ns_create_lock
392
393 # Wait for the parent to finish setting up the ns/proxy.
394 ns_setup_lock.Wait()
395 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800396
397 # Set up child side of the network.
398 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500399 ('ip', 'link', 'set', 'up', 'lo'),
400 ('ip', 'address', 'add',
401 '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
402 'dev', veth_guest),
403 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800404 )
405 try:
406 for cmd in commands:
407 cros_build_lib.RunCommand(cmd, print_cmd=False)
408 except cros_build_lib.RunCommandError:
409 raise SystemExit('Running %r failed!' % (cmd,))
410
411 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
412 for proto in ('http', 'https', 'ftp'):
413 os.environ[proto + '_proxy'] = proxy_url
414 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
415 os.environ.pop(v, None)
416 return
417
Josh Triplett472a4182013-03-08 11:48:57 -0800418 # Set up parent side of the network.
419 uid = int(os.environ.get('SUDO_UID', '0'))
420 gid = int(os.environ.get('SUDO_GID', '0'))
421 if uid == 0 or gid == 0:
422 for username in PROXY_APACHE_FALLBACK_USERS:
423 try:
424 pwnam = pwd.getpwnam(username)
425 uid, gid = pwnam.pw_uid, pwnam.pw_gid
426 break
427 except KeyError:
428 continue
429 if uid == 0 or gid == 0:
430 raise SystemExit('Could not find a non-root user to run Apache as')
431
432 chroot_parent, chroot_base = os.path.split(options.chroot)
433 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
434 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
435
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500436 # Wait for the child to create the net ns.
437 ns_create_lock.Wait()
438 del ns_create_lock
439
Josh Triplett472a4182013-03-08 11:48:57 -0800440 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500441 'User #%u' % uid,
442 'Group #%u' % gid,
443 'PidFile %s' % pid_file,
444 'ErrorLog %s' % log_file,
445 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
446 'ServerName %s' % PROXY_HOST_IP,
447 'ProxyRequests On',
448 'AllowCONNECT %s' % ' '.join(map(str, PROXY_CONNECT_PORTS)),
Josh Triplett472a4182013-03-08 11:48:57 -0800449 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500450 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
451 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800452 ]
453 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500454 ('ip', 'link', 'add', 'name', veth_host,
455 'type', 'veth', 'peer', 'name', veth_guest),
456 ('ip', 'address', 'add',
457 '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK),
458 'dev', veth_host),
459 ('ip', 'link', 'set', veth_host, 'up'),
460 ([apache_bin, '-f', '/dev/null'] +
461 [arg for d in apache_directives for arg in ('-C', d)]),
462 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800463 )
464 cmd = None # Make cros lint happy.
465 try:
466 for cmd in commands:
467 cros_build_lib.RunCommand(cmd, print_cmd=False)
468 except cros_build_lib.RunCommandError:
469 # Clean up existing interfaces, if any.
470 cmd_cleanup = ('ip', 'link', 'del', veth_host)
471 try:
472 cros_build_lib.RunCommand(cmd_cleanup, print_cmd=False)
473 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700474 logging.error('running %r failed', cmd_cleanup)
Josh Triplett472a4182013-03-08 11:48:57 -0800475 raise SystemExit('Running %r failed!' % (cmd,))
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500476
477 # Signal the child that the net ns/proxy is fully configured now.
478 ns_setup_lock.Post()
479 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800480
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400481 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800482
483
Mike Frysingera78a56e2012-11-20 06:02:30 -0500484def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700485 """Re-execute cros_sdk as root.
486
487 Also unshare the mount namespace so as to ensure that processes outside
488 the chroot can't mess with our mounts.
489 """
490 if os.geteuid() != 0:
Mike Frysingera78a56e2012-11-20 06:02:30 -0500491 cmd = _SudoCommand() + ['--'] + argv
492 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500493 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400494 # We must set up the cgroups mounts before we enter our own namespace.
495 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800496 cgroups.Cgroup.InitSystem()
David James56e6c2c2012-10-24 23:54:41 -0700497
498
Mike Frysinger34db8692013-11-11 14:54:08 -0500499def _CreateParser(sdk_latest_version, bootstrap_latest_version):
500 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400501 usage = ('usage: %(prog)s [options] '
502 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
503 parser = commandline.ArgumentParser(usage=usage, description=__doc__,
504 caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700505
Mike Frysinger34db8692013-11-11 14:54:08 -0500506 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500507 default_chroot = os.path.join(constants.SOURCE_ROOT,
508 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400509 parser.add_argument(
Brian Harring218e13c2012-10-10 16:21:26 -0700510 '--chroot', dest='chroot', default=default_chroot, type='path',
511 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600512 parser.add_argument('--nouse-image', dest='use_image', action='store_false',
513 default=True,
514 help='Do not mount the chroot on a loopback image; '
515 'instead, create it directly in a directory.')
Brian Harringb938c782012-02-29 15:14:38 -0800516
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400517 parser.add_argument('--chrome_root', type='path',
518 help='Mount this chrome root into the SDK chroot')
519 parser.add_argument('--chrome_root_mount', type='path',
520 help='Mount chrome into this path inside SDK chroot')
521 parser.add_argument('--nousepkg', action='store_true', default=False,
522 help='Do not use binary packages when creating a chroot.')
523 parser.add_argument('-u', '--url', dest='sdk_url',
524 help='Use sdk tarball located at this url. Use file:// '
525 'for local files.')
526 parser.add_argument('--sdk-version',
527 help=('Use this sdk version. For prebuilt, current is %r'
528 ', for bootstrapping it is %r.'
529 % (sdk_latest_version, bootstrap_latest_version)))
530 parser.add_argument('--workspace',
531 help='Workspace directory to mount into the chroot.')
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900532 parser.add_argument('--goma_dir', type='path',
533 help='Goma installed directory to mount into the chroot.')
534 parser.add_argument('--goma_client_json', type='path',
535 help='Service account json file to use goma on bot. '
536 'Mounted into the chroot.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400537 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500538
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700539 # SDK overlay tarball options (mutually exclusive).
540 group = parser.add_mutually_exclusive_group()
541 group.add_argument('--toolchains',
542 help=('Comma-separated list of toolchains we expect to be '
543 'using on the chroot. Used for downloading a '
544 'corresponding SDK toolchains group (if one is '
545 'found), which may speed up chroot initialization '
546 'when building for the first time. Otherwise this '
547 'has no effect and will not restrict the chroot in '
548 'any way. Ignored if using --bootstrap.'))
549 group.add_argument('--board',
550 help=('The board we intend to be building in the chroot. '
551 'Used for deriving the list of required toolchains '
552 '(see --toolchains).'))
553
Mike Frysinger34db8692013-11-11 14:54:08 -0500554 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400555 group = parser.add_argument_group('Commands')
556 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500557 '--enter', action='store_true', default=False,
558 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400559 group.add_argument(
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500560 '--create', action='store_true', default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500561 help='Create the chroot only if it does not already exist. '
562 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400563 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500564 '--bootstrap', action='store_true', default=False,
565 help='Build everything from scratch, including the sdk. '
566 'Use this only if you need to validate a change '
567 'that affects SDK creation itself (toolchain and '
568 'build are typically the only folk who need this). '
569 'Note this will quite heavily slow down the build. '
570 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400571 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500572 '-r', '--replace', action='store_true', default=False,
573 help='Replace an existing SDK chroot. Basically an alias '
574 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400575 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500576 '--delete', action='store_true', default=False,
577 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400578 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500579 '--download', action='store_true', default=False,
580 help='Download the sdk.')
581 commands = group
582
Mike Frysinger80dfce92014-04-21 10:58:53 -0400583 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400584 group = parser.add_argument_group('Namespaces')
585 group.add_argument('--proxy-sim', action='store_true', default=False,
586 help='Simulate a restrictive network requiring an outbound'
587 ' proxy.')
588 group.add_argument('--no-ns-pid', dest='ns_pid',
589 default=True, action='store_false',
590 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400591
Mike Frysinger34db8692013-11-11 14:54:08 -0500592 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400593 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500594 'Internal Chromium OS Build Team Options',
595 'Caution: these are for meant for the Chromium OS build team only')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400596 group.add_argument('--buildbot-log-version', default=False,
597 action='store_true',
598 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500599
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400600 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500601
602
603def main(argv):
604 conf = cros_build_lib.LoadKeyValueFile(
605 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
606 ignore_missing=True)
607 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
608 bootstrap_latest_version = conf.get('BOOTSTRAP_LATEST_VERSION', '<unknown>')
609 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400610 options = parser.parse_args(argv)
611 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800612
613 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500614 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800615
Brian Harring1790ac42012-09-23 08:53:33 -0700616 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700617 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600618 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700619 "cros_sdk is currently only supported on x86_64; you're running"
620 " %s. Please find a x86_64 machine." % (host,))
621
Josh Triplett472a4182013-03-08 11:48:57 -0800622 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
623 if options.proxy_sim:
624 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Brian Harringb938c782012-02-29 15:14:38 -0800625
Benjamin Gordon040a1162017-06-29 13:44:47 -0600626 if (sdk_latest_version == '<unknown>' or
627 bootstrap_latest_version == '<unknown>'):
628 cros_build_lib.Die(
629 'No SDK version was found. '
630 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
631 'Please change to a directory inside your Chromium OS source tree\n'
632 'and retry. If you need to setup a Chromium OS source tree, see\n'
633 ' http://www.chromium.org/chromium-os/developer-guide')
634
David James471532c2013-01-21 10:23:31 -0800635 _ReExecuteIfNeeded([sys.argv[0]] + argv)
636
Brian Harring218e13c2012-10-10 16:21:26 -0700637 # Expand out the aliases...
638 if options.replace:
639 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800640
Brian Harring218e13c2012-10-10 16:21:26 -0700641 if options.bootstrap:
642 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800643
Brian Harring218e13c2012-10-10 16:21:26 -0700644 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400645 # pylint: disable=protected-access
646 # This _group_actions access sucks, but upstream decided to not include an
647 # alternative to optparse's option_list, and this is what they recommend.
Brian Harring218e13c2012-10-10 16:21:26 -0700648 options.enter |= not any(getattr(options, x.dest)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400649 for x in commands._group_actions)
650 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700651 options.enter |= bool(chroot_command)
652
653 if options.enter and options.delete and not options.create:
654 parser.error("Trying to enter the chroot when --delete "
655 "was specified makes no sense.")
656
Benjamin Gordon35194f12017-07-19 10:26:22 -0600657 # Discern if we need to create the chroot.
658 chroot_exists = os.path.exists(os.path.join(
659 options.chroot, 'etc', 'cros_chroot_version'))
Brian Harring218e13c2012-10-10 16:21:26 -0700660 if options.create or options.enter:
661 # Only create if it's being wiped, or if it doesn't exist.
662 if not options.delete and chroot_exists:
663 options.create = False
664 else:
665 options.download = True
666
667 # Finally, flip create if necessary.
668 if options.enter:
669 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800670
Benjamin Gordoncfa9c162017-08-03 13:49:29 -0600671 # If we're going to be using a loopback image for the chroot, we need
672 # LVM tools installed. We can skip the check if something that looks
673 # like a chroot is already visible in the expected place, since we won't
674 # actually call any LVM commands in that case.
675 if options.use_image and options.create:
676 missing = osutils.FindMissingBinaries(IMAGE_NEEDED_TOOLS)
677 if missing:
678 raise SystemExit(
679 'The tool(s) %s were not found.\n'
680 'Please make sure the lvm2 and thin-provisioning-tools packages '
681 'are installed on your host.\n'
682 'Example(ubuntu):\n'
683 ' sudo apt-get install lvm2 thin-provisioning-tools\n\n'
684 'If you want to run without lvm2, pass --nouse-image (chroot '
685 'snapshots will be unavailable).'
686 % ', '.join(missing))
687
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600688 # This dance is to support mounting the chroot inside a separate mount
689 # namespace. While we're here in the original namespace, we set up a
690 # temporary shared subtree
691 # (https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt) and
692 # then let CreateChroot operate inside there. Since it's shared, we can see
693 # the mount that gets created. make_chroot.sh marks its mount private inside
694 # the namespace, so none of the inner mounts leak out. After the chroot has
695 # been created, we will return to this mount namespace (using an fd that we
696 # open here and save) and move the mount from the temporary spot to the
697 # requested final location. Once this is done, future uses of the same chroot
698 # don't have to jump through any of these hoops until it gets unmounted. If
699 # we're running on a system where things are mounted shared by default then
700 # all this isn't necessary, but it seems safer to assume we need this setup
701 # rather than try to detect it.
702 if options.use_image:
703 chroot_temp_parent = options.chroot + '.build'
704 chroot_temp_mount = os.path.join(chroot_temp_parent, 'chroot')
705 already_mounted = [m for m in osutils.IterateMountPoints()
706 if m.destination == chroot_temp_parent]
707 if not already_mounted and options.create:
708 osutils.SafeMakedirsNonRoot(chroot_temp_mount)
709 osutils.Mount(chroot_temp_parent, chroot_temp_parent, '', osutils.MS_BIND)
710 osutils.Mount('', chroot_temp_parent, '', osutils.MS_SHARED)
711 parent_ns_file = open('/proc/%d/ns/mnt' % (os.getppid(),))
712 parent_ns = parent_ns_file.fileno()
713
714 # If we're going to delete, also make sure the chroot isn't mounted
715 # before we enter the new mount namespace.
716 if options.delete:
717 osutils.UmountTree(options.chroot)
718
719 namespaces.SimpleUnshare()
720 if options.ns_pid:
721 first_pid = namespaces.CreatePidNs()
722 else:
723 first_pid = None
724
Brian Harringb938c782012-02-29 15:14:38 -0800725 if not options.sdk_version:
Brian Harring1790ac42012-09-23 08:53:33 -0700726 sdk_version = (bootstrap_latest_version if options.bootstrap
727 else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -0800728 else:
729 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -0500730 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -0700731 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -0800732
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700733 # Based on selections, determine the tarball to fetch.
Brian Harring1790ac42012-09-23 08:53:33 -0700734 if options.sdk_url:
735 urls = [options.sdk_url]
736 elif options.bootstrap:
737 urls = GetStage3Urls(sdk_version)
738 else:
739 urls = GetArchStageTarballs(sdk_version)
740
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700741 # Get URLs for the toolchains overlay, if one is to be used.
742 toolchains_overlay_urls = None
743 if not options.bootstrap:
744 toolchains = None
745 if options.toolchains:
746 toolchains = options.toolchains.split(',')
747 elif options.board:
748 toolchains = toolchain.GetToolchainsForBoard(options.board).keys()
749
750 if toolchains:
751 toolchains_overlay_urls = GetToolchainsOverlayUrls(sdk_version,
752 toolchains)
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700753
Brian Harringb6cf9142012-09-01 20:43:17 -0700754 lock_path = os.path.dirname(options.chroot)
Gilad Arnoldfbe40e22015-03-18 14:52:16 -0700755 lock_path = os.path.join(
756 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
Mike Frysinger80dfce92014-04-21 10:58:53 -0400757 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -0700758 with locking.FileLock(lock_path, 'chroot lock') as lock:
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700759 toolchains_overlay_tarball = None
Brian Harring1790ac42012-09-23 08:53:33 -0700760
Josh Triplett472a4182013-03-08 11:48:57 -0800761 if options.proxy_sim:
762 _ProxySimSetup(options)
763
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600764 if options.delete and (os.path.exists(options.chroot) or
765 os.path.exists(options.chroot + '.img')):
David James56e6c2c2012-10-24 23:54:41 -0700766 lock.write_lock()
767 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -0800768
David James56e6c2c2012-10-24 23:54:41 -0700769 sdk_cache = os.path.join(options.cache_dir, 'sdks')
770 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -0700771 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -0700772
David James56e6c2c2012-10-24 23:54:41 -0700773 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500774 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -0700775 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -0700776 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -0700777 continue
778 lock.write_lock(
779 "Upgrade to %r needed but chroot is locked; please exit "
780 "all instances so this upgrade can finish." % src)
781 if not os.path.exists(src):
782 # Note that while waiting for the write lock, src may've vanished;
783 # it's a rare race during the upgrade process that's a byproduct
784 # of us avoiding taking a write lock to do the src check. If we
785 # took a write lock for that check, it would effectively limit
786 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -0700787 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -0700788 elif not os.path.exists(target):
789 # Upgrade occurred, but a reversion, or something whacky
790 # occurred writing to the old location. Wipe and continue.
791 os.rename(src, target)
792 else:
793 # Upgrade occurred once already, but either a reversion or
794 # some before/after separate cros_sdk usage is at play.
795 # Wipe and continue.
796 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -0700797
David James56e6c2c2012-10-24 23:54:41 -0700798 if options.download:
799 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700800 sdk_tarball = FetchRemoteTarballs(
801 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
802 if toolchains_overlay_urls:
803 toolchains_overlay_tarball = FetchRemoteTarballs(
804 sdk_cache, toolchains_overlay_urls, 'SDK toolchains overlay',
805 allow_none=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700806
David James56e6c2c2012-10-24 23:54:41 -0700807 if options.create:
808 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700809 CreateChroot(options.chroot, sdk_tarball, toolchains_overlay_tarball,
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700810 options.cache_dir,
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600811 nousepkg=(options.bootstrap or options.nousepkg),
812 useimage=options.use_image)
Brian Harring1790ac42012-09-23 08:53:33 -0700813
David James56e6c2c2012-10-24 23:54:41 -0700814 if options.enter:
815 lock.read_lock()
816 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Don Garrett230d1b22015-03-09 16:21:19 -0700817 options.chrome_root_mount, options.workspace,
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900818 options.goma_dir, options.goma_client_json,
Don Garrett230d1b22015-03-09 16:21:19 -0700819 chroot_command)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600820
821 # Remount the inner chroot mount back up to the original namespace. See above
822 # for details.
823 if options.use_image and options.create:
Benjamin Gordon56c845a2017-08-04 17:42:19 -0600824 vg, lv = cros_build_lib.FindChrootMountSource(chroot_temp_mount)
Benjamin Gordon386b9eb2017-07-20 09:21:33 -0600825
826 # Clean up inside the child mount namespace. Normally these will disappear
827 # as soon as the last process exits the mount namespace, but we want to be
828 # able to clean up the underlying directories without waiting for the forked
829 # "init" copes of cros_sdk to exit the namespace. This is safe to do even
830 # with multiple processes in the same chroot because the other cros_sdk
831 # copies will have their own mount namespace.
832 chroot_mounts = _FindSubmounts(chroot_temp_parent, options.chroot)
833 osutils.UmountTree(chroot_temp_parent)
834 osutils.UmountTree(options.chroot)
835
836 namespaces.SetNS(parent_ns, 0)
837 chroot_mounts = _FindSubmounts(chroot_temp_parent, options.chroot)
838 if not options.chroot in chroot_mounts:
839 if not vg or not lv:
840 cros_build_lib.Die('Unable to find VG/LV mounted on %s after building '
841 'chroot.' % chroot_temp_mount)
842 osutils.UmountTree(chroot_temp_parent)
843 osutils.RmDir(chroot_temp_parent, ignore_missing=True)
844
845 chroot_dev_path = '/dev/mapper/%s-%s' % (vg, lv)
846 osutils.Mount(chroot_dev_path, options.chroot, 'ext4', osutils.MS_NOATIME)