ia64/xen-unstable

view tools/python/xen/xend/server/SrvDaemon.py @ 12786:59f438d2739b

Added rudimentary "xend reload" functionality. This allows you to reconfigure
the services offered by Xend, without restarting the daemon itself.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Mon Dec 04 13:37:53 2006 +0000 (2006-12-04)
parents 36fe7ca48e54
children 258722281202
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 os.path
10 import signal
11 import stat
12 import sys
13 import threading
14 import time
15 import linecache
16 import pwd
17 import re
18 import traceback
20 import xen.lowlevel.xc
22 from xen.xend.XendLogging import log
23 from xen.xend import osdep
24 from xen.util import mkdir
26 import relocate
27 import SrvServer
28 from params import *
31 XEND_PROCESS_NAME = 'xend'
34 class Daemon:
35 """The xend daemon.
36 """
37 def __init__(self):
38 self.traceon = False
39 self.tracefile = None
40 self.traceindent = 0
41 self.child = 0
44 def cleanup_xend(self, kill):
45 """Clean up the Xend pidfile.
46 If a running process is found, kills it if 'kill' is true.
48 @param kill: whether to kill the process
49 @return running process id or 0
50 """
51 running = 0
52 pid = read_pid(XEND_PID_FILE)
53 if find_process(pid, XEND_PROCESS_NAME):
54 if kill:
55 os.kill(pid, signal.SIGTERM)
56 else:
57 running = pid
58 if running == 0 and os.path.isfile(XEND_PID_FILE):
59 os.remove(XEND_PID_FILE)
60 return running
63 def reloadConfig(self):
64 """
65 """
66 pid = read_pid(XEND_PID_FILE)
67 if find_process(pid, XEND_PROCESS_NAME):
68 os.kill(pid, signal.SIGHUP)
71 def status(self):
72 """Returns the status of the xend daemon.
73 The return value is defined by the LSB:
74 0 Running
75 3 Not running
76 """
77 if self.cleanup_xend(False) == 0:
78 return 3
79 else:
80 return 0
83 def fork_pid(self):
84 """Fork and write the pid of the child to XEND_PID_FILE.
86 @return: pid of child in parent, 0 in child
87 """
89 self.child = os.fork()
91 if self.child:
92 # Parent
93 pidfile = open(XEND_PID_FILE, 'w')
94 try:
95 pidfile.write(str(self.child))
96 finally:
97 pidfile.close()
99 return self.child
102 def daemonize(self):
103 # Detach from TTY.
105 # Become the group leader (already a child process)
106 os.setsid()
108 # Fork, this allows the group leader to exit,
109 # which means the child can never again regain control of the
110 # terminal
111 if os.fork():
112 os._exit(0)
114 # Detach from standard file descriptors, and redirect them to
115 # /dev/null or the log as appropriate.
116 # We open the log file first, so that we can diagnose a failure to do
117 # so _before_ we close stderr.
118 try:
119 parent = os.path.dirname(XEND_DEBUG_LOG)
120 mkdir.parents(parent, stat.S_IRWXU)
121 fd = os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT|os.O_APPEND)
122 except Exception, exn:
123 print >>sys.stderr, exn
124 print >>sys.stderr, ("Xend failed to open %s. Exiting!" %
125 XEND_DEBUG_LOG)
126 sys.exit(1)
128 os.close(0)
129 os.close(1)
130 os.close(2)
131 if XEND_DEBUG:
132 os.open('/dev/null', os.O_RDONLY)
133 os.dup(fd)
134 os.dup(fd)
135 else:
136 os.open('/dev/null', os.O_RDWR)
137 os.dup(0)
138 os.dup(fd)
139 os.close(fd)
141 print >>sys.stderr, ("Xend started at %s." %
142 time.asctime(time.localtime()))
145 def start(self, trace=0):
146 """Attempts to start the daemons.
147 The return value is defined by the LSB:
148 0 Success
149 4 Insufficient privileges
150 """
151 xend_pid = self.cleanup_xend(False)
153 if self.set_user():
154 return 4
155 os.chdir("/")
157 if xend_pid > 0:
158 # Trying to run an already-running service is a success.
159 return 0
161 ret = 0
163 # If we're not going to create a daemon, simply
164 # call the run method right here.
165 if not XEND_DAEMONIZE:
166 self.tracing(trace)
167 self.run(None)
168 return ret
170 # we use a pipe to communicate between the parent and the child process
171 # this way we know when the child has actually initialized itself so
172 # we can avoid a race condition during startup
174 r,w = os.pipe()
175 if os.fork():
176 os.close(w)
177 r = os.fdopen(r, 'r')
178 try:
179 s = r.read()
180 finally:
181 r.close()
182 if not len(s):
183 ret = 1
184 else:
185 ret = int(s)
186 else:
187 os.close(r)
188 # Child
189 self.daemonize()
190 self.tracing(trace)
192 # If Xend proper segfaults, then we want to restart it. Thus,
193 # we fork a child for running Xend itself, and if it segfaults
194 # (or exits any way other than cleanly) then we run it again.
195 # The first time through we want the server to write to the (r,w)
196 # pipe created above, so that we do not exit until the server is
197 # ready to receive requests. All subsequent restarts we don't
198 # want this behaviour, or the pipe will eventually fill up, so
199 # we just pass None into run in subsequent cases (by clearing w
200 # in the parent of the first fork). On some operating systems,
201 # restart is managed externally, so we won't fork, and just exit.
202 while True:
204 if not osdep.xend_autorestart:
205 self.run(os.fdopen(w, 'w'))
206 break
208 pid = self.fork_pid()
209 if pid:
210 if w is not None:
211 os.close(w)
212 w = None
214 (_, status) = os.waitpid(pid, 0)
216 if os.WIFEXITED(status):
217 code = os.WEXITSTATUS(status)
218 log.info('Xend exited with status %d.', code)
219 sys.exit(code)
221 if os.WIFSIGNALED(status):
222 sig = os.WTERMSIG(status)
224 if sig in (signal.SIGINT, signal.SIGTERM):
225 log.info('Xend stopped due to signal %d.', sig)
226 sys.exit(0)
227 else:
228 log.fatal(
229 'Xend died due to signal %d! Restarting it.',
230 sig)
231 else:
232 self.run(w and os.fdopen(w, 'w') or None)
233 # if we reach here, the child should quit.
234 os._exit(0)
236 return ret
238 def tracing(self, traceon):
239 """Turn tracing on or off.
241 @param traceon: tracing flag
242 """
243 if traceon == self.traceon:
244 return
245 self.traceon = traceon
246 if traceon:
247 self.tracefile = open(XEND_TRACE_FILE, 'w+', 1)
248 self.traceindent = 0
249 sys.settrace(self.trace)
250 try:
251 threading.settrace(self.trace) # Only in Python >= 2.3
252 except:
253 pass
255 def print_trace(self, string):
256 for i in range(self.traceindent):
257 ch = " "
258 if (i % 5):
259 ch = ' '
260 else:
261 ch = '|'
262 self.tracefile.write(ch)
263 self.tracefile.write(string)
265 def trace(self, frame, event, arg):
266 if not self.traceon:
267 print >>self.tracefile
268 print >>self.tracefile, '-' * 20, 'TRACE OFF', '-' * 20
269 self.tracefile.close()
270 self.tracefile = None
271 return None
272 if event == 'call':
273 code = frame.f_code
274 filename = code.co_filename
275 m = re.search('.*xend/(.*)', filename)
276 if not m:
277 return None
278 modulename = m.group(1)
279 if re.search('sxp.py', modulename):
280 return None
281 self.traceindent += 1
282 self.print_trace("> %s:%s\n"
283 % (modulename, code.co_name))
284 elif event == 'line':
285 filename = frame.f_code.co_filename
286 lineno = frame.f_lineno
287 self.print_trace("%4d %s" %
288 (lineno, linecache.getline(filename, lineno)))
289 elif event == 'return':
290 code = frame.f_code
291 filename = code.co_filename
292 m = re.search('.*xend/(.*)', filename)
293 if not m:
294 return None
295 modulename = m.group(1)
296 self.print_trace("< %s:%s\n"
297 % (modulename, code.co_name))
298 self.traceindent -= 1
299 elif event == 'exception':
300 self.print_trace("! Exception:\n")
301 (ex, val, tb) = arg
302 traceback.print_exception(ex, val, tb, 10, self.tracefile)
303 #del tb
304 return self.trace
306 def set_user(self):
307 # Set the UID.
308 try:
309 os.setuid(pwd.getpwnam(XEND_USER)[2])
310 return 0
311 except KeyError:
312 print >>sys.stderr, "Error: no such user '%s'" % XEND_USER
313 return 1
315 def stop(self):
316 return self.cleanup_xend(True)
318 def run(self, status):
319 try:
320 log.info("Xend Daemon started")
322 xc = xen.lowlevel.xc.xc()
323 xinfo = xc.xeninfo()
324 log.info("Xend changeset: %s.", xinfo['xen_changeset'])
325 del xc
327 try:
328 from xen import VERSION
329 log.info("Xend version: %s", VERSION)
330 except ImportError:
331 log.info("Xend version: Unknown.")
333 relocate.listenRelocation()
334 servers = SrvServer.create()
335 servers.start(status)
336 del servers
338 except Exception, ex:
339 print >>sys.stderr, 'Exception starting xend:', ex
340 if XEND_DEBUG:
341 traceback.print_exc()
342 log.exception("Exception starting xend (%s)" % ex)
343 if status:
344 status.write('1')
345 status.close()
346 sys.exit(1)
348 def instance():
349 global inst
350 try:
351 inst
352 except:
353 inst = Daemon()
354 return inst
357 def read_pid(pidfile):
358 """Read process id from a file.
360 @param pidfile: file to read
361 @return pid or 0
362 """
363 if os.path.isfile(pidfile) and os.path.getsize(pidfile):
364 try:
365 f = open(pidfile, 'r')
366 try:
367 return int(f.read())
368 finally:
369 f.close()
370 except:
371 return 0
372 else:
373 return 0
376 def find_process(pid, name):
377 """Search for a process.
379 @param pid: process id
380 @param name: process name
381 @return: pid if found, 0 otherwise
382 """
383 running = 0
384 if pid:
385 lines = os.popen('ps %d 2>/dev/null' % pid).readlines()
386 exp = '^ *%d.+%s' % (pid, name)
387 for line in lines:
388 if re.search(exp, line):
389 running = pid
390 break
391 return running
394 def main(argv = None):
395 global XEND_DAEMONIZE
397 XEND_DAEMONIZE = False
398 if argv is None:
399 argv = sys.argv
401 try:
402 daemon = instance()
404 r,w = os.pipe()
405 daemon.run(os.fdopen(w, 'w'))
406 return 0
407 except Exception, exn:
408 log.fatal(exn)
409 return 1
412 if __name__ == "__main__":
413 sys.exit(main())