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

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: mattgiuca
  • Date: 2008-01-25 05:38:02 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:312
Full client-side testing - functional.

tutorial.js: Added all the code necessary to send the request, parse the
response, and generate the DOM nodes to display the result to the user.

tutorial.css: Added list styles.
Added media/images/tutorial with pass/fail/exception icons.

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.
70
70
 
71
71
# Import modules from the website is tricky since they're in the www
72
72
# directory.
73
 
sys.path.append(os.path.join(os.getcwd(), 'lib'))
 
73
sys.path.append(os.path.join(os.getcwd(), 'www'))
74
74
import conf
75
75
import common.makeuser
76
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
77
# Operating system files to copy over into the jail.
83
78
# These will be copied from the given place on the OS file system into the
84
79
# same place within the jail.
99
94
    '/bin/ls',
100
95
    '/bin/echo',
101
96
    # 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',
 
97
    '/usr/bin/python2.5',
142
98
    # Needed by matplotlib
143
99
    '/usr/lib/i686/cmov/libssl.so.0.9.8',
144
100
    '/usr/lib/i686/cmov/libcrypto.so.0.9.8',
162
118
]
163
119
# Symlinks to make within the jail. Src mapped to dst.
164
120
JAIL_LINKS = {
165
 
    'python%s' % PYTHON_VERSION: 'jail/usr/bin/python',
 
121
    'python2.5': 'jail/usr/bin/python',
166
122
}
167
123
# Trees to copy. Src mapped to dst (these will be passed to action_copytree).
168
124
JAIL_COPYTREES = {
169
 
    '/usr/lib/python%s' % PYTHON_VERSION:
170
 
        'jail/usr/lib/python%s' % PYTHON_VERSION,
 
125
    '/usr/lib/python2.5': 'jail/usr/lib/python2.5',
171
126
    '/usr/share/matplotlib': 'jail/usr/share/matplotlib',
172
127
    '/etc/ld.so.conf.d': 'jail/etc/ld.so.conf.d',
173
128
}
174
129
 
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", "/",
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
 
config_options.append(ConfigOption("svn_conf", "/opt/ivle/svn/svn.conf",
274
 
    """The location of the subversion configuration file used by apache
275
 
to host the user repositories:""",
276
 
    """
277
 
# The location of the subversion configuration file used by
278
 
# apache to host the user repositories."""))
279
 
config_options.append(ConfigOption("svn_repo_path", "/home/informatics/repositories",
280
 
    """The root directory for the subversion repositories:""",
281
 
    """
282
 
# The root directory for the subversion repositories."""))
283
 
config_options.append(ConfigOption("svn_auth_ivle", "/opt/ivle/svn/ivle.auth",
284
 
    """The location of the password file used to authenticate users
285
 
of the subversion repository from the ivle server:""",
286
 
    """
287
 
# The location of the password file used to authenticate users
288
 
# of the subversion repository from the ivle server."""))
289
 
config_options.append(ConfigOption("svn_auth_local", "/opt/ivle/svn/local.auth",
290
 
    """The location of the password file used to authenticate local users
291
 
of the subversion repository:""",
292
 
    """
293
 
# The location of the password file used to authenticate local users
294
 
# of the subversion repository."""))
295
 
config_options.append(ConfigOption("usrmgt_host", "localhost",
296
 
    """The hostname where the usrmgt-server runs:""",
297
 
    """
298
 
# The hostname where the usrmgt-server runs."""))
299
 
config_options.append(ConfigOption("usrmgt_port", "2178",
300
 
    """The port where the usrmgt-server runs:""",
301
 
    """
302
 
# The port where the usrmgt-server runs."""))
303
 
config_options.append(ConfigOption("usrmgt_magic", "",
304
 
    """The password for the usrmgt-server:""",
305
 
    """
306
 
# The password for the usrmgt-server."""))
307
 
 
308
130
# Try importing existing conf, but if we can't just set up defaults
309
131
# The reason for this is that these settings are used by other phases
310
132
# of setup besides conf, so we need to know them.
311
133
# Also this allows you to hit Return to accept the existing value.
312
134
try:
313
 
    confmodule = __import__("lib/conf/conf")
314
 
    for opt in config_options:
315
 
        try:
316
 
            globals()[opt.option_name] = confmodule.__dict__[opt.option_name]
317
 
        except:
318
 
            globals()[opt.option_name] = opt.default
 
135
    confmodule = __import__("www/conf/conf")
 
136
    try:
 
137
        root_dir = confmodule.root_dir
 
138
    except:
 
139
        root_dir = "/ivle"
 
140
    try:
 
141
        ivle_install_dir = confmodule.ivle_install_dir
 
142
    except:
 
143
        ivle_install_dir = "/opt/ivle"
 
144
    try:
 
145
        public_host = confmodule.public_host
 
146
    except:
 
147
        public_host = "public.localhost"
 
148
    try:
 
149
        jail_base = confmodule.jail_base
 
150
    except:
 
151
        jail_base = "/home/informatics/jails"
 
152
    try:
 
153
        subjects_base = confmodule.subjects_base
 
154
    except:
 
155
        subjects_base = "/home/informatics/subjects"
319
156
except ImportError:
320
157
    # Just set reasonable defaults
321
 
    for opt in config_options:
322
 
        globals()[opt.option_name] = opt.default
 
158
    root_dir = "/ivle"
 
159
    ivle_install_dir = "/opt/ivle"
 
160
    public_host = "public.localhost"
 
161
    jail_base = "/home/informatics/jails"
 
162
    subjects_base = "/home/informatics/subjects"
 
163
# Always defaults
 
164
allowed_uids = "0"
323
165
 
324
166
# Try importing install_list, but don't fail if we can't, because listmake can
325
167
# function without it.
334
176
# as necessary, and include it in the distribution.
335
177
listmake_mimetypes = ['text/x-python', 'text/html',
336
178
    'application/x-javascript', 'application/javascript',
337
 
    'text/css', 'image/png', 'application/xml']
 
179
    'text/css', 'image/png']
338
180
 
339
181
# Main function skeleton from Guido van Rossum
340
182
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
372
214
    try:
373
215
        oper_func = {
374
216
            'help' : help,
375
 
            'config' : conf,
 
217
            'conf' : conf,
376
218
            'build' : build,
377
219
            'listmake' : listmake,
378
220
            'install' : install,
393
235
Operation (and args) can be:
394
236
    help [operation]
395
237
    listmake (developer use only)
396
 
    config [args]
 
238
    conf [args]
397
239
    build
398
 
    install [--nojail] [--nosubjects] [-n|--dry]
 
240
    install [--nojail] [-n|--dry]
399
241
"""
400
242
        return 1
401
243
    elif len(args) != 1:
414
256
be copied upon installation. This should be run by the developer before
415
257
cutting a distribution, and the listfile it generates should be included in
416
258
the distribution, avoiding the administrator having to run it."""
417
 
    elif operation == 'config':
418
 
        print """python setup.py config [args]
 
259
    elif operation == 'conf':
 
260
        print """python setup.py conf [args]
419
261
Configures IVLE with machine-specific details, most notably, various paths.
420
262
Either prompts the administrator for these details or accepts them as
421
263
command-line args. Will be interactive only if there are no arguments given.
425
267
to rebuild/install), just provide ivle_install_dir as the IVLE trunk
426
268
directory, and run build/install one time.
427
269
 
428
 
Creates lib/conf/conf.py and trampoline/conf.h.
 
270
Creates www/conf/conf.py and trampoline/conf.h.
429
271
 
430
 
Args are:"""
431
 
        for opt in config_options:
432
 
            print "    --" + opt.option_name
433
 
        print """As explained in the interactive prompt or conf.py.
 
272
Args are:
 
273
    --root_dir
 
274
    --ivle_install_dir
 
275
    --public_host
 
276
    --jail_base
 
277
    --subjects_base
 
278
    --allowed_uids
 
279
As explained in the interactive prompt or conf.py.
434
280
"""
435
281
    elif operation == 'build':
436
282
        print """python -O setup.py build [--dry|-n]
447
293
 
448
294
--dry | -n  Print out the actions but don't do anything."""
449
295
    elif operation == 'install':
450
 
        print """sudo python setup.py install [--nojail] [--nosubjects][--dry|-n]
 
296
        print """sudo python setup.py install [--nojail] [--dry|-n]
451
297
(Requires root)
452
298
Create target install directory ($target).
453
299
Create $target/bin.
455
301
chown and chmod the installed trampoline.
456
302
Copy www/ to $target.
457
303
Copy jail/ to jails template directory (unless --nojail specified).
458
 
Copy subjects/ to subjects directory (unless --nosubjects specified).
459
304
 
460
 
--nojail        Do not copy the jail.
461
 
--nosubjects    Do not copy the subjects and problems directories.
 
305
--nojail    Do not copy the jail.
462
306
--dry | -n  Print out the actions but don't do anything."""
463
307
    elif operation == 'updatejails':
464
308
        print """sudo python setup.py updatejails [--dry|-n]
475
319
def listmake(args):
476
320
    # We build two separate lists, by walking www and console
477
321
    list_www = build_list_py_files('www')
478
 
    list_lib = build_list_py_files('lib')
479
 
    list_subjects = build_list_py_files('subjects', no_top_level=True)
480
 
    list_problems = build_list_py_files('problems', no_top_level=True)
481
 
    list_scripts = [
482
 
        "scripts/python-console",
483
 
        "scripts/fileservice",
484
 
        "scripts/usrmgt-server",
485
 
    ]
 
322
    list_console = build_list_py_files('console')
486
323
    # Make sure that the files generated by conf are in the list
487
324
    # (since listmake is typically run before conf)
488
 
    if "lib/conf/conf.py" not in list_lib:
489
 
        list_lib.append("lib/conf/conf.py")
 
325
    if "www/conf/conf.py" not in list_www:
 
326
        list_www.append("www/conf/conf.py")
 
327
    # Make sure that console/python-console is in the list
 
328
    if "console/python-console" not in list_console:
 
329
        list_console.append("console/python-console")
490
330
    # Write these out to a file
491
331
    cwd = os.getcwd()
492
332
    # the files that will be created/overwritten
506
346
list_www = """)
507
347
        writelist_pretty(file, list_www)
508
348
        file.write("""
509
 
# List of all installable files in lib directory.
510
 
list_lib = """)
511
 
        writelist_pretty(file, list_lib)
512
 
        file.write("""
513
 
# List of all installable files in scripts directory.
514
 
list_scripts = """)
515
 
        writelist_pretty(file, list_scripts)
516
 
        file.write("""
517
 
# List of all installable files in subjects directory.
518
 
# This is to install sample subjects and material.
519
 
list_subjects = """)
520
 
        writelist_pretty(file, list_subjects)
521
 
        file.write("""
522
 
# List of all installable files in problems directory.
523
 
# This is to install sample exercise material.
524
 
list_problems = """)
525
 
        writelist_pretty(file, list_problems)
 
349
# List of all installable files in console directory.
 
350
list_console = """)
 
351
        writelist_pretty(file, list_console)
526
352
 
527
353
        file.close()
528
354
    except IOError, (errno, strerror):
539
365
 
540
366
    return 0
541
367
 
542
 
def build_list_py_files(dir, no_top_level=False):
 
368
def build_list_py_files(dir):
543
369
    """Builds a list of all py files found in a directory and its
544
 
    subdirectories. Returns this as a list of strings.
545
 
    no_top_level=True means the file paths will not include the top-level
546
 
    directory.
547
 
    """
 
370
    subdirectories. Returns this as a list of strings."""
548
371
    pylist = []
549
372
    for (dirpath, dirnames, filenames) in os.walk(dir):
550
373
        # Exclude directories beginning with a '.' (such as '.svn')
552
375
        # All *.py files are added to the list
553
376
        pylist += [os.path.join(dirpath, item) for item in filenames
554
377
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
555
 
    if no_top_level:
556
 
        for i in range(0, len(pylist)):
557
 
            _, pylist[i] = pylist[i].split(os.sep, 1)
558
378
    return pylist
559
379
 
560
380
def writelist_pretty(file, list):
568
388
        file.write(']\n')
569
389
 
570
390
def conf(args):
571
 
    global db_port, usrmgt_port
 
391
    global root_dir, ivle_install_dir, jail_base, subjects_base
 
392
    global public_host, allowed_uids
572
393
    # Set up some variables
573
394
 
574
395
    cwd = os.getcwd()
575
396
    # the files that will be created/overwritten
576
 
    conffile = os.path.join(cwd, "lib/conf/conf.py")
577
 
    jailconffile = os.path.join(cwd, "lib/conf/jailconf.py")
 
397
    conffile = os.path.join(cwd, "www/conf/conf.py")
578
398
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
579
399
 
580
400
    # Get command-line arguments to avoid asking questions.
581
401
 
582
 
    optnames = []
583
 
    for opt in config_options:
584
 
        optnames.append(opt.option_name + "=")
585
 
    (opts, args) = getopt.gnu_getopt(args, "", optnames)
 
402
    (opts, args) = getopt.gnu_getopt(args, "", ['root_dir=',
 
403
                    'ivle_install_dir=', 'jail_base=', 'allowed_uids='])
586
404
 
587
405
    if args != []:
588
406
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
594
412
        print """This tool will create the following files:
595
413
    %s
596
414
    %s
597
 
    %s
598
415
prompting you for details about your configuration. The file will be
599
416
overwritten if it already exists. It will *not* install or deploy IVLE.
600
417
 
601
418
Please hit Ctrl+C now if you do not wish to do this.
602
 
""" % (conffile, jailconffile, conf_hfile)
 
419
""" % (conffile, conf_hfile)
603
420
 
604
421
        # Get information from the administrator
605
422
        # If EOF is encountered at any time during the questioning, just exit
606
423
        # silently
607
424
 
608
 
        for opt in config_options:
609
 
            globals()[opt.option_name] = \
610
 
                query_user(globals()[opt.option_name], opt.prompt)
 
425
        root_dir = query_user(root_dir,
 
426
        """Root directory where IVLE is located (in URL space):""")
 
427
        ivle_install_dir = query_user(ivle_install_dir,
 
428
        'Root directory where IVLE will be installed (on the local file '
 
429
        'system):')
 
430
        jail_base = query_user(jail_base,
 
431
        """Root directory where the jails (containing user files) are stored
 
432
(on the local file system):""")
 
433
        subjects_base = query_user(subjects_base,
 
434
        """Root directory where the subject directories (containing worksheets
 
435
and other per-subject files) are stored (on the local file system):""")
 
436
        public_host = query_user(public_host,
 
437
        """Hostname which will cause the server to go into "public mode",
 
438
providing login-free access to student's published work:""")
 
439
        allowed_uids = query_user(allowed_uids,
 
440
        """UID of the web server process which will run IVLE.
 
441
Only this user may execute the trampoline. May specify multiple users as
 
442
a comma-separated list.
 
443
    (eg. "1002,78")""")
 
444
 
611
445
    else:
612
446
        opts = dict(opts)
613
447
        # Non-interactive mode. Parse the options.
614
 
        for opt in config_options:
615
 
            if '--' + opt.option_name in opts:
616
 
                globals()[opt.option_name] = opts['--' + opt.option_name]
 
448
        if '--root_dir' in opts:
 
449
            root_dir = opts['--root_dir']
 
450
        if '--ivle_install_dir' in opts:
 
451
            ivle_install_dir = opts['--ivle_install_dir']
 
452
        if '--jail_base' in opts:
 
453
            jail_base = opts['--jail_base']
 
454
        if '--subjects_base' in opts:
 
455
            jail_base = opts['--subjects_base']
 
456
        if '--public_host' in opts:
 
457
            public_host = opts['--public_host']
 
458
        if '--allowed_uids' in opts:
 
459
            allowed_uids = opts['--allowed_uids']
617
460
 
618
461
    # Error handling on input values
619
462
    try:
620
 
        allowed_uids_list = map(int, allowed_uids.split(','))
 
463
        allowed_uids = map(int, allowed_uids.split(','))
621
464
    except ValueError:
622
465
        print >>sys.stderr, (
623
466
        "Invalid UID list (%s).\n"
624
467
        "Must be a comma-separated list of integers." % allowed_uids)
625
468
        return 1
626
 
    try:
627
 
        db_port = int(db_port)
628
 
        if db_port < 0 or db_port >= 65536: raise ValueError()
629
 
    except ValueError:
630
 
        print >>sys.stderr, (
631
 
        "Invalid DB port (%s).\n"
632
 
        "Must be an integer between 0 and 65535." % repr(db_port))
633
 
        return 1
634
 
    try:
635
 
        usrmgt_port = int(usrmgt_port)
636
 
        if usrmgt_port < 0 or usrmgt_port >= 65536: raise ValueError()
637
 
    except ValueError:
638
 
        print >>sys.stderr, (
639
 
        "Invalid user management port (%s).\n"
640
 
        "Must be an integer between 0 and 65535." % repr(usrmgt_port))
641
 
        return 1
642
469
 
643
 
    # Write lib/conf/conf.py
 
470
    # Write www/conf/conf.py
644
471
 
645
472
    try:
646
473
        conf = open(conffile, "w")
649
476
# conf.py
650
477
# Miscellaneous application settings
651
478
 
652
 
""")
653
 
        for opt in config_options:
654
 
            conf.write('%s\n%s = %s\n' % (opt.comment, opt.option_name,
655
 
                repr(globals()[opt.option_name])))
656
 
 
657
 
        conf.close()
658
 
    except IOError, (errno, strerror):
659
 
        print "IO error(%s): %s" % (errno, strerror)
660
 
        sys.exit(1)
661
 
 
662
 
    print "Successfully wrote lib/conf/conf.py"
663
 
 
664
 
    # Write conf/jailconf.py
665
 
 
666
 
    try:
667
 
        conf = open(jailconffile, "w")
668
 
 
669
 
        # In the "in-jail" version of conf, we don't need MOST of the details
670
 
        # (it would be a security risk to have them here).
671
 
        # So we just write root_dir, and jail_base is "/".
672
 
        # (jail_base being "/" means "jail-relative" paths are relative to "/"
673
 
        # when inside the jail.)
674
 
        conf.write("""# IVLE Configuration File
675
 
# conf.py
676
 
# Miscellaneous application settings
677
 
# (User jail version)
678
 
 
679
479
 
680
480
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
681
481
# with this).
682
482
# eg. "/" or "/ivle".
683
 
root_dir = %s
 
483
root_dir = "%s"
 
484
 
 
485
# In the local file system, where IVLE is actually installed.
 
486
# This directory should contain the "www" and "bin" directories.
 
487
ivle_install_dir = "%s"
 
488
 
 
489
# The server goes into "public mode" if the browser sends a request with this
 
490
# host. This is for security reasons - we only serve public student files on a
 
491
# separate domain to the main IVLE site.
 
492
# Public mode does not use cookies, and serves only public content.
 
493
# Private mode (normal mode) requires login, and only serves files relevant to
 
494
# the logged-in user.
 
495
public_host = "%s"
684
496
 
685
497
# In the local file system, where are the student/user file spaces located.
686
498
# The user jails are expected to be located immediately in subdirectories of
687
499
# this location.
688
 
jail_base = '/'
 
500
jail_base = "%s"
689
501
 
690
 
# The hostname for serving publicly accessible pages
691
 
public_host = %s
692
 
""" % (repr(root_dir),repr(public_host)))
 
502
# In the local file system, where are the per-subject file spaces located.
 
503
# The individual subject directories are expected to be located immediately
 
504
# in subdirectories of this location.
 
505
subjects_base = "%s"
 
506
""" % (root_dir, ivle_install_dir, public_host, jail_base, subjects_base))
693
507
 
694
508
        conf.close()
695
509
    except IOError, (errno, strerror):
696
510
        print "IO error(%s): %s" % (errno, strerror)
697
511
        sys.exit(1)
698
512
 
699
 
    print "Successfully wrote lib/conf/jailconf.py"
 
513
    print "Successfully wrote www/conf/conf.py"
700
514
 
701
515
    # Write trampoline/conf.h
702
516
 
721
535
 * (Note that root is an implicit member of this list).
722
536
 */
723
537
static const int allowed_uids[] = { %s };
724
 
""" % (repr(jail_base)[1:-1], repr(allowed_uids_list)[1:-1]))
725
 
    # Note: The above uses PYTHON reprs, not C reprs
726
 
    # However they should be the same with the exception of the outer
727
 
    # characters, which are stripped off and replaced
 
538
""" % (jail_base, repr(allowed_uids)[1:-1]))
728
539
 
729
540
        conf.close()
730
541
    except IOError, (errno, strerror):
736
547
    print
737
548
    print "You may modify the configuration at any time by editing"
738
549
    print conffile
739
 
    print jailconffile
740
550
    print conf_hfile
741
551
    print
742
552
    return 0
751
561
        print "Dry run (no actions will be executed\n"
752
562
 
753
563
    # Compile the trampoline
754
 
    curdir = os.getcwd()
755
 
    os.chdir('trampoline')
756
 
    action_runprog('make', [], dry)
757
 
    os.chdir(curdir)
 
564
    action_runprog('gcc', ['-Wall', '-o', 'trampoline/trampoline',
 
565
        'trampoline/trampoline.c'], dry)
758
566
 
759
567
    # Create the jail and its subdirectories
760
568
    # Note: Other subdirs will be made by copying files
763
571
    action_mkdir('jail/tmp', dry)
764
572
 
765
573
    # Copy all console and operating system files into the jail
766
 
    action_copylist(install_list.list_scripts, 'jail/opt/ivle', dry)
 
574
    action_copylist(install_list.list_console, 'jail/opt/ivle', dry)
767
575
    copy_os_files_jail(dry)
768
576
    # Chmod the python console
769
 
    action_chmod_x('jail/opt/ivle/scripts/python-console', dry)
770
 
    action_chmod_x('jail/opt/ivle/scripts/fileservice', dry)
 
577
    action_chmod_x('jail/opt/ivle/console/python-console', dry)
771
578
    
772
 
    # Also copy the IVLE lib directory into the jail
773
 
    # This is necessary for running certain scripts
774
 
    action_copylist(install_list.list_lib, 'jail/opt/ivle', dry)
775
 
    # IMPORTANT: The file jail/opt/ivle/lib/conf/conf.py contains details
776
 
    # which could compromise security if left in the jail (such as the DB
777
 
    # password).
778
 
    # The "safe" version is in jailconf.py. Delete conf.py and replace it with
779
 
    # jailconf.py.
780
 
    action_copyfile('lib/conf/jailconf.py',
781
 
        'jail/opt/ivle/lib/conf/conf.py', dry)
782
579
 
783
580
    # Compile .py files into .pyc or .pyo files
784
581
    compileall.compile_dir('www', quiet=True)
785
 
    compileall.compile_dir('lib', quiet=True)
786
 
    compileall.compile_dir('scripts', quiet=True)
787
 
    compileall.compile_dir('jail/opt/ivle/lib', quiet=True)
788
 
 
789
 
    # Set up ivle.pth inside the jail
790
 
    # Need to set /opt/ivle/lib to be on the import path
791
 
    ivle_pth = \
792
 
        "jail/usr/lib/python%s/site-packages/ivle.pth" % PYTHON_VERSION
793
 
    f = open(ivle_pth, 'w')
794
 
    f.write('/opt/ivle/lib\n')
795
 
    f.close()
 
582
    compileall.compile_dir('console', quiet=True)
796
583
 
797
584
    return 0
798
585
 
815
602
 
816
603
def install(args):
817
604
    # Get "dry" and "nojail" variables from command line
818
 
    (opts, args) = getopt.gnu_getopt(args, "n",
819
 
        ['dry', 'nojail', 'nosubjects'])
 
605
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry', 'nojail'])
820
606
    opts = dict(opts)
821
607
    dry = '-n' in opts or '--dry' in opts
822
608
    nojail = '--nojail' in opts
823
 
    nosubjects = '--nosubjects' in opts
824
609
 
825
610
    if dry:
826
611
        print "Dry run (no actions will be executed\n"
840
625
    # chown trampoline to root and set setuid bit
841
626
    action_chown_setuid(tramppath, dry)
842
627
 
843
 
    # Copy the www and lib directories using the list
 
628
    # Copy the www directory using the list
844
629
    action_copylist(install_list.list_www, ivle_install_dir, dry)
845
 
    action_copylist(install_list.list_lib, ivle_install_dir, dry)
846
630
 
847
631
    if not nojail:
848
632
        # Copy the local jail directory built by the build action
849
633
        # to the jails template directory (it will be used as a template
850
634
        # for all the students' jails).
851
635
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
852
 
    if not nosubjects:
853
 
        # Copy the subjects and problems directories across
854
 
        action_copylist(install_list.list_subjects, subjects_base, dry,
855
 
            srcdir="./subjects")
856
 
        action_copylist(install_list.list_problems, problems_base, dry,
857
 
            srcdir="./problems")
858
636
 
859
637
    # Append IVLE path to ivle.pth in python site packages
860
638
    # (Unless it's already there)
861
639
    ivle_pth = os.path.join(sys.prefix,
862
 
        "lib/python%s/site-packages/ivle.pth" % PYTHON_VERSION)
 
640
        "lib/python2.5/site-packages/ivle.pth")
863
641
    ivle_www = os.path.join(ivle_install_dir, "www")
864
 
    ivle_lib = os.path.join(ivle_install_dir, "lib")
865
642
    write_ivle_pth = True
866
 
    write_ivle_lib_pth = True
867
643
    try:
868
644
        file = open(ivle_pth, 'r')
869
645
        for line in file:
870
646
            if line.strip() == ivle_www:
871
647
                write_ivle_pth = False
872
 
            elif line.strip() == ivle_lib:
873
 
                write_ivle_lib_pth = False
874
 
        file.close()
 
648
                break
875
649
    except (IOError, OSError):
876
650
        pass
877
651
    if write_ivle_pth:
878
652
        action_append(ivle_pth, ivle_www)
879
 
    if write_ivle_lib_pth:
880
 
        action_append(ivle_pth, ivle_lib)
881
653
 
882
654
    return 0
883
655
 
945
717
    if ret != 0:
946
718
        raise RunError(prog, ret)
947
719
 
948
 
def action_remove(path, dry):
949
 
    """Calls rmtree, deleting the target file if it exists."""
950
 
    try:
951
 
        print "rm -r", path
952
 
        if not dry:
953
 
            shutil.rmtree(path, True)
954
 
    except OSError, (err, msg):
955
 
        if err != errno.EEXIST:
956
 
            raise
957
 
        # Otherwise, didn't exist, so we don't care
958
 
 
959
720
def action_rename(src, dst, dry):
960
721
    """Calls rename. Deletes the target if it already exists."""
961
 
    action_remove(dst, dry)
 
722
    if os.access(dst, os.F_OK):
 
723
        print "rm -r", dst
 
724
        if not dry:
 
725
            shutil.rmtree(dst, True)
962
726
    print "mv ", src, dst
963
727
    if dry: return
964
728
    try:
984
748
    directories as necessary.
985
749
 
986
750
    See shutil.copytree."""
987
 
    action_remove(dst, dry)
 
751
    if os.access(dst, os.F_OK):
 
752
        print "rm -r", dst
 
753
        if not dry:
 
754
            shutil.rmtree(dst, True)
988
755
    print "cp -r", src, dst
989
756
    if dry: return
990
757
    shutil.copytree(src, dst, True)
993
760
    """Hard-links an entire directory tree. Same as copytree but the created
994
761
    files are hard-links not actual copies. Removes the existing destination.
995
762
    """
996
 
    action_remove(dst, dry)
 
763
    if os.access(dst, os.F_OK):
 
764
        print "rm -r", dst
 
765
        if not dry:
 
766
            shutil.rmtree(dst, True)
997
767
    print "<cp with hardlinks> -r", src, dst
998
768
    if dry: return
999
769
    common.makeuser.linktree(src, dst)
1000
770
 
1001
 
def action_copylist(srclist, dst, dry, srcdir="."):
 
771
def action_copylist(srclist, dst, dry):
1002
772
    """Copies all files in a list to a new location. The files in the list
1003
773
    are read relative to the current directory, and their destinations are the
1004
774
    same paths relative to dst. Creates all parent directories as necessary.
1005
 
    srcdir is "." by default, can be overridden.
1006
775
    """
1007
776
    for srcfile in srclist:
1008
777
        dstfile = os.path.join(dst, srcfile)
1009
 
        srcfile = os.path.join(srcdir, srcfile)
1010
778
        dstdir = os.path.split(dstfile)[0]
1011
779
        if not os.path.isdir(dstdir):
1012
780
            action_mkdir(dstdir, dry)
1021
789
def action_copyfile(src, dst, dry):
1022
790
    """Copies one file to a new location. Creates all parent directories
1023
791
    as necessary.
1024
 
    Warn if file not found.
1025
792
    """
1026
793
    dstdir = os.path.split(dst)[0]
1027
794
    if not os.path.isdir(dstdir):
1031
798
        try:
1032
799
            shutil.copyfile(src, dst)
1033
800
            shutil.copymode(src, dst)
1034
 
        except (shutil.Error, IOError), e:
1035
 
            print "Warning: " + str(e)
 
801
        except shutil.Error:
 
802
            pass
1036
803
 
1037
804
def action_symlink(src, dst, dry):
1038
805
    """Creates a symlink in a given location. Creates all parent directories
1068
835
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
1069
836
 
1070
837
def action_chmod_x(file, dry):
1071
 
    """Chmod 755 a file (sets permissions to rwxr-xr-x)."""
1072
 
    print "chmod 755", file
 
838
    """Chmod +xs a file (sets execute permission)."""
 
839
    print "chmod u+rwx", file
1073
840
    if not dry:
1074
 
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR
1075
 
            | stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH)
 
841
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
1076
842
 
1077
843
def query_user(default, prompt):
1078
844
    """Prompts the user for a string, which is read from a line of stdin.