135
120
if res != 0 and throw_on_error:
136
121
raise Exception("Unable to create ivle-auth for %s" % login)
138
# Make sure the file is owned by the web server
140
chown_to_webserver(conf.svn_auth_ivle)
144
def generate_manifest(basedir, targetdir, parent=''):
145
""" From a basedir and a targetdir work out which files are missing or out
146
of date and need to be added/updated and which files are redundant and need
149
parent: This is used for the recursive call to track the relative paths
150
that we have decended.
153
cmp = filecmp.dircmp(basedir, targetdir)
155
# Add all new files and files that have changed
156
to_add = [os.path.join(parent,x) for x in (cmp.left_only + cmp.diff_files)]
158
# Remove files that are redundant
159
to_remove = [os.path.join(parent,x) for x in cmp.right_only]
162
for d in cmp.common_dirs:
163
newbasedir = os.path.join(basedir, d)
164
newtargetdir = os.path.join(targetdir, d)
165
newparent = os.path.join(parent, d)
166
(sadd,sremove) = generate_manifest(newbasedir, newtargetdir, newparent)
170
return (to_add, to_remove)
173
def make_jail(username, uid, force=True, manifest=None, svn_pass=None):
125
def make_jail(username, uid, force=True):
174
126
"""Creates a new user's jail space, in the jail directory as configured in
177
This expects there to be a "staging" directory within the jail root which
129
This expects there to be a "template" directory within the jail root which
178
130
contains all the files for a sample student jail. It creates the student's
179
131
directory in the jail root, by making a hard-link copy of every file in the
180
staging directory, recursively.
132
template directory, recursively.
182
134
Returns the path to the user's home directory.
191
143
force: If false, exception if jail already exists for this user.
192
144
If true (default), overwrites it, but preserves home directory.
194
manifest: If provided this will be a pair (to_add, to_remove) of files or
195
directories to add or remove from the jail.
197
svn_pass: If provided this will be a string, the randomly-generated
198
Subversion password for this user (if you happen to already have it).
199
If not provided, it will be read from the database.
201
146
# MUST run as root or some of this may fail
202
147
if os.getuid() != 0:
203
148
raise Exception("Must run make_jail as root")
205
stagingdir = os.path.join(conf.jail_base, '__staging__')
206
if not os.path.isdir(stagingdir):
207
raise Exception("Staging jail directory does not exist: " +
150
templatedir = os.path.join(conf.jail_base, 'template')
151
if not os.path.isdir(templatedir):
152
raise Exception("Template jail directory does not exist: " +
209
154
# tempdir is for putting backup homes in
210
tempdir = os.path.join(conf.jail_base, '__temp__')
155
tempdir = os.path.join(conf.jail_base, 'temp')
211
156
if not os.path.exists(tempdir):
212
157
os.makedirs(tempdir)
213
158
elif not os.path.isdir(tempdir):
234
179
# the backup will be un-made.
235
180
# XXX This will still leave the user's jail in an unusable state,
236
181
# but at least they won't lose their files.
238
(to_add, to_remove) = manifest
239
# Remove redundant files and directories
241
dst = os.path.join(userdir, d)
242
src = os.path.join(stagingdir, d)
243
if os.path.isdir(dst):
245
elif os.path.isfile(dst):
249
dst = os.path.join(userdir, d)
250
src = os.path.join(stagingdir, d)
251
# Clear the previous file/dir
252
if os.path.isdir(dst):
254
elif os.path.isfile(dst):
257
if os.path.isdir(src):
259
elif os.path.isfile(src):
263
# No manifest, do a full rebuild
264
shutil.rmtree(userdir)
265
# Hard-link (copy aliasing) the entire tree over
266
linktree(stagingdir, userdir)
182
shutil.rmtree(userdir)
184
# Hard-link (copy aliasing) the entire tree over
185
linktree(templatedir, userdir)
268
187
# Set up the user's home directory (restore backup)
269
188
# First make sure the directory is empty and its parent exists
275
194
# directory). But it shouldn't fail as homedir should not exist.
276
195
os.makedirs(homedir)
277
196
shutil.move(homebackup, homedir)
278
userhomedir = os.path.join(homedir, username) # Return value
197
return os.path.join(homedir, username)
280
199
# No user jail exists
281
200
# Hard-link (copy aliasing) the entire tree over
282
linktree(stagingdir, userdir)
201
linktree(templatedir, userdir)
284
203
# Set up the user's home directory
285
204
userhomedir = os.path.join(homedir, username)
288
207
os.chown(userhomedir, uid, uid)
289
208
# Chmod to rwxr-xr-x (755)
290
209
os.chmod(userhomedir, 0755)
292
# There is 1 special file which should not be hard-linked, but instead
293
# generated specific to this user: /opt/ivle/lib/conf/conf.py.
294
# "__" username "__" users are exempt (special)
295
if not (username.startswith("__") and username.endswith("__")):
296
make_conf_py(username, userdir, stagingdir, svn_pass)
300
def make_conf_py(username, user_jail_dir, staging_dir, svn_pass=None):
302
Creates (overwriting any existing file) a file /opt/ivle/lib/conf/conf.py
303
in a given user's jail.
305
user_jail_dir: User's jail dir, ie. conf.jail_base + username
306
staging_dir: The dir with the staging copy of the jail. (With the
307
template conf.py file).
308
svn_pass: As with make_jail. User's SVN password, but if not supplied,
309
will look up in the DB.
311
# Note: It is important to delete this file and recreate it (somewhat
312
# ironically beginning by pasting the same contents in again), rather than
314
# Note that all files initially are aliased, so appending would result
315
# in a massive aliasing problem. Deleting and recreating ensures that
316
# the conf.py files are unique to each jail.
317
template_conf_path = os.path.join(staging_dir,"opt/ivle/lib/conf/conf.py")
318
conf_path = os.path.join(user_jail_dir, "opt/ivle/lib/conf/conf.py")
320
# If svn_pass isn't supplied, grab it from the DB
323
svn_pass = dbconn.get_user(username).svn_pass
326
# Read the contents of the template conf file
328
template_conf_file = open(template_conf_path, "r")
329
template_conf_data = template_conf_file.read()
330
template_conf_file.close()
332
# Couldn't open template conf.py for some reason
333
# Just treat it as empty file
334
template_conf_data = ("# Warning: Problem building config script.\n"
335
"# Could not find template conf.py file.\n")
337
# Remove the target conf file if it exists
342
conf_file = open(conf_path, "w")
343
conf_file.write(template_conf_data)
344
conf_file.write("\n# The login name for the owner of the jail\n")
345
conf_file.write("login = %s\n" % repr(username))
346
conf_file.write("\n")
347
conf_file.write("# The subversion-only password for the owner of "
349
conf_file.write("svn_pass = %s\n" % repr(svn_pass))
352
# Make this file world-readable
353
# (chmod 644 conf_path)
354
os.chmod(conf_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP
357
212
def linktree(src, dst):
358
213
"""Recursively hard-link a directory tree using os.link().