ia64/xen-unstable

view tools/python/xen/xend/server/SrvDaemon.py @ 12725:36fe7ca48e54

Tidy up the creation of directories that Xend needs. This avoids potential
races in this creation.

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