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
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
32
# Creates www/conf/conf.py and trampoline/conf.h.
35
# Compiles all files and sets up a jail template in the source directory.
37
# Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
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.
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
# setup.py install [--nojail] [--dry|n]
53
# Create target install directory ($target).
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).
60
# TODO: List in help, and handle, args for the conf operation
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.
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
78
# Just set reasonable defaults
80
ivle_install_dir = "/opt/ivle"
81
jail_base = "/home/informatics/jails"
85
# Main function skeleton from Guido van Rossum
86
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
88
class Usage(Exception):
89
def __init__(self, msg):
33
92
def main(argv=None):
56
oper_func = call_operator(operation)
57
return oper_func(argv[2:])
115
# Call the requested operation's function
121
'listmake' : listmake,
123
}[operation](argv[2:])
125
print >>sys.stderr, (
126
"""Invalid operation '%s'. Try python setup.py help."""
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"
140
# Operation functions
61
print """Usage: python setup.py operation [options]
144
print """Usage: python setup.py operation [args]
145
Operation (and args) can be:
67
For help and options for a specific operation use 'help [operation]'."""
70
oper_func = call_operator(operator)
71
oper_func(['operator','--help'])
73
def call_operator(operation):
74
# Call the requested operation's function
78
'build' : setup.build.build,
79
'install' : setup.install.install,
149
install [--nojail] [-n|--dry]
153
print """Usage: python setup.py help [operation]"""
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
166
Creates www/conf/conf.py and trampoline/conf.h.
169
elif operation == 'build':
170
print """python setup.py build
171
Compiles all files and sets up a jail template in the source directory.
173
Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
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]
190
Create target install directory ($target).
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).
197
--nojail Do not copy the jail.
198
--dry | -n Print out the actions but don't do anything."""
82
200
print >>sys.stderr, (
83
201
"""Invalid operation '%s'. Try python setup.py help."""
206
global root_dir, ivle_install_dir, jail_base, allowed_uids
207
# Set up some variables
210
# the files that will be created/overwritten
211
conffile = os.path.join(cwd, "www/conf/conf.py")
212
conf_hfile = os.path.join(cwd, "trampoline/conf.h")
214
# Fixed config options that we don't ask the admin
216
default_app = "dummy"
218
print """This tool will create the following files:
221
prompting you for details about your configuration. The file will be
222
overwritten if it already exists. It will *not* install or deploy IVLE.
224
Please hit Ctrl+C now if you do not wish to do this.
225
""" % (conffile, conf_hfile)
227
# Get information from the administrator
228
# If EOF is encountered at any time during the questioning, just exit
231
root_dir = query_user(root_dir,
232
"""Root directory where IVLE is located (in URL space):""")
233
ivle_install_dir = query_user(ivle_install_dir,
234
'Root directory where IVLE will be installed (on the local file '
236
jail_base = query_user(jail_base,
237
"""Root directory where the jails (containing user files) are stored
238
(on the local file system):""")
239
allowed_uids = query_user(allowed_uids,
240
"""UID of the web server process which will run IVLE.
241
Only this user may execute the trampoline. May specify multiple users as
242
a comma-separated list.
245
# Error handling on input values
248
allowed_uids = map(int, allowed_uids.split(','))
250
print >>sys.stderr, (
251
"Invalid UID list (%s).\n"
252
"Must be a comma-separated list of integers." % allowed_uids)
255
# Write www/conf/conf.py
258
conf = open(conffile, "w")
260
conf.write("""# IVLE Configuration File
262
# Miscellaneous application settings
265
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
267
# eg. "/" or "/ivle".
270
# In the local file system, where IVLE is actually installed.
271
# This directory should contain the "www" and "bin" directories.
272
ivle_install_dir = "%s"
274
# In the local file system, where are the student/user file spaces located.
275
# The user jails are expected to be located immediately in subdirectories of
279
# Which application to load by default (if the user navigates to the top level
280
# of the site). This is the app's URL name.
281
# Note that if this app requires authentication, the user will first be
282
# presented with the login screen.
284
""" % (root_dir, ivle_install_dir, jail_base, default_app))
287
except IOError, (errno, strerror):
288
print "IO error(%s): %s" % (errno, strerror)
291
print "Successfully wrote www/conf/conf.py"
293
# Write trampoline/conf.h
296
conf = open(conf_hfile, "w")
298
conf.write("""/* IVLE Configuration File
300
* Administrator settings required by trampoline.
301
* Note: trampoline will have to be rebuilt in order for changes to this file
305
/* In the local file system, where are the jails located.
306
* The trampoline does not allow the creation of a jail anywhere besides
307
* jail_base or a subdirectory of jail_base.
309
static const char* jail_base = "%s";
311
/* Which user IDs are allowed to run the trampoline.
312
* This list should be limited to the web server user.
313
* (Note that root is an implicit member of this list).
315
static const int allowed_uids[] = { %s };
316
""" % (jail_base, repr(allowed_uids)[1:-1]))
319
except IOError, (errno, strerror):
320
print "IO error(%s): %s" % (errno, strerror)
323
print "Successfully wrote trampoline/conf.h"
326
print "You may modify the configuration at any time by editing"
333
dry = False # Set to True later if --dry
335
# Compile the trampoline
336
action_runprog('gcc', ['-Wall', '-o', 'trampoline/trampoline',
337
'trampoline/trampoline.c'], dry)
339
# Create the jail and its subdirectories
341
action_mkdir('jail/bin')
342
action_mkdir('jail/lib')
343
action_mkdir('jail/usr/bin')
344
action_mkdir('jail/usr/lib')
345
action_mkdir('jail/opt/ivle')
346
action_mkdir('jail/home')
347
action_mkdir('jail/tmp')
349
# TODO: Copy console into the jail
350
# TODO: Copy operating system files into the jail
351
# TODO: Compile .py files into .pyc files
363
# The actions call Python os functions but print actions and handle dryness.
364
# May still throw os exceptions if errors occur.
367
"""Represents an error when running a program (nonzero return)."""
368
def __init__(self, prog, retcode):
370
self.retcode = retcode
372
return str(self.prog) + " returned " + repr(self.retcode)
374
def action_runprog(prog, args, dry):
375
"""Runs a unix program. Searches in $PATH. Synchronous (waits for the
376
program to return). Runs in the current environment. First prints the
377
action as a "bash" line.
379
Throws a RunError with a retcode of the return value of the program,
380
if the program did not return 0.
382
prog: String. Name of the program. (No path required, if in $PATH).
383
args: [String]. Arguments to the program.
384
dry: Bool. If True, prints but does not execute.
386
print prog, string.join(args, ' ')
388
ret = os.spawnvp(os.P_WAIT, prog, args)
390
raise RunError(prog, ret)
392
def action_mkdir(path):
393
"""Calls mkdir. Silently ignored if the directory already exists.
394
Creates all parent directories as necessary."""
395
print "mkdir -p", path
398
except OSError, (err, msg):
399
if err != errno.EEXIST:
402
def query_user(default, prompt):
403
"""Prompts the user for a string, which is read from a line of stdin.
404
Exits silently if EOF is encountered. Returns the string, with spaces
405
removed from the beginning and end.
407
Returns default if a 0-length line (after spaces removed) was read.
409
sys.stdout.write('%s\n (default: "%s")\n>' % (prompt, default))
411
val = sys.stdin.readline()
412
except KeyboardInterrupt:
414
sys.stdout.write("\n")
416
sys.stdout.write("\n")
418
if val == '': sys.exit(1)
419
# If empty line, return default
421
if val == '': return default
88
424
if __name__ == "__main__":