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

97 by mattgiuca
Moved template.py and setup.py to better places.
1
#!/usr/bin/env python
2
# IVLE - Informatics Virtual Learning Environment
3
# Copyright (C) 2007-2008 The University of Melbourne
4
#
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
9
#
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19
# Module: setup
20
# Author: Matt Giuca
21
# Date:   12/12/2007
22
23
# This is a command-line application, for use by the administrator.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
24
# This program configures, builds and installs IVLE in three separate steps.
25
# It is called with at least one argument, which specifies which operation to
26
# take.
27
118 by mattgiuca
setup.py: Added copytree and copylist actions.
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
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
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).
97 by mattgiuca
Moved template.py and setup.py to better places.
59
60
import os
119 by mattgiuca
setup.py: Added install action. Completely works!
61
import stat
118 by mattgiuca
setup.py: Added copytree and copylist actions.
62
import shutil
97 by mattgiuca
Moved template.py and setup.py to better places.
63
import sys
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
64
import getopt
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
65
import string
66
import errno
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
67
import mimetypes
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
68
import compileall
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
69
import getopt
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
70
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
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
118 by mattgiuca
setup.py: Added copytree and copylist actions.
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
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
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
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
103
# Main function skeleton from Guido van Rossum
104
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
105
106
def main(argv=None):
107
    if argv is None:
108
        argv = sys.argv
109
110
    # Print the opening spiel including the GPL notice
111
112
    print """IVLE - Informatics Virtual Learning Environment Setup
97 by mattgiuca
Moved template.py and setup.py to better places.
113
Copyright (C) 2007-2008 The University of Melbourne
114
IVLE comes with ABSOLUTELY NO WARRANTY.
115
This is free software, and you are welcome to redistribute it
116
under certain conditions. See LICENSE.txt for details.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
117
118
IVLE Setup
119
"""
120
121
    # First argument is the name of the setup operation
122
    try:
123
        operation = argv[1]
124
    except IndexError:
125
        # Print usage message and exit
126
        help([])
127
        return 1
128
129
    # Call the requested operation's function
130
    try:
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
131
        oper_func = {
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
132
            'help' : help,
133
            'conf' : conf,
134
            'build' : build,
135
            'listmake' : listmake,
136
            'install' : install,
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
137
        }[operation]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
138
    except KeyError:
139
        print >>sys.stderr, (
140
            """Invalid operation '%s'. Try python setup.py help."""
141
            % operation)
129 by mattgiuca
setup.py: Minor fix, exits cleanly if arguments are invalid.
142
        return 1
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
143
    return oper_func(argv[2:])
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
144
145
# Operation functions
146
147
def help(args):
148
    if args == []:
149
        print """Usage: python setup.py operation [args]
150
Operation (and args) can be:
151
    help [operation]
152
    conf [args]
153
    build
154
    install [--nojail] [-n|--dry]
155
"""
156
        return 1
157
    elif len(args) != 1:
158
        print """Usage: python setup.py help [operation]"""
159
        return 2
160
    else:
161
        operation = args[0]
162
163
    if operation == 'help':
164
        print """python setup.py help [operation]
165
Prints the usage message or detailed help on an operation, then exits."""
118 by mattgiuca
setup.py: Added copytree and copylist actions.
166
    elif operation == 'listmake':
167
        print """python setup.py listmake
168
(For developer use only)
169
Recurses through the source tree and builds a list of all files which should
170
be copied upon installation. This should be run by the developer before
171
cutting a distribution, and the listfile it generates should be included in
172
the distribution, avoiding the administrator having to run it."""
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
173
    elif operation == 'conf':
174
        print """python setup.py conf [args]
175
Configures IVLE with machine-specific details, most notably, various paths.
176
Either prompts the administrator for these details or accepts them as
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
177
command-line args. Will be interactive only if there are no arguments given.
178
Takes defaults from existing conf file if it exists.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
179
Creates www/conf/conf.py and trampoline/conf.h.
180
Args are:
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
181
    --root_dir
182
    --ivle_install_dir
183
    --jail_base
184
    --allowed_uids
185
As explained in the interactive prompt or conf.py.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
186
"""
187
    elif operation == 'build':
118 by mattgiuca
setup.py: Added copytree and copylist actions.
188
        print """python -O setup.py build [--dry|-n]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
189
Compiles all files and sets up a jail template in the source directory.
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
190
-O is recommended to cause compilation to be optimised.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
191
Details:
192
Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
193
Creates jail/.
194
Creates standard subdirs inside the jail, eg bin, opt, home, tmp.
195
Copies console/ to a location within the jail.
196
Copies OS programs and files to corresponding locations within the jail
197
  (eg. python and Python libs, ld.so, etc).
118 by mattgiuca
setup.py: Added copytree and copylist actions.
198
Generates .pyc or .pyo files for all the IVLE .py files.
199
200
--dry | -n  Print out the actions but don't do anything."""
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
201
    elif operation == 'install':
202
        print """sudo python setup.py install [--nojail] [--dry|-n]
203
(Requires root)
204
Create target install directory ($target).
205
Create $target/bin.
206
Copy trampoline/trampoline to $target/bin.
207
chown and chmod the installed trampoline.
208
Copy www/ to $target.
209
Copy jail/ to jails template directory (unless --nojail specified).
210
211
--nojail    Do not copy the jail.
212
--dry | -n  Print out the actions but don't do anything."""
213
    else:
214
        print >>sys.stderr, (
215
            """Invalid operation '%s'. Try python setup.py help."""
216
            % operation)
217
    return 1
218
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
219
def listmake(args):
220
    # We build two separate lists, by walking www and console
221
    list_www = build_list_py_files('www')
222
    list_console = build_list_py_files('console')
223
    # Make sure that the files generated by conf are in the list
224
    # (since listmake is typically run before conf)
225
    if "www/conf/conf.py" not in list_www:
226
        list_www.append("www/conf/conf.py")
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
227
    # Make sure that console/python-console is in the list
228
    if "console/python-console" not in list_console:
229
        list_console.append("console/python-console")
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
230
    # Write these out to a file
231
    cwd = os.getcwd()
232
    # the files that will be created/overwritten
233
    listfile = os.path.join(cwd, "install_list.py")
234
235
    try:
236
        file = open(listfile, "w")
237
238
        file.write("""# IVLE Configuration File
239
# install_list.py
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
240
# Provides lists of all files to be installed by `setup.py install' from
241
# certain directories.
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
242
# Note that any files with the given filename plus 'c' or 'o' (that is,
243
# compiled .pyc or .pyo files) will be copied as well.
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
244
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
245
# List of all installable files in www directory.
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
246
list_www = """)
247
        writelist_pretty(file, list_www)
248
        file.write("""
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
249
# List of all installable files in console directory.
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
250
list_console = """)
251
        writelist_pretty(file, list_console)
252
253
        file.close()
254
    except IOError, (errno, strerror):
255
        print "IO error(%s): %s" % (errno, strerror)
256
        sys.exit(1)
257
258
    print "Successfully wrote install_list.py"
259
260
    print
261
    print ("You may modify the set of installable files before cutting the "
262
            "distribution:")
263
    print listfile
264
    print
265
266
    return 0
267
268
def build_list_py_files(dir):
269
    """Builds a list of all py files found in a directory and its
270
    subdirectories. Returns this as a list of strings."""
271
    pylist = []
272
    for (dirpath, dirnames, filenames) in os.walk(dir):
273
        # Exclude directories beginning with a '.' (such as '.svn')
274
        filter_mutate(lambda x: x[0] != '.', dirnames)
275
        # All *.py files are added to the list
276
        pylist += [os.path.join(dirpath, item) for item in filenames
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
277
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
278
    return pylist
279
280
def writelist_pretty(file, list):
281
    """Writes a list one element per line, to a file."""
282
    if list == []:
283
        file.write("[]\n")
284
    else:
285
        file.write('[\n')
286
        for elem in list:
287
            file.write('    %s,\n' % repr(elem))
288
        file.write(']\n')
289
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
290
def conf(args):
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
291
    global root_dir, ivle_install_dir, jail_base, allowed_uids
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
292
    # Set up some variables
293
294
    cwd = os.getcwd()
295
    # the files that will be created/overwritten
296
    conffile = os.path.join(cwd, "www/conf/conf.py")
297
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
298
299
    # Fixed config options that we don't ask the admin
300
    default_app = "dummy"
301
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
302
    # Get command-line arguments to avoid asking questions.
303
304
    (opts, args) = getopt.gnu_getopt(args, "", ['root_dir=',
305
                    'ivle_install_dir=', 'jail_base=', 'allowed_uids='])
306
307
    if args != []:
308
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
309
        return 2
310
311
    if opts == []:
312
        # Interactive mode. Prompt the user for all the values.
313
314
        print """This tool will create the following files:
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
315
    %s
316
    %s
97 by mattgiuca
Moved template.py and setup.py to better places.
317
prompting you for details about your configuration. The file will be
318
overwritten if it already exists. It will *not* install or deploy IVLE.
319
320
Please hit Ctrl+C now if you do not wish to do this.
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
321
""" % (conffile, conf_hfile)
97 by mattgiuca
Moved template.py and setup.py to better places.
322
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
323
        # Get information from the administrator
324
        # If EOF is encountered at any time during the questioning, just exit
325
        # silently
97 by mattgiuca
Moved template.py and setup.py to better places.
326
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
327
        root_dir = query_user(root_dir,
328
        """Root directory where IVLE is located (in URL space):""")
329
        ivle_install_dir = query_user(ivle_install_dir,
330
        'Root directory where IVLE will be installed (on the local file '
331
        'system):')
332
        jail_base = query_user(jail_base,
333
        """Root directory where the jails (containing user files) are stored
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
334
(on the local file system):""")
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
335
        allowed_uids = query_user(allowed_uids,
336
        """UID of the web server process which will run IVLE.
112 by mattgiuca
setup.py: A few comment changes.
337
Only this user may execute the trampoline. May specify multiple users as
338
a comma-separated list.
339
    (eg. "1002,78")""")
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
340
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
341
    else:
342
        opts = dict(opts)
343
        # Non-interactive mode. Parse the options.
344
        if '--root_dir' in opts:
345
            root_dir = opts['--root_dir']
346
        if '--ivle_install_dir' in opts:
347
            ivle_install_dir = opts['--ivle_install_dir']
348
        if '--jail_base' in opts:
349
            jail_base = opts['--jail_base']
350
        if '--allowed_uids' in opts:
351
            allowed_uids = opts['--allowed_uids']
352
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
353
    # Error handling on input values
354
    try:
112 by mattgiuca
setup.py: A few comment changes.
355
        allowed_uids = map(int, allowed_uids.split(','))
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
356
    except ValueError:
112 by mattgiuca
setup.py: A few comment changes.
357
        print >>sys.stderr, (
358
        "Invalid UID list (%s).\n"
359
        "Must be a comma-separated list of integers." % allowed_uids)
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
360
        return 1
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
361
362
    # Write www/conf/conf.py
363
364
    try:
365
        conf = open(conffile, "w")
366
367
        conf.write("""# IVLE Configuration File
97 by mattgiuca
Moved template.py and setup.py to better places.
368
# conf.py
369
# Miscellaneous application settings
370
371
372
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
373
# with this).
374
# eg. "/" or "/ivle".
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
375
root_dir = "%s"
376
377
# In the local file system, where IVLE is actually installed.
378
# This directory should contain the "www" and "bin" directories.
379
ivle_install_dir = "%s"
97 by mattgiuca
Moved template.py and setup.py to better places.
380
381
# In the local file system, where are the student/user file spaces located.
382
# The user jails are expected to be located immediately in subdirectories of
383
# this location.
106 by mattgiuca
Renamed "student_dir" to "jail_base" across the suite.
384
jail_base = "%s"
97 by mattgiuca
Moved template.py and setup.py to better places.
385
386
# Which application to load by default (if the user navigates to the top level
387
# of the site). This is the app's URL name.
388
# Note that if this app requires authentication, the user will first be
389
# presented with the login screen.
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
390
default_app = "%s"
106 by mattgiuca
Renamed "student_dir" to "jail_base" across the suite.
391
""" % (root_dir, ivle_install_dir, jail_base, default_app))
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
392
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
393
        conf.close()
394
    except IOError, (errno, strerror):
395
        print "IO error(%s): %s" % (errno, strerror)
396
        sys.exit(1)
397
398
    print "Successfully wrote www/conf/conf.py"
399
400
    # Write trampoline/conf.h
401
402
    try:
403
        conf = open(conf_hfile, "w")
404
405
        conf.write("""/* IVLE Configuration File
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
406
 * conf.h
407
 * Administrator settings required by trampoline.
408
 * Note: trampoline will have to be rebuilt in order for changes to this file
409
 * to take effect.
410
 */
411
412
/* In the local file system, where are the jails located.
413
 * The trampoline does not allow the creation of a jail anywhere besides
414
 * jail_base or a subdirectory of jail_base.
415
 */
416
static const char* jail_base = "%s";
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
417
418
/* Which user IDs are allowed to run the trampoline.
419
 * This list should be limited to the web server user.
420
 * (Note that root is an implicit member of this list).
421
 */
112 by mattgiuca
setup.py: A few comment changes.
422
static const int allowed_uids[] = { %s };
423
""" % (jail_base, repr(allowed_uids)[1:-1]))
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
424
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
425
        conf.close()
426
    except IOError, (errno, strerror):
427
        print "IO error(%s): %s" % (errno, strerror)
428
        sys.exit(1)
429
430
    print "Successfully wrote trampoline/conf.h"
431
432
    print
433
    print "You may modify the configuration at any time by editing"
434
    print conffile
435
    print conf_hfile
436
    print
437
    return 0
438
439
def build(args):
121 by mattgiuca
setup: build and install now read command line options
440
    # Get "dry" variable from command line
441
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
442
    opts = dict(opts)
443
    dry = '-n' in opts or '--dry' in opts
444
445
    if dry:
446
        print "Dry run (no actions will be executed\n"
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
447
448
    # Compile the trampoline
449
    action_runprog('gcc', ['-Wall', '-o', 'trampoline/trampoline',
450
        'trampoline/trampoline.c'], dry)
451
452
    # Create the jail and its subdirectories
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
453
    # Note: Other subdirs will be made by copying files
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
454
    action_mkdir('jail', dry)
455
    action_mkdir('jail/home', dry)
456
    action_mkdir('jail/tmp', dry)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
457
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
458
    # Copy all console and operating system files into the jail
118 by mattgiuca
setup.py: Added copytree and copylist actions.
459
    action_copylist(install_list.list_console, 'jail/opt/ivle', dry)
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
460
    copy_os_files_jail(dry)
118 by mattgiuca
setup.py: Added copytree and copylist actions.
461
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
462
    # Compile .py files into .pyc or .pyo files
463
    compileall.compile_dir('www', quiet=True)
464
    compileall.compile_dir('console', quiet=True)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
465
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
466
    return 0
467
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
468
def copy_os_files_jail(dry):
469
    """Copies necessary Operating System files from their usual locations
470
    into the jail/ directory of the cwd."""
471
    # Currently source paths are configured for Ubuntu.
472
    copy_file_to_jail('/lib/ld-linux.so.2', dry)
473
    copy_file_to_jail('/lib/tls/i686/cmov/libc.so.6', dry)
474
    copy_file_to_jail('/lib/tls/i686/cmov/libdl.so.2', dry)
475
    copy_file_to_jail('/lib/tls/i686/cmov/libm.so.6', dry)
476
    copy_file_to_jail('/lib/tls/i686/cmov/libpthread.so.0', dry)
477
    copy_file_to_jail('/lib/tls/i686/cmov/libutil.so.1', dry)
478
    copy_file_to_jail('/usr/bin/python2.5', dry)
479
    # TODO: ln -s jail/usr/bin/python2.5 jail/usr/bin/python
480
    action_copytree('/usr/lib/python2.5', 'jail/usr/lib/python2.5', dry)
481
482
def copy_file_to_jail(src, dry):
483
    """Copies a single file from an absolute location into the same location
484
    within the jail. src must begin with a '/'. The jail will be located
485
    in a 'jail' subdirectory of the current path."""
486
    action_copyfile(src, 'jail' + src, dry)
487
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
488
def install(args):
121 by mattgiuca
setup: build and install now read command line options
489
    # Get "dry" and "nojail" variables from command line
490
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry', 'nojail'])
491
    opts = dict(opts)
492
    dry = '-n' in opts or '--dry' in opts
493
    nojail = '--nojail' in opts
494
495
    if dry:
496
        print "Dry run (no actions will be executed\n"
119 by mattgiuca
setup.py: Added install action. Completely works!
497
498
    if not dry and os.geteuid() != 0:
499
        print >>sys.stderr, "Must be root to run install"
500
        print >>sys.stderr, "(I need to chown some files)."
501
        return 1
502
503
    # Create the target (install) directory
504
    action_mkdir(ivle_install_dir, dry)
505
506
    # Create bin and copy the compiled files there
507
    action_mkdir(os.path.join(ivle_install_dir, 'bin'), dry)
508
    tramppath = os.path.join(ivle_install_dir, 'bin/trampoline')
509
    action_copyfile('trampoline/trampoline', tramppath, dry)
510
    # chown trampoline to root and set setuid bit
511
    action_chown_setuid(tramppath, dry)
512
513
    # Copy the www directory using the list
514
    action_copylist(install_list.list_www, ivle_install_dir, dry)
515
516
    if not nojail:
517
        # Copy the local jail directory built by the build action
518
        # to the jails template directory (it will be used as a template
519
        # for all the students' jails).
520
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
123 by mattgiuca
setup.py: install creates a symlink from python pointing to python2.5.
521
        # Set up symlinks inside the jail
522
        action_symlink(os.path.join(jail_base, 'template/usr/bin/python2.5'),
523
            os.path.join(jail_base, 'template/usr/bin/python'), dry)
119 by mattgiuca
setup.py: Added install action. Completely works!
524
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
525
    return 0
526
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
527
# The actions call Python os functions but print actions and handle dryness.
528
# May still throw os exceptions if errors occur.
529
108 by mattgiuca
setup.py: Added RunError class. action_runprog now throws a RunError if the
530
class RunError:
531
    """Represents an error when running a program (nonzero return)."""
532
    def __init__(self, prog, retcode):
533
        self.prog = prog
534
        self.retcode = retcode
535
    def __str__(self):
536
        return str(self.prog) + " returned " + repr(self.retcode)
537
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
538
def action_runprog(prog, args, dry):
539
    """Runs a unix program. Searches in $PATH. Synchronous (waits for the
540
    program to return). Runs in the current environment. First prints the
541
    action as a "bash" line.
542
108 by mattgiuca
setup.py: Added RunError class. action_runprog now throws a RunError if the
543
    Throws a RunError with a retcode of the return value of the program,
544
    if the program did not return 0.
545
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
546
    prog: String. Name of the program. (No path required, if in $PATH).
547
    args: [String]. Arguments to the program.
548
    dry: Bool. If True, prints but does not execute.
549
    """
550
    print prog, string.join(args, ' ')
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
551
    if dry: return
552
    ret = os.spawnvp(os.P_WAIT, prog, args)
553
    if ret != 0:
554
        raise RunError(prog, ret)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
555
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
556
def action_mkdir(path, dry):
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
557
    """Calls mkdir. Silently ignored if the directory already exists.
558
    Creates all parent directories as necessary."""
559
    print "mkdir -p", path
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
560
    if dry: return
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
561
    try:
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
562
        os.makedirs(path)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
563
    except OSError, (err, msg):
564
        if err != errno.EEXIST:
565
            raise
566
118 by mattgiuca
setup.py: Added copytree and copylist actions.
567
def action_copytree(src, dst, dry):
568
    """Copies an entire directory tree. Symlinks are seen as normal files and
569
    copies of the entire file (not the link) are made. Creates all parent
570
    directories as necessary.
571
572
    See shutil.copytree."""
573
    if os.access(dst, os.F_OK):
574
        print "rm -r", dst
575
        if not dry:
576
            shutil.rmtree(dst, True)
577
    print "cp -r", src, dst
578
    if dry: return
579
    shutil.copytree(src, dst)
580
581
def action_copylist(srclist, dst, dry):
582
    """Copies all files in a list to a new location. The files in the list
583
    are read relative to the current directory, and their destinations are the
584
    same paths relative to dst. Creates all parent directories as necessary.
585
    """
586
    for srcfile in srclist:
587
        dstfile = os.path.join(dst, srcfile)
588
        dstdir = os.path.split(dstfile)[0]
589
        if not os.path.isdir(dstdir):
590
            action_mkdir(dstdir, dry)
591
        print "cp -f", srcfile, dstfile
592
        if not dry:
593
            shutil.copyfile(srcfile, dstfile)
594
119 by mattgiuca
setup.py: Added install action. Completely works!
595
def action_copyfile(src, dst, dry):
596
    """Copies one file to a new location. Creates all parent directories
597
    as necessary.
598
    """
599
    dstdir = os.path.split(dst)[0]
600
    if not os.path.isdir(dstdir):
601
        action_mkdir(dstdir, dry)
602
    print "cp -f", src, dst
603
    if not dry:
604
        shutil.copyfile(src, dst)
605
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
606
def action_symlink(src, dst, dry):
607
    """Creates a symlink in a given location. Creates all parent directories
608
    as necessary.
609
    """
610
    dstdir = os.path.split(dst)[0]
611
    if not os.path.isdir(dstdir):
612
        action_mkdir(dstdir, dry)
613
    print "ln -s", src, dst
614
    if not dry:
615
        os.symlink(src, dst)
616
119 by mattgiuca
setup.py: Added install action. Completely works!
617
def action_chown_setuid(file, dry):
618
    """Chowns a file to root, and sets the setuid bit on the file.
619
    Calling this function requires the euid to be root.
620
    The actual mode of path is set to: rws--s--s
621
    """
622
    print "chown root:root", file
623
    if not dry:
624
        os.chown(file, 0, 0)
625
    print "chmod a+xs", file
626
    print "chmod u+rw", file
627
    if not dry:
628
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
629
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
630
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
631
def query_user(default, prompt):
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
632
    """Prompts the user for a string, which is read from a line of stdin.
633
    Exits silently if EOF is encountered. Returns the string, with spaces
634
    removed from the beginning and end.
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
635
636
    Returns default if a 0-length line (after spaces removed) was read.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
637
    """
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
638
    sys.stdout.write('%s\n    (default: "%s")\n>' % (prompt, default))
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
639
    try:
640
        val = sys.stdin.readline()
641
    except KeyboardInterrupt:
642
        # Ctrl+C
643
        sys.stdout.write("\n")
644
        sys.exit(1)
645
    sys.stdout.write("\n")
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
646
    # If EOF, exit
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
647
    if val == '': sys.exit(1)
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
648
    # If empty line, return default
649
    val = val.strip()
650
    if val == '': return default
651
    return val
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
652
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
653
def filter_mutate(function, list):
654
    """Like built-in filter, but mutates the given list instead of returning a
655
    new one. Returns None."""
656
    i = len(list)-1
657
    while i >= 0:
658
        # Delete elements which do not match
659
        if not function(list[i]):
660
            del list[i]
661
        i -= 1
662
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
663
if __name__ == "__main__":
664
    sys.exit(main())