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

« back to all changes in this revision

Viewing changes to setup/util.py

  • Committer: apeel
  • Date: 2008-03-14 01:33:50 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:688
setup.py now creates the /etc/init.d script for usrmgr-server, and install_proc.txt has instructions on installing it.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# IVLE - Informatics Virtual Learning Environment
2
 
# Copyright (C) 2007-2008 The University of Melbourne
3
 
#
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.
8
 
#
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.
13
 
#
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
17
 
 
18
 
# Module: setup/util
19
 
# Author: Matt Giuca, Refactored by David Coles
20
 
# Date:   02/07/2008
21
 
 
22
 
# setup/util.py
23
 
# Contains a set of functions useful for the setup program.
24
 
 
25
 
import os
26
 
import shutil
27
 
import errno
28
 
import sys
29
 
import string
30
 
import stat
31
 
import optparse
32
 
import mimetypes
33
 
 
34
 
__all__ = ['PYTHON_VERSION', 'copy_file_to_jail', 'RunError',
35
 
           'action_runprog', 'action_remove', 'action_rename', 'action_mkdir',
36
 
           'action_copytree', 'action_copylist', 'action_copyfile',
37
 
           'action_symlink', 'action_append', 'action_chown',
38
 
           'action_chown_setuid', 'action_chmod_x', 'action_make_private',
39
 
           'query_user', 'filter_mutate', 'get_svn_revision', 'InstallList',
40
 
           'wwwuid']
41
 
 
42
 
# Determine which Python version (2.4 or 2.5, for example) we are running,
43
 
# and use that as the filename to the Python directory.
44
 
# Just get the first 3 characters of sys.version.
45
 
PYTHON_VERSION = sys.version[0:3]
46
 
 
47
 
# Location of standard programs
48
 
RSYNC = '/usr/bin/rsync'
49
 
 
50
 
# UID of the Webserver
51
 
wwwuid = 33
52
 
 
53
 
def copy_file_to_jail(src, dry):
54
 
    """Copies a single file from an absolute location into the same location
55
 
    within the jail. src must begin with a '/'. The jail will be located
56
 
    in a 'jail' subdirectory of the current path."""
57
 
    action_copyfile(src, 'jail' + src, dry)
58
 
 
59
 
# The actions call Python os functions but print actions and handle dryness.
60
 
# May still throw os exceptions if errors occur.
61
 
 
62
 
class RunError:
63
 
    """Represents an error when running a program (nonzero return)."""
64
 
    def __init__(self, prog, retcode):
65
 
        self.prog = prog
66
 
        self.retcode = retcode
67
 
    def __str__(self):
68
 
        return str(self.prog) + " returned " + repr(self.retcode)
69
 
 
70
 
def action_runprog(prog, args, dry):
71
 
    """Runs a unix program. Searches in $PATH. Synchronous (waits for the
72
 
    program to return). Runs in the current environment. First prints the
73
 
    action as a "bash" line.
74
 
 
75
 
    Throws a RunError with a retcode of the return value of the program,
76
 
    if the program did not return 0.
77
 
 
78
 
    prog: String. Name of the program. (No path required, if in $PATH).
79
 
    args: [String]. Arguments to the program. (Note, this does not allow you to
80
 
        set argv[0]; it will always be prog.)
81
 
    dry: Bool. If True, prints but does not execute.
82
 
    """
83
 
    print prog, string.join(args, ' ')
84
 
    if dry: return
85
 
    ret = os.spawnvp(os.P_WAIT, prog, [prog] + args)
86
 
    if ret != 0:
87
 
        raise RunError(prog, ret)
88
 
 
89
 
def action_remove(path, dry):
90
 
    """Calls rmtree, deleting the target file if it exists."""
91
 
    try:
92
 
        print "rm -r", path
93
 
        if not dry:
94
 
            shutil.rmtree(path, True)
95
 
    except OSError, (err, msg):
96
 
        if err != errno.EEXIST:
97
 
            raise
98
 
        # Otherwise, didn't exist, so we don't care
99
 
 
100
 
def action_rename(src, dst, dry):
101
 
    """Calls rename. Deletes the target if it already exists."""
102
 
    action_remove(dst, dry)
103
 
    print "mv ", src, dst
104
 
    if dry: return
105
 
    try:
106
 
        os.rename(src, dst)
107
 
    except OSError, (err, msg):
108
 
        if err != errno.EEXIST:
109
 
            raise
110
 
 
111
 
def action_mkdir(path, dry):
112
 
    """Calls mkdir. Silently ignored if the directory already exists.
113
 
    Creates all parent directories as necessary."""
114
 
    print "mkdir -p", path
115
 
    if dry: return
116
 
    try:
117
 
        os.makedirs(path)
118
 
    except OSError, (err, msg):
119
 
        if err != errno.EEXIST:
120
 
            raise
121
 
 
122
 
def action_copytree(src, dst, dry):
123
 
    """Copies an entire directory tree. Symlinks are seen as normal files and
124
 
    copies of the entire file (not the link) are made. Creates all parent
125
 
    directories as necessary.
126
 
 
127
 
    See shutil.copytree."""
128
 
    # Allow copying over itself
129
 
    if (os.path.normpath(os.path.join(os.getcwd(),src)) ==
130
 
        os.path.normpath(os.path.join(os.getcwd(),dst))):
131
 
        return
132
 
    
133
 
    # Try to do the copy with rsync, if that fails just copy
134
 
    try:
135
 
        action_runprog(RSYNC, ['-a','--delete',src + '/',dst], dry)
136
 
    except RunError:
137
 
        if dry: return
138
 
        action_remove(dst, dry)
139
 
        print "cp -r", src, dst
140
 
        shutil.copytree(src, dst, True)
141
 
 
142
 
def action_copylist(srclist, dst, dry, srcdir=".", onlybasename=False):
143
 
    """Copies all files in a list to a new location. The files in the list
144
 
    are read relative to the current directory, and their destinations are the
145
 
    same paths relative to dst. Creates all parent directories as necessary.
146
 
    srcdir is "." by default, can be overridden.
147
 
 
148
 
    If onlybasename is True, only the basename of the source is appended to
149
 
    the destination.
150
 
    """
151
 
    for srcfile in srclist:
152
 
        if onlybasename:
153
 
            dstfile = os.path.join(dst, os.path.basename(srcfile))
154
 
        else:
155
 
            dstfile = os.path.join(dst, srcfile)
156
 
        srcfile = os.path.join(srcdir, srcfile)
157
 
        dstdir = os.path.split(dstfile)[0]
158
 
        if not os.path.isdir(dstdir):
159
 
            action_mkdir(dstdir, dry)
160
 
        print "cp -f", srcfile, dstfile
161
 
        if not dry:
162
 
            try:
163
 
                shutil.copyfile(srcfile, dstfile)
164
 
                shutil.copymode(srcfile, dstfile)
165
 
            except shutil.Error:
166
 
                pass
167
 
 
168
 
def action_copyfile(src, dst, dry):
169
 
    """Copies one file to a new location. Creates all parent directories
170
 
    as necessary.
171
 
    Warn if file not found.
172
 
    """
173
 
    dstdir = os.path.split(dst)[0]
174
 
    if not os.path.isdir(dstdir):
175
 
        action_mkdir(dstdir, dry)
176
 
    print "cp -f", src, dst
177
 
    if not dry:
178
 
        try:
179
 
            shutil.copyfile(src, dst)
180
 
            shutil.copymode(src, dst)
181
 
        except (shutil.Error, IOError), e:
182
 
            print "Warning: " + str(e)
183
 
 
184
 
def action_symlink(src, dst, dry):
185
 
    """Creates a symlink in a given location. Creates all parent directories
186
 
    as necessary.
187
 
    """
188
 
    dstdir = os.path.split(dst)[0]
189
 
    if not os.path.isdir(dstdir):
190
 
        action_mkdir(dstdir, dry)
191
 
    # Delete existing file
192
 
    if os.path.exists(dst):
193
 
        os.remove(dst)
194
 
    print "ln -fs", src, dst
195
 
    if not dry:
196
 
        os.symlink(src, dst)
197
 
 
198
 
def action_append(ivle_pth, ivle_www):
199
 
    file = open(ivle_pth, 'a+')
200
 
    file.write(ivle_www + '\n')
201
 
    file.close()
202
 
 
203
 
def action_chown_setuid(file, dry):
204
 
    """Chowns a file to root, and sets the setuid bit on the file.
205
 
    Calling this function requires the euid to be root.
206
 
    The actual mode of path is set to: rws--s--s
207
 
    """
208
 
    print "chown root:root", file
209
 
    if not dry:
210
 
        os.chown(file, 0, 0)
211
 
    print "chmod a+xs", file
212
 
    print "chmod u+rw", file
213
 
    if not dry:
214
 
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
215
 
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
216
 
 
217
 
def action_chmod_x(file, dry):
218
 
    """Chmod 755 a file (sets permissions to rwxr-xr-x)."""
219
 
    print "chmod 755", file
220
 
    if not dry:
221
 
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR
222
 
            | stat.S_IXGRP | stat.S_IRGRP | stat.S_IXOTH | stat.S_IROTH)
223
 
 
224
 
def action_chown(file, uid, gid, dry):
225
 
    """Chowns a file to the specified numeric UID and GID."""
226
 
    print "chown %s:%s %s"%(uid, gid, file)
227
 
    if not dry:
228
 
        os.chown(file, uid, gid)
229
 
 
230
 
def action_make_private(file, dry):
231
 
    """Ensures that a file is private to IVLE (chowns to www-data and chmod to 
232
 
    600)"""
233
 
    action_chown(file, wwwuid, wwwuid, dry)
234
 
    print "chmod 600", file
235
 
    if not dry:
236
 
        os.chmod(file, stat.S_IRUSR | stat.S_IWUSR)
237
 
 
238
 
def query_user(default, prompt):
239
 
    """Prompts the user for a string, which is read from a line of stdin.
240
 
    Exits silently if EOF is encountered. Returns the string, with spaces
241
 
    removed from the beginning and end.
242
 
 
243
 
    Returns default if a 0-length line (after spaces removed) was read.
244
 
    """
245
 
    if default is None:
246
 
        # A default of None means the value will be computed specially, so we
247
 
        # can't really tell you what it is
248
 
        defaultstr = "computed"
249
 
    elif isinstance(default, basestring):
250
 
        defaultstr = '"%s"' % default
251
 
    else:
252
 
        defaultstr = repr(default)
253
 
    sys.stdout.write('%s\n    (default: %s)\n>' % (prompt, defaultstr))
254
 
    try:
255
 
        val = sys.stdin.readline()
256
 
    except KeyboardInterrupt:
257
 
        # Ctrl+C
258
 
        sys.stdout.write("\n")
259
 
        sys.exit(1)
260
 
    sys.stdout.write("\n")
261
 
    # If EOF, exit
262
 
    if val == '': sys.exit(1)
263
 
    # If empty line, return default
264
 
    val = val.strip()
265
 
    if val == '': return default
266
 
    return val
267
 
 
268
 
def filter_mutate(function, list):
269
 
    """Like built-in filter, but mutates the given list instead of returning a
270
 
    new one. Returns None."""
271
 
    i = len(list)-1
272
 
    while i >= 0:
273
 
        # Delete elements which do not match
274
 
        if not function(list[i]):
275
 
            del list[i]
276
 
        i -= 1
277
 
 
278
 
def get_svn_revision():
279
 
    """Returns either the current SVN revision of this build, or None"""
280
 
    import pysvn
281
 
    try:
282
 
        svn = pysvn.Client()
283
 
        entry = svn.info('.')
284
 
        revnum = entry.revision.number
285
 
    except pysvn.ClientError, e:
286
 
        revnum = None
287
 
    return revnum
288
 
 
289
 
### InstallList and helpers ###
290
 
 
291
 
# Mime types which will automatically be placed in the list by InstallList.
292
 
installlist_mimetypes = ['text/x-python', 'text/html',
293
 
    'application/x-javascript', 'application/javascript',
294
 
    'text/css', 'image/png', 'image/gif', 'application/xml']
295
 
 
296
 
def build_list_py_files(dir, no_top_level=False):
297
 
    """Builds a list of all py files found in a directory and its
298
 
    subdirectories. Returns this as a list of strings.
299
 
    no_top_level=True means the file paths will not include the top-level
300
 
    directory.
301
 
    """
302
 
    pylist = []
303
 
    for (dirpath, dirnames, filenames) in os.walk(dir):
304
 
        # Exclude directories beginning with a '.' (such as '.svn')
305
 
        filter_mutate(lambda x: x[0] != '.', dirnames)
306
 
        # All *.py files are added to the list
307
 
        pylist += [os.path.join(dirpath, item) for item in filenames
308
 
            if mimetypes.guess_type(item)[0] in installlist_mimetypes]
309
 
    if no_top_level:
310
 
        for i in range(0, len(pylist)):
311
 
            _, pylist[i] = pylist[i].split(os.sep, 1)
312
 
    return pylist
313
 
 
314
 
class InstallList(object):
315
 
    # We build two separate lists, by walking www and console
316
 
    list_www = property(lambda self: build_list_py_files('www'))
317
 
 
318
 
    list_ivle_lib = property(lambda self: build_list_py_files('ivle'))
319
 
 
320
 
    list_subjects = property(lambda self: build_list_py_files('subjects',
321
 
                             no_top_level=True))
322
 
 
323
 
    list_exercises = property(lambda self: build_list_py_files('exercises',
324
 
                              no_top_level=True))
325
 
 
326
 
    list_services = [
327
 
        "services/python-console",
328
 
        "services/fileservice",
329
 
        "services/serveservice",
330
 
        "services/interpretservice",
331
 
        "services/usrmgt-server",
332
 
        "services/diffservice",
333
 
        "services/svnlogservice",
334
 
    ]
335
 
 
336
 
    list_user_binaries = [
337
 
        "bin/ivle-enrol",
338
 
        "bin/ivle-enrolallusers",
339
 
        "bin/ivle-listusers",
340
 
        "bin/ivle-makeuser",
341
 
        "bin/ivle-marks",
342
 
        "bin/ivle-mountallusers",
343
 
        "bin/ivle-remakeuser",
344
 
        "bin/ivle-showenrolment",
345
 
    ]