| 1 | import os
|
|---|
| 2 | import sys
|
|---|
| 3 | import string
|
|---|
| 4 | from Tkinter import *
|
|---|
| 5 | from ScrolledText import ScrolledText
|
|---|
| 6 | from Dialog import Dialog
|
|---|
| 7 | import signal
|
|---|
| 8 |
|
|---|
| 9 | BUFSIZE = 512
|
|---|
| 10 |
|
|---|
| 11 | class ShellWindow(ScrolledText):
|
|---|
| 12 |
|
|---|
| 13 | def __init__(self, master=None, shell=None, **cnf):
|
|---|
| 14 | if not shell:
|
|---|
| 15 | try:
|
|---|
| 16 | shell = os.environ['SHELL']
|
|---|
| 17 | except KeyError:
|
|---|
| 18 | shell = '/bin/sh'
|
|---|
| 19 | shell = shell + ' -i'
|
|---|
| 20 | args = string.split(shell)
|
|---|
| 21 | shell = args[0]
|
|---|
| 22 |
|
|---|
| 23 | apply(ScrolledText.__init__, (self, master), cnf)
|
|---|
| 24 | self.pos = '1.0'
|
|---|
| 25 | self.bind('<Return>', self.inputhandler)
|
|---|
| 26 | self.bind('<Control-c>', self.sigint)
|
|---|
| 27 | self.bind('<Control-t>', self.sigterm)
|
|---|
| 28 | self.bind('<Control-k>', self.sigkill)
|
|---|
| 29 | self.bind('<Control-d>', self.sendeof)
|
|---|
| 30 |
|
|---|
| 31 | self.pid, self.fromchild, self.tochild = spawn(shell, args)
|
|---|
| 32 | self.tk.createfilehandler(self.fromchild, READABLE,
|
|---|
| 33 | self.outputhandler)
|
|---|
| 34 |
|
|---|
| 35 | def outputhandler(self, file, mask):
|
|---|
| 36 | data = os.read(file, BUFSIZE)
|
|---|
| 37 | if not data:
|
|---|
| 38 | self.tk.deletefilehandler(file)
|
|---|
| 39 | pid, sts = os.waitpid(self.pid, 0)
|
|---|
| 40 | print 'pid', pid, 'status', sts
|
|---|
| 41 | self.pid = None
|
|---|
| 42 | detail = sts>>8
|
|---|
| 43 | cause = sts & 0xff
|
|---|
| 44 | if cause == 0:
|
|---|
| 45 | msg = "exit status %d" % detail
|
|---|
| 46 | else:
|
|---|
| 47 | msg = "killed by signal %d" % (cause & 0x7f)
|
|---|
| 48 | if cause & 0x80:
|
|---|
| 49 | msg = msg + " -- core dumped"
|
|---|
| 50 | Dialog(self.master,
|
|---|
| 51 | text=msg,
|
|---|
| 52 | title="Exit status",
|
|---|
| 53 | bitmap='warning',
|
|---|
| 54 | default=0,
|
|---|
| 55 | strings=('OK',))
|
|---|
| 56 | return
|
|---|
| 57 | self.insert(END, data)
|
|---|
| 58 | self.pos = self.index("end - 1 char")
|
|---|
| 59 | self.yview_pickplace(END)
|
|---|
| 60 |
|
|---|
| 61 | def inputhandler(self, *args):
|
|---|
| 62 | if not self.pid:
|
|---|
| 63 | self.no_process()
|
|---|
| 64 | return "break"
|
|---|
| 65 | self.insert(END, "\n")
|
|---|
| 66 | line = self.get(self.pos, "end - 1 char")
|
|---|
| 67 | self.pos = self.index(END)
|
|---|
| 68 | os.write(self.tochild, line)
|
|---|
| 69 | return "break"
|
|---|
| 70 |
|
|---|
| 71 | def sendeof(self, *args):
|
|---|
| 72 | if not self.pid:
|
|---|
| 73 | self.no_process()
|
|---|
| 74 | return "break"
|
|---|
| 75 | os.close(self.tochild)
|
|---|
| 76 | return "break"
|
|---|
| 77 |
|
|---|
| 78 | def sendsig(self, sig):
|
|---|
| 79 | if not self.pid:
|
|---|
| 80 | self.no_process()
|
|---|
| 81 | return "break"
|
|---|
| 82 | os.kill(self.pid, sig)
|
|---|
| 83 | return "break"
|
|---|
| 84 |
|
|---|
| 85 | def sigint(self, *args):
|
|---|
| 86 | return self.sendsig(signal.SIGINT)
|
|---|
| 87 |
|
|---|
| 88 | def sigquit(self, *args):
|
|---|
| 89 | return self.sendsig(signal.SIGQUIT)
|
|---|
| 90 |
|
|---|
| 91 | def sigterm(self, *args):
|
|---|
| 92 | return self.sendsig(signal.SIGTERM)
|
|---|
| 93 |
|
|---|
| 94 | def sigkill(self, *args):
|
|---|
| 95 | return self.sendsig(signal.SIGKILL)
|
|---|
| 96 |
|
|---|
| 97 | def no_process(self):
|
|---|
| 98 | Dialog(self.master,
|
|---|
| 99 | text="No active process",
|
|---|
| 100 | title="No process",
|
|---|
| 101 | bitmap='error',
|
|---|
| 102 | default=0,
|
|---|
| 103 | strings=('OK',))
|
|---|
| 104 |
|
|---|
| 105 | MAXFD = 100 # Max number of file descriptors (os.getdtablesize()???)
|
|---|
| 106 |
|
|---|
| 107 | def spawn(prog, args):
|
|---|
| 108 | p2cread, p2cwrite = os.pipe()
|
|---|
| 109 | c2pread, c2pwrite = os.pipe()
|
|---|
| 110 | pid = os.fork()
|
|---|
| 111 | if pid == 0:
|
|---|
| 112 | # Child
|
|---|
| 113 | for i in 0, 1, 2:
|
|---|
| 114 | try:
|
|---|
| 115 | os.close(i)
|
|---|
| 116 | except os.error:
|
|---|
| 117 | pass
|
|---|
| 118 | if os.dup(p2cread) <> 0:
|
|---|
| 119 | sys.stderr.write('popen2: bad read dup\n')
|
|---|
| 120 | if os.dup(c2pwrite) <> 1:
|
|---|
| 121 | sys.stderr.write('popen2: bad write dup\n')
|
|---|
| 122 | if os.dup(c2pwrite) <> 2:
|
|---|
| 123 | sys.stderr.write('popen2: bad write dup\n')
|
|---|
| 124 | for i in range(3, MAXFD):
|
|---|
| 125 | try:
|
|---|
| 126 | os.close(i)
|
|---|
| 127 | except:
|
|---|
| 128 | pass
|
|---|
| 129 | try:
|
|---|
| 130 | os.execvp(prog, args)
|
|---|
| 131 | finally:
|
|---|
| 132 | sys.stderr.write('execvp failed\n')
|
|---|
| 133 | os._exit(1)
|
|---|
| 134 | os.close(p2cread)
|
|---|
| 135 | os.close(c2pwrite)
|
|---|
| 136 | return pid, c2pread, p2cwrite
|
|---|
| 137 |
|
|---|
| 138 | def test():
|
|---|
| 139 | shell = string.join(sys.argv[1:])
|
|---|
| 140 | root = Tk()
|
|---|
| 141 | root.minsize(1, 1)
|
|---|
| 142 | if shell:
|
|---|
| 143 | w = ShellWindow(root, shell=shell)
|
|---|
| 144 | else:
|
|---|
| 145 | w = ShellWindow(root)
|
|---|
| 146 | w.pack(expand=1, fill=BOTH)
|
|---|
| 147 | w.focus_set()
|
|---|
| 148 | w.tk.mainloop()
|
|---|
| 149 |
|
|---|
| 150 | if __name__ == '__main__':
|
|---|
| 151 | test()
|
|---|