1825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart# Use of this source code is governed by a BSD-style license that can be
3825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart# found in the LICENSE file.
4825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
55f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewartimport errno
6825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewartimport os
70174334399baf09670d361acfb2ac84ab951b3b4Paul Stewartimport shutil
8825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewartimport time
9825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
10825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewartfrom autotest_lib.client.bin import utils
11825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
12825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewartclass NetworkChroot(object):
13825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    """Implements a chroot environment that runs in a separate network
14825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    namespace from the caller.  This is useful for network tests that
15825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    involve creating a server on the other end of a virtual ethernet
16825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    pair.  This object is initialized with an interface name to pass
17825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    to the chroot, as well as the IP address to assign to this
18825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    interface, since in passing the interface into the chroot, any
19825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    pre-configured address is removed.
20825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
21825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    The startup of the chroot is an orchestrated process where a
22825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    small startup script is run to perform the following tasks:
23825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart      - Write out pid file which will be a handle to the
24825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        network namespace that that |interface| should be passed to.
25825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart      - Wait for the network namespace to be passed in, by performing
26825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        a "sleep" and writing the pid of this process as well.  Our
27825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        parent will kill this process to resume the startup process.
28825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart      - We can now configure the network interface with an address.
29825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart      - At this point, we can now start any user-requested server
30825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        processes.
31825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    """
3260dc4d6a6ea7620898027f77f1818f2a8acd72dbPaul Stewart    BIND_ROOT_DIRECTORIES = ('bin', 'dev', 'dev/pts', 'lib', 'lib32', 'lib64',
33825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart                             'proc', 'sbin', 'sys', 'usr', 'usr/local')
3460dc4d6a6ea7620898027f77f1818f2a8acd72dbPaul Stewart    # Subset of BIND_ROOT_DIRECTORIES that should be mounted writable.
3560dc4d6a6ea7620898027f77f1818f2a8acd72dbPaul Stewart    BIND_ROOT_WRITABLE_DIRECTORIES = frozenset(('dev/pts',))
366adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley    # Directories we'll bind mount when we want to bridge DBus namespaces.
376adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley    # Includes directories containing the system bus socket and machine ID.
386adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley    DBUS_BRIDGE_DIRECTORIES = ('var/run/dbus/', 'var/lib/dbus/')
3960dc4d6a6ea7620898027f77f1818f2a8acd72dbPaul Stewart
40825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    ROOT_DIRECTORIES = ('etc',  'tmp', 'var', 'var/log', 'var/run')
41825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    STARTUP = 'etc/chroot_startup.sh'
42825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    STARTUP_DELAY_SECONDS = 5
430174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart    STARTUP_PID_FILE = 'var/run/vpn_startup.pid'
440174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart    STARTUP_SLEEPER_PID_FILE = 'var/run/vpn_sleeper.pid'
450174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart    COPIED_CONFIG_FILES = [
460174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        'etc/ld.so.cache'
470174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart    ]
48825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    CONFIG_FILE_TEMPLATES = {
49825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        STARTUP:
50825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            '#!/bin/sh\n'
51825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'exec > /var/log/startup.log 2>&1\n'
52825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'set -x\n'
53825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'echo $$ > /%(startup-pidfile)s\n'
54825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'sleep %(startup-delay-seconds)d &\n'
55825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'echo $! > /%(sleeper-pidfile)s &\n'
56825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'wait\n'
57825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'ip addr add %(local-ip-and-prefix)s dev %(local-interface-name)s\n'
58825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'ip link set %(local-interface-name)s up\n'
59825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    }
60825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    CONFIG_FILE_VALUES = {
610174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        'sleeper-pidfile': STARTUP_SLEEPER_PID_FILE,
62825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        'startup-delay-seconds': STARTUP_DELAY_SECONDS,
630174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        'startup-pidfile': STARTUP_PID_FILE
64825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    }
65825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
66825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def __init__(self, interface, address, prefix):
67825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._interface = interface
68825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
69825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        # Copy these values from the class-static since specific instances
70825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        # of this class are allowed to modify their contents.
716adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        self._bind_root_directories = list(self.BIND_ROOT_DIRECTORIES)
72825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._root_directories = list(self.ROOT_DIRECTORIES)
730174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        self._copied_config_files = list(self.COPIED_CONFIG_FILES)
74825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._config_file_templates = self.CONFIG_FILE_TEMPLATES.copy()
75825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._config_file_values = self.CONFIG_FILE_VALUES.copy()
76825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
77825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._config_file_values.update({
78825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'local-interface-name': interface,
79825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'local-ip': address,
80825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            'local-ip-and-prefix': '%s/%d' % (address, prefix)
81825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        })
82825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
83825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
84825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def startup(self):
85825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Create the chroot and start user processes."""
86825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self.make_chroot()
87825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self.write_configs()
88825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self.run(['/bin/bash', os.path.join('/', self.STARTUP), '&'])
89825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self.move_interface_to_chroot_namespace()
900174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        self.kill_pid_file(self.STARTUP_SLEEPER_PID_FILE)
91825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
92825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
93825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def shutdown(self):
94825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Remove the chroot filesystem in which the VPN server was running"""
95825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        # TODO(pstew): Some processes take a while to exit, which will cause
96825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        # the cleanup below to fail to complete successfully...
9789003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart        time.sleep(10)
98825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        utils.system_output('rm -rf --one-file-system %s' % self._temp_dir,
99825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart                            ignore_status=True)
100825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
101825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
102825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def add_config_templates(self, template_dict):
103825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Add a filename-content dict to the set of templates for the chroot
104825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
105825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        @param template_dict dict containing filename-content pairs for
106825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            templates to be applied to the chroot.  The keys to this dict
107825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            should not contain a leading '/'.
108825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
109825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """
110825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._config_file_templates.update(template_dict)
111825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
112825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
113825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def add_config_values(self, value_dict):
114825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Add a name-value dict to the set of values for the config template
115825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
116825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        @param value_dict dict containing key-value pairs of values that will
117825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            be applied to the config file templates.
118825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
119825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """
120825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._config_file_values.update(value_dict)
121825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
122825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
1236adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley    def add_copied_config_files(self, files):
1246adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        """Add |files| to the set to be copied to the chroot.
1256adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley
1266adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        @param files iterable object containing a list of files to
1276adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley            be copied into the chroot.  These elements should not contain a
1286adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley            leading '/'.
1296adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley
1306adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        """
1316adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        self._copied_config_files += files
1326adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley
1336adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley
134825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def add_root_directories(self, directories):
135825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Add |directories| to the set created within the chroot.
136825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
137825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        @param directories list/tuple containing a list of directories to
138825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            be created in the chroot.  These elements should not contain a
139825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            leading '/'.
140825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
141825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """
142825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._root_directories += directories
143825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
144825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
145825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def add_startup_command(self, command):
146825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Add a command to the script run when the chroot starts up.
147825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
148825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        @param command string containing the command line to run.
149825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
150825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """
151825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._config_file_templates[self.STARTUP] += '%s\n' % command
152825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
153825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
154825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def get_log_contents(self):
155825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Return the logfiles from the chroot."""
156825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        return utils.system_output("head -10000 %s" %
1570174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart                                   self.chroot_path("var/log/*"))
1580174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart
1590174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart
1606adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley    def bridge_dbus_namespaces(self):
1616adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        """Make the system DBus daemon visible inside the chroot."""
1626adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        # Need the system socket and the machine-id.
1636adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        self._bind_root_directories += self.DBUS_BRIDGE_DIRECTORIES
1646adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley
1656adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley
1660174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart    def chroot_path(self, path):
1670174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        """Returns the the path within the chroot for |path|.
1680174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart
1690174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        @param path string filename within the choot.  This should not
1700174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart            contain a leading '/'.
1710174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart
1720174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        """
1730174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        return os.path.join(self._temp_dir, path.lstrip('/'))
174825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
175825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
17689003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart    def get_pid_file(self, pid_file, missing_ok=False):
177825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Returns the integer contents of |pid_file| in the chroot.
178825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
179825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        @param pid_file string containing the filename within the choot
180825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            to read and convert to an integer.  This should not contain a
181825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            leading '/'.
1825f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart        @param missing_ok bool indicating whether exceptions due to failure
1835f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart            to open the pid file should be caught.  If true a missing pid
1845f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart            file will cause this method to return 0.  If false, a missing
1855f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart            pid file will cause an exception.
186825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
187825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """
18889003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart        chroot_pid_file = self.chroot_path(pid_file)
1895f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart        try:
1905f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart            with open(chroot_pid_file) as f:
1915f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart                return int(f.read())
1925f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart        except IOError, e:
1935f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart            if not missing_ok or e.errno != errno.ENOENT:
1945f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart                raise e
1955f0a57585e4ea85033b7ffcb68177174ecd1e685Paul Stewart
19689003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart            return 0
197825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
198825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
19989003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart    def kill_pid_file(self, pid_file, missing_ok=False):
200825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Kills the process belonging to |pid_file| in the chroot.
201825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
20289003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart        @param pid_file string filename within the chroot to gain the process ID
203825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            which this method will kill.
20489003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart        @param missing_ok bool indicating whether a missing pid file is okay,
20589003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart            and should be ignored.
206825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
207825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """
2086adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        pid = self.get_pid_file(pid_file, missing_ok=missing_ok)
20989003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart        if missing_ok and pid == 0:
21089003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart            return
21189003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart        utils.system('kill %d' % pid, ignore_status=True)
212825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
213825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
214825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def make_chroot(self):
215825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Make a chroot filesystem."""
216825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._temp_dir = utils.system_output('mktemp -d /tmp/chroot.XXXXXXXXX')
21789003937480a6ca69bfe30c04d7ec198fc7bcf46Paul Stewart        utils.system('chmod go+rX %s' % self._temp_dir)
218825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        for rootdir in self._root_directories:
2190174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart            os.mkdir(self.chroot_path(rootdir))
220825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
221825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        self._jail_args = []
2226adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley        for rootdir in self._bind_root_directories:
223825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            src_path = os.path.join('/', rootdir)
2240174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart            dst_path = self.chroot_path(rootdir)
225825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            if not os.path.exists(src_path):
226825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart                continue
227825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            elif os.path.islink(src_path):
228825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart                link_path = os.readlink(src_path)
229825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart                os.symlink(link_path, dst_path)
230825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart            else:
2316adc4bc45def76077f00302ff0afebb3a9552a6bChristopher Wiley                os.makedirs(dst_path)  # Recursively create directories.
23260dc4d6a6ea7620898027f77f1818f2a8acd72dbPaul Stewart                mount_arg = '%s,%s' % (src_path, src_path)
23360dc4d6a6ea7620898027f77f1818f2a8acd72dbPaul Stewart                if rootdir in self.BIND_ROOT_WRITABLE_DIRECTORIES:
23460dc4d6a6ea7620898027f77f1818f2a8acd72dbPaul Stewart                    mount_arg += ',1'
23560dc4d6a6ea7620898027f77f1818f2a8acd72dbPaul Stewart                self._jail_args += [ '-b', mount_arg ]
236825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
2370174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart        for config_file in self._copied_config_files:
2380174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart            src_path = os.path.join('/', config_file)
2390174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart            dst_path = self.chroot_path(config_file)
2400174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart            if os.path.exists(src_path):
2410174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart                shutil.copyfile(src_path, dst_path)
2420174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart
243825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
244825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def move_interface_to_chroot_namespace(self):
245825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Move network interface to the network namespace of the server."""
246825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        utils.system('ip link set %s netns %d' %
2470174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart                     (self._interface,
2480174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart                      self.get_pid_file(self.STARTUP_PID_FILE)))
249825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
250825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
251e6f9e43354f1fb248530ce2beea6cb448cb7fba0Paul Stewart    def run(self, args, ignore_status=False):
252825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Run a command in a chroot, within a separate network namespace.
253825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
254825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        @param args list containing the command line arguments to run.
255e6f9e43354f1fb248530ce2beea6cb448cb7fba0Paul Stewart        @param ignore_status bool set to true if a failure should be ignored.
256825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
257825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """
258825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        utils.system('minijail0 -e -C %s %s' %
259e6f9e43354f1fb248530ce2beea6cb448cb7fba0Paul Stewart                     (self._temp_dir, ' '.join(self._jail_args + args)),
260e6f9e43354f1fb248530ce2beea6cb448cb7fba0Paul Stewart                     ignore_status=ignore_status)
261825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
262825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart
263825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart    def write_configs(self):
264825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        """Write out config files"""
265825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart        for config_file, template in self._config_file_templates.iteritems():
2660174334399baf09670d361acfb2ac84ab951b3b4Paul Stewart            with open(self.chroot_path(config_file), 'w') as f:
267825b19d16d586a0bd48ae3364e3b4da36c300933Paul Stewart                f.write(template % self._config_file_values)
268