083c945bc1d1c01974ffd3fa0df67cec2feaadad
[util-vserver.git] / python / vserverimpl.c
1 /* Copyright 2005 Princeton University
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions
5 are met: 
6
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9       
10 * Redistributions in binary form must reproduce the above
11 copyright notice, this list of conditions and the following
12 disclaimer in the documentation and/or other materials provided
13 with the distribution.
14       
15 * Neither the name of the copyright holder nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
18       
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRINCETON
23 UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29 WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE. 
31
32 */
33
34 #include <Python.h>
35
36 #include <errno.h>
37 #include <stdint.h>
38 #include <sys/resource.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <sys/socket.h>
43 #include <arpa/inet.h>
44 #include <ifaddrs.h>
45 #include <stddef.h>
46 #include <fcntl.h>
47 #include <sys/mount.h>
48 #include <utmp.h>
49
50 #include "config.h"
51 #include "pathconfig.h"
52 #include "virtual.h"
53 #include "vserver.h"
54 #include "planetlab.h"
55 #include "vserver-internal.h"
56
57 #define NONE  ({ Py_INCREF(Py_None); Py_None; })
58
59 /*
60  * context create
61  */
62 static PyObject *
63 vserver_chcontext(PyObject *self, PyObject *args)
64 {
65   int  ctx_is_new;
66   xid_t  ctx;
67   uint_least64_t bcaps = 0;
68
69   if (!PyArg_ParseTuple(args, "I|K", &ctx, &bcaps))
70     return NULL;
71   bcaps |= ~vc_get_insecurebcaps();
72
73   if ((ctx_is_new = pl_chcontext(ctx, bcaps, 0)) < 0)
74     return PyErr_SetFromErrno(PyExc_OSError);
75
76   return PyBool_FromLong(ctx_is_new);
77 }
78
79 static PyObject *
80 vserver_setup_done(PyObject *self, PyObject *args)
81 {
82   xid_t  ctx;
83
84   if (!PyArg_ParseTuple(args, "I", &ctx))
85     return NULL;
86
87   if (pl_setup_done(ctx) < 0)
88     return PyErr_SetFromErrno(PyExc_OSError);
89
90   return NONE;
91 }
92
93 static PyObject *
94 vserver_isrunning(PyObject *self, PyObject *args)
95 {
96   xid_t  ctx;
97   PyObject *ret;
98   struct stat statbuf;
99   char fname[64];
100
101   if (!PyArg_ParseTuple(args, "I", &ctx))
102     return NULL;
103
104   sprintf(fname,"/proc/virtual/%d", ctx);
105
106   if(stat(&fname[0],&statbuf)==0)
107     ret = PyBool_FromLong(1);
108   else
109     ret = PyBool_FromLong(0);
110
111   return ret;
112 }
113
114 static PyObject *
115 __vserver_get_rlimit(xid_t xid, int resource) {
116   struct vc_rlimit limits;
117   PyObject *ret;
118
119   errno = 0;
120   if (vc_get_rlimit(xid, resource, &limits)==-1)
121     ret = PyErr_SetFromErrno(PyExc_OSError);
122   else
123     ret = Py_BuildValue("LLL",limits.hard, limits.soft, limits.min);
124
125   return ret;
126 }
127
128 static PyObject *
129 vserver_get_rlimit(PyObject *self, PyObject *args) {
130   xid_t xid;
131   int resource;
132   PyObject *ret;
133
134   if (!PyArg_ParseTuple(args, "Ii", &xid, &resource))
135     ret = NULL;
136   else
137     ret = __vserver_get_rlimit(xid, resource);
138
139   return ret;
140 }
141
142 static PyObject *
143 vserver_set_rlimit(PyObject *self, PyObject *args) {
144   struct vc_rlimit limits;
145   struct rlimit lim;
146   xid_t xid;
147   int resource, lresource;
148   PyObject *ret;
149
150   limits.min = VC_LIM_KEEP;
151   limits.soft = VC_LIM_KEEP;
152   limits.hard = VC_LIM_KEEP;
153
154   if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
155     return NULL;
156
157   lresource = resource;
158   switch (resource) {
159   case VC_VLIMIT_NSOCK:
160   case VC_VLIMIT_ANON:
161   case VC_VLIMIT_SHMEM:
162     goto do_vc_set_rlimit;
163   case VC_VLIMIT_OPENFD:
164     lresource = RLIMIT_NOFILE;
165     break;
166   default:
167     break;
168   }
169
170   getrlimit(lresource,&lim);
171   if (adjust_lim(&limits,&lim)) {
172     setrlimit(lresource, &lim);
173   }
174
175  do_vc_set_rlimit:
176   errno = 0;
177   if (vc_set_rlimit(xid, resource, &limits)==-1) 
178     ret = PyErr_SetFromErrno(PyExc_OSError);
179   else
180     ret = __vserver_get_rlimit(xid, resource);
181
182   return ret;
183 }
184
185 /*
186  * setsched
187  */
188 static PyObject *
189 vserver_setsched(PyObject *self, PyObject *args)
190 {
191   xid_t  ctx;
192   uint32_t  cpu_share;
193   uint32_t  cpu_sched_flags = VC_VXF_SCHED_FLAGS;
194
195   if (!PyArg_ParseTuple(args, "II|I", &ctx, &cpu_share, &cpu_sched_flags))
196     return NULL;
197
198   /* ESRCH indicates that there are no processes in the context */
199   if (pl_setsched(ctx, cpu_share, cpu_sched_flags) &&
200       errno != ESRCH)
201     return PyErr_SetFromErrno(PyExc_OSError);
202
203   return NONE;
204 }
205
206 static PyObject *
207 vserver_get_dlimit(PyObject *self, PyObject *args)
208 {
209   PyObject *res;
210   char* path;
211   unsigned xid;
212   struct vc_ctx_dlimit data;
213   int r;
214
215   if (!PyArg_ParseTuple(args, "si", &path,&xid))
216     return NULL;
217
218   memset(&data, 0, sizeof(data));
219   r = vc_get_dlimit(path, xid, 0, &data);
220   if (r>=0) {
221     res = Py_BuildValue("(i,i,i,i,i)",
222                         data.space_used,
223                         data.space_total,
224                         data.inodes_used,
225                         data.inodes_total,
226                         data.reserved);
227   } else {
228     res = PyErr_SetFromErrno(PyExc_OSError);
229   }
230
231   return res;
232 }
233
234
235 static PyObject *
236 vserver_set_dlimit(PyObject *self, PyObject *args)
237 {
238   char* path;
239   unsigned xid;
240   struct vc_ctx_dlimit data;
241
242   memset(&data,0,sizeof(data));
243   if (!PyArg_ParseTuple(args, "siiiiii", &path,
244                         &xid,
245                         &data.space_used,
246                         &data.space_total,
247                         &data.inodes_used,
248                         &data.inodes_total,
249                         &data.reserved))
250     return NULL;
251
252   if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
253       vc_set_dlimit(path, xid, 0, &data))
254     return PyErr_SetFromErrno(PyExc_OSError);
255
256   return NONE;  
257 }
258
259 static PyObject *
260 vserver_unset_dlimit(PyObject *self, PyObject *args)
261 {
262   char  *path;
263   unsigned  xid;
264
265   if (!PyArg_ParseTuple(args, "si", &path, &xid))
266     return NULL;
267
268   if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
269     return PyErr_SetFromErrno(PyExc_OSError);
270
271   return NONE;  
272 }
273
274 static PyObject *
275 vserver_killall(PyObject *self, PyObject *args)
276 {
277   xid_t ctx;
278   int   sig;
279   struct vc_ctx_flags cflags = {
280     .flagword = 0,
281     .mask = VC_VXF_PERSISTENT
282   };
283   struct vc_net_flags nflags = {
284     .flagword = 0,
285     .mask = VC_NXF_PERSISTENT
286   };
287
288   if (!PyArg_ParseTuple(args, "Ii", &ctx, &sig))
289     return NULL;
290
291   if (vc_ctx_kill(ctx, 0, sig) && errno != ESRCH)
292     return PyErr_SetFromErrno(PyExc_OSError);
293
294   if (vc_set_cflags(ctx, &cflags) && errno != ESRCH)
295     return PyErr_SetFromErrno(PyExc_OSError);
296
297   if (vc_set_nflags(ctx, &nflags) && errno != ESRCH)
298     return PyErr_SetFromErrno(PyExc_OSError);
299
300   return NONE;
301 }
302
303 static PyObject *
304 vserver_set_bcaps(PyObject *self, PyObject *args)
305 {
306   xid_t ctx;
307   struct vc_ctx_caps caps;
308
309   if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
310     return NULL;
311
312   caps.bmask = vc_get_insecurebcaps();
313   caps.cmask = caps.ccaps = 0;
314   if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
315     return PyErr_SetFromErrno(PyExc_OSError);
316
317   return NONE;
318 }
319
320 static PyObject *
321 vserver_text2bcaps(PyObject *self, PyObject *args)
322 {
323   struct vc_ctx_caps caps = { .bcaps = 0 };
324   const char *list;
325   int len;
326   struct vc_err_listparser err;
327
328   if (!PyArg_ParseTuple(args, "s#", &list, &len))
329     return NULL;
330
331   vc_list2bcap(list, len, &err, &caps);
332
333   return Py_BuildValue("K", caps.bcaps);
334 }
335
336 static PyObject *
337 vserver_get_bcaps(PyObject *self, PyObject *args)
338 {
339   xid_t ctx;
340   struct vc_ctx_caps caps;
341
342   if (!PyArg_ParseTuple(args, "I", &ctx))
343     return NULL;
344
345   if (vc_get_ccaps(ctx, &caps) == -1) {
346     if (errno != -ESRCH)
347       return PyErr_SetFromErrno(PyExc_OSError);
348     else
349       caps.bcaps = 0;
350   }
351
352   return Py_BuildValue("K", caps.bcaps & vc_get_insecurebcaps());
353 }
354
355 static PyObject *
356 vserver_bcaps2text(PyObject *self, PyObject *args)
357 {
358   struct vc_ctx_caps caps = { .bcaps = 0 };
359   PyObject *list;
360   const char *cap;
361
362   if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
363     return NULL;
364
365   list = PyString_FromString("");
366
367   while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
368     if (list == NULL)
369       break;
370     PyString_ConcatAndDel(&list, PyString_FromFormat(
371                           (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
372                           cap));
373   }
374
375   return list;
376 }
377
378 static const struct AF_to_vcNET {
379   int af;
380   vc_net_nx_type vc_net;
381   size_t len;
382   size_t offset;
383 } converter[] = {
384   { AF_INET,  vcNET_IPV4, sizeof(struct in_addr),  offsetof(struct sockaddr_in,  sin_addr.s_addr) },
385   { AF_INET6, vcNET_IPV6, sizeof(struct in6_addr), offsetof(struct sockaddr_in6, sin6_addr.s6_addr) },
386   { 0, 0, 0, 0 }
387 };
388
389 static inline int
390 convert_address(const char *str, vc_net_nx_type *type, void *dst)
391 {
392   const struct AF_to_vcNET *i;
393   for (i = converter; i->af; i++) {
394     if (inet_pton(i->af, str, dst)) {
395       *type = i->vc_net;
396       return 0;
397     }
398   }
399   return -1;
400 }
401
402 static int
403 get_mask(struct vc_net_nx *addr)
404 {
405   const struct AF_to_vcNET *i;
406   struct ifaddrs *head, *ifa;
407   int ret = 0;
408
409   for (i = converter; i->af; i++) {
410     if (i->vc_net == addr->type)
411       break;
412   }
413   if (!i) {
414     errno = EINVAL;
415     return -1;
416   }
417
418   if (getifaddrs(&head) == -1)
419     return -1;
420   for (ifa = head; ifa; ifa = ifa->ifa_next) {
421     if (ifa->ifa_addr->sa_family == i->af &&
422         memcmp((char *) ifa->ifa_addr + i->offset, addr->ip, i->len) == 0) {
423       switch (addr->type) {
424       case vcNET_IPV4:
425         memcpy(&addr->mask[0], ifa->ifa_netmask + i->offset, i->len);
426         break;
427       case vcNET_IPV6: {
428         uint32_t *m = ((struct sockaddr_in6 *) ifa->ifa_netmask)->sin6_addr.s6_addr32;
429         /* optimization for the common case */
430         if ((m[1] & 1) == 1 && (m[2] & 0x80000000) == 0)
431           addr->mask[0] = 64;
432         else {
433           addr->mask[0] = 0;
434           while (m[addr->mask[0] / 32] & (addr->mask[0] % 32))
435             addr->mask[0]++;
436         }
437         break;
438         }
439       }
440       ret = 1;
441       break;
442     }
443   }
444   /* no match, use a default */
445   if (!ret) {
446     switch (addr->type) {
447     case vcNET_IPV4:    addr->mask[0] = htonl(0xffffff00); break;
448     case vcNET_IPV6:    addr->mask[0] = 64; break;
449     default:            addr->mask[0] = 0; break;
450     }
451   }
452   freeifaddrs(head);
453   return ret;
454 }
455
456 /* XXX These two functions are really similar */
457 static PyObject *
458 vserver_net_add(PyObject *self, PyObject *args)
459 {
460   struct vc_net_nx addr;
461   nid_t nid;
462   const char *ip;
463
464   if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
465     return NULL;
466
467   if (convert_address(ip, &addr.type, &addr.ip) == -1)
468     return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
469
470   switch (get_mask(&addr)) {
471   case -1:
472     return PyErr_SetFromErrno(PyExc_OSError);
473   case 0:
474     /* XXX error here? */
475     break;
476   }
477   addr.count = 1;
478
479   if (vc_net_add(nid, &addr) == -1 && errno != ESRCH)
480     return PyErr_SetFromErrno(PyExc_OSError);
481
482   return NONE;
483 }
484
485 static PyObject *
486 vserver_net_remove(PyObject *self, PyObject *args)
487 {
488   struct vc_net_nx addr;
489   nid_t nid;
490   const char *ip;
491
492   if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
493     return NULL;
494
495   if (strcmp(ip, "all") == 0)
496     addr.type = vcNET_ANY;
497   else if (strcmp(ip, "all4") == 0)
498     addr.type = vcNET_IPV4A;
499   else if (strcmp(ip, "all6") == 0)
500     addr.type = vcNET_IPV6A;
501   else
502     if (convert_address(ip, &addr.type, &addr.ip) == -1)
503       return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
504
505   switch (get_mask(&addr)) {
506   case -1:
507     return PyErr_SetFromErrno(PyExc_OSError);
508   }
509   addr.count = 1;
510
511   if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
512     return PyErr_SetFromErrno(PyExc_OSError);
513
514   return NONE;
515 }
516
517 struct secure_dirs {
518   int host_fd;
519   int cwd_fd;
520   int guest_fd;
521   int target_fd;
522 };
523
524 static inline int
525 fchroot(int fd)
526 {
527   if (fchdir(fd) == -1 || chroot(".") == -1)
528     return -1;
529   return 0;
530 }
531
532 static inline int
533 restore_dirs(struct secure_dirs *dirs)
534 {
535   if (dirs->host_fd != -1) {
536     if (fchroot(dirs->host_fd) == -1)
537       return -1;
538     if (close(dirs->host_fd) == -1)
539       return -1;
540   }
541   if (dirs->guest_fd != -1) {
542     if (close(dirs->guest_fd) == -1)
543       return -1;
544   }
545   if (dirs->target_fd != -1) {
546     if (close(dirs->target_fd) == -1)
547       return -1;
548   }
549   if (dirs->cwd_fd != -1) {
550     if (fchdir(dirs->cwd_fd) == -1)
551       return -1;
552     if (close(dirs->cwd_fd) == -1)
553       return -1;
554   }
555   return 0;
556 }
557
558 static inline int
559 secure_chdir(struct secure_dirs *dirs, const char *guest, const char *target)
560 {
561   dirs->host_fd = dirs->cwd_fd = dirs->guest_fd = dirs->target_fd = -1;
562
563   dirs->host_fd = open("/", O_RDONLY|O_DIRECTORY);
564   if (dirs->host_fd == -1)
565     return -1;
566
567   dirs->cwd_fd = open(".", O_RDONLY|O_DIRECTORY);
568   if (dirs->cwd_fd == -1)
569     return -1;
570
571   dirs->guest_fd = open(guest, O_RDONLY|O_DIRECTORY);
572   if (dirs->guest_fd == -1)
573     return -1;
574   if (fchroot(dirs->guest_fd) == -1)
575     return -1;
576
577   dirs->target_fd = open(target, O_RDONLY|O_DIRECTORY);
578   if (dirs->target_fd == -1)
579     return -1;
580
581   if (fchroot(dirs->host_fd) == -1 || close(dirs->host_fd) == -1)
582     return -1;
583   dirs->host_fd = -1;
584   if (close(dirs->guest_fd) == -1)
585     return -1;
586   dirs->guest_fd = -1;
587
588   if (fchdir(dirs->target_fd) == -1 || close(dirs->target_fd) == -1)
589     return -1;
590
591   return 0;
592 }
593
594 static PyObject *
595 vserver_mount(PyObject *self, PyObject *args)
596 {
597   const char *guest, *target, *source, *type, *data = NULL;
598   unsigned long flags = 0;
599   struct secure_dirs dirs;
600
601   if (!PyArg_ParseTuple(args, "ssss|ks", &source, &guest, &target, &type,
602                         &flags, &data))
603     return NULL;
604
605   if (secure_chdir(&dirs, guest, target) == -1)
606     goto out;
607   if (mount(source, ".", type, flags, data) == -1)
608     goto out;
609   restore_dirs(&dirs);
610
611   return NONE;
612
613 out:
614   restore_dirs(&dirs);
615   return PyErr_SetFromErrno(PyExc_OSError);
616 }
617
618 static PyObject *
619 vserver_umount(PyObject *self, PyObject *args)
620 {
621   const char *guest, *target;
622   int flags = 0;
623   char *path;
624   PyObject *ret;
625
626   if (!PyArg_ParseTuple(args, "ss|i", &guest, &target, &flags))
627     return NULL;
628
629   path = calloc(strlen(guest) + strlen(target) + 2, sizeof(char));
630   sprintf(path, "%s/%s", guest, target);
631   if (umount2(path, flags) == -1)
632     ret = PyErr_SetFromErrno(PyExc_OSError);
633   else
634     ret = NONE;
635   free(path);
636
637   return ret;
638 }
639
640 static PyObject *
641 vserver_set_runlevel(PyObject *self, PyObject *args)
642 {
643   const char *file;
644   int runlevel;
645   struct utmp ut;
646
647   if (!PyArg_ParseTuple(args, "si", &file, &runlevel))
648     return NULL;
649
650   utmpname(file);
651   setutent();
652   memset(&ut, 0, sizeof(ut));
653   ut.ut_type = RUN_LVL;
654   ut.ut_pid = ('#' << 8) + runlevel + '0';
655   pututline(&ut);
656   endutent();
657
658   return NONE;
659 }
660
661 static PyMethodDef  methods[] = {
662   { "chcontext", vserver_chcontext, METH_VARARGS,
663     "chcontext to vserver with provided flags" },
664   { "setup_done", vserver_setup_done, METH_VARARGS,
665     "Release vserver setup lock" },
666   { "setsched", vserver_setsched, METH_VARARGS,
667     "Change vserver scheduling attributes for given vserver context" },
668   { "setdlimit", vserver_set_dlimit, METH_VARARGS,
669     "Set disk limits for given vserver context" },
670   { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
671     "Remove disk limits for given vserver context" },
672   { "getdlimit", vserver_get_dlimit, METH_VARARGS,
673     "Get disk limits for given vserver context" },
674   { "setrlimit", vserver_set_rlimit, METH_VARARGS,
675     "Set resource limits for given resource of a vserver context" },
676   { "getrlimit", vserver_get_rlimit, METH_VARARGS,
677     "Get resource limits for given resource of a vserver context" },
678   { "killall", vserver_killall, METH_VARARGS,
679     "Send signal to all processes in vserver context" },
680   { "isrunning", vserver_isrunning, METH_VARARGS,
681     "Check if vserver is running"},
682   { "setbcaps", vserver_set_bcaps, METH_VARARGS,
683     "Set POSIX capabilities of a vserver context" },
684   { "getbcaps", vserver_get_bcaps, METH_VARARGS,
685     "Get POSIX capabilities of a vserver context" },
686   { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
687     "Translate a string of capabilities to a bitmap" },
688   { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
689     "Translate a capability-bitmap into a string" },
690   { "netadd", vserver_net_add, METH_VARARGS,
691     "Assign an IP address to a context" },
692   { "netremove", vserver_net_remove, METH_VARARGS,
693     "Remove IP address(es) from a context" },
694   { "mount", vserver_mount, METH_VARARGS,
695     "Perform the mount() system call" },
696   { "umount", vserver_umount, METH_VARARGS,
697     "Perform the umount2() system call" },
698   { "setrunlevel", vserver_set_runlevel, METH_VARARGS,
699     "Set the runlevel in utmp" },
700   { NULL, NULL, 0, NULL }
701 };
702
703 PyMODINIT_FUNC
704 initvserverimpl(void)
705 {
706   PyObject  *mod;
707
708   mod = Py_InitModule("vserverimpl", methods);
709
710   /* export the set of 'safe' capabilities */
711   PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
712
713   /* export the default vserver directory */
714   PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
715
716   /* export limit-related constants */
717   PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
718   PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
719   PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
720
721   PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
722   PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
723   PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
724   PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
725   PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
726   PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
727   PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
728
729   PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
730   PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
731
732   PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
733   PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
734   PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
735   PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);
736
737   /* scheduler flags */
738   PyModule_AddIntConstant(mod,
739                           "VS_SCHED_CPU_GUARANTEED",
740                           VS_SCHED_CPU_GUARANTEED);
741 }