import os import sys import string from Tkinter import * from ScrolledText import ScrolledText from Dialog import Dialog import signal TK_READABLE = 1 TK_WRITABLE = 2 TK_EXCEPTION = 4 BUFSIZE = 512 class ShellWindow(ScrolledText): def __init__(self, master = None, cnf = {}): try: shell = cnf['shell'] del cnf['shell'] except KeyError: try: shell = os.environ['SHELL'] except KeyError: shell = '/bin/sh' shell = shell + ' -i' args = string.split(shell) shell = args[0] ScrolledText.__init__(self, master, cnf) self.pos = '1.0' self.bind('', self.inputhandler) self.bind('', self.sigint) self.bind('', self.sigterm) self.bind('', self.sigkill) self.bind('', self.sendeof) self.pid, self.fromchild, self.tochild = spawn(shell, args) self.tk.createfilehandler(self.fromchild, TK_READABLE, self.outputhandler) def outputhandler(self, file, mask): data = os.read(file, BUFSIZE) if not data: self.tk.deletefilehandler(file) pid, sts = os.waitpid(self.pid, 0) print 'pid', pid, 'status', sts self.pid = None detail = sts>>8 cause = sts & 0xff if cause == 0: msg = "exit status %d" % detail else: msg = "killed by signal %d" % (cause & 0x7f) if cause & 0x80: msg = msg + " -- core dumped" Dialog(self.master, { 'text': msg, 'title': "Exit status", 'bitmap': 'warning', 'default': 0, 'strings': ('OK',), }) return self.insert('end', data) self.pos = self.index('end') self.yview_pickplace('end') def inputhandler(self, *args): if not self.pid: Dialog(self.master, { 'text': "No active process", 'title': "No process", 'bitmap': 'error', 'default': 0, 'strings': ('OK',), }) return self.insert('end', '\n') line = self.get(self.pos, 'end') self.pos = self.index('end') os.write(self.tochild, line) def sendeof(self, *args): if not self.pid: Dialog(self.master, { 'text': "No active process", 'title': "No process", 'bitmap': 'error', 'default': 0, 'strings': ('OK',), }) return os.close(self.tochild) def sendsig(self, sig): if not self.pid: Dialog(self.master, { 'text': "No active process", 'title': "No process", 'bitmap': 'error', 'default': 0, 'strings': ('OK',), }) return os.kill(self.pid, sig) def sigint(self, *args): self.sendsig(signal.SIGINT) def sigquit(self, *args): self.sendsig(signal.SIGQUIT) def sigterm(self, *args): self.sendsig(signal.SIGTERM) def sigkill(self, *args): self.sendsig(signal.SIGKILL) MAXFD = 100 # Max number of file descriptors (os.getdtablesize()???) def spawn(prog, args): p2cread, p2cwrite = os.pipe() c2pread, c2pwrite = os.pipe() pid = os.fork() if pid == 0: # Child os.close(0) os.close(1) os.close(2) if os.dup(p2cread) <> 0: sys.stderr.write('popen2: bad read dup\n') if os.dup(c2pwrite) <> 1: sys.stderr.write('popen2: bad write dup\n') if os.dup(c2pwrite) <> 2: sys.stderr.write('popen2: bad write dup\n') for i in range(3, MAXFD): try: os.close(i) except: pass try: os.execvp(prog, args) finally: print 'execvp failed' os._exit(1) os.close(p2cread) os.close(c2pwrite) return pid, c2pread, p2cwrite def test(): shell = string.join(sys.argv[1:]) cnf = {} if shell: cnf['shell'] = shell root = Tk() root.minsize(1, 1) w = ShellWindow(root, cnf) w.pack({'expand': 1, 'fill': 'both'}) w.focus_set() w.tk.mainloop() if __name__ == '__main__': test()