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

« back to all changes in this revision

Viewing changes to ivle/interpret.py

Modified the database so that exercises are now stored in the database, rather
than in flat files.

This also necessitated adding new tables and storm classes for test suites
and test cases.

Note that this commit merely changes the database and adds a script to
upload exercises. The code for actually reading exercises has yet
to be changed.

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.util import IVLEError, IVLEJailError
 
26
import ivle.conf
27
27
 
28
28
import functools
29
29
 
88
88
        self.linebuf = ""
89
89
        self.headers = {}       # Header names : values
90
90
 
91
 
def execute_cgi(interpreter, uid, jail_dir, working_dir, script_path,
92
 
                req, gentle):
 
91
def execute_cgi(interpreter, trampoline, uid, jail_dir, working_dir,
 
92
                script_path, req, gentle):
93
93
    """
94
94
    trampoline: Full path on the local system to the CGI wrapper program
95
95
        being executed.
105
105
    its environment.
106
106
    """
107
107
 
108
 
    trampoline = os.path.join(req.config['paths']['lib'], 'trampoline')
109
 
 
110
108
    # Support no-op trampoline runs.
111
109
    if interpreter is None:
112
110
        interpreter = '/bin/true'
136
134
        del os.environ[k]
137
135
    for (k,v) in req.get_cgi_environ().items():
138
136
        os.environ[k] = v
139
 
    fixup_environ(req, script_path)
 
137
    fixup_environ(req)
140
138
 
141
139
    # usage: tramp uid jail_dir working_dir script_path
142
140
    pid = subprocess.Popen(
143
 
        [trampoline, str(uid), req.config['paths']['jails']['mounts'],
144
 
         req.config['paths']['jails']['src'],
145
 
         req.config['paths']['jails']['template'],
146
 
         jail_dir, working_dir, interpreter, script_path],
 
141
        [trampoline, str(uid), jail_dir, working_dir, interpreter,
 
142
        script_path],
147
143
        stdin=f, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
148
144
        cwd=tramp_dir)
149
145
 
345
341
    <pre>
346
342
""" % (warning, text))
347
343
 
 
344
location_cgi_python = os.path.join(ivle.conf.lib_path, "trampoline")
 
345
 
348
346
# Mapping of interpreter names (as given in conf/app/server.py) to
349
347
# interpreter functions.
350
348
 
351
349
interpreter_objects = {
352
350
    'cgi-python'
353
 
        : functools.partial(execute_cgi, "/usr/bin/python"),
 
351
        : functools.partial(execute_cgi, "/usr/bin/python",
 
352
            location_cgi_python),
354
353
    'noop'
355
 
        : functools.partial(execute_cgi, None),
 
354
        : functools.partial(execute_cgi, None,
 
355
            location_cgi_python),
356
356
    # Should also have:
357
357
    # cgi-generic
358
358
    # python-server-page
359
359
}
360
360
 
361
 
def fixup_environ(req, script_path):
 
361
def fixup_environ(req):
362
362
    """Assuming os.environ has been written with the CGI variables from
363
363
    apache, make a few changes for security and correctness.
364
364
 
384
384
        del env['PATH']
385
385
    except: pass
386
386
 
 
387
    # Remove SCRIPT_FILENAME. Not part of CGI spec (see SCRIPT_NAME).
 
388
 
 
389
    # PATH_INFO is wrong because the script doesn't physically exist.
 
390
    # Apache makes it relative to the "serve" app. It should actually be made
 
391
    # relative to the student's script. intepretservice does that in the jail,
 
392
    # so here we just clear it.
 
393
    env['PATH_INFO'] = ''
 
394
    env['PATH_TRANSLATED'] = ''
 
395
 
387
396
    # CGI specifies that REMOTE_HOST SHOULD be set, and MAY just be set to
388
397
    # REMOTE_ADDR. Since Apache does not appear to set this, set it to
389
398
    # REMOTE_ADDR.
390
399
    if 'REMOTE_HOST' not in env and 'REMOTE_ADDR' in env:
391
400
        env['REMOTE_HOST'] = env['REMOTE_ADDR']
392
401
 
393
 
    env['PATH_INFO'] = ''
394
 
    del env['PATH_TRANSLATED']
395
 
 
396
 
    normuri = os.path.normpath(req.uri)
397
 
    env['SCRIPT_NAME'] = normuri
398
 
 
399
402
    # SCRIPT_NAME is the path to the script WITHOUT PATH_INFO.
400
 
    # We don't care about these if the script is null (ie. noop).
401
 
    # XXX: We check for /home because we don't want to interfere with
402
 
    # CGIRequest, which fileservice still uses.
403
 
    if script_path and script_path.startswith('/home'):
404
 
        normscript = os.path.normpath(script_path)
405
 
 
406
 
        uri_into_jail = studpath.to_home_path(os.path.normpath(req.path))
407
 
 
408
 
        # PATH_INFO is wrong because the script doesn't physically exist.
409
 
        env['PATH_INFO'] = uri_into_jail[len(normscript):]
410
 
        if len(env['PATH_INFO']) > 0:
411
 
            env['SCRIPT_NAME'] = normuri[:-len(env['PATH_INFO'])]
 
403
    script_name = req.uri
 
404
    env['SCRIPT_NAME'] = script_name
412
405
 
413
406
    # SERVER_SOFTWARE is actually not Apache but IVLE, since we are
414
407
    # custom-making the CGI request.
415
 
    env['SERVER_SOFTWARE'] = "IVLE/" + ivle.__version__
 
408
    env['SERVER_SOFTWARE'] = "IVLE/" + str(ivle.conf.ivle_version)
416
409
 
417
410
    # Additional environment variables
418
 
    username = split_path(req.path)[0]
 
411
    username = studpath.url_to_jailpaths(req.path)[0]
419
412
    env['HOME'] = os.path.join('/home', username)
420
413
 
421
414
class ExecutionError(Exception):
422
415
    pass
423
416
 
424
 
def execute_raw(config, user, jail_dir, working_dir, binary, args):
 
417
def execute_raw(user, jail_dir, working_dir, binary, args):
425
418
    '''Execute a binary in a user's jail, returning the raw output.
426
419
 
427
420
    The binary is executed in the given working directory with the given
428
421
    args. A tuple of (stdout, stderr) is returned.
429
422
    '''
430
423
 
431
 
    tramp = os.path.join(config['paths']['lib'], 'trampoline')
432
 
    tramp_dir = os.path.split(tramp)[0]
 
424
    tramp = location_cgi_python
 
425
    tramp_dir = os.path.split(location_cgi_python)[0]
433
426
 
434
427
    # Fire up trampoline. Vroom, vroom.
435
428
    proc = subprocess.Popen(
436
 
        [tramp, str(user.unixid), config['paths']['jails']['mounts'],
437
 
         config['paths']['jails']['src'],
438
 
         config['paths']['jails']['template'],
439
 
         jail_dir, working_dir, binary] + args,
 
429
        [tramp, str(user.unixid), jail_dir, working_dir, binary] + args,
440
430
        stdin=subprocess.PIPE, stdout=subprocess.PIPE,
441
431
        stderr=subprocess.PIPE, cwd=tramp_dir, close_fds=True)
442
 
 
443
 
    (stdout, stderr) = proc.communicate()
444
 
    exitcode = proc.returncode
 
432
    exitcode = proc.wait()
445
433
 
446
434
    if exitcode != 0:
447
435
        raise ExecutionError('subprocess ended with code %d, stderr %s' %
448
436
                             (exitcode, proc.stderr.read()))
449
 
    return (stdout, stderr)
 
437
    return (proc.stdout.read(), proc.stderr.read())