1#! /usr/bin/env python 2 3""" 4 start socket based minimal readline exec server 5 6 it can exeuted in 2 modes of operation 7 8 1. as normal script, that listens for new connections 9 10 2. via existing_gateway.remote_exec (as imported module) 11 12""" 13# this part of the program only executes on the server side 14# 15 16import sys 17import os 18 19progname = 'socket_readline_exec_server-1.2' 20 21 22def get_fcntl(): 23 try: 24 import fcntl 25 except ImportError: 26 fcntl = None 27 return fcntl 28 29fcntl = get_fcntl() 30 31debug = 0 32 33if debug: # and not os.isatty(sys.stdin.fileno()) 34 f = open('/tmp/execnet-socket-pyout.log', 'w') 35 old = sys.stdout, sys.stderr 36 sys.stdout = sys.stderr = f 37 38 39def print_(*args): 40 print(" ".join(str(arg) for arg in args)) 41 42if sys.version_info > (3, 0): 43 exec("""def exec_(source, locs): 44 exec(source, locs)""") 45else: 46 exec("""def exec_(source, locs): 47 exec source in locs""") 48 49 50def exec_from_one_connection(serversock): 51 print_(progname, 'Entering Accept loop', serversock.getsockname()) 52 clientsock, address = serversock.accept() 53 print_(progname, 'got new connection from %s %s' % address) 54 clientfile = clientsock.makefile('rb') 55 print_("reading line") 56 # rstrip so that we can use \r\n for telnet testing 57 source = clientfile.readline().rstrip() 58 clientfile.close() 59 g = { 60 'clientsock': clientsock, 61 'address': address, 62 'execmodel': execmodel, 63 } 64 source = eval(source) 65 if source: 66 co = compile(source+'\n', source, 'exec') 67 print_(progname, 'compiled source, executing') 68 try: 69 exec_(co, g) # noqa 70 finally: 71 print_(progname, 'finished executing code') 72 # background thread might hold a reference to this (!?) 73 # clientsock.close() 74 75 76def bind_and_listen(hostport, execmodel): 77 socket = execmodel.socket 78 if isinstance(hostport, str): 79 host, port = hostport.split(':') 80 hostport = (host, int(port)) 81 serversock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 82 # set close-on-exec 83 if hasattr(fcntl, 'FD_CLOEXEC'): 84 old = fcntl.fcntl(serversock.fileno(), fcntl.F_GETFD) 85 fcntl.fcntl(serversock.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC) 86 # allow the address to be re-used in a reasonable amount of time 87 if os.name == 'posix' and sys.platform != 'cygwin': 88 serversock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 89 90 serversock.bind(hostport) 91 serversock.listen(5) 92 return serversock 93 94 95def startserver(serversock, loop=False): 96 try: 97 while 1: 98 try: 99 exec_from_one_connection(serversock) 100 except (KeyboardInterrupt, SystemExit): 101 raise 102 except: 103 if debug: 104 import traceback 105 traceback.print_exc() 106 else: 107 excinfo = sys.exc_info() 108 print_("got exception", excinfo[1]) 109 if not loop: 110 break 111 finally: 112 print_("leaving socketserver execloop") 113 serversock.shutdown(2) 114 115if __name__ == '__main__': 116 import sys 117 if len(sys.argv) > 1: 118 hostport = sys.argv[1] 119 else: 120 hostport = ':8888' 121 from execnet.gateway_base import get_execmodel 122 execmodel = get_execmodel("thread") 123 serversock = bind_and_listen(hostport, execmodel) 124 startserver(serversock, loop=False) 125elif __name__ == '__channelexec__': 126 chan = globals()['channel'] 127 execmodel = chan.gateway.execmodel 128 bindname = chan.receive() 129 sock = bind_and_listen(bindname, execmodel) 130 port = sock.getsockname() 131 chan.send(port) 132 startserver(sock) 133