1
2/* UNIX group file access module */
3
4#include "Python.h"
5#include "posixmodule.h"
6
7#include <grp.h>
8
9#include "clinic/grpmodule.c.h"
10/*[clinic input]
11module grp
12[clinic start generated code]*/
13/*[clinic end generated code: output=da39a3ee5e6b4b0d input=cade63f2ed1bd9f8]*/
14
15static PyStructSequence_Field struct_group_type_fields[] = {
16 {"gr_name", "group name"},
17 {"gr_passwd", "password"},
18 {"gr_gid", "group id"},
19 {"gr_mem", "group members"},
20 {0}
21};
22
23PyDoc_STRVAR(struct_group__doc__,
24"grp.struct_group: Results from getgr*() routines.\n\n\
25This object may be accessed either as a tuple of\n\
26 (gr_name,gr_passwd,gr_gid,gr_mem)\n\
27or via the object attributes as named in the above tuple.\n");
28
29static PyStructSequence_Desc struct_group_type_desc = {
30 "grp.struct_group",
31 struct_group__doc__,
32 struct_group_type_fields,
33 4,
34};
35
36
37typedef struct {
38 PyTypeObject *StructGrpType;
39} grpmodulestate;
40
41static inline grpmodulestate*
42get_grp_state(PyObject *module)
43{
44 void *state = PyModule_GetState(module);
45 assert(state != NULL);
46 return (grpmodulestate *)state;
47}
48
49static struct PyModuleDef grpmodule;
50
51#define DEFAULT_BUFFER_SIZE 1024
52
53static PyObject *
54mkgrent(PyObject *module, struct group *p)
55{
56 int setIndex = 0;
57 PyObject *v, *w;
58 char **member;
59
60 v = PyStructSequence_New(get_grp_state(module)->StructGrpType);
61 if (v == NULL)
62 return NULL;
63
64 if ((w = PyList_New(0)) == NULL) {
65 Py_DECREF(v);
66 return NULL;
67 }
68 for (member = p->gr_mem; *member != NULL; member++) {
69 PyObject *x = PyUnicode_DecodeFSDefault(*member);
70 if (x == NULL || PyList_Append(w, x) != 0) {
71 Py_XDECREF(x);
72 Py_DECREF(w);
73 Py_DECREF(v);
74 return NULL;
75 }
76 Py_DECREF(x);
77 }
78
79#define SET(i,val) PyStructSequence_SET_ITEM(v, i, val)
80 SET(setIndex++, PyUnicode_DecodeFSDefault(p->gr_name));
81 if (p->gr_passwd)
82 SET(setIndex++, PyUnicode_DecodeFSDefault(p->gr_passwd));
83 else {
84 SET(setIndex++, Py_None);
85 Py_INCREF(Py_None);
86 }
87 SET(setIndex++, _PyLong_FromGid(p->gr_gid));
88 SET(setIndex++, w);
89#undef SET
90
91 if (PyErr_Occurred()) {
92 Py_DECREF(v);
93 return NULL;
94 }
95
96 return v;
97}
98
99/*[clinic input]
100grp.getgrgid
101
102 id: object
103
104Return the group database entry for the given numeric group ID.
105
106If id is not valid, raise KeyError.
107[clinic start generated code]*/
108
109static PyObject *
110grp_getgrgid_impl(PyObject *module, PyObject *id)
111/*[clinic end generated code: output=30797c289504a1ba input=15fa0e2ccf5cda25]*/
112{
113 PyObject *retval = NULL;
114 int nomem = 0;
115 char *buf = NULL, *buf2 = NULL;
116 gid_t gid;
117 struct group *p;
118
119 if (!_Py_Gid_Converter(id, &gid)) {
120 return NULL;
121 }
122#ifdef HAVE_GETGRGID_R
123 int status;
124 Py_ssize_t bufsize;
125 /* Note: 'grp' will be used via pointer 'p' on getgrgid_r success. */
126 struct group grp;
127
128 Py_BEGIN_ALLOW_THREADS
129 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
130 if (bufsize == -1) {
131 bufsize = DEFAULT_BUFFER_SIZE;
132 }
133
134 while (1) {
135 buf2 = PyMem_RawRealloc(buf, bufsize);
136 if (buf2 == NULL) {
137 p = NULL;
138 nomem = 1;
139 break;
140 }
141 buf = buf2;
142 status = getgrgid_r(gid, &grp, buf, bufsize, &p);
143 if (status != 0) {
144 p = NULL;
145 }
146 if (p != NULL || status != ERANGE) {
147 break;
148 }
149 if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
150 nomem = 1;
151 break;
152 }
153 bufsize <<= 1;
154 }
155
156 Py_END_ALLOW_THREADS
157#else
158 p = getgrgid(gid);
159#endif
160 if (p == NULL) {
161 PyMem_RawFree(buf);
162 if (nomem == 1) {
163 return PyErr_NoMemory();
164 }
165 PyObject *gid_obj = _PyLong_FromGid(gid);
166 if (gid_obj == NULL)
167 return NULL;
168 PyErr_Format(PyExc_KeyError, "getgrgid(): gid not found: %S", gid_obj);
169 Py_DECREF(gid_obj);
170 return NULL;
171 }
172 retval = mkgrent(module, p);
173#ifdef HAVE_GETGRGID_R
174 PyMem_RawFree(buf);
175#endif
176 return retval;
177}
178
179/*[clinic input]
180grp.getgrnam
181
182 name: unicode
183
184Return the group database entry for the given group name.
185
186If name is not valid, raise KeyError.
187[clinic start generated code]*/
188
189static PyObject *
190grp_getgrnam_impl(PyObject *module, PyObject *name)
191/*[clinic end generated code: output=67905086f403c21c input=08ded29affa3c863]*/
192{
193 char *buf = NULL, *buf2 = NULL, *name_chars;
194 int nomem = 0;
195 struct group *p;
196 PyObject *bytes, *retval = NULL;
197
198 if ((bytes = PyUnicode_EncodeFSDefault(name)) == NULL)
199 return NULL;
200 /* check for embedded null bytes */
201 if (PyBytes_AsStringAndSize(bytes, &name_chars, NULL) == -1)
202 goto out;
203#ifdef HAVE_GETGRNAM_R
204 int status;
205 Py_ssize_t bufsize;
206 /* Note: 'grp' will be used via pointer 'p' on getgrnam_r success. */
207 struct group grp;
208
209 Py_BEGIN_ALLOW_THREADS
210 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
211 if (bufsize == -1) {
212 bufsize = DEFAULT_BUFFER_SIZE;
213 }
214
215 while(1) {
216 buf2 = PyMem_RawRealloc(buf, bufsize);
217 if (buf2 == NULL) {
218 p = NULL;
219 nomem = 1;
220 break;
221 }
222 buf = buf2;
223 status = getgrnam_r(name_chars, &grp, buf, bufsize, &p);
224 if (status != 0) {
225 p = NULL;
226 }
227 if (p != NULL || status != ERANGE) {
228 break;
229 }
230 if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
231 nomem = 1;
232 break;
233 }
234 bufsize <<= 1;
235 }
236
237 Py_END_ALLOW_THREADS
238#else
239 p = getgrnam(name_chars);
240#endif
241 if (p == NULL) {
242 if (nomem == 1) {
243 PyErr_NoMemory();
244 }
245 else {
246 PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %R", name);
247 }
248 goto out;
249 }
250 retval = mkgrent(module, p);
251out:
252 PyMem_RawFree(buf);
253 Py_DECREF(bytes);
254 return retval;
255}
256
257/*[clinic input]
258grp.getgrall
259
260Return a list of all available group entries, in arbitrary order.
261
262An entry whose name starts with '+' or '-' represents an instruction
263to use YP/NIS and may not be accessible via getgrnam or getgrgid.
264[clinic start generated code]*/
265
266static PyObject *
267grp_getgrall_impl(PyObject *module)
268/*[clinic end generated code: output=585dad35e2e763d7 input=d7df76c825c367df]*/
269{
270 PyObject *d;
271 struct group *p;
272
273 if ((d = PyList_New(0)) == NULL)
274 return NULL;
275 setgrent();
276 while ((p = getgrent()) != NULL) {
277 PyObject *v = mkgrent(module, p);
278 if (v == NULL || PyList_Append(d, v) != 0) {
279 Py_XDECREF(v);
280 Py_DECREF(d);
281 endgrent();
282 return NULL;
283 }
284 Py_DECREF(v);
285 }
286 endgrent();
287 return d;
288}
289
290static PyMethodDef grp_methods[] = {
291 GRP_GETGRGID_METHODDEF
292 GRP_GETGRNAM_METHODDEF
293 GRP_GETGRALL_METHODDEF
294 {NULL, NULL}
295};
296
297PyDoc_STRVAR(grp__doc__,
298"Access to the Unix group database.\n\
299\n\
300Group entries are reported as 4-tuples containing the following fields\n\
301from the group database, in order:\n\
302\n\
303 gr_name - name of the group\n\
304 gr_passwd - group password (encrypted); often empty\n\
305 gr_gid - numeric ID of the group\n\
306 gr_mem - list of members\n\
307\n\
308The gid is an integer, name and password are strings. (Note that most\n\
309users are not explicitly listed as members of the groups they are in\n\
310according to the password database. Check both databases to get\n\
311complete membership information.)");
312
313static int
314grpmodule_exec(PyObject *module)
315{
316 grpmodulestate *state = get_grp_state(module);
317
318 state->StructGrpType = PyStructSequence_NewType(&struct_group_type_desc);
319 if (state->StructGrpType == NULL) {
320 return -1;
321 }
322 if (PyModule_AddType(module, state->StructGrpType) < 0) {
323 return -1;
324 }
325 return 0;
326}
327
328static PyModuleDef_Slot grpmodule_slots[] = {
329 {Py_mod_exec, grpmodule_exec},
330 {0, NULL}
331};
332
333static int grpmodule_traverse(PyObject *m, visitproc visit, void *arg) {
334 Py_VISIT(get_grp_state(m)->StructGrpType);
335 return 0;
336}
337
338static int grpmodule_clear(PyObject *m) {
339 Py_CLEAR(get_grp_state(m)->StructGrpType);
340 return 0;
341}
342
343static void grpmodule_free(void *m) {
344 grpmodule_clear((PyObject *)m);
345}
346
347static struct PyModuleDef grpmodule = {
348 PyModuleDef_HEAD_INIT,
349 .m_name = "grp",
350 .m_doc = grp__doc__,
351 .m_size = sizeof(grpmodulestate),
352 .m_methods = grp_methods,
353 .m_slots = grpmodule_slots,
354 .m_traverse = grpmodule_traverse,
355 .m_clear = grpmodule_clear,
356 .m_free = grpmodule_free,
357};
358
359PyMODINIT_FUNC
360PyInit_grp(void)
361{
362 return PyModuleDef_Init(&grpmodule);
363}
364