Separate the PlanetLab modifications from util-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_share;
196   uint32_t  cpu_sched_flags = VC_VXF_SCHED_FLAGS;
197
198   if (!PyArg_ParseTuple(args, "II|I", &ctx, &cpu_share, &cpu_sched_flags))
199     return NULL;
200
201   /* ESRCH indicates that there are no processes in the context */
202   if (pl_setsched(ctx, cpu_share, cpu_sched_flags) &&
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_IPV6 | 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     addr.vna_type |= VC_NXA_TYPE_ADDR;
509   }
510
511   switch (get_mask(&addr)) {
512   case -1:
513     return PyErr_SetFromErrno(PyExc_OSError);
514   }
515
516   if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
517     return PyErr_SetFromErrno(PyExc_OSError);
518
519   return NONE;
520 }
521
522 struct secure_dirs {
523   int host_fd;
524   int cwd_fd;
525   int guest_fd;
526   int target_fd;
527 };
528
529 static inline int
530 fchroot(int fd)
531 {
532   if (fchdir(fd) == -1 || chroot(".") == -1)
533     return -1;
534   return 0;
535 }
536
537 static inline int
538 restore_dirs(struct secure_dirs *dirs)
539 {
540   if (dirs->host_fd != -1) {
541     if (fchroot(dirs->host_fd) == -1)
542       return -1;
543     if (close(dirs->host_fd) == -1)
544       return -1;
545   }
546   if (dirs->guest_fd != -1) {
547     if (close(dirs->guest_fd) == -1)
548       return -1;
549   }
550   if (dirs->target_fd != -1) {
551     if (close(dirs->target_fd) == -1)
552       return -1;
553   }
554   if (dirs->cwd_fd != -1) {
555     if (fchdir(dirs->cwd_fd) == -1)
556       return -1;
557     if (close(dirs->cwd_fd) == -1)
558       return -1;
559   }
560   return 0;
561 }
562
563 static inline int
564 secure_chdir(struct secure_dirs *dirs, const char *guest, const char *target)
565 {
566   dirs->host_fd = dirs->cwd_fd = dirs->guest_fd = dirs->target_fd = -1;
567
568   dirs->host_fd = open("/", O_RDONLY|O_DIRECTORY);
569   if (dirs->host_fd == -1)
570     return -1;
571
572   dirs->cwd_fd = open(".", O_RDONLY|O_DIRECTORY);
573   if (dirs->cwd_fd == -1)
574     return -1;
575
576   dirs->guest_fd = open(guest, O_RDONLY|O_DIRECTORY);
577   if (dirs->guest_fd == -1)
578     return -1;
579   if (fchroot(dirs->guest_fd) == -1)
580     return -1;
581
582   dirs->target_fd = open(target, O_RDONLY|O_DIRECTORY);
583   if (dirs->target_fd == -1)
584     return -1;
585
586   if (fchroot(dirs->host_fd) == -1 || close(dirs->host_fd) == -1)
587     return -1;
588   dirs->host_fd = -1;
589   if (close(dirs->guest_fd) == -1)
590     return -1;
591   dirs->guest_fd = -1;
592
593   if (fchdir(dirs->target_fd) == -1 || close(dirs->target_fd) == -1)
594     return -1;
595
596   return 0;
597 }
598
599 static PyObject *
600 vserver_mount(PyObject *self, PyObject *args)
601 {
602   const char *guest, *target, *source, *type, *data = NULL;
603   unsigned long flags = 0;
604   struct secure_dirs dirs;
605
606   if (!PyArg_ParseTuple(args, "ssss|ks", &source, &guest, &target, &type,
607                         &flags, &data))
608     return NULL;
609
610   if (secure_chdir(&dirs, guest, target) == -1)
611     goto out;
612   if (mount(source, ".", type, flags, data) == -1)
613     goto out;
614   restore_dirs(&dirs);
615
616   return NONE;
617
618 out:
619   restore_dirs(&dirs);
620   return PyErr_SetFromErrno(PyExc_OSError);
621 }
622
623 static PyObject *
624 vserver_umount(PyObject *self, PyObject *args)
625 {
626   const char *guest, *target;
627   int flags = 0;
628   char *path;
629   PyObject *ret;
630
631   if (!PyArg_ParseTuple(args, "ss|i", &guest, &target, &flags))
632     return NULL;
633
634   path = calloc(strlen(guest) + strlen(target) + 2, sizeof(char));
635   sprintf(path, "%s/%s", guest, target);
636   if (umount2(path, flags) == -1)
637     ret = PyErr_SetFromErrno(PyExc_OSError);
638   else
639     ret = NONE;
640   free(path);
641
642   return ret;
643 }
644
645 static PyObject *
646 vserver_set_runlevel(PyObject *self, PyObject *args)
647 {
648   const char *file;
649   int runlevel;
650   struct utmp ut;
651
652   if (!PyArg_ParseTuple(args, "si", &file, &runlevel))
653     return NULL;
654
655   utmpname(file);
656   setutent();
657   memset(&ut, 0, sizeof(ut));
658   ut.ut_type = RUN_LVL;
659   ut.ut_pid = ('#' << 8) + runlevel + '0';
660   pututline(&ut);
661   endutent();
662
663   return NONE;
664 }
665
666 static PyMethodDef  methods[] = {
667   { "chcontext", vserver_chcontext, METH_VARARGS,
668     "chcontext to vserver with provided flags" },
669   { "setup_done", vserver_setup_done, METH_VARARGS,
670     "Release vserver setup lock" },
671   { "setsched", vserver_setsched, METH_VARARGS,
672     "Change vserver scheduling attributes for given vserver context" },
673   { "setdlimit", vserver_set_dlimit, METH_VARARGS,
674     "Set disk limits for given vserver context" },
675   { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
676     "Remove disk limits for given vserver context" },
677   { "getdlimit", vserver_get_dlimit, METH_VARARGS,
678     "Get disk limits for given vserver context" },
679   { "setrlimit", vserver_set_rlimit, METH_VARARGS,
680     "Set resource limits for given resource of a vserver context" },
681   { "getrlimit", vserver_get_rlimit, METH_VARARGS,
682     "Get resource limits for given resource of a vserver context" },
683   { "killall", vserver_killall, METH_VARARGS,
684     "Send signal to all processes in vserver context" },
685   { "isrunning", vserver_isrunning, METH_VARARGS,
686     "Check if vserver is running"},
687   { "setbcaps", vserver_set_bcaps, METH_VARARGS,
688     "Set POSIX capabilities of a vserver context" },
689   { "getbcaps", vserver_get_bcaps, METH_VARARGS,
690     "Get POSIX capabilities of a vserver context" },
691   { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
692     "Translate a string of capabilities to a bitmap" },
693   { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
694     "Translate a capability-bitmap into a string" },
695   { "netadd", vserver_net_add, METH_VARARGS,
696     "Assign an IP address to a context" },
697   { "netremove", vserver_net_remove, METH_VARARGS,
698     "Remove IP address(es) from a context" },
699   { "mount", vserver_mount, METH_VARARGS,
700     "Perform the mount() system call" },
701   { "umount", vserver_umount, METH_VARARGS,
702     "Perform the umount2() system call" },
703   { "setrunlevel", vserver_set_runlevel, METH_VARARGS,
704     "Set the runlevel in utmp" },
705   { NULL, NULL, 0, NULL }
706 };
707
708 PyMODINIT_FUNC
709 initvserverimpl(void)
710 {
711   PyObject  *mod;
712
713   mod = Py_InitModule("vserverimpl", methods);
714
715   /* export the set of 'safe' capabilities */
716   PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
717
718   /* export the default vserver directory */
719   PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
720
721   /* export limit-related constants */
722   PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
723   PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
724   PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
725
726   PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
727   PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
728   PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
729   PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
730   PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
731   PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
732   PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
733
734   PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
735   PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
736
737   PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
738   PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
739   PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
740   PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);
741
742   /* scheduler flags */
743   PyModule_AddIntConstant(mod,
744                           "VS_SCHED_CPU_GUARANTEED",
745                           VS_SCHED_CPU_GUARANTEED);
746 }