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