#!/usr/bin/python

import os
import sys
import fcntl
import tty
import termios

hostfsdir = '/host'

def main():
    try:
        channels = None
        for arg in open('/proc/cmdline').read().split():
            argsplit = arg.split('=')
            if len(argsplit) > 1 and argsplit[0] == 'umlrun':
                channels = argsplit[1].split(',')

        if not channels:
            print "No umlrun invocation detected, proceeding with normal startup"
            cleanup()
            sys.exit(0)

        controlchan = open(umldev_to_devpath(channels[0]),'r+')
        datafd = os.open(umldev_to_devpath(channels[1]), os.O_RDWR)

        # Disable echo
        tcattr = termios.tcgetattr(controlchan.fileno())
        tcattr[3] = tcattr[3] & ~termios.ECHO
        termios.tcsetattr(controlchan.fileno(), termios.TCSANOW, tcattr)

        # set close-on-exec
        fcntl.fcntl(controlchan.fileno(), fcntl.F_SETFD, 1)
        
        controlchan.write('umlrun-2\n')

        SUCCESS, FAILURE = 0, 1
        while 1:
            line = controlchan.readline()
            if not line:
                break

            command,args = line[:-1].split(' ', 1)
            if not command:
                break

            status, text = FAILURE, 'Internal error'
            if command == 'EXEC' or command == 'EXEC_INTERACTIVE':
                if command == 'EXEC':
                    execstatus = execute_shellcmd(args, datafd)
                else:
                    execstatus = execute_shellcmd(args, datafd, 1)
                if execstatus == 0:
                    status, text = SUCCESS, 'Command executed successfully'
                else:
                    status, text = FAILURE, 'Command exited with status %d' % execstatus
            elif command == 'SHUTDOWN':
                controlchan.write('200 Shutting down\n')
                os.system('shutdown -h -t1 now')
                break
            elif command == 'FASTSHUTDOWN':
                controlchan.write('200 Shutting down\n')
                os.system('halt -n -f')
                break
            elif command == 'SETENV':
                name, value = args.split(' ', 1)
                os.putenv(name, value)
                status, text = SUCCESS, 'Environment variable set'
            elif command == 'MOUNT_HOST':
                if os.getenv('UMLRUN_HOSTMOUNT'):
                    status, text = SUCCESS, 'Host already mounted'
                else:
                    cwd = args
                    if not os.path.isdir(hostfsdir):
                        os.makedirs(hostfsdir)
                    status = os.system('mount -t hostfs hostfs ' + hostfsdir)
                    if status != 0:
                        status, text = 'mount command gave status %d' % status

                    os.putenv('UMLRUN_HOSTMOUNT', hostfsdir)
                    os.putenv('UMLRUN_HOSTCWD', hostfsdir + cwd)
                    status, text = SUCCESS, 'Host mounted'
            elif command == 'ADDUSER':
                username, uid = args.split(' ', 1)
                ret = os.system('adduser --uid %s %s' % (username, uid))
                if ret != 0:
                    status, text = FAILURE, 'adduser command gave status %d' % ret
            else:
                status, text = FAILURE, 'Unrecognized command %s' % command

            if status == SUCCESS:
                response = '200 ' + text
            else:
                response = '400 ' + text

            controlchan.write(response + '\n')

    finally:
        cleanup()

def cleanup():
    os.chdir('/')

def umldev_to_devpath(dev):
    if dev[:3] == 'ssl':
        return '/dev/ttyS' + dev[3:]
    if dev[:3] == 'con':
        return '/dev/tty' + dev[3:]
    raise 'Unrecognized UML device: ' + dev

def execute_shellcmd(cmd, iofd, interactive=0):
    pid = os.fork()
    if pid == 0:
        if interactive:
            os.setsid()
            fcntl.ioctl(iofd, tty.TIOCSCTTY)
        os.dup2(iofd, 0)
        os.dup2(iofd, 1)
        os.dup2(iofd, 2)
        os.execl('/bin/sh','sh','-c',cmd)
        sys.exit(1)
    else:
        status = os.waitpid(pid,0)
        if os.WIFEXITED(status[1]):
            return os.WEXITSTATUS(status[1])
        return status[1]

if __name__ == '__main__':
    main()
