blob: ee63ef8ae201ca1fa153b0327b4b3abf7fd80026 [file] [log] [blame]
Andrew MacIntyref47d60f2002-02-22 11:06:30 +00001# this module is an OS/2 oriented replacement for the grp standard
2# extension module.
3
4# written by Andrew MacIntyre, April 2001.
Andrew MacIntyre71d74e82003-07-10 12:52:54 +00005# updated July 2003, adding field accessor support
Andrew MacIntyref47d60f2002-02-22 11:06:30 +00006
Tim Peters182b5ac2004-07-18 06:16:08 +00007# note that this implementation checks whether ":" or ";" as used as
Andrew MacIntyref47d60f2002-02-22 11:06:30 +00008# the field separator character.
9
Tim Peters182b5ac2004-07-18 06:16:08 +000010"""Replacement for grp standard extension module, intended for use on
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000011OS/2 and similar systems which don't normally have an /etc/group file.
12
13The standard Unix group database is an ASCII text file with 4 fields per
14record (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
Tim Peters182b5ac2004-07-18 06:16:08 +000020Note that members are only included in the group file for groups that
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000021aren't their primary groups.
22(see the section 8.2 of the Python Library Reference)
23
Tim Peters182b5ac2004-07-18 06:16:08 +000024This implementation differs from the standard Unix implementation by
25allowing use of the platform's native path separator character - ';' on OS/2,
26DOS and MS-Windows - as the field separator in addition to the Unix
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000027standard ":".
28
Tim Peters182b5ac2004-07-18 06:16:08 +000029The module looks for the group database at the following locations
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000030(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
35Classes
36-------
37
38None
39
40Functions
41---------
42
43getgrgid(gid) - return the record for group-id gid as a 4-tuple
44
45getgrnam(name) - return the record for group 'name' as a 4-tuple
46
47getgrall() - return a list of 4-tuples, each tuple being one record
48 (NOTE: the order is arbitrary)
49
50Attributes
51----------
52
53group_file - the path of the group database file
54
55"""
56
57import os
58
59# try and find the group file
60__group_path = []
Collin Winter04706892007-09-01 20:35:04 +000061if 'ETC_GROUP' in os.environ:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000062 __group_path.append(os.environ['ETC_GROUP'])
Collin Winter04706892007-09-01 20:35:04 +000063if 'ETC' in os.environ:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000064 __group_path.append('%s/group' % os.environ['ETC'])
Collin Winter04706892007-09-01 20:35:04 +000065if 'PYTHONHOME' in os.environ:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000066 __group_path.append('%s/Etc/group' % os.environ['PYTHONHOME'])
67
68group_file = None
69for __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 = [':']
82if os.pathsep:
83 if os.pathsep != ':':
84 __field_sep.append(os.pathsep)
85
86# helper routine to identify which separator character is in use
87def __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:
Collin Wintere45be282007-08-23 00:01:55 +000097 raise KeyError('>> group database fields not delimited <<')
Andrew MacIntyref47d60f2002-02-22 11:06:30 +000098
Andrew MacIntyre71d74e82003-07-10 12:52:54 +000099# 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.
102class 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
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000133# read the whole file, parsing each entry into tuple form
134# with dictionaries to speed recall by GID or group name
135def __read_group_file():
136 if group_file:
137 group = open(group_file, 'r')
138 else:
Collin Wintere45be282007-08-23 00:01:55 +0000139 raise KeyError('>> no group database <<')
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000140 gidx = {}
141 namx = {}
142 sep = None
143 while 1:
144 entry = group.readline().strip()
145 if len(entry) > 3:
Benjamin Peterson2a691a82008-03-31 01:51:45 +0000146 if sep is None:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000147 sep = __get_field_sep(entry)
148 fields = entry.split(sep)
149 fields[2] = int(fields[2])
Andrew MacIntyre71d74e82003-07-10 12:52:54 +0000150 fields[3] = [f.strip() for f in fields[3].split(',')]
151 record = Group(*fields)
Collin Winter04706892007-09-01 20:35:04 +0000152 if fields[2] not in gidx:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000153 gidx[fields[2]] = record
Collin Winter04706892007-09-01 20:35:04 +0000154 if fields[0] not in namx:
Andrew MacIntyref47d60f2002-02-22 11:06:30 +0000155 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
166def getgrgid(gid):
167 g, n = __read_group_file()
168 return g[gid]
169
170# return the group database entry by group name
171def getgrnam(name):
172 g, n = __read_group_file()
173 return n[name]
174
175# return all the group database entries
176def getgrall():
177 g, n = __read_group_file()
178 return g.values()
179
180# test harness
181if __name__ == '__main__':
182 getgrall()