Typo
[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
47 #include "config.h"
48 #include "pathconfig.h"
49 #include "virtual.h"
50 #include "vserver.h"
51 #include "planetlab.h"
52 #include "vserver-internal.h"
53
54 #define NONE  ({ Py_INCREF(Py_None); Py_None; })
55
56 /*
57  * context create
58  */
59 static PyObject *
60 vserver_chcontext(PyObject *self, PyObject *args)
61 {
62   int  ctx_is_new;
63   xid_t  ctx;
64   uint_least64_t bcaps = 0;
65
66   if (!PyArg_ParseTuple(args, "I|K", &ctx, &bcaps))
67     return NULL;
68   bcaps |= ~vc_get_insecurebcaps();
69
70   if ((ctx_is_new = pl_chcontext(ctx, bcaps, 0)) < 0)
71     return PyErr_SetFromErrno(PyExc_OSError);
72
73   return PyBool_FromLong(ctx_is_new);
74 }
75
76 static PyObject *
77 vserver_setup_done(PyObject *self, PyObject *args)
78 {
79   xid_t  ctx;
80
81   if (!PyArg_ParseTuple(args, "I", &ctx))
82     return NULL;
83
84   if (pl_setup_done(ctx) < 0)
85     return PyErr_SetFromErrno(PyExc_OSError);
86
87   return NONE;
88 }
89
90 static PyObject *
91 vserver_isrunning(PyObject *self, PyObject *args)
92 {
93   xid_t  ctx;
94   PyObject *ret;
95   struct stat statbuf;
96   char fname[64];
97
98   if (!PyArg_ParseTuple(args, "I", &ctx))
99     return NULL;
100
101   sprintf(fname,"/proc/virtual/%d", ctx);
102
103   if(stat(&fname[0],&statbuf)==0)
104     ret = PyBool_FromLong(1);
105   else
106     ret = PyBool_FromLong(0);
107
108   return ret;
109 }
110
111 static PyObject *
112 __vserver_get_rlimit(xid_t xid, int resource) {
113   struct vc_rlimit limits;
114   PyObject *ret;
115
116   errno = 0;
117   if (vc_get_rlimit(xid, resource, &limits)==-1)
118     ret = PyErr_SetFromErrno(PyExc_OSError);
119   else
120     ret = Py_BuildValue("LLL",limits.hard, limits.soft, limits.min);
121
122   return ret;
123 }
124
125 static PyObject *
126 vserver_get_rlimit(PyObject *self, PyObject *args) {
127   xid_t xid;
128   int resource;
129   PyObject *ret;
130
131   if (!PyArg_ParseTuple(args, "Ii", &xid, &resource))
132     ret = NULL;
133   else
134     ret = __vserver_get_rlimit(xid, resource);
135
136   return ret;
137 }
138
139 static PyObject *
140 vserver_set_rlimit(PyObject *self, PyObject *args) {
141   struct vc_rlimit limits;
142   struct rlimit lim;
143   xid_t xid;
144   int resource, lresource;
145   PyObject *ret;
146
147   limits.min = VC_LIM_KEEP;
148   limits.soft = VC_LIM_KEEP;
149   limits.hard = VC_LIM_KEEP;
150
151   if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
152     return NULL;
153
154   lresource = resource;
155   switch (resource) {
156   case VC_VLIMIT_NSOCK:
157   case VC_VLIMIT_ANON:
158   case VC_VLIMIT_SHMEM:
159     goto do_vc_set_rlimit;
160   case VC_VLIMIT_OPENFD:
161     lresource = RLIMIT_NOFILE;
162     break;
163   default:
164     break;
165   }
166
167   getrlimit(lresource,&lim);
168   if (adjust_lim(&limits,&lim)) {
169     setrlimit(lresource, &lim);
170   }
171
172  do_vc_set_rlimit:
173   errno = 0;
174   if (vc_set_rlimit(xid, resource, &limits)==-1) 
175     ret = PyErr_SetFromErrno(PyExc_OSError);
176   else
177     ret = __vserver_get_rlimit(xid, resource);
178
179   return ret;
180 }
181
182 /*
183  * setsched
184  */
185 static PyObject *
186 vserver_setsched(PyObject *self, PyObject *args)
187 {
188   xid_t  ctx;
189   uint32_t  cpu_share;
190   uint32_t  cpu_sched_flags = VC_VXF_SCHED_FLAGS;
191
192   if (!PyArg_ParseTuple(args, "II|I", &ctx, &cpu_share, &cpu_sched_flags))
193     return NULL;
194
195   /* ESRCH indicates that there are no processes in the context */
196   if (pl_setsched(ctx, cpu_share, cpu_sched_flags) &&
197       errno != ESRCH)
198     return PyErr_SetFromErrno(PyExc_OSError);
199
200   return NONE;
201 }
202
203 static PyObject *
204 vserver_get_dlimit(PyObject *self, PyObject *args)
205 {
206   PyObject *res;
207   char* path;
208   unsigned xid;
209   struct vc_ctx_dlimit data;
210   int r;
211
212   if (!PyArg_ParseTuple(args, "si", &path,&xid))
213     return NULL;
214
215   memset(&data, 0, sizeof(data));
216   r = vc_get_dlimit(path, xid, 0, &data);
217   if (r>=0) {
218     res = Py_BuildValue("(i,i,i,i,i)",
219                         data.space_used,
220                         data.space_total,
221                         data.inodes_used,
222                         data.inodes_total,
223                         data.reserved);
224   } else {
225     res = PyErr_SetFromErrno(PyExc_OSError);
226   }
227
228   return res;
229 }
230
231
232 static PyObject *
233 vserver_set_dlimit(PyObject *self, PyObject *args)
234 {
235   char* path;
236   unsigned xid;
237   struct vc_ctx_dlimit data;
238
239   memset(&data,0,sizeof(data));
240   if (!PyArg_ParseTuple(args, "siiiiii", &path,
241                         &xid,
242                         &data.space_used,
243                         &data.space_total,
244                         &data.inodes_used,
245                         &data.inodes_total,
246                         &data.reserved))
247     return NULL;
248
249   if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
250       vc_set_dlimit(path, xid, 0, &data))
251     return PyErr_SetFromErrno(PyExc_OSError);
252
253   return NONE;  
254 }
255
256 static PyObject *
257 vserver_unset_dlimit(PyObject *self, PyObject *args)
258 {
259   char  *path;
260   unsigned  xid;
261
262   if (!PyArg_ParseTuple(args, "si", &path, &xid))
263     return NULL;
264
265   if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
266     return PyErr_SetFromErrno(PyExc_OSError);
267
268   return NONE;  
269 }
270
271 static PyObject *
272 vserver_killall(PyObject *self, PyObject *args)
273 {
274   xid_t ctx;
275   int   sig;
276   struct vc_ctx_flags cflags = {
277     .flagword = 0,
278     .mask = VC_VXF_PERSISTENT
279   };
280   struct vc_net_flags nflags = {
281     .flagword = 0,
282     .mask = VC_NXF_PERSISTENT
283   };
284
285   if (!PyArg_ParseTuple(args, "Ii", &ctx, &sig))
286     return NULL;
287
288   if (vc_ctx_kill(ctx, 0, sig) && errno != ESRCH)
289     return PyErr_SetFromErrno(PyExc_OSError);
290
291   if (vc_set_cflags(ctx, &cflags) && errno != ESRCH)
292     return PyErr_SetFromErrno(PyExc_OSError);
293
294   if (vc_set_nflags(ctx, &nflags) && errno != ESRCH)
295     return PyErr_SetFromErrno(PyExc_OSError);
296
297   return NONE;
298 }
299
300 static PyObject *
301 vserver_set_bcaps(PyObject *self, PyObject *args)
302 {
303   xid_t ctx;
304   struct vc_ctx_caps caps;
305
306   if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
307     return NULL;
308
309   caps.bmask = vc_get_insecurebcaps();
310   caps.cmask = caps.ccaps = 0;
311   if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
312     return PyErr_SetFromErrno(PyExc_OSError);
313
314   return NONE;
315 }
316
317 static PyObject *
318 vserver_text2bcaps(PyObject *self, PyObject *args)
319 {
320   struct vc_ctx_caps caps = { .bcaps = 0 };
321   const char *list;
322   int len;
323   struct vc_err_listparser err;
324
325   if (!PyArg_ParseTuple(args, "s#", &list, &len))
326     return NULL;
327
328   vc_list2bcap(list, len, &err, &caps);
329
330   return Py_BuildValue("K", caps.bcaps);
331 }
332
333 static PyObject *
334 vserver_get_bcaps(PyObject *self, PyObject *args)
335 {
336   xid_t ctx;
337   struct vc_ctx_caps caps;
338
339   if (!PyArg_ParseTuple(args, "I", &ctx))
340     return NULL;
341
342   if (vc_get_ccaps(ctx, &caps) == -1) {
343     if (errno != -ESRCH)
344       return PyErr_SetFromErrno(PyExc_OSError);
345     else
346       caps.bcaps = 0;
347   }
348
349   return Py_BuildValue("K", caps.bcaps & vc_get_insecurebcaps());
350 }
351
352 static PyObject *
353 vserver_bcaps2text(PyObject *self, PyObject *args)
354 {
355   struct vc_ctx_caps caps = { .bcaps = 0 };
356   PyObject *list;
357   const char *cap;
358
359   if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
360     return NULL;
361
362   list = PyString_FromString("");
363
364   while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
365     if (list == NULL)
366       break;
367     PyString_ConcatAndDel(&list, PyString_FromFormat(
368                           (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
369                           cap));
370   }
371
372   return list;
373 }
374
375 static const struct AF_to_vcNET {
376   int af;
377   vc_net_nx_type vc_net;
378   size_t len;
379   size_t offset;
380 } converter[] = {
381   { AF_INET,  vcNET_IPV4, sizeof(struct in_addr),  offsetof(struct sockaddr_in,  sin_addr.s_addr) },
382   { AF_INET6, vcNET_IPV6, sizeof(struct in6_addr), offsetof(struct sockaddr_in6, sin6_addr.s6_addr) },
383   { 0, 0 }
384 };
385
386 static inline int
387 convert_address(const char *str, vc_net_nx_type *type, void *dst)
388 {
389   const struct AF_to_vcNET *i;
390   for (i = converter; i->af; i++) {
391     if (inet_pton(i->af, str, dst)) {
392       *type = i->vc_net;
393       return 0;
394     }
395   }
396   return -1;
397 }
398
399 static int
400 get_mask(struct vc_net_nx *addr)
401 {
402   const struct AF_to_vcNET *i;
403   struct ifaddrs *head, *ifa;
404   int ret = 0;
405
406   for (i = converter; i->af; i++) {
407     if (i->vc_net == addr->type)
408       break;
409   }
410   if (!i) {
411     errno = EINVAL;
412     return -1;
413   }
414
415   if (getifaddrs(&head) == -1)
416     return -1;
417   for (ifa = head; ifa; ifa = ifa->ifa_next) {
418     if (ifa->ifa_addr->sa_family == i->af &&
419         memcmp((char *) ifa->ifa_addr + i->offset, addr->ip, i->len) == 0) {
420       switch (addr->type) {
421       case vcNET_IPV4:
422         memcpy(&addr->mask[0], ifa->ifa_netmask + i->offset, i->len);
423         break;
424       case vcNET_IPV6: {
425         uint32_t *m = ((struct sockaddr_in6 *) ifa->ifa_netmask)->sin6_addr.s6_addr32;
426         /* optimization for the common case */
427         if ((m[1] & 1) == 1 && (m[2] & 0x80000000) == 0)
428           addr->mask[0] = 64;
429         else {
430           addr->mask[0] = 0;
431           while (m[addr->mask[0] / 32] & (addr->mask[0] % 32))
432             addr->mask[0]++;
433         }
434         break;
435       }
436       ret = 1;
437       break;
438     }
439   }
440   /* no match, use a default */
441   if (!ret) {
442     switch (addr->type) {
443     case vcNET_IPV4:    addr->mask[0] = htonl(0xffffff00); break;
444     case vcNET_IPV6:    addr->mask[0] = 64; break;
445     default:            addr->mask[0] = 0; break;
446     }
447   }
448   freeifaddrs(head);
449   return ret;
450 }
451
452 /* XXX These two functions are really similar */
453 static PyObject *
454 vserver_net_add(PyObject *self, PyObject *args)
455 {
456   struct vc_net_nx addr;
457   nid_t nid;
458   const char *ip;
459
460   if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
461     return NULL;
462
463   if (convert_address(ip, &addr.type, &addr.ip) == -1)
464     return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
465
466   switch (get_mask(&addr)) {
467   case -1:
468     return PyErr_SetFromErrno(PyExc_OSError);
469   case 0:
470     /* XXX error here? */
471     break;
472   }
473   addr.count = 1;
474
475   if (vc_net_add(nid, &addr) == -1 && errno != ESRCH)
476     return PyErr_SetFromErrno(PyExc_OSError);
477
478   return NONE;
479 }
480
481 static PyObject *
482 vserver_net_remove(PyObject *self, PyObject *args)
483 {
484   struct vc_net_nx addr;
485   nid_t nid;
486   const char *ip;
487
488   if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
489     return NULL;
490
491   if (strcmp(ip, "all") == 0)
492     addr.type = vcNET_ANY;
493   else if (strcmp(ip, "all4") == 0)
494     addr.type = vcNET_IPV4A;
495   else if (strcmp(ip, "all6") == 0)
496     addr.type = vcNET_IPV6A;
497   else
498     if (convert_address(ip, &addr.type, &addr.ip) == -1)
499       return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
500
501   switch (get_mask(&addr)) {
502   case -1:
503     return PyErr_SetFromErrno(PyExc_OSError);
504   }
505   addr.count = 1;
506
507   if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
508     return PyErr_SetFromErrno(PyExc_OSError);
509
510   return NONE;
511 }
512
513 static PyMethodDef  methods[] = {
514   { "chcontext", vserver_chcontext, METH_VARARGS,
515     "chcontext to vserver with provided flags" },
516   { "setup_done", vserver_setup_done, METH_VARARGS,
517     "Release vserver setup lock" },
518   { "setsched", vserver_setsched, METH_VARARGS,
519     "Change vserver scheduling attributes for given vserver context" },
520   { "setdlimit", vserver_set_dlimit, METH_VARARGS,
521     "Set disk limits for given vserver context" },
522   { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
523     "Remove disk limits for given vserver context" },
524   { "getdlimit", vserver_get_dlimit, METH_VARARGS,
525     "Get disk limits for given vserver context" },
526   { "setrlimit", vserver_set_rlimit, METH_VARARGS,
527     "Set resource limits for given resource of a vserver context" },
528   { "getrlimit", vserver_get_rlimit, METH_VARARGS,
529     "Get resource limits for given resource of a vserver context" },
530   { "killall", vserver_killall, METH_VARARGS,
531     "Send signal to all processes in vserver context" },
532   { "isrunning", vserver_isrunning, METH_VARARGS,
533     "Check if vserver is running"},
534   { "setbcaps", vserver_set_bcaps, METH_VARARGS,
535     "Set POSIX capabilities of a vserver context" },
536   { "getbcaps", vserver_get_bcaps, METH_VARARGS,
537     "Get POSIX capabilities of a vserver context" },
538   { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
539     "Translate a string of capabilities to a bitmap" },
540   { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
541     "Translate a capability-bitmap into a string" },
542   { "netadd", vserver_net_add, METH_VARARGS,
543     "Assign an IP address to a context" },
544   { "netremove", vserver_net_remove, METH_VARARGS,
545     "Remove IP address(es) from a context" },
546   { NULL, NULL, 0, NULL }
547 };
548
549 PyMODINIT_FUNC
550 initvserverimpl(void)
551 {
552   PyObject  *mod;
553
554   mod = Py_InitModule("vserverimpl", methods);
555
556   /* export the set of 'safe' capabilities */
557   PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
558
559   /* export the default vserver directory */
560   PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
561
562   /* export limit-related constants */
563   PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
564   PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
565   PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
566
567   PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
568   PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
569   PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
570   PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
571   PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
572   PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
573   PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
574
575   PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
576   PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
577
578   PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
579   PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
580   PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
581   PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);
582
583   /* scheduler flags */
584   PyModule_AddIntConstant(mod,
585                           "VS_SCHED_CPU_GUARANTEED",
586                           VS_SCHED_CPU_GUARANTEED);
587 }