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

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: mattgiuca
  • Date: 2008-01-11 08:44:39 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:188
browser.js: Can now (shakily) handle directory listings. (lots of code!)
Minor changes to HTML, CSS, and util.js.

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.
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(), 'www'))
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 matplotlib
104
 
    '/usr/lib/i686/cmov/libssl.so.0.9.8',
105
 
    '/usr/lib/i686/cmov/libcrypto.so.0.9.8',
106
 
    '/lib/tls/i686/cmov/libnsl.so.1',
107
 
    '/usr/lib/libz.so.1',
108
 
    '/usr/lib/atlas/liblapack.so.3',
109
 
    '/usr/lib/atlas/libblas.so.3',
110
 
    '/usr/lib/libg2c.so.0',
111
 
    '/usr/lib/libstdc++.so.6',
112
 
    '/usr/lib/libfreetype.so.6',
113
 
    '/usr/lib/libpng12.so.0',
114
 
    '/usr/lib/libBLT.2.4.so.8.4',
115
 
    '/usr/lib/libtk8.4.so.0',
116
 
    '/usr/lib/libtcl8.4.so.0',
117
 
    '/usr/lib/tcl8.4/init.tcl',
118
 
    '/usr/lib/libX11.so.6',
119
 
    '/usr/lib/libXau.so.6',
120
 
    '/usr/lib/libXdmcp.so.6',
121
 
    '/lib/libgcc_s.so.1',
122
 
    '/etc/matplotlibrc',
123
 
]
124
 
# Symlinks to make within the jail. Src mapped to dst.
125
 
JAIL_LINKS = {
126
 
    'python%s' % PYTHON_VERSION: 'jail/usr/bin/python',
127
 
}
128
 
# Trees to copy. Src mapped to dst (these will be passed to action_copytree).
129
 
JAIL_COPYTREES = {
130
 
    '/usr/lib/python%s' % PYTHON_VERSION:
131
 
        'jail/usr/lib/python%s' % PYTHON_VERSION,
132
 
    '/usr/share/matplotlib': 'jail/usr/share/matplotlib',
133
 
    '/etc/ld.so.conf.d': 'jail/etc/ld.so.conf.d',
134
 
}
135
 
 
136
 
class ConfigOption:
137
 
    """A configuration option; one of the things written to conf.py."""
138
 
    def __init__(self, option_name, default, prompt, comment):
139
 
        """Creates a configuration option.
140
 
        option_name: Name of the variable in conf.py. Also name of the
141
 
            command-line argument to setup.py conf.
142
 
        default: Default value for this variable.
143
 
        prompt: (Short) string presented during the interactive prompt in
144
 
            setup.py conf.
145
 
        comment: (Long) comment string stored in conf.py. Each line of this
146
 
            string should begin with a '#'.
147
 
        """
148
 
        self.option_name = option_name
149
 
        self.default = default
150
 
        self.prompt = prompt
151
 
        self.comment = comment
152
 
 
153
 
# Configuration options, defaults and descriptions
154
 
config_options = []
155
 
config_options.append(ConfigOption("root_dir", "/ivle",
156
 
    """Root directory where IVLE is located (in URL space):""",
157
 
    """
158
 
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
159
 
# with this).
160
 
# eg. "/" or "/ivle"."""))
161
 
config_options.append(ConfigOption("ivle_install_dir", "/opt/ivle",
162
 
    'Root directory where IVLE will be installed (on the local file '
163
 
    'system):',
164
 
    """
165
 
# In the local file system, where IVLE is actually installed.
166
 
# This directory should contain the "www" and "bin" directories."""))
167
 
config_options.append(ConfigOption("jail_base", "/home/informatics/jails",
168
 
    """Root directory where the jails (containing user files) are stored
169
 
(on the local file system):""",
170
 
    """
171
 
# In the local file system, where are the student/user file spaces located.
172
 
# The user jails are expected to be located immediately in subdirectories of
173
 
# this location."""))
174
 
config_options.append(ConfigOption("subjects_base",
175
 
    "/home/informatics/subjects",
176
 
    """Root directory where the subject directories (containing worksheets
177
 
and other per-subject files) are stored (on the local file system):""",
178
 
    """
179
 
# In the local file system, where are the per-subject file spaces located.
180
 
# The individual subject directories are expected to be located immediately
181
 
# in subdirectories of this location."""))
182
 
config_options.append(ConfigOption("public_host", "public.localhost",
183
 
    """Hostname which will cause the server to go into "public mode",
184
 
providing login-free access to student's published work:""",
185
 
    """
186
 
# The server goes into "public mode" if the browser sends a request with this
187
 
# host. This is for security reasons - we only serve public student files on a
188
 
# separate domain to the main IVLE site.
189
 
# Public mode does not use cookies, and serves only public content.
190
 
# Private mode (normal mode) requires login, and only serves files relevant to
191
 
# the logged-in user."""))
192
 
config_options.append(ConfigOption("allowed_uids", "33",
193
 
    """UID of the web server process which will run IVLE.
194
 
Only this user may execute the trampoline. May specify multiple users as
195
 
a comma-separated list.
196
 
    (eg. "1002,78")""",
197
 
    """
198
 
# The User-ID of the web server process which will run IVLE, and any other
199
 
# users who are allowed to run the trampoline. This is stores as a string of
200
 
# comma-separated integers, simply because it is not used within Python, only
201
 
# used by the setup program to write to conf.h (see setup.py config)."""))
202
 
config_options.append(ConfigOption("db_host", "localhost",
203
 
    """PostgreSQL Database config
204
 
==========================
205
 
Hostname of the DB server:""",
206
 
    """
207
 
### PostgreSQL Database config ###
208
 
# Database server hostname"""))
209
 
config_options.append(ConfigOption("db_port", "5432",
210
 
    """Port of the DB server:""",
211
 
    """
212
 
# Database server port"""))
213
 
config_options.append(ConfigOption("db_dbname", "ivle",
214
 
    """Database name:""",
215
 
    """
216
 
# Database name"""))
217
 
config_options.append(ConfigOption("db_user", "postgres",
218
 
    """Username for DB server login:""",
219
 
    """
220
 
# Database username"""))
221
 
config_options.append(ConfigOption("db_password", "",
222
 
    """Password for DB server login:
223
 
    (Caution: This password is stored in plaintext in www/conf/conf.py)""",
224
 
    """
225
 
# Database password"""))
226
 
 
227
71
# Try importing existing conf, but if we can't just set up defaults
228
72
# The reason for this is that these settings are used by other phases
229
73
# of setup besides conf, so we need to know them.
230
74
# Also this allows you to hit Return to accept the existing value.
231
75
try:
232
76
    confmodule = __import__("www/conf/conf")
233
 
    for opt in config_options:
234
 
        try:
235
 
            globals()[opt.option_name] = confmodule.__dict__[opt.option_name]
236
 
        except:
237
 
            globals()[opt.option_name] = opt.default
 
77
    root_dir = confmodule.root_dir
 
78
    ivle_install_dir = confmodule.ivle_install_dir
 
79
    jail_base = confmodule.jail_base
238
80
except ImportError:
239
81
    # Just set reasonable defaults
240
 
    for opt in config_options:
241
 
        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"
242
87
 
243
88
# Try importing install_list, but don't fail if we can't, because listmake can
244
89
# function without it.
253
98
# as necessary, and include it in the distribution.
254
99
listmake_mimetypes = ['text/x-python', 'text/html',
255
100
    'application/x-javascript', 'application/javascript',
256
 
    'text/css', 'image/png', 'application/xml']
 
101
    'text/css', 'image/png']
257
102
 
258
103
# Main function skeleton from Guido van Rossum
259
104
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
282
127
        return 1
283
128
 
284
129
    # Disallow run as root unless installing
285
 
    if (operation != 'install' and operation != 'updatejails'
286
 
        and os.geteuid() == 0):
 
130
    if operation != 'install' and os.geteuid() == 0:
287
131
        print >>sys.stderr, "I do not want to run this stage as root."
288
132
        print >>sys.stderr, "Please run as a normal user."
289
133
        return 1
291
135
    try:
292
136
        oper_func = {
293
137
            'help' : help,
294
 
            'config' : conf,
 
138
            'conf' : conf,
295
139
            'build' : build,
296
140
            'listmake' : listmake,
297
141
            'install' : install,
298
 
            'updatejails' : updatejails,
299
142
        }[operation]
300
143
    except KeyError:
301
144
        print >>sys.stderr, (
312
155
Operation (and args) can be:
313
156
    help [operation]
314
157
    listmake (developer use only)
315
 
    config [args]
 
158
    conf [args]
316
159
    build
317
 
    install [--nojail] [--nosubjects] [-n|--dry]
 
160
    install [--nojail] [-n|--dry]
318
161
"""
319
162
        return 1
320
163
    elif len(args) != 1:
333
176
be copied upon installation. This should be run by the developer before
334
177
cutting a distribution, and the listfile it generates should be included in
335
178
the distribution, avoiding the administrator having to run it."""
336
 
    elif operation == 'config':
337
 
        print """python setup.py config [args]
 
179
    elif operation == 'conf':
 
180
        print """python setup.py conf [args]
338
181
Configures IVLE with machine-specific details, most notably, various paths.
339
182
Either prompts the administrator for these details or accepts them as
340
183
command-line args. Will be interactive only if there are no arguments given.
346
189
 
347
190
Creates www/conf/conf.py and trampoline/conf.h.
348
191
 
349
 
Args are:"""
350
 
        for opt in config_options:
351
 
            print "    --" + opt.option_name
352
 
        print """As explained in the interactive prompt or conf.py.
 
192
Args are:
 
193
    --root_dir
 
194
    --ivle_install_dir
 
195
    --jail_base
 
196
    --allowed_uids
 
197
As explained in the interactive prompt or conf.py.
353
198
"""
354
199
    elif operation == 'build':
355
200
        print """python -O setup.py build [--dry|-n]
366
211
 
367
212
--dry | -n  Print out the actions but don't do anything."""
368
213
    elif operation == 'install':
369
 
        print """sudo python setup.py install [--nojail] [--nosubjects][--dry|-n]
 
214
        print """sudo python setup.py install [--nojail] [--dry|-n]
370
215
(Requires root)
371
216
Create target install directory ($target).
372
217
Create $target/bin.
374
219
chown and chmod the installed trampoline.
375
220
Copy www/ to $target.
376
221
Copy jail/ to jails template directory (unless --nojail specified).
377
 
Copy subjects/ to subjects directory (unless --nosubjects specified).
378
 
 
379
 
--nojail        Do not copy the jail.
380
 
--nosubjects    Do not copy the subjects.
381
 
--dry | -n  Print out the actions but don't do anything."""
382
 
    elif operation == 'updatejails':
383
 
        print """sudo python setup.py updatejails [--dry|-n]
384
 
(Requires root)
385
 
Copy jail/ to each subdirectory in jails directory.
386
 
 
 
222
 
 
223
--nojail    Do not copy the jail.
387
224
--dry | -n  Print out the actions but don't do anything."""
388
225
    else:
389
226
        print >>sys.stderr, (
395
232
    # We build two separate lists, by walking www and console
396
233
    list_www = build_list_py_files('www')
397
234
    list_console = build_list_py_files('console')
398
 
    list_subjects = build_list_py_files('subjects', no_top_level=True)
399
235
    # Make sure that the files generated by conf are in the list
400
236
    # (since listmake is typically run before conf)
401
237
    if "www/conf/conf.py" not in list_www:
425
261
# List of all installable files in console directory.
426
262
list_console = """)
427
263
        writelist_pretty(file, list_console)
428
 
        file.write("""
429
 
# List of all installable files in subjects directory.
430
 
# This is to install sample subjects and material.
431
 
list_subjects = """)
432
 
        writelist_pretty(file, list_subjects)
433
264
 
434
265
        file.close()
435
266
    except IOError, (errno, strerror):
446
277
 
447
278
    return 0
448
279
 
449
 
def build_list_py_files(dir, no_top_level=False):
 
280
def build_list_py_files(dir):
450
281
    """Builds a list of all py files found in a directory and its
451
 
    subdirectories. Returns this as a list of strings.
452
 
    no_top_level=True means the file paths will not include the top-level
453
 
    directory.
454
 
    """
 
282
    subdirectories. Returns this as a list of strings."""
455
283
    pylist = []
456
284
    for (dirpath, dirnames, filenames) in os.walk(dir):
457
285
        # Exclude directories beginning with a '.' (such as '.svn')
459
287
        # All *.py files are added to the list
460
288
        pylist += [os.path.join(dirpath, item) for item in filenames
461
289
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
462
 
    if no_top_level:
463
 
        for i in range(0, len(pylist)):
464
 
            _, pylist[i] = pylist[i].split(os.sep, 1)
465
290
    return pylist
466
291
 
467
292
def writelist_pretty(file, list):
475
300
        file.write(']\n')
476
301
 
477
302
def conf(args):
478
 
    global db_port
 
303
    global root_dir, ivle_install_dir, jail_base, allowed_uids
479
304
    # Set up some variables
480
305
 
481
306
    cwd = os.getcwd()
483
308
    conffile = os.path.join(cwd, "www/conf/conf.py")
484
309
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
485
310
 
 
311
    # Fixed config options that we don't ask the admin
 
312
    default_app = "dummy"
 
313
 
486
314
    # Get command-line arguments to avoid asking questions.
487
315
 
488
 
    optnames = []
489
 
    for opt in config_options:
490
 
        optnames.append(opt.option_name + "=")
491
 
    (opts, args) = getopt.gnu_getopt(args, "", optnames)
 
316
    (opts, args) = getopt.gnu_getopt(args, "", ['root_dir=',
 
317
                    'ivle_install_dir=', 'jail_base=', 'allowed_uids='])
492
318
 
493
319
    if args != []:
494
320
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
510
336
        # If EOF is encountered at any time during the questioning, just exit
511
337
        # silently
512
338
 
513
 
        for opt in config_options:
514
 
            globals()[opt.option_name] = \
515
 
                query_user(globals()[opt.option_name], opt.prompt)
 
339
        root_dir = query_user(root_dir,
 
340
        """Root directory where IVLE is located (in URL space):""")
 
341
        ivle_install_dir = query_user(ivle_install_dir,
 
342
        'Root directory where IVLE will be installed (on the local file '
 
343
        'system):')
 
344
        jail_base = query_user(jail_base,
 
345
        """Root directory where the jails (containing user files) are stored
 
346
(on the local file system):""")
 
347
        allowed_uids = query_user(allowed_uids,
 
348
        """UID of the web server process which will run IVLE.
 
349
Only this user may execute the trampoline. May specify multiple users as
 
350
a comma-separated list.
 
351
    (eg. "1002,78")""")
 
352
 
516
353
    else:
517
354
        opts = dict(opts)
518
355
        # Non-interactive mode. Parse the options.
519
 
        for opt in config_options:
520
 
            if '--' + opt.option_name in opts:
521
 
                globals()[opt.option_name] = opts['--' + opt.option_name]
 
356
        if '--root_dir' in opts:
 
357
            root_dir = opts['--root_dir']
 
358
        if '--ivle_install_dir' in opts:
 
359
            ivle_install_dir = opts['--ivle_install_dir']
 
360
        if '--jail_base' in opts:
 
361
            jail_base = opts['--jail_base']
 
362
        if '--allowed_uids' in opts:
 
363
            allowed_uids = opts['--allowed_uids']
522
364
 
523
365
    # Error handling on input values
524
366
    try:
525
 
        allowed_uids_list = map(int, allowed_uids.split(','))
 
367
        allowed_uids = map(int, allowed_uids.split(','))
526
368
    except ValueError:
527
369
        print >>sys.stderr, (
528
370
        "Invalid UID list (%s).\n"
529
371
        "Must be a comma-separated list of integers." % allowed_uids)
530
372
        return 1
531
 
    try:
532
 
        db_port = int(db_port)
533
 
        if db_port < 0 or db_port >= 65536: raise ValueError()
534
 
    except ValueError:
535
 
        print >>sys.stderr, (
536
 
        "Invalid DB port (%s).\n"
537
 
        "Must be an integer between 0 and 65535." % repr(db_port))
538
 
        return 1
539
373
 
540
374
    # Write www/conf/conf.py
541
375
 
546
380
# conf.py
547
381
# Miscellaneous application settings
548
382
 
549
 
""")
550
 
        for opt in config_options:
551
 
            conf.write('%s\n%s = %s\n' % (opt.comment, opt.option_name,
552
 
                repr(globals()[opt.option_name])))
 
383
 
 
384
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
 
385
# with this).
 
386
# eg. "/" or "/ivle".
 
387
root_dir = "%s"
 
388
 
 
389
# In the local file system, where IVLE is actually installed.
 
390
# This directory should contain the "www" and "bin" directories.
 
391
ivle_install_dir = "%s"
 
392
 
 
393
# In the local file system, where are the student/user file spaces located.
 
394
# The user jails are expected to be located immediately in subdirectories of
 
395
# this location.
 
396
jail_base = "%s"
 
397
 
 
398
# Which application to load by default (if the user navigates to the top level
 
399
# of the site). This is the app's URL name.
 
400
# Note that if this app requires authentication, the user will first be
 
401
# presented with the login screen.
 
402
default_app = "%s"
 
403
""" % (root_dir, ivle_install_dir, jail_base, default_app))
553
404
 
554
405
        conf.close()
555
406
    except IOError, (errno, strerror):
581
432
 * (Note that root is an implicit member of this list).
582
433
 */
583
434
static const int allowed_uids[] = { %s };
584
 
""" % (jail_base, repr(allowed_uids_list)[1:-1]))
 
435
""" % (jail_base, repr(allowed_uids)[1:-1]))
585
436
 
586
437
        conf.close()
587
438
    except IOError, (errno, strerror):
607
458
        print "Dry run (no actions will be executed\n"
608
459
 
609
460
    # Compile the trampoline
610
 
    curdir = os.getcwd()
611
 
    os.chdir('trampoline')
612
 
    action_runprog('make', [], dry)
613
 
    os.chdir(curdir)
 
461
    action_runprog('gcc', ['-Wall', '-o', 'trampoline/trampoline',
 
462
        'trampoline/trampoline.c'], dry)
614
463
 
615
464
    # Create the jail and its subdirectories
616
465
    # Note: Other subdirs will be made by copying files
621
470
    # Copy all console and operating system files into the jail
622
471
    action_copylist(install_list.list_console, 'jail/opt/ivle', dry)
623
472
    copy_os_files_jail(dry)
624
 
    # Chmod the python console
625
 
    action_chmod_x('jail/opt/ivle/console/python-console', dry)
626
 
    
627
473
 
628
474
    # Compile .py files into .pyc or .pyo files
629
475
    compileall.compile_dir('www', quiet=True)
635
481
    """Copies necessary Operating System files from their usual locations
636
482
    into the jail/ directory of the cwd."""
637
483
    # Currently source paths are configured for Ubuntu.
638
 
    for filename in JAIL_FILES:
639
 
        copy_file_to_jail(filename, dry)
640
 
    for src, dst in JAIL_LINKS.items():
641
 
        action_symlink(src, dst, dry)
642
 
    for src, dst in JAIL_COPYTREES.items():
643
 
        action_copytree(src, dst, dry)
 
484
    copy_file_to_jail('/lib/ld-linux.so.2', dry)
 
485
    copy_file_to_jail('/lib/tls/i686/cmov/libc.so.6', dry)
 
486
    copy_file_to_jail('/lib/tls/i686/cmov/libdl.so.2', dry)
 
487
    copy_file_to_jail('/lib/tls/i686/cmov/libm.so.6', dry)
 
488
    copy_file_to_jail('/lib/tls/i686/cmov/libpthread.so.0', dry)
 
489
    copy_file_to_jail('/lib/tls/i686/cmov/libutil.so.1', dry)
 
490
    copy_file_to_jail('/usr/bin/python2.5', dry)
 
491
    action_symlink('python2.5', 'jail/usr/bin/python', dry)
 
492
    action_copytree('/usr/lib/python2.5', 'jail/usr/lib/python2.5', dry)
644
493
 
645
494
def copy_file_to_jail(src, dry):
646
495
    """Copies a single file from an absolute location into the same location
650
499
 
651
500
def install(args):
652
501
    # Get "dry" and "nojail" variables from command line
653
 
    (opts, args) = getopt.gnu_getopt(args, "n",
654
 
        ['dry', 'nojail', 'nosubjects'])
 
502
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry', 'nojail'])
655
503
    opts = dict(opts)
656
504
    dry = '-n' in opts or '--dry' in opts
657
505
    nojail = '--nojail' in opts
658
 
    nosubjects = '--nosubjects' in opts
659
506
 
660
507
    if dry:
661
508
        print "Dry run (no actions will be executed\n"
683
530
        # to the jails template directory (it will be used as a template
684
531
        # for all the students' jails).
685
532
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
686
 
    if not nosubjects:
687
 
        # Copy the subjects directory across
688
 
        action_copylist(install_list.list_subjects, subjects_base, dry,
689
 
            srcdir="./subjects")
690
 
 
691
 
    # Append IVLE path to ivle.pth in python site packages
692
 
    # (Unless it's already there)
693
 
    ivle_pth = os.path.join(sys.prefix,
694
 
        "lib/python%s/site-packages/ivle.pth" % PYTHON_VERSION)
695
 
    ivle_www = os.path.join(ivle_install_dir, "www")
696
 
    write_ivle_pth = True
697
 
    try:
698
 
        file = open(ivle_pth, 'r')
699
 
        for line in file:
700
 
            if line.strip() == ivle_www:
701
 
                write_ivle_pth = False
702
 
                break
703
 
    except (IOError, OSError):
704
 
        pass
705
 
    if write_ivle_pth:
706
 
        action_append(ivle_pth, ivle_www)
707
 
 
708
 
    return 0
709
 
 
710
 
def updatejails(args):
711
 
    # Get "dry" variable from command line
712
 
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
713
 
    opts = dict(opts)
714
 
    dry = '-n' in opts or '--dry' in opts
715
 
 
716
 
    if dry:
717
 
        print "Dry run (no actions will be executed\n"
718
 
 
719
 
    if not dry and os.geteuid() != 0:
720
 
        print >>sys.stderr, "Must be root to run install"
721
 
        print >>sys.stderr, "(I need to chown some files)."
722
 
        return 1
723
 
 
724
 
    # Update the template jail directory in case it hasn't been installed
725
 
    # recently.
726
 
    action_copytree('jail', os.path.join(jail_base, 'template'), dry)
727
 
 
728
 
    # Re-link all the files in all students jails.
729
 
    for dir in os.listdir(jail_base):
730
 
        if dir == 'template': continue
731
 
        # First back up the student's home directory
732
 
        temp_home = os.tmpnam()
733
 
        action_rename(os.path.join(jail_base, dir, 'home'), temp_home, dry)
734
 
        # Delete the student's jail and relink the jail files
735
 
        action_linktree(os.path.join(jail_base, 'template'),
736
 
            os.path.join(jail_base, dir), dry)
737
 
        # Restore the student's home directory
738
 
        action_rename(temp_home, os.path.join(jail_base, dir, 'home'), dry)
739
 
        # Set up the user's home directory just in case they don't have a
740
 
        # directory for this yet
741
 
        action_mkdir(os.path.join(jail_base, dir, 'home', dir), dry)
742
533
 
743
534
    return 0
744
535
 
771
562
    if ret != 0:
772
563
        raise RunError(prog, ret)
773
564
 
774
 
def action_rename(src, dst, dry):
775
 
    """Calls rename. Deletes the target if it already exists."""
776
 
    if os.access(dst, os.F_OK):
777
 
        print "rm -r", dst
778
 
        if not dry:
779
 
            shutil.rmtree(dst, True)
780
 
    print "mv ", src, dst
781
 
    if dry: return
782
 
    try:
783
 
        os.rename(src, dst)
784
 
    except OSError, (err, msg):
785
 
        if err != errno.EEXIST:
786
 
            raise
787
 
 
788
565
def action_mkdir(path, dry):
789
566
    """Calls mkdir. Silently ignored if the directory already exists.
790
567
    Creates all parent directories as necessary."""
810
587
    if dry: return
811
588
    shutil.copytree(src, dst, True)
812
589
 
813
 
def action_linktree(src, dst, dry):
814
 
    """Hard-links an entire directory tree. Same as copytree but the created
815
 
    files are hard-links not actual copies. Removes the existing destination.
816
 
    """
817
 
    if os.access(dst, os.F_OK):
818
 
        print "rm -r", dst
819
 
        if not dry:
820
 
            shutil.rmtree(dst, True)
821
 
    print "<cp with hardlinks> -r", src, dst
822
 
    if dry: return
823
 
    common.makeuser.linktree(src, dst)
824
 
 
825
 
def action_copylist(srclist, dst, dry, srcdir="."):
 
590
def action_copylist(srclist, dst, dry):
826
591
    """Copies all files in a list to a new location. The files in the list
827
592
    are read relative to the current directory, and their destinations are the
828
593
    same paths relative to dst. Creates all parent directories as necessary.
829
 
    srcdir is "." by default, can be overridden.
830
594
    """
831
595
    for srcfile in srclist:
832
596
        dstfile = os.path.join(dst, srcfile)
833
 
        srcfile = os.path.join(srcdir, srcfile)
834
597
        dstdir = os.path.split(dstfile)[0]
835
598
        if not os.path.isdir(dstdir):
836
599
            action_mkdir(dstdir, dry)
845
608
def action_copyfile(src, dst, dry):
846
609
    """Copies one file to a new location. Creates all parent directories
847
610
    as necessary.
848
 
    Warn if file not found.
849
611
    """
850
612
    dstdir = os.path.split(dst)[0]
851
613
    if not os.path.isdir(dstdir):
855
617
        try:
856
618
            shutil.copyfile(src, dst)
857
619
            shutil.copymode(src, dst)
858
 
        except (shutil.Error, IOError), e:
859
 
            print "Warning: " + str(e)
 
620
        except shutil.Error:
 
621
            pass
860
622
 
861
623
def action_symlink(src, dst, dry):
862
624
    """Creates a symlink in a given location. Creates all parent directories
872
634
    if not dry:
873
635
        os.symlink(src, dst)
874
636
 
875
 
def action_append(ivle_pth, ivle_www):
876
 
    file = open(ivle_pth, 'a+')
877
 
    file.write(ivle_www + '\n')
878
 
    file.close()
879
 
 
880
637
def action_chown_setuid(file, dry):
881
638
    """Chowns a file to root, and sets the setuid bit on the file.
882
639
    Calling this function requires the euid to be root.
891
648
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
892
649
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
893
650
 
894
 
def action_chmod_x(file, dry):
895
 
    """Chmod +xs a file (sets execute permission)."""
896
 
    print "chmod u+rwx", file
897
 
    if not dry:
898
 
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
899
 
 
900
651
def query_user(default, prompt):
901
652
    """Prompts the user for a string, which is read from a line of stdin.
902
653
    Exits silently if EOF is encountered. Returns the string, with spaces