root / logilab.pylintinstaller / logilab / common / daemon.py

Revision 202:d67e86292521, 5.3 kB (checked in by tziade@…, 9 months ago)

added logilab.pylintinstaller

Line 
1# This program is free software; you can redistribute it and/or modify it under
2# the terms of the GNU General Public License as published by the Free Software
3# Foundation; either version 2 of the License, or (at your option) any later
4# version.
5#
6# This program is distributed in the hope that it will be useful, but WITHOUT
7# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9#
10# You should have received a copy of the GNU General Public License along with
11# this program; if not, write to the Free Software Foundation, Inc.,
12# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
13""" Copyright (c) 2002-2003 LOGILAB S.A. (Paris, FRANCE).
14 http://www.logilab.fr/ -- mailto:contact@logilab.fr
15
16a daemon mix-in class
17"""
18
19__revision__ = '$Id: daemon.py,v 1.10 2005-11-22 13:13:01 syt Exp $'
20
21import os, signal, sys, time
22from logilab.common.logger import make_logger, LOG_ALERT, LOG_NOTICE
23
24class DaemonMixIn:
25    """ mixin to make a daemon from watchers/queriers
26    """
27
28    def __init__(self, configmod) :
29        self.delay = configmod.DELAY
30        self.name = str(self.__class__).split('.')[-1]
31        self._pid_file = os.path.join('/tmp', '%s.pid'%self.name)
32        if os.path.exists(self._pid_file):
33            raise Exception('''Another instance of %s must be running.
34If it i not the case, remove the file %s''' % (self.name, self._pid_file))
35        self._alive = 1
36        self._sleeping = 0
37        treshold = configmod.LOG_TRESHOLD
38        if configmod.NODETACH:
39            configmod.log = make_logger('print', treshold, self.name).log
40        else:
41            configmod.log = make_logger('syslog', treshold, self.name).log
42        self.config = configmod
43
44    def _daemonize(self):
45        if not self.config.NODETACH:
46            # fork so the parent can exist
47            if (os.fork()):
48                return -1
49            # deconnect from tty and create a new session
50            os.setsid()
51            # fork again so the parent, (the session group leader), can exit.
52            # as a non-session group leader, we can never regain a controlling
53            # terminal.
54            if (os.fork()):
55                return -1
56            # move to the root to avoit mount pb
57            os.chdir('/')
58            # set paranoid umask
59            os.umask(077)
60            # write pid in a file
61            f = open(self._pid_file, 'w')
62            f.write(str(os.getpid()))
63            f.close()
64            # close standard descriptors
65            sys.stdin.close()
66            sys.stdout.close()
67            sys.stderr.close()
68            # put signal handler
69            signal.signal(signal.SIGTERM, self.signal_handler)
70            signal.signal(signal.SIGHUP, self.signal_handler)
71               
72       
73    def run(self):
74        """ optionaly go in daemon mode and
75        do what concrete classe has to do and pauses for delay between runs
76        If self.delay is negative, do a pause before starting
77        """
78        if self._daemonize() == -1:
79            return
80        self.config.log(LOG_NOTICE, '%s instance started' % self.name)
81        if self.delay < 0:
82            self.delay = -self.delay
83            time.sleep(self.delay)
84        while 1:
85            try:
86                self._run()
87            except Exception, e:
88                # display for info, sleep, and hope the problem will be solved
89                # later.
90                self.config.log(LOG_ALERT, 'Internal error: %s'%(e))
91            if not self._alive:
92                break
93            try:
94                self._sleeping = 1
95                time.sleep(self.delay)
96                self._sleeping = 0
97            except SystemExit:
98                break
99        self.config.log(LOG_NOTICE, '%s instance exited'%self.name)
100        # remove pid file
101        os.remove(self._pid_file)
102       
103    def signal_handler(self, sig_num, stack_frame):
104        if sig_num == signal.SIGTERM:
105            if self._sleeping:
106                # we are sleeping so we can exit without fear
107                self.config.log(LOG_NOTICE, 'exit on SIGTERM')
108                sys.exit(0)
109            else:
110                self.config.log(LOG_NOTICE, 'exit on SIGTERM (on next turn)')
111                self._alive = 0
112        elif sig_num == signal.SIGHUP:
113            self.config.log(LOG_NOTICE, 'reloading configuration on SIGHUP')
114            reload(self.config)
115
116    def _run(self):
117        """should be overidden in the mixed class"""
118        raise NotImplementedError()
119   
120## command line utilities ######################################################
121
122L_OPTIONS = ["help", "log=", "delay=", 'no-detach']
123S_OPTIONS = 'hl:d:n'
124
125def print_help(modconfig):
126    print """  --help or -h
127    displays this message
128  --log <log_level>
129    log treshold (7 record everything, 0 record only emergency.)
130    Defaults to %s
131  --delay <delay>
132    the number of seconds between two runs.
133    Defaults to %s""" % (modconfig.LOG_TRESHOLD, modconfig.DELAY)
134
135def handle_option(modconfig, opt_name, opt_value, help_meth):
136    if opt_name in ('-h','--help'):           
137        help_meth()
138        sys.exit(0)
139    elif opt_name in ('-l','--log'):
140        modconfig.LOG_TRESHOLD = int(opt_value)
141    elif opt_name in ('-d', '--delay'):
142        modconfig.DELAY = int(opt_value)
143    elif opt_name in ('-n', '--no-detach'):
144        modconfig.NODETACH = 1
Note: See TracBrowser for help on using the browser.