blob: e0790e229767cc2ed309c733bce9142c2bef2a02 [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
Mike Frysingercc838832014-05-24 13:10:30 -040068
Brian Harring1790ac42012-09-23 08:53:33 -070069def GetArchStageTarballs(version):
Brian Harringb938c782012-02-29 15:14:38 -080070 """Returns the URL for a given arch/version"""
Brian Harring1790ac42012-09-23 08:53:33 -070071 extension = {'bz2':'tbz2', 'xz':'tar.xz'}
Mike Frysinger8e727a32013-01-16 16:57:53 -050072 return [toolchain.GetSdkURL(suburl='cros-sdk-%s.%s'
73 % (version, extension[compressor]))
Brian Harring1790ac42012-09-23 08:53:33 -070074 for compressor in COMPRESSION_PREFERENCE]
75
76
77def GetStage3Urls(version):
Mike Frysinger8e727a32013-01-16 16:57:53 -050078 return [toolchain.GetSdkURL(suburl='stage3-amd64-%s.tar.%s' % (version, ext))
Brian Harring1790ac42012-09-23 08:53:33 -070079 for ext in COMPRESSION_PREFERENCE]
Brian Harringb938c782012-02-29 15:14:38 -080080
81
Gilad Arnold6a8f0452015-06-04 11:25:18 -070082def GetToolchainsOverlayUrls(version, toolchains):
83 """Returns the URL(s) for a toolchains SDK overlay.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -070084
85 Args:
86 version: The SDK version used, e.g. 2015.05.27.145939. We use the year and
87 month components to point to a subdirectory on the SDK bucket where
Gilad Arnold6a8f0452015-06-04 11:25:18 -070088 overlays are stored (.../2015/05/ in this case).
89 toolchains: Iterable of toolchain target strings (e.g. 'i686-pc-linux-gnu').
Gilad Arnoldecc86fa2015-05-22 12:06:04 -070090
91 Returns:
Gilad Arnold6a8f0452015-06-04 11:25:18 -070092 List of alternative download URLs for an SDK overlay tarball that contains
93 the given toolchains.
Gilad Arnoldecc86fa2015-05-22 12:06:04 -070094 """
Gilad Arnold6a8f0452015-06-04 11:25:18 -070095 toolchains_desc = '-'.join(sorted(toolchains))
Gilad Arnoldecc86fa2015-05-22 12:06:04 -070096 suburl_template = os.path.join(
97 *(version.split('.')[:2] +
Gilad Arnold6a8f0452015-06-04 11:25:18 -070098 ['cros-sdk-overlay-toolchains-%s-%s.tar.%%s' %
99 (toolchains_desc, version)]))
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700100 return [toolchain.GetSdkURL(suburl=suburl_template % ext)
101 for ext in COMPRESSION_PREFERENCE]
102
103
104def FetchRemoteTarballs(storage_dir, urls, desc, allow_none=False):
Mike Frysinger34db8692013-11-11 14:54:08 -0500105 """Fetches a tarball given by url, and place it in |storage_dir|.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200106
107 Args:
Mike Frysinger34db8692013-11-11 14:54:08 -0500108 storage_dir: Path where to save the tarball.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200109 urls: List of URLs to try to download. Download will stop on first success.
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700110 desc: A string describing what tarball we're downloading (for logging).
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700111 allow_none: Don't fail if none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200112
113 Returns:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700114 Full path to the downloaded file, or None if |allow_none| and no URL worked.
115
116 Raises:
117 ValueError: If |allow_none| is False and none of the URLs worked.
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200118 """
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200119
Brian Harring1790ac42012-09-23 08:53:33 -0700120 # Note we track content length ourselves since certain versions of curl
121 # fail if asked to resume a complete file.
122 # pylint: disable=C0301,W0631
123 # https://sourceforge.net/tracker/?func=detail&atid=100976&aid=3482927&group_id=976
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700124 logging.notice('Downloading %s tarball...', desc)
Brian Norriscf8aef42016-09-27 10:43:39 -0700125 status_re = re.compile(r'^HTTP/[0-9]+(\.[0-9]+)? 200')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200126 for url in urls:
Brian Harring1790ac42012-09-23 08:53:33 -0700127 # http://www.logilab.org/ticket/8766
128 # pylint: disable=E1101
129 parsed = urlparse.urlparse(url)
130 tarball_name = os.path.basename(parsed.path)
131 if parsed.scheme in ('', 'file'):
132 if os.path.exists(parsed.path):
133 return parsed.path
134 continue
135 content_length = 0
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700136 logging.debug('Attempting download from %s', url)
David Jamesc93e6a4d2014-01-13 11:37:36 -0800137 result = retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900138 ['-I', url], print_cmd=False, debug_level=logging.NOTICE,
139 capture_output=True)
Brian Harring1790ac42012-09-23 08:53:33 -0700140 successful = False
141 for header in result.output.splitlines():
Brian Norrisd37e2f72016-08-22 16:09:24 -0700142 # We must walk the output to find the 200 code for use cases where
Brian Harring1790ac42012-09-23 08:53:33 -0700143 # a proxy is involved and may have pushed down the actual header.
Brian Norrisd37e2f72016-08-22 16:09:24 -0700144 if status_re.match(header):
Brian Harring1790ac42012-09-23 08:53:33 -0700145 successful = True
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500146 elif header.lower().startswith('content-length:'):
147 content_length = int(header.split(':', 1)[-1].strip())
Brian Harring1790ac42012-09-23 08:53:33 -0700148 if successful:
149 break
150 if successful:
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200151 break
152 else:
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700153 if allow_none:
154 return None
155 raise ValueError('No valid URLs found!')
Zdenek Behanfd0efe42012-04-13 04:36:40 +0200156
Brian Harringae0a5322012-09-15 01:46:51 -0700157 tarball_dest = os.path.join(storage_dir, tarball_name)
Brian Harring1790ac42012-09-23 08:53:33 -0700158 current_size = 0
159 if os.path.exists(tarball_dest):
160 current_size = os.path.getsize(tarball_dest)
161 if current_size > content_length:
David James56e6c2c2012-10-24 23:54:41 -0700162 osutils.SafeUnlink(tarball_dest)
Brian Harring1790ac42012-09-23 08:53:33 -0700163 current_size = 0
Zdenek Behanb2fa72e2012-03-16 04:49:30 +0100164
Brian Harring1790ac42012-09-23 08:53:33 -0700165 if current_size < content_length:
David Jamesc93e6a4d2014-01-13 11:37:36 -0800166 retry_util.RunCurl(
Hidehiko Abee55af7f2017-05-01 18:38:04 +0900167 ['--fail', '-L', '-y', '30', '-C', '-', '--output', tarball_dest, url],
168 print_cmd=False, debug_level=logging.NOTICE)
Brian Harringb938c782012-02-29 15:14:38 -0800169
Brian Harring1790ac42012-09-23 08:53:33 -0700170 # Cleanup old tarballs now since we've successfull fetched; only cleanup
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700171 # the tarballs for our prefix, or unknown ones. This gets a bit tricky
172 # because we might have partial overlap between known prefixes.
173 my_prefix = tarball_name.rsplit('-', 1)[0] + '-'
174 all_prefixes = ('stage3-amd64-', 'cros-sdk-', 'cros-sdk-overlay-')
175 ignored_prefixes = [prefix for prefix in all_prefixes if prefix != my_prefix]
Brian Harring1790ac42012-09-23 08:53:33 -0700176 for filename in os.listdir(storage_dir):
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700177 if (filename == tarball_name or
178 any([(filename.startswith(p) and
179 not (len(my_prefix) > len(p) and filename.startswith(my_prefix)))
180 for p in ignored_prefixes])):
Brian Harring1790ac42012-09-23 08:53:33 -0700181 continue
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700182 logging.info('Cleaning up old tarball: %s', filename)
David James56e6c2c2012-10-24 23:54:41 -0700183 osutils.SafeUnlink(os.path.join(storage_dir, filename))
Zdenek Behan9c644dd2012-04-05 06:24:02 +0200184
Brian Harringb938c782012-02-29 15:14:38 -0800185 return tarball_dest
186
187
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700188def CreateChroot(chroot_path, sdk_tarball, toolchains_overlay_tarball,
David Pursell3528e8e2015-07-07 11:22:15 -0700189 cache_dir, nousepkg=False):
Brian Harringb938c782012-02-29 15:14:38 -0800190 """Creates a new chroot from a given SDK"""
Brian Harringb938c782012-02-29 15:14:38 -0800191
Brian Harring1790ac42012-09-23 08:53:33 -0700192 cmd = MAKE_CHROOT + ['--stage3_path', sdk_tarball,
Brian Harringae0a5322012-09-15 01:46:51 -0700193 '--chroot', chroot_path,
194 '--cache_dir', cache_dir]
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700195
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700196 if toolchains_overlay_tarball:
197 cmd.extend(['--toolchains_overlay_path', toolchains_overlay_tarball])
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700198
Mike Frysinger2de7f042012-07-10 04:45:03 -0400199 if nousepkg:
200 cmd.append('--nousepkg')
Brian Harringb938c782012-02-29 15:14:38 -0800201
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700202 logging.notice('Creating chroot. This may take a few minutes...')
Brian Harringb938c782012-02-29 15:14:38 -0800203 try:
204 cros_build_lib.RunCommand(cmd, print_cmd=False)
205 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700206 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800207
208
209def DeleteChroot(chroot_path):
210 """Deletes an existing chroot"""
211 cmd = MAKE_CHROOT + ['--chroot', chroot_path,
212 '--delete']
213 try:
Ralph Nathan7070e6a2015-04-02 10:16:43 -0700214 logging.notice('Deleting chroot.')
Brian Harringb938c782012-02-29 15:14:38 -0800215 cros_build_lib.RunCommand(cmd, print_cmd=False)
216 except cros_build_lib.RunCommandError:
Brian Harring98b54902012-03-23 04:05:42 -0700217 raise SystemExit('Running %r failed!' % cmd)
Brian Harringb938c782012-02-29 15:14:38 -0800218
219
Brian Harringae0a5322012-09-15 01:46:51 -0700220def EnterChroot(chroot_path, cache_dir, chrome_root, chrome_root_mount,
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900221 workspace, goma_dir, goma_client_json, additional_args):
Brian Harringb938c782012-02-29 15:14:38 -0800222 """Enters an existing SDK chroot"""
Mike Frysingere5456972013-06-13 00:07:23 -0400223 st = os.statvfs(os.path.join(chroot_path, 'usr', 'bin', 'sudo'))
224 # The os.ST_NOSUID constant wasn't added until python-3.2.
225 if st.f_flag & 0x2:
226 cros_build_lib.Die('chroot cannot be in a nosuid mount')
227
Brian Harringae0a5322012-09-15 01:46:51 -0700228 cmd = ENTER_CHROOT + ['--chroot', chroot_path, '--cache_dir', cache_dir]
Brian Harringb938c782012-02-29 15:14:38 -0800229 if chrome_root:
230 cmd.extend(['--chrome_root', chrome_root])
231 if chrome_root_mount:
232 cmd.extend(['--chrome_root_mount', chrome_root_mount])
Don Garrett230d1b22015-03-09 16:21:19 -0700233 if workspace:
234 cmd.extend(['--workspace_root', workspace])
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900235 if goma_dir:
236 cmd.extend(['--goma_dir', goma_dir])
237 if goma_client_json:
238 cmd.extend(['--goma_client_json', goma_client_json])
Don Garrett230d1b22015-03-09 16:21:19 -0700239
Brian Harringb938c782012-02-29 15:14:38 -0800240 if len(additional_args) > 0:
241 cmd.append('--')
242 cmd.extend(additional_args)
Brian Harring7199e7d2012-03-23 04:10:08 -0700243
Ting-Yuan Huangf56d9af2017-06-19 16:08:32 -0700244 # ThinLTO opens lots of files at the same time.
245 resource.setrlimit(resource.RLIMIT_NOFILE, (32768, 32768))
Ralph Nathan549d3502015-03-26 17:38:42 -0700246 ret = cros_build_lib.RunCommand(cmd, print_cmd=False, error_code_ok=True,
247 mute_output=False)
Brian Harring7199e7d2012-03-23 04:10:08 -0700248 # If we were in interactive mode, ignore the exit code; it'll be whatever
249 # they last ran w/in the chroot and won't matter to us one way or another.
250 # Note this does allow chroot entrance to fail and be ignored during
251 # interactive; this is however a rare case and the user will immediately
252 # see it (nor will they be checking the exit code manually).
253 if ret.returncode != 0 and additional_args:
Richard Barnette5c728a42015-03-18 11:50:21 -0700254 raise SystemExit(ret.returncode)
Brian Harringb938c782012-02-29 15:14:38 -0800255
256
David James56e6c2c2012-10-24 23:54:41 -0700257def _SudoCommand():
258 """Get the 'sudo' command, along with all needed environment variables."""
259
David James5a73b4d2013-03-07 10:23:40 -0800260 # Pass in the ENVIRONMENT_WHITELIST and ENV_PASSTHRU variables so that
261 # scripts in the chroot know what variables to pass through.
David James56e6c2c2012-10-24 23:54:41 -0700262 cmd = ['sudo']
David James5a73b4d2013-03-07 10:23:40 -0800263 for key in constants.CHROOT_ENVIRONMENT_WHITELIST + constants.ENV_PASSTHRU:
David James56e6c2c2012-10-24 23:54:41 -0700264 value = os.environ.get(key)
265 if value is not None:
266 cmd += ['%s=%s' % (key, value)]
267
268 # Pass in the path to the depot_tools so that users can access them from
269 # within the chroot.
Mike Frysinger08e75f12014-08-13 01:30:09 -0400270 cmd += ['DEPOT_TOOLS=%s' % constants.DEPOT_TOOLS_DIR]
Mike Frysinger749251e2014-01-29 05:04:27 -0500271
David James56e6c2c2012-10-24 23:54:41 -0700272 return cmd
273
274
Josh Triplett472a4182013-03-08 11:48:57 -0800275def _ReportMissing(missing):
276 """Report missing utilities, then exit.
277
278 Args:
279 missing: List of missing utilities, as returned by
280 osutils.FindMissingBinaries. If non-empty, will not return.
281 """
282
283 if missing:
284 raise SystemExit(
285 'The tool(s) %s were not found.\n'
286 'Please install the appropriate package in your host.\n'
287 'Example(ubuntu):\n'
288 ' sudo apt-get install <packagename>'
289 % ', '.join(missing))
290
291
292def _ProxySimSetup(options):
293 """Set up proxy simulator, and return only in the child environment.
294
295 TODO: Ideally, this should support multiple concurrent invocations of
296 cros_sdk --proxy-sim; currently, such invocations will conflict with each
297 other due to the veth device names and IP addresses. Either this code would
298 need to generate fresh, unused names for all of these before forking, or it
299 would need to support multiple concurrent cros_sdk invocations sharing one
300 proxy and allowing it to exit when unused (without counting on any local
301 service-management infrastructure on the host).
302 """
303
304 may_need_mpm = False
305 apache_bin = osutils.Which('apache2')
306 if apache_bin is None:
307 apache_bin = osutils.Which('apache2', PROXY_APACHE_FALLBACK_PATH)
308 if apache_bin is None:
309 _ReportMissing(('apache2',))
310 else:
311 may_need_mpm = True
312
313 # Module names and .so names included for ease of grepping.
314 apache_modules = [('proxy_module', 'mod_proxy.so'),
315 ('proxy_connect_module', 'mod_proxy_connect.so'),
316 ('proxy_http_module', 'mod_proxy_http.so'),
317 ('proxy_ftp_module', 'mod_proxy_ftp.so')]
318
319 # Find the apache module directory, and make sure it has the modules we need.
320 module_dirs = {}
321 for g in PROXY_APACHE_MODULE_GLOBS:
322 for mod, so in apache_modules:
323 for f in glob.glob(os.path.join(g, so)):
324 module_dirs.setdefault(os.path.dirname(f), []).append(so)
325 for apache_module_path, modules_found in module_dirs.iteritems():
326 if len(modules_found) == len(apache_modules):
327 break
328 else:
329 # Appease cros lint, which doesn't understand that this else block will not
330 # fall through to the subsequent code which relies on apache_module_path.
331 apache_module_path = None
332 raise SystemExit(
333 'Could not find apache module path containing all required modules: %s'
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500334 % ', '.join(so for mod, so in apache_modules))
Josh Triplett472a4182013-03-08 11:48:57 -0800335
336 def check_add_module(name):
337 so = 'mod_%s.so' % name
338 if os.access(os.path.join(apache_module_path, so), os.F_OK):
339 mod = '%s_module' % name
340 apache_modules.append((mod, so))
341 return True
342 return False
343
344 check_add_module('authz_core')
345 if may_need_mpm:
346 for mpm in PROXY_APACHE_MPMS:
347 if check_add_module('mpm_%s' % mpm):
348 break
349
350 veth_host = '%s-host' % PROXY_VETH_PREFIX
351 veth_guest = '%s-guest' % PROXY_VETH_PREFIX
352
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500353 # Set up locks to sync the net namespace setup. We need the child to create
354 # the net ns first, and then have the parent assign the guest end of the veth
355 # interface to the child's new network namespace & bring up the proxy. Only
356 # then can the child move forward and rely on the network being up.
357 ns_create_lock = locking.PipeLock()
358 ns_setup_lock = locking.PipeLock()
Josh Triplett472a4182013-03-08 11:48:57 -0800359
360 pid = os.fork()
361 if not pid:
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500362 # Create our new isolated net namespace.
Josh Triplett472a4182013-03-08 11:48:57 -0800363 namespaces.Unshare(namespaces.CLONE_NEWNET)
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500364
365 # Signal the parent the ns is ready to be configured.
366 ns_create_lock.Post()
367 del ns_create_lock
368
369 # Wait for the parent to finish setting up the ns/proxy.
370 ns_setup_lock.Wait()
371 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800372
373 # Set up child side of the network.
374 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500375 ('ip', 'link', 'set', 'up', 'lo'),
376 ('ip', 'address', 'add',
377 '%s/%u' % (PROXY_GUEST_IP, PROXY_NETMASK),
378 'dev', veth_guest),
379 ('ip', 'link', 'set', veth_guest, 'up'),
Josh Triplett472a4182013-03-08 11:48:57 -0800380 )
381 try:
382 for cmd in commands:
383 cros_build_lib.RunCommand(cmd, print_cmd=False)
384 except cros_build_lib.RunCommandError:
385 raise SystemExit('Running %r failed!' % (cmd,))
386
387 proxy_url = 'http://%s:%u' % (PROXY_HOST_IP, PROXY_PORT)
388 for proto in ('http', 'https', 'ftp'):
389 os.environ[proto + '_proxy'] = proxy_url
390 for v in ('all_proxy', 'RSYNC_PROXY', 'no_proxy'):
391 os.environ.pop(v, None)
392 return
393
Josh Triplett472a4182013-03-08 11:48:57 -0800394 # Set up parent side of the network.
395 uid = int(os.environ.get('SUDO_UID', '0'))
396 gid = int(os.environ.get('SUDO_GID', '0'))
397 if uid == 0 or gid == 0:
398 for username in PROXY_APACHE_FALLBACK_USERS:
399 try:
400 pwnam = pwd.getpwnam(username)
401 uid, gid = pwnam.pw_uid, pwnam.pw_gid
402 break
403 except KeyError:
404 continue
405 if uid == 0 or gid == 0:
406 raise SystemExit('Could not find a non-root user to run Apache as')
407
408 chroot_parent, chroot_base = os.path.split(options.chroot)
409 pid_file = os.path.join(chroot_parent, '.%s-apache-proxy.pid' % chroot_base)
410 log_file = os.path.join(chroot_parent, '.%s-apache-proxy.log' % chroot_base)
411
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500412 # Wait for the child to create the net ns.
413 ns_create_lock.Wait()
414 del ns_create_lock
415
Josh Triplett472a4182013-03-08 11:48:57 -0800416 apache_directives = [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500417 'User #%u' % uid,
418 'Group #%u' % gid,
419 'PidFile %s' % pid_file,
420 'ErrorLog %s' % log_file,
421 'Listen %s:%u' % (PROXY_HOST_IP, PROXY_PORT),
422 'ServerName %s' % PROXY_HOST_IP,
423 'ProxyRequests On',
424 'AllowCONNECT %s' % ' '.join(map(str, PROXY_CONNECT_PORTS)),
Josh Triplett472a4182013-03-08 11:48:57 -0800425 ] + [
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500426 'LoadModule %s %s' % (mod, os.path.join(apache_module_path, so))
427 for (mod, so) in apache_modules
Josh Triplett472a4182013-03-08 11:48:57 -0800428 ]
429 commands = (
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500430 ('ip', 'link', 'add', 'name', veth_host,
431 'type', 'veth', 'peer', 'name', veth_guest),
432 ('ip', 'address', 'add',
433 '%s/%u' % (PROXY_HOST_IP, PROXY_NETMASK),
434 'dev', veth_host),
435 ('ip', 'link', 'set', veth_host, 'up'),
436 ([apache_bin, '-f', '/dev/null'] +
437 [arg for d in apache_directives for arg in ('-C', d)]),
438 ('ip', 'link', 'set', veth_guest, 'netns', str(pid)),
Josh Triplett472a4182013-03-08 11:48:57 -0800439 )
440 cmd = None # Make cros lint happy.
441 try:
442 for cmd in commands:
443 cros_build_lib.RunCommand(cmd, print_cmd=False)
444 except cros_build_lib.RunCommandError:
445 # Clean up existing interfaces, if any.
446 cmd_cleanup = ('ip', 'link', 'del', veth_host)
447 try:
448 cros_build_lib.RunCommand(cmd_cleanup, print_cmd=False)
449 except cros_build_lib.RunCommandError:
Ralph Nathan59900422015-03-24 10:41:17 -0700450 logging.error('running %r failed', cmd_cleanup)
Josh Triplett472a4182013-03-08 11:48:57 -0800451 raise SystemExit('Running %r failed!' % (cmd,))
Mike Frysinger77bf4af2016-02-26 17:13:15 -0500452
453 # Signal the child that the net ns/proxy is fully configured now.
454 ns_setup_lock.Post()
455 del ns_setup_lock
Josh Triplett472a4182013-03-08 11:48:57 -0800456
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400457 process_util.ExitAsStatus(os.waitpid(pid, 0)[1])
Josh Triplett472a4182013-03-08 11:48:57 -0800458
459
Mike Frysingera78a56e2012-11-20 06:02:30 -0500460def _ReExecuteIfNeeded(argv):
David James56e6c2c2012-10-24 23:54:41 -0700461 """Re-execute cros_sdk as root.
462
463 Also unshare the mount namespace so as to ensure that processes outside
464 the chroot can't mess with our mounts.
465 """
466 if os.geteuid() != 0:
Mike Frysingera78a56e2012-11-20 06:02:30 -0500467 cmd = _SudoCommand() + ['--'] + argv
468 os.execvp(cmd[0], cmd)
Mike Frysingera78a56e2012-11-20 06:02:30 -0500469 else:
Mike Frysinger80dfce92014-04-21 10:58:53 -0400470 # We must set up the cgroups mounts before we enter our own namespace.
471 # This way it is a shared resource in the root mount namespace.
Josh Triplette759b232013-03-08 13:03:43 -0800472 cgroups.Cgroup.InitSystem()
Mike Frysinger7a5fd842014-11-26 18:10:07 -0500473 namespaces.SimpleUnshare()
David James56e6c2c2012-10-24 23:54:41 -0700474
475
Mike Frysinger34db8692013-11-11 14:54:08 -0500476def _CreateParser(sdk_latest_version, bootstrap_latest_version):
477 """Generate and return the parser with all the options."""
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400478 usage = ('usage: %(prog)s [options] '
479 '[VAR1=val1 ... VAR2=val2] [--] [command [args]]')
480 parser = commandline.ArgumentParser(usage=usage, description=__doc__,
481 caching=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700482
Mike Frysinger34db8692013-11-11 14:54:08 -0500483 # Global options.
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500484 default_chroot = os.path.join(constants.SOURCE_ROOT,
485 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400486 parser.add_argument(
Brian Harring218e13c2012-10-10 16:21:26 -0700487 '--chroot', dest='chroot', default=default_chroot, type='path',
488 help=('SDK chroot dir name [%s]' % constants.DEFAULT_CHROOT_DIR))
Brian Harringb938c782012-02-29 15:14:38 -0800489
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400490 parser.add_argument('--chrome_root', type='path',
491 help='Mount this chrome root into the SDK chroot')
492 parser.add_argument('--chrome_root_mount', type='path',
493 help='Mount chrome into this path inside SDK chroot')
494 parser.add_argument('--nousepkg', action='store_true', default=False,
495 help='Do not use binary packages when creating a chroot.')
496 parser.add_argument('-u', '--url', dest='sdk_url',
497 help='Use sdk tarball located at this url. Use file:// '
498 'for local files.')
499 parser.add_argument('--sdk-version',
500 help=('Use this sdk version. For prebuilt, current is %r'
501 ', for bootstrapping it is %r.'
502 % (sdk_latest_version, bootstrap_latest_version)))
503 parser.add_argument('--workspace',
504 help='Workspace directory to mount into the chroot.')
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900505 parser.add_argument('--goma_dir', type='path',
506 help='Goma installed directory to mount into the chroot.')
507 parser.add_argument('--goma_client_json', type='path',
508 help='Service account json file to use goma on bot. '
509 'Mounted into the chroot.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400510 parser.add_argument('commands', nargs=argparse.REMAINDER)
Mike Frysinger34db8692013-11-11 14:54:08 -0500511
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700512 # SDK overlay tarball options (mutually exclusive).
513 group = parser.add_mutually_exclusive_group()
514 group.add_argument('--toolchains',
515 help=('Comma-separated list of toolchains we expect to be '
516 'using on the chroot. Used for downloading a '
517 'corresponding SDK toolchains group (if one is '
518 'found), which may speed up chroot initialization '
519 'when building for the first time. Otherwise this '
520 'has no effect and will not restrict the chroot in '
521 'any way. Ignored if using --bootstrap.'))
522 group.add_argument('--board',
523 help=('The board we intend to be building in the chroot. '
524 'Used for deriving the list of required toolchains '
525 '(see --toolchains).'))
526
Mike Frysinger34db8692013-11-11 14:54:08 -0500527 # Commands.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400528 group = parser.add_argument_group('Commands')
529 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500530 '--enter', action='store_true', default=False,
531 help='Enter the SDK chroot. Implies --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400532 group.add_argument(
Mike Frysingerd6e2df02014-11-26 02:55:04 -0500533 '--create', action='store_true', default=False,
Mike Frysinger34db8692013-11-11 14:54:08 -0500534 help='Create the chroot only if it does not already exist. '
535 'Implies --download.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400536 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500537 '--bootstrap', action='store_true', default=False,
538 help='Build everything from scratch, including the sdk. '
539 'Use this only if you need to validate a change '
540 'that affects SDK creation itself (toolchain and '
541 'build are typically the only folk who need this). '
542 'Note this will quite heavily slow down the build. '
543 'This option implies --create --nousepkg.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400544 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500545 '-r', '--replace', action='store_true', default=False,
546 help='Replace an existing SDK chroot. Basically an alias '
547 'for --delete --create.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400548 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500549 '--delete', action='store_true', default=False,
550 help='Delete the current SDK chroot if it exists.')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400551 group.add_argument(
Mike Frysinger34db8692013-11-11 14:54:08 -0500552 '--download', action='store_true', default=False,
553 help='Download the sdk.')
554 commands = group
555
Mike Frysinger80dfce92014-04-21 10:58:53 -0400556 # Namespace options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400557 group = parser.add_argument_group('Namespaces')
558 group.add_argument('--proxy-sim', action='store_true', default=False,
559 help='Simulate a restrictive network requiring an outbound'
560 ' proxy.')
561 group.add_argument('--no-ns-pid', dest='ns_pid',
562 default=True, action='store_false',
563 help='Do not create a new PID namespace.')
Mike Frysinger80dfce92014-04-21 10:58:53 -0400564
Mike Frysinger34db8692013-11-11 14:54:08 -0500565 # Internal options.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400566 group = parser.add_argument_group(
Mike Frysinger34db8692013-11-11 14:54:08 -0500567 'Internal Chromium OS Build Team Options',
568 'Caution: these are for meant for the Chromium OS build team only')
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400569 group.add_argument('--buildbot-log-version', default=False,
570 action='store_true',
571 help='Log SDK version for buildbot consumption')
Mike Frysinger34db8692013-11-11 14:54:08 -0500572
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400573 return parser, commands
Mike Frysinger34db8692013-11-11 14:54:08 -0500574
575
576def main(argv):
577 conf = cros_build_lib.LoadKeyValueFile(
578 os.path.join(constants.SOURCE_ROOT, constants.SDK_VERSION_FILE),
579 ignore_missing=True)
580 sdk_latest_version = conf.get('SDK_LATEST_VERSION', '<unknown>')
581 bootstrap_latest_version = conf.get('BOOTSTRAP_LATEST_VERSION', '<unknown>')
582 parser, commands = _CreateParser(sdk_latest_version, bootstrap_latest_version)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400583 options = parser.parse_args(argv)
584 chroot_command = options.commands
Brian Harringb938c782012-02-29 15:14:38 -0800585
586 # Some sanity checks first, before we ask for sudo credentials.
Mike Frysinger8fd67dc2012-12-03 23:51:18 -0500587 cros_build_lib.AssertOutsideChroot()
Brian Harringb938c782012-02-29 15:14:38 -0800588
Brian Harring1790ac42012-09-23 08:53:33 -0700589 host = os.uname()[4]
Brian Harring1790ac42012-09-23 08:53:33 -0700590 if host != 'x86_64':
Benjamin Gordon040a1162017-06-29 13:44:47 -0600591 cros_build_lib.Die(
Brian Harring1790ac42012-09-23 08:53:33 -0700592 "cros_sdk is currently only supported on x86_64; you're running"
593 " %s. Please find a x86_64 machine." % (host,))
594
Josh Triplett472a4182013-03-08 11:48:57 -0800595 _ReportMissing(osutils.FindMissingBinaries(NEEDED_TOOLS))
596 if options.proxy_sim:
597 _ReportMissing(osutils.FindMissingBinaries(PROXY_NEEDED_TOOLS))
Brian Harringb938c782012-02-29 15:14:38 -0800598
Benjamin Gordon040a1162017-06-29 13:44:47 -0600599 if (sdk_latest_version == '<unknown>' or
600 bootstrap_latest_version == '<unknown>'):
601 cros_build_lib.Die(
602 'No SDK version was found. '
603 'Are you in a Chromium source tree instead of Chromium OS?\n\n'
604 'Please change to a directory inside your Chromium OS source tree\n'
605 'and retry. If you need to setup a Chromium OS source tree, see\n'
606 ' http://www.chromium.org/chromium-os/developer-guide')
607
David James471532c2013-01-21 10:23:31 -0800608 _ReExecuteIfNeeded([sys.argv[0]] + argv)
Mike Frysinger80dfce92014-04-21 10:58:53 -0400609 if options.ns_pid:
Mike Frysingere2d8f0d2014-11-01 13:09:26 -0400610 first_pid = namespaces.CreatePidNs()
Mike Frysinger80dfce92014-04-21 10:58:53 -0400611 else:
612 first_pid = None
David James471532c2013-01-21 10:23:31 -0800613
Brian Harring218e13c2012-10-10 16:21:26 -0700614 # Expand out the aliases...
615 if options.replace:
616 options.delete = options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800617
Brian Harring218e13c2012-10-10 16:21:26 -0700618 if options.bootstrap:
619 options.create = True
Brian Harringb938c782012-02-29 15:14:38 -0800620
Brian Harring218e13c2012-10-10 16:21:26 -0700621 # If a command is not given, default to enter.
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400622 # pylint: disable=protected-access
623 # This _group_actions access sucks, but upstream decided to not include an
624 # alternative to optparse's option_list, and this is what they recommend.
Brian Harring218e13c2012-10-10 16:21:26 -0700625 options.enter |= not any(getattr(options, x.dest)
Mike Frysinger2f95cfc2015-06-04 04:00:26 -0400626 for x in commands._group_actions)
627 # pylint: enable=protected-access
Brian Harring218e13c2012-10-10 16:21:26 -0700628 options.enter |= bool(chroot_command)
629
630 if options.enter and options.delete and not options.create:
631 parser.error("Trying to enter the chroot when --delete "
632 "was specified makes no sense.")
633
Benjamin Gordon35194f12017-07-19 10:26:22 -0600634 # Discern if we need to create the chroot.
635 chroot_exists = os.path.exists(os.path.join(
636 options.chroot, 'etc', 'cros_chroot_version'))
Brian Harring218e13c2012-10-10 16:21:26 -0700637 if options.create or options.enter:
638 # Only create if it's being wiped, or if it doesn't exist.
639 if not options.delete and chroot_exists:
640 options.create = False
641 else:
642 options.download = True
643
644 # Finally, flip create if necessary.
645 if options.enter:
646 options.create |= not chroot_exists
Brian Harringb938c782012-02-29 15:14:38 -0800647
Brian Harringb938c782012-02-29 15:14:38 -0800648 if not options.sdk_version:
Brian Harring1790ac42012-09-23 08:53:33 -0700649 sdk_version = (bootstrap_latest_version if options.bootstrap
650 else sdk_latest_version)
Brian Harringb938c782012-02-29 15:14:38 -0800651 else:
652 sdk_version = options.sdk_version
Mike Frysinger34db8692013-11-11 14:54:08 -0500653 if options.buildbot_log_version:
Prathmesh Prabhu17f07422015-07-17 11:40:40 -0700654 logging.PrintBuildbotStepText(sdk_version)
Brian Harringb938c782012-02-29 15:14:38 -0800655
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700656 # Based on selections, determine the tarball to fetch.
Brian Harring1790ac42012-09-23 08:53:33 -0700657 if options.sdk_url:
658 urls = [options.sdk_url]
659 elif options.bootstrap:
660 urls = GetStage3Urls(sdk_version)
661 else:
662 urls = GetArchStageTarballs(sdk_version)
663
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700664 # Get URLs for the toolchains overlay, if one is to be used.
665 toolchains_overlay_urls = None
666 if not options.bootstrap:
667 toolchains = None
668 if options.toolchains:
669 toolchains = options.toolchains.split(',')
670 elif options.board:
671 toolchains = toolchain.GetToolchainsForBoard(options.board).keys()
672
673 if toolchains:
674 toolchains_overlay_urls = GetToolchainsOverlayUrls(sdk_version,
675 toolchains)
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700676
Brian Harringb6cf9142012-09-01 20:43:17 -0700677 lock_path = os.path.dirname(options.chroot)
Gilad Arnoldfbe40e22015-03-18 14:52:16 -0700678 lock_path = os.path.join(
679 lock_path, '.%s_lock' % os.path.basename(options.chroot).lstrip('.'))
Mike Frysinger80dfce92014-04-21 10:58:53 -0400680 with cgroups.SimpleContainChildren('cros_sdk', pid=first_pid):
David James56e6c2c2012-10-24 23:54:41 -0700681 with locking.FileLock(lock_path, 'chroot lock') as lock:
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700682 toolchains_overlay_tarball = None
Brian Harring1790ac42012-09-23 08:53:33 -0700683
Josh Triplett472a4182013-03-08 11:48:57 -0800684 if options.proxy_sim:
685 _ProxySimSetup(options)
686
David James56e6c2c2012-10-24 23:54:41 -0700687 if options.delete and os.path.exists(options.chroot):
688 lock.write_lock()
689 DeleteChroot(options.chroot)
Brian Harringb938c782012-02-29 15:14:38 -0800690
David James56e6c2c2012-10-24 23:54:41 -0700691 sdk_cache = os.path.join(options.cache_dir, 'sdks')
692 distfiles_cache = os.path.join(options.cache_dir, 'distfiles')
Yu-Ju Hong2c066762013-10-28 14:05:08 -0700693 osutils.SafeMakedirsNonRoot(options.cache_dir)
Brian Harringae0a5322012-09-15 01:46:51 -0700694
David James56e6c2c2012-10-24 23:54:41 -0700695 for target in (sdk_cache, distfiles_cache):
Mike Frysinger648ba2d2013-01-08 14:19:34 -0500696 src = os.path.join(constants.SOURCE_ROOT, os.path.basename(target))
David James56e6c2c2012-10-24 23:54:41 -0700697 if not os.path.exists(src):
Prathmesh Prabhu06a50562016-10-22 01:41:44 -0700698 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -0700699 continue
700 lock.write_lock(
701 "Upgrade to %r needed but chroot is locked; please exit "
702 "all instances so this upgrade can finish." % src)
703 if not os.path.exists(src):
704 # Note that while waiting for the write lock, src may've vanished;
705 # it's a rare race during the upgrade process that's a byproduct
706 # of us avoiding taking a write lock to do the src check. If we
707 # took a write lock for that check, it would effectively limit
708 # all cros_sdk for a chroot to a single instance.
Prathmesh Prabhu06a50562016-10-22 01:41:44 -0700709 osutils.SafeMakedirsNonRoot(target)
David James56e6c2c2012-10-24 23:54:41 -0700710 elif not os.path.exists(target):
711 # Upgrade occurred, but a reversion, or something whacky
712 # occurred writing to the old location. Wipe and continue.
713 os.rename(src, target)
714 else:
715 # Upgrade occurred once already, but either a reversion or
716 # some before/after separate cros_sdk usage is at play.
717 # Wipe and continue.
718 osutils.RmDir(src)
Brian Harringae0a5322012-09-15 01:46:51 -0700719
David James56e6c2c2012-10-24 23:54:41 -0700720 if options.download:
721 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700722 sdk_tarball = FetchRemoteTarballs(
723 sdk_cache, urls, 'stage3' if options.bootstrap else 'SDK')
724 if toolchains_overlay_urls:
725 toolchains_overlay_tarball = FetchRemoteTarballs(
726 sdk_cache, toolchains_overlay_urls, 'SDK toolchains overlay',
727 allow_none=True)
Brian Harring218e13c2012-10-10 16:21:26 -0700728
David James56e6c2c2012-10-24 23:54:41 -0700729 if options.create:
730 lock.write_lock()
Gilad Arnold6a8f0452015-06-04 11:25:18 -0700731 CreateChroot(options.chroot, sdk_tarball, toolchains_overlay_tarball,
Gilad Arnoldecc86fa2015-05-22 12:06:04 -0700732 options.cache_dir,
David Pursell3528e8e2015-07-07 11:22:15 -0700733 nousepkg=(options.bootstrap or options.nousepkg))
Brian Harring1790ac42012-09-23 08:53:33 -0700734
David James56e6c2c2012-10-24 23:54:41 -0700735 if options.enter:
736 lock.read_lock()
737 EnterChroot(options.chroot, options.cache_dir, options.chrome_root,
Don Garrett230d1b22015-03-09 16:21:19 -0700738 options.chrome_root_mount, options.workspace,
Hidehiko Abeb5daf2f2017-03-02 17:57:43 +0900739 options.goma_dir, options.goma_client_json,
Don Garrett230d1b22015-03-09 16:21:19 -0700740 chroot_command)