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 | os.closerange(3, MAXFD)
|
---|
125 | try:
|
---|
126 | os.execvp(prog, args)
|
---|
127 | finally:
|
---|
128 | sys.stderr.write('execvp failed\n')
|
---|
129 | os._exit(1)
|
---|
130 | os.close(p2cread)
|
---|
131 | os.close(c2pwrite)
|
---|
132 | return pid, c2pread, p2cwrite
|
---|
133 |
|
---|
134 | def test():
|
---|
135 | shell = string.join(sys.argv[1:])
|
---|
136 | root = Tk()
|
---|
137 | root.minsize(1, 1)
|
---|
138 | if shell:
|
---|
139 | w = ShellWindow(root, shell=shell)
|
---|
140 | else:
|
---|
141 | w = ShellWindow(root)
|
---|
142 | w.pack(expand=1, fill=BOTH)
|
---|
143 | w.focus_set()
|
---|
144 | w.tk.mainloop()
|
---|
145 |
|
---|
146 | if __name__ == '__main__':
|
---|
147 | test()
|
---|