#!/usr/bin/python
#
# adt-virt-schroot is part of autopkgtest
# autopkgtest is a tool for testing Debian binary packages
#
# autopkgtest is Copyright (C) 2006-2007 Canonical Ltd.
#
# 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; either version 2 of the License, or
# (at your option) any later version.
#
# 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, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# See the file CREDITS for a full list of credits information (often
# installed as /usr/share/doc/autopkgtest/CREDITS).

import sys
import os
import re
import grp
import pwd
import subprocess
from optparse import OptionParser

try:
    our_base = os.environ['AUTOPKGTEST_BASE'] + '/lib'
except KeyError:
    our_base = '/usr/share/autopkgtest/python'
sys.path.insert(1, our_base)

import VirtSubproc
capabilities = ['downtmp-shared-fifo']

schroot = None
sessid = None
rootdir = None


def pw_uid(exp_name):
    try:
        return pwd.getpwnam(exp_name).pw_uid
    except KeyError:
        return None


def gr_gid(exp_name):
    try:
        return grp.getgrnam(exp_name).gr_gid
    except KeyError:
        return None


def match(exp_names, ids, extract_id):
    for exp_name in [n for n in exp_names.split(',') if n]:
        if extract_id(exp_name) in ids:
            return True
    return False


def parse_args():
    global schroot, sessid

    usage = "%prog [<options>] <schroot>"
    parser = OptionParser(usage=usage)

    parser.add_option('-d', '--debug', action='store_true')
    parser.add_option('-s', '--session-id',
                      help='custom schroot session ID for easy identification '
                      'in "schroot --list --all-sessions"')

    (opts, args) = parser.parse_args()
    if len(args) != 1:
        parser.error("need exactly one arg, schroot name")

    sessid = opts.session_id
    schroot = args[0]

    info = VirtSubproc.execute('schroot --config -c', [schroot],
                               downp=False, outp=True)
    cfg = {}
    ignore_re = re.compile('\#|\[|\s*$')
    for entry in info.split("\n"):
        if ignore_re.match(entry):
            continue
        (key, val) = entry.split("=", 2)
        cfg[key] = val

    VirtSubproc.debuglevel = opts.debug

    VirtSubproc.debug('schroot config: %s' % cfg)

    if re.search('snapshot', cfg['type']):
        VirtSubproc.debug('have "revert" capability')
        capabilities.append('revert')

    if (match(cfg['root-users'], [os.getuid()], pw_uid) or
            match(cfg['root-groups'], [os.getgid()] + os.getgroups(), gr_gid) or
            os.getuid() == 0):
        VirtSubproc.debug('have "root-on-testbed" capability')
        capabilities.append('root-on-testbed')

    if os.geteuid() != 0:
        username = pwd.getpwuid(os.geteuid()).pw_name
        capabilities.append('suggested-normal-user=' + username)


def hook_open():
    global schroot, sessid
    sessid = VirtSubproc.execute(
        'schroot -b -c', [schroot] + (sessid and ['-n', sessid] or []),
        downp=False, outp=True)
    VirtSubproc.down = ['schroot', '-r', '-d', '/', '-c', sessid]
    if 'root-on-testbed' in capabilities:
        VirtSubproc.down += ['-u', 'root']
    VirtSubproc.down += ['--']
    VirtSubproc.downkind = 'auxverb'


def hook_downtmp():
    global capabilities

    d = VirtSubproc.downtmp_mktemp()

    # determine mount location
    # FIXME: "schroot --location -c sessid" really ought to work, but fails; so
    # grep from --all-sessions until this gets fixed
    location = VirtSubproc.execute('schroot --location --all-sessions | grep',
                                   [sessid], downp=False, outp=True).strip()
    VirtSubproc.debug('location of schroot session: %s' % location)
    downtmp_host = '%s/%s' % (location, d)
    if os.access(downtmp_host, os.W_OK):
        VirtSubproc.debug('%s is writable, registering as downtmp_host' % downtmp_host)
        capabilities.append('downtmp-host=' + downtmp_host)
    else:
        VirtSubproc.debug('%s is not writable, downtmp_host not supported' % downtmp_host)
    return d


def hook_revert():
    hook_cleanup()
    hook_open()


def hook_cleanup():
    global schroot, sessid, capabilities
    VirtSubproc.downtmp_remove()
    VirtSubproc.execute('schroot -e -c', [sessid], downp=False)
    capabilities = [c for c in capabilities if not c.startswith('downtmp-host')]


def hook_forked_inchild():
    pass


def hook_capabilities():
    return capabilities


def hook_shell(stdin, stdout, stderr, dir):
    argv = ['schroot', '-r', '-d', dir, '-c', sessid]
    if 'root-on-testbed' in capabilities:
        argv += ['-u', 'root']
    with open(stdin, 'rb') as sin:
        with open(stdout, 'wb') as sout:
            with open(stderr, 'wb') as serr:
                subprocess.call(argv, stdin=sin, stdout=sout, stderr=serr)


parse_args()
VirtSubproc.main()
