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

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: mattgiuca
  • Date: 2007-12-21 06:15:19 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:124
dispatch/request: Added new fields: method and username.
                  Added new methods: get_session and get_fieldstorage.
dispatch/html: Writes out username of logged in user, and a "logout" link.
dispatch/__init__: Handles special logout app.
                   Performs authentication, and stores username in req.
                   No longer lets apache print HTML errors. Just returns OK.
dispatch: Added login module.

Added new package: auth (handles authentication and authorization).
    Currently just does hard-coded dummy authentication.

debuginfo: Added new info for new fields of request, and also prints out
        fieldstorage and session information.

conf/apps.py: debuginfo does not require auth (careful!)
              Commented out debuginfo (no longer displays warning)

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
# cutting a distribution, and the listfile it generates should be included in
32
32
# the distribution, avoiding the administrator having to run it.
33
33
 
34
 
# setup.py config [args]
 
34
# setup.py conf [args]
35
35
# Configures IVLE with machine-specific details, most notably, various paths.
36
36
# Either prompts the administrator for these details or accepts them as
37
37
# command-line args.
38
 
# Creates lib/conf/conf.py and trampoline/conf.h.
 
38
# Creates www/conf/conf.py and trampoline/conf.h.
39
39
 
40
40
# setup.py build
41
41
# Compiles all files and sets up a jail template in the source directory.
68
68
import compileall
69
69
import getopt
70
70
 
71
 
# Import modules from the website is tricky since they're in the www
72
 
# directory.
73
 
sys.path.append(os.path.join(os.getcwd(), 'lib'))
74
 
import conf
75
 
import common.makeuser
76
 
 
77
 
# Determine which Python version (2.4 or 2.5, for example) we are running,
78
 
# and use that as the filename to the Python directory.
79
 
# Just get the first 3 characters of sys.version.
80
 
PYTHON_VERSION = sys.version[0:3]
81
 
 
82
 
# Operating system files to copy over into the jail.
83
 
# These will be copied from the given place on the OS file system into the
84
 
# same place within the jail.
85
 
JAIL_FILES = [
86
 
    '/lib/ld-linux.so.2',
87
 
    '/lib/tls/i686/cmov/libc.so.6',
88
 
    '/lib/tls/i686/cmov/libdl.so.2',
89
 
    '/lib/tls/i686/cmov/libm.so.6',
90
 
    '/lib/tls/i686/cmov/libpthread.so.0',
91
 
    '/lib/tls/i686/cmov/libutil.so.1',
92
 
    '/etc/ld.so.conf',
93
 
    '/etc/ld.so.cache',
94
 
    # These 2 files do not exist in Ubuntu
95
 
    #'/etc/ld.so.preload',
96
 
    #'/etc/ld.so.nohwcap',
97
 
    # UNIX commands
98
 
    '/usr/bin/strace',
99
 
    '/bin/ls',
100
 
    '/bin/echo',
101
 
    # Needed by python
102
 
    '/usr/bin/python%s' % PYTHON_VERSION,
103
 
    # Needed by fileservice
104
 
    '/lib/libcom_err.so.2',
105
 
    '/lib/libcrypt.so.1',
106
 
    '/lib/libkeyutils.so.1',
107
 
    '/lib/libresolv.so.2',
108
 
    '/lib/librt.so.1',
109
 
    '/lib/libuuid.so.1',
110
 
    '/usr/lib/libapr-1.so.0',
111
 
    '/usr/lib/libaprutil-1.so.0',
112
 
    '/usr/lib/libdb-4.4.so',
113
 
    '/usr/lib/libexpat.so.1',
114
 
    '/usr/lib/libgcrypt.so.11',
115
 
    '/usr/lib/libgnutls.so.13',
116
 
    '/usr/lib/libgpg-error.so.0',
117
 
    '/usr/lib/libgssapi_krb5.so.2',
118
 
    '/usr/lib/libk5crypto.so.3',
119
 
    '/usr/lib/libkrb5.so.3',
120
 
    '/usr/lib/libkrb5support.so.0',
121
 
    '/usr/lib/liblber.so.2',
122
 
    '/usr/lib/libldap_r.so.2',
123
 
    '/usr/lib/libneon.so.26',
124
 
    '/usr/lib/libpq.so.5',
125
 
    '/usr/lib/libsasl2.so.2',
126
 
    '/usr/lib/libsqlite3.so.0',
127
 
    '/usr/lib/libsvn_client-1.so.1',
128
 
    '/usr/lib/libsvn_delta-1.so.1',
129
 
    '/usr/lib/libsvn_diff-1.so.1',
130
 
    '/usr/lib/libsvn_fs-1.so.1',
131
 
    '/usr/lib/libsvn_fs_base-1.so.1',
132
 
    '/usr/lib/libsvn_fs_fs-1.so.1',
133
 
    '/usr/lib/libsvn_ra-1.so.1',
134
 
    '/usr/lib/libsvn_ra_dav-1.so.1',
135
 
    '/usr/lib/libsvn_ra_local-1.so.1',
136
 
    '/usr/lib/libsvn_ra_svn-1.so.1',
137
 
    '/usr/lib/libsvn_repos-1.so.1',
138
 
    '/usr/lib/libsvn_subr-1.so.1',
139
 
    '/usr/lib/libsvn_wc-1.so.1',
140
 
    '/usr/lib/libtasn1.so.3',
141
 
    '/usr/lib/libxml2.so.2',
142
 
    # Needed by matplotlib
143
 
    '/usr/lib/i686/cmov/libssl.so.0.9.8',
144
 
    '/usr/lib/i686/cmov/libcrypto.so.0.9.8',
145
 
    '/lib/tls/i686/cmov/libnsl.so.1',
146
 
    '/usr/lib/libz.so.1',
147
 
    '/usr/lib/atlas/liblapack.so.3',
148
 
    '/usr/lib/atlas/libblas.so.3',
149
 
    '/usr/lib/libg2c.so.0',
150
 
    '/usr/lib/libstdc++.so.6',
151
 
    '/usr/lib/libfreetype.so.6',
152
 
    '/usr/lib/libpng12.so.0',
153
 
    '/usr/lib/libBLT.2.4.so.8.4',
154
 
    '/usr/lib/libtk8.4.so.0',
155
 
    '/usr/lib/libtcl8.4.so.0',
156
 
    '/usr/lib/tcl8.4/init.tcl',
157
 
    '/usr/lib/libX11.so.6',
158
 
    '/usr/lib/libXau.so.6',
159
 
    '/usr/lib/libXdmcp.so.6',
160
 
    '/lib/libgcc_s.so.1',
161
 
    '/etc/matplotlibrc',
162
 
]
163
 
# Symlinks to make within the jail. Src mapped to dst.
164
 
JAIL_LINKS = {
165
 
    'python%s' % PYTHON_VERSION: 'jail/usr/bin/python',
166
 
}
167
 
# Trees to copy. Src mapped to dst (these will be passed to action_copytree).
168
 
JAIL_COPYTREES = {
169
 
    '/usr/lib/python%s' % PYTHON_VERSION:
170
 
        'jail/usr/lib/python%s' % PYTHON_VERSION,
171
 
    '/usr/share/matplotlib': 'jail/usr/share/matplotlib',
172
 
    '/etc/ld.so.conf.d': 'jail/etc/ld.so.conf.d',
173
 
}
174
 
 
175
 
class ConfigOption:
176
 
    """A configuration option; one of the things written to conf.py."""
177
 
    def __init__(self, option_name, default, prompt, comment):
178
 
        """Creates a configuration option.
179
 
        option_name: Name of the variable in conf.py. Also name of the
180
 
            command-line argument to setup.py conf.
181
 
        default: Default value for this variable.
182
 
        prompt: (Short) string presented during the interactive prompt in
183
 
            setup.py conf.
184
 
        comment: (Long) comment string stored in conf.py. Each line of this
185
 
            string should begin with a '#'.
186
 
        """
187
 
        self.option_name = option_name
188
 
        self.default = default
189
 
        self.prompt = prompt
190
 
        self.comment = comment
191
 
 
192
 
# Configuration options, defaults and descriptions
193
 
config_options = []
194
 
config_options.append(ConfigOption("root_dir", "/ivle",
195
 
    """Root directory where IVLE is located (in URL space):""",
196
 
    """
197
 
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
198
 
# with this).
199
 
# eg. "/" or "/ivle"."""))
200
 
config_options.append(ConfigOption("ivle_install_dir", "/opt/ivle",
201
 
    'Root directory where IVLE will be installed (on the local file '
202
 
    'system):',
203
 
    """
204
 
# In the local file system, where IVLE is actually installed.
205
 
# This directory should contain the "www" and "bin" directories."""))
206
 
config_options.append(ConfigOption("jail_base", "/home/informatics/jails",
207
 
    """Root directory where the jails (containing user files) are stored
208
 
(on the local file system):""",
209
 
    """
210
 
# In the local file system, where are the student/user file spaces located.
211
 
# The user jails are expected to be located immediately in subdirectories of
212
 
# this location."""))
213
 
config_options.append(ConfigOption("subjects_base",
214
 
    "/home/informatics/subjects",
215
 
    """Root directory where the subject directories (containing worksheets
216
 
and other per-subject files) are stored (on the local file system):""",
217
 
    """
218
 
# In the local file system, where are the per-subject file spaces located.
219
 
# The individual subject directories are expected to be located immediately
220
 
# in subdirectories of this location."""))
221
 
config_options.append(ConfigOption("problems_base",
222
 
    "/home/informatics/problems",
223
 
    """Root directory where the problem directories (containing
224
 
subject-independent problem sheets) are stored (on the local file
225
 
system):""",
226
 
    """
227
 
# In the local file system, where are the subject-independent problem sheet
228
 
# file spaces located."""))
229
 
config_options.append(ConfigOption("public_host", "public.localhost",
230
 
    """Hostname which will cause the server to go into "public mode",
231
 
providing login-free access to student's published work:""",
232
 
    """
233
 
# The server goes into "public mode" if the browser sends a request with this
234
 
# host. This is for security reasons - we only serve public student files on a
235
 
# separate domain to the main IVLE site.
236
 
# Public mode does not use cookies, and serves only public content.
237
 
# Private mode (normal mode) requires login, and only serves files relevant to
238
 
# the logged-in user."""))
239
 
config_options.append(ConfigOption("allowed_uids", "33",
240
 
    """UID of the web server process which will run IVLE.
241
 
Only this user may execute the trampoline. May specify multiple users as
242
 
a comma-separated list.
243
 
    (eg. "1002,78")""",
244
 
    """
245
 
# The User-ID of the web server process which will run IVLE, and any other
246
 
# users who are allowed to run the trampoline. This is stores as a string of
247
 
# comma-separated integers, simply because it is not used within Python, only
248
 
# used by the setup program to write to conf.h (see setup.py config)."""))
249
 
config_options.append(ConfigOption("db_host", "localhost",
250
 
    """PostgreSQL Database config
251
 
==========================
252
 
Hostname of the DB server:""",
253
 
    """
254
 
### PostgreSQL Database config ###
255
 
# Database server hostname"""))
256
 
config_options.append(ConfigOption("db_port", "5432",
257
 
    """Port of the DB server:""",
258
 
    """
259
 
# Database server port"""))
260
 
config_options.append(ConfigOption("db_dbname", "ivle",
261
 
    """Database name:""",
262
 
    """
263
 
# Database name"""))
264
 
config_options.append(ConfigOption("db_user", "postgres",
265
 
    """Username for DB server login:""",
266
 
    """
267
 
# Database username"""))
268
 
config_options.append(ConfigOption("db_password", "",
269
 
    """Password for DB server login:
270
 
    (Caution: This password is stored in plaintext in lib/conf/conf.py)""",
271
 
    """
272
 
# Database password"""))
273
 
 
274
71
# Try importing existing conf, but if we can't just set up defaults
275
72
# The reason for this is that these settings are used by other phases
276
73
# of setup besides conf, so we need to know them.
277
74
# Also this allows you to hit Return to accept the existing value.
278
75
try:
279
 
    confmodule = __import__("lib/conf/conf")
280
 
    for opt in config_options:
281
 
        try:
282
 
            globals()[opt.option_name] = confmodule.__dict__[opt.option_name]
283
 
        except:
284
 
            globals()[opt.option_name] = opt.default
 
76
    confmodule = __import__("www/conf/conf")
 
77
    root_dir = confmodule.root_dir
 
78
    ivle_install_dir = confmodule.ivle_install_dir
 
79
    jail_base = confmodule.jail_base
285
80
except ImportError:
286
81
    # Just set reasonable defaults
287
 
    for opt in config_options:
288
 
        globals()[opt.option_name] = opt.default
 
82
    root_dir = "/ivle"
 
83
    ivle_install_dir = "/opt/ivle"
 
84
    jail_base = "/home/informatics/jails"
 
85
# Always defaults
 
86
allowed_uids = "0"
289
87
 
290
88
# Try importing install_list, but don't fail if we can't, because listmake can
291
89
# function without it.
300
98
# as necessary, and include it in the distribution.
301
99
listmake_mimetypes = ['text/x-python', 'text/html',
302
100
    'application/x-javascript', 'application/javascript',
303
 
    'text/css', 'image/png', 'application/xml']
 
101
    'text/css', 'image/png']
304
102
 
305
103
# Main function skeleton from Guido van Rossum
306
104
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
328
126
        help([])
329
127
        return 1
330
128
 
331
 
    # Disallow run as root unless installing
332
 
    if (operation != 'install' and operation != 'updatejails'
333
 
        and os.geteuid() == 0):
334
 
        print >>sys.stderr, "I do not want to run this stage as root."
335
 
        print >>sys.stderr, "Please run as a normal user."
336
 
        return 1
337
129
    # Call the requested operation's function
338
130
    try:
339
131
        oper_func = {
340
132
            'help' : help,
341
 
            'config' : conf,
 
133
            'conf' : conf,
342
134
            'build' : build,
343
135
            'listmake' : listmake,
344
136
            'install' : install,
345
 
            'updatejails' : updatejails,
346
137
        }[operation]
347
138
    except KeyError:
348
139
        print >>sys.stderr, (
349
140
            """Invalid operation '%s'. Try python setup.py help."""
350
141
            % operation)
351
 
        return 1
352
142
    return oper_func(argv[2:])
353
143
 
354
144
# Operation functions
358
148
        print """Usage: python setup.py operation [args]
359
149
Operation (and args) can be:
360
150
    help [operation]
361
 
    listmake (developer use only)
362
 
    config [args]
 
151
    conf [args]
363
152
    build
364
 
    install [--nojail] [--nosubjects] [-n|--dry]
 
153
    install [--nojail] [-n|--dry]
365
154
"""
366
155
        return 1
367
156
    elif len(args) != 1:
380
169
be copied upon installation. This should be run by the developer before
381
170
cutting a distribution, and the listfile it generates should be included in
382
171
the distribution, avoiding the administrator having to run it."""
383
 
    elif operation == 'config':
384
 
        print """python setup.py config [args]
 
172
    elif operation == 'conf':
 
173
        print """python setup.py conf [args]
385
174
Configures IVLE with machine-specific details, most notably, various paths.
386
175
Either prompts the administrator for these details or accepts them as
387
176
command-line args. Will be interactive only if there are no arguments given.
388
177
Takes defaults from existing conf file if it exists.
389
 
 
390
 
To run IVLE out of the source directory (allowing development without having
391
 
to rebuild/install), just provide ivle_install_dir as the IVLE trunk
392
 
directory, and run build/install one time.
393
 
 
394
 
Creates lib/conf/conf.py and trampoline/conf.h.
395
 
 
396
 
Args are:"""
397
 
        for opt in config_options:
398
 
            print "    --" + opt.option_name
399
 
        print """As explained in the interactive prompt or conf.py.
 
178
Creates www/conf/conf.py and trampoline/conf.h.
 
179
Args are:
 
180
    --root_dir
 
181
    --ivle_install_dir
 
182
    --jail_base
 
183
    --allowed_uids
 
184
As explained in the interactive prompt or conf.py.
400
185
"""
401
186
    elif operation == 'build':
402
187
        print """python -O setup.py build [--dry|-n]
413
198
 
414
199
--dry | -n  Print out the actions but don't do anything."""
415
200
    elif operation == 'install':
416
 
        print """sudo python setup.py install [--nojail] [--nosubjects][--dry|-n]
 
201
        print """sudo python setup.py install [--nojail] [--dry|-n]
417
202
(Requires root)
418
203
Create target install directory ($target).
419
204
Create $target/bin.
421
206
chown and chmod the installed trampoline.
422
207
Copy www/ to $target.
423
208
Copy jail/ to jails template directory (unless --nojail specified).
424
 
Copy subjects/ to subjects directory (unless --nosubjects specified).
425
 
 
426
 
--nojail        Do not copy the jail.
427
 
--nosubjects    Do not copy the subjects and problems directories.
428
 
--dry | -n  Print out the actions but don't do anything."""
429
 
    elif operation == 'updatejails':
430
 
        print """sudo python setup.py updatejails [--dry|-n]
431
 
(Requires root)
432
 
Copy jail/ to each subdirectory in jails directory.
433
 
 
 
209
 
 
210
--nojail    Do not copy the jail.
434
211
--dry | -n  Print out the actions but don't do anything."""
435
212
    else:
436
213
        print >>sys.stderr, (
441
218
def listmake(args):
442
219
    # We build two separate lists, by walking www and console
443
220
    list_www = build_list_py_files('www')
444
 
    list_lib = build_list_py_files('lib')
445
 
    list_scripts = build_list_py_files('scripts')
446
 
    list_subjects = build_list_py_files('subjects', no_top_level=True)
447
 
    list_problems = build_list_py_files('problems', no_top_level=True)
 
221
    list_console = build_list_py_files('console')
448
222
    # Make sure that the files generated by conf are in the list
449
223
    # (since listmake is typically run before conf)
450
 
    if "lib/conf/conf.py" not in list_lib:
451
 
        list_lib.append("lib/conf/conf.py")
 
224
    if "www/conf/conf.py" not in list_www:
 
225
        list_www.append("www/conf/conf.py")
452
226
    # Make sure that console/python-console is in the list
453
 
    if "scripts/python-console" not in list_scripts:
454
 
        list_scripts.append("scripts/python-console")
455
 
    if "scripts/fileservice" not in list_scripts:
456
 
        list_scripts.append("scripts/fileservice")
 
227
    if "console/python-console" not in list_console:
 
228
        list_console.append("console/python-console")
457
229
    # Write these out to a file
458
230
    cwd = os.getcwd()
459
231
    # the files that will be created/overwritten
473
245
list_www = """)
474
246
        writelist_pretty(file, list_www)
475
247
        file.write("""
476
 
# List of all installable files in lib directory.
477
 
list_lib = """)
478
 
        writelist_pretty(file, list_lib)
479
 
        file.write("""
480
 
# List of all installable files in scripts directory.
481
 
list_scripts = """)
482
 
        writelist_pretty(file, list_scripts)
483
 
        file.write("""
484
 
# List of all installable files in subjects directory.
485
 
# This is to install sample subjects and material.
486
 
list_subjects = """)
487
 
        writelist_pretty(file, list_subjects)
488
 
        file.write("""
489
 
# List of all installable files in problems directory.
490
 
# This is to install sample exercise material.
491
 
list_problems = """)
492
 
        writelist_pretty(file, list_problems)
 
248
# List of all installable files in console directory.
 
249
list_console = """)
 
250
        writelist_pretty(file, list_console)
493
251
 
494
252
        file.close()
495
253
    except IOError, (errno, strerror):
506
264
 
507
265
    return 0
508
266
 
509
 
def build_list_py_files(dir, no_top_level=False):
 
267
def build_list_py_files(dir):
510
268
    """Builds a list of all py files found in a directory and its
511
 
    subdirectories. Returns this as a list of strings.
512
 
    no_top_level=True means the file paths will not include the top-level
513
 
    directory.
514
 
    """
 
269
    subdirectories. Returns this as a list of strings."""
515
270
    pylist = []
516
271
    for (dirpath, dirnames, filenames) in os.walk(dir):
517
272
        # Exclude directories beginning with a '.' (such as '.svn')
519
274
        # All *.py files are added to the list
520
275
        pylist += [os.path.join(dirpath, item) for item in filenames
521
276
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
522
 
    if no_top_level:
523
 
        for i in range(0, len(pylist)):
524
 
            _, pylist[i] = pylist[i].split(os.sep, 1)
525
277
    return pylist
526
278
 
527
279
def writelist_pretty(file, list):
535
287
        file.write(']\n')
536
288
 
537
289
def conf(args):
538
 
    global db_port
 
290
    global root_dir, ivle_install_dir, jail_base, allowed_uids
539
291
    # Set up some variables
540
292
 
541
293
    cwd = os.getcwd()
542
294
    # the files that will be created/overwritten
543
 
    conffile = os.path.join(cwd, "lib/conf/conf.py")
544
 
    jailconffile = os.path.join(cwd, "lib/conf/jailconf.py")
 
295
    conffile = os.path.join(cwd, "www/conf/conf.py")
545
296
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
546
297
 
 
298
    # Fixed config options that we don't ask the admin
 
299
    default_app = "dummy"
 
300
 
547
301
    # Get command-line arguments to avoid asking questions.
548
302
 
549
 
    optnames = []
550
 
    for opt in config_options:
551
 
        optnames.append(opt.option_name + "=")
552
 
    (opts, args) = getopt.gnu_getopt(args, "", optnames)
 
303
    (opts, args) = getopt.gnu_getopt(args, "", ['root_dir=',
 
304
                    'ivle_install_dir=', 'jail_base=', 'allowed_uids='])
553
305
 
554
306
    if args != []:
555
307
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
561
313
        print """This tool will create the following files:
562
314
    %s
563
315
    %s
564
 
    %s
565
316
prompting you for details about your configuration. The file will be
566
317
overwritten if it already exists. It will *not* install or deploy IVLE.
567
318
 
568
319
Please hit Ctrl+C now if you do not wish to do this.
569
 
""" % (conffile, jailconffile, conf_hfile)
 
320
""" % (conffile, conf_hfile)
570
321
 
571
322
        # Get information from the administrator
572
323
        # If EOF is encountered at any time during the questioning, just exit
573
324
        # silently
574
325
 
575
 
        for opt in config_options:
576
 
            globals()[opt.option_name] = \
577
 
                query_user(globals()[opt.option_name], opt.prompt)
 
326
        root_dir = query_user(root_dir,
 
327
        """Root directory where IVLE is located (in URL space):""")
 
328
        ivle_install_dir = query_user(ivle_install_dir,
 
329
        'Root directory where IVLE will be installed (on the local file '
 
330
        'system):')
 
331
        jail_base = query_user(jail_base,
 
332
        """Root directory where the jails (containing user files) are stored
 
333
(on the local file system):""")
 
334
        allowed_uids = query_user(allowed_uids,
 
335
        """UID of the web server process which will run IVLE.
 
336
Only this user may execute the trampoline. May specify multiple users as
 
337
a comma-separated list.
 
338
    (eg. "1002,78")""")
 
339
 
578
340
    else:
579
341
        opts = dict(opts)
580
342
        # Non-interactive mode. Parse the options.
581
 
        for opt in config_options:
582
 
            if '--' + opt.option_name in opts:
583
 
                globals()[opt.option_name] = opts['--' + opt.option_name]
 
343
        if '--root_dir' in opts:
 
344
            root_dir = opts['--root_dir']
 
345
        if '--ivle_install_dir' in opts:
 
346
            ivle_install_dir = opts['--ivle_install_dir']
 
347
        if '--jail_base' in opts:
 
348
            jail_base = opts['--jail_base']
 
349
        if '--allowed_uids' in opts:
 
350
            allowed_uids = opts['--allowed_uids']
584
351
 
585
352
    # Error handling on input values
586
353
    try:
587
 
        allowed_uids_list = map(int, allowed_uids.split(','))
 
354
        allowed_uids = map(int, allowed_uids.split(','))
588
355
    except ValueError:
589
356
        print >>sys.stderr, (
590
357
        "Invalid UID list (%s).\n"
591
358
        "Must be a comma-separated list of integers." % allowed_uids)
592
359
        return 1
593
 
    try:
594
 
        db_port = int(db_port)
595
 
        if db_port < 0 or db_port >= 65536: raise ValueError()
596
 
    except ValueError:
597
 
        print >>sys.stderr, (
598
 
        "Invalid DB port (%s).\n"
599
 
        "Must be an integer between 0 and 65535." % repr(db_port))
600
 
        return 1
601
360
 
602
 
    # Write lib/conf/conf.py
 
361
    # Write www/conf/conf.py
603
362
 
604
363
    try:
605
364
        conf = open(conffile, "w")
608
367
# conf.py
609
368
# Miscellaneous application settings
610
369
 
611
 
""")
612
 
        for opt in config_options:
613
 
            conf.write('%s\n%s = %s\n' % (opt.comment, opt.option_name,
614
 
                repr(globals()[opt.option_name])))
615
 
 
616
 
        conf.close()
617
 
    except IOError, (errno, strerror):
618
 
        print "IO error(%s): %s" % (errno, strerror)
619
 
        sys.exit(1)
620
 
 
621
 
    print "Successfully wrote lib/conf/conf.py"
622
 
 
623
 
    # Write conf/jailconf.py
624
 
 
625
 
    try:
626
 
        conf = open(jailconffile, "w")
627
 
 
628
 
        # In the "in-jail" version of conf, we don't need MOST of the details
629
 
        # (it would be a security risk to have them here).
630
 
        # So we just write root_dir, and jail_base is "/".
631
 
        # (jail_base being "/" means "jail-relative" paths are relative to "/"
632
 
        # when inside the jail.)
633
 
        conf.write("""# IVLE Configuration File
634
 
# conf.py
635
 
# Miscellaneous application settings
636
 
# (User jail version)
637
 
 
638
370
 
639
371
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
640
372
# with this).
641
373
# eg. "/" or "/ivle".
642
 
root_dir = %s
 
374
root_dir = "%s"
 
375
 
 
376
# In the local file system, where IVLE is actually installed.
 
377
# This directory should contain the "www" and "bin" directories.
 
378
ivle_install_dir = "%s"
643
379
 
644
380
# In the local file system, where are the student/user file spaces located.
645
381
# The user jails are expected to be located immediately in subdirectories of
646
382
# this location.
647
 
jail_base = '/'
 
383
jail_base = "%s"
648
384
 
649
 
# The hostname for serving publicly accessible pages
650
 
public_host = %s
651
 
""" % (repr(root_dir),repr(public_host)))
 
385
# Which application to load by default (if the user navigates to the top level
 
386
# of the site). This is the app's URL name.
 
387
# Note that if this app requires authentication, the user will first be
 
388
# presented with the login screen.
 
389
default_app = "%s"
 
390
""" % (root_dir, ivle_install_dir, jail_base, default_app))
652
391
 
653
392
        conf.close()
654
393
    except IOError, (errno, strerror):
655
394
        print "IO error(%s): %s" % (errno, strerror)
656
395
        sys.exit(1)
657
396
 
658
 
    print "Successfully wrote lib/conf/jailconf.py"
 
397
    print "Successfully wrote www/conf/conf.py"
659
398
 
660
399
    # Write trampoline/conf.h
661
400
 
680
419
 * (Note that root is an implicit member of this list).
681
420
 */
682
421
static const int allowed_uids[] = { %s };
683
 
""" % (repr(jail_base)[1:-1], repr(allowed_uids_list)[1:-1]))
684
 
    # Note: The above uses PYTHON reprs, not C reprs
685
 
    # However they should be the same with the exception of the outer
686
 
    # characters, which are stripped off and replaced
 
422
""" % (jail_base, repr(allowed_uids)[1:-1]))
687
423
 
688
424
        conf.close()
689
425
    except IOError, (errno, strerror):
695
431
    print
696
432
    print "You may modify the configuration at any time by editing"
697
433
    print conffile
698
 
    print jailconffile
699
434
    print conf_hfile
700
435
    print
701
436
    return 0
710
445
        print "Dry run (no actions will be executed\n"
711
446
 
712
447
    # Compile the trampoline
713
 
    curdir = os.getcwd()
714
 
    os.chdir('trampoline')
715
 
    action_runprog('make', [], dry)
716
 
    os.chdir(curdir)
 
448
    action_runprog('gcc', ['-Wall', '-o', 'trampoline/trampoline',
 
449
        'trampoline/trampoline.c'], dry)
717
450
 
718
451
    # Create the jail and its subdirectories
719
452
    # Note: Other subdirs will be made by copying files
722
455
    action_mkdir('jail/tmp', dry)
723
456
 
724
457
    # Copy all console and operating system files into the jail
725
 
    action_copylist(install_list.list_scripts, 'jail/opt/ivle', dry)
 
458
    action_copylist(install_list.list_console, 'jail/opt/ivle', dry)
726
459
    copy_os_files_jail(dry)
727
 
    # Chmod the python console
728
 
    action_chmod_x('jail/opt/ivle/scripts/python-console', dry)
729
 
    action_chmod_x('jail/opt/ivle/scripts/fileservice', dry)
730
 
    
731
 
    # Also copy the IVLE lib directory into the jail
732
 
    # This is necessary for running certain scripts
733
 
    action_copylist(install_list.list_lib, 'jail/opt/ivle', dry)
734
 
    # IMPORTANT: The file jail/opt/ivle/lib/conf/conf.py contains details
735
 
    # which could compromise security if left in the jail (such as the DB
736
 
    # password).
737
 
    # The "safe" version is in jailconf.py. Delete conf.py and replace it with
738
 
    # jailconf.py.
739
 
    action_copyfile('lib/conf/jailconf.py',
740
 
        'jail/opt/ivle/lib/conf/conf.py', dry)
741
460
 
742
461
    # Compile .py files into .pyc or .pyo files
743
462
    compileall.compile_dir('www', quiet=True)
744
 
    compileall.compile_dir('lib', quiet=True)
745
 
    compileall.compile_dir('scripts', quiet=True)
746
 
    compileall.compile_dir('jail/opt/ivle/lib', quiet=True)
747
 
 
748
 
    # Set up ivle.pth inside the jail
749
 
    # Need to set /opt/ivle/lib to be on the import path
750
 
    ivle_pth = \
751
 
        "jail/usr/lib/python%s/site-packages/ivle.pth" % PYTHON_VERSION
752
 
    f = open(ivle_pth, 'w')
753
 
    f.write('/opt/ivle/lib\n')
754
 
    f.close()
 
463
    compileall.compile_dir('console', quiet=True)
755
464
 
756
465
    return 0
757
466
 
759
468
    """Copies necessary Operating System files from their usual locations
760
469
    into the jail/ directory of the cwd."""
761
470
    # Currently source paths are configured for Ubuntu.
762
 
    for filename in JAIL_FILES:
763
 
        copy_file_to_jail(filename, dry)
764
 
    for src, dst in JAIL_LINKS.items():
765
 
        action_symlink(src, dst, dry)
766
 
    for src, dst in JAIL_COPYTREES.items():
767
 
        action_copytree(src, dst, dry)
 
471
    copy_file_to_jail('/lib/ld-linux.so.2', dry)
 
472
    copy_file_to_jail('/lib/tls/i686/cmov/libc.so.6', dry)
 
473
    copy_file_to_jail('/lib/tls/i686/cmov/libdl.so.2', dry)
 
474
    copy_file_to_jail('/lib/tls/i686/cmov/libm.so.6', dry)
 
475
    copy_file_to_jail('/lib/tls/i686/cmov/libpthread.so.0', dry)
 
476
    copy_file_to_jail('/lib/tls/i686/cmov/libutil.so.1', dry)
 
477
    copy_file_to_jail('/usr/bin/python2.5', dry)
 
478
    # TODO: ln -s jail/usr/bin/python2.5 jail/usr/bin/python
 
479
    action_copytree('/usr/lib/python2.5', 'jail/usr/lib/python2.5', dry)
768
480
 
769
481
def copy_file_to_jail(src, dry):
770
482
    """Copies a single file from an absolute location into the same location
774
486
 
775
487
def install(args):
776
488
    # Get "dry" and "nojail" variables from command line
777
 
    (opts, args) = getopt.gnu_getopt(args, "n",
778
 
        ['dry', 'nojail', 'nosubjects'])
 
489
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry', 'nojail'])
779
490
    opts = dict(opts)
780
491
    dry = '-n' in opts or '--dry' in opts
781
492
    nojail = '--nojail' in opts
782
 
    nosubjects = '--nosubjects' in opts
783
493
 
784
494
    if dry:
785
495
        print "Dry run (no actions will be executed\n"
799
509
    # chown trampoline to root and set setuid bit
800
510
    action_chown_setuid(tramppath, dry)
801
511
 
802
 
    # Copy the www and lib directories using the list
 
512
    # Copy the www directory using the list
803
513
    action_copylist(install_list.list_www, ivle_install_dir, dry)
804
 
    action_copylist(install_list.list_lib, ivle_install_dir, dry)
805
514
 
806
515
    if not nojail:
807
516
        # Copy the local jail directory built by the build action
808
517
        # to the jails template directory (it will be used as a template
809
518
        # for all the students' jails).
810
519
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
811
 
    if not nosubjects:
812
 
        # Copy the subjects and problems directories across
813
 
        action_copylist(install_list.list_subjects, subjects_base, dry,
814
 
            srcdir="./subjects")
815
 
        action_copylist(install_list.list_problems, problems_base, dry,
816
 
            srcdir="./problems")
817
 
 
818
 
    # Append IVLE path to ivle.pth in python site packages
819
 
    # (Unless it's already there)
820
 
    ivle_pth = os.path.join(sys.prefix,
821
 
        "lib/python%s/site-packages/ivle.pth" % PYTHON_VERSION)
822
 
    ivle_www = os.path.join(ivle_install_dir, "www")
823
 
    ivle_lib = os.path.join(ivle_install_dir, "lib")
824
 
    write_ivle_pth = True
825
 
    write_ivle_lib_pth = True
826
 
    try:
827
 
        file = open(ivle_pth, 'r')
828
 
        for line in file:
829
 
            if line.strip() == ivle_www:
830
 
                write_ivle_pth = False
831
 
            elif line.strip() == ivle_lib:
832
 
                write_ivle_lib_pth = False
833
 
        file.close()
834
 
    except (IOError, OSError):
835
 
        pass
836
 
    if write_ivle_pth:
837
 
        action_append(ivle_pth, ivle_www)
838
 
    if write_ivle_lib_pth:
839
 
        action_append(ivle_pth, ivle_lib)
840
 
 
841
 
    return 0
842
 
 
843
 
def updatejails(args):
844
 
    # Get "dry" variable from command line
845
 
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
846
 
    opts = dict(opts)
847
 
    dry = '-n' in opts or '--dry' in opts
848
 
 
849
 
    if dry:
850
 
        print "Dry run (no actions will be executed\n"
851
 
 
852
 
    if not dry and os.geteuid() != 0:
853
 
        print >>sys.stderr, "Must be root to run install"
854
 
        print >>sys.stderr, "(I need to chown some files)."
855
 
        return 1
856
 
 
857
 
    # Update the template jail directory in case it hasn't been installed
858
 
    # recently.
859
 
    action_copytree('jail', os.path.join(jail_base, 'template'), dry)
860
 
 
861
 
    # Re-link all the files in all students jails.
862
 
    for dir in os.listdir(jail_base):
863
 
        if dir == 'template': continue
864
 
        # First back up the student's home directory
865
 
        temp_home = os.tmpnam()
866
 
        action_rename(os.path.join(jail_base, dir, 'home'), temp_home, dry)
867
 
        # Delete the student's jail and relink the jail files
868
 
        action_linktree(os.path.join(jail_base, 'template'),
869
 
            os.path.join(jail_base, dir), dry)
870
 
        # Restore the student's home directory
871
 
        action_rename(temp_home, os.path.join(jail_base, dir, 'home'), dry)
872
 
        # Set up the user's home directory just in case they don't have a
873
 
        # directory for this yet
874
 
        action_mkdir(os.path.join(jail_base, dir, 'home', dir), dry)
 
520
        # Set up symlinks inside the jail
 
521
        action_symlink(os.path.join(jail_base, 'template/usr/bin/python2.5'),
 
522
            os.path.join(jail_base, 'template/usr/bin/python'), dry)
875
523
 
876
524
    return 0
877
525
 
904
552
    if ret != 0:
905
553
        raise RunError(prog, ret)
906
554
 
907
 
def action_remove(path, dry):
908
 
    """Calls rmtree, deleting the target file if it exists."""
909
 
    try:
910
 
        print "rm -r", path
911
 
        if not dry:
912
 
            shutil.rmtree(path, True)
913
 
    except OSError, (err, msg):
914
 
        if err != errno.EEXIST:
915
 
            raise
916
 
        # Otherwise, didn't exist, so we don't care
917
 
 
918
 
def action_rename(src, dst, dry):
919
 
    """Calls rename. Deletes the target if it already exists."""
920
 
    action_remove(dst, dry)
921
 
    print "mv ", src, dst
922
 
    if dry: return
923
 
    try:
924
 
        os.rename(src, dst)
925
 
    except OSError, (err, msg):
926
 
        if err != errno.EEXIST:
927
 
            raise
928
 
 
929
555
def action_mkdir(path, dry):
930
556
    """Calls mkdir. Silently ignored if the directory already exists.
931
557
    Creates all parent directories as necessary."""
943
569
    directories as necessary.
944
570
 
945
571
    See shutil.copytree."""
946
 
    action_remove(dst, dry)
 
572
    if os.access(dst, os.F_OK):
 
573
        print "rm -r", dst
 
574
        if not dry:
 
575
            shutil.rmtree(dst, True)
947
576
    print "cp -r", src, dst
948
577
    if dry: return
949
 
    shutil.copytree(src, dst, True)
950
 
 
951
 
def action_linktree(src, dst, dry):
952
 
    """Hard-links an entire directory tree. Same as copytree but the created
953
 
    files are hard-links not actual copies. Removes the existing destination.
954
 
    """
955
 
    action_remove(dst, dry)
956
 
    print "<cp with hardlinks> -r", src, dst
957
 
    if dry: return
958
 
    common.makeuser.linktree(src, dst)
959
 
 
960
 
def action_copylist(srclist, dst, dry, srcdir="."):
 
578
    shutil.copytree(src, dst)
 
579
 
 
580
def action_copylist(srclist, dst, dry):
961
581
    """Copies all files in a list to a new location. The files in the list
962
582
    are read relative to the current directory, and their destinations are the
963
583
    same paths relative to dst. Creates all parent directories as necessary.
964
 
    srcdir is "." by default, can be overridden.
965
584
    """
966
585
    for srcfile in srclist:
967
586
        dstfile = os.path.join(dst, srcfile)
968
 
        srcfile = os.path.join(srcdir, srcfile)
969
587
        dstdir = os.path.split(dstfile)[0]
970
588
        if not os.path.isdir(dstdir):
971
589
            action_mkdir(dstdir, dry)
972
590
        print "cp -f", srcfile, dstfile
973
591
        if not dry:
974
 
            try:
975
 
                shutil.copyfile(srcfile, dstfile)
976
 
                shutil.copymode(srcfile, dstfile)
977
 
            except shutil.Error:
978
 
                pass
 
592
            shutil.copyfile(srcfile, dstfile)
979
593
 
980
594
def action_copyfile(src, dst, dry):
981
595
    """Copies one file to a new location. Creates all parent directories
982
596
    as necessary.
983
 
    Warn if file not found.
984
597
    """
985
598
    dstdir = os.path.split(dst)[0]
986
599
    if not os.path.isdir(dstdir):
987
600
        action_mkdir(dstdir, dry)
988
601
    print "cp -f", src, dst
989
602
    if not dry:
990
 
        try:
991
 
            shutil.copyfile(src, dst)
992
 
            shutil.copymode(src, dst)
993
 
        except (shutil.Error, IOError), e:
994
 
            print "Warning: " + str(e)
 
603
        shutil.copyfile(src, dst)
995
604
 
996
605
def action_symlink(src, dst, dry):
997
606
    """Creates a symlink in a given location. Creates all parent directories
1000
609
    dstdir = os.path.split(dst)[0]
1001
610
    if not os.path.isdir(dstdir):
1002
611
        action_mkdir(dstdir, dry)
1003
 
    # Delete existing file
1004
 
    if os.path.exists(dst):
1005
 
        os.remove(dst)
1006
 
    print "ln -fs", src, dst
 
612
    print "ln -s", src, dst
1007
613
    if not dry:
1008
614
        os.symlink(src, dst)
1009
615
 
1010
 
def action_append(ivle_pth, ivle_www):
1011
 
    file = open(ivle_pth, 'a+')
1012
 
    file.write(ivle_www + '\n')
1013
 
    file.close()
1014
 
 
1015
616
def action_chown_setuid(file, dry):
1016
617
    """Chowns a file to root, and sets the setuid bit on the file.
1017
618
    Calling this function requires the euid to be root.
1026
627
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1027
628
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
1028
629
 
1029
 
def action_chmod_x(file, dry):
1030
 
    """Chmod 755 a file (sets permissions to rwxr-xr-x)."""
1031
 
    print "chmod 755", file
1032
 
    if not dry:
1033
 
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR
1034
 
            | stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH)
1035
 
 
1036
630
def query_user(default, prompt):
1037
631
    """Prompts the user for a string, which is read from a line of stdin.
1038
632
    Exits silently if EOF is encountered. Returns the string, with spaces