Optimize the 0.0.0.0 case, and get it working on 2.3 kernels.
[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 vc_rlimit_mask mask;
149   uint32_t bitmask;
150   xid_t xid;
151   int resource;
152   PyObject *ret;
153
154   limits.min = VC_LIM_KEEP;
155   limits.soft = VC_LIM_KEEP;
156   limits.hard = VC_LIM_KEEP;
157
158   if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
159     return NULL;
160
161   errno = 0;
162
163   if (vc_get_rlimit_mask(xid, &mask)==-1) {
164     ret = PyErr_SetFromErrno(PyExc_OSError);
165   } else {
166     bitmask = (1<<resource);
167     if ((mask.min|mask.soft|mask.hard) & bitmask) 
168       if (vc_set_rlimit(xid, resource, &limits)==-1) 
169         ret = PyErr_SetFromErrno(PyExc_OSError);
170       else
171         ret = __vserver_get_rlimit(xid, resource);
172   }
173
174   return ret;
175 }
176
177 /*
178  * setsched
179  */
180 static PyObject *
181 vserver_setsched(PyObject *self, PyObject *args)
182 {
183   xid_t  ctx;
184   uint32_t  cpu_min;
185   uint32_t  cpu_share;
186
187   if (!PyArg_ParseTuple(args, "II|I", &ctx, &cpu_min, &cpu_share))
188     return NULL;
189
190   /* ESRCH indicates that there are no processes in the context */
191   if (pl_setsched(ctx, cpu_min, cpu_share) &&
192       errno != ESRCH)
193     return PyErr_SetFromErrno(PyExc_OSError);
194
195   return NONE;
196 }
197
198 static PyObject *
199 vserver_get_dlimit(PyObject *self, PyObject *args)
200 {
201   PyObject *res;
202   char* path;
203   unsigned xid;
204   struct vc_ctx_dlimit data;
205   int r;
206
207   if (!PyArg_ParseTuple(args, "si", &path,&xid))
208     return NULL;
209
210   memset(&data, 0, sizeof(data));
211   r = vc_get_dlimit(path, xid, 0, &data);
212   if (r>=0) {
213     res = Py_BuildValue("(i,i,i,i,i)",
214                         data.space_used,
215                         data.space_total,
216                         data.inodes_used,
217                         data.inodes_total,
218                         data.reserved);
219   } else {
220     res = PyErr_SetFromErrno(PyExc_OSError);
221   }
222
223   return res;
224 }
225
226
227 static PyObject *
228 vserver_set_dlimit(PyObject *self, PyObject *args)
229 {
230   char* path;
231   unsigned xid;
232   struct vc_ctx_dlimit data;
233
234   memset(&data,0,sizeof(data));
235   if (!PyArg_ParseTuple(args, "siiiiii", &path,
236                         &xid,
237                         &data.space_used,
238                         &data.space_total,
239                         &data.inodes_used,
240                         &data.inodes_total,
241                         &data.reserved))
242     return NULL;
243
244   if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
245       vc_set_dlimit(path, xid, 0, &data))
246     return PyErr_SetFromErrno(PyExc_OSError);
247
248   return NONE;  
249 }
250
251 static PyObject *
252 vserver_unset_dlimit(PyObject *self, PyObject *args)
253 {
254   char  *path;
255   unsigned  xid;
256
257   if (!PyArg_ParseTuple(args, "si", &path, &xid))
258     return NULL;
259
260   if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
261     return PyErr_SetFromErrno(PyExc_OSError);
262
263   return NONE;  
264 }
265
266 static PyObject *
267 vserver_killall(PyObject *self, PyObject *args)
268 {
269   xid_t ctx;
270   int   sig;
271   struct vc_ctx_flags cflags = {
272     .flagword = 0,
273     .mask = VC_VXF_PERSISTENT
274   };
275   struct vc_net_flags nflags = {
276     .flagword = 0,
277     .mask = VC_NXF_PERSISTENT
278   };
279
280   if (!PyArg_ParseTuple(args, "Ii", &ctx, &sig))
281     return NULL;
282
283   if (vc_ctx_kill(ctx, 0, sig) && errno != ESRCH)
284     return PyErr_SetFromErrno(PyExc_OSError);
285
286   if (vc_set_cflags(ctx, &cflags) && errno != ESRCH)
287     return PyErr_SetFromErrno(PyExc_OSError);
288
289   if (vc_set_nflags(ctx, &nflags) && errno != ESRCH)
290     return PyErr_SetFromErrno(PyExc_OSError);
291
292   return NONE;
293 }
294
295 static PyObject *
296 vserver_set_bcaps(PyObject *self, PyObject *args)
297 {
298   xid_t ctx;
299   struct vc_ctx_caps caps;
300
301   if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
302     return NULL;
303
304   caps.bmask = vc_get_insecurebcaps();
305   caps.cmask = caps.ccaps = 0;
306   if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
307     return PyErr_SetFromErrno(PyExc_OSError);
308
309   return NONE;
310 }
311
312 static PyObject *
313 vserver_text2bcaps(PyObject *self, PyObject *args)
314 {
315   struct vc_ctx_caps caps = { .bcaps = 0 };
316   const char *list;
317   int len;
318   struct vc_err_listparser err;
319
320   if (!PyArg_ParseTuple(args, "s#", &list, &len))
321     return NULL;
322
323   vc_list2bcap(list, len, &err, &caps);
324
325   return Py_BuildValue("K", caps.bcaps);
326 }
327
328 static PyObject *
329 vserver_get_bcaps(PyObject *self, PyObject *args)
330 {
331   xid_t ctx;
332   struct vc_ctx_caps caps;
333
334   if (!PyArg_ParseTuple(args, "I", &ctx))
335     return NULL;
336
337   if (vc_get_ccaps(ctx, &caps) == -1) {
338     if (errno != -ESRCH)
339       return PyErr_SetFromErrno(PyExc_OSError);
340     else
341       caps.bcaps = 0;
342   }
343
344   return Py_BuildValue("K", caps.bcaps & vc_get_insecurebcaps());
345 }
346
347 static PyObject *
348 vserver_bcaps2text(PyObject *self, PyObject *args)
349 {
350   struct vc_ctx_caps caps = { .bcaps = 0 };
351   PyObject *list;
352   const char *cap;
353
354   if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
355     return NULL;
356
357   list = PyString_FromString("");
358
359   while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
360     if (list == NULL)
361       break;
362     PyString_ConcatAndDel(&list, PyString_FromFormat(
363                           (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
364                           cap));
365   }
366
367   return list;
368 }
369
370 static inline int
371 convert_address(const char *str, struct vc_net_addr *addr)
372 {
373   void *dst;
374   if (inet_pton(AF_INET6, str, addr->vna_v6_ip.s6_addr) > 0) {
375     addr->vna_type = VC_NXA_TYPE_IPV6;
376     return 0;
377   }
378   else if (inet_pton(AF_INET, str, &addr->vna_v4_ip.s_addr) > 0) {
379     addr->vna_type = VC_NXA_TYPE_IPV4;
380     return 0;
381   }
382   return -1;
383 }
384
385 static int
386 mask_to_prefix(void *data, int limit)
387 {
388   uint8_t *mask = data;
389   int prefix;
390   for (prefix = 0; prefix < limit && mask[prefix >> 3] & (1 << (prefix & 0x07)); prefix++)
391     ;
392   return prefix;
393 }
394
395 static int
396 get_mask(struct vc_net_addr *addr)
397 {
398   struct ifaddrs *head, *ifa;
399   int ret = 0;
400   int family, offset, len;
401   void *ip;
402
403   switch (addr->vna_type) {
404   case VC_NXA_TYPE_IPV4:
405     family = AF_INET;
406     offset = offsetof(struct sockaddr_in,  sin_addr.s_addr);
407     ip = &addr->vna_v4_ip.s_addr;
408     len = 4;
409     addr->vna_v4_mask.s_addr = htonl(0xffffff00);
410     addr->vna_prefix = 24;
411     break;
412   case VC_NXA_TYPE_IPV6:
413     family = AF_INET6;
414     offset = offsetof(struct sockaddr_in6, sin6_addr.s6_addr);
415     ip = addr->vna_v6_ip.s6_addr;
416     len = 16;
417     addr->vna_v6_mask.s6_addr32[9] = addr->vna_v6_mask.s6_addr32[1] = 0xffffffff;
418     addr->vna_v6_mask.s6_addr32[2] = addr->vna_v6_mask.s6_addr32[3] = 0x00000000;
419     addr->vna_prefix = 64;
420     break;
421   default:
422     errno = EINVAL;
423     return -1;
424   }
425
426   if (getifaddrs(&head) == -1)
427     return -1;
428   for (ifa = head; ifa; ifa = ifa->ifa_next) {
429     if (ifa->ifa_addr->sa_family == family &&
430         memcmp((char *) ifa->ifa_addr + offset, ip, len) == 0) {
431       switch (addr->vna_type) {
432       case VC_NXA_TYPE_IPV4:
433         memcpy(&addr->vna_v4_mask.s_addr, ifa->ifa_netmask + offset, len);
434         addr->vna_prefix = mask_to_prefix(&addr->vna_v4_mask.s_addr, 32);
435         break;
436       case VC_NXA_TYPE_IPV6:
437         memcpy(addr->vna_v6_mask.s6_addr, ifa->ifa_netmask + offset, len);
438         addr->vna_prefix = mask_to_prefix(addr->vna_v6_mask.s6_addr, 128);
439         break;
440       }
441       ret = 1;
442       break;
443     }
444   }
445   freeifaddrs(head);
446   return ret;
447 }
448
449 /* XXX These two functions are really similar */
450 static PyObject *
451 vserver_net_add(PyObject *self, PyObject *args)
452 {
453   struct vc_net_addr addr = { .vna_type = 0 };
454   nid_t nid;
455   const char *ip;
456
457   if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
458     return NULL;
459
460   /* Optimize standard case, which also needs to be handled differently */
461   if (strcmp(ip, "0.0.0.0") == 0) {
462     addr.vna_type = VC_NXA_TYPE_MASK | VC_NXA_TYPE_IPV4;
463     addr.vna_flags = 0;
464     addr.vna_prefix = 0;
465     addr.vna_parent = 0;
466     addr.vna_v4_mask.s_addr = 0;
467     addr.vna_v4_ip.s_addr = 0;
468   }
469   else {
470     if (convert_address(ip, &addr) == -1)
471       return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
472
473     switch (get_mask(&addr)) {
474     case -1:
475       return PyErr_SetFromErrno(PyExc_OSError);
476     case 0:
477       /* XXX error here? */
478       break;
479     }
480     addr.vna_type |= VC_NXA_TYPE_ADDR;
481   }
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 }