-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathApplicationRunner.py
97 lines (81 loc) · 2.79 KB
/
ApplicationRunner.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
#!/bin/python3
import os
import signal
import sys
import threading
import logging
from threading import Timer
"""
This class is responsible for running BeeeOn application
in forked process in the background. stderr and stdout
are redirected to specified files.
Every BeeeOn application is supposed to send SIGTERM signal to
specified process on successfull initialization(via the "-N{PID} argument").
This signal is expected to be received in a specified timeout.
This object is thread unsafe and should be handled within a single thread.
"""
class ApplicationRunner:
"""
Creates an application runner.
Arguments:
binaryPath -- application's executable path.
configPath -- main configuration file used for application, its used as "-c argument".
stderrFile -- Application's stderr is redirected to this file.
stdoutFile -- Application's stdout is redirected to this file.
startupTimeout -- Timeout for application to start, if the SIGTERM is not received in this timeout, application is killed.
"""
def __init__(self, binaryPath, configPath, stderrFile, stdoutFile, valgrind = False, startupTimeout = 5):
self.binaryPath = binaryPath
self.configPath = configPath
self.valgrind = valgrind
self.override = {}
self.pid = None
self.stderrFile = stderrFile
self.stdoutFile = stdoutFile
self.startupTimeout = startupTimeout
self.event = threading.Event()
def overrideUpdate(self, key, value):
self.override[key] = value
def startupHandler(self, signum, frame):
self.event.set()
def start(self):
if self.pid != None:
raise Exception("Already running")
signal.signal(signal.SIGTERM, self.startupHandler)
parentPid = os.getpid()
logging.debug("Override options: " + str(self.override))
pid = os.fork()
if (pid == 0): # child
fdout = os.open(self.stdoutFile, os.O_WRONLY|os.O_CREAT|os.O_TRUNC)
fderr = os.open(self.stderrFile, os.O_WRONLY|os.O_CREAT|os.O_TRUNC)
os.dup2(fdout, sys.stdout.fileno())
os.dup2(fderr, sys.stderr.fileno())
if self.valgrind:
args = ["/usr/bin/valgrind", self.binaryPath, "-c", self.configPath]
else:
args = [self.binaryPath, "-c", self.configPath]
for key, value in self.override.items():
args.append("-D" + key + "=" + value)
args.append("-N" + str(parentPid))
if self.valgrind:
os.execv("/usr/bin/valgrind", args)
else:
os.execv(self.binaryPath, args)
else:
self.pid = pid
res = self.event.wait(self.startupTimeout)
self.event.clear()
return res
def stop(self, timeout = 15):
kill = lambda pid: (os.kill(pid, signal.SIGKILL))
if self.pid is None:
raise Exception("Nothing to stop")
killTimer = Timer(timeout, kill, [self.pid])
try:
killTimer.start()
os.kill(self.pid, signal.SIGINT)
res = os.waitpid(self.pid, 0)
return res[1]
finally:
killTimer.cancel()
self.pid = None