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;
150 int resource, lresource;
153 limits.min = VC_LIM_KEEP;
154 limits.soft = VC_LIM_KEEP;
155 limits.hard = VC_LIM_KEEP;
157 if (!PyArg_ParseTuple(args, "IiLLL", &xid, &resource, &limits.hard, &limits.soft, &limits.min))
160 lresource = resource;
162 case VC_VLIMIT_NSOCK:
164 case VC_VLIMIT_SHMEM:
165 goto do_vc_set_rlimit;
166 case VC_VLIMIT_OPENFD:
167 lresource = RLIMIT_NOFILE;
173 getrlimit(lresource,&lim);
174 if (adjust_lim(&limits,&lim)) {
175 setrlimit(lresource, &lim);
180 if (vc_set_rlimit(xid, resource, &limits)==-1)
181 ret = PyErr_SetFromErrno(PyExc_OSError);
183 ret = __vserver_get_rlimit(xid, resource);
192 vserver_setsched(PyObject *self, PyObject *args)
196 uint32_t cpu_sched_flags = VC_VXF_SCHED_FLAGS;
198 if (!PyArg_ParseTuple(args, "II|I", &ctx, &cpu_share, &cpu_sched_flags))
201 /* ESRCH indicates that there are no processes in the context */
202 if (pl_setsched(ctx, cpu_share, cpu_sched_flags) &&
204 return PyErr_SetFromErrno(PyExc_OSError);
210 vserver_get_dlimit(PyObject *self, PyObject *args)
215 struct vc_ctx_dlimit data;
218 if (!PyArg_ParseTuple(args, "si", &path,&xid))
221 memset(&data, 0, sizeof(data));
222 r = vc_get_dlimit(path, xid, 0, &data);
224 res = Py_BuildValue("(i,i,i,i,i)",
231 res = PyErr_SetFromErrno(PyExc_OSError);
239 vserver_set_dlimit(PyObject *self, PyObject *args)
243 struct vc_ctx_dlimit data;
245 memset(&data,0,sizeof(data));
246 if (!PyArg_ParseTuple(args, "siiiiii", &path,
255 if ((vc_add_dlimit(path, xid, 0) && errno != EEXIST) ||
256 vc_set_dlimit(path, xid, 0, &data))
257 return PyErr_SetFromErrno(PyExc_OSError);
263 vserver_unset_dlimit(PyObject *self, PyObject *args)
268 if (!PyArg_ParseTuple(args, "si", &path, &xid))
271 if (vc_rem_dlimit(path, xid, 0) && errno != ESRCH)
272 return PyErr_SetFromErrno(PyExc_OSError);
278 vserver_killall(PyObject *self, PyObject *args)
282 struct vc_ctx_flags cflags = {
284 .mask = VC_VXF_PERSISTENT
286 struct vc_net_flags nflags = {
288 .mask = VC_NXF_PERSISTENT
291 if (!PyArg_ParseTuple(args, "Ii", &ctx, &sig))
294 if (vc_ctx_kill(ctx, 0, sig) && errno != ESRCH)
295 return PyErr_SetFromErrno(PyExc_OSError);
297 if (vc_set_cflags(ctx, &cflags) && errno != ESRCH)
298 return PyErr_SetFromErrno(PyExc_OSError);
300 if (vc_set_nflags(ctx, &nflags) && errno != ESRCH)
301 return PyErr_SetFromErrno(PyExc_OSError);
307 vserver_set_bcaps(PyObject *self, PyObject *args)
310 struct vc_ctx_caps caps;
312 if (!PyArg_ParseTuple(args, "IK", &ctx, &caps.bcaps))
315 caps.bmask = vc_get_insecurebcaps();
316 caps.cmask = caps.ccaps = 0;
317 if (vc_set_ccaps(ctx, &caps) == -1 && errno != ESRCH)
318 return PyErr_SetFromErrno(PyExc_OSError);
324 vserver_text2bcaps(PyObject *self, PyObject *args)
326 struct vc_ctx_caps caps = { .bcaps = 0 };
329 struct vc_err_listparser err;
331 if (!PyArg_ParseTuple(args, "s#", &list, &len))
334 vc_list2bcap(list, len, &err, &caps);
336 return Py_BuildValue("K", caps.bcaps);
340 vserver_get_bcaps(PyObject *self, PyObject *args)
343 struct vc_ctx_caps caps;
345 if (!PyArg_ParseTuple(args, "I", &ctx))
348 if (vc_get_ccaps(ctx, &caps) == -1) {
350 return PyErr_SetFromErrno(PyExc_OSError);
355 return Py_BuildValue("K", caps.bcaps & vc_get_insecurebcaps());
359 vserver_bcaps2text(PyObject *self, PyObject *args)
361 struct vc_ctx_caps caps = { .bcaps = 0 };
365 if (!PyArg_ParseTuple(args, "K", &caps.bcaps))
368 list = PyString_FromString("");
370 while ((cap = vc_lobcap2text(&caps.bcaps)) != NULL) {
373 PyString_ConcatAndDel(&list, PyString_FromFormat(
374 (PyString_Size(list) > 0 ? ",CAP_%s" : "CAP_%s" ),
382 convert_address(const char *str, struct vc_net_addr *addr)
385 if (inet_pton(AF_INET6, str, addr->vna_v6_ip.s6_addr) > 0) {
386 addr->vna_type = VC_NXA_TYPE_IPV6;
389 else if (inet_pton(AF_INET, str, &addr->vna_v4_ip.s_addr) > 0) {
390 addr->vna_type = VC_NXA_TYPE_IPV4;
397 mask_to_prefix(void *data, int limit)
399 uint8_t *mask = data;
401 for (prefix = 0; prefix < limit && mask[prefix >> 3] & (1 << (prefix & 0x07)); prefix++)
407 get_mask(struct vc_net_addr *addr)
409 struct ifaddrs *head, *ifa;
411 int family, offset, len;
414 switch (addr->vna_type) {
415 case VC_NXA_TYPE_IPV4:
417 offset = offsetof(struct sockaddr_in, sin_addr.s_addr);
418 ip = &addr->vna_v4_ip.s_addr;
420 addr->vna_v4_mask.s_addr = htonl(0xffffff00);
421 addr->vna_prefix = 24;
423 case VC_NXA_TYPE_IPV6:
425 offset = offsetof(struct sockaddr_in6, sin6_addr.s6_addr);
426 ip = addr->vna_v6_ip.s6_addr;
428 addr->vna_v6_mask.s6_addr32[9] = addr->vna_v6_mask.s6_addr32[1] = 0xffffffff;
429 addr->vna_v6_mask.s6_addr32[2] = addr->vna_v6_mask.s6_addr32[3] = 0x00000000;
430 addr->vna_prefix = 64;
437 if (getifaddrs(&head) == -1)
439 for (ifa = head; ifa; ifa = ifa->ifa_next) {
440 if (ifa->ifa_addr->sa_family == family &&
441 memcmp((char *) ifa->ifa_addr + offset, ip, len) == 0) {
442 switch (addr->vna_type) {
443 case VC_NXA_TYPE_IPV4:
444 memcpy(&addr->vna_v4_mask.s_addr, ifa->ifa_netmask + offset, len);
445 addr->vna_prefix = mask_to_prefix(&addr->vna_v4_mask.s_addr, 32);
447 case VC_NXA_TYPE_IPV6:
448 memcpy(addr->vna_v6_mask.s6_addr, ifa->ifa_netmask + offset, len);
449 addr->vna_prefix = mask_to_prefix(addr->vna_v6_mask.s6_addr, 128);
460 /* XXX These two functions are really similar */
462 vserver_net_add(PyObject *self, PyObject *args)
464 struct vc_net_addr addr;
468 if (!PyArg_ParseTuple(args, "Is", &nid, &ip))
471 if (convert_address(ip, &addr) == -1)
472 return PyErr_Format(PyExc_ValueError, "%s is not a valid IP address", ip);
474 switch (get_mask(&addr)) {
476 return PyErr_SetFromErrno(PyExc_OSError);
478 /* XXX error here? */
481 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_IPV6 | 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);
508 addr.vna_type |= VC_NXA_TYPE_ADDR;
511 switch (get_mask(&addr)) {
513 return PyErr_SetFromErrno(PyExc_OSError);
516 if (vc_net_remove(nid, &addr) == -1 && errno != ESRCH)
517 return PyErr_SetFromErrno(PyExc_OSError);
532 if (fchdir(fd) == -1 || chroot(".") == -1)
538 restore_dirs(struct secure_dirs *dirs)
540 if (dirs->host_fd != -1) {
541 if (fchroot(dirs->host_fd) == -1)
543 if (close(dirs->host_fd) == -1)
546 if (dirs->guest_fd != -1) {
547 if (close(dirs->guest_fd) == -1)
550 if (dirs->target_fd != -1) {
551 if (close(dirs->target_fd) == -1)
554 if (dirs->cwd_fd != -1) {
555 if (fchdir(dirs->cwd_fd) == -1)
557 if (close(dirs->cwd_fd) == -1)
564 secure_chdir(struct secure_dirs *dirs, const char *guest, const char *target)
566 dirs->host_fd = dirs->cwd_fd = dirs->guest_fd = dirs->target_fd = -1;
568 dirs->host_fd = open("/", O_RDONLY|O_DIRECTORY);
569 if (dirs->host_fd == -1)
572 dirs->cwd_fd = open(".", O_RDONLY|O_DIRECTORY);
573 if (dirs->cwd_fd == -1)
576 dirs->guest_fd = open(guest, O_RDONLY|O_DIRECTORY);
577 if (dirs->guest_fd == -1)
579 if (fchroot(dirs->guest_fd) == -1)
582 dirs->target_fd = open(target, O_RDONLY|O_DIRECTORY);
583 if (dirs->target_fd == -1)
586 if (fchroot(dirs->host_fd) == -1 || close(dirs->host_fd) == -1)
589 if (close(dirs->guest_fd) == -1)
593 if (fchdir(dirs->target_fd) == -1 || close(dirs->target_fd) == -1)
600 vserver_mount(PyObject *self, PyObject *args)
602 const char *guest, *target, *source, *type, *data = NULL;
603 unsigned long flags = 0;
604 struct secure_dirs dirs;
606 if (!PyArg_ParseTuple(args, "ssss|ks", &source, &guest, &target, &type,
610 if (secure_chdir(&dirs, guest, target) == -1)
612 if (mount(source, ".", type, flags, data) == -1)
620 return PyErr_SetFromErrno(PyExc_OSError);
624 vserver_umount(PyObject *self, PyObject *args)
626 const char *guest, *target;
631 if (!PyArg_ParseTuple(args, "ss|i", &guest, &target, &flags))
634 path = calloc(strlen(guest) + strlen(target) + 2, sizeof(char));
635 sprintf(path, "%s/%s", guest, target);
636 if (umount2(path, flags) == -1)
637 ret = PyErr_SetFromErrno(PyExc_OSError);
646 vserver_set_runlevel(PyObject *self, PyObject *args)
652 if (!PyArg_ParseTuple(args, "si", &file, &runlevel))
657 memset(&ut, 0, sizeof(ut));
658 ut.ut_type = RUN_LVL;
659 ut.ut_pid = ('#' << 8) + runlevel + '0';
666 static PyMethodDef methods[] = {
667 { "chcontext", vserver_chcontext, METH_VARARGS,
668 "chcontext to vserver with provided flags" },
669 { "setup_done", vserver_setup_done, METH_VARARGS,
670 "Release vserver setup lock" },
671 { "setsched", vserver_setsched, METH_VARARGS,
672 "Change vserver scheduling attributes for given vserver context" },
673 { "setdlimit", vserver_set_dlimit, METH_VARARGS,
674 "Set disk limits for given vserver context" },
675 { "unsetdlimit", vserver_unset_dlimit, METH_VARARGS,
676 "Remove disk limits for given vserver context" },
677 { "getdlimit", vserver_get_dlimit, METH_VARARGS,
678 "Get disk limits for given vserver context" },
679 { "setrlimit", vserver_set_rlimit, METH_VARARGS,
680 "Set resource limits for given resource of a vserver context" },
681 { "getrlimit", vserver_get_rlimit, METH_VARARGS,
682 "Get resource limits for given resource of a vserver context" },
683 { "killall", vserver_killall, METH_VARARGS,
684 "Send signal to all processes in vserver context" },
685 { "isrunning", vserver_isrunning, METH_VARARGS,
686 "Check if vserver is running"},
687 { "setbcaps", vserver_set_bcaps, METH_VARARGS,
688 "Set POSIX capabilities of a vserver context" },
689 { "getbcaps", vserver_get_bcaps, METH_VARARGS,
690 "Get POSIX capabilities of a vserver context" },
691 { "text2bcaps", vserver_text2bcaps, METH_VARARGS,
692 "Translate a string of capabilities to a bitmap" },
693 { "bcaps2text", vserver_bcaps2text, METH_VARARGS,
694 "Translate a capability-bitmap into a string" },
695 { "netadd", vserver_net_add, METH_VARARGS,
696 "Assign an IP address to a context" },
697 { "netremove", vserver_net_remove, METH_VARARGS,
698 "Remove IP address(es) from a context" },
699 { "mount", vserver_mount, METH_VARARGS,
700 "Perform the mount() system call" },
701 { "umount", vserver_umount, METH_VARARGS,
702 "Perform the umount2() system call" },
703 { "setrunlevel", vserver_set_runlevel, METH_VARARGS,
704 "Set the runlevel in utmp" },
705 { NULL, NULL, 0, NULL }
709 initvserverimpl(void)
713 mod = Py_InitModule("vserverimpl", methods);
715 /* export the set of 'safe' capabilities */
716 PyModule_AddIntConstant(mod, "CAP_SAFE", ~vc_get_insecurebcaps());
718 /* export the default vserver directory */
719 PyModule_AddStringConstant(mod, "VSERVER_BASEDIR", DEFAULT_VSERVERDIR);
721 /* export limit-related constants */
722 PyModule_AddIntConstant(mod, "DLIMIT_KEEP", (int)VC_CDLIM_KEEP);
723 PyModule_AddIntConstant(mod, "DLIMIT_INF", (int)VC_CDLIM_INFINITY);
724 PyModule_AddIntConstant(mod, "VC_LIM_KEEP", (int)VC_LIM_KEEP);
726 PyModule_AddIntConstant(mod, "RLIMIT_CPU", (int)RLIMIT_CPU);
727 PyModule_AddIntConstant(mod, "RLIMIT_RSS", (int)RLIMIT_RSS);
728 PyModule_AddIntConstant(mod, "RLIMIT_NPROC", (int)RLIMIT_NPROC);
729 PyModule_AddIntConstant(mod, "RLIMIT_NOFILE", (int)RLIMIT_NOFILE);
730 PyModule_AddIntConstant(mod, "RLIMIT_MEMLOCK", (int)RLIMIT_MEMLOCK);
731 PyModule_AddIntConstant(mod, "RLIMIT_AS", (int)RLIMIT_AS);
732 PyModule_AddIntConstant(mod, "RLIMIT_LOCKS", (int)RLIMIT_LOCKS);
734 PyModule_AddIntConstant(mod, "RLIMIT_SIGPENDING", (int)RLIMIT_SIGPENDING);
735 PyModule_AddIntConstant(mod, "RLIMIT_MSGQUEUE", (int)RLIMIT_MSGQUEUE);
737 PyModule_AddIntConstant(mod, "VLIMIT_NSOCK", (int)VC_VLIMIT_NSOCK);
738 PyModule_AddIntConstant(mod, "VLIMIT_OPENFD", (int)VC_VLIMIT_OPENFD);
739 PyModule_AddIntConstant(mod, "VLIMIT_ANON", (int)VC_VLIMIT_ANON);
740 PyModule_AddIntConstant(mod, "VLIMIT_SHMEM", (int)VC_VLIMIT_SHMEM);
742 /* scheduler flags */
743 PyModule_AddIntConstant(mod,
744 "VS_SCHED_CPU_GUARANTEED",
745 VS_SCHED_CPU_GUARANTEED);