~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 md5
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
24
import os
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
25
import random
26
import socket
27
import sys
28
import uuid
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
29
30
import cjson
31
432 by drtomc
usrmgt: more work on this. Still some work to go.
32
from common import (util, studpath, chat)
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
33
import conf
34
257 by mattgiuca
consoleservice: Rewrote this so it runs the console inside the jail (using the
35
trampoline_path = os.path.join(conf.ivle_install_dir, "bin/trampoline")
36
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
37
console_dir = "/opt/ivle/scripts"                   # Within jail
38
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
39
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
40
def handle(req):
41
    """Handler for the Console Service AJAX backend application."""
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
42
    if len(req.path) > 0 and req.path[-1] == os.sep:
43
        path = req.path[:-1]
44
    else:
45
        path = req.path
46
    # The path determines which "command" we are receiving
47
    if req.path == "start":
48
        handle_start(req)
628 by drtomc
console: Add output based interrupt. This allows users to interrupt long
49
    elif req.path == "interrupt":
50
        handle_chat(req, kind='interrupt')
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
51
    elif req.path == "chat":
52
        handle_chat(req)
365 by drtomc
Make the console accept blocks of code.
53
    elif req.path == "block":
54
        handle_chat(req, kind="block")
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
55
    else:
56
        req.throw_error(req.HTTP_BAD_REQUEST)
57
58
def handle_start(req):
506 by mattgiuca
dispatch.__init__, dispatch.request, cgirequest:
59
    jail_path = os.path.join(conf.jail_base, req.user.login)
60
    working_dir = os.path.join("/home", req.user.login)   # Within jail
257 by mattgiuca
consoleservice: Rewrote this so it runs the console inside the jail (using the
61
62
    # Get the UID of the logged-in user
505 by mattgiuca
dispatch.html, consoleservice, userservice, interpret:
63
    uid = req.user.unixid
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
64
65
    # Set request attributes
66
    req.content_type = "text/plain"
67
    req.write_html_head_foot = False
68
69
    # TODO: Figure out the host name the console server is running on.
398 by drtomc
apps.py: put debuginfo back to false.
70
    host = socket.gethostname()
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
71
72
    # Create magic
73
    # TODO
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
74
    magic = md5.new(uuid.uuid4().bytes).digest().encode('hex')
75
76
    # Try to find a free port on the server.
77
    # Just try some random ports in the range [3000,8000)
78
    # until we either succeed, or give up. If you think this
79
    # sounds risky, it isn't:
80
    # For N ports (e.g. 5000) with k (e.g. 100) in use, the
81
    # probability of failing to find a free port in t (e.g. 5) tries
82
    # is (k / N) ** t (e.g. 3.2*10e-9).
83
84
    tries = 0
85
    while tries < 5:
86
        port = int(random.uniform(3000, 8000))
87
88
        # Start the console server (port, magic)
89
        # trampoline usage: tramp uid jail_dir working_dir script_path args
90
        # console usage:    python-console port magic
91
        cmd = ' '.join([trampoline_path, str(uid), jail_path,
92
                            console_dir, python_path, console_path,
603 by mattgiuca
Console now starts up in the user's home directory.
93
                            str(port), str(magic), working_dir])
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
94
95
        res = os.system(cmd)
96
97
        if res == 0:
98
            # success
99
            break;
100
101
        tries += 1
102
103
    if tries == 5:
104
        raise Exception, "unable to find a free port!"
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
105
432 by drtomc
usrmgt: more work on this. Still some work to go.
106
    # Assemble the key and return it.
107
    key = cjson.encode({"host": host, "port": port, "magic": magic})
108
    req.write(cjson.encode(key.encode("hex")))
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
109
365 by drtomc
Make the console accept blocks of code.
110
def handle_chat(req, kind = "chat"):
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
111
    # The request *should* have the following four fields:
432 by drtomc
usrmgt: more work on this. Still some work to go.
112
    # host, port, magic: Host and port where the console server lives,
113
    # and the secret to use to digitally sign the communication with the
114
    # console server.
115
    # text: Fields to pass along to the console server
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
116
    # It simply acts as a proxy to the console server
272 by mattgiuca
consoleservice: Error if not POST.
117
    if req.method != "POST":
118
        req.throw_error(req.HTTP_BAD_REQUEST)
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
119
    fields = req.get_fieldstorage()
120
    try:
432 by drtomc
usrmgt: more work on this. Still some work to go.
121
        key = cjson.decode(fields.getfirst("key").value.decode("hex"))
122
        host = key['host']
123
        port = key['port']
124
        magic = key['magic']
276 by mattgiuca
Console now runs inside IVLE (without requiring an IFRAME). The separate
125
    except AttributeError:
126
        # Any of the getfirsts returned None
127
        req.throw_error(req.HTTP_BAD_REQUEST)
128
    # If text is None, it was probably just an empty line
129
    try:
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
130
        text = fields.getfirst("text").value
131
    except AttributeError:
276 by mattgiuca
Console now runs inside IVLE (without requiring an IFRAME). The separate
132
        text = ""
271 by mattgiuca
consoleservice: Wrote "chat" mode; works as a proxy to the real console
133
432 by drtomc
usrmgt: more work on this. Still some work to go.
134
    msg = {'cmd':kind, 'text':text}
135
    response = chat.chat(host, port, msg, magic, decode = False)
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
136
    req.content_type = "text/plain"
432 by drtomc
usrmgt: more work on this. Still some work to go.
137
    req.write(response)
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
138