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>
52 #include "planetlab.h"
54 static inline PyObject *inc_and_ret_none(void)
60 #define NONE inc_and_ret_none()
62 #define PL_INSECURE_BCAPS (vc_get_insecurebcaps() | (1 << VC_CAP_NET_BIND_SERVICE))
63 #define PL_INSECURE_CCAPS vc_get_insecureccaps()
69 vserver_chcontext(PyObject *self, PyObject *args)
73 uint_least64_t bcaps = 0;
74 int unshare_netns = 0;
76 if (!PyArg_ParseTuple(args, "I|KI", &ctx, &bcaps, &unshare_netns))
78 bcaps |= ~PL_INSECURE_BCAPS;
80 if ((ctx_is_new = pl_chcontext(ctx, bcaps, 0, unshare_netns)) < 0)
81 return PyErr_SetFromErrno(PyExc_OSError);
83 return PyBool_FromLong(ctx_is_new);
87 vserver_setup_done(PyObject *self, PyObject *args)
91 if (!PyArg_ParseTuple(args, "I", &ctx))
94 if (pl_setup_done(ctx) < 0)
95 return PyErr_SetFromErrno(PyExc_OSError);
101 vserver_isrunning(PyObject *self, PyObject *args)
108 if (!PyArg_ParseTuple(args, "I", &ctx))
111 sprintf(fname,"/proc/virtual/%d", ctx);
113 if(stat(&fname[0],&statbuf)==0)
114 ret = PyBool_FromLong(1);
116 ret = PyBool_FromLong(0);
122 __vserver_get_rlimit(xid_t xid, int resource) {
123 struct vc_rlimit limits;
127 if (vc_get_rlimit(xid, resource, &limits)==-1)
128 ret = PyErr_SetFromErrno(PyExc_OSError);
130 ret = Py_BuildValue("LLL",limits.hard, limits.soft, limits.min);
136 vserver_get_rlimit(PyObject *self, PyObject *args) {
141 if (!PyArg_ParseTuple(args, "Ii", &xid, &resource))
144 ret = __vserver_get_rlimit(xid, resource);
150 vserver_set_rlimit(PyObject *self, PyObject *args) {
151 struct vc_rlimit limits;
152 struct vc_rlimit_mask mask;
158 limits.min = VC_LIM_KEEP;
159 limits.soft = VC_LIM_KEEP;
160 limits.hard = VC_LIM_KEEP;
162 if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
167 if (vc_get_rlimit_mask(xid, &mask)==-1) {
168 ret = PyErr_SetFromErrno(PyExc_OSError);
170 bitmask = (1<<resource);
171 if ((mask.min|mask.soft|mask.hard) & bitmask)
172 if (vc_set_rlimit(xid, resource, &limits)==-1)
173 ret = PyErr_SetFromErrno(PyExc_OSError);
175 ret = __vserver_get_rlimit(xid, resource);
185 vserver_setsched(PyObject *self, PyObject *args)
191 if (!PyArg_ParseTuple(args, "II|I", &ctx, &cpu_min, &cpu_share))
194 /* ESRCH indicates that there are no processes in the context */
195 if (pl_setsched(ctx, cpu_min, cpu_share) &&
197 return PyErr_SetFromErrno(PyExc_OSError);
203 vserver_get_dlimit(PyObject *self, PyObject *args)
208 struct vc_ctx_dlimit data;
211 if (!PyArg_ParseTuple(args, "si", &path,&xid))
214 memset(&data, 0, sizeof(data));
215 r = vc_get_dlimit(path, xid, 0, &data);
217 res = Py_BuildValue("(i,i,i,i,i)",
224 res = PyErr_SetFromErrno(PyExc_OSError);
232 vserver_set_dlimit(PyObject *self, PyObject *args)
236 struct vc_ctx_dlimit data;
238 memset(&data,0,sizeof(data));
239 if (!PyArg_ParseTuple(args, "siiiiii", &path,
248 if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
249 vc_set_dlimit(path, xid, 0, &data))
250 return PyErr_SetFromErrno(PyExc_OSError);
256 vserver_unset_dlimit(PyObject *self, PyObject *args)
261 if (!PyArg_ParseTuple(args, "si", &path, &xid))
264 if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
265 return PyErr_SetFromErrno(PyExc_OSError);
271 vserver_killall(PyObject *self, PyObject *args)
275 struct vc_ctx_flags cflags = {
277 .mask = VC_VXF_PERSISTENT
279 struct vc_net_flags nflags = {
281 .mask = VC_NXF_PERSISTENT
284 if (!PyArg_ParseTuple(args, "Ii", &ctx, &sig))
287 if (vc_ctx_kill(ctx, 0, sig) && errno != ESRCH)
288 return PyErr_SetFromErrno(PyExc_OSError);
290 if (vc_set_cflags(ctx, &cflags) && errno != ESRCH)
291 return PyErr_SetFromErrno(PyExc_OSError);
293 if (vc_set_nflags(ctx, &nflags) && errno != ESRCH)
294 return PyErr_SetFromErrno(PyExc_OSError);
300 vserver_set_bcaps(PyObject *self, PyObject *args)
303 struct vc_ctx_caps caps;
305 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
308 caps.bmask = PL_INSECURE_BCAPS;
309 caps.cmask = caps.ccaps = 0;
310 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
311 return PyErr_SetFromErrno(PyExc_OSError);
317 vserver_text2bcaps(PyObject *self, PyObject *args)
319 struct vc_ctx_caps caps = { .bcaps = 0 };
322 struct vc_err_listparser err;
324 if (!PyArg_ParseTuple(args, "s#", &list, &len))
327 vc_list2bcap(list, len, &err, &caps);
329 return Py_BuildValue("K", caps.bcaps);
333 vserver_get_bcaps(PyObject *self, PyObject *args)
336 struct vc_ctx_caps caps;
338 if (!PyArg_ParseTuple(args, "I", &ctx))
341 if (vc_get_ccaps(ctx, &caps) == -1) {
343 return PyErr_SetFromErrno(PyExc_OSError);
348 return Py_BuildValue("K", caps.bcaps & PL_INSECURE_BCAPS);
352 vserver_bcaps2text(PyObject *self, PyObject *args)
354 struct vc_ctx_caps caps = { .bcaps = 0 };
358 if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
361 list = PyString_FromString("");
363 while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
366 PyString_ConcatAndDel(&list, PyString_FromFormat(
367 (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
375 vserver_set_ccaps(PyObject *self, PyObject *args)
378 struct vc_ctx_caps caps;
380 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.ccaps))
383 caps.cmask = PL_INSECURE_CCAPS;
384 caps.bmask = caps.bcaps = 0;
385 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
386 return PyErr_SetFromErrno(PyExc_OSError);
392 vserver_text2ccaps(PyObject *self, PyObject *args)
394 struct vc_ctx_caps caps = { .ccaps = 0 };
397 struct vc_err_listparser err;
399 if (!PyArg_ParseTuple(args, "s#", &list, &len))
402 vc_list2ccap(list, len, &err, &caps);
404 return Py_BuildValue("K", caps.ccaps);
408 vserver_get_ccaps(PyObject *self, PyObject *args)
411 struct vc_ctx_caps caps;
413 if (!PyArg_ParseTuple(args, "I", &ctx))
416 if (vc_get_ccaps(ctx, &caps) == -1) {
418 return PyErr_SetFromErrno(PyExc_OSError);
423 return Py_BuildValue("K", caps.ccaps & PL_INSECURE_CCAPS);
427 vserver_ccaps2text(PyObject *self, PyObject *args)
429 struct vc_ctx_caps caps = { .ccaps = 0 };
433 if (!PyArg_ParseTuple(args, "K", &caps.ccaps))
436 list = PyString_FromString("");
438 while ((cap = vc_loccap2text(&caps.ccaps)) != NULL) {
441 PyString_ConcatAndDel(&list, PyString_FromFormat(
442 (PyString_Size(list) > 0 ? ",%s" : "%s" ),
450 convert_address(const char *str, struct vc_net_addr *addr)
453 if (inet_pton(AF_INET6, str, addr->vna_v6_ip.s6_addr) > 0) {
454 addr->vna_type = VC_NXA_TYPE_IPV6;
457 else if (inet_pton(AF_INET, str, &addr->vna_v4_ip.s_addr) > 0) {
458 addr->vna_type = VC_NXA_TYPE_IPV4;
465 mask_to_prefix(void *data, int limit)
467 uint8_t *mask = data;
469 for (prefix = 0; prefix < limit && mask[prefix >> 3] & (1 << (prefix & 0x07)); prefix++)
475 get_mask(struct vc_net_addr *addr)
477 struct ifaddrs *head, *ifa;
479 int family, offset, len;
482 switch (addr->vna_type) {
483 case VC_NXA_TYPE_IPV4:
485 offset = offsetof(struct sockaddr_in, sin_addr.s_addr);
486 ip = &addr->vna_v4_ip.s_addr;
488 addr->vna_v4_mask.s_addr = htonl(0xffffff00);
489 addr->vna_prefix = 24;
491 case VC_NXA_TYPE_IPV6:
493 offset = offsetof(struct sockaddr_in6, sin6_addr.s6_addr);
494 ip = addr->vna_v6_ip.s6_addr;
496 addr->vna_v6_mask.s6_addr32[9] = addr->vna_v6_mask.s6_addr32[1] = 0xffffffff;
497 addr->vna_v6_mask.s6_addr32[2] = addr->vna_v6_mask.s6_addr32[3] = 0x00000000;
498 addr->vna_prefix = 64;
505 if (getifaddrs(&head) == -1)
507 for (ifa = head; ifa; ifa = ifa->ifa_next) {
508 if (ifa->ifa_addr->sa_family == family &&
509 memcmp((char *) ifa->ifa_addr + offset, ip, len) == 0) {
510 switch (addr->vna_type) {
511 case VC_NXA_TYPE_IPV4:
512 memcpy(&addr->vna_v4_mask.s_addr, ifa->ifa_netmask + offset, len);
513 addr->vna_prefix = mask_to_prefix(&addr->vna_v4_mask.s_addr, 32);
515 case VC_NXA_TYPE_IPV6:
516 memcpy(addr->vna_v6_mask.s6_addr, ifa->ifa_netmask + offset, len);
517 addr->vna_prefix = mask_to_prefix(addr->vna_v6_mask.s6_addr, 128);
528 /* XXX These two functions are really similar */
530 vserver_net_add(PyObject *self, PyObject *args)
532 struct vc_net_addr addr = { .vna_type = 0 };
536 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
539 /* Optimize standard case, which also needs to be handled differently */
540 if (strcmp(ip, "0.0.0.0") == 0) {
541 addr.vna_type = VC_NXA_TYPE_MASK | VC_NXA_TYPE_IPV4;
545 addr.vna_v4_mask.s_addr = 0;
546 addr.vna_v4_ip.s_addr = 0;
549 if (convert_address(ip, &addr) == -1)
550 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
552 switch (get_mask(&addr)) {
554 return PyErr_SetFromErrno(PyExc_OSError);
556 /* XXX error here? */
559 addr.vna_type |= VC_NXA_TYPE_ADDR;
562 if (vc_net_add(nid, &addr) == -1 && errno != ESRCH)
563 return PyErr_SetFromErrno(PyExc_OSError);
569 vserver_net_remove(PyObject *self, PyObject *args)
571 struct vc_net_addr addr;
575 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
578 if (strcmp(ip, "all") == 0)
579 addr.vna_type = VC_NXA_TYPE_ANY;
580 else if (strcmp(ip, "all4") == 0)
581 addr.vna_type = VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_ANY;
582 else if (strcmp(ip, "all6") == 0)
583 addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ANY;
585 if (convert_address(ip, &addr) == -1)
586 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
588 switch (get_mask(&addr)) {
590 return PyErr_SetFromErrno(PyExc_OSError);
593 addr.vna_type |= VC_NXA_TYPE_ADDR;
596 if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
597 return PyErr_SetFromErrno(PyExc_OSError);
612 if (fchdir(fd) == -1 || chroot(".") == -1)
618 restore_dirs(struct secure_dirs *dirs)
620 if (dirs->host_fd != -1) {
621 if (fchroot(dirs->host_fd) == -1)
623 if (close(dirs->host_fd) == -1)
626 if (dirs->guest_fd != -1) {
627 if (close(dirs->guest_fd) == -1)
630 if (dirs->target_fd != -1) {
631 if (close(dirs->target_fd) == -1)
634 if (dirs->cwd_fd != -1) {
635 if (fchdir(dirs->cwd_fd) == -1)
637 if (close(dirs->cwd_fd) == -1)
644 secure_chdir(struct secure_dirs *dirs, const char *guest, const char *target)
646 dirs->host_fd = dirs->cwd_fd = dirs->guest_fd = dirs->target_fd = -1;
648 dirs->host_fd = open("/", O_RDONLY|O_DIRECTORY);
649 if (dirs->host_fd == -1)
652 dirs->cwd_fd = open(".", O_RDONLY|O_DIRECTORY);
653 if (dirs->cwd_fd == -1)
656 dirs->guest_fd = open(guest, O_RDONLY|O_DIRECTORY);
657 if (dirs->guest_fd == -1)
659 if (fchroot(dirs->guest_fd) == -1)
662 dirs->target_fd = open(target, O_RDONLY|O_DIRECTORY);
663 if (dirs->target_fd == -1)
666 if (fchroot(dirs->host_fd) == -1 || close(dirs->host_fd) == -1)
669 if (close(dirs->guest_fd) == -1)
673 if (fchdir(dirs->target_fd) == -1 || close(dirs->target_fd) == -1)
680 vserver_mount(PyObject *self, PyObject *args)
682 const char *guest, *target, *source, *type, *data = NULL;
683 unsigned long flags = 0;
684 struct secure_dirs dirs;
686 if (!PyArg_ParseTuple(args, "ssss|ks", &source, &guest, &target, &type,
690 if (secure_chdir(&dirs, guest, target) == -1)
692 if (mount(source, ".", type, flags, data) == -1 && errno != EBUSY)
700 return PyErr_SetFromErrno(PyExc_OSError);
704 vserver_umount(PyObject *self, PyObject *args)
706 const char *guest, *target;
711 if (!PyArg_ParseTuple(args, "ss|i", &guest, &target, &flags))
714 path = calloc(strlen(guest) + strlen(target) + 2, sizeof(char));
715 sprintf(path, "%s/%s", guest, target);
716 if (umount2(path, flags) == -1)
717 ret = PyErr_SetFromErrno(PyExc_OSError);
726 vserver_set_runlevel(PyObject *self, PyObject *args)
732 if (!PyArg_ParseTuple(args, "si", &file, &runlevel))
737 memset(&ut, 0, sizeof(ut));
738 ut.ut_type = RUN_LVL;
739 ut.ut_pid = ('#' << 8) + runlevel + '0';
747 vserver_set_name(PyObject *self, PyObject *args)
752 if (!PyArg_ParseTuple(args, "II", &ctx, &slice_id))
755 if (vc_set_vhi_name(ctx, vcVHI_CONTEXT, (char *)&slice_id, sizeof(slice_id)) != 0 && errno != ESRCH) {
756 return PyErr_SetFromErrno(PyExc_OSError);
763 vserver_get_name(PyObject *self, PyObject *args)
768 if (!PyArg_ParseTuple(args, "I", &ctx))
771 if (vc_get_vhi_name(ctx, vcVHI_CONTEXT, (char *)&slice_id, sizeof(slice_id)) != 0) {
772 ret = PyErr_SetFromErrno(PyExc_OSError);
774 ret = Py_BuildValue("i", slice_id);
779 static PyMethodDef methods[] = {
780 { "chcontext", vserver_chcontext, METH_VARARGS,
781 "chcontext to vserver with provided flags" },
782 { "setup_done", vserver_setup_done, METH_VARARGS,
783 "Release vserver setup lock" },
784 { "setsched", vserver_setsched, METH_VARARGS,
785 "Change vserver scheduling attributes for given vserver context" },
786 { "setdlimit", vserver_set_dlimit, METH_VARARGS,
787 "Set disk limits for given vserver context" },
788 { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
789 "Remove disk limits for given vserver context" },
790 { "getdlimit", vserver_get_dlimit, METH_VARARGS,
791 "Get disk limits for given vserver context" },
792 { "setrlimit", vserver_set_rlimit, METH_VARARGS,
793 "Set resource limits for given resource of a vserver context" },
794 { "getrlimit", vserver_get_rlimit, METH_VARARGS,
795 "Get resource limits for given resource of a vserver context" },
796 { "killall", vserver_killall, METH_VARARGS,
797 "Send signal to all processes in vserver context" },
798 { "isrunning", vserver_isrunning, METH_VARARGS,
799 "Check if vserver is running"},
800 { "setbcaps", vserver_set_bcaps, METH_VARARGS,
801 "Set POSIX capabilities of a vserver context" },
802 { "getbcaps", vserver_get_bcaps, METH_VARARGS,
803 "Get POSIX capabilities of a vserver context" },
804 { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
805 "Translate a string of capabilities to a bitmap" },
806 { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
807 "Translate a capability-bitmap into a string" },
808 { "setccaps", vserver_set_ccaps, METH_VARARGS,
809 "Set context capabilities of a vserver context" },
810 { "getccaps", vserver_get_ccaps, METH_VARARGS,
811 "Get context capabilities of a vserver context" },
812 { "text2ccaps", vserver_text2ccaps, METH_VARARGS,
813 "Translate a string of context capabilities to a bitmap" },
814 { "ccaps2text", vserver_ccaps2text, METH_VARARGS,
815 "Translate a context-capability-bitmap into a string" },
816 { "netadd", vserver_net_add, METH_VARARGS,
817 "Assign an IP address to a context" },
818 { "netremove", vserver_net_remove, METH_VARARGS,
819 "Remove IP address(es) from a context" },
820 { "mount", vserver_mount, METH_VARARGS,
821 "Perform the mount() system call" },
822 { "umount", vserver_umount, METH_VARARGS,
823 "Perform the umount2() system call" },
824 { "setrunlevel", vserver_set_runlevel, METH_VARARGS,
825 "Set the runlevel in utmp" },
826 { "setname", vserver_set_name, METH_VARARGS,
827 "Set the vcVHI_CONTEXT for a xid." },
828 { "getname", vserver_get_name, METH_VARARGS,
829 "Get the vcVHI_CONTEXT for a xid." },
830 { NULL, NULL, 0, NULL }
834 initvserverimpl(void)
838 mod = Py_InitModule("vserverimpl", methods);
840 /* export the set of 'safe' capabilities */
841 PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
843 /* export the default vserver directory */
844 PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
846 /* export limit-related constants */
847 PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
848 PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
849 PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
851 PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
852 PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
853 PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
854 PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
855 PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
856 PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
857 PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
859 PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
860 PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
862 PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
863 PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
864 PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
865 PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);