source: ogAgent-Git/src/opengnsys/linux/daemon.py @ 324ffda

configure-ptt-chedecorare-oglive-methodsejecutarscript-b64fix-cfg2objfixes-winlgromero-filebeatmainno-ptt-paramogcore1oglogoglog2override-moduleping1ping2ping3ping4report-progresstls
Last change on this file since 324ffda was 9424789, checked in by Ramón M. Gómez <ramongomez@…>, 5 years ago

#940: Adapt code to run the Linux daemon.

  • Property mode set to 100644
File size: 5.8 KB
Line 
1# -*- coding: utf-8 -*-
2#
3# Copyright (c) 2014 Virtual Cable S.L.
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without modification,
7# are permitted provided that the following conditions are met:
8#
9#    * Redistributions of source code must retain the above copyright notice,
10#      this list of conditions and the following disclaimer.
11#    * Redistributions in binary form must reproduce the above copyright notice,
12#      this list of conditions and the following disclaimer in the documentation
13#      and/or other materials provided with the distribution.
14#    * Neither the name of Virtual Cable S.L. nor the names of its contributors
15#      may be used to endorse or promote products derived from this software
16#      without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28"""
29@author: : http://www.jejik.com/authors/sander_marechal/
30@see: : http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
31"""
32
33import atexit
34import os
35import sys
36import time
37from opengnsys.log import logger
38from signal import SIGTERM
39
40
41class Daemon:
42    """
43    A generic daemon class.
44
45    Usage: subclass the Daemon class and override the run() method
46    """
47    def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
48        self.stdin = stdin
49        self.stdout = stdout
50        self.stderr = stderr
51        self.pidfile = pidfile
52
53    def daemonize(self):
54        """
55        do the UNIX double-fork magic, see Stevens' "Advanced
56        Programming in the UNIX Environment" for details (ISBN 0201563177)
57        http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
58        """
59        try:
60            pid = os.fork()
61            if pid > 0:
62                # exit first parent
63                sys.exit(0)
64        except OSError as e:
65            logger.error("fork #1 error: {}".format(e))
66            sys.stderr.write("fork #1 failed: {}\n".format(e))
67            sys.exit(1)
68
69        # decouple from parent environment
70        os.chdir("/")
71        os.setsid()
72        os.umask(0)
73
74        # do second fork
75        try:
76            pid = os.fork()
77            if pid > 0:
78                # exit from second parent
79                sys.exit(0)
80        except OSError as e:
81            logger.error("fork #2 error: {}".format(e))
82            sys.stderr.write("fork #2 failed: {}\n".format(e))
83            sys.exit(1)
84
85        # redirect standard file descriptors
86        sys.stdout.flush()
87        sys.stderr.flush()
88        si = open(self.stdin, 'r')
89        so = open(self.stdout, 'a+')
90        se = open(self.stderr, 'a+')
91        os.dup2(si.fileno(), sys.stdin.fileno())
92        os.dup2(so.fileno(), sys.stdout.fileno())
93        os.dup2(se.fileno(), sys.stderr.fileno())
94
95        # write pidfile
96        atexit.register(self.delpid)
97        pid = str(os.getpid())
98        with open(self.pidfile, 'w+') as f:
99            f.write("{}\n".format(pid))
100
101    def delpid(self):
102        try:
103            os.remove(self.pidfile)
104        except Exception:
105            # Not found/not permissions or whatever...
106            pass
107
108    def start(self):
109        """
110        Start the daemon
111        """
112        logger.debug('Starting daemon')
113        # Check for a pidfile to see if the daemon already runs
114        try:
115            pf = open(self.pidfile, 'r')
116            pid = int(pf.read().strip())
117            pf.close()
118        except IOError:
119            pid = None
120
121        if pid:
122            message = "pidfile {} already exist. Daemon already running?\n".format(pid)
123            logger.error(message)
124            sys.stderr.write(message)
125            sys.exit(1)
126
127        # Start the daemon
128        self.daemonize()
129        try:
130            self.run()
131        except Exception as e:
132            logger.error('Exception running process: {}'.format(e))
133
134        if os.path.exists(self.pidfile):
135            os.remove(self.pidfile)
136
137    def stop(self):
138        """
139        Stop the daemon
140        """
141        # Get the pid from the pidfile
142        try:
143            pf = open(self.pidfile, 'r')
144            pid = int(pf.read().strip())
145            pf.close()
146        except IOError:
147            pid = None
148
149        if pid is None:
150            message = "pidfile {} does not exist. Daemon not running?\n".format(self.pidfile)
151            logger.info(message)
152            # sys.stderr.write(message)
153            return  # not an error in a restart
154
155        # Try killing the daemon process
156        try:
157            for i in range(10):
158                os.kill(pid, SIGTERM)
159                time.sleep(1)
160        except OSError as err:
161            if err.errno == 3:  # No such process
162                if os.path.exists(self.pidfile):
163                    os.remove(self.pidfile)
164            else:
165                sys.stderr.write(err)
166                sys.exit(1)
167
168    def restart(self):
169        """
170        Restart the daemon
171        """
172        self.stop()
173        self.start()
174
175    # Overridables
176    def run(self):
177        """
178        You should override this method when you subclass Daemon. It will be called after the process has been
179        daemonized by start() or restart().
180        """
Note: See TracBrowser for help on using the repository browser.