-
Notifications
You must be signed in to change notification settings - Fork 0
/
daemon.py
121 lines (106 loc) · 3.08 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
'''
Daemon implementation
- http://legacy.python.org/dev/peps/pep-3143/
- http://stackoverflow.com/questions/473620/how-do-you-create-a-daemon-in-python
- http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
- http://code.activestate.com/recipes/278731/
- https://pypi.python.org/pypi/python-daemon/
- http://stackoverflow.com/questions/4790876/what-is-the-de-facto-library-for-creating-python-daemons
- http://www.jstorimer.com/blogs/workingwithcode/7766093-daemon-processes-in-ruby
- https://github.com/mperham/sidekiq/blob/master/bin/sidekiqctl
- https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/cli.rb
Day 2: I have spent the past few hours reading up on linux processes, process groups, sessions, and zombie processes.
I believe I have a solid understand of the tiny details now.
Unfortunately, I don't have a solid understanding of the calculus for my midterm in ~18 hours
'''
import os
import sys
import time
import atexit
import signal
import systemd.daemon
def read_pid(pidfile):
pid = None
try:
with open(pidfile, encoding='ascii') as f:
try:
pid = int(f.readline().strip())
try:
os.kill(pid, 0)
except ProcessLookupError:
delete_pid(pidfile)
pid = None
except ValueError:
delete_pid(pidfile)
except FileNotFoundError:
pass
return pid
def open_pid(pidfile, success):
try:
with open(pidfile, 'x', encoding='ascii') as f:
success(f)
except FileExistsError:
return False
return True
def create_pid(pidfile, pid):
return open_pid(pidfile, lambda f: write_pid(f, pid))
def write_pid(f, pid):
f.write('{:d}\n'.format(pid))
def delete_pid(pidfile):
try:
os.remove(pidfile)
except FileNotFoundError:
pass
def systemd_ready():
if systemd.daemon.booted():
systemd.daemon.notify('READY=1')
# From python subprocess source
def close_fds():
max_fds = os.sysconf('SC_OPEN_MAX')
os.closerange(3, max_fds)
# stdin, stdout, stderr, and working directory expected to be set (e.g. systemd)
class Daemon:
def __init__(self, pidfile):
self.pidfile = pidfile
def daemonize(self):
pid = os.fork()
if pid > 0:
os._exit(0)
os.setsid()
pid = os.fork()
if pid > 0:
os._exit(0)
close_fds()
atexit.register(lambda: delete_pid(self.pidfile))
return create_pid(self.pidfile, os.getpid())
def start(self, do_it):
pid = read_pid(self.pidfile)
if not pid is None:
sys.stderr.write('Pidfile {:s} with process {:d} already exists\n'.format(self.pidfile, pid))
return
if self.daemonize():
self.run(do_it)
else:
sys.stderr.write('Pidfile {:s} with process {:d} already exists (tried creating pidfile)\n'.format(self.pidfile, pid))
return
def stop(self, timeout):
pid = read_pid(self.pidfile)
if pid is None:
return
try:
os.kill(pid, signal.SIGTERM)
except ProcessLookupError:
return
i = 0
while i < timeout:
try:
time.sleep(1)
os.kill(pid, 0)
i += 1
except ProcessLookupError:
return
sys.stderr.write('Process {:d} did not stop after {:d} seconds, killing\n'.format(pid, timeout))
os.kill(pid, signal.SIGKILL)
def run(self, nike):
# Just do it (for Radu)
nike()