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