blob: c5a04019009a398b25c5e4672039146857968edd [file] [log] [blame]
Guido van Rossum630112e1995-01-10 17:08:10 +00001import os
2import sys
Benjamin Petersond6d63f52009-01-04 18:53:28 +00003from tkinter import *
4from tkinter.scrolledtext import ScrolledText
5from tkinter.dialog import Dialog
Guido van Rossum630112e1995-01-10 17:08:10 +00006import signal
7
Guido van Rossum630112e1995-01-10 17:08:10 +00008BUFSIZE = 512
9
10class ShellWindow(ScrolledText):
11
Tim Peters182b5ac2004-07-18 06:16:08 +000012 def __init__(self, master=None, shell=None, **cnf):
13 if not shell:
14 try:
15 shell = os.environ['SHELL']
16 except KeyError:
17 shell = '/bin/sh'
18 shell = shell + ' -i'
Georg Brandl856023a2010-10-25 17:50:20 +000019 args = shell.split()
Tim Peters182b5ac2004-07-18 06:16:08 +000020 shell = args[0]
Guido van Rossum630112e1995-01-10 17:08:10 +000021
Neal Norwitzd9108552006-03-17 08:00:19 +000022 ScrolledText.__init__(self, master, **cnf)
Tim Peters182b5ac2004-07-18 06:16:08 +000023 self.pos = '1.0'
24 self.bind('<Return>', self.inputhandler)
25 self.bind('<Control-c>', self.sigint)
26 self.bind('<Control-t>', self.sigterm)
27 self.bind('<Control-k>', self.sigkill)
28 self.bind('<Control-d>', self.sendeof)
Guido van Rossum630112e1995-01-10 17:08:10 +000029
Tim Peters182b5ac2004-07-18 06:16:08 +000030 self.pid, self.fromchild, self.tochild = spawn(shell, args)
31 self.tk.createfilehandler(self.fromchild, READABLE,
32 self.outputhandler)
Guido van Rossum630112e1995-01-10 17:08:10 +000033
Tim Peters182b5ac2004-07-18 06:16:08 +000034 def outputhandler(self, file, mask):
Georg Brandl856023a2010-10-25 17:50:20 +000035 data = os.read(file, BUFSIZE).decode()
Tim Peters182b5ac2004-07-18 06:16:08 +000036 if not data:
37 self.tk.deletefilehandler(file)
38 pid, sts = os.waitpid(self.pid, 0)
Collin Winter6f2df4d2007-07-17 20:59:35 +000039 print('pid', pid, 'status', sts)
Tim Peters182b5ac2004-07-18 06:16:08 +000040 self.pid = None
41 detail = sts>>8
42 cause = sts & 0xff
43 if cause == 0:
44 msg = "exit status %d" % detail
45 else:
46 msg = "killed by signal %d" % (cause & 0x7f)
47 if cause & 0x80:
48 msg = msg + " -- core dumped"
49 Dialog(self.master,
50 text=msg,
51 title="Exit status",
52 bitmap='warning',
53 default=0,
54 strings=('OK',))
55 return
56 self.insert(END, data)
57 self.pos = self.index("end - 1 char")
58 self.yview_pickplace(END)
Guido van Rossum630112e1995-01-10 17:08:10 +000059
Tim Peters182b5ac2004-07-18 06:16:08 +000060 def inputhandler(self, *args):
61 if not self.pid:
62 self.no_process()
63 return "break"
64 self.insert(END, "\n")
65 line = self.get(self.pos, "end - 1 char")
66 self.pos = self.index(END)
Georg Brandl856023a2010-10-25 17:50:20 +000067 os.write(self.tochild, line.encode())
Tim Peters182b5ac2004-07-18 06:16:08 +000068 return "break"
Guido van Rossum630112e1995-01-10 17:08:10 +000069
Tim Peters182b5ac2004-07-18 06:16:08 +000070 def sendeof(self, *args):
71 if not self.pid:
72 self.no_process()
73 return "break"
74 os.close(self.tochild)
75 return "break"
Guido van Rossum630112e1995-01-10 17:08:10 +000076
Tim Peters182b5ac2004-07-18 06:16:08 +000077 def sendsig(self, sig):
78 if not self.pid:
79 self.no_process()
80 return "break"
81 os.kill(self.pid, sig)
82 return "break"
Guido van Rossum630112e1995-01-10 17:08:10 +000083
Tim Peters182b5ac2004-07-18 06:16:08 +000084 def sigint(self, *args):
85 return self.sendsig(signal.SIGINT)
Guido van Rossum630112e1995-01-10 17:08:10 +000086
Tim Peters182b5ac2004-07-18 06:16:08 +000087 def sigquit(self, *args):
88 return self.sendsig(signal.SIGQUIT)
Guido van Rossum630112e1995-01-10 17:08:10 +000089
Tim Peters182b5ac2004-07-18 06:16:08 +000090 def sigterm(self, *args):
91 return self.sendsig(signal.SIGTERM)
Guido van Rossum630112e1995-01-10 17:08:10 +000092
Tim Peters182b5ac2004-07-18 06:16:08 +000093 def sigkill(self, *args):
94 return self.sendsig(signal.SIGKILL)
Guido van Rossum89cb67b1996-07-30 18:57:18 +000095
Tim Peters182b5ac2004-07-18 06:16:08 +000096 def no_process(self):
97 Dialog(self.master,
98 text="No active process",
99 title="No process",
100 bitmap='error',
101 default=0,
102 strings=('OK',))
Guido van Rossum630112e1995-01-10 17:08:10 +0000103
Tim Peters182b5ac2004-07-18 06:16:08 +0000104MAXFD = 100 # Max number of file descriptors (os.getdtablesize()???)
Guido van Rossum630112e1995-01-10 17:08:10 +0000105
106def spawn(prog, args):
Tim Peters182b5ac2004-07-18 06:16:08 +0000107 p2cread, p2cwrite = os.pipe()
108 c2pread, c2pwrite = os.pipe()
109 pid = os.fork()
110 if pid == 0:
111 # Child
112 for i in 0, 1, 2:
113 try:
114 os.close(i)
115 except os.error:
116 pass
Neal Norwitz3bd844e2006-08-29 04:39:12 +0000117 if os.dup(p2cread) != 0:
Tim Peters182b5ac2004-07-18 06:16:08 +0000118 sys.stderr.write('popen2: bad read dup\n')
Neal Norwitz3bd844e2006-08-29 04:39:12 +0000119 if os.dup(c2pwrite) != 1:
Tim Peters182b5ac2004-07-18 06:16:08 +0000120 sys.stderr.write('popen2: bad write dup\n')
Neal Norwitz3bd844e2006-08-29 04:39:12 +0000121 if os.dup(c2pwrite) != 2:
Tim Peters182b5ac2004-07-18 06:16:08 +0000122 sys.stderr.write('popen2: bad write dup\n')
Christian Heimesd3eb5a152008-02-24 00:38:49 +0000123 os.closerange(3, MAXFD)
Tim Peters182b5ac2004-07-18 06:16:08 +0000124 try:
125 os.execvp(prog, args)
126 finally:
127 sys.stderr.write('execvp failed\n')
128 os._exit(1)
129 os.close(p2cread)
130 os.close(c2pwrite)
131 return pid, c2pread, p2cwrite
Guido van Rossum630112e1995-01-10 17:08:10 +0000132
133def test():
Georg Brandl856023a2010-10-25 17:50:20 +0000134 shell = ' '.join(sys.argv[1: ])
Tim Peters182b5ac2004-07-18 06:16:08 +0000135 root = Tk()
136 root.minsize(1, 1)
137 if shell:
138 w = ShellWindow(root, shell=shell)
139 else:
140 w = ShellWindow(root)
141 w.pack(expand=1, fill=BOTH)
142 w.focus_set()
143 w.tk.mainloop()
Guido van Rossum630112e1995-01-10 17:08:10 +0000144
145if __name__ == '__main__':
Tim Peters182b5ac2004-07-18 06:16:08 +0000146 test()