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

418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
1
#!/usr/bin/python
2
3
# usage:
603 by mattgiuca
Console now starts up in the user's home directory.
4
#   python-console <port> <magic> [<working-dir>]
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
5
6
import cjson
7
import codeop
8
import md5
9
import os
10
import Queue
11
import signal
12
import socket
13
import sys
14
from threading import Thread
526 by drtomc
python-console: trivial bugfix - missing import.
15
from functools import partial
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
16
432 by drtomc
usrmgt: more work on this. Still some work to go.
17
import common.chat
18
598 by drtomc
console: send output back to the browser progressively.
19
class Interrupt(Exception):
628 by drtomc
console: Add output based interrupt. This allows users to interrupt long
20
    def __init__(self):
21
        Exception.__init__(self, "Interrupted!")
598 by drtomc
console: send output back to the browser progressively.
22
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
23
class ExpiryTimer(object):
24
    def __init__(self, idle):
25
        self.idle = idle
564 by drtomc
python-console: Fix the timeout code.
26
        signal.signal(signal.SIGALRM, partial(self.timeout))
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
27
28
    def ping(self):
29
        signal.alarm(self.idle)
30
31
    def start(self, time):
32
        signal.alarm(time)
33
34
    def stop(self):
35
        self.ping()
36
37
    def timeout(self, signum, frame):
38
        sys.exit(1)
39
        
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
40
class StdinFromWeb(object):
41
    def __init__(self, cmdQ, lineQ):
42
        self.cmdQ = cmdQ
43
        self.lineQ = lineQ
44
45
    def readline(self):
46
        self.cmdQ.put({"input":None})
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
47
        expiry.ping()
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
48
        ln = self.lineQ.get()
49
        if 'chat' in ln:
50
            return ln['chat']
648 by drtomc
console: fix a trivial bug which caused it to loop if you got a syntax error in your first command.
51
        if 'interrupt' in ln:
52
            raise Interrupt()
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
53
598 by drtomc
console: send output back to the browser progressively.
54
class StdoutToWeb(object):
55
    def __init__(self, cmdQ, lineQ):
56
        self.cmdQ = cmdQ
57
        self.lineQ = lineQ
58
        self.remainder = ''
59
60
    def write(self, stuff):
660 by drtomc
console: buffering now tries to buffer enough, but not too much.
61
        self.remainder = self.remainder + stuff
62
63
        # if there's less than 128 bytes, buffer
64
        if len(self.remainder) < 128:
641 by drtomc
console: slightly more aggressive output buffering - wait till we've at least
65
            return
66
660 by drtomc
console: buffering now tries to buffer enough, but not too much.
67
        # if there's lots, then send it in 1/2K blocks
68
        while len(self.remainder) > 512:
69
            blk = self.remainder[0:512]
70
            self.cmdQ.put({"output":blk})
71
            expiry.ping()
72
            ln = self.lineQ.get()
73
            self.remainder = self.remainder[512:]
74
75
        # Finally, split the remainder up into lines, and ship all the
76
        # completed lines off to the server.
77
        lines = self.remainder.split("\n")
598 by drtomc
console: send output back to the browser progressively.
78
        self.remainder = lines[-1]
79
        del lines[-1]
80
81
        if len(lines) > 0:
599 by drtomc
console: improve end of line handling.
82
            lines.append('')
83
            text = "\n".join(lines)
84
            self.cmdQ.put({"output":text})
598 by drtomc
console: send output back to the browser progressively.
85
            expiry.ping()
86
            ln = self.lineQ.get()
87
            if 'interrupt' in ln:
88
                raise Interrupt()
89
90
    def flush(self):
599 by drtomc
console: improve end of line handling.
91
        if len(self.remainder) > 0:
92
            self.cmdQ.put({"output":self.remainder})
93
            expiry.ping()
94
            ln = self.lineQ.get()
95
            self.remainder = ''
96
            if 'interrupt' in ln:
97
                raise Interrupt()
598 by drtomc
console: send output back to the browser progressively.
98
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
99
class PythonRunner(Thread):
100
    def __init__(self, cmdQ, lineQ):
101
        self.cmdQ = cmdQ
102
        self.lineQ = lineQ
648 by drtomc
console: fix a trivial bug which caused it to loop if you got a syntax error in your first command.
103
        self.stdout = StdoutToWeb(self.cmdQ, self.lineQ)
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
104
        Thread.__init__(self)
105
106
    def execCmd(self, cmd):
107
        try:
108
            sys.stdin = StdinFromWeb(self.cmdQ, self.lineQ)
599 by drtomc
console: improve end of line handling.
109
            sys.stdout = self.stdout
110
            sys.stderr = self.stdout
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
111
            res = eval(cmd, self.globs, self.locls)
599 by drtomc
console: improve end of line handling.
112
            self.stdout.flush()
113
            self.cmdQ.put({"okay":res})
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
114
            self.curr_cmd = ''
115
        except Exception, exc:
599 by drtomc
console: improve end of line handling.
116
            self.stdout.flush()
117
            self.cmdQ.put({"exc":str(exc)})
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
118
            self.curr_cmd = ''
119
120
    def run(self):
598 by drtomc
console: send output back to the browser progressively.
121
        self.globs = {}
122
        self.globs['__builtins__'] = globals()['__builtins__']
123
        self.locls = {}
124
        self.curr_cmd = ''
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
125
        compiler = codeop.CommandCompiler()
126
127
        while True:
128
            ln = self.lineQ.get()
129
            if 'chat' in ln:
130
                if self.curr_cmd == '':
131
                    self.curr_cmd = ln['chat']
132
                else:
133
                    self.curr_cmd = self.curr_cmd + '\n' + ln['chat']
134
                try:
135
                    cmd = compiler(self.curr_cmd)
136
                    if cmd is None:
137
                        # The command was incomplete,
138
                        # so send back a None, so the
139
                        # client can print a '...'
140
                        self.cmdQ.put({"more":None})
141
                    else:
142
                        self.execCmd(cmd)
143
                except Exception, exc:
599 by drtomc
console: improve end of line handling.
144
                    self.stdout.flush()
145
                    self.cmdQ.put({"exc":str(exc)})
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
146
                    self.curr_cmd = ''
147
            if 'block' in ln:
148
                # throw away a partial command.
149
                try:
150
                    cmd = compile(ln['block'], "<web session>", 'exec');
151
                    self.execCmd(cmd)
152
                except Exception, exc:
599 by drtomc
console: improve end of line handling.
153
                    self.stdout.flush()
154
                    self.cmdQ.put({"exc":str(exc)})
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
155
                    self.curr_cmd = ''
156
157
def daemonize():
158
    if os.fork():   # launch child and...
159
        os._exit(0) # kill off parent
160
    os.setsid()
161
    if os.fork():   # launch child and...
162
        os._exit(0) # kill off parent again.
163
    os.umask(077)
164
165
# The global 'magic' is the secret that the client and server share
166
# which is used to create and md5 digest to authenticate requests.
167
# It is assigned a real value at startup.
168
magic = ''
169
170
cmdQ = Queue.Queue()
171
lineQ = Queue.Queue()
172
interpThread = PythonRunner(cmdQ, lineQ)
173
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
174
# Default expiry time of 15 minutes
175
expiry = ExpiryTimer(15 * 60)
176
432 by drtomc
usrmgt: more work on this. Still some work to go.
177
def initializer():
178
    interpThread.setDaemon(True)
179
    interpThread.start()
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
180
    expiry.ping()
432 by drtomc
usrmgt: more work on this. Still some work to go.
181
182
def dispatch_msg(msg):
522 by drtomc
Add quite a lot of stuff to get usrmgt happening.
183
    expiry.ping()
432 by drtomc
usrmgt: more work on this. Still some work to go.
184
    lineQ.put({msg['cmd']:msg['text']})
185
    return cmdQ.get()
186
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
187
if __name__ == "__main__":
188
    port = int(sys.argv[1])
189
    magic = sys.argv[2]
603 by mattgiuca
Console now starts up in the user's home directory.
190
    if len(sys.argv) >= 4:
191
        # working_dir
192
        os.chdir(sys.argv[3])
662 by drtomc
console: set the environment variable HOME so matplotlib works in the console.
193
        os.environ['HOME'] = sys.argv[3]
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
194
432 by drtomc
usrmgt: more work on this. Still some work to go.
195
    common.chat.start_server(port, magic, True, dispatch_msg, initializer)