Code

Fix for Python 2.5
[nagixsc.git] / nagixsc / __init__.py
index ba961828b503887a2513e6f48d3295c8526eae68..182e7332b7c6d4704533303ac352e4c39c314ee1 100644 (file)
@@ -8,6 +8,7 @@ import mimetools
 import os
 import random
 import shlex
+import signal
 import socket
 import string
 import subprocess
@@ -20,6 +21,11 @@ def debug(level, verb, string):
 
 ##############################################################################
 
+class ExecTimeoutError(Exception):
+       pass
+
+##############################################################################
+
 def available_encodings():
        return ['base64', 'plain',]
 
@@ -60,7 +66,10 @@ def read_inifile(inifile):
 
 ##############################################################################
 
-def exec_check(host_name, service_descr, cmdline):
+def exec_timeout_handler(signum, frame):
+       raise ExecTimeoutError
+
+def exec_check(host_name, service_descr, cmdline, timeout=None, timeout_returncode=2):
        cmdarray = shlex.split(cmdline)
 
        check = {}
@@ -72,13 +81,30 @@ def exec_check(host_name, service_descr, cmdline):
                check['returncode'] = 127
                return check
 
+       if timeout:
+               signal.signal(signal.SIGALRM, exec_timeout_handler)
+               signal.alarm(timeout)
+
        try:
-               cmd     = subprocess.Popen(cmdarray, stdout=subprocess.PIPE)
+               cmd = subprocess.Popen(cmdarray, stdout=subprocess.PIPE)
                check['output'] = cmd.communicate()[0].rstrip()
                check['returncode'] = cmd.returncode
        except OSError:
                check['output'] = 'Could not execute "%s"' % cmdline
                check['returncode'] = 127
+       except ExecTimeoutError:
+               check['output'] = 'Plugin timed out after %s seconds' % timeout
+               check['returncode'] = timeout_returncode
+
+       if timeout:
+               signal.alarm(0)
+               try:
+                       if sys.version_info >= (2, 6):
+                               cmd.terminate()
+                       else:
+                               os.kill(cmd.pid, 15)
+               except OSError:
+                       pass
 
        check['timestamp'] = datetime.datetime.now().strftime('%s')
        return check
@@ -89,6 +115,18 @@ def exec_check(host_name, service_descr, cmdline):
 def conf2dict(config, opt_host=None, opt_service=None):
        checks = []
 
+       # Read "plugin_timeout" from "[nagixsc]", default "None" (no timeout)
+       try:
+               timeout = config.getint('nagixsc','plugin_timeout')
+       except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+               timeout = None
+
+       # Read "plugin_timeout_returncode" from "[nagixsc]", default "2" (CRITICAL)
+       try:
+               timeout_returncode = config.getint('nagixsc','plugin_timeout_returncode')
+       except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
+               timeout_returncode = 2
+
        # Sections are Hosts (not 'nagixsc'), options in sections are Services
        hosts = config.sections()
        if 'nagixsc' in hosts:
@@ -113,7 +151,7 @@ def conf2dict(config, opt_host=None, opt_service=None):
                # Look for host check
                if '_host_check' in services and not opt_service:
                        cmdline = config.get(host, '_host_check')
-                       check = exec_check(host_name, None, cmdline)
+                       check = exec_check(host_name, None, cmdline, timeout, timeout_returncode)
                        checks.append(check)
 
 
@@ -129,7 +167,7 @@ def conf2dict(config, opt_host=None, opt_service=None):
                        if service[0] != '_':
                                cmdline = config.get(host, service)
 
-                               check = exec_check(host_name, service, cmdline)
+                               check = exec_check(host_name, service, cmdline, timeout, timeout_returncode)
                                checks.append(check)
 
        return checks
@@ -383,6 +421,14 @@ def xml_from_dict(checks, encoding='base64'):
        return xmldoc
 
 
+def xml_merge(xmldocs):
+       checks = []
+       for xmldoc in xmldocs:
+               checks.extend(xml_to_dict(xmldoc))
+       newxmldoc = xml_from_dict(checks)
+       return newxmldoc
+
+
 def check_mark_outdated(check, now, maxtimediff, markold):
        timedelta = now - check['timestamp']
        if timedelta > maxtimediff:
@@ -429,7 +475,7 @@ def daemonize(pidfile=None, stdin='/dev/null', stdout='/dev/null', stderr='/dev/
                if pid > 0:
                        sys.exit(0)
        except OSError, e:
-               sys.stderr.write("1st fork failed: (%d) %sn" % (e.errno, e.strerror))
+               sys.stderr.write("1st fork failed: (%d) %s\n" % (e.errno, e.strerror))
                sys.exit(1)
        # Prepare 2nd fork
        os.chdir("/")
@@ -441,8 +487,18 @@ def daemonize(pidfile=None, stdin='/dev/null', stdout='/dev/null', stderr='/dev/
                if pid > 0:
                        sys.exit(0)
        except OSError, e:
-               sys.stderr.write("2nd fork failed: (%d) %sn" % (e.errno, e.strerror))
+               sys.stderr.write("2nd fork failed: (%d) %s\n" % (e.errno, e.strerror))
                sys.exit(1)
+
+       # Try to write PID file
+       if pidfile:
+               pid = str(os.getpid())
+               try:
+                       file(pidfile, 'w+').write('%s\n' % pid)
+               except IOError:
+                       sys.stderr.write("Could not write PID file, exiting...\n")
+                       sys.exit(1)
+
        # Redirect stdin, stdout, stderr
        sys.stdout.flush()
        sys.stderr.flush()
@@ -453,15 +509,11 @@ def daemonize(pidfile=None, stdin='/dev/null', stdout='/dev/null', stderr='/dev/
        os.dup2(so.fileno(), sys.stdout.fileno())
        os.dup2(se.fileno(), sys.stderr.fileno())
 
-       if pidfile:
-               pid = str(os.getpid())
-               file(pidfile, 'w+').write('%s\n' % pid)
-
        return
 
 ##############################################################################
 
-class MyHTTPServer(BaseHTTPServer.HTTPServer):
+class MyHTTPServer(SocketServer.ForkingMixIn, BaseHTTPServer.HTTPServer):
        def __init__(self, server_address, HandlerClass, ssl=False, sslpemfile=None):
                if ssl:
                        # FIXME: SSL is in Py2.6