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