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

« back to all changes in this revision

Viewing changes to setup.py

Add tabs to the new framework. Move the app icons into the apps themselves.

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
# Date:   12/12/2007
22
22
 
23
23
# This is a command-line application, for use by the administrator.
24
 
# This program configures, builds and installs IVLE in three separate steps.
 
24
# This program is a frontend for the modules in the setup packages that 
 
25
# configure, build and install IVLE in three separate steps.
25
26
# It is called with at least one argument, which specifies which operation to
26
27
# take.
27
28
 
28
 
# setup.py listmake (for developer use only)
29
 
# Recurses through the source tree and builds a list of all files which should
30
 
# be copied upon installation. This should be run by the developer before
31
 
# cutting a distribution, and the listfile it generates should be included in
32
 
# the distribution, avoiding the administrator having to run it.
33
 
 
34
 
# setup.py conf [args]
35
 
# Configures IVLE with machine-specific details, most notably, various paths.
36
 
# Either prompts the administrator for these details or accepts them as
37
 
# command-line args.
38
 
# Creates www/conf/conf.py and trampoline/conf.h.
39
 
 
40
 
# setup.py build
41
 
# Compiles all files and sets up a jail template in the source directory.
42
 
# Details:
43
 
# Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
44
 
# Creates jail/.
45
 
# Creates standard subdirs inside the jail, eg bin, opt, home, tmp.
46
 
# Copies console/ to a location within the jail.
47
 
# Copies OS programs and files to corresponding locations within the jail
48
 
#   (eg. python and Python libs, ld.so, etc).
49
 
# Generates .pyc files for all the IVLE .py files.
50
 
 
51
 
# setup.py install [--nojail] [--dry|n]
52
 
# (Requires root)
53
 
# Create target install directory ($target).
54
 
# Create $target/bin.
55
 
# Copy trampoline/trampoline to $target/bin.
56
 
# chown and chmod the installed trampoline.
57
 
# Copy www/ to $target.
58
 
# Copy jail/ to jails template directory (unless --nojail specified).
59
 
 
60
 
import os
61
 
import stat
62
 
import shutil
63
29
import sys
64
 
import getopt
65
 
import string
66
 
import errno
67
 
import mimetypes
68
 
import compileall
69
 
import getopt
70
 
 
71
 
# Try importing existing conf, but if we can't just set up defaults
72
 
# The reason for this is that these settings are used by other phases
73
 
# of setup besides conf, so we need to know them.
74
 
# Also this allows you to hit Return to accept the existing value.
75
 
try:
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
80
 
except ImportError:
81
 
    # Just set reasonable defaults
82
 
    root_dir = "/ivle"
83
 
    ivle_install_dir = "/opt/ivle"
84
 
    jail_base = "/home/informatics/jails"
85
 
# Always defaults
86
 
allowed_uids = "0"
87
 
 
88
 
# Try importing install_list, but don't fail if we can't, because listmake can
89
 
# function without it.
90
 
try:
91
 
    import install_list
92
 
except:
93
 
    pass
94
 
 
95
 
# Mime types which will automatically be placed in the list by listmake.
96
 
# Note that listmake is not intended to be run by the final user (the system
97
 
# administrator who installs this), so the developers can customize the list
98
 
# as necessary, and include it in the distribution.
99
 
listmake_mimetypes = ['text/x-python', 'text/html',
100
 
    'application/x-javascript', 'application/javascript',
101
 
    'text/css', 'image/png']
102
 
 
103
 
# Main function skeleton from Guido van Rossum
104
 
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
 
30
import setup.configure
 
31
import setup.build
 
32
import setup.install
 
33
 
105
34
 
106
35
def main(argv=None):
107
36
    if argv is None:
110
39
    # Print the opening spiel including the GPL notice
111
40
 
112
41
    print """IVLE - Informatics Virtual Learning Environment Setup
113
 
Copyright (C) 2007-2008 The University of Melbourne
 
42
Copyright (C) 2007-2009 The University of Melbourne
114
43
IVLE comes with ABSOLUTELY NO WARRANTY.
115
44
This is free software, and you are welcome to redistribute it
116
45
under certain conditions. See LICENSE.txt for details.
126
55
        help([])
127
56
        return 1
128
57
 
 
58
    oper_func = call_operator(operation)
 
59
    return oper_func(argv[2:])
 
60
 
 
61
def help(args):
 
62
    if len(args)!=1:
 
63
        print """Usage: python setup.py operation [options]
 
64
Operation can be:
 
65
    help [operation]
 
66
    config
 
67
    build
 
68
    install
 
69
 
 
70
    For help and options for a specific operation use 'help [operation]'."""
 
71
    else:
 
72
        operator = args[0]
 
73
        oper_func = call_operator(operator)
 
74
        oper_func(['operator','--help'])
 
75
 
 
76
def call_operator(operation):
129
77
    # Call the requested operation's function
130
78
    try:
131
79
        oper_func = {
132
80
            'help' : help,
133
 
            'conf' : conf,
134
 
            'build' : build,
135
 
            'listmake' : listmake,
136
 
            'install' : install,
 
81
            'config' : setup.configure.configure,
 
82
            'build' : setup.build.build,
 
83
            'install' : setup.install.install,
 
84
            #'updatejails' : None,
137
85
        }[operation]
138
86
    except KeyError:
139
87
        print >>sys.stderr, (
140
88
            """Invalid operation '%s'. Try python setup.py help."""
141
89
            % operation)
142
 
    return oper_func(argv[2:])
143
 
 
144
 
# Operation functions
145
 
 
146
 
def help(args):
147
 
    if args == []:
148
 
        print """Usage: python setup.py operation [args]
149
 
Operation (and args) can be:
150
 
    help [operation]
151
 
    conf [args]
152
 
    build
153
 
    install [--nojail] [-n|--dry]
154
 
"""
155
 
        return 1
156
 
    elif len(args) != 1:
157
 
        print """Usage: python setup.py help [operation]"""
158
 
        return 2
159
 
    else:
160
 
        operation = args[0]
161
 
 
162
 
    if operation == 'help':
163
 
        print """python setup.py help [operation]
164
 
Prints the usage message or detailed help on an operation, then exits."""
165
 
    elif operation == 'listmake':
166
 
        print """python setup.py listmake
167
 
(For developer use only)
168
 
Recurses through the source tree and builds a list of all files which should
169
 
be copied upon installation. This should be run by the developer before
170
 
cutting a distribution, and the listfile it generates should be included in
171
 
the distribution, avoiding the administrator having to run it."""
172
 
    elif operation == 'conf':
173
 
        print """python setup.py conf [args]
174
 
Configures IVLE with machine-specific details, most notably, various paths.
175
 
Either prompts the administrator for these details or accepts them as
176
 
command-line args. Will be interactive only if there are no arguments given.
177
 
Takes defaults from existing conf file if it exists.
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.
185
 
"""
186
 
    elif operation == 'build':
187
 
        print """python -O setup.py build [--dry|-n]
188
 
Compiles all files and sets up a jail template in the source directory.
189
 
-O is recommended to cause compilation to be optimised.
190
 
Details:
191
 
Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
192
 
Creates jail/.
193
 
Creates standard subdirs inside the jail, eg bin, opt, home, tmp.
194
 
Copies console/ to a location within the jail.
195
 
Copies OS programs and files to corresponding locations within the jail
196
 
  (eg. python and Python libs, ld.so, etc).
197
 
Generates .pyc or .pyo files for all the IVLE .py files.
198
 
 
199
 
--dry | -n  Print out the actions but don't do anything."""
200
 
    elif operation == 'install':
201
 
        print """sudo python setup.py install [--nojail] [--dry|-n]
202
 
(Requires root)
203
 
Create target install directory ($target).
204
 
Create $target/bin.
205
 
Copy trampoline/trampoline to $target/bin.
206
 
chown and chmod the installed trampoline.
207
 
Copy www/ to $target.
208
 
Copy jail/ to jails template directory (unless --nojail specified).
209
 
 
210
 
--nojail    Do not copy the jail.
211
 
--dry | -n  Print out the actions but don't do anything."""
212
 
    else:
213
 
        print >>sys.stderr, (
214
 
            """Invalid operation '%s'. Try python setup.py help."""
215
 
            % operation)
216
 
    return 1
217
 
 
218
 
def listmake(args):
219
 
    # We build two separate lists, by walking www and console
220
 
    list_www = build_list_py_files('www')
221
 
    list_console = build_list_py_files('console')
222
 
    # Make sure that the files generated by conf are in the list
223
 
    # (since listmake is typically run before conf)
224
 
    if "www/conf/conf.py" not in list_www:
225
 
        list_www.append("www/conf/conf.py")
226
 
    # Make sure that console/python-console is in the list
227
 
    if "console/python-console" not in list_console:
228
 
        list_console.append("console/python-console")
229
 
    # Write these out to a file
230
 
    cwd = os.getcwd()
231
 
    # the files that will be created/overwritten
232
 
    listfile = os.path.join(cwd, "install_list.py")
233
 
 
234
 
    try:
235
 
        file = open(listfile, "w")
236
 
 
237
 
        file.write("""# IVLE Configuration File
238
 
# install_list.py
239
 
# Provides lists of all files to be installed by `setup.py install' from
240
 
# certain directories.
241
 
# Note that any files with the given filename plus 'c' or 'o' (that is,
242
 
# compiled .pyc or .pyo files) will be copied as well.
243
 
 
244
 
# List of all installable files in www directory.
245
 
list_www = """)
246
 
        writelist_pretty(file, list_www)
247
 
        file.write("""
248
 
# List of all installable files in console directory.
249
 
list_console = """)
250
 
        writelist_pretty(file, list_console)
251
 
 
252
 
        file.close()
253
 
    except IOError, (errno, strerror):
254
 
        print "IO error(%s): %s" % (errno, strerror)
255
 
        sys.exit(1)
256
 
 
257
 
    print "Successfully wrote install_list.py"
258
 
 
259
 
    print
260
 
    print ("You may modify the set of installable files before cutting the "
261
 
            "distribution:")
262
 
    print listfile
263
 
    print
264
 
 
265
 
    return 0
266
 
 
267
 
def build_list_py_files(dir):
268
 
    """Builds a list of all py files found in a directory and its
269
 
    subdirectories. Returns this as a list of strings."""
270
 
    pylist = []
271
 
    for (dirpath, dirnames, filenames) in os.walk(dir):
272
 
        # Exclude directories beginning with a '.' (such as '.svn')
273
 
        filter_mutate(lambda x: x[0] != '.', dirnames)
274
 
        # All *.py files are added to the list
275
 
        pylist += [os.path.join(dirpath, item) for item in filenames
276
 
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
277
 
    return pylist
278
 
 
279
 
def writelist_pretty(file, list):
280
 
    """Writes a list one element per line, to a file."""
281
 
    if list == []:
282
 
        file.write("[]\n")
283
 
    else:
284
 
        file.write('[\n')
285
 
        for elem in list:
286
 
            file.write('    %s,\n' % repr(elem))
287
 
        file.write(']\n')
288
 
 
289
 
def conf(args):
290
 
    global root_dir, ivle_install_dir, jail_base, allowed_uids
291
 
    # Set up some variables
292
 
 
293
 
    cwd = os.getcwd()
294
 
    # the files that will be created/overwritten
295
 
    conffile = os.path.join(cwd, "www/conf/conf.py")
296
 
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
297
 
 
298
 
    # Fixed config options that we don't ask the admin
299
 
    default_app = "dummy"
300
 
 
301
 
    # Get command-line arguments to avoid asking questions.
302
 
 
303
 
    (opts, args) = getopt.gnu_getopt(args, "", ['root_dir=',
304
 
                    'ivle_install_dir=', 'jail_base=', 'allowed_uids='])
305
 
 
306
 
    if args != []:
307
 
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
308
 
        return 2
309
 
 
310
 
    if opts == []:
311
 
        # Interactive mode. Prompt the user for all the values.
312
 
 
313
 
        print """This tool will create the following files:
314
 
    %s
315
 
    %s
316
 
prompting you for details about your configuration. The file will be
317
 
overwritten if it already exists. It will *not* install or deploy IVLE.
318
 
 
319
 
Please hit Ctrl+C now if you do not wish to do this.
320
 
""" % (conffile, conf_hfile)
321
 
 
322
 
        # Get information from the administrator
323
 
        # If EOF is encountered at any time during the questioning, just exit
324
 
        # silently
325
 
 
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
 
 
340
 
    else:
341
 
        opts = dict(opts)
342
 
        # Non-interactive mode. Parse the options.
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']
351
 
 
352
 
    # Error handling on input values
353
 
    try:
354
 
        allowed_uids = map(int, allowed_uids.split(','))
355
 
    except ValueError:
356
 
        print >>sys.stderr, (
357
 
        "Invalid UID list (%s).\n"
358
 
        "Must be a comma-separated list of integers." % allowed_uids)
359
 
        return 1
360
 
 
361
 
    # Write www/conf/conf.py
362
 
 
363
 
    try:
364
 
        conf = open(conffile, "w")
365
 
 
366
 
        conf.write("""# IVLE Configuration File
367
 
# conf.py
368
 
# Miscellaneous application settings
369
 
 
370
 
 
371
 
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
372
 
# with this).
373
 
# eg. "/" or "/ivle".
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"
379
 
 
380
 
# In the local file system, where are the student/user file spaces located.
381
 
# The user jails are expected to be located immediately in subdirectories of
382
 
# this location.
383
 
jail_base = "%s"
384
 
 
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))
391
 
 
392
 
        conf.close()
393
 
    except IOError, (errno, strerror):
394
 
        print "IO error(%s): %s" % (errno, strerror)
395
 
        sys.exit(1)
396
 
 
397
 
    print "Successfully wrote www/conf/conf.py"
398
 
 
399
 
    # Write trampoline/conf.h
400
 
 
401
 
    try:
402
 
        conf = open(conf_hfile, "w")
403
 
 
404
 
        conf.write("""/* IVLE Configuration File
405
 
 * conf.h
406
 
 * Administrator settings required by trampoline.
407
 
 * Note: trampoline will have to be rebuilt in order for changes to this file
408
 
 * to take effect.
409
 
 */
410
 
 
411
 
/* In the local file system, where are the jails located.
412
 
 * The trampoline does not allow the creation of a jail anywhere besides
413
 
 * jail_base or a subdirectory of jail_base.
414
 
 */
415
 
static const char* jail_base = "%s";
416
 
 
417
 
/* Which user IDs are allowed to run the trampoline.
418
 
 * This list should be limited to the web server user.
419
 
 * (Note that root is an implicit member of this list).
420
 
 */
421
 
static const int allowed_uids[] = { %s };
422
 
""" % (jail_base, repr(allowed_uids)[1:-1]))
423
 
 
424
 
        conf.close()
425
 
    except IOError, (errno, strerror):
426
 
        print "IO error(%s): %s" % (errno, strerror)
427
 
        sys.exit(1)
428
 
 
429
 
    print "Successfully wrote trampoline/conf.h"
430
 
 
431
 
    print
432
 
    print "You may modify the configuration at any time by editing"
433
 
    print conffile
434
 
    print conf_hfile
435
 
    print
436
 
    return 0
437
 
 
438
 
def build(args):
439
 
    # Get "dry" variable from command line
440
 
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
441
 
    opts = dict(opts)
442
 
    dry = '-n' in opts or '--dry' in opts
443
 
 
444
 
    if dry:
445
 
        print "Dry run (no actions will be executed\n"
446
 
 
447
 
    # Compile the trampoline
448
 
    action_runprog('gcc', ['-Wall', '-o', 'trampoline/trampoline',
449
 
        'trampoline/trampoline.c'], dry)
450
 
 
451
 
    # Create the jail and its subdirectories
452
 
    # Note: Other subdirs will be made by copying files
453
 
    action_mkdir('jail', dry)
454
 
    action_mkdir('jail/home', dry)
455
 
    action_mkdir('jail/tmp', dry)
456
 
 
457
 
    # Copy all console and operating system files into the jail
458
 
    action_copylist(install_list.list_console, 'jail/opt/ivle', dry)
459
 
    copy_os_files_jail(dry)
460
 
 
461
 
    # Compile .py files into .pyc or .pyo files
462
 
    compileall.compile_dir('www', quiet=True)
463
 
    compileall.compile_dir('console', quiet=True)
464
 
 
465
 
    return 0
466
 
 
467
 
def copy_os_files_jail(dry):
468
 
    """Copies necessary Operating System files from their usual locations
469
 
    into the jail/ directory of the cwd."""
470
 
    # Currently source paths are configured for Ubuntu.
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)
480
 
 
481
 
def copy_file_to_jail(src, dry):
482
 
    """Copies a single file from an absolute location into the same location
483
 
    within the jail. src must begin with a '/'. The jail will be located
484
 
    in a 'jail' subdirectory of the current path."""
485
 
    action_copyfile(src, 'jail' + src, dry)
486
 
 
487
 
def install(args):
488
 
    # Get "dry" and "nojail" variables from command line
489
 
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry', 'nojail'])
490
 
    opts = dict(opts)
491
 
    dry = '-n' in opts or '--dry' in opts
492
 
    nojail = '--nojail' in opts
493
 
 
494
 
    if dry:
495
 
        print "Dry run (no actions will be executed\n"
496
 
 
497
 
    if not dry and os.geteuid() != 0:
498
 
        print >>sys.stderr, "Must be root to run install"
499
 
        print >>sys.stderr, "(I need to chown some files)."
500
 
        return 1
501
 
 
502
 
    # Create the target (install) directory
503
 
    action_mkdir(ivle_install_dir, dry)
504
 
 
505
 
    # Create bin and copy the compiled files there
506
 
    action_mkdir(os.path.join(ivle_install_dir, 'bin'), dry)
507
 
    tramppath = os.path.join(ivle_install_dir, 'bin/trampoline')
508
 
    action_copyfile('trampoline/trampoline', tramppath, dry)
509
 
    # chown trampoline to root and set setuid bit
510
 
    action_chown_setuid(tramppath, dry)
511
 
 
512
 
    # Copy the www directory using the list
513
 
    action_copylist(install_list.list_www, ivle_install_dir, dry)
514
 
 
515
 
    if not nojail:
516
 
        # Copy the local jail directory built by the build action
517
 
        # to the jails template directory (it will be used as a template
518
 
        # for all the students' jails).
519
 
        action_copytree('jail', os.path.join(jail_base, 'template'), 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)
523
 
 
524
 
    return 0
525
 
 
526
 
# The actions call Python os functions but print actions and handle dryness.
527
 
# May still throw os exceptions if errors occur.
528
 
 
529
 
class RunError:
530
 
    """Represents an error when running a program (nonzero return)."""
531
 
    def __init__(self, prog, retcode):
532
 
        self.prog = prog
533
 
        self.retcode = retcode
534
 
    def __str__(self):
535
 
        return str(self.prog) + " returned " + repr(self.retcode)
536
 
 
537
 
def action_runprog(prog, args, dry):
538
 
    """Runs a unix program. Searches in $PATH. Synchronous (waits for the
539
 
    program to return). Runs in the current environment. First prints the
540
 
    action as a "bash" line.
541
 
 
542
 
    Throws a RunError with a retcode of the return value of the program,
543
 
    if the program did not return 0.
544
 
 
545
 
    prog: String. Name of the program. (No path required, if in $PATH).
546
 
    args: [String]. Arguments to the program.
547
 
    dry: Bool. If True, prints but does not execute.
548
 
    """
549
 
    print prog, string.join(args, ' ')
550
 
    if dry: return
551
 
    ret = os.spawnvp(os.P_WAIT, prog, args)
552
 
    if ret != 0:
553
 
        raise RunError(prog, ret)
554
 
 
555
 
def action_mkdir(path, dry):
556
 
    """Calls mkdir. Silently ignored if the directory already exists.
557
 
    Creates all parent directories as necessary."""
558
 
    print "mkdir -p", path
559
 
    if dry: return
560
 
    try:
561
 
        os.makedirs(path)
562
 
    except OSError, (err, msg):
563
 
        if err != errno.EEXIST:
564
 
            raise
565
 
 
566
 
def action_copytree(src, dst, dry):
567
 
    """Copies an entire directory tree. Symlinks are seen as normal files and
568
 
    copies of the entire file (not the link) are made. Creates all parent
569
 
    directories as necessary.
570
 
 
571
 
    See shutil.copytree."""
572
 
    if os.access(dst, os.F_OK):
573
 
        print "rm -r", dst
574
 
        if not dry:
575
 
            shutil.rmtree(dst, True)
576
 
    print "cp -r", src, dst
577
 
    if dry: return
578
 
    shutil.copytree(src, dst)
579
 
 
580
 
def action_copylist(srclist, dst, dry):
581
 
    """Copies all files in a list to a new location. The files in the list
582
 
    are read relative to the current directory, and their destinations are the
583
 
    same paths relative to dst. Creates all parent directories as necessary.
584
 
    """
585
 
    for srcfile in srclist:
586
 
        dstfile = os.path.join(dst, srcfile)
587
 
        dstdir = os.path.split(dstfile)[0]
588
 
        if not os.path.isdir(dstdir):
589
 
            action_mkdir(dstdir, dry)
590
 
        print "cp -f", srcfile, dstfile
591
 
        if not dry:
592
 
            shutil.copyfile(srcfile, dstfile)
593
 
 
594
 
def action_copyfile(src, dst, dry):
595
 
    """Copies one file to a new location. Creates all parent directories
596
 
    as necessary.
597
 
    """
598
 
    dstdir = os.path.split(dst)[0]
599
 
    if not os.path.isdir(dstdir):
600
 
        action_mkdir(dstdir, dry)
601
 
    print "cp -f", src, dst
602
 
    if not dry:
603
 
        shutil.copyfile(src, dst)
604
 
 
605
 
def action_symlink(src, dst, dry):
606
 
    """Creates a symlink in a given location. Creates all parent directories
607
 
    as necessary.
608
 
    """
609
 
    dstdir = os.path.split(dst)[0]
610
 
    if not os.path.isdir(dstdir):
611
 
        action_mkdir(dstdir, dry)
612
 
    print "ln -s", src, dst
613
 
    if not dry:
614
 
        os.symlink(src, dst)
615
 
 
616
 
def action_chown_setuid(file, dry):
617
 
    """Chowns a file to root, and sets the setuid bit on the file.
618
 
    Calling this function requires the euid to be root.
619
 
    The actual mode of path is set to: rws--s--s
620
 
    """
621
 
    print "chown root:root", file
622
 
    if not dry:
623
 
        os.chown(file, 0, 0)
624
 
    print "chmod a+xs", file
625
 
    print "chmod u+rw", file
626
 
    if not dry:
627
 
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
628
 
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
629
 
 
630
 
def query_user(default, prompt):
631
 
    """Prompts the user for a string, which is read from a line of stdin.
632
 
    Exits silently if EOF is encountered. Returns the string, with spaces
633
 
    removed from the beginning and end.
634
 
 
635
 
    Returns default if a 0-length line (after spaces removed) was read.
636
 
    """
637
 
    sys.stdout.write('%s\n    (default: "%s")\n>' % (prompt, default))
638
 
    try:
639
 
        val = sys.stdin.readline()
640
 
    except KeyboardInterrupt:
641
 
        # Ctrl+C
642
 
        sys.stdout.write("\n")
643
 
        sys.exit(1)
644
 
    sys.stdout.write("\n")
645
 
    # If EOF, exit
646
 
    if val == '': sys.exit(1)
647
 
    # If empty line, return default
648
 
    val = val.strip()
649
 
    if val == '': return default
650
 
    return val
651
 
 
652
 
def filter_mutate(function, list):
653
 
    """Like built-in filter, but mutates the given list instead of returning a
654
 
    new one. Returns None."""
655
 
    i = len(list)-1
656
 
    while i >= 0:
657
 
        # Delete elements which do not match
658
 
        if not function(list[i]):
659
 
            del list[i]
660
 
        i -= 1
 
90
        sys.exit(1)
 
91
    return oper_func
661
92
 
662
93
if __name__ == "__main__":
663
94
    sys.exit(main())
 
95