#!/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 get_executor(name):
    # Convert the name into a valid Python module name. This is a convenience
    # for users just because '-' is easier to type than '_'.
    name = name.replace('-', '_')

    try:
        executor = getattr(sandboxlib, name)
    except AttributeError:
        raise RuntimeError(
            "%s is not a known executor in this version of 'sandboxlib'." %
            name)

    return executor


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'],
        type=str, default='chroot',
        help="which sandboxing backend to use")

    return parser.parse_args()


def run():
    args = parse_args()

    executor = 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)
