Exemplo n.º 1
0
def run_server(options, args):
    global IO_loop, Http_server, Local_client, Host_secret, Trace_shell
    import signal

    def auth_token(secret, connection_id, client_nonce, server_nonce):
        """Return (client_token, server_token)"""
        SIGN_SEP = "|"
        prefix = SIGN_SEP.join([connection_id, client_nonce, server_nonce]) + SIGN_SEP
        return [hmac.new(str(secret), prefix+conn_type, digestmod=hashlib.sha256).hexdigest()[:24] for conn_type in ("client", "server")]

    class AuthHandler(tornado.web.RequestHandler):
        def get(self):
            self.set_header("Content-Type", "text/plain")
            client_nonce = self.get_argument("nonce", "")
            if not client_nonce:
                raise tornado.web.HTTPError(401)
            server_nonce = "1%018d" % random.randrange(0, 10**18)   # 1 prefix to keep leading zeros when stringified
            try:
                client_token, server_token = auth_token(Gterm_secret, "graphterm", client_nonce, server_nonce)
            except Exception:
                raise tornado.web.HTTPError(401)

            # TODO NOTE: Save server_token to authenticate next connection

            self.set_header("Content-Type", "text/plain")
            self.write(server_nonce+":"+client_token)

    try:
        # Create App directory
        os.mkdir(App_dir, 0700)
    except OSError:
        if os.stat(App_dir).st_mode != 0700:
            # Protect App directory
            os.chmod(App_dir, 0700)

    auth_file = ""
    random_auth = False
    if not options.auth_code:
        # Default (random) auth code
        random_auth = True
        auth_code = GTSocket.get_auth_code()
    elif options.auth_code == "none":
        # No auth code
        auth_code = ""
        GTSocket.set_auth_code(auth_code)
    else:
        # Specified auth code
        auth_code = options.auth_code
        GTSocket.set_auth_code(auth_code)

    http_port = options.port
    http_host = options.host
    internal_host = options.internal_host or http_host
    internal_port = options.internal_port or http_port-1

    handlers = []
    if options.server_auth:
        handlers += [(r"/_auth/.*", AuthHandler)]
        with open(Gterm_secret_file, "w") as f:
            f.write("%d %d %s\n" % (http_port, os.getpid(), Gterm_secret))
        os.chmod(Gterm_secret_file, stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR)

    if options.auth_users:
        for user in options.auth_users.split(","):
            if user:
                if random_auth:
                    # Personalized user auth codes
                    GTSocket._auth_users[user] = hmac.new(str(random_auth), user, digestmod=hashlib.sha256).hexdigest()[:HEX_DIGITS]
                else:
                    # Same auth code for all users
                    GTSocket._auth_users[user] = GTSocket.get_auth_code()

    if auth_code:
        if os.getenv("HOME", ""):
            auth_file = Default_auth_file
            try:
                with open(auth_file, "w") as f:
                    f.write("%s://%s:%d/?code=%s\n" % (PROTOCOL, http_host, http_port, auth_code));
                    if GTSocket._auth_users:
                        for user, key in GTSocket._auth_users.items():
                            f.write("%s %s://%s:%d/?user=%s&code=%s\n" % (user, PROTOCOL, http_host, http_port, user, key));
                    os.chmod(auth_file, stat.S_IRUSR|stat.S_IWUSR|stat.S_IXUSR)
            except Exception:
                logging.warning("Failed to create auth file: %s", auth_file)
                auth_file = ""

    handlers += [(r"/_websocket/.*", GTSocket),
                 (r"/static/(.*)", tornado.web.StaticFileHandler, {"path": Doc_rootdir}),
                 (r"/blob/(.*)", ProxyFileHandler, {}),
                 (r"/file/(.*)", ProxyFileHandler, {}),
                 (r"/().*", tornado.web.StaticFileHandler, {"path": Doc_rootdir, "default_filename": "index.html"}),
                 ]

    application = tornado.web.Application(handlers)

    ##logging.warning("DocRoot: "+Doc_rootdir);

    IO_loop = tornado.ioloop.IOLoop.instance()

    ssl_options = None
    if options.https or options.client_cert:
        cert_dir = App_dir
        server_name = "localhost"
        certfile = cert_dir+"/"+server_name+".crt"
        keyfile = cert_dir+"/"+server_name+".key"

        new = not (os.path.exists(certfile) and os.path.exists(keyfile))
        fingerprint = ssl_cert_gen(server_name, cwd=cert_dir, new=new)
        if not fingerprint:
            print >> sys.stderr, "gtermserver: Failed to generate server SSL certificate"
            sys.exit(1)
        print >> sys.stderr, fingerprint

        ssl_options = {"certfile": certfile, "keyfile": keyfile}
        if options.client_cert:
            if options.client_cert == ".":
                ssl_options["ca_certs"] = certfile
            elif not os.path.exists(options.client_cert):
                print >> sys.stderr, "Client cert file %s not found" % options.client_cert
                sys.exit(1)
            else:
                ssl_options["ca_certs"] = options.client_cert
            ssl_options["cert_reqs"] = ssl.CERT_REQUIRED

    internal_server_ssl = {"certfile": certfile, "keyfile": keyfile} if options.internal_https else None
    internal_client_ssl = {"cert_reqs": ssl.CERT_REQUIRED, "ca_certs": certfile} if options.internal_https else None
    TerminalConnection.start_tcp_server(internal_host, internal_port, io_loop=IO_loop,
                                        key_secret=(options.server_secret or None), ssl_options=internal_server_ssl)

    if options.internal_https or options.nolocal:
        # Internal https causes tornado to loop  (client fails to connect to server)
        # Connecting to internal https from another process seems to be OK.
        # Need to rewrite packetserver.PacketConnection to use tornado.netutil.TCPServer
        Local_client, Host_secret, Trace_shell = None, None, None
    else:
        oshell_globals = globals() if otrace and options.oshell else None
        Local_client, Host_secret, Trace_shell = gtermhost.gterm_connect(LOCAL_HOST, internal_host,
                                                         server_port=internal_port,
                                                         connect_kw={"ssl_options": internal_client_ssl,
                                                                     "command": options.shell_command,
                                                                     "term_type": options.term_type,
                                                                     "term_encoding": options.term_encoding,
                                                                     "key_secret": options.server_secret or None,
                                                                     "lterm_logfile": options.lterm_logfile,
                                                                     "widget_port":
                                                                       (gtermhost.DEFAULT_HTTP_PORT-2 if options.widgets else 0)},
                                                         oshell_globals=oshell_globals,
                                                         oshell_unsafe=True,
                                                         oshell_thread=(not options.oshell_input),
                                                         oshell_no_input=(not options.oshell_input),
                                                         oshell_web_interface=TraceInterface,
                                                         io_loop=IO_loop)
        xterm = Local_client.xterm
        killterm = Local_client.remove_term

    Http_server = tornado.httpserver.HTTPServer(application, ssl_options=ssl_options)
    Http_server.listen(http_port, address=http_host)
    logging.warning("Auth code = %s %s" % (GTSocket.get_auth_code(), auth_file))

    if options.terminal:
        url = "%s://%s:%d/local/new" % ("https" if options.https else "http", http_host, http_port)
        gtermapi.open_browser(url)

    def test_fun():
        raise Exception("TEST EXCEPTION")

    def stop_server():
        global Http_server
        print >> sys.stderr, "\nStopping server"
        gtermhost.gterm_shutdown(Trace_shell)
        if Http_server:
            Http_server.stop()
            Http_server = None
        def stop_server_aux():
            IO_loop.stop()

        # Need to stop IO_loop only after all other scheduled shutdowns have completed
        IO_loop.add_callback(stop_server_aux)

    def sigterm(signal, frame):
        logging.warning("SIGTERM signal received")
        IO_loop.add_callback(stop_server)
    signal.signal(signal.SIGTERM, sigterm)

    try:
        ioloop_thread = threading.Thread(target=IO_loop.start)
        ioloop_thread.start()
        time.sleep(1)   # Time to start thread
        print >> sys.stderr, "GraphTerm server (v%s) listening on %s:%s" % (about.version, http_host, http_port)

        print >> sys.stderr, "\nType ^C to stop server"
        if Trace_shell:
            Trace_shell.loop()
        if not Trace_shell or not options.oshell_input:
            while Http_server:
                time.sleep(1)
    except KeyboardInterrupt:
        print >> sys.stderr, "Interrupted"

    finally:
        try:
            if options.server_auth:
                os.remove(Gterm_secret_file)
        except Exception:
            pass
    IO_loop.add_callback(stop_server)
Exemplo n.º 2
0
def main(args=None):
    import imp
    global Gterm_host, Host_secret, Trace_shell

    if args is None:
         args = sys.argv[1:]

    funcname = ""
    server = "localhost"
    hostname = ""
    j = 0
    while j < len(args)-1:
        if args[j] == "-f":
            funcname = args[j+1]
        elif args[j] == "-n":
            hostname = args[j+1]
        elif args[j] == "-s":
            server = args[j+1]
        else:
            break
        j += 2

    if j >= len(args):
        print >> sys.stderr, "Usage: gotrace [-f function_name] [-n hostname] [-s server_addr[:port] (default: localhost:%d)] program_file [arg1 arg2 ...]" % gtermhost.DEFAULT_HOST_PORT
        sys.exit(1)

    filepath = args[j]
    args = args[j+1:]
    
    if not os.path.isfile(filepath) or not os.access(filepath, os.R_OK):
        print >> sys.stderr, "gotrace: Unable to read file %s" % filepath
        sys.exit(1)

    abspath = os.path.abspath(filepath)
    filedir, basename = os.path.split(abspath)
    modname, extension = os.path.splitext(basename)

    if not hostname:
        hostname = modname

    if ":" in server:
        server, sep, port = server.partition(":")
        port = int(port)
    else:
        port = gtermhost.DEFAULT_HOST_PORT

    # Load program as module
    modfile, modpath, moddesc = imp.find_module(modname, [filedir])
    modobj = imp.load_module(modname, modfile, modpath, moddesc)

    orig_funcobj = getattr(modobj, funcname, None) if funcname else None
    if funcname and not callable(orig_funcobj):
        print >> sys.stderr, "gotrace: Program %s does not have function named '%s'" % (filepath, funcname)
        sys.exit(1)
        
    # Connect to gterm as host, invoking OShell
    oshell_globals = modobj.__dict__

    Gterm_host, Host_secret, Trace_shell = gtermhost.gterm_connect(hostname, server,
                                                         server_port=port,
                                                         connect_kw={},
                                                         oshell_globals=oshell_globals,
                                                         oshell_thread=True,
                                                         oshell_unsafe=True,
                                                         oshell_init=modname+".trc")
    def host_shutdown():
        print >> sys.stderr, "Shutting down"
        gtermhost.gterm_shutdown(Trace_shell)

    def sigterm(signal, frame):
        logging.warning("SIGTERM signal received")
        host_shutdown()

    signal.signal(signal.SIGTERM, sigterm)

    try:
        if funcname:
            # Delay to ensure tracing has started
            time.sleep(1)

            # Call function in module (may be wrapped, if being traced)
            funcobj = getattr(modobj, funcname)
            if args:
                funcobj(args)
            else:
                funcobj()
        else:
            # Blocks until run command is issued
            Trace_shell.loop(wait_to_run=True)

    except Exception, excp:
        traceback.print_exc()
        print >> sys.stderr, "\nType ^C to abort"
        Trace_shell.execute("cd ~~")
        while not Trace_shell.shutting_down:
            time.sleep(1)