etbemon-1.3.4/0000755000175100017510000000000013615476430011301 5ustar rjcrjcetbemon-1.3.4/alert.d/0000755000175100017510000000000013221737211012620 5ustar rjcrjcetbemon-1.3.4/alert.d/file.alert0000755000175100017510000000356210230411542014572 0ustar rjcrjc#!/usr/bin/perl # # file.alert - log alert to a file # # The first line from STDIN is summary information, adequate to send # to a pager or email subject line. # # Jim Trocki, trockij@arctic.org # # $Id: file.alert,v 1.2 2005/04/17 07:42:26 trockij Exp $ # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # $RCSID='$Id: file.alert,v 1.2 2005/04/17 07:42:26 trockij Exp $'; use Getopt::Std; getopts ("d:S:s:g:h:t:l:uOT"); $summary=; chomp $summary; $summary = $opt_S if (defined $opt_S); $file = shift; $file = "file" if (!defined $file); $file = "$opt_d/$file" if ($opt_d); $ALERT = $ENV{"MON_ALERTTYPE"} || "UNKNOWN ALERT"; if (defined $ENV{"MON_OPSTATUS"}) { $OPSTATUS = $ENV{"MON_OPSTATUS"}; } else { $OPSTATUS = "UNKNOWN OPSTATUS"; } $t = localtime($opt_t); ($wday,$mon,$day,$tm) = split (/\s+/, $t); open (F, ">>$file") || die "could not append to $file: $!\n"; print F <) { print F; } print F ".\n"; close (F); etbemon-1.3.4/alert.d/irc.alert0000755000175100017510000000776310230411542014437 0ustar rjcrjc#!/usr/bin/perl # # irc.alert - irc alert for "mon" # # options are: # -s service # -g group # -h "host1 host2 host3..." # -t tmnow # -u (if upalert) # -T (if trap) # -O (if traptimeout) # # -j join the channel before doing PRIVMSG # (some channel modes prevent PRIVMSG from # user who hasn't joined the channel) # -c channel name of the channel (without leading #) # -S server irc server # -U user user for irc server # -n nick nick # -d post alert detail to irc channel # -N num try num different nicks before giving up # -p secs when showing detail, pause secs between # sending each line. secs may be fractional. # # Jim Trocki, trockij@arctic.org # # $Id: irc.alert,v 1.2 2005/04/17 07:42:26 trockij Exp $ # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # use strict; use IO::Socket::INET; use Getopt::Std; use English; my %opt; getopts ("s:g:h:t:uTOjc:S:U:n:dN:p:", \%opt); my $CHAN = $opt{"c"} || "mon"; my $NICK = $opt{"n"} || "mon"; my $USER = $opt{"U"} || $NICK; my $SERVER = $opt{"S"} || die "must supply server via -S\n"; my $NICK_TRIES = $opt{"N"} || 5; my $PAUSE = $opt{"p"} || 0; my $TIMEOUT = 10; # # read in what the mon server sends us about the alert # my $summary = <>; $summary = "UNKNOWN" if ($summary eq ""); my @details; while (<>) { chomp; push @details, $_; } eval { local $SIG{ALRM} = sub { die "Timeout Alarm" }; alarm $TIMEOUT; # # make the connection # my $s = new IO::Socket::INET ( "PeerAddr" => "$SERVER:6667", "Proto" => "tcp", "Timeout" => 10, ); die if (!defined $s); # # register with the irc server # print $s "NICK $NICK\r\n"; print $s "USER $USER uplift.transmeta.com $USER :$USER\r\n"; my $nick_tries = 0; # # if we get in, there will be a "001" reply # from the server. deal with nick collisions. # while (<$s>) { s/\r\n//; # # we're in # last if (/^:\S+\s+001\s/); # # nick already in use, pick a new one # if (/^:\S+\s+433\s/ || /^:\S+\s+432\s/) { if (++$nick_tries >= $NICK_TRIES) { print $s "QUIT\r\n"; die "could not get an unused nick, giving up\n"; } my ($nick, $num) = ($NICK, 0); if ($NICK =~ /_/) { ($nick, $num) = split (/_/, $NICK); } $NICK = "$nick" . "_" . ++$num; print $s "NICK $NICK\r\n"; } } # # /join the channel if requested # if ($opt{"j"}) { print $s "JOIN #$CHAN\r\n"; } my @t = split (/\s+/, scalar (localtime ($opt{"t"} ? $opt{"t"} : time))); my $t = "$t[2]-$t[1] $t[3]"; my $alert = $opt{"u"} ? "UPALERT" : "ALERT"; print $s "PRIVMSG #$CHAN :$alert $t ($opt{g}/$opt{s}): $summary\r\n"; # # print out the detail if requested # if ($opt{"d"}) { foreach my $detail (@details) { print $s "PRIVMSG #$CHAN : $t ($opt{g}/$opt{s}): $detail\r\n"; if ($PAUSE) { my ($rin, $win, $ein); select ($rin, $win, $ein, $PAUSE); } } } # # /leave the channel # if ($opt{"j"}) { print $s "PART #$CHAN\r\n"; } print $s "QUIT :byebye\r\n"; while (<$s>) { # whatever } close $s; alarm 0; }; if ($EVAL_ERROR) { die "$EVAL_ERROR"; } etbemon-1.3.4/alert.d/mail.alert0000755000175100017510000000426510230411542014576 0ustar rjcrjc#!/usr/bin/perl # # mail.alert - Mail alert for mon # # The first line from STDIN is summary information, adequate to send # to a pager or email subject line. # # -f from@addr.x set the smtp envelope "from" address # # Jim Trocki, trockij@arctic.org # # $Id: mail.alert,v 1.3 2005/04/17 07:42:26 trockij Exp $ # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # $RCSID='$Id: mail.alert,v 1.3 2005/04/17 07:42:26 trockij Exp $'; use Getopt::Std; use Text::Wrap; getopts ("S:s:g:h:t:l:f:u"); $summary=; chomp $summary; $summary = $opt_S if (defined $opt_S); $mailaddrs = join (',', @ARGV); $mailfrom = "-f $opt_f -F $opt_f" if (defined $opt_f); $ALERT = $opt_u ? "UPALERT" : "ALERT"; $t = localtime($opt_t); ($wday,$mon,$day,$tm) = split (/\s+/, $t); open (MAIL, "| /usr/lib/sendmail -oi -t $mailfrom") || die "could not open pipe to mail: $!\n"; print MAIL <) { print MAIL; } close (MAIL); etbemon-1.3.4/alert.d/mailxmpp.alert0000644000175100017510000000620713221737211015505 0ustar rjcrjc#!/usr/bin/perl use strict; use BSD::Resource; # # mailxmpp.alert - Mail and xmpp alert for mon # # The first line from STDIN is summary information, adequate to send # to a pager or email subject line. # # -f from@addr.x set the smtp envelope "from" address # -x destination xmpp address # -m destination smtp address # # Jim Trocki, trockij@arctic.org # XMPP added by Russell Coker russell@coker.com.au # # $Id: mail.alert,v 1.3 2005/04/17 07:42:26 trockij Exp $ # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # setrlimit(RLIMIT_CPU, 5, 5) or die "Can't set CPU time limit to 5 seconds"; use Getopt::Std; use Text::Wrap; our $opt_S; our $opt_s; our $opt_g; our $opt_h; our $opt_l; our $opt_f; our $opt_t; our $opt_u; our $opt_m; our $opt_x; getopts ("S:s:g:h:t:l:f:um:x:"); my $summary=; chomp $summary; $summary = $opt_S if (defined $opt_S); use Sys::Hostname; my $host = hostname; my $mailfrom = "-f $opt_f -F $opt_f" if (defined $opt_f); my $ALERT = $opt_u ? "UPALERT" : "ALERT"; my $t = localtime($opt_t); my ($wday,$mon,$day,$tm) = split (/\s+/, $t); open (MAIL, "| /usr/lib/sendmail -oi -t $mailfrom") || die "could not open pipe to mail: $!\n"; my @xmpprec = split(/,/, $opt_x); # use hostname as the resource open (XMPP, "| /usr/bin/sendxmpp -a /etc/ssl/certs -t @xmpprec -r $host") || die "could not open pipe to sendxmpp: $!\n"; print MAIL <) { print MAIL; print XMPP; } close (MAIL); close (XMPP); etbemon-1.3.4/alert.d/netpage.alert0000755000175100017510000000333110230411542015270 0ustar rjcrjc#!/usr/bin/perl # # netpage.alert - network page alert for mon # # The first line from STDIN is summary information, adequate to send # to a pager or email subject line. Even though this code is entirely # trivial, I wrote it just so that when it's specified in the mon.cf # file, it is clear that the paging alert is network-dependent. # # Jim Trocki, trockij@arctic.org # # $Id: netpage.alert,v 1.2 2005/04/17 07:42:26 trockij Exp $ # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # $RCSID='$Id: netpage.alert,v 1.2 2005/04/17 07:42:26 trockij Exp $'; use Getopt::Std; getopts ("s:g:h:t:l:u"); $summary=; chomp $summary; $pageaddrs = join (',', @ARGV); $t = localtime($opt_t); ($wday,$mon,$day,$tm) = split (/\s+/, $t); $ALERT = $opt_u ? "UPALERT" : "ALERT"; open (MAIL, "| /usr/lib/sendmail -oi -t") || die "could not open pipe to mail: $!\n"; print MAIL <) { print MAIL; } close (MAIL); etbemon-1.3.4/alert.d/qpage.alert0000755000175100017510000000401010230411542014735 0ustar rjcrjc#!/usr/bin/perl # # qpage.alert - send an alert via QuickPage # # This will accept multiple pager IDs in @ARGV and call qpage for # each one of them, but you should probably use qpage groups if possible. # # qpage-specific options: # -c coverage area # -f SNPP CALLerid # -l service level # -q SNPP server, translates to "qpage -s" # # Jim Trocki, trockij@arctic.org # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # use Getopt::Std; getopts ("s:g:h:t:c:f:l:q:uv"); # # the first line is summary information, adequate to send to a pager # or email subject line # # # the following lines normally contain more detailed information, # but this is monitor-dependent # @MSG=; $summary = shift @MSG; chomp $summary; $t = localtime($opt_t); ($wday,$mon,$day,$tm) = split (/\s+/, $t); $ALERT = $opt_u ? "UPALERT" : "ALERT"; foreach $pagedest (@ARGV) { if ($opt_v) { if (open(QPAGE, "| qpage -p $pagedest 2>/dev/null")) { print QPAGE "$ALERT $opt_g/$opt_s: $summary ($wday $mon $day $tm)\n"; print QPAGE @MSG; close QPAGE; } else { die "could not open pipe to qpage: $!\n"; } } else { if (system ("qpage -p $pagedest " . "'$ALERT $opt_g/$opt_s: $summary ($wday $mon $day $tm)'" . "2>/dev/null")) { die "could not open pipe to qpage: $?\n"; } } } etbemon-1.3.4/alert.d/snpp.alert0000755000175100017510000000342010616437070014640 0ustar rjcrjc#!/usr/bin/perl -w # # snpp.alert - Pure perl SNPP client # # Copyright (C) 1998, Michael Alan Dorman # # snpp.alert is based on the alert.template distributed by mon. # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id: snpp.alert,v 1.1.1.1.4.1 2007/05/03 19:55:36 trockij Exp $ # use strict; use vars qw /$opt_g $opt_q $opt_s $opt_t $opt_u/; use Getopt::Std; use Net::SNPP; getopts ("s:g:h:t:l:q:u"); my $opt_q ||= 'localhost'; # # the first line is summary information, adequate to send to a pager # or email subject line # # # the following lines normally contain more detailed information, # but this is monitor-dependent # # see the "Alert Programs" section in mon(1) for an explanation # of the options that are passed to the monitor script. # my $summary = ; chomp $summary; my $t = localtime ($opt_t); my ($wday,$mon,$day,$tm) = split (/\s+/, $t); my $snpp = Net::SNPP->new ($opt_q) or die; my $ALERT = $opt_u ? "UPALERT" : "ALERT"; $snpp->send ( Pager => [ @ARGV ], Message => "$ALERT $opt_g/$opt_s: $summary ($wday $mon $day $tm)" ); $snpp->quit; etbemon-1.3.4/alert.d/test.alert0000755000175100017510000000017213135636127014643 0ustar rjcrjc#!/bin/sh # # $Id: test.alert,v 1.1.1.1 2004/06/09 05:18:07 trockij Exp $ echo "`date` $*" >> /var/log/mon/test.alert.log etbemon-1.3.4/alert.d/trap.alert0000755000175100017510000000400113455600207014620 0ustar rjcrjc#!/usr/bin/perl # # Trap alert, for use with mon-0.38pre* and greater. # # Specify user and pass via MON_TRAP_USER (-U) and MON_TRAP_PASS (-P) # # Jim Trocki, trockij@arctic.org # # $Id: trap.alert,v 1.3 2005/04/17 07:42:26 trockij Exp $ # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # use Getopt::Std; use Mon::Client; use Socket; getopts ("s:g:h:t:l:o:uU:P:T:"); $summary=; chomp $summary; $detail = ""; while () { $detail .= $_; } chomp $detail; $t = time; $USER = ($ENV{"MON_TRAP_USER"} || $opt_U) || ""; $PASS = ($ENV{"MON_TRAP_PASS"} || $opt_P) || ""; $OPST = defined $ENV{"MON_OPSTATUS"} ? $ENV{"MON_OPSTATUS"} : 0; if ($opt_o) { $OPST = int ($opt_o); } foreach $op (keys %Mon::Client::OPSTAT) { $OPSTATUS = $op if ($Mon::Client::OPSTAT{$op} == $OPST); } $c = new Mon::Client ( port => getservbyname ('mon', 'udp') || 2583, ); $c->username($USER) if ($USER); $c->password($PASS) if ($PASS); foreach $host (@ARGV) { $c->host($host); $res = $c->send_trap( group => $ENV{MON_GROUP}, service => $ENV{MON_SERVICE}, retval => $ENV{MON_RETVAL}, opstatus => $OPSTATUS, summary => $summary, detail => $detail, ); print STDERR "Error sending trap to $host\n" if (!$res); print STDERR "Error is: ". $c->error() . "\n" if (!$res); } exit; etbemon-1.3.4/bin/0000755000175100017510000000000013615476730012054 5ustar rjcrjcetbemon-1.3.4/bin/btrfs.helper.cpp0000644000175100017510000000267413325225226015155 0ustar rjcrjc#include #include #include #include int main(int argc, char **argv) { if(argc != 2) { fprintf(stderr, "ERROR: Must specify mountpoint of BTRFS filesystem\n"); return 1; } pid_t pid = fork(); if(-1 == pid) { fprintf(stderr, "ERROR: Fork failed\n"); return 1; } else if(pid) { int wstatus = 0; if(wait(&wstatus) == -1) { fprintf(stderr, "ERROR: wait(2) error\n"); return 1; } if(!WIFEXITED(wstatus) || WEXITSTATUS(wstatus)) { fprintf(stderr, "ERROR: Error running btrfs device stats\n"); return 1; } } else { char * const child_args[] = { "/bin/btrfs", "device", "stats", argv[1], NULL }; execv(child_args[0], child_args); fprintf(stderr, "ERROR: Can't execute %s\n", child_args[0]); return 1; } pid = fork(); if(-1 == pid) { fprintf(stderr, "ERROR: Fork failed\n"); return 1; } else if(pid) { int wstatus = 0; if(wait(&wstatus) == -1) { fprintf(stderr, "ERROR: wait(2) error\n"); return 1; } if(!WIFEXITED(wstatus) || WEXITSTATUS(wstatus)) { fprintf(stderr, "ERROR: Error running btrfs subvol list\n"); return 1; } } else { char * const child_args[] = { "/bin/btrfs", "subvol", "list", argv[1], NULL }; execv(child_args[0], child_args); fprintf(stderr, "ERROR: Can't execute %s\n", child_args[0]); return 1; } } etbemon-1.3.4/bin/zfs.helper.cpp0000644000175100017510000000064313604573137014640 0ustar rjcrjc#include #include #include #include int main(int argc, char **argv) { if(argc != 2) { fprintf(stderr, "ERROR: Must specify pool name of ZFS filesystem\n"); return 1; } char * const child_args[] = { "/sbin/zpool", "status", argv[1], NULL }; execv(child_args[0], child_args); fprintf(stderr, "ERROR: Can't execute %s\n", child_args[0]); return 1; } etbemon-1.3.4/bin/Makefile0000644000175100017510000000050213604573151013502 0ustar rjcrjcALLPROGS=btrfs.helper zfs.helper all: $(ALLPROGS) # use -Wno-write-strings to make it easy to deal with exec() taking pointers # to non-const data WFLAGS=-Wall -W -Wshadow -Wpointer-arith -Wcast-align -Wcast-qual -pedantic -Wno-write-strings CC=gcc -O2 -g $(WFLAGS) %: %.cpp $(CC) $< -o $@ clean: rm -f $(ALLPROGS) etbemon-1.3.4/clients/0000755000175100017510000000000013137761622012741 5ustar rjcrjcetbemon-1.3.4/clients/skymon/0000755000175100017510000000000013135637032014254 5ustar rjcrjcetbemon-1.3.4/clients/skymon/README0000644000175100017510000000271010230411542015121 0ustar rjcrjc$Id: README,v 1.2 2005/04/17 07:42:26 trockij Exp $ This is a "moncmd" interface to a SkyTel 2-way pager. It utilizes procmail filters and password authentication to do its trick. I would not call this a "secure" authentication mechanism, but in Marcus Ranum-speak it is "really nice". Use at your own risk. It would be even more "really nice" if this did SecureID or S/Key. Also keep in mind that all queries and all results pass through the Great Wide Internet to get back to your pager. INSTALLATION 1. Do this from the /usr/doc/mon/examples directory: mkdir ~/.skytel chmod 0700 ~/.skytel cp skymon.allow ~/.skytel/allow 2. Create an encrypted password using the following Perl snippet, substituting "password" with the password that you want, and "salt" with a *2-letter* salt. perl -e 'print crypt("password", "salt"), "\n"' > ~/.skytel/password chmod 0600 password 3. Add the contents of the "skymon.procmail" file to your .procmailrc. OPERATION Commands are sent via email with the following format: /password:command Commands are the following, and can only be used if they exist in the "allow" file: eh enable host es enable service ew enable watch dh host reason disable host ds watch service reason disable service dw watch reason disable watch lf list failures ld list disabled The idea behind the brevity is that it's a pain to compose messages on that silly little keypad. --------------------- Jim Trocki trockij@arctic.org etbemon-1.3.4/clients/skymon/allow0000644000175100017510000000024410061516617015315 0ustar rjcrjc# # commands to allow through the pager interface # # $Id: allow,v 1.1.1.1 2004/06/09 05:18:07 trockij Exp $ # # enable eh es ew # disable dh ds dw # list lf ld etbemon-1.3.4/clients/skymon/procmail0000644000175100017510000000045010061516617016004 0ustar rjcrjc#Add this entry to your .procmailrc file, substituting "PIN" for #your SkyTel 7-digit 2-way PIN, and your SkyTel pager email address #(PIN@skymail.com). # # $Id: procmail,v 1.1.1.1 2004/06/09 05:18:07 trockij Exp $ # :0 H B D b c * ^From: PIN@skytel.com * ^/[a-zA-Z0-9]*: |skymon PIN@skymail.com etbemon-1.3.4/clients/skymon/skymon0000755000175100017510000001105110230411542015505 0ustar rjcrjc#!/usr/bin/perl # # handle mon commands send via a 2-way pager # # see /usr/doc/mon/README.skymon for information # # skytel pager email address should be supplied as first argument # # Jim Trocki, trockij@arctic.org # # $Id: skymon,v 1.2 2005/04/17 07:42:26 trockij Exp $ # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # $PASS = ""; $BUF = "\n"; $MONHOST = "monhost"; $ADDR = shift; die "no address specified\n" if ($ADDR eq ""); # # load command permissions # &load_allow() || die "could not load allow file:$!\n"; $p = 0; while (<>) { if (/^\/(\w+):(.*)/) { $password = $1; $cmd = $2; &check_password ($password) || die "pass\n"; foreach $c (split (/;/, $cmd)) { if ($BUF ne "\n" && $p) { $BUF .= "----\n"; } &parse_cmd ($c); $p = 1; } } } close (OUT); &mail_cmd() if ($BUF ne "\n"); exit; # # check password # sub check_password { my ($pass) = @_; my ($salt); &load_password() || return 0; print "$pass [$PASS]\n"; $salt = substr ($PASS, 0, 2); if (crypt ($pass, $salt) ne $PASS) { return 0; } return 1; } sub load_allow { my ($l); open (P, "$ENV{HOME}/.skytel/allow") || return 0; while (

) { next if /^\s*$/; next if /^\s*#/; $l = $_; chomp $l; $allow{$l} = 1; } close (P); } sub load_password { open (P, "$ENV{HOME}/.skytel/password") || return 0; $PASS =

; close (P); chomp $PASS; return 1; } sub load_address { open (P, "$ENV{HOME}/.skytel/address") || return 0; $ADDR =

; close (P); chomp $ADDR; return 1; } sub parse_cmd { my ($cmd) = @_; my ($c, @args); ($c, @args) = split (/\s+/, $cmd); # # list # if ($c eq "lf" && $allow{"lf"}) { &do_list (@args); } elsif ($c eq "ld" && $allow{"ld"}) { &do_list_disabled(@args); # # disable # } elsif ($c eq "dh" && $allow{"dh"}) { &do_command ("/usr/local/bin/moncmd -s $MONHOST disable host @args"); } elsif ($c eq "dw" && $allow{"dw"}) { &do_command ("/usr/local/bin/moncmd -s $MONHOST disable watch @args"); } elsif ($c eq "ds" && $allow{"ds"}) { &do_command ("/usr/local/bin/moncmd -s $MONHOST disable service @args"); # # enable # } elsif ($c eq "eh" && $allow{"eh"}) { &do_command ("/usr/local/bin/moncmd -s $MONHOST enable host @args"); } elsif ($c eq "ew" && $allow{"ew"}) { &do_command ("/usr/local/bin/moncmd -s $MONHOST enable watch @args"); } elsif ($c eq "es" && $allow{"es"}) { &do_command ("/usr/local/bin/moncmd -s $MONHOST enable service @args"); # # ack (not yet implemented) # } elsif ($c eq "a" && $allow{"a"}) { } } # # list failures # sub do_list { my (@args) = @_; my ($g, $s, $o, $l, $p); open (IN, "/usr/local/bin/moncmd -s monhost list failures|") || return; $p = 0; while () { last if (/220.*completed/); $l = $_; chomp $l; ($g, $s, $o) = ($l =~ (/^(\S+)\s+(\S+)\s+\d+\s+\d+\s+failed\s+(.*)/)); $BUF .= "\n" if ($p); $BUF .= "$g/$s:$o\n"; $p = 1; } close (IN); } # # list disabled # sub do_list_disabled { my (@args) = @_; open (IN, "/usr/local/bin/moncmd -s $MONHOST list disabled|") || return; $p = 0; while () { last if (/220.*completed/); $l = $_; chomp $l; $BUF .= "\n" if ($p); $BUF .= "$l\n"; $p = 1; } close (IN); } # # do_command # sub do_command { my ($cmd) = @_; my ($p); open (C, "$cmd|") || return; $p = 0; while () { $BUF .= "\n" if ($p); $BUF .= $_; $p = 1; } close (C); } # # mail the buffer back to the pager # sub mail_cmd { # &load_address() || die "could not load address\n"; # print "$BUF"; open (MAIL, "| /usr/lib/sendmail -oi -t") || die "could not open pipe to mail: $!\n"; print MAIL < insert n+1 empty lines .\" for manpage-specific macros, see man(7) .TH "SKYMON" "1" "February 12, 2007" "Dario Minnucci " "" .SH "NAME" skymon \- "moncmd" interface to a SkyTel 2\-way pager .SH "DESCRIPTION" .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. \fBskymon\fP This is a "moncmd" interface to a SkyTel 2\-way pager. .PP It utilizes procmail filters and password authentication to do its trick. I would not call this a "secure" authentication mechanism, but in Marcus Ranum\-speak it is "really nice". Use at your own risk. .PP It would be even more "really nice" if this did SecureID or S/Key. .PP Also keep in mind that all queries and all results pass through the Great Wide Internet to get back to your pager. .SH "OPERATION" Commands are sent via email with the following format: /password:command Commands are the following, and can only be used if they exist in the "allow" file: .TP .B eh Enable host .TP .B es Enable service .TP .B ew Enable watch .TP .B dh host reason Disable host .TP .B ds watch service reason Disable service .TP .B dw watch reason Disable watch .TP .B lf List failures .TP .B ld List disabled .PP The idea behind the brevity is that it's a pain to compose messages on that silly little keypad. .SH "SEE ALSO" .BR mon (8), .BR moncmd (1), .BR monshow (1) .SH "AUTHOR" skymon was written by Jim Trocki, .PP This manual page was written by Dario Minnucci , for the Debian project (but may be used by others). etbemon-1.3.4/clients/batch-example0000755000175100017510000000044610061516617015400 0ustar rjcrjc#!/bin/sh # # an example of calling moncmd in batch mode # # $Id: batch-example,v 1.1.1.1 2004/06/09 05:18:07 trockij Exp $ # trap "stty echo && echo && exit" 2 echo -n "Password: " stty -echo read p echo stty echo cat < # Based on the Mon program by Jim Trocki . # http://www.kernel.org/software/mon/ # Rewritten to support Mon::Client, mod_perl, taint mode, # authentication, the strict pragma, and other visual/functional # enhancements by Andrew Ryan . # Downtime logging contributed by Martha H Greenberg # Site-specific customization routines contributed by Ed Ravin # # ---------------------------------------------------------------------- # $Id: mon.cgi,v 1.4.2.1 2007/05/03 19:55:38 trockij Exp $ # ---------------------------------------------------------------------- # # # INSTRUCTIONS # Install this cgi script to wherever your cgi-bin directory sits # on your mon server. If you don't have a web server installed, try # http://www.apache.org. This script hasn't been tested with any # web server, although there is no reason it wouldn't work under # any other web server as a CGI script. # # This script now runs cleanly under mod_perl (tested under apache 1.3.9, # mod_perl 1.21), if you're running that. Global variables have not # been eliminated but at least we're being careful now. # # This script also runs cleanly under taint mode, which is activated # by using the -T switch for CGI scripts, and by using the directive # "PerlTaintCheck On" in your httpd.conf file if you are running # under mod_perl. # # Modify the "Configurable Parameters" section below to customize it # to your site's settings. mon.cgi also supports an optional config # file which allows you to set all the same parameters. Please # see the file README.site-customization for more details. # # If you want to easily customize the look and feel of mon.cgi, # as well as various other configuration options, copy the sample # mon.cgi.cf file (in the /config directory of this distribution) # into a location where your webserver can read it, and edit the # line beginning '$moncgi_config_file = ""' to reflect the path # to your config file. You can then change the look and feel of # mon.cgi, as well as implement access controls, directly from this # file. # # If you want to do a lot of the things that this script lets you do, # and you don't want any authorization to be necessary, then # you need to open up your auth.cf file to allow anyone to perform # actions that you would like mon.cgi to perform. # # No authentication might work in an environment where there are very # few Mon users and they can all be trusted equally, or if you want # to use mon.cgi in a sort of "read-only" capacity, where all users # can list, for example, but no web users can enable/disable # monitoring and/or control the server in any way. # # Alternatively, if you want to use authentication, you need to have # a working authentication setup with Mon from the command line # before attempting to make authentication work with mon.cgi. # # Authentication is very flexible, and is trivial to implement in # mon.cgi, assuming you already have authentication working from # the command line. Just un-comment out the "$must_login line, change # $app_secret to be something unique (VERY IMPORTANT!) and # mon will start requiring authentication for ALL commands. # # Authentication users should change their app secret on a regular # basis if security is a concern. Actually, if security is a concern, # don't run mon.cgi, because unless you use SSL, AND your monhost is # on the same server as your web server, AND you use a short timeout # on cookies, AND you change your app secret often and keep it # VERY secure, you don't have a secure web system. But this simple # authentication mechanism is enough to keep most people happy. # # # This script will require the CGI perl module. Available at any # perl CPAN site. See http://www.perl.org for details. Oh, and don't # forget Mon::Client, but the assumption is you are already running # mon in some fashion and so you know this already. # # In addition, if you want to use the authentication piece of mon.cgi, # you need to install the Crypt::TripleDES module, also available # (tested w/ Crypt::TripleDES v0.24), and your browser needs to allow # cookies (or else you need to be prepared to type in your username # and password an awful lot!). # # # BUGS # Probably many. # Send bugs/comments about this software to # Andrew Ryan # Please include any output from your web server's error log that the # script might have output, this will help immensely in solving problems. # Also please include the versions of mon.cgi, Mon and Mon::Client you # are using. # BEGIN { # Auto-detect if we are running under mod_perl or CGI. $USE_MOD_PERL = exists $ENV{'MOD_PERL'} ? 1 : 0; if ($USE_MOD_PERL) { # Use the cgi module and compile all methods at # the beginning but only once use CGI qw (-compile :standard) ; } else { # Use the cgi module and compile all methods only # when they are invoked via the autoloader. #use CGI qw (-debug) ; #DEBUG use CGI qw (:standard) ; } $CGI::POST_MAX=1024 * 100; # max 100K posts $CGI::DISABLE_UPLOADS = 1; # no uploads } # Configurable Parameters ---------------------------------------------- # Basic global vars use Mon::Client; # mon client interface use strict; # because strict is GOOD use vars qw($RCSID $RCSVERSION $VERSION $AUTHOR $organization $monadmin $logo $logo_link $reload_time $monhost $monport $url $login_expire_time $cookie_name $cookie_path %cgiparams $vcookie_name $vcookie_path $vcookie @views $curview $monhost_and_port_args $monhost_and_port_args_meta $has_read_config $moncgi_config_file $cf_file_mtime $untaint_ack_msgs @show_watch $show_watch_strict $required_mon_client_version); # Formatting-related global vars use vars qw($BGCOLOR $TEXTCOLOR $LINKCOLOR $VLINKCOLOR $greenlight_color $redlight_color $unchecked_color $yellowlight_color $disabled_color $fixed_font_face $sans_serif_font_face $dtlog_max_failures_per_page); # Security-related global vars use vars qw($must_login $app_secret %loginhash $des $has_prompted_for_auth $destroy_auth_cookie $default_username $default_password); $has_prompted_for_auth = ""; #this must always be cleared for mod_perl undef $destroy_auth_cookie; #this must always be undef'd for mod_perl undef %cgiparams; # this must always be undef'd for mod_perl undef $monhost_and_port_args; # This is defined if the user overrided monhost or monport undef $monhost_and_port_args_meta; # This is defined if the user overrided monhost or monport undef @show_watch; undef $show_watch_strict; $RCSID = '$Id: mon.cgi,v 1.4.2.1 2007/05/03 19:55:38 trockij Exp $'; $RCSVERSION = '$Revision: 1.4.2.1 $'; $VERSION = $RCSVERSION; $VERSION =~ s/\Revision: //i; $VERSION =~ s/\$//g ; $VERSION =~ s/\s+//g; $AUTHOR = 'andrewr@nam-shub.com'; $required_mon_client_version = "0.11"; #Version of Mon::Client which we require for successful operation # # If you want to use a config file to specify mon.cgi parameters, put # the full path to the file in this variable. # # If you do not wish to use a config file, leave this variable empty. # $moncgi_config_file = ""; # # This subroutine initializes the configuration variables which # can be set here, but also overridden with the optional mon.cgi # config file. # # We put this subroutine at the top of the code so that users # can get to it more easily. # sub initialize_config_globals ; sub initialize_config_globals { undef $has_read_config ; #undef this for mod_perl $must_login = ""; #this must always be undef'd for mod_perl $organization = ""; # Organization name. $monadmin = "BOFH\@your.domain"; # Your e-mail address. # note: must escape @ signs! $logo = ""; # Company or mon logo. $reload_time = 180; # Seconds for page reload. $monhost = "localhost"; # Mon server hostname. $monport = "2583"; # Mon port number. #$must_login = "yes"; # Uncomment this out if you want # authentication to be mandatory # for all connections to the mon server. #!!! WARNING!!!!! You must change $app_secret to something unique to your site! $app_secret = '1.90LK=R==36jlel492jl><>header(); print $webpage->start_html(-title=>"mon.cgi error: insufficient Mon::Client version", -BGCOLOR=>$BGCOLOR, -TEXT=>$TEXTCOLOR, -LINK=>$LINKCOLOR, -VLINK=>$VLINKCOLOR, -META=>{ 'generator'=>"mon.cgi $VERSION ($AUTHOR)", }, ); print $webpage->h3("Insufficient version ($Mon::Client::VERSION) of the Mon::Client perl module installed. Please upgrade your Mon::Client to at least version $required_mon_client_version before running mon.cgi."); print $webpage->h3("Also note, if you're running mon.cgi under Apache+mod_perl, you'll need to restart Apache after upgrading the Mon::Client library."); print $webpage->end_html; exit; } # # Read CGI params -- these overwrite anything in a config file # or hard-coded. # This can change the value of the $monhost and $monport # global variables defined in initialize_config_globals and # moncgi_read_cf. # # This can cause a problem if $monhost or $monport are defined here and we are running mod_perl... # &moncgi_get_params; # # Used to escape HTML in ack's # if ($untaint_ack_msgs =~ /^y(es)?$/i) { eval "use HTML::Entities" ; } else { undef $untaint_ack_msgs; } # Initialize a TripleDES global if login is required, # otherwise undef $must_login if ($must_login =~ /^y(es)?$/i) { eval "use Crypt::TripleDES"; $des = new Crypt::TripleDES; } else { $must_login = ""; } # # Set (or unset) $show_watch_strict according to its value # if ($must_login =~ /^y(es)?$/i) { $show_watch_strict = 1; } else { $show_watch_strict = ""; } # #Initialize the wordy descriptions of alert variables # %alert_vars = ( 'depend' => "Dependencies, if any", 'service' => "Service being monitored", 'last_check' => "The last time this service was checked", 'timer' => "Time remaining until this service is next checked", 'last_summary' => "Summary output from most recent failure of this service", 'opstatus' => "Current status of this service (0=error, 1=OK, 7=unchecked)", 'alerts_sent' => "Number of alerts sent", 'interval' => "Test interval, in seconds", 'last_detail' => "Detail output from the most recent failure of this service", 'monitor' => "Monitor used to test this service", 'last_trap' => "Last time a trap was received on this service", 'last_alert' => "Last time an alert was sent for this service", 'last_success' => "Last time this service returned an OK result", 'group' => "Hostgroup", 'failure_duration' => "Length of failure", 'ack' => "Acknowledgement status (1=failed service was ack'ed)", 'ackcomment' => "Comment issued by the acknowledger", 'first_failure' => "First failure time of this service", 'last_failure' => "Last failure time of this service", 'depend' => "Hostgroups/Services on which this service depends", 'last_check' => "Time this service was last checked", 'service' => "Service being checked", 'last_opstatus' => "Previous opstatus for this service (0=error, 1=OK, 7=unchecked)", 'exitval' => "Last exit value of monitor for this service (0=OK, anything else indicates failure)", 'depstatus' => "Dependency status (1 = dependencies OK, 0=dependencies not OK or no dependencies)", 'last_summary' => "Summary output from most recent failure of this service", 'last_detail' => "Detail output from the most recent failure of this service", ); # These are variables from svc_details which should be represented as # pretty-printed time strings. Mon gives them to us as UNIX time(2), so we # have to convert. They used to be hardcoded deep into the code, this # is an attempt at a readability improvement. # Example representation: '(31 days, 17 hours, 53 minutes, 25 seconds ago)' @time_based_alert_vars = ( "last_check", "last_failure", "last_trap", "last_alert", "last_success", "last_failure", "first_failure", ); # These are variables from svc_details which should be represented as # "pretty printed" seconds/minutes/hours/days. # Example representation: '4 minutes, 19 seconds' @pp_sec_alert_vars = ( "timer", "interval", "failure_duration", ); %auth_commands = ( # This global tracks the authorization # status of all commands mon.cgi can # issue for a user. It is a candidate for # inclusion in a cookie someday. list => { auth=>0, bgcolor=>""}, reset => { auth=>0, bgcolor=>""}, stop => { auth=>0, bgcolor=>""}, start => { auth=>0, bgcolor=>""}, savestate => { auth=>0, bgcolor=>""}, loadstate => { auth=>0, bgcolor=>""}, disable => { auth=>0, bgcolor=>""}, enable => { auth=>0, bgcolor=>""}, test => { auth=>0, bgcolor=>""}, ack => { auth=>0, bgcolor=>""}, reload => { auth=>0, bgcolor=>""}, ); $auth_commands_checked = 0; # This global tracks whether authorization # for all commands has been checked. ############################################################### # Function definitions begin below ############################################################### # # Forward declare all functions, for our sanity. # # # General functions # sub pp_sec ; sub pp_sec_brief ; sub arithmetic_mean ; sub median ; sub std_dev ; sub validate_name ; sub gen_ciphertext ; sub gen_cleartext ; # # Base mon.cgi pages # sub setup_page ; sub print_bar ; sub query_opstatus ; sub can_show_group ; sub list_status ; sub query_group ; sub end_page ; sub list_alerthist ; sub svc_details ; sub list_disabled ; sub list_dtlog ; sub list_pids ; # # mon functions # sub mon_connect ; sub mon_views; sub mon_list_group ; sub mon_list_watch ; sub mon_list_failures ; sub mon_list_successes ; sub mon_list_opstatus ; sub mon_list_disabled ; sub mon_reload ; sub mon_loadstate ; sub mon_savestate ; sub mon_loadstate_savestate ; sub mon_schedctl ; sub mon_list_pids ; sub mon_list_descriptions ; sub mon_enable ; sub mon_disable ; sub mon_test_service ; sub mon_test_config ; sub mon_reset ; sub mon_list_alerthist ; sub mon_list_sched_state ; sub mon_list_dtlog ; sub mon_ack ; sub mon_servertime ; sub mon_checkauth ; sub mon_state_change_enable_only ; sub mon_state_change ; # # mon.cgi functions # sub moncgi_get_params ; sub moncgi_logout ; sub moncgi_authform ; sub moncgi_generic_button ; sub moncgi_switch_user ; sub moncgi_print_service_table_legend ; sub moncgi_list_dtlog_navtable ; sub moncgi_test_all ; sub moncgi_reset ; sub moncgi_read_cf ; sub moncgi_login ; sub moncgi_custom_print_bar ; sub moncgi_custom_commands; ############################################################### # General functions, not specific to Mon or mon.cgi ############################################################### sub pp_sec { # This routine converts a number of seconds into a text string # suitable for (pretty) printing. The dtlog from Mon reports downtime # in seconds, and we want to present the user with more meaningful # data than "the service has been down for 13638 seconds" # # By Martha Greenberg w/ pedantic plural # modifications by Andrew. use integer; my $n = $_[0]; my ($days, $hrs, $min, $sec) = ($n / 86400, $n % 86400 / 3600, $n % 3600 / 60, $n % 60); my $s = $sec . " second"; $s .= "s" if $sec != 1; #because 0 is plural too :) if ($min > 0) { if ($min == 1) { $s = $min . " minute, " . $s; } else { $s = $min . " minutes, " . $s; } } if ($hrs > 0) { if ($hrs == 1) { $s = $hrs . " hour, " . $s; } else { $s = $hrs . " hours, " . $s; } } if ($days > 0) { if ($days == 1) { $s = $days . " day, " . $s; } else { $s = $days . " days, " . $s; } } return $s; } sub pp_sec_brief { # This routine converts a number of seconds into a text string # suitable for brief (yet pretty) printing. # # We use this on the opstatus page to display deltas of times (for # last check, next check). use integer; my $n = $_[0]; my $s; if ($n >= 0) { $s .= "+" ; } else { $s .= "-" ; $n = abs($n); } my ($days, $hrs, $min, $sec) = ($n / 86400, $n % 86400 / 3600, $n % 3600 / 60, $n % 60); if ($days > 0) { $s .= $days . "d"; } if ($hrs > 0) { $s .= $hrs . "h"; } if ($min > 0) { $s .= $min . "m"; } $s .= "${sec}s" ; return $s; } sub arithmetic_mean { # Given an array of numbers, this function returns the arithmetic mean return 0 if scalar(@_) == 0 ; #don't waste our time my $sum = 0; foreach (@_) { $sum += $_; } return $sum/scalar(@_); } sub median { # Given an array of numbers, this function returns the median return 0 if scalar(@_) == 0 ; #don't waste our time my $middle_element_index = int(scalar(@_)/2); @_ = sort {$a <=> $b} @_; if (scalar(@_)%2 == 1) { #odd num of elements, take the middle return $_[$middle_element_index]; } else { # even # of elements, take the avg of the 2 middle elements return &arithmetic_mean($_[$middle_element_index],$_[($middle_element_index-1)]); } } sub std_dev { # Given an array of numbers, this function returns their # standard deviation. return 0 if scalar(@_) < 2 ; #don't waste our time my $sum = 0; my $mean = &arithmetic_mean(@_); foreach (@_) { $sum += ( $_ - $mean )**2 ; } return ( $sum/(scalar(@_) - 1) )**0.5 ; } sub validate_name { # Return untainted host or group name if safe, undef otherwise. # Because you can never scrub your input too well. return $_[0] =~ /^([\w.\-_]+)$/ ? $1 : undef; } sub gen_ciphertext { # This function takes as its input a piece of plaintext and # returns a piece of ASCII, 3DES-encoded ciphertext, or undef if 3DES fails # for some reason. The key used is the global value "$app_secret". my ($plaintext) = (@_); my $ciphertext ; if ($ciphertext = $des->encrypt3 ("$plaintext", "$app_secret" )) { # convert key to hex $ciphertext = unpack("H*", $ciphertext) ; # print "ciphertext is $ciphertext
\n"; #DEBUG return $ciphertext; } else { return undef; } } sub gen_cleartext { # This function takes as its input a piece of ASCII, 3DES-encoded # ciphertext and returns a piece of plaintext, # or undef if 3DES fails for some reason. The key used is the # global value "$app_secret". my ($ciphertext) = (@_); my $plaintext ; return undef if ! $ciphertext; #convert key to format decrypt3() will understand $ciphertext = pack("H*", $ciphertext) ; if ($plaintext = $des->decrypt3 ("$ciphertext", "$app_secret" )) { # print "plaintext is $plaintext
\n"; #DEBUG return $plaintext; } else { return undef; } } ############################################################### # Presentation functions. These all have the common feature # that they format a bunch of information and present it in # a nice(?) way to the user. ############################################################### sub setup_page { # Setup the html doc headers and such # Also, get/set username/password cookie if $must_login is in effect my ($title) = (@_); my (@time, $ttime); my $title_color = "$TEXTCOLOR"; my $page_title = "$organization : " if $organization ne ""; $page_title = "${page_title}MON - $title ($monhost:$monport)"; my $time_now = time; my @expires_time = gmtime($time_now + $login_expire_time); # Put the cookie date format in the standard cookie format my $expires = sprintf ("%s, %.2d-%s-%d %.2d:%.2d:%.2d GMT", @days_of_week[$expires_time[6]], $expires_time[3], @year_months[$expires_time[4]], $expires_time[5] + 1900, @expires_time[2,1,0]); my ($encrypted_password, $cookie, $cookie_value); my $refresh_url; # # Define $args as null if it is not currently defined # $args = "" if ! defined($args); # Set the refresh page to always be the summary page, unless # certain commands are selected. if ( $command =~ "^query_opstatus_" ) { $refresh_url = "$url?${monhost_and_port_args_meta}command=$command"; } elsif ( ($command eq "mon_test_service") || ($command eq "svc_details") ) { $refresh_url = "$url?${monhost_and_port_args_meta}command=svc_details&args=$args"; } elsif ($command eq "query_group") { $refresh_url = "$url?${monhost_and_port_args_meta}command=query_group&args=$args"; } elsif ($command eq "list_dtlog") { $refresh_url = "$url?${monhost_and_port_args_meta}command=list_dtlog&args=$args"; } else { $refresh_url = "$url?${monhost_and_port_args_meta}command=query_opstatus"; } if ($must_login) { if ( ( defined($loginhash{'username'}) ) && ( $loginhash{'username'} ne "" ) ) { # Don't get the username and password from the cookie # if the user just submitted it via the login form. # Encrypt the password for cookie storage. $encrypted_password = &gen_ciphertext($loginhash{'password'}) ; } else { # Get the existing cookie and parse it $cookie_value = $webpage->cookie(-name=>"$cookie_name", ); ($loginhash{'username'},$loginhash{'password'}) = split(':', $cookie_value) if $cookie_value; $encrypted_password = $loginhash{'password'} ; # Decrypt the password string (if any) for use by the app, # unless the user just submitted it in cleartext. $loginhash{'password'} = &gen_cleartext($loginhash{'password'}) ; # for some reason (bug?) I get a space at the end of the password # that is returned here, so for now let's take it out, since # spaces are illegal in passwords anyway. $loginhash{'password'} =~ s/\s+//g if defined($loginhash{'password'}) ; } # Set up the new cookie (re-issue a new cookie with every access) if ($destroy_auth_cookie) { $cookie_value = "" ; } elsif ( defined($loginhash{'password'}) ) { $cookie_value = "$loginhash{'username'}:$encrypted_password" ; } else { # no username was supplied $cookie_value = "" ; } $cookie = $webpage->cookie(-name=>"$cookie_name", -value=>"$cookie_value", -expires=>"$expires", -path=>"$cookie_path", ); mon_views(); print $webpage->header( -cookie=>[$cookie,$vcookie], -refresh=>"$reload_time; URL=$refresh_url", ); } else { # Plain & simple, no cookie, no passsword $encrypted_password = "" ; mon_views(); print $webpage->header( -cookie => [$vcookie], -refresh=>"$reload_time; URL=$refresh_url", ); } print $webpage->start_html(-title=>"$page_title", -BGCOLOR=>$BGCOLOR, -TEXT=>$TEXTCOLOR, -LINK=>$LINKCOLOR, -VLINK=>$VLINKCOLOR, -META=>{ 'generator'=>"mon.cgi $VERSION ($AUTHOR)", }, ); # Useful for debugging username/password/cookie issues #DEBUG # print "cookie value is $cookie_value
\n"; #DEBUG # print "encrypt passwd is $encrypted_password
\n"; #DEBUG # print "username is "$loginhash{'username'}"
\n"; #DEBUG # print "decrypt passwd is "$loginhash{'password'}"
\n"; #DEBUG # # Print the logo image, if it was defined by the user. # if ($logo) { $webpage->print("\n"); } else { $webpage->print("\"[$organization\n"); } $webpage->print("
\n"); # # If the user has given a logo_link link, then insert an # anchor tag to it here, if logo_link was defined by the # user. # if ($logo_link) { $webpage->print("\"[$organization

MON: $title

MON: $title

\n"); $webpage->print("


\n"); } else { #just print the generic page with no logo and no link $webpage->print("\n"); if (@views) { $webpage->print(""); } $webpage->print("

MON: $title

", $webpage->start_form, $webpage->popup_menu(-name=>'setview', -values=>["--all--",sort(@views)], -default=>$curview), $webpage->submit(-name=>'Change View'), $webpage->end_form, "
\n"); } &print_bar; @time = localtime($time); $ttime = sprintf ("%.2d:%.2d:%.2d on %s, %.2d-%s-%d", @time[2,1,0], @days_of_week[$time[6]], $time[3], @year_months[$time[4]], $time[5] + 1900 ); $webpage->print("
"); $webpage->print ("\nThis information was presented at $ttime"); # # If the user is currently logged in, tell the user who # they are logged in as. # If the user is NOT currently logged in, offer to log # them in. # if ( ($loginhash{'username'}) && ($loginhash{'username'} ne $default_username) ) { $webpage->print (" to user $loginhash{'username'} (log off user $loginhash{'username'})") ; } else { $webpage->print (" (log in)"); } if ($curview && $curview ne '--all--') { $webpage->print("
Current View: $curview. If you're not seeing what you're expecting to see, try changing views via the menu at the top of this page."); } $webpage->print(".
"); } sub print_bar { # Print the command bar. Called at both the beginning and the end # of each page. # my $button = "INPUT TYPE=\"submit\" NAME=\"command\""; my $table_width = "100%"; my $face = $sans_serif_font_face; my ($cmd, $command, $desc); my $all_commands_unauthorized = 1; my $i; foreach $cmd (keys %auth_commands) { $auth_commands{$cmd}{'auth'} = &mon_checkauth($cmd); #last if $auth_commands{$cmd}{'auth'} == -1; # stop checking if we can't # contact the server $auth_commands{$cmd}{'bgcolor'} = $auth_commands{$cmd}{'auth'} == 0 ? "bgcolor=\"$disabled_color\"" : ""; $all_commands_unauthorized = 0 if $auth_commands{$cmd}{'auth'} == 1 ; } # Check to see if authentication is required and the selected user has # no permissions to do anything. This can only happen if your mon admin # is very cruel and gives you an account with no permissions (should # be very rare), or if a user enters the wrong password for a valid # account name (the usual case). # The main thing I don't like about this solution is that it embeds # the default l/p into the URL, but this isn't a secure password # anyway, right? $cmd = "list"; #set $cmd to the most basic of commands if ($auth_commands{$cmd}{'auth'} == 0) { $webpage->print("

Cannot connect to the mon server. Check the mon process to see if it is running.


\n"); } elsif ( ($must_login) && ($all_commands_unauthorized == 1) ) { $webpage->print("

You are attempting to log in with the username "$loginhash{'username'}" but your password is incorrect.

Either enter in the correct username/password above or click here to clear your authentication credentials and log back in as the default user.


\n"); } $auth_commands_checked = 1; # We have to lay the tables out by hand bec. of the colspanning :( $webpage->print("\n"); # # Print the first row of the command table # $webpage->print("\n"); $webpage->print("\t\n"); $webpage->print("\t\n"); $webpage->print("\t\n"); $webpage->print("\t\n"); $webpage->print("\t\n"); $webpage->print("\t\n"); $webpage->print("\n"); # # Print the second row of the menu table # $webpage->print("\n"); $webpage->print("\t\n"); $webpage->print("\t\n") ; $webpage->print("\t\n"); $webpage->print("\t\n"); $webpage->print("\t\n"); $webpage->print("\t\n"); $webpage->print("\t\n"); $webpage->print("\n"); # # Print the optional third row of the command table # This row can be defined at individual sites and contain commands # of your choice. Thanks to Ed Ravin (eravin@panix.com) for this. # &moncgi_custom_print_bar($face); # row 3, if any, for the local site $webpage->print("
Show Operational Status (summary) (failures only)Show Alert HistoryLoad scheduler stateStart schedulerList Disabled Hosts/ Watches/ SvcsTest Mon Config File
Show Operational Status   (full)Show Downtime LogSave scheduler stateStop schedulerReload auth fileList Mon PIDsReset Mon
"); } # query the server operational status ---------------------------------- sub query_opstatus { my ($detail_level) = (@_); my ($retval); # some variables for failures my (%op_success, %op_failure); my @scheduler_status = &mon_list_sched_state ; my $opstatus_table_width = "100%"; # the width of the whole opstatus tbl my $service_column_width = "40%"; # the width of the Service column my $service_legend_table_width = "70%"; # the width of the service legend table $webpage->print("
\n"); if ($scheduler_status[0] != 0) { $webpage->print ("The scheduler on $monhost:$monport is currently running. "); } else { my @sched_down_time = localtime ($scheduler_status[1]); my $pretty_sched_down_time = sprintf ("%.2d:%.2d:%.2d, %s-%s-%s\n", @sched_down_time[2, 1, 0, 3], @year_months[$sched_down_time[4]], $sched_down_time[5]+1900); if ($scheduler_status[1] != 0) { $webpage->print ("
The scheduler has been stopped since $pretty_sched_down_time.
\n"); } else { #value is undef, scheduler cannot be contacted (or auth failure) $webpage->print ("
The scheduler cannot be contacted at this time.
\n"); } } $webpage->print ("This page will reload every $reload_time seconds.
"); $webpage->print("

\n"); %op_success = &mon_list_successes; %op_failure = &mon_list_failures ; $webpage->print(""); $webpage->print ("\n"); $webpage->print ("\n"); $webpage->print ("\n"); # Give extra notification if the scheduler is down (this is a big deal!) unless ($scheduler_status[0] != 0) { if ($scheduler_status[1] != 0) { $webpage->print ("\n"); } else { $webpage->print ("\n"); } } &list_status($detail_level, %op_failure) if (%op_failure); if ($detail_level eq "failures") { $webpage->print ("\n") unless %op_failure; } else { &list_status($detail_level, %op_success) if (%op_success); } $webpage->print("
Host GroupService (legend)Last CheckedEst. Next Check
! SCHEDULER IS NOT RUNNING. RESULTS SHOWN BELOW MAY NOT BE CORRECT !
! SCHEDULER CANNOT BE CONTACTED. RESULTS SHOWN BELOW MAY NOT BE CORRECT !
No failures found.
\n"); # Print the legend below the table &moncgi_print_service_table_legend ($service_legend_table_width); } # # This subroutine tests whether a given group is allowed to be # shown to the user. # Inputs: Name of group to check # Outputs: 1 Group is allowed to be shown # 0 Group is not allowed to be shown # sub can_show_group { my ($group) = (@_); my $watch; # Do not print out the status for this group # unless it is on the "allowed" list if (@show_watch) { #user defined one or more watch keywords # # Loop through each access control and look for a match # foreach $watch (@show_watch) { if ( $group =~ m/^$watch$/ ) { #we found a match #print STDERR "Group $group matched '$watch'\n"; #DEBUG return 1; } } } else { #user didn't define any watch keywords, so show everything return 1; } return 0; } sub list_status { # This function lists the status of all hosts and services. It is # kind of a mess, but this is the function that 90% of the time # you will be viewing, and it has to do a lot. It could still be # cleaned up considerably though. # my ($detail_level, %op) = (@_); my (%group_list, $group, $service, $s, $g, $h); my (@time); my $bg_fail = $redlight_color ; my $bg_fail_noalerts = $yellowlight_color ; my $bg_ok = $greenlight_color; my $td_bg_color; my $face = $sans_serif_font_face; my %d = &mon_list_disabled ; my %desc = &mon_list_descriptions if $detail_level eq "full"; my $servertime = &mon_servertime; my (%ONDS, %ONDS_lastcheck, %ONDS_nextcheck) ; #special hashes of arrays for "OK, Non-Disabled Services" my %OPSTAT = %Mon::Client::OPSTAT; my $service_disabled_string ; my ($service_acked_string, $ackcomment) ; my $host_disabled_string ; my $watch_disabled_string ; my $failure_string; my $desc_string ; my %saw ; #used for sorting my @disabled_hosts; foreach $group (sort keys %op) { #begin group loop # Only show this group if we are allowed to see it next unless &can_show_group($group) ; if ($detail_level eq "full") { # get a list of members of the group if we haven't already # we need the defined() to deal with certain empty groups $group_list{$group} = [ &mon_list_group($group) ] unless (@{$group_list{$group}}); } foreach $service (sort keys %{$op{$group}}) { #begin service loop $s = \%{$op{$group}->{$service}}; $service_disabled_string = ""; $service_acked_string = ""; $host_disabled_string = ""; $watch_disabled_string = ""; $desc_string = ""; undef %saw; undef @disabled_hosts; $service_disabled_string = "(DISABLED)" if ${ d{"services"}{$group}{$service} }; # assemble the ACK message, if any. # Escape the HTML to avoid any potential nastiness if the # user requested it, otherwise, just pass it on through # as is. if ( $op{$group}{$service}{'ack'} != 0 ) { if ($untaint_ack_msgs) { # # We untaint # $ackcomment = $op{$group}{$service}{'ackcomment'} eq "" ? "(no ack msg)" : HTML::Entities::encode_entities($op{$group}{$service}{'ackcomment'}) ; } else { # # We don't untaint # $ackcomment = $op{$group}{$service}{'ackcomment'} eq "" ? "(no ack msg)" : $op{$group}{$service}{'ackcomment'} ; } $service_acked_string = " (ACKED: $ackcomment)" ; } foreach $g (keys %{$d{"hosts"}}) { foreach $h (keys %{$d{"hosts"}{$group}}) { push(@disabled_hosts , $h); } } # uniq and sort the returned array of disabled hosts @saw{@disabled_hosts} = (); @disabled_hosts = sort keys %saw; $host_disabled_string = join(" " , @disabled_hosts) if scalar(@disabled_hosts) > 0 ; $host_disabled_string = "
($host_disabled_string DISABLED)\n" if ($host_disabled_string ne ""); $watch_disabled_string = "(DISABLED)" if (${d{"watches"}{$group}}); $td_bg_color = ( ($watch_disabled_string ne "") || ($host_disabled_string ne "") ) ? $disabled_color : $BGCOLOR ; # Don't print the service individually if we are in brief mode # mode and the service is an ONDS. next if ( ($s->{"opstatus"} == $OPSTAT{"ok"}) && ($service_disabled_string eq "") && ($detail_level ne "full") ); # Now print the first column (the group and its status) $webpage->print("\n"); # check to see if full display was requested if ($detail_level eq "full") { $desc_string = ($desc{$group}{$service} ne "") ? " "$desc{$group}{$service}"" : " <no description specified>" ; $webpage->print("$watch_disabled_string"); $webpage->print("$group
\n("); $webpage->print(join(", ",@{$group_list{$group}})); $webpage->print(")$host_disabled_string\n"); } else { $webpage->print("\n"); $webpage->print("$watch_disabled_string"); $webpage->print("$group$host_disabled_string\n"); } # Now print the second column (the service and its status) if ($s->{"opstatus"} == $OPSTAT{"untested"}) { # for untested, don't use a bg in table cell and change # font color instead. $td_bg_color = ($service_disabled_string eq "") ? $unchecked_color : $disabled_color ; $webpage->print("\n"); $webpage->print("$service_disabled_string"); $webpage->print("${service}"); $webpage->print("
(UNCHECKED)
${desc_string}\n"); $webpage->print("\n"); } elsif ($s->{"opstatus"} == $OPSTAT{"fail"}) { # Check to see if the service has issued any alerts. # If not, then we call this service "failing" instead # of failed, on the assumption that if it hasn't # generated an alert yet, it isn't "really" important, # although you'd still like to know about it. if ( $s->{"alerts_sent"} == 0 ) { $td_bg_color = $bg_fail_noalerts ; $failure_string = "FAILED,NOALERTS" ; } else { $td_bg_color = $bg_fail ; $failure_string = "FAILED" ; # Also give the # of alerts if "full" view was selected $failure_string .= ",alerts_sent=" . $s->{"alerts_sent"} if $detail_level eq "full"; } $td_bg_color = ($service_disabled_string eq "") ? $td_bg_color : $disabled_color ; $webpage->print(""); $webpage->print("$service_disabled_string"); $webpage->print("${service}${desc_string} : \n"); $webpage->print("$s->{last_summary}\n"); $webpage->print("
($failure_string)"); $webpage->print(" ${service_acked_string}") if $service_acked_string ne ""; $webpage->print("\n"); } elsif ($s->{"opstatus"} == $OPSTAT{"ok"}) { $td_bg_color = ($service_disabled_string eq "") ? $bg_ok : $disabled_color ; $webpage->print(""); $webpage->print("$service_disabled_string"); $webpage->print("${service}${desc_string}\n"); } else { my $txt = ""; for (keys %OPSTAT) { $txt = $_ if ($s->{"opstatus"} == $OPSTAT{$_}); } $webpage->print(""); $webpage->print("${service_disabled_string}${service} (details: $txt)\n"); } if ($s->{"opstatus"} == $OPSTAT{"untested"}) { $webpage->print(""); $webpage->print("--"); $webpage->print("\n"); } else { $webpage->print(""); if ($s->{"last_check"}) { #traps have a null value for last_check # Service IS NOT a trap if ($s->{"opstatus"} == $OPSTAT{"fail"}) { #check svc status # If service is failing, print last checked time # and also print out last_OK time print &pp_sec_brief($s->{"last_check"} - $servertime); # We need to check if the var is defined, since traps # can throw us off. $webpage->print("
(Last OK: "); if ( (!defined($s->{"last_success"})) || ($s->{"last_success"} == 0) ) { #service is currently failed and does not have a last_success time defined # The event has never occurred $webpage->print ("Never") ; } else { #service is currently failed and has a last_success time defined # Pretty-print the time that the service was last OK @time = localtime ($s->{"last_success"}); $_ = $s->{"last_success"}; # Also calculate the delta and pretty-print that $s->{"last_success"} .= "(" . &pp_sec($time - $_) . " ago)"; print &pp_sec_brief($s->{"last_success"} - $servertime); } $webpage->print(")"); } else { # If service is not failing, just print last checked time print &pp_sec_brief($s->{"last_check"} - $servertime); } } else { # Service IS a trap, or has never been checked $webpage->print("--"); } $webpage->print("
\n"); } $webpage->print(""); # Handle case where service is a trap and hence last_check=undef if ( ( $s->{"timer"} == 0 ) && (!($s->{"last_check"})) ) { $webpage->print("--"); } else { $webpage->print (""); $webpage->print (&pp_sec_brief($s->{"timer"})); $webpage->print (""); $webpage->print ("  (test all on $group)") ; } $webpage->print("\n"); # The next 4 lines are the old way of printing (absolute time) #@time = localtime ($qtime + $s->{"timer"}); #$webpage->print(""); #printf("%.2d:%.2d:%.2d\n", @time[2, 1, 0] ); #$webpage->print("\n"); $webpage->print(""); } # end $service loop # # NEW: print a "compressed" version of OK, Non-Disabled Services (ONDS) # (the assumption being that ONDS's are not all that interesting, # let's not use up a lot of screen real estate discussing them) # # The whole thing is contingent upon us being in "brief" mode if ($detail_level ne "full") { # Build the array of ONDS's foreach $service (sort keys %{ $op{$group} }) { $s = \%{ $op{$group}->{$service} }; next if ( ${d{"services"}{$group}{$service}} ) ; if ($s->{"opstatus"} == $OPSTAT{"ok"}) { push (@{ $ONDS{$group} }, "$service"); if ($s->{"last_check"}) { #check to see if service is a trap # Service IS NOT a trap push (@{ $ONDS_lastcheck{$group} }, &pp_sec_brief($s->{"last_check"} - $servertime) ); } else { # Service IS a trap push (@{ $ONDS_lastcheck{$group} }, "--"); } if ( ( $s->{"timer"} == 0 ) && (!($s->{"last_check"})) ) { # Service IS a trap push (@{ $ONDS_nextcheck{$group} }, "--"); } else { # Service IS NOT a trap #push (@{ $ONDS_nextcheck{$group} }, &pp_sec_brief($s->{"timer"}) ); push (@{ $ONDS_nextcheck{$group} }, "" . &pp_sec_brief($s->{"timer"}) .""); } } } # print the OK, no disabled services for this host if any exist if ( $ONDS{$group} && (@{ $ONDS{$group} }) ) { $td_bg_color = ( ($watch_disabled_string ne "") || ($host_disabled_string ne "") ) ? $disabled_color : $BGCOLOR ; $webpage->print("\n"); $webpage->print(""); $webpage->print("$watch_disabled_string$group$host_disabled_string"); $webpage->print("\n"); $webpage->print(""); print join(", ", @{$ONDS{$group}}); $webpage->print("\n"); #Now print out the time(s) $webpage->print(""); print join(", ", @{$ONDS_lastcheck{$group}}); $webpage->print("\n"); $webpage->print(""); # Add the "test all" command if there is more than # one service in the group. push (@{ $ONDS_nextcheck{$group} }, "(test all on $group)") if scalar(@{ $ONDS_nextcheck{$group} }) > 1 ; print join(", ", @{$ONDS_nextcheck{$group}}); $webpage->print("\n"); $webpage->print("\n"); } } # end of ONDS printing } # end of $group loop } sub query_group { # # Print out info about the hosts in a particular hostgroup # my ($args) = (@_); my $group = &validate_name ($args); my (%hosts, $host, $retval, $c, $e, $s, $service, $status, %op, $bgcolor, $msg, $ena_checked, $dis_checked) ; my %OPSTAT = %Mon::Client::OPSTAT; my $table_width = "90%"; # Color to shade the cells depending on whether a user is allowed to # execute the disable/enable commands. my $disable_command_bgcolor = $auth_commands{'disable'}{'auth'} == 0 ? "bgcolor=\"$disabled_color\"" : "" ; my $enable_command_bgcolor = $auth_commands{'enable'}{'auth'} == 0 ? "bgcolor=\"$disabled_color\"" : "" ; my %d = &mon_list_disabled ; if (!defined $group) { $webpage->print("Invalid host group \"$group\"\n"); return undef; } @_ = &mon_list_group($group) ; # turn the array into a hash, which is what we really want foreach (@_) { $hosts{$_} = ""; } $webpage->print("
This page will reload every $reload_time seconds.
"); # Only show the rest of this page if we are allowed to see # information about this group. if ($show_watch_strict) { unless ( &can_show_group($group) ) { print $webpage->h3("You are not authorized to see detailed information for hostgroup '$group'."); print $webpage->h4("Please contact your system administrator for access."); return 0; } } $webpage->print("
Reload this page immediately.
\n"); ############################################################# # The table is divided into 3 sections: hostgroup, hosts, # and services ############################################################# # Start the form and set up our defaults print $webpage->startform(-action=>"$url"); $webpage->param('command','mon_state_change'); print $webpage->hidden(-name=>'command', ); $webpage->param('group',"$group"); print $webpage->hidden(-name=>'group', ); $webpage->param('h',"$monhost"); print $webpage->hidden(-name=>'h', ); $webpage->param('monport',"$monport"); print $webpage->hidden(-name=>'p', ); # Start the table $webpage->print("
"); ############################################################# # Print the hostgroup portion of the table ############################################################# $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print("\n"); $webpage->print(""); $bgcolor = ( ${d{"watches"}{$group}} ) ? $disabled_color : $BGCOLOR; $webpage->print("") ; $dis_checked = ( ${d{"watches"}{$group}} ) ? "checked" : ""; $ena_checked = (!${d{"watches"}{$group}} ) ? "checked" : ""; $webpage->print(""); $webpage->print(""); $webpage->print("\n"); ############################################################# # Print the hosts portion of the table ############################################################# $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print(""); foreach $host (keys %hosts) { if ($host =~ /^\*/) { $host =~ s/^\*// ; #strip the * or else mon dies $hosts{$host} = "disabled"; } else { $hosts{$host} = "enabled"; } } foreach $host (sort keys %hosts) { next if ($host =~ /^\*/); #ignore disabled hosts $webpage->print(""); if ($hosts{$host} eq "disabled") { #check to see if the host is disabled # the host is currently disabled $host =~ s/^\*// ; #strip the * or else mon dies (should already be gone but just in case, this is a bad thing to happen) $webpage->print(""); $webpage->print(""); $webpage->print(""); } else { #host is not currently disabled $webpage->print(""); $webpage->print(""); $webpage->print(""); } $webpage->print( "\n") ; } ############################################################# # Print the services portion of the table ############################################################# $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print("\n"); %op = &mon_list_opstatus; foreach $service (sort keys %{$op{$group}}) { #begin service loop $s = \%{$op{$group}->{$service}}; undef $bgcolor; if (${d{"services"}{$group}{$service}}) { #service is disabled $bgcolor = "$disabled_color"; } $dis_checked = ( ${d{"services"}{$group}{$service}} ) ? "checked" : ""; $ena_checked = (!${d{"services"}{$group}{$service}} ) ? "checked" : ""; if ($s->{"opstatus"} == $OPSTAT{"ok"}) { #OK $bgcolor = "$greenlight_color" unless $bgcolor; $msg="(status: OK)"; } elsif ($s->{"opstatus"} == $OPSTAT{"fail"}) { unless ($bgcolor) { if ( $s->{"alerts_sent"} == 0 ) { $bgcolor = "$yellowlight_color"; $msg=": $s->{'last_summary'}
(status: FAILED,NOALERTS)"; } else { $bgcolor = "$redlight_color"; $msg=": $s->{'last_summary'}
(status: FAILED)"; } if ( $s->{'ackcomment'} ne "" ) { $msg .= "(ACKED: $s->{'ackcomment'})"; } else { $msg .= "(no ack msg)"; } } } elsif ($s->{"opstatus"} == $OPSTAT{"untested"}) { $bgcolor = "$unchecked_color" unless $bgcolor; $msg="(status: UNTESTED)"; } $webpage->print("") ; # Check whether the service is disabled or enabled $webpage->print(""); $webpage->print(""); $webpage->print("\n"); } $webpage->print(""); $webpage->print(""); $webpage->print("
Hostgroup "$group"EnabledDisabled
$group (list downtime log for hostgroup $group)
Members of hostgroup \"$group\"EnabledDisabled
$host (DISABLED)$host
Services monitored on hostgroup \"$group\"
(test all services on hostgroup $group)
EnabledDisabled
$service $msg
    (list downtime log for $group:$service)
") ; $webpage->print("
    (test service $service on group $group immediately)
"); print $webpage->reset(-name=>'Cancel Changes'); $webpage->print("
"); print $webpage->submit(-name=>'Apply Changes'); $webpage->print("
"); print $webpage->end_form(); } sub end_page { # End the document with a footer and contact info &print_bar; if ($monadmin ne "") { $webpage->print("
For questions about this server,
contact $monadmin
mon.cgi v$VERSION
"); } print $webpage->end_html; } sub list_alerthist { # This function lists the alert history formatted in a table my @l = &mon_list_alerthist ; my ($line, $localtime); my $table_width = "80%"; $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); foreach $line (reverse sort {$a->{"time"} <=> $b->{"time"}} (@l)) { # Only show the alert if we are allowed to see information # about this group. if ($show_watch_strict) { next unless &can_show_group($line->{group}); } $localtime = localtime ($line->{"time"}); $webpage->print(""); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $line->{"alert"} =~ s{^.*\/([^/]*)$}{$1}; $webpage->print("\n"); my $args = "-"; if ($line->{"args"} !~ /^\s*$/) { $args = $line->{"args"}; } $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); } $webpage->print("
GroupServiceTypeTimeAlertArgsSummary
print("args=$line->{group}\">$line->{group}$line->{service}$line->{type}$localtime$line->{alert}$args$line->{summary}
\n"); print $webpage->hr; } sub svc_details { # Lists details about a particular alert's status, regardless of # whether the alert is successful or not. # As of 1.40, this function has been expanded considerably, and # has also been given the benefit of additional verbiage from the # global variables %alert_vars, @time_based_alert_vars, and # @pp_sec_alert_vars # my ($arg) = (@_); my ($group, $service) = split (/\,/, $arg); # Only show the rest of this page if we are allowed to see # information about this group. if ($show_watch_strict) { unless ( &can_show_group($group) ) { print $webpage->h3("You are not authorized to see detailed information for hostgroup '$group'."); print $webpage->h4("Please contact your system administrator for access."); return 0; } } my $status; my $retval; my (@pids, $server, $acknowledge_string, $name_string, $ackcomment_default); my (%op, $s, $g, $var, @time); my $table_width = "90%"; #let's give both tables the same width my $font_color; my %d = &mon_list_disabled; my %desc = &mon_list_descriptions; my $servertime = &mon_servertime; my @group_members = &mon_list_group($group) ; my $time_now = $servertime; my $enable_command_bgcolor = $auth_commands{'enable'}{'auth'} == 0 ? "bgcolor=\"$disabled_color\"" : "" ; my $disable_command_bgcolor = $auth_commands{'disable'}{'auth'} == 0 ? "bgcolor=\"$disabled_color\"" : "" ; my $test_command_bgcolor = $auth_commands{'test'}{'auth'} == 0 ? "bgcolor=\"$disabled_color\"" : "" ; my $ack_command_bgcolor = $auth_commands{'ack'}{'auth'} == 0 ? "bgcolor=\"$disabled_color\"" : "" ; my $disabled_hosts_string ; # Determine whether the service is failing or not # We'll be optimistic and assume it's NOT failing :) # Actually, unchecked services also show up as "ok". %op = &mon_list_successes; if ($op{$group}{$service}) { $status = "ok"; } else { $status = "fail"; %op = &mon_list_failures; } $webpage->print("
This page will reload every $reload_time seconds.
"); print $webpage->hr; $webpage->print("
"); if (${d{"services"}{$group}{$service}}) { #service is disabled $webpage->print ("Test detail for disabled service $service in group $group \n"); } elsif ($op{$group}{$service}{'opstatus'} == 1) { #OK $webpage->print ("Success detail for group $group\n"); $webpage->print ("and service test $service "); } elsif ($op{$group}{$service}{'opstatus'} == 7) { # service is Untested $webpage->print ("Test detail for group $group and service test $service"); $webpage->print ("
(service is UNCHECKED!)"); } else { # service is Failed (or some other opstatus code I don't know about) # If a service fails, print the detail and summary information # at the top. Yes, it's buried down in the middle of the page, but # it's important enough to take up screen real estate with and # bring it to the top. $font_color = ($op{$group}{$service}{'alerts_sent'} == 0) ? $yellowlight_color : $redlight_color ; $webpage->print("\n"); $webpage->print(""); # Now print the detail and summary information for the failed service $op{$group}->{$service}->{'last_summary'} = "<not specified>" if $op{$group}->{$service}->{'last_summary'} eq "" ; $op{$group}->{$service}->{'last_detail'} = "<not specified>" if $op{$group}->{$service}->{'last_detail'} eq "" ; $op{$group}->{$service}->{'last_detail'} =~ s/\n/
/g; $webpage->print("\n"); $webpage->print("\n"); $webpage->print("
"); $webpage->print("
"); $webpage->print ("Failure detail for group $group "); $webpage->print ("and service test $service: \n"); $webpage->print("
"); $webpage->print("
Failure summary:$op{$group}->{$service}->{'last_summary'}
Failure detail:$op{$group}->{$service}->{'last_detail'}
"); } # Issue warning if the whole group has been disabled. if ( ${d{"watches"}{$group}} ) { $webpage->print ("
(NOTE: group $group is disabled.)"); } # Issue warning if any hosts in the group are currently disabled. foreach (@group_members) { if ($_ =~ s/^\*//) { $disabled_hosts_string = " $_" . $disabled_hosts_string ; } } $disabled_hosts_string = "
(NOTE: The following host(s) in group $group are disabled: $disabled_hosts_string)" if $disabled_hosts_string ne ""; $webpage->print ($disabled_hosts_string); # Check to see if a test for this service is currently running # and report back if it is (because the status might not have updated # yet...) @pids = &mon_list_pids; if (!(@pids)) { $webpage->print("Unable to to determine whether this service is currently being tested (list_pids failed)!\n"); } else { shift @pids; #discard server PID, we don't need it for (@pids) { if ( ($_->{"watch"} eq $group) && ($_->{"service"} eq $service) ) { # we have a match, a monitor is currently running for # this group and this service. $webpage->print("
NOTE: A monitor for this service is currently running as PID $_->{'pid'}. Results/opstatus codes might change when this test finishes!\n"); } } } $webpage->print("
Reload this page immediately.
"); $webpage->print("
"); print $webpage->br; $webpage->print(""); $webpage->print ("\n"); if (${d{"services"}{$group}{$service}}) { #service is disabled, offer to enable $webpage->print("\n"); } else { #service is enabled, offer to disable $webpage->print("\n"); } $webpage->print (""); $webpage->print("\n"); $webpage->print("
Test service $service on group $group immediately(ENABLE service $service in group $group)(DISABLE service $service in group $group)List downtime log for service $service and group $group
\n"); # If the service is in a failure state, offer to ack it. if ($status eq "fail") { $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print("\n"); $webpage->print("
"); if ($op{$group}{$service}{'ack'} != 0) { # Service has already been acked, offer to re-ack $acknowledge_string = "Re-acknowledge this failure:
(changes the acknowledgement message)
"; $ackcomment_default = "Was:\"$op{$group}{$service}{'ackcomment'}\""; } else { # Service has not yet been acked, offer to ack $acknowledge_string = "Acknowledge this failure:
(disables all subsequent alerts for this failure period)
"; $ackcomment_default = "${name_string}"; } $webpage->print("$acknowledge_string "); $webpage->print("
"); # It is crucial that we reset the param value, otherwise the # value of the default will be ignored. print $webpage->startform(-action=>"$url"); $webpage->param('ackcomment', $ackcomment_default); $webpage->param('h',"$monhost"); print $webpage->hidden(-name=>'h'); $webpage->param('p',"$monport"); print $webpage->hidden(-name=>'p'); print $webpage->textfield(-name=>'ackcomment', -value=>"$ackcomment_default", -size=>40, ); # The textarea is nice, but you lose the ability to hit ENTER to # submit your ack, which I really really like. # print $webpage->textarea(-name=>'ackcomment', # -value=>"$ackcomment_default", # -rows=>2, # -wrap=>'soft', # -columns=>40, # ); $webpage->print("  "); # We also have to reset this param value $webpage->param('command','mon_ack'); print $webpage->hidden(-name=>'command', ); $webpage->param('args',"$group,$service"); # We also have to reset this param value print $webpage->hidden(-name=>'args', ); print $webpage->submit(-name=>'ack'); print $webpage->end_form(); $webpage->print("
\n"); } $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); my $desc_string = ($desc{$group}{$service} ne "") ? ""$desc{$group}{$service}"" : "<no description specified>" ; $webpage->print("\n"); foreach $var (sort {$b cmp $a } (keys %{$op{$group}{$service}}) ) { # Special cases where we have a time formatted in secs # since 1970, we'll make it look purty if (grep /^${var}$/, @time_based_alert_vars) { # We need to check if the var is defined, since traps # can throw us off. if ( (!defined($op{$group}->{$service}->{$var})) || ($op{$group}->{$service}->{$var} == 0) ) { # The event has never occurred $op{$group}->{$service}->{$var} = "Never" ; } else { # Pretty-print the time @time = localtime ($op{$group}->{$service}->{$var}); $_ = $op{$group}->{$service}->{$var}; $op{$group}->{$service}->{$var} = sprintf ("%.2d:%.2d:%.2d, %s-%s-%s\n", @time[2, 1, 0, 3], @year_months[$time[4]], $time[5]+1900); # Also calculate the delta and pretty-print that $op{$group}->{$service}->{$var} .= "(" . &pp_sec($time_now - $_) . " ago)"; } } elsif ($op{$group}->{$service}->{$var} eq "") { # special case where value of $var is empty #(i.e. mon has never seen the service fail) $op{$group}->{$service}->{$var} = "-"; } elsif (grep /^${var}$/, @pp_sec_alert_vars) { if ( (!defined($op{$group}->{$service}->{$var})) || ($op{$group}->{$service}->{$var} == 0) ) { # The event has never occurred $op{$group}->{$service}->{$var} = "Never" ; } else { # Special case where time is a duration and # should be pretty printed. $op{$group}->{$service}->{$var} = &pp_sec($op{$group}->{$service}->{$var}); } } $op{$group}->{$service}->{$var} =~ s/\n/
/g; $webpage->print("\n"); } $webpage->print("
Variable Description (name)Value
Service Description$desc_string
$alert_vars{$var} ($var)$op{$group}->{$service}->{$var}
\n"); print $webpage->hr; } sub list_disabled { # This function lists all the disabled watches, services, and hosts # and returns the result as pretty(?) HTML my (%d, $group, $service, $host, $watch); my (@disabled_hosts, @disabled_svcs); my $enable_command_bgcolor = $auth_commands{'enable'}{'auth'} == 0 ? "bgcolor=\"$disabled_color\"" : $BGCOLOR ; %d = &mon_list_disabled; print $webpage->hr; $webpage->print("
\n"); # Start the form and set up our defaults print $webpage->startform(-action=>"$url"); $webpage->param('command','mon_state_change_enable_only'); print $webpage->hidden(-name=>'command', ); $webpage->param('h',"$monhost"); print $webpage->hidden(-name=>'h'); $webpage->param('p',"$monport"); print $webpage->hidden(-name=>'p'); $webpage->print("\n"); $webpage->print( ""); $webpage->print(""); $webpage->print(""); $webpage->print( "\n"); $webpage->print( ""); if ( scalar(keys %{$d{"watches"}}) > 0 ) { for (keys %{$d{"watches"}}) { $webpage->print(""); $webpage->print("\n"); } } else { $webpage->print("\n"); } # # Disabled hosts portion of table # $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print( "\n"); foreach $group (keys %{$d{"hosts"}}) { foreach $host (keys %{$d{"hosts"}{$group}}) { push(@disabled_hosts,""); push(@disabled_hosts,""); } } if (scalar(@disabled_hosts) > 0 ) { print join("\n", @disabled_hosts); } else { $webpage->print(""); $webpage->print(""); $webpage->print("\n"); } # # Disabled services portion of table # $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print("\n"); foreach $watch (keys %{$d{"services"}}) { foreach $service (keys %{$d{"services"}{$watch}}) { push(@disabled_svcs, ""); push(@disabled_svcs,""); } } if (scalar(@disabled_svcs) > 0 ) { print join("\n", @disabled_svcs); } else { $webpage->print("\n"); } $webpage->print(""); $webpage->print("\n"); $webpage->print("

Disabled Watches

Enable?

$_
<NONE>

Disabled Hosts

Enable?

$group:$host
<NONE>

Disabled Services

Enable?

$watch:$service
<NONE>
"); print $webpage->reset(-name=>'Cancel Changes'); $webpage->print("
"); print $webpage->submit(-name=>'Apply Changes'); $webpage->print("
\n"); print $webpage->end_form(); $webpage->print("
\n"); } sub list_dtlog { # Accepts the following arguments, all of which are optional: # (group,service,sortby,first log entry to show, last log entry to show) # to sort by. # # Default sort key is failtime. # # If {service,group} is null, shows detail about all failures # for the given {group,service}. # # Default first log entry to show is 1. # # Default last log entry to show is 1+$dtlog_max_failures_per_page. # # No arguments means show all service failures for all groups, # sorted by failtime. # # Someday it would be nice to take args like time ranges, etc., but # that capability should really be built into Mon itself since it # is such a useful feature and something which could leverage a lot # of mon's timeperiod work as well. # # Original patch by Martha H Greenberg # my ($arg) = (@_); my ($group, $service,$sortby,$dtlog_begin,$dtlog_end); if ( defined($arg) ) { ($group, $service,$sortby,$dtlog_begin,$dtlog_end) = split (/\,/, $arg) ; } else { $group = ""; $service = ""; $sortby = "" ; $dtlog_begin = ""; $dtlog_end = ""; } $dtlog_begin = 1 unless ( ($dtlog_begin) && ($dtlog_begin > 0) ); $dtlog_end = ($dtlog_begin + $dtlog_max_failures_per_page - 1) unless ( ($dtlog_end) && ($dtlog_end > 0) ); $sortby = "failtime" unless ($sortby); my $face = $sans_serif_font_face; my $summary_table_width = "80%"; my $dt_table_width = "100%"; my $i; # This has keeps track of the sortby keys and what # their descriptions map to. my %sortby_key = ("group" => "Group", "service" => "Service", "failtime" => "Service Failure Begin Time", "timeup" => "Service Failure End Time", "downtime" => "Total Observed Failure Time", "interval" => "Testing Interval", "summary" => "Summary", ); print $webpage->hr; my ($line, $localtimeup, $localfailtime, $ppdowntime, $ppinterval); my ($first_failure_time, $total_failures, $mtbf, $mean_recovery_time, $median_recovery_time, $std_dev_recovery_time, $min_recovery_time, $max_recovery_time, @l) = &mon_list_dtlog($group, $service) ; my ($ppfft, $ppmtbf, $ppmean_recovery_time, $ppmedian_recovery_time, $ppmin_recovery_time, $ppmax_recovery_time, $ppstd_dev_recovery_time); # Estimated uptime calculation my $time_now = time; my $approx_uptime_pct = ( ( ($time_now - $first_failure_time + $mtbf ) > 0) && ( ($time_now - $first_failure_time + $mtbf - scalar(@l) * $mean_recovery_time > 0 ) ) ) ? sprintf("%.2f%", ( ( ($time_now - $first_failure_time + $mtbf) - (scalar(@l) * $mean_recovery_time) ) / ($time_now - $first_failure_time + $mtbf) ) * 100 ) : "<not applicable>"; $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $ppfft = $first_failure_time; my @fftime = localtime ($first_failure_time); $ppfft = sprintf ("%s, %s %d, %.2d at %.2d:%.2d:%.2d", @days_of_week[$fftime[6]], @year_months[$fftime[4]], $fftime[3], $fftime[5] + 1900 , @fftime[2,1,0] ); $webpage->print("\n\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); $ppmtbf = &pp_sec($mtbf); $webpage->print("\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); $ppmean_recovery_time = &pp_sec($mean_recovery_time); $webpage->print("\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); $ppmedian_recovery_time = &pp_sec($median_recovery_time); $webpage->print("\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); $ppstd_dev_recovery_time = &pp_sec($std_dev_recovery_time); $webpage->print("\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); $ppmin_recovery_time = &pp_sec($min_recovery_time); $webpage->print("\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); $ppmax_recovery_time = &pp_sec($max_recovery_time); $webpage->print("\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("
\n"); $webpage->print("Downtime Summary For Hostgroup "); if ($group eq "") { $webpage->print("<any>"); } else { $webpage->print(""$group""); } $webpage->print(" and Service "); if ($service eq "") { $webpage->print("<any>"); } else { $webpage->print(""$service""); } $webpage->print("
Log begins at:$ppfft
Total observed service failures:$total_failures
Mean time between service failures:$ppmtbf
Mean observed service failure time:$ppmean_recovery_time
Median observed service failure time:$ppmedian_recovery_time
Standard deviation of observed service failure times:$ppstd_dev_recovery_time
Minimum observed service failure time:$ppmin_recovery_time
Maximum observed service failure time:$ppmax_recovery_time
Approximate percentage of time in failure-free operation:$approx_uptime_pct
\n"); return 0 if scalar(@l) == 0; # stop if we returned no downtime events ($dtlog_begin, $dtlog_end) = &moncgi_list_dtlog_navtable ($url, $group, $service, $sortby, $dtlog_begin, $dtlog_end, $total_failures, scalar(@l), %sortby_key) ; # Start printing the actual downtime log table. # Print the header as a table with a thicker border $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); # default sort order is "failtime", so no need to resort if ($sortby ne "failtime") { # do a forward-alphanumeric or reverse-numeric sort, # depending on the sortby parameter if ( ($sortby eq "group") || ($sortby eq "service") || ($sortby eq "summary") ) { @l = (sort {$a->{"$sortby"} cmp $b->{"$sortby"}}(@l)); } else { @l = (reverse sort {$a->{"$sortby"} <=> $b->{"$sortby"}}(@l)); } } # Now print the rows of the downtime table for ( $i = $dtlog_begin ; $i <= $dtlog_end ; $i++ ) { $line = $l[$i-1]; $webpage->print(""); $webpage->print(""); $webpage->print("\n"); $localfailtime = localtime ($line->{"failtime"}); $webpage->print("\n"); $localtimeup = localtime ($line->{"timeup"}); $webpage->print("\n"); $ppdowntime = &pp_sec ($line->{"downtime"}); $webpage->print("\n"); $ppinterval = &pp_sec ($line->{"interval"}); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); } $webpage->print("
Entry$sortby_key{\"group\"}$sortby_key{\"service\"}$sortby_key{\"failtime\"}$sortby_key{\"timeup\"}$sortby_key{\"downtime\"}$sortby_key{\"interval\"}$sortby_key{\"summary\"}
$i{\"group\"}\">"); $webpage->print("$line->{\"group\"}{\"group\"},$line->{\"service\"}\">"); $webpage->print("$line->{\"service\"}$localfailtime$localtimeup$ppdowntime$ppinterval$line->{\"summary\"}
\n"); &moncgi_list_dtlog_navtable ($url, $group, $service, $sortby, $dtlog_begin, $dtlog_end, $total_failures, scalar(@l), %sortby_key) ; print $webpage->hr; undef @l; } sub list_pids { my $retval; my @pids = &mon_list_pids ; print $webpage->hr; print $webpage->h2("List of mon PID's:"); $webpage->print("Unable to list PID's on server!
\n") if !(@pids); my $server = shift @pids; $webpage->print ("Server PID is $server

\n"); if ( scalar(@pids) > 0 ) { $webpage->print("PID's of currently active monitors:
\n"); $webpage->print("
\n"); for (@pids) { $webpage->print (join ("", "\n", )); } $webpage->print("
HostgroupServicePID
", $_->{"watch"}, "", $_->{"service"}, "", $_->{"pid"}, "
\n"); } else { $webpage->print("<No monitors are running at this time>
\n"); } } ############################################################### # Mon-specific functions. These all have the common feature # that they connect to a Mon server and retrieve some data. # Generally these functions are called by the Presentation functions, # or if they are called directly they do no special output # formatting. ############################################################### sub mon_connect { # Performs the basic connection, and if necessary, authentication, # to a mon server. # If successful, returns a 1 # If unsuccessful because of login failure, returns -1 # If unsuccessful because of other reasons, returns 0 my $retval; # # If we're not connected, we need to connect and possibly authenticate # if ( (! defined($c->connected()) ) || ( $c->connected() == 0 ) ) { $c->connect(); if ($c->error) { $retval = $c->error; print "mon_connect: Could not contact mon server "$monhost": $retval \n" if $connect_failed == 0 ; $connect_failed = 1; #set the global $connect_failed var return 0; } # # Test to see if login is required, and if so, # set the username and password. # if ($must_login) { #print "
Login is "$loginhash{\"username\"}" and passwordis "$loginhash{\"password\"}"\n
"; # # Test to see if username and password are blank. # If so, then use the default username/password. # if ( ( ( ! defined($loginhash{"username"}) ) && ( ! defined($loginhash{"password"}) ) ) || ( ( $loginhash{"username"} eq "" ) && ( $loginhash{"password"} eq "" ) ) ) { # Login is required but no username/password was given, so # try the login and password to the default account $loginhash{"username"} = $default_username ; $loginhash{"password"} = $default_password ; } $c->login(%loginhash); #print "connected as user $loginhash{'username'}\n"; #DEBUG } } if ( ($must_login) && ( defined($c->error) ) && ( ($c->error =~ /530 login unsuccessful/) || ($c->error =~ /no password/) || ($c->error =~ /no username/) ) ) { # Login was required and unsuccessful, present the authform # if it hasn't already been presented. Since some presentation # functions call multiple methods you could very easily # end up with multiple prompts, which is confusing to the # user. &moncgi_authform ($command,"$args") unless $has_prompted_for_auth; $has_prompted_for_auth = 1; return -1; } return 1; } sub mon_views { my ($viewreq); my $conn = &mon_connect ; return 0 if $conn == 0; @views = $c->list_views (); print STDERR "list_views failed" if ($c->error); $viewreq = $webpage->param(-name=>"setview"); if ($viewreq && ($viewreq eq '--all--' || grep(/^$viewreq$/, @views))) { $vcookie = $webpage->cookie(-name=>"$vcookie_name", -value=>"$viewreq", -expires=>"+1y", -path=>"$vcookie_path", ); $curview = $viewreq; } else { $curview = $webpage->cookie(-name=>"$vcookie_name"); } if ($curview && $curview ne '--all--') { $c->setview($curview); } return 1; } sub mon_list_group { # List all the hosts in a given group. Returns an array of hosts # if successful, or undef if failure. my ($group) = (@_); my (@hosts, $retval); my $conn = &mon_connect ; return 0 if $conn == 0; @hosts = $c->list_group ($group); unless ($c->error) { return @hosts ; } else { $retval = $c->error; $webpage->print ("Could not list groups on mon server "$monhost": $retval"); return undef; } } sub mon_list_watch { # List all the watches. Returns an array of defined watch groups and # services. # if successful, or undef if failure. my ($group) = (@_); my (@hosts, $retval); my $conn = &mon_connect ; return 0 if $conn == 0; @hosts = $c->list_watch ($group); unless ($c->error) { return @hosts ; } else { $retval = $c->error; $webpage->print ("Could not list watches on mon server "$monhost": $retval"); return undef; } } sub mon_list_failures { # This function returns a hash of failures. my (%op, $retval); my $conn = &mon_connect ; return 0 if $conn == 0; %op = $c->list_failures(); if ($c->error) { $retval = $c->error; print "Could not execute list failures command mon server on server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"; &moncgi_switch_user($retval); } else { #print "
list_failures command executed successfully
\n"; } return %op; } sub mon_list_successes { # This function returns a hash of successes my (%op, $retval); my $conn = &mon_connect ; return 0 if $conn == 0; %op = $c->list_successes(); if ($c->error) { $retval = $c->error; print "Could not execute list successes command on server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"; &moncgi_switch_user($retval); } return %op; } sub mon_list_opstatus { # This function returns a hash of opstatus # It should accept an optional anonymous array argument, of # [group, service ...] to only return # the opstatus of the group/service pairs you are interested in. # # But it doesn't because I am lazy and don't need this feature # right now :) #my ($criteria) = @_; my (%op, $retval); my $conn = &mon_connect ; return 0 if $conn == 0; %op = $c->list_opstatus(); if ($c->error) { $retval = $c->error; print "Could not execute list opstatus command on server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); } return %op; } sub mon_list_disabled { # This function lists all the disabled watches, services, and hosts # and returns the result as a hash my (%d, $retval); my $conn = &mon_connect ; return 0 if $conn == 0; %d = $c->list_disabled(); if ($c->error) { $retval = $c->error; print "Could not execute list disabled command mon server on server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); } else { #print "
list_disabled command executed successfully
\n"; } return %d; } sub mon_reload { # Reload mon config file. # Right now the only option supported is to reload the auth.cf file. # my ($what) = (@_); print $webpage->hr; print $webpage->h2("Reloading mon..."); my $retval; my $conn = &mon_connect ; return 0 if $conn == 0; $retval = $c->reload($what); if ($c->error) { $retval = $c->error; print "Could not reload "$what" mon server on server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); } else { print "mon server on "$monhost" successfully reloaded.\n"; } } sub mon_loadstate { # A simple wrapper function that calls mon_loadstate_savestate with # the proper arguments. my ($state) = (@_); $state = "disabled" if $state eq ""; print $webpage->hr; print $webpage->h2("Loading saved state for $state..."); &mon_loadstate_savestate("load",$state); } sub mon_savestate { # A simple wrapper function that calls mon_loadstate_savestate with # the proper arguments. my ($state) = (@_); $state = "disabled" if $state eq ""; print $webpage->hr; print $webpage->h2("Saving current state for $state..."); &mon_loadstate_savestate("save",$state); } sub mon_loadstate_savestate { # Loads or saves state of a mon object specifed by $target. Currently # the only object supported by mon for loading/saving state is the # state of the scheduler, so using this function with $target="" will # load or save the state of the scheduler. # # The load/save action is specified by the variable $action my ($action, $state) = (@_); my $retval; my $conn = &mon_connect ; return 0 if $conn == 0; if ($action eq "save") { ($retval = $c->savestate($state)) || ($retval = $c->error); } elsif ($action eq "load") { ($retval = $c->loadstate($state)) || ($retval = $c->error); } if ($c->error ne "") { print "Could not $action state mon server for state "$state" on server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); } else { print "$action state succeded on mon server "$monhost".\n"; } } # Stop or start scheduler --------------------------------------------- sub mon_schedctl { # Either stops or starts the scheduler, depending on how it was called, # either with the "stop" argument or the "start" argument. # No return value. my ($action) = (@_); print $webpage->hr; print $webpage->h2("MON: $action scheduler..."); my $retval; my $conn = &mon_connect ; return 0 if $conn == 0; if ($action eq "stop") { $retval = $c->stop(); } elsif ($action eq "start") { $retval = $c->start(); } if ($c->error) { $retval = $c->error; print "Could not $action mon server on server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); } else { print "$action scheduler on mon server on "$monhost" succeeded.\n"; } } sub mon_list_pids { my $retval; my @pids; my $conn = &mon_connect ; #test return 0 if $conn == 0; @pids = $c->list_pids; # my $server = shift @pids; if ($c->error) { $retval = $c->error; print "Could not list pids on mon server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"; &moncgi_switch_user($retval); return undef; } else { return @pids; } } sub mon_list_descriptions { # This subroutine executes the list_descriptions() routine and, # if successful, returns a hash of service descriptions, indexed # by watch and service. Returns undef on failure. # my $retval; my %desc; my $conn = &mon_connect ; return 0 if $conn == 0; %desc = $c->list_descriptions; if ($c->error) { $retval = $c->error; print "Could not list descriptions on mon server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); return undef; } else { return %desc; } } # Enable a disabled host/watch/service ---------------------------------- sub mon_enable { my ($arg) = (@_); my ($type, $arg1, $arg2) = split (/\,/, $arg); print $webpage->h2("Enabling service..."); my $retval; # Only show the rest of this page if we are allowed to see # information about this group. if ( ($show_watch_strict) && ( ! $type eq "host") ) { unless ( &can_show_group($arg1) ) { print $webpage->h3("You are not authorized to see detailed information for hostgroup '$arg1'."); print $webpage->h4("Please contact your system administrator for access."); return 0; } } my $conn = &mon_connect ; return 0 if $conn == 0; if ($type eq "service") { $retval = $c->enable_service($arg1, $arg2); } elsif ($type eq "host") { $retval = $c->enable_host($arg1); } elsif ($type eq "watch") { $retval = $c->enable_watch($arg1); } if ($c->error) { $retval = $c->error; print "Could not successfully execute command enable_${type} with arguments "$arg1" and "$arg2" on server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); return 0; } else { print "enable_${type} succeeded for "; if ($type eq "service") { print "
watch $arg1, service $arg2"; } elsif ($type eq "host") { print "
host $arg1"; } elsif ($type eq "watch") { print "
watch $arg1"; } print "
\n"; } return 1; } # Disable an enabled service ---------------------------------------------- sub mon_disable { my ($arg) = (@_); my ($type, $arg1, $arg2) = split (/\,/, $arg); print $webpage->h2("Disabling service..."); my $retval; # Only show the rest of this page if we are allowed to see # information about this group. if ( ($show_watch_strict) && ( ! $type eq "host") ) { unless ( &can_show_group($arg1) ) { print $webpage->h3("You are not authorized to see detailed information for hostgroup '$arg1'."); print $webpage->h4("Please contact your system administrator for access."); return 0; } } my $conn = &mon_connect ; return 0 if $conn == 0; if ($type eq "service") { $retval = $c->disable_service($arg1, $arg2); } elsif ($type eq "host") { $retval = $c->disable_host($arg1); } elsif ($type eq "watch") { $retval = $c->disable_watch($arg1); } if ($c->error) { $retval = $c->error; print "Could not successfully execute command disable_${type} with arguments "$arg1" and "$arg2" on server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); } else { print "disable_${type} succeeded for "; if ($type eq "service") { print "
watch $arg1, service $arg2"; } elsif ($type eq "host") { print "
host $arg1"; } elsif ($type eq "watch") { print "
watch $arg1"; } print "
\n"; } } sub mon_test_service { # Test a service immediately. # Accepts as arguments a group and a service, and optionally an # test argument, which can be either alert, upalert, or startupalert. # Default test is "alert" my ($arg) = (@_); my ($group, $service,$test) = split (/\,/, $arg); # Only show the rest of this page if we are allowed to see # information about this group. if ($show_watch_strict) { unless ( &can_show_group($group) ) { print $webpage->h3("You are not authorized to see detailed information for hostgroup '$group'."); print $webpage->h4("Please contact your system administrator for access."); return 0; } } $test = "monitor" if $test eq ""; print $webpage->h2("Performing $test test on service $service in hostgroup $group..."); my $retval; my $conn = &mon_connect ; return 0 if $conn == 0; $retval = $c->test($test, $group, $service); if ($c->error) { $retval = $c->error; print "Could not successfully execute command "$test" test service "$service" on hostgroup "$group" on server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"; &moncgi_switch_user($retval); } else { print "test $test completed for service $service and hostgroup $group:


"; print " $retval
\n"; } } sub mon_test_config { # Test the mon config file immediately. # Takes no argument (there is only one config file after all) # print $webpage->h2("Testing the syntax of your mon config file..."); my $retval; my $conn = &mon_connect ; return 0 if $conn == 0; my @s = $c->test_config; if ($c->error) { $retval = $c->error; if ( $retval !~ /^520 test config completed/ ) { # command not authorized print "Could not successfully execute command "test config" on server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); } elsif ( (defined ($s[0])) && ($s[0] == 0) ) { # there are config file errors $webpage->print("Error in config file found:
" . $s[1] . "\n\n
"); $webpage->print("Please note that you may have other errors in your configuration file, but the checking stops after the first one is found.
"); } else { # some other error occurred print "Could not successfully execute command "test config" on server "$monhost":
$retval (perhaps you don't have permissions in auth.cf?)
\n"; &moncgi_switch_user($retval); } } else { $webpage->print("Test config completed OK, no errors were found in your config file

\n"); } } # Reset mon ---------------------------------------------------- sub mon_reset { ($args) = (@_); print $webpage->hr; print $webpage->h2("Reset mon..."); my $retval; my $conn = &mon_connect ; return 0 if $conn == 0; if ( $args eq "keepstate" ) { # specify we want to keep the scheduler state $retval = $c->reset($args); } else { # reset scheduler state, don't give reset any arguments $retval = $c->reset(); } if ($c->error) { $retval = $c->error; $webpage->print ("Could not reset mon server on server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"); &moncgi_switch_user($retval); } else { $webpage->print ("mon server on "$monhost" successfully reset.

"); if ( $args eq "keepstate" ) { $webpage->print ("Scheduler state was NOT reset.

All previously disabled hosts/groups/services are still disabled."); } else { $webpage->print ("Scheduler state was reset.

All hosts/groups/services are now enabled."); } $webpage->print ("
\n"); } } # List alert history -------------------------------------------------- sub mon_list_alerthist { print $webpage->hr; print $webpage->h2("Alert History:"); my $retval ; my $conn = &mon_connect ; return 0 if $conn == 0; my @l = $c->list_alerthist(); if ($c->error) { $retval = $c->error; print "Could not list alert history on mon server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"; &moncgi_switch_user($retval); } else { #print "
alert history on on "$monhost" successfully retrieved.
\n"; } return @l; } sub mon_list_sched_state { # This function returns an array, @scheduler_state, which # contains the state of the scheduler. This is not exactly # documented, but @scheduler_state[0]==0 seems to indicate # that the scheduler is stopped and @scheduler_state[1] # seems to hold the time (in epoch seconds) since the scheduler was # stopped. my (@scheduler_state, $retval); my $conn = &mon_connect ; return 0 if $conn == 0; @scheduler_state = $c->list_state(); if ($c->error) { $retval = $c->error; print "Could not execute list state command mon server on server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"; &moncgi_switch_user($retval); } else { #print "
list state command executed successfully
\n"; } return @scheduler_state; } sub mon_list_dtlog { # Lists the downtime log (all of it) and returns the results as # an array of hash references. my ($group, $service) = (@_); my $retval ; my (@ltmp, @l, $line); my (@recovery_times); my ($first_failure_time, $mtbf, $mean_recovery_time, $median_recovery_time, $std_dev_recovery_time, $max_recovery_time, $min_recovery_time); my $max_recovery_time_default = -1; #some arbitrary number less than 0 my $min_recovery_time_default = 9999999999999; #some arbitrary really big number my $conn = &mon_connect ; return 0 if $conn == 0 ; @ltmp = $c->list_dtlog(); if ($c->error) { $retval = $c->error; $webpage->print("Could not list downtime log on mon server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"); &moncgi_switch_user($retval); return undef; } my $time_now = time; $max_recovery_time = $max_recovery_time_default; # initialize this to something really small $min_recovery_time = $min_recovery_time_default; # initialize this to something really big $first_failure_time = $time_now; # # Loop through all downtimes, get first downtime, min and max, # and filter based on criteria/specifications. # foreach $line (reverse sort {$a->{"failtime"} <=> $b->{"failtime"}}(@ltmp)){ # # Test to see if this is the first failure time. # if ($line->{"failtime"} < $first_failure_time) { # since this list is already sorted, this will only be true # the very first time we go thru this loop $first_failure_time = $line->{"failtime"} if $line->{"failtime"} < $first_failure_time ; } # # Skip this line if a group and/or service param was # specified and the param doesn't match. # if ( ( defined($group) ) && ($group ne "") && ($group ne $line->{"group"}) ) { next; } if ( ( defined($service) ) && ($service ne "") && ($service ne $line->{"service"}) ) { next; } # # Only show this downtime log entry if we are allowed to see # information about this group. This is probably slow but # no one said security was efficient! # if ($show_watch_strict) { unless ( &can_show_group($line->{"group"}) ) { # This line should not be shown to the user next; } } # # Add this downtime to the list of downtimes # for statistical calculation purposes. # push(@l, $line); push(@recovery_times, $line->{"downtime"}); # # set min and max downtimes # $min_recovery_time = $line->{"downtime"} if $line->{"downtime"} < $min_recovery_time; $max_recovery_time = $line->{"downtime"} if $line->{"downtime"} > $max_recovery_time; } undef @ltmp; #we don't need @ltmp's memory anymore # # Calculate mean recovery time # $mean_recovery_time = &arithmetic_mean(@recovery_times); # # also calculate median recovery time # $median_recovery_time = &median(@recovery_times); # # calculate the mean time between failures as: # (total elapsed time since first failure + E(time until first failure))/(total # of failures) # $mtbf = (scalar(@recovery_times) == 0) ? 0 : ($time_now - $first_failure_time + (($time_now - $first_failure_time) / scalar(@recovery_times))) / scalar(@recovery_times); # # Calculate std deviation of failure times # $std_dev_recovery_time = &std_dev(@recovery_times); # # In case $max_recovery_time and $min_recovery_time are unset # (i.e. there were no failures), set them to sensible defaults. # $max_recovery_time = 0 if $max_recovery_time == $max_recovery_time_default; $min_recovery_time = 0 if $min_recovery_time == $min_recovery_time_default; return $first_failure_time, scalar(@recovery_times), $mtbf, $mean_recovery_time, $median_recovery_time, $std_dev_recovery_time, $min_recovery_time, $max_recovery_time, @l ; } sub mon_ack { # This subroutine takes a comma separated list of a group and a service # as input. # It relies on the global "$ackcomment" for its text. # # If the user is authenticated as anyone other than the default # user, mon_ack inserts their name into the ack comment. # # mon_ack sends an acknowledgement to the mon server for the given # service on the host, the effect of which is to disable any further # alerts for the current failure period. # Ack'ing a service that is not in a failure state will produce the # following error: # "520 service is in a non-failure state" # # We get around this by only presenting the option to ack for # services that are currently in a failure state. # # This function does not return any values. my ($args) = @_; my ($group, $service) = split (/\,/, $args); # Only show the rest of this page if we are allowed to see # information about this group. if ($show_watch_strict) { unless ( &can_show_group($group) ) { print $webpage->h3("You are not authorized to see detailed information for hostgroup '$group'."); print $webpage->h4("Please contact your system administrator for access."); return 0; } } my $retval ; my $conn = &mon_connect ; return 0 if $conn == 0 ; my $name_string = ( ($loginhash{"username"} eq $default_username) || ($loginhash{"username"} eq "") ) ? "" : "[$loginhash{'username'}]: "; # Ack the service w/comment $retval = $c->ack($group, $service, "${name_string}${ackcomment}"); if ($c->error) { $retval = $c->error; $webpage->print("mon_ack: Could not ack service \"$service\" in group \"$group\" on mon server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"); &moncgi_switch_user($retval); } else { $webpage->print("Service \"$service\" in group \"$group\" acknowledged successfully."); } } sub mon_servertime { # This subroutine calls the Mon::Client::servertime function and # returns the time as a scalar, or undef if there is an error. my $retval; my $servertime; my $conn = &mon_connect ; return 0 if $conn == 0; $servertime = $c->servertime; if ($c->error) { $retval = $c->error; print "Could not get server time on mon server "$monhost": $retval (perhaps you don't have permissions in auth.cf?)\n"; &moncgi_switch_user($retval); return undef; } else { return $servertime; } } sub mon_checkauth { # This subroutine checks the authorization for a given command # to see if it is authorized by the server. # Returns: # -1 if a connection with the server cannot be made # 0 if the command is authorized, # 1 if the command is not # authorized, and returns the error string if checkauth fails # (which really shouldn't happen unless a Mon server isn't running) my ($cmd) = @_; my $retval ; my $conn = &mon_connect ; return 0 if $conn == 0 ; return -1 if $conn == -1 ; $retval = $c->checkauth($cmd); if ($retval == 0) { # command not authorized $retval = 0; } elsif ($retval == 1) { # command authorized $retval = 1; } else { # This command should not fail unless a mon server is not running $retval = $c->error; $webpage->print("mon_checkauth: Could not check auth for \"$cmd\" on mon server "$monhost": $retval\n"); # &moncgi_switch_user($retval); } return $retval; } sub mon_state_change_enable_only { # This is called only by list_disabled, and wraps mon_state_change # because mon_state_change assumes only one group and this can # span multiple groups (although the command is enable only, which # simplifies things some). my ($group, $param); my @groups = &mon_list_watch ; foreach $param ( keys(%cgiparams) ) { # For each matching action, try to enable or else exit # in case of an authentication failure (this makes us # keep our CGi variables so that when we do authenticate, # all of our actions are executed). if ($param =~ /^enagroup_/) { &mon_enable("watch,$cgiparams{$param}") || return undef; } elsif ($param =~ /^enahost_/) { &mon_enable("host,$cgiparams{$param}") || return undef; } elsif ($param =~ /^enasvc_/) { &mon_enable("service,$cgiparams{$param}") || return undef; } } &list_disabled; } sub mon_state_change { # This function changes one or more of the states of the hostgroup # given as an argument. It uses the global value %cgiparam to know # what else (hosts and/or services) to modify. my ($group, %hosts, $host, %op, $service); $group = $cgiparams{'group'}; my %d = &mon_list_disabled ; # List the state of the group if (!defined $group) { $webpage->print("Invalid host group \"$group\"\n"); return undef; } # Disable/enable group, if that was requested if ( ( defined ($cgiparams{"group_$group"}) ) && ( $cgiparams{"group_$group"} eq "ena" ) ) { # Enable group if the group is already disabled if ( ${d{"watches"}{$group}} ) { &mon_enable("watch,$group") || return 0; } } elsif ( ( defined ($cgiparams{"group_$group"}) ) && ( $cgiparams{"group_$group"} eq "dis" ) ) { # Disable group if the group is already enabled if ( ! ${d{"watches"}{$group}} ) { &mon_disable("watch,$group") || return 0; } } @_ = &mon_list_group($group) ; # List each member of the group, check to see if there is something # defined for the group. # If so, then change the state if that is required. # Don't make a state change if one is not required. foreach $host (@_) { # Hosts are disabled if they begin with an asterisk # (this is mon's convention, not mine) #print STDERR "Host is $host\n"; #DEBUG if ($host =~ m/^\*/) { # Host is disabled, check to see if we should try and re-enable $host =~ s/^\*//; if ( ( defined($cgiparams{"host_$host"}) ) && ( $cgiparams{"host_$host"} eq "ena") ) { # Try to enable and stop if we can't. &mon_enable("host,$host") || return 0; } } else { # Host is enabled, check to see if we should try and disable if ( ( defined($cgiparams{"host_$host"}) ) && ( $cgiparams{"host_$host"} eq "dis") ) { # Try to disable and stop if we can't. &mon_disable("host,$host") || return 0; } } } # Check each service on the host to see if its state # needs to change. %op = &mon_list_opstatus; foreach $service (sort keys %{ $op{$group} }) { if ( ( defined($cgiparams{"svc_$service"}) ) && ( $cgiparams{"svc_$service"} eq "dis") ) { if (! ${d{"services"}{$group}{$service}}) { #service is enabled # Try to enable and stop if we # can't (i.e. no permissions) &mon_disable("service,$group,$service") || return 0; } } elsif ( ( defined($cgiparams{"svc_$service"}) ) && ( $cgiparams{"svc_$service"} eq "ena") ) { if (${d{"services"}{$group}{$service}}) { #service is disabled # Try to enable and stop if we # can't (i.e. no permissions) &mon_enable("service,$group,$service") || return 0; } } } &query_group($group); } ############################################################### # Mon.cgi-specific functions # The moncgi_* fuctions generally do not manipulate Mon in any # way or present any Mon output to the user. # The moncgi_* functions will each generally call one or more # mon_* functions. ############################################################### # Get the params from the form ----------------------------------------- sub moncgi_get_params { my (@names, $name, $monhost_not_null, $monport_not_null); # # First get params we know about and expect to get. # $command = $webpage->param('command'); $args = $webpage->param('args'); $rt = $webpage->param('rt'); # return to value for pages # which need to keep state info about # which page called them. $rtargs = $webpage->param('rtargs'); #args for $rt $ackcomment = $webpage->param('ackcomment'); #ackcomment # # For the login form, grab username and password. # $loginhash{'username'} = $webpage->param('username'); $loginhash{'password'} = $webpage->param('password'); # # Now get any more parameters which we may have defined. # @names = $webpage->param; foreach $name (@names) { $cgiparams{$name} = $webpage->param($name); } $monhost_not_null = 1 if defined($cgiparams{"h"}) && $cgiparams{"h"} ne ""; $monport_not_null = 1 if defined($cgiparams{"p"}) && $cgiparams{"p"} ne ""; # # Allow user to override values of monhost and monport with # CGI params. # # Untaint monhost and monport, first remove bogus characters, # then "officially" untaint them using $1 # if ( defined($monhost_not_null) ) { # print STDERR "Monhost is defined as '$cgiparams{'h'}'\n"; #DEBUG $monhost = $cgiparams{"h"} ; $monhost =~ s/[^\w.-]//g; $monhost =~ /(.*)/; $monhost = $1; } if ( defined($monport_not_null) ) { # print STDERR "Monport is defined as '$cgiparams{'p'}'\n"; #DEBUG $monport = $cgiparams{"p"} ; $monport =~ s/[^\d]//g; $monport =~ /(.*)/; $monport = $1; } # # If the user gave either a host or port argument that may (or # may not) override the hard-coded value, we need to preserve # this value and encode it in all future URL's that we # generate, so that the value will be preserved when the # mon.cgi page auto-reloads. We also undef $has_read_config, # for mod_perl namespace purposes (scenario: instance 1 # of mon.cgi is invoked with a h= parameter, instance 2 is not, # the instance 2 will eventually pick up instance 1's h= # parameter. # # if ( ( defined($monhost_not_null) ) || ( defined($monport_not_null) ) ) { #print STDERR "Monhost is defined as '$cgiparams{'h'}' (h=$monhost)\n"; #DEBUG #print STDERR "Monport is defined as '$cgiparams{'p'}' (p=$monport)\n"; #DEBUG # # The META tag doesn't respect & for some reason, so # we define another variable. # At least under Navigator 4.x, this is true. # $monhost_and_port_args_meta = "h=$monhost&p=$monport&"; $monhost_and_port_args = "h=$monhost&p=$monport&"; undef $has_read_config ; } else { #user did not enter in either monhost or monport args #print STDERR "Monport ('$monport_not_null':'$cgiparams{'p'}') and monhost('$monhost_not_null':'$cgiparams{'h'}') are undefined.\n"; #DEBUG #undef $monhost_and_port_args_meta; #undef $monhost_and_port_args; $monhost_and_port_args_meta = ""; $monhost_and_port_args = ""; } } sub moncgi_logout { # This subroutine provides the written evidence to the user # that they have been logged out of the mon server. The actual # logging out is done in the setup_page() routine, when the # auth cookie is destroyed in accordance to the value of the # global variable $destroy_auth_cookie print $webpage->hr; print $webpage->h3("User $loginhash{'username'} has been logged off.
"); print $webpage->h3("You will need to re-authenticate to perform further privileged actions."); } # # This subroutine presents the authentication form to the user, # and then runs the command the user was trying to execute with # the new, improved level of privilege. # # This subroutine is usually called because a user tried to # do something that they aren't authorized to do. # # The special command name "moncgi_login" is given when the user # wants to log in without actually doing anything that requires # privs. # sub moncgi_authform { my ($command, $args) = (@_); print $webpage->startform(-method=>'POST', ); $webpage->print("
"); print $webpage->hr; # # Test to see if the command was the special command # "moncgi_login". # if ($command eq "moncgi_login") { $command = "query_opstatus"; print $webpage->h3("Please enter your mon username and password below.
"); } else { print $webpage->h3("You must authenticate as a user of sufficient privilege to perform this command.
"); } # # Start the table. # $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print("\n"); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print("
MON username:"); print $webpage->textfield(-name=>'username', -size=>8, -maxlength=>100, ); $webpage->print("
MON password:"); # Reset the passwd param so it's always blank before # printing out the password field. $webpage->param('password',""); print $webpage->password_field(-name=>'password', -size=>8, -value=>"", -maxlength=>100, ); $webpage->print("
"); print $webpage->submit(-name=>'Login', ); $webpage->print("
\n"); $webpage->print("
"); print $webpage->br; # if this works correctly, the command will be re-executed with # the credentials the user just entered $webpage->param('command', $command); $webpage->param('args', $args); $webpage->param('ackcomment', $ackcomment); # Now pass on the rest of the CGI params as hidden # fields if there are any params passed to the form. foreach (keys (%cgiparams) ) { print $webpage->hidden(-name=>"$_", -value=>"$cgiparams{$_}", ); } print $webpage->end_form; } # Generic button function --------------------------------------------- # Not strictly necessary, but could be useful if you wished to disable # certain features of the client or add new ones in a test capacity. sub moncgi_generic_button { my ($title, $command) = (@_); print $webpage->hr; print $webpage->h2("$title"); $webpage->print ("(command $command not implemented in this client)\n"); print $webpage->hr; } sub moncgi_switch_user { # This subroutine is called after a command fails because a user # is authenticated as a user without sufficient permission to perform # the requested command, so we give the user a chance to re-authenticate # as someone of sufficient privilege. my ($retval) = (@_) ; if ( ($must_login) && ($retval =~ /520 command could not be executed/) ) { # User doesn't have permission to perform command &moncgi_authform ($command,"$args") unless $has_prompted_for_auth; $has_prompted_for_auth = 1; } } # # This subroutine just prints out the legend table explaining what # colors mean what. I broke it out as a separate function so I # could experiment with its location (top or bottom of table). # # Inputs: Width of table (e.g. "70%") # Outputs: always returns 1 sub moncgi_print_service_table_legend { my ($service_table_width) = (@_); # Old way to draw table if (1 == 0 ) { $webpage->print(""); $webpage->print ("\n"); $webpage->print("\n"); $webpage->print("\n "); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("\n"); $webpage->print("
Service color legend:UncheckedGoodFailed
(no alerts sent)
Failed
(alerts sent)
Disabled
"); } # New way to draw table $webpage->print(""); $webpage->print(""); $webpage->print ("\n"); $webpage->print("\n"); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print("\n"); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print("
Service color legend:
(top of table)
UncheckedGoodFailed
(no alerts sent)
Failed
(alerts sent)
Disabled
$unchecked_color$greenlight_color$yellowlight_color$redlight_color$disabled_color
\n"); return 1; } sub moncgi_list_dtlog_navtable { # This function is called by list_dtlog. It takes as arguments # the following: ($url, $group, $service, $sortby, $dtlog_begin, # $dtlog_end, $total_failures, $num_events, %sortby_key). # # This function doesn't print any downtime log information per # se. It just prints out navigation for the user to browse the # downtime log. # # This function prints out a table allowing the user to navigate # between the previous N downtime log entries and the next N downtime # log entries, where N is a number we derive based on what the range # the user asked for, what the maximum entries per page to show is, # and the total number of failures observed. # # The reason it's a separate function is that we want to have the # footer placed at the top and the bottom of the downtime log # table, so we're calling it twice. my ($url, $group, $service, $sortby, $dtlog_begin, $dtlog_end, $total_failures, $num_events, %sortby_key) = (@_); my ($next_matches_lower, $next_matches_upper, $prev_matches_lower, $prev_matches_upper); return ($dtlog_begin, $dtlog_end) if $num_events == 0; # stop if we returned no downtime events # Lower bound on which failures to show as next # min($dtlog_end + 1, $total_failures $next_matches_lower = ($total_failures < $dtlog_end + 1) ? $total_failures : $dtlog_end + 1; # Upper bound on which failures to show as next # min($next_matches_lower + $dtlog_max_failures_per_page - 1, $total_failures) $next_matches_upper = ( $total_failures < ($next_matches_lower + $dtlog_max_failures_per_page - 1) ) ? $total_failures : ($next_matches_lower + $dtlog_max_failures_per_page - 1) ; # now reset $dtlog_end in case we were handed a bogus end point $dtlog_end = $next_matches_upper if ($dtlog_end > $num_events); # now reset $dtlog_begin in case we were handed a bogus begin point $dtlog_begin = $dtlog_end if $dtlog_begin > $num_events; # Lower bound on which failures to show as prev # max(1, $dtlog_begin - $dtlog_max_failures_per_page) $prev_matches_lower = ($dtlog_begin - $dtlog_max_failures_per_page > 0) ? ($dtlog_begin - $dtlog_max_failures_per_page ) : 1 ; #$webpage->print("total_failures=$total_failures, dtlog_begin=$dtlog_begin, dtlog_end=$dtlog_end, prev_matches_lower=$prev_matches_lower, next_matches_upper = $next_matches_upper, next_matches_lower = $next_matches_lower\n"); #DEBUG # Start printing the "show previous, show next" table $webpage->print("
\n"); # Only give the option to show previous entries if there are previous # entries to show. if ( ($prev_matches_lower > 0) && ($dtlog_begin > 1) ) { # in the case where ($dtlog_begin - $dtlog_max_failures_per_page) # (e.g. we are currently showing entry 9-23 and want to display 1-8) if ( ( $dtlog_begin > 1) && ($dtlog_begin - $dtlog_max_failures_per_page < 0) ) { $prev_matches_lower = 1; } # Needs to be min($dtlog_begin -1, $total_failures) $prev_matches_upper = ($total_failures < $dtlog_begin - 1) ? $total_failures : $dtlog_begin - 1; printf("" , $url, ${monhost_and_port_args}, $group, $service, $sortby, $prev_matches_lower, $prev_matches_upper ); printf("See events %d-%d\n" , $prev_matches_lower, $prev_matches_upper) ; } # Print the current entries being shown $webpage->print("Displaying downtime events $dtlog_begin-$dtlog_end of $total_failures
(sorting by $sortby_key{$sortby})
"); # Only give the option to show subsequent entries if there are subsequent # entries to show. if ( ($next_matches_upper <= $total_failures) && ($dtlog_end < $total_failures) ) { printf("", $url, ${monhost_and_port_args}, $group, $service, $sortby, $next_matches_lower , $next_matches_upper ); printf("See events %d-%d\n", $next_matches_lower , $next_matches_upper) ; } $webpage->print("

\n"); # It is possible for these values to be changed in this routine, # so send back the values to the calling routine. return ($dtlog_begin, $dtlog_end); } sub moncgi_test_all { # Tests all services for a particular hostgroup # # Inputs: group name # Outputs: void # my ($group) = (@_); # Only show the rest of this page if we are allowed to see # information about this group. if ($show_watch_strict) { unless ( &can_show_group($group) ) { print $webpage->h3("You are not authorized to see detailed information for hostgroup '$group'."); print $webpage->h4("Please contact your system administrator for access."); return 0; } } my (%s, $service); my $conn = &mon_connect; return 0 if $conn == 0; %s = &mon_list_opstatus; if ( ! &mon_checkauth("test") ) { #command is unauthorized &moncgi_switch_user("520 command could not be executed"); return 0; } foreach $service (keys %{$s{$group}}) { &mon_test_service("$group,$service"); } return 1; } # # This subroutine presents a table for the user to reset keepstate # or to reset without keepstate. To do nothing is also an option. # sub moncgi_reset { my $reset_command_bgcolor = $auth_commands{'reset'}{'auth'} == 0 ? "bgcolor=\"$disabled_color\"" : $BGCOLOR ; $webpage->print("

"); $webpage->print(""); $webpage->print(""); $webpage->print(""); $webpage->print("
Reset mon server, keep scheduler state

This clears the state of the scheduler, re-reads the config file, and keeps all watches/hosts/services in their current disabled/enabled state.

You usually want to use this method of resetting the server.

Reset mon server, reset scheduler state

This clears the state of the scheduler, re-reads the config file, and sets all watches/hosts/services to ENABLED.

You only want to use this option if you're sure you want to re-enable all groups/hosts/services!

Do nothing and return to the main status screen


"); return 1; } # # This subroutine reads and parses the optional mon.cgi config file, # and alters global variable values accordingly. # # Inputs: config filename to read # # Outputs: 1 if up-to-date config file has been read # 0 if config file contains errors. # undef if up-to-date config file was not read (file not found) # sub moncgi_read_cf { my ($cf_file) = (@_) ; my ($newcf_file_mtime, $key, $val); # # Test that config file can be read, and if it can, check to see # if we've already read it. # if(-r $cf_file) { # First we check if we have ever read a config file. This # is controlled by the global variable $has_read_config # If so, then we check to see if the copy we have read is # the latest copy. # If not, then we try to read the config file. if ($has_read_config) { # # Since we've already read a config file at least once, # we now check to see if we need to read it again. # We re-read the config file if the mtime of the config # file is different (older OR newer) than the last config # file we read. # $newcf_file_mtime = (stat($cf_file))[9]; if ($newcf_file_mtime == $cf_file_mtime) { #print STDERR "Skipping config read ($newcf_file_mtime == $cf_file_mtime)\n"; #DEBUG return 1; } else { # # Record the new mtime # #print STDERR "Re-reading config ($newcf_file_mtime == $cf_file_mtime)\n"; #DEBUG $cf_file_mtime = $newcf_file_mtime; } } else { # # We've never read the config before, so we get the # initial config file mtime. # $cf_file_mtime = (stat($cf_file))[9]; } } else { print STDERR "mon.cgi: moncgi_read_cf: Unable to open config file '$cf_file' for reading: $!\n"; return undef; } # # Start reading config file # if ( open (CF , "$cf_file") ) { while () { chomp; # Skip blank lines and comment lines next if /^\s*\#/; next if /^\s*$/; # Strip off extra blank space at beg. and end of each line s/^\s*//; s/\s*$//; # # Parse config file lines # if (/^(\w+) \s* = \s* (.*) \s*$/ix) { $key = $1; $val = $2; # # Trivially untaint $key and $val. # We'll do the "real" untainting within the # config file parsing, later. # $key =~ /(.*)/; $key = $1; $val =~ /(.*)/; $val = $1; # # Look for matching key/value pairs and assign them # to the proper variable. Complain if a key/value # pair doesn't match. # if ($key eq "organization") { $organization = $val; } elsif ($key eq "monadmin") { $monadmin = $val; } elsif ($key eq "logo") { $logo = $val ; } elsif ($key eq "logo_link") { $logo_link = $val ; } elsif ($key eq "reload_time") { if ($val <= 0) { print STDERR "mon.cgi: moncgi_read_cf: $cf_file: dtlog_max_failures_per_page must be a number > 0\n"; return 0; } $reload_time = $val; } elsif ($key eq "monhost") { # strip out all non-valid chars for taint purposes $val =~ s/[^\d\w.-]//g; $monhost = $val; } elsif ($key eq "monport") { # strip out all non-digits for taint purposes $val =~ s/[^\d]//g; if ($val <= 0) { print STDERR "mon.cgi: moncgi_read_cf: $cf_file: monport must be a number > 0\n"; return 0; } $monport = $val; } elsif ($key eq "must_login") { $must_login = $val; } elsif ($key eq "app_secret") { $app_secret = $val; } elsif ($key eq "default_username") { $default_username = $val; } elsif ($key eq "default_password") { $default_password = $val; } elsif ($key eq "login_expire_time") { if ($val <= 0) { print STDERR "mon.cgi: moncgi_read_cf: $cf_file: login_expire_time must be a number > 0\n"; return 0; } $login_expire_time = $val; } elsif ($key eq "cookie_name") { $cookie_name = $val; } elsif ($key eq "cookie_path") { $cookie_path = $val; } elsif ($key eq "fixed_font_face") { $fixed_font_face = $val; } elsif ($key eq "sans_serif_font_face") { $sans_serif_font_face = $val; } elsif ($key eq "BGCOLOR") { $BGCOLOR = $val; } elsif ($key eq "TEXTCOLOR") { $TEXTCOLOR = $val; } elsif ($key eq "LINKCOLOR") { $LINKCOLOR = $val; } elsif ($key eq "VLINKCOLOR") { $VLINKCOLOR = $val; } elsif ($key eq "greenlight_color") { $greenlight_color = $val; } elsif ($key eq "redlight_color") { $redlight_color = $val; } elsif ($key eq "yellowlight_color") { $yellowlight_color = $val; } elsif ($key eq "unchecked_color") { $unchecked_color = $val; } elsif ($key eq "dtlog_max_failures_per_page") { if ($val <= 0) { print STDERR "mon.cgi: moncgi_read_cf: $cf_file: dtlog_max_failures_per_page must be a number > 0\n"; return 0; } $dtlog_max_failures_per_page = $val; } elsif ($key eq "untaint_ack_msgs") { $untaint_ack_msgs = $val; } elsif ($key eq "watch") { push(@show_watch, $val); } elsif ($key eq "show_watch_strict") { $show_watch_strict = $val; } else { print STDERR "mon.cgi: moncgi_read_cf: Unknown key-value pair in config file $cf_file: '$key = $val'\n"; return 0; } } else { print STDERR "mon.cgi: moncgi_read_cf: Unparseable config file line in config file '$cf_file': $_\n"; return 0; } } } else { print STDERR "mon.cgi: moncgi_read_cf: Unable to open config file '$cf_file' for reading: $!\n"; return undef; } # # If we've gotten this far, it means the config file was # successfully parsed. # # # Set the global variable that indicates that we have read the # config file. # $has_read_config = 1; #print STDERR "Read the config file!\n"; #DEBUG return 1; } # # This subroutine allows the user to log in without attempting # a privileged action. Trivial but useful. # sub moncgi_login { &moncgi_authform ("moncgi_login",""); } # # This subroutine allows site admins to define their own custom # rows of the command table, which will appear as a third row to the # main command table. Take a look at the sample that's here below. # # You're on your own here with whatever code you put in this subroutine! # Go nuts! # sub moncgi_custom_print_bar { # # This is a sample routine, contributed by Ed Ravin (eravin@panix.com). # # Everything is commented out, and none of the functions are implemented # here, but this should give you the idea of what a custom command # bar would look like. # my ($face)= (@_); #$webpage->print("\n"); #$webpage->print("\tLaunch Space Shuttle\n"); #$webpage->print("\tTake Coffee Break for 30 minutes\n"); #$webpage->print("\tReset Soda Machine\n"); #$webpage->print("\tAcknowledge All Alarms\n"); #$webpage->print("\n"); } # # This subroutine extends the main command processing loop at the end # of this script with your own custom commands. Used in conjunction # with moncgi_custom_print_bar, and other subroutines that you # define in this script, moncgi_custom_commands provides a nice way # to extend mon.cgi at your site. # From Ed Ravin (eravin@panix.com) # sub moncgi_custom_commands { if ($command eq "replace_this_string_with_a_custom_command") { &setup_page("Custom Command #1"); # &call_your_first_custom_code_here; } elsif ($command eq "replace_this_string_with_another_custom_command") { &setup_page("Custom Command #2"); # &call_your_second_custom_code_here; } # as many other custom commands as you care to define go below else # didn't find anything { return 0; } 1; # did find something, suppress further command processing } ############################################################### # Main program ############################################################### # # Instantiate the mon client # $c = new Mon::Client ( host => $monhost, port => $monport, ); if ($command eq "query_opstatus" ){ # Summary opstatus view &setup_page("Operation Status: Summary View"); &query_opstatus("summary"); } elsif ($command eq "query_group" ){ # Expand hostgroup. &setup_page("Group Expansion"); &query_group($args); } elsif ($command eq "list_alerthist"){ # Alert history button. &setup_page("List the alert history"); &list_alerthist; } elsif ($command eq "svc_details"){ # View failure details. &setup_page("Service Details"); &svc_details($args); } elsif ($command eq "list_disabled"){ # List disabled hosts button. &setup_page("List disabled hosts"); &list_disabled; } elsif ($command eq "mon_test_service"){ # Test a service immediately &setup_page("Test Service"); &mon_test_service($args); sleep 1; # if we don't sleep here, svc_details will kick off before # the test does and it will look like we aren't running a test. &svc_details($args); } elsif ($command eq "mon_schedctl"){ # Stop/start the scheduler &setup_page("$args scheduler"); &mon_schedctl ($args); &query_opstatus("summary"); } elsif ($command eq "list_dtlog"){ # List the downtime log &setup_page("List Downtime Log"); &list_dtlog($args); } elsif ($command eq "mon_disable"){ # Disable a host/group/service &setup_page("Disable alert for host, group. or service"); &mon_disable($args); if ($rt eq "query_group") { &query_group($rtargs); } else { &query_opstatus("summary"); } } elsif ($command eq "mon_enable"){ # Enable a host/group/service &setup_page("Enable alert for host, group, or service"); &mon_enable($args); if ($rt eq "query_group") { &query_group($rtargs); } elsif ($rt eq "svc") { &query_opstatus("summary"); } else { &query_opstatus; } } elsif ($command eq "list_pids"){ # View pid button. &setup_page("List pids of server, alerts and monitors."); &list_pids; } elsif ($command eq "mon_reset"){ # Reset mon button. &setup_page("Reset mon"); &mon_reset($args); } elsif ($command eq "mon_ack"){ # Reset mon button. &setup_page("Acknowledge service failure"); &mon_ack($args); #$ackcomment is a global &svc_details($args); } elsif ($command eq "mon_reload"){ # Reload button. &setup_page("Reload Mon"); &mon_reload($args); } elsif ($command eq "mon_loadstate"){ # load mon scheduler state &setup_page("Load Scheduler State"); # right now we expect $args to be empty, since loadstate doesn't take # any arguments, but someday it might, so we prepare. &mon_loadstate($args); } elsif ($command eq "mon_savestate"){ # save mon scheduler state &setup_page("Save Scheduler State"); # right now we expect $args to be empty, since loadstate doesn't take # any arguments, but someday it might, so we prepare. &mon_savestate($args); } elsif ($command eq "moncgi_logout"){ # Log out as auth user. $destroy_auth_cookie = "yes"; &setup_page("Logging out"); &moncgi_logout; } elsif ($command eq "query_opstatus_full"){ # Full operations status &setup_page("Operation Status: Full View"); &query_opstatus("full"); } elsif ($command eq "query_opstatus_failures") { &setup_page("Operation Status: Failures Only"); &query_opstatus("failures"); # Selection "mon_opstatus" will fall through to else. } elsif ($command eq "mon_state_change") { &setup_page("Operation Status: Disable/Enable Groups/Hosts/Services"); &mon_state_change; } elsif ($command eq "mon_state_change_enable_only") { &setup_page("Operation Status: Enable Groups/Hosts/Services"); &mon_state_change_enable_only; } elsif ($command eq "moncgi_test_all") { &setup_page("Operation Status: Test All Services In Group"); &moncgi_test_all($args); } elsif ($command eq "mon_test_config") { &setup_page("Test Mon Config File Syntax"); &mon_test_config($args); } elsif ($command eq "moncgi_reset") { &setup_page("Reset mon Server"); &moncgi_reset($args); } elsif ($command eq "moncgi_login") { &setup_page("Log In To mon Server"); &moncgi_login($args); } elsif ( &moncgi_custom_commands) # check for user extensions { # The actual custom commands are processed # inside &moncgi_custom_commands. # # moncgi_custom_commands returns non-zero if it finds # a command to execute; } else { # All else. &setup_page("Operation Status: Summary View"); &query_opstatus("summary"); } $webpage->print("
"); # # Some stuff we keep around for debugging # #print "commands is $command, args is $args
\n"; #DEBUG #print $webpage->dump; #DEBUG &end_page; $c->disconnect(); etbemon-1.3.4/clients/moncmd0000755000175100017510000001243210230411542014126 0ustar rjcrjc#!/usr/bin/perl # # moncmd - send a command to the mon server # # Jim Trocki, trockij@arctic.org # # $Id: moncmd,v 1.3 2005/04/17 07:42:26 trockij Exp $ # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # use Getopt::Std; use Socket; use English; getopts ("ahf:l:s:p:rd"); sub usage; sub do_cmd; $MONSERVER = "localhost"; $MONSERVER = $ENV{"MONHOST"} if (defined ($ENV{"MONHOST"})); $MONSERVER = $opt_s if ($opt_s); $MONPORT = $opt_p || getservbyname ("mon", "tcp") || 2583; if ($opt_h) { usage; } if (!defined ($MONSERVER)) { die "No host specified or found in MONHOST\n"; } $SIG{INT} = \&handle_sig; $SIG{TERM} = \&handle_sig; # # does the input come from stdin or a file? # if ($opt_f) { if ($opt_f eq "-") { $H = STDIN; } else { open (IN, $opt_f) || die "could not open input file: $!\n"; $H = IN; } } elsif (!@ARGV) { if (-t STDIN) { print <); print "\n"; system "stty echo"; die "invalid password\n" if ($PASS =~ /^\s*$/); } elsif (!@ARGV) { $cmd = <$H>; while (defined ($cmd) && $cmd =~ /user=|pass=/i) { chomp $cmd; if ($cmd =~ /^user=(\S+)$/i) { $USER=$1 if (!defined ($USER)); } elsif ($cmd =~ /^pass=(\S+)$/i) { $PASS=$1; } $cmd = <$H>; } } die "inadequate authentication information supplied\n" if ($USER eq "" || $PASS eq ""); } # # set up TCP socket # $iaddr = inet_aton ($MONSERVER) || die "Unable to find server '$MONSERVER'\n"; if ($MONPORT =~ /\D/) { $MONPORT = getservbyname ($MONPORT, 'tcp') } $paddr = sockaddr_in ($MONPORT, $iaddr); $proto = getprotobyname ('tcp'); socket (MON, PF_INET, SOCK_STREAM, $proto) || die "could not create socket: $!\n"; connect (MON, $paddr) || die "could not connect: $!\n"; select (MON); $| = 1; select (STDOUT); #if( defined(my $line = )) { # chomp $line; # unless( $line =~ /^220\s/) { # die "didn't receive expected welcome message\n"; # } #} else { # die "error communicating with mon server: $!\n"; #} # # authenticate self to the server if necessary # if ($opt_a) { ($l, @out) = do_cmd(MON, "login $USER $PASS"); die "Could not authenticate\n" if ($l =~ /^530/); } if ($opt_f or !@ARGV) { $cmd = <$H> if ($opt_f || !@ARGV); $l = ""; while (defined ($cmd) && defined ($l)) { # # send the command # chomp $cmd; ($l, @out) = do_cmd (MON, $cmd); last if (!defined ($l)); for (@out) { print "$_\n"; } print "$l\n"; $cmd = <$H>; } close ($H); } else { ($l, @out) = do_cmd (MON, "@ARGV"); for (@out) { print "$_\n"; } print "$l\n"; } # # log out # do_cmd (MON, "quit"); close(MON); # # submit a command to the server, wait for a response # sub do_cmd { my ($fd, $cmd) = @_; my ($l, @out); return ("", undef) if ($cmd =~ /^\s*$/); @out = (); print $fd "$cmd\n"; while (defined($l = <$fd>)) { chomp $l; if ($l =~ /^(\d{3}\s)/) { last; } push (@out, $l); } ($l, @out); } # # usage # sub usage { print < Wed Jan 2 12:23:44 EST 2002 # Release Version: 1.2 # $Header: /cvsroot/mon/mon/clients/monfailures,v 1.1.1.1 2004/06/09 05:18:07 trockij Exp $ use strict; my %opt; use Getopt::Long; GetOptions (\%opt, "debug", "server=s", "port=s", "user=s", "password=s"); ############################ configurable stuff my $default_user=""; my $default_password= ""; ############################ my $debug= $opt{'debug'} || 0; my (%failures); my ($now); use Mon::Client; my $mon; # find the client if (!defined ($mon = Mon::Client->new)) { die "$0: could not create client object: $@"; } if (defined $opt{'server'}) { $mon->host ($opt{'server'}); } else { $mon->host ("localhost"); } $mon->port ($opt{'port'}) if (defined $opt{'port'}); $mon->username($opt{'user'} || $default_user); $mon->password($opt{'password'} || $default_password); $mon->connect; die "$0: Could not connect to server: " . $mon->error . "\n" unless $mon->connected; if ($mon->username ne "") { $mon->login; die "$0: login failure: " . $mon->error . "\n" if $mon->error; } # Load data from Mon %failures = $mon->list_failures; die "$0: Error doing list_failures : " . $mon->error if ($mon->error); $now= time; # time mon data was fetched # group=thathost service=port8888 opstatus=0 last_opstatus=0 exitval=1 timer=11 # last_success=0 last_trap=0 last_check=955058065 ack=0 ackcomment='' # alerts_sent=0 depstatus=0 depend='' monitor='tcp.monitor -p 8888' # last_summary='thathost' # last_detail='\0athathost could not connect: Connection refused\0a' # last_failure=955058067 interval=60 first_failure=955055062 # failure_duration=3052 my ($watch, $service, $downtime, $summary, $acked); format STDOUT_TOP =  Hostgroup:Service Down Since Error Summary ----------------- ---------- ------------- . format STDOUT = @<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<< $watch . ":" . $service, $downtime, $summary . # list out any failures if (%failures) { foreach $watch (keys %failures) { foreach $service (keys %{$failures{$watch}}) { my $sref= \%{$failures{$watch}->{$service}}; $downtime= localtime $sref->{'first_failure'}; $acked= $sref->{'ack'} !=0; $summary= $sref->{'last_summary'}; $summary= "[acked] $summary" if $acked; write; } } print "\n"; exit(1); } else { print "No failures found.\n"; exit(0); } etbemon-1.3.4/clients/monremote.pl0000755000175100017510000001032710146140375015302 0ustar rjcrjc#!/usr/bin/perl -w # # monremote.pl - Propagates client (user) requests from one mon process to another, # via mon.cgi # # David Nolan, vitroth@cmu.edu # # $Id: monremote.pl,v 1.2 2004/11/15 14:45:17 vitroth Exp $ # # Copyright (C) 2002 Carnegie Mellon University # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # monremote.pl will issue HTTPS requests to a remote Mon server. You must provide it with an # SSL certificate & key, and tell it where mon.cgi is running on the remote server. # Configuration block # SSL Key. You can use your apache server's key for simplicity $crt = '/usr/local/apache/conf/ssl.crt/server.crt'; $key = '/usr/local/apache/conf/ssl.key/server.key'; # List of servers to propagate changes to @hosts = ('monserver1.example.com', 'monserver2.example.com'); # Name of your master Mon server, to prevent propagations from any other hosts $mon_master = "mon-master.example.com"; # URI of mon.cgi on the remote servers. If its different on individual servers # you'll need to do extra work. (Make the hosts list a hash, with the URLS.) $path = '/cbin/mon.cgi'; # Set this to non-zero to enable debugging $debug = 0; # Comment this out once you've edited the config block above. die "monremote.pl must be customized for your environment! Please edit the configuration block."; # You shouldn't need to change anything below here. use LWP::UserAgent; use Sys::Hostname; if (!defined $ARGV[0]) { print "Usage: monremote.pl (enable|disable|test) (watch |host |service )\n"; exit; } # Make sure we're running on the master. $hostname = hostname; $hostname =~ tr/a-z/A-Z/; $mon_master =~ tr/a-z/A-Z/; if ($hostname ne $mon_master) { print STDERR "No propagation from servers other then the master!\n"; exit -1; } # Figure out what the argument portions of the URL need to be. if ($ARGV[0] eq 'disable') { $args = "?command=mon_disable"; if ($ARGV[1] eq 'watch') { $args .= "&args=watch,$ARGV[2]&rt=none"; } elsif ($ARGV[1] eq 'service') { $args .= "&args=service,$ARGV[2],$ARGV[3]&rt=none"; } elsif ($ARGV[1] eq 'host') { $args .= "&args=host,$ARGV[2]&rt=none"; } } elsif ($ARGV[0] eq 'enable') { $args = "?command=mon_enable"; if ($ARGV[1] eq 'watch') { $args .= "&args=watch,$ARGV[2]&rt=none"; } elsif ($ARGV[1] eq 'service') { $args .= "&args=service,$ARGV[2],$ARGV[3]&rt=none"; } elsif ($ARGV[1] eq 'host') { $args .= "&args=host,$ARGV[2]&rt=none"; } } elsif ($ARGV[0] eq 'test') { $args = "?command=mon_test_service&args=$ARGV[1],$ARGV[2]"; } else { print STDERR "Unknown command $ARGV[0]\n"; exit -1; } $ENV{HTTPS_CERT_FILE} = $crt; $ENV{HTTPS_KEY_FILE} = $key; # Now fork and do the work. # We fork so that we don't wait for each individual request to finish. # We fork twice so that the kernel will take care of process cleanup for us. $pid = fork; if ($pid) { waitpid ($pid, 0); print STDERR "Parent exiting\n" if ($debug); exit 0; } else { foreach $host (@hosts) { if (fork) { next; } my $ua = LWP::UserAgent->new; $ua->agent("MonRemote/0.1"); print STDERR "@ARGV\n" if ($debug); print STDERR "$args\n" if ($debug); my $req = HTTP::Request->new(GET => "https://$host/$path$args"); $req ->content_type('application/x-www-form-urlencoded'); my $res = $ua->request($req); if ($res->is_success) { print STDERR "Worker exiting\n" if ($debug); exit 0; } else { print STDERR "\n$host\n@ARGV\nRequest to remote server failed\n"; exit 0; } } print STDERR "Child exiting\n" if ($debug); exit 0; } etbemon-1.3.4/clients/monshow0000755000175100017510000007161613137761622014374 0ustar rjcrjc#!/usr/bin/perl -T # # monshow - concise, user view-based opstatus summary # # Jim Trocki, trockij@arctic.org # # $Id: monshow,v 1.3 2005/04/17 07:42:26 trockij Exp $ # # Copyright (C) 1998, Jim Trocki # # 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # use strict "vars"; use Getopt::Long; use English; use CGI; use Text::Wrap; use Mon::Client; use Data::Dumper; # # forward declarations # sub usage; sub get_auth; sub read_cf; sub err_die; sub expand_watch; sub secs_to_hms; sub display_allok; sub compose_detail; sub compose_header; sub compose_disabled; sub compose_table; sub compose_trailer; sub get_client_status; sub opstatus_color; sub pre_pad_if_empty; sub tdiff_string; # # set this to the path of your view configuration files # my $VIEWPATH = "/etc/mon/monshow"; my %OPSTAT = %Mon::Client::OPSTAT; my $WORD = '[a-zA-Z0-9_-]+'; my $OUT_BUF = ""; my $e; $= = 1000; $SIG{INT} = \&handle_sig; $SIG{TERM} = \&handle_sig; # safe $PATH for taint mode $ENV{PATH} = '/usr/local/bin:/usr/bin:/bin'; my ($DEP, $GROUP, $SERVICE, $STATUS, $TIME, $NEXT, $ALERTS, $SUMMARY, $DESC); # Untaint args, tainting is just for stuff which comes from CGI. for (@ARGV) { /(.*)/s or die; $_ = $1; } my %opt; GetOptions (\%opt, "showall", "auth", "help", "full", "disabled", "rcfile=s", "login=s", "server=s", "port=i", "prot=s", "detail=s", "watch=s", "view=s") || usage; my $CGI; my %QUERY_ARGS; if (defined $ENV{"REQUEST_METHOD"}) { unless ($CGI = new CGI) { $CGI = 1; err_die ("Can't create CGI object\n"); } } if (!$CGI && $opt{"help"}) { usage; } if ($CGI) { foreach my $e (split (/\?/, $ENV{"QUERY_STRING"})) { next if ($e !~ /=/); my ($var, $val) = split (/=/, $e); $QUERY_ARGS{$var} = $val; } } elsif (!$CGI && @ARGV != 0) { usage "No non-switch args expected\n"; } my $CF = { "host" => "localhost", "full" => 0, "show-disabled" => 0, "bg" => "d5d5d5", "bg-ok" => "a0d0a0", "bg-fail" => "e088b7", "bg-untested" => "e0e0e0", "table-color" => "cccccc", "summary-len" => 80, }; my $GLOBAL = { "view-name" => undef, }; # # read config file # my ($e, $what) = read_cf ($CF); if ($e ne "") { err_die ("while reading config file, $e"); } # # cmdline args override config file # if (!$CGI) { $CF->{"all"} = 1 if ($opt{"showall"}); $CF->{"full"} = 1 if ($opt{"full"}); $CF->{"detail"} = $opt{"detail"} if ($opt{"detail"} ne ""); $CF->{"host"} = $opt{"server"} if ($opt{"server"}); $CF->{"port"} = $opt{"port"} if ($opt{"port"}); $CF->{"prot"} = $opt{"prot"} if ($opt{"prot"}); } # # Interrupt to see if a watch= option was specified # if( $CGI && $QUERY_ARGS{'watch'} ) { # # make a single entry of a pointer to a single array # $what = [ [ $QUERY_ARGS{"watch"} ] ]; } elsif( ! $CGI && $opt{'watch'} ) { # # make a single entry of a pointer to a single array # $what = [ [ $opt{"watch"} ] ]; } # # retrieve client status # my ($e, $st) = get_client_status ($what); if ($e ne "") { err_die ($e); } expand_watch ($what, $st); my $rows = select_table ($what, $st); compose_header ($st->{"state"}); # # CGI invocation # if ($CGI) { if ($QUERY_ARGS{"disabled"}) { compose_disabled ($st->{"disabled"}); } elsif (!$QUERY_ARGS{"detail"}) { compose_table ($rows, $st); compose_disabled ($st->{"disabled"}) if ($CF->{"show-disabled"}); if ($QUERY_ARGS{"watch"}) { $OUT_BUF .= <Back to summary table
EOB } } elsif ($QUERY_ARGS{"detail"}) { compose_detail ($QUERY_ARGS{"detail"}, $st); } } # # cmdline invocation # else { if ($opt{"disabled"}) { compose_disabled ($st->{"disabled"}); } elsif ($CF->{"detail"} ne "") { compose_detail ($CF->{"detail"}, $st); } else { compose_table ($rows, $st); compose_disabled ($st->{"disabled"}) if ($CF->{"show-disabled"}); } } compose_trailer; if ($CGI) { print <query ("login"); $pass = $CGI->query ("password"); } else { if ($opt{"login"}) { $login = $opt{"login"}; } else { return "could not determine username" if (!defined ($login = getpwuid($EUID))); } if (-t STDIN) { system "stty -echo"; print "Password: "; chop ($pass = ); print "\n"; system "stty echo"; return "invalid password" if ($pass =~ /^\s*$/); } else { my $cmd; while (defined ($cmd = <>)) { chomp $cmd; if ($cmd =~ /^user=(\S+)$/i) { $login = $1; } elsif ($cmd =~ /^pass=(\S+)$/i) { $pass = $1; } last if (defined ($login) && defined ($pass)); } } } return "inadequate authentication information supplied" if ($login eq "" || $pass eq ""); return ("", $login, $pass); } # # config file # sub read_cf { my $CF = shift; my ($group, $service); my @RC; my $view = 0; my $RC = "/etc/mon/monshowrc"; if ($CGI) { if ($ENV{"PATH_INFO"} =~ /^\/\S+/) { my $p=$ENV{"PATH_INFO"}; $p =~ s/^[.\/]*//; $p =~ s/\/*$//; $p =~ s/\.\.//g; $RC = "$VIEWPATH/$p"; $GLOBAL->{"view-name"} = $p; $view = 1; } elsif ($QUERY_ARGS{"view"} ne "") { $QUERY_ARGS{"view"} =~ s/^[.\/]*//; $QUERY_ARGS{"view"} =~ s/\.\.//g; $GLOBAL->{"view-name"} = $QUERY_ARGS{"view"}; $RC = "$VIEWPATH/$QUERY_ARGS{view}"; $view = 1; } elsif (-f ".monshowrc") { $RC = ".monshowrc"; } } else { if ($opt{"rcfile"}) { $RC = $opt{"rcfile"}; } elsif ($opt{"view"} ne "") { $RC = "$VIEWPATH/$opt{view}"; $GLOBAL->{"view-name"} = $opt{"view"}; $view = 1; } elsif (-f "$ENV{HOME}/.monshowrc") { $RC = "$ENV{HOME}/.monshowrc"; } } if ($opt{"old"}) { $CF->{"prot"} = "0.37.0"; $CF->{"port"} = 32777; } if (-f $RC) { open (IN, $RC) || return "could not read $RC: $!"; my $html_header = 0; my $link_text = 0; my $link_group = ""; my $link_service = ""; while () { next if (/^\s*#/ || /^\s*$/); if ($html_header) { if (/^END\s*$/) { $html_header = 0; next; } else { $CF->{"html-header"} .= $_; next; } } elsif ($link_text) { if (/^END\s*$/) { $link_text = 0; next; } else { $CF->{"links"}->{$link_group}->{$link_service}->{"link-text"} .= $_; next; } } else { chomp; s/^\s*//; s/\s*$//; } if (/^set \s+ (\S+) \s* (\S+)?/ix) { my $cmd = $1; my $arg = $2; if ($cmd eq "show-disabled") { } elsif ($cmd eq "host") { } elsif ($cmd eq "prot") { } elsif ($cmd eq "port") { } elsif ($cmd eq "full") { } elsif ($cmd eq "bg") { } elsif ($cmd eq "bg-ok") { } elsif ($cmd eq "bg-fail") { } elsif ($cmd eq "bg-untested") { } elsif ($cmd eq "table-color") { } elsif ($cmd eq "html-header") { $html_header = 1; next; } elsif ($cmd eq "refresh") { } elsif ($cmd eq "summary-len") { } else { print STDERR "unknown set, line $.\n"; next; } if ($arg ne "") { $CF->{$cmd} = $arg; } else { $CF->{$cmd} = 1; } } elsif (/^watch \s+ (\S+)/x) { push (@RC, [$1]); } elsif (/^service \s+ (\S+) \s+ (\S+)/x) { push (@RC, [$1, $2]); } elsif (/^link \s+ (\S+) \s+ (\S+) \s+ (.*)\s*/ix) { $CF->{"links"}->{$1}->{$2}->{"link"} = $3; } elsif (/^link-text \s+ (\S+) \s+ (\S+)/ix) { $link_text = 1; $link_group = $1; $link_service = $2; next; } else { my $lnum = $.; close (IN); err_die ("error in config file, line $."); } } close (IN); } elsif (! -f $RC && $view) { err_die ("no view found"); } return ("", \@RC); } sub secs_to_hms { my ($s) = @_; my ($dd, $hh, $mm, $ss); $dd = int ($s / 86400); $s -= $dd * 86400; $hh = int ($s / 3600); $s -= $hh * 3600; $mm = int ($s / 60); $s -= $mm * 60; $ss = $s; if ($dd == 0) { sprintf("%02d:%02d:%02d", $hh, $mm, $ss); } else { sprintf("%d days, %02d:%02d:%02d", $dd, $hh, $mm, $ss); } } # # exit displaying error in appropriate output format # sub err_die { my $msg = shift; if ($CGI) { print < Error

Error

$msg
EOF } else { print < All systems OK

EOF } else { print "\nAll systems OK\n"; } } # # client status # # return ("", $state, \%opstatus, \%disabled, \%deps, \%groups, \%descriptions); # sub get_client_status { my $what = shift; my $cl; if (!defined ($cl = Mon::Client->new)) { return "could not create client object: $@"; } my ($username, $pass); if ($opt{"auth"} && !$CGI) { my $e; ($e, $username, $pass) = get_auth; if ($e ne "") { return "$e"; } $cl->username ($username); $cl->password ($pass); } $cl->host ($CF->{"host"}) if (defined $CF->{"host"}); $cl->port ($CF->{"port"}) if (defined $CF->{"port"}); $cl->port ($CF->{"prot"}) if (defined $CF->{"prot"}); $cl->connect; if ($cl->error) { return "Could not connect to server: " . $cl->error; } # # authenticate self to the server if necessary # if ($opt{"auth"} && !defined ($cl->login)) { my $e = $cl->error; $cl->disconnect; return "Could authenticate: $e"; } # # get disabled things # my %disabled = $cl->list_disabled; if ($cl->error) { my $e = $cl->error; $cl->disconnect; return "could not get disabled: $e"; } # # get state # my ($running, $t) = $cl->list_state; if ($cl->error) { my $e = $cl->error; $cl->disconnect; return "could not get state: $e"; } my $state; if ($running) { $state = $t; } else { $state = "scheduler stopped since " . localtime ($t); } # # group/service list # my @watch = $cl->list_watch; if ($cl->error) { my $e = $cl->error; $cl->disconnect; return "could not get opstatus: $e"; } # # get opstatus # my %opstatus; if (@{$what} == 0) { %opstatus = $cl->list_opstatus; if ($cl->error) { my $e = $cl->error; $cl->disconnect; return "could not get opstatus: $e"; } } else { my @list; foreach my $r (@{$what}) { if (@{$r} == 2) { push @list, $r; } else { foreach my $w (@watch) { next if ($r->[0] ne $w->[0]); push @list, $w; } } } %opstatus = $cl->list_opstatus (@list); if ($cl->error) { my $e = $cl->error; $cl->disconnect; return "could not get opstatus: $e"; } } # # dependencies # my %deps; if ($opt{"deps"}) { %deps = $cl->list_deps; if ($cl->error) { my $e = $cl->error; $cl->disconnect; return "could not list deps: $e"; } } # # descriptions # my %desc; %desc = $cl->list_descriptions; if ($cl->error) { my $e = $cl->error; $cl->disconnect; return "could not list descriptions: $e"; } # # groups # my %groups; if ($QUERY_ARGS{"detail"} || $CF->{"detail"} ne "") { foreach my $g (keys %opstatus) { my @g = $cl->list_group ($g); if ($cl->error) { my $e = $cl->error; $cl->disconnect; return "could not list group: $e"; } grep {s/\*//} @g; $groups{$g} = [@g]; } } # # log out # if (!defined $cl->disconnect) { return "error while disconnecting: " . $cl->error; } return ("", { "state" => $state, "opstatus" => \%opstatus, "disabled" => \%disabled, "deps" => \%deps, "groups" => \%groups, "desc" => \%desc, "watch" => \@watch, }); } sub compose_header { my $state = shift; my $t = localtime; # # HTML stuff # if ($CGI) { $OUT_BUF = < Operational Status EOF if ($CF->{"refresh"}) { $OUT_BUF .= < {refresh}> EOF } $OUT_BUF .= < EOF if ($CF->{"html-header"} ne "") { $OUT_BUF .= $CF->{"html-header"}; } $OUT_BUF .= <Operational Status EOF } foreach my $l (@{$rows}) { my ($depstate, $group, $service) = @{$l}; my $sref = \%{$st->{"opstatus"}->{$group}->{$service}}; $STATUS = "unknown"; $TIME = ""; $DEP = $depstate; my $last = ""; my $bgcolor = opstatus_color ($sref->{"opstatus"}); if ($sref->{"opstatus"} == $OPSTAT{"untested"}) { $STATUS = "untested"; $TIME = "untested"; } elsif ($sref->{"opstatus"} == $OPSTAT{"ok"}) { $STATUS = "-"; } elsif ($sref->{"opstatus"} == $OPSTAT{"fail"}) { if ($sref->{"ack"}) { if ($CGI) { $STATUS = "" . "ACK FAIL"; } else { $STATUS = "ACK FAIL"; } } else { $STATUS = "FAIL"; } } if ($depstate eq "") { $DEP = "-"; } $GROUP = $group; $SERVICE = $service; $DESC = $st->{"desc"}->{$group}->{$service}; $DESC = pre_pad_if_empty ($DESC) if ($CGI); if ($TIME eq "") { $TIME = tdiff_string (time - $sref->{"last_check"}); } if ($sref->{"timer"} < 60) { $NEXT = "$sref->{timer}s"; } else { $NEXT = secs_to_hms ($sref->{"timer"}); } if (length ($sref->{"last_summary"}) > $CF->{"summary-len"}) { $SUMMARY = substr ($sref->{"last_summary"}, 0, $CF->{"summary-len"}) . "..."; } else { $SUMMARY = $sref->{"last_summary"}; } $ALERTS = $sref->{"alerts_sent"} || "none"; my $fmt; if (!$CGI) { $fmt = < EOF } } if ($CGI) { $OUT_BUF .= < EOF } } sub compose_disabled { my $disabled = shift; if (!keys %{$disabled->{"watches"}} && !keys %{$disabled->{"services"}} && !keys %{$disabled->{"hosts"}}) { if ($CGI) { $OUT_BUF .= < Nothing is disabled.

EOF } else { print "\nNothing is disabled.\n"; } return; } if (keys %{$disabled->{"watches"}}) { if ($CGI) { $OUT_BUF .= < Disabled Watches EOF } else { print "\nDISABLED WATCHES:\n"; } foreach my $watch (keys %{$disabled->{"watches"}}) { if ($CGI) { $OUT_BUF .= "$watch
\n"; } else { print "$watch\n"; } } } my @disabled_services; foreach my $watch (keys %{$disabled->{"services"}}) { foreach my $service (keys %{$disabled->{"services"}{$watch}}) { push (@disabled_services, "$watch $service");; } } if (@disabled_services) { if ($CGI) { $OUT_BUF .= < Disabled Services EOF } else { print "\nDISABLED SERVICES\n"; } for (@disabled_services) { if ($CGI) { $OUT_BUF .= "$_
\n"; } else { print "$_\n"; } } } if (keys %{$disabled->{"hosts"}}) { if ($CGI) { $OUT_BUF .= < Disabled Hosts EOF } else { print "\nDISABLED HOSTS:\n"; } foreach my $group (keys %{$disabled->{"hosts"}}) { my @HOSTS = (); foreach my $host (keys %{$disabled->{"hosts"}{$group}}) { push (@HOSTS, $host); } if ($CGI) { $OUT_BUF .= sprintf ("%-15s %s
\n", $group, "@HOSTS"); } else { printf ("%-15s %s\n", $group, "@HOSTS"); } } } } sub compose_trailer { if ($CGI) { $OUT_BUF .= < EOF } } sub compose_detail { my ($args, $st) = @_; my ($group, $service) = split (/,/, $args, 2); if (!defined ($st->{"opstatus"}->{$group}->{$service})) { err_die ("$group/$service not a valid service"); } my $sref = \%{$st->{"opstatus"}->{$group}->{$service}}; my $d; my $bgcolor = opstatus_color ($sref->{"opstatus"}); $bgcolor = "bgcolor=\"#$bgcolor\"" if ($bgcolor ne ""); foreach my $k (keys %OPSTAT) { if ($OPSTAT{$k} == $sref->{"opstatus"}) { $sref->{"opstatus"} = "$k ($sref->{opstatus})"; last; } } foreach my $k (qw (opstatus exitval last_check timer ack ackcomment)) { if ($CGI && $sref->{$k} =~ /^\s*$/) { $d->{$k} = "

 
"; } else { $d->{$k} = $sref->{$k}; } } my $t = time; $d->{"last_check"} = tdiff_string ($t - $d->{"last_check"}) . " ago"; $d->{"timer"} = "in " . tdiff_string ($d->{"timer"}); foreach my $k (qw (last_success last_failure first_failure last_alert)) { if ($sref->{$k}) { $d->{$k} = localtime ($sref->{$k}); } } if ($sref->{"first_failure"}) { $d->{"failure_duration"} = secs_to_hms ($sref->{"failure_duration"}); } # # HTML output # if ($CGI) { my $sum = pre_pad_if_empty ($sref->{"last_summary"}); my $descr = pre_pad_if_empty ($st->{"desc"}->{$group}->{$service}); my $hosts = pre_pad_if_empty ("@{$st->{groups}->{$group}}"); $OUT_BUF .= <Detail for $group/$service
EOF if ($GLOBAL->{"view-name"} ne "") { $OUT_BUF .= < EOF } $OUT_BUF .= <
Server: $CF->{host}
Time: $t
State: $state
View: $GLOBAL->{"view-name"}
Color legend
all is OK
failure
untested


EOF } else { print <{host} time: $t state: $state EOF } } sub select_table { my ($what, $st) = @_; my @rows; # # display everything real nice # if ($CF->{"all"} || @{$what} == 0) { foreach my $group (keys %{$st->{"opstatus"}}) { foreach my $service (keys %{$st->{"opstatus"}->{$group}}) { push (@rows, [$group, $service]); } } } else { @rows = @{$what}; } my (%DEP, %DEPROOT); foreach my $l (@rows) { my ($group, $service) = @{$l}; my $sref = \%{$st->{"opstatus"}->{$group}->{$service}}; next if (!defined $sref->{"opstatus"}); # # disabled things # # fuckin' Perl, man. Just be referencing # $st->{"disabled"}->{"watches"}->{$group}, perl automagically # defines that hash element for you. Great. # if (defined ($st->{"disabled"}->{"watches"}) && defined ($st->{"disabled"}->{"watches"}->{$group})) { next; } elsif (defined ($st->{"disabled"}->{"services"}) && defined ($st->{"disabled"}->{"services"}->{$group}) && defined ($st->{"disabled"}->{"services"}->{$service})) { next; } # # potential root dependencies # elsif ($sref->{"depend"} eq "") { push (@{$DEPROOT{$sref->{"opstatus"}}}, ["R", $group, $service]); } # # things which have dependencies # else { push (@{$DEP{$sref->{"opstatus"}}}, ["D", $group, $service]); } } if ($CF->{"full"}) { [ @{$DEPROOT{$OPSTAT{"fail"}}}, @{$DEPROOT{$OPSTAT{"linkdown"}}}, @{$DEPROOT{$OPSTAT{"timeout"}}}, @{$DEPROOT{$OPSTAT{"coldstart"}}}, @{$DEPROOT{$OPSTAT{"warmstart"}}}, @{$DEPROOT{$OPSTAT{"untested"}}}, @{$DEPROOT{$OPSTAT{"unknown"}}}, @{$DEP{$OPSTAT{"fail"}}}, @{$DEP{$OPSTAT{"linkdown"}}}, @{$DEP{$OPSTAT{"timeout"}}}, @{$DEP{$OPSTAT{"coldstart"}}}, @{$DEP{$OPSTAT{"warmstart"}}}, @{$DEPROOT{$OPSTAT{"ok"}}}, @{$DEP{$OPSTAT{"ok"}}}, @{$DEP{$OPSTAT{"untested"}}}, @{$DEP{$OPSTAT{"unknown"}}}, ]; } else { [ @{$DEPROOT{$OPSTAT{"fail"}}}, @{$DEPROOT{$OPSTAT{"linkdown"}}}, @{$DEPROOT{$OPSTAT{"timeout"}}}, @{$DEPROOT{$OPSTAT{"coldstart"}}}, @{$DEPROOT{$OPSTAT{"warmstart"}}}, @{$DEP{$OPSTAT{"fail"}}}, @{$DEP{$OPSTAT{"linkdown"}}}, @{$DEP{$OPSTAT{"timeout"}}}, @{$DEP{$OPSTAT{"coldstart"}}}, @{$DEP{$OPSTAT{"warmstart"}}}, ]; } } # # build the table # sub compose_table { my ($rows, $st) = @_; if (@{$rows} == 0) { display_allok; return; } # # display the failure table # if ($CGI) { $OUT_BUF .= <
Dep Group Service Desc. Last check Next check Alerts Status Summary
$DEP $GROUP $SERVICE $DESC $TIME $NEXT $ALERTS $STATUS $SUMMARY
Description: $descr
Summary: $sum
Hosts: $hosts
Detail:
$sref->{last_detail}
	
EOF if ($d->{"ack"}) { my $comment = pre_pad_if_empty ($d->{"ackcomment"}); $OUT_BUF .= <

Acknowledgment of failure

$comment EOF } $OUT_BUF .= < EOF # # VAR: # variable name from "show opstatus" # # DESCR: # display name for variable # # IFZERO: # 0 = nothing special # 1 = do not display if zero # 2 = do not display if eq "" # # TYPE: # s = seconds # b = boolean # my ($VAR, $DESCR, $IFZERO, $TYPE) = (0..3); foreach my $k ( ["opstatus", "Operational Status", 0], ["exitval", "Exit Value", 0], ["depend", "Dependency", 2], ["monitor", "Monitor Program", 2], ["last_check", "Last Check", 2], ["timer", "Next Check", 2], ["last_success", "Last Success", 2], ["last_failure", "Last Failure", 2], ["first_failure", "First Failure", 2], ["failure_duration", "Failure Duration", 2], ["interval", "Schedule Interval", 0, "s"], ["exclude_period", "Exclude Period", 2], ["exclude_hosts", "Exclude Hosts", 2], ["randskew", "Random Skew", 1, "s"], ["alerts_sent", "Alerts Sent", 1], ["last_alert", "Last Alert", 2], ["monitor_duration", "Monitor Execution Duration", 2, "s"], ["monitor_running", "Monitor currently running", 0, "b"], ) { my $v = undef; if ($d->{$k->[$VAR]} ne "") { $v = \$d->{$k->[$VAR]}; } elsif ($sref->{$k->[$VAR]} ne "") { $v = \$sref->{$k->[$VAR]}; } # # convert types into display form # if ($k->[$TYPE] eq "s") { if ($$v >= 0) { $$v = secs_to_hms ($$v); } } elsif ($k->[$TYPE] eq "b") { $$v = $$v == 0 ? "false" : "true"; } # # display if zero? # next if ($k->[$IFZERO] == 1 && $$v == 0); next if ($k->[$IFZERO] == 2 && $$v eq ""); $OUT_BUF .= < $k->[$DESCR]: $$v EOF } $OUT_BUF .= < EOF # # custom links # if (defined ($CF->{"links"}->{$group}->{$service})) { if (defined ($CF->{"links"}->{$group}->{$service}->{"link-text"})) { $OUT_BUF .= <

{links}->{$group}->{$service}->{link}\">More Information

$CF->{links}->{$group}->{$service}->{'link-text'} EOF } else { $OUT_BUF .= < $CF->{links}->{$group}->{$service}->{link} EOF } } $OUT_BUF .= < Back to $group table Back to summary table

EOF } # # text output # else { my $n; $Text::Wrap::columns = 70; $n->{"desc"} = wrap (" ", " ", $st->{"desc"}->{$group}->{$service}); $n->{"last_summary"} = wrap (" ", " ", $sref->{"last_summary"}); $n->{"hosts"} = wrap (" ", " ", join (" ", @{$st->{groups}->{$group}})); print <{desc} summary ------- $n->{last_summary} hosts ----- $n->{hosts} -----DETAIL----- $sref->{last_detail} -----DETAIL----- EOF if ($d->{"ack"}) { print <{ackcomment} EOF } print <{opstatus} exitval: $d->{exitval} depend: $d->{depend} monitor: $d->{monitor} last check: $d->{last_check} next_check: $d->{timer} EOF } } sub opstatus_color { my $o = shift; my %color_hash = ( $OPSTAT{"untested"} => $CF->{"bg-untested"}, $OPSTAT{"ok"} => $CF->{"bg-ok"}, $OPSTAT{"fail"} => $CF->{"bg-fail"}, ); $color_hash{$o} || ""; } sub tdiff_string { my $time = shift; my $s; if ($time <= 90) { $s = "${time}s"; } else { $s = secs_to_hms ($time); } } # # for each watch entry which specifies only "group", # expand it into "group service" # sub expand_watch { my $what = shift; my $st = shift; for (my $i=0; $i<@{$what}; $i++) { if (@{$what->[$i]} == 1) { my @list; foreach my $l (@{$st->{"watch"}}) { if ($l->[0] eq $what->[$i]->[0]) { push @list, $l; } } splice (@{$what}, $i, 1, @list); } } } sub pre_pad_if_empty { my $l = shift; return "

 
" if ($l =~ /^\s*$/); $l; } etbemon-1.3.4/debian/0000755000175100017510000000000013615476543012530 5ustar rjcrjcetbemon-1.3.4/debian/source/0000755000175100017510000000000013455034225014015 5ustar rjcrjcetbemon-1.3.4/debian/source/format0000644000175100017510000000000413142727216015224 0ustar rjcrjc1.0 etbemon-1.3.4/debian/tmpfiles.d/0000755000175100017510000000000013151431364014560 5ustar rjcrjcetbemon-1.3.4/debian/NEWS0000644000175100017510000000052712355124105013213 0ustar rjcrjcmon (1.2.0-3) unstable; urgency=low The following alerts and monitors are not longer provided by 'mon' package: * imap-ssl.monitor * ipvs.alert * ipvs.monitor * smblist.monitor These scripts (and many others) are provided by 'mon-contrib' package. -- Dario Minnucci Mon, 11 Jul 2011 11:05:05 +0200 etbemon-1.3.4/debian/README.source0000644000175100017510000000046212355124105014671 0ustar rjcrjcREADME.source ------------- The Debian package uses quilt(1) for the modifications of the upstream source. If you want to change something it is best to use the quilt approach as documented in /usr/share/doc/quilt/README.source -- Dario Minnucci Wed, 20 Jan 2010 14:36:13 +0100 etbemon-1.3.4/debian/TODO0000644000175100017510000000016612355124105013203 0ustar rjcrjc# # TODO # * Add NEWS.Debian file to inform about alerts or monitors now availables via 'mon-contrib' package etbemon-1.3.4/debian/compat0000644000175100017510000000000313152761624013720 0ustar rjcrjc11 etbemon-1.3.4/debian/copyright0000644000175100017510000000630412423210772014450 0ustar rjcrjcFormat: http://anonscm.debian.org/viewvc/dep/web/deps/dep5.mdwn?view=co&revision=207 Upstream-Name: mon Upstream-Contact: Jim Trocki Source: http://sourceforge.net/projects/mon/files/mon/ Files: * Copyright: 1997-2009 Jim Trocki License: GPL-2+ 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. Files: clients/mon.cgi Copyright: 2007, Jim Trocki 2007, Andrew Ryan 2007, Martha H Greenberg 2007, Ed Ravin License: GPL-2+ 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. Files: debian/* Copyright: 2002-2005, Roderick Schertler 2006, Michael Ablassmeier 2007-2014, Dario Minnucci License: GPL-2 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 package; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA . On Debian systems, the full text of the GNU General Public License version 2 can be found in the file `/usr/share/common-licenses/GPL-2'. etbemon-1.3.4/debian/mon.default0000644000175100017510000000040213151417721014647 0ustar rjcrjc# Defaults for mon initscript # Created by Dario Minnucci # Master system-wide mon switch. # The initscript will not run if ENABLED is set # to values other than: "yes", "true" or "1". ENABLED="yes" # Daemon options DAEMON_OPTS="-f " etbemon-1.3.4/debian/mon.dirs0000644000175100017510000000033613022213627014166 0ustar rjcrjcetc/mon etc/default usr/bin usr/lib/cgi-bin usr/lib/mon/alert.d usr/lib/mon/mon.d usr/lib/mon/state.d /usr/lib/mon/bin /usr/lib/mon/mon-local.d usr/sbin usr/share/doc/mon usr/share/doc/mon/examples var/lib/mon var/log/mon etbemon-1.3.4/debian/mon.docs0000644000175100017510000000076213150505655014167 0ustar rjcrjc#CHANGES COPYRIGHT CREDITS KNOWN-PROBLEMS README TODO VERSION alert.template doc/CHANGES.mon.cgi doc/globals doc/how-to-write-a-monitor.txt doc/how-to-write-an-alert.txt doc/README.alerts doc/README.cgi-bin doc/README.hints doc/README.mon.cgi doc/README.monitors doc/README.msql-mysql.monitor doc/README.paging doc/README.protocol doc/README.rpc.monitor doc/README.snmpdiskspace.monitor doc/README.snmpvar.monitor doc/README.software doc/README.syslog.monitor doc/README.traps doc/README.variables etbemon-1.3.4/debian/mon.examples0000644000175100017510000000042012355124105015035 0ustar rjcrjcetc/auth.cf etc/example.cf etc/example.m4 etc/example.monshowrc etc/mon.cgi.cf etc/na_quota.cf etc/netappfree.cf etc/snmpdiskspace.cf etc/snmpopt.cf etc/snmpvar.cf etc/snmpvar.def etc/syslog-monitor.conf etc/very-simple.cf clients/batch-example clients/monremote.pl utils etbemon-1.3.4/debian/mon.links0000644000175100017510000000013512355124105014342 0ustar rjcrjc/usr/bin/monshow /usr/lib/cgi-bin/monshow.cgi /etc/mon/monshowrc /usr/lib/cgi-bin/.monshowrc etbemon-1.3.4/debian/mon.postrm0000644000175100017510000000067112355124105014553 0ustar rjcrjc#!/bin/sh # # Author: Dario Minnucci # Date: Fri, 29 Jun 2007 00:28:43 +0200 # Delete 'mon' user on purge step. # set -e #echo "----------------------------------------------" #echo " debian/postrm: $1" #echo "----------------------------------------------" case "$1" in purge) # Remove 'mon' user echo -n "Removing user 'mon' ... " userdel mon echo "done." ;; esac #DEBHELPER# etbemon-1.3.4/debian/mon.preinst0000644000175100017510000000150712355124105014712 0ustar rjcrjc#!/bin/sh # # Author: Dario Minnucci # Date: Fri, 29 Jun 2007 00:28:43 +0200 # # 'mon' user name availability is checked on install step. # 'mon' group name availability is checked on install step. # set -e #echo "----------------------------------------------" #echo " debian/mon.preinst: $1" #echo "----------------------------------------------" add_user_if_needed () { # # Check if 'mon' user is already present in the system, # if not, then create 'mon' user and group. # if ! getent passwd mon >/dev/null; then echo -n "Creating user 'mon' ... " adduser \ --gecos "mon daemon" \ --quiet \ --system \ --group \ --disabled-password \ --home /var/lib/mon \ mon echo "done." fi } case "$1" in install | upgrade) add_user_if_needed ;; esac #DEBHELPER# etbemon-1.3.4/debian/mon.prerm0000644000175100017510000000050612355124105014351 0ustar rjcrjc#!/bin/sh # # Author: Dario Minnucci # Date: Fri, 29 Jun 2007 00:43:09 +0200 # ### Username availability is checked on install step. # set -e ##echo "----------------------------------------------" ##echo " debian/prerm: $1" ##echo "----------------------------------------------" #DEBHELPER# etbemon-1.3.4/debian/watch0000644000175100017510000000006212355124105013537 0ustar rjcrjcversion=3 http://sf.net/mon/mon-([\d.]+)\.tar\.gz etbemon-1.3.4/debian/etc/0000755000175100017510000000000013212007106013255 5ustar rjcrjcetbemon-1.3.4/debian/etc/mon/0000755000175100017510000000000013212007422014047 5ustar rjcrjcetbemon-1.3.4/debian/README.Debian0000644000175100017510000000434113455035423014561 0ustar rjcrjcREADME.Debian: -------------- * New and deprecated alerts and monitors in mon-1.2.0: ------------------------------------------------------ - Added alerts: · irc.alert - Added monitors: · dns-query.monitor · local-syslog.monitor · lpd.monitor · ntpdate.monitor · radius.monitor · snmpdiskspace.monitor · snmpvar.monitor · trace.monitor - Removed alerts: · remote.alert - Removed monitors: · http_t.monitor · http_tp.monitor · http_tpp.monitor - Rename monitors: · dialin.monitor to dialin.monitor.wrap * Daemon runs under 'mon' user: ------------------------------- The daemon is not longer running as 'daemon' user. Since version 0.99.2-12, mon runs under user 'mon', that is created at the package installation/update process. * Daemon control via /etc/default/mon: -------------------------------------- Now 'mon' daemon can be controlled through /etc/default/mon directly. These 2 options allows you to control the daemon behaviour: - ENABLED: If you don't want to run 'mon' at boot time, please ensure the variable ENABLED in /etc/default/mon is set to something different to : "yes", "true" or "1" Default is: ENABLED="yes" - DAEMON_OPTS: Additional options can be passed to the daemon by adding them to DAEMON_OPTS. Default is DAEMON_OPTS=" -f -c /etc/mon/mon.cf -A /etc/mon/auth.cf" * Using CGI: ------------ If a webserver with CGI support is available on your system, both web interfaces can be accessed from the following URL: http:///cgi-bin/monshow.cgi OR http:///cgi-bin/mon.cgi The layout and behaviour of both interfaces, can be modified via an independent configuration files. Changes on 'monshow.cgi' should be made via it's configuration file '/etc/mon/monshowrc' For details see: /usr/share/doc/mon/README.cgi-bin /usr/share/doc/mon/examples/example.monshowrc * Using local monitors: ----------------------- Under Debian GNU/Linux, paths for alerts and monitors differs from the original paths provided by software in order to comply with FHS. Our alerts are located under '/usr/lib/mon/alert.d' and our monitors under '/usr/lib/mon/mon.d' directory. Please take this into account if your intend to add your own scripts. etbemon-1.3.4/debian/mon.postinst0000644000175100017510000000334113604576132015120 0ustar rjcrjc#!/bin/sh # # Author: Dario Minnucci # Date: Fri, 29 Jun 2007 02:51:52 +0200 # # 'mon' user creation in configure step. # set -e #echo "----------------------------------------------" #echo " debian/mon.postinst: $1" #echo "----------------------------------------------" case "$1" in configure) chown mon:root "/var/log/mon" for FILE in /usr/lib/mon/bin/btrfs.helper /usr/lib/mon/bin/zfs.helper /usr/lib/mon/mon-local.d/deleted-mapped.monitor ; do if ! dpkg-statoverride --list $FILE > /dev/null 2>&1 then chown root:mon $FILE chmod 4750 $FILE fi done # # Check if pidfile directory already exists and change ownership" # if [ -d "/run/mon" ] ; then ############################################################ # Compativility with versions older than 0.99.2-12: ############################################################ # On previous versions, /run/mon directory ownership was # set to 'daemon:root' in order to manage the pidfile. # With the introduction of the user 'mon', the init script # switches to user 'mon' before starting the daemon startup # process. Because of this, we need to change the directory # ownership to ensure the pidfile is created. ############################################################ chown mon:root "/run/mon" fi ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac if [ -d /run/systemd/system ]; then systemctl --system daemon-reload >/dev/null || true if [ -n "$2" ]; then _dh_action=try-restart else _dh_action=start fi deb-systemd-invoke $_dh_action mon.service >/dev/null || true fi #DEBHELPER# etbemon-1.3.4/debian/rules0000755000175100017510000000140713604576401013602 0ustar rjcrjc#!/usr/bin/make -f #export DH_VERBOSE=1 %: dh $@ override_dh_auto_clean: dh_auto_clean $(MAKE) -C mon.d clean $(MAKE) -C mon-local.d clean $(MAKE) -C bin clean override_dh_auto_build: dh_auto_build $(MAKE) -C mon.d CFLAGS="$(CFLAGS)" && cd $(CURDIR) $(MAKE) -C mon-local.d CFLAGS="$(CFLAGS)" $(MAKE) -C bin CFLAGS="$(CFLAGS)" && cd $(CURDIR) override_dh_install: dh_install dh_installdocs chmod +x $(CURDIR)/debian/mon/usr/lib/mon/alert.d/*.alert chmod +x $(CURDIR)/debian/mon/usr/lib/mon/mon.d/*.monitor chmod +x $(CURDIR)/debian/mon/usr/lib/mon/mon-local.d/*.monitor chmod +x $(CURDIR)/debian/mon/usr/lib/mon/bin/* dh_installsystemd override_dh_installman: dh_installman clients/skymon/skymon.1 doc/mon.8 doc/moncmd.1 doc/monshow.1 doc/monfailures.1 etbemon-1.3.4/debian/mon.install0000644000175100017510000000115213604576742014710 0ustar rjcrjcetc/auth.cf etc/mon etc/mon.cf etc/mon etc/monshowrc etc/mon etc/init.d/mon etc/init.d systemd/tmpfiles.d/mon.conf usr/lib/tmpfiles.d systemd/mon.service lib/systemd/system clients/mon.cgi usr/lib/cgi-bin clients/moncmd usr/bin clients/monfailures usr/bin clients/monshow usr/bin clients/skymon/skymon usr/bin mon usr/sbin alert.d/*.alert usr/lib/mon/alert.d mon.d/*.monitor usr/lib/mon/mon.d mon.d/*.monitor.wrap usr/lib/mon/mon.d mon-local.d/*.monitor usr/lib/mon/mon-local.d mon-local.d/*.monitor.real usr/lib/mon/mon-local.d bin/*.helper usr/lib/mon/bin state.d/* usr/lib/mon/state.d etbemon-1.3.4/debian/control0000644000175100017510000000300113606502736014117 0ustar rjcrjcSource: etbemon Section: admin Priority: optional Maintainer: Debian Mon Maintainers Uploaders: Dario Minnucci , Russell Coker Build-Depends: debhelper (>= 11), perl, libtime-period-perl Standards-Version: 4.3.0 Homepage: https://mon.wiki.kernel.org Vcs-Git: git://anonscm.debian.org/collab-maint/mon.git Vcs-Browser: http://anonscm.debian.org/gitweb/?p=collab-maint/mon.git Package: mon Architecture: any Depends: mon-client (>= 1.2.0), libtime-period-perl, adduser, ${shlibs:Depends}, ${misc:Depends} Recommends: fping, libauthen-pam-perl, libfilesys-df-perl, libnet-perl, libnet-dns-perl, libnet-ldap-perl, libnet-telnet-perl, libsnmp-perl, libstatistics-descriptive-perl, libtime-parsedate-perl, libcrypt-ssleay-perl, libmail-imapclient-perl, libtimedate-perl, swaks, libcgi-pm-perl, bc, libproc-processtable-perl Suggests: mon-contrib Conflicts: mon-contrib (<= 1.0+dfsg-3+nmu1) Description: monitor hosts/services/whatever and alert about problems "mon" is a tool for monitoring the availability of services. Services may be network-related, environmental conditions, or anything that can be tested with software. If a service is unavailable mon can tell you with syslog, email, your pager or a script of your choice. You can control who gets each alert based on the time of day or day of week, and you can control how often an existing problem is re-alerted. . More information can be found at http://mon.wiki.kernel.org etbemon-1.3.4/debian/changelog0000644000175100017510000007205713615473254014411 0ustar rjcrjcetbemon (1.3.4-1) unstable; urgency=medium * Added monitor for deleted mapped files for detecting upgraded shared objects * Change Standards version to 4.3.0 -- Russell Coker Sun, 02 Feb 2020 18:13:30 +1100 etbemon (1.3.3-6) unstable; urgency=medium * Add SUID helper for zfs and for deleted-mapped.monitor * Made deleted-mapped.monitor ignore /dev/zero as memory mapped usage routinely reports that as deleted. -- Russell Coker Mon, 06 Jan 2020 19:59:16 +1100 etbemon (1.3.3-5) unstable; urgency=medium * Made loadavg.monitor check swap space and made the output nicer. * Made linux-temp.monitor display processes with top CPU use and give better summary. * Give better log errors from mon on userfile auth failures. * Added password authentication support to remote.monitor * Added deleted-mapped.monitor that checks for files that are memory mapped but have been deleted, experimental and may get false positives. Designed to deal with security updates to shared objects that are in use. -- Russell Coker Mon, 06 Jan 2020 17:04:00 +1100 etbemon (1.3.3-4) unstable; urgency=medium * Make dns.monitor correctly display a summary when multiple queries are used * Stop loadavg.monitor from saying "Text read from file" * Make ps.monitor also check for lua5.2 * Link btrfs.helper with gcc instead of g++ to reduce dependencies. * Better comments in auth.cf and README.traps * Better logging of trap password issues * Fixed a username bug in trap.alert * imapnew.monitor now deletes messages even if there is a delay or clock skew * Make btrfs.monitor correctly handle count files and have better comments. * Fix the build to clean up the compiled helper programs, and build with correct diff. Closes: #909854 * Make trapbind default to 127.0.0.1. Closes: #910741 * Fix README.monitors. Closes: #910743 -- Russell Coker Tue, 30 Apr 2019 20:38:26 +1000 etbemon (1.3.3-3) unstable; urgency=medium * Make btrfs.monitor check for subvols and also use a SETUID helper instead of sudo -- Russell Coker Mon, 23 Jul 2018 15:24:34 +1000 etbemon (1.3.3-2) unstable; urgency=medium * Don't abort mon.cgi when $ONDS{$group} is invalid * Removed defined() around hash and array checks not supported in recent perl * Check Received: headers for long delays and clock skew in imapnew.monitor -- Russell Coker Wed, 11 Jul 2018 01:40:04 +1000 etbemon (1.3.3-1) unstable; urgency=medium * Added SNI support to sslcert.monitor. * New upstream version because the previous 2 Debian releases have enough changes to make it worthwhile. -- Russell Coker Wed, 16 May 2018 16:16:54 +1000 etbemon (1.3.2-3) unstable; urgency=medium * Make smtpswaks.monitor use long form swaks arguments for ease of searching the man page * Made mailxmpp.alert set a CPU time limit -- Russell Coker Mon, 01 Jan 2018 15:25:22 +1100 etbemon (1.3.2-2) unstable; urgency=medium * Conflict with mon-contrib (<= 1.0+dfsg-3+nmu1), Closes: #876088 * Use dh_installsystemd to comply with latest debhelper, Closes: #878850 * Recommends libtimedate-perl for imap test * Added support for authentication to smtpswaks.monitorm, for relay tests. -- Russell Coker Thu, 07 Dec 2017 01:57:42 +1100 etbemon (1.3.2-1) unstable; urgency=medium * Made http.monitor use strict and made it not give an error if the server doesn't close the connection after sending the data. * Made dns.monitor give a more detailed summary. * Made zfs.monitor give a summary of the error count when it's deemed to be OK. * Rename softraid2.monitor to softraid.monitor * Make freespace.monitor check for Inodes free if filesystem supports it and recommend libfilesys-df-perl which it now uses. * Remove all configuration options from /etc/default/mon for Debian and put them in the sample mon.cf file. * Moved sample configuration files out of the debian directory for the benefit of other distributions. * Added new systemd subdirectory for systemd config files * Added systemd service file and use debhelper compat level 11 to get automatic dh_systemd stuff * Rewrote sslcert.monitor in perl and made it give better messages and also status on no error. * Made loadavg.monitor not display D status heading if there are no D state processes, only display the first 120 characters of the process details, and not display processes using less than 5% CPU. * Made mailxmpp.alert use TLS for XMPP -- Russell Coker Sun, 17 Sep 2017 16:19:34 +1000 etbemon (1.3.1-1) unstable; urgency=medium * Fixed up the hack for freespace.monitor. * Stop using quilt as upstream will be identical to Debian in almost all cases. * Made remote.monitor correctly report an error on connect timeout Closes: #870239 Made the exclude option on remote.monitor work correctly * Made loadavg.monitor correctly sort by CPU time and ignore 0.0% and ps header Closes: #870238 * Removed the hack for freespace.monitor * Changed the summary-len in monshow to 80, as we already line wrap on a 80 column terminal might as well go big * Made the password option in msql-mysql.monitor read from a file if the first character is / -- Russell Coker Thu, 10 Aug 2017 10:42:03 +1000 etbemon (1.3.0-1) unstable; urgency=medium * Forked upstream and renamed it to etbemon -- Russell Coker Tue, 25 Jul 2017 22:37:33 +1000 mon (1.2.0-12) unstable; urgency=medium * Made btrfs and zfs checks accept low and high numbers for error counts. * Add softraid2.monitor which doesn't use diff, has no problems with drive ordering, and allows specifying which arrays must exist. -- Russell Coker Wed, 12 Jul 2017 23:15:01 +1000 mon (1.2.0-11) unstable; urgency=medium * Make zfs.monitor and btrfs.monitor alert when you forget to fix the okcount after fixing an error. * Made loadavg.monitor correctly sort the CPU time and send a sort error to /dev/null * Tweaked the output of imapnew.monitor * Made the remote.monitor have better debugging and put it in a separate patch, also added --exclude option * Use /run instead of /var/run * Recommend swaks for smtpswaks.monitor * Recommend libcgi-pm-perl for monshow * Recommend bc for sslcert.monitor * Recommend libproc-processtable-perl for ps.monitor -- Russell Coker Sun, 18 Jun 2017 18:52:20 +1000 mon (1.2.0-10) unstable; urgency=medium * Change to team maintainership. * Make loadavg.monitor report on D state processes and processes using the most CPU. * Added imapnew.monitor to check for new messages via IMAP and recommends libmail-imapclient-perl for it. -- Russell Coker Tue, 23 May 2017 18:46:51 +1000 mon (1.2.0-9+nmu4) unstable; urgency=medium * Use the correct name for the tmpfiles.d file Closes: #851354 -- Russell Coker Mon, 16 Jan 2017 12:16:21 +1100 mon (1.2.0-9+nmu3) unstable; urgency=medium * Add sslcert.monitor to check certificate expiry and smtpswaks.monitor to check mail servers with swaks -- Russell Coker Wed, 21 Dec 2016 21:08:11 +1100 mon (1.2.0-9+nmu2) unstable; urgency=medium * Move freespace.monitor to local directory. -- Russell Coker Wed, 21 Dec 2016 20:47:11 +1100 mon (1.2.0-9+nmu1) unstable; urgency=medium * Non-maintainer upload. * Make default config bind to 127.0.0.1 Closes: #820712 * Add restorecon and tmpfiles.d file Closes: #822606 * Don't specify config options on both the default command-line and the default configuration file Closes: #821360 * Move local monitor scripts to /usr/lib/mon/mon-local.d to separate them from network checks Closes: #847437 * Added remote.monitor to this package and made it strict. Closes: #847441 Fixed the problems with it Closes: #824176 * Added ps.monitor and made it strict. Also made it handle perl, python, and lua5.1 interpreters correctly. Closes: #847444 * Conflicts with mon-contrib <= 1.0+dfsg-3 because of remote.monitor and ps.monitor * Added mailxmpp.alert, an alert that sends mail and XMPP messages * Added linux-temp.monitor for local system temperature and loadavg.monitor * Extended ping.monitor to support ipv6 and specifying the ping count -- Russell Coker Mon, 12 Dec 2016 10:00:22 +1100 mon (1.2.0-9) unstable; urgency=medium * debian/control: - Bump Standards-Version to 3.9.6 (no changes) - libtime-modules-perl has been renamed by upstream, so it was updated to it's current name 'libtime-parsedate-perl' (Closes: #749875) * debian/mon.init.d: required-start facility changed from $all to $syslog to deal with lintian error: "E: mon: init.d-script-depends-on-all-virtual-facility etc/init.d/mon required-start" * debian/copyright: Years updated to 2014 -- Dario Minnucci Sun, 26 Oct 2014 16:23:52 +0100 mon (1.2.0-8) unstable; urgency=low * debian/patches: - Added 06-fix-dns-monitor.diff: Fixes use of deprecated defined(@array) calls. Thanks to Brogniaux Gaëtan. (Closes: #731737) * debian/control: - Bump Standards-Version to 3.9.5 (no changes) -- Dario Minnucci Tue, 31 Dec 2013 04:16:37 +0100 mon (1.2.0-7) unstable; urgency=low * debian/copyright: - Upstream-Source URL updated. (Closes: #659993) - Years updated to 2013 * debian/watch: Modified to use SF sources. Thanks to Nick Black. (Closes: #695242) * debian/mon.dirs: Added /var/lib/mon. Thanks to Andreas Beckmann. (Closes: #661087) * debian/control: - Bump Standards-Version to 3.9.4 (no changes) - Use canonical URI on VCS-* fields -- Dario Minnucci Thu, 21 Feb 2013 03:18:47 +0100 mon (1.2.0-6) unstable; urgency=low * debian/control: Really set debhelper compat to >=9. * debian/copyright: Source URL updated. - Thanks to Jari Aalto (Closes: #655357) * debian/patches: - 04-fix-659152-report.monitor-ignores-command-line-options.diff Fix for report.monitor ignores command-line options. Thanks to Kevin McCormick (Closes: #659152) * debian/mon.postrm: Fix to not remove user 'mon' twice. (Closes: #656351) -- Dario Minnucci Sun, 12 Feb 2012 17:00:45 +0100 mon (1.2.0-5) unstable; urgency=low * Enable hardened build flags: - Added patch 00-mon-hardened-build-flags.diff. - Set debhelper compat to 9. - Added debian/source.lintian-overrides Thanks to Moritz Muehlenhoff. (Closes: #655137) * debian/copyright: Copyright years updated. * debian/control: Homepage URL updated. -- Dario Minnucci Tue, 10 Jan 2012 13:55:53 +0100 mon (1.2.0-4) unstable; urgency=low * debian/mon.{postinst,postrm,preinst}: Better handling of 'mon' user creation/deletion. (Closes: #628082) * debian/mon.postinst: Change ownership on /var/log/mon/ to let the daemon to write logfiles. (Closes: #645981) * debian/copyright: - Copyright years updated. - Updated for DEP-5 compatibility. * debian/control: Upgrade debhelper to >=8 * debian/mon.doc: Don't install changelog twice. -- Dario Minnucci Fri, 25 Nov 2011 11:28:26 +0100 mon (1.2.0-3) unstable; urgency=low * debian/control: Bump Standards-Version to 3.9.2 * List of dropped alerts and monitors that will be provided by a new package 'mon-contrib': - 06-add-imap-ssl.monitor.diff - 06-add-ipvs.alert.diff - 06-add-ipvs.monitor.diff - 06-add-smblist.monitor.diff * debian/patches: - Remove from 04-fix-SNMP-module-calls.diff previous patching about SNMP module call on file mon/mon.d/hpnp.monitor. Thanks to Marc F. Clemente (Closes: #613720) * debian/README.Debian: Fix for lintian warning: spelling-error-in-readme-debian -- Dario Minnucci Mon, 11 Jul 2011 11:04:57 +0200 mon (1.2.0-2) unstable; urgency=low * Hosted under git.debian.org (collab-main branch): http://git.debian.org/?p=collab-maint/mon.git * debian/control: - Updated debhelper dependency to (>= 7.0.50~) - Added Vcs-Git and Vcs-Browser fileds - Bump Standards-Version to 3.9.1: - Switch to dpkg-source 3.0 (quilt) format * debian/patches: - Added 01-fix-logging-to-syslog.diff. (Closes:#611751) Thanks to Allan Wind - 04-fix-SNMP-module-calls.diff: Modified to use SNMP module. (Closes:#596292). Thanks to Marc F. Clemente * debian/README.Debian: - Typos fixed * debian/mon.default: Change default state directory to /var/lib/mon to give mon a chance to write state information. (Closes:611772) Thanks to Allan Wind -- Dario Minnucci Sun, 13 Feb 2011 00:53:38 +0100 mon (1.2.0-1) unstable; urgency=low * New upstream release (Closes: #433265) * Maintainer email address updated * debian/mon.default: Added more configuration parameters * debian/control: - Added 'libtime-modules-perl', 'libcrypt-ssleay-perl' to 'Reccomends' needed by 'imap-ssl.monitor' to work. - Long description: Project URL updated - Patch system moved from 'dpatch' to 'quilt' * debian/patches/*: Added DEP3 headers - Reapplied patches: - 00_security_fixes.patch: New version has no fix for #496398 this yet - Deapplied patches: - 01_cfbasedir_fix.patch: Now changes are made via /etc/default/mon file - 03_remove_revision_control_headers.patch: New version is not including previous maintainer's revision control headers anymore. - Modified patches: - 04_fix_SNMP_module_calls.patch: Added fixes on new distributed files: - mon.d/hpnp.monitor - mon.d/snmpdiskspace.monitor - mon.d/snmpvar.monitor - 05_manpage_fixes.patch: Splited into independent manpage fixes: - 05_manpage_fix_mon.8.patch: For mon.8 manpage. - 05_manpage_fix_moncmd.1.patch: For moncmd.1 manpage. - 06_add_scripts_for_ipvs: Splited into: - 06_add_ipvs.alert.patch - 06_add_ipvs.monitor.patch - New patches: - 06_add_imap-ssl.monitor.patch: Adds imap-ssl.monitor. (Closes: #545160) - 06_add_smblist.monitor.patch: Adds smblist.monitor * debian/copyright: Updated to DEP5 format proposal. (see: http://dep.debian.net/deps/dep5/ for details) * debian/mon.init.d: - Change Required-Start to $all to start the daemon the latest possible. - Added $remote_fs dependencies -- Dario Minnucci Thu, 21 Jan 2010 17:15:21 +0100 mon (0.99.2-15) unstable; urgency=medium * debian/mon.init.d: Fixes to start the daemon later on system boot. (Closes: #547556) * debian/control: Removed dependencies on libtime-hires-perl, provided by perl -- Dario Minnucci (midget) Tue, 22 Sep 2009 16:50:11 +0200 mon (0.99.2-14) unstable; urgency=low * debian/control: debhelper compatibility updated to >=7 ${misc:Depends} added to fix debhelper-but-no-misc-depends lintian warning. Standards-Version bumped to 3.8.3: Added debian/README.source file. * debian/rules: deprecated dh_clean -k in favour of dh_prep * debian/mon.dirs: Remove creation dirs: usr/lib/mon/cgi-bin/, var/log/mon and var/run/mon * debian/mon.init.d: Was completely rewritten to be LSB compliant. (Closes: #538133) Added support for ENABLED args: "yes", "true" or "1". (Closes: #522546) Added dynamical control for recreation of /var/run/mon directory. * debian/copyright: Fixed copyright-refers-to-symlink-license * debian/README.Debian: Updated Explanations on how to use local monitors (Closes: #477164) * debian/NEWS.Debian: Removed * Added 'set -e' to: mon.mon.preinst, mon.postinst, mon.prerm, mon.postrm * debian/patches/05_manpage_fixes: Fixes typos in manpages * debian/patches/06_add_scripts_for_ipvs: Add scripts for ipvs support. Thanks to Richard Hartmann. (Closes: #500810) * Do not apply debian/patches/02_enable_monshow_full_mode.dpatch anymore to revert behaviour of 'monshow --full'. (Closes: #422866) * debian/etc/mon/monshowrc: Provides an initial .monshowrc configuration for monshow.cgi * Symbolic links added: monshow.cgi is a symbolic link of /usr/bin/monshow .monshowrc is a symbolic link of /etc/mon/monshowrc -- Dario Minnucci (midget) Thu, 03 Sep 2009 07:29:40 +0200 mon (0.99.2-13) unstable; urgency=low * debian/control: Conforms with latest Standards Version 3.8.0 * debian/control: Added 'Homepage' field * debian/patches/00_security_fixes: (Closes: #496398) -- Dario Minnucci (midget) Wed, 10 Sep 2008 14:19:23 +0200 mon (0.99.2-12) unstable; urgency=low * debian/control: Maintainer's name changed due to GPG signature update. * Changes to run mon as user mon. (Closes: #117550) * debian/mon.default: Added. (Closes: #311352) * Arguments are now managed in the new /etc/default/mon file which solves bug #305108. (Closes: #305108) * debian/mon.init.d: Completely rewritten. * debian/mon.postinst: Completely rewritten. * debian/mon.postinst: Completely rewritten. * debian/mon.preinst: Completely rewritten. * debian/mon.prerm: Completely rewritten. * debian/patches/01_cfbasedir_fix: More harcoded paths fixed. * patches/04_SNMP_module_call_fixes.dpatch: Issued to solve SNMP module issue reported on BTS #432858. (Closes: #432858) - asyncreboot.monitor: SNMP module call updated. - cpqhealth.monitor: SNMP module call updated. - foundry-chassis.monitor: SNMP module call updated. - na_quota.monitor: SNMP module call updated. - netappfree.monitor: SNMP module call updated. - process.monitor: SNMP module call updated. - reboot.monitor: SNMP module call updated. - silkworm.monitor: SNMP module call updated. - xedia-ipsec-tunnel.monitor: SNMP module call updated. -- Dario Minnucci (midget) Mon, 30 Jul 2007 00:56:22 +0200 mon (0.99.2-11) unstable; urgency=low * debian/watch: Rewritten to match upstream version format. * clients/skymon/skymon.1: Added basic manpage for skymon (Closes: #410614) * doc/mon.8: SEE ALSO section updated. * doc/moncmd.1: SEE ALSO section updated. * doc/monshow.1: SEE ALSO section updated. * debian/control: 'Suggests:' upgraded to 'Recommends:' (Closes: #181778). For further information, please read NEWS.Debian file. -- Dario Minnucci Mon, 5 Mar 2007 03:13:44 +0100 mon (0.99.2-10) unstable; urgency=low * New maintainer (Closes: #337944) * mon.d/smblist.monitor: Added Samba monitor contributed by Matthew Astley (Closes: #180872) * debian/control: Added dpatch to Build-Depends: (for dpatch support) * A few patches were issued: - patches/01_cfbasedir_fix: Fixes the configuration base directory. - patches/02_enable_monshow_full_mode: Shows all watches on cgi execution. - patches/03_remove_revision_control_headers: Removes revision control headers from most of the distributed files. (Closes: #322566) NOTE: A few headers used within the code still remains there. You can find remaining revision control headers the following files: - alert.d/file.alert - alert.d/netpage.alert - alert.d/mail.alert - mon - mon.d/http_tpp.monitor - mon.d/smtp3.monitor - mon.d/file_change.monitor * debian/NEWS.Debian file added reporting changes -- Dario Minnucci Sun, 11 Feb 2007 22:38:27 +0100 mon (0.99.2-9) unstable; urgency=low * QA upload. * Set maintainer to QA Group; Orphaned: #337944 * Bump compat level to debhelper 5 * Depend on at least debhelper 5 * Add missing binary-indep target to debian/rules * Update debian/copyright * Add missing lsb section to debian/init * Conforms with latest Standards Version 3.7.2 -- Michael Ablassmeier Thu, 2 Nov 2006 16:25:44 +0100 mon (0.99.2-8) unstable; urgency=low * msql-mysql.monitor: Use $dbh->tables rather than $dbh->func('_ListTables'), thanks to Clement 'nodens' Hermann and Jade Nicoletti (closes: #275347). * http_t.monitor: s/joint/join/, thanks to Bryan Chow (closes: #276553). * smtp.monitor: Support continued quit (221-) messages, thanks to Joe Edmonds (closes: #282104). * smtp3.monitor: Support continued banner (220-) messages, thanks to Brian Grossman (closes: #296943). -- Roderick Schertler Fri, 25 Mar 2005 21:44:00 -0500 mon (0.99.2-7) unstable; urgency=low * Back-port fix for broken host parsing in auth.cf trap section (closes: #251236). * Fix "alertafter