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