Monitorix/monitorix

526 lines
15 KiB
Plaintext
Raw Normal View History

2012-10-22 15:26:37 +01:00
#!/usr/bin/env perl
#
# Monitorix - A lightweight system monitoring tool.
#
2013-01-04 08:27:05 +00:00
# Copyright (C) 2005-2013 by Jordi Sanfeliu <jordi@fibranet.cat>
2012-10-22 15:26:37 +01:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
require 5.006;
2013-01-04 08:27:05 +00:00
use strict;
use warnings;
use FindBin qw($Bin);
2013-01-17 16:56:38 +00:00
use lib $Bin . "/lib", "/usr/lib/monitorix";
2013-01-04 08:27:05 +00:00
use Monitorix;
2013-01-31 17:48:29 +00:00
use HTTPServer;
2013-01-10 11:18:28 +00:00
use POSIX qw(WNOHANG LC_TIME setlocale uname pause setsid);
2013-01-04 08:27:05 +00:00
use Config::General;
2012-10-22 15:26:37 +01:00
use Getopt::Std;
2013-01-17 16:56:38 +00:00
use Cwd qw(abs_path);
2013-01-04 08:27:05 +00:00
2012-10-22 15:26:37 +01:00
# Force a standard locale
$ENV{LANG} = "";
setlocale(LC_TIME, "C");
$SIG{'INT' } = 'INT_handler';
$SIG{'ABRT'} = 'INT_handler';
$SIG{'QUIT'} = 'INT_handler';
$SIG{'TRAP'} = 'INT_handler';
$SIG{'STOP'} = 'INT_handler';
$SIG{'TERM'} = 'INT_handler';
$SIG{'CHLD'} = 'CHLD_handler';
$SIG{'HUP' } = 'HUP_handler';
$SIG{'ALRM'} = 'ALRM_handler';
2013-05-31 10:32:17 +01:00
use constant VERSION => "3.2.1";
2013-06-03 07:04:57 +01:00
use constant RELDATE => "03-Jun-2013";
2012-10-22 15:26:37 +01:00
my @suppsys = ("Linux", "FreeBSD", "OpenBSD", "NetBSD");
2013-02-07 14:54:31 +00:00
our %config;
2013-01-04 08:27:05 +00:00
our %options;
2012-10-22 15:26:37 +01:00
sub INT_handler {
my ($signal) = @_;
logger("SIG$signal caught.");
2013-01-04 08:27:05 +00:00
flush_accounting_rules(\%config, $options{d});
2013-01-31 17:48:29 +00:00
if(lc($config{httpd_builtin}->{enabled} eq "y")) {
kill(15, $config{httpd_pid});
}
2012-10-22 15:26:37 +01:00
logger("Exiting.");
exit(0);
}
sub CHLD_handler {
my $pid = waitpid(-1, WNOHANG);
}
sub HUP_handler {
my ($signal) = @_;
my $myself = (caller(0))[3];
my (undef, undef, $uid) = getpwnam($config{httpd_builtin}->{user});
my (undef, undef, $gid) = getgrnam($config{httpd_builtin}->{group});
2012-10-22 15:26:37 +01:00
logger("SIG$signal caught.");
2013-01-04 08:27:05 +00:00
# upon receiving a SIGHUP signal the logfile is re-opened
2012-10-22 15:26:37 +01:00
close(STDOUT);
close(STDERR);
2013-01-10 11:18:28 +00:00
open(STDOUT, ">> $config{log_file}") || logger("Can't write to LOG: $!");
open(STDERR, ">> $config{log_file}") || logger("Can't write to LOG: $!");
2012-10-22 15:26:37 +01:00
logger("$myself: reopening log file.");
# create the HTTPd logfile
open(OUT, "> " . $config{httpd_builtin}->{log_file});
close(OUT);
chown($uid, $gid, $config{httpd_builtin}->{log_file});
2012-10-22 15:26:37 +01:00
}
sub ALRM_handler {
my $myself = (caller(0))[3];
my ($sec, $min, $hour, $mday) = localtime(time);
2013-01-04 08:27:05 +00:00
# call all enabled graphs on every minute
2012-10-22 15:26:37 +01:00
if($sec == 0) {
2013-01-04 08:27:05 +00:00
foreach my $f (@{$config{func_update}}) {
my $update = $f . "_update";
my $d = $f;
logger("$myself: calling $update()") unless !$options{d};
undef($d) if(!grep {trim($_) eq $d} (@{$config{debug}}));
if(defined($options{d}) && $options{d} eq "all") {
$d = $f;
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
{
no strict "refs";
eval { &$update($f, \%config, $d); };
if($@) {
logger("$myself: $update(): $@");
2013-01-04 08:27:05 +00:00
}
2012-10-22 15:26:37 +01:00
}
}
if(lc($config{traffacct}->{enabled}) eq "y" && lc($config{traffacct}->{reports}->{enabled}) eq "y") {
my $d = "traffacct";
undef($d) if(!grep {trim($_) eq $d} (@{$config{debug}}));
# at 00:00h
2012-10-22 15:26:37 +01:00
if($min == 0 && $hour == 0) {
2013-01-04 08:27:05 +00:00
# collect traffic accounting every day
eval { traffacct::traffacct_getcounters(\%config, $d); };
if($@) {
logger("$myself: traffacct::traffacct_getcounters(): $@");
}
# send reports every first day of a month
2012-10-22 15:26:37 +01:00
if($mday == 1) {
eval { traffacct::traffacct_sendreports(\%config, $d); };
if($@) {
logger("$myself: traffacct::traffacct_sendreports(): $@");
}
2012-10-22 15:26:37 +01:00
}
}
}
}
alarm(1);
}
sub daemonize {
2013-01-10 11:18:28 +00:00
chdir("/") || die "Can't chdir to /: $!";
open(STDIN, "< /dev/null") || die "Can't read /dev/null: $!";
open(STDOUT, ">> $config{log_file}") || die "Can't write to LOG: $!";
umask(022) || die "Unable to umask 022: $!";
exit if fork(); # parent exits
(setsid() != -1) || die "Can't start a new session: $!";
open(STDERR, ">> $config{log_file}") || die "Can't write to LOG: $!";
2012-10-22 15:26:37 +01:00
}
sub usage {
print(STDERR << "EOF");
Usage: monitorix -c configfile [-p pidfile] [-d none | graph[,graph] | all ] [-v]
EOF
exit(1);
}
sub create_index {
my $myself = (caller(0))[3];
my $n;
my $gname;
2013-01-04 08:27:05 +00:00
my $theme = $config{theme};
my $bgcolor;
my $table_back_color;
my $title_back_color;
my $title_fore_color;
# force to only one trailing slash
(my $base_url = $config{base_url}) =~ s/\/*$/\//;
(my $base_cgi = $config{base_cgi}) =~ s/\/*$/\//;
2013-01-04 08:27:05 +00:00
if($theme eq "black") {
$bgcolor = $config{$theme}->{main_bg};
$table_back_color = $config{$theme}->{graph_bg};
$title_back_color = $config{$theme}->{title_bg};
$title_fore_color = $config{$theme}->{title_fg};
} elsif(!$theme || $theme eq "white") {
2012-10-22 15:26:37 +01:00
$bgcolor = "#ffffff";
$table_back_color = "#CCCCCC";
$title_back_color = "#777777";
$title_fore_color = "#CCCC00";
} else {
2013-01-04 08:27:05 +00:00
logger("$myself: ERROR: invalid value in 'theme' option") unless !$options{d};
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
if(!open(OUT, "> $config{base_dir}/index.html")) {
2013-01-10 11:18:28 +00:00
die "unable to create '${config{base_dir}}index.html': $!";
2012-10-22 15:26:37 +01:00
}
print(OUT <<EOF);
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
2013-01-04 08:27:05 +00:00
<title>$config{title}</title>
<link rel="shortcut icon" href="$base_url$config{favicon}">
2012-10-22 15:26:37 +01:00
</head>
<body bgcolor="$bgcolor" text="#888888" vlink="#888888" link="#888888">
<center>
<p>
<br>
<font face="Verdana, sans-serif">
<table>
<tr>
<td>
<a href="http://www.monitorix.org/"><img src="$base_url$config{logo_top}" border="0"></a>
2012-10-22 15:26:37 +01:00
</td>
</tr>
<tr>
<td>
<h2 align="right">v@{[VERSION]}</h2>
</td>
</tr>
</table>
<p>
<form action="${base_cgi}monitorix.cgi" method="get">
2012-10-22 15:26:37 +01:00
<table cellspacing="5" cellpadding="0" bgcolor="$table_back_color" border="1">
<tr>
<td bgcolor="$title_back_color">
<font color="$title_fore_color">
<b>&nbsp;Hostname&nbsp;</b>
</font>
</td>
<td bgcolor="$title_back_color">
<font color="$title_fore_color">
<b>&nbsp;Graph&nbsp;</b>
</font>
</td>
</tr>
<tr>
EOF
print(OUT " <td bgcolor='$bgcolor'>\n");
print(OUT " <select name='mode' size='1'>\n");
2013-01-07 17:11:17 +00:00
print(OUT " <optgroup label='Local Host'>\n");
print(OUT " <option value='localhost'>localhost</option>\n");
print(OUT " </optgroup>\n");
2013-01-04 08:27:05 +00:00
2013-01-07 17:11:17 +00:00
if(scalar(my @remotehost_list = split(',', $config{multihost}->{remotehost_list})) && lc($config{multihost}->{enabled}) eq "y") {
2012-10-22 15:26:37 +01:00
print(OUT " <optgroup label='Multihost'>\n");
2013-01-07 17:11:17 +00:00
print(OUT " <option value='multihost.all'>All</option>\n");
2013-01-04 08:27:05 +00:00
for($n = 0; $n < scalar(@remotehost_list); $n++) {
print(OUT " <option value='multihost.$n'>" . trim($remotehost_list[$n]) . "</option>\n");
2012-10-22 15:26:37 +01:00
}
print(OUT " </optgroup>\n");
2013-01-07 17:11:17 +00:00
if(lc($config{multihost}->{groups}) eq "y" ) {
my @remotegroup_list = split(',', $config{multihost}->{remotegroup_list});
2013-01-04 08:27:05 +00:00
print(OUT " <optgroup label='Multihost-Groups'>\n");
print(OUT " <option value='multihost.group'>All Groups</option>\n");
for($n = 0; $n < scalar(@remotegroup_list); $n++) {
print(OUT " <option value='multihost.group$n'>" . trim($remotegroup_list[$n]) . "</option>\n");
}
print(OUT " </optgroup>\n");
2012-10-22 15:26:37 +01:00
}
}
if(scalar(my @tal = split(',', $config{traffacct}->{list})) && lc($config{traffacct}->{enabled}) eq "y") {
2013-01-07 17:11:17 +00:00
print(OUT " <optgroup label='Traffic Accounting'>\n");
print(OUT " <option value='traffacct.all'>All</option>\n");
for($n = 0; $n < scalar(@tal); $n++) {
print(OUT " <option value='traffacct.$n'>" . trim($tal[$n]) . "</option>\n");
2012-10-22 15:26:37 +01:00
}
print(OUT " </optgroup>\n");
}
print(OUT " </select>\n");
print(OUT " </td>\n");
print(OUT " <td bgcolor='$bgcolor'>\n");
print(OUT " <select name='graph' size='1'>\n");
print(OUT " <option value='all'>All graphs</option>\n");
2013-01-04 08:27:05 +00:00
foreach (split(',', $config{graph_name})) {
my $g = trim($_);
if(lc($config{graph_enable}->{$g}) eq "y") {
print(OUT " <optgroup label='" . $config{graph_title}->{$g} . "'>\n");
2012-10-22 15:26:37 +01:00
if($g eq "proc") {
2013-01-04 08:27:05 +00:00
for($n = 0; $n < $config{proc}->{max}; $n++) {
2012-10-22 15:26:37 +01:00
$gname = "_" . $g;
2013-01-04 08:27:05 +00:00
print(OUT " <option value='" . $gname . $n . "'>" . $config{graphs}->{$gname} . " " . $n . "</option>\n");
2012-10-22 15:26:37 +01:00
}
next;
}
if($g eq "fs") {
$n = 0;
foreach my $k (sort keys %{$config{graphs}}) {
if($k =~ m/$g/) {
$gname = "_" . $g . ++$n;
my $gname2 = "_" . $g . "0" . $n;
if($config{graphs}->{$gname}) {
print(OUT " <option value='" . $gname2 ."'>" . $config{graphs}->{$gname} . "</option>\n");
}
}
}
next;
}
2012-10-22 15:26:37 +01:00
if($g eq "net") {
my $n2;
2013-01-04 08:27:05 +00:00
for($n = 0; $n < scalar(my @nl = split(',', $config{net}->{list})); $n++) {
2012-10-22 15:26:37 +01:00
$gname = "_" . $g;
for($n2 = 1; $n2 <= 3; $n2++) {
2013-01-04 08:27:05 +00:00
my $str = trim($nl[$n]) . " " . $config{graphs}->{$gname . $n2};
2012-10-22 15:26:37 +01:00
print(OUT " <option value='" . $gname . $n . $n2 . "'>" . $str . "</option>\n");
}
}
next;
}
if($g eq "port") {
2013-01-04 08:27:05 +00:00
for($n = 0; $n < $config{port}->{max} && $n < scalar(my @port_list = split(',', $config{port}->{list})); $n++) {
my $num = trim($port_list[$n]);
my $name = trim((split(',', $config{port}->{desc}->{$num}))[0]);
my $pcon = trim((split(',', $config{port}->{desc}->{$num}))[2]);
2012-10-22 15:26:37 +01:00
$gname = "_" . $g;
foreach my $c (split('/', $pcon)) {
$c = uc(trim($c));
print(OUT " <option value='" . $gname . $n . "'>" . $config{graphs}->{$gname} . " " . $num . " (" . $c . "-" . $name . ")" . "</option>\n");
}
2012-10-22 15:26:37 +01:00
}
next;
}
if($g eq "fail2ban") {
2013-01-04 08:27:05 +00:00
for($n = 0; $n < scalar(my @fail2ban_list = split(',', $config{fail2ban}->{list})); $n++) {
my $name = trim($fail2ban_list[$n]);
2012-10-22 15:26:37 +01:00
$gname = "_" . $g;
2013-01-04 08:27:05 +00:00
print(OUT " <option value='" . $gname . $n . "'>" . $name . "</option>\n");
2012-10-22 15:26:37 +01:00
}
next;
}
$n = 0;
2013-01-04 08:27:05 +00:00
foreach my $k (sort keys %{$config{graphs}}) {
2012-10-22 15:26:37 +01:00
if($k =~ m/$g/) {
$gname = "_" . $g . ++$n;
2013-01-04 08:27:05 +00:00
if($config{graphs}->{$gname}) {
print(OUT " <option value='" . $gname ."'>" . $config{graphs}->{$gname} . "</option>\n");
2012-10-22 15:26:37 +01:00
}
}
}
print(OUT " </optgroup>\n");
}
}
print(OUT " </select>\n");
print(OUT " </td>\n");
print(OUT <<EOF);
</tr>
</table>
<p>
<table cellspacing="5" cellpadding="0" bgcolor="$table_back_color" border="1">
<tr>
<td bgcolor="$title_back_color">
<input type="radio" checked name="when" value="1day">
<font color="$title_fore_color"><b>Daily&nbsp;</b></font>
</td>
<td bgcolor="$title_back_color">
<input type="radio" name="when" value="1week">
<font color="$title_fore_color"><b>Weekly&nbsp;</b></font>
</td>
<td bgcolor="$title_back_color">
<input type="radio" name="when" value="1month">
<font color="$title_fore_color"><b>Monthly&nbsp;</b></font>
</td>
<td bgcolor="$title_back_color">
<input type="radio" name="when" value="1year">
<font color="$title_fore_color"><b>Yearly&nbsp;</b></font>
</td>
</tr>
</table>
<p>
2013-01-04 08:27:05 +00:00
<input type="hidden" name="color" value="$theme">
2012-10-22 15:26:37 +01:00
<input type="submit" value=" Ok ">
</form>
</font>
</center>
</body>
</html>
EOF
close(OUT);
}
2013-01-04 08:27:05 +00:00
# Main
2012-10-22 15:26:37 +01:00
# ----------------------------------------------------------------------------
2013-01-04 08:27:05 +00:00
getopts("d:vc:p:", \%options) || usage();
2012-10-22 15:26:37 +01:00
2013-01-04 08:27:05 +00:00
if($options{v}) {
print("Monitorix version " . VERSION . " (" . RELDATE . ")\n");
print("by Jordi Sanfeliu <jordi\@fibranet.cat>\n");
print("http://www.monitorix.org/\n\n");
exit(0);
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
if(!$options{c}) {
usage();
exit(1);
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
$options{c} = abs_path($options{c}) unless $^V lt 5.6.2;
if(!stat($options{c})) {
2013-01-10 11:18:28 +00:00
die "can't open file '$options{c}': $!";
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
# load configuration file
my $conf = new Config::General(
-ConfigFile => $options{c},
);
%config = $conf->getall;
$config{debug} = ();
$config{func_update} = ();
2012-10-22 15:26:37 +01:00
2013-01-04 08:27:05 +00:00
# get the current OS and kernel version and check its support
my $release;
($config{os}, undef, $release) = uname();
my ($major, $minor) = split('\.', $release);
$minor =~ m/^(\d+)/;
$config{kernel} = $major . "." . $1;
2013-01-04 08:27:05 +00:00
if(!grep {$_ eq $config{os}} @suppsys) {
2013-01-10 11:18:28 +00:00
die "FATAL: your operating system ($config{os}) is not supported.";
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
if(grep {$_ eq $config{os}} ("FreeBSD", "OpenBSD", "NetBSD")) {
$SIG{'CHLD'} = 'DEFAULT';
}
2012-10-22 15:26:37 +01:00
2013-01-04 08:27:05 +00:00
$0 = sprintf("%s %s%s%s%s",
$^V lt 5.6.2 ? "monitorix" : abs_path($0),
$options{c} ? "-c $options{c}" : "",
$options{p} ? " -p $options{p}" : "",
$options{d} ? " -d $options{d}" : "",
$options{v} ? " -v" : "");
2012-10-22 15:26:37 +01:00
2013-03-14 14:42:42 +00:00
daemonize();
2013-01-04 08:27:05 +00:00
logger("Starting Monitorix version " . VERSION . " (pid $$).");
2012-10-22 15:26:37 +01:00
2013-01-04 08:27:05 +00:00
if($options{p}) {
$options{p} = abs_path($options{p});
open(OUT, "> $options{p}")
2013-01-10 11:18:28 +00:00
|| die "could not open '$options{p}' for writing: $!";
2013-01-04 08:27:05 +00:00
print(OUT "$$");
close(OUT);
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
# change to a safety directory
unless(chdir("/tmp")) {
2013-03-15 06:57:21 +00:00
die "can't chdir to /tmp: $!";
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
if($options{d}) {
if($options{d} ne "none" && $options{d} ne "all") {
@{$config{debug}} = split(',', $options{d});
foreach my $t (@{$config{debug}}) {
if(!grep {trim($_) eq $t} (split(',', $config{graph_name} . ", traffacct"))) {
2013-01-10 11:18:28 +00:00
die "Invalid debug key '$t'";
2012-10-22 15:26:37 +01:00
}
}
}
2013-01-04 08:27:05 +00:00
logger("Entering in debug mode.");
logger("Changed process name to '$0'.");
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
# save the path of the configuration file
2013-01-31 17:48:29 +00:00
if(open(OUT, "> " . $config{base_dir} . "/cgi/monitorix.conf.path")) {
2013-01-04 08:27:05 +00:00
print(OUT "$options{c}\n");
close(OUT);
} else {
2013-01-31 17:48:29 +00:00
logger("Unable to create the file '$config{base_dir}/cgi/monitorix.conf.path'.");
}
2013-01-04 08:27:05 +00:00
flush_accounting_rules(\%config, $options{d});
2012-10-22 15:26:37 +01:00
2013-01-04 08:27:05 +00:00
logger("Initializing graphs.") unless !$options{d};
foreach (split(',', $config{graph_name} . ", traffacct")) {
2013-01-04 08:27:05 +00:00
my $g = trim($_);
my $e = "n";
if($g eq "traffacct") {
$e = lc($config{$g}->{enabled});
} else {
$e = lc($config{graph_enable}->{$g});
}
if($e eq "y") {
2013-01-04 08:27:05 +00:00
my $init = $g . "_init";
my $d = $g;
2012-10-22 15:26:37 +01:00
2013-01-04 08:27:05 +00:00
undef($d) if(!grep {trim($_) eq $d} (@{$config{debug}}));
if(defined($options{d}) && $options{d} eq "all") {
$d = $g;
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
eval "use $g qw(" . $init . " " . $g . "_update)";
if($@) {
logger("WARNING: unable to find module '$g'");
2012-10-22 15:26:37 +01:00
next;
}
2013-01-04 08:27:05 +00:00
{
no strict "refs";
eval { &$init($g, \%config, $d); };
2012-10-22 15:26:37 +01:00
}
2013-01-04 08:27:05 +00:00
logger("WARNING: unexpected errors in function $init()") if($@);
2012-10-22 15:26:37 +01:00
}
}
2013-01-04 08:27:05 +00:00
if(!scalar($config{func_update})) {
2012-10-22 15:26:37 +01:00
logger("nothing to do, exiting.");
exit(0);
}
2013-01-04 08:27:05 +00:00
logger("Generating the 'index.html' file.") unless !$options{d};
2012-10-22 15:26:37 +01:00
create_index();
if(lc($config{httpd_builtin}->{enabled} eq "y")) {
httpd_setup(\%config, $options{d});
2013-03-04 16:16:11 +00:00
logger("Built-in HTTP server pid is '$config{httpd_pid}'.") if (defined($config{httpd_pid}));
}
2013-01-04 08:27:05 +00:00
logger("Ok, done.") unless !$options{d};
2012-10-22 15:26:37 +01:00
alarm(1);
while(1) {
pause();
2012-10-22 15:26:37 +01:00
}