~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to lib/common/makeuser.py

  • Committer: William Grant
  • Date: 2008-07-07 04:55:07 UTC
  • mfrom: (unknown (missing))
  • Revision ID: wgrant@ugrad.unimelb.edu.au-20080707045507-wca484wpjxppy7ln
Merge jails-redux branch. We now use aufs rather than hardlinking tens
of thousands of jail files hundreds of times.

Reconfiguration and reconstruction of all jails will be required.
Probably a good idea to start jails from scratch if possible.

Show diffs side-by-side

added added

removed removed

Lines of Context:
43
43
import uuid
44
44
import warnings
45
45
import filecmp
 
46
import logging
46
47
import conf
47
48
import db
48
49
 
170
171
    return (to_add, to_remove)
171
172
 
172
173
 
173
 
def make_jail(username, uid, force=True, manifest=None, svn_pass=None):
 
174
def make_jail(username, uid, force=True, svn_pass=None):
174
175
    """Creates a new user's jail space, in the jail directory as configured in
175
176
    conf.py.
176
177
 
177
 
    This expects there to be a "staging" directory within the jail root which
178
 
    contains all the files for a sample student jail. It creates the student's
179
 
    directory in the jail root, by making a hard-link copy of every file in the
180
 
    staging directory, recursively.
 
178
    This only creates things within /home - everything else is expected to be
 
179
    part of another UnionFS branch.
181
180
 
182
181
    Returns the path to the user's home directory.
183
182
 
191
190
    force: If false, exception if jail already exists for this user.
192
191
    If true (default), overwrites it, but preserves home directory.
193
192
 
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.
196
 
 
197
193
    svn_pass: If provided this will be a string, the randomly-generated
198
194
    Subversion password for this user (if you happen to already have it).
199
195
    If not provided, it will be read from the database.
202
198
    if os.getuid() != 0:
203
199
        raise Exception("Must run make_jail as root")
204
200
    
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: " +
208
 
            stagingdir)
209
201
    # tempdir is for putting backup homes in
210
202
    tempdir = os.path.join(conf.jail_base, '__temp__')
211
203
    if not os.path.exists(tempdir):
213
205
    elif not os.path.isdir(tempdir):
214
206
        os.unlink(tempdir)
215
207
        os.mkdir(tempdir)
216
 
    userdir = os.path.join(conf.jail_base, username)
 
208
    userdir = os.path.join(conf.jail_src_base, username)
217
209
    homedir = os.path.join(userdir, 'home')
 
210
    userhomedir = os.path.join(homedir, username)   # Return value
218
211
 
219
212
    if os.path.exists(userdir):
220
213
        if not force:
221
214
            raise Exception("User's jail already exists")
222
215
        # User jail already exists. Blow it away but preserve their home
223
 
        # directory.
 
216
        # directory. It should be all that is there anyway, but you never
 
217
        # know!
224
218
        # Ignore warnings about the use of tmpnam
225
219
        warnings.simplefilter('ignore')
226
220
        homebackup = os.tempnam(tempdir)
229
223
        # into a directory if it already exists, just fails. Therefore it is
230
224
        # not susceptible to tmpnam symlink attack.
231
225
        shutil.move(homedir, homebackup)
232
 
        try:
233
 
            # Any errors that occur after making the backup will be caught and
234
 
            # the backup will be un-made.
235
 
            # XXX This will still leave the user's jail in an unusable state,
236
 
            # but at least they won't lose their files.
237
 
            if manifest:
238
 
                (to_add, to_remove) = manifest
239
 
                # Remove redundant files and directories
240
 
                for d in to_remove:
241
 
                    dst = os.path.join(userdir, d)
242
 
                    src = os.path.join(stagingdir, d)
243
 
                    if os.path.isdir(dst):
244
 
                        shutil.rmtree(dst)
245
 
                    elif os.path.isfile(dst):
246
 
                        os.remove(dst)
247
 
                # Add new files
248
 
                for d in to_add:
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):
253
 
                        shutil.rmtree(dst)
254
 
                    elif os.path.isfile(dst):
255
 
                        os.remove(dst)
256
 
                    # Link the file/dirs
257
 
                    if os.path.isdir(src):
258
 
                        linktree(src, dst)
259
 
                    elif os.path.isfile(src):
260
 
                        os.link(src, dst)
261
 
                    
262
 
            else:
263
 
                # No manifest, do a full rebuild
264
 
                shutil.rmtree(userdir)
265
 
                # Hard-link (copy aliasing) the entire tree over
266
 
                linktree(stagingdir, userdir)
267
 
        finally:
268
 
            # Set up the user's home directory (restore backup)
269
 
            # First make sure the directory is empty and its parent exists
270
 
            try:
271
 
                shutil.rmtree(homedir)
272
 
            except:
273
 
                pass
274
 
            # XXX If this fails the user's directory will be lost (in the temp
275
 
            # directory). But it shouldn't fail as homedir should not exist.
276
 
            os.makedirs(homedir)
277
 
            shutil.move(homebackup, homedir)
278
 
        userhomedir = os.path.join(homedir, username)   # Return value
 
226
        shutil.rmtree(userdir)
 
227
        os.makedirs(homedir)
 
228
        shutil.move(homebackup, homedir)
279
229
    else:
280
230
        # No user jail exists
281
 
        # Hard-link (copy aliasing) the entire tree over
282
 
        linktree(stagingdir, userdir)
283
 
 
284
231
        # Set up the user's home directory
285
 
        userhomedir = os.path.join(homedir, username)
286
 
        os.mkdir(userhomedir)
 
232
        os.makedirs(userhomedir)
287
233
        # Chown (and set the GID to the same as the UID).
288
234
        os.chown(userhomedir, uid, uid)
289
235
        # Chmod to rwxr-xr-x (755)
290
236
        os.chmod(userhomedir, 0755)
291
237
 
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.
 
238
    # There is 1 special file which needs to be generated specific to this
 
239
    # user: /opt/ivle/lib/conf/conf.py.
294
240
    # "__" username "__" users are exempt (special)
295
241
    if not (username.startswith("__") and username.endswith("__")):
296
 
        make_conf_py(username, userdir, stagingdir, svn_pass)
 
242
        make_conf_py(username, userdir, conf.jail_system, svn_pass)
297
243
 
298
244
    return userhomedir
299
245
 
300
246
def make_conf_py(username, user_jail_dir, staging_dir, svn_pass=None):
301
247
    """
302
 
    Creates (overwriting any existing file) a file /opt/ivle/lib/conf/conf.py
303
 
    in a given user's jail.
 
248
    Creates (overwriting any existing file, and creating directories) a
 
249
    file /opt/ivle/lib/conf/conf.py in a given user's jail.
304
250
    username: Username.
305
251
    user_jail_dir: User's jail dir, ie. conf.jail_base + username
306
252
    staging_dir: The dir with the staging copy of the jail. (With the
308
254
    svn_pass: As with make_jail. User's SVN password, but if not supplied,
309
255
        will look up in the DB.
310
256
    """
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
313
 
    # simply appending.
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
257
    template_conf_path = os.path.join(staging_dir,"opt/ivle/lib/conf/conf.py")
318
258
    conf_path = os.path.join(user_jail_dir, "opt/ivle/lib/conf/conf.py")
 
259
    os.makedirs(os.path.dirname(conf_path))
319
260
 
320
261
    # If svn_pass isn't supplied, grab it from the DB
321
262
    if svn_pass is None:
334
275
        template_conf_data = ("# Warning: Problem building config script.\n"
335
276
                              "# Could not find template conf.py file.\n")
336
277
 
337
 
    # Remove the target conf file if it exists
338
 
    try:
339
 
        os.remove(conf_path)
340
 
    except OSError:
341
 
        pass
342
278
    conf_file = open(conf_path, "w")
343
279
    conf_file.write(template_conf_data)
344
280
    conf_file.write("\n# The login name for the owner of the jail\n")
421
357
    # Make sure the file is owned by the web server
422
358
    if create == "c":
423
359
        chown_to_webserver(conf.svn_auth_local)
 
360
 
 
361
def mount_jail(login):
 
362
    # This is where we'll mount to...
 
363
    destdir = os.path.join(conf.jail_base, login)
 
364
    # ... and this is where we'll get the user bits.
 
365
    srcdir = os.path.join(conf.jail_src_base, login)
 
366
    try:
 
367
        if not os.path.exists(destdir):
 
368
            os.mkdir(destdir)
 
369
        if os.system('/bin/mount -t aufs -o dirs=%s:%s=ro none %s'
 
370
                     % (srcdir, conf.jail_system, destdir)) == 0:
 
371
            logging.info("mounted user %s's jail." % login)
 
372
        else:
 
373
            logging.error("failed to mount user %s's jail!" % login)
 
374
    except Exception, message:
 
375
        logging.warning(str(message))