/*
    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