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