xref: /third_party/python/Modules/grpmodule.c (revision 7db96d56)
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