Package logilab :: Package common :: Module daemon
[frames] | no frames]

Source Code for Module logilab.common.daemon

  1  # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 
  2  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr 
  3  # 
  4  # This file is part of logilab-common. 
  5  # 
  6  # logilab-common is free software: you can redistribute it and/or modify it under 
  7  # the terms of the GNU Lesser General Public License as published by the Free 
  8  # Software Foundation, either version 2.1 of the License, or (at your option) any 
  9  # later version. 
 10  # 
 11  # logilab-common is distributed in the hope that it will be useful, but WITHOUT 
 12  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 13  # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 14  # details. 
 15  # 
 16  # You should have received a copy of the GNU Lesser General Public License along 
 17  # with logilab-common.  If not, see <http://www.gnu.org/licenses/>. 
 18  """A daemonize function (for Unices) and daemon mix-in class""" 
 19   
 20  __docformat__ = "restructuredtext en" 
 21   
 22  import os 
 23  import errno 
 24  import signal 
 25  import sys 
 26  import time 
 27  import warnings 
 28   
 29   
30 -def daemonize(pidfile):
31 # See http://www.erlenstar.demon.co.uk/unix/faq_toc.html#TOC16 32 # XXX unix specific 33 # 34 # fork so the parent can exit 35 if os.fork(): # launch child and... 36 return 1 37 # deconnect from tty and create a new session 38 os.setsid() 39 # fork again so the parent, (the session group leader), can exit. 40 # as a non-session group leader, we can never regain a controlling 41 # terminal. 42 if os.fork(): # launch child again. 43 return 1 44 # move to the root to avoit mount pb 45 os.chdir('/') 46 # set paranoid umask 47 os.umask(077) 48 # redirect standard descriptors 49 null = os.open('/dev/null', os.O_RDWR) 50 for i in range(3): 51 try: 52 os.dup2(null, i) 53 except OSError, e: 54 if e.errno != errno.EBADF: 55 raise 56 os.close(null) 57 # filter warnings 58 warnings.filterwarnings('ignore') 59 # write pid in a file 60 if pidfile: 61 # ensure the directory where the pid-file should be set exists (for 62 # instance /var/run/cubicweb may be deleted on computer restart) 63 piddir = os.path.dirname(pidfile) 64 if not os.path.exists(piddir): 65 os.makedirs(piddir) 66 f = file(pidfile, 'w') 67 f.write(str(os.getpid())) 68 f.close() 69 return None
70 71
72 -class DaemonMixIn:
73 """Mixin to make a daemon from watchers/queriers. 74 """ 75
76 - def __init__(self, configmod) :
77 self.delay = configmod.DELAY 78 self.name = str(self.__class__).split('.')[-1] 79 self._pid_file = os.path.join('/tmp', '%s.pid'%self.name) 80 if os.path.exists(self._pid_file): 81 raise Exception('''Another instance of %s must be running. 82 If it i not the case, remove the file %s''' % (self.name, self._pid_file)) 83 self._alive = 1 84 self._sleeping = 0 85 self.config = configmod
86
87 - def _daemonize(self):
88 if not self.config.NODETACH: 89 if daemonize(self._pid_file) is None: 90 # put signal handler 91 signal.signal(signal.SIGTERM, self.signal_handler) 92 signal.signal(signal.SIGHUP, self.signal_handler) 93 else: 94 return -1
95
96 - def run(self):
97 """ optionally go in daemon mode and 98 do what concrete class has to do and pauses for delay between runs 99 If self.delay is negative, do a pause before starting 100 """ 101 if self._daemonize() == -1: 102 return 103 if self.delay < 0: 104 self.delay = -self.delay 105 time.sleep(self.delay) 106 while 1: 107 try: 108 self._run() 109 except Exception, ex: 110 # display for info, sleep, and hope the problem will be solved 111 # later. 112 self.config.exception('Internal error: %s', ex) 113 if not self._alive: 114 break 115 try: 116 self._sleeping = 1 117 time.sleep(self.delay) 118 self._sleeping = 0 119 except SystemExit: 120 break 121 self.config.info('%s instance exited', self.name) 122 # remove pid file 123 os.remove(self._pid_file)
124
125 - def signal_handler(self, sig_num, stack_frame):
126 if sig_num == signal.SIGTERM: 127 if self._sleeping: 128 # we are sleeping so we can exit without fear 129 self.config.debug('exit on SIGTERM') 130 sys.exit(0) 131 else: 132 self.config.debug('exit on SIGTERM (on next turn)') 133 self._alive = 0 134 elif sig_num == signal.SIGHUP: 135 self.config.info('reloading configuration on SIGHUP') 136 reload(self.config)
137
138 - def _run(self):
139 """should be overridden in the mixed class""" 140 raise NotImplementedError()
141 142 143 import logging 144 from logilab.common.logging_ext import set_log_methods 145 set_log_methods(DaemonMixIn, logging.getLogger('lgc.daemon')) 146 147 ## command line utilities ###################################################### 148 149 L_OPTIONS = ["help", "log=", "delay=", 'no-detach'] 150 S_OPTIONS = 'hl:d:n' 151 161
162 -def handle_option(modconfig, opt_name, opt_value, help_meth):
163 if opt_name in ('-h','--help'): 164 help_meth() 165 sys.exit(0) 166 elif opt_name in ('-l','--log'): 167 modconfig.LOG_TRESHOLD = int(opt_value) 168 elif opt_name in ('-d', '--delay'): 169 modconfig.DELAY = int(opt_value) 170 elif opt_name in ('-n', '--no-detach'): 171 modconfig.NODETACH = 1
172