~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
316 by drtomc
setup.py - name the configuration command "config" to bring it into line with
34
# setup.py config [args]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
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.
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
38
# Creates lib/conf/conf.py and trampoline/conf.h.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
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
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
71
# Import modules from the website is tricky since they're in the www
72
# directory.
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
73
sys.path.append(os.path.join(os.getcwd(), 'lib'))
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
74
import conf
75
import common.makeuser
76
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
77
# Determine which Python version (2.4 or 2.5, for example) we are running,
78
# and use that as the filename to the Python directory.
79
# Just get the first 3 characters of sys.version.
80
PYTHON_VERSION = sys.version[0:3]
81
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
82
# Operating system files to copy over into the jail.
83
# These will be copied from the given place on the OS file system into the
84
# same place within the jail.
85
JAIL_FILES = [
86
    '/lib/ld-linux.so.2',
87
    '/lib/tls/i686/cmov/libc.so.6',
88
    '/lib/tls/i686/cmov/libdl.so.2',
89
    '/lib/tls/i686/cmov/libm.so.6',
90
    '/lib/tls/i686/cmov/libpthread.so.0',
91
    '/lib/tls/i686/cmov/libutil.so.1',
253 by mattgiuca
setup.py: chmods python-console when building.
92
    '/etc/ld.so.conf',
93
    '/etc/ld.so.cache',
94
    # These 2 files do not exist in Ubuntu
95
    #'/etc/ld.so.preload',
96
    #'/etc/ld.so.nohwcap',
97
    # UNIX commands
98
    '/usr/bin/strace',
99
    '/bin/ls',
100
    '/bin/echo',
101
    # Needed by python
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
102
    '/usr/bin/python%s' % PYTHON_VERSION,
434 by drtomc
setup.py: add a bunch of shared objects to be added to the jail for svn.
103
    # Needed by fileservice
104
    '/lib/libcom_err.so.2',
105
    '/lib/libcrypt.so.1',
106
    '/lib/libkeyutils.so.1',
107
    '/lib/libresolv.so.2',
108
    '/lib/librt.so.1',
109
    '/lib/libuuid.so.1',
110
    '/usr/lib/libapr-1.so.0',
111
    '/usr/lib/libaprutil-1.so.0',
112
    '/usr/lib/libdb-4.4.so',
113
    '/usr/lib/libexpat.so.1',
114
    '/usr/lib/libgcrypt.so.11',
115
    '/usr/lib/libgnutls.so.13',
116
    '/usr/lib/libgpg-error.so.0',
117
    '/usr/lib/libgssapi_krb5.so.2',
118
    '/usr/lib/libk5crypto.so.3',
119
    '/usr/lib/libkrb5.so.3',
120
    '/usr/lib/libkrb5support.so.0',
121
    '/usr/lib/liblber.so.2',
122
    '/usr/lib/libldap_r.so.2',
123
    '/usr/lib/libneon.so.26',
124
    '/usr/lib/libpq.so.5',
125
    '/usr/lib/libsasl2.so.2',
126
    '/usr/lib/libsqlite3.so.0',
127
    '/usr/lib/libsvn_client-1.so.1',
128
    '/usr/lib/libsvn_delta-1.so.1',
129
    '/usr/lib/libsvn_diff-1.so.1',
130
    '/usr/lib/libsvn_fs-1.so.1',
131
    '/usr/lib/libsvn_fs_base-1.so.1',
132
    '/usr/lib/libsvn_fs_fs-1.so.1',
133
    '/usr/lib/libsvn_ra-1.so.1',
134
    '/usr/lib/libsvn_ra_dav-1.so.1',
135
    '/usr/lib/libsvn_ra_local-1.so.1',
136
    '/usr/lib/libsvn_ra_svn-1.so.1',
137
    '/usr/lib/libsvn_repos-1.so.1',
138
    '/usr/lib/libsvn_subr-1.so.1',
139
    '/usr/lib/libsvn_wc-1.so.1',
140
    '/usr/lib/libtasn1.so.3',
141
    '/usr/lib/libxml2.so.2',
253 by mattgiuca
setup.py: chmods python-console when building.
142
    # Needed by matplotlib
143
    '/usr/lib/i686/cmov/libssl.so.0.9.8',
144
    '/usr/lib/i686/cmov/libcrypto.so.0.9.8',
145
    '/lib/tls/i686/cmov/libnsl.so.1',
146
    '/usr/lib/libz.so.1',
147
    '/usr/lib/atlas/liblapack.so.3',
148
    '/usr/lib/atlas/libblas.so.3',
149
    '/usr/lib/libg2c.so.0',
150
    '/usr/lib/libstdc++.so.6',
151
    '/usr/lib/libfreetype.so.6',
152
    '/usr/lib/libpng12.so.0',
153
    '/usr/lib/libBLT.2.4.so.8.4',
154
    '/usr/lib/libtk8.4.so.0',
155
    '/usr/lib/libtcl8.4.so.0',
156
    '/usr/lib/tcl8.4/init.tcl',
157
    '/usr/lib/libX11.so.6',
158
    '/usr/lib/libXau.so.6',
159
    '/usr/lib/libXdmcp.so.6',
160
    '/lib/libgcc_s.so.1',
161
    '/etc/matplotlibrc',
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
162
]
163
# Symlinks to make within the jail. Src mapped to dst.
164
JAIL_LINKS = {
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
165
    'python%s' % PYTHON_VERSION: 'jail/usr/bin/python',
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
166
}
167
# Trees to copy. Src mapped to dst (these will be passed to action_copytree).
168
JAIL_COPYTREES = {
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
169
    '/usr/lib/python%s' % PYTHON_VERSION:
170
        'jail/usr/lib/python%s' % PYTHON_VERSION,
253 by mattgiuca
setup.py: chmods python-console when building.
171
    '/usr/share/matplotlib': 'jail/usr/share/matplotlib',
172
    '/etc/ld.so.conf.d': 'jail/etc/ld.so.conf.d',
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
173
}
174
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
175
class ConfigOption:
176
    """A configuration option; one of the things written to conf.py."""
177
    def __init__(self, option_name, default, prompt, comment):
178
        """Creates a configuration option.
179
        option_name: Name of the variable in conf.py. Also name of the
180
            command-line argument to setup.py conf.
181
        default: Default value for this variable.
182
        prompt: (Short) string presented during the interactive prompt in
183
            setup.py conf.
184
        comment: (Long) comment string stored in conf.py. Each line of this
185
            string should begin with a '#'.
186
        """
187
        self.option_name = option_name
188
        self.default = default
189
        self.prompt = prompt
190
        self.comment = comment
191
192
# Configuration options, defaults and descriptions
193
config_options = []
485 by mattgiuca
setup.py: Changed the defaut root_dir from "/ivle" to "/".
194
config_options.append(ConfigOption("root_dir", "/",
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
195
    """Root directory where IVLE is located (in URL space):""",
196
    """
197
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
198
# with this).
199
# eg. "/" or "/ivle"."""))
200
config_options.append(ConfigOption("ivle_install_dir", "/opt/ivle",
201
    'Root directory where IVLE will be installed (on the local file '
202
    'system):',
203
    """
204
# In the local file system, where IVLE is actually installed.
205
# This directory should contain the "www" and "bin" directories."""))
206
config_options.append(ConfigOption("jail_base", "/home/informatics/jails",
207
    """Root directory where the jails (containing user files) are stored
208
(on the local file system):""",
209
    """
210
# In the local file system, where are the student/user file spaces located.
211
# The user jails are expected to be located immediately in subdirectories of
212
# this location."""))
213
config_options.append(ConfigOption("subjects_base",
214
    "/home/informatics/subjects",
215
    """Root directory where the subject directories (containing worksheets
216
and other per-subject files) are stored (on the local file system):""",
217
    """
218
# In the local file system, where are the per-subject file spaces located.
219
# The individual subject directories are expected to be located immediately
220
# in subdirectories of this location."""))
395 by mattgiuca
Tutorial: split subjects directory into subjects and problems.
221
config_options.append(ConfigOption("problems_base",
222
    "/home/informatics/problems",
223
    """Root directory where the problem directories (containing
224
subject-independent problem sheets) are stored (on the local file
225
system):""",
226
    """
227
# In the local file system, where are the subject-independent problem sheet
228
# file spaces located."""))
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
229
config_options.append(ConfigOption("public_host", "public.localhost",
230
    """Hostname which will cause the server to go into "public mode",
231
providing login-free access to student's published work:""",
232
    """
233
# The server goes into "public mode" if the browser sends a request with this
234
# host. This is for security reasons - we only serve public student files on a
235
# separate domain to the main IVLE site.
236
# Public mode does not use cookies, and serves only public content.
237
# Private mode (normal mode) requires login, and only serves files relevant to
238
# the logged-in user."""))
239
config_options.append(ConfigOption("allowed_uids", "33",
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.
243
    (eg. "1002,78")""",
244
    """
245
# The User-ID of the web server process which will run IVLE, and any other
246
# users who are allowed to run the trampoline. This is stores as a string of
247
# comma-separated integers, simply because it is not used within Python, only
248
# used by the setup program to write to conf.h (see setup.py config)."""))
359 by mattgiuca
setup.py: Added config options for database settings.
249
config_options.append(ConfigOption("db_host", "localhost",
250
    """PostgreSQL Database config
251
==========================
252
Hostname of the DB server:""",
253
    """
254
### PostgreSQL Database config ###
255
# Database server hostname"""))
256
config_options.append(ConfigOption("db_port", "5432",
257
    """Port of the DB server:""",
258
    """
259
# Database server port"""))
363 by mattgiuca
setup.py: Added new config option - "database name"
260
config_options.append(ConfigOption("db_dbname", "ivle",
261
    """Database name:""",
262
    """
263
# Database name"""))
359 by mattgiuca
setup.py: Added config options for database settings.
264
config_options.append(ConfigOption("db_user", "postgres",
265
    """Username for DB server login:""",
266
    """
267
# Database username"""))
268
config_options.append(ConfigOption("db_password", "",
269
    """Password for DB server login:
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
270
    (Caution: This password is stored in plaintext in lib/conf/conf.py)""",
359 by mattgiuca
setup.py: Added config options for database settings.
271
    """
272
# Database password"""))
510 by mattgiuca
setup.py: Added 2 new config options for the LDAP authentication server.
273
274
config_options.append(ConfigOption("ldap_url", "ldaps://www.example.com",
275
    """URL for LDAP authentication server:""",
276
    """
277
# URL for LDAP authentication server"""))
278
config_options.append(ConfigOption("ldap_format_string",
279
    "uid=%s,ou=users,o=example",
280
    """Format string for LDAP auth request:
281
    (Must contain a single "%s" for the user's login name)""",
282
    """
283
# Format string for LDAP auth request
284
# (Must contain a single "%s" for the user's login name)"""))
285
460 by drtomc
setup.py: Add a bunch of config stuff we need.
286
config_options.append(ConfigOption("svn_conf", "/opt/ivle/svn/svn.conf",
287
    """The location of the subversion configuration file used by apache
288
to host the user repositories:""",
289
    """
290
# The location of the subversion configuration file used by
291
# apache to host the user repositories."""))
467 by drtomc
makeuser: Add some of the helper functions for activating users.
292
config_options.append(ConfigOption("svn_repo_path", "/home/informatics/repositories",
293
    """The root directory for the subversion repositories:""",
294
    """
295
# The root directory for the subversion repositories."""))
460 by drtomc
setup.py: Add a bunch of config stuff we need.
296
config_options.append(ConfigOption("svn_auth_ivle", "/opt/ivle/svn/ivle.auth",
297
    """The location of the password file used to authenticate users
298
of the subversion repository from the ivle server:""",
299
    """
300
# The location of the password file used to authenticate users
301
# of the subversion repository from the ivle server."""))
302
config_options.append(ConfigOption("svn_auth_local", "/opt/ivle/svn/local.auth",
303
    """The location of the password file used to authenticate local users
304
of the subversion repository:""",
305
    """
306
# The location of the password file used to authenticate local users
307
# of the subversion repository."""))
308
config_options.append(ConfigOption("usrmgt_host", "localhost",
309
    """The hostname where the usrmgt-server runs:""",
310
    """
311
# The hostname where the usrmgt-server runs."""))
312
config_options.append(ConfigOption("usrmgt_port", "2178",
313
    """The port where the usrmgt-server runs:""",
314
    """
315
# The port where the usrmgt-server runs."""))
316
config_options.append(ConfigOption("usrmgt_magic", "",
317
    """The password for the usrmgt-server:""",
318
    """
319
# The password for the usrmgt-server."""))
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
320
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
321
# Try importing existing conf, but if we can't just set up defaults
322
# The reason for this is that these settings are used by other phases
323
# of setup besides conf, so we need to know them.
324
# Also this allows you to hit Return to accept the existing value.
325
try:
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
326
    confmodule = __import__("lib/conf/conf")
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
327
    for opt in config_options:
328
        try:
329
            globals()[opt.option_name] = confmodule.__dict__[opt.option_name]
330
        except:
331
            globals()[opt.option_name] = opt.default
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
332
except ImportError:
333
    # Just set reasonable defaults
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
334
    for opt in config_options:
335
        globals()[opt.option_name] = opt.default
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
336
118 by mattgiuca
setup.py: Added copytree and copylist actions.
337
# Try importing install_list, but don't fail if we can't, because listmake can
338
# function without it.
339
try:
340
    import install_list
341
except:
342
    pass
343
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
344
# Mime types which will automatically be placed in the list by listmake.
345
# Note that listmake is not intended to be run by the final user (the system
346
# administrator who installs this), so the developers can customize the list
347
# as necessary, and include it in the distribution.
348
listmake_mimetypes = ['text/x-python', 'text/html',
349
    'application/x-javascript', 'application/javascript',
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
350
    'text/css', 'image/png', 'application/xml']
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
351
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
352
# Main function skeleton from Guido van Rossum
353
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
354
355
def main(argv=None):
356
    if argv is None:
357
        argv = sys.argv
358
359
    # Print the opening spiel including the GPL notice
360
361
    print """IVLE - Informatics Virtual Learning Environment Setup
97 by mattgiuca
Moved template.py and setup.py to better places.
362
Copyright (C) 2007-2008 The University of Melbourne
363
IVLE comes with ABSOLUTELY NO WARRANTY.
364
This is free software, and you are welcome to redistribute it
365
under certain conditions. See LICENSE.txt for details.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
366
367
IVLE Setup
368
"""
369
370
    # First argument is the name of the setup operation
371
    try:
372
        operation = argv[1]
373
    except IndexError:
374
        # Print usage message and exit
375
        help([])
376
        return 1
377
131 by mattgiuca
setup.py:
378
    # Disallow run as root unless installing
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
379
    if (operation != 'install' and operation != 'updatejails'
380
        and os.geteuid() == 0):
131 by mattgiuca
setup.py:
381
        print >>sys.stderr, "I do not want to run this stage as root."
382
        print >>sys.stderr, "Please run as a normal user."
383
        return 1
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
384
    # Call the requested operation's function
385
    try:
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
386
        oper_func = {
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
387
            'help' : help,
316 by drtomc
setup.py - name the configuration command "config" to bring it into line with
388
            'config' : conf,
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
389
            'build' : build,
390
            'listmake' : listmake,
391
            'install' : install,
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
392
            'updatejails' : updatejails,
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
393
        }[operation]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
394
    except KeyError:
395
        print >>sys.stderr, (
396
            """Invalid operation '%s'. Try python setup.py help."""
397
            % operation)
129 by mattgiuca
setup.py: Minor fix, exits cleanly if arguments are invalid.
398
        return 1
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
399
    return oper_func(argv[2:])
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
400
401
# Operation functions
402
403
def help(args):
404
    if args == []:
405
        print """Usage: python setup.py operation [args]
406
Operation (and args) can be:
407
    help [operation]
131 by mattgiuca
setup.py:
408
    listmake (developer use only)
316 by drtomc
setup.py - name the configuration command "config" to bring it into line with
409
    config [args]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
410
    build
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
411
    install [--nojail] [--nosubjects] [-n|--dry]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
412
"""
413
        return 1
414
    elif len(args) != 1:
415
        print """Usage: python setup.py help [operation]"""
416
        return 2
417
    else:
418
        operation = args[0]
419
420
    if operation == 'help':
421
        print """python setup.py help [operation]
422
Prints the usage message or detailed help on an operation, then exits."""
118 by mattgiuca
setup.py: Added copytree and copylist actions.
423
    elif operation == 'listmake':
424
        print """python setup.py listmake
425
(For developer use only)
426
Recurses through the source tree and builds a list of all files which should
427
be copied upon installation. This should be run by the developer before
428
cutting a distribution, and the listfile it generates should be included in
429
the distribution, avoiding the administrator having to run it."""
316 by drtomc
setup.py - name the configuration command "config" to bring it into line with
430
    elif operation == 'config':
431
        print """python setup.py config [args]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
432
Configures IVLE with machine-specific details, most notably, various paths.
433
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!
434
command-line args. Will be interactive only if there are no arguments given.
435
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
436
437
To run IVLE out of the source directory (allowing development without having
438
to rebuild/install), just provide ivle_install_dir as the IVLE trunk
439
directory, and run build/install one time.
440
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
441
Creates lib/conf/conf.py and trampoline/conf.h.
133 by mattgiuca
setup.py: Now allows IVLE to be installed over itself, in order to allow
442
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
443
Args are:"""
444
        for opt in config_options:
445
            print "    --" + opt.option_name
446
        print """As explained in the interactive prompt or conf.py.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
447
"""
448
    elif operation == 'build':
118 by mattgiuca
setup.py: Added copytree and copylist actions.
449
        print """python -O setup.py build [--dry|-n]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
450
Compiles all files and sets up a jail template in the source directory.
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
451
-O is recommended to cause compilation to be optimised.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
452
Details:
453
Compiles (GCC) trampoline/trampoline.c to trampoline/trampoline.
454
Creates jail/.
455
Creates standard subdirs inside the jail, eg bin, opt, home, tmp.
456
Copies console/ to a location within the jail.
457
Copies OS programs and files to corresponding locations within the jail
458
  (eg. python and Python libs, ld.so, etc).
118 by mattgiuca
setup.py: Added copytree and copylist actions.
459
Generates .pyc or .pyo files for all the IVLE .py files.
460
461
--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
462
    elif operation == 'install':
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
463
        print """sudo python setup.py install [--nojail] [--nosubjects][--dry|-n]
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
464
(Requires root)
465
Create target install directory ($target).
466
Create $target/bin.
467
Copy trampoline/trampoline to $target/bin.
468
chown and chmod the installed trampoline.
469
Copy www/ to $target.
470
Copy jail/ to jails template directory (unless --nojail specified).
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
471
Copy subjects/ to subjects directory (unless --nosubjects specified).
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
472
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
473
--nojail        Do not copy the jail.
395 by mattgiuca
Tutorial: split subjects directory into subjects and problems.
474
--nosubjects    Do not copy the subjects and problems directories.
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
475
--dry | -n  Print out the actions but don't do anything."""
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
476
    elif operation == 'updatejails':
477
        print """sudo python setup.py updatejails [--dry|-n]
478
(Requires root)
479
Copy jail/ to each subdirectory in jails directory.
480
481
--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
482
    else:
483
        print >>sys.stderr, (
484
            """Invalid operation '%s'. Try python setup.py help."""
485
            % operation)
486
    return 1
487
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
488
def listmake(args):
489
    # We build two separate lists, by walking www and console
490
    list_www = build_list_py_files('www')
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
491
    list_lib = build_list_py_files('lib')
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
492
    list_subjects = build_list_py_files('subjects', no_top_level=True)
395 by mattgiuca
Tutorial: split subjects directory into subjects and problems.
493
    list_problems = build_list_py_files('problems', no_top_level=True)
477 by mattgiuca
setup.py: Fixed creation of "scripts" directory in listmake.
494
    list_scripts = [
495
        "scripts/python-console",
496
        "scripts/fileservice",
497
        "scripts/usrmgt-server",
498
    ]
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
499
    # Make sure that the files generated by conf are in the list
500
    # (since listmake is typically run before conf)
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
501
    if "lib/conf/conf.py" not in list_lib:
427 by mattgiuca
setup.py: Fix (put conf.py in wrong list in listmake)
502
        list_lib.append("lib/conf/conf.py")
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
503
    # Write these out to a file
504
    cwd = os.getcwd()
505
    # the files that will be created/overwritten
506
    listfile = os.path.join(cwd, "install_list.py")
507
508
    try:
509
        file = open(listfile, "w")
510
511
        file.write("""# IVLE Configuration File
512
# install_list.py
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
513
# Provides lists of all files to be installed by `setup.py install' from
514
# certain directories.
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
515
# Note that any files with the given filename plus 'c' or 'o' (that is,
516
# compiled .pyc or .pyo files) will be copied as well.
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
517
117 by mattgiuca
setup.py: listmake uses mimetypes to select all files with certain mime types,
518
# List of all installable files in www directory.
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
519
list_www = """)
520
        writelist_pretty(file, list_www)
521
        file.write("""
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
522
# List of all installable files in lib directory.
523
list_lib = """)
524
        writelist_pretty(file, list_lib)
525
        file.write("""
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
526
# List of all installable files in scripts directory.
527
list_scripts = """)
528
        writelist_pretty(file, list_scripts)
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
529
        file.write("""
530
# List of all installable files in subjects directory.
531
# This is to install sample subjects and material.
532
list_subjects = """)
533
        writelist_pretty(file, list_subjects)
395 by mattgiuca
Tutorial: split subjects directory into subjects and problems.
534
        file.write("""
535
# List of all installable files in problems directory.
536
# This is to install sample exercise material.
537
list_problems = """)
538
        writelist_pretty(file, list_problems)
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
539
540
        file.close()
541
    except IOError, (errno, strerror):
542
        print "IO error(%s): %s" % (errno, strerror)
543
        sys.exit(1)
544
545
    print "Successfully wrote install_list.py"
546
547
    print
548
    print ("You may modify the set of installable files before cutting the "
549
            "distribution:")
550
    print listfile
551
    print
552
553
    return 0
554
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
555
def build_list_py_files(dir, no_top_level=False):
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
556
    """Builds a list of all py files found in a directory and its
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
557
    subdirectories. Returns this as a list of strings.
558
    no_top_level=True means the file paths will not include the top-level
559
    directory.
560
    """
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
561
    pylist = []
562
    for (dirpath, dirnames, filenames) in os.walk(dir):
563
        # Exclude directories beginning with a '.' (such as '.svn')
564
        filter_mutate(lambda x: x[0] != '.', dirnames)
565
        # All *.py files are added to the list
566
        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,
567
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
568
    if no_top_level:
569
        for i in range(0, len(pylist)):
570
            _, pylist[i] = pylist[i].split(os.sep, 1)
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
571
    return pylist
572
573
def writelist_pretty(file, list):
574
    """Writes a list one element per line, to a file."""
575
    if list == []:
576
        file.write("[]\n")
577
    else:
578
        file.write('[\n')
579
        for elem in list:
580
            file.write('    %s,\n' % repr(elem))
581
        file.write(']\n')
582
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
583
def conf(args):
487 by mattgiuca
setup.py: Added code to convert usrmgt_port into an int before writing to conf
584
    global db_port, usrmgt_port
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
585
    # Set up some variables
586
587
    cwd = os.getcwd()
588
    # the files that will be created/overwritten
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
589
    conffile = os.path.join(cwd, "lib/conf/conf.py")
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
590
    jailconffile = os.path.join(cwd, "lib/conf/jailconf.py")
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
591
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
592
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
593
    # Get command-line arguments to avoid asking questions.
594
364 by mattgiuca
setup.py: Fixed config command-line args (forgot to make general).
595
    optnames = []
596
    for opt in config_options:
597
        optnames.append(opt.option_name + "=")
598
    (opts, args) = getopt.gnu_getopt(args, "", optnames)
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
599
600
    if args != []:
601
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
602
        return 2
603
604
    if opts == []:
605
        # Interactive mode. Prompt the user for all the values.
606
607
        print """This tool will create the following files:
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
608
    %s
609
    %s
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
610
    %s
97 by mattgiuca
Moved template.py and setup.py to better places.
611
prompting you for details about your configuration. The file will be
612
overwritten if it already exists. It will *not* install or deploy IVLE.
613
614
Please hit Ctrl+C now if you do not wish to do this.
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
615
""" % (conffile, jailconffile, conf_hfile)
97 by mattgiuca
Moved template.py and setup.py to better places.
616
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
617
        # Get information from the administrator
618
        # If EOF is encountered at any time during the questioning, just exit
619
        # silently
97 by mattgiuca
Moved template.py and setup.py to better places.
620
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
621
        for opt in config_options:
622
            globals()[opt.option_name] = \
623
                query_user(globals()[opt.option_name], opt.prompt)
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
624
    else:
625
        opts = dict(opts)
626
        # Non-interactive mode. Parse the options.
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
627
        for opt in config_options:
628
            if '--' + opt.option_name in opts:
629
                globals()[opt.option_name] = opts['--' + opt.option_name]
120 by mattgiuca
setup.py: Added command-line argument mode for conf. This completely works!
630
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
631
    # Error handling on input values
632
    try:
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
633
        allowed_uids_list = map(int, allowed_uids.split(','))
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
634
    except ValueError:
112 by mattgiuca
setup.py: A few comment changes.
635
        print >>sys.stderr, (
636
        "Invalid UID list (%s).\n"
637
        "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
638
        return 1
364 by mattgiuca
setup.py: Fixed config command-line args (forgot to make general).
639
    try:
640
        db_port = int(db_port)
641
        if db_port < 0 or db_port >= 65536: raise ValueError()
642
    except ValueError:
643
        print >>sys.stderr, (
644
        "Invalid DB port (%s).\n"
645
        "Must be an integer between 0 and 65535." % repr(db_port))
646
        return 1
487 by mattgiuca
setup.py: Added code to convert usrmgt_port into an int before writing to conf
647
    try:
648
        usrmgt_port = int(usrmgt_port)
649
        if usrmgt_port < 0 or usrmgt_port >= 65536: raise ValueError()
650
    except ValueError:
651
        print >>sys.stderr, (
652
        "Invalid user management port (%s).\n"
653
        "Must be an integer between 0 and 65535." % repr(usrmgt_port))
654
        return 1
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
655
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
656
    # Write lib/conf/conf.py
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
657
658
    try:
659
        conf = open(conffile, "w")
660
661
        conf.write("""# IVLE Configuration File
97 by mattgiuca
Moved template.py and setup.py to better places.
662
# conf.py
663
# Miscellaneous application settings
664
358 by mattgiuca
setup.py: Gutted out the config options code. It was getting so there were
665
""")
666
        for opt in config_options:
364 by mattgiuca
setup.py: Fixed config command-line args (forgot to make general).
667
            conf.write('%s\n%s = %s\n' % (opt.comment, opt.option_name,
668
                repr(globals()[opt.option_name])))
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
669
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
670
        conf.close()
671
    except IOError, (errno, strerror):
672
        print "IO error(%s): %s" % (errno, strerror)
673
        sys.exit(1)
674
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
675
    print "Successfully wrote lib/conf/conf.py"
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
676
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
677
    # Write conf/jailconf.py
678
679
    try:
680
        conf = open(jailconffile, "w")
681
682
        # In the "in-jail" version of conf, we don't need MOST of the details
683
        # (it would be a security risk to have them here).
684
        # So we just write root_dir, and jail_base is "/".
685
        # (jail_base being "/" means "jail-relative" paths are relative to "/"
686
        # when inside the jail.)
687
        conf.write("""# IVLE Configuration File
688
# conf.py
689
# Miscellaneous application settings
690
# (User jail version)
691
692
693
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
694
# with this).
695
# eg. "/" or "/ivle".
696
root_dir = %s
697
698
# In the local file system, where are the student/user file spaces located.
699
# The user jails are expected to be located immediately in subdirectories of
700
# this location.
701
jail_base = '/'
435 by drtomc
setup.py: Fix a couple of jail config glitches.
702
703
# The hostname for serving publicly accessible pages
704
public_host = %s
705
""" % (repr(root_dir),repr(public_host)))
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
706
707
        conf.close()
708
    except IOError, (errno, strerror):
709
        print "IO error(%s): %s" % (errno, strerror)
710
        sys.exit(1)
711
712
    print "Successfully wrote lib/conf/jailconf.py"
713
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
714
    # Write trampoline/conf.h
715
716
    try:
717
        conf = open(conf_hfile, "w")
718
719
        conf.write("""/* IVLE Configuration File
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
720
 * conf.h
721
 * Administrator settings required by trampoline.
722
 * Note: trampoline will have to be rebuilt in order for changes to this file
723
 * to take effect.
724
 */
725
726
/* In the local file system, where are the jails located.
727
 * The trampoline does not allow the creation of a jail anywhere besides
728
 * jail_base or a subdirectory of jail_base.
729
 */
730
static const char* jail_base = "%s";
110 by mattgiuca
setup.py: Added to trampoline/conf.h an "allowed_uids" array. Asks the user
731
732
/* Which user IDs are allowed to run the trampoline.
733
 * This list should be limited to the web server user.
734
 * (Note that root is an implicit member of this list).
735
 */
112 by mattgiuca
setup.py: A few comment changes.
736
static const int allowed_uids[] = { %s };
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
737
""" % (repr(jail_base)[1:-1], repr(allowed_uids_list)[1:-1]))
738
    # Note: The above uses PYTHON reprs, not C reprs
739
    # However they should be the same with the exception of the outer
740
    # characters, which are stripped off and replaced
100 by mattgiuca
setup.py: Added a new config variable, ivle_install_dir.
741
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
742
        conf.close()
743
    except IOError, (errno, strerror):
744
        print "IO error(%s): %s" % (errno, strerror)
745
        sys.exit(1)
746
747
    print "Successfully wrote trampoline/conf.h"
748
749
    print
750
    print "You may modify the configuration at any time by editing"
751
    print conffile
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
752
    print jailconffile
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
753
    print conf_hfile
754
    print
755
    return 0
756
757
def build(args):
121 by mattgiuca
setup: build and install now read command line options
758
    # Get "dry" variable from command line
759
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
760
    opts = dict(opts)
761
    dry = '-n' in opts or '--dry' in opts
762
763
    if dry:
764
        print "Dry run (no actions will be executed\n"
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
765
766
    # Compile the trampoline
349 by mattgiuca
setup.py: Changed so instead of directly calling gcc on the trampoline, calls
767
    curdir = os.getcwd()
768
    os.chdir('trampoline')
769
    action_runprog('make', [], dry)
770
    os.chdir(curdir)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
771
772
    # Create the jail and its subdirectories
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
773
    # Note: Other subdirs will be made by copying files
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
774
    action_mkdir('jail', dry)
775
    action_mkdir('jail/home', dry)
776
    action_mkdir('jail/tmp', dry)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
777
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
778
    # Copy all console and operating system files into the jail
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
779
    action_copylist(install_list.list_scripts, 'jail/opt/ivle', dry)
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
780
    copy_os_files_jail(dry)
253 by mattgiuca
setup.py: chmods python-console when building.
781
    # Chmod the python console
418 by mattgiuca
Renamed trunk/console to trunk/scripts. We are now able to put more scripts in
782
    action_chmod_x('jail/opt/ivle/scripts/python-console', dry)
783
    action_chmod_x('jail/opt/ivle/scripts/fileservice', dry)
253 by mattgiuca
setup.py: chmods python-console when building.
784
    
422 by mattgiuca
setup.py: Needs to copy the "lib" directory into the jail. Now does this and
785
    # Also copy the IVLE lib directory into the jail
786
    # This is necessary for running certain scripts
787
    action_copylist(install_list.list_lib, 'jail/opt/ivle', dry)
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
788
    # IMPORTANT: The file jail/opt/ivle/lib/conf/conf.py contains details
789
    # which could compromise security if left in the jail (such as the DB
790
    # password).
791
    # The "safe" version is in jailconf.py. Delete conf.py and replace it with
792
    # jailconf.py.
435 by drtomc
setup.py: Fix a couple of jail config glitches.
793
    action_copyfile('lib/conf/jailconf.py',
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
794
        'jail/opt/ivle/lib/conf/conf.py', dry)
118 by mattgiuca
setup.py: Added copytree and copylist actions.
795
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
796
    # Compile .py files into .pyc or .pyo files
797
    compileall.compile_dir('www', quiet=True)
422 by mattgiuca
setup.py: Needs to copy the "lib" directory into the jail. Now does this and
798
    compileall.compile_dir('lib', quiet=True)
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
799
    compileall.compile_dir('scripts', quiet=True)
422 by mattgiuca
setup.py: Needs to copy the "lib" directory into the jail. Now does this and
800
    compileall.compile_dir('jail/opt/ivle/lib', quiet=True)
801
802
    # Set up ivle.pth inside the jail
803
    # Need to set /opt/ivle/lib to be on the import path
804
    ivle_pth = \
805
        "jail/usr/lib/python%s/site-packages/ivle.pth" % PYTHON_VERSION
806
    f = open(ivle_pth, 'w')
807
    f.write('/opt/ivle/lib\n')
808
    f.close()
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
809
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
810
    return 0
811
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
812
def copy_os_files_jail(dry):
813
    """Copies necessary Operating System files from their usual locations
814
    into the jail/ directory of the cwd."""
815
    # Currently source paths are configured for Ubuntu.
251 by mattgiuca
setup.py: Worked the "files to copy into jail" out into a separate list
816
    for filename in JAIL_FILES:
817
        copy_file_to_jail(filename, dry)
818
    for src, dst in JAIL_LINKS.items():
819
        action_symlink(src, dst, dry)
820
    for src, dst in JAIL_COPYTREES.items():
821
        action_copytree(src, dst, dry)
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
822
823
def copy_file_to_jail(src, dry):
824
    """Copies a single file from an absolute location into the same location
825
    within the jail. src must begin with a '/'. The jail will be located
826
    in a 'jail' subdirectory of the current path."""
827
    action_copyfile(src, 'jail' + src, dry)
828
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
829
def install(args):
121 by mattgiuca
setup: build and install now read command line options
830
    # Get "dry" and "nojail" variables from command line
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
831
    (opts, args) = getopt.gnu_getopt(args, "n",
832
        ['dry', 'nojail', 'nosubjects'])
121 by mattgiuca
setup: build and install now read command line options
833
    opts = dict(opts)
834
    dry = '-n' in opts or '--dry' in opts
835
    nojail = '--nojail' in opts
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
836
    nosubjects = '--nosubjects' in opts
121 by mattgiuca
setup: build and install now read command line options
837
838
    if dry:
839
        print "Dry run (no actions will be executed\n"
119 by mattgiuca
setup.py: Added install action. Completely works!
840
841
    if not dry and os.geteuid() != 0:
842
        print >>sys.stderr, "Must be root to run install"
843
        print >>sys.stderr, "(I need to chown some files)."
844
        return 1
845
846
    # Create the target (install) directory
847
    action_mkdir(ivle_install_dir, dry)
848
849
    # Create bin and copy the compiled files there
850
    action_mkdir(os.path.join(ivle_install_dir, 'bin'), dry)
851
    tramppath = os.path.join(ivle_install_dir, 'bin/trampoline')
852
    action_copyfile('trampoline/trampoline', tramppath, dry)
853
    # chown trampoline to root and set setuid bit
854
    action_chown_setuid(tramppath, dry)
855
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
856
    # Copy the www and lib directories using the list
119 by mattgiuca
setup.py: Added install action. Completely works!
857
    action_copylist(install_list.list_www, ivle_install_dir, dry)
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
858
    action_copylist(install_list.list_lib, ivle_install_dir, dry)
119 by mattgiuca
setup.py: Added install action. Completely works!
859
860
    if not nojail:
861
        # Copy the local jail directory built by the build action
862
        # to the jails template directory (it will be used as a template
863
        # for all the students' jails).
864
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
865
    if not nosubjects:
395 by mattgiuca
Tutorial: split subjects directory into subjects and problems.
866
        # Copy the subjects and problems directories across
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
867
        action_copylist(install_list.list_subjects, subjects_base, dry,
868
            srcdir="./subjects")
395 by mattgiuca
Tutorial: split subjects directory into subjects and problems.
869
        action_copylist(install_list.list_problems, problems_base, dry,
870
            srcdir="./problems")
119 by mattgiuca
setup.py: Added install action. Completely works!
871
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
872
    # Append IVLE path to ivle.pth in python site packages
873
    # (Unless it's already there)
874
    ivle_pth = os.path.join(sys.prefix,
317 by mattgiuca
doc/dependencies: Added dependency on matplotlib.
875
        "lib/python%s/site-packages/ivle.pth" % PYTHON_VERSION)
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
876
    ivle_www = os.path.join(ivle_install_dir, "www")
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
877
    ivle_lib = os.path.join(ivle_install_dir, "lib")
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
878
    write_ivle_pth = True
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
879
    write_ivle_lib_pth = True
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
880
    try:
881
        file = open(ivle_pth, 'r')
882
        for line in file:
883
            if line.strip() == ivle_www:
884
                write_ivle_pth = False
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
885
            elif line.strip() == ivle_lib:
886
                write_ivle_lib_pth = False
887
        file.close()
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
888
    except (IOError, OSError):
889
        pass
890
    if write_ivle_pth:
891
        action_append(ivle_pth, ivle_www)
409 by mattgiuca
Moved www/conf and www/common to a new directory lib. This separates the "web"
892
    if write_ivle_lib_pth:
893
        action_append(ivle_pth, ivle_lib)
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
894
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
895
    return 0
896
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
897
def updatejails(args):
898
    # Get "dry" variable from command line
899
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
900
    opts = dict(opts)
901
    dry = '-n' in opts or '--dry' in opts
902
903
    if dry:
904
        print "Dry run (no actions will be executed\n"
905
906
    if not dry and os.geteuid() != 0:
907
        print >>sys.stderr, "Must be root to run install"
908
        print >>sys.stderr, "(I need to chown some files)."
909
        return 1
910
911
    # Update the template jail directory in case it hasn't been installed
912
    # recently.
913
    action_copytree('jail', os.path.join(jail_base, 'template'), dry)
914
915
    # Re-link all the files in all students jails.
916
    for dir in os.listdir(jail_base):
917
        if dir == 'template': continue
918
        # First back up the student's home directory
919
        temp_home = os.tmpnam()
920
        action_rename(os.path.join(jail_base, dir, 'home'), temp_home, dry)
921
        # Delete the student's jail and relink the jail files
922
        action_linktree(os.path.join(jail_base, 'template'),
923
            os.path.join(jail_base, dir), dry)
924
        # Restore the student's home directory
925
        action_rename(temp_home, os.path.join(jail_base, dir, 'home'), dry)
926
        # Set up the user's home directory just in case they don't have a
927
        # directory for this yet
928
        action_mkdir(os.path.join(jail_base, dir, 'home', dir), dry)
929
930
    return 0
931
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
932
# The actions call Python os functions but print actions and handle dryness.
933
# May still throw os exceptions if errors occur.
934
108 by mattgiuca
setup.py: Added RunError class. action_runprog now throws a RunError if the
935
class RunError:
936
    """Represents an error when running a program (nonzero return)."""
937
    def __init__(self, prog, retcode):
938
        self.prog = prog
939
        self.retcode = retcode
940
    def __str__(self):
941
        return str(self.prog) + " returned " + repr(self.retcode)
942
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
943
def action_runprog(prog, args, dry):
944
    """Runs a unix program. Searches in $PATH. Synchronous (waits for the
945
    program to return). Runs in the current environment. First prints the
946
    action as a "bash" line.
947
108 by mattgiuca
setup.py: Added RunError class. action_runprog now throws a RunError if the
948
    Throws a RunError with a retcode of the return value of the program,
949
    if the program did not return 0.
950
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
951
    prog: String. Name of the program. (No path required, if in $PATH).
952
    args: [String]. Arguments to the program.
953
    dry: Bool. If True, prints but does not execute.
954
    """
955
    print prog, string.join(args, ' ')
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
956
    if dry: return
957
    ret = os.spawnvp(os.P_WAIT, prog, args)
958
    if ret != 0:
959
        raise RunError(prog, ret)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
960
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
961
def action_remove(path, dry):
962
    """Calls rmtree, deleting the target file if it exists."""
434 by drtomc
setup.py: add a bunch of shared objects to be added to the jail for svn.
963
    try:
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
964
        print "rm -r", path
965
        if not dry:
966
            shutil.rmtree(path, True)
434 by drtomc
setup.py: add a bunch of shared objects to be added to the jail for svn.
967
    except OSError, (err, msg):
968
        if err != errno.EEXIST:
969
            raise
970
        # Otherwise, didn't exist, so we don't care
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
971
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
972
def action_rename(src, dst, dry):
973
    """Calls rename. Deletes the target if it already exists."""
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
974
    action_remove(dst, dry)
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
975
    print "mv ", src, dst
976
    if dry: return
977
    try:
978
        os.rename(src, dst)
979
    except OSError, (err, msg):
980
        if err != errno.EEXIST:
981
            raise
982
116 by mattgiuca
setup.py: mkdir now properly obeys "dry".
983
def action_mkdir(path, dry):
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
984
    """Calls mkdir. Silently ignored if the directory already exists.
985
    Creates all parent directories as necessary."""
986
    print "mkdir -p", path
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
987
    if dry: return
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
988
    try:
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
989
        os.makedirs(path)
107 by mattgiuca
setup.py: Added "action" functions which encapsulate calling OS functions.
990
    except OSError, (err, msg):
991
        if err != errno.EEXIST:
992
            raise
993
118 by mattgiuca
setup.py: Added copytree and copylist actions.
994
def action_copytree(src, dst, dry):
995
    """Copies an entire directory tree. Symlinks are seen as normal files and
996
    copies of the entire file (not the link) are made. Creates all parent
997
    directories as necessary.
998
999
    See shutil.copytree."""
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
1000
    action_remove(dst, dry)
118 by mattgiuca
setup.py: Added copytree and copylist actions.
1001
    print "cp -r", src, dst
1002
    if dry: return
132 by mattgiuca
setup.py: File copying is more robust - preserves symlinks and permissions
1003
    shutil.copytree(src, dst, True)
118 by mattgiuca
setup.py: Added copytree and copylist actions.
1004
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
1005
def action_linktree(src, dst, dry):
1006
    """Hard-links an entire directory tree. Same as copytree but the created
1007
    files are hard-links not actual copies. Removes the existing destination.
1008
    """
433 by mattgiuca
setup.py: Conf now writes another file, lib/conf/jailconf.py. This file is a
1009
    action_remove(dst, dry)
252 by mattgiuca
setup.py: Added action "updatejails" which wipes all student jails, replacing
1010
    print "<cp with hardlinks> -r", src, dst
1011
    if dry: return
1012
    common.makeuser.linktree(src, dst)
1013
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
1014
def action_copylist(srclist, dst, dry, srcdir="."):
118 by mattgiuca
setup.py: Added copytree and copylist actions.
1015
    """Copies all files in a list to a new location. The files in the list
1016
    are read relative to the current directory, and their destinations are the
1017
    same paths relative to dst. Creates all parent directories as necessary.
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
1018
    srcdir is "." by default, can be overridden.
118 by mattgiuca
setup.py: Added copytree and copylist actions.
1019
    """
1020
    for srcfile in srclist:
1021
        dstfile = os.path.join(dst, srcfile)
313 by mattgiuca
test/test_framework: Updated examples, a bit of better descriptions, sample
1022
        srcfile = os.path.join(srcdir, srcfile)
118 by mattgiuca
setup.py: Added copytree and copylist actions.
1023
        dstdir = os.path.split(dstfile)[0]
1024
        if not os.path.isdir(dstdir):
1025
            action_mkdir(dstdir, dry)
1026
        print "cp -f", srcfile, dstfile
1027
        if not dry:
133 by mattgiuca
setup.py: Now allows IVLE to be installed over itself, in order to allow
1028
            try:
1029
                shutil.copyfile(srcfile, dstfile)
1030
                shutil.copymode(srcfile, dstfile)
1031
            except shutil.Error:
1032
                pass
118 by mattgiuca
setup.py: Added copytree and copylist actions.
1033
119 by mattgiuca
setup.py: Added install action. Completely works!
1034
def action_copyfile(src, dst, dry):
1035
    """Copies one file to a new location. Creates all parent directories
1036
    as necessary.
318 by mattgiuca
setup.py: action_copyfile now handles errors as warnings, instead of quitting
1037
    Warn if file not found.
119 by mattgiuca
setup.py: Added install action. Completely works!
1038
    """
1039
    dstdir = os.path.split(dst)[0]
1040
    if not os.path.isdir(dstdir):
1041
        action_mkdir(dstdir, dry)
1042
    print "cp -f", src, dst
1043
    if not dry:
133 by mattgiuca
setup.py: Now allows IVLE to be installed over itself, in order to allow
1044
        try:
1045
            shutil.copyfile(src, dst)
1046
            shutil.copymode(src, dst)
318 by mattgiuca
setup.py: action_copyfile now handles errors as warnings, instead of quitting
1047
        except (shutil.Error, IOError), e:
1048
            print "Warning: " + str(e)
119 by mattgiuca
setup.py: Added install action. Completely works!
1049
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
1050
def action_symlink(src, dst, dry):
1051
    """Creates a symlink in a given location. Creates all parent directories
1052
    as necessary.
1053
    """
1054
    dstdir = os.path.split(dst)[0]
1055
    if not os.path.isdir(dstdir):
1056
        action_mkdir(dstdir, dry)
131 by mattgiuca
setup.py:
1057
    # Delete existing file
1058
    if os.path.exists(dst):
1059
        os.remove(dst)
1060
    print "ln -fs", src, dst
122 by mattgiuca
setup.py: build action now copies all operating system files into the jail.
1061
    if not dry:
1062
        os.symlink(src, dst)
1063
256 by mattgiuca
Changed the way IVLE's path is loaded into Python's sys.path. Now a file
1064
def action_append(ivle_pth, ivle_www):
1065
    file = open(ivle_pth, 'a+')
1066
    file.write(ivle_www + '\n')
1067
    file.close()
1068
119 by mattgiuca
setup.py: Added install action. Completely works!
1069
def action_chown_setuid(file, dry):
1070
    """Chowns a file to root, and sets the setuid bit on the file.
1071
    Calling this function requires the euid to be root.
1072
    The actual mode of path is set to: rws--s--s
1073
    """
1074
    print "chown root:root", file
1075
    if not dry:
1076
        os.chown(file, 0, 0)
1077
    print "chmod a+xs", file
1078
    print "chmod u+rw", file
1079
    if not dry:
1080
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
1081
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
1082
253 by mattgiuca
setup.py: chmods python-console when building.
1083
def action_chmod_x(file, dry):
371 by mattgiuca
setup.py: Fixed chmodding python-console. (Turns out this was a bug in setup,
1084
    """Chmod 755 a file (sets permissions to rwxr-xr-x)."""
1085
    print "chmod 755", file
253 by mattgiuca
setup.py: chmods python-console when building.
1086
    if not dry:
371 by mattgiuca
setup.py: Fixed chmodding python-console. (Turns out this was a bug in setup,
1087
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR
1088
            | stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH)
253 by mattgiuca
setup.py: chmods python-console when building.
1089
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
1090
def query_user(default, prompt):
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
1091
    """Prompts the user for a string, which is read from a line of stdin.
1092
    Exits silently if EOF is encountered. Returns the string, with spaces
1093
    removed from the beginning and end.
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
1094
1095
    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
1096
    """
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
1097
    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
1098
    try:
1099
        val = sys.stdin.readline()
1100
    except KeyboardInterrupt:
1101
        # Ctrl+C
1102
        sys.stdout.write("\n")
1103
        sys.exit(1)
1104
    sys.stdout.write("\n")
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
1105
    # If EOF, exit
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
1106
    if val == '': sys.exit(1)
113 by mattgiuca
setup.py: Now loads defaults, if possible, from the existing conf.py.
1107
    # If empty line, return default
1108
    val = val.strip()
1109
    if val == '': return default
1110
    return val
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
1111
114 by mattgiuca
setup.py: Wrote listmake (and ancillary functions).
1112
def filter_mutate(function, list):
1113
    """Like built-in filter, but mutates the given list instead of returning a
1114
    new one. Returns None."""
1115
    i = len(list)-1
1116
    while i >= 0:
1117
        # Delete elements which do not match
1118
        if not function(list[i]):
1119
            del list[i]
1120
        i -= 1
1121
104 by mattgiuca
setup.py: Replaced the simple script with a full options processing script
1122
if __name__ == "__main__":
1123
    sys.exit(main())