71
# Import modules from the website is tricky since they're in the www
73
sys.path.append(os.path.join(os.getcwd(), 'www'))
75
import common.makeuser
77
# Operating system files to copy over into the jail.
78
# These will be copied from the given place on the OS file system into the
79
# same place within the jail.
82
'/lib/tls/i686/cmov/libc.so.6',
83
'/lib/tls/i686/cmov/libdl.so.2',
84
'/lib/tls/i686/cmov/libm.so.6',
85
'/lib/tls/i686/cmov/libpthread.so.0',
86
'/lib/tls/i686/cmov/libutil.so.1',
89
# These 2 files do not exist in Ubuntu
90
#'/etc/ld.so.preload',
91
#'/etc/ld.so.nohwcap',
98
# Needed by matplotlib
99
'/usr/lib/i686/cmov/libssl.so.0.9.8',
100
'/usr/lib/i686/cmov/libcrypto.so.0.9.8',
101
'/lib/tls/i686/cmov/libnsl.so.1',
102
'/usr/lib/libz.so.1',
103
'/usr/lib/atlas/liblapack.so.3',
104
'/usr/lib/atlas/libblas.so.3',
105
'/usr/lib/libg2c.so.0',
106
'/usr/lib/libstdc++.so.6',
107
'/usr/lib/libfreetype.so.6',
108
'/usr/lib/libpng12.so.0',
109
'/usr/lib/libBLT.2.4.so.8.4',
110
'/usr/lib/libtk8.4.so.0',
111
'/usr/lib/libtcl8.4.so.0',
112
'/usr/lib/tcl8.4/init.tcl',
113
'/usr/lib/libX11.so.6',
114
'/usr/lib/libXau.so.6',
115
'/usr/lib/libXdmcp.so.6',
116
'/lib/libgcc_s.so.1',
119
# Symlinks to make within the jail. Src mapped to dst.
121
'python2.5': 'jail/usr/bin/python',
123
# Trees to copy. Src mapped to dst (these will be passed to action_copytree).
125
'/usr/lib/python2.5': 'jail/usr/lib/python2.5',
126
'/usr/share/matplotlib': 'jail/usr/share/matplotlib',
127
'/etc/ld.so.conf.d': 'jail/etc/ld.so.conf.d',
71
130
# Try importing existing conf, but if we can't just set up defaults
72
131
# The reason for this is that these settings are used by other phases
73
132
# of setup besides conf, so we need to know them.
74
133
# Also this allows you to hit Return to accept the existing value.
76
135
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
137
root_dir = confmodule.root_dir
141
ivle_install_dir = confmodule.ivle_install_dir
143
ivle_install_dir = "/opt/ivle"
145
public_host = confmodule.public_host
147
public_host = "public.localhost"
149
jail_base = confmodule.jail_base
151
jail_base = "/home/informatics/jails"
153
subjects_base = confmodule.subjects_base
155
subjects_base = "/home/informatics/subjects"
80
156
except ImportError:
81
157
# Just set reasonable defaults
82
158
root_dir = "/ivle"
83
159
ivle_install_dir = "/opt/ivle"
160
public_host = "public.localhost"
84
161
jail_base = "/home/informatics/jails"
162
subjects_base = "/home/informatics/subjects"
86
164
allowed_uids = "0"
98
176
# as necessary, and include it in the distribution.
99
177
listmake_mimetypes = ['text/x-python', 'text/html',
100
178
'application/x-javascript', 'application/javascript',
101
'text/css', 'image/png']
179
'text/css', 'image/png', 'application/xml']
103
181
# Main function skeleton from Guido van Rossum
104
182
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
175
262
Either prompts the administrator for these details or accepts them as
176
263
command-line args. Will be interactive only if there are no arguments given.
177
264
Takes defaults from existing conf file if it exists.
266
To run IVLE out of the source directory (allowing development without having
267
to rebuild/install), just provide ivle_install_dir as the IVLE trunk
268
directory, and run build/install one time.
178
270
Creates www/conf/conf.py and trampoline/conf.h.
181
274
--ivle_install_dir
184
279
As explained in the interactive prompt or conf.py.
206
301
chown and chmod the installed trampoline.
207
302
Copy www/ to $target.
208
303
Copy jail/ to jails template directory (unless --nojail specified).
210
--nojail Do not copy the jail.
304
Copy subjects/ to subjects directory (unless --nosubjects specified).
306
--nojail Do not copy the jail.
307
--nosubjects Do not copy the subjects.
308
--dry | -n Print out the actions but don't do anything."""
309
elif operation == 'updatejails':
310
print """sudo python setup.py updatejails [--dry|-n]
312
Copy jail/ to each subdirectory in jails directory.
211
314
--dry | -n Print out the actions but don't do anything."""
213
316
print >>sys.stderr, (
267
def build_list_py_files(dir):
376
def build_list_py_files(dir, no_top_level=False):
268
377
"""Builds a list of all py files found in a directory and its
269
subdirectories. Returns this as a list of strings."""
378
subdirectories. Returns this as a list of strings.
379
no_top_level=True means the file paths will not include the top-level
271
383
for (dirpath, dirnames, filenames) in os.walk(dir):
272
384
# Exclude directories beginning with a '.' (such as '.svn')
331
444
jail_base = query_user(jail_base,
332
445
"""Root directory where the jails (containing user files) are stored
333
446
(on the local file system):""")
447
subjects_base = query_user(subjects_base,
448
"""Root directory where the subject directories (containing worksheets
449
and other per-subject files) are stored (on the local file system):""")
450
public_host = query_user(public_host,
451
"""Hostname which will cause the server to go into "public mode",
452
providing login-free access to student's published work:""")
334
453
allowed_uids = query_user(allowed_uids,
335
454
"""UID of the web server process which will run IVLE.
336
455
Only this user may execute the trampoline. May specify multiple users as
377
500
# This directory should contain the "www" and "bin" directories.
378
501
ivle_install_dir = "%s"
503
# The server goes into "public mode" if the browser sends a request with this
504
# host. This is for security reasons - we only serve public student files on a
505
# separate domain to the main IVLE site.
506
# Public mode does not use cookies, and serves only public content.
507
# Private mode (normal mode) requires login, and only serves files relevant to
508
# the logged-in user.
380
511
# In the local file system, where are the student/user file spaces located.
381
512
# The user jails are expected to be located immediately in subdirectories of
385
# Which application to load by default (if the user navigates to the top level
386
# of the site). This is the app's URL name.
387
# Note that if this app requires authentication, the user will first be
388
# presented with the login screen.
390
""" % (root_dir, ivle_install_dir, jail_base, default_app))
516
# In the local file system, where are the per-subject file spaces located.
517
# The individual subject directories are expected to be located immediately
518
# in subdirectories of this location.
520
""" % (root_dir, ivle_install_dir, public_host, jail_base, subjects_base))
393
523
except IOError, (errno, strerror):
468
601
"""Copies necessary Operating System files from their usual locations
469
602
into the jail/ directory of the cwd."""
470
603
# Currently source paths are configured for Ubuntu.
471
copy_file_to_jail('/lib/ld-linux.so.2', dry)
472
copy_file_to_jail('/lib/tls/i686/cmov/libc.so.6', dry)
473
copy_file_to_jail('/lib/tls/i686/cmov/libdl.so.2', dry)
474
copy_file_to_jail('/lib/tls/i686/cmov/libm.so.6', dry)
475
copy_file_to_jail('/lib/tls/i686/cmov/libpthread.so.0', dry)
476
copy_file_to_jail('/lib/tls/i686/cmov/libutil.so.1', dry)
477
copy_file_to_jail('/usr/bin/python2.5', dry)
478
# TODO: ln -s jail/usr/bin/python2.5 jail/usr/bin/python
479
action_copytree('/usr/lib/python2.5', 'jail/usr/lib/python2.5', dry)
604
for filename in JAIL_FILES:
605
copy_file_to_jail(filename, dry)
606
for src, dst in JAIL_LINKS.items():
607
action_symlink(src, dst, dry)
608
for src, dst in JAIL_COPYTREES.items():
609
action_copytree(src, dst, dry)
481
611
def copy_file_to_jail(src, dry):
482
612
"""Copies a single file from an absolute location into the same location
487
617
def install(args):
488
618
# Get "dry" and "nojail" variables from command line
489
(opts, args) = getopt.gnu_getopt(args, "n", ['dry', 'nojail'])
619
(opts, args) = getopt.gnu_getopt(args, "n",
620
['dry', 'nojail', 'nosubjects'])
490
621
opts = dict(opts)
491
622
dry = '-n' in opts or '--dry' in opts
492
623
nojail = '--nojail' in opts
624
nosubjects = '--nosubjects' in opts
495
627
print "Dry run (no actions will be executed\n"
517
649
# to the jails template directory (it will be used as a template
518
650
# for all the students' jails).
519
651
action_copytree('jail', os.path.join(jail_base, 'template'), dry)
520
# Set up symlinks inside the jail
521
action_symlink(os.path.join(jail_base, 'template/usr/bin/python2.5'),
522
os.path.join(jail_base, 'template/usr/bin/python'), dry)
653
# Copy the subjects directory across
654
action_copylist(install_list.list_subjects, subjects_base, dry,
657
# Append IVLE path to ivle.pth in python site packages
658
# (Unless it's already there)
659
ivle_pth = os.path.join(sys.prefix,
660
"lib/python2.5/site-packages/ivle.pth")
661
ivle_www = os.path.join(ivle_install_dir, "www")
662
write_ivle_pth = True
664
file = open(ivle_pth, 'r')
666
if line.strip() == ivle_www:
667
write_ivle_pth = False
669
except (IOError, OSError):
672
action_append(ivle_pth, ivle_www)
676
def updatejails(args):
677
# Get "dry" variable from command line
678
(opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
680
dry = '-n' in opts or '--dry' in opts
683
print "Dry run (no actions will be executed\n"
685
if not dry and os.geteuid() != 0:
686
print >>sys.stderr, "Must be root to run install"
687
print >>sys.stderr, "(I need to chown some files)."
690
# Update the template jail directory in case it hasn't been installed
692
action_copytree('jail', os.path.join(jail_base, 'template'), dry)
694
# Re-link all the files in all students jails.
695
for dir in os.listdir(jail_base):
696
if dir == 'template': continue
697
# First back up the student's home directory
698
temp_home = os.tmpnam()
699
action_rename(os.path.join(jail_base, dir, 'home'), temp_home, dry)
700
# Delete the student's jail and relink the jail files
701
action_linktree(os.path.join(jail_base, 'template'),
702
os.path.join(jail_base, dir), dry)
703
# Restore the student's home directory
704
action_rename(temp_home, os.path.join(jail_base, dir, 'home'), dry)
705
# Set up the user's home directory just in case they don't have a
706
# directory for this yet
707
action_mkdir(os.path.join(jail_base, dir, 'home', dir), dry)
553
738
raise RunError(prog, ret)
740
def action_rename(src, dst, dry):
741
"""Calls rename. Deletes the target if it already exists."""
742
if os.access(dst, os.F_OK):
745
shutil.rmtree(dst, True)
746
print "mv ", src, dst
750
except OSError, (err, msg):
751
if err != errno.EEXIST:
555
754
def action_mkdir(path, dry):
556
755
"""Calls mkdir. Silently ignored if the directory already exists.
557
756
Creates all parent directories as necessary."""
575
774
shutil.rmtree(dst, True)
576
775
print "cp -r", src, dst
578
shutil.copytree(src, dst)
580
def action_copylist(srclist, dst, dry):
777
shutil.copytree(src, dst, True)
779
def action_linktree(src, dst, dry):
780
"""Hard-links an entire directory tree. Same as copytree but the created
781
files are hard-links not actual copies. Removes the existing destination.
783
if os.access(dst, os.F_OK):
786
shutil.rmtree(dst, True)
787
print "<cp with hardlinks> -r", src, dst
789
common.makeuser.linktree(src, dst)
791
def action_copylist(srclist, dst, dry, srcdir="."):
581
792
"""Copies all files in a list to a new location. The files in the list
582
793
are read relative to the current directory, and their destinations are the
583
794
same paths relative to dst. Creates all parent directories as necessary.
795
srcdir is "." by default, can be overridden.
585
797
for srcfile in srclist:
586
798
dstfile = os.path.join(dst, srcfile)
799
srcfile = os.path.join(srcdir, srcfile)
587
800
dstdir = os.path.split(dstfile)[0]
588
801
if not os.path.isdir(dstdir):
589
802
action_mkdir(dstdir, dry)
590
803
print "cp -f", srcfile, dstfile
592
shutil.copyfile(srcfile, dstfile)
806
shutil.copyfile(srcfile, dstfile)
807
shutil.copymode(srcfile, dstfile)
594
811
def action_copyfile(src, dst, dry):
595
812
"""Copies one file to a new location. Creates all parent directories
609
830
dstdir = os.path.split(dst)[0]
610
831
if not os.path.isdir(dstdir):
611
832
action_mkdir(dstdir, dry)
612
print "ln -s", src, dst
833
# Delete existing file
834
if os.path.exists(dst):
836
print "ln -fs", src, dst
614
838
os.symlink(src, dst)
840
def action_append(ivle_pth, ivle_www):
841
file = open(ivle_pth, 'a+')
842
file.write(ivle_www + '\n')
616
845
def action_chown_setuid(file, dry):
617
846
"""Chowns a file to root, and sets the setuid bit on the file.
618
847
Calling this function requires the euid to be root.
627
856
os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
628
857
| stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
859
def action_chmod_x(file, dry):
860
"""Chmod +xs a file (sets execute permission)."""
861
print "chmod u+rwx", file
863
os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
630
865
def query_user(default, prompt):
631
866
"""Prompts the user for a string, which is read from a line of stdin.
632
867
Exits silently if EOF is encountered. Returns the string, with spaces