~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
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
32
from ivle import (util, studpath, chat, console)
33
import ivle.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
36
def handle(req):
37
    """Handler for the Console Service AJAX backend application."""
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
38
    if len(req.path) > 0 and req.path[-1] == os.sep:
39
        path = req.path[:-1]
40
    else:
41
        path = req.path
42
    # The path determines which "command" we are receiving
43
    if req.path == "start":
44
        handle_start(req)
628 by drtomc
console: Add output based interrupt. This allows users to interrupt long
45
    elif req.path == "interrupt":
46
        handle_chat(req, kind='interrupt')
991 by dcoles
Console: Some improvements to the python console code - most notably the
47
    elif req.path == "restart":
1040 by dcoles
Console: Fixed the 'reset' button. Console service should call 'terminate'
48
        handle_chat(req, kind='terminate')
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
49
    elif req.path == "chat":
50
        handle_chat(req)
365 by drtomc
Make the console accept blocks of code.
51
    elif req.path == "block":
52
        handle_chat(req, kind="block")
1017 by dcoles
Console: Refactored a lot of the console stuff into a class to make calls to
53
    elif req.path == "flush":
54
        handle_chat(req, kind="flush")
1016 by dcoles
Console: Added a 'inspect' mode to the console to allow a bit of code to be run
55
    elif req.path == "inspect":
56
        handle_chat(req, kind="inspect")
265 by mattgiuca
apps: "serve" app now requires authentication like all other apps.
57
    else:
58
        req.throw_error(req.HTTP_BAD_REQUEST)
59
60
def handle_start(req):
872 by dcoles
Console: Allow console to be started with a set working directory (defaults to
61
    # Changes the state on the server - must be POST
62
    if req.method != "POST":
63
        req.throw_error(req.HTTP_BAD_REQUEST)
64
    
65
    # See if we have been given extra params
66
    fields = req.get_fieldstorage()
67
    try:
68
        startdir = fields.getfirst("startdir").value
69
        working_dir = os.path.join("/home", req.user.login, startdir)
70
    except AttributeError:
71
        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
72
73
    # Get the UID of the logged-in user
505 by mattgiuca
dispatch.html, consoleservice, userservice, interpret:
74
    uid = req.user.unixid
216 by mattgiuca
Added new app "consoleservice", ajax back end which runs the console daemon.
75
76
    # Set request attributes
77
    req.content_type = "text/plain"
78
    req.write_html_head_foot = False
79
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
80
    # Start the server
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
81
    jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
1017 by dcoles
Console: Refactored a lot of the console stuff into a class to make calls to
82
    cons = console.Console(uid, jail_path, working_dir)
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
83
84
    # Assemble the key and return it.
1017 by dcoles
Console: Refactored a lot of the console stuff into a class to make calls to
85
    key = cjson.encode(
86
        {"host": cons.host, "port": cons.port, "magic": cons.magic})
1060 by wagrant
Make IVLE work fine in Firefox 3.1 (ie. Gecko/XULRunner 1.9.1). Gecko 1.9.1 has
87
    req.write(cjson.encode({"key": key.encode("hex")}))
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
88
89
def handle_chat(req, kind = "chat"):
90
    # The request *should* have the following four fields:
91
    # host, port, magic: Host and port where the console server lives,
92
    # and the secret to use to digitally sign the communication with the
93
    # console server.
94
    # text: Fields to pass along to the console server
95
    # It simply acts as a proxy to the console server
96
    if req.method != "POST":
97
        req.throw_error(req.HTTP_BAD_REQUEST)
1079 by William Grant
Merge setup-refactor branch. This completely breaks existing installations;
98
    jail_path = os.path.join(ivle.conf.jail_base, req.user.login)
977 by dcoles
Console: Work on making console more responsive to signals (rather than just
99
    working_dir = os.path.join("/home", req.user.login)   # Within jail
100
    uid = req.user.unixid
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
101
    fields = req.get_fieldstorage()
102
    try:
103
        key = cjson.decode(fields.getfirst("key").value.decode("hex"))
104
        host = key['host']
105
        port = key['port']
106
        magic = key['magic']
107
    except AttributeError:
108
        # Any of the getfirsts returned None
109
        req.throw_error(req.HTTP_BAD_REQUEST)
110
    # If text is None, it was probably just an empty line
111
    try:
870 by wagrant
consoleservice: Console input is UTF-8. Treat it as such!
112
        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
113
    except AttributeError:
114
        text = ""
115
116
    msg = {'cmd':kind, 'text':text}
117
    try:
118
        response = chat.chat(host, port, msg, magic, decode = False)
977 by dcoles
Console: Work on making console more responsive to signals (rather than just
119
        
120
        # Snoop the response from python-console to check that it's valid
121
        try:
122
            decoded_response = cjson.decode(response)
123
        except cjson.DecodeError:
124
            # Could not decode the reply from the python-console server
1038 by dcoles
ConsoleService: Fix small regression. Ensure that the console is restarted if
125
            decoded_response = {"terminate":
990 by dcoles
Consoleservice: Fixed a slightly confusing error message. This occurs when the
126
                "Communication to console process lost"}
1034 by dcoles
Console: Refactored the code to support supplying of stdin to the console.
127
        if "terminate" in decoded_response:
977 by dcoles
Console: Work on making console more responsive to signals (rather than just
128
            response = restart_console(uid, jail_path, working_dir,
1034 by dcoles
Console: Refactored the code to support supplying of stdin to the console.
129
                decoded_response["terminate"])
977 by dcoles
Console: Work on making console more responsive to signals (rather than just
130
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
131
    except socket.error, (enumber, estring):
132
        if enumber == errno.ECONNREFUSED:
133
            # Timeout: Restart the session
977 by dcoles
Console: Work on making console more responsive to signals (rather than just
134
            response = restart_console(uid, jail_path, working_dir,
135
                "The IVLE console has timed out due to inactivity")
1043 by dcoles
Console: Minor fixes. ConsoleService will now also restart the python-console
136
        elif enumber == errno.ECONNRESET:
137
            # Communication issue: Restart the session
138
            response = restart_console(uid, jail_path, working_dir,
139
                "Connection with the console has been reset")
737 by dcoles
Console: The consoleservice is now able to work out when a console has timed
140
        else:
141
            # Some other error - probably serious
142
            raise socket.error, (enumber, estring)
143
144
    req.content_type = "text/plain"
145
    req.write(response)
146
977 by dcoles
Console: Work on making console more responsive to signals (rather than just
147
def restart_console(uid, jail_path, working_dir, reason):
148
    """Tells the client that it must be issued a new console since the old 
149
    console is no longer availible. The client must accept the new key.
150
    Returns the JSON response to be given to the client.
151
    """
152
    # Start a new console server console
1017 by dcoles
Console: Refactored a lot of the console stuff into a class to make calls to
153
    cons = console.Console(uid, jail_path, working_dir)
977 by dcoles
Console: Work on making console more responsive to signals (rather than just
154
155
    # Make a JSON object to tell the browser to restart its console client
1017 by dcoles
Console: Refactored a lot of the console stuff into a class to make calls to
156
    new_key = cjson.encode(
157
        {"host": cons.host, "port": cons.port, "magic": cons.magic})
977 by dcoles
Console: Work on making console more responsive to signals (rather than just
158
    json_restart = {
159
        "restart": reason,
160
        "key": new_key.encode("hex"),
161
    }
162
    
163
    return cjson.encode(json_restart)