xref: /third_party/python/Modules/resource.c (revision 7db96d56)
1
2#include "Python.h"
3#include <sys/resource.h>
4#include <sys/time.h>
5#include <string.h>
6#include <errno.h>
7#include <unistd.h>
8
9/* On some systems, these aren't in any header file.
10   On others they are, with inconsistent prototypes.
11   We declare the (default) return type, to shut up gcc -Wall;
12   but we can't declare the prototype, to avoid errors
13   when the header files declare it different.
14   Worse, on some Linuxes, getpagesize() returns a size_t... */
15
16#define doubletime(TV) ((double)(TV).tv_sec + (TV).tv_usec * 0.000001)
17
18/*[clinic input]
19module resource
20[clinic start generated code]*/
21/*[clinic end generated code: output=da39a3ee5e6b4b0d input=e89d38ed52609d7c]*/
22
23/*[python input]
24class pid_t_converter(CConverter):
25    type = 'pid_t'
26    format_unit = '" _Py_PARSE_PID "'
27[python start generated code]*/
28/*[python end generated code: output=da39a3ee5e6b4b0d input=0c1d19f640d57e48]*/
29
30#include "clinic/resource.c.h"
31
32PyDoc_STRVAR(struct_rusage__doc__,
33"struct_rusage: Result from getrusage.\n\n"
34"This object may be accessed either as a tuple of\n"
35"    (utime,stime,maxrss,ixrss,idrss,isrss,minflt,majflt,\n"
36"    nswap,inblock,oublock,msgsnd,msgrcv,nsignals,nvcsw,nivcsw)\n"
37"or via the attributes ru_utime, ru_stime, ru_maxrss, and so on.");
38
39static PyStructSequence_Field struct_rusage_fields[] = {
40    {"ru_utime",        "user time used"},
41    {"ru_stime",        "system time used"},
42    {"ru_maxrss",       "max. resident set size"},
43    {"ru_ixrss",        "shared memory size"},
44    {"ru_idrss",        "unshared data size"},
45    {"ru_isrss",        "unshared stack size"},
46    {"ru_minflt",       "page faults not requiring I/O"},
47    {"ru_majflt",       "page faults requiring I/O"},
48    {"ru_nswap",        "number of swap outs"},
49    {"ru_inblock",      "block input operations"},
50    {"ru_oublock",      "block output operations"},
51    {"ru_msgsnd",       "IPC messages sent"},
52    {"ru_msgrcv",       "IPC messages received"},
53    {"ru_nsignals",     "signals received"},
54    {"ru_nvcsw",        "voluntary context switches"},
55    {"ru_nivcsw",       "involuntary context switches"},
56    {0}
57};
58
59static PyStructSequence_Desc struct_rusage_desc = {
60    "resource.struct_rusage",           /* name */
61    struct_rusage__doc__,       /* doc */
62    struct_rusage_fields,       /* fields */
63    16          /* n_in_sequence */
64};
65
66typedef struct {
67  PyTypeObject *StructRUsageType;
68} resourcemodulestate;
69
70
71static inline resourcemodulestate*
72get_resource_state(PyObject *module)
73{
74    void *state = PyModule_GetState(module);
75    assert(state != NULL);
76    return (resourcemodulestate *)state;
77}
78
79static struct PyModuleDef resourcemodule;
80
81#ifdef HAVE_GETRUSAGE
82/*[clinic input]
83resource.getrusage
84
85    who: int
86    /
87
88[clinic start generated code]*/
89
90static PyObject *
91resource_getrusage_impl(PyObject *module, int who)
92/*[clinic end generated code: output=8fad2880ba6a9843 input=5c857bcc5b9ccb1b]*/
93{
94    struct rusage ru;
95    PyObject *result;
96
97    if (getrusage(who, &ru) == -1) {
98        if (errno == EINVAL) {
99            PyErr_SetString(PyExc_ValueError,
100                            "invalid who parameter");
101            return NULL;
102        }
103        PyErr_SetFromErrno(PyExc_OSError);
104        return NULL;
105    }
106
107    result = PyStructSequence_New(
108        get_resource_state(module)->StructRUsageType);
109    if (!result)
110        return NULL;
111
112    PyStructSequence_SET_ITEM(result, 0,
113                    PyFloat_FromDouble(doubletime(ru.ru_utime)));
114    PyStructSequence_SET_ITEM(result, 1,
115                    PyFloat_FromDouble(doubletime(ru.ru_stime)));
116    PyStructSequence_SET_ITEM(result, 2, PyLong_FromLong(ru.ru_maxrss));
117    PyStructSequence_SET_ITEM(result, 3, PyLong_FromLong(ru.ru_ixrss));
118    PyStructSequence_SET_ITEM(result, 4, PyLong_FromLong(ru.ru_idrss));
119    PyStructSequence_SET_ITEM(result, 5, PyLong_FromLong(ru.ru_isrss));
120    PyStructSequence_SET_ITEM(result, 6, PyLong_FromLong(ru.ru_minflt));
121    PyStructSequence_SET_ITEM(result, 7, PyLong_FromLong(ru.ru_majflt));
122    PyStructSequence_SET_ITEM(result, 8, PyLong_FromLong(ru.ru_nswap));
123    PyStructSequence_SET_ITEM(result, 9, PyLong_FromLong(ru.ru_inblock));
124    PyStructSequence_SET_ITEM(result, 10, PyLong_FromLong(ru.ru_oublock));
125    PyStructSequence_SET_ITEM(result, 11, PyLong_FromLong(ru.ru_msgsnd));
126    PyStructSequence_SET_ITEM(result, 12, PyLong_FromLong(ru.ru_msgrcv));
127    PyStructSequence_SET_ITEM(result, 13, PyLong_FromLong(ru.ru_nsignals));
128    PyStructSequence_SET_ITEM(result, 14, PyLong_FromLong(ru.ru_nvcsw));
129    PyStructSequence_SET_ITEM(result, 15, PyLong_FromLong(ru.ru_nivcsw));
130
131    if (PyErr_Occurred()) {
132        Py_DECREF(result);
133        return NULL;
134    }
135
136    return result;
137}
138#endif
139
140static int
141py2rlimit(PyObject *limits, struct rlimit *rl_out)
142{
143    PyObject *curobj, *maxobj;
144    limits = PySequence_Tuple(limits);
145    if (!limits)
146        /* Here limits is a borrowed reference */
147        return -1;
148
149    if (PyTuple_GET_SIZE(limits) != 2) {
150        PyErr_SetString(PyExc_ValueError,
151                        "expected a tuple of 2 integers");
152        goto error;
153    }
154    curobj = PyTuple_GET_ITEM(limits, 0);
155    maxobj = PyTuple_GET_ITEM(limits, 1);
156#if !defined(HAVE_LARGEFILE_SUPPORT)
157    rl_out->rlim_cur = PyLong_AsLong(curobj);
158    if (rl_out->rlim_cur == (rlim_t)-1 && PyErr_Occurred())
159        goto error;
160    rl_out->rlim_max = PyLong_AsLong(maxobj);
161    if (rl_out->rlim_max == (rlim_t)-1 && PyErr_Occurred())
162        goto error;
163#else
164    /* The limits are probably bigger than a long */
165    rl_out->rlim_cur = PyLong_AsLongLong(curobj);
166    if (rl_out->rlim_cur == (rlim_t)-1 && PyErr_Occurred())
167        goto error;
168    rl_out->rlim_max = PyLong_AsLongLong(maxobj);
169    if (rl_out->rlim_max == (rlim_t)-1 && PyErr_Occurred())
170        goto error;
171#endif
172
173    Py_DECREF(limits);
174    rl_out->rlim_cur = rl_out->rlim_cur & RLIM_INFINITY;
175    rl_out->rlim_max = rl_out->rlim_max & RLIM_INFINITY;
176    return 0;
177
178error:
179    Py_DECREF(limits);
180    return -1;
181}
182
183static PyObject*
184rlimit2py(struct rlimit rl)
185{
186    if (sizeof(rl.rlim_cur) > sizeof(long)) {
187        return Py_BuildValue("LL",
188                             (long long) rl.rlim_cur,
189                             (long long) rl.rlim_max);
190    }
191    return Py_BuildValue("ll", (long) rl.rlim_cur, (long) rl.rlim_max);
192}
193
194/*[clinic input]
195resource.getrlimit
196
197    resource: int
198    /
199
200[clinic start generated code]*/
201
202static PyObject *
203resource_getrlimit_impl(PyObject *module, int resource)
204/*[clinic end generated code: output=98327b25061ffe39 input=a697cb0004cb3c36]*/
205{
206    struct rlimit rl;
207
208    if (resource < 0 || resource >= RLIM_NLIMITS) {
209        PyErr_SetString(PyExc_ValueError,
210                        "invalid resource specified");
211        return NULL;
212    }
213
214    if (getrlimit(resource, &rl) == -1) {
215        PyErr_SetFromErrno(PyExc_OSError);
216        return NULL;
217    }
218    return rlimit2py(rl);
219}
220
221/*[clinic input]
222resource.setrlimit
223
224    resource: int
225    limits: object
226    /
227
228[clinic start generated code]*/
229
230static PyObject *
231resource_setrlimit_impl(PyObject *module, int resource, PyObject *limits)
232/*[clinic end generated code: output=4e82ec3f34d013d1 input=6235a6ce23b4ca75]*/
233{
234    struct rlimit rl;
235
236    if (resource < 0 || resource >= RLIM_NLIMITS) {
237        PyErr_SetString(PyExc_ValueError,
238                        "invalid resource specified");
239        return NULL;
240    }
241
242    if (PySys_Audit("resource.setrlimit", "iO", resource,
243                    limits ? limits : Py_None) < 0) {
244        return NULL;
245    }
246
247    if (py2rlimit(limits, &rl) < 0) {
248        return NULL;
249    }
250
251    if (setrlimit(resource, &rl) == -1) {
252        if (errno == EINVAL)
253            PyErr_SetString(PyExc_ValueError,
254                            "current limit exceeds maximum limit");
255        else if (errno == EPERM)
256            PyErr_SetString(PyExc_ValueError,
257                            "not allowed to raise maximum limit");
258        else
259            PyErr_SetFromErrno(PyExc_OSError);
260        return NULL;
261    }
262    Py_RETURN_NONE;
263}
264
265#ifdef HAVE_PRLIMIT
266/*[clinic input]
267resource.prlimit
268
269    pid: pid_t
270    resource: int
271    [
272    limits: object
273    ]
274    /
275
276[clinic start generated code]*/
277
278static PyObject *
279resource_prlimit_impl(PyObject *module, pid_t pid, int resource,
280                      int group_right_1, PyObject *limits)
281/*[clinic end generated code: output=ee976b393187a7a3 input=b77743bdccc83564]*/
282{
283    struct rlimit old_limit, new_limit;
284    int retval;
285
286    if (resource < 0 || resource >= RLIM_NLIMITS) {
287        PyErr_SetString(PyExc_ValueError,
288                        "invalid resource specified");
289        return NULL;
290    }
291
292    if (PySys_Audit("resource.prlimit", "iiO", pid, resource,
293                    limits ? limits : Py_None) < 0) {
294        return NULL;
295    }
296
297    if (group_right_1) {
298        if (py2rlimit(limits, &new_limit) < 0) {
299            return NULL;
300        }
301        retval = prlimit(pid, resource, &new_limit, &old_limit);
302    }
303    else {
304        retval = prlimit(pid, resource, NULL, &old_limit);
305    }
306
307    if (retval == -1) {
308        if (errno == EINVAL) {
309            PyErr_SetString(PyExc_ValueError,
310                            "current limit exceeds maximum limit");
311        } else {
312            PyErr_SetFromErrno(PyExc_OSError);
313        }
314        return NULL;
315    }
316    return rlimit2py(old_limit);
317}
318#endif /* HAVE_PRLIMIT */
319
320/*[clinic input]
321resource.getpagesize -> int
322[clinic start generated code]*/
323
324static int
325resource_getpagesize_impl(PyObject *module)
326/*[clinic end generated code: output=9ba93eb0f3d6c3a9 input=546545e8c1f42085]*/
327{
328    long pagesize = 0;
329#if defined(HAVE_GETPAGESIZE)
330    pagesize = getpagesize();
331#elif defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
332    pagesize = sysconf(_SC_PAGE_SIZE);
333#else
334#   error "unsupported platform: resource.getpagesize()"
335#endif
336    return pagesize;
337}
338
339/* List of functions */
340
341static struct PyMethodDef
342resource_methods[] = {
343    RESOURCE_GETRUSAGE_METHODDEF
344    RESOURCE_GETRLIMIT_METHODDEF
345    RESOURCE_PRLIMIT_METHODDEF
346    RESOURCE_SETRLIMIT_METHODDEF
347    RESOURCE_GETPAGESIZE_METHODDEF
348    {NULL, NULL}                             /* sentinel */
349};
350
351
352/* Module initialization */
353
354static int
355resource_exec(PyObject *module)
356{
357    resourcemodulestate *state = get_resource_state(module);
358#define ADD_INT(module, value)                                    \
359    do {                                                          \
360        if (PyModule_AddIntConstant(module, #value, value) < 0) { \
361            return -1;                                            \
362        }                                                         \
363    } while (0)
364
365    /* Add some symbolic constants to the module */
366    Py_INCREF(PyExc_OSError);
367    if (PyModule_AddObject(module, "error", PyExc_OSError) < 0) {
368        Py_DECREF(PyExc_OSError);
369        return -1;
370    }
371
372    state->StructRUsageType = PyStructSequence_NewType(&struct_rusage_desc);
373    if (state->StructRUsageType == NULL) {
374        return -1;
375    }
376    if (PyModule_AddType(module, state->StructRUsageType) < 0) {
377        return -1;
378    }
379
380    /* insert constants */
381#ifdef RLIMIT_CPU
382    ADD_INT(module, RLIMIT_CPU);
383#endif
384
385#ifdef RLIMIT_FSIZE
386    ADD_INT(module, RLIMIT_FSIZE);
387#endif
388
389#ifdef RLIMIT_DATA
390    ADD_INT(module, RLIMIT_DATA);
391#endif
392
393#ifdef RLIMIT_STACK
394    ADD_INT(module, RLIMIT_STACK);
395#endif
396
397#ifdef RLIMIT_CORE
398    ADD_INT(module, RLIMIT_CORE);
399#endif
400
401#ifdef RLIMIT_NOFILE
402    ADD_INT(module, RLIMIT_NOFILE);
403#endif
404
405#ifdef RLIMIT_OFILE
406    ADD_INT(module, RLIMIT_OFILE);
407#endif
408
409#ifdef RLIMIT_VMEM
410    ADD_INT(module, RLIMIT_VMEM);
411#endif
412
413#ifdef RLIMIT_AS
414    ADD_INT(module, RLIMIT_AS);
415#endif
416
417#ifdef RLIMIT_RSS
418    ADD_INT(module, RLIMIT_RSS);
419#endif
420
421#ifdef RLIMIT_NPROC
422    ADD_INT(module, RLIMIT_NPROC);
423#endif
424
425#ifdef RLIMIT_MEMLOCK
426    ADD_INT(module, RLIMIT_MEMLOCK);
427#endif
428
429#ifdef RLIMIT_SBSIZE
430    ADD_INT(module, RLIMIT_SBSIZE);
431#endif
432
433/* Linux specific */
434#ifdef RLIMIT_MSGQUEUE
435    ADD_INT(module, RLIMIT_MSGQUEUE);
436#endif
437
438#ifdef RLIMIT_NICE
439    ADD_INT(module, RLIMIT_NICE);
440#endif
441
442#ifdef RLIMIT_RTPRIO
443    ADD_INT(module, RLIMIT_RTPRIO);
444#endif
445
446#ifdef RLIMIT_RTTIME
447    ADD_INT(module, RLIMIT_RTTIME);
448#endif
449
450#ifdef RLIMIT_SIGPENDING
451    ADD_INT(module, RLIMIT_SIGPENDING);
452#endif
453
454/* target */
455#ifdef RUSAGE_SELF
456    ADD_INT(module, RUSAGE_SELF);
457#endif
458
459#ifdef RUSAGE_CHILDREN
460    ADD_INT(module, RUSAGE_CHILDREN);
461#endif
462
463#ifdef RUSAGE_BOTH
464    ADD_INT(module, RUSAGE_BOTH);
465#endif
466
467#ifdef RUSAGE_THREAD
468    ADD_INT(module, RUSAGE_THREAD);
469#endif
470
471/* FreeBSD specific */
472
473#ifdef RLIMIT_SWAP
474    ADD_INT(module, RLIMIT_SWAP);
475#endif
476
477#ifdef RLIMIT_SBSIZE
478    ADD_INT(module, RLIMIT_SBSIZE);
479#endif
480
481#ifdef RLIMIT_NPTS
482    ADD_INT(module, RLIMIT_NPTS);
483#endif
484
485#ifdef RLIMIT_KQUEUES
486    ADD_INT(module, RLIMIT_KQUEUES);
487#endif
488
489    PyObject *v;
490    if (sizeof(RLIM_INFINITY) > sizeof(long)) {
491        v = PyLong_FromLongLong((long long) RLIM_INFINITY);
492    } else
493    {
494        v = PyLong_FromLong((long) RLIM_INFINITY);
495    }
496    if (!v) {
497        return -1;
498    }
499
500    if (PyModule_AddObject(module, "RLIM_INFINITY", v) < 0) {
501        Py_DECREF(v);
502        return -1;
503    }
504    return 0;
505
506#undef ADD_INT
507}
508
509static struct PyModuleDef_Slot resource_slots[] = {
510    {Py_mod_exec, resource_exec},
511    {0, NULL}
512};
513
514static int
515resourcemodule_traverse(PyObject *m, visitproc visit, void *arg) {
516    Py_VISIT(get_resource_state(m)->StructRUsageType);
517    return 0;
518}
519
520static int
521resourcemodule_clear(PyObject *m) {
522    Py_CLEAR(get_resource_state(m)->StructRUsageType);
523    return 0;
524}
525
526static void
527resourcemodule_free(void *m) {
528    resourcemodule_clear((PyObject *)m);
529}
530
531static struct PyModuleDef resourcemodule = {
532    PyModuleDef_HEAD_INIT,
533    .m_name = "resource",
534    .m_size = sizeof(resourcemodulestate),
535    .m_methods = resource_methods,
536    .m_slots = resource_slots,
537    .m_traverse = resourcemodule_traverse,
538    .m_clear = resourcemodule_clear,
539    .m_free = resourcemodule_free,
540};
541
542PyMODINIT_FUNC
543PyInit_resource(void)
544{
545    return PyModuleDef_Init(&resourcemodule);
546}
547