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

216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
1
# IVLE
2
# Copyright (C) 2007-2008 The University of Melbourne
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18
# App: consoleservice
19
# Author: Matt Giuca, Tom Conway
20
# Date: 14/1/2008
21
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
22
import cStringIO
23
import httplib
24
import md5
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
25
import os
257 by mattgiuca
consoleservice: Rewrote this so it runs the console inside the jail (using the
26
import pwd
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
27
import random
28
import socket
29
import sys
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
30
import urllib
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
31
import uuid
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
32
33
import cjson
34
35
from common import (util, studpath)
36
import conf
37
257 by mattgiuca
consoleservice: Rewrote this so it runs the console inside the jail (using the
38
trampoline_path = os.path.join(conf.ivle_install_dir, "bin/trampoline")
39
python_path = "/usr/bin/python"                     # Within jail
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
40
console_dir = "/opt/ivle/scripts"                   # Within jail
41
console_path = "/opt/ivle/scripts/python-console"   # Within jail
257 by mattgiuca
consoleservice: Rewrote this so it runs the console inside the jail (using the
42
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
43
def handle(req):
44
    """Handler for the Console Service AJAX backend application."""
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
45
    if len(req.path) > 0 and req.path[-1] == os.sep:
46
        path = req.path[:-1]
47
    else:
48
        path = req.path
49
    # The path determines which "command" we are receiving
50
    if req.path == "start":
51
        handle_start(req)
52
    elif req.path == "chat":
53
        handle_chat(req)
365 by drtomc
Make the console accept blocks of code.
54
    elif req.path == "block":
55
        handle_chat(req, kind="block")
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
56
    else:
57
        req.throw_error(req.HTTP_BAD_REQUEST)
58
59
def handle_start(req):
257 by mattgiuca
consoleservice: Rewrote this so it runs the console inside the jail (using the
60
    jail_path = os.path.join(conf.jail_base, req.username)
61
    working_dir = os.path.join("/home", req.username)   # Within jail
62
63
    # Get the UID of the logged-in user
64
    try:
65
        (_,_,uid,_,_,_,_) = pwd.getpwnam(req.username)
66
    except KeyError:
67
        # The user does not exist. This should have already failed the
68
        # previous test.
69
        req.throw_error(req.HTTP_INTERNAL_SERVER_ERROR)
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
70
71
    # Set request attributes
72
    req.content_type = "text/plain"
73
    req.write_html_head_foot = False
74
75
    # TODO: Figure out the host name the console server is running on.
398 by drtomc
apps.py: put debuginfo back to false.
76
    host = socket.gethostname()
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
77
78
    # Create magic
79
    # TODO
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
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!"
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
114
115
    # Return port, magic
116
    req.write(cjson.encode({"host": host, "port": port, "magic": magic}))
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
117
365 by drtomc
Make the console accept blocks of code.
118
def handle_chat(req, kind = "chat"):
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
119
    # The request *should* have the following four fields:
120
    # host, port: Host and port where the console server apparently lives
121
    # digest, text: Fields to pass along to the console server
122
    # It simply acts as a proxy to the console server
272 by mattgiuca
consoleservice: Error if not POST.
123
    if req.method != "POST":
124
        req.throw_error(req.HTTP_BAD_REQUEST)
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
125
    fields = req.get_fieldstorage()
126
    try:
127
        host = fields.getfirst("host").value
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
128
        port = int(fields.getfirst("port").value)
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
129
        digest = fields.getfirst("digest").value
276 by mattgiuca
Console now runs inside IVLE (without requiring an IFRAME). The separate
130
    except AttributeError:
131
        # Any of the getfirsts returned None
132
        req.throw_error(req.HTTP_BAD_REQUEST)
133
    # If text is None, it was probably just an empty line
134
    try:
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
135
        text = fields.getfirst("text").value
136
    except AttributeError:
276 by mattgiuca
Console now runs inside IVLE (without requiring an IFRAME). The separate
137
        text = ""
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
138
365 by drtomc
Make the console accept blocks of code.
139
    msg = {'cmd':kind, 'text':text, 'digest':digest}
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
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()
276 by mattgiuca
Console now runs inside IVLE (without requiring an IFRAME). The separate
156
    
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
157
    sok.close()
158
159
    req.content_type = "text/plain"
160
    req.write(inp)
161