1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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=None, uid=None, umask=077):
31 """daemonize a Unix process. Set paranoid umask by default.
32
33 Return 1 in the original process, 2 in the first fork, and None for the
34 second fork (eg daemon process).
35 """
36
37
38
39
40 if os.fork():
41 return 1
42
43 os.setsid()
44
45
46
47 if os.fork():
48 return 2
49
50 os.chdir('/')
51
52 if umask is not None:
53 os.umask(umask)
54
55 null = os.open('/dev/null', os.O_RDWR)
56 for i in range(3):
57 try:
58 os.dup2(null, i)
59 except OSError, e:
60 if e.errno != errno.EBADF:
61 raise
62 os.close(null)
63
64 warnings.filterwarnings('ignore')
65
66 if pidfile:
67
68
69 piddir = os.path.dirname(pidfile)
70 if not os.path.exists(piddir):
71 os.makedirs(piddir)
72 f = file(pidfile, 'w')
73 f.write(str(os.getpid()))
74 f.close()
75
76 if uid:
77 try:
78 uid = int(uid)
79 except ValueError:
80 from pwd import getpwnam
81 uid = getpwnam(uid).pw_uid
82 os.setuid(uid)
83 return None
84
85
87 """Mixin to make a daemon from watchers/queriers.
88 """
89
91 self.delay = configmod.DELAY
92 self.name = str(self.__class__).split('.')[-1]
93 self._pid_file = os.path.join('/tmp', '%s.pid'%self.name)
94 if os.path.exists(self._pid_file):
95 raise Exception('''Another instance of %s must be running.
96 If it i not the case, remove the file %s''' % (self.name, self._pid_file))
97 self._alive = 1
98 self._sleeping = 0
99 self.config = configmod
100
102 if not self.config.NODETACH:
103 if daemonize(self._pid_file) is None:
104
105 signal.signal(signal.SIGTERM, self.signal_handler)
106 signal.signal(signal.SIGHUP, self.signal_handler)
107 else:
108 return -1
109
111 """ optionally go in daemon mode and
112 do what concrete class has to do and pauses for delay between runs
113 If self.delay is negative, do a pause before starting
114 """
115 if self._daemonize() == -1:
116 return
117 if self.delay < 0:
118 self.delay = -self.delay
119 time.sleep(self.delay)
120 while True:
121 try:
122 self._run()
123 except Exception, ex:
124
125
126 self.config.exception('Internal error: %s', ex)
127 if not self._alive:
128 break
129 try:
130 self._sleeping = 1
131 time.sleep(self.delay)
132 self._sleeping = 0
133 except SystemExit:
134 break
135 self.config.info('%s instance exited', self.name)
136
137 os.remove(self._pid_file)
138
140 if sig_num == signal.SIGTERM:
141 if self._sleeping:
142
143 self.config.debug('exit on SIGTERM')
144 sys.exit(0)
145 else:
146 self.config.debug('exit on SIGTERM (on next turn)')
147 self._alive = 0
148 elif sig_num == signal.SIGHUP:
149 self.config.info('reloading configuration on SIGHUP')
150 reload(self.config)
151
153 """should be overridden in the mixed class"""
154 raise NotImplementedError()
155
156
157 import logging
158 from logilab.common.logging_ext import set_log_methods
159 set_log_methods(DaemonMixIn, logging.getLogger('lgc.daemon'))
160
161
162
163 L_OPTIONS = ["help", "log=", "delay=", 'no-detach']
164 S_OPTIONS = 'hl:d:n'
165
167 print """ --help or -h
168 displays this message
169 --log <log_level>
170 log treshold (7 record everything, 0 record only emergency.)
171 Defaults to %s
172 --delay <delay>
173 the number of seconds between two runs.
174 Defaults to %s""" % (modconfig.LOG_TRESHOLD, modconfig.DELAY)
175
177 if opt_name in ('-h', '--help'):
178 help_meth()
179 sys.exit(0)
180 elif opt_name in ('-l', '--log'):
181 modconfig.LOG_TRESHOLD = int(opt_value)
182 elif opt_name in ('-d', '--delay'):
183 modconfig.DELAY = int(opt_value)
184 elif opt_name in ('-n', '--no-detach'):
185 modconfig.NODETACH = 1
186