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