~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
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
34
import errno
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
35
257 by mattgiuca
consoleservice: Rewrote this so it runs the console inside the jail (using the
36
trampoline_path = os.path.join(conf.ivle_install_dir, "bin/trampoline")
37
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
38
console_dir = "/opt/ivle/scripts"                   # Within jail
39
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
40
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
41
def handle(req):
42
    """Handler for the Console Service AJAX backend application."""
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
43
    if len(req.path) > 0 and req.path[-1] == os.sep:
44
        path = req.path[:-1]
45
    else:
46
        path = req.path
47
    # The path determines which "command" we are receiving
48
    if req.path == "start":
49
        handle_start(req)
628 by drtomc
console: Add output based interrupt. This allows users to interrupt long
50
    elif req.path == "interrupt":
51
        handle_chat(req, kind='interrupt')
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
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):
872 by dcoles
Console: Allow console to be started with a set working directory (defaults to
60
    # Changes the state on the server - must be POST
61
    if req.method != "POST":
62
        req.throw_error(req.HTTP_BAD_REQUEST)
63
    
64
    # See if we have been given extra params
65
    fields = req.get_fieldstorage()
66
    try:
67
        startdir = fields.getfirst("startdir").value
68
        working_dir = os.path.join("/home", req.user.login, startdir)
69
    except AttributeError:
70
        working_dir = os.path.join("/home", req.user.login)
257 by mattgiuca
consoleservice: Rewrote this so it runs the console inside the jail (using the
71
72
    # Get the UID of the logged-in user
505 by mattgiuca
dispatch.html, consoleservice, userservice, interpret:
73
    uid = req.user.unixid
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
74
75
    # Set request attributes
76
    req.content_type = "text/plain"
77
    req.write_html_head_foot = False
78
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
79
    # Start the server
872 by dcoles
Console: Allow console to be started with a set working directory (defaults to
80
    jail_path = os.path.join(conf.jail_base, req.user.login)
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
81
    (host, port, magic) = start_console(uid, jail_path, working_dir)
82
83
    # Assemble the key and return it.
84
    key = cjson.encode({"host": host, "port": port, "magic": magic})
85
    req.write(cjson.encode(key.encode("hex")))
86
87
def handle_chat(req, kind = "chat"):
88
    # The request *should* have the following four fields:
89
    # host, port, magic: Host and port where the console server lives,
90
    # and the secret to use to digitally sign the communication with the
91
    # console server.
92
    # text: Fields to pass along to the console server
93
    # It simply acts as a proxy to the console server
94
    if req.method != "POST":
95
        req.throw_error(req.HTTP_BAD_REQUEST)
96
    fields = req.get_fieldstorage()
97
    try:
98
        key = cjson.decode(fields.getfirst("key").value.decode("hex"))
99
        host = key['host']
100
        port = key['port']
101
        magic = key['magic']
102
    except AttributeError:
103
        # Any of the getfirsts returned None
104
        req.throw_error(req.HTTP_BAD_REQUEST)
105
    # If text is None, it was probably just an empty line
106
    try:
870 by wagrant
consoleservice: Console input is UTF-8. Treat it as such!
107
        text = fields.getfirst("text").value.decode('utf-8')
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
108
    except AttributeError:
109
        text = ""
110
111
    msg = {'cmd':kind, 'text':text}
112
    try:
113
        response = chat.chat(host, port, msg, magic, decode = False)
114
    except socket.error, (enumber, estring):
115
        if enumber == errno.ECONNREFUSED:
116
            # Timeout: Restart the session
117
            jail_path = os.path.join(conf.jail_base, req.user.login)
118
            working_dir = os.path.join("/home", req.user.login)   # Within jail
119
            
120
            # Get the UID of the logged-in user
121
            uid = req.user.unixid
122
123
            # Start the console
124
            (host, port, magic) = start_console(uid, jail_path, working_dir)
125
126
            # Make a JSON object to tell the browser to restart its console 
127
            # client
128
            new_key = cjson.encode(
129
                {"host": host, "port": port, "magic": magic})
130
            json_restart = {
131
                "restart": "The IVLE console has timed out due to inactivity",
132
                "key": new_key.encode("hex"),
133
            }
134
            response = cjson.encode(json_restart)
135
        else:
136
            # Some other error - probably serious
137
            raise socket.error, (enumber, estring)
138
139
    req.content_type = "text/plain"
140
    req.write(response)
141
142
def start_console(uid, jail_path, working_dir):
143
    """Starts up a console service for user uid, inside chroot jail jail_path 
144
    with work directory of working_dir
145
    Returns a tupple (host, port, magic)
146
    """
147
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
148
    # TODO: Figure out the host name the console server is running on.
398 by drtomc
apps.py: put debuginfo back to false.
149
    host = socket.gethostname()
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
150
151
    # Create magic
152
    # TODO
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
153
    magic = md5.new(uuid.uuid4().bytes).digest().encode('hex')
154
155
    # Try to find a free port on the server.
156
    # Just try some random ports in the range [3000,8000)
157
    # until we either succeed, or give up. If you think this
158
    # sounds risky, it isn't:
159
    # For N ports (e.g. 5000) with k (e.g. 100) in use, the
160
    # probability of failing to find a free port in t (e.g. 5) tries
161
    # is (k / N) ** t (e.g. 3.2*10e-9).
162
163
    tries = 0
164
    while tries < 5:
165
        port = int(random.uniform(3000, 8000))
166
167
        # Start the console server (port, magic)
168
        # trampoline usage: tramp uid jail_dir working_dir script_path args
169
        # console usage:    python-console port magic
170
        cmd = ' '.join([trampoline_path, str(uid), jail_path,
171
                            console_dir, python_path, console_path,
603 by mattgiuca
Console now starts up in the user's home directory.
172
                            str(port), str(magic), working_dir])
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
173
174
        res = os.system(cmd)
175
176
        if res == 0:
177
            # success
178
            break;
179
180
        tries += 1
181
795 by dcoles
ConsoleService: Small change in error message. Previous message would warn user
182
    # If we can't start the console after 5 attemps (can't find a free port 
183
    # during random probing, syntax errors, segfaults) throw an exception.
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
184
    if tries == 5:
795 by dcoles
ConsoleService: Small change in error message. Previous message would warn user
185
        raise Exception, "unable to start console service!"
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
186
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
187
    return (host, port, magic)
352 by drtomc
Changed console so it no longer uses HTTP, just sends JSON over a normal socket.
188