6c8e97c0cf8e5ead6ba4e744cc9c137dbe506135
[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       }
437       ret = 1;
438       break;
439     }
440   }
441   /* no match, use a default */
442   if (!ret) {
443     switch (addr->type) {
444     case vcNET_IPV4:    addr->mask[0] = htonl(0xffffff00); break;
445     case vcNET_IPV6:    addr->mask[0] = 64; break;
446     default:            addr->mask[0] = 0; break;
447     }
448   }
449   freeifaddrs(head);
450   return ret;
451 }
452
453 /* XXX These two functions are really similar */
454 static PyObject *
455 vserver_net_add(PyObject *self, PyObject *args)
456 {
457   struct vc_net_nx addr;
458   nid_t nid;
459   const char *ip;
460
461   if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
462     return NULL;
463
464   if (convert_address(ip, &addr.type, &addr.ip) == -1)
465     return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
466
467   switch (get_mask(&addr)) {
468   case -1:
469     return PyErr_SetFromErrno(PyExc_OSError);
470   case 0:
471     /* XXX error here? */
472     break;
473   }
474   addr.count = 1;
475
476   if (vc_net_add(nid, &addr) == -1 && errno != ESRCH)
477     return PyErr_SetFromErrno(PyExc_OSError);
478
479   return NONE;
480 }
481
482 static PyObject *
483 vserver_net_remove(PyObject *self, PyObject *args)
484 {
485   struct vc_net_nx addr;
486   nid_t nid;
487   const char *ip;
488
489   if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
490     return NULL;
491
492   if (strcmp(ip, "all") == 0)
493     addr.type = vcNET_ANY;
494   else if (strcmp(ip, "all4") == 0)
495     addr.type = vcNET_IPV4A;
496   else if (strcmp(ip, "all6") == 0)
497     addr.type = vcNET_IPV6A;
498   else
499     if (convert_address(ip, &addr.type, &addr.ip) == -1)
500       return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
501
502   switch (get_mask(&addr)) {
503   case -1:
504     return PyErr_SetFromErrno(PyExc_OSError);
505   }
506   addr.count = 1;
507
508   if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
509     return PyErr_SetFromErrno(PyExc_OSError);
510
511   return NONE;
512 }
513
514 static PyMethodDef  methods[] = {
515   { "chcontext", vserver_chcontext, METH_VARARGS,
516     "chcontext to vserver with provided flags" },
517   { "setup_done", vserver_setup_done, METH_VARARGS,
518     "Release vserver setup lock" },
519   { "setsched", vserver_setsched, METH_VARARGS,
520     "Change vserver scheduling attributes for given vserver context" },
521   { "setdlimit", vserver_set_dlimit, METH_VARARGS,
522     "Set disk limits for given vserver context" },
523   { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
524     "Remove disk limits for given vserver context" },
525   { "getdlimit", vserver_get_dlimit, METH_VARARGS,
526     "Get disk limits for given vserver context" },
527   { "setrlimit", vserver_set_rlimit, METH_VARARGS,
528     "Set resource limits for given resource of a vserver context" },
529   { "getrlimit", vserver_get_rlimit, METH_VARARGS,
530     "Get resource limits for given resource of a vserver context" },
531   { "killall", vserver_killall, METH_VARARGS,
532     "Send signal to all processes in vserver context" },
533   { "isrunning", vserver_isrunning, METH_VARARGS,
534     "Check if vserver is running"},
535   { "setbcaps", vserver_set_bcaps, METH_VARARGS,
536     "Set POSIX capabilities of a vserver context" },
537   { "getbcaps", vserver_get_bcaps, METH_VARARGS,
538     "Get POSIX capabilities of a vserver context" },
539   { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
540     "Translate a string of capabilities to a bitmap" },
541   { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
542     "Translate a capability-bitmap into a string" },
543   { "netadd", vserver_net_add, METH_VARARGS,
544     "Assign an IP address to a context" },
545   { "netremove", vserver_net_remove, METH_VARARGS,
546     "Remove IP address(es) from a context" },
547   { NULL, NULL, 0, NULL }
548 };
549
550 PyMODINIT_FUNC
551 initvserverimpl(void)
552 {
553   PyObject  *mod;
554
555   mod = Py_InitModule("vserverimpl", methods);
556
557   /* export the set of 'safe' capabilities */
558   PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
559
560   /* export the default vserver directory */
561   PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
562
563   /* export limit-related constants */
564   PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
565   PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
566   PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
567
568   PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
569   PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
570   PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
571   PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
572   PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
573   PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
574   PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
575
576   PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
577   PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
578
579   PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
580   PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
581   PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
582   PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);
583
584   /* scheduler flags */
585   PyModule_AddIntConstant(mod,
586                           "VS_SCHED_CPU_GUARANTEED",
587                           VS_SCHED_CPU_GUARANTEED);
588 }