ia64/xen-unstable

view tools/python/xen/xend/server/SrvDaemon.py @ 13642:ba3ec84c9423

[XEND] Add missing ConsoleController.py

Signed-off-by: Alastair Tse <atse@xensource.com>
author Alastair Tse <atse@xensource.com>
date Fri Jan 26 02:44:35 2007 +0000 (2007-01-26)
parents 10a6d7eff555
children 1ada28678762
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 os._exit(0)
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 if re.search('SrvServer.py', modulename):
282 return None
283 self.traceindent += 1
284 self.print_trace("> %s:%s\n"
285 % (modulename, code.co_name))
286 elif event == 'line':
287 filename = frame.f_code.co_filename
288 lineno = frame.f_lineno
289 self.print_trace("%4d %s" %
290 (lineno, linecache.getline(filename, lineno)))
291 elif event == 'return':
292 code = frame.f_code
293 filename = code.co_filename
294 m = re.search('.*xend/(.*)', filename)
295 if not m:
296 return None
297 modulename = m.group(1)
298 self.print_trace("< %s:%s\n"
299 % (modulename, code.co_name))
300 self.traceindent -= 1
301 elif event == 'exception':
302 self.print_trace("! Exception:\n")
303 (ex, val, tb) = arg
304 traceback.print_exception(ex, val, tb, 10, self.tracefile)
305 #del tb
306 return self.trace
308 def set_user(self):
309 # Set the UID.
310 try:
311 os.setuid(pwd.getpwnam(XEND_USER)[2])
312 return 0
313 except KeyError:
314 print >>sys.stderr, "Error: no such user '%s'" % XEND_USER
315 return 1
317 def stop(self):
318 return self.cleanup_xend(True)
320 def run(self, status):
321 try:
322 log.info("Xend Daemon started")
324 xc = xen.lowlevel.xc.xc()
325 xinfo = xc.xeninfo()
326 log.info("Xend changeset: %s.", xinfo['xen_changeset'])
327 del xc
329 try:
330 from xen import VERSION
331 log.info("Xend version: %s", VERSION)
332 except ImportError:
333 log.info("Xend version: Unknown.")
335 relocate.listenRelocation()
336 servers = SrvServer.create()
337 servers.start(status)
338 del servers
340 except Exception, ex:
341 print >>sys.stderr, 'Exception starting xend:', ex
342 if XEND_DEBUG:
343 traceback.print_exc()
344 log.exception("Exception starting xend (%s)" % ex)
345 if status:
346 status.write('1')
347 status.close()
348 sys.exit(1)
350 def instance():
351 global inst
352 try:
353 inst
354 except:
355 inst = Daemon()
356 return inst
359 def read_pid(pidfile):
360 """Read process id from a file.
362 @param pidfile: file to read
363 @return pid or 0
364 """
365 if os.path.isfile(pidfile) and os.path.getsize(pidfile):
366 try:
367 f = open(pidfile, 'r')
368 try:
369 return int(f.read())
370 finally:
371 f.close()
372 except:
373 return 0
374 else:
375 return 0
378 def find_process(pid, name):
379 """Search for a process.
381 @param pid: process id
382 @param name: process name
383 @return: pid if found, 0 otherwise
384 """
385 running = 0
386 if pid:
387 lines = os.popen('ps %d 2>/dev/null' % pid).readlines()
388 exp = '^ *%d.+%s' % (pid, name)
389 for line in lines:
390 if re.search(exp, line):
391 running = pid
392 break
393 return running
396 def main(argv = None):
397 global XEND_DAEMONIZE
399 XEND_DAEMONIZE = False
400 if argv is None:
401 argv = sys.argv
403 try:
404 daemon = instance()
406 r,w = os.pipe()
407 daemon.run(os.fdopen(w, 'w'))
408 return 0
409 except Exception, exn:
410 log.fatal(exn)
411 return 1
414 if __name__ == "__main__":
415 sys.exit(main())