addhomedir: invalidate the group table too
[mspang/pyceo.git] / bin / addhomedir
1 #!/usr/bin/python2.4 --
2 """
3 addhomedir - a script to create home directories for new users
4
5 For the most part, this script mimics the behavior of adduser(8) when
6 creating a home directory. It creates the directory, copies files
7 from /etc/skel, sets permissions, and sets quotas.
8 """
9 import os, sys, re, pwd, getopt, stat
10
11 CONFIG_FILE = '/etc/csc/accounts.cf'
12
13 safe_environment = ['LOGNAME', 'USERNAME', 'USER', 'HOME', 'TERM', 'LANG'
14     'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY',
15     'LC_NUMERIC', 'LC_TIME', 'UID', 'GID', 'SSH_CONNECTION', 'SSH_AUTH_SOCK',
16     'SSH_CLIENT']
17
18 for key in os.environ.keys():
19     if key not in safe_environment:
20         del os.environ[key]
21
22 os.environ['PATH'] = '/usr/sbin:/sbin:/usr/bin:/bin'
23 os.umask(0)
24
25 try:
26     os.setreuid(0, 0)
27     os.setregid(0, 0)
28 except OSError:
29     print "You must be root to use this command."
30     sys.exit(1)
31
32 for pathent in sys.path[:]:
33     if not pathent.find('/usr') == 0:
34         sys.path.remove(pathent)
35
36 from csc.common import conf
37
38 usage = "usage: %s [--quiet] username" % sys.argv[0]
39
40 try:
41     options, arguments = getopt.gnu_getopt(sys.argv[1:], '', ['quiet'])
42 except getopt.GetoptError, e:
43     print usage
44     sys.exit(2)
45
46 if len(arguments) != 1:
47     print usage
48     sys.exit(2)
49
50 quiet = ('--quiet','') in options
51 program = os.path.basename(sys.argv[0])
52 username = arguments[0]
53
54 def fail(message):
55     print >> sys.stderr, "%s: %s" % (program, message)
56     sys.exit(1)
57
58 def debug(message):
59     if not quiet:
60         print message
61
62 def warn(message):
63     print >> sys.stderr, "warning: %s" % message
64
65 try:
66     cfg = conf.read(CONFIG_FILE)
67 except conf.ConfigurationException:
68     fail("could not import configuration from %s" % CONFIG_FILE)
69
70 try:
71     string_fields = ['homedir_regex', 'skeleton_dir', 'quota_prototype']
72     integer_fields = ['homedir_min_uid', 'homedir_mode']
73     conf.check_string_fields(CONFIG_FILE, string_fields, cfg)
74     conf.check_integer_fields(CONFIG_FILE, integer_fields, cfg)
75 except conf.ConfigurationException, e:
76     fail(e)
77
78 if not os.path.isdir(cfg['skeleton_dir']):
79     fail("invalid skeleton dir %s" % cfg['skeleton_dir'])
80
81 try:
82     os.spawnvp(os.P_WAIT, "/usr/sbin/nscd", ("/usr/sbin/nscd", "-i", "passwd"))
83     os.spawnvp(os.P_WAIT, "/usr/sbin/nscd", ("/usr/sbin/nscd", "-i", "group"))
84     pwent = pwd.getpwnam(username)
85     uid = pwent.pw_uid
86     gid = pwent.pw_gid
87     homedir = pwent.pw_dir
88 except KeyError:
89     fail("%s: invalid user" % username)
90     
91 if uid < cfg['homedir_min_uid']:
92     fail("uid of account %s is less than homedir_min_uid" % username)
93
94 if not re.match(cfg['homedir_regex'], homedir):
95     fail("homedir of account %s does not match homedir_regex" % username)
96
97 if os.path.exists(homedir):
98     fail("home directory %s exists" % homedir)
99
100 parentdir = os.path.dirname(homedir)
101 if not os.path.isdir(parentdir):
102     fail("parent directory %s does not exist" % parentdir)
103
104 try:
105     debug("creating %s" % homedir)
106     os.mkdir(homedir, cfg['homedir_mode'])
107     os.chown(homedir, uid, gid)
108
109     # copy files from /etc/skel or similar (location is configurable)
110     skel_files = os.listdir(cfg['skeleton_dir'])
111     for filename in skel_files:
112
113         srcpath = cfg['skeleton_dir'] + '/' + filename
114         destpath = homedir + '/' + filename
115         srcstat = os.lstat(srcpath)
116         perm = srcstat.st_mode & 0777
117         
118         if srcstat.st_uid or srcstat.st_gid:
119             warn("skipping %s due to ownership" % srcpath)
120             continue
121         
122         if stat.S_ISLNK(srcstat.st_mode):
123             linkdest = os.readlink(srcpath)
124             debug("linking %s to %s" % (destpath, linkdest))
125             os.symlink(linkdest, destpath)
126             os.lchown(destpath, uid, gid)
127
128         elif stat.S_ISDIR(srcstat.st_mode):
129             debug("adding directory %s" % destpath)
130             os.mkdir(destpath, perm)
131             os.chown(destpath, uid, gid)
132
133         elif stat.S_ISREG(srcstat.st_mode):
134             debug("adding %s" % destpath)
135             src  = open(srcpath)
136             destfd = os.open(destpath, os.O_CREAT|os.O_EXCL|os.O_WRONLY, perm)
137             dest = os.fdopen(destfd, 'w')
138             dest.write(src.read())
139             src.close()
140             dest.close()
141             os.chown(destpath, uid, gid)
142
143         else:
144             warn("file type of %s not supported" % srcpath)
145
146     # set the user's quota
147     setquota = '/usr/sbin/setquota'
148     if os.access(setquota, os.X_OK):
149         protouser = cfg['quota_prototype']
150         debug("copying quotas from %s to %s" % (protouser, username))
151         args = [ setquota, '-u', '-p', protouser, username, '-a' ]
152         result = os.spawnv(os.P_WAIT, setquota, args)
153         if result:
154             warn("setquota returned error code %d" % result)
155     else:
156         warn("cannot find setquota, not setting quota")
157
158     debug("done!")
159
160 except OSError, e:
161     fail(e)