Add method to enter a vserver
[util-vserver.git] / python / vserver.py
1 # Copyright 2005 Princeton University
2
3 import errno
4 import fcntl
5 import os
6 import re
7
8 import linuxcaps
9 import passfdimpl
10 import vserverimpl
11
12 from util_vserver_vars import *
13
14 CAP_SAFE = (linuxcaps.CAP_CHOWN |
15             linuxcaps.CAP_DAC_OVERRIDE |
16             linuxcaps.CAP_DAC_READ_SEARCH |
17             linuxcaps.CAP_FOWNER |
18             linuxcaps.CAP_FSETID |
19             linuxcaps.CAP_KILL |
20             linuxcaps.CAP_SETGID |
21             linuxcaps.CAP_SETUID |
22             linuxcaps.CAP_SETPCAP |
23             linuxcaps.CAP_SYS_TTY_CONFIG |
24             linuxcaps.CAP_LEASE |
25             linuxcaps.CAP_SYS_CHROOT |
26             linuxcaps.CAP_SYS_PTRACE)
27
28 #
29 # these are the flags taken from the kernel linux/vserver/legacy.h
30 #
31 FLAGS_LOCK = 1
32 FLAGS_SCHED = 2  # XXX - defined in util-vserver/src/chcontext.c
33 FLAGS_NPROC = 4
34 FLAGS_PRIVATE = 8
35 FLAGS_INIT = 16
36 FLAGS_HIDEINFO = 32
37 FLAGS_ULIMIT = 64
38 FLAGS_NAMESPACE = 128
39
40
41               
42 class VServer:
43
44     def __init__(self, name):
45
46         self.name = name
47         self.config = self.__read_config_file("/etc/vservers.conf")
48         self.config.update(self.__read_config_file("/etc/vservers/%s.conf" %
49                                                    self.name))
50         self.flags = 0
51         flags = self.config["S_FLAGS"].split(" ")
52         if "lock" in flags:
53             self.flags |= FLAGS_LOCK
54         if "nproc" in flags:
55             self.flags |= FLAGS_NPROC
56         self.remove_caps = ~CAP_SAFE
57         self.ctx = int(self.config["S_CONTEXT"])
58
59     config_var_re = re.compile(r"^ *([A-Z_]+)=(.*)\n?$", re.MULTILINE)
60
61     def __read_config_file(self, filename):
62
63         f = open(filename, "r")
64         data = f.read()
65         f.close()
66         config = {}
67         for m in self.config_var_re.finditer(data):
68             (key, val) = m.groups()
69             config[key] = val.strip('"')
70         return config
71
72     def __do_chroot(self):
73
74         return os.chroot("%s/%s" % (VROOTDIR, self.name))
75
76     def open(self, filename, mode = "r"):
77
78         (sendsock, recvsock) = passfdimpl.socketpair()
79         child_pid = os.fork()
80         if child_pid == 0:
81             try:
82                 # child process
83                 self.__do_chroot()
84                 f = open(filename, mode)
85                 passfdimpl.sendmsg(f.fileno(), sendsock)
86                 os._exit(0)
87             except EnvironmentError, ex:
88                 (result, errmsg) = (ex.errno, ex.strerror)
89             except Exception, ex:
90                 (result, errmsg) = (255, str(ex))
91             os.write(sendsock, errmsg)
92             os._exit(result)
93
94         # parent process
95
96         # XXX - need this since a lambda can't raise an exception
97         def __throw(ex):
98             raise ex
99
100         os.close(sendsock)
101         throw = lambda : __throw(Exception(errmsg))
102         while True:
103             try:
104                 (pid, status) = os.waitpid(child_pid, 0)
105                 if os.WIFEXITED(status):
106                     result = os.WEXITSTATUS(status)
107                     if result != 255:
108                         errmsg = os.strerror(result)
109                         throw = lambda : __throw(IOError(result, errmsg))
110                     else:
111                         errmsg = "unexpected exception in child"
112                 else:
113                     result = -1
114                     errmsg = "child killed"
115                 break
116             except OSError, ex:
117                 if ex.errno != errno.EINTR:
118                     os.close(recvsock)
119                     raise ex
120         fcntl.fcntl(recvsock, fcntl.F_SETFL, os.O_NONBLOCK)
121         try:
122             (fd, errmsg) = passfdimpl.recvmsg(recvsock)
123         except OSError, ex:
124             if ex.errno != errno.EAGAIN:
125                 throw = lambda : __throw(ex)
126             fd = 0
127         os.close(recvsock)
128         if not fd:
129             throw()
130
131         return os.fdopen(fd)
132
133     def enter(self):
134
135         state_file = open("/var/run/vservers/%s.ctx" % self.name, "w")
136         self.__do_chroot()
137         vserverimpl.chcontext(self.ctx, self.remove_caps)
138         print >>state_file, "S_CONTEXT=%d" % self.ctx
139         print >>state_file, "S_PROFILE=%s" % self.config.get("S_PROFILE", "")
140         state_file.close()