aboutsummaryrefslogtreecommitdiff
path: root/daemon_example.py
blob: 1ab9376cc6736f50f3640f517123331a76a208d9 (plain)
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
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Example on how to create a daemon.

If you are confused on how the daemon library works you can get some ideas
from the tests.

https://pagure.io/python-daemon/blob/master/f/test/test_runner.py
"""
import os
import sys
from logging import FileHandler, Formatter, getLogger, info
from os.path import isfile
from signal import SIGINT, SIGTERM, signal
from time import sleep


class MyDaemon(object):
    """Daemon class. Not for production use, example only."""

    def __init__(self):
        """Initialize the paths and the pidfile of the daemon."""
        # Setup logging
        log = getLogger()
        handler = FileHandler('/var/log/python_daemon.log')
        formatter = Formatter('%(asctime)s %(levelname)s %(message)s')
        handler.setFormatter(formatter)
        log.addHandler(handler)
        log.setLevel('INFO')
        self.pidfile_path = '/var/run/python_daemon.pid'
        # Register handlers to exit when we get a kill signal
        signal(SIGINT, self.exit_gracefully)
        signal(SIGTERM, self.exit_gracefully)

    def exit_gracefully(self, signum, frame):
        """Exit Gracefully."""
        info('I died')
        self.remove_pidfile()
        exit()

    def create_pidfile(self):
        """Create a pidfile for the daemon."""
        info('Creating pid file')
        with open(self.pidfile_path, mode='w', encoding='utf-8') as a_file:
            a_file.write(str('{}\n'.format(self.pid)))

    def remove_pidfile(self):
        """Remove the pidfile."""
        info('Removing pidfile: {}'.format(self.pidfile_path))
        os.remove(self.pidfile_path)

    def detach_process(self):
        """Simple method for daemonizing process."""
        newpid = os.fork()
        if newpid > 0:
            exit()
        else:
            os.setsid()
            newpid = os.fork()
            if newpid > 0:
                exit()
            else:
                self.pid = os.getpid()
                return

    def run(self):
        """Entrypoint for the daemon."""
        if isfile(self.pidfile_path):
            print('Pidfile already exists, Daemon already running')
            exit()
        info('Starting up the python daemon.')
        self.detach_process()
        # redirect stdin, stdout and stderr to devnull
        f = open(os.devnull, 'w')
        sys.stdin = f
        sys.stdout = f
        sys.stderr = f
        # Create the pid file
        self.create_pidfile()
        while True:
            info('I am alive')
            sleep(1)

    def stop(self):
        """Stop a currently running process."""
        try:
            with open(self.pidfile_path, encoding='utf-8') as a_file:
                pid = a_file.readline()
                pid = pid.strip()  # Remove whitespace
                os.kill(int(pid), SIGTERM)
                exit()
        except FileNotFoundError:
            print('no pidfile found, is the daemon running?')
            exit()


# run the daemon
if __name__ == '__main__':
    daemon = MyDaemon()
    if len(sys.argv) <= 1:
        print('Usage: ./daemon_example.py \'start|stop\'')
        exit()
    if sys.argv[1] == 'start':
        daemon.run()
        exit()
    if sys.argv[1] == 'stop':
        daemon.stop()
        exit()
    print('Error: I dont know that command')