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* |
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 * |
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 |
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* |
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 * |
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 * |
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 * |
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 |
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 |
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 |
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 |
519 | resourcemodule_clear(PyObject *m) { |
520 | Py_CLEAR(get_resource_state(m)->StructRUsageType); |
521 | return 0; |
522 | } |
523 | |
524 | static void |
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 |
541 | PyInit_resource(void) |
542 | { |
543 | return PyModuleDef_Init(&resourcemodule); |
544 | } |
545 | |