X-Git-Url: https://git.realraum.at/?a=blobdiff_plain;f=update-web-status.py;h=7c297ce24322b16a1472bdd066104c1ed81b9454;hb=5b856a88fc79e962229d1c8ec58d196ed17b00d0;hp=d8c3af98a7fc2b31d82d7af485caa0cacf25fc8e;hpb=ec263cff7a56d6955bf31fb17216bb888d43ba1e;p=svn42.git diff --git a/update-web-status.py b/update-web-status.py index d8c3af9..7c297ce 100755 --- a/update-web-status.py +++ b/update-web-status.py @@ -1,124 +1,243 @@ #!/usr/bin/python # -*- coding: utf-8 -*- import os +import os.path import sys #import threading import logging +import logging.handlers import urllib import time import signal import re import socket import subprocess +import types +import ConfigParser -#logging.basicConfig(level=logging.INFO,filename='/var/log/tmp/tuer.log',format="%(asctime)s %(message)s",datefmt="%Y-%m-%d %H:%M") -logging.basicConfig(level=logging.ERROR,format="%(asctime)s %(message)s",datefmt="%Y-%m-%d %H:%M") +logger = logging.getLogger() +logger.setLevel(logging.INFO) +lh_syslog = logging.handlers.SysLogHandler(address="/dev/log",facility=logging.handlers.SysLogHandler.LOG_LOCAL2) +lh_syslog.setFormatter(logging.Formatter('update-web-status.py: %(levelname)s %(message)s')) +logger.addHandler(lh_syslog) +lh_stderr = logging.StreamHandler() +logger.addHandler(lh_stderr) -url_open = 'https://www.realraum.at/cgi/status.cgi?pass=jako16&set=%3Chtml%3E%3Cbody%20bgcolor=%22lime%22%3E%3Ccenter%3E%3Cb%3ET%26uuml%3Br%20ist%20Offen%3C/b%3E%3C/center%3E%3C/body%3E%3C/html%3E' -url_closed = 'https://www.realraum.at/cgi/status.cgi?pass=jako16&set=%3Chtml%3E%3Cbody%20bgcolor=%22red%22%3E%3Cb%3E%3Ccenter%3ET%26uuml%3Br%20ist%20Geschlossen%3C/center%3E%3C/b%3E%3C/body%3E%3C/html%3E' -sendxmpp_recipients = 'xro@jabber.tittelbach.at otti@wirdorange.org' -sendxmpp_cmd = 'sendxmpp -u realrauminfo -p 5SPjTdub -j jabber.tittelbach.at -r torwaechter -t ' -sendxmpp_msg_opened="Realraum Tür wurde%s geöffnet" -sendxmpp_msg_closed="Realraum Tür wurde%s geschlossen" -action_by="" +class UWSConfig: + def __init__(self,configfile=None): + self.configfile=configfile + self.config_parser=ConfigParser.ConfigParser() + self.config_parser.add_section('web') + self.config_parser.set('web','cgiuri','https://www.realraum.at/cgi/status.cgi?pass=jako16&set=') + self.config_parser.set('web','htmlopen','
Tür ist Offen
') + self.config_parser.set('web','htmlclosed','
Tür ist Geschlossen
') + self.config_parser.add_section('debug') + self.config_parser.set('debug','enabled',"False") + self.config_mtime=0 + if not self.configfile is None: + try: + cf_handle = open(self.configfile,"r") + cf_handle.close() + except IOError: + self.writeConfigFile() + else: + self.checkConfigUpdates() + + def checkConfigUpdates(self): + global logger + if self.configfile is None: + return + logging.debug("Checking Configfile mtime: "+self.configfile) + try: + mtime = os.path.getmtime(self.configfile) + except (IOError,OSError): + return + if self.config_mtime < mtime: + logging.debug("Reading Configfile") + try: + self.config_parser.read(self.configfile) + self.config_mtime=os.path.getmtime(self.configfile) + except (ConfigParser.ParsingError, IOError), pe_ex: + logging.error("Error parsing Configfile: "+str(pe_ex)) + if self.config_parser.get('debug','enabled') == "True": + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.INFO) + + def writeConfigFile(self): + if self.configfile is None: + return + logging.debug("Writing Configfile "+self.configfile) + try: + cf_handle = open(self.configfile,"w") + self.config_parser.write(cf_handle) + cf_handle.close() + self.config_mtime=os.path.getmtime(self.configfile) + except IOError, io_ex: + logging.error("Error writing Configfile: "+str(io_ex)) + self.configfile=None -def display_open(): + def __getattr__(self, name): + underscore_pos=name.find('_') + if underscore_pos < 0: + raise AttributeError + try: + return self.config_parser.get(name[0:underscore_pos], name[underscore_pos+1:]) + except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): + raise AttributeError + +def popenTimeout1(cmd, pinput, returncode_ok=[0], ptimeout = 20.0, pcheckint = 0.25): + logging.debug("popenTimeout1: starting: " + cmd) try: - #print "accessing %s\n" % self.last_status_set - f = urllib.urlopen(url_open) - f.read() - f.close() - except: - pass + sppoo = subprocess.Popen(cmd, stdin=subprocess.PIPE, shell=True) + sppoo.communicate(input=pinput) + timeout_counter=ptimeout + while timeout_counter > 0: + time.sleep(pcheckint) + timeout_counter -= pcheckint + if not sppoo.poll() is None: + logging.debug("popenTimeout2: subprocess %d finished, returncode: %d" % (sppoo.pid,sppoo.returncode)) + return (sppoo.returncode in returncode_ok) + #timeout reached + logging.error("popenTimeout1: subprocess took too long (>%fs), sending SIGTERM to pid %d" % (ptimeout,sppoo.pid)) + if sys.hexversion >= 0x020600F0: + sppoo.terminate() + else: + subprocess.call(["kill",str(sppoo.pid)]) + time.sleep(1.0) + if sppoo.poll() is None: + logging.error("popenTimeout1: subprocess still alive, sending SIGKILL to pid %d" % (sppoo.pid)) + if sys.hexversion >= 0x020600F0: + sppoo.kill() + else: + subprocess.call(["kill","-9",str(sppoo.pid)]) + return False + except Exception, e: + logging.error("popenTimeout1: "+str(e)) + return False + +def popenTimeout2(cmd, pinput, returncode_ok=[0], ptimeout=21): + logging.debug("popenTimeout2: starting: " + cmd) try: - logging.debug("Starting " + sendxmpp_cmd+sendxmpp_recipients) - sppoo = subprocess.Popen(sendxmpp_cmd+sendxmpp_recipients,stdin=subprocess.PIPE,shell=True) - sppoo.communicate(input=(sendxmpp_msg_opened % action_by)+time.strftime(" (%Y-%m-%d %T)")) + sppoo = subprocess.Popen(cmd, stdin=subprocess.PIPE, shell=True) + if sys.hexversion >= 0x020600F0: + old_shandler = signal.signal(signal.SIGALRM,lambda sn,sf: sppoo.kill()) + else: + old_shandler = signal.signal(signal.SIGALRM,lambda sn,sf: os.system("kill -9 %d" % sppoo.pid)) + signal.alarm(ptimeout) #schedule alarm + sppoo.communicate(input=pinput) sppoo.wait() - logging.debug("XMPP Message about door opening sent") - except: - pass + signal.alarm(0) #disable pending alarms + signal.signal(signal.SIGALRM, old_shandler) + logging.debug("popenTimeout2: subprocess %d finished, returncode: %d" % (sppoo.pid,sppoo.returncode)) + if sppoo.returncode < 0: + logging.error("popenTimeout2: subprocess took too long (>%ds) and pid %d was killed" % (ptimeout,sppoo.pid)) + return (sppoo.returncode in returncode_ok) + except Exception, e: + logging.error("popenTimeout2: "+str(e)) + try: + signal.signal(signal.SIGALRM, old_shandler) + except: + pass + return False -def display_closed(): +def touchURL(url): try: - #print "accessing %s\n" % self.last_status_set - f = urllib.urlopen(url_closed) - f.read() + f = urllib.urlopen(url) + rq_response = f.read() + logging.debug("touchURL: Response "+rq_response) f.close() + return rq_response except Exception, e: - logging.error(str(e)) - pass - try: - logging.debug("Starting " + sendxmpp_cmd+sendxmpp_recipients) - sppoo = subprocess.Popen(sendxmpp_cmd+sendxmpp_recipients,stdin=subprocess.PIPE,shell=True) - sppoo.communicate(input=(sendxmpp_msg_closed % action_by)+time.strftime(" (%Y-%m-%d %T)")) - sppoo.wait() - logging.debug("XMPP Message about door closing sent") - except Exception, e: - logging.error(str(e)) - pass + logging.error("touchURL: "+str(e)) -def exit_handler(signum, frame): - logging.info("Door Status Listener stopping") +def setRealraumHtmlStatus(htmlcode): + htmlcode_escaped = re.sub(r'[^\x30-\x39\x41-\x7E]',lambda m:"%%%x"%ord(m.group(0)),htmlcode) + if touchURL(uwscfg.web_cgiuri + htmlcode_escaped) != htmlcode: + logging.error("setRealraumHtmlStatus: Error setting Status, Output does not match Input") + +def displayOpen(): + setRealraumHtmlStatus(uwscfg.web_htmlopen) + +def displayClosed(): + setRealraumHtmlStatus(uwscfg.web_htmlclosed) + +def exitHandler(signum, frame): + logging.info("Update-Web-Status stopping") try: conn.close() - except Exception, e: - logging.error(str(e)) + except: pass try: sockhandle.close() - except Exception, e: - logging.error(str(e)) + except: pass sys.exit(0) #signals proapbly don't work because of readline -#signal.signal(signal.SIGTERM, exit_handler) -signal.signal(signal.SIGINT, exit_handler) -signal.signal(signal.SIGQUIT, exit_handler) +#signal.signal(signal.SIGTERM, exitHandler) +signal.signal(signal.SIGINT, exitHandler) +signal.signal(signal.SIGQUIT, exitHandler) logging.info("Door Status Listener started") if len(sys.argv) > 1: - socketfile=sys.argv[1] + socketfile = sys.argv[1] else: socketfile = "/var/run/tuer/door_cmd.socket" + if len(sys.argv) > 2: - sendxmpp_recipients = " ".join(sys.argv[2:]) -sockhandle=socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) -re_status = re.compile(r'Status: (\w+), idle') -re_request = re.compile(r'Request: (\w+) (?:Card )?(.+)') + uwscfg = UWSConfig(sys.argv[2]) +else: + uwscfg = UWSConfig() + +#socket.setdefaulttimeout(10.0) #affects all new Socket Connections (urllib as well) +RE_STATUS = re.compile(r'Status: (\w+), idle') +RE_REQUEST = re.compile(r'Request: (\w+) (?:Card )?(.+)') +RE_ERROR = re.compile(r'Error: (.+)') while True: try: + if not os.path.exists(socketfile): + logging.debug("Socketfile '%s' not found, waiting 5 secs" % socketfile) + time.sleep(5) + continue + sockhandle = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sockhandle.connect(socketfile) conn = os.fdopen(sockhandle.fileno()) sockhandle.send("listen\n") sockhandle.send("status\n") while True: line = conn.readline() - logging.info("Got Line: "+line) - m = re_status.match(line) + logging.debug("Got Line: " + line) + + uwscfg.checkConfigUpdates() + + if line == "": + raise Exception("EOF on Socket, daemon seems to have quit") + + m = RE_STATUS.match(line) if not m is None: status = m.group(1) if status == "opened": - display_open() + displayOpen() if status == "closed": - display_closed() - m = re_request.match(line) - if not m is None: - #(rq_action,rq_by) = m.group(1,2) - action_by=" von "+m.group(2) - else: - action_by="" - except Exception, e: - logging.error(str(e)) - try: - conn.close() - except: - pass + displayClosed() + #~ m = RE_REQUEST.match(line) + #~ if not m is None: + #~ #(rq_action,rq_by) = m.group(1,2) + #~ action_by = " von " + m.group(2) + #~ else: + #~ action_by = "" + #~ m = RE_ERROR.match(line) + #~ if not m is None: + #~ errorstr = m.group(1) + #~ #handle Error + except Exception, ex: + logging.error("main: "+str(ex)) try: sockhandle.close() except: pass + conn=None + sockhandle=None time.sleep(5) -