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()
66 vserver_chcontext(PyObject *self, PyObject *args)
70 uint_least64_t bcaps = 0;
72 if (!PyArg_ParseTuple(args, "I|K", &ctx, &bcaps))
74 bcaps |= ~(vc_get_insecurebcaps() | (1 << VC_CAP_NET_BIND_SERVICE));
76 if ((ctx_is_new = pl_chcontext(ctx, bcaps, 0)) < 0)
77 return PyErr_SetFromErrno(PyExc_OSError);
79 return PyBool_FromLong(ctx_is_new);
83 vserver_setup_done(PyObject *self, PyObject *args)
87 if (!PyArg_ParseTuple(args, "I", &ctx))
90 if (pl_setup_done(ctx) < 0)
91 return PyErr_SetFromErrno(PyExc_OSError);
97 vserver_isrunning(PyObject *self, PyObject *args)
104 if (!PyArg_ParseTuple(args, "I", &ctx))
107 sprintf(fname,"/proc/virtual/%d", ctx);
109 if(stat(&fname[0],&statbuf)==0)
110 ret = PyBool_FromLong(1);
112 ret = PyBool_FromLong(0);
118 __vserver_get_rlimit(xid_t xid, int resource) {
119 struct vc_rlimit limits;
123 if (vc_get_rlimit(xid, resource, &limits)==-1)
124 ret = PyErr_SetFromErrno(PyExc_OSError);
126 ret = Py_BuildValue("LLL",limits.hard, limits.soft, limits.min);
132 vserver_get_rlimit(PyObject *self, PyObject *args) {
137 if (!PyArg_ParseTuple(args, "Ii", &xid, &resource))
140 ret = __vserver_get_rlimit(xid, resource);
146 vserver_set_rlimit(PyObject *self, PyObject *args) {
147 struct vc_rlimit limits;
148 struct vc_rlimit_mask mask;
154 limits.min = VC_LIM_KEEP;
155 limits.soft = VC_LIM_KEEP;
156 limits.hard = VC_LIM_KEEP;
158 if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
163 if (vc_get_rlimit_mask(xid, &mask)==-1) {
164 ret = PyErr_SetFromErrno(PyExc_OSError);
166 bitmask = (1<<resource);
167 if ((mask.min|mask.soft|mask.hard) & bitmask)
168 if (vc_set_rlimit(xid, resource, &limits)==-1)
169 ret = PyErr_SetFromErrno(PyExc_OSError);
171 ret = __vserver_get_rlimit(xid, resource);
181 vserver_setsched(PyObject *self, PyObject *args)
187 if (!PyArg_ParseTuple(args, "II|I", &ctx, &cpu_min, &cpu_share))
190 /* ESRCH indicates that there are no processes in the context */
191 if (pl_setsched(ctx, cpu_min, cpu_share) &&
193 return PyErr_SetFromErrno(PyExc_OSError);
199 vserver_get_dlimit(PyObject *self, PyObject *args)
204 struct vc_ctx_dlimit data;
207 if (!PyArg_ParseTuple(args, "si", &path,&xid))
210 memset(&data, 0, sizeof(data));
211 r = vc_get_dlimit(path, xid, 0, &data);
213 res = Py_BuildValue("(i,i,i,i,i)",
220 res = PyErr_SetFromErrno(PyExc_OSError);
228 vserver_set_dlimit(PyObject *self, PyObject *args)
232 struct vc_ctx_dlimit data;
234 memset(&data,0,sizeof(data));
235 if (!PyArg_ParseTuple(args, "siiiiii", &path,
244 if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
245 vc_set_dlimit(path, xid, 0, &data))
246 return PyErr_SetFromErrno(PyExc_OSError);
252 vserver_unset_dlimit(PyObject *self, PyObject *args)
257 if (!PyArg_ParseTuple(args, "si", &path, &xid))
260 if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
261 return PyErr_SetFromErrno(PyExc_OSError);
267 vserver_killall(PyObject *self, PyObject *args)
271 struct vc_ctx_flags cflags = {
273 .mask = VC_VXF_PERSISTENT
275 struct vc_net_flags nflags = {
277 .mask = VC_NXF_PERSISTENT
280 if (!PyArg_ParseTuple(args, "Ii", &ctx, &sig))
283 if (vc_ctx_kill(ctx, 0, sig) && errno != ESRCH)
284 return PyErr_SetFromErrno(PyExc_OSError);
286 if (vc_set_cflags(ctx, &cflags) && errno != ESRCH)
287 return PyErr_SetFromErrno(PyExc_OSError);
289 if (vc_set_nflags(ctx, &nflags) && errno != ESRCH)
290 return PyErr_SetFromErrno(PyExc_OSError);
296 vserver_set_bcaps(PyObject *self, PyObject *args)
299 struct vc_ctx_caps caps;
301 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
304 caps.bmask = vc_get_insecurebcaps();
305 caps.cmask = caps.ccaps = 0;
306 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
307 return PyErr_SetFromErrno(PyExc_OSError);
313 vserver_text2bcaps(PyObject *self, PyObject *args)
315 struct vc_ctx_caps caps = { .bcaps = 0 };
318 struct vc_err_listparser err;
320 if (!PyArg_ParseTuple(args, "s#", &list, &len))
323 vc_list2bcap(list, len, &err, &caps);
325 return Py_BuildValue("K", caps.bcaps);
329 vserver_get_bcaps(PyObject *self, PyObject *args)
332 struct vc_ctx_caps caps;
334 if (!PyArg_ParseTuple(args, "I", &ctx))
337 if (vc_get_ccaps(ctx, &caps) == -1) {
339 return PyErr_SetFromErrno(PyExc_OSError);
344 return Py_BuildValue("K", caps.bcaps & vc_get_insecurebcaps());
348 vserver_bcaps2text(PyObject *self, PyObject *args)
350 struct vc_ctx_caps caps = { .bcaps = 0 };
354 if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
357 list = PyString_FromString("");
359 while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
362 PyString_ConcatAndDel(&list, PyString_FromFormat(
363 (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
371 convert_address(const char *str, struct vc_net_addr *addr)
374 if (inet_pton(AF_INET6, str, addr->vna_v6_ip.s6_addr) > 0) {
375 addr->vna_type = VC_NXA_TYPE_IPV6;
378 else if (inet_pton(AF_INET, str, &addr->vna_v4_ip.s_addr) > 0) {
379 addr->vna_type = VC_NXA_TYPE_IPV4;
386 mask_to_prefix(void *data, int limit)
388 uint8_t *mask = data;
390 for (prefix = 0; prefix < limit && mask[prefix >> 3] & (1 << (prefix & 0x07)); prefix++)
396 get_mask(struct vc_net_addr *addr)
398 struct ifaddrs *head, *ifa;
400 int family, offset, len;
403 switch (addr->vna_type) {
404 case VC_NXA_TYPE_IPV4:
406 offset = offsetof(struct sockaddr_in, sin_addr.s_addr);
407 ip = &addr->vna_v4_ip.s_addr;
409 addr->vna_v4_mask.s_addr = htonl(0xffffff00);
410 addr->vna_prefix = 24;
412 case VC_NXA_TYPE_IPV6:
414 offset = offsetof(struct sockaddr_in6, sin6_addr.s6_addr);
415 ip = addr->vna_v6_ip.s6_addr;
417 addr->vna_v6_mask.s6_addr32[9] = addr->vna_v6_mask.s6_addr32[1] = 0xffffffff;
418 addr->vna_v6_mask.s6_addr32[2] = addr->vna_v6_mask.s6_addr32[3] = 0x00000000;
419 addr->vna_prefix = 64;
426 if (getifaddrs(&head) == -1)
428 for (ifa = head; ifa; ifa = ifa->ifa_next) {
429 if (ifa->ifa_addr->sa_family == family &&
430 memcmp((char *) ifa->ifa_addr + offset, ip, len) == 0) {
431 switch (addr->vna_type) {
432 case VC_NXA_TYPE_IPV4:
433 memcpy(&addr->vna_v4_mask.s_addr, ifa->ifa_netmask + offset, len);
434 addr->vna_prefix = mask_to_prefix(&addr->vna_v4_mask.s_addr, 32);
436 case VC_NXA_TYPE_IPV6:
437 memcpy(addr->vna_v6_mask.s6_addr, ifa->ifa_netmask + offset, len);
438 addr->vna_prefix = mask_to_prefix(addr->vna_v6_mask.s6_addr, 128);
449 /* XXX These two functions are really similar */
451 vserver_net_add(PyObject *self, PyObject *args)
453 struct vc_net_addr addr = { .vna_type = 0 };
457 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
460 /* Optimize standard case, which also needs to be handled differently */
461 if (strcmp(ip, "0.0.0.0") == 0) {
462 addr.vna_type = VC_NXA_TYPE_MASK | VC_NXA_TYPE_IPV4;
466 addr.vna_v4_mask.s_addr = 0;
467 addr.vna_v4_ip.s_addr = 0;
470 if (convert_address(ip, &addr) == -1)
471 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
473 switch (get_mask(&addr)) {
475 return PyErr_SetFromErrno(PyExc_OSError);
477 /* XXX error here? */
480 addr.vna_type |= VC_NXA_TYPE_ADDR;
483 if (vc_net_add(nid, &addr) == -1 && errno != ESRCH)
484 return PyErr_SetFromErrno(PyExc_OSError);
490 vserver_net_remove(PyObject *self, PyObject *args)
492 struct vc_net_addr addr;
496 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
499 if (strcmp(ip, "all") == 0)
500 addr.vna_type = VC_NXA_TYPE_ANY;
501 else if (strcmp(ip, "all4") == 0)
502 addr.vna_type = VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_ANY;
503 else if (strcmp(ip, "all6") == 0)
504 addr.vna_type = VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_ANY;
506 if (convert_address(ip, &addr) == -1)
507 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
509 switch (get_mask(&addr)) {
511 return PyErr_SetFromErrno(PyExc_OSError);
514 addr.vna_type |= VC_NXA_TYPE_ADDR;
517 if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
518 return PyErr_SetFromErrno(PyExc_OSError);
533 if (fchdir(fd) == -1 || chroot(".") == -1)
539 restore_dirs(struct secure_dirs *dirs)
541 if (dirs->host_fd != -1) {
542 if (fchroot(dirs->host_fd) == -1)
544 if (close(dirs->host_fd) == -1)
547 if (dirs->guest_fd != -1) {
548 if (close(dirs->guest_fd) == -1)
551 if (dirs->target_fd != -1) {
552 if (close(dirs->target_fd) == -1)
555 if (dirs->cwd_fd != -1) {
556 if (fchdir(dirs->cwd_fd) == -1)
558 if (close(dirs->cwd_fd) == -1)
565 secure_chdir(struct secure_dirs *dirs, const char *guest, const char *target)
567 dirs->host_fd = dirs->cwd_fd = dirs->guest_fd = dirs->target_fd = -1;
569 dirs->host_fd = open("/", O_RDONLY|O_DIRECTORY);
570 if (dirs->host_fd == -1)
573 dirs->cwd_fd = open(".", O_RDONLY|O_DIRECTORY);
574 if (dirs->cwd_fd == -1)
577 dirs->guest_fd = open(guest, O_RDONLY|O_DIRECTORY);
578 if (dirs->guest_fd == -1)
580 if (fchroot(dirs->guest_fd) == -1)
583 dirs->target_fd = open(target, O_RDONLY|O_DIRECTORY);
584 if (dirs->target_fd == -1)
587 if (fchroot(dirs->host_fd) == -1 || close(dirs->host_fd) == -1)
590 if (close(dirs->guest_fd) == -1)
594 if (fchdir(dirs->target_fd) == -1 || close(dirs->target_fd) == -1)
601 vserver_mount(PyObject *self, PyObject *args)
603 const char *guest, *target, *source, *type, *data = NULL;
604 unsigned long flags = 0;
605 struct secure_dirs dirs;
607 if (!PyArg_ParseTuple(args, "ssss|ks", &source, &guest, &target, &type,
611 if (secure_chdir(&dirs, guest, target) == -1)
613 if (mount(source, ".", type, flags, data) == -1)
621 return PyErr_SetFromErrno(PyExc_OSError);
625 vserver_umount(PyObject *self, PyObject *args)
627 const char *guest, *target;
632 if (!PyArg_ParseTuple(args, "ss|i", &guest, &target, &flags))
635 path = calloc(strlen(guest) + strlen(target) + 2, sizeof(char));
636 sprintf(path, "%s/%s", guest, target);
637 if (umount2(path, flags) == -1)
638 ret = PyErr_SetFromErrno(PyExc_OSError);
647 vserver_set_runlevel(PyObject *self, PyObject *args)
653 if (!PyArg_ParseTuple(args, "si", &file, &runlevel))
658 memset(&ut, 0, sizeof(ut));
659 ut.ut_type = RUN_LVL;
660 ut.ut_pid = ('#' << 8) + runlevel + '0';
667 static PyMethodDef methods[] = {
668 { "chcontext", vserver_chcontext, METH_VARARGS,
669 "chcontext to vserver with provided flags" },
670 { "setup_done", vserver_setup_done, METH_VARARGS,
671 "Release vserver setup lock" },
672 { "setsched", vserver_setsched, METH_VARARGS,
673 "Change vserver scheduling attributes for given vserver context" },
674 { "setdlimit", vserver_set_dlimit, METH_VARARGS,
675 "Set disk limits for given vserver context" },
676 { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
677 "Remove disk limits for given vserver context" },
678 { "getdlimit", vserver_get_dlimit, METH_VARARGS,
679 "Get disk limits for given vserver context" },
680 { "setrlimit", vserver_set_rlimit, METH_VARARGS,
681 "Set resource limits for given resource of a vserver context" },
682 { "getrlimit", vserver_get_rlimit, METH_VARARGS,
683 "Get resource limits for given resource of a vserver context" },
684 { "killall", vserver_killall, METH_VARARGS,
685 "Send signal to all processes in vserver context" },
686 { "isrunning", vserver_isrunning, METH_VARARGS,
687 "Check if vserver is running"},
688 { "setbcaps", vserver_set_bcaps, METH_VARARGS,
689 "Set POSIX capabilities of a vserver context" },
690 { "getbcaps", vserver_get_bcaps, METH_VARARGS,
691 "Get POSIX capabilities of a vserver context" },
692 { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
693 "Translate a string of capabilities to a bitmap" },
694 { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
695 "Translate a capability-bitmap into a string" },
696 { "netadd", vserver_net_add, METH_VARARGS,
697 "Assign an IP address to a context" },
698 { "netremove", vserver_net_remove, METH_VARARGS,
699 "Remove IP address(es) from a context" },
700 { "mount", vserver_mount, METH_VARARGS,
701 "Perform the mount() system call" },
702 { "umount", vserver_umount, METH_VARARGS,
703 "Perform the umount2() system call" },
704 { "setrunlevel", vserver_set_runlevel, METH_VARARGS,
705 "Set the runlevel in utmp" },
706 { NULL, NULL, 0, NULL }
710 initvserverimpl(void)
714 mod = Py_InitModule("vserverimpl", methods);
716 /* export the set of 'safe' capabilities */
717 PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
719 /* export the default vserver directory */
720 PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
722 /* export limit-related constants */
723 PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
724 PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
725 PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
727 PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
728 PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
729 PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
730 PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
731 PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
732 PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
733 PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
735 PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
736 PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
738 PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
739 PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
740 PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
741 PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);