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 #define NONE ({ Py_INCREF(Py_None); Py_None; })
63 vserver_chcontext(PyObject *self, PyObject *args)
67 uint_least64_t bcaps = 0;
69 if (!PyArg_ParseTuple(args, "I|K", &ctx, &bcaps))
71 bcaps |= ~vc_get_insecurebcaps();
73 if ((ctx_is_new = pl_chcontext(ctx, bcaps, 0)) < 0)
74 return PyErr_SetFromErrno(PyExc_OSError);
76 return PyBool_FromLong(ctx_is_new);
80 vserver_setup_done(PyObject *self, PyObject *args)
84 if (!PyArg_ParseTuple(args, "I", &ctx))
87 if (pl_setup_done(ctx) < 0)
88 return PyErr_SetFromErrno(PyExc_OSError);
94 vserver_isrunning(PyObject *self, PyObject *args)
101 if (!PyArg_ParseTuple(args, "I", &ctx))
104 sprintf(fname,"/proc/virtual/%d", ctx);
106 if(stat(&fname[0],&statbuf)==0)
107 ret = PyBool_FromLong(1);
109 ret = PyBool_FromLong(0);
115 __vserver_get_rlimit(xid_t xid, int resource) {
116 struct vc_rlimit limits;
120 if (vc_get_rlimit(xid, resource, &limits)==-1)
121 ret = PyErr_SetFromErrno(PyExc_OSError);
123 ret = Py_BuildValue("LLL",limits.hard, limits.soft, limits.min);
129 vserver_get_rlimit(PyObject *self, PyObject *args) {
134 if (!PyArg_ParseTuple(args, "Ii", &xid, &resource))
137 ret = __vserver_get_rlimit(xid, resource);
143 vserver_set_rlimit(PyObject *self, PyObject *args) {
144 struct vc_rlimit limits;
147 int resource, lresource;
150 limits.min = VC_LIM_KEEP;
151 limits.soft = VC_LIM_KEEP;
152 limits.hard = VC_LIM_KEEP;
154 if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
157 lresource = resource;
159 case VC_VLIMIT_NSOCK:
161 case VC_VLIMIT_SHMEM:
162 goto do_vc_set_rlimit;
163 case VC_VLIMIT_OPENFD:
164 lresource = RLIMIT_NOFILE;
170 getrlimit(lresource,&lim);
171 if (adjust_lim(&limits,&lim)) {
172 setrlimit(lresource, &lim);
177 if (vc_set_rlimit(xid, resource, &limits)==-1)
178 ret = PyErr_SetFromErrno(PyExc_OSError);
180 ret = __vserver_get_rlimit(xid, resource);
189 vserver_setsched(PyObject *self, PyObject *args)
193 uint32_t cpu_sched_flags = VC_VXF_SCHED_FLAGS;
195 if (!PyArg_ParseTuple(args, "II|I", &ctx, &cpu_share, &cpu_sched_flags))
198 /* ESRCH indicates that there are no processes in the context */
199 if (pl_setsched(ctx, cpu_share, cpu_sched_flags) &&
201 return PyErr_SetFromErrno(PyExc_OSError);
207 vserver_get_dlimit(PyObject *self, PyObject *args)
212 struct vc_ctx_dlimit data;
215 if (!PyArg_ParseTuple(args, "si", &path,&xid))
218 memset(&data, 0, sizeof(data));
219 r = vc_get_dlimit(path, xid, 0, &data);
221 res = Py_BuildValue("(i,i,i,i,i)",
228 res = PyErr_SetFromErrno(PyExc_OSError);
236 vserver_set_dlimit(PyObject *self, PyObject *args)
240 struct vc_ctx_dlimit data;
242 memset(&data,0,sizeof(data));
243 if (!PyArg_ParseTuple(args, "siiiiii", &path,
252 if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
253 vc_set_dlimit(path, xid, 0, &data))
254 return PyErr_SetFromErrno(PyExc_OSError);
260 vserver_unset_dlimit(PyObject *self, PyObject *args)
265 if (!PyArg_ParseTuple(args, "si", &path, &xid))
268 if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
269 return PyErr_SetFromErrno(PyExc_OSError);
275 vserver_killall(PyObject *self, PyObject *args)
279 struct vc_ctx_flags cflags = {
281 .mask = VC_VXF_PERSISTENT
283 struct vc_net_flags nflags = {
285 .mask = VC_NXF_PERSISTENT
288 if (!PyArg_ParseTuple(args, "Ii", &ctx, &sig))
291 if (vc_ctx_kill(ctx, 0, sig) && errno != ESRCH)
292 return PyErr_SetFromErrno(PyExc_OSError);
294 if (vc_set_cflags(ctx, &cflags) && errno != ESRCH)
295 return PyErr_SetFromErrno(PyExc_OSError);
297 if (vc_set_nflags(ctx, &nflags) && errno != ESRCH)
298 return PyErr_SetFromErrno(PyExc_OSError);
304 vserver_set_bcaps(PyObject *self, PyObject *args)
307 struct vc_ctx_caps caps;
309 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
312 caps.bmask = vc_get_insecurebcaps();
313 caps.cmask = caps.ccaps = 0;
314 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
315 return PyErr_SetFromErrno(PyExc_OSError);
321 vserver_text2bcaps(PyObject *self, PyObject *args)
323 struct vc_ctx_caps caps = { .bcaps = 0 };
326 struct vc_err_listparser err;
328 if (!PyArg_ParseTuple(args, "s#", &list, &len))
331 vc_list2bcap(list, len, &err, &caps);
333 return Py_BuildValue("K", caps.bcaps);
337 vserver_get_bcaps(PyObject *self, PyObject *args)
340 struct vc_ctx_caps caps;
342 if (!PyArg_ParseTuple(args, "I", &ctx))
345 if (vc_get_ccaps(ctx, &caps) == -1) {
347 return PyErr_SetFromErrno(PyExc_OSError);
352 return Py_BuildValue("K", caps.bcaps & vc_get_insecurebcaps());
356 vserver_bcaps2text(PyObject *self, PyObject *args)
358 struct vc_ctx_caps caps = { .bcaps = 0 };
362 if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
365 list = PyString_FromString("");
367 while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
370 PyString_ConcatAndDel(&list, PyString_FromFormat(
371 (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
378 static const struct AF_to_vcNET {
380 vc_net_nx_type vc_net;
384 { AF_INET, vcNET_IPV4, sizeof(struct in_addr), offsetof(struct sockaddr_in, sin_addr.s_addr) },
385 { AF_INET6, vcNET_IPV6, sizeof(struct in6_addr), offsetof(struct sockaddr_in6, sin6_addr.s6_addr) },
390 convert_address(const char *str, vc_net_nx_type *type, void *dst)
392 const struct AF_to_vcNET *i;
393 for (i = converter; i->af; i++) {
394 if (inet_pton(i->af, str, dst)) {
403 get_mask(struct vc_net_nx *addr)
405 const struct AF_to_vcNET *i;
406 struct ifaddrs *head, *ifa;
409 for (i = converter; i->af; i++) {
410 if (i->vc_net == addr->type)
418 if (getifaddrs(&head) == -1)
420 for (ifa = head; ifa; ifa = ifa->ifa_next) {
421 if (ifa->ifa_addr->sa_family == i->af &&
422 memcmp((char *) ifa->ifa_addr + i->offset, addr->ip, i->len) == 0) {
423 switch (addr->type) {
425 memcpy(&addr->mask[0], ifa->ifa_netmask + i->offset, i->len);
428 uint32_t *m = ((struct sockaddr_in6 *) ifa->ifa_netmask)->sin6_addr.s6_addr32;
429 /* optimization for the common case */
430 if ((m[1] & 1) == 1 && (m[2] & 0x80000000) == 0)
434 while (m[addr->mask[0] / 32] & (addr->mask[0] % 32))
444 /* no match, use a default */
446 switch (addr->type) {
447 case vcNET_IPV4: addr->mask[0] = htonl(0xffffff00); break;
448 case vcNET_IPV6: addr->mask[0] = 64; break;
449 default: addr->mask[0] = 0; break;
456 /* XXX These two functions are really similar */
458 vserver_net_add(PyObject *self, PyObject *args)
460 struct vc_net_nx addr;
464 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
467 if (convert_address(ip, &addr.type, &addr.ip) == -1)
468 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
470 switch (get_mask(&addr)) {
472 return PyErr_SetFromErrno(PyExc_OSError);
474 /* XXX error here? */
479 if (vc_net_add(nid, &addr) == -1 && errno != ESRCH)
480 return PyErr_SetFromErrno(PyExc_OSError);
486 vserver_net_remove(PyObject *self, PyObject *args)
488 struct vc_net_nx addr;
492 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
495 if (strcmp(ip, "all") == 0)
496 addr.type = vcNET_ANY;
497 else if (strcmp(ip, "all4") == 0)
498 addr.type = vcNET_IPV4A;
499 else if (strcmp(ip, "all6") == 0)
500 addr.type = vcNET_IPV6A;
502 if (convert_address(ip, &addr.type, &addr.ip) == -1)
503 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
505 switch (get_mask(&addr)) {
507 return PyErr_SetFromErrno(PyExc_OSError);
511 if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
512 return PyErr_SetFromErrno(PyExc_OSError);
527 if (fchdir(fd) == -1 || chroot(".") == -1)
533 restore_dirs(struct secure_dirs *dirs)
535 if (dirs->host_fd != -1) {
536 if (fchroot(dirs->host_fd) == -1)
538 if (close(dirs->host_fd) == -1)
541 if (dirs->guest_fd != -1) {
542 if (close(dirs->guest_fd) == -1)
545 if (dirs->target_fd != -1) {
546 if (close(dirs->target_fd) == -1)
549 if (dirs->cwd_fd != -1) {
550 if (fchdir(dirs->cwd_fd) == -1)
552 if (close(dirs->cwd_fd) == -1)
559 secure_chdir(struct secure_dirs *dirs, const char *guest, const char *target)
561 dirs->host_fd = dirs->cwd_fd = dirs->guest_fd = dirs->target_fd = -1;
563 dirs->host_fd = open("/", O_RDONLY|O_DIRECTORY);
564 if (dirs->host_fd == -1)
567 dirs->cwd_fd = open(".", O_RDONLY|O_DIRECTORY);
568 if (dirs->cwd_fd == -1)
571 dirs->guest_fd = open(guest, O_RDONLY|O_DIRECTORY);
572 if (dirs->guest_fd == -1)
574 if (fchroot(dirs->guest_fd) == -1)
577 dirs->target_fd = open(target, O_RDONLY|O_DIRECTORY);
578 if (dirs->target_fd == -1)
581 if (fchroot(dirs->host_fd) == -1 || close(dirs->host_fd) == -1)
584 if (close(dirs->guest_fd) == -1)
588 if (fchdir(dirs->target_fd) == -1 || close(dirs->target_fd) == -1)
595 vserver_mount(PyObject *self, PyObject *args)
597 const char *guest, *target, *source, *type, *data = NULL;
598 unsigned long flags = 0;
599 struct secure_dirs dirs;
601 if (!PyArg_ParseTuple(args, "ssss|ks", &source, &guest, &target, &type,
605 if (secure_chdir(&dirs, guest, target) == -1)
607 if (mount(source, ".", type, flags, data) == -1)
615 return PyErr_SetFromErrno(PyExc_OSError);
619 vserver_umount(PyObject *self, PyObject *args)
621 const char *guest, *target;
626 if (!PyArg_ParseTuple(args, "ss|i", &guest, &target, &flags))
629 path = calloc(strlen(guest) + strlen(target) + 2, sizeof(char));
630 sprintf(path, "%s/%s", guest, target);
631 if (umount2(path, flags) == -1)
632 ret = PyErr_SetFromErrno(PyExc_OSError);
641 vserver_set_runlevel(PyObject *self, PyObject *args)
647 if (!PyArg_ParseTuple(args, "si", &file, &runlevel))
652 memset(&ut, 0, sizeof(ut));
653 ut.ut_type = RUN_LVL;
654 ut.ut_pid = ('#' << 8) + runlevel + '0';
661 static PyMethodDef methods[] = {
662 { "chcontext", vserver_chcontext, METH_VARARGS,
663 "chcontext to vserver with provided flags" },
664 { "setup_done", vserver_setup_done, METH_VARARGS,
665 "Release vserver setup lock" },
666 { "setsched", vserver_setsched, METH_VARARGS,
667 "Change vserver scheduling attributes for given vserver context" },
668 { "setdlimit", vserver_set_dlimit, METH_VARARGS,
669 "Set disk limits for given vserver context" },
670 { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
671 "Remove disk limits for given vserver context" },
672 { "getdlimit", vserver_get_dlimit, METH_VARARGS,
673 "Get disk limits for given vserver context" },
674 { "setrlimit", vserver_set_rlimit, METH_VARARGS,
675 "Set resource limits for given resource of a vserver context" },
676 { "getrlimit", vserver_get_rlimit, METH_VARARGS,
677 "Get resource limits for given resource of a vserver context" },
678 { "killall", vserver_killall, METH_VARARGS,
679 "Send signal to all processes in vserver context" },
680 { "isrunning", vserver_isrunning, METH_VARARGS,
681 "Check if vserver is running"},
682 { "setbcaps", vserver_set_bcaps, METH_VARARGS,
683 "Set POSIX capabilities of a vserver context" },
684 { "getbcaps", vserver_get_bcaps, METH_VARARGS,
685 "Get POSIX capabilities of a vserver context" },
686 { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
687 "Translate a string of capabilities to a bitmap" },
688 { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
689 "Translate a capability-bitmap into a string" },
690 { "netadd", vserver_net_add, METH_VARARGS,
691 "Assign an IP address to a context" },
692 { "netremove", vserver_net_remove, METH_VARARGS,
693 "Remove IP address(es) from a context" },
694 { "mount", vserver_mount, METH_VARARGS,
695 "Perform the mount() system call" },
696 { "umount", vserver_umount, METH_VARARGS,
697 "Perform the umount2() system call" },
698 { "setrunlevel", vserver_set_runlevel, METH_VARARGS,
699 "Set the runlevel in utmp" },
700 { NULL, NULL, 0, NULL }
704 initvserverimpl(void)
708 mod = Py_InitModule("vserverimpl", methods);
710 /* export the set of 'safe' capabilities */
711 PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
713 /* export the default vserver directory */
714 PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
716 /* export limit-related constants */
717 PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
718 PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
719 PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
721 PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
722 PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
723 PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
724 PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
725 PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
726 PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
727 PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
729 PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
730 PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
732 PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
733 PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
734 PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
735 PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);
737 /* scheduler flags */
738 PyModule_AddIntConstant(mod,
739 "VS_SCHED_CPU_GUARANTEED",
740 VS_SCHED_CPU_GUARANTEED);