ia64/xen-unstable

view tools/python/xen/xend/server/SrvDaemon.py @ 9903:a19cc748469e

Debugging Xend with XEND_DAEMONIZE set to False doesn't work as
expected. Xend actually creates two children and the XEND_DAEMONIZE
flag only prevents one of the children from being created. This
patch causes none of the children to be created. With this patch,
Xend functionality is performed in the process executed from the
command line (i.e., /usr/sbin/xend). This patch makes it possible to
debug Xend with pdb.

Signed-off-by: Randy Thelen <rthelen@netapp.com>
author kaf24@firebug.cl.cam.ac.uk
date Sun Apr 30 09:52:59 2006 +0100 (2006-04-30)
parents fa5dddabda0c
children 00822a3a57d9
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)
199 return ret
201 def tracing(self, traceon):
202 """Turn tracing on or off.
204 @param traceon: tracing flag
205 """
206 if traceon == self.traceon:
207 return
208 self.traceon = traceon
209 if traceon:
210 self.tracefile = open(XEND_TRACE_FILE, 'w+', 1)
211 self.traceindent = 0
212 sys.settrace(self.trace)
213 try:
214 threading.settrace(self.trace) # Only in Python >= 2.3
215 except:
216 pass
218 def print_trace(self, string):
219 for i in range(self.traceindent):
220 ch = " "
221 if (i % 5):
222 ch = ' '
223 else:
224 ch = '|'
225 self.tracefile.write(ch)
226 self.tracefile.write(string)
228 def trace(self, frame, event, arg):
229 if not self.traceon:
230 print >>self.tracefile
231 print >>self.tracefile, '-' * 20, 'TRACE OFF', '-' * 20
232 self.tracefile.close()
233 self.tracefile = None
234 return None
235 if event == 'call':
236 code = frame.f_code
237 filename = code.co_filename
238 m = re.search('.*xend/(.*)', filename)
239 if not m:
240 return None
241 modulename = m.group(1)
242 if re.search('sxp.py', modulename):
243 return None
244 self.traceindent += 1
245 self.print_trace("> %s:%s\n"
246 % (modulename, code.co_name))
247 elif event == 'line':
248 filename = frame.f_code.co_filename
249 lineno = frame.f_lineno
250 self.print_trace("%4d %s" %
251 (lineno, linecache.getline(filename, lineno)))
252 elif event == 'return':
253 code = frame.f_code
254 filename = code.co_filename
255 m = re.search('.*xend/(.*)', filename)
256 if not m:
257 return None
258 modulename = m.group(1)
259 self.print_trace("< %s:%s\n"
260 % (modulename, code.co_name))
261 self.traceindent -= 1
262 elif event == 'exception':
263 self.print_trace("! Exception:\n")
264 (ex, val, tb) = arg
265 traceback.print_exception(ex, val, tb, 10, self.tracefile)
266 #del tb
267 return self.trace
269 def set_user(self):
270 # Set the UID.
271 try:
272 os.setuid(pwd.getpwnam(XEND_USER)[2])
273 return 0
274 except KeyError:
275 print >>sys.stderr, "Error: no such user '%s'" % XEND_USER
276 return 1
278 def stop(self):
279 return self.cleanup_xend(True)
281 def run(self, status):
282 try:
283 log.info("Xend Daemon started")
285 xc = xen.lowlevel.xc.xc()
286 xinfo = xc.xeninfo()
287 log.info("Xend changeset: %s.", xinfo['xen_changeset'])
288 del xc
290 relocate.listenRelocation()
291 servers = SrvServer.create()
292 servers.start(status)
293 except Exception, ex:
294 print >>sys.stderr, 'Exception starting xend:', ex
295 if XEND_DEBUG:
296 traceback.print_exc()
297 log.exception("Exception starting xend (%s)" % ex)
298 if status:
299 status.write('1')
300 status.close()
301 sys.exit(1)
303 def instance():
304 global inst
305 try:
306 inst
307 except:
308 inst = Daemon()
309 return inst
312 def read_pid(pidfile):
313 """Read process id from a file.
315 @param pidfile: file to read
316 @return pid or 0
317 """
318 if os.path.isfile(pidfile) and os.path.getsize(pidfile):
319 try:
320 f = open(pidfile, 'r')
321 try:
322 return int(f.read())
323 finally:
324 f.close()
325 except:
326 return 0
327 else:
328 return 0
331 def find_process(pid, name):
332 """Search for a process.
334 @param pid: process id
335 @param name: process name
336 @return: pid if found, 0 otherwise
337 """
338 running = 0
339 if pid:
340 lines = os.popen('ps %d 2>/dev/null' % pid).readlines()
341 exp = '^ *%d.+%s' % (pid, name)
342 for line in lines:
343 if re.search(exp, line):
344 running = pid
345 break
346 return running
349 def main(argv = None):
350 global XEND_DAEMONIZE
352 XEND_DAEMONIZE = False
353 if argv is None:
354 argv = sys.argv
356 try:
357 daemon = instance()
359 r,w = os.pipe()
360 daemon.run(os.fdopen(w, 'w'))
361 return 0
362 except Exception, exn:
363 log.fatal(exn)
364 return 1
367 if __name__ == "__main__":
368 sys.exit(main())