|
1 # this module is an OS/2 oriented replacement for the grp standard |
|
2 # extension module. |
|
3 |
|
4 # written by Andrew MacIntyre, April 2001. |
|
5 # updated July 2003, adding field accessor support |
|
6 |
|
7 # note that this implementation checks whether ":" or ";" as used as |
|
8 # the field separator character. |
|
9 |
|
10 """Replacement for grp standard extension module, intended for use on |
|
11 OS/2 and similar systems which don't normally have an /etc/group file. |
|
12 |
|
13 The standard Unix group database is an ASCII text file with 4 fields per |
|
14 record (line), separated by a colon: |
|
15 - group name (string) |
|
16 - group password (optional encrypted string) |
|
17 - group id (integer) |
|
18 - group members (comma delimited list of userids, with no spaces) |
|
19 |
|
20 Note that members are only included in the group file for groups that |
|
21 aren't their primary groups. |
|
22 (see the section 8.2 of the Python Library Reference) |
|
23 |
|
24 This implementation differs from the standard Unix implementation by |
|
25 allowing use of the platform's native path separator character - ';' on OS/2, |
|
26 DOS and MS-Windows - as the field separator in addition to the Unix |
|
27 standard ":". |
|
28 |
|
29 The module looks for the group database at the following locations |
|
30 (in order first to last): |
|
31 - ${ETC_GROUP} (or %ETC_GROUP%) |
|
32 - ${ETC}/group (or %ETC%/group) |
|
33 - ${PYTHONHOME}/Etc/group (or %PYTHONHOME%/Etc/group) |
|
34 |
|
35 Classes |
|
36 ------- |
|
37 |
|
38 None |
|
39 |
|
40 Functions |
|
41 --------- |
|
42 |
|
43 getgrgid(gid) - return the record for group-id gid as a 4-tuple |
|
44 |
|
45 getgrnam(name) - return the record for group 'name' as a 4-tuple |
|
46 |
|
47 getgrall() - return a list of 4-tuples, each tuple being one record |
|
48 (NOTE: the order is arbitrary) |
|
49 |
|
50 Attributes |
|
51 ---------- |
|
52 |
|
53 group_file - the path of the group database file |
|
54 |
|
55 """ |
|
56 |
|
57 import os |
|
58 |
|
59 # try and find the group file |
|
60 __group_path = [] |
|
61 if os.environ.has_key('ETC_GROUP'): |
|
62 __group_path.append(os.environ['ETC_GROUP']) |
|
63 if os.environ.has_key('ETC'): |
|
64 __group_path.append('%s/group' % os.environ['ETC']) |
|
65 if os.environ.has_key('PYTHONHOME'): |
|
66 __group_path.append('%s/Etc/group' % os.environ['PYTHONHOME']) |
|
67 |
|
68 group_file = None |
|
69 for __i in __group_path: |
|
70 try: |
|
71 __f = open(__i, 'r') |
|
72 __f.close() |
|
73 group_file = __i |
|
74 break |
|
75 except: |
|
76 pass |
|
77 |
|
78 # decide what field separator we can try to use - Unix standard, with |
|
79 # the platform's path separator as an option. No special field conversion |
|
80 # handlers are required for the group file. |
|
81 __field_sep = [':'] |
|
82 if os.pathsep: |
|
83 if os.pathsep != ':': |
|
84 __field_sep.append(os.pathsep) |
|
85 |
|
86 # helper routine to identify which separator character is in use |
|
87 def __get_field_sep(record): |
|
88 fs = None |
|
89 for c in __field_sep: |
|
90 # there should be 3 delimiter characters (for 4 fields) |
|
91 if record.count(c) == 3: |
|
92 fs = c |
|
93 break |
|
94 if fs: |
|
95 return fs |
|
96 else: |
|
97 raise KeyError, '>> group database fields not delimited <<' |
|
98 |
|
99 # class to match the new record field name accessors. |
|
100 # the resulting object is intended to behave like a read-only tuple, |
|
101 # with each member also accessible by a field name. |
|
102 class Group: |
|
103 def __init__(self, name, passwd, gid, mem): |
|
104 self.__dict__['gr_name'] = name |
|
105 self.__dict__['gr_passwd'] = passwd |
|
106 self.__dict__['gr_gid'] = gid |
|
107 self.__dict__['gr_mem'] = mem |
|
108 self.__dict__['_record'] = (self.gr_name, self.gr_passwd, |
|
109 self.gr_gid, self.gr_mem) |
|
110 |
|
111 def __len__(self): |
|
112 return 4 |
|
113 |
|
114 def __getitem__(self, key): |
|
115 return self._record[key] |
|
116 |
|
117 def __setattr__(self, name, value): |
|
118 raise AttributeError('attribute read-only: %s' % name) |
|
119 |
|
120 def __repr__(self): |
|
121 return str(self._record) |
|
122 |
|
123 def __cmp__(self, other): |
|
124 this = str(self._record) |
|
125 if this == other: |
|
126 return 0 |
|
127 elif this < other: |
|
128 return -1 |
|
129 else: |
|
130 return 1 |
|
131 |
|
132 |
|
133 # read the whole file, parsing each entry into tuple form |
|
134 # with dictionaries to speed recall by GID or group name |
|
135 def __read_group_file(): |
|
136 if group_file: |
|
137 group = open(group_file, 'r') |
|
138 else: |
|
139 raise KeyError, '>> no group database <<' |
|
140 gidx = {} |
|
141 namx = {} |
|
142 sep = None |
|
143 while 1: |
|
144 entry = group.readline().strip() |
|
145 if len(entry) > 3: |
|
146 if sep is None: |
|
147 sep = __get_field_sep(entry) |
|
148 fields = entry.split(sep) |
|
149 fields[2] = int(fields[2]) |
|
150 fields[3] = [f.strip() for f in fields[3].split(',')] |
|
151 record = Group(*fields) |
|
152 if not gidx.has_key(fields[2]): |
|
153 gidx[fields[2]] = record |
|
154 if not namx.has_key(fields[0]): |
|
155 namx[fields[0]] = record |
|
156 elif len(entry) > 0: |
|
157 pass # skip empty or malformed records |
|
158 else: |
|
159 break |
|
160 group.close() |
|
161 if len(gidx) == 0: |
|
162 raise KeyError |
|
163 return (gidx, namx) |
|
164 |
|
165 # return the group database entry by GID |
|
166 def getgrgid(gid): |
|
167 g, n = __read_group_file() |
|
168 return g[gid] |
|
169 |
|
170 # return the group database entry by group name |
|
171 def getgrnam(name): |
|
172 g, n = __read_group_file() |
|
173 return n[name] |
|
174 |
|
175 # return all the group database entries |
|
176 def getgrall(): |
|
177 g, n = __read_group_file() |
|
178 return g.values() |
|
179 |
|
180 # test harness |
|
181 if __name__ == '__main__': |
|
182 getgrall() |