2 # -*- encoding: utf-8; py-indent-offset: 4 -*-
3 # +------------------------------------------------------------------+
4 # | ____ _ _ __ __ _ __ |
5 # | / ___| |__ ___ ___| | __ | \/ | |/ / |
6 # | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / |
7 # | | |___| | | | __/ (__| < | | | | . \ |
8 # | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ |
10 # | Copyright Mathias Kettner 2014 mk@mathias-kettner.de |
11 # +------------------------------------------------------------------+
13 # This file is part of Check_MK.
14 # The official homepage is at http://mathias-kettner.de/check_mk.
16 # check_mk is free software; you can redistribute it and/or modify it
17 # under the terms of the GNU General Public License as published by
18 # the Free Software Foundation in version 2. check_mk is distributed
19 # in the hope that it will be useful, but WITHOUT ANY WARRANTY; with-
20 # out even the implied warranty of MERCHANTABILITY or FITNESS FOR A
21 # PARTICULAR PURPOSE. See the GNU General Public License for more de-
22 # tails. You should have received a copy of the GNU General Public
23 # License along with GNU Make; see the file COPYING. If not, write
24 # to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 # Boston, MA 02110-1301 USA.
29 opt_debug
= "-d" in sys
.argv
or "--debug" in sys
.argv
31 # The following settings are tried to be autodetected
33 'apache_config_dir': "Configuration directory of Apache",
34 'cgiurl': "URL of Nagios CGI programs",
35 'check_icmp_path': "Path to check_icmp Plugin",
36 'htdocsdir': "Directory of Nagios' static web pages",
37 'htpasswd_file': "File of Nagios' HTTP users and passwords",
38 'livestatus_in_nagioscfg': "Wether nagios.cfg loads livestatus module",
39 'nagconfdir': "Directory of Nagios objects (see cfg_dir)",
40 'nagiosaddconf': "Snippet to add to nagios.cfg",
41 'nagios_auth_name': "HTTP Basic AuthName for Nagios",
42 'nagios_binary': "Absolute path to Nagios binary itself",
43 'nagios_version': "Nagios version",
44 'nagios_config_file': "Absolute path to nagios.cfg",
45 'nagios_startscript': "Nagios startskript (usually in /etc/init.d)",
46 'nagios_status_file': "Absolute path to Nagios' status.dat",
47 'nagiosurl': "Base-URL of Nagios web pages",
48 'nagiosuser': "System user running the Nagios process",
49 'nagpipe': "Absolute path to Nagios' command pipe (nagios.cmd)",
50 'check_result_path': "Absolute path to Nagios' checkresults directory",
51 'pnp_url': "URL of PNP4Nagios",
52 'pnpconffile': "PNP4Nagios configuration file for its PHP pages",
53 'pnphtdocsdir': "PNP4Nagios www document root directory",
54 'pnptemplates': "directory of PHP templates for PNP4Nagios",
55 'rrddir': "Base directory where RRDs are stored",
56 'wwwgroup': "Common group of Nagios and Apache",
57 'wwwuser': "System user apache runs with",
60 # Ich suche nach Prozessen mit folgenden Kriterien:
61 # - Der Text 'nagios' im Namen
62 # - eines der Worte muss '-d' oder '--daemon' sein.
63 # - Das letzte Wort ist der Name einer existierenden Datei
65 # /usr/sbin/nagios3 -d /etc/nagios3/nagios.cfg
68 class Sorry(Exception):
69 def __init__(self
, reason
):
71 super(Sorry
, self
).__init
__(reason
)
74 def find_pid_and_configfile():
75 procs
= os
.popen("ps ax -o pid,ppid,user,command").readlines()
78 if line
.find('nagios.cfg') >= 0 or line
.find('icinga.cfg') >= 0:
79 pids
.append(line
.split()[0])
82 if line
.find('nagios.cfg') >= 0 or line
.find('icinga.cfg') >= 0:
85 if '-d' in words
or '--daemon' in words
:
89 configfile
= words
[-1]
91 continue # this is not the main thread. It has
92 # another process as parent!
93 if os
.path
.exists(configfile
):
94 return int(pid
), user
, configfile
95 except Exception as e
:
99 raise Sorry("Cannot find Nagios/Icinga process. Is it running?")
102 def find_apache_properties(nagiosuser
, nagios_htdocs_dir
):
105 # Search in running processes for Apache
106 for line
in os
.popen("ps ax -o pid,user,command").readlines():
108 if len(parts
) >= 3 and \
109 (parts
[2].endswith("/apache2") or \
110 parts
[2].endswith("/httpd") or \
111 parts
[2].endswith("/httpd2-prefork") or \
112 parts
[2].endswith("/httpd2-worker")
116 apache_pid
= int(parts
[0])
119 raise Exception("Cannot find Apache process. Is it running?")
121 def scan_apacheconf(apache_conffile
):
123 if apache_conffile
[0] != '/':
124 apache_conffile
= httpd_root
+ "/" + apache_conffile
126 for line
in file(apache_conffile
):
127 parts
= line
.strip().split()
128 if len(parts
) == 2 and parts
[0].lower() == "include":
129 if parts
[1].endswith("/") or parts
[1].endswith("/*.conf"):
130 confdir
= "/".join(parts
[1].split("/")[:-1])
131 if confdir
[0] != "/":
132 confdir
= httpd_root
+ "/" + confdir
133 if not os
.path
.exists(confdir
):
135 confdirs
.append(confdir
) # put at front of list
138 confdirs
+= scan_apacheconf(parts
[1]) # recursive scan
145 nagios_htpasswd_file
= None
146 nagios_auth_name
= None
147 apache_binary
= process_executable(apache_pid
)
148 apache_conffile
= None
149 apache_confdir
= None
151 for line
in os
.popen("%s -V 2>&1" % apache_binary
): # nosec
153 if parts
[0] == "-D" and len(parts
) > 1:
154 p
= parts
[1].split("=")
155 if len(p
) == 2 and p
[0] == "SERVER_CONFIG_FILE":
156 apache_conffile
= p
[1].replace('"', "")
157 elif len(p
) == 2 and p
[0] == "HTTPD_ROOT":
158 httpd_root
= p
[1].replace('"', "")
160 confdirs
= scan_apacheconf(apache_conffile
)
162 if len(confdirs
) > 0:
163 apache_confdir
= confdirs
[0]
166 if dir.endswith("/conf.d"):
169 # Search for Nagios configuration. We are interested
170 # in the authentication configuration. Most Nagios
171 # installations use HTTP basic auth with a htpasswd
172 # user file. We want to use that user file for the
173 # check_mk web pages.
175 def remove_quotes(text
):
179 if text
[-1] in '"\'':
188 for confdir
in confdirs
:
189 for fn
in os
.listdir(confdir
):
191 conffile
= file(confdir
+ "/" + fn
)
195 for line
in conffile
:
196 if nagios_htdocs_dir
in line
:
199 if len(parts
) == 2 and parts
[0].lower() == "authuserfile":
200 path
= remove_quotes(parts
[1])
201 if os
.path
.exists(path
):
202 new_auth_files
.append(path
)
203 if len(parts
) > 1 and parts
[0].lower() == "authname":
204 parts
= line
.split(None, 1)
205 new_auth_names
.append(remove_quotes(parts
[1].strip()))
207 if len(parts
) > 1 and parts
[0].lower().startswith(
208 "<directory") and "pnp4nagios" in line
:
209 cleanedup
= line
.replace("<", "").replace(">", "").replace(
211 cleanedup
= cleanedup
[9:]
212 dir = cleanedup
.strip()
213 if os
.path
.exists(dir) and os
.path
.exists(
214 dir + "/application/config/config.php"):
215 result
['pnphtdocsdir'] = dir
216 result
['pnptemplates'] = dir + "/templates"
217 except Exception as e
:
220 auth_names
+= new_auth_names
221 auth_files
+= new_auth_files
223 except Exception as e
:
225 if len(auth_files
) > 0:
226 nagios_htpasswd_file
= auth_files
[0]
227 if len(auth_names
) > 0:
228 nagios_auth_name
= auth_names
[0]
236 apache_confdir
= None
237 nagios_htpasswd_file
= None
239 www_groups
= os
.popen("id -nG " + wwwuser
).read().split() # nosec
240 nagios_groups
= os
.popen("id -nG " + nagiosuser
).read().split() # nosec
241 common_groups
= [g
for g
in www_groups
if g
in nagios_groups
]
242 if len(common_groups
) > 1:
243 if 'nagios' in common_groups
:
244 common_group
= 'nagios'
245 elif 'icinga' in common_groups
:
246 common_group
= 'icinga'
248 common_group
= common_groups
[0]
249 elif len(common_groups
) == 1:
250 common_group
= common_groups
[0]
254 return wwwuser
, common_group
, apache_confdir
, nagios_htpasswd_file
, nagios_auth_name
257 def process_environment(pid
):
258 # Umgebung des Prozesses bestimmen. Ich brauch das nicht,
259 # aber der folgende Code ist einfach cool, oder?
262 for line
in file("/proc/%d/environ" % pid
).read().split("\0"):
264 var
, value
= entry
.split('=', 1)
268 raise Sorry("Cannot get environment of process %d. Aren't you root?" % pid
)
271 def process_executable(pid
):
273 return os
.readlink("/proc/%d/exe" % pid
).split(" ", 1)[0]
275 raise Sorry("Cannot get executable of process %d. Aren't you root?" % pid
)
280 # Liste der offenen Dateien. Das ist schon nuetzlicher,
281 # denn hier sieht man z.B. die Commandpipe
282 procpath
= "/proc/%d/fd" % pid
283 return [os
.readlink(procpath
+ "/" + entry
) for entry
in os
.listdir(procpath
)]
285 raise Sorry("Cannot get open files of process %d. Aren't you root?" % pid
)
288 def find_pipes(filenames
):
292 mode
= os
.stat(f
)[stat
.ST_MODE
]
293 if stat
.S_ISFIFO(mode
):
300 def parse_nagios_config(configfile
):
302 for line
in file(configfile
):
304 if line
== "" or line
[0] == '#':
307 key
, value
= line
.split('=', 1)
308 conf
.append((key
, value
))
310 pass # ignore invalid line (as Nagios seems to do)
316 # Jetzt will ich noch das Verzeichnis fuer die Schablonen
317 # von PNP finden. Ich erkenne es daran, dass es ein Verzeichnis
318 # ist, in dem 'templates' und 'templates.dist' liegen. Dieses
319 # Verzeichnis liegt hoffentlich innerhalb der Webseite von
320 # Nagios selbst. Dieser Pfad ist in cgi.cfg festgelegt. Das ganze
321 # klappt nur bei PNP 0.4
322 if 'pnptemplates' not in result
:
326 def func(arg
, dirname
, names
):
327 if 'templates' in names
and 'templates.dist' in names
:
328 found
.append(dirname
+ "/templates")
330 os
.path
.walk(cgiconf
['physical_html_path'], func
, None)
331 result
['pnptemplates'] = found
[0]
332 if 'pnphtdocsdir' not in result
:
333 result
['pnphtdocsdir'] = result
['pnptemplates'].rsplit('/', 1)[0]
337 # Suche die Konfigurationsdatei von PNP4Nagios. Denn ich will
338 # den Eintrag finden, der auf die RRDs zeigt. Den braucht
339 # check_mk für das direkte Eintragen in die RRD-Datenbanken
341 pnppath
= os
.path
.dirname(result
['pnptemplates'])
342 index_php
= pnppath
+ "/index.php"
343 for line
in file(index_php
):
345 # $config = "/usr/local/nagios/etc/pnp/config";
346 if line
.startswith('$config =') and line
.endswith('";'):
347 pnpconffile
= line
.split('"')[1] + ".php"
348 result
['pnpconffile'] = pnpconffile
349 result
['pnpconfdir'] = pnpconffile
.rsplit("/", 1)[0]
356 if 'pnpconffile' not in result
:
357 kohanaconf
= result
['pnphtdocsdir'] + "/application/config/config.php"
358 if os
.path
.exists(kohanaconf
):
359 for line
in file(kohanaconf
):
361 if not line
.startswith("#") and "pnp_etc_path" in line
:
362 last
= line
.split('=')[-1].strip()
363 dir = last
.replace("'", "").replace(";", "").replace('"', "")
364 if os
.path
.exists(dir):
365 result
['pnpconfdir'] = dir
366 result
['pnpconffile'] = dir + "/config.php"
372 for line
in file(result
['pnpconffile']):
374 if line
.startswith("$conf['rrdbase']") and line
.endswith('";'):
375 rrddir
= line
.split('"')[1]
376 if rrddir
.endswith('/'):
378 result
['rrddir'] = rrddir
379 elif (line
.startswith("$conf['base_url']")
380 or line
.startswith("$conf['pnp_base']")) \
381 and line
.endswith(";"):
382 pnp_url
= line
.split('"')[1]
383 if not pnp_url
.endswith("/"):
385 result
["pnp_url"] = pnp_url
391 site
= os
.getenv("OMD_SITE")
392 root
= os
.getenv("OMD_ROOT")
393 if not site
or not root
:
397 'apache_config_dir': root
+ "/etc/apache/conf.d",
398 'cgiurl': "/" + site
+ "/nagios/cgi-bin/",
399 'check_icmp_path': root
+ "/lib/nagios/plugins/check_icmp",
400 'htdocsdir': root
+ "/share/nagios/htdocs",
401 'htpasswd_file': root
+ "/etc/htpasswd",
402 'livestatus_in_nagioscfg': False,
403 'nagconfdir': root
+ "/etc/nagios/conf.d",
405 'nagios_auth_name': "OMD Monitoring Site " + site
,
406 'nagios_binary': root
+ "/bin/nagios",
407 'nagios_config_file': root
+ "/tmp/nagios/nagios.cfg",
408 'nagios_startscript': root
+ "/etc/init.d/nagios",
409 'nagios_status_file': root
+ "/var/nagios/status.dat",
410 'nagiosurl': "/" + site
+ "/nagios/",
412 'nagpipe': root
+ "/tmp/run/nagios.cmd",
413 'check_result_path': root
+ "/tmp/nagios/checkresults",
414 'pnp_url': "/" + site
+ "/pnp4nagios/",
415 'pnpconffile': root
+ "/etc/pnp4nagios/config.php",
416 'pnphtdocsdir': root
+ "/share/pnp4nagios/htdocs",
417 'pnptemplates': root
+ "/local/share/check_mk/pnp-templates",
418 'rrddir': root
+ "/var/pnp4nagios/perfdata",
425 # _ __ ___ __ _(_)_ __
426 # | '_ ` _ \ / _` | | '_ \
427 # | | | | | | (_| | | | | |
428 # |_| |_| |_|\__,_|_|_| |_|
432 result
= detect_omd()
436 pid
, nagiosuser
, configfile
= find_pid_and_configfile()
437 nagios_dir
= os
.path
.dirname(configfile
)
438 result
['nagios_config_file'] = configfile
439 result
['nagiosuser'] = nagiosuser
440 pipes
= find_pipes(open_files(pid
))
442 result
['nagpipe'] = pipes
[0]
445 result
['nagios_binary'] = process_executable(pid
)
448 result
['nagios_version'] = ""
449 for line
in os
.popen(result
["nagios_binary"] + " --version 2>/dev/null"): # nosec
450 if line
.startswith("Nagios Core") or line
.startswith("Icinga Core"):
451 result
['nagios_version'] = line
.split()[2]
453 # Path to startscript
454 for path
in ['/etc/init.d/nagios', '/etc/init.d/nagios3', '/etc/init.d/icinga']:
455 if os
.path
.exists(path
):
456 result
['nagios_startscript'] = path
459 nagconf
= parse_nagios_config(configfile
)
460 nagconf_dict
= dict(nagconf
)
461 if "check_result_path" in nagconf_dict
:
462 result
['check_result_path'] = nagconf_dict
['check_result_path']
465 cgifile
= os
.path
.dirname(configfile
) + "/cgi.cfg"
466 cgiconf
= dict(parse_nagios_config(cgifile
))
467 result
['htdocsdir'] = cgiconf
['physical_html_path']
471 # Suche nach cfg_dir Direktiven. Wir suchen
472 # einen flauschigen Platz fuer unsere Konfigdateien
473 cfg_dirs
= [value
for key
, value
in nagconf
if key
== 'cfg_dir']
474 if len(cfg_dirs
) > 0:
475 # Wenn es mehrere gibt, bevorzuge ich das, das im gleichen
476 # Verzeichnis, wie die Nagios-Konfigdatei selbst liegt.
477 # Debian legt ein cfg_dir fuer die Plugins an....
478 if len(cfg_dirs
) == 1:
479 result
['nagconfdir'] = cfg_dirs
[0]
481 dir = os
.path
.dirname(configfile
)
483 if os
.path
.dirname(d
) == dir:
484 result
['nagconfdir'] = d
487 result
['nagconfdir'] = cfg_dirs
[0]
489 # Mist. Kein cfg_dir in nagios.cfg. Das ist z.B. bei
490 # der immer noch verbreiteten Defaultkonfig der Fall.
491 # Wir legen einfach selbst eins fest und hängen das
492 # eigenmächtig hinten an die Config an
493 nagconfdir
= nagios_dir
+ "/check_mk.d"
494 result
['nagconfdir'] = nagconfdir
495 result
['nagiosaddconf'] = "cfg_dir=" + nagconfdir
497 # Find path to status.dat, the Nagios status file. We
498 # need that for the check_mk web pages. Normally the
499 # path is configured in nagios.cfg. If no - we still
500 # have a chance by parsing the output of nagios3stats.
501 nagios_status_file
= nagconf_dict
.get("status_file")
502 if not nagios_status_file
:
503 for stats_name
in ["stats", "tats"]:
505 stats_bin
= result
['nagios_binary'] + stats_name
506 for line
in os
.popen(stats_bin
+ " 2>/dev/null"): # nosec
507 if line
.startswith("Status File:"):
509 nagios_status_file
= parts
[-1]
511 elif line
.startswith("Error reading status file"):
513 nagios_status_file
= parts
[-1][1:-1]
518 if nagios_status_file
:
519 result
['nagios_status_file'] = nagios_status_file
521 # Ermittle $USER1$ Variablen, da sie in den Plugin-Pfaden
522 # auftauchen koennen.
525 for line
in file(nagconf_dict
['resource_file']):
527 if line
.startswith('$') and '=' in line
:
528 varname
, value
= line
.split('=', 1)
529 uservars
[varname
.strip()] = value
.strip()
533 # Suche nach einem Eintrag zum Laden des livestatus
534 # Moduls. Er darf auch auskommentiert sein. Dann lassen
535 # wir den Benutzer damit in Ruhe
537 for line
in file(configfile
):
538 if "broker_module=" in line
and "/livestatus.o" in line
:
541 result
['livestatus_in_nagioscfg'] = found
543 # Jetzt wird's schwieriger: Ich suche nach check_icmp.
544 # Ich will keinen find machen, da das erstens ewig
545 # dauern kann und zweitens eventl. eine falsche Stelle
546 # findet, z.B. innerhalb eines ausgepackten und kompilierten
547 # Quellcodes der nagios-plugins. Daher suche ich in
548 # allen Objektdateien von Nagios nach command_line.
549 # Damit ermittle ich alle Verzeichnisse, in denen Plugins
550 # liegen. Dort suche ich dann nach check_icmp. Zur Sicherheit
551 # suche ich aber auch unter '/usr/lib/nagios' und '/usr/local/nagios/libexec'
552 # und '/usr/local/nagios/plugins'
555 os
.path
.walk(dir, lambda x
, dirname
, names
: found
.append((dirname
, names
)), None)
557 for dirname
, names
in found
:
559 if name
.endswith(".cfg"):
560 path
= dirname
+ "/" + name
562 for line
in file(path
):
563 if line
.strip() == '':
565 parts
= line
.strip().split()
566 if parts
[0] == "command_line":
568 for var
, value
in uservars
.items():
569 path
= path
.replace(var
, value
)
570 if path
.startswith('/') and path
not in plugin_paths
:
571 plugin_paths
.append(path
)
575 for dir in plugin_paths
+ \
576 [ '/usr/lib/nagios/plugins',
577 '/usr/lib64/nagios/plugins',
578 '/usr/local/nagios/libexec',
579 '/usr/local/nagios/plugins' ]:
581 mode
= os
.stat(dir)[stat
.ST_MODE
]
582 if not stat
.S_ISDIR(mode
):
583 dir = os
.path
.dirname(dir)
584 filenames
= os
.listdir(dir)
586 for filename
in filenames
:
587 if filename
== 'check_icmp':
588 result
['check_icmp_path'] = dir + '/' + filename
593 # Die Basis-Url fuer Nagios ist leider auch nicht immer
596 result
['nagiosurl'] = cgiconf
['url_html_path']
597 result
['cgiurl'] = result
['nagiosurl'] + "/cgi-bin"
601 # Suche eine Gruppe, die Nagios mit dem Apache gemeinsam
602 # hat. Diese brauchen wir z.B. für logwatch
604 wwwuser
, wwwgroup
, apache_confdir
, nagios_htpasswd_file
, nagios_auth_name
= \
605 find_apache_properties(nagiosuser
, result
['htdocsdir'])
607 result
['wwwuser'] = wwwuser
609 result
['wwwgroup'] = wwwgroup
611 result
['apache_config_dir'] = apache_confdir
612 if nagios_htpasswd_file
:
613 result
['htpasswd_file'] = nagios_htpasswd_file
615 result
['nagios_auth_name'] = nagios_auth_name
616 except Exception as e
:
617 sys
.stderr
.write("\033[1;41;35m Cannot determine Apache properties. \033[0m\n"
622 print "# Result of autodetection"
623 for var
, value
in result
.items():
625 descr
= target_values
.get(var
)
629 print "# (unknown value)"
630 print "%s='%s'" % (var
, value
)
632 for var
, descr
in target_values
.items():
633 if var
not in result
:
636 print "# NOT DETECTED: %s" % var
639 sys
.stderr
.write("\033[1;41;35m Sorry: %s \033[0m\n" % e
.reason
)
642 except Exception as e
:
646 sys
.stderr
.write("* Sorry, something unexpected happened: %s\n\n" % e
)
648 traceback
.print_exc()