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