Package ZenUtils :: Module ZenDaemon
[hide private]
[frames] | no frames]

Source Code for Module ZenUtils.ZenDaemon

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13   
 14  __doc__="""ZenDaemon 
 15   
 16  Base class for making deamon programs 
 17   
 18  $Id: ZenDaemon.py,v 1.9 2003/08/29 20:33:10 edahl Exp $""" 
 19   
 20  __version__ = "$Revision: 1.9 $"[11:-2] 
 21   
 22  import sys 
 23  import os 
 24  import pwd 
 25  import signal 
 26  import logging 
 27   
 28  from CmdBase import CmdBase 
 29  from Utils import zenPath, HtmlFormatter 
 30   
 31  # Daemon creation code below based on Recipe by Chad J. Schroeder 
 32  # File mode creation mask of the daemon. 
 33  UMASK = 0022 
 34  # Default working directory for the daemon. 
 35  WORKDIR = "/" 
 36   
 37  # only close stdin/out/err 
 38  MAXFD = 3  
 39   
 40  # The standard I/O file descriptors are redirected to /dev/null by default. 
 41  if (hasattr(os, "devnull")): 
 42     REDIRECT_TO = os.devnull 
 43  else: 
 44     REDIRECT_TO = "/dev/null" 
 45   
 46   
47 -class ZenDaemon(CmdBase):
48 49 pidfile = None 50
51 - def __init__(self, noopts=0, keeproot=False):
52 CmdBase.__init__(self, noopts) 53 self.keeproot=keeproot 54 self.zenhome = zenPath() 55 self.zenvar = zenPath("var") 56 if not noopts: 57 # These handlers do not get called if run as daemon. In that 58 # case twisted's handlers are called instead. 59 # See ticket #1757 60 signal.signal(signal.SIGINT, self.sigTerm) 61 signal.signal(signal.SIGTERM, self.sigTerm) 62 signal.signal(signal.SIGHUP, self.sigTerm) 63 if self.options.daemon: 64 self.changeUser() 65 self.becomeDaemon()
66 67
68 - def setupLogging(self):
69 rlog = logging.getLogger() 70 rlog.setLevel(logging.WARN) 71 mname = self.__class__.__name__ 72 self.log = logging.getLogger("zen."+ mname) 73 zlog = logging.getLogger("zen") 74 zlog.setLevel(self.options.logseverity) 75 if self.options.daemon or self.options.logpath: 76 if self.options.logpath: 77 if not os.path.isdir(os.path.dirname(self.options.logpath)): 78 raise SystemExit("logpath:%s doesn't exist" % 79 self.options.logpath) 80 logdir = self.options.logpath 81 else: 82 logdir = zenPath("log") 83 logfile = os.path.join(logdir, mname.lower()+".log") 84 h = logging.FileHandler(logfile) 85 h.setFormatter(logging.Formatter( 86 "%(asctime)s %(levelname)s %(name)s: %(message)s", 87 "%Y-%m-%d %H:%M:%S")) 88 rlog.addHandler(h) 89 else: 90 logging.basicConfig() 91 if self.options.weblog: 92 [ h.setFormatter(HtmlFormatter()) for h in rlog.handlers ]
93 94
95 - def changeUser(self):
96 if not self.keeproot: 97 try: 98 cname = pwd.getpwuid(os.getuid())[0] 99 pwrec = pwd.getpwnam(self.options.uid) 100 os.setuid(pwrec.pw_uid) 101 os.environ['HOME'] = pwrec.pw_dir 102 except (KeyError, OSError): 103 print >>sys.stderr, "WARN: user:%s not found running as:%s"%( 104 self.options.uid,cname)
105 106
107 - def becomeDaemon(self):
108 """Code below comes from the excelent recipe by Chad J. Schroeder. 109 """ 110 try: 111 pid = os.fork() 112 except OSError, e: 113 raise Exception, "%s [%d]" % (e.strerror, e.errno) 114 115 if (pid == 0): # The first child. 116 os.setsid() 117 try: 118 pid = os.fork() # Fork a second child. 119 except OSError, e: 120 raise Exception, "%s [%d]" % (e.strerror, e.errno) 121 122 if (pid == 0): # The second child. 123 os.chdir(WORKDIR) 124 os.umask(UMASK) 125 else: 126 os._exit(0) # Exit parent (the first child) of the second child. 127 else: 128 os._exit(0) # Exit parent of the first child. 129 130 # Iterate through and close all stdin/out/err 131 for fd in range(0, MAXFD): 132 try: 133 os.close(fd) 134 except OSError: # ERROR, fd wasn't open to begin with (ignored) 135 pass 136 137 os.open(REDIRECT_TO, os.O_RDWR) # standard input (0) 138 # Duplicate standard input to standard output and standard error. 139 os.dup2(0, 1) # standard output (1) 140 os.dup2(0, 2) # standard error (2) 141 if os.path.exists(self.zenvar): 142 myname = sys.argv[0].split(os.sep)[-1] + ".pid" 143 self.pidfile = os.path.join(self.zenvar, myname) 144 fp = open(self.pidfile, 'w') 145 fp.write(str(os.getpid())) 146 fp.close() 147 else: 148 raise SystemExit("ERROR: unable to open pid file %s" % self.pidfile) 149 return(0)
150 151
152 - def sigTerm(self, *unused):
153 # This probably won't be called when running as daemon. 154 # See ticket #1757 155 stop = getattr(self, "stop", None) 156 if callable(stop): stop() 157 if self.pidfile and os.path.exists(self.pidfile): 158 self.log.info("delete pidfile %s", self.pidfile) 159 os.remove(self.pidfile) 160 self.log.info('Daemon %s shutting down' % self.__class__.__name__) 161 raise SystemExit
162 163
164 - def buildOptions(self):
165 CmdBase.buildOptions(self) 166 self.parser.add_option('--uid',dest='uid',default="zenoss", 167 help='user to become when running default:zenoss') 168 self.parser.add_option('-c', '--cycle',dest='cycle', 169 action="store_true", default=False, 170 help="Cycle continuously on cycleInterval from zope") 171 self.parser.add_option('-D', '--daemon', default=False, 172 dest='daemon',action="store_true", 173 help="Become a unix daemon") 174 self.parser.add_option('--weblog', default=False, 175 dest='weblog',action="store_true", 176 help="output log info in html table format")
177