/*
wmnetmon - A network ICMP ping host monitoring tool.
Copyright (C) 1999 Alvaro Lopes <alvieboy@alvie.com>
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
*/
#define VERSION "0.2"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/xpm.h>
#include <fcntl.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <ctype.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <math.h>
#include "config.h"
#include "wmgeneral.h"
#include "wmnetmon.h"
/* Please define this in the Makefile if you want to force coredumps */
#ifdef FORCECORE
#include <sys/resource.h>
#endif
#include "config.h"
#include "leds.h"
#include "pinger.h"
#include "list.h"
#include "configfile.h"
#include "logger.h"
/* Global variables */
ListItem hosts;
char *configfile = 0;
/* Files - FIXME */
char soundfile[128],soundplayer[128];
/* Pixmap */
#include "wmnetmon.xpm"
char *wmnetmon_mask_bits;
const int wmnetmon_mask_width=64;
const int wmnetmon_mask_height=64;
const time_t max_sleep_time=250000L;
int poll_time=30;
int poll_yellowtime=60;
int poll_redtime=200;
int default_flags = 0;
ledarray leds;
int need_to_scroll_title = 0;
void drawtimeout(hostmon *h);
void cleararea(int x, int y, int width, int height)
{
copyXPMArea(89,17,width,height,x,y);
}
void settitles (char*newtitle, int permanent, int justscroll)
{
static char title[MAX_HOSTNAME+1];
static char temptitle[MAX_HOSTNAME+1];
static int currenttitle; /* 0-> title, 1-> temp */
static int scroll_direction;
if (!newtitle && need_to_scroll_title) {
need_to_scroll_title += scroll_direction;
if (!need_to_scroll_title) {
scroll_direction=1;
need_to_scroll_title=1;
} else if (need_to_scroll_title >
(strlen(currenttitle ? temptitle: title)*5 - 55)) {
scroll_direction=-1;
need_to_scroll_title--;
}
}
if (!justscroll)
currenttitle=0;
if (newtitle) {
if (permanent) strcpy(title,newtitle);
else strcpy(temptitle,newtitle);
currenttitle = ! permanent;
scroll_direction=1;
need_to_scroll_title = (strlen(newtitle) > 11);
}
cleararea(4,4,56,8);
writetextat(4,4,currenttitle ? temptitle : title);
RedrawWindow();
}
void settemptitle(char*newtitle)
{
settitles(newtitle,0,0);
}
void settitle(char*newtitle)
{
settitles(newtitle,1,0);
}
void writetextat(int x,int y, char *string)
{
int cpos,index;
int hoffset,partial;
int skip; /* Number of characters to skip at beggining */
int total; /* Big total number of chars to display -> 11 or 12 */
if (need_to_scroll_title<5)
skip=0;
else
skip = ((need_to_scroll_title - 1 )) / 5;
if (need_to_scroll_title)
total = (need_to_scroll_title-1) % 5 == 0 ? 11 : 12;
else total=11;
if ( total > strlen(string) )
total = strlen(string);
for (cpos=skip; cpos < total+skip ; cpos++) {
if (string[cpos]!=' ') {
if (string[cpos]<='9')
index=string[cpos]-'0'+('z'-'a'+1);
else
index=tolower(string[cpos])-'a';
/* index is the source offset */
if (! need_to_scroll_title)
copyXPMArea(5*index ,64, 5 ,6, x + (5*cpos) , y);
else {
/* Partial columns of the first char we won't display */
partial = (need_to_scroll_title - 1) % 5;
/* hoffset is where we start to draw the character
*/
hoffset= x + ( (cpos-skip)*5 ) - partial;
if (hoffset<x) hoffset=x;
/*if (partial==0)
printf("need to skip %d pixels, total %d, hoffset %d\n",partial,(need_to_scroll_title - 1),hoffset);
*/
if (cpos==skip) {
copyXPMArea(5*index + partial ,64, 5 - partial ,6, hoffset , y);
} else
/*if ((cpos-skip)==11) {
copyXPMArea(5*index ,64, 5 ,6, hoffset , y);
} else
{*/
copyXPMArea(5*index ,64, 5 ,6, hoffset , y);
/*}*/
}
}
}
}
void flashleds(struct led *leds[XLEDS][YLEDS], int color)
{
int i,j;
for (i=0; i<XLEDS; i++)
{
for (j=0; j<YLEDS; j++)
{
leds[i][j]->current_status = color;
}
usleep(100000);
display_leds(0);
RedrawWindow();
}
}
void checkledtouch(ListItem hst, int x, int y)
{
ListItem iterator;
hostmon *h;
/* leds start at 6, 16 */
if (x < 6 || y < 16) return;
for (iterator=hst;iterator;iterator=iterator->next)
{
h=(hostmon*)ListData(iterator);
if (x >= h->lx && y >= h->ly)
{
if (x <= (h->lx +5) && y <= (h->ly +5 ))
{
settemptitle(h->hostname);
return;
}
}
}
settemptitle("SELECT HOST");
}
void checkledbuttonpress(ListItem hst, XButtonEvent ev)
{
ListItem iterator;
hostmon *h;
/* leds start at 6, 16 */
if (ev.x < 6 || ev.y < 16) return;
for (iterator=hst;iterator;iterator=iterator->next)
{
h=(hostmon*)ListData(iterator);
if (ev.x >= h->lx && ev.y >= h->ly)
{
if (ev.x <= (h->lx +5) && ev.y <= (h->ly +5 ))
{
if (ev.button == 3) {
if (h->flags&FLAG_MUTED) {
h->flags &= ~FLAG_MUTED;
leds[h->x][h->y]->current_status=LED_OFF;
}
else {
h->flags |= FLAG_MUTED;
leds[h->x][h->y]->current_status=LED_BLUE;
}
}
}
}
}
}
hostmon* add_host(pinger *p, char *hostip, char *hostname)
{
hostmon *hm;
pinger_host *h;
int numberofhosts;
int cx,cy;
numberofhosts=ListSize(&hosts);
cy = numberofhosts / XLEDS;
cx = numberofhosts % XLEDS;
hm=malloc(sizeof(hostmon));
h=pinger_addhost(p,hostip);
if (!h) return 0;
hm->h=h;
strncpy(hm->hostname, hostname, MAX_HOSTNAME);
hm->flags=default_flags;
leds[cx][cy]=add_led(6 + cx*6, 16+ cy*6 ,LED_OFF);
hm->lx = 6 + cx*6;
hm->ly = 16+ cy*6;
hm->x=cx;
hm->y=cy;
/*
cx++;
if (cx>=XLEDS) {
cy++; cx=0;
}
*/
return ListInsert(&hosts,hm)!=0 ? hm : 0 ;
}
int init_pinger_hosts(pinger *p, ListItem *hosts, struct led *leds[XLEDS][YLEDS])
{
ListInit(hosts);
if (readconf(p,leds,configfile)!=0)
return -1;
display_leds(0);
RedrawWindow();
return 0;
}
void usage()
{
printf("WMNetMon: a network host/services monitoring tool\n\n");
printf("Usage: wmnetmon [-d] [-t <time>] [-y <seconds>] [-r <seconds>] [-c <filename>] [-h] \n");
printf("-h: shows this help screen\n");
printf("-d: turns on debugmode\n");
printf("-c: use the specified configuration file instead $HOME/.wmnetmonrc\n");
printf("-t <poll time>: specifies the poll time in seconds. Default is 30 seconds\n");
printf("-y <seconds>: specifies amount of time to wait for a reply before turning the\n");
printf(" yellow led on. Default is 60 seconds\n");
printf("-r <seconds>: same as above for the red (flasing) led. Default is 200 seconds.");
printf("\n\nPlease read the enclosed wmnetmonrc for configuration file details\n\n");
}
extern int debug;
static RETSIGTYPE debug_signal(int sig)
{
if (debug) {
fprintf(stderr,"got SIGUSR1: turning OFF debug messages\n");
debug=0;
} else
{
fprintf(stderr,"got SIGUSR1: turning ON debug messages\n");
debug=1;
}
(void)signal(SIGUSR1,&debug_signal);
}
static RETSIGTYPE chld_signal(int sig)
{
while (waitpid(0, (int *) (NULL), (WNOHANG | WUNTRACED)) > 0);
}
int parseargs (int argc, char **argv)
{
int opt;
/* Options */
for (opt=1; opt<argc; opt++) {
if ((*argv[opt]) == '-') {
switch (*(argv[opt]+1)) {
case 't':
if (!argv[opt+1]) {
fprintf(stderr,"Error: poll time not specified\n");
return -1;
}
poll_time = (unsigned int)
strtoul(argv[opt+1],NULL,10);
if (poll_time < 1 ) {
fprintf(stderr,"Error: invalid poll time \"%s\"\n",
argv[opt+1]);
return -1;
}
opt++;
break;
case 'y':
if (!argv[opt+1]) {
fprintf(stderr,"Error: time not specified\n");
return -1;
}
poll_yellowtime = (unsigned int)
strtoul(argv[opt+1],NULL,10);
if (poll_yellowtime < 1 ) {
fprintf(stderr,"Error: invalid time \"%s\"\n",
argv[opt+1]);
return -1;
}
opt++;
break;
case 'r':
if (!argv[opt+1]) {
fprintf(stderr,"Error: time not specified\n");
return -1;
}
poll_redtime = (unsigned int)
strtoul(argv[opt+1],NULL,10);
if (poll_redtime < 1 ) {
fprintf(stderr,"Error: invalid time \"%s\"\n",
argv[opt+1]);
return -1;
}
opt++;
break;
case 'h':
usage();
return -1;
break;
case 'c':
if (! argv[opt+1]) {
fprintf(stderr,"you must specify configfile with -c\n");
return -1;
}
configfile=argv[opt+1];
opt++;
break;
case 'd': debug=1;
break;
default:
fprintf(stderr,"Error: unrecognized option \"-%c\"\n",*(argv[opt]+1));
usage();
return -1;
}
}
else {
fprintf(stderr,"Invalid option: %s\n",argv[opt]);
return -1;
}
}
return 0;
}
void drawtimeout(hostmon *h)
{
int i,c;
unsigned long lt[TIMEOUT_SIZE];
const int average_samples=3;
long max=1,height;
cleararea(4,14,55,45);
i = h->h->timeoutptr + 1;
if (i>TIMEOUT_SIZE) i=0;
XSetForeground(display, NormalGC, GetColor("green"));
/* Create a local copy of the timeout table */
for (c=0;c<TIMEOUT_SIZE;c++) {
lt[c] = h->h->timeout[i++];
if (h->h->timeout[c] > max)
max = h->h->timeout[c];
if (i>TIMEOUT_SIZE) i=0;
}
/* Calculate 3-value averages */
for (c=0 ; c<TIMEOUT_SIZE - average_samples ; c++) {
for (i=0; i<(average_samples-1); i++) {
lt[c] += lt[c+i+1];
}
lt[c] /= average_samples;
}
printf("Max timeout = %lu\n",max);
for (c=0; c<TIMEOUT_SIZE - average_samples; c++) {
height = (45 * lt[c] / max) ;
printf("%ld ",height);
XDrawLine(display, wmgen.pixmap, NormalGC,4+c, 59 - height , 4+c, 59);
}
printf("\n");
}
int main(int argc,char *argv[])
{
XEvent Event;
#ifdef FORCECORE
struct rlimit corelim;
#endif
time_t last=0,now=0,lastchanged=0;
pinger p;
int failures=0,last_was_failure=0;
pinger_host *current_poll=0;
int polling=0;
time_t sleep_time;
int title_counter;
if (pinger_init(&p)!=0)
exit(-1);
(*soundfile)=0;
(*soundplayer)=0;
#ifdef FORCECORE
corelim.rlim_cur=corelim.rlim_max=RLIM_INFINITY;
if (setrlimit(RLIMIT_CORE, &corelim)) {
perror("unlimit core failed");
return -1;
}
#endif
if (parseargs(argc,argv)<0)
return -1;
/* Install debug signal handler */
signal(SIGUSR1, &debug_signal);
/* install child signal */
signal(SIGCHLD, &chld_signal);
bzero(leds,sizeof(leds));
wmnetmon_mask_bits=malloc(64*64);
createXBMfromXPM(wmnetmon_mask_bits, wmnetmon_xpm, wmnetmon_mask_width, wmnetmon_mask_height);
openXwindow(argc,argv,wmnetmon_xpm,wmnetmon_mask_bits,
wmnetmon_mask_width, wmnetmon_mask_height);
free( wmnetmon_mask_bits );
XSelectInput(display,win,MW_EVENTS);
XSelectInput(display,iconwin,MW_EVENTS);
init_leds(81,5, 69,5, 87,5, 75,5, 93,5, 99,5);
if (init_pinger_hosts(&p,&hosts,leds)!=0) return -1;
settitle("ALL SYS OK");
RedrawWindow();
(void)pinger_pollinit(&p);
/* (void)init_log(LOG_USES_SYSLOG,0);*/
/* do_log("Starting to log as PID %d",getpid());*/
while(1)
{
now=time(0);
if (last == 0 || (now-last) >= poll_time ) {
/* Start Polling */
last=now;
polling = 1;
}
if (polling) {
current_poll = pinger_pollnext(&p);
if (!current_poll)
polling=0;
}
title_counter = 0;
do {
sleep_time=need_to_scroll_title ? max_sleep_time / 10 : max_sleep_time;
do {
sleep_time=pinger_checkoneandsleep(&p, sleep_time );
} while (sleep_time > 0);
if (need_to_scroll_title) {
settitles(NULL,0,1);
title_counter++;
}
} while (need_to_scroll_title && title_counter<10);
if (((now % 10)==0) && lastchanged != now) {
hostmon *h;
ListItem iterator;
failures=0;
lastchanged=now;
for (iterator=hosts;iterator;iterator=iterator->next) {
int delay;
h = (hostmon*)ListData(iterator);
if (h->h == 0) continue ; /* fix core dump if host unresolved -- led will stay dark read */
if ((h->flags & FLAG_IGNORE_ON_MUTE) &&
(h->flags & FLAG_MUTED) ) {
leds[h->x][h->y]->current_status=LED_BLUE;
h->h->flags |= PINGER_DONTPING;
continue;
} else
h->h->flags &= ~PINGER_DONTPING;
if (!h->h->udpport)
delay = h->h->lastping - h->h->lastreply;
else delay= h->h->udpstatus ? poll_redtime+1 :0 ;
if (debug) fprintf(stderr,"delay for host %s is %d seconds\n",
h->hostname,
delay);
if ((!h->flags&FLAG_MUTED) && delay > poll_yellowtime && delay <= poll_redtime)
leds[h->x][h->y]->current_status=LED_YELLOW;
else
if ( delay > poll_redtime) {
if ( h->flags & FLAG_DONTWARN && !(h->flags&FLAG_MUTED)) {
leds[h->x][h->y]->current_status=LED_OFF;
} else
{
if (!(h->flags&FLAG_MUTED)) {
if (! ( ( leds[h->x][h->y]->current_status & LED_RED ) &&
( leds[h->x][h->y]->current_status & LED_FLASHING ) ) )
leds[h->x][h->y]->current_status=LED_RED | LED_FLASHING;
failures++;
} else {
if (!(h->flags & FLAG_IGNORE_ON_MUTE)) {
leds[h->x][h->y]->current_status=LED_PURPLE;
}
}
}
}
else if (! (h->flags & FLAG_IGNORE_ON_MUTE) &&
! (h->flags & FLAG_MUTED) )
leds[h->x][h->y]->current_status=LED_GREEN;
else
leds[h->x][h->y]->current_status=LED_BLUE;
}
if (failures && (*soundfile)) {
pid_t id;
/* play sound file - using fork() */
switch (id=fork()) {
case 0: {
char *tok[20]; /* max 20 params */
int i=1;
char *command;
command=strtok(soundplayer," ");
tok[0]=command;
while ( (tok[i]=strtok(NULL,"")) ) i++;
tok[i]=soundfile;
tok[i+1]=0;
if (debug) {
fprintf(stderr,"running %s with params ",
command);
for (i=0;tok[i];i++)
fprintf(stderr,"\"%s\"",tok[i]);
fprintf(stderr,"\n");
}
if (execv(command,tok)<0) {
perror("running execv");
exit(-1);
}
}
break;
case -1:
perror("error running external event - fork");
}
}
}
if (display_leds(0))
RedrawWindow();
if (last_was_failure && ! failures) {
/* Changed to OK state */
settitle("ALL SYS OK");
last_was_failure=0;
}
else if (failures && ! last_was_failure) {
/* System failed */
settitle("SYS FAILURE");
last_was_failure=1;
}
while (XPending(display))
{
XNextEvent(display,&Event);
switch(Event.type)
{
case Expose:
if(Event.xexpose.count == 0 )
RedrawWindow();
break;
case DestroyNotify:
XDestroyWindow(display, Root);
XFreeGC( display , NormalGC );
XCloseDisplay(display);
ListFree( &hosts );
{
ListItem iterator = (ListItem) p.hosts ;
for( ; iterator != (ListItem ) NULL ; iterator = iterator->next )
{
pinger_host * ph = (pinger_host *) ListData(iterator); /* iterator->data ; */
if ( (struct sockaddr_in *) NULL != ph->sock )
free( (void *) ph->sock );
if ( (struct sockaddr_in *) NULL != ph->tcpsock )
free( (void *) ph->tcpsock );
}
}
ListFree( &p.hosts );
exit(0);
case LeaveNotify:
settitle(0);
break;
case EnterNotify:
settemptitle("SELECT HOST");
break;
case MotionNotify:
/* xmotionevent */
checkledtouch(hosts,Event.xmotion.x, Event.xmotion.y);
break;
case ButtonPress:
/* Do we need ButtonRelease ? */
checkledbuttonpress(hosts,Event.xbutton);
break;
default:
break;
}
}
XFlush(display);
}
}
syntax highlighted by Code2HTML, v. 0.9.1