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

« back to all changes in this revision

Viewing changes to ivle/interpret.py

  • Committer: me at id
  • Date: 2009-01-15 05:53:45 UTC
  • mto: This revision was merged to the branch mainline in revision 1090.
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:branches%2Fstorm:1161
bin/ivle-showenrolment: Switch to ivle.database.User.enrolments from
    ivle.db.get_enrolment, removing the dependency on ivle.db.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 
22
22
# Runs a student script in a safe execution environment.
23
23
 
24
 
import ivle
25
24
from ivle import studpath
26
 
from ivle.util import IVLEError, IVLEJailError, split_path
 
25
from ivle import db
 
26
from ivle.util import IVLEError, IVLEJailError
 
27
import ivle.conf
27
28
 
28
29
import functools
29
30
 
38
39
 
39
40
CGI_BLOCK_SIZE = 65535
40
41
 
 
42
uids = {}
 
43
 
 
44
def get_uid(login):
 
45
    """Get the unix uid corresponding to the given login name.
 
46
       If it is not in the dictionary of uids, then consult the
 
47
       database and retrieve an update of the user table."""
 
48
    global uids
 
49
    if login in uids:
 
50
        return uids[login]
 
51
 
 
52
    conn = db.DB()
 
53
    res = conn.get_all('login', ['login', 'unixid'])
 
54
    def repack(flds):
 
55
        return (flds['login'], flds['unixid'])
 
56
    uids = dict(map(repack,res))
 
57
 
 
58
    return uids[login]
 
59
 
41
60
def interpret_file(req, owner, jail_dir, filename, interpreter, gentle=True):
42
61
    """Serves a file by interpreting it using one of IVLE's builtin
43
62
    interpreters. All interpreters are intended to run in the user's jail. The
45
64
    to the individual interpreters to create the jail.
46
65
 
47
66
    req: An IVLE request object.
48
 
    owner: The user who owns the file being served.
 
67
    owner: Username of the user who owns the file being served.
49
68
    jail_dir: Absolute path to the user's jail.
50
69
    filename: Absolute filename within the user's jail.
51
70
    interpreter: A function object to call.
60
79
        filename_abs = os.path.join(os.sep, filename)
61
80
        filename_rel = filename
62
81
 
 
82
    # Get the UID of the owner of the file
63
83
    # (Note: files are executed by their owners, not the logged in user.
64
84
    # This ensures users are responsible for their own programs and also
65
85
    # allows them to be executed by the public).
 
86
    uid = get_uid(owner)
66
87
 
67
88
    # Split up req.path again, this time with respect to the jail
68
89
    (working_dir, _) = os.path.split(filename_abs)
73
94
    # (Note that paths "relative" to the jail actually begin with a '/' as
74
95
    # they are absolute in the jailspace)
75
96
 
76
 
    return interpreter(owner.unixid, jail_dir, working_dir, filename_abs, req,
 
97
    return interpreter(uid, jail_dir, working_dir, filename_abs, req,
77
98
                       gentle)
78
99
 
79
100
class CGIFlags:
88
109
        self.linebuf = ""
89
110
        self.headers = {}       # Header names : values
90
111
 
91
 
def execute_cgi(interpreter, uid, jail_dir, working_dir, script_path,
92
 
                req, gentle):
 
112
def execute_cgi(interpreter, trampoline, uid, jail_dir, working_dir,
 
113
                script_path, req, gentle):
93
114
    """
94
115
    trampoline: Full path on the local system to the CGI wrapper program
95
116
        being executed.
105
126
    its environment.
106
127
    """
107
128
 
108
 
    trampoline = os.path.join(req.config['paths']['lib'], 'trampoline')
109
 
 
110
129
    # Support no-op trampoline runs.
111
130
    if interpreter is None:
112
131
        interpreter = '/bin/true'
136
155
        del os.environ[k]
137
156
    for (k,v) in req.get_cgi_environ().items():
138
157
        os.environ[k] = v
139
 
    fixup_environ(req, script_path)
 
158
    fixup_environ(req)
140
159
 
141
160
    # usage: tramp uid jail_dir working_dir script_path
142
 
    cmd_line = [trampoline, str(uid), req.config['paths']['jails']['mounts'],
143
 
         req.config['paths']['jails']['src'],
144
 
         req.config['paths']['jails']['template'],
145
 
         jail_dir, working_dir, interpreter, script_path]
146
 
    # Popen doesn't like unicode strings. It hateses them.
147
 
    cmd_line = [(s.encode('utf-8') if isinstance(s, unicode) else s)
148
 
                for s in cmd_line]
149
 
    pid = subprocess.Popen(cmd_line,
 
161
    pid = subprocess.Popen(
 
162
        [trampoline, str(uid), jail_dir, working_dir, interpreter,
 
163
        script_path],
150
164
        stdin=f, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
151
165
        cwd=tramp_dir)
152
166
 
348
362
    <pre>
349
363
""" % (warning, text))
350
364
 
 
365
location_cgi_python = os.path.join(ivle.conf.lib_path, "trampoline")
 
366
 
351
367
# Mapping of interpreter names (as given in conf/app/server.py) to
352
368
# interpreter functions.
353
369
 
354
370
interpreter_objects = {
355
371
    'cgi-python'
356
 
        : functools.partial(execute_cgi, "/usr/bin/python"),
 
372
        : functools.partial(execute_cgi, "/usr/bin/python",
 
373
            location_cgi_python),
357
374
    'noop'
358
 
        : functools.partial(execute_cgi, None),
 
375
        : functools.partial(execute_cgi, None,
 
376
            location_cgi_python),
359
377
    # Should also have:
360
378
    # cgi-generic
361
379
    # python-server-page
362
380
}
363
381
 
364
 
def fixup_environ(req, script_path):
 
382
def fixup_environ(req):
365
383
    """Assuming os.environ has been written with the CGI variables from
366
384
    apache, make a few changes for security and correctness.
367
385
 
387
405
        del env['PATH']
388
406
    except: pass
389
407
 
 
408
    # Remove SCRIPT_FILENAME. Not part of CGI spec (see SCRIPT_NAME).
 
409
 
 
410
    # PATH_INFO is wrong because the script doesn't physically exist.
 
411
    # Apache makes it relative to the "serve" app. It should actually be made
 
412
    # relative to the student's script. intepretservice does that in the jail,
 
413
    # so here we just clear it.
 
414
    env['PATH_INFO'] = ''
 
415
    env['PATH_TRANSLATED'] = ''
 
416
 
390
417
    # CGI specifies that REMOTE_HOST SHOULD be set, and MAY just be set to
391
418
    # REMOTE_ADDR. Since Apache does not appear to set this, set it to
392
419
    # REMOTE_ADDR.
393
420
    if 'REMOTE_HOST' not in env and 'REMOTE_ADDR' in env:
394
421
        env['REMOTE_HOST'] = env['REMOTE_ADDR']
395
422
 
396
 
    env['PATH_INFO'] = ''
397
 
    del env['PATH_TRANSLATED']
398
 
 
399
 
    normuri = os.path.normpath(req.uri)
400
 
    env['SCRIPT_NAME'] = normuri
401
 
 
402
423
    # SCRIPT_NAME is the path to the script WITHOUT PATH_INFO.
403
 
    # We don't care about these if the script is null (ie. noop).
404
 
    # XXX: We check for /home because we don't want to interfere with
405
 
    # CGIRequest, which fileservice still uses.
406
 
    if script_path and script_path.startswith('/home'):
407
 
        normscript = os.path.normpath(script_path)
408
 
 
409
 
        uri_into_jail = studpath.to_home_path(os.path.normpath(req.path))
410
 
 
411
 
        # PATH_INFO is wrong because the script doesn't physically exist.
412
 
        env['PATH_INFO'] = uri_into_jail[len(normscript):]
413
 
        if len(env['PATH_INFO']) > 0:
414
 
            env['SCRIPT_NAME'] = normuri[:-len(env['PATH_INFO'])]
 
424
    script_name = req.uri
 
425
    env['SCRIPT_NAME'] = script_name
415
426
 
416
427
    # SERVER_SOFTWARE is actually not Apache but IVLE, since we are
417
428
    # custom-making the CGI request.
418
 
    env['SERVER_SOFTWARE'] = "IVLE/" + ivle.__version__
 
429
    env['SERVER_SOFTWARE'] = "IVLE/" + str(ivle.conf.ivle_version)
419
430
 
420
431
    # Additional environment variables
421
 
    username = split_path(req.path)[0]
 
432
    username = studpath.url_to_jailpaths(req.path)[0]
422
433
    env['HOME'] = os.path.join('/home', username)
423
 
 
424
 
class ExecutionError(Exception):
425
 
    pass
426
 
 
427
 
def execute_raw(config, user, jail_dir, working_dir, binary, args):
428
 
    '''Execute a binary in a user's jail, returning the raw output.
429
 
 
430
 
    The binary is executed in the given working directory with the given
431
 
    args. A tuple of (stdout, stderr) is returned.
432
 
    '''
433
 
 
434
 
    tramp = os.path.join(config['paths']['lib'], 'trampoline')
435
 
    tramp_dir = os.path.split(tramp)[0]
436
 
 
437
 
    # Fire up trampoline. Vroom, vroom.
438
 
    cmd_line = [tramp, str(user.unixid), config['paths']['jails']['mounts'],
439
 
         config['paths']['jails']['src'],
440
 
         config['paths']['jails']['template'],
441
 
         jail_dir, working_dir, binary] + args
442
 
    # Popen doesn't like unicode strings. It hateses them.
443
 
    cmd_line = [(s.encode('utf-8') if isinstance(s, unicode) else s)
444
 
                for s in cmd_line]
445
 
    proc = subprocess.Popen(cmd_line,
446
 
        stdin=subprocess.PIPE, stdout=subprocess.PIPE,
447
 
        stderr=subprocess.PIPE, cwd=tramp_dir, close_fds=True)
448
 
 
449
 
    (stdout, stderr) = proc.communicate()
450
 
    exitcode = proc.returncode
451
 
 
452
 
    if exitcode != 0:
453
 
        raise ExecutionError('subprocess ended with code %d, stderr %s' %
454
 
                             (exitcode, proc.stderr.read()))
455
 
    return (stdout, stderr)