ia64/xen-unstable

view tools/python/xen/xend/server/SrvDaemon.py @ 7943:3a544c04cc61

Added method so that SrvDaemon can be run in the foreground without all the
forking implied by the start method. This makes it easier to run valgrind
against Xend, or run it with a pydebug-compiled Python.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author emellor@leeni.uk.xensource.com
date Mon Nov 21 12:44:43 2005 +0100 (2005-11-21)
parents a51ec6d425e7
children 87b520c30cb2
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.server import SrvServer
20 from xen.xend.XendLogging import log
22 import relocate
23 from params import *
26 class Daemon:
27 """The xend daemon.
28 """
29 def __init__(self):
30 self.shutdown = 0
31 self.traceon = 0
32 self.tracefile = None
33 self.traceindent = 0
34 self.child = 0
36 def read_pid(self, pidfile):
37 """Read process id from a file.
39 @param pidfile: file to read
40 @return pid or 0
41 """
42 if os.path.isfile(pidfile) and os.path.getsize(pidfile):
43 try:
44 f = open(pidfile, 'r')
45 try:
46 return int(f.read())
47 finally:
48 f.close()
49 except:
50 return 0
51 else:
52 return 0
54 def find_process(self, pid, name):
55 """Search for a process.
57 @param pid: process id
58 @param name: process name
59 @return: pid if found, 0 otherwise
60 """
61 running = 0
62 if pid:
63 lines = os.popen('ps %d 2>/dev/null' % pid).readlines()
64 exp = '^ *%d.+%s' % (pid, name)
65 for line in lines:
66 if re.search(exp, line):
67 running = pid
68 break
69 return running
71 def cleanup_process(self, pidfile, name, kill):
72 """Clean up the pidfile for a process.
73 If a running process is found, kills it if 'kill' is true.
75 @param pidfile: pid file
76 @param name: process name
77 @param kill: whether to kill the process
78 @return running process id or 0
79 """
80 running = 0
81 pid = self.read_pid(pidfile)
82 if self.find_process(pid, name):
83 if kill:
84 os.kill(pid, 1)
85 else:
86 running = pid
87 if running == 0 and os.path.isfile(pidfile):
88 os.remove(pidfile)
89 return running
91 def cleanup_xend(self, kill):
92 return self.cleanup_process(XEND_PID_FILE, "xend", kill)
94 def status(self):
95 """Returns the status of the xend daemon.
96 The return value is defined by the LSB:
97 0 Running
98 3 Not running
99 """
100 if self.cleanup_process(XEND_PID_FILE, "xend", False) == 0:
101 return 3
102 else:
103 return 0
105 def fork_pid(self, pidfile):
106 """Fork and write the pid of the child to 'pidfile'.
108 @param pidfile: pid file
109 @return: pid of child in parent, 0 in child
110 """
112 self.child = os.fork()
114 if self.child:
115 # Parent
116 pidfile = open(pidfile, 'w')
117 try:
118 pidfile.write(str(self.child))
119 finally:
120 pidfile.close()
122 return self.child
124 def daemonize(self):
125 if not XEND_DAEMONIZE: return
126 # Detach from TTY.
127 os.setsid()
129 # Detach from standard file descriptors, and redirect them to
130 # /dev/null or the log as appropriate.
131 os.close(0)
132 os.close(1)
133 os.close(2)
134 if XEND_DEBUG:
135 os.open('/dev/null', os.O_RDONLY)
136 os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT)
137 os.dup(1)
138 else:
139 os.open('/dev/null', os.O_RDWR)
140 os.dup(0)
141 os.open(XEND_DEBUG_LOG, os.O_WRONLY|os.O_CREAT)
144 def start(self, trace=0):
145 """Attempts to start the daemons.
146 The return value is defined by the LSB:
147 0 Success
148 4 Insufficient privileges
149 """
150 xend_pid = self.cleanup_xend(False)
152 if self.set_user():
153 return 4
154 os.chdir("/")
156 if xend_pid > 0:
157 # Trying to run an already-running service is a success.
158 return 0
160 ret = 0
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 self.fork_pid(XEND_PID_FILE):
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.tracing(trace)
182 self.run(os.fdopen(w, 'w'))
184 return ret
186 def tracing(self, traceon):
187 """Turn tracing on or off.
189 @param traceon: tracing flag
190 """
191 if traceon == self.traceon:
192 return
193 self.traceon = traceon
194 if traceon:
195 self.tracefile = open(XEND_TRACE_FILE, 'w+', 1)
196 self.traceindent = 0
197 sys.settrace(self.trace)
198 try:
199 threading.settrace(self.trace) # Only in Python >= 2.3
200 except:
201 pass
203 def print_trace(self, string):
204 for i in range(self.traceindent):
205 ch = " "
206 if (i % 5):
207 ch = ' '
208 else:
209 ch = '|'
210 self.tracefile.write(ch)
211 self.tracefile.write(string)
213 def trace(self, frame, event, arg):
214 if not self.traceon:
215 print >>self.tracefile
216 print >>self.tracefile, '-' * 20, 'TRACE OFF', '-' * 20
217 self.tracefile.close()
218 self.tracefile = None
219 return None
220 if event == 'call':
221 code = frame.f_code
222 filename = code.co_filename
223 m = re.search('.*xend/(.*)', filename)
224 if not m:
225 return None
226 modulename = m.group(1)
227 if re.search('sxp.py', modulename):
228 return None
229 self.traceindent += 1
230 self.print_trace("> %s:%s\n"
231 % (modulename, code.co_name))
232 elif event == 'line':
233 filename = frame.f_code.co_filename
234 lineno = frame.f_lineno
235 self.print_trace("%4d %s" %
236 (lineno, linecache.getline(filename, lineno)))
237 elif event == 'return':
238 code = frame.f_code
239 filename = code.co_filename
240 m = re.search('.*xend/(.*)', filename)
241 if not m:
242 return None
243 modulename = m.group(1)
244 self.print_trace("< %s:%s\n"
245 % (modulename, code.co_name))
246 self.traceindent -= 1
247 elif event == 'exception':
248 self.print_trace("! Exception:\n")
249 (ex, val, tb) = arg
250 traceback.print_exception(ex, val, tb, 10, self.tracefile)
251 #del tb
252 return self.trace
254 def set_user(self):
255 # Set the UID.
256 try:
257 os.setuid(pwd.getpwnam(XEND_USER)[2])
258 return 0
259 except KeyError:
260 print >>sys.stderr, "Error: no such user '%s'" % XEND_USER
261 return 1
263 def stop(self):
264 return self.cleanup_xend(True)
266 def run(self, status):
267 try:
268 log.info("Xend Daemon started")
270 xc = xen.lowlevel.xc.new()
271 xinfo = xc.xeninfo()
272 log.info("Xend changeset: %s.", xinfo['xen_changeset'])
273 del xc
275 relocate.listenRelocation()
276 servers = SrvServer.create()
277 self.daemonize()
278 servers.start(status)
279 except Exception, ex:
280 print >>sys.stderr, 'Exception starting xend:', ex
281 if XEND_DEBUG:
282 traceback.print_exc()
283 log.exception("Exception starting xend (%s)" % ex)
284 status.write('1')
285 status.close()
286 self.exit(1)
288 def exit(self, rc=0):
289 # Calling sys.exit() raises a SystemExit exception, which only
290 # kills the current thread. Calling os._exit() makes the whole
291 # Python process exit immediately. There doesn't seem to be another
292 # way to exit a Python with running threads.
293 #sys.exit(rc)
294 os._exit(rc)
296 def instance():
297 global inst
298 try:
299 inst
300 except:
301 inst = Daemon()
302 return inst
305 def main(argv = None):
306 global XEND_DAEMONIZE
308 XEND_DAEMONIZE = 0
309 if argv is None:
310 argv = sys.argv
312 try:
313 daemon = instance()
315 r,w = os.pipe()
316 daemon.run(os.fdopen(w, 'w'))
317 return 0
318 except Exception, exn:
319 log.fatal(exn)
320 return 1
323 if __name__ == "__main__":
324 sys.exit(main())