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

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: mattgiuca
  • Date: 2007-12-21 01:42:15 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:115
Deleted common/jail.py (bad cruft).

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 is a frontend for the modules in the setup packages that 
25
 
# build and install IVLE in separate steps.
 
24
# This program configures, builds and installs IVLE in three separate steps.
26
25
# It is called with at least one argument, which specifies which operation to
27
26
# take.
28
27
 
 
28
# setup.py conf [args]
 
29
# Configures IVLE with machine-specific details, most notably, various paths.
 
30
# Either prompts the administrator for these details or accepts them as
 
31
# command-line args.
 
32
# Creates www/conf/conf.py and trampoline/conf.h.
 
33
 
 
34
# setup.py build
 
35
# Compiles all files and sets up a jail template in the source directory.
 
36
# Details:
 
37
# Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
 
38
# Creates jail/.
 
39
# Creates standard subdirs inside the jail, eg bin, opt, home, tmp.
 
40
# Copies console/ to a location within the jail.
 
41
# Copies OS programs and files to corresponding locations within the jail
 
42
#   (eg. python and Python libs, ld.so, etc).
 
43
# Generates .pyc files for all the IVLE .py files.
 
44
 
 
45
# setup.py listmake (for developer use only)
 
46
# Recurses through the source tree and builds a list of all files which should
 
47
# be copied upon installation. This should be run by the developer before
 
48
# cutting a distribution, and the listfile it generates should be included in
 
49
# the distribution, avoiding the administrator having to run it.
 
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
# TODO: List in help, and handle, args for the conf operation
 
61
 
 
62
import os
29
63
import sys
30
 
import setup.build
31
 
import setup.install
 
64
import getopt
 
65
import string
 
66
import errno
 
67
 
 
68
# Try importing existing conf, but if we can't just set up defaults
 
69
# The reason for this is that these settings are used by other phases
 
70
# of setup besides conf, so we need to know them.
 
71
# Also this allows you to hit Return to accept the existing value.
 
72
try:
 
73
    confmodule = __import__("www/conf/conf")
 
74
    root_dir = confmodule.root_dir
 
75
    ivle_install_dir = confmodule.ivle_install_dir
 
76
    jail_base = confmodule.jail_base
 
77
except ImportError:
 
78
    # Just set reasonable defaults
 
79
    root_dir = "/ivle"
 
80
    ivle_install_dir = "/opt/ivle"
 
81
    jail_base = "/home/informatics/jails"
 
82
# Always defaults
 
83
allowed_uids = "0"
 
84
 
 
85
# Main function skeleton from Guido van Rossum
 
86
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
 
87
 
 
88
class Usage(Exception):
 
89
    def __init__(self, msg):
 
90
        self.msg = msg
32
91
 
33
92
def main(argv=None):
34
93
    if argv is None:
37
96
    # Print the opening spiel including the GPL notice
38
97
 
39
98
    print """IVLE - Informatics Virtual Learning Environment Setup
40
 
Copyright (C) 2007-2009 The University of Melbourne
 
99
Copyright (C) 2007-2008 The University of Melbourne
41
100
IVLE comes with ABSOLUTELY NO WARRANTY.
42
101
This is free software, and you are welcome to redistribute it
43
102
under certain conditions. See LICENSE.txt for details.
53
112
        help([])
54
113
        return 1
55
114
 
56
 
    oper_func = call_operator(operation)
57
 
    return oper_func(argv[2:])
 
115
    # Call the requested operation's function
 
116
    try:
 
117
        return {
 
118
            'help' : help,
 
119
            'conf' : conf,
 
120
            'build' : build,
 
121
            'listmake' : listmake,
 
122
            'install' : install,
 
123
        }[operation](argv[2:])
 
124
    except KeyError:
 
125
        print >>sys.stderr, (
 
126
            """Invalid operation '%s'. Try python setup.py help."""
 
127
            % operation)
 
128
 
 
129
    try:
 
130
        try:
 
131
            opts, args = getopt.getopt(argv[1:], "h", ["help"])
 
132
        except getopt.error, msg:
 
133
            raise Usage(msg)
 
134
        # more code, unchanged
 
135
    except Usage, err:
 
136
        print >>sys.stderr, err.msg
 
137
        print >>sys.stderr, "for help use --help"
 
138
        return 2
 
139
 
 
140
# Operation functions
58
141
 
59
142
def help(args):
60
 
    if len(args)!=1:
61
 
        print """Usage: python setup.py operation [options]
62
 
Operation can be:
 
143
    if args == []:
 
144
        print """Usage: python setup.py operation [args]
 
145
Operation (and args) can be:
63
146
    help [operation]
 
147
    conf [args]
64
148
    build
65
 
    install
66
 
 
67
 
    For help and options for a specific operation use 'help [operation]'."""
68
 
    else:
69
 
        operator = args[0]
70
 
        oper_func = call_operator(operator)
71
 
        oper_func(['operator','--help'])
72
 
 
73
 
def call_operator(operation):
74
 
    # Call the requested operation's function
75
 
    try:
76
 
        oper_func = {
77
 
            'help' : help,
78
 
            'build' : setup.build.build,
79
 
            'install' : setup.install.install,
80
 
        }[operation]
81
 
    except KeyError:
 
149
    install [--nojail] [-n|--dry]
 
150
"""
 
151
        return 1
 
152
    elif len(args) != 1:
 
153
        print """Usage: python setup.py help [operation]"""
 
154
        return 2
 
155
    else:
 
156
        operation = args[0]
 
157
 
 
158
    if operation == 'help':
 
159
        print """python setup.py help [operation]
 
160
Prints the usage message or detailed help on an operation, then exits."""
 
161
    elif operation == 'conf':
 
162
        print """python setup.py conf [args]
 
163
Configures IVLE with machine-specific details, most notably, various paths.
 
164
Either prompts the administrator for these details or accepts them as
 
165
command-line args.
 
166
Creates www/conf/conf.py and trampoline/conf.h.
 
167
Args are:
 
168
"""
 
169
    elif operation == 'build':
 
170
        print """python setup.py build
 
171
Compiles all files and sets up a jail template in the source directory.
 
172
Details:
 
173
Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
 
174
Creates jail/.
 
175
Creates standard subdirs inside the jail, eg bin, opt, home, tmp.
 
176
Copies console/ to a location within the jail.
 
177
Copies OS programs and files to corresponding locations within the jail
 
178
  (eg. python and Python libs, ld.so, etc).
 
179
Generates .pyc files for all the IVLE .py files."""
 
180
    elif operation == 'listmake':
 
181
        print """python setup.py listmake
 
182
(For developer use only)
 
183
Recurses through the source tree and builds a list of all files which should
 
184
be copied upon installation. This should be run by the developer before
 
185
cutting a distribution, and the listfile it generates should be included in
 
186
the distribution, avoiding the administrator having to run it."""
 
187
    elif operation == 'install':
 
188
        print """sudo python setup.py install [--nojail] [--dry|-n]
 
189
(Requires root)
 
190
Create target install directory ($target).
 
191
Create $target/bin.
 
192
Copy trampoline/trampoline to $target/bin.
 
193
chown and chmod the installed trampoline.
 
194
Copy www/ to $target.
 
195
Copy jail/ to jails template directory (unless --nojail specified).
 
196
 
 
197
--nojail    Do not copy the jail.
 
198
--dry | -n  Print out the actions but don't do anything."""
 
199
    else:
82
200
        print >>sys.stderr, (
83
201
            """Invalid operation '%s'. Try python setup.py help."""
84
202
            % operation)
85
 
        sys.exit(1)
86
 
    return oper_func
 
203
    return 1
 
204
 
 
205
def listmake(args):
 
206
    # We build two separate lists, by walking www and console
 
207
    list_www = build_list_py_files('www')
 
208
    list_console = build_list_py_files('console')
 
209
    # Make sure that the files generated by conf are in the list
 
210
    # (since listmake is typically run before conf)
 
211
    if "www/conf/conf.py" not in list_www:
 
212
        list_www.append("www/conf/conf.py")
 
213
    # Write these out to a file
 
214
    cwd = os.getcwd()
 
215
    # the files that will be created/overwritten
 
216
    listfile = os.path.join(cwd, "install_list.py")
 
217
 
 
218
    try:
 
219
        file = open(listfile, "w")
 
220
 
 
221
        file.write("""# IVLE Configuration File
 
222
# install_list.py
 
223
# Provides lists of all Python files to be installed by `setup.py install'.
 
224
 
 
225
# List of all installable Python files in www directory.
 
226
list_www = """)
 
227
        writelist_pretty(file, list_www)
 
228
        file.write("""
 
229
# List of all installable Python files in console directory.
 
230
list_console = """)
 
231
        writelist_pretty(file, list_console)
 
232
 
 
233
        file.close()
 
234
    except IOError, (errno, strerror):
 
235
        print "IO error(%s): %s" % (errno, strerror)
 
236
        sys.exit(1)
 
237
 
 
238
    print "Successfully wrote install_list.py"
 
239
 
 
240
    print
 
241
    print ("You may modify the set of installable files before cutting the "
 
242
            "distribution:")
 
243
    print listfile
 
244
    print
 
245
 
 
246
    return 0
 
247
 
 
248
def build_list_py_files(dir):
 
249
    """Builds a list of all py files found in a directory and its
 
250
    subdirectories. Returns this as a list of strings."""
 
251
    pylist = []
 
252
    for (dirpath, dirnames, filenames) in os.walk(dir):
 
253
        # Exclude directories beginning with a '.' (such as '.svn')
 
254
        filter_mutate(lambda x: x[0] != '.', dirnames)
 
255
        # All *.py files are added to the list
 
256
        pylist += [os.path.join(dirpath, item) for item in filenames
 
257
            if item.endswith('.py')]
 
258
    return pylist
 
259
 
 
260
def writelist_pretty(file, list):
 
261
    """Writes a list one element per line, to a file."""
 
262
    if list == []:
 
263
        file.write("[]\n")
 
264
    else:
 
265
        file.write('[\n')
 
266
        for elem in list:
 
267
            file.write('    %s,\n' % repr(elem))
 
268
        file.write(']\n')
 
269
 
 
270
def conf(args):
 
271
    global root_dir, ivle_install_dir, jail_base, allowed_uids
 
272
    # Set up some variables
 
273
 
 
274
    cwd = os.getcwd()
 
275
    # the files that will be created/overwritten
 
276
    conffile = os.path.join(cwd, "www/conf/conf.py")
 
277
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
 
278
 
 
279
    # Fixed config options that we don't ask the admin
 
280
 
 
281
    default_app = "dummy"
 
282
 
 
283
    print """This tool will create the following files:
 
284
    %s
 
285
    %s
 
286
prompting you for details about your configuration. The file will be
 
287
overwritten if it already exists. It will *not* install or deploy IVLE.
 
288
 
 
289
Please hit Ctrl+C now if you do not wish to do this.
 
290
""" % (conffile, conf_hfile)
 
291
 
 
292
    # Get information from the administrator
 
293
    # If EOF is encountered at any time during the questioning, just exit
 
294
    # silently
 
295
 
 
296
    root_dir = query_user(root_dir,
 
297
    """Root directory where IVLE is located (in URL space):""")
 
298
    ivle_install_dir = query_user(ivle_install_dir,
 
299
    'Root directory where IVLE will be installed (on the local file '
 
300
    'system):')
 
301
    jail_base = query_user(jail_base,
 
302
    """Root directory where the jails (containing user files) are stored
 
303
(on the local file system):""")
 
304
    allowed_uids = query_user(allowed_uids,
 
305
    """UID of the web server process which will run IVLE.
 
306
Only this user may execute the trampoline. May specify multiple users as
 
307
a comma-separated list.
 
308
    (eg. "1002,78")""")
 
309
 
 
310
    # Error handling on input values
 
311
 
 
312
    try:
 
313
        allowed_uids = map(int, allowed_uids.split(','))
 
314
    except ValueError:
 
315
        print >>sys.stderr, (
 
316
        "Invalid UID list (%s).\n"
 
317
        "Must be a comma-separated list of integers." % allowed_uids)
 
318
        return 1
 
319
 
 
320
    # Write www/conf/conf.py
 
321
 
 
322
    try:
 
323
        conf = open(conffile, "w")
 
324
 
 
325
        conf.write("""# IVLE Configuration File
 
326
# conf.py
 
327
# Miscellaneous application settings
 
328
 
 
329
 
 
330
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
 
331
# with this).
 
332
# eg. "/" or "/ivle".
 
333
root_dir = "%s"
 
334
 
 
335
# In the local file system, where IVLE is actually installed.
 
336
# This directory should contain the "www" and "bin" directories.
 
337
ivle_install_dir = "%s"
 
338
 
 
339
# In the local file system, where are the student/user file spaces located.
 
340
# The user jails are expected to be located immediately in subdirectories of
 
341
# this location.
 
342
jail_base = "%s"
 
343
 
 
344
# Which application to load by default (if the user navigates to the top level
 
345
# of the site). This is the app's URL name.
 
346
# Note that if this app requires authentication, the user will first be
 
347
# presented with the login screen.
 
348
default_app = "%s"
 
349
""" % (root_dir, ivle_install_dir, jail_base, default_app))
 
350
 
 
351
        conf.close()
 
352
    except IOError, (errno, strerror):
 
353
        print "IO error(%s): %s" % (errno, strerror)
 
354
        sys.exit(1)
 
355
 
 
356
    print "Successfully wrote www/conf/conf.py"
 
357
 
 
358
    # Write trampoline/conf.h
 
359
 
 
360
    try:
 
361
        conf = open(conf_hfile, "w")
 
362
 
 
363
        conf.write("""/* IVLE Configuration File
 
364
 * conf.h
 
365
 * Administrator settings required by trampoline.
 
366
 * Note: trampoline will have to be rebuilt in order for changes to this file
 
367
 * to take effect.
 
368
 */
 
369
 
 
370
/* In the local file system, where are the jails located.
 
371
 * The trampoline does not allow the creation of a jail anywhere besides
 
372
 * jail_base or a subdirectory of jail_base.
 
373
 */
 
374
static const char* jail_base = "%s";
 
375
 
 
376
/* Which user IDs are allowed to run the trampoline.
 
377
 * This list should be limited to the web server user.
 
378
 * (Note that root is an implicit member of this list).
 
379
 */
 
380
static const int allowed_uids[] = { %s };
 
381
""" % (jail_base, repr(allowed_uids)[1:-1]))
 
382
 
 
383
        conf.close()
 
384
    except IOError, (errno, strerror):
 
385
        print "IO error(%s): %s" % (errno, strerror)
 
386
        sys.exit(1)
 
387
 
 
388
    print "Successfully wrote trampoline/conf.h"
 
389
 
 
390
    print
 
391
    print "You may modify the configuration at any time by editing"
 
392
    print conffile
 
393
    print conf_hfile
 
394
    print
 
395
    return 0
 
396
 
 
397
def build(args):
 
398
    dry = False     # Set to True later if --dry
 
399
 
 
400
    # Compile the trampoline
 
401
    action_runprog('gcc', ['-Wall', '-o', 'trampoline/trampoline',
 
402
        'trampoline/trampoline.c'], dry)
 
403
 
 
404
    # Create the jail and its subdirectories
 
405
    action_mkdir('jail')
 
406
    action_mkdir('jail/bin')
 
407
    action_mkdir('jail/lib')
 
408
    action_mkdir('jail/usr/bin')
 
409
    action_mkdir('jail/usr/lib')
 
410
    action_mkdir('jail/opt/ivle')
 
411
    action_mkdir('jail/home')
 
412
    action_mkdir('jail/tmp')
 
413
 
 
414
    # TODO: Copy console into the jail
 
415
    # TODO: Copy operating system files into the jail
 
416
    # TODO: Compile .py files into .pyc files
 
417
 
 
418
    return 0
 
419
 
 
420
def install(args):
 
421
    print "Install"
 
422
    return 0
 
423
 
 
424
# The actions call Python os functions but print actions and handle dryness.
 
425
# May still throw os exceptions if errors occur.
 
426
 
 
427
class RunError:
 
428
    """Represents an error when running a program (nonzero return)."""
 
429
    def __init__(self, prog, retcode):
 
430
        self.prog = prog
 
431
        self.retcode = retcode
 
432
    def __str__(self):
 
433
        return str(self.prog) + " returned " + repr(self.retcode)
 
434
 
 
435
def action_runprog(prog, args, dry):
 
436
    """Runs a unix program. Searches in $PATH. Synchronous (waits for the
 
437
    program to return). Runs in the current environment. First prints the
 
438
    action as a "bash" line.
 
439
 
 
440
    Throws a RunError with a retcode of the return value of the program,
 
441
    if the program did not return 0.
 
442
 
 
443
    prog: String. Name of the program. (No path required, if in $PATH).
 
444
    args: [String]. Arguments to the program.
 
445
    dry: Bool. If True, prints but does not execute.
 
446
    """
 
447
    print prog, string.join(args, ' ')
 
448
    if dry: return
 
449
    ret = os.spawnvp(os.P_WAIT, prog, args)
 
450
    if ret != 0:
 
451
        raise RunError(prog, ret)
 
452
 
 
453
def action_mkdir(path):
 
454
    """Calls mkdir. Silently ignored if the directory already exists.
 
455
    Creates all parent directories as necessary."""
 
456
    print "mkdir -p", path
 
457
    if dry: return
 
458
    try:
 
459
        os.makedirs(path)
 
460
    except OSError, (err, msg):
 
461
        if err != errno.EEXIST:
 
462
            raise
 
463
 
 
464
def query_user(default, prompt):
 
465
    """Prompts the user for a string, which is read from a line of stdin.
 
466
    Exits silently if EOF is encountered. Returns the string, with spaces
 
467
    removed from the beginning and end.
 
468
 
 
469
    Returns default if a 0-length line (after spaces removed) was read.
 
470
    """
 
471
    sys.stdout.write('%s\n    (default: "%s")\n>' % (prompt, default))
 
472
    try:
 
473
        val = sys.stdin.readline()
 
474
    except KeyboardInterrupt:
 
475
        # Ctrl+C
 
476
        sys.stdout.write("\n")
 
477
        sys.exit(1)
 
478
    sys.stdout.write("\n")
 
479
    # If EOF, exit
 
480
    if val == '': sys.exit(1)
 
481
    # If empty line, return default
 
482
    val = val.strip()
 
483
    if val == '': return default
 
484
    return val
 
485
 
 
486
def filter_mutate(function, list):
 
487
    """Like built-in filter, but mutates the given list instead of returning a
 
488
    new one. Returns None."""
 
489
    i = len(list)-1
 
490
    while i >= 0:
 
491
        # Delete elements which do not match
 
492
        if not function(list[i]):
 
493
            del list[i]
 
494
        i -= 1
87
495
 
88
496
if __name__ == "__main__":
89
497
    sys.exit(main())
90