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
def copy_file_to_jail(src, dry):
47
"""Copies a single file from an absolute location into the same location
48
within the jail. src must begin with a '/'. The jail will be located
49
in a 'jail' subdirectory of the current path."""
50
action_copyfile(src, 'jail' + src, dry)
52
# The actions call Python os functions but print actions and handle dryness.
53
# May still throw os exceptions if errors occur.
56
"""Represents an error when running a program (nonzero return)."""
57
def __init__(self, prog, retcode):
59
self.retcode = retcode
61
return str(self.prog) + " returned " + repr(self.retcode)
63
def action_runprog(prog, args, dry):
64
"""Runs a unix program. Searches in $PATH. Synchronous (waits for the
65
program to return). Runs in the current environment. First prints the
66
action as a "bash" line.
68
Throws a RunError with a retcode of the return value of the program,
69
if the program did not return 0.
71
prog: String. Name of the program. (No path required, if in $PATH).
72
args: [String]. Arguments to the program. (Note, this does not allow you to
73
set argv[0]; it will always be prog.)
74
dry: Bool. If True, prints but does not execute.
76
print prog, string.join(args, ' ')
78
ret = os.spawnvp(os.P_WAIT, prog, [prog] + args)
80
raise RunError(prog, ret)
82
def action_remove(path, dry):
83
"""Calls rmtree, deleting the target file if it exists."""
87
shutil.rmtree(path, True)
88
except OSError, (err, msg):
89
if err != errno.EEXIST:
91
# Otherwise, didn't exist, so we don't care
93
def action_rename(src, dst, dry):
94
"""Calls rename. Deletes the target if it already exists."""
95
action_remove(dst, dry)
100
except OSError, (err, msg):
101
if err != errno.EEXIST:
104
def action_mkdir(path, dry):
105
"""Calls mkdir. Silently ignored if the directory already exists.
106
Creates all parent directories as necessary."""
107
print "mkdir -p", path
111
except OSError, (err, msg):
112
if err != errno.EEXIST:
115
def action_copytree(src, dst, dry):
116
"""Copies an entire directory tree. Symlinks are seen as normal files and
117
copies of the entire file (not the link) are made. Creates all parent
118
directories as necessary.
120
See shutil.copytree."""
121
# Allow copying over itself
122
if (os.path.normpath(os.path.join(os.getcwd(),src)) ==
123
os.path.normpath(os.path.join(os.getcwd(),dst))):
126
# Try to do the copy with rsync, if that fails just copy
128
action_runprog(RSYNC, ['-a','--delete',src + '/',dst], dry)
130
print "cp -r", src, dst
132
action_remove(dst, dry)
133
shutil.copytree(src, dst, True)
135
def action_linktree(src, dst, dry):
136
"""Hard-links an entire directory tree. Same as copytree but the created
137
files are hard-links not actual copies. Removes the existing destination.
139
action_remove(dst, dry)
140
print "<cp with hardlinks> -r", src, dst
142
common.makeuser.linktree(src, dst)
144
def action_copylist(srclist, dst, dry, srcdir="."):
145
"""Copies all files in a list to a new location. The files in the list
146
are read relative to the current directory, and their destinations are the
147
same paths relative to dst. Creates all parent directories as necessary.
148
srcdir is "." by default, can be overridden.
150
for srcfile in srclist:
151
dstfile = os.path.join(dst, srcfile)
152
srcfile = os.path.join(srcdir, srcfile)
153
dstdir = os.path.split(dstfile)[0]
154
if not os.path.isdir(dstdir):
155
action_mkdir(dstdir, dry)
156
print "cp -f", srcfile, dstfile
159
shutil.copyfile(srcfile, dstfile)
160
shutil.copymode(srcfile, dstfile)
164
def action_copyfile(src, dst, dry):
165
"""Copies one file to a new location. Creates all parent directories
167
Warn if file not found.
169
dstdir = os.path.split(dst)[0]
170
if not os.path.isdir(dstdir):
171
action_mkdir(dstdir, dry)
172
print "cp -f", src, dst
175
shutil.copyfile(src, dst)
176
shutil.copymode(src, dst)
177
except (shutil.Error, IOError), e:
178
print "Warning: " + str(e)
180
def action_symlink(src, dst, dry):
181
"""Creates a symlink in a given location. Creates all parent directories
184
dstdir = os.path.split(dst)[0]
185
if not os.path.isdir(dstdir):
186
action_mkdir(dstdir, dry)
187
# Delete existing file
188
if os.path.exists(dst):
190
print "ln -fs", src, dst
194
def action_append(ivle_pth, ivle_www):
195
file = open(ivle_pth, 'a+')
196
file.write(ivle_www + '\n')
199
def action_chown_setuid(file, dry):
200
"""Chowns a file to root, and sets the setuid bit on the file.
201
Calling this function requires the euid to be root.
202
The actual mode of path is set to: rws--s--s
204
print "chown root:root", file
207
print "chmod a+xs", file
208
print "chmod u+rw", file
210
os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
211
| stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
213
def action_chmod_x(file, dry):
214
"""Chmod 755 a file (sets permissions to rwxr-xr-x)."""
215
print "chmod 755", file
217
os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR
218
| stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH)
220
def query_user(default, prompt):
221
"""Prompts the user for a string, which is read from a line of stdin.
222
Exits silently if EOF is encountered. Returns the string, with spaces
223
removed from the beginning and end.
225
Returns default if a 0-length line (after spaces removed) was read.
227
sys.stdout.write('%s\n (default: "%s")\n>' % (prompt, default))
229
val = sys.stdin.readline()
230
except KeyboardInterrupt:
232
sys.stdout.write("\n")
234
sys.stdout.write("\n")
236
if val == '': sys.exit(1)
237
# If empty line, return default
239
if val == '': return default
242
def filter_mutate(function, list):
243
"""Like built-in filter, but mutates the given list instead of returning a
244
new one. Returns None."""
247
# Delete elements which do not match
248
if not function(list[i]):
252
def get_svn_revision():
253
"""Returns either the current SVN revision of this build, or None"""
256
entry = svn.info('.')
257
revnum = entry.revision.number
258
except pysvn.ClientError, e: