ia64/xen-unstable

view tools/xend/lib/main.py @ 1460:d403dd1a7801

bitkeeper revision 1.955.1.1 (40c9cf3fKColt7CnfYsnIOunlJfkKA)

Tweak.
author maw48@labyrinth.cl.cam.ac.uk
date Fri Jun 11 15:26:55 2004 +0000 (2004-06-11)
parents 3ee528275c75
children 2c6f86e0083e
line source
2 ###########################################################
3 ## xend.py -- Xen controller daemon
4 ## Copyright (c) 2004, K A Fraser (University of Cambridge)
5 ###########################################################
7 import errno, re, os, pwd, select, signal, socket, struct, sys, time
8 import xend.blkif, xend.netif, xend.console, xend.manager, xend.utils, Xc
11 # The following parameters could be placed in a configuration file.
12 PID = '/var/run/xend.pid'
13 LOG = '/var/log/xend.log'
14 USER = 'root'
15 CONTROL_DIR = '/var/run/xend'
16 UNIX_SOCK = 'management_sock' # relative to CONTROL_DIR
19 CMSG_CONSOLE = 0
20 CMSG_BLKIF_BE = 1
21 CMSG_BLKIF_FE = 2
22 CMSG_NETIF_BE = 3
23 CMSG_NETIF_FE = 4
26 def port_from_dom(dom):
27 global port_list
28 for idx, port in port_list.items():
29 if port.remote_dom == dom:
30 return port
31 return None
34 def send_management_response(response, addr):
35 try:
36 response = str(response)
37 print "Mgmt_rsp[%s]: %s" % (addr, response)
38 management_interface.sendto(response, addr)
39 except socket.error, error:
40 pass
43 def daemon_loop():
44 # Could we do this more nicely? The xend.manager functions need access
45 # to this global state to do their work.
46 global port_list, notifier, management_interface, mgmt_req_addr, dom0_port
48 # Lists of all interfaces, indexed by local event-channel port.
49 port_list = {}
51 xc = Xc.new()
53 # Ignore writes to disconnected sockets. We clean up differently.
54 signal.signal(signal.SIGPIPE, signal.SIG_IGN)
56 # Construct the management interface. This is a UNIX domain socket via
57 # which we receive 'request' datagrams. Each request is a string that
58 # can be eval'ed as a Python statement. Responses can be remotely eval'ed
59 # by the requester to create a Python dictionary of result values.
60 management_interface = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
61 if os.path.exists(CONTROL_DIR+'/'+UNIX_SOCK):
62 os.unlink(CONTROL_DIR+'/'+UNIX_SOCK)
63 management_interface.setblocking(False)
64 management_interface.bind(CONTROL_DIR+'/'+UNIX_SOCK)
66 # Interface via which we receive event notifications from other guest
67 # OSes. This interface also allows us to clear/acknowledge outstanding
68 # notifications.
69 notifier = xend.utils.notifier()
71 # The DOM0 control interface is not set up via the management interface.
72 # Note that console messages don't come our way (actually, only driver
73 # back-ends should use the DOM0 control interface).
74 dom0_port = xend.utils.port(0)
75 xend.netif.be_port = dom0_port
76 xend.blkif.be_port = dom0_port
77 notifier.bind(dom0_port.local_port)
78 port_list[dom0_port.local_port] = dom0_port
80 ##
81 ## MAIN LOOP
82 ##
83 while 1:
85 # Construct a poll set. We wait on:
86 # 1. Requests on the management interface.
87 # 2. Incoming event-channel notifications.
88 # Furthermore, for each active control interface:
89 # 3. Incoming console data.
90 # 4. Space for outgoing console data (if there is data to send).
91 waitset = select.poll()
92 waitset.register(management_interface, select.POLLIN)
93 waitset.register(notifier, select.POLLIN)
94 for idx, con_if in xend.console.interface.list_by_fd.items():
95 if not con_if.closed():
96 pflags = select.POLLIN
97 if not con_if.rbuf.empty() and con_if.connected():
98 pflags = select.POLLIN | select.POLLOUT
99 waitset.register(con_if.sock.fileno(), pflags)
101 # Wait for something to do...
102 fdset = waitset.poll()
104 # Look for messages on the management interface.
105 # These should consist of executable Python statements that call
106 # well-known management functions (e.g., new_control_interface(dom=9)).
107 try:
108 data, mgmt_req_addr = management_interface.recvfrom(2048)
109 except socket.error, error:
110 if error[0] != errno.EAGAIN:
111 raise
112 else:
113 if mgmt_req_addr:
114 # Evaluate the request in an exception-trapping sandbox.
115 try:
116 print "Mgmt_req[%s]: %s" % (mgmt_req_addr, data)
117 response = eval('xend.manager.'+data)
119 except:
120 # Catch all exceptions and turn into an error response:
121 # status: False
122 # error_type: 'exception'
123 # exception_type: name of exception type.
124 # exception value: textual exception value.
125 exc_type, exc_val = sys.exc_info()[:2]
126 response = { 'success': False }
127 response['error_type'] = 'exception'
128 response['exception_type'] = str(exc_type)
129 response['exception_value'] = str(exc_val)
130 response = str(response)
132 # Try to send a response to the requester.
133 if response:
134 send_management_response(response, mgmt_req_addr)
136 # Do work for every console interface that hit in the poll set.
137 for (fd, events) in fdset:
138 if xend.console.interface.list_by_fd.has_key(fd):
139 con_if = xend.console.interface.list_by_fd[fd]
140 con_if.socket_work()
141 # We may now have pending data to send via the control
142 # interface. If so then send all we can and notify the remote.
143 port = port_list[con_if.key]
144 if con_if.ctrlif_transmit_work(port):
145 port.notify()
147 # Process control-interface notifications from other guest OSes.
148 while 1:
149 # Grab a notification, if there is one.
150 notification = notifier.read()
151 if not notification:
152 break
153 (idx, type) = notification
155 if not port_list.has_key(idx):
156 continue
158 port = port_list[idx]
159 work_done = False
161 con_if = False
162 if xend.console.interface.list.has_key(idx):
163 con_if = xend.console.interface.list[idx]
165 blk_if = False
166 if xend.blkif.interface.list.has_key(idx):
167 blk_if = xend.blkif.interface.list[idx]
169 net_if = False
170 if xend.netif.interface.list.has_key(idx):
171 net_if = xend.netif.interface.list[idx]
173 # If we pick up a disconnect notification then we do any necessary
174 # cleanup.
175 if type == notifier.EXCEPTION:
176 ret = xc.evtchn_status(idx)
177 if ret['status'] == 'unbound':
178 notifier.unbind(idx)
179 del port_list[idx], port
180 if con_if:
181 con_if.destroy()
182 del con_if
183 if blk_if:
184 blk_if.destroy()
185 del blk_if
186 if net_if:
187 net_if.destroy()
188 del net_if
189 continue
191 # Process incoming requests.
192 while port.request_to_read():
193 msg = port.read_request()
194 work_done = True
195 type = (msg.get_header())['type']
196 if type == CMSG_CONSOLE and con_if:
197 con_if.ctrlif_rx_req(port, msg)
198 elif type == CMSG_BLKIF_FE and blk_if:
199 blk_if.ctrlif_rx_req(port, msg)
200 elif type == CMSG_BLKIF_BE and port == xend.blkif.be_port:
201 xend.blkif.backend_rx_req(port, msg)
202 elif type == CMSG_NETIF_FE and net_if:
203 net_if.ctrlif_rx_req(port, msg)
204 elif type == CMSG_NETIF_BE and port == xend.netif.be_port:
205 xend.netif.backend_rx_req(port, msg)
206 else:
207 port.write_response(msg)
209 # Process incoming responses.
210 while port.response_to_read():
211 msg = port.read_response()
212 work_done = True
213 type = (msg.get_header())['type']
214 if type == CMSG_BLKIF_BE and port == xend.blkif.be_port:
215 xend.blkif.backend_rx_rsp(port, msg)
216 elif type == CMSG_NETIF_BE and port == xend.netif.be_port:
217 xend.netif.backend_rx_rsp(port, msg)
219 # Send console data.
220 if con_if and con_if.ctrlif_transmit_work(port):
221 work_done = True
223 # Send blkif messages.
224 if blk_if and blk_if.ctrlif_transmit_work(port):
225 work_done = True
227 # Send netif messages.
228 if net_if and net_if.ctrlif_transmit_work(port):
229 work_done = True
231 # Back-end block-device work.
232 if port == dom0_port and xend.blkif.backend_do_work(port):
233 work_done = True
235 # Back-end network-device work.
236 if port == xend.netif.be_port and xend.netif.backend_do_work(port):
237 work_done = True
239 # Finally, notify the remote end of any work that we did.
240 if work_done:
241 port.notify()
243 # Unmask notifications for this port.
244 notifier.unmask(idx)
248 def cleanup_daemon(kill=False):
249 # No cleanup to do if the PID file is empty.
250 if not os.path.isfile(PID) or not os.path.getsize(PID):
251 return 0
252 # Read the PID of the previous invocation and search active process list.
253 pid = open(PID, 'r').read()
254 lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
255 for line in lines:
256 if re.search('^ *' + pid + '.+xend', line):
257 if not kill:
258 print "Daemon is already running (PID %d)" % int(pid)
259 return 1
260 # Old daemon is still active: terminate it.
261 os.kill(int(pid), 1)
262 # Delete the, now stale, PID file.
263 os.remove(PID)
264 return 0
268 def start_daemon():
269 if cleanup_daemon(kill=False):
270 return 1
272 if not os.path.exists(CONTROL_DIR):
273 os.mkdir(CONTROL_DIR)
275 # Open log file. Truncate it if non-empty, and request line buffering.
276 if os.path.isfile(LOG):
277 os.rename(LOG, LOG+'.old')
278 logfile = open(LOG, 'w+', 1)
280 # Detach from TTY.
281 os.setsid()
283 # Set the UID.
284 try:
285 os.setuid(pwd.getpwnam(USER)[2])
286 except KeyError, error:
287 print "Error: no such user '%s'" % USER
288 return 1
290 # Ensure that zombie children are automatically reaped.
291 xend.utils.autoreap()
293 # Fork -- parent writes the PID file and exits.
294 pid = os.fork()
295 if pid:
296 pidfile = open(PID, 'w')
297 pidfile.write(str(pid))
298 pidfile.close()
299 return 0
301 # Close down standard file handles
302 try:
303 os.close(0) # stdin
304 os.close(1) # stdout
305 os.close(2) # stderr
306 except:
307 pass
309 # Redirect output to log file, then enter the main loop.
310 sys.stdout = sys.stderr = logfile
311 daemon_loop()
312 return 0
316 def stop_daemon():
317 return cleanup_daemon(kill=True)