Beginnings of vserver module to support manipulation of vservers without
[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
11 from vserver_vars import *
12
13 CAP_SAFE = (linuxcaps.CAP_CHOWN |
14             linuxcaps.CAP_DAC_OVERRIDE |
15             linuxcaps.CAP_DAC_READ_SEARCH |
16             linuxcaps.CAP_FOWNER |
17             linuxcaps.CAP_FSETID |
18             linuxcaps.CAP_KILL |
19             linuxcaps.CAP_SETGID |
20             linuxcaps.CAP_SETUID |
21             linuxcaps.CAP_SETPCAP |
22             linuxcaps.CAP_SYS_TTY_CONFIG |
23             linuxcaps.CAP_LEASE |
24             linuxcaps.CAP_SYS_CHROOT |
25             linuxcaps.CAP_SYS_PTRACE)
26
27 #
28 # XXX - these are the flags taken from chcontext.c, but they don't match
29 # up with those apparently used by the kernel
30 #
31 FLAGS_LOCK = 1
32 FLAGS_SCHED = 2
33 FLAGS_NPROC = 4
34 FLAGS_PRIVATE = 8
35 FLAGS_FAKEINIT = 16
36 FLAGS_HIDEINFO = 32
37 FLAGS_ULIMIT = 64
38
39
40               
41 class VServer:
42
43     def __init__(self, name):
44
45         self.name = name
46         self.config = self.__read_config_file("/etc/vservers.conf")
47         self.config.update(self.__read_config_file("/etc/vservers/%s.conf" %
48                                                    self.name))
49         self.flags = 0
50         flags = self.config["S_FLAGS"].split(" ")
51         if "lock" in flags:
52             self.flags |= FLAGS_LOCK
53         if "nproc" in flags:
54             self.flags |= FLAGS_NPROC
55         self.remove_caps = ~CAP_SAFE
56         print "%x %x" % (self.flags, ~self.remove_caps)
57
58     config_var_re = re.compile(r"^ *([A-Z_]+)=(.*)\n?$", re.MULTILINE)
59
60     def __read_config_file(self, filename):
61
62         f = open(filename, "r")
63         data = f.read()
64         f.close()
65         config = {}
66         for m in self.config_var_re.finditer(data):
67             (key, val) = m.groups()
68             config[key] = val.strip('"')
69         return config
70
71     def open(self, filename, mode = "r"):
72
73         (sendsock, recvsock) = passfdimpl.socketpair()
74         child_pid = os.fork()
75         if child_pid == 0:
76             try:
77                 # child process
78                 os.chroot("%s/%s" % (VROOTDIR, self.name))
79                 f = open(filename, mode)
80                 passfdimpl.sendmsg(f.fileno(), sendsock)
81                 os._exit(0)
82             except EnvironmentError, ex:
83                 (result, errmsg) = (ex.errno, ex.strerror)
84             except Exception, ex:
85                 (result, errmsg) = (255, str(ex))
86             os.write(sendsock, errmsg)
87             os._exit(result)
88
89         # parent process
90
91         # XXX - need this since a lambda can't raise an exception
92         def __throw(ex):
93             raise ex
94
95         os.close(sendsock)
96         throw = lambda : __throw(Exception(errmsg))
97         while True:
98             try:
99                 (pid, status) = os.waitpid(child_pid, 0)
100                 if os.WIFEXITED(status):
101                     result = os.WEXITSTATUS(status)
102                     if result != 255:
103                         errmsg = os.strerror(result)
104                         throw = lambda : __throw(IOError(result, errmsg))
105                     else:
106                         errmsg = "unexpected exception in child"
107                 else:
108                     result = -1
109                     errmsg = "child killed"
110                 break
111             except OSError, ex:
112                 if ex.errno != errno.EINTR:
113                     os.close(recvsock)
114                     raise ex
115         fcntl.fcntl(recvsock, fcntl.F_SETFL, os.O_NONBLOCK)
116         try:
117             (fd, errmsg) = passfdimpl.recvmsg(recvsock)
118         except OSError, ex:
119             if ex.errno != errno.EAGAIN:
120                 throw = lambda : __throw(ex)
121             fd = 0
122         os.close(recvsock)
123         if not fd:
124             throw()
125
126         return os.fdopen(fd)