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
|
"""A daemon mix-in class.
:copyright: 2000-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: General Public License version 2 - http://www.gnu.org/licenses
"""
__docformat__ = "restructuredtext en"
import os, signal, sys, time
from logilab.common.logger import make_logger, LOG_ALERT, LOG_NOTICE
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
treshold = configmod.LOG_TRESHOLD
if configmod.NODETACH:
configmod.log = make_logger('print', treshold, self.name).log
else:
configmod.log = make_logger('syslog', treshold, self.name).log
self.config = configmod
def _daemonize(self):
if not self.config.NODETACH:
# fork so the parent can exist
if (os.fork()):
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()):
return -1
# move to the root to avoit mount pb
os.chdir('/')
# set paranoid umask
os.umask(077)
# write pid in a file
f = open(self._pid_file, 'w')
f.write(str(os.getpid()))
f.close()
# close standard descriptors
sys.stdin.close()
sys.stdout.close()
sys.stderr.close()
# put signal handler
signal.signal(signal.SIGTERM, self.signal_handler)
signal.signal(signal.SIGHUP, self.signal_handler)
def run(self):
""" optionaly go in daemon mode and
do what concrete classe has to do and pauses for delay between runs
If self.delay is negative, do a pause before starting
"""
if self._daemonize() == -1:
return
self.config.log(LOG_NOTICE, '%s instance started' % self.name)
if self.delay < 0:
self.delay = -self.delay
time.sleep(self.delay)
while 1:
try:
self._run()
except Exception, e:
# display for info, sleep, and hope the problem will be solved
# later.
self.config.log(LOG_ALERT, 'Internal error: %s'%(e))
if not self._alive:
break
try:
self._sleeping = 1
time.sleep(self.delay)
self._sleeping = 0
except SystemExit:
break
self.config.log(LOG_NOTICE, '%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.log(LOG_NOTICE, 'exit on SIGTERM')
sys.exit(0)
else:
self.config.log(LOG_NOTICE, 'exit on SIGTERM (on next turn)')
self._alive = 0
elif sig_num == signal.SIGHUP:
self.config.log(LOG_NOTICE, 'reloading configuration on SIGHUP')
reload(self.config)
def _run(self):
"""should be overidden in the mixed class"""
raise NotImplementedError()
## 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
|