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

95 by mattgiuca
Moved some things out of www into their respective dirs, console and
1
#!/usr/bin/python
2
3
# usage:
4
#   python-console <port> <magic>
5
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
6
import cjson
95 by mattgiuca
Moved some things out of www into their respective dirs, console and
7
import codeop
126 by drtomc
A basic version of the console going!
8
import cStringIO
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
9
import md5
10
import os
11
import Queue
126 by drtomc
A basic version of the console going!
12
import signal
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
13
import socket
14
import sys
212 by drtomc
Gosh, it all looks pretty simple in the end!
15
from threading import Thread
16
17
class StdinFromWeb(object):
18
    def __init__(self, cmdQ, lineQ):
19
        self.cmdQ = cmdQ
20
        self.lineQ = lineQ
21
22
    def readline(self):
23
        # stop the clock!
128 by drtomc
A couple of bug fixes.
24
        signal.alarm(0)
212 by drtomc
Gosh, it all looks pretty simple in the end!
25
        self.cmdQ.put({"input":None})
340 by drtomc
Mostly fix the block-of-text to the console problem. The tutorial system should call "block" rather than "chat" to send the block of text.
26
        ln = self.lineQ.get()
27
        if 'chat' in ln:
28
            # restart the clock:
29
            # Some of our 5 seconds may have elapsed, but never mind.
30
            signal.alarm(5)
31
            return ln['chat']
212 by drtomc
Gosh, it all looks pretty simple in the end!
32
33
class PythonRunner(Thread):
34
    def __init__(self, cmdQ, lineQ):
35
        self.cmdQ = cmdQ
36
        self.lineQ = lineQ
348 by mattgiuca
python-console: Set PythonRunner.out on init to avoid a crash.
37
        self.out = cStringIO.StringIO()
212 by drtomc
Gosh, it all looks pretty simple in the end!
38
        Thread.__init__(self)
39
365 by drtomc
Make the console accept blocks of code.
40
    def execCmd(self, cmd):
41
        try:
42
            sys.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
43
            sys.stdout = self.out
44
            sys.stderr = self.out
45
            signal.alarm(5)
46
            res = eval(cmd, self.globs, self.locls)
47
            signal.alarm(0)
48
            self.cmdQ.put({"okay":(self.out.getvalue(),res)})
49
            self.curr_cmd = ''
50
            self.out = cStringIO.StringIO()
51
        except Exception, exc:
52
            signal.alarm(0)
53
            self.cmdQ.put({"exc":(self.out.getvalue(),str(exc))})
54
            self.curr_cmd = ''
55
            self.out = cStringIO.StringIO()
56
212 by drtomc
Gosh, it all looks pretty simple in the end!
57
    def run(self):
263 by drtomc
A few little cleanups.
58
        self.init_state()
212 by drtomc
Gosh, it all looks pretty simple in the end!
59
        compiler = codeop.CommandCompiler()
95 by mattgiuca
Moved some things out of www into their respective dirs, console and
60
212 by drtomc
Gosh, it all looks pretty simple in the end!
61
        while True:
340 by drtomc
Mostly fix the block-of-text to the console problem. The tutorial system should call "block" rather than "chat" to send the block of text.
62
            ln = self.lineQ.get()
63
            if 'chat' in ln:
64
                if self.curr_cmd == '':
65
                    self.curr_cmd = ln['chat']
212 by drtomc
Gosh, it all looks pretty simple in the end!
66
                else:
340 by drtomc
Mostly fix the block-of-text to the console problem. The tutorial system should call "block" rather than "chat" to send the block of text.
67
                    self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
68
                try:
69
                    cmd = compiler(self.curr_cmd)
70
                    if cmd is None:
71
                        # The command was incomplete,
72
                        # so send back a None, so the
73
                        # client can print a '...'
74
                        self.cmdQ.put({"more":None})
75
                    else:
365 by drtomc
Make the console accept blocks of code.
76
                        self.execCmd(cmd)
340 by drtomc
Mostly fix the block-of-text to the console problem. The tutorial system should call "block" rather than "chat" to send the block of text.
77
                except Exception, exc:
78
                    signal.alarm(0)
79
                    self.cmdQ.put({"exc":(self.out.getvalue(),str(exc))})
80
                    self.curr_cmd = ''
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
81
                    self.out = cStringIO.StringIO()
340 by drtomc
Mostly fix the block-of-text to the console problem. The tutorial system should call "block" rather than "chat" to send the block of text.
82
            if 'block' in ln:
83
                # throw away a partial command.
84
                try:
85
                    cmd = compile(ln['block'], "<web session>", 'exec');
365 by drtomc
Make the console accept blocks of code.
86
                    self.execCmd(cmd)
340 by drtomc
Mostly fix the block-of-text to the console problem. The tutorial system should call "block" rather than "chat" to send the block of text.
87
                except Exception, exc:
88
                    signal.alarm(0)
89
                    self.cmdQ.put({"exc":(self.out.getvalue(),str(exc))})
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
90
                    self.curr_cmd = ''
91
                    self.out = cStringIO.StringIO()
212 by drtomc
Gosh, it all looks pretty simple in the end!
92
263 by drtomc
A few little cleanups.
93
    def init_state(self):
94
        self.globs = {}
95
        self.globs['__builtins__'] = globals()['__builtins__']
96
        self.locls = {}
97
        self.curr_cmd = ''
98
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
99
def daemonize():
100
    if os.fork():   # launch child and...
101
        os._exit(0) # kill off parent
102
    os.setsid()
103
    if os.fork():   # launch child and...
104
        os._exit(0) # kill off parent again.
105
    os.umask(077)
95 by mattgiuca
Moved some things out of www into their respective dirs, console and
106
107
# The global 'magic' is the secret that the client and server share
108
# which is used to create and md5 digest to authenticate requests.
109
# It is assigned a real value at startup.
110
magic = ''
111
212 by drtomc
Gosh, it all looks pretty simple in the end!
112
cmdQ = Queue.Queue()
113
lineQ = Queue.Queue()
114
interpThread = PythonRunner(cmdQ, lineQ)
95 by mattgiuca
Moved some things out of www into their respective dirs, console and
115
116
if __name__ == "__main__":
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
117
    port = int(sys.argv[1])
95 by mattgiuca
Moved some things out of www into their respective dirs, console and
118
    magic = sys.argv[2]
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
119
120
    # Attempt to open the socket.
121
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
122
    s.bind(('', port))
123
    s.listen(1)
124
125
    # Excellent! It worked. Let's turn ourself into a daemon,
126
    # then get on with the job of being a python interpreter.
127
    daemonize()
128
264 by drtomc
Fix the "Aaarrgghh! ^C doesn't work problem". The multiple console process
129
    interpThread.setDaemon(True)
212 by drtomc
Gosh, it all looks pretty simple in the end!
130
    interpThread.start()
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
131
132
    while True:
133
        (conn, addr) = s.accept()
134
        try:
135
            # Grab the input
136
            buf = cStringIO.StringIO()
137
            blk = conn.recv(1024)
138
            while blk:
139
                buf.write(blk)
140
                try:
141
                    blk = conn.recv(1024, socket.MSG_DONTWAIT)
142
                except:
143
                    # Exception thrown if it WOULD block (but we
144
                    # told it not to wait) - ie. we are done
145
                    blk = None
146
            inp = buf.getvalue()
147
            msg = cjson.decode(inp)
148
            
149
            # Check that the message is 
150
            digest = md5.new(msg['text'] + magic).digest().encode('hex')
151
            if msg['digest'] != digest:
152
                conn.close()
153
                continue
154
155
            lineQ.put({msg['cmd']:msg['text']})
156
            r = cmdQ.get()
157
            conn.sendall(cjson.encode(r))
158
            conn.close()
159
        except Exception, e:
160
            conn.close()