ia64/xen-unstable

view tools/python/xen/xend/server/SrvDaemon.py @ 12076:5fe8e9ebcf5c

[XEND] Make XMLRPC Server shutdown cleanly.

We can now catch the end of Xend and clean up domains accordingly.

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Thu Oct 05 17:29:19 2006 +0100 (2006-10-05)
parents 00822a3a57d9
children 397cc120ae18
line source
1 ###########################################################
2 ## Xen controller daemon
3 ## Copyright (c) 2004, K A Fraser (University of Cambridge)
4 ## Copyright (C) 2004, Mike Wray <mike.wray@hp.com>
5 ## Copyright (C) 2005, XenSource Ltd
6 ###########################################################
8 import os
9 import signal
10 import sys
11 import threading
12 import linecache
13 import pwd
14 import re
15 import traceback
17 import xen.lowlevel.xc
19 from xen.xend.XendLogging import log
21 import relocate
22 import SrvServer
23 from params import *
26 XEND_PROCESS_NAME = 'xend'
29 class Daemon:
30 """The xend daemon.
31 """
32 def __init__(self):
33 self.traceon = False
34 self.tracefile = None
35 self.traceindent = 0
36 self.child = 0
39 def cleanup_xend(self, kill):
40 """Clean up the Xend pidfile.
41 If a running process is found, kills it if 'kill' is true.
43 @param kill: whether to kill the process
44 @return running process id or 0
45 """
46 running = 0
47 pid = read_pid(XEND_PID_FILE)
48 if find_process(pid, XEND_PROCESS_NAME):
49 if kill:
50 os.kill(pid, signal.SIGTERM)
51 else:
52 running = pid
53 if running == 0 and os.path.isfile(XEND_PID_FILE):
54 os.remove(XEND_PID_FILE)
55 return running
58 def status(self):
59 """Returns the status of the xend daemon.
60 The return value is defined by the LSB:
61 0 Running
62 3 Not running
63 """
64 if self.cleanup_xend(False) == 0:
65 return 3
66 else:
67 return 0
70 def fork_pid(self):
71 """Fork and write the pid of the child to XEND_PID_FILE.
73 @return: pid of child in parent, 0 in child
74 """
76 self.child = os.fork()
78 if self.child:
79 # Parent
80 pidfile = open(XEND_PID_FILE, 'w')
81 try:
82 pidfile.write(str(self.child))
83 finally:
84 pidfile.close()
86 return self.child
89 def daemonize(self):
90 # Detach from TTY.
92 # Become the group leader (already a child process)
93 os.setsid()
95 # Fork, this allows the group leader to exit,
96 # which means the child can never again regain control of the
97 # terminal
98 if os.fork():
99 os._exit(0)
101 # Detach from standard file descriptors, and redirect them to
102 # /dev/null or the log as appropriate.
103 os.close(0)
104 os.close(1)
105 os.close(2)
106 if XEND_DEBUG:
107 os.open('/dev/null', os.O_RDONLY)
108 os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT)
109 os.dup(1)
110 else:
111 os.open('/dev/null', os.O_RDWR)
112 os.dup(0)
113 os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT)
116 def start(self, trace=0):
117 """Attempts to start the daemons.
118 The return value is defined by the LSB:
119 0 Success
120 4 Insufficient privileges
121 """
122 xend_pid = self.cleanup_xend(False)
124 if self.set_user():
125 return 4
126 os.chdir("/")
128 if xend_pid > 0:
129 # Trying to run an already-running service is a success.
130 return 0
132 ret = 0
134 # If we're not going to create a daemon, simply
135 # call the run method right here.
136 if not XEND_DAEMONIZE:
137 self.tracing(trace)
138 self.run(None)
139 return ret
141 # we use a pipe to communicate between the parent and the child process
142 # this way we know when the child has actually initialized itself so
143 # we can avoid a race condition during startup
145 r,w = os.pipe()
146 if os.fork():
147 os.close(w)
148 r = os.fdopen(r, 'r')
149 try:
150 s = r.read()
151 finally:
152 r.close()
153 if not len(s):
154 ret = 1
155 else:
156 ret = int(s)
157 else:
158 os.close(r)
159 # Child
160 self.daemonize()
161 self.tracing(trace)
163 # If Xend proper segfaults, then we want to restart it. Thus,
164 # we fork a child for running Xend itself, and if it segfaults
165 # (or exits any way other than cleanly) then we run it again.
166 # The first time through we want the server to write to the (r,w)
167 # pipe created above, so that we do not exit until the server is
168 # ready to receive requests. All subsequent restarts we don't
169 # want this behaviour, or the pipe will eventually fill up, so
170 # we just pass None into run in subsequent cases (by clearing w
171 # in the parent of the first fork).
172 while True:
173 pid = self.fork_pid()
174 if pid:
175 if w is not None:
176 os.close(w)
177 w = None
179 (_, status) = os.waitpid(pid, 0)
181 if os.WIFEXITED(status):
182 code = os.WEXITSTATUS(status)
183 log.info('Xend exited with status %d.', code)
184 sys.exit(code)
186 if os.WIFSIGNALED(status):
187 sig = os.WTERMSIG(status)
189 if sig in (signal.SIGINT, signal.SIGTERM):
190 log.info('Xend stopped due to signal %d.', sig)
191 sys.exit(0)
192 else:
193 log.fatal(
194 'Xend died due to signal %d! Restarting it.',
195 sig)
196 else:
197 self.run(w and os.fdopen(w, 'w') or None)
198 # if we reach here, the child should quit.
199 os._exit(0)
201 return ret
203 def tracing(self, traceon):
204 """Turn tracing on or off.
206 @param traceon: tracing flag
207 """
208 if traceon == self.traceon:
209 return
210 self.traceon = traceon
211 if traceon:
212 self.tracefile = open(XEND_TRACE_FILE, 'w+', 1)
213 self.traceindent = 0
214 sys.settrace(self.trace)
215 try:
216 threading.settrace(self.trace) # Only in Python >= 2.3
217 except:
218 pass
220 def print_trace(self, string):
221 for i in range(self.traceindent):
222 ch = " "
223 if (i % 5):
224 ch = ' '
225 else:
226 ch = '|'
227 self.tracefile.write(ch)
228 self.tracefile.write(string)
230 def trace(self, frame, event, arg):
231 if not self.traceon:
232 print >>self.tracefile
233 print >>self.tracefile, '-' * 20, 'TRACE OFF', '-' * 20
234 self.tracefile.close()
235 self.tracefile = None
236 return None
237 if event == 'call':
238 code = frame.f_code
239 filename = code.co_filename
240 m = re.search('.*xend/(.*)', filename)
241 if not m:
242 return None
243 modulename = m.group(1)
244 if re.search('sxp.py', modulename):
245 return None
246 self.traceindent += 1
247 self.print_trace("> %s:%s\n"
248 % (modulename, code.co_name))
249 elif event == 'line':
250 filename = frame.f_code.co_filename
251 lineno = frame.f_lineno
252 self.print_trace("%4d %s" %
253 (lineno, linecache.getline(filename, lineno)))
254 elif event == 'return':
255 code = frame.f_code
256 filename = code.co_filename
257 m = re.search('.*xend/(.*)', filename)
258 if not m:
259 return None
260 modulename = m.group(1)
261 self.print_trace("< %s:%s\n"
262 % (modulename, code.co_name))
263 self.traceindent -= 1
264 elif event == 'exception':
265 self.print_trace("! Exception:\n")
266 (ex, val, tb) = arg
267 traceback.print_exception(ex, val, tb, 10, self.tracefile)
268 #del tb
269 return self.trace
271 def set_user(self):
272 # Set the UID.
273 try:
274 os.setuid(pwd.getpwnam(XEND_USER)[2])
275 return 0
276 except KeyError:
277 print >>sys.stderr, "Error: no such user '%s'" % XEND_USER
278 return 1
280 def stop(self):
281 return self.cleanup_xend(True)
283 def run(self, status):
284 try:
285 log.info("Xend Daemon started")
287 xc = xen.lowlevel.xc.xc()
288 xinfo = xc.xeninfo()
289 log.info("Xend changeset: %s.", xinfo['xen_changeset'])
290 del xc
292 relocate.listenRelocation()
293 servers = SrvServer.create()
294 servers.start(status)
295 del servers
297 except Exception, ex:
298 print >>sys.stderr, 'Exception starting xend:', ex
299 if XEND_DEBUG:
300 traceback.print_exc()
301 log.exception("Exception starting xend (%s)" % ex)
302 if status:
303 status.write('1')
304 status.close()
305 sys.exit(1)
307 def instance():
308 global inst
309 try:
310 inst
311 except:
312 inst = Daemon()
313 return inst
316 def read_pid(pidfile):
317 """Read process id from a file.
319 @param pidfile: file to read
320 @return pid or 0
321 """
322 if os.path.isfile(pidfile) and os.path.getsize(pidfile):
323 try:
324 f = open(pidfile, 'r')
325 try:
326 return int(f.read())
327 finally:
328 f.close()
329 except:
330 return 0
331 else:
332 return 0
335 def find_process(pid, name):
336 """Search for a process.
338 @param pid: process id
339 @param name: process name
340 @return: pid if found, 0 otherwise
341 """
342 running = 0
343 if pid:
344 lines = os.popen('ps %d 2>/dev/null' % pid).readlines()
345 exp = '^ *%d.+%s' % (pid, name)
346 for line in lines:
347 if re.search(exp, line):
348 running = pid
349 break
350 return running
353 def main(argv = None):
354 global XEND_DAEMONIZE
356 XEND_DAEMONIZE = False
357 if argv is None:
358 argv = sys.argv
360 try:
361 daemon = instance()
363 r,w = os.pipe()
364 daemon.run(os.fdopen(w, 'w'))
365 return 0
366 except Exception, exn:
367 log.fatal(exn)
368 return 1
371 if __name__ == "__main__":
372 sys.exit(main())