#!/usr/bin/env python # vim: ts=8 sts=4 sw=4 expandtab import sys import os import getpass import resource import subprocess import shlex import argparse import re import socket import time from runsics_def import * # Re-open stdout unbuffered because we want to see feedback live as it happens. sys.stdout = os.fdopen(sys.stdout.fileno(), 'w',0) # TODO These numbers should be available on the system to any application server_port = { 'sics': {'telnet': 60001, 'interrupt': 60002, 'server': 60003, 'quieck': 60004}, 'scriptval': {'telnet': 60011, 'interrupt': 60012, 'server': 60013, 'quieck': 60014} } inst_test_sockoffset = { 'echidna' : 0, 'wombat' : 100, 'kowari' : 200, 'dingo' : 300, 'quokka' : 400, 'platypus' : 500, 'pelican' : 600, 'taipan' : 700, 'lyrebird' : 800, 'kookaburra' : 900, 'bilby' : 1000, 'emu' : 1100 } deflt_dir = '/usr/local/sics/server' deflt_sockoffset = 'none' deflt_user = inst_user sics_killer = inst_user sics_checker = 'root' if os.environ.has_key('TEST_SICS'): deflt_test = os.environ['TEST_SICS'] else: deflt_test = 'none' def sics_preexec(): resource.setrlimit(resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY)) os.umask(0022) # TODO Launch simulated devices if TEST_SICS=fakedev. Must write launch script first. def start_cmd(server, args): if status_fn(server, args) != (0,0): print '%s is already running. Cowardly refusing to launch it twice' % server return if (args.sockoffset != 'none'): soffset = ':' + str(inst_test_sockoffset[args.sockoffset]) else: soffset = '' sicsenv = { 'none': '', 'fullsim': 'SICS_SIMULATION=full%s' % soffset, 'fakedev': 'SICS_SIMULATION=fakedev%s' % soffset, 'scriptval': 'SICS_SIMULATION=script_validator%s' % soffset } # Set SICS_SIMULATION environment variable if (server == 'scriptval'): SIMENV = sicsenv['scriptval'] else: SIMENV = sicsenv[args.test] start_str = 'sudo -u %s %s %s/SICServer -d %s' % (args.user, SIMENV, args.dir, args.config) # print "DEBUG: start_str:%s" % (start_str) # Suppress output if launching the script validator if (server == 'scriptval'): with open(os.devnull) as fp: subprocess.call(shlex.split(start_str), preexec_fn=sics_preexec, cwd=args.dir, stderr=fp, stdout=fp) else: subprocess.call(shlex.split(start_str), preexec_fn=sics_preexec, cwd=args.dir) status_cmd(server, args) def stop_cmd(server, args): INTMSG = 'SICSINT 6' (serv_port, pid) = status_cmd(server, args) if pid: if (args.sockoffset == 'none'): int_port = server_port[server]['interrupt'] else: int_port = server_port[server]['interrupt'] + inst_test_sockoffset[args.sockoffset] print "STOP %s(%d) by sending '%s' to socket %d" % (server, pid, INTMSG, int_port) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto( INTMSG + '\n\r', ('localhost', int_port) ) sock.close() if status_cmd(server, args) != (0,0): sys.stdout.write('Waiting ') for n in range(3): sys.stdout.write('.') time.sleep(1) if status_fn(server, args) != (0,0): continue else: break print '' else: return if status_fn(server, args) != (0,0): print 'Failed to stop %s' % server print "Fragging PID %d with default KILL" % (pid) subprocess.call(shlex.split('sudo -u %s /bin/kill %d' % (sics_killer, pid))) else: return if status_fn(server, args) != (0,0): print 'Failed again!' print "Terminating PID %d with EXTREME PREJUDICE (-15)" % (pid) subprocess.call(shlex.split('sudo -u %s /bin/kill -15 %d' % sics_killer, (pid))) else: return if status_cmd(server, args) != (0,0): print 'Why wont you die, Powers!!??' print 'I give up. Try calling a SICS programmer or sysadmin' def status_fn(server, args): if (args.sockoffset == 'none'): sock = server_port[server]['server'] else: sock = server_port[server]['server'] + inst_test_sockoffset[args.sockoffset] status_str = 'sudo -u %s netstat -nltp' % sics_checker netstat_str, err = subprocess.Popen(shlex.split(status_str), stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() # ffr: We can replace the previous Popen().communicate() line with the following two lines if Python is upgraded to V2.7 # with open(os.devnull) as fp: # netstat_str = subprocess.check_output(shlex.split(status_str), stderr=fp) # #Find PID of SICServer listening on server port m = re.search(':%d .* (\d+)\/SICServer' % sock, netstat_str) if m: pid = int(m.group(1)) return (sock, pid) else: return (0,0) def status_cmd(server, args): (sock,pid) = status_fn(server, args) if pid: print '%s listening on port %d, PID=%d' % (server, sock, pid) else: print '%s not running' % server return (sock, pid) args_adv_grp_help = """ Options providing more control. Usually only useful for SICS developers and testers. If you are running multiple instances of SICS on a single host you can set the TEST_SICS environment variable to one of the --test values to get nice defaults. """ args_config_help = 'Run SICS with the given configuration file. Default=%s' args_dir_help = 'Run SICS from the given directory. Default=%s' args_test_help = 'Run SICS in the given mode. Default=%s' args_user_help = 'Run SICS as the given user. Default=%s' args_sockoffset_help = """ Offset the sockets to allow multiple SICS instances on a single host. Default=%s """ args_sicsonly_help = 'Just run SICS without the script validator' args_scriptval_help = """ Launch script validator as well as SICServer. Use this to override the default action when the TEST_SICS environmant variable is set. """ args_dev_help = """ Launch SICS only in current directory listening on socket %s and with simulation = fullsim """ def main(**kwargs): global sics_killer, sics_checker runsics_cmd = {'start': start_cmd, 'stop': stop_cmd, 'status': status_cmd} # Setup defaults. Use nice defaults in test environments. # Parse arguments parser = argparse.ArgumentParser( description='SICS Server startup script.') cmd_grp = parser.add_argument_group('Commands', 'Runsics commands') adv_grp = parser.add_argument_group('Advanced', args_adv_grp_help) # Mandatory arguments cmd_grp.add_argument('cmd', choices=['start', 'stop', 'status']) # Advanced arguments adv_grp.add_argument('-c', '--config', help= args_config_help % inst_config, default = inst_config ) adv_grp.add_argument('-d', '--dir', help=args_dir_help % deflt_dir, default = deflt_dir) adv_grp.add_argument('-t', '--test', choices=['fullsim', 'fakedev', 'scriptval'], help=args_dir_help % deflt_test, default=deflt_test) adv_grp.add_argument('-u', '--user', help= args_user_help % deflt_user, default = deflt_user ) adv_grp.add_argument('--sockoffset', help= args_sockoffset_help % deflt_sockoffset, choices=inst_test_sockoffset.keys(), default = deflt_sockoffset ) exc_grp = parser.add_mutually_exclusive_group() exc_grp.add_argument('--sicsonly', help= args_sicsonly_help, action='store_true', default=False) exc_grp.add_argument('--scriptval', help= args_scriptval_help, action='store_true', default=False) exc_grp.add_argument('--dev', help= args_dir_help % (server_port['sics']['server'] + inst_test_sockoffset[inst_name]), action='store_true') args = parser.parse_args() # By default don't launch script validator in development environments. if args.dev: if args.scriptval: args.sicsonly = False else: args.sicsonly = True args.sockoffset = inst_name args.user = getpass.getuser() sics_killer = getpass.getuser() sics_checker = getpass.getuser() if (args.test == 'none'): args.test = 'fullsim' if (args.dir == deflt_dir): args.dir = './' # Launch SICS if (args.sicsonly): runsics_cmd[args.cmd]('sics', args) else: runsics_cmd[args.cmd]('sics', args) runsics_cmd[args.cmd]('scriptval', args) if __name__ == '__main__': main()