From c0c38f3c7f66232a98ba5ccd1687d4cbcc537549 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 17 Sep 2010 20:02:36 -0700 Subject: s/\t/ /g, and add some comments --- tools/ajaxterm/ajaxterm.py | 1116 ++++++++++++++++++++++---------------------- 1 file changed, 558 insertions(+), 558 deletions(-) diff --git a/tools/ajaxterm/ajaxterm.py b/tools/ajaxterm/ajaxterm.py index b8a42f4b2..e2c156fd7 100755 --- a/tools/ajaxterm/ajaxterm.py +++ b/tools/ajaxterm/ajaxterm.py @@ -14,572 +14,572 @@ import string, subprocess, uuid global g_server class Terminal: - def __init__(self,width=80,height=24): - self.width=width - self.height=height - self.init() - self.reset() - def init(self): - self.esc_seq={ - "\x00": None, - "\x05": self.esc_da, - "\x07": None, - "\x08": self.esc_0x08, - "\x09": self.esc_0x09, - "\x0a": self.esc_0x0a, - "\x0b": self.esc_0x0a, - "\x0c": self.esc_0x0a, - "\x0d": self.esc_0x0d, - "\x0e": None, - "\x0f": None, - "\x1b#8": None, - "\x1b=": None, - "\x1b>": None, - "\x1b(0": None, - "\x1b(A": None, - "\x1b(B": None, - "\x1b[c": self.esc_da, - "\x1b[0c": self.esc_da, - "\x1b]R": None, - "\x1b7": self.esc_save, - "\x1b8": self.esc_restore, - "\x1bD": None, - "\x1bE": None, - "\x1bH": None, - "\x1bM": self.esc_ri, - "\x1bN": None, - "\x1bO": None, - "\x1bZ": self.esc_da, - "\x1ba": None, - "\x1bc": self.reset, - "\x1bn": None, - "\x1bo": None, - } - for k,v in self.esc_seq.items(): - if v==None: - self.esc_seq[k]=self.esc_ignore - # regex - d={ - r'\[\??([0-9;]*)([@ABCDEFGHJKLMPXacdefghlmnqrstu`])' : self.csi_dispatch, - r'\]([^\x07]+)\x07' : self.esc_ignore, - } - self.esc_re=[] - for k,v in d.items(): - self.esc_re.append((re.compile('\x1b'+k),v)) - # define csi sequences - self.csi_seq={ - '@': (self.csi_at,[1]), - '`': (self.csi_G,[1]), - 'J': (self.csi_J,[0]), - 'K': (self.csi_K,[0]), - } - for i in [i[4] for i in dir(self) if i.startswith('csi_') and len(i)==5]: - if not self.csi_seq.has_key(i): - self.csi_seq[i]=(getattr(self,'csi_'+i),[1]) - # Init 0-256 to latin1 and html translation table - self.trl1="" - for i in range(256): - if i<32: - self.trl1+=" " - elif i<127 or i>160: - self.trl1+=chr(i) - else: - self.trl1+="?" - self.trhtml="" - for i in range(256): - if i==0x0a or (i>32 and i<127) or i>160: - self.trhtml+=chr(i) - elif i<=32: - self.trhtml+="\xa0" - else: - self.trhtml+="?" - def reset(self,s=""): - self.scr=array.array('i',[0x000700]*(self.width*self.height)) - self.st=0 - self.sb=self.height-1 - self.cx_bak=self.cx=0 - self.cy_bak=self.cy=0 - self.cl=0 - self.sgr=0x000700 - self.buf="" - self.outbuf="" - self.last_html="" - def peek(self,y1,x1,y2,x2): - return self.scr[self.width*y1+x1:self.width*y2+x2] - def poke(self,y,x,s): - pos=self.width*y+x - self.scr[pos:pos+len(s)]=s - def zero(self,y1,x1,y2,x2): - w=self.width*(y2-y1)+x2-x1+1 - z=array.array('i',[0x000700]*w) - self.scr[self.width*y1+x1:self.width*y2+x2+1]=z - def scroll_up(self,y1,y2): - self.poke(y1,0,self.peek(y1+1,0,y2,self.width)) - self.zero(y2,0,y2,self.width-1) - def scroll_down(self,y1,y2): - self.poke(y1+1,0,self.peek(y1,0,y2-1,self.width)) - self.zero(y1,0,y1,self.width-1) - def scroll_right(self,y,x): - self.poke(y,x+1,self.peek(y,x,y,self.width)) - self.zero(y,x,y,x) - def cursor_down(self): - if self.cy>=self.st and self.cy<=self.sb: - self.cl=0 - q,r=divmod(self.cy+1,self.sb+1) - if q: - self.scroll_up(self.st,self.sb) - self.cy=self.sb - else: - self.cy=r - def cursor_right(self): - q,r=divmod(self.cx+1,self.width) - if q: - self.cl=1 - else: - self.cx=r - def echo(self,c): - if self.cl: - self.cursor_down() - self.cx=0 - self.scr[(self.cy*self.width)+self.cx]=self.sgr|ord(c) - self.cursor_right() - def esc_0x08(self,s): - self.cx=max(0,self.cx-1) - def esc_0x09(self,s): - x=self.cx+8 - q,r=divmod(x,8) - self.cx=(q*8)%self.width - def esc_0x0a(self,s): - self.cursor_down() - def esc_0x0d(self,s): - self.cl=0 - self.cx=0 - def esc_save(self,s): - self.cx_bak=self.cx - self.cy_bak=self.cy - def esc_restore(self,s): - self.cx=self.cx_bak - self.cy=self.cy_bak - self.cl=0 - def esc_da(self,s): - self.outbuf="\x1b[?6c" - def esc_ri(self,s): - self.cy=max(self.st,self.cy-1) - if self.cy==self.st: - self.scroll_down(self.st,self.sb) - def esc_ignore(self,*s): - pass -# print "term:ignore: %s"%repr(s) - def csi_dispatch(self,seq,mo): - # CSI sequences - s=mo.group(1) - c=mo.group(2) - f=self.csi_seq.get(c,None) - if f: - try: - l=[min(int(i),1024) for i in s.split(';') if len(i)<4] - except ValueError: - l=[] - if len(l)==0: - l=f[1] - f[0](l) -# else: -# print 'csi ignore',c,l - def csi_at(self,l): - for i in range(l[0]): - self.scroll_right(self.cy,self.cx) - def csi_A(self,l): - self.cy=max(self.st,self.cy-l[0]) - def csi_B(self,l): - self.cy=min(self.sb,self.cy+l[0]) - def csi_C(self,l): - self.cx=min(self.width-1,self.cx+l[0]) - self.cl=0 - def csi_D(self,l): - self.cx=max(0,self.cx-l[0]) - self.cl=0 - def csi_E(self,l): - self.csi_B(l) - self.cx=0 - self.cl=0 - def csi_F(self,l): - self.csi_A(l) - self.cx=0 - self.cl=0 - def csi_G(self,l): - self.cx=min(self.width,l[0])-1 - def csi_H(self,l): - if len(l)<2: l=[1,1] - self.cx=min(self.width,l[1])-1 - self.cy=min(self.height,l[0])-1 - self.cl=0 - def csi_J(self,l): - if l[0]==0: - self.zero(self.cy,self.cx,self.height-1,self.width-1) - elif l[0]==1: - self.zero(0,0,self.cy,self.cx) - elif l[0]==2: - self.zero(0,0,self.height-1,self.width-1) - def csi_K(self,l): - if l[0]==0: - self.zero(self.cy,self.cx,self.cy,self.width-1) - elif l[0]==1: - self.zero(self.cy,0,self.cy,self.cx) - elif l[0]==2: - self.zero(self.cy,0,self.cy,self.width-1) - def csi_L(self,l): - for i in range(l[0]): - if self.cy=self.st and self.cy<=self.sb: - for i in range(l[0]): - self.scroll_up(self.cy,self.sb) - def csi_P(self,l): - w,cx,cy=self.width,self.cx,self.cy - end=self.peek(cy,cx,cy,w) - self.csi_K([0]) - self.poke(cy,cx,end[l[0]:]) - def csi_X(self,l): - self.zero(self.cy,self.cx,self.cy,self.cx+l[0]) - def csi_a(self,l): - self.csi_C(l) - def csi_c(self,l): - #'\x1b[?0c' 0-8 cursor size - pass - def csi_d(self,l): - self.cy=min(self.height,l[0])-1 - def csi_e(self,l): - self.csi_B(l) - def csi_f(self,l): - self.csi_H(l) - def csi_h(self,l): - if l[0]==4: - pass -# print "insert on" - def csi_l(self,l): - if l[0]==4: - pass -# print "insert off" - def csi_m(self,l): - for i in l: - if i==0 or i==39 or i==49 or i==27: - self.sgr=0x000700 - elif i==1: - self.sgr=(self.sgr|0x000800) - elif i==7: - self.sgr=0x070000 - elif i>=30 and i<=37: - c=i-30 - self.sgr=(self.sgr&0xff08ff)|(c<<8) - elif i>=40 and i<=47: - c=i-40 - self.sgr=(self.sgr&0x00ffff)|(c<<16) -# else: -# print "CSI sgr ignore",l,i -# print 'sgr: %r %x'%(l,self.sgr) - def csi_r(self,l): - if len(l)<2: l=[0,self.height] - self.st=min(self.height-1,l[0]-1) - self.sb=min(self.height-1,l[1]-1) - self.sb=max(self.st,self.sb) - def csi_s(self,l): - self.esc_save(0) - def csi_u(self,l): - self.esc_restore(0) - def escape(self): - e=self.buf - if len(e)>32: -# print "error %r"%e - self.buf="" - elif e in self.esc_seq: - self.esc_seq[e](e) - self.buf="" - else: - for r,f in self.esc_re: - mo=r.match(e) - if mo: - f(e,mo) - self.buf="" - break -# if self.buf=='': print "ESC %r\n"%e - def write(self,s): - for i in s: - if len(self.buf) or (i in self.esc_seq): - self.buf+=i - self.escape() - elif i == '\x1b': - self.buf+=i - else: - self.echo(i) - def read(self): - b=self.outbuf - self.outbuf="" - return b - def dump(self): - r='' - for i in self.scr: - r+=chr(i&255) - return r - def dumplatin1(self): - return self.dump().translate(self.trl1) - def dumphtml(self,color=1): - h=self.height - w=self.width - r="" - span="" - span_bg,span_fg=-1,-1 - for i in range(h*w): - q,c=divmod(self.scr[i],256) - if color: - bg,fg=divmod(q,256) - else: - bg,fg=0,7 - if i==self.cy*w+self.cx: - bg,fg=1,7 - if (bg!=span_bg or fg!=span_fg or i==h*w-1): - if len(span): - r+='%s'%(span_fg,span_bg,cgi.escape(span.translate(self.trhtml))) - span="" - span_bg,span_fg=bg,fg - span+=chr(c) - if i%w==w-1: - span+='\n' - r='
%s
'%r - if self.last_html==r: - return '' - else: - self.last_html=r -# print self - return r - def __repr__(self): - d=self.dumplatin1() - r="" - for i in range(self.height): - r+="|%s|\n"%d[self.width*i:self.width*(i+1)] - return r + def __init__(self,width=80,height=24): + self.width=width + self.height=height + self.init() + self.reset() + def init(self): + self.esc_seq={ + "\x00": None, + "\x05": self.esc_da, + "\x07": None, + "\x08": self.esc_0x08, + "\x09": self.esc_0x09, + "\x0a": self.esc_0x0a, + "\x0b": self.esc_0x0a, + "\x0c": self.esc_0x0a, + "\x0d": self.esc_0x0d, + "\x0e": None, + "\x0f": None, + "\x1b#8": None, + "\x1b=": None, + "\x1b>": None, + "\x1b(0": None, + "\x1b(A": None, + "\x1b(B": None, + "\x1b[c": self.esc_da, + "\x1b[0c": self.esc_da, + "\x1b]R": None, + "\x1b7": self.esc_save, + "\x1b8": self.esc_restore, + "\x1bD": None, + "\x1bE": None, + "\x1bH": None, + "\x1bM": self.esc_ri, + "\x1bN": None, + "\x1bO": None, + "\x1bZ": self.esc_da, + "\x1ba": None, + "\x1bc": self.reset, + "\x1bn": None, + "\x1bo": None, + } + for k,v in self.esc_seq.items(): + if v==None: + self.esc_seq[k]=self.esc_ignore + # regex + d={ + r'\[\??([0-9;]*)([@ABCDEFGHJKLMPXacdefghlmnqrstu`])' : self.csi_dispatch, + r'\]([^\x07]+)\x07' : self.esc_ignore, + } + self.esc_re=[] + for k,v in d.items(): + self.esc_re.append((re.compile('\x1b'+k),v)) + # define csi sequences + self.csi_seq={ + '@': (self.csi_at,[1]), + '`': (self.csi_G,[1]), + 'J': (self.csi_J,[0]), + 'K': (self.csi_K,[0]), + } + for i in [i[4] for i in dir(self) if i.startswith('csi_') and len(i)==5]: + if not self.csi_seq.has_key(i): + self.csi_seq[i]=(getattr(self,'csi_'+i),[1]) + # Init 0-256 to latin1 and html translation table + self.trl1="" + for i in range(256): + if i<32: + self.trl1+=" " + elif i<127 or i>160: + self.trl1+=chr(i) + else: + self.trl1+="?" + self.trhtml="" + for i in range(256): + if i==0x0a or (i>32 and i<127) or i>160: + self.trhtml+=chr(i) + elif i<=32: + self.trhtml+="\xa0" + else: + self.trhtml+="?" + def reset(self,s=""): + self.scr=array.array('i',[0x000700]*(self.width*self.height)) + self.st=0 + self.sb=self.height-1 + self.cx_bak=self.cx=0 + self.cy_bak=self.cy=0 + self.cl=0 + self.sgr=0x000700 + self.buf="" + self.outbuf="" + self.last_html="" + def peek(self,y1,x1,y2,x2): + return self.scr[self.width*y1+x1:self.width*y2+x2] + def poke(self,y,x,s): + pos=self.width*y+x + self.scr[pos:pos+len(s)]=s + def zero(self,y1,x1,y2,x2): + w=self.width*(y2-y1)+x2-x1+1 + z=array.array('i',[0x000700]*w) + self.scr[self.width*y1+x1:self.width*y2+x2+1]=z + def scroll_up(self,y1,y2): + self.poke(y1,0,self.peek(y1+1,0,y2,self.width)) + self.zero(y2,0,y2,self.width-1) + def scroll_down(self,y1,y2): + self.poke(y1+1,0,self.peek(y1,0,y2-1,self.width)) + self.zero(y1,0,y1,self.width-1) + def scroll_right(self,y,x): + self.poke(y,x+1,self.peek(y,x,y,self.width)) + self.zero(y,x,y,x) + def cursor_down(self): + if self.cy>=self.st and self.cy<=self.sb: + self.cl=0 + q,r=divmod(self.cy+1,self.sb+1) + if q: + self.scroll_up(self.st,self.sb) + self.cy=self.sb + else: + self.cy=r + def cursor_right(self): + q,r=divmod(self.cx+1,self.width) + if q: + self.cl=1 + else: + self.cx=r + def echo(self,c): + if self.cl: + self.cursor_down() + self.cx=0 + self.scr[(self.cy*self.width)+self.cx]=self.sgr|ord(c) + self.cursor_right() + def esc_0x08(self,s): + self.cx=max(0,self.cx-1) + def esc_0x09(self,s): + x=self.cx+8 + q,r=divmod(x,8) + self.cx=(q*8)%self.width + def esc_0x0a(self,s): + self.cursor_down() + def esc_0x0d(self,s): + self.cl=0 + self.cx=0 + def esc_save(self,s): + self.cx_bak=self.cx + self.cy_bak=self.cy + def esc_restore(self,s): + self.cx=self.cx_bak + self.cy=self.cy_bak + self.cl=0 + def esc_da(self,s): + self.outbuf="\x1b[?6c" + def esc_ri(self,s): + self.cy=max(self.st,self.cy-1) + if self.cy==self.st: + self.scroll_down(self.st,self.sb) + def esc_ignore(self,*s): + pass +# print "term:ignore: %s"%repr(s) + def csi_dispatch(self,seq,mo): + # CSI sequences + s=mo.group(1) + c=mo.group(2) + f=self.csi_seq.get(c,None) + if f: + try: + l=[min(int(i),1024) for i in s.split(';') if len(i)<4] + except ValueError: + l=[] + if len(l)==0: + l=f[1] + f[0](l) +# else: +# print 'csi ignore',c,l + def csi_at(self,l): + for i in range(l[0]): + self.scroll_right(self.cy,self.cx) + def csi_A(self,l): + self.cy=max(self.st,self.cy-l[0]) + def csi_B(self,l): + self.cy=min(self.sb,self.cy+l[0]) + def csi_C(self,l): + self.cx=min(self.width-1,self.cx+l[0]) + self.cl=0 + def csi_D(self,l): + self.cx=max(0,self.cx-l[0]) + self.cl=0 + def csi_E(self,l): + self.csi_B(l) + self.cx=0 + self.cl=0 + def csi_F(self,l): + self.csi_A(l) + self.cx=0 + self.cl=0 + def csi_G(self,l): + self.cx=min(self.width,l[0])-1 + def csi_H(self,l): + if len(l)<2: l=[1,1] + self.cx=min(self.width,l[1])-1 + self.cy=min(self.height,l[0])-1 + self.cl=0 + def csi_J(self,l): + if l[0]==0: + self.zero(self.cy,self.cx,self.height-1,self.width-1) + elif l[0]==1: + self.zero(0,0,self.cy,self.cx) + elif l[0]==2: + self.zero(0,0,self.height-1,self.width-1) + def csi_K(self,l): + if l[0]==0: + self.zero(self.cy,self.cx,self.cy,self.width-1) + elif l[0]==1: + self.zero(self.cy,0,self.cy,self.cx) + elif l[0]==2: + self.zero(self.cy,0,self.cy,self.width-1) + def csi_L(self,l): + for i in range(l[0]): + if self.cy=self.st and self.cy<=self.sb: + for i in range(l[0]): + self.scroll_up(self.cy,self.sb) + def csi_P(self,l): + w,cx,cy=self.width,self.cx,self.cy + end=self.peek(cy,cx,cy,w) + self.csi_K([0]) + self.poke(cy,cx,end[l[0]:]) + def csi_X(self,l): + self.zero(self.cy,self.cx,self.cy,self.cx+l[0]) + def csi_a(self,l): + self.csi_C(l) + def csi_c(self,l): + #'\x1b[?0c' 0-8 cursor size + pass + def csi_d(self,l): + self.cy=min(self.height,l[0])-1 + def csi_e(self,l): + self.csi_B(l) + def csi_f(self,l): + self.csi_H(l) + def csi_h(self,l): + if l[0]==4: + pass +# print "insert on" + def csi_l(self,l): + if l[0]==4: + pass +# print "insert off" + def csi_m(self,l): + for i in l: + if i==0 or i==39 or i==49 or i==27: + self.sgr=0x000700 + elif i==1: + self.sgr=(self.sgr|0x000800) + elif i==7: + self.sgr=0x070000 + elif i>=30 and i<=37: + c=i-30 + self.sgr=(self.sgr&0xff08ff)|(c<<8) + elif i>=40 and i<=47: + c=i-40 + self.sgr=(self.sgr&0x00ffff)|(c<<16) +# else: +# print "CSI sgr ignore",l,i +# print 'sgr: %r %x'%(l,self.sgr) + def csi_r(self,l): + if len(l)<2: l=[0,self.height] + self.st=min(self.height-1,l[0]-1) + self.sb=min(self.height-1,l[1]-1) + self.sb=max(self.st,self.sb) + def csi_s(self,l): + self.esc_save(0) + def csi_u(self,l): + self.esc_restore(0) + def escape(self): + e=self.buf + if len(e)>32: +# print "error %r"%e + self.buf="" + elif e in self.esc_seq: + self.esc_seq[e](e) + self.buf="" + else: + for r,f in self.esc_re: + mo=r.match(e) + if mo: + f(e,mo) + self.buf="" + break +# if self.buf=='': print "ESC %r\n"%e + def write(self,s): + for i in s: + if len(self.buf) or (i in self.esc_seq): + self.buf+=i + self.escape() + elif i == '\x1b': + self.buf+=i + else: + self.echo(i) + def read(self): + b=self.outbuf + self.outbuf="" + return b + def dump(self): + r='' + for i in self.scr: + r+=chr(i&255) + return r + def dumplatin1(self): + return self.dump().translate(self.trl1) + def dumphtml(self,color=1): + h=self.height + w=self.width + r="" + span="" + span_bg,span_fg=-1,-1 + for i in range(h*w): + q,c=divmod(self.scr[i],256) + if color: + bg,fg=divmod(q,256) + else: + bg,fg=0,7 + if i==self.cy*w+self.cx: + bg,fg=1,7 + if (bg!=span_bg or fg!=span_fg or i==h*w-1): + if len(span): + r+='%s'%(span_fg,span_bg,cgi.escape(span.translate(self.trhtml))) + span="" + span_bg,span_fg=bg,fg + span+=chr(c) + if i%w==w-1: + span+='\n' + r='
%s
'%r + if self.last_html==r: + return '' + else: + self.last_html=r +# print self + return r + def __repr__(self): + d=self.dumplatin1() + r="" + for i in range(self.height): + r+="|%s|\n"%d[self.width*i:self.width*(i+1)] + return r class SynchronizedMethod: - def __init__(self,lock,orig): - self.lock=lock - self.orig=orig - def __call__(self,*l): - self.lock.acquire() - r=self.orig(*l) - self.lock.release() - return r + def __init__(self,lock,orig): + self.lock=lock + self.orig=orig + def __call__(self,*l): + self.lock.acquire() + r=self.orig(*l) + self.lock.release() + return r class Multiplex: - def __init__(self,cmd=None): - signal.signal(signal.SIGCHLD, signal.SIG_IGN) - self.cmd=cmd - self.proc={} - self.lock=threading.RLock() - self.thread=threading.Thread(target=self.loop) - self.alive=1 - self.lastActivity=time.time() - # synchronize methods - for name in ['create','fds','proc_read','proc_write','dump','die','run']: - orig=getattr(self,name) - setattr(self,name,SynchronizedMethod(self.lock,orig)) - self.thread.start() - def create(self,w=80,h=25): - pid,fd=pty.fork() - if pid==0: - try: - fdl=[int(i) for i in os.listdir('/proc/self/fd')] - except OSError: - fdl=range(256) - for i in [i for i in fdl if i>2]: - try: - os.close(i) - except OSError: - pass - if self.cmd: - cmd=['/bin/sh','-c',self.cmd] - elif os.getuid()==0: - cmd=['/bin/login'] - else: - sys.stdout.write("Login: ") - login=sys.stdin.readline().strip() - if re.match('^[0-9A-Za-z-_. ]+$',login): - cmd=['ssh'] - cmd+=['-oPreferredAuthentications=keyboard-interactive,password'] - cmd+=['-oNoHostAuthenticationForLocalhost=yes'] - cmd+=['-oLogLevel=FATAL'] - cmd+=['-F/dev/null','-l',login,'localhost'] - else: - os._exit(0) - env={} - env["COLUMNS"]=str(w) - env["LINES"]=str(h) - env["TERM"]="linux" - env["PATH"]=os.environ['PATH'] - os.execvpe(cmd[0],cmd,env) - else: - fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) - # python bug http://python.org/sf/1112949 on amd64 - fcntl.ioctl(fd, struct.unpack('i',struct.pack('I',termios.TIOCSWINSZ))[0], struct.pack("HHHH",h,w,0,0)) - self.proc[fd]={'pid':pid,'term':Terminal(w,h),'buf':'','time':time.time()} - return fd - def die(self): - self.alive=0 - def run(self): - return self.alive - def fds(self): - return self.proc.keys() - def proc_kill(self,fd): - if fd in self.proc: - self.proc[fd]['time']=0 - t=time.time() - for i in self.proc.keys(): - t0=self.proc[i]['time'] - if (t-t0)>120: - try: - os.close(i) - os.kill(self.proc[i]['pid'],signal.SIGTERM) - except (IOError,OSError): - pass - del self.proc[i] - def proc_read(self,fd): - try: - t=self.proc[fd]['term'] - t.write(os.read(fd,65536)) - reply=t.read() - if reply: - os.write(fd,reply) - self.proc[fd]['time']=time.time() - except (KeyError,IOError,OSError): - self.proc_kill(fd) - def proc_write(self,fd,s): - try: - os.write(fd,s) - except (IOError,OSError): - self.proc_kill(fd) - def dump(self,fd,color=1): - try: - return self.proc[fd]['term'].dumphtml(color) - except KeyError: - return False - def loop(self): - while self.run(): - fds=self.fds() - i,o,e=select.select(fds, [], [], 1.0) - print str(time.time()) + " " + str(self.lastActivity) - if time.time() - self.lastActivity > 120: - global g_server - g_server.shutdown() - for fd in i: - self.proc_read(fd) - if len(i): - time.sleep(0.002) - for i in self.proc.keys(): - try: - os.close(i) - os.kill(self.proc[i]['pid'],signal.SIGTERM) - except (IOError,OSError): - pass + def __init__(self,cmd=None): + signal.signal(signal.SIGCHLD, signal.SIG_IGN) + self.cmd=cmd + self.proc={} + self.lock=threading.RLock() + self.thread=threading.Thread(target=self.loop) + self.alive=1 + self.lastActivity=time.time() + # synchronize methods + for name in ['create','fds','proc_read','proc_write','dump','die','run']: + orig=getattr(self,name) + setattr(self,name,SynchronizedMethod(self.lock,orig)) + self.thread.start() + def create(self,w=80,h=25): + pid,fd=pty.fork() + if pid==0: + try: + fdl=[int(i) for i in os.listdir('/proc/self/fd')] + except OSError: + fdl=range(256) + for i in [i for i in fdl if i>2]: + try: + os.close(i) + except OSError: + pass + if self.cmd: + cmd=['/bin/sh','-c',self.cmd] + elif os.getuid()==0: + cmd=['/bin/login'] + else: + sys.stdout.write("Login: ") + login=sys.stdin.readline().strip() + if re.match('^[0-9A-Za-z-_. ]+$',login): + cmd=['ssh'] + cmd+=['-oPreferredAuthentications=keyboard-interactive,password'] + cmd+=['-oNoHostAuthenticationForLocalhost=yes'] + cmd+=['-oLogLevel=FATAL'] + cmd+=['-F/dev/null','-l',login,'localhost'] + else: + os._exit(0) + env={} + env["COLUMNS"]=str(w) + env["LINES"]=str(h) + env["TERM"]="linux" + env["PATH"]=os.environ['PATH'] + os.execvpe(cmd[0],cmd,env) + else: + fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) + # python bug http://python.org/sf/1112949 on amd64 + fcntl.ioctl(fd, struct.unpack('i',struct.pack('I',termios.TIOCSWINSZ))[0], struct.pack("HHHH",h,w,0,0)) + self.proc[fd]={'pid':pid,'term':Terminal(w,h),'buf':'','time':time.time()} + return fd + def die(self): + self.alive=0 + def run(self): + return self.alive + def fds(self): + return self.proc.keys() + def proc_kill(self,fd): + if fd in self.proc: + self.proc[fd]['time']=0 + t=time.time() + for i in self.proc.keys(): + t0=self.proc[i]['time'] + if (t-t0)>120: + try: + os.close(i) + os.kill(self.proc[i]['pid'],signal.SIGTERM) + except (IOError,OSError): + pass + del self.proc[i] + def proc_read(self,fd): + try: + t=self.proc[fd]['term'] + t.write(os.read(fd,65536)) + reply=t.read() + if reply: + os.write(fd,reply) + self.proc[fd]['time']=time.time() + except (KeyError,IOError,OSError): + self.proc_kill(fd) + def proc_write(self,fd,s): + try: + os.write(fd,s) + except (IOError,OSError): + self.proc_kill(fd) + def dump(self,fd,color=1): + try: + return self.proc[fd]['term'].dumphtml(color) + except KeyError: + return False + def loop(self): + while self.run(): + fds=self.fds() + i,o,e=select.select(fds, [], [], 1.0) + if time.time() - self.lastActivity > 120: + global g_server + g_server.shutdown() + for fd in i: + self.proc_read(fd) + if len(i): + time.sleep(0.002) + for i in self.proc.keys(): + try: + os.close(i) + os.kill(self.proc[i]['pid'],signal.SIGTERM) + except (IOError,OSError): + pass class AjaxTerm: - def __init__(self,cmd=None,index_file='ajaxterm.html',token=None): - self.files={} - self.token=token - for i in ['css','html','js']: - for j in glob.glob('*.%s'%i): - self.files[j]=file(j).read() - self.files['index']=file(index_file).read() - self.mime = mimetypes.types_map.copy() - self.mime['.html']= 'text/html; charset=UTF-8' - self.multi = Multiplex(cmd) - self.session = {} - def __call__(self, environ, start_response): - req = qweb.QWebRequest(environ, start_response,session=None) - if req.PATH_INFO.endswith('/u'): - s=req.REQUEST["s"] - k=req.REQUEST["k"] - c=req.REQUEST["c"] - w=req.REQUEST.int("w") - h=req.REQUEST.int("h") - if s in self.session: - term=self.session[s] - else: - raise 'Not Authorized' - if not (w>2 and w<256 and h>2 and h<100): - w,h=80,25 - term=self.session[s]=self.multi.create(w,h) - if k: - self.multi.proc_write(term,k) - time.sleep(0.002) - self.multi.lastActivity = time.time(); - dump=self.multi.dump(term,c) - req.response_headers['Content-Type']='text/xml' - if isinstance(dump,str): - req.write(dump) - req.response_gzencode=1 - else: - del self.session[s] - req.write('') -# print "sessions %r"%self.session - else: - n=os.path.basename(req.PATH_INFO) - if n in self.files: - req.response_headers['Content-Type'] = self.mime.get(os.path.splitext(n)[1].lower(), 'application/octet-stream') - req.write(self.files[n]) - elif req.REQUEST['token'] == self.token: - req.response_headers['Content-Type'] = 'text/html; charset=UTF-8' - session_id = str(uuid.uuid4()) - req.write(string.Template(self.files['index']).substitute(session_id=session_id)) - term=self.session[session_id]=self.multi.create(80,25) - else: - raise "Not Authorized" - return req + def __init__(self,cmd=None,index_file='ajaxterm.html',token=None): + self.files={} + self.token=token + for i in ['css','html','js']: + for j in glob.glob('*.%s'%i): + self.files[j]=file(j).read() + self.files['index']=file(index_file).read() + self.mime = mimetypes.types_map.copy() + self.mime['.html']= 'text/html; charset=UTF-8' + self.multi = Multiplex(cmd) + self.session = {} + def __call__(self, environ, start_response): + req = qweb.QWebRequest(environ, start_response,session=None) + if req.PATH_INFO.endswith('/u'): + s=req.REQUEST["s"] + k=req.REQUEST["k"] + c=req.REQUEST["c"] + w=req.REQUEST.int("w") + h=req.REQUEST.int("h") + if s in self.session: + term=self.session[s] + else: + raise Exception('Not Authorized') + # The original code below was insecure, because it allowed unauthorized sessions to be created + # if not (w>2 and w<256 and h>2 and h<100): + # w,h=80,25 + # term=self.session[s]=self.multi.create(w,h) + if k: + self.multi.proc_write(term,k) + time.sleep(0.002) + self.multi.lastActivity = time.time(); + dump=self.multi.dump(term,c) + req.response_headers['Content-Type']='text/xml' + if isinstance(dump,str): + req.write(dump) + req.response_gzencode=1 + else: + del self.session[s] + req.write('') +# print "sessions %r"%self.session + else: + n=os.path.basename(req.PATH_INFO) + if n in self.files: + req.response_headers['Content-Type'] = self.mime.get(os.path.splitext(n)[1].lower(), 'application/octet-stream') + req.write(self.files[n]) + elif req.REQUEST['token'] == self.token: + req.response_headers['Content-Type'] = 'text/html; charset=UTF-8' + session_id = str(uuid.uuid4()) + req.write(string.Template(self.files['index']).substitute(session_id=session_id)) + term=self.session[session_id]=self.multi.create(80,25) + else: + raise Exception("Not Authorized") + return req def main(): - parser = optparse.OptionParser() - parser.add_option("-p", "--port", dest="port", default="8022", help="Set the TCP port (default: 8022)") - parser.add_option("-c", "--command", dest="cmd", default=None,help="set the command (default: /bin/login or ssh 0.0.0.0)") - parser.add_option("-l", "--log", action="store_true", dest="log",default=0,help="log requests to stderr (default: quiet mode)") - parser.add_option("-d", "--daemon", action="store_true", dest="daemon", default=0, help="run as daemon in the background") - parser.add_option("-P", "--pidfile",dest="pidfile",default="/var/run/ajaxterm.pid",help="set the pidfile (default: /var/run/ajaxterm.pid)") - parser.add_option("-i", "--index", dest="index_file", default="ajaxterm.html",help="default index file (default: ajaxterm.html)") - parser.add_option("-u", "--uid", dest="uid", help="Set the daemon's user id") - parser.add_option("-t", "--token", dest="token", help="Set authorization token") - (o, a) = parser.parse_args() - if o.daemon: - pid=os.fork() - if pid == 0: - #os.setsid() ? - os.setpgrp() - nullin = file('/dev/null', 'r') - nullout = file('/dev/null', 'w') - os.dup2(nullin.fileno(), sys.stdin.fileno()) - os.dup2(nullout.fileno(), sys.stdout.fileno()) - os.dup2(nullout.fileno(), sys.stderr.fileno()) - if os.getuid()==0 and o.uid: - try: - os.setuid(int(o.uid)) - except: - os.setuid(pwd.getpwnam(o.uid).pw_uid) - else: - try: - file(o.pidfile,'w+').write(str(pid)+'\n') - except: - pass - print 'AjaxTerm at http://0.0.0.0:%s/ pid: %d' % (o.port,pid) - sys.exit(0) - else: - print 'AjaxTerm at http://0.0.0.0:%s/' % o.port - at=AjaxTerm(o.cmd,o.index_file,o.token) -# f=lambda:os.system('firefox http://localhost:%s/&'%o.port) -# qweb.qweb_wsgi_autorun(at,ip='localhost',port=int(o.port),threaded=0,log=o.log,callback_ready=None) - try: - global g_server - g_server = qweb.QWebWSGIServer(at,ip='0.0.0.0',port=int(o.port),threaded=0,log=o.log) - g_server.serve_forever() - except KeyboardInterrupt,e: - sys.excepthook(*sys.exc_info()) - at.multi.die() + parser = optparse.OptionParser() + parser.add_option("-p", "--port", dest="port", default="8022", help="Set the TCP port (default: 8022)") + parser.add_option("-c", "--command", dest="cmd", default=None,help="set the command (default: /bin/login or ssh 0.0.0.0)") + parser.add_option("-l", "--log", action="store_true", dest="log",default=0,help="log requests to stderr (default: quiet mode)") + parser.add_option("-d", "--daemon", action="store_true", dest="daemon", default=0, help="run as daemon in the background") + parser.add_option("-P", "--pidfile",dest="pidfile",default="/var/run/ajaxterm.pid",help="set the pidfile (default: /var/run/ajaxterm.pid)") + parser.add_option("-i", "--index", dest="index_file", default="ajaxterm.html",help="default index file (default: ajaxterm.html)") + parser.add_option("-u", "--uid", dest="uid", help="Set the daemon's user id") + parser.add_option("-t", "--token", dest="token", help="Set authorization token") + (o, a) = parser.parse_args() + if o.daemon: + pid=os.fork() + if pid == 0: + #os.setsid() ? + os.setpgrp() + nullin = file('/dev/null', 'r') + nullout = file('/dev/null', 'w') + os.dup2(nullin.fileno(), sys.stdin.fileno()) + os.dup2(nullout.fileno(), sys.stdout.fileno()) + os.dup2(nullout.fileno(), sys.stderr.fileno()) + if os.getuid()==0 and o.uid: + try: + os.setuid(int(o.uid)) + except: + os.setuid(pwd.getpwnam(o.uid).pw_uid) + else: + try: + file(o.pidfile,'w+').write(str(pid)+'\n') + except: + pass + print 'AjaxTerm at http://0.0.0.0:%s/ pid: %d' % (o.port,pid) + sys.exit(0) + else: + print 'AjaxTerm at http://0.0.0.0:%s/' % o.port + at=AjaxTerm(o.cmd,o.index_file,o.token) +# f=lambda:os.system('firefox http://localhost:%s/&'%o.port) +# qweb.qweb_wsgi_autorun(at,ip='localhost',port=int(o.port),threaded=0,log=o.log,callback_ready=None) + try: + global g_server + g_server = qweb.QWebWSGIServer(at,ip='0.0.0.0',port=int(o.port),threaded=0,log=o.log) + g_server.serve_forever() + except KeyboardInterrupt,e: + sys.excepthook(*sys.exc_info()) + at.multi.die() if __name__ == '__main__': - main() + main() -- cgit