Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 1 | import os |
| 2 | import sys |
Benjamin Peterson | d6d63f5 | 2009-01-04 18:53:28 +0000 | [diff] [blame] | 3 | from tkinter import * |
| 4 | from tkinter.scrolledtext import ScrolledText |
| 5 | from tkinter.dialog import Dialog |
Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 6 | import signal |
| 7 | |
Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 8 | BUFSIZE = 512 |
| 9 | |
| 10 | class ShellWindow(ScrolledText): |
| 11 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 12 | 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 Brandl | 856023a | 2010-10-25 17:50:20 +0000 | [diff] [blame^] | 19 | args = shell.split() |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 20 | shell = args[0] |
Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 21 | |
Neal Norwitz | d910855 | 2006-03-17 08:00:19 +0000 | [diff] [blame] | 22 | ScrolledText.__init__(self, master, **cnf) |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 23 | 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 Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 29 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 30 | self.pid, self.fromchild, self.tochild = spawn(shell, args) |
| 31 | self.tk.createfilehandler(self.fromchild, READABLE, |
| 32 | self.outputhandler) |
Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 33 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 34 | def outputhandler(self, file, mask): |
Georg Brandl | 856023a | 2010-10-25 17:50:20 +0000 | [diff] [blame^] | 35 | data = os.read(file, BUFSIZE).decode() |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 36 | if not data: |
| 37 | self.tk.deletefilehandler(file) |
| 38 | pid, sts = os.waitpid(self.pid, 0) |
Collin Winter | 6f2df4d | 2007-07-17 20:59:35 +0000 | [diff] [blame] | 39 | print('pid', pid, 'status', sts) |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 40 | 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 Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 59 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 60 | 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 Brandl | 856023a | 2010-10-25 17:50:20 +0000 | [diff] [blame^] | 67 | os.write(self.tochild, line.encode()) |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 68 | return "break" |
Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 69 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 70 | 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 Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 76 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 77 | 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 Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 83 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 84 | def sigint(self, *args): |
| 85 | return self.sendsig(signal.SIGINT) |
Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 86 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 87 | def sigquit(self, *args): |
| 88 | return self.sendsig(signal.SIGQUIT) |
Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 89 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 90 | def sigterm(self, *args): |
| 91 | return self.sendsig(signal.SIGTERM) |
Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 92 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 93 | def sigkill(self, *args): |
| 94 | return self.sendsig(signal.SIGKILL) |
Guido van Rossum | 89cb67b | 1996-07-30 18:57:18 +0000 | [diff] [blame] | 95 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 96 | 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 Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 103 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 104 | MAXFD = 100 # Max number of file descriptors (os.getdtablesize()???) |
Guido van Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 105 | |
| 106 | def spawn(prog, args): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 107 | 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 Norwitz | 3bd844e | 2006-08-29 04:39:12 +0000 | [diff] [blame] | 117 | if os.dup(p2cread) != 0: |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 118 | sys.stderr.write('popen2: bad read dup\n') |
Neal Norwitz | 3bd844e | 2006-08-29 04:39:12 +0000 | [diff] [blame] | 119 | if os.dup(c2pwrite) != 1: |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 120 | sys.stderr.write('popen2: bad write dup\n') |
Neal Norwitz | 3bd844e | 2006-08-29 04:39:12 +0000 | [diff] [blame] | 121 | if os.dup(c2pwrite) != 2: |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 122 | sys.stderr.write('popen2: bad write dup\n') |
Christian Heimes | d3eb5a15 | 2008-02-24 00:38:49 +0000 | [diff] [blame] | 123 | os.closerange(3, MAXFD) |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 124 | 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 Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 132 | |
| 133 | def test(): |
Georg Brandl | 856023a | 2010-10-25 17:50:20 +0000 | [diff] [blame^] | 134 | shell = ' '.join(sys.argv[1: ]) |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 135 | 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 Rossum | 630112e | 1995-01-10 17:08:10 +0000 | [diff] [blame] | 144 | |
| 145 | if __name__ == '__main__': |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame] | 146 | test() |