25
25
# It is called with at least one argument, which specifies which operation to
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.
34
28
# setup.py conf [args]
35
29
# Configures IVLE with machine-specific details, most notably, various paths.
36
30
# Either prompts the administrator for these details or accepts them as
48
42
# (eg. python and Python libs, ld.so, etc).
49
43
# Generates .pyc files for all the IVLE .py files.
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.
51
51
# setup.py install [--nojail] [--dry|n]
53
53
# Create target install directory ($target).
88
# Try importing install_list, but don't fail if we can't, because listmake can
89
# function without it.
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']
103
85
# Main function skeleton from Guido van Rossum
104
86
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
88
class Usage(Exception):
89
def __init__(self, msg):
106
92
def main(argv=None):
129
115
# Call the requested operation's function
135
121
'listmake' : listmake,
136
122
'install' : install,
123
}[operation](argv[2:])
139
125
print >>sys.stderr, (
140
126
"""Invalid operation '%s'. Try python setup.py help."""
142
return oper_func(argv[2:])
131
opts, args = getopt.getopt(argv[1:], "h", ["help"])
132
except getopt.error, msg:
134
# more code, unchanged
136
print >>sys.stderr, err.msg
137
print >>sys.stderr, "for help use --help"
144
140
# Operation functions
162
158
if operation == 'help':
163
159
print """python setup.py help [operation]
164
160
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
161
elif operation == 'conf':
173
162
print """python setup.py conf [args]
174
163
Configures IVLE with machine-specific details, most notably, various paths.
175
164
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
166
Creates www/conf/conf.py and trampoline/conf.h.
184
As explained in the interactive prompt or conf.py.
186
169
elif operation == 'build':
187
print """python -O setup.py build [--dry|-n]
170
print """python setup.py build
188
171
Compiles all files and sets up a jail template in the source directory.
189
-O is recommended to cause compilation to be optimised.
191
173
Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
194
176
Copies console/ to a location within the jail.
195
177
Copies OS programs and files to corresponding locations within the jail
196
178
(eg. python and Python libs, ld.so, etc).
197
Generates .pyc or .pyo files for all the IVLE .py files.
199
--dry | -n Print out the actions but don't do anything."""
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."""
200
187
elif operation == 'install':
201
188
print """sudo python setup.py install [--nojail] [--dry|-n]
223
210
# (since listmake is typically run before conf)
224
211
if "www/conf/conf.py" not in list_www:
225
212
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
213
# Write these out to a file
230
214
cwd = os.getcwd()
231
215
# the files that will be created/overwritten
237
221
file.write("""# IVLE Configuration File
238
222
# 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.
223
# Provides lists of all Python files to be installed by `setup.py install'.
244
# List of all installable files in www directory.
225
# List of all installable Python files in www directory.
246
227
writelist_pretty(file, list_www)
248
# List of all installable files in console directory.
229
# List of all installable Python files in console directory.
249
230
list_console = """)
250
231
writelist_pretty(file, list_console)
296
277
conf_hfile = os.path.join(cwd, "trampoline/conf.h")
298
279
# Fixed config options that we don't ask the admin
299
281
default_app = "dummy"
301
# Get command-line arguments to avoid asking questions.
303
(opts, args) = getopt.gnu_getopt(args, "", ['root_dir=',
304
'ivle_install_dir=', 'jail_base=', 'allowed_uids='])
307
print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
311
# Interactive mode. Prompt the user for all the values.
313
print """This tool will create the following files:
283
print """This tool will create the following files:
316
286
prompting you for details about your configuration. The file will be
319
289
Please hit Ctrl+C now if you do not wish to do this.
320
290
""" % (conffile, conf_hfile)
322
# Get information from the administrator
323
# If EOF is encountered at any time during the questioning, just exit
292
# Get information from the administrator
293
# If EOF is encountered at any time during the questioning, just exit
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 '
331
jail_base = query_user(jail_base,
332
"""Root directory where the jails (containing user files) are stored
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 '
301
jail_base = query_user(jail_base,
302
"""Root directory where the jails (containing user files) are stored
333
303
(on the local file system):""")
334
allowed_uids = query_user(allowed_uids,
335
"""UID of the web server process which will run IVLE.
304
allowed_uids = query_user(allowed_uids,
305
"""UID of the web server process which will run IVLE.
336
306
Only this user may execute the trampoline. May specify multiple users as
337
307
a comma-separated list.
338
308
(eg. "1002,78")""")
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']
352
310
# Error handling on input values
354
313
allowed_uids = map(int, allowed_uids.split(','))
355
314
except ValueError:
443
402
'trampoline/trampoline.c'], dry)
445
404
# Create the jail and its subdirectories
446
action_mkdir('jail', dry)
447
action_mkdir('jail/bin', dry)
448
action_mkdir('jail/lib', dry)
449
action_mkdir('jail/usr/bin', dry)
450
action_mkdir('jail/usr/lib', dry)
451
action_mkdir('jail/opt/ivle', dry)
452
action_mkdir('jail/home', dry)
453
action_mkdir('jail/tmp', dry)
455
# Copy all console files into the jail
456
action_copylist(install_list.list_console, 'jail/opt/ivle', dry)
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')
414
# TODO: Copy console into the jail
458
415
# TODO: Copy operating system files into the jail
460
# Compile .py files into .pyc or .pyo files
461
compileall.compile_dir('www', quiet=True)
462
compileall.compile_dir('console', quiet=True)
416
# TODO: Compile .py files into .pyc files
466
420
def install(args):
467
# Create the target directory
468
nojail = False # Set to True later if --nojail
469
dry = False # Set to True later if --dry
471
if not dry and os.geteuid() != 0:
472
print >>sys.stderr, "Must be root to run install"
473
print >>sys.stderr, "(I need to chown some files)."
476
# Create the target (install) directory
477
action_mkdir(ivle_install_dir, dry)
479
# Create bin and copy the compiled files there
480
action_mkdir(os.path.join(ivle_install_dir, 'bin'), dry)
481
tramppath = os.path.join(ivle_install_dir, 'bin/trampoline')
482
action_copyfile('trampoline/trampoline', tramppath, dry)
483
# chown trampoline to root and set setuid bit
484
action_chown_setuid(tramppath, dry)
486
# Copy the www directory using the list
487
action_copylist(install_list.list_www, ivle_install_dir, dry)
490
# Copy the local jail directory built by the build action
491
# to the jails template directory (it will be used as a template
492
# for all the students' jails).
493
action_copytree('jail', os.path.join(jail_base, 'template'), dry)
497
424
# The actions call Python os functions but print actions and handle dryness.
534
461
if err != errno.EEXIST:
537
def action_copytree(src, dst, dry):
538
"""Copies an entire directory tree. Symlinks are seen as normal files and
539
copies of the entire file (not the link) are made. Creates all parent
540
directories as necessary.
542
See shutil.copytree."""
543
if os.access(dst, os.F_OK):
546
shutil.rmtree(dst, True)
547
print "cp -r", src, dst
549
shutil.copytree(src, dst)
551
def action_copylist(srclist, dst, dry):
552
"""Copies all files in a list to a new location. The files in the list
553
are read relative to the current directory, and their destinations are the
554
same paths relative to dst. Creates all parent directories as necessary.
556
for srcfile in srclist:
557
dstfile = os.path.join(dst, srcfile)
558
dstdir = os.path.split(dstfile)[0]
559
if not os.path.isdir(dstdir):
560
action_mkdir(dstdir, dry)
561
print "cp -f", srcfile, dstfile
563
shutil.copyfile(srcfile, dstfile)
565
def action_copyfile(src, dst, dry):
566
"""Copies one file to a new location. Creates all parent directories
569
dstdir = os.path.split(dst)[0]
570
if not os.path.isdir(dstdir):
571
action_mkdir(dstdir, dry)
572
print "cp -f", src, dst
574
shutil.copyfile(src, dst)
576
def action_chown_setuid(file, dry):
577
"""Chowns a file to root, and sets the setuid bit on the file.
578
Calling this function requires the euid to be root.
579
The actual mode of path is set to: rws--s--s
581
print "chown root:root", file
584
print "chmod a+xs", file
585
print "chmod u+rw", file
587
os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
588
| stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
590
464
def query_user(default, prompt):
591
465
"""Prompts the user for a string, which is read from a line of stdin.
592
466
Exits silently if EOF is encountered. Returns the string, with spaces