Skip to content

Commit 2cc9aaa

Browse files
author
Sebastian
committed
Initialized daemon
1 parent 6de4038 commit 2cc9aaa

File tree

6 files changed

+199
-13
lines changed

6 files changed

+199
-13
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,8 @@ The connection between RTOC server and client can be encrypted end-to-end (DES)
245245
- [Jsonsocket from mdebbar](https://github.com/mdebbar/jsonsocket)
246246
- [Taurus PyQtGraph](https://github.com/taurus-org/taurus_pyqtgraph.git)
247247
- [ImportCode script from avtivestate.com](http://code.activestate.com/recipes/82234-importing-a-dynamically-generated-module/)
248-
- [VC820Py from adnidor (for HoldPeak_VC820 plugin)](https://github.com/adnidor/vc820py)
249248
- [PyQt5 CSV-Editor](https://python-forum.io/Thread-Read-Write-CSV-Qt5)
249+
- [Python Daemon](https://web.archive.org/web/20160320091458/http://www.jejik.com/files/examples/daemon3x.py)
250250

251251
All icons used in this software (including plugins) are kindly provided by [Icons8](www.icons8.com)
252252

RTOC.service

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[Unit]
2+
Description=RTOC Daemon
3+
4+
[Service]
5+
Type=simple
6+
ExecStart=/usr/bin/python3 -m RTOC -s start
7+
WorkingDirectory=/opt/project/
8+
Environment=API_KEY=123456789
9+
Environment=API_PASS=password
10+
Restart=always
11+
RestartSec=2
12+
13+
[Install]
14+
WantedBy=sysinit.target

RTOC/RTOC.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from .data.RTPlotWidget import RTPlotWidget
2121
from .data.Actions import Actions
2222
from . import RTOC_Import
23+
from .data.Daemon import Daemon
24+
2325
except ImportError:
2426
import RTLogger
2527
import RTOC_Import
@@ -30,10 +32,11 @@
3032
from data.eventWidget import EventWidget
3133
from data.RTPlotWidget import RTPlotWidget
3234
from data.Actions import Actions
35+
from data.Daemon import Daemon
3336

3437
if os.name == 'nt':
3538
import ctypes
36-
myappid = 'mycompany.myproduct.subproduct.version' # arbitrary string
39+
myappid = 'RTOC.1.9.7.3' # arbitrary string
3740
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
3841

3942
try:
@@ -586,6 +589,16 @@ def toggleDragView(self, status=True):
586589
self.drag_content.hide()
587590

588591

592+
class RTOCDaemon(Daemon):
593+
def __init__(self, pidfile, port=5050):
594+
self.pidfile = pidfile
595+
self.port = 5050
596+
597+
def run(self):
598+
# Or simply merge your code with MyDaemon.
599+
logger = RTLogger(True, self.port)
600+
601+
589602
class RTOC_TCP(QtWidgets.QMainWindow):
590603
def __init__(self):
591604
super(RTOC_TCP, self).__init__()
@@ -735,11 +748,21 @@ def main():
735748
'RTOC.py [-h, -s] [-r <Remoteadress>]\n -h: Hilfe\n-s: TCP-Server ohne GUI\n-r <Remoteadresse>: TCP-Client zu RTOC-Server\n-p: Starte TCP-Server auf anderem Port (Standart: 5050)')
736749
sys.exit()
737750
elif opt == '-s':
738-
logger = RTLogger.RTLogger(True, port)
739-
#runInBackground()
740-
while logger.run:
741-
time.sleep(1)
742-
sys.exit(0)
751+
# logger = RTLogger.RTLogger(True, port)
752+
# #runInBackground()
753+
# while logger.run:
754+
# time.sleep(1)
755+
# sys.exit(0)
756+
command = arg
757+
daemon = RTOCDaemon('/tmp/RTOCDaemon.pid')
758+
if command == 'stop':
759+
daemon.stop()
760+
elif command == 'restart':
761+
daemon.restart()
762+
elif command == 'start':
763+
daemon.start()
764+
else:
765+
print('Unknown server command: '+str(command)+'\nUse "start", "stop" or "restart"')
743766
elif opt in ("-r", "--remote"):
744767
remotepath = arg
745768
startRemoteRTOC(remotepath)

RTOC/__main__.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,21 @@
1010
try:
1111
from .RTOC import RTOC, RTOC_TCP
1212
from .RTLogger import RTLogger
13+
from .data.Daemon import Daemon
1314
except:
1415
from RTOC import RTOC, RTOC_TCP
1516
from RTLogger import RTLogger
17+
from data.Daemon import Daemon
18+
19+
20+
class RTOCDaemon(Daemon):
21+
def __init__(self, pidfile, port=5050):
22+
self.pidfile = pidfile
23+
self.port = 5050
24+
25+
def run(self):
26+
# Or simply merge your code with MyDaemon.
27+
logger = RTLogger(True, self.port)
1628

1729

1830
def main():
@@ -21,7 +33,7 @@ def main():
2133
startRTOC()
2234
else:
2335
for opt, arg in opts:
24-
if opt == '-p':
36+
if opt in ('-p', '--port'):
2537
port = int(arg)
2638
break
2739
else:
@@ -32,17 +44,24 @@ def main():
3244
'RTOC.py [-h, -s] [-r <Remoteadress>]\n -h: Hilfe\n-s: TCP-Server ohne GUI\n-r <Remoteadresse>: TCP-Client zu RTOC-Server\n-p: Starte TCP-Server auf anderem Port (Standart: 5050)')
3345
sys.exit()
3446
elif opt == '-s':
35-
logger = RTLogger(True, port)
36-
#runInBackground()
37-
while logger.run:
38-
time.sleep(1)
39-
sys.exit(0)
47+
command = arg
48+
daemon = RTOCDaemon('/tmp/RTOCDaemon.pid')
49+
if command == 'stop':
50+
daemon.stop()
51+
elif command == 'restart':
52+
daemon.restart()
53+
elif command == 'start':
54+
daemon.start()
55+
else:
56+
print('Unknown server command: '+str(command)+'\nUse "start", "stop" or "restart"')
57+
4058
elif opt in ("-r", "--remote"):
4159
remotepath = arg
4260
startRemoteRTOC(remotepath)
4361
sys.exit(0)
4462
startRTOC(None, port)
4563

64+
4665
def setStyleSheet(app, myapp):
4766
if os.name == 'posix':
4867
type = 'QDarkStyle'

RTOC/data/Daemon.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""Generic linux daemon base class for python 3.x.
2+
3+
Source: https://web.archive.org/web/20160320091458/http://www.jejik.com/files/examples/daemon3x.py"""
4+
5+
import sys
6+
import os
7+
import time
8+
import atexit
9+
import signal
10+
11+
12+
class Daemon:
13+
"""A generic daemon class.
14+
15+
Usage: subclass the daemon class and override the run() method."""
16+
17+
def __init__(self, pidfile):
18+
self.pidfile = pidfile
19+
20+
def daemonize(self):
21+
"""Deamonize class. UNIX double fork mechanism."""
22+
23+
try:
24+
pid = os.fork()
25+
if pid > 0:
26+
# exit first parent
27+
sys.exit(0)
28+
except OSError as err:
29+
sys.stderr.write('fork #1 failed: {0}\n'.format(err))
30+
sys.exit(1)
31+
32+
# decouple from parent environment
33+
os.chdir('/')
34+
os.setsid()
35+
os.umask(0)
36+
37+
# do second fork
38+
try:
39+
pid = os.fork()
40+
if pid > 0:
41+
42+
# exit from second parent
43+
sys.exit(0)
44+
except OSError as err:
45+
sys.stderr.write('fork #2 failed: {0}\n'.format(err))
46+
sys.exit(1)
47+
48+
# redirect standard file descriptors
49+
sys.stdout.flush()
50+
sys.stderr.flush()
51+
si = open(os.devnull, 'r')
52+
so = open(os.devnull, 'a+')
53+
se = open(os.devnull, 'a+')
54+
55+
os.dup2(si.fileno(), sys.stdin.fileno())
56+
os.dup2(so.fileno(), sys.stdout.fileno())
57+
os.dup2(se.fileno(), sys.stderr.fileno())
58+
59+
# write pidfile
60+
atexit.register(self.delpid)
61+
62+
pid = str(os.getpid())
63+
with open(self.pidfile, 'w+') as f:
64+
f.write(pid + '\n')
65+
66+
def delpid(self):
67+
os.remove(self.pidfile)
68+
69+
def start(self):
70+
"""Start the daemon."""
71+
72+
# Check for a pidfile to see if the daemon already runs
73+
try:
74+
with open(self.pidfile, 'r') as pf:
75+
76+
pid = int(pf.read().strip())
77+
except IOError:
78+
pid = None
79+
80+
if pid:
81+
message = "pidfile {0} already exist. " + \
82+
"Daemon already running?\n"
83+
sys.stderr.write(message.format(self.pidfile))
84+
sys.exit(1)
85+
86+
# Start the daemon
87+
self.daemonize()
88+
self.run()
89+
90+
def stop(self):
91+
"""Stop the daemon."""
92+
93+
# Get the pid from the pidfile
94+
try:
95+
with open(self.pidfile, 'r') as pf:
96+
pid = int(pf.read().strip())
97+
except IOError:
98+
pid = None
99+
100+
if not pid:
101+
message = "pidfile {0} does not exist. " + \
102+
"Daemon not running?\n"
103+
sys.stderr.write(message.format(self.pidfile))
104+
return # not an error in a restart
105+
106+
# Try killing the daemon process
107+
try:
108+
while 1:
109+
os.kill(pid, signal.SIGTERM)
110+
time.sleep(0.1)
111+
except OSError as err:
112+
e = str(err.args)
113+
if e.find("No such process") > 0:
114+
if os.path.exists(self.pidfile):
115+
os.remove(self.pidfile)
116+
else:
117+
print(str(err.args))
118+
sys.exit(1)
119+
120+
def restart(self):
121+
"""Restart the daemon."""
122+
self.stop()
123+
self.start()
124+
125+
def run(self):
126+
"""You should override this method when you subclass Daemon.
127+
128+
It will be called after the process has been daemonized by
129+
start() or restart()."""

docs/_build/html/debug.log

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
[0106/193934.456:ERROR:registration_protocol_win.cc(84)] TransactNamedPipe: Die Pipe wurde beendet. (0x6D)
2+
[0107/010420.291:ERROR:registration_protocol_win.cc(84)] TransactNamedPipe: Die Pipe wurde beendet. (0x6D)

0 commit comments

Comments
 (0)