blob: 14047cfcf8b78888eea9b493c80f007b8007719e [file] [log] [blame]
#! /usr/bin/python
# Copyright 2016 Google Inc. All Rights Reserved.
"""Run a process on a pty, listen for clients on a socket.
For processes that are long-lived, or need a tty.
Use 'nc localhost 1234' to connect to the process and ^D to disconnect.
"""
__author__ = 'edjames@google.com (Ed James)'
import getopt
import os
import select
import signal
import socket
import sys
# arg defaults
def_host = 'localhost'
def_port = 1964
def usage():
print ('Usage: %s [-h)ost hostname] [-p)ort port] [-b)lock] command [args]'
% sys.argv[0])
print ' defaults: hostname=%s port=%d' % (def_host, def_port)
print ' The block flag waits for clients before reading from the process,'
print ' blocking it. Use this if you want clients to get all output,'
print ' at the cost of blocking the process when no client is connected.'
def main():
try:
opts, args = getopt.getopt(sys.argv[1:], 'h:p:b',
['host=', 'port=', 'block'])
except getopt.GetoptError as err:
print str(err)
usage()
sys.exit(1)
host = def_host
port = def_port
block = False
for o, a in opts:
if o in ('-h', '--host'):
host = a
elif o in ('-p', '--port'):
port = int(a)
elif o in ('-b', '--block'):
block = True
else:
print 'unknown option: %s' % o
usage()
sys.exit(1)
command = args
if not args:
print 'required command is missing'
usage()
sys.exit(1)
print '!!! listening on %s:%d' % (host, port)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(5)
s_fd = s.fileno()
pid, pty_fd = os.forkpty()
if pid == 0:
os.execvp(command[0], command)
sys.exit(1)
client_fd = -1
while True:
# build list of fd's for select
rlist = []
if not block or client_fd != -1:
rlist.append(pty_fd)
if client_fd == -1:
rlist.append(s_fd)
else:
rlist.append(client_fd)
# wait for input
rready, unused_, unused_ = select.select(rlist, [], [])
# handle new client
if s_fd in rready:
(client, unused_) = s.accept()
client_fd = client.fileno()
print '!!! new client'
# handle client input
if client_fd in rready:
try:
data = os.read(client_fd, 1024)
except OSError as err:
print '!!! client exception: %s' % str(err)
data = ''
if not data:
print '!!! client EOF'
client.close()
client_fd = -1
else:
print '<<< ', data
try:
os.write(pty_fd, data)
except OSError as err:
print '!!! write to server exception: %s' % str(err)
break
# handle command output
if pty_fd in rready:
try:
data = os.read(pty_fd, 1024)
except OSError as err:
print '!!! read from server exception: %s' % str(err)
break
if not data:
print '!!! server process EOF'
break
if client_fd != -1:
try:
os.write(client_fd, data)
sys.stdout.write(data)
except OSError as err:
print '!!! client exception: %s' % str(err)
client.close()
client_fd = -1
# shut down
if client_fd != -1:
client.close()
s.close()
os.close(pty_fd)
os.kill(pid, signal.SIGTERM)
sys.exit(1)
if __name__ == '__main__':
main()