Created
October 3, 2011 19:47
-
-
Save brownan/1260038 to your computer and use it in GitHub Desktop.
Run commands as if connecting through a slow modem
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from __future__ import division | |
import sys | |
import pty | |
import os | |
import subprocess | |
import termios | |
import fcntl | |
import signal | |
import time | |
usage = """ | |
Run a program as if through a slow modem. | |
Usage: baud.py <baud> <program> [args] ... | |
e.x.: $ baud.py 300 bash | |
""" | |
#Good reference on Linux TTY semantics: | |
#http://www.win.tue.nl/~aeb/linux/lk/lk-10.html | |
def postfork(): | |
"""This is executed in the forked child, before it executes the requested | |
program | |
""" | |
# Become a session leader by creating a new session. This detaches us from | |
# the previous controlling terminal | |
os.setsid() | |
# Now use the TIOCSCTTY ioctl to set our controlling terminal to whatever | |
# stdout is (the pty created earlier) | |
fcntl.ioctl(1, termios.TIOCSCTTY) | |
def passsignalto(proc): | |
"""Return a signal handler that calls passes through to the given proc on | |
call""" | |
def handler(signal, frame): | |
proc.send_signal(signal) | |
return handler | |
def main(): | |
try: | |
baud = int(sys.argv[1]) | |
path = sys.argv[2] | |
arglist = sys.argv[3:] | |
except (IndexError, ValueError): | |
print usage | |
sys.exit(1) | |
# Create a new pseudo-terminal pair. We'll read from the master side and | |
# connect the child to the slave side. | |
master, slave = pty.openpty() | |
# Set terminal options to the same as the current controlling terminal | |
termios.tcsetattr(slave, termios.TCSANOW, | |
termios.tcgetattr(sys.stdout.fileno()) | |
) | |
proc = subprocess.Popen([path]+arglist,#stdin=slave, | |
stdout=slave, stderr=slave, close_fds=True, | |
preexec_fn=postfork) | |
os.close(slave) | |
# Since we're still technically controlling the terminal the user is | |
# interacting with, we still get keyboard interrupts from it. Pass them | |
# through with a special signal handler. | |
signal.signal(signal.SIGINT, passsignalto(proc)) | |
while proc.poll() == None: | |
try: | |
a = os.read(master, 1) | |
except OSError as e: | |
if e.errno == 4: | |
# Interrupted system call, probably due to a ctrl-c. Ignore | |
continue | |
print "OSError %s" % e | |
break | |
if not a: | |
break | |
sys.stdout.write(a) | |
sys.stdout.flush() | |
time.sleep(1/baud) | |
if __name__ == "__main__": | |
try: | |
main() | |
finally: | |
print "%s exited." % sys.argv[0] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment