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

« back to all changes in this revision

Viewing changes to www/apps/consoleservice/__init__.py

  • 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:
19
19
# Author: Matt Giuca, Tom Conway
20
20
# Date: 14/1/2008
21
21
 
 
22
import cStringIO
 
23
import httplib
 
24
import md5
22
25
import os
23
26
import pwd
24
 
import httplib
 
27
import random
 
28
import socket
 
29
import sys
25
30
import urllib
 
31
import uuid
26
32
 
27
33
import cjson
28
34
 
45
51
        handle_start(req)
46
52
    elif req.path == "chat":
47
53
        handle_chat(req)
 
54
    #elif req.path == "block":
 
55
    #    handle_block(req)
48
56
    else:
49
57
        req.throw_error(req.HTTP_BAD_REQUEST)
50
58
 
67
75
    # TODO: Figure out the host name the console server is running on.
68
76
    host = req.hostname
69
77
 
70
 
    # Find an available port on the server.
71
 
    # TODO
72
 
    port = 1025
73
 
 
74
78
    # Create magic
75
79
    # TODO
76
 
    magic = "xyzzy"
77
 
 
78
 
    # Start the console server (port, magic)
79
 
    # trampoline usage: tramp uid jail_dir working_dir script_path args
80
 
    # console usage:    python-console port magic
81
 
    # TODO: Cleanup (don't use os.system)
82
 
    # TODO: Pass working_dir as argument, let console cd to it
83
 
    # Use "&" to run as a background process
84
 
    cmd = ' '.join([trampoline_path, str(uid), jail_path, console_dir,
85
 
        python_path, console_path, str(port), str(magic), "&"])
86
 
    #req.write(cmd + '\n')
87
 
    os.system(cmd)
 
80
    magic = md5.new(uuid.uuid4().bytes).digest().encode('hex')
 
81
 
 
82
    # Try to find a free port on the server.
 
83
    # Just try some random ports in the range [3000,8000)
 
84
    # until we either succeed, or give up. If you think this
 
85
    # sounds risky, it isn't:
 
86
    # For N ports (e.g. 5000) with k (e.g. 100) in use, the
 
87
    # probability of failing to find a free port in t (e.g. 5) tries
 
88
    # is (k / N) ** t (e.g. 3.2*10e-9).
 
89
 
 
90
    tries = 0
 
91
    while tries < 5:
 
92
        port = int(random.uniform(3000, 8000))
 
93
 
 
94
        # Start the console server (port, magic)
 
95
        # trampoline usage: tramp uid jail_dir working_dir script_path args
 
96
        # console usage:    python-console port magic
 
97
        # TODO: Pass working_dir as argument, let console cd to it
 
98
        cmd = ' '.join([trampoline_path, str(uid), jail_path,
 
99
                            console_dir, python_path, console_path,
 
100
                            str(port), str(magic)])
 
101
 
 
102
        print >> sys.stderr, cmd
 
103
        res = os.system(cmd)
 
104
        print >> sys.stderr, res
 
105
 
 
106
        if res == 0:
 
107
            # success
 
108
            break;
 
109
 
 
110
        tries += 1
 
111
 
 
112
    if tries == 5:
 
113
        raise Exception, "unable to find a free port!"
88
114
 
89
115
    # Return port, magic
90
116
    req.write(cjson.encode({"host": host, "port": port, "magic": magic}))
99
125
    fields = req.get_fieldstorage()
100
126
    try:
101
127
        host = fields.getfirst("host").value
102
 
        port = fields.getfirst("port").value
 
128
        port = int(fields.getfirst("port").value)
103
129
        digest = fields.getfirst("digest").value
104
130
    except AttributeError:
105
131
        # Any of the getfirsts returned None
110
136
    except AttributeError:
111
137
        text = ""
112
138
 
113
 
    # Open an HTTP connection
114
 
    url = ("http://" + urllib.quote(host) + ":" + urllib.quote(port)
115
 
            + "/chat");
116
 
    body = ("digest=" + urllib.quote(digest)
117
 
            + "&text=" + urllib.quote(text))
118
 
    headers = {"Content-Type": "application/x-www-form-urlencoded"}
119
 
    try:
120
 
        conn = httplib.HTTPConnection(host, port)
121
 
    except:
122
 
        req.throw_error(req.HTTP_BAD_REQUEST)
123
 
    conn.request("POST", url, body, headers)
124
 
 
125
 
    response = conn.getresponse()
 
139
    msg = {'cmd':'chat', 'text':text, 'digest':digest}
 
140
 
 
141
    sok = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
142
    sok.connect((host, port))
 
143
    sok.send(cjson.encode(msg))
 
144
 
 
145
    buf = cStringIO.StringIO()
 
146
    blk = sok.recv(1024)
 
147
    while blk:
 
148
        buf.write(blk)
 
149
        try:
 
150
            blk = conn.recv(1024, socket.MSG_DONTWAIT)
 
151
        except:
 
152
            # Exception thrown if it WOULD block (but we
 
153
            # told it not to wait) - ie. we are done
 
154
            blk = None
 
155
    inp = buf.getvalue()
126
156
    
127
 
    req.status = response.status
128
 
    # NOTE: Ignoring arbitrary headers returned by the server
129
 
    # Probably not necessary to proxy them
130
 
    req.content_type = response.getheader("Content-Type", "text/plain")
131
 
    req.write(response.read())
132
 
    conn.close()
 
157
    sok.close()
 
158
 
 
159
    req.content_type = "text/plain"
 
160
    req.write(inp)
 
161