~ubuntu-branches/ubuntu/quantal/nova/quantal-proposed

« back to all changes in this revision

Viewing changes to tools/ajaxterm/ajaxterm.py

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2011-01-21 11:48:06 UTC
  • mto: This revision was merged to the branch mainline in revision 9.
  • Revision ID: james.westby@ubuntu.com-20110121114806-v8fvnnl6az4m4ohv
Tags: upstream-2011.1~bzr597
ImportĀ upstreamĀ versionĀ 2011.1~bzr597

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
 
 
3
""" Ajaxterm """
 
4
 
 
5
import array,cgi,fcntl,glob,mimetypes,optparse,os,pty,random,re,signal,select,sys,threading,time,termios,struct,pwd
 
6
 
 
7
os.chdir(os.path.normpath(os.path.dirname(__file__)))
 
8
# Optional: Add QWeb in sys path
 
9
sys.path[0:0]=glob.glob('../../python')
 
10
 
 
11
import qweb
 
12
import string, subprocess, uuid
 
13
 
 
14
global g_server
 
15
TIMEOUT=300
 
16
 
 
17
class Terminal:
 
18
    def __init__(self,width=80,height=24):
 
19
        self.width=width
 
20
        self.height=height
 
21
        self.init()
 
22
        self.reset()
 
23
    def init(self):
 
24
        self.esc_seq={
 
25
            "\x00": None,
 
26
            "\x05": self.esc_da,
 
27
            "\x07": None,
 
28
            "\x08": self.esc_0x08,
 
29
            "\x09": self.esc_0x09,
 
30
            "\x0a": self.esc_0x0a,
 
31
            "\x0b": self.esc_0x0a,
 
32
            "\x0c": self.esc_0x0a,
 
33
            "\x0d": self.esc_0x0d,
 
34
            "\x0e": None,
 
35
            "\x0f": None,
 
36
            "\x1b#8": None,
 
37
            "\x1b=": None,
 
38
            "\x1b>": None,
 
39
            "\x1b(0": None,
 
40
            "\x1b(A": None,
 
41
            "\x1b(B": None,
 
42
            "\x1b[c": self.esc_da,
 
43
            "\x1b[0c": self.esc_da,
 
44
            "\x1b]R": None,
 
45
            "\x1b7": self.esc_save,
 
46
            "\x1b8": self.esc_restore,
 
47
            "\x1bD": None,
 
48
            "\x1bE": None,
 
49
            "\x1bH": None,
 
50
            "\x1bM": self.esc_ri,
 
51
            "\x1bN": None,
 
52
            "\x1bO": None,
 
53
            "\x1bZ": self.esc_da,
 
54
            "\x1ba": None,
 
55
            "\x1bc": self.reset,
 
56
            "\x1bn": None,
 
57
            "\x1bo": None,
 
58
        }
 
59
        for k,v in self.esc_seq.items():
 
60
            if v==None:
 
61
                self.esc_seq[k]=self.esc_ignore
 
62
        # regex
 
63
        d={
 
64
            r'\[\??([0-9;]*)([@ABCDEFGHJKLMPXacdefghlmnqrstu`])' : self.csi_dispatch,
 
65
            r'\]([^\x07]+)\x07' : self.esc_ignore,
 
66
        }
 
67
        self.esc_re=[]
 
68
        for k,v in d.items():
 
69
            self.esc_re.append((re.compile('\x1b'+k),v))
 
70
        # define csi sequences
 
71
        self.csi_seq={
 
72
            '@': (self.csi_at,[1]),
 
73
            '`': (self.csi_G,[1]),
 
74
            'J': (self.csi_J,[0]),
 
75
            'K': (self.csi_K,[0]),
 
76
        }
 
77
        for i in [i[4] for i in dir(self) if i.startswith('csi_') and len(i)==5]:
 
78
            if not self.csi_seq.has_key(i):
 
79
                self.csi_seq[i]=(getattr(self,'csi_'+i),[1])
 
80
        # Init 0-256 to latin1 and html translation table
 
81
        self.trl1=""
 
82
        for i in range(256):
 
83
            if i<32:
 
84
                self.trl1+=" "
 
85
            elif i<127 or i>160:
 
86
                self.trl1+=chr(i)
 
87
            else:
 
88
                self.trl1+="?"
 
89
        self.trhtml=""
 
90
        for i in range(256):
 
91
            if i==0x0a or (i>32 and i<127) or i>160:
 
92
                self.trhtml+=chr(i)
 
93
            elif i<=32:
 
94
                self.trhtml+="\xa0"
 
95
            else:
 
96
                self.trhtml+="?"
 
97
    def reset(self,s=""):
 
98
        self.scr=array.array('i',[0x000700]*(self.width*self.height))
 
99
        self.st=0
 
100
        self.sb=self.height-1
 
101
        self.cx_bak=self.cx=0
 
102
        self.cy_bak=self.cy=0
 
103
        self.cl=0
 
104
        self.sgr=0x000700
 
105
        self.buf=""
 
106
        self.outbuf=""
 
107
        self.last_html=""
 
108
    def peek(self,y1,x1,y2,x2):
 
109
        return self.scr[self.width*y1+x1:self.width*y2+x2]
 
110
    def poke(self,y,x,s):
 
111
        pos=self.width*y+x
 
112
        self.scr[pos:pos+len(s)]=s
 
113
    def zero(self,y1,x1,y2,x2):
 
114
        w=self.width*(y2-y1)+x2-x1+1
 
115
        z=array.array('i',[0x000700]*w)
 
116
        self.scr[self.width*y1+x1:self.width*y2+x2+1]=z
 
117
    def scroll_up(self,y1,y2):
 
118
        self.poke(y1,0,self.peek(y1+1,0,y2,self.width))
 
119
        self.zero(y2,0,y2,self.width-1)
 
120
    def scroll_down(self,y1,y2):
 
121
        self.poke(y1+1,0,self.peek(y1,0,y2-1,self.width))
 
122
        self.zero(y1,0,y1,self.width-1)
 
123
    def scroll_right(self,y,x):
 
124
        self.poke(y,x+1,self.peek(y,x,y,self.width))
 
125
        self.zero(y,x,y,x)
 
126
    def cursor_down(self):
 
127
        if self.cy>=self.st and self.cy<=self.sb:
 
128
            self.cl=0
 
129
            q,r=divmod(self.cy+1,self.sb+1)
 
130
            if q:
 
131
                self.scroll_up(self.st,self.sb)
 
132
                self.cy=self.sb
 
133
            else:
 
134
                self.cy=r
 
135
    def cursor_right(self):
 
136
        q,r=divmod(self.cx+1,self.width)
 
137
        if q:
 
138
            self.cl=1
 
139
        else:
 
140
            self.cx=r
 
141
    def echo(self,c):
 
142
        if self.cl:
 
143
            self.cursor_down()
 
144
            self.cx=0
 
145
        self.scr[(self.cy*self.width)+self.cx]=self.sgr|ord(c)
 
146
        self.cursor_right()
 
147
    def esc_0x08(self,s):
 
148
        self.cx=max(0,self.cx-1)
 
149
    def esc_0x09(self,s):
 
150
        x=self.cx+8
 
151
        q,r=divmod(x,8)
 
152
        self.cx=(q*8)%self.width
 
153
    def esc_0x0a(self,s):
 
154
        self.cursor_down()
 
155
    def esc_0x0d(self,s):
 
156
        self.cl=0
 
157
        self.cx=0
 
158
    def esc_save(self,s):
 
159
        self.cx_bak=self.cx
 
160
        self.cy_bak=self.cy
 
161
    def esc_restore(self,s):
 
162
        self.cx=self.cx_bak
 
163
        self.cy=self.cy_bak
 
164
        self.cl=0
 
165
    def esc_da(self,s):
 
166
        self.outbuf="\x1b[?6c"
 
167
    def esc_ri(self,s):
 
168
        self.cy=max(self.st,self.cy-1)
 
169
        if self.cy==self.st:
 
170
            self.scroll_down(self.st,self.sb)
 
171
    def esc_ignore(self,*s):
 
172
        pass
 
173
#        print "term:ignore: %s"%repr(s)
 
174
    def csi_dispatch(self,seq,mo):
 
175
    # CSI sequences
 
176
        s=mo.group(1)
 
177
        c=mo.group(2)
 
178
        f=self.csi_seq.get(c,None)
 
179
        if f:
 
180
            try:
 
181
                l=[min(int(i),1024) for i in s.split(';') if len(i)<4]
 
182
            except ValueError:
 
183
                l=[]
 
184
            if len(l)==0:
 
185
                l=f[1]
 
186
            f[0](l)
 
187
#        else:
 
188
#            print 'csi ignore',c,l
 
189
    def csi_at(self,l):
 
190
        for i in range(l[0]):
 
191
            self.scroll_right(self.cy,self.cx)
 
192
    def csi_A(self,l):
 
193
        self.cy=max(self.st,self.cy-l[0])
 
194
    def csi_B(self,l):
 
195
        self.cy=min(self.sb,self.cy+l[0])
 
196
    def csi_C(self,l):
 
197
        self.cx=min(self.width-1,self.cx+l[0])
 
198
        self.cl=0
 
199
    def csi_D(self,l):
 
200
        self.cx=max(0,self.cx-l[0])
 
201
        self.cl=0
 
202
    def csi_E(self,l):
 
203
        self.csi_B(l)
 
204
        self.cx=0
 
205
        self.cl=0
 
206
    def csi_F(self,l):
 
207
        self.csi_A(l)
 
208
        self.cx=0
 
209
        self.cl=0
 
210
    def csi_G(self,l):
 
211
        self.cx=min(self.width,l[0])-1
 
212
    def csi_H(self,l):
 
213
        if len(l)<2: l=[1,1]
 
214
        self.cx=min(self.width,l[1])-1
 
215
        self.cy=min(self.height,l[0])-1
 
216
        self.cl=0
 
217
    def csi_J(self,l):
 
218
        if l[0]==0:
 
219
            self.zero(self.cy,self.cx,self.height-1,self.width-1)
 
220
        elif l[0]==1:
 
221
            self.zero(0,0,self.cy,self.cx)
 
222
        elif l[0]==2:
 
223
            self.zero(0,0,self.height-1,self.width-1)
 
224
    def csi_K(self,l):
 
225
        if l[0]==0:
 
226
            self.zero(self.cy,self.cx,self.cy,self.width-1)
 
227
        elif l[0]==1:
 
228
            self.zero(self.cy,0,self.cy,self.cx)
 
229
        elif l[0]==2:
 
230
            self.zero(self.cy,0,self.cy,self.width-1)
 
231
    def csi_L(self,l):
 
232
        for i in range(l[0]):
 
233
            if self.cy<self.sb:
 
234
                self.scroll_down(self.cy,self.sb)
 
235
    def csi_M(self,l):
 
236
        if self.cy>=self.st and self.cy<=self.sb:
 
237
            for i in range(l[0]):
 
238
                self.scroll_up(self.cy,self.sb)
 
239
    def csi_P(self,l):
 
240
        w,cx,cy=self.width,self.cx,self.cy
 
241
        end=self.peek(cy,cx,cy,w)
 
242
        self.csi_K([0])
 
243
        self.poke(cy,cx,end[l[0]:])
 
244
    def csi_X(self,l):
 
245
        self.zero(self.cy,self.cx,self.cy,self.cx+l[0])
 
246
    def csi_a(self,l):
 
247
        self.csi_C(l)
 
248
    def csi_c(self,l):
 
249
        #'\x1b[?0c' 0-8 cursor size
 
250
        pass
 
251
    def csi_d(self,l):
 
252
        self.cy=min(self.height,l[0])-1
 
253
    def csi_e(self,l):
 
254
        self.csi_B(l)
 
255
    def csi_f(self,l):
 
256
        self.csi_H(l)
 
257
    def csi_h(self,l):
 
258
        if l[0]==4:
 
259
            pass
 
260
#            print "insert on"
 
261
    def csi_l(self,l):
 
262
        if l[0]==4:
 
263
            pass
 
264
#            print "insert off"
 
265
    def csi_m(self,l):
 
266
        for i in l:
 
267
            if i==0 or i==39 or i==49 or i==27:
 
268
                self.sgr=0x000700
 
269
            elif i==1:
 
270
                self.sgr=(self.sgr|0x000800)
 
271
            elif i==7:
 
272
                self.sgr=0x070000
 
273
            elif i>=30 and i<=37:
 
274
                c=i-30
 
275
                self.sgr=(self.sgr&0xff08ff)|(c<<8)
 
276
            elif i>=40 and i<=47:
 
277
                c=i-40
 
278
                self.sgr=(self.sgr&0x00ffff)|(c<<16)
 
279
#            else:
 
280
#                print "CSI sgr ignore",l,i
 
281
#        print 'sgr: %r %x'%(l,self.sgr)
 
282
    def csi_r(self,l):
 
283
        if len(l)<2: l=[0,self.height]
 
284
        self.st=min(self.height-1,l[0]-1)
 
285
        self.sb=min(self.height-1,l[1]-1)
 
286
        self.sb=max(self.st,self.sb)
 
287
    def csi_s(self,l):
 
288
        self.esc_save(0)
 
289
    def csi_u(self,l):
 
290
        self.esc_restore(0)
 
291
    def escape(self):
 
292
        e=self.buf
 
293
        if len(e)>32:
 
294
#            print "error %r"%e
 
295
            self.buf=""
 
296
        elif e in self.esc_seq:
 
297
            self.esc_seq[e](e)
 
298
            self.buf=""
 
299
        else:
 
300
            for r,f in self.esc_re:
 
301
                mo=r.match(e)
 
302
                if mo:
 
303
                    f(e,mo)
 
304
                    self.buf=""
 
305
                    break
 
306
#        if self.buf=='': print "ESC %r\n"%e
 
307
    def write(self,s):
 
308
        for i in s:
 
309
            if len(self.buf) or (i in self.esc_seq):
 
310
                self.buf+=i
 
311
                self.escape()
 
312
            elif i == '\x1b':
 
313
                self.buf+=i
 
314
            else:
 
315
                self.echo(i)
 
316
    def read(self):
 
317
        b=self.outbuf
 
318
        self.outbuf=""
 
319
        return b
 
320
    def dump(self):
 
321
        r=''
 
322
        for i in self.scr:
 
323
            r+=chr(i&255)
 
324
        return r
 
325
    def dumplatin1(self):
 
326
        return self.dump().translate(self.trl1)
 
327
    def dumphtml(self,color=1):
 
328
        h=self.height
 
329
        w=self.width
 
330
        r=""
 
331
        span=""
 
332
        span_bg,span_fg=-1,-1
 
333
        for i in range(h*w):
 
334
            q,c=divmod(self.scr[i],256)
 
335
            if color:
 
336
                bg,fg=divmod(q,256)
 
337
            else:
 
338
                bg,fg=0,7
 
339
            if i==self.cy*w+self.cx:
 
340
                bg,fg=1,7
 
341
            if (bg!=span_bg or fg!=span_fg or i==h*w-1):
 
342
                if len(span):
 
343
                    r+='<span class="f%d b%d">%s</span>'%(span_fg,span_bg,cgi.escape(span.translate(self.trhtml)))
 
344
                span=""
 
345
                span_bg,span_fg=bg,fg
 
346
            span+=chr(c)
 
347
            if i%w==w-1:
 
348
                span+='\n'
 
349
        r='<?xml version="1.0" encoding="ISO-8859-1"?><pre class="term">%s</pre>'%r
 
350
        if self.last_html==r:
 
351
            return '<?xml version="1.0"?><idem></idem>'
 
352
        else:
 
353
            self.last_html=r
 
354
#            print self
 
355
            return r
 
356
    def __repr__(self):
 
357
        d=self.dumplatin1()
 
358
        r=""
 
359
        for i in range(self.height):
 
360
            r+="|%s|\n"%d[self.width*i:self.width*(i+1)]
 
361
        return r
 
362
 
 
363
class SynchronizedMethod:
 
364
    def __init__(self,lock,orig):
 
365
        self.lock=lock
 
366
        self.orig=orig
 
367
    def __call__(self,*l):
 
368
        self.lock.acquire()
 
369
        r=self.orig(*l)
 
370
        self.lock.release()
 
371
        return r
 
372
 
 
373
class Multiplex:
 
374
    def __init__(self,cmd=None):
 
375
        signal.signal(signal.SIGCHLD, signal.SIG_IGN)
 
376
        self.cmd=cmd
 
377
        self.proc={}
 
378
        self.lock=threading.RLock()
 
379
        self.thread=threading.Thread(target=self.loop)
 
380
        self.alive=1
 
381
        self.lastActivity=time.time()
 
382
        # synchronize methods
 
383
        for name in ['create','fds','proc_read','proc_write','dump','die','run']:
 
384
            orig=getattr(self,name)
 
385
            setattr(self,name,SynchronizedMethod(self.lock,orig))
 
386
        self.thread.start()
 
387
    def create(self,w=80,h=25):
 
388
        pid,fd=pty.fork()
 
389
        if pid==0:
 
390
            try:
 
391
                fdl=[int(i) for i in os.listdir('/proc/self/fd')]
 
392
            except OSError:
 
393
                fdl=range(256)
 
394
            for i in [i for i in fdl if i>2]:
 
395
                try:
 
396
                    os.close(i)
 
397
                except OSError:
 
398
                    pass
 
399
            if self.cmd:
 
400
                cmd=['/bin/sh','-c',self.cmd]
 
401
            elif os.getuid()==0:
 
402
                cmd=['/bin/login']
 
403
            else:
 
404
                sys.stdout.write("Login: ")
 
405
                login=sys.stdin.readline().strip()
 
406
                if re.match('^[0-9A-Za-z-_. ]+$',login):
 
407
                    cmd=['ssh']
 
408
                    cmd+=['-oPreferredAuthentications=keyboard-interactive,password']
 
409
                    cmd+=['-oNoHostAuthenticationForLocalhost=yes']
 
410
                    cmd+=['-oLogLevel=FATAL']
 
411
                    cmd+=['-F/dev/null','-l',login,'localhost']
 
412
                else:
 
413
                    os._exit(0)
 
414
            env={}
 
415
            env["COLUMNS"]=str(w)
 
416
            env["LINES"]=str(h)
 
417
            env["TERM"]="linux"
 
418
            env["PATH"]=os.environ['PATH']
 
419
            os.execvpe(cmd[0],cmd,env)
 
420
        else:
 
421
            fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK)
 
422
            # python bug http://python.org/sf/1112949 on amd64
 
423
            fcntl.ioctl(fd, struct.unpack('i',struct.pack('I',termios.TIOCSWINSZ))[0], struct.pack("HHHH",h,w,0,0))
 
424
            self.proc[fd]={'pid':pid,'term':Terminal(w,h),'buf':'','time':time.time()}
 
425
            return fd
 
426
    def die(self):
 
427
        self.alive=0
 
428
    def run(self):
 
429
        return self.alive
 
430
    def fds(self):
 
431
        return self.proc.keys()
 
432
    def proc_kill(self,fd):
 
433
        if fd in self.proc:
 
434
            self.proc[fd]['time']=0
 
435
        t=time.time()
 
436
        for i in self.proc.keys():
 
437
            t0=self.proc[i]['time']
 
438
            if (t-t0)>TIMEOUT:
 
439
                try:
 
440
                    os.close(i)
 
441
                    os.kill(self.proc[i]['pid'],signal.SIGTERM)
 
442
                except (IOError,OSError):
 
443
                    pass
 
444
                del self.proc[i]
 
445
    def proc_read(self,fd):
 
446
        try:
 
447
            t=self.proc[fd]['term']
 
448
            t.write(os.read(fd,65536))
 
449
            reply=t.read()
 
450
            if reply:
 
451
                os.write(fd,reply)
 
452
            self.proc[fd]['time']=time.time()
 
453
        except (KeyError,IOError,OSError):
 
454
            self.proc_kill(fd)
 
455
    def proc_write(self,fd,s):
 
456
        try:
 
457
            os.write(fd,s)
 
458
        except (IOError,OSError):
 
459
            self.proc_kill(fd)
 
460
    def dump(self,fd,color=1):
 
461
        try:
 
462
            return self.proc[fd]['term'].dumphtml(color)
 
463
        except KeyError:
 
464
            return False
 
465
    def loop(self):
 
466
        while self.run():
 
467
            fds=self.fds()
 
468
            i,o,e=select.select(fds, [], [], 1.0)
 
469
            if time.time() - self.lastActivity > TIMEOUT:
 
470
                global g_server
 
471
                g_server.shutdown()
 
472
            for fd in i:
 
473
                self.proc_read(fd)
 
474
            if len(i):
 
475
                time.sleep(0.002)
 
476
        for i in self.proc.keys():
 
477
            try:
 
478
                os.close(i)
 
479
                os.kill(self.proc[i]['pid'],signal.SIGTERM)
 
480
            except (IOError,OSError):
 
481
                pass
 
482
 
 
483
class AjaxTerm:
 
484
    def __init__(self,cmd=None,index_file='ajaxterm.html',token=None):
 
485
        self.files={}
 
486
        self.token=token
 
487
        for i in ['css','html','js']:
 
488
            for j in glob.glob('*.%s'%i):
 
489
                self.files[j]=file(j).read()
 
490
        self.files['index']=file(index_file).read()
 
491
        self.mime = mimetypes.types_map.copy()
 
492
        self.mime['.html']= 'text/html; charset=UTF-8'
 
493
        self.multi = Multiplex(cmd)
 
494
        self.session = {}
 
495
    def __call__(self, environ, start_response):
 
496
        req = qweb.QWebRequest(environ, start_response,session=None)
 
497
        if req.PATH_INFO.endswith('/u'):
 
498
            s=req.REQUEST["s"]
 
499
            k=req.REQUEST["k"]
 
500
            c=req.REQUEST["c"]
 
501
            w=req.REQUEST.int("w")
 
502
            h=req.REQUEST.int("h")
 
503
            if s in self.session:
 
504
                term=self.session[s]
 
505
            else:
 
506
                raise Exception('Not Authorized')
 
507
                # The original code below was insecure, because it allowed unauthorized sessions to be created
 
508
                # if not (w>2 and w<256 and h>2 and h<100):
 
509
                # w,h=80,25
 
510
                # term=self.session[s]=self.multi.create(w,h)
 
511
            if k:
 
512
                self.multi.proc_write(term,k)
 
513
            time.sleep(0.002)
 
514
            self.multi.lastActivity = time.time();
 
515
            dump=self.multi.dump(term,c)
 
516
            req.response_headers['Content-Type']='text/xml'
 
517
            if isinstance(dump,str):
 
518
                req.write(dump)
 
519
                req.response_gzencode=1
 
520
            else:
 
521
                del self.session[s]
 
522
                req.write('<?xml version="1.0"?><idem></idem>')
 
523
#            print "sessions %r"%self.session
 
524
        else:
 
525
            n=os.path.basename(req.PATH_INFO)
 
526
            if n in self.files:
 
527
                req.response_headers['Content-Type'] = self.mime.get(os.path.splitext(n)[1].lower(), 'application/octet-stream')
 
528
                req.write(self.files[n])
 
529
            elif req.REQUEST['token'] == self.token:
 
530
                req.response_headers['Content-Type'] = 'text/html; charset=UTF-8'
 
531
                session_id = str(uuid.uuid4())
 
532
                req.write(string.Template(self.files['index']).substitute(session_id=session_id))
 
533
                term=self.session[session_id]=self.multi.create(80,25)
 
534
            else:
 
535
                raise Exception("Not Authorized")
 
536
        return req
 
537
 
 
538
def main():
 
539
    parser = optparse.OptionParser()
 
540
    parser.add_option("-p", "--port", dest="port", default="8022", help="Set the TCP port (default: 8022)")
 
541
    parser.add_option("-c", "--command", dest="cmd", default=None,help="set the command (default: /bin/login or ssh 0.0.0.0)")
 
542
    parser.add_option("-l", "--log", action="store_true", dest="log",default=0,help="log requests to stderr (default: quiet mode)")
 
543
    parser.add_option("-d", "--daemon", action="store_true", dest="daemon", default=0, help="run as daemon in the background")
 
544
    parser.add_option("-P", "--pidfile",dest="pidfile",default="/var/run/ajaxterm.pid",help="set the pidfile (default: /var/run/ajaxterm.pid)")
 
545
    parser.add_option("-i", "--index", dest="index_file", default="ajaxterm.html",help="default index file (default: ajaxterm.html)")
 
546
    parser.add_option("-u", "--uid", dest="uid", help="Set the daemon's user id")
 
547
    parser.add_option("-t", "--token", dest="token", help="Set authorization token")
 
548
    (o, a) = parser.parse_args()
 
549
    if o.daemon:
 
550
        pid=os.fork()
 
551
        if pid == 0:
 
552
            #os.setsid() ?
 
553
            os.setpgrp()
 
554
            nullin = file('/dev/null', 'r')
 
555
            nullout = file('/dev/null', 'w')
 
556
            os.dup2(nullin.fileno(), sys.stdin.fileno())
 
557
            os.dup2(nullout.fileno(), sys.stdout.fileno())
 
558
            os.dup2(nullout.fileno(), sys.stderr.fileno())
 
559
            if os.getuid()==0 and o.uid:
 
560
                try:
 
561
                    os.setuid(int(o.uid))
 
562
                except:
 
563
                    os.setuid(pwd.getpwnam(o.uid).pw_uid)
 
564
        else:
 
565
            try:
 
566
                file(o.pidfile,'w+').write(str(pid)+'\n')
 
567
            except:
 
568
                pass
 
569
            print 'AjaxTerm at http://0.0.0.0:%s/ pid: %d' % (o.port,pid)
 
570
            sys.exit(0)
 
571
    else:
 
572
        print 'AjaxTerm at http://0.0.0.0:%s/' % o.port
 
573
    at=AjaxTerm(o.cmd,o.index_file,o.token)
 
574
#    f=lambda:os.system('firefox http://localhost:%s/&'%o.port)
 
575
#    qweb.qweb_wsgi_autorun(at,ip='localhost',port=int(o.port),threaded=0,log=o.log,callback_ready=None)
 
576
    try:
 
577
        global g_server
 
578
        g_server = qweb.QWebWSGIServer(at,ip='0.0.0.0',port=int(o.port),threaded=0,log=o.log)
 
579
        g_server.serve_forever()
 
580
    except KeyboardInterrupt,e:
 
581
        sys.excepthook(*sys.exc_info())
 
582
    at.multi.die()
 
583
 
 
584
if __name__ == '__main__':
 
585
    main()
 
586