socketserver.py 3.6 KB

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