debuggers.hg

view tools/python/xen/xend/server/SrvDaemon.py @ 16754:b64be2bc7a91

xend: Remove hardcoded (and apparently unused) xend version.
Signed-off-by: Jim Fehlig <jfehlig@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jan 15 14:24:05 2008 +0000 (2008-01-15)
parents c857bf38f015
children f81467ad1356
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 relocate.listenRelocation()
339 servers = SrvServer.create()
340 servers.start(status)
341 del servers
343 except Exception, ex:
344 print >>sys.stderr, 'Exception starting xend:', ex
345 if XEND_DEBUG:
346 traceback.print_exc()
347 log.exception("Exception starting xend (%s)" % ex)
348 if status:
349 status.write('1')
350 status.close()
351 sys.exit(1)
353 def instance():
354 global inst
355 try:
356 inst
357 except:
358 inst = Daemon()
359 return inst
362 def read_pid(pidfile):
363 """Read process id from a file.
365 @param pidfile: file to read
366 @return pid or 0
367 """
368 if os.path.isfile(pidfile) and os.path.getsize(pidfile):
369 try:
370 f = open(pidfile, 'r')
371 try:
372 return int(f.read())
373 finally:
374 f.close()
375 except:
376 return 0
377 else:
378 return 0
381 def find_process(pid, name):
382 """Search for a process.
384 @param pid: process id
385 @param name: process name
386 @return: pid if found, 0 otherwise
387 """
388 running = 0
389 if pid:
390 lines = os.popen('ps %d 2>/dev/null' % pid).readlines()
391 exp = '^ *%d.+%s' % (pid, name)
392 for line in lines:
393 if re.search(exp, line):
394 running = pid
395 break
396 return running
399 def main(argv = None):
400 global XEND_DAEMONIZE
402 XEND_DAEMONIZE = False
403 if argv is None:
404 argv = sys.argv
406 try:
407 daemon = instance()
409 r,w = os.pipe()
410 daemon.run(os.fdopen(w, 'w'))
411 return 0
412 except Exception, exn:
413 log.fatal(exn)
414 return 1
417 if __name__ == "__main__":
418 sys.exit(main())