summaryrefslogtreecommitdiff
path: root/run-sandbox
blob: 06582df85d0def16a0ad0201ba59b0b3a9bbbbc7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/python3
#
# Copyright (C) 2015  Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.


'''Commandline 'run things in a sandbox' tool.'''


import argparse
import logging
import sys

import sandboxlib


def info(message, *args):
    # FIXME: disable by default, add --verbose flag
    print(message % args)
    logging.info(message % args)


def parse_args():
    parser = argparse.ArgumentParser(description="Run something in a sandbox.")

    parser.add_argument(
        'sandbox', metavar='PATH', type=str,
        help="path to sandbox (image file, or directory tree)")
    parser.add_argument(
        'command', metavar='COMMAND', type=str, nargs='?',
        help="command to run in sandbox")

    parser.add_argument(
        '--cwd',
        type=str, required=False,
        help="current working directory for COMMAND")
    parser.add_argument(
        '--executor', '-e',
        choices=['chroot', 'linux_user_chroot', 'linux-user-chroot', 'bubblewrap'],
        type=str, default='chroot',
        help="which sandboxing backend to use")

    return parser.parse_args()


def run():
    args = parse_args()

    executor = sandboxlib.get_executor(args.executor)

    if sandboxlib.load.appc.is_app_container_image(args.sandbox):
        info("%s is an App Container image." % args.sandbox)
        context = sandboxlib.load.appc.unpack_app_container_image(
            args.sandbox)
        with context as (rootfs_path, manifest):
            if args.command is None:
                command = manifest['app']['exec']
            else:
                command = args.command

            cwd = None
            if args.cwd is not None:
                cwd = args.cwd
            elif 'workingDirectory' in manifest['app']:
                cwd = manifest['app']['workingDirectory']

            env = sandboxlib.load.appc.BASE_ENVIRONMENT.copy()

            if 'environment' in manifest['app']:
                for item in manifest['app']['environment']:
                    env[item['name']] = item['value']

            env['AC_APP_NAME'] = manifest['name']

            sharing_config = executor.maximum_possible_isolation()

            extra_mounts = [
                (None, '/proc', 'proc', None)
            ]

            exit, out, err = executor.run_sandbox(
                rootfs_path, command, cwd=cwd, env=env,
                extra_mounts=extra_mounts, **sharing_config)

            # We'll take a punt on the output being valid UTF-8.
            print(out.decode('utf-8'))
            print(err.decode('utf-8'))
    else:
        # We should at minimum handle filesystem trees as well.
        raise RuntimeError(
            "Only App Container images are supported right now.")


try:
    run()
except RuntimeError as e:
    print("ERROR: %s" % e)