-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdaemon.py
executable file
·147 lines (120 loc) · 4.39 KB
/
daemon.py
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
132
133
134
135
136
137
138
139
140
141
142
#!/usr/bin/env python
# daemon.py
import atexit
from __future__ import print_function
import os
import signal
import sys
import time
class Daemon:
def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
self.pidfile = pidfile
self.stdin = stdin
self.stdout = stdout
self.stderr = stderr
def daemonize(self):
"""Set up daemon."""
# First fork (detaches from parent)
try:
if os.fork() > 0:
raise SystemExit(0) # Parent exit
except OSError as e:
raise RuntimeError('fork #1 failed.')
os.chdir('/')
os.umask(0)
os.setsid()
# Second fork (relinquish session leadership)
try:
if os.fork() > 0:
raise SystemExit(0)
except OSError as e:
raise RuntimeError('fork #2 failed.')
# Flush I/O buffers
sys.stdout.flush()
sys.stderr.flush()
# Replace file descriptors for stdin, stdout, and stderr
with open(self.stdin, 'r', 0) as f:
os.dup2(f.fileno(), sys.stdin.fileno())
with open(self.stdout, 'w', 0) as f:
os.dup2(f.fileno(), sys.stdout.fileno())
with open(self.stderr, 'w', 0) as f:
os.dup2(f.fileno(), sys.stderr.fileno())
# Write the PID file
self.pid = os.getpid()
with open(self.pidfile, 'w') as f:
print(self.pid, file=f)
# Arrange to have the PID file removed on exit/signal
atexit.register(lambda: os.remove(self.pidfile))
# Signal handler for termination (required)
def sigterm_handler(signo, frame):
raise SystemExit(1)
signal.signal(signal.SIGTERM, sigterm_handler)
def _get_pid_from_file(self):
try:
with open(self.pidfile, 'r') as f:
pid = int(f.read().strip())
# File doesn't exist
except IOError:
pid = None
# File exists, but PID is not a number
except ValueError:
print('Error: PID file {0} exists but PID is not a number.'.format(self.pidfile))
raise SystemExit(1)
return pid
def start(self):
"""Start daemon."""
pid = self._get_pid_from_file()
print(pid)
if pid is None:
sys.stderr.write('Daemon starting...\n')
self.daemonize()
self.run()
else:
sys.stderr.write('Daemon already running...PID: {0}, PID file: {1}\n'.format(pid, self.pidfile))
def status(self):
"""Get status of daemon."""
pid = self._get_pid_from_file()
if pid is None:
sys.stderr.write('Daemon not running.\n')
else:
sys.stderr.write('Daemon running...PID: {0}, PID file: {1}\n'.format(pid, self.pidfile))
def stop(self):
"""Stop daemon."""
pid = self._get_pid_from_file()
if pid is None:
sys.stderr.write('Daemon not running.\n')
else:
sys.stderr.write('Stopping daemon...\n')
os.kill(pid, signal.SIGTERM)
def run(self):
"""Does the work. Subclasses should override this method."""
raise NotImplementedError('run: Subclasses must override this method.')
if __name__ == '__main__':
"""Self-test code."""
args = 'start|stop|status'
def show_usage():
"""Print usage."""
print('Usage: {0} {1}'.format(sys.argv[0], args), file=sys.stderr)
if len(sys.argv) != 2:
show_usage()
raise SystemExit(1)
class MyDaemon(Daemon):
def run(self):
"""Print pid to file, then start the work loop."""
sys.stdout.write('Daemon started with pid {0}\n'.format(self.pid))
# Loop forever, doing some simulated work
while True:
sys.stdout.write('Daemon Alive! {0}\n'.format(time.ctime()))
time.sleep(2)
sys.stdout.flush()
daemon = MyDaemon(pidfile='/tmp/daemon.pid', stdout='/tmp/daemon.log', stderr='/tmp/daemon.log')
if sys.argv[1] == 'start':
daemon.start()
elif sys.argv[1] == 'stop':
daemon.stop()
elif sys.argv[1] == 'status':
daemon.status()
else:
print('Unknown command: {0!r}'.format(sys.argv[1]), file=sys.stderr)
show_usage()
raise SystemExit(1)