1 /* Copyright 2005 Princeton University
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
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.
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.
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.
38 #include <sys/resource.h>
39 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <arpa/inet.h>
47 #include <sys/mount.h>
51 #include "pathconfig.h"
54 #include "planetlab.h"
55 #include "vserver-internal.h"
57 static inline PyObject *inc_and_ret_none(void)
63 #define NONE inc_and_ret_none()
69 vserver_chcontext(PyObject *self, PyObject *args)
73 uint_least64_t bcaps = 0;
75 if (!PyArg_ParseTuple(args, "I|K", &ctx, &bcaps))
77 bcaps |= ~vc_get_insecurebcaps();
79 if ((ctx_is_new = pl_chcontext(ctx, bcaps, 0)) < 0)
80 return PyErr_SetFromErrno(PyExc_OSError);
82 return PyBool_FromLong(ctx_is_new);
86 vserver_setup_done(PyObject *self, PyObject *args)
90 if (!PyArg_ParseTuple(args, "I", &ctx))
93 if (pl_setup_done(ctx) < 0)
94 return PyErr_SetFromErrno(PyExc_OSError);
100 vserver_isrunning(PyObject *self, PyObject *args)
107 if (!PyArg_ParseTuple(args, "I", &ctx))
110 sprintf(fname,"/proc/virtual/%d", ctx);
112 if(stat(&fname[0],&statbuf)==0)
113 ret = PyBool_FromLong(1);
115 ret = PyBool_FromLong(0);
121 __vserver_get_rlimit(xid_t xid, int resource) {
122 struct vc_rlimit limits;
126 if (vc_get_rlimit(xid, resource, &limits)==-1)
127 ret = PyErr_SetFromErrno(PyExc_OSError);
129 ret = Py_BuildValue("LLL",limits.hard, limits.soft, limits.min);
135 vserver_get_rlimit(PyObject *self, PyObject *args) {
140 if (!PyArg_ParseTuple(args, "Ii", &xid, &resource))
143 ret = __vserver_get_rlimit(xid, resource);
149 vserver_set_rlimit(PyObject *self, PyObject *args) {
150 struct vc_rlimit limits;
153 int resource, lresource;
156 limits.min = VC_LIM_KEEP;
157 limits.soft = VC_LIM_KEEP;
158 limits.hard = VC_LIM_KEEP;
160 if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
163 lresource = resource;
165 case VC_VLIMIT_NSOCK:
167 case VC_VLIMIT_SHMEM:
168 goto do_vc_set_rlimit;
169 case VC_VLIMIT_OPENFD:
170 lresource = RLIMIT_NOFILE;
176 getrlimit(lresource,&lim);
177 if (adjust_lim(&limits,&lim)) {
178 setrlimit(lresource, &lim);
183 if (vc_set_rlimit(xid, resource, &limits)==-1)
184 ret = PyErr_SetFromErrno(PyExc_OSError);
186 ret = __vserver_get_rlimit(xid, resource);
195 vserver_setsched(PyObject *self, PyObject *args)
199 uint32_t cpu_sched_flags = VC_VXF_SCHED_FLAGS;
201 if (!PyArg_ParseTuple(args, "II|I", &ctx, &cpu_share, &cpu_sched_flags))
204 /* ESRCH indicates that there are no processes in the context */
205 if (pl_setsched(ctx, cpu_share, cpu_sched_flags) &&
207 return PyErr_SetFromErrno(PyExc_OSError);
213 vserver_get_dlimit(PyObject *self, PyObject *args)
218 struct vc_ctx_dlimit data;
221 if (!PyArg_ParseTuple(args, "si", &path,&xid))
224 memset(&data, 0, sizeof(data));
225 r = vc_get_dlimit(path, xid, 0, &data);
227 res = Py_BuildValue("(i,i,i,i,i)",
234 res = PyErr_SetFromErrno(PyExc_OSError);
242 vserver_set_dlimit(PyObject *self, PyObject *args)
246 struct vc_ctx_dlimit data;
248 memset(&data,0,sizeof(data));
249 if (!PyArg_ParseTuple(args, "siiiiii", &path,
258 if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
259 vc_set_dlimit(path, xid, 0, &data))
260 return PyErr_SetFromErrno(PyExc_OSError);
266 vserver_unset_dlimit(PyObject *self, PyObject *args)
271 if (!PyArg_ParseTuple(args, "si", &path, &xid))
274 if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
275 return PyErr_SetFromErrno(PyExc_OSError);
281 vserver_killall(PyObject *self, PyObject *args)
285 struct vc_ctx_flags cflags = {
287 .mask = VC_VXF_PERSISTENT
289 struct vc_net_flags nflags = {
291 .mask = VC_NXF_PERSISTENT
294 if (!PyArg_ParseTuple(args, "Ii", &ctx, &sig))
297 if (vc_ctx_kill(ctx, 0, sig) && errno != ESRCH)
298 return PyErr_SetFromErrno(PyExc_OSError);
300 if (vc_set_cflags(ctx, &cflags) && errno != ESRCH)
301 return PyErr_SetFromErrno(PyExc_OSError);
303 if (vc_set_nflags(ctx, &nflags) && errno != ESRCH)
304 return PyErr_SetFromErrno(PyExc_OSError);
310 vserver_set_bcaps(PyObject *self, PyObject *args)
313 struct vc_ctx_caps caps;
315 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
318 caps.bmask = vc_get_insecurebcaps();
319 caps.cmask = caps.ccaps = 0;
320 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
321 return PyErr_SetFromErrno(PyExc_OSError);
327 vserver_text2bcaps(PyObject *self, PyObject *args)
329 struct vc_ctx_caps caps = { .bcaps = 0 };
332 struct vc_err_listparser err;
334 if (!PyArg_ParseTuple(args, "s#", &list, &len))
337 vc_list2bcap(list, len, &err, &caps);
339 return Py_BuildValue("K", caps.bcaps);
343 vserver_get_bcaps(PyObject *self, PyObject *args)
346 struct vc_ctx_caps caps;
348 if (!PyArg_ParseTuple(args, "I", &ctx))
351 if (vc_get_ccaps(ctx, &caps) == -1) {
353 return PyErr_SetFromErrno(PyExc_OSError);
358 return Py_BuildValue("K", caps.bcaps & vc_get_insecurebcaps());
362 vserver_bcaps2text(PyObject *self, PyObject *args)
364 struct vc_ctx_caps caps = { .bcaps = 0 };
368 if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
371 list = PyString_FromString("");
373 while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
376 PyString_ConcatAndDel(&list, PyString_FromFormat(
377 (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
385 convert_address(const char *str, struct vc_net_addr *addr)
388 if (inet_pton(AF_INET6, str, addr->vna_v6_ip.s6_addr) > 0) {
389 addr->vna_type = VC_NXA_TYPE_IPV6;
392 else if (inet_pton(AF_INET, str, &addr->vna_v4_ip.s_addr) > 0) {
393 addr->vna_type = VC_NXA_TYPE_IPV4;
400 mask_to_prefix(void *data, int limit)
402 uint8_t *mask = data;
404 for (prefix = 0; prefix < limit && mask[prefix >> 3] & (1 << (prefix & 0x07)); prefix++)
410 get_mask(struct vc_net_addr *addr)
412 struct ifaddrs *head, *ifa;
414 int family, offset, len;
417 switch (addr->vna_type) {
418 case VC_NXA_TYPE_IPV4:
420 offset = offsetof(struct sockaddr_in, sin_addr.s_addr);
421 ip = &addr->vna_v4_ip.s_addr;
423 addr->vna_v4_mask.s_addr = htonl(0xffffff00);
424 addr->vna_prefix = 24;
426 case VC_NXA_TYPE_IPV6:
428 offset = offsetof(struct sockaddr_in6, sin6_addr.s6_addr);
429 ip = addr->vna_v6_ip.s6_addr;
431 addr->vna_v6_mask.s6_addr32[9] = addr->vna_v6_mask.s6_addr32[1] = 0xffffffff;
432 addr->vna_v6_mask.s6_addr32[2] = addr->vna_v6_mask.s6_addr32[3] = 0x00000000;
433 addr->vna_prefix = 64;
440 if (getifaddrs(&head) == -1)
442 for (ifa = head; ifa; ifa = ifa->ifa_next) {
443 if (ifa->ifa_addr->sa_family == family &&
444 memcmp((char *) ifa->ifa_addr + offset, ip, len) == 0) {
445 switch (addr->vna_type) {
446 case VC_NXA_TYPE_IPV4:
447 memcpy(&addr->vna_v4_mask.s_addr, ifa->ifa_netmask + offset, len);
448 addr->vna_prefix = mask_to_prefix(&addr->vna_v4_mask.s_addr, 32);
450 case VC_NXA_TYPE_IPV6:
451 memcpy(addr->vna_v6_mask.s6_addr, ifa->ifa_netmask + offset, len);
452 addr->vna_prefix = mask_to_prefix(addr->vna_v6_mask.s6_addr, 128);
463 /* XXX These two functions are really similar */
465 vserver_net_add(PyObject *self, PyObject *args)
467 struct vc_net_addr addr;
471 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
474 if (convert_address(ip, &addr) == -1)
475 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
477 switch (get_mask(&addr)) {
479 return PyErr_SetFromErrno(PyExc_OSError);
481 /* XXX error here? */
484 addr.vna_type |= VC_NXA_TYPE_ADDR;
486 if (vc_net_add(nid, &addr) == -1 && errno != ESRCH)
487 return PyErr_SetFromErrno(PyExc_OSError);
493 vserver_net_remove(PyObject *self, PyObject *args)
495 struct vc_net_addr addr;
499 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
502 if (strcmp(ip, "all") == 0)
503 addr.vna_type = VC_NXA_TYPE_ANY;
504 else if (strcmp(ip, "all4") == 0)
505 addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ANY;
506 else if (strcmp(ip, "all6") == 0)
507 addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ANY;
509 if (convert_address(ip, &addr) == -1)
510 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
511 addr.vna_type |= VC_NXA_TYPE_ADDR;
514 switch (get_mask(&addr)) {
516 return PyErr_SetFromErrno(PyExc_OSError);
519 if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
520 return PyErr_SetFromErrno(PyExc_OSError);
535 if (fchdir(fd) == -1 || chroot(".") == -1)
541 restore_dirs(struct secure_dirs *dirs)
543 if (dirs->host_fd != -1) {
544 if (fchroot(dirs->host_fd) == -1)
546 if (close(dirs->host_fd) == -1)
549 if (dirs->guest_fd != -1) {
550 if (close(dirs->guest_fd) == -1)
553 if (dirs->target_fd != -1) {
554 if (close(dirs->target_fd) == -1)
557 if (dirs->cwd_fd != -1) {
558 if (fchdir(dirs->cwd_fd) == -1)
560 if (close(dirs->cwd_fd) == -1)
567 secure_chdir(struct secure_dirs *dirs, const char *guest, const char *target)
569 dirs->host_fd = dirs->cwd_fd = dirs->guest_fd = dirs->target_fd = -1;
571 dirs->host_fd = open("/", O_RDONLY|O_DIRECTORY);
572 if (dirs->host_fd == -1)
575 dirs->cwd_fd = open(".", O_RDONLY|O_DIRECTORY);
576 if (dirs->cwd_fd == -1)
579 dirs->guest_fd = open(guest, O_RDONLY|O_DIRECTORY);
580 if (dirs->guest_fd == -1)
582 if (fchroot(dirs->guest_fd) == -1)
585 dirs->target_fd = open(target, O_RDONLY|O_DIRECTORY);
586 if (dirs->target_fd == -1)
589 if (fchroot(dirs->host_fd) == -1 || close(dirs->host_fd) == -1)
592 if (close(dirs->guest_fd) == -1)
596 if (fchdir(dirs->target_fd) == -1 || close(dirs->target_fd) == -1)
603 vserver_mount(PyObject *self, PyObject *args)
605 const char *guest, *target, *source, *type, *data = NULL;
606 unsigned long flags = 0;
607 struct secure_dirs dirs;
609 if (!PyArg_ParseTuple(args, "ssss|ks", &source, &guest, &target, &type,
613 if (secure_chdir(&dirs, guest, target) == -1)
615 if (mount(source, ".", type, flags, data) == -1)
623 return PyErr_SetFromErrno(PyExc_OSError);
627 vserver_umount(PyObject *self, PyObject *args)
629 const char *guest, *target;
634 if (!PyArg_ParseTuple(args, "ss|i", &guest, &target, &flags))
637 path = calloc(strlen(guest) + strlen(target) + 2, sizeof(char));
638 sprintf(path, "%s/%s", guest, target);
639 if (umount2(path, flags) == -1)
640 ret = PyErr_SetFromErrno(PyExc_OSError);
649 vserver_set_runlevel(PyObject *self, PyObject *args)
655 if (!PyArg_ParseTuple(args, "si", &file, &runlevel))
660 memset(&ut, 0, sizeof(ut));
661 ut.ut_type = RUN_LVL;
662 ut.ut_pid = ('#' << 8) + runlevel + '0';
669 static PyMethodDef methods[] = {
670 { "chcontext", vserver_chcontext, METH_VARARGS,
671 "chcontext to vserver with provided flags" },
672 { "setup_done", vserver_setup_done, METH_VARARGS,
673 "Release vserver setup lock" },
674 { "setsched", vserver_setsched, METH_VARARGS,
675 "Change vserver scheduling attributes for given vserver context" },
676 { "setdlimit", vserver_set_dlimit, METH_VARARGS,
677 "Set disk limits for given vserver context" },
678 { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
679 "Remove disk limits for given vserver context" },
680 { "getdlimit", vserver_get_dlimit, METH_VARARGS,
681 "Get disk limits for given vserver context" },
682 { "setrlimit", vserver_set_rlimit, METH_VARARGS,
683 "Set resource limits for given resource of a vserver context" },
684 { "getrlimit", vserver_get_rlimit, METH_VARARGS,
685 "Get resource limits for given resource of a vserver context" },
686 { "killall", vserver_killall, METH_VARARGS,
687 "Send signal to all processes in vserver context" },
688 { "isrunning", vserver_isrunning, METH_VARARGS,
689 "Check if vserver is running"},
690 { "setbcaps", vserver_set_bcaps, METH_VARARGS,
691 "Set POSIX capabilities of a vserver context" },
692 { "getbcaps", vserver_get_bcaps, METH_VARARGS,
693 "Get POSIX capabilities of a vserver context" },
694 { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
695 "Translate a string of capabilities to a bitmap" },
696 { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
697 "Translate a capability-bitmap into a string" },
698 { "netadd", vserver_net_add, METH_VARARGS,
699 "Assign an IP address to a context" },
700 { "netremove", vserver_net_remove, METH_VARARGS,
701 "Remove IP address(es) from a context" },
702 { "mount", vserver_mount, METH_VARARGS,
703 "Perform the mount() system call" },
704 { "umount", vserver_umount, METH_VARARGS,
705 "Perform the umount2() system call" },
706 { "setrunlevel", vserver_set_runlevel, METH_VARARGS,
707 "Set the runlevel in utmp" },
708 { NULL, NULL, 0, NULL }
712 initvserverimpl(void)
716 mod = Py_InitModule("vserverimpl", methods);
718 /* export the set of 'safe' capabilities */
719 PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
721 /* export the default vserver directory */
722 PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
724 /* export limit-related constants */
725 PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
726 PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
727 PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
729 PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
730 PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
731 PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
732 PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
733 PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
734 PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
735 PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
737 PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
738 PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
740 PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
741 PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
742 PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
743 PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);
745 /* scheduler flags */
746 PyModule_AddIntConstant(mod,
747 "VS_SCHED_CPU_GUARANTEED",
748 VS_SCHED_CPU_GUARANTEED);