1
# IVLE - Informatics Virtual Learning Environment
2
# Copyright (C) 2007-2008 The University of Melbourne
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# Module: setup/setuputil
19
# Author: Matt Giuca, Refactored by David Coles
23
# Contains a set of functions useful for the setup program.
33
# Import modules from the website is tricky since they're in the www
35
sys.path.append('lib')
36
import common.makeuser
38
# Determine which Python version (2.4 or 2.5, for example) we are running,
39
# and use that as the filename to the Python directory.
40
# Just get the first 3 characters of sys.version.
41
PYTHON_VERSION = sys.version[0:3]
43
# Location of standard programs
44
RSYNC = '/usr/bin/rsync'
46
# UID of the Webserver
49
def copy_file_to_jail(src, dry):
50
"""Copies a single file from an absolute location into the same location
51
within the jail. src must begin with a '/'. The jail will be located
52
in a 'jail' subdirectory of the current path."""
53
action_copyfile(src, 'jail' + src, dry)
55
# The actions call Python os functions but print actions and handle dryness.
56
# May still throw os exceptions if errors occur.
59
"""Represents an error when running a program (nonzero return)."""
60
def __init__(self, prog, retcode):
62
self.retcode = retcode
64
return str(self.prog) + " returned " + repr(self.retcode)
66
def action_runprog(prog, args, dry):
67
"""Runs a unix program. Searches in $PATH. Synchronous (waits for the
68
program to return). Runs in the current environment. First prints the
69
action as a "bash" line.
71
Throws a RunError with a retcode of the return value of the program,
72
if the program did not return 0.
74
prog: String. Name of the program. (No path required, if in $PATH).
75
args: [String]. Arguments to the program. (Note, this does not allow you to
76
set argv[0]; it will always be prog.)
77
dry: Bool. If True, prints but does not execute.
79
print prog, string.join(args, ' ')
81
ret = os.spawnvp(os.P_WAIT, prog, [prog] + args)
83
raise RunError(prog, ret)
85
def action_remove(path, dry):
86
"""Calls rmtree, deleting the target file if it exists."""
90
shutil.rmtree(path, True)
91
except OSError, (err, msg):
92
if err != errno.EEXIST:
94
# Otherwise, didn't exist, so we don't care
96
def action_rename(src, dst, dry):
97
"""Calls rename. Deletes the target if it already exists."""
98
action_remove(dst, dry)
103
except OSError, (err, msg):
104
if err != errno.EEXIST:
107
def action_mkdir(path, dry):
108
"""Calls mkdir. Silently ignored if the directory already exists.
109
Creates all parent directories as necessary."""
110
print "mkdir -p", path
114
except OSError, (err, msg):
115
if err != errno.EEXIST:
118
def action_copytree(src, dst, dry):
119
"""Copies an entire directory tree. Symlinks are seen as normal files and
120
copies of the entire file (not the link) are made. Creates all parent
121
directories as necessary.
123
See shutil.copytree."""
124
# Allow copying over itself
125
if (os.path.normpath(os.path.join(os.getcwd(),src)) ==
126
os.path.normpath(os.path.join(os.getcwd(),dst))):
129
# Try to do the copy with rsync, if that fails just copy
131
action_runprog(RSYNC, ['-a','--delete',src + '/',dst], dry)
133
print "cp -r", src, dst
135
action_remove(dst, dry)
136
shutil.copytree(src, dst, True)
138
def action_linktree(src, dst, dry):
139
"""Hard-links an entire directory tree. Same as copytree but the created
140
files are hard-links not actual copies. Removes the existing destination.
142
action_remove(dst, dry)
143
print "<cp with hardlinks> -r", src, dst
145
common.makeuser.linktree(src, dst)
147
def action_copylist(srclist, dst, dry, srcdir="."):
148
"""Copies all files in a list to a new location. The files in the list
149
are read relative to the current directory, and their destinations are the
150
same paths relative to dst. Creates all parent directories as necessary.
151
srcdir is "." by default, can be overridden.
153
for srcfile in srclist:
154
dstfile = os.path.join(dst, srcfile)
155
srcfile = os.path.join(srcdir, srcfile)
156
dstdir = os.path.split(dstfile)[0]
157
if not os.path.isdir(dstdir):
158
action_mkdir(dstdir, dry)
159
print "cp -f", srcfile, dstfile
162
shutil.copyfile(srcfile, dstfile)
163
shutil.copymode(srcfile, dstfile)
167
def action_copyfile(src, dst, dry):
168
"""Copies one file to a new location. Creates all parent directories
170
Warn if file not found.
172
dstdir = os.path.split(dst)[0]
173
if not os.path.isdir(dstdir):
174
action_mkdir(dstdir, dry)
175
print "cp -f", src, dst
178
shutil.copyfile(src, dst)
179
shutil.copymode(src, dst)
180
except (shutil.Error, IOError), e:
181
print "Warning: " + str(e)
183
def action_symlink(src, dst, dry):
184
"""Creates a symlink in a given location. Creates all parent directories
187
dstdir = os.path.split(dst)[0]
188
if not os.path.isdir(dstdir):
189
action_mkdir(dstdir, dry)
190
# Delete existing file
191
if os.path.exists(dst):
193
print "ln -fs", src, dst
197
def action_append(ivle_pth, ivle_www):
198
file = open(ivle_pth, 'a+')
199
file.write(ivle_www + '\n')
202
def action_chown_setuid(file, dry):
203
"""Chowns a file to root, and sets the setuid bit on the file.
204
Calling this function requires the euid to be root.
205
The actual mode of path is set to: rws--s--s
207
print "chown root:root", file
210
print "chmod a+xs", file
211
print "chmod u+rw", file
213
os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
214
| stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
216
def action_chmod_x(file, dry):
217
"""Chmod 755 a file (sets permissions to rwxr-xr-x)."""
218
print "chmod 755", file
220
os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR
221
| stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH)
223
def action_make_private(file, dry):
224
"""Ensures that a file is private to IVLE (chowns to www-data and chmod to
226
print "chown %s:%s %s"%(wwwuid, wwwuid, file)
228
os.chown(file, wwwuid, wwwuid)
229
print "chmod 600", file
231
os.chmod(file, stat.S_IRUSR | stat.S_IWUSR)
233
def query_user(default, prompt):
234
"""Prompts the user for a string, which is read from a line of stdin.
235
Exits silently if EOF is encountered. Returns the string, with spaces
236
removed from the beginning and end.
238
Returns default if a 0-length line (after spaces removed) was read.
240
sys.stdout.write('%s\n (default: "%s")\n>' % (prompt, default))
242
val = sys.stdin.readline()
243
except KeyboardInterrupt:
245
sys.stdout.write("\n")
247
sys.stdout.write("\n")
249
if val == '': sys.exit(1)
250
# If empty line, return default
252
if val == '': return default
255
def filter_mutate(function, list):
256
"""Like built-in filter, but mutates the given list instead of returning a
257
new one. Returns None."""
260
# Delete elements which do not match
261
if not function(list[i]):
265
def get_svn_revision():
266
"""Returns either the current SVN revision of this build, or None"""
269
entry = svn.info('.')
270
revnum = entry.revision.number
271
except pysvn.ClientError, e: