diff --git a/data/etc/revpipyload/revpipyload.conf b/data/etc/revpipyload/revpipyload.conf
index 58ea476..ab69a8d 100644
--- a/data/etc/revpipyload/revpipyload.conf
+++ b/data/etc/revpipyload/revpipyload.conf
@@ -7,8 +7,11 @@ plcarguments=
plcuid=1000
plcgid=1000
plcslave=0
+plcslaveacl=
+plcslaveport = 55234
pythonversion=3
xmlrpc=0
+xmlrpcacl =
xmlrpcport=55123
zeroonerror=0
zeroonexit=0
diff --git a/doc/revpipyload.html b/doc/revpipyload.html
index 59ac401..590311d 100644
--- a/doc/revpipyload.html
+++ b/doc/revpipyload.html
@@ -32,7 +32,7 @@ begrenzt werden!
Global Attributes
-| configrsc |
| picontrolreset |
| procimg |
| pyloadverion |
| rapcatalog |
+| configrsc |
| picontrolreset |
| procimg |
| pyloadverion |
| rapcatalog |
| re_ipacl |
Classes
@@ -61,6 +61,9 @@ Classes
Functions
+| _ipmatch |
+Prueft IP gegen ACL List und gibt ACL aus. |
+
| _zeroprocimg |
Setzt Prozessabbild auf NULL. |
@@ -471,6 +474,12 @@ Methods
xml_plcrunning |
Prueft ob das PLC Programm noch lauft. |
+| xml_plcslavestart |
+Startet den PLC Slave Server. |
+
+| xml_plcslavestop |
+Stoppt den PLC Slave Server. |
+
| xml_plcstart |
Startet das PLC Programm. |
@@ -664,6 +673,31 @@ Prueft ob das PLC Programm noch lauft.
True, wenn das PLC Programm noch lauft
+
+
+RevPiPyLoad.xml_plcslavestart
+xml_plcslavestart()
+
+Startet den PLC Slave Server.
+
+- Returns:
+-
+Statuscode:
+ 0: erfolgreich gestartet
+ -1: Nicht aktiv in Konfiguration
+ -2: Laeuft bereits
+
+
+
+RevPiPyLoad.xml_plcslavestop
+xml_plcslavestop()
+
+Stoppt den PLC Slave Server.
+
+- Returns:
+-
+True, wenn stop erfolgreich
+
RevPiPyLoad.xml_plcstart
@@ -830,10 +864,18 @@ Static Methods
RevPiSlave (Constructor)
-RevPiSlave()
+RevPiSlave(acl, port=55234)
Instantiiert RevPiSlave-Klasse.
-
+
+- acl
+-
+Stringliste mit Leerstellen getrennt
+
- port
+-
+Listen Port fuer plc Slaveserver
+
+
RevPiSlave.newlogfile
newlogfile()
@@ -890,7 +932,7 @@ Static Methods
RevPiSlaveDev (Constructor)
-RevPiSlaveDev(devcon, deadtime)
+RevPiSlaveDev(devcon, deadtime, acl)
RevPiSlaveDev.run
@@ -900,6 +942,27 @@ RevPiSlaveDev.run
RevPiSlaveDev.stop
stop()
+
+
+
+_ipmatch
+_ipmatch(ipaddress, dict_acl)
+
+Prueft IP gegen ACL List und gibt ACL aus.
+
+- ipaddress
+-
+zum pruefen
+
- dict_acl
+-
+ACL Dict gegen die IP zu pruefen ist
+
+
+- Returns:
+-
+int() ACL Wert oder -1 wenn nicht gefunden
+
+
diff --git a/eric-revpipyload.api b/eric-revpipyload.api
index 3c7b587..c2279f9 100644
--- a/eric-revpipyload.api
+++ b/eric-revpipyload.api
@@ -49,6 +49,8 @@ revpipyload.RevPiPyLoad.xml_getprocimg?4()
revpipyload.RevPiPyLoad.xml_plcdownload?4(mode="tar", pictory=False)
revpipyload.RevPiPyLoad.xml_plcexitcode?4()
revpipyload.RevPiPyLoad.xml_plcrunning?4()
+revpipyload.RevPiPyLoad.xml_plcslavestart?4()
+revpipyload.RevPiPyLoad.xml_plcslavestop?4()
revpipyload.RevPiPyLoad.xml_plcstart?4()
revpipyload.RevPiPyLoad.xml_plcstop?4()
revpipyload.RevPiPyLoad.xml_plcupload?4(filedata, filename)
@@ -62,13 +64,15 @@ revpipyload.RevPiPyLoad?1()
revpipyload.RevPiSlave.newlogfile?4()
revpipyload.RevPiSlave.run?4()
revpipyload.RevPiSlave.stop?4()
-revpipyload.RevPiSlave?1()
+revpipyload.RevPiSlave?1(acl, port=55234)
revpipyload.RevPiSlaveDev.run?4()
revpipyload.RevPiSlaveDev.stop?4()
-revpipyload.RevPiSlaveDev?1(devcon, deadtime)
+revpipyload.RevPiSlaveDev?1(devcon, deadtime, acl)
+revpipyload._ipmatch?5(ipaddress, dict_acl)
revpipyload._zeroprocimg?5()
revpipyload.configrsc?7
revpipyload.picontrolreset?7
revpipyload.procimg?7
revpipyload.pyloadverion?7
revpipyload.rapcatalog?7
+revpipyload.re_ipacl?7
diff --git a/revpipyload.e4p b/revpipyload.e4p
index eff7f78..bd9eb3b 100644
--- a/revpipyload.e4p
+++ b/revpipyload.e4p
@@ -1,7 +1,7 @@
-
+
en_US
@@ -9,7 +9,7 @@
Python3
Console
Dieser Loader wird über das Init-System geladen und führt das angegebene Pythonprogramm aus. Es ist für den RevolutionPi gedacht um automatisch das SPS-Programm zu starten.
- 0.4.2
+ 0.5.0
Sven Sager
akira@narux.de
diff --git a/revpipyload/revpipyload.py b/revpipyload/revpipyload.py
index 7033e9e..92095d5 100755
--- a/revpipyload/revpipyload.py
+++ b/revpipyload/revpipyload.py
@@ -38,12 +38,11 @@ import signal
import socket
import subprocess
import tarfile
-import warnings
import zipfile
from concurrent import futures
from configparser import ConfigParser
from json import loads as jloads
-from re import match as rematch
+from re import fullmatch as refullmatch
from shutil import rmtree
from sys import stdout as sysstdout
from tempfile import mkstemp
@@ -56,9 +55,26 @@ from xmlrpc.server import SimpleXMLRPCServer
configrsc = None
picontrolreset = "/opt/KUNBUS/piControlReset"
procimg = "/dev/piControl0"
-pyloadverion = "0.4.2"
+pyloadverion = "0.5.0"
rapcatalog = None
+re_ipacl = "(([\\d\\*]{1,3}\\.){3}[\\d\\*]{1,3},[0-1] ?)+"
+
+
+def _ipmatch(ipaddress, dict_acl):
+ """Prueft IP gegen ACL List und gibt ACL aus.
+
+ @param ipaddress zum pruefen
+ @param dict_acl ACL Dict gegen die IP zu pruefen ist
+ @return int() ACL Wert oder -1 wenn nicht gefunden
+
+ """
+ for aclip in sorted(dict_acl, reverse=True):
+ regex = aclip.replace(".", "\\.").replace("*", "\\d{1,3}")
+ if refullmatch(regex, ipaddress) is not None:
+ return str(dict_acl[aclip])
+ return "-1"
+
def _zeroprocimg():
"""Setzt Prozessabbild auf NULL."""
@@ -439,16 +455,26 @@ class RevPiPlc(Thread):
class RevPiSlave(Thread):
- def __init__(self):
- """Instantiiert RevPiSlave-Klasse."""
+ def __init__(self, acl, port=55234):
+ """Instantiiert RevPiSlave-Klasse.
+ @param acl Stringliste mit Leerstellen getrennt
+ @param port Listen Port fuer plc Slaveserver"""
super().__init__()
self.deadtime = 0.5
self._evt_exit = Event()
self.exitcode = None
- self.th_dev = []
+ self._port = port
+ self._th_dev = []
self.zeroonerror = False
self.zeroonexit = False
+ # ACLs aufbereiten
+ self.dict_acl = {}
+ for host in acl.split():
+ aclsplit = host.split(",")
+ self.dict_acl[aclsplit[0]] = \
+ "0" if len(aclsplit) == 1 else aclsplit[1]
+
def newlogfile(self):
"""Konfiguriert die FileHandler auf neue Logdatei."""
pass
@@ -460,9 +486,8 @@ class RevPiSlave(Thread):
self.so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
while not self._evt_exit.is_set():
try:
- self.so.bind(("", 55234))
+ self.so.bind(("", self._port))
except:
- warnings.warn("can not bind socket - retry", Warning)
proginit.logger.warning("can not bind socket - retry")
self._evt_exit.wait(1)
else:
@@ -480,20 +505,30 @@ class RevPiSlave(Thread):
proginit.logger.exception("after accept")
continue
- # Thread starten
- th = RevPiSlaveDev(tup_sock, self.deadtime)
- th.start()
- self.th_dev.append(th)
+ # ACL prüfen
+ aclstatus = _ipmatch(tup_sock[1][0], self.dict_acl)
+ if aclstatus == "-1":
+ tup_sock[0].close()
+ proginit.logger.warning(
+ "host ip '{}' does not match revpiacl - disconnect"
+ "".format(tup_sock[1][0])
+ )
+ else:
+ # Thread starten
+ th = RevPiSlaveDev(tup_sock, self.deadtime, aclstatus)
+ th.start()
+ self._th_dev.append(th)
+
+ # TODO: tote Threads entfernen
# Alle Threads beenden
- for th in self.th_dev:
+ for th in self._th_dev:
th.stop()
# Socket schließen
self.so.close()
self.exitcode = 0
- warnings.resetwarnings()
proginit.logger.debug("leave RevPiSlave.run()")
def stop(self):
@@ -508,8 +543,9 @@ class RevPiSlave(Thread):
class RevPiSlaveDev(Thread):
- def __init__(self, devcon, deadtime):
+ def __init__(self, devcon, deadtime, acl):
super().__init__()
+ self._acl = acl
self.daemon = True
self._deadtime = deadtime
self._devcon, self._addr = devcon
@@ -522,8 +558,15 @@ class RevPiSlaveDev(Thread):
def run(self):
proginit.logger.debug("enter RevPiSlaveDev.run()")
- msgcli = [b'DATA', b'PICT', b'SEND']
- proginit.logger.info("connected from {}".format(self._addr))
+ proginit.logger.info(
+ "got new connection from host {} with acl {}".format(
+ self._addr, self._acl)
+ )
+
+ # CMDs anhand ACL aufbauen
+ msgcli = [b'DATA', b'PICT']
+ if self._acl == "1":
+ msgcli.append(b'SEND')
# Prozessabbild öffnen
fh_proc = open(procimg, "r+b", 0)
@@ -717,10 +760,33 @@ class RevPiPyLoad():
self.globalconfig["DEFAULT"].get("plcworkdir", ".")
self.plcslave = \
int(self.globalconfig["DEFAULT"].get("plcslave", 0))
+
+ # PLC Slave ACL laden und prüfen
+ plcslaveacl = \
+ self.globalconfig["DEFAULT"].get("plcslaveacl", "")
+ if len(plcslaveacl) > 0 and refullmatch(re_ipacl, plcslaveacl) is None:
+ self.plcslaveacl = ""
+ proginit.logger.warning("can not load plcslaveacl - wrong format")
+ else:
+ self.plcslaveacl = plcslaveacl
+
+ self.plcslaveport = \
+ int(self.globalconfig["DEFAULT"].get("plcslaveport", 55234))
self.pythonver = \
int(self.globalconfig["DEFAULT"].get("pythonversion", 3))
self.xmlrpc = \
int(self.globalconfig["DEFAULT"].get("xmlrpc", 0))
+
+ # XML ACL laden und prüfen
+ # TODO: xmlrpcacl auswerten
+ xmlrpcacl = \
+ self.globalconfig["DEFAULT"].get("xmlrpcacl", "")
+ if len(xmlrpcacl) > 0 and refullmatch(re_ipacl, xmlrpcacl) is None:
+ self.xmlrpcacl = ""
+ proginit.logger.warning("can not load xmlrpcacl - wrong format")
+ else:
+ self.xmlrpcacl = xmlrpcacl
+
self.zeroonerror = \
int(self.globalconfig["DEFAULT"].get("zeroonerror", 1))
self.zeroonexit = \
@@ -731,6 +797,10 @@ class RevPiPyLoad():
# PLC Thread konfigurieren
self.plc = self._plcthread()
+ if self.plcslave:
+ self.th_plcslave = RevPiSlave(self.plcslaveacl, self.plcslaveport)
+ else:
+ self.th_plcslave = None
# XMLRPC-Server Instantiieren und konfigurieren
if self.xmlrpc >= 1:
@@ -791,6 +861,10 @@ class RevPiPyLoad():
self.xml_plcupload, "plcupload")
self.xsrv.register_function(
self.xml_plcuploadclean, "plcuploadclean")
+ self.xsrv.register_function(
+ self.xml_plcslavestart, "plcslavestart")
+ self.xsrv.register_function(
+ self.xml_plcslavestop, "plcslavestop")
self.xsrv.register_function(
lambda: os.system(picontrolreset), "resetpicontrol")
self.xsrv.register_function(
@@ -816,31 +890,24 @@ class RevPiPyLoad():
proginit.logger.debug("enter RevPiPyLoad._plcthread()")
th_plc = None
- if self.plcslave:
- # Slaveausfuehrung übergeben
- th_plc = RevPiSlave()
- th_plc.zeroonerror = self.zeroonerror
- th_plc.zeroonexit = self.zeroonexit
+ # Prüfen ob Programm existiert
+ if not os.path.exists(os.path.join(self.plcworkdir, self.plcprog)):
+ proginit.logger.error("plc file does not exists {}".format(
+ os.path.join(self.plcworkdir, self.plcprog)
+ ))
+ return None
- else:
- # Prüfen ob Programm existiert
- if not os.path.exists(os.path.join(self.plcworkdir, self.plcprog)):
- proginit.logger.error("plc file does not exists {}".format(
- os.path.join(self.plcworkdir, self.plcprog)
- ))
- return None
-
- proginit.logger.debug("create PLC watcher")
- th_plc = RevPiPlc(
- os.path.join(self.plcworkdir, self.plcprog),
- self.plcarguments,
- self.pythonver
- )
- th_plc.autoreload = self.autoreload
- th_plc.gid = int(self.globalconfig["DEFAULT"].get("plcgid", 65534))
- th_plc.uid = int(self.globalconfig["DEFAULT"].get("plcuid", 65534))
- th_plc.zeroonerror = self.zeroonerror
- th_plc.zeroonexit = self.zeroonexit
+ proginit.logger.debug("create PLC watcher")
+ th_plc = RevPiPlc(
+ os.path.join(self.plcworkdir, self.plcprog),
+ self.plcarguments,
+ self.pythonver
+ )
+ th_plc.autoreload = self.autoreload
+ th_plc.gid = int(self.globalconfig["DEFAULT"].get("plcgid", 65534))
+ th_plc.uid = int(self.globalconfig["DEFAULT"].get("plcuid", 65534))
+ th_plc.zeroonerror = self.zeroonerror
+ th_plc.zeroonexit = self.zeroonexit
proginit.logger.debug("leave RevPiPyLoad._plcthread()")
return th_plc
@@ -939,6 +1006,10 @@ class RevPiPyLoad():
self.tpe = futures.ThreadPoolExecutor(max_workers=1)
self.tpe.submit(self.xsrv.serve_forever)
+ if self.plcslave:
+ # Slaveausfuehrung übergeben
+ self.th_plcslave.start()
+
if self.autostart:
proginit.logger.debug("starting revpiplc-thread")
if self.plc is not None:
@@ -971,8 +1042,14 @@ class RevPiPyLoad():
proginit.logger.info("stopping revpipyload")
self._exit = True
+ if self.th_plcslave is not None and self.th_plcslave.is_alive():
+ proginit.logger.debug("stopping revpi slave thread")
+ self.th_plcslave.stop()
+ self.th_plcslave.join()
+ proginit.logger.debug("revpi slave thread successfully closed")
+
if self.plc is not None and self.plc.is_alive():
- proginit.logger.debug("stopping revpiplc-thread")
+ proginit.logger.debug("stopping revpiplc thread")
self.plc.stop()
self.plc.join()
proginit.logger.debug("revpiplc thread successfully closed")
@@ -996,8 +1073,11 @@ class RevPiPyLoad():
dc["plcprogram"] = self.plcprog
dc["plcarguments"] = self.plcarguments
dc["plcslave"] = self.plcslave
+ dc["plcslaveacl"] = self.plcslaveacl
+ dc["plcslaveport"] = self.plcslaveport
dc["pythonversion"] = self.pythonver
dc["xmlrpc"] = self.xmlrpc
+ dc["xmlrpcacl"] = self.xmlrpcacl
dc["xmlrpcport"] = \
self.globalconfig["DEFAULT"].get("xmlrpcport", 55123)
dc["zeroonerror"] = self.zeroonerror
@@ -1170,8 +1250,11 @@ class RevPiPyLoad():
"plcprogram": ".+",
"plcarguments": ".*",
"plcslave": "[01]",
+ "plcslaveacl": re_ipacl,
+ "plcslaveport": "[0-9]{,5}",
"pythonversion": "[23]",
"xmlrpc": "[0-3]",
+ "xmlrpcacl": re_ipacl,
"xmlrpcport": "[0-9]{,5}",
"zeroonerror": "[01]",
"zeroonexit": "[01]"
@@ -1180,7 +1263,7 @@ class RevPiPyLoad():
# Werte übernehmen
for key in keys:
if key in dc:
- if rematch(keys[key], str(dc[key])) is None:
+ if refullmatch(keys[key], str(dc[key])) is None:
proginit.logger.error(
"got wrong setting '{}' with value '{}'".format(
key, dc[key]
@@ -1276,6 +1359,36 @@ class RevPiPyLoad():
else:
return False
+ def xml_plcslavestart(self):
+ """Startet den PLC Slave Server.
+
+ @return Statuscode:
+ 0: erfolgreich gestartet
+ -1: Nicht aktiv in Konfiguration
+ -2: Laeuft bereits
+
+ """
+ if self.plcslave:
+ if self.th_plcslave is not None and self.th_plcslave.is_alive():
+ return -2
+ else:
+ self.th_plcslave = RevPiSlave(
+ self.plcslaveacl, self.plcslaveport
+ )
+ self.th_plcslave.start()
+ return 0
+ else:
+ return -1
+
+ def xml_plcslavestop(self):
+ """Stoppt den PLC Slave Server.
+ @return True, wenn stop erfolgreich"""
+ if self.th_plcslave is not None:
+ self.th_plcslave.stop()
+ return True
+ else:
+ return False
+
if __name__ == "__main__":
root = RevPiPyLoad()
diff --git a/setup.py b/setup.py
index 64cce8f..2a9f059 100644
--- a/setup.py
+++ b/setup.py
@@ -27,7 +27,7 @@ setup(
license="LGPLv3",
name="revpipyload",
- version="0.4.2",
+ version="0.5.0",
scripts=["data/revpipyload"],