~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to console/python-console

  • Committer: drtomc
  • Date: 2008-01-31 08:11:37 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:352
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
Reason: Easier to control (since web.py offered limited control over *when* to Daemonise). Ultimately, this was necessary to get the port allocation strategy working.

* Replaced the HTTP / web.py code with socket code. (Web.py is no longer a dependency).
* Sundry bug fixes.
* Daemonization code - moved out of a Shell "&" call on the Apache side to a proper Daemon code on the spawned server side.

The upshot is we can now spawn any number of servers. Console is DONE for Alpha release.


YOU HAVE NO IDEA HOW TRICKY THIS WAS!!!

Show diffs side-by-side

added added

removed removed

Lines of Context:
3
3
# usage:
4
4
#   python-console <port> <magic>
5
5
 
6
 
import sys
7
 
import web
8
 
import md5
 
6
import cjson
9
7
import codeop
10
 
import cjson
11
 
import cgi
12
8
import cStringIO
 
9
import md5
 
10
import os
 
11
import Queue
13
12
import signal
14
 
import Queue
 
13
import socket
 
14
import sys
15
15
from threading import Thread
16
16
 
17
17
class StdinFromWeb(object):
59
59
                        # The command was complete,
60
60
                        # so evaluate it!
61
61
                        sys.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
62
 
                        self.out = cStringIO.StringIO()
63
62
                        sys.stdout = self.out
64
63
                        sys.stderr = self.out
65
64
                        signal.alarm(5)
67
66
                        signal.alarm(0)
68
67
                        self.cmdQ.put({"okay":(self.out.getvalue(),res)})
69
68
                        self.curr_cmd = ''
 
69
                        self.out = cStringIO.StringIO()
70
70
                except Exception, exc:
71
71
                    signal.alarm(0)
72
72
                    self.cmdQ.put({"exc":(self.out.getvalue(),str(exc))})
73
73
                    self.curr_cmd = ''
 
74
                    self.out = cStringIO.StringIO()
74
75
            if 'block' in ln:
75
76
                # throw away a partial command.
76
 
                self.curr_cmd = ''
77
77
                try:
78
78
                    cmd = compile(ln['block'], "<web session>", 'exec');
79
79
                    
80
80
                    sys.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
81
81
                    self.out = cStringIO.StringIO()
82
 
                    sys.stdout = out
83
 
                    sys.stderr = out
 
82
                    sys.stdout = self.out
 
83
                    sys.stderr = self.out
84
84
                    signal.alarm(5)
85
85
                    res = eval(cmd, self.globs, self.locls)
86
86
                    signal.alarm(0)
87
87
                    self.cmdQ.put({"okay":(self.out.getvalue(),res)})
88
88
                    self.curr_cmd = ''
 
89
                    self.out = cStringIO.StringIO()
89
90
                except Exception, exc:
90
91
                    signal.alarm(0)
91
92
                    self.cmdQ.put({"exc":(self.out.getvalue(),str(exc))})
 
93
                    self.curr_cmd = ''
 
94
                    self.out = cStringIO.StringIO()
92
95
 
93
96
    def init_state(self):
94
97
        self.globs = {}
96
99
        self.locls = {}
97
100
        self.curr_cmd = ''
98
101
 
99
 
urls = (
100
 
    '/chat',        'chat',
101
 
    '/block',       'block')
 
102
def daemonize():
 
103
    if os.fork():   # launch child and...
 
104
        os._exit(0) # kill off parent
 
105
    os.setsid()
 
106
    if os.fork():   # launch child and...
 
107
        os._exit(0) # kill off parent again.
 
108
    os.umask(077)
102
109
 
103
110
# The global 'magic' is the secret that the client and server share
104
111
# which is used to create and md5 digest to authenticate requests.
105
112
# It is assigned a real value at startup.
106
113
magic = ''
107
114
 
108
 
class chat:
109
 
 
110
 
    def POST(self):
111
 
        inp = web.input()
112
 
 
113
 
        # Authenticate
114
 
        digest = md5.new(inp.text + magic).digest().encode('hex')
115
 
        if inp.digest != digest:
116
 
            web.output("401 Unauthorized")
117
 
            web.ctx.status = '401 Unauthorized'
118
 
            return
119
 
 
120
 
        # Okay, so the authentication succeeded,
121
 
        # so now we have the trivial matter of actually
122
 
        # executing the python....
123
 
        lineQ.put({'chat':inp.text})
124
 
        r = cmdQ.get()
125
 
        sys.__stderr__.write(cjson.encode(r) + "\n")
126
 
        web.output(cjson.encode(r))
127
 
 
128
 
class block:
129
 
 
130
 
    def POST(self):
131
 
        inp = web.input()
132
 
 
133
 
        # Authenticate
134
 
        digest = md5.new(inp.text + magic).digest().encode('hex')
135
 
        if inp.digest != digest:
136
 
            web.output("401 Unauthorized")
137
 
            web.ctx.status = '401 Unauthorized'
138
 
            return
139
 
 
140
 
        # Okay, so the authentication succeeded,
141
 
        # so now we have the trivial matter of actually
142
 
        # executing the python....
143
 
        lineQ.put({'block':inp.text})
144
 
        r = cmdQ.get()
145
 
        sys.__stderr__.write(cjson.encode(r) + "\n")
146
 
        web.output(cjson.encode(r))
147
 
 
148
115
cmdQ = Queue.Queue()
149
116
lineQ = Queue.Queue()
150
117
interpThread = PythonRunner(cmdQ, lineQ)
151
118
 
152
119
if __name__ == "__main__":
 
120
    port = int(sys.argv[1])
153
121
    magic = sys.argv[2]
 
122
 
 
123
    # Attempt to open the socket.
 
124
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
125
    s.bind(('', port))
 
126
    s.listen(1)
 
127
 
 
128
    # Excellent! It worked. Let's turn ourself into a daemon,
 
129
    # then get on with the job of being a python interpreter.
 
130
    daemonize()
 
131
 
154
132
    interpThread.setDaemon(True)
155
133
    interpThread.start()
156
 
    web.run(urls, globals())
 
134
 
 
135
    while True:
 
136
        (conn, addr) = s.accept()
 
137
        try:
 
138
            # Grab the input
 
139
            buf = cStringIO.StringIO()
 
140
            blk = conn.recv(1024)
 
141
            while blk:
 
142
                buf.write(blk)
 
143
                try:
 
144
                    blk = conn.recv(1024, socket.MSG_DONTWAIT)
 
145
                except:
 
146
                    # Exception thrown if it WOULD block (but we
 
147
                    # told it not to wait) - ie. we are done
 
148
                    blk = None
 
149
            inp = buf.getvalue()
 
150
            msg = cjson.decode(inp)
 
151
            
 
152
            # Check that the message is 
 
153
            digest = md5.new(msg['text'] + magic).digest().encode('hex')
 
154
            if msg['digest'] != digest:
 
155
                conn.close()
 
156
                continue
 
157
 
 
158
            lineQ.put({msg['cmd']:msg['text']})
 
159
            r = cmdQ.get()
 
160
            conn.sendall(cjson.encode(r))
 
161
            conn.close()
 
162
        except Exception, e:
 
163
            conn.close()