165 lines
3.5 KiB
Python
Executable File
165 lines
3.5 KiB
Python
Executable File
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('<Return>', self.inputhandler)
|
|
self.bind('<Control-c>', self.sigint)
|
|
self.bind('<Control-t>', self.sigterm)
|
|
self.bind('<Control-k>', self.sigkill)
|
|
self.bind('<Control-d>', 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()
|