ia64/xen-unstable

view tools/python/xen/xend/server/SrvDaemon.py @ 14923:3d613faed8c3

xend: Settle on 0666 pre-umask permissions for xend-debug.log.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Tue Apr 24 23:40:40 2007 +0100 (2007-04-24)
parents 58e98e803378
children dea0ba4e0de1
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
42 self.traceLock = threading.Lock()
45 def cleanup_xend(self, kill):
46 """Clean up the Xend pidfile.
47 If a running process is found, kills it if 'kill' is true.
49 @param kill: whether to kill the process
50 @return running process id or 0
51 """
52 running = 0
53 pid = read_pid(XEND_PID_FILE)
54 if find_process(pid, XEND_PROCESS_NAME):
55 if kill:
56 os.kill(pid, signal.SIGTERM)
57 else:
58 running = pid
59 if running == 0 and os.path.isfile(XEND_PID_FILE):
60 os.remove(XEND_PID_FILE)
61 return running
64 def reloadConfig(self):
65 """
66 """
67 pid = read_pid(XEND_PID_FILE)
68 if find_process(pid, XEND_PROCESS_NAME):
69 os.kill(pid, signal.SIGHUP)
72 def status(self):
73 """Returns the status of the xend daemon.
74 The return value is defined by the LSB:
75 0 Running
76 3 Not running
77 """
78 if self.cleanup_xend(False) == 0:
79 return 3
80 else:
81 return 0
84 def fork_pid(self):
85 """Fork and write the pid of the child to XEND_PID_FILE.
87 @return: pid of child in parent, 0 in child
88 """
90 self.child = os.fork()
92 if self.child:
93 # Parent
94 pidfile = open(XEND_PID_FILE, 'w')
95 try:
96 pidfile.write(str(self.child))
97 finally:
98 pidfile.close()
100 return self.child
103 def daemonize(self):
104 # Detach from TTY.
106 # Become the group leader (already a child process)
107 os.setsid()
109 # Fork, this allows the group leader to exit,
110 # which means the child can never again regain control of the
111 # terminal
112 if os.fork():
113 os._exit(0)
115 # Detach from standard file descriptors, and redirect them to
116 # /dev/null or the log as appropriate.
117 # We open the log file first, so that we can diagnose a failure to do
118 # so _before_ we close stderr.
119 try:
120 parent = os.path.dirname(XEND_DEBUG_LOG)
121 mkdir.parents(parent, stat.S_IRWXU)
122 fd = os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT|os.O_APPEND, 0666)
123 except Exception, exn:
124 print >>sys.stderr, exn
125 print >>sys.stderr, ("Xend failed to open %s. Exiting!" %
126 XEND_DEBUG_LOG)
127 sys.exit(1)
129 os.close(0)
130 os.close(1)
131 os.close(2)
132 if XEND_DEBUG:
133 os.open('/dev/null', os.O_RDONLY)
134 os.dup(fd)
135 os.dup(fd)
136 else:
137 os.open('/dev/null', os.O_RDWR)
138 os.dup(0)
139 os.dup(fd)
140 os.close(fd)
142 print >>sys.stderr, ("Xend started at %s." %
143 time.asctime(time.localtime()))
146 def start(self, trace=0):
147 """Attempts to start the daemons.
148 The return value is defined by the LSB:
149 0 Success
150 4 Insufficient privileges
151 """
152 xend_pid = self.cleanup_xend(False)
154 if self.set_user():
155 return 4
156 os.chdir("/")
158 if xend_pid > 0:
159 # Trying to run an already-running service is a success.
160 return 0
162 ret = 0
164 # If we're not going to create a daemon, simply
165 # call the run method right here.
166 if not XEND_DAEMONIZE:
167 self.tracing(trace)
168 self.run(None)
169 return ret
171 # we use a pipe to communicate between the parent and the child process
172 # this way we know when the child has actually initialized itself so
173 # we can avoid a race condition during startup
175 r,w = os.pipe()
176 if os.fork():
177 os.close(w)
178 r = os.fdopen(r, 'r')
179 try:
180 s = r.read()
181 finally:
182 r.close()
183 if not len(s):
184 ret = 1
185 else:
186 ret = int(s)
187 else:
188 os.close(r)
189 # Child
190 self.daemonize()
191 self.tracing(trace)
193 # If Xend proper segfaults, then we want to restart it. Thus,
194 # we fork a child for running Xend itself, and if it segfaults
195 # (or exits any way other than cleanly) then we run it again.
196 # The first time through we want the server to write to the (r,w)
197 # pipe created above, so that we do not exit until the server is
198 # ready to receive requests. All subsequent restarts we don't
199 # want this behaviour, or the pipe will eventually fill up, so
200 # we just pass None into run in subsequent cases (by clearing w
201 # in the parent of the first fork). On some operating systems,
202 # restart is managed externally, so we won't fork, and just exit.
203 while True:
205 if not osdep.xend_autorestart:
206 self.run(os.fdopen(w, 'w'))
207 os._exit(0)
209 pid = self.fork_pid()
210 if pid:
211 if w is not None:
212 os.close(w)
213 w = None
215 (_, status) = os.waitpid(pid, 0)
217 if os.WIFEXITED(status):
218 code = os.WEXITSTATUS(status)
219 log.info('Xend exited with status %d.', code)
220 sys.exit(code)
222 if os.WIFSIGNALED(status):
223 sig = os.WTERMSIG(status)
225 if sig in (signal.SIGINT, signal.SIGTERM):
226 log.info('Xend stopped due to signal %d.', sig)
227 sys.exit(0)
228 else:
229 log.fatal(
230 'Xend died due to signal %d! Restarting it.',
231 sig)
232 else:
233 self.run(w and os.fdopen(w, 'w') or None)
234 # if we reach here, the child should quit.
235 os._exit(0)
237 return ret
239 def tracing(self, traceon):
240 """Turn tracing on or off.
242 @param traceon: tracing flag
243 """
244 if traceon == self.traceon:
245 return
246 self.traceon = traceon
247 if traceon:
248 self.tracefile = open(XEND_TRACE_FILE, 'w+', 1)
249 self.traceindent = 0
250 sys.settrace(self.trace)
251 try:
252 threading.settrace(self.trace) # Only in Python >= 2.3
253 except:
254 pass
256 def print_trace(self, string):
257 self.tracefile.write("%s: "% threading.currentThread().getName())
258 for i in range(self.traceindent):
259 ch = " "
260 if (i % 5):
261 ch = ' '
262 else:
263 ch = '|'
264 self.tracefile.write(ch)
265 self.tracefile.write(string)
267 def trace(self, frame, event, arg):
268 self.traceLock.acquire()
269 try:
270 if not self.traceon:
271 print >>self.tracefile
272 print >>self.tracefile, '-' * 20, 'TRACE OFF', '-' * 20
273 self.tracefile.close()
274 self.tracefile = None
275 return None
276 if event == 'call':
277 code = frame.f_code
278 filename = code.co_filename
279 m = re.search('.*xend/(.*)', filename)
280 if not m:
281 return None
282 modulename = m.group(1)
283 if modulename.endswith('.pyc'):
284 modulename = modulename[:-1]
285 if modulename == 'sxp.py' or \
286 modulename == 'XendLogging.py' or \
287 modulename == 'XendMonitor.py' or \
288 modulename == 'server/SrvServer.py':
289 return None
290 self.traceindent += 1
291 self.print_trace("> %s:%s\n"
292 % (modulename, code.co_name))
293 elif event == 'line':
294 filename = frame.f_code.co_filename
295 lineno = frame.f_lineno
296 self.print_trace("%4d %s" %
297 (lineno, linecache.getline(filename, lineno)))
298 elif event == 'return':
299 code = frame.f_code
300 filename = code.co_filename
301 m = re.search('.*xend/(.*)', filename)
302 if not m:
303 return None
304 modulename = m.group(1)
305 self.print_trace("< %s:%s\n"
306 % (modulename, code.co_name))
307 self.traceindent -= 1
308 elif event == 'exception':
309 self.print_trace("! Exception:\n")
310 (ex, val, tb) = arg
311 traceback.print_exception(ex, val, tb, 10, self.tracefile)
312 #del tb
313 return self.trace
314 finally:
315 self.traceLock.release()
317 def set_user(self):
318 # Set the UID.
319 try:
320 os.setuid(pwd.getpwnam(XEND_USER)[2])
321 return 0
322 except KeyError:
323 print >>sys.stderr, "Error: no such user '%s'" % XEND_USER
324 return 1
326 def stop(self):
327 return self.cleanup_xend(True)
329 def run(self, status):
330 try:
331 log.info("Xend Daemon started")
333 xc = xen.lowlevel.xc.xc()
334 xinfo = xc.xeninfo()
335 log.info("Xend changeset: %s.", xinfo['xen_changeset'])
336 del xc
338 try:
339 from xen import VERSION
340 log.info("Xend version: %s", VERSION)
341 except ImportError:
342 log.info("Xend version: Unknown.")
344 relocate.listenRelocation()
345 servers = SrvServer.create()
346 servers.start(status)
347 del servers
349 except Exception, ex:
350 print >>sys.stderr, 'Exception starting xend:', ex
351 if XEND_DEBUG:
352 traceback.print_exc()
353 log.exception("Exception starting xend (%s)" % ex)
354 if status:
355 status.write('1')
356 status.close()
357 sys.exit(1)
359 def instance():
360 global inst
361 try:
362 inst
363 except:
364 inst = Daemon()
365 return inst
368 def read_pid(pidfile):
369 """Read process id from a file.
371 @param pidfile: file to read
372 @return pid or 0
373 """
374 if os.path.isfile(pidfile) and os.path.getsize(pidfile):
375 try:
376 f = open(pidfile, 'r')
377 try:
378 return int(f.read())
379 finally:
380 f.close()
381 except:
382 return 0
383 else:
384 return 0
387 def find_process(pid, name):
388 """Search for a process.
390 @param pid: process id
391 @param name: process name
392 @return: pid if found, 0 otherwise
393 """
394 running = 0
395 if pid:
396 lines = os.popen('ps %d 2>/dev/null' % pid).readlines()
397 exp = '^ *%d.+%s' % (pid, name)
398 for line in lines:
399 if re.search(exp, line):
400 running = pid
401 break
402 return running
405 def main(argv = None):
406 global XEND_DAEMONIZE
408 XEND_DAEMONIZE = False
409 if argv is None:
410 argv = sys.argv
412 try:
413 daemon = instance()
415 r,w = os.pipe()
416 daemon.run(os.fdopen(w, 'w'))
417 return 0
418 except Exception, exn:
419 log.fatal(exn)
420 return 1
423 if __name__ == "__main__":
424 sys.exit(main())