├── src ├── .gdbinit ├── .cvsignore ├── Makefile.am ├── debug.h ├── rrd.h ├── cfgtest.c ├── tv_macros.h ├── debug.c ├── conf.h ├── cfgparser2.l ├── apinger.h ├── apinger.conf ├── conf.c ├── icmp6.c ├── cfgparser1.y ├── rrd.c ├── icmp.c ├── main.c └── apinger.c ├── patches ├── README └── apinger-0.3-beep.patch ├── doc ├── .cvsignore ├── Makefile.am └── FAQ.xml ├── autogen.sh ├── AUTHORS ├── .cvsignore ├── Makefile.am ├── TODO ├── BUGS ├── .gitignore ├── README ├── NEWS ├── acinclude.m4 └── configure.ac /src/.gdbinit: -------------------------------------------------------------------------------- 1 | set args -f -d 2 | handle SIGPIPE nostop 3 | -------------------------------------------------------------------------------- /patches/README: -------------------------------------------------------------------------------- 1 | This directory is for patches from apinger users. 2 | Do not add it to EXTRA_DIST in the Makefile.am 3 | -------------------------------------------------------------------------------- /doc/.cvsignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | Makefile.in 3 | *.aux 4 | *.dvi 5 | *.fo 6 | *.log 7 | *.out 8 | *.html 9 | *.ps 10 | *.pdf 11 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | aclocal 3 | autoheader 4 | touch ChangeLog 5 | automake -a -c 6 | [ -s ChangeLog ] || rm -f ChangeLog 7 | autoconf 8 | ./configure CFLAGS="-ggdb -Wall" $* 9 | 10 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Author: 2 | Jacek Konieczny 3 | 4 | 5 | Big thanks to: 6 | 7 | Michael L. Hostbaek 8 | for great help with FreeBSD port 9 | 10 | -- 11 | -------------------------------------------------------------------------------- /src/.cvsignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.output 3 | *.tab.c 4 | *.tab.h 5 | *.yy.c 6 | .deps 7 | Makefile 8 | Makefile.in 9 | apinger 10 | cfgparser1.c 11 | cfgparser1.h 12 | cfgparser2.c 13 | cfgtest 14 | tags 15 | -------------------------------------------------------------------------------- /.cvsignore: -------------------------------------------------------------------------------- 1 | *.tar.gz 2 | COPYING 3 | INSTALL 4 | Makefile 5 | Makefile.in 6 | aclocal.m4 7 | config.* 8 | configure 9 | depcomp 10 | install-sh 11 | missing 12 | mkinstalldirs 13 | stamp-h* 14 | tags 15 | ylwrap 16 | autom4te.cache 17 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | EXTRA_DIST = autogen.sh TODO BUGS 3 | 4 | SUBDIRS = src doc 5 | 6 | .PHONY: ChangeLog 7 | 8 | ChangeLog: 9 | test -d .git && make cl-stamp || : 10 | 11 | cl-stamp: .git 12 | git log > ChangeLog 13 | touch cl-stamp 14 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - fix bugs 2 | - add more configurable messages 3 | - improve config file parser, so multi-line strings with control characters may be used 4 | - dependency between targets (eg. don't report "down" alarm on server, when router is down) 5 | 6 | -- 7 | -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | 2 | - sometimes crashes on exit (segmentation or memory fault) 3 | - on high load/short intervals there are errors in loss computation. 4 | When such error is detected apinger will abort() 5 | - code is ugly, this is probably unfixable without total rewrite 6 | 7 | -- 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .deps/ 3 | COPYING 4 | ChangeLog 5 | INSTALL 6 | Makefile 7 | Makefile.in 8 | aclocal.m4 9 | apinger-*.tar.gz 10 | autom4te.cache/ 11 | cl-stamp 12 | config.h 13 | config.h.in 14 | config.log 15 | config.status 16 | configure 17 | depcomp 18 | doc/FAQ.html 19 | install-sh 20 | missing 21 | src/apinger 22 | src/cfgparser1.c 23 | src/cfgparser1.h 24 | src/cfgparser1.output 25 | src/cfgparser2.c 26 | stamp-h1 27 | ylwrap 28 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | EXTRA_DIST = cfgparser1.h apinger.conf 3 | EXTRA_PROGRAMS = cfgtest 4 | sbin_PROGRAMS = apinger 5 | 6 | apinger_SOURCES = \ 7 | apinger.c \ 8 | apinger.h \ 9 | cfgparser1.y \ 10 | cfgparser2.l \ 11 | conf.c \ 12 | conf.h \ 13 | debug.h \ 14 | icmp.c \ 15 | icmp6.c \ 16 | main.c \ 17 | debug.c \ 18 | rrd.c \ 19 | rrd.h \ 20 | tv_macros.h 21 | 22 | 23 | cfgtest_SOURCES = \ 24 | cfgparser1.y \ 25 | cfgparser2.l \ 26 | cfgtest.c \ 27 | conf.c 28 | 29 | AM_CFLAGS=-D"SYSCONFDIR=\"$(sysconfdir)\"" 30 | 31 | AM_YFLAGS=-d 32 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | dist_noinst_DATA=FAQ.html 3 | #noinst_DATA=FAQ.pdf FAQ.ps 4 | 5 | EXTRA_DIST=FAQ.xml 6 | CLEANFILES=*.aux *.dvi *.fo *.log *.out 7 | MAINTAINERCLEANFILES=FAQ.html FAQ.pdf FAQ.ps 8 | STYLESHEET_DIR=/usr/share/sgml/docbook/xsl-stylesheets 9 | HTML_STYLESHEET=$(STYLESHEET_DIR)/html/docbook.xsl 10 | FO_STYLESHEET=$(STYLESHEET_DIR)/fo/docbook.xsl 11 | 12 | SUFFIXES=.xml .fo .dvi .html .ps .pdf 13 | .PHONY: validate 14 | 15 | .fo.pdf: 16 | pdfxmltex $< 17 | 18 | .fo.dvi: 19 | xmltex $< 20 | 21 | .dvi.ps: 22 | dvips -o $@ $< 23 | 24 | .xml.html: 25 | xsltproc -o $@ $(HTML_STYLESHEET) $< 26 | 27 | .xml.fo: 28 | xsltproc -o $@ $(FO_STYLESHEET) $< 29 | 30 | validate: 31 | xmllint --valid --noout *.xml 32 | -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #ifndef debug_h 21 | #define debug_h 22 | 23 | void logit(const char *format, ...); 24 | void debug(const char *format, ...); 25 | void myperror(const char *prefix); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /src/rrd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #ifndef rrd_h 21 | #define rrd_h 22 | 23 | void rrd_create(void); 24 | void rrd_update(void); 25 | int rrd_print_cgi(const char *graph_dir,const char *graph_location); 26 | void rrd_close(void); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | Alarm Pinger v. 0.6.1 3 | (c) 2002 Jacek Konieczny 4 | ============================================ 5 | 6 | Alarm Pinger (apinger) is a little tool which monitors various IP devices by 7 | simple ICMP echo requests. There are various other tools, that can do this, 8 | but most of them are shell or perl scripts, spawning many processes, thus much 9 | CPU-expensive, especially when one wants continuous monitoring and fast 10 | response on target failure. Alarm Pinger is a single program written in C, so 11 | it doesn't need much CPU power even when monitoring many targets with frequent 12 | probes. Alarm Pinger supports both IPv4 and IPv6. The code have been tested 13 | on Linux and FreeBSD. 14 | 15 | Alarm Pinger is quite configurable via $sysconfdir/apinger.conf file. The 16 | configuration file contains definitions for alarms, targets and various 17 | parameters. See the apinger.conf in the src/ directory for description. 18 | 19 | apinger needs root privileges to start (to create raw sockets), but will drop 20 | them before sending or receiving any packets. 21 | 22 | Copying and Warranty 23 | -------------------- 24 | This program is free software; you can redistribute it and/or modify it under 25 | the terms of the GNU General Public License version 2 as published by the Free 26 | Software Foundation. 27 | 28 | This program is distributed in the hope that it will be useful, but WITHOUT ANY 29 | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 30 | A PARTICULAR PURPOSE. See the GNU General Public License for more details. 31 | 32 | You should have received a copy of the GNU General Public License along with 33 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple 34 | Place, Suite 330, Boston, MA 02111-1307 USA 35 | 36 | -- 37 | -------------------------------------------------------------------------------- /src/cfgtest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #ifdef HAVE_STDLIB_H 21 | #include 22 | #endif 23 | #include "conf.h" 24 | 25 | struct config default_config={ 26 | NULL,NULL,NULL, /* pool, alarms, targets */ 27 | { /* alarm defaults */ 28 | AL_NONE, /* type */ 29 | "default", /* name */ 30 | "root", /* mailto */ 31 | "nobody", /* mailfrom */ 32 | NULL, /* mailenvfrom */ 33 | {}, /* params */ 34 | NULL /* next */ 35 | }, 36 | { /* target defaults */ 37 | "default", /* name */ 38 | "", /* description */ 39 | 1000, /* interval */ 40 | 20, /* avg_delay_samples */ 41 | 5, /* avg_loss_delay_samples */ 42 | 50, /* avg_loss_samples */ 43 | 44 | NULL,NULL /* alarms, next */ 45 | }, 46 | 0, /* debug */ 47 | "nobody", /* user */ 48 | NULL, /* group */ 49 | "/var/run/apinger.pid" /* pid file */ 50 | }; 51 | 52 | int foreground=1; 53 | 54 | 55 | int main(int argc,char *argv[]){ 56 | int ret; 57 | 58 | ret=load_config("/etc/apinger.conf"); 59 | return ret; 60 | } 61 | -------------------------------------------------------------------------------- /src/tv_macros.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef timerisset 3 | # define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) 4 | #endif 5 | #ifndef timerclear 6 | # define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) 7 | #endif 8 | #ifndef timercmp 9 | # define timercmp(a, b, CMP) \ 10 | (((a)->tv_sec == (b)->tv_sec) ? \ 11 | ((a)->tv_usec CMP (b)->tv_usec) : \ 12 | ((a)->tv_sec CMP (b)->tv_sec)) 13 | #endif 14 | #ifndef timeradd 15 | # define timeradd(a, b, result) \ 16 | do { \ 17 | (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ 18 | (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ 19 | if ((result)->tv_usec >= 1000000) \ 20 | { \ 21 | ++(result)->tv_sec; \ 22 | (result)->tv_usec -= 1000000; \ 23 | } \ 24 | } while (0) 25 | #endif 26 | #ifndef timersub 27 | # define timersub(a, b, result) \ 28 | do { \ 29 | (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ 30 | (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ 31 | if ((result)->tv_usec < 0) { \ 32 | --(result)->tv_sec; \ 33 | (result)->tv_usec += 1000000; \ 34 | } \ 35 | } while (0) 36 | #endif 37 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #include "config.h" 21 | #include "apinger.h" 22 | 23 | #include 24 | #ifdef HAVE_STDLIB_H 25 | # include 26 | #endif 27 | #ifdef HAVE_SYSLOG_H 28 | # include 29 | #endif 30 | #ifdef HAVE_ERRNO_H 31 | # include 32 | #endif 33 | #ifdef HAVE_STRING_H 34 | # include 35 | #endif 36 | #ifdef HAVE_STDARG_H 37 | # include 38 | #endif 39 | 40 | 41 | void logit(const char *format, ...){ 42 | va_list args; 43 | 44 | va_start(args, format); 45 | if (foreground){ 46 | time_t t=time(NULL); 47 | static char buf[100]; 48 | strftime(buf,100,"%b %d %H:%M:%S",localtime(&t)); 49 | fprintf(stderr,"[%s] ",buf); 50 | vfprintf(stderr, format, args); 51 | fprintf(stderr,"\n"); 52 | } 53 | else{ 54 | vsyslog(LOG_ERR,format,args); 55 | } 56 | va_end(args); 57 | } 58 | 59 | void debug(const char *format, ...){ 60 | va_list args; 61 | 62 | if (!config->debug) return; 63 | 64 | va_start(args, format); 65 | if (foreground){ 66 | time_t t=time(NULL); 67 | static char buf[100]; 68 | strftime(buf,100,"%b %d %H:%M:%S",localtime(&t)); 69 | fprintf(stderr,"[%s] ",buf); 70 | vfprintf(stderr, format, args); 71 | fprintf(stderr,"\n"); 72 | } 73 | else{ 74 | vsyslog(LOG_DEBUG,format,args); 75 | } 76 | va_end(args); 77 | } 78 | 79 | void myperror(const char *prefix){ 80 | if (foreground) 81 | perror(prefix); 82 | else 83 | syslog(LOG_ERR,"%s: %s",prefix,strerror(errno)); 84 | } 85 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | * Version 0.6.1 2 | 3 | Main changes from 0.6: 4 | 5 | - target specific alarm list may replace default alarm list 6 | instead of being appended. Use "alarms override" directive 7 | to do this. 8 | 9 | * Version 0.6 10 | 11 | Main changes from 0.5: 12 | 13 | - apinger should properly kill child processes now 14 | 15 | - some (poor) documentation: FAQ 16 | 17 | - duplicate targets in combined reports removed 18 | 19 | - "forked receiver" disabled by default 20 | 21 | - proper use of pid_file and mailer configuration options 22 | 23 | - automatic check for loss measurements errors (caused by a bug 24 | which I was not able to fix). Apinger will abort, when the 25 | error is found! 26 | 27 | - IPv6 support made optional (enabled by default) 28 | 29 | - better Message-Id and References headers in report messages 30 | 31 | 32 | * Version 0.5 33 | 34 | Main changes from 0.4.1: 35 | - statistics gathering using RRDTool. May be used for making 36 | graphs of packet loss and delays. 37 | 38 | - much more precise delay measurement 39 | 40 | * Version 0.4.1 41 | 42 | Main changes from 0.4: 43 | - some useless and nonportable debugging code removed. 44 | Fixes build on FreeBSD. 45 | 46 | * Version 0.4 47 | 48 | Main changes from 0.3: 49 | 50 | - bugfixes 51 | - ability to merge reports of multiple alarms fired in some 52 | configured interval ('combine' configuration directive) 53 | - possible memory-leaks removed 54 | - ability to repeat reports while alarm is on 55 | 56 | * Version 0.3 57 | 58 | Main changes from 0.2: 59 | 60 | - file with status of targets may be written 61 | - configurable mail subject 62 | - external commands may be executed when alarm if fired/canceled 63 | - mailer (with custom options) may be configured 64 | - Message-Id and References headers are generated for mail messages 65 | so "ALARM" and "alarm-canceled" messages may be paired together 66 | by threading MUA 67 | 68 | * Version 0.2 69 | 70 | Main changes from 0.1: 71 | 72 | - portability fixes (it compiles on FreeBSD now). 73 | - fixed bug with "down" alarms being fired on startup 74 | - sysconfdir set by configure script is used instead hardcoded 75 | /etc directory for config file 76 | 77 | * Version 0.1 78 | 79 | This is the initial release 80 | 81 | -- 82 | -------------------------------------------------------------------------------- /acinclude.m4: -------------------------------------------------------------------------------- 1 | 2 | AC_PREREQ([2.52]) 3 | 4 | # JK_LINUX_FILTER 5 | # Test for Linux socket filtering 6 | # ----------------------------------------------------- 7 | AC_DEFUN([JK_LINUX_FILTER], 8 | [AC_CHECK_HEADERS([linux/types.h],HAS_LINUX_TYPES_H=yes) 9 | if test "x$HAS_LINUX_TYPES_H" = "xyes" ; then 10 | AC_CHECK_HEADERS([linux/filter.h],[],[],[ 11 | #include 12 | #include 13 | #include 14 | ]) 15 | fi 16 | ]) 17 | 18 | # JK_AP_INET 19 | # Test for IPv4, ICMP services needed for apinger 20 | # ----------------------------------------------------- 21 | AC_DEFUN([JK_AP_INET], 22 | [ AC_CHECK_HEADERS([sys/types.h],[jk_inet_includes="$jk_inet_includes 23 | #include "]) 24 | AC_CHECK_HEADERS([netinet/in_systm.h],[jk_inet_includes="$jk_inet_includes 25 | #include "],,[$jk_inet_includes]) 26 | AC_CHECK_HEADERS([netinet/in.h],[jk_inet_includes="$jk_inet_includes 27 | #include "],,[$jk_inet_includes]) 28 | AC_CHECK_HEADERS([netinet/ip.h],[jk_icmp_includes="$jk_icmp_includes 29 | #include "],,[$jk_inet_includes]) 30 | AC_CHECK_HEADERS([netinet/ip_icmp.h],[jk_icmp_includes="$jk_icmp_includes 31 | #include "],,[$jk_inet_includes 32 | $jk_icmp_includes]) 33 | 34 | AC_CHECK_TYPES([struct sockaddr_in],,AC_MSG_ERROR(some needed type is missing),[$jk_inet_includes]) 35 | 36 | AC_CHECK_MEMBERS([struct ip.ip_hl],[], 37 | AC_MSG_ERROR(struct ip not defined or not compatible), 38 | [$jk_inet_includes 39 | $jk_icmp_includes]) 40 | 41 | AC_CHECK_MEMBERS([struct icmp.icmp_type, struct icmp.icmp_code,struct icmp.icmp_cksum, struct icmp.icmp_seq,struct icmp.icmp_id],[], 42 | AC_MSG_ERROR(struct icmp not defined or not compatible), 43 | [$jk_inet_includes 44 | $jk_icmp_includes]) 45 | ]) 46 | 47 | # JK_AP_INET6 48 | # Test for IPv6, ICMP6 services needed for apinger 49 | # ----------------------------------------------------- 50 | AC_DEFUN([JK_AP_INET6], 51 | [AC_REQUIRE([JK_AP_INET])dnl 52 | AC_CHECK_HEADERS([netinet/ip6.h],[jk_icmp6_includes="$jk_icmp6_includes 53 | #include "],,[$jk_inet_includes]) 54 | AC_CHECK_HEADERS([netinet/icmp6.h],[jk_icmp6_includes="$jk_icmp6_includes 55 | #include "],,[$jk_inet_includes 56 | $jk_icmp6_includes]) 57 | 58 | AC_CHECK_TYPES([struct sockaddr_in6],,[HAVE_IPV6=no],[$jk_inet_includes]) 59 | 60 | if test "x$HAVE_IPV6" != "xno"; then 61 | AC_CHECK_MEMBERS([struct icmp6_hdr.icmp6_type,struct icmp6_hdr.icmp6_code, 62 | struct icmp6_hdr.icmp6_cksum, struct icmp6_hdr.icmp6_seq, 63 | struct icmp6_hdr.icmp6_id],,[HAVE_IPV6=no],[$jk_inet_includes 64 | $jk_icmp6_includes]) 65 | fi 66 | 67 | if test "x$HAVE_IPV6" = "xno"; then 68 | AC_MSG_WARN([No IPv6 support in apinger for this system]) 69 | else 70 | AC_DEFINE(HAVE_IPV6,1,[Define for IPv6 support]) 71 | fi 72 | 73 | ]) 74 | 75 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | 2 | # Process this file with autoconf to produce a configure script. 3 | AC_INIT([apinger], [0.6.1], [jajcus@jajcus.net]) 4 | AC_CONFIG_SRCDIR([src/apinger.c]) 5 | AM_INIT_AUTOMAKE 6 | AM_CONFIG_HEADER([config.h]) 7 | 8 | # Checks for programs. 9 | AC_PROG_YACC 10 | AC_PROG_CC 11 | AM_PROG_LEX 12 | 13 | # Checks for libraries. 14 | AC_ARG_WITH(efence,AC_HELP_STRING([--with-efence],[use efence (default is NO)]),[ 15 | if test "x$with_efence" != "xno" ; then 16 | AC_CHECK_LIB([efence], [malloc]) 17 | fi 18 | ]) 19 | 20 | # Checks for header files. 21 | AC_HEADER_STDC 22 | AC_HEADER_SYS_WAIT 23 | AC_CHECK_HEADERS([arpa/inet.h errno.h malloc.h \ 24 | stddef.h stdlib.h string.h sys/socket.h \ 25 | sys/time.h syslog.h unistd.h time.h \ 26 | assert.h sys/poll.h signal.h pwd.h grp.h stdarg.h\ 27 | limits.h sys/wait.h sched.h sys/ioctl.h sys/uio.h]) 28 | AC_HEADER_TIME 29 | 30 | JK_LINUX_FILTER 31 | JK_AP_INET 32 | 33 | AC_ARG_ENABLE(ipv6,[AC_HELP_STRING([--disable-ipv6], 34 | [Disable IPv6 support.])], 35 | [],[enable_ipv6=yes]) 36 | if test "x$enable_ipv6" = "xyes" ; then 37 | JK_AP_INET6 38 | fi 39 | 40 | # Checks for typedefs, structures, and compiler characteristics. 41 | AC_C_CONST 42 | AC_TYPE_PID_T 43 | AC_TYPE_SIZE_T 44 | AC_HEADER_TIME 45 | AC_STRUCT_TM 46 | 47 | # Checks for library functions. 48 | AC_FUNC_FORK 49 | AC_FUNC_MALLOC 50 | AC_TYPE_SIGNAL 51 | AC_FUNC_STRFTIME 52 | AC_CHECK_FUNCS([gettimeofday inet_ntoa memset socket \ 53 | strdup strerror strpbrk poll vsyslog time popen setvbuf access], 54 | [],AC_MSG_ERROR(some needed function is missing)) 55 | 56 | AC_CHECK_FUNCS([sched_yield recvmsg]) 57 | 58 | AC_ARG_ENABLE(forked-receiver,[AC_HELP_STRING([--enable-forked-receiver], 59 | [Create subprocess for receiving pings.])], 60 | [],[enable_forked_receiver=no]) 61 | 62 | if test "x$enable_forked_receiver" = "xyes" ; then 63 | AC_DEFINE(FORKED_RECEIVER,[],[Define to enable receiver subprocess]) 64 | dnl else 65 | dnl AC_MSG_CHECKING([if PIPE_BUF is big enough]) 66 | dnl if test "x$enable_forked_receiver" != "xno" ; then 67 | dnl AC_TRY_RUN([ 68 | dnl #ifdef HAVE_SYS_TYPES_H 69 | dnl # include 70 | dnl #endif 71 | dnl #ifdef HAVE_LIMITS_H 72 | dnl # include 73 | dnl #endif 74 | dnl 75 | dnl int main(int argc,char **argv){ 76 | dnl 77 | dnl if (PIPE_BUF<1024) return 1; 78 | dnl return 0; 79 | dnl }], 80 | dnl [AC_DEFINE(FORKED_RECEIVER,[1],[Define to enable receiver subprocess]) 81 | dnl AC_MSG_RESULT([yes])],[AC_MSG_RESULT([no]) 82 | dnl AC_MSG_WARN([Receiver subprocess will not be used.])]) 83 | dnl fi 84 | fi 85 | 86 | AC_ARG_WITH(rrdtool,[AC_HELP_STRING([--with-rrdtool=path],[Location of rrdtool program])], 87 | [ RRDTOOL="$withval" ],[ AC_PATH_PROG([RRDTOOL],[rrdtool],[rrdtool]) ]) 88 | AC_ARG_WITH(rrdcgi,[AC_HELP_STRING([--with-rrdcgi=path],[Location of rrdcgi program])], 89 | [RRDCGI="$withval"],[AC_PATH_PROG([RRDCGI],[rrdcgi],[/usr/bin/rrdcgi])]) 90 | 91 | AC_DEFINE_UNQUOTED(RRDTOOL,"$RRDTOOL",[Set to the path of rrdtool program]) 92 | AC_DEFINE_UNQUOTED(RRDCGI,"$RRDCGI",[Set to the path of rrdcgi program]) 93 | 94 | AC_CONFIG_FILES([Makefile src/Makefile doc/Makefile]) 95 | AC_OUTPUT 96 | -------------------------------------------------------------------------------- /src/conf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #ifndef conf_h 21 | #define conf_h 22 | 23 | struct pool_item { 24 | struct pool_item *next; 25 | }; 26 | 27 | void *pool_malloc(struct pool_item **pool,size_t size); 28 | char *pool_strdup(struct pool_item **pool,const char *str); 29 | void pool_free(struct pool_item **pool,void *ptr); 30 | void pool_clear(struct pool_item **pool); 31 | 32 | #define PNEW(pool,type,size) ((type *)pool_malloc(&pool,sizeof(type)*size)) 33 | 34 | enum alarm_type { 35 | AL_NONE=-1, 36 | AL_DOWN=0, 37 | AL_DELAY, 38 | AL_LOSS, 39 | NR_ALARMS 40 | }; 41 | 42 | struct alarm_cfg { 43 | enum alarm_type type; 44 | char *name; 45 | char *mailto; 46 | char *mailfrom; 47 | char *mailenvfrom; 48 | char *mailsubject; 49 | char *command_on; 50 | char *command_off; 51 | char *pipe_on; 52 | char *pipe_off; 53 | int combine_interval; 54 | int repeat_interval; 55 | int repeat_max; 56 | union { 57 | int val; 58 | struct { 59 | int low; 60 | int high; 61 | }lh; 62 | }p; 63 | struct alarm_cfg *next; 64 | }; 65 | 66 | struct alarm_list { 67 | struct alarm_cfg *alarm; 68 | struct alarm_list *next; 69 | }; 70 | 71 | struct target_cfg { 72 | char *name; 73 | char *description; 74 | int interval; 75 | int avg_delay_samples; 76 | int avg_loss_delay_samples; 77 | int avg_loss_samples; 78 | char *rrd_filename; 79 | 80 | struct alarm_list *alarms; 81 | int alarms_override; 82 | struct target_cfg *next; 83 | }; 84 | 85 | struct config { 86 | struct pool_item *pool; 87 | struct alarm_cfg *alarms; 88 | struct target_cfg *targets; 89 | struct alarm_cfg alarm_defaults; 90 | struct target_cfg target_defaults; 91 | int rrd_interval; 92 | int debug; 93 | char *user; 94 | char *group; 95 | char *mailer; 96 | char *pid_file; 97 | char *status_file; 98 | int status_interval; 99 | char *timestamp_format; 100 | }; 101 | 102 | extern struct config cur_config,default_config; 103 | extern struct config * config; 104 | extern struct alarm_cfg *cur_alarm; 105 | extern struct target_cfg *cur_target; 106 | 107 | struct alarm_cfg * make_alarm(); 108 | struct target_cfg * make_target(); 109 | void add_alarm(enum alarm_type type); 110 | void add_target(void); 111 | struct alarm_list *alarm2list(const char *aname,struct alarm_list *list); 112 | 113 | int load_config(const char *filename); 114 | void free_config(void); 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /doc/FAQ.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 |
8 | 9 | 10 | Jacek 11 | Konieczny 12 | jajcus@jajcus.net 13 | 14 | Frequently Asked Questions about Alarm Pinger 15 | 16 | 17 | 18 | 19 | 20 | What is Alarm Pinger? 21 | 22 | 23 | Alarm pinger is a tool for network monitoring. It test 24 | periodically reachability of one of more network devices and the 25 | quality of their network connections. 26 | 27 | 28 | 29 | 30 | Can Alarm Pinger be user to monitor services? 31 | 32 | 33 | No. The only way Alarm Pinger monitor things is sending ICMP 34 | echo-requests and analyzing ICMP echo-replies. So only host 35 | reachability is tested. 36 | 37 | 38 | 39 | 40 | Can Alarm Pinger use TCP/UDP/anything 41 | instead of ICMP for pinging? 42 | 43 | 44 | No. It just do plain ICMP echo queries. 45 | 46 | 47 | 48 | 49 | 50 | Why can't I use host names for target names? 51 | 52 | 53 | Because it would be very hard to interpret results if the 54 | host name resolves to more than one IP address. 55 | 56 | 57 | 58 | 59 | 60 | I don't want any messages mailed to me. How can I turn them off? 61 | 62 | 63 | Just comment out mailto and 64 | mailfrom lines in 65 | apinger.conf file. 66 | 67 | 68 | 69 | 70 | 71 | How can I graph apinger measurements? 72 | 73 | 74 | You will need rrdtool installed on 75 | the system, where apinger is running. Make 76 | sure apinger will find it. You may pass the 77 | path to the configure script before 78 | apinger compilation. 79 | To make apinger actually store the 80 | measurements, you need to set rrd interval global 81 | configuration option to some time interval and rrd 82 | file target configuration option to path of the database which 83 | will be written by apinger. The database should be 84 | writable by user set with user option. 85 | To graph the stored measurements you can use 86 | rrdcgi script generated by 87 | apinger with option followed by 88 | path to a directory where the graphs will be stored. Usually you will 89 | also need to pass followed with location of those 90 | graphs as seen on the HTTP server. 91 | 92 | 93 | 94 | 95 |
96 | 97 | 100 | -------------------------------------------------------------------------------- /src/cfgparser2.l: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | 21 | %{ 22 | #include 23 | #include "cfgparser1.h" 24 | #include "debug.h" 25 | 26 | #define LOC yylloc.first_line=yylloc.last_line; yylloc.first_column=yylloc.last_column 27 | #define LOCINC yylloc.last_column+=yyleng 28 | extern YYLTYPE yylloc; 29 | 30 | %} 31 | %option noyywrap 32 | 33 | 34 | DIGIT [0-9] 35 | 36 | %% 37 | 38 | {DIGIT}+ { LOC; LOCINC; yylval.i=atoi(yytext); return INTEGER; } 39 | 40 | {DIGIT}+("."{DIGIT}+)?(([um]?s)|m|h) { 41 | double f; 42 | double mn=1; 43 | 44 | LOC; 45 | LOCINC; 46 | 47 | if (yyleng>1 && yytext[yyleng-1]=='h'){ 48 | yytext[yyleng-1]='\000'; 49 | mn=60*60*1000; 50 | } 51 | else if (yyleng>1 && yytext[yyleng-1]=='m'){ 52 | yytext[yyleng-1]='\000'; 53 | mn=60*1000; 54 | } 55 | else if (yyleng>1 && yytext[yyleng-1]=='s'){ 56 | yytext[yyleng-1]='\000'; 57 | mn=1000; 58 | if (yyleng>2 && yytext[yyleng-2]=='m'){ 59 | yytext[yyleng-1]='\000'; 60 | mn=1; 61 | } 62 | else if (yyleng>2 && yytext[yyleng-2]=='u'){ 63 | yytext[yyleng-1]='\000'; 64 | mn=0.001; 65 | } 66 | } 67 | f=atof(yytext)*mn; 68 | yylval.i=f; return TIME; 69 | } 70 | 71 | alarm { LOC; LOCINC; return ALARM; } 72 | alarms { LOC; LOCINC; return ALARMS; } 73 | avg_delay_samples { LOC; LOCINC; return AVG_DELAY_SAMPLES; } 74 | avg_loss_delay_samples { LOC; LOCINC; return AVG_LOSS_DELAY_SAMPLES; } 75 | avg_loss_samples { LOC; LOCINC; return AVG_LOSS_SAMPLES; } 76 | combine { LOC; LOCINC; return COMBINE; } 77 | command { LOC; LOCINC; return COMMAND; } 78 | debug { LOC; LOCINC; return DEBUG; } 79 | default { LOC; LOCINC; return DEFAULT; } 80 | delay { LOC; LOCINC; return DELAY; } 81 | delay_high { LOC; LOCINC; return DELAY_HIGH; } 82 | delay_low { LOC; LOCINC; return DELAY_LOW; } 83 | description { LOC; LOCINC; return DESCRIPTION; } 84 | down { LOC; LOCINC; return DOWN; } 85 | false { LOC; LOCINC; return FALSE; } 86 | file { LOC; LOCINC; return FILE_; } 87 | group { LOC; LOCINC; return GROUP; } 88 | interval { LOC; LOCINC; return INTERVAL; } 89 | loss { LOC; LOCINC; return LOSS; } 90 | mailenvfrom { LOC; LOCINC; return MAILENVFROM; } 91 | mailer { LOC; LOCINC; return MAILER; } 92 | mailfrom { LOC; LOCINC; return MAILFROM; } 93 | mailsubject { LOC; LOCINC; return MAILSUBJECT; } 94 | mailto { LOC; LOCINC; return MAILTO; } 95 | no { LOC; LOCINC; return NO; } 96 | off { LOC; LOCINC; return OFF; } 97 | on { LOC; LOCINC; return ON; } 98 | override { LOC; LOCINC; return OVERRIDE; } 99 | percent_high { LOC; LOCINC; return PERCENT_HIGH; } 100 | percent_low { LOC; LOCINC; return PERCENT_LOW; } 101 | pid_file { LOC; LOCINC; return PID_FILE; } 102 | pipe { LOC; LOCINC; return PIPE; } 103 | repeat { LOC; LOCINC; return REPEAT; } 104 | rrd { LOC; LOCINC; return RRD; } 105 | status { LOC; LOCINC; return STATUS; } 106 | target { LOC; LOCINC; return TARGET; } 107 | time { LOC; LOCINC; return TIME_; } 108 | timestamp_format { LOC; LOCINC; return TIMESTAMP_FORMAT; } 109 | true { LOC; LOCINC; return TRUE; } 110 | user { LOC; LOCINC; return USER; } 111 | yes { LOC; LOCINC; return YES; } 112 | 113 | \"[^"\n]*\" { LOC; LOCINC; yytext[yyleng-1]='\000'; yylval.s=yytext+1; return STRING; } 114 | 115 | [{};,] { LOC; LOCINC; return yytext[0]; } 116 | \n { LOC; yylloc.last_line++; yylloc.last_column=0; return '\n'; } 117 | 118 | "//"[^\n]* { LOC; LOCINC; } 119 | "#"[^\n]* { LOC; LOCINC; } 120 | 121 | [ \t]+ { LOC; LOCINC; } 122 | 123 | . { LOC; LOCINC; yylval.s=yytext; 124 | logit("Unexpected character: '%c'", yytext[0]); 125 | return ERROR; 126 | } 127 | 128 | %% 129 | 130 | void *p=yyunput; 131 | /* 132 | vi: ft=lex 133 | */ 134 | -------------------------------------------------------------------------------- /src/apinger.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #ifndef apinger_h 21 | #define apinger_h 22 | 23 | #define CONFIG SYSCONFDIR "/apinger.conf" 24 | 25 | #if TIME_WITH_SYS_TIME 26 | # include 27 | # include 28 | #else 29 | # ifdef HAVE_SYS_TIME_H 30 | # include 31 | # else 32 | # include 33 | # endif 34 | #endif 35 | #ifdef HAVE_SYS_TYPES_H 36 | # include 37 | #endif 38 | #ifdef HAVE_SYS_SOCKET_H 39 | # include 40 | #endif 41 | #ifdef HAVE_NETINET_IN_SYSTM_H 42 | # include 43 | #endif 44 | #ifdef HAVE_NETINET_IN_H 45 | # include 46 | #endif 47 | #include "conf.h" 48 | 49 | union addr { 50 | struct sockaddr addr; 51 | struct sockaddr_in addr4; 52 | struct sockaddr_in6 addr6; 53 | }; 54 | 55 | struct active_alarm_list { 56 | struct alarm_cfg *alarm; 57 | struct active_alarm_list *next; 58 | char *msgid; 59 | int num_repeats; 60 | struct timeval next_repeat; 61 | }; 62 | 63 | 64 | struct target { 65 | char *name; /* name (IP address as string) */ 66 | char *description; /* description */ 67 | 68 | union addr addr; /* target address */ 69 | 70 | char *queue; /* 71 | contains info about recently sent packets 72 | "1" means it was received */ 73 | int last_sent; /* sequence number of the last ping sent */ 74 | int last_received; /* sequence number of the last ping received */ 75 | struct timeval last_received_tv; /* timestamp of the last ping received */ 76 | int received; /* number of packets received */ 77 | int upreceived; /* number of packets received during recent target uptime */ 78 | int upsent; /* number of packets send during recent target uptime */ 79 | int recently_lost; /* number of packets lost between 80 | last_sent-200 to last_sent-100 81 | for avg. lost computation */ 82 | double *rbuf; /* bufor of received pings 83 | (for avarage delay computation) */ 84 | double delay_sum; 85 | 86 | struct timeval next_probe; /* time when next probe is scheduled */ 87 | 88 | struct active_alarm_list *active_alarms; 89 | struct target_cfg *config; 90 | 91 | struct target *next; 92 | }; 93 | 94 | #define AVG_DELAY_KNOWN(t) (t->upsent >= t->config->avg_delay_samples) 95 | #define AVG_DELAY(t) ((t->received>=t->config->avg_delay_samples)?(t->delay_sum/t->config->avg_delay_samples):((t->received>0)?(t->delay_sum/t->received):(0))) 96 | 97 | #define AVG_LOSS_KNOWN(t) (t->upsent > t->config->avg_loss_delay_samples+t->config->avg_loss_samples) 98 | #define AVG_LOSS(t) (100*((double)t->recently_lost)/t->config->avg_loss_samples) 99 | 100 | struct trace_info { 101 | struct timeval timestamp; 102 | int seq; 103 | void *target_id; 104 | }; 105 | 106 | #ifdef FORKED_RECEIVER 107 | struct piped_info { 108 | struct trace_info ti; 109 | int icmp_seq; 110 | struct timeval recv_timestamp; 111 | }; 112 | #endif 113 | 114 | struct target *targets; 115 | 116 | extern int foreground; 117 | extern char *config_file; 118 | 119 | extern int icmp_sock; 120 | extern int icmp6_sock; 121 | extern int ident; 122 | 123 | extern struct timeval next_probe; 124 | 125 | int make_icmp_socket(void); 126 | void recv_icmp(void); 127 | void send_icmp_probe(struct target *t,int seq); 128 | 129 | int make_icmp6_socket(void); 130 | void recv_icmp6(void); 131 | void send_icmp6_probe(struct target *t,int seq); 132 | 133 | #ifdef FORKED_RECEIVER 134 | void pipe_reply(struct timeval time_recv,int seq,struct trace_info *ti); 135 | #endif 136 | void analyze_reply(struct timeval time_recv,int seq,struct trace_info *ti); 137 | void main_loop(void); 138 | 139 | const char * subst_macros(const char *string,struct target *t,struct alarm_cfg *a,int on); 140 | 141 | extern volatile int interrupted_by; 142 | extern volatile int reload_request; 143 | extern volatile int status_request; 144 | extern volatile int sigpipe_received; 145 | 146 | #define NEW(type,size) ((type *)malloc(sizeof(type)*size)) 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /src/apinger.conf: -------------------------------------------------------------------------------- 1 | # Example config file for Alarm Pinger 2 | 3 | ######################################## 4 | ## General options 5 | 6 | ## Should debug messages be generated? (default: off) 7 | #debug on 8 | 9 | ## User and group the pinger should run as 10 | user "nobody" 11 | group "nobody" 12 | 13 | ## Mailer to use (default: "/usr/lib/sendmail -t") 14 | #mailer "/var/qmail/bin/qmail-inject" 15 | 16 | ## Location of the pid-file (default: "/var/run/apinger.pid") 17 | #pid_file "/tmp/apinger.pid" 18 | 19 | ## Format of timestamp (%s macro) (default: "%b %d %H:%M:%S") 20 | #timestamp_format "%Y%m%d%H%M%S" 21 | 22 | ######################################## 23 | ## Status output parameters 24 | 25 | #status { 26 | # ## File where the status information whould be written to 27 | # file "/tmp/apinger.status" 28 | # 29 | # ## Interval between file updates 30 | # ## when 0 or not set, file is written only when SIGUSR1 is received 31 | # interval 5m 32 | #} 33 | 34 | ######################################## 35 | # RRDTool status gathering configuration 36 | 37 | # Interval between RRD updates 38 | #rrd interval 30s; 39 | 40 | 41 | ######################################## 42 | ## Alarm definitions 43 | 44 | ## Alarm defaults 45 | ## These parameters can be overriden in a specific alarm configuration 46 | alarm default { 47 | 48 | ## Following "macros" may be used in options below: 49 | ## %t - target name (address) 50 | ## %T - target description 51 | ## %a - alarm name 52 | ## %A - alarm type ("down"/"loss"/"delay") 53 | ## %r - reason of message ("ALARM"/"alarm canceled"/"alarm canceled (config reload)") 54 | ## %p - probes send 55 | ## %P - probes received 56 | ## %l - recent average packet loss 57 | ## %d - recent average delay 58 | ## %s - current timestamp 59 | ## %% - '%' character 60 | 61 | ## Mailbox where alarm report should be sent 62 | mailto "root@localhost" 63 | 64 | ## What to put in the "From: " header 65 | mailfrom "root@localhost" 66 | 67 | ## What should be the envelope sender 68 | #mailenvfrom "root@localhost" 69 | 70 | ## Subject of the mail message (default: "%r: %T(%t) *** %a ***") 71 | #mailsubject "%s %r: %a: %T" 72 | 73 | ## Command to execute when alarm is fired 74 | #command on "/usr/local/bin/do_something_about %t" 75 | #command off "/usr/local/bin/thanks_for_fixing %t" 76 | 77 | ## Command the report should be piped to 78 | #pipe "sms 0-800-my-modem-is-dead" 79 | 80 | ## Combine all alarms that are fired in the 5s interval 81 | ## so one report is send for all of them 82 | #combine 5s 83 | 84 | ## Repeat alarm actions each 5 minutes, but max 10 times (0 whould mean no limit) 85 | #repeat 5m 10 86 | } 87 | 88 | ## Specific alarm definitions follow. 89 | ## Each defined with: 90 | ## alarm { ... } 91 | ## and contains alarm parameters, generic, which may also be used in the 92 | ## "alarm default" section and type-specific ones 93 | ## More than one alarm of a given type may be defined. 94 | 95 | ## "Down" alarm definition. 96 | ## This alarm will be fired when target doesn't respond for 30 seconds. 97 | alarm down "down" { 98 | time 30s 99 | } 100 | 101 | ## "Delay" alarm definition. 102 | ## This alarm will be fired when responses are delayed more than 200ms 103 | ## it will be canceled, when the delay drops below 100ms 104 | alarm delay "delay" { 105 | delay_low 100ms 106 | delay_high 200ms 107 | } 108 | 109 | ## "Loss" alarm definition. 110 | ## This alarm will be fired when packet loss goes over 20% 111 | ## it will be canceled, when the loss drops below 10% 112 | alarm loss "loss" { 113 | percent_low 10 114 | percent_high 20 115 | } 116 | 117 | ######################################## 118 | ## Target definitions 119 | 120 | ## Target defaults 121 | ## These parameters can be overriden in a specific target configuration 122 | target default { 123 | ## How often the probe should be sent 124 | interval 1s 125 | 126 | ## How many replies should be used to compute average delay 127 | ## for controlling "delay" alarms 128 | avg_delay_samples 10 129 | 130 | ## How many probes should be used to compute average loss 131 | avg_loss_samples 50 132 | 133 | ## The delay (in samples) after which loss is computed 134 | ## without this delays larger than interval would be treated as loss 135 | avg_loss_delay_samples 20 136 | 137 | ## Names of the alarms that may be generated for the target 138 | alarms "down","delay","loss" 139 | 140 | ## Location of the RRD 141 | #rrd file "/tmp/apinger-%t.rrd" 142 | } 143 | 144 | ## Targets to probe 145 | ## Each one defined with: 146 | ## target
{ ... } 147 | ## The parameters are those described above in the "target default" section 148 | ## plus the "description" parameter. 149 | ## the
should be IPv4 or IPv6 address (not hostname!) 150 | target "127.0.0.1" { description "localhost IPv4"; } 151 | target "::1" { 152 | description "localhost IPv6"; 153 | 154 | # generate _only_ "down" alarms 155 | # "alarms" directive without "override" keyword adds alarms to the default list 156 | alarms override "down"; 157 | } 158 | -------------------------------------------------------------------------------- /src/conf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #include "config.h" 21 | #include 22 | #ifdef HAVE_STDLIB_H 23 | # include 24 | #endif 25 | #ifdef HAVE_STRING_H 26 | # include 27 | #endif 28 | #include "conf.h" 29 | #include "cfgparser1.h" 30 | 31 | #ifdef HAVE_ASSERT_H 32 | # include 33 | #else 34 | # define assert(cond) 35 | #endif 36 | 37 | struct config * config=NULL; 38 | 39 | void *pool_malloc(struct pool_item **pool,size_t size){ 40 | struct pool_item *pi; 41 | char *p; 42 | 43 | p=(char *)malloc(size+sizeof(struct pool_item)); 44 | assert(p!=NULL); 45 | pi=(struct pool_item *)p; 46 | p+=sizeof(struct pool_item); 47 | pi->next=*pool; 48 | *pool=pi; 49 | return p; 50 | } 51 | 52 | char *pool_strdup(struct pool_item **pool,const char *str){ 53 | char *p; 54 | 55 | assert(str!=NULL); 56 | p=(char *)pool_malloc(pool,strlen(str)+1); 57 | strcpy(p,str); 58 | return p; 59 | } 60 | 61 | void pool_free(struct pool_item **pool,void *ptr){ 62 | struct pool_item *pi,*pi1; 63 | 64 | pi1=NULL; 65 | for(pi=*pool;pi;pi=pi->next){ 66 | if (pi+1==ptr){ 67 | if (pi1){ 68 | pi1->next=pi->next; 69 | } 70 | else{ 71 | *pool=pi->next; 72 | } 73 | free(pi); 74 | return; 75 | } 76 | pi1=pi; 77 | } 78 | fprintf(stderr,"poll_free: pointer not from the pool\n"); 79 | exit(1); 80 | } 81 | 82 | void pool_clear(struct pool_item **pool){ 83 | struct pool_item *pi,*pi1; 84 | 85 | for(pi=*pool;pi;pi=pi1){ 86 | pi1=pi->next; 87 | free(pi); 88 | } 89 | } 90 | 91 | struct alarm_cfg * make_alarm(){ 92 | cur_alarm=PNEW(cur_config.pool,struct alarm_cfg,1); 93 | memset(cur_alarm,0,sizeof(struct alarm_cfg)); 94 | return cur_alarm; 95 | } 96 | 97 | struct target_cfg * make_target(){ 98 | 99 | cur_target=PNEW(cur_config.pool,struct target_cfg,1); 100 | memset(cur_target,0,sizeof(struct target_cfg)); 101 | return cur_target; 102 | } 103 | 104 | void add_alarm(enum alarm_type type){ 105 | cur_alarm->type=type; 106 | cur_alarm->next=cur_config.alarms; 107 | cur_config.alarms=cur_alarm; 108 | cur_alarm=NULL; 109 | } 110 | 111 | void add_target(void){ 112 | cur_target->next=cur_config.targets; 113 | cur_config.targets=cur_target; 114 | cur_target=NULL; 115 | } 116 | 117 | struct alarm_list *alarm2list(const char *aname,struct alarm_list *list){ 118 | struct alarm_cfg *ac; 119 | struct alarm_list *al; 120 | 121 | for(ac=cur_config.alarms;ac!=NULL;ac=ac->next) 122 | if (strcmp(ac->name,aname)==0) break; 123 | if (ac==NULL){ 124 | fprintf(stderr,"Alarm '%s' not found.\n",aname); 125 | return list; 126 | } 127 | al=PNEW(cur_config.pool,struct alarm_list,1); 128 | al->alarm=ac; 129 | al->next=list; 130 | return al; 131 | } 132 | 133 | extern FILE *yyin, *yyout; 134 | extern int yydebug; 135 | extern YYLTYPE yylloc; 136 | int yyparse(void); 137 | 138 | int load_config(const char *filename){ 139 | struct target_cfg *t; 140 | struct alarm_cfg *a; 141 | struct alarm_list *al; 142 | int ret; 143 | 144 | yyin=fopen(filename,"r"); 145 | if (yyin==NULL) return -1; 146 | yydebug=0; 147 | memset(&yylloc,0,sizeof(yylloc)); 148 | cur_config=default_config; 149 | ret=yyparse(); 150 | fclose(yyin); 151 | if (ret==0){ 152 | for(a=cur_config.alarms;a;a=a->next){ 153 | if (a->mailto==NULL) 154 | a->mailto=cur_config.alarm_defaults.mailto; 155 | if (a->mailfrom==NULL) 156 | a->mailfrom=cur_config.alarm_defaults.mailfrom; 157 | if (a->mailenvfrom==NULL) 158 | a->mailenvfrom=cur_config.alarm_defaults.mailenvfrom; 159 | if (a->mailsubject==NULL) 160 | a->mailsubject=cur_config.alarm_defaults.mailsubject; 161 | if (a->command_on==NULL) 162 | a->command_on=cur_config.alarm_defaults.command_on; 163 | if (a->command_off==NULL) 164 | a->command_off=cur_config.alarm_defaults.command_off; 165 | if (a->pipe_on==NULL) 166 | a->pipe_on=cur_config.alarm_defaults.pipe_on; 167 | if (a->pipe_off==NULL) 168 | a->pipe_off=cur_config.alarm_defaults.pipe_off; 169 | if (a->combine_interval==0) 170 | a->combine_interval=cur_config.alarm_defaults.combine_interval; 171 | if (a->repeat_interval==0){ 172 | a->repeat_interval=cur_config.alarm_defaults.repeat_interval; 173 | a->repeat_max=cur_config.alarm_defaults.repeat_max; 174 | } 175 | } 176 | for(t=cur_config.targets;t;t=t->next){ 177 | if (t->description==NULL) 178 | t->description=cur_config.target_defaults.description; 179 | if (t->interval<=0) 180 | t->interval=cur_config.target_defaults.interval; 181 | if (t->avg_delay_samples<=0) 182 | t->avg_delay_samples=cur_config.target_defaults.avg_delay_samples; 183 | if (t->avg_loss_samples<=0) 184 | t->avg_loss_samples=cur_config.target_defaults.avg_loss_samples; 185 | if (t->avg_loss_delay_samples<=0) 186 | t->avg_loss_delay_samples=cur_config.target_defaults.avg_loss_delay_samples; 187 | if (t->rrd_filename==NULL) 188 | t->rrd_filename=cur_config.target_defaults.rrd_filename; 189 | for(al=t->alarms;al && al->next;al=al->next); 190 | if (t->alarms_override==0){ 191 | if (al) 192 | al->next=cur_config.target_defaults.alarms; 193 | else 194 | t->alarms=cur_config.target_defaults.alarms; 195 | } 196 | } 197 | if (config!=NULL){ 198 | struct pool_item *pool=config->pool; 199 | pool_clear(&pool); 200 | config=NULL; 201 | } 202 | 203 | config=PNEW(cur_config.pool,struct config,1); 204 | memcpy(config,&cur_config,sizeof(struct config)); 205 | } 206 | memset(&cur_config,0,sizeof(cur_config)); 207 | return ret; 208 | } 209 | 210 | void free_config(void){ 211 | struct pool_item *pool; 212 | 213 | pool=config->pool; 214 | pool_clear(&pool); 215 | } 216 | 217 | -------------------------------------------------------------------------------- /src/icmp6.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #include "config.h" 21 | 22 | #ifdef HAVE_IPV6 23 | #include "apinger.h" 24 | 25 | #ifdef HAVE_STDLIB_H 26 | # include 27 | #endif 28 | #ifdef HAVE_UNISTD_H 29 | # include 30 | #endif 31 | #ifdef HAVE_ARPA_INET_H 32 | # include 33 | #endif 34 | #ifdef HAVE_NETINET_ICMP6_H 35 | # include 36 | #endif 37 | #ifdef HAVE_NETINET_IP6_H 38 | # include 39 | #endif 40 | #ifdef HAVE_ERRNO_H 41 | # include 42 | #endif 43 | #ifdef HAVE_SCHED_H 44 | # include 45 | #endif 46 | #ifdef HAVE_SYS_IOCTL_H 47 | # include 48 | #endif 49 | #ifdef HAVE_SYS_TYPES_H 50 | # include 51 | #endif 52 | #ifdef HAVE_SYS_UIO_H 53 | # include 54 | #endif 55 | #include "debug.h" 56 | 57 | #ifdef HAVE_LINUX_FILTER_H 58 | # ifdef HAVE_LINUX_TYPES_H 59 | # include 60 | # endif 61 | # include 62 | #endif /* HAVE_LINUX_FILTER_H */ 63 | 64 | void install_filter6(){ 65 | 66 | #ifdef HAVE_LINUX_FILTER_H 67 | static struct sock_filter insns[] = { 68 | BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 4), /* Load icmp echo ident */ 69 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */ 70 | BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */ 71 | BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 0), /* Load icmp type */ 72 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP6_ECHO_REPLY, 1, 0), /* Echo? */ 73 | BPF_STMT(BPF_RET|BPF_K, ~0U), /* No. It passes. This must not happen. */ 74 | BPF_STMT(BPF_RET|BPF_K, 0), /* Echo with wrong ident. Reject. */ 75 | }; 76 | static struct sock_fprog filter = { 77 | sizeof insns / sizeof(insns[0]), 78 | insns 79 | }; 80 | 81 | insns[1] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1); 82 | 83 | if (setsockopt(icmp6_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) 84 | myperror("WARNING: failed to install socket filter\n"); 85 | #endif /* HAVE_LINUX_FILTER_H */ 86 | 87 | } 88 | 89 | 90 | void send_icmp6_probe(struct target *t,int seq){ 91 | static char buf[1024]; 92 | struct icmp6_hdr *p=(struct icmp6_hdr *)buf; 93 | struct trace_info ti; 94 | struct timeval cur_time; 95 | int size; 96 | int ret; 97 | 98 | p->icmp6_type=ICMP6_ECHO_REQUEST; 99 | p->icmp6_code=0; 100 | p->icmp6_cksum=0; 101 | p->icmp6_seq=seq%65536; 102 | p->icmp6_id=ident; 103 | 104 | #ifdef HAVE_SCHED_YIELD 105 | /* Give away our time now, or we may be stopped between gettimeofday() and sendto() */ 106 | sched_yield(); 107 | #endif 108 | gettimeofday(&cur_time,NULL); 109 | ti.timestamp=cur_time; 110 | ti.target_id=t; 111 | ti.seq=seq; 112 | memcpy(p+1,&ti,sizeof(ti)); 113 | size=sizeof(*p)+sizeof(ti); 114 | 115 | ret=sendto(icmp6_sock,p,size,MSG_DONTWAIT, 116 | (struct sockaddr *)&t->addr.addr6,sizeof(t->addr.addr6)); 117 | if (ret<0){ 118 | if (config->debug) myperror("sendto"); 119 | } 120 | } 121 | 122 | void recv_icmp6(void){ 123 | int len,icmplen,datalen; 124 | char buf[1024]; 125 | char abuf[100]; 126 | const char *name; 127 | struct sockaddr_in6 from; 128 | struct icmp6_hdr *icmp; 129 | struct timeval time_recv; 130 | struct timeval *time_recvp=NULL; 131 | #ifdef HAVE_RECVMSG 132 | char ans_data[4096]; 133 | struct iovec iov; 134 | struct msghdr msg; 135 | struct cmsghdr *c; 136 | 137 | iov.iov_base=buf; 138 | iov.iov_len=1000; 139 | msg.msg_name=&from; 140 | msg.msg_namelen=sizeof(from); 141 | msg.msg_iov=&iov; 142 | msg.msg_iovlen=1; 143 | msg.msg_control=ans_data; 144 | msg.msg_controllen=sizeof(ans_data); 145 | len=recvmsg(icmp6_sock, &msg, MSG_DONTWAIT); 146 | #else 147 | socklen_t sl; 148 | 149 | sl=sizeof(from); 150 | len=recvfrom(icmp6_sock,buf,1024,0,(struct sockaddr *)&from,&sl); 151 | #endif 152 | if (len<0){ 153 | if (errno==EAGAIN) return; 154 | myperror("recvfrom"); 155 | return; 156 | } 157 | if (len==0) return; 158 | #if defined(HAVE_RECVMSG) && defined(SO_TIMESTAMP) 159 | debug("checking CMSG..."); 160 | for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) { 161 | debug("CMSG level: %i type: %i",c->cmsg_level,c->cmsg_type); 162 | if (c->cmsg_level != SOL_SOCKET || c->cmsg_type != SO_TIMESTAMP) 163 | continue; 164 | if (c->cmsg_len < CMSG_LEN(sizeof(struct timeval))) 165 | continue; 166 | time_recvp = (struct timeval*)CMSG_DATA(c); 167 | debug("Got timestamp from CMSG"); 168 | } 169 | #endif 170 | if (time_recvp==NULL){ 171 | #ifdef SIOCGSTAMP 172 | if (!ioctl(icmp6_sock, SIOCGSTAMP, &time_recv)){ 173 | debug("Got timestamp from ioctl()"); 174 | }else 175 | #endif 176 | { 177 | gettimeofday(&time_recv,NULL); 178 | debug("Got timestamp from gettimeofday()"); 179 | } 180 | time_recvp=&time_recv; 181 | } 182 | icmplen=len; 183 | icmp=(struct icmp6_hdr *)buf; 184 | if (icmp->icmp6_type != ICMP6_ECHO_REPLY) return; 185 | if (icmp->icmp6_id != ident) return; 186 | 187 | name=inet_ntop(AF_INET6,&from.sin6_addr,abuf,100); 188 | debug("Ping reply from %s",name); 189 | datalen=icmplen-sizeof(*icmp); 190 | if (datalen!=sizeof(struct trace_info)){ 191 | debug("Packet data truncated."); 192 | return; 193 | } 194 | #ifdef FORKED_RECEIVER 195 | pipe_reply(*time_recvp,icmp->icmp6_seq,(struct trace_info*)(icmp+1)); 196 | #else 197 | analyze_reply(*time_recvp,icmp->icmp6_seq,(struct trace_info*)(icmp+1)); 198 | #endif 199 | } 200 | 201 | 202 | int make_icmp6_socket(void){ 203 | int opt; 204 | 205 | icmp6_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); 206 | if (icmp6_sock<0) 207 | myperror("socket"); 208 | else { 209 | opt=2; 210 | #if defined(SOL_RAW) && defined(IPV6_CHECKSUM) 211 | if (setsockopt(icmp6_sock, SOL_RAW, IPV6_CHECKSUM, &opt, sizeof(int))) 212 | myperror("setsockopt(IPV6_CHECKSUM)"); 213 | #endif 214 | #ifdef SO_TIMESTAMP 215 | opt=1; 216 | if (setsockopt(icmp6_sock, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt))) 217 | myperror("setsockopt(SO_TIMESTAMP)"); 218 | #endif 219 | /*install_filter6();*/ 220 | } 221 | return icmp6_sock; 222 | } 223 | 224 | #else /*HAVE_IPV6*/ 225 | #include "apinger.h" 226 | 227 | int make_icmp6_socket(void){ return -1; } 228 | void recv_icmp6(void){} 229 | void send_icmp6_probe(struct target *t,int seq){} 230 | 231 | #endif /*HAVE_IPV6*/ 232 | -------------------------------------------------------------------------------- /src/cfgparser1.y: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | 21 | %{ 22 | 23 | #include 24 | #ifdef HAVE_STDLIB_H 25 | # include 26 | #endif 27 | #ifdef HAVE_STRING_H 28 | # include 29 | #endif 30 | #include "conf.h" 31 | #include "debug.h" 32 | 33 | #define YYDEBUG 1 34 | #define YYERROR_VERBOSE 1 35 | 36 | void yyerror(const char *s); 37 | int yylex(void); 38 | 39 | extern FILE *yyin, *yyout; 40 | struct config cur_config; 41 | struct alarm_cfg *cur_alarm; 42 | struct target_cfg *cur_target; 43 | 44 | 45 | %} 46 | 47 | %verbose 48 | %locations 49 | %expect 14 50 | %union { 51 | int i; 52 | char *s; 53 | struct alarm_cfg *a; 54 | struct target_cfg *t; 55 | struct config *c; 56 | struct alarm_list *al; 57 | } 58 | 59 | %token TIME 60 | %token INTEGER 61 | %token STRING 62 | 63 | %token DEBUG 64 | %token USER 65 | %token GROUP 66 | %token PID_FILE 67 | %token MAILER 68 | %token TIMESTAMP_FORMAT 69 | %token RRD 70 | 71 | 72 | %token STATUS 73 | %token ALARM 74 | %token TARGET 75 | 76 | %token OVERRIDE 77 | %token DEFAULT 78 | 79 | %token MAILTO 80 | %token MAILFROM 81 | %token MAILENVFROM 82 | %token MAILSUBJECT 83 | %token COMMAND 84 | %token PIPE 85 | %token COMBINE 86 | %token REPEAT 87 | 88 | %token DOWN 89 | %token LOSS 90 | %token DELAY 91 | 92 | %token TIME_ 93 | %token PERCENT_LOW 94 | %token PERCENT_HIGH 95 | %token DELAY_LOW 96 | %token DELAY_HIGH 97 | 98 | %token DESCRIPTION 99 | %token ALARMS 100 | %token INTERVAL 101 | %token AVG_DELAY_SAMPLES 102 | %token AVG_LOSS_SAMPLES 103 | %token AVG_LOSS_DELAY_SAMPLES 104 | 105 | %token FILE_ 106 | 107 | %token ERROR 108 | 109 | %token ON OFF 110 | %token YES NO 111 | %token TRUE FALSE 112 | 113 | %type string 114 | %type alarmlist 115 | %type makealarm getdefalarm 116 | %type maketarget getdeftarget 117 | %type boolean 118 | 119 | %% 120 | 121 | config: /* */ 122 | | DEBUG boolean { cur_config.debug=$2; } 123 | | USER string { cur_config.user=$2; } 124 | | GROUP string { cur_config.group=$2; } 125 | | MAILER string { cur_config.mailer=$2; } 126 | | TIMESTAMP_FORMAT string { cur_config.timestamp_format=$2; } 127 | | PID_FILE string { cur_config.pid_file=$2; } 128 | | STATUS '{' statuscfg '}' 129 | | RRD INTERVAL TIME { cur_config.rrd_interval=$3; } 130 | | alarm 131 | | target 132 | | config separator config 133 | | error 134 | { 135 | logit("Configuration file syntax error. Line %i, character %i", 136 | @$.first_line+1,@$.first_column+1); 137 | YYABORT; 138 | } 139 | ; 140 | 141 | makealarm: { $$=make_alarm(); } 142 | ; 143 | 144 | maketarget: { $$=make_target(); } 145 | ; 146 | 147 | getdefalarm: { 148 | $$=&cur_config.alarm_defaults; 149 | cur_alarm=$$; 150 | } 151 | ; 152 | 153 | getdeftarget: { 154 | $$=&cur_config.target_defaults; 155 | cur_target=$$; 156 | } 157 | ; 158 | 159 | alarm: ALARM getdefalarm DEFAULT '{' alarmcommoncfg '}' 160 | | ALARM makealarm DOWN string '{' alarmdowncfg '}' 161 | { 162 | cur_alarm->name=$4; 163 | add_alarm(AL_DOWN); 164 | } 165 | | ALARM makealarm LOSS string '{' alarmlosscfg '}' 166 | { 167 | cur_alarm->name=$4; 168 | add_alarm(AL_LOSS); 169 | } 170 | | ALARM makealarm DELAY string '{' alarmdelaycfg '}' 171 | { 172 | cur_alarm->name=$4; 173 | add_alarm(AL_DELAY); 174 | } 175 | ; 176 | 177 | alarmcommoncfg: alarmcommon 178 | | alarmcommoncfg separator alarmcommoncfg 179 | ; 180 | 181 | alarmlosscfg: alarmcommon 182 | | PERCENT_LOW INTEGER 183 | { cur_alarm->p.lh.low=$2; } 184 | | PERCENT_HIGH INTEGER 185 | { cur_alarm->p.lh.high=$2; } 186 | | alarmlosscfg separator alarmlosscfg 187 | ; 188 | 189 | alarmdelaycfg: alarmcommon 190 | | DELAY_LOW TIME 191 | { cur_alarm->p.lh.low=$2; } 192 | | DELAY_HIGH TIME 193 | { cur_alarm->p.lh.high=$2; } 194 | | alarmdelaycfg separator alarmdelaycfg 195 | ; 196 | 197 | alarmdowncfg: alarmcommon 198 | | TIME_ TIME 199 | { cur_alarm->p.val=$2; } 200 | | alarmdowncfg separator alarmdowncfg 201 | ; 202 | 203 | alarmcommon: /* */ 204 | | MAILTO string 205 | { cur_alarm->mailto=$2; } 206 | | MAILFROM string 207 | { cur_alarm->mailfrom=$2; } 208 | | MAILENVFROM string 209 | { cur_alarm->mailenvfrom=$2; } 210 | | MAILSUBJECT string 211 | { cur_alarm->mailsubject=$2; } 212 | | COMMAND string 213 | { 214 | if (cur_alarm->command_on==NULL) cur_alarm->command_on=$2; 215 | if (cur_alarm->command_off==NULL) cur_alarm->command_off=$2; 216 | } 217 | | COMMAND ON string 218 | { cur_alarm->command_on=$3; } 219 | | COMMAND OFF string 220 | { cur_alarm->command_off=$3; } 221 | | PIPE string 222 | { 223 | if (cur_alarm->pipe_on==NULL) cur_alarm->pipe_on=$2; 224 | if (cur_alarm->pipe_off==NULL) cur_alarm->pipe_off=$2; 225 | } 226 | | PIPE ON string 227 | { cur_alarm->pipe_on=$3; } 228 | | PIPE OFF string 229 | { cur_alarm->pipe_off=$3; } 230 | | COMBINE TIME 231 | { cur_alarm->combine_interval=$2; } 232 | | REPEAT TIME INTEGER 233 | { cur_alarm->repeat_interval=$2; cur_alarm->repeat_max=$3; } 234 | | REPEAT TIME 235 | { cur_alarm->combine_interval=$2; cur_alarm->repeat_max=0;} 236 | ; 237 | 238 | 239 | target: TARGET getdeftarget DEFAULT '{' targetcfg '}' 240 | | TARGET maketarget string '{' targetcfg '}' 241 | { 242 | cur_target->name=$3; 243 | add_target(); 244 | } 245 | ; 246 | 247 | targetcfg: /* */ 248 | | DESCRIPTION string 249 | { cur_target->description=$2; } 250 | | ALARMS alarmlist 251 | { cur_target->alarms=$2; } 252 | | ALARMS OVERRIDE alarmlist 253 | { cur_target->alarms_override=1; cur_target->alarms=$3; } 254 | | INTERVAL INTEGER 255 | { cur_target->interval=$2; } 256 | | INTERVAL TIME 257 | { cur_target->interval=$2; } 258 | | AVG_DELAY_SAMPLES INTEGER 259 | { cur_target->avg_delay_samples=$2; } 260 | | AVG_LOSS_SAMPLES INTEGER 261 | { cur_target->avg_loss_samples=$2; } 262 | | AVG_LOSS_DELAY_SAMPLES INTEGER 263 | { cur_target->avg_loss_delay_samples=$2; } 264 | | RRD FILE_ string 265 | { cur_target->rrd_filename=$3; } 266 | | targetcfg separator targetcfg 267 | ; 268 | 269 | alarmlist: string 270 | { $$=alarm2list($1,NULL); } 271 | | alarmlist ',' string 272 | { $$=alarm2list($3,$1); } 273 | ; 274 | 275 | statuscfg: /* */ 276 | | FILE_ string 277 | { cur_config.status_file=$2; } 278 | | INTERVAL INTEGER 279 | { cur_config.status_interval=$2; } 280 | | INTERVAL TIME 281 | { cur_config.status_interval=$2; } 282 | | statuscfg separator statuscfg 283 | ; 284 | 285 | 286 | string: STRING { $$=pool_strdup(&cur_config.pool,$1); } 287 | ; 288 | 289 | boolean: ON { $$=1; } 290 | | OFF { $$=0; } 291 | | YES { $$=1; } 292 | | NO { $$=0; } 293 | | TRUE { $$=1; } 294 | | FALSE { $$=0; } 295 | ; 296 | 297 | separator: '\n' 298 | | ';' 299 | ; 300 | 301 | 302 | %% 303 | void yyerror (const char *s) { 304 | logit("%s", s); 305 | } 306 | 307 | -------------------------------------------------------------------------------- /src/rrd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #include "config.h" 21 | #include "apinger.h" 22 | #include "rrd.h" 23 | #include "debug.h" 24 | 25 | #include 26 | #ifdef HAVE_STDLIB_H 27 | # include 28 | #endif 29 | #ifdef HAVE_UNISTD_H 30 | # include 31 | #endif 32 | #ifdef HAVE_STRING_H 33 | # include 34 | #endif 35 | #ifdef HAVE_STDARG_H 36 | # include 37 | #endif 38 | 39 | #define RRDTOOL_RESTART_TIME (60*5) 40 | 41 | FILE *rrdtool_pipe=NULL; 42 | time_t last_rrdtool_start=0; 43 | time_t rrdtool_waiting=0; 44 | 45 | int rrd_init(void){ 46 | time_t cur_t; 47 | 48 | if (rrdtool_pipe) 49 | pclose(rrdtool_pipe); 50 | rrdtool_pipe=NULL; 51 | if (!config->rrd_interval){ 52 | return -1; 53 | } 54 | cur_t=time(NULL); 55 | if (cur_t-last_rrdtool_startdebug){ 64 | rrdtool_pipe=popen(RRDTOOL " -","w"); 65 | } 66 | else{ 67 | rrdtool_pipe=popen(RRDTOOL " - >/dev/null 2>&1","w"); 68 | } 69 | if (rrdtool_pipe==NULL){ 70 | myperror(RRDTOOL); 71 | return -1; 72 | } 73 | #if defined(HAVE_SETVBUF) && defined(_IOLBF) 74 | setvbuf(rrdtool_pipe, (char *)NULL, _IOLBF, 0); 75 | #endif 76 | return 0; 77 | } 78 | 79 | int rrd_write(const char *format,...){ 80 | va_list args; 81 | int ret; 82 | 83 | va_start(args, format); 84 | if (foreground && config->debug){ 85 | vfprintf(stderr, format, args); 86 | } 87 | ret=vfprintf(rrdtool_pipe,format,args); 88 | va_end(args); 89 | return ret; 90 | } 91 | 92 | void rrd_create(void){ 93 | struct target *t; 94 | const char *filename; 95 | int ret; 96 | 97 | for(t=targets;t!=NULL;t=t->next){ 98 | if (t->config->rrd_filename==NULL) continue; 99 | filename=subst_macros(t->config->rrd_filename,t,NULL,0); 100 | #if defined(HAVE_ACCESS) && defined(F_OK) 101 | if (access(filename,F_OK)==0) continue; 102 | #else 103 | { 104 | FILE *f; 105 | f=fopen(filename,"r"); 106 | if (f!=NULL){ 107 | fclose(f); 108 | continue; 109 | } 110 | } 111 | #endif 112 | if (rrdtool_pipe==NULL) 113 | if (rrd_init()) return; 114 | ret=rrd_write("create %s" 115 | " DS:loss:GAUGE:600:0:100" 116 | " DS:delay:GAUGE:600:0:100000" 117 | " RRA:AVERAGE:0.5:1:600" 118 | " RRA:AVERAGE:0.5:6:700" 119 | " RRA:AVERAGE:0.5:24:775" 120 | " RRA:AVERAGE:0.5:288:796\n",filename); 121 | if (ret<0){ 122 | myperror("Error while feeding rrdtool"); 123 | pclose(rrdtool_pipe); 124 | rrdtool_pipe=NULL; 125 | } 126 | } 127 | } 128 | 129 | void rrd_update(void){ 130 | struct target *t; 131 | const char *filename; 132 | int ret; 133 | 134 | if (sigpipe_received) { 135 | sigpipe_received=0; 136 | if (rrdtool_pipe) pclose(rrdtool_pipe); 137 | rrdtool_pipe=NULL; 138 | } 139 | for(t=targets;t!=NULL;t=t->next){ 140 | if (t->config->rrd_filename==NULL) continue; 141 | if (rrdtool_pipe==NULL) 142 | if (rrd_init()) return; 143 | filename=subst_macros(t->config->rrd_filename,t,NULL,0); 144 | ret=rrd_write("update %s -t loss:delay %li",filename,(long)time(NULL)); 145 | if (ret>0){ 146 | if (t->upsent > t->config->avg_loss_delay_samples 147 | +t->config->avg_loss_samples){ 148 | ret=rrd_write(":%f", 149 | 100*((double)t->recently_lost)/ 150 | t->config->avg_loss_samples); 151 | } 152 | else ret=rrd_write(":U"); 153 | } 154 | if (ret>0){ 155 | if (t->upsent > t->config->avg_delay_samples){ 156 | rrd_write(":%f", 157 | (t->delay_sum/t->config->avg_delay_samples)/1000); 158 | } 159 | else ret=rrd_write(":U"); 160 | } 161 | if (ret>0) 162 | ret=rrd_write("\n"); 163 | if (ret<0){ 164 | myperror("Error while feeding rrdtool"); 165 | pclose(rrdtool_pipe); 166 | rrdtool_pipe=NULL; 167 | } 168 | } 169 | } 170 | 171 | int rrd_print_cgi(const char *graph_dir,const char *graph_location){ 172 | struct target_cfg *tc; 173 | struct target t; 174 | const char *rrd_filename,*p; 175 | char *base_filename,*buf,*p1; 176 | char *ebuf; 177 | int num_esc; 178 | 179 | printf("#!" RRDCGI "\n\n"); 180 | printf("\n\n Alarm Pinger statistics \n\n"); 181 | printf("\n

Alarm Pinger statistics

\n"); 182 | printf("

Daily packet loss and delay summary

\n"); 183 | memset(&t,0,sizeof(t)); 184 | for(tc=config->targets;tc;tc=tc->next){ 185 | if (tc->rrd_filename==NULL) continue; 186 | t.name=tc->name; 187 | t.description=tc->description; 188 | rrd_filename=subst_macros(tc->rrd_filename,&t,NULL,0); 189 | buf=strdup(rrd_filename); 190 | base_filename=strrchr(buf,'/'); 191 | if (base_filename!=NULL) 192 | *base_filename++=0; 193 | else 194 | base_filename=buf; 195 | p1=strrchr(base_filename,'.'); 196 | if (p1!=NULL) *p1=0; 197 | num_esc=0; 198 | for(p=rrd_filename;*p;p++) 199 | if (*p==':' || *p=='\\') num_esc++; 200 | if (num_esc>0){ 201 | ebuf=NEW(char,strlen(rrd_filename)+num_esc+1); 202 | p1=ebuf; 203 | for(p=rrd_filename;*p;p++){ 204 | if (*p==':' || *p=='\\') *p1++='\\'; 205 | *p1++=*p; 206 | } 207 | *p1++=0; 208 | rrd_filename=ebuf; 209 | } 210 | else ebuf=NULL; 211 | printf("

'\n", 213 | graph_location); 214 | printf("-a PNG -h 200 -w 800 --lazy -v 'Packet RTT (s)'\n"); 215 | printf("-t 'Packet delay summary for %s (%s)'\n",tc->name,tc->description); 216 | printf("-s -1d -l 0\n"); 217 | printf("DEF:delay=%s:delay:AVERAGE\n",rrd_filename); 218 | printf("AREA:delay#00a000:\n"); 219 | printf("LINE1:delay#004000:\n"); 220 | printf("GPRINT:delay:MIN:\"Minimum\\: %%7.3lf%%ss\"\n"); 221 | printf("GPRINT:delay:AVERAGE:\"Average\\: %%7.3lf%%ss\"\n"); 222 | printf("GPRINT:delay:MAX:\"Maximum\\: %%7.3lf%%ss\\j\"\n"); 223 | printf(">

"); 224 | 225 | printf("

'\n", 227 | graph_location); 228 | printf("-a PNG -h 200 -w 800 --lazy -v 'Packet loss (%%)'\n"); 229 | printf("-t 'Packet loss summary for %s (%s)'\n",tc->name,tc->description); 230 | printf("-s -1d -l 0 -u 100\n"); 231 | printf("DEF:loss=%s:loss:AVERAGE\n",rrd_filename); 232 | printf("AREA:loss#f00000:\n"); 233 | printf("LINE1:loss#700000:\n"); 234 | printf("GPRINT:loss:MIN:\"Minimum\\: %%5.1lf%%%%\"\n"); 235 | printf("GPRINT:loss:AVERAGE:\"Average\\: %%5.1lf%%%%\"\n"); 236 | printf("GPRINT:loss:MAX:\"Maximum\\: %%5.1lf%%%%\\j\"\n"); 237 | printf(">

\n"); 238 | free(buf); 239 | free(ebuf); 240 | } 241 | printf("

apinger by Jacek Konieczny

\n"); 242 | printf("\n"); 243 | return 0; 244 | } 245 | 246 | void rrd_close(void){ 247 | 248 | if (rrdtool_pipe) 249 | pclose(rrdtool_pipe); 250 | } 251 | -------------------------------------------------------------------------------- /src/icmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #include "config.h" 21 | #include "apinger.h" 22 | 23 | #ifdef HAVE_STDLIB_H 24 | # include 25 | #endif 26 | #ifdef HAVE_UNISTD_H 27 | # include 28 | #endif 29 | #ifdef HAVE_STRING_H 30 | # include 31 | #endif 32 | #ifdef HAVE_NETINET_IN_SYSTM_H 33 | # include 34 | #endif 35 | #ifdef HAVE_NETINET_IP_H 36 | # include 37 | #endif 38 | #ifdef HAVE_NETINET_IP_ICMP_H 39 | # include 40 | #endif 41 | #ifdef HAVE_ARPA_INET_H 42 | # include 43 | #endif 44 | #ifdef HAVE_ERRNO_H 45 | # include 46 | #endif 47 | #ifdef HAVE_SCHED_H 48 | # include 49 | #endif 50 | #ifdef HAVE_SYS_IOCTL_H 51 | # include 52 | #endif 53 | #ifdef HAVE_SYS_TYPES_H 54 | # include 55 | #endif 56 | #ifdef HAVE_SYS_UIO_H 57 | # include 58 | #endif 59 | #include "debug.h" 60 | 61 | #ifdef HAVE_LINUX_FILTER_H 62 | # ifdef HAVE_LINUX_TYPES_H 63 | # include 64 | # endif 65 | # include 66 | #endif /* HAVE_LINUX_FILTER_H */ 67 | 68 | /* filter instalation code borrowed from iputils */ 69 | void install_filter(){ 70 | #ifdef HAVE_LINUX_FILTER_H 71 | static struct sock_filter insns[] = { 72 | BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), /* Skip IP header. F..g BSD... Look into ping. */ 73 | BPF_STMT(BPF_LD|BPF_H|BPF_IND, 4), /* Load icmp echo ident */ 74 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0xAAAA, 0, 1), /* Ours? */ 75 | BPF_STMT(BPF_RET|BPF_K, ~0U), /* Yes, it passes. */ 76 | BPF_STMT(BPF_LD|BPF_B|BPF_IND, 0), /* Load icmp type */ 77 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */ 78 | BPF_STMT(BPF_RET|BPF_K, 0xFFFFFFF), /* No. It passes. */ 79 | BPF_STMT(BPF_RET|BPF_K, 0) /* Echo with wrong ident. Reject. */ 80 | }; 81 | static struct sock_fprog filter = { 82 | sizeof insns / sizeof(insns[0]), 83 | insns 84 | }; 85 | 86 | /* Patch bpflet for current identifier. */ 87 | insns[2] = (struct sock_filter)BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(ident), 0, 1); 88 | 89 | if (setsockopt(icmp_sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter))) 90 | myperror("WARNING: failed to install socket filter\n"); 91 | #endif /* HAS_LINUX_FILTER_H */ 92 | } 93 | 94 | /* function borrowed from iputils */ 95 | u_short in_cksum(const u_short *addr, register int len, u_short csum){ 96 | 97 | register int nleft = len; 98 | const u_short *w = addr; 99 | register u_short answer; 100 | register int sum = csum; 101 | 102 | /* 103 | * Our algorithm is simple, using a 32 bit accumulator (sum), 104 | * we add sequential 16 bit words to it, and at the end, fold 105 | * back all the carry bits from the top 16 bits into the lower 106 | * 16 bits. 107 | */ 108 | while (nleft > 1) { 109 | sum += *w++; 110 | nleft -= 2; 111 | } 112 | 113 | /* mop up an odd byte, if necessary */ 114 | if (nleft == 1) 115 | sum += htons(*(u_char *)w << 8); 116 | 117 | /* 118 | * add back carry outs from top 16 bits to low 16 bits 119 | */ 120 | sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ 121 | sum += (sum >> 16); /* add carry */ 122 | answer = ~sum; /* truncate to 16 bits */ 123 | return (answer); 124 | } 125 | 126 | void send_icmp_probe(struct target *t,int seq){ 127 | static char buf[1024]; 128 | struct icmp *p=(struct icmp *)buf; 129 | struct trace_info ti; 130 | struct timeval cur_time; 131 | int size; 132 | int ret; 133 | 134 | 135 | p->icmp_type=ICMP_ECHO; 136 | p->icmp_code=0; 137 | p->icmp_cksum=0; 138 | p->icmp_seq=seq%65536; 139 | p->icmp_id=ident; 140 | 141 | #ifdef HAVE_SCHED_YIELD 142 | /* Give away our time now, or we may be stopped between gettimeofday() and sendto() */ 143 | sched_yield(); 144 | #endif 145 | gettimeofday(&cur_time,NULL); 146 | ti.timestamp=cur_time; 147 | ti.target_id=t; 148 | ti.seq=seq; 149 | memcpy(p+1,&ti,sizeof(ti)); 150 | size=sizeof(*p)+sizeof(ti); 151 | 152 | p->icmp_cksum = in_cksum((u_short *)p,size,0); 153 | ret=sendto(icmp_sock,p,size,MSG_DONTWAIT, 154 | (struct sockaddr *)&t->addr.addr4,sizeof(t->addr.addr4)); 155 | if (ret<0){ 156 | if (config->debug) myperror("sendto"); 157 | } 158 | } 159 | 160 | void recv_icmp(void){ 161 | int len,hlen,icmplen,datalen; 162 | char buf[1024]; 163 | struct sockaddr_in from; 164 | struct icmp *icmp; 165 | struct ip *ip; 166 | struct timeval time_recv; 167 | struct timeval *time_recvp=NULL; 168 | #ifdef HAVE_RECVMSG 169 | char ans_data[4096]; 170 | struct iovec iov; 171 | struct msghdr msg; 172 | struct cmsghdr *c; 173 | 174 | iov.iov_base=buf; 175 | iov.iov_len=1000; 176 | msg.msg_name=&from; 177 | msg.msg_namelen=sizeof(from); 178 | msg.msg_iov=&iov; 179 | msg.msg_iovlen=1; 180 | msg.msg_control=ans_data; 181 | msg.msg_controllen=sizeof(ans_data); 182 | len=recvmsg(icmp_sock, &msg, MSG_DONTWAIT); 183 | #else 184 | socklen_t sl; 185 | 186 | sl=sizeof(from); 187 | len=recvfrom(icmp_sock,buf,1024,MSG_DONTWAIT,(struct sockaddr *)&from,&sl); 188 | #endif 189 | if (len<0){ 190 | if (errno==EAGAIN) return; 191 | myperror("recvfrom"); 192 | return; 193 | } 194 | if (len==0) return; 195 | #if defined(HAVE_RECVMSG) && defined(SO_TIMESTAMP) 196 | debug("checking CMSG..."); 197 | for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) { 198 | debug("CMSG level: %i type: %i",c->cmsg_level,c->cmsg_type); 199 | if (c->cmsg_level != SOL_SOCKET || c->cmsg_type != SO_TIMESTAMP) 200 | continue; 201 | if (c->cmsg_len < CMSG_LEN(sizeof(struct timeval))) 202 | continue; 203 | time_recvp = (struct timeval*)CMSG_DATA(c); 204 | debug("Got timestampt from CMSG"); 205 | } 206 | #endif 207 | if (time_recvp==NULL){ 208 | #ifdef SIOCGSTAMP 209 | if (!ioctl(icmp_sock, SIOCGSTAMP, &time_recv)){ 210 | debug("Got timestampt from ioctl()"); 211 | }else 212 | #endif 213 | gettimeofday(&time_recv,NULL); 214 | time_recvp=&time_recv; 215 | } 216 | ip=(struct ip *)buf; 217 | hlen=ip->ip_hl*4; 218 | if (lenip_hl<5) { 219 | debug("Too short packet reveiced"); 220 | return; 221 | } 222 | icmplen=len-hlen; 223 | icmp=(struct icmp *)(buf+hlen); 224 | if (icmp->icmp_type != ICMP_ECHOREPLY){ 225 | debug("Other (%i) icmp type received",icmp->icmp_type); 226 | return; 227 | } 228 | if (icmp->icmp_id != ident){ 229 | debug("Alien echo-reply received"); 230 | return; 231 | } 232 | debug("Ping reply from %s",inet_ntoa(from.sin_addr)); 233 | datalen=icmplen-sizeof(*icmp); 234 | if (datalen!=sizeof(struct trace_info)){ 235 | debug("Packet data truncated."); 236 | return; 237 | } 238 | #ifdef FORKED_RECEIVER 239 | pipe_reply(*time_recvp,icmp->icmp_seq,(struct trace_info*)(icmp+1)); 240 | #else 241 | analyze_reply(*time_recvp,icmp->icmp_seq,(struct trace_info*)(icmp+1)); 242 | #endif 243 | } 244 | 245 | int make_icmp_socket(void){ 246 | int on; 247 | 248 | icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 249 | if (icmp_sock<0) 250 | myperror("socket"); 251 | #ifdef SO_TIMESTAMP 252 | else{ 253 | on=1; 254 | if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on))) 255 | myperror("setsockopt(SO_TIMESTAMP)"); 256 | } 257 | #endif 258 | return icmp_sock; 259 | } 260 | 261 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #include "config.h" 21 | #include "apinger.h" 22 | 23 | #include 24 | #ifdef HAVE_STDLIB_H 25 | # include 26 | #endif 27 | #ifdef HAVE_UNISTD_H 28 | # include 29 | #endif 30 | #ifdef HAVE_SIGNAL_H 31 | # include 32 | #endif 33 | #ifdef HAVE_PWD_H 34 | # include 35 | #endif 36 | #ifdef HAVE_GRP_H 37 | # include 38 | #endif 39 | #ifdef HAVE_SYS_WAIT_H 40 | # include 41 | #endif 42 | #ifdef HAVE_ERRNO_H 43 | # include 44 | #endif 45 | 46 | #include "conf.h" 47 | #include "debug.h" 48 | #include "rrd.h" 49 | 50 | /* Global variables */ 51 | struct target *targets=NULL; 52 | struct config default_config={ 53 | NULL,NULL,NULL, /* pool, alarms, targets */ 54 | { /* alarm defaults */ 55 | AL_NONE, /* type */ 56 | "default", /* name */ 57 | NULL, /* mailto */ 58 | "nobody", /* mailfrom */ 59 | NULL, /* mailenvfrom */ 60 | "%r: %T(%t) *** %a ***", /* mailsubject */ 61 | NULL, /* command on */ 62 | NULL, /* command off */ 63 | NULL, /* pipe on */ 64 | NULL, /* pipe off */ 65 | 0, /* combine_interval */ 66 | 0, /* repeat_interval */ 67 | 0, /* repeat_max */ 68 | {}, /* params */ 69 | NULL /* next */ 70 | }, 71 | { /* target defaults */ 72 | "default", /* name */ 73 | "", /* description */ 74 | 1000, /* interval */ 75 | 20, /* avg_delay_samples */ 76 | 5, /* avg_loss_delay_samples */ 77 | 50, /* avg_loss_samples */ 78 | NULL, /* rrd filename */ 79 | 80 | NULL,0,NULL /* alarms, alarms_override, next */ 81 | }, 82 | 0, /* rrd_interval */ 83 | 0, /* debug */ 84 | "nobody", /* user */ 85 | NULL, /* group */ 86 | "/usr/lib/sendmail -t", /* mailer */ 87 | "/var/run/apinger.pid", /* pid file */ 88 | NULL, /* status file */ 89 | 0, /* status interval */ 90 | "%b %d %H:%M:%S" /* timestamp format */ 91 | }; 92 | 93 | int foreground=1; 94 | char *config_file=CONFIG; 95 | 96 | int icmp_sock; 97 | int icmp6_sock; 98 | int ident; 99 | 100 | struct timeval next_probe={0,0}; 101 | 102 | /* Interrupt handler */ 103 | typedef void (*sighandler_t)(int); 104 | volatile int reload_request=0; 105 | volatile int status_request=0; 106 | volatile int interrupted_by=0; 107 | volatile int sigpipe_received=0; 108 | void signal_handler(int signum){ 109 | 110 | if (signum==SIGPIPE){ 111 | signal(SIGPIPE,signal_handler); 112 | sigpipe_received=1; 113 | } 114 | else if (signum==SIGHUP){ 115 | signal(SIGHUP,signal_handler); 116 | reload_request=1; 117 | } 118 | else if (signum==SIGUSR1){ 119 | signal(SIGUSR1,signal_handler); 120 | status_request=1; 121 | } 122 | else{ 123 | interrupted_by=signum; 124 | } 125 | } 126 | 127 | #ifdef FORKED_RECEIVER 128 | void sigchld_handler (int signum) { 129 | int pid, status, serrno; 130 | 131 | serrno = errno; 132 | while (1) { 133 | pid = waitpid (WAIT_ANY, &status, WNOHANG); 134 | if (pid <= 0) break; 135 | } 136 | errno = serrno; 137 | } 138 | #endif 139 | 140 | void usage(const char *name){ 141 | fprintf(stderr,"Alarm Pinger " PACKAGE_VERSION " (c) 2002 Jacek Konieczny \n"); 142 | fprintf(stderr,"Usage:\n"); 143 | fprintf(stderr,"\t%s [-c ] [-f] [-d]\n",name); 144 | fprintf(stderr,"\t%s [-c ] -g [-l ]\n",name); 145 | fprintf(stderr,"\t%s -h\n",name); 146 | fprintf(stderr,"\n"); 147 | fprintf(stderr,"\t-c \talternate config file path.\n"); 148 | fprintf(stderr,"\t-f\trun in foreground.\n"); 149 | fprintf(stderr,"\t-d\tdebug on.\n"); 150 | fprintf(stderr,"\t-g \tgenerate simple rrd-cgi script.\n"); 151 | fprintf(stderr, "\t\t is a directory where generated graph will be stored.\n"); 152 | fprintf(stderr,"\t-l \tHTTP location of generated graphs.\n"); 153 | fprintf(stderr,"\t-h\tthis help message.\n"); 154 | } 155 | 156 | int main(int argc,char *argv[]){ 157 | struct passwd *pw; 158 | struct group *gr; 159 | int c; 160 | FILE *pidfile; 161 | pid_t pid; 162 | int i; 163 | int do_debug=0; 164 | int stay_foreground=0; 165 | char *graph_dir=NULL; 166 | char *graph_location="/apinger/"; 167 | 168 | while((c=getopt(argc,argv,"fdhc:g:l:")) != -1){ 169 | switch(c){ 170 | case 'f': 171 | stay_foreground=1; 172 | break; 173 | case 'd': 174 | do_debug=1; 175 | break; 176 | case 'h': 177 | usage(argv[0]); 178 | return 1; 179 | case 'c': 180 | config_file=optarg; 181 | break; 182 | case 'g': 183 | graph_dir=optarg; 184 | break; 185 | case 'l': 186 | graph_location=optarg; 187 | break; 188 | case ':': 189 | fprintf (stderr, "Command-line option argument missing.\n"); 190 | return 1; 191 | case '?': 192 | fprintf (stderr, "Unknown option `-%c'.\n", optopt); 193 | return 1; 194 | default: 195 | return 1; 196 | } 197 | } 198 | if (load_config(config_file)){ 199 | logit("Couldn't read config (\"%s\").",config_file); 200 | return 1; 201 | } 202 | if (do_debug) config->debug=1; 203 | 204 | if (graph_dir!=NULL) 205 | return rrd_print_cgi(graph_dir,graph_location); 206 | 207 | if (!stay_foreground){ 208 | pidfile=fopen(config->pid_file,"r"); 209 | if (pidfile){ 210 | int n=fscanf(pidfile,"%d",&pid); 211 | if (n>0 && pid>0 && kill(pid,0)==0){ 212 | fprintf(stderr,"pinger already running\n"); 213 | return 1; 214 | } 215 | fclose(pidfile); 216 | } 217 | } 218 | 219 | make_icmp_socket(); 220 | make_icmp6_socket(); 221 | if (icmp6_sock<0 && icmp_sock<0){ 222 | return 1; 223 | } 224 | 225 | pw=getpwnam(config->user); 226 | if (!pw) { 227 | debug("getpwnam(\"%s\") failed.",config->user); 228 | return 1; 229 | } 230 | if (config->group){ 231 | gr=getgrnam(config->group); 232 | if (!gr) { 233 | debug("getgrnam(\"%s\") failed.",config->group); 234 | return 1; 235 | } 236 | } 237 | else gr=NULL; 238 | 239 | 240 | if (!stay_foreground){ 241 | pid=fork(); 242 | if (pid<0){ 243 | perror("fork"); 244 | exit(1); 245 | } 246 | if (pid>0){ /* parent */ 247 | pidfile=fopen(config->pid_file,"w"); 248 | if (!pidfile){ 249 | fprintf(stderr,"Couldn't open pid file for writting."); 250 | perror(config->pid_file); 251 | return 1; 252 | } 253 | fprintf(pidfile,"%i\n",pid); 254 | fchown(fileno(pidfile),pw->pw_uid,gr?gr->gr_gid:pw->pw_gid); 255 | fclose(pidfile); 256 | free_config(); 257 | exit(0); 258 | } 259 | foreground=0; 260 | for(i=0;i<255;i++) 261 | if (i!=icmp_sock && i!=icmp6_sock) 262 | close(i); 263 | setsid(); 264 | } 265 | 266 | if (initgroups(pw->pw_name,pw->pw_gid)){ 267 | myperror("initgroups"); 268 | return 1; 269 | } 270 | if (setgid(pw->pw_gid)){ 271 | myperror("setgid"); 272 | return 1; 273 | } 274 | if (setuid(pw->pw_uid)){ 275 | myperror("setuid"); 276 | return 1; 277 | } 278 | 279 | ident=getpid(); 280 | signal(SIGTERM,signal_handler); 281 | signal(SIGINT,signal_handler); 282 | signal(SIGHUP,signal_handler); 283 | signal(SIGUSR1,signal_handler); 284 | signal(SIGPIPE,signal_handler); 285 | #ifdef FORKED_RECEIVER 286 | signal(SIGCHLD,sigchld_handler); 287 | #endif 288 | main_loop(); 289 | if (icmp_sock>=0) close(icmp_sock); 290 | if (icmp6_sock>=0) close(icmp6_sock); 291 | 292 | logit("Exiting on signal %i.",interrupted_by); 293 | 294 | if (!foreground){ 295 | /*clear the pid file*/ 296 | pidfile=fopen(config->pid_file,"w"); 297 | if (pidfile) fclose(pidfile); 298 | /* try to remove it. Most probably this will fail */ 299 | unlink(config->pid_file); 300 | } 301 | 302 | free_config(); 303 | 304 | return 0; 305 | } 306 | 307 | -------------------------------------------------------------------------------- /patches/apinger-0.3-beep.patch: -------------------------------------------------------------------------------- 1 | Provided by: "Christian W. Zuckschwerdt" 2 | 3 | [...] 4 | >- Integrate audible alarms into apinger. That way there's no need to 5 | > spawn a process on every alarm. 6 | > 7 | >- Call the beeper every n-samples with the current delay value. That way 8 | > an idle machine will tick softly and a busy machine will click more 9 | > notebly. 10 | [...] 11 | >I hacked a beeper program and mashed it into apinger. The patch is 12 | >attached. 13 | > 14 | 15 | diff -ruN apinger-0.3-pristine/src/Makefile.am apinger-0.3-mod/src/Makefile.am 16 | --- apinger-0.3-pristine/src/Makefile.am Thu Jul 18 10:37:07 2002 17 | +++ apinger-0.3-mod/src/Makefile.am Tue Jul 23 18:53:25 2002 18 | @@ -7,6 +7,8 @@ 19 | apinger_SOURCES = \ 20 | apinger.c \ 21 | apinger.h \ 22 | + beeplib.c \ 23 | + beeplib.h \ 24 | cfgparser1.y \ 25 | cfgparser2.l \ 26 | conf.c \ 27 | diff -ruN apinger-0.3-pristine/src/apinger.c apinger-0.3-mod/src/apinger.c 28 | --- apinger-0.3-pristine/src/apinger.c Fri Jul 19 14:05:46 2002 29 | +++ apinger-0.3-mod/src/apinger.c Tue Jul 23 19:25:32 2002 30 | @@ -20,6 +20,7 @@ 31 | 32 | #include "config.h" 33 | #include "apinger.h" 34 | +#include "beeplib.h" 35 | 36 | #include 37 | #ifdef HAVE_STDLIB_H 38 | @@ -317,6 +318,13 @@ 39 | return; 40 | } 41 | } 42 | + if (on>0) command=a->beep_on; 43 | + else command=a->beep_off; 44 | + if (command){ 45 | + command=subst_macros(command,t,a,on); 46 | + debug("Beeping: %s",command); 47 | + beepcmd(command); 48 | + } 49 | if (on>0) command=a->pipe_on; 50 | else command=a->pipe_off; 51 | if (command){ 52 | diff -ruN apinger-0.3-pristine/src/beeplib.c apinger-0.3-mod/src/beeplib.c 53 | --- apinger-0.3-pristine/src/beeplib.c Thu Jan 1 01:00:00 1970 54 | +++ apinger-0.3-mod/src/beeplib.c Tue Jul 23 19:44:40 2002 55 | @@ -0,0 +1,271 @@ 56 | +/* beep - just what it sounds like, makes the console beep - but with 57 | + * precision control. See the man page for details. 58 | + * 59 | + * Try beep -h for command line args 60 | + * 61 | + * This code is copyright (C) Johnathan Nightingale, 2000. 62 | + * 63 | + * This code may distributed only under the terms of the GNU Public License 64 | + * which can be found at http://www.gnu.org/copyleft or in the file COPYING 65 | + * supplied with this code. 66 | + * 67 | + * This code is not distributed with warranties of any kind, including implied 68 | + * warranties of merchantability or fitness for a particular use or ability to 69 | + * breed pandas in captivity, it just can't be done. 70 | + * 71 | + * Bug me, I like it: http://johnath.com/ or johnath@johnath.com 72 | + */ 73 | + 74 | +#include 75 | +#include 76 | +#include 77 | +#include 78 | +#include 79 | +#include 80 | +#include 81 | +#include 82 | +#include 83 | +#include 84 | + 85 | +/* I don't know where this number comes from, I admit that freely. A 86 | + wonderful human named Raine M. Ekman used it in a program that played 87 | + a tune at the console, and apparently, it's how the kernel likes its 88 | + sound requests to be phrased. If you see Raine, thank him for me. 89 | + 90 | + June 28, email from Peter Tirsek (peter at tirsek dot com): 91 | + 92 | + This number represents the fixed frequency of the original PC XT's 93 | + timer chip (the 8254 AFAIR), which is approximately 1.193 MHz. This 94 | + number is divided with the desired frequency to obtain a counter value, 95 | + that is subsequently fed into the timer chip, tied to the PC speaker. 96 | + The chip decreases this counter at every tick (1.193 MHz) and when it 97 | + reaches zero, it toggles the state of the speaker (on/off, or in/out), 98 | + resets the counter to the original value, and starts over. The end 99 | + result of this is a tone at approximately the desired frequency. :) 100 | +*/ 101 | +#ifndef CLOCK_TICK_RATE 102 | +#define CLOCK_TICK_RATE 1193180 103 | +#endif 104 | + 105 | +#define VERSION_STRING "beep-1.2.2" 106 | +char *copyright = 107 | +"Copyright (C) Johnathan Nightingale, 2002. " 108 | +"Use and Distribution subject to GPL. " 109 | +"For information: http://www.gnu.org/copyleft/."; 110 | + 111 | +/* Meaningful Defaults */ 112 | +#define DEFAULT_FREQ 440.0 /* Middle A */ 113 | +#define DEFAULT_LENGTH 200 /* milliseconds */ 114 | +#define DEFAULT_REPS 1 115 | +#define DEFAULT_DELAY 100 /* milliseconds */ 116 | +#define DEFAULT_END_DELAY NO_END_DELAY 117 | +#define DEFAULT_STDIN_BEEP NO_STDIN_BEEP 118 | + 119 | +/* Other Constants */ 120 | +#define NO_END_DELAY 0 121 | +#define YES_END_DELAY 1 122 | + 123 | +#define NO_STDIN_BEEP 0 124 | +#define LINE_STDIN_BEEP 1 125 | +#define CHAR_STDIN_BEEP 2 126 | + 127 | +typedef struct beep_parms_t { 128 | + float freq; /* tone frequency (Hz) */ 129 | + int length; /* tone length (ms) */ 130 | + int reps; /* # of repetitions */ 131 | + int delay; /* delay between reps (ms) */ 132 | + int end_delay; /* do we delay after last rep? */ 133 | + int stdin_beep; /* are we using stdin triggers? We have three options: 134 | + - just beep and terminate (default) 135 | + - beep after a line of input 136 | + - beep after a character of input 137 | + In the latter two cases, pass the text back out again, 138 | + so that beep can be tucked appropriately into a text- 139 | + processing pipe. 140 | + */ 141 | + struct beep_parms_t *next; /* in case -n/--new is used. */ 142 | +} beep_parms_t; 143 | + 144 | +#define MAX_ARGS 50 145 | +#define DELIM " \t\n" 146 | + 147 | +int make_command_line(char ***argv, char *s) { 148 | + int argc; 149 | + char *p; 150 | + 151 | + *argv = calloc(MAX_ARGS, sizeof(char *)); 152 | + 153 | + /* the copy needs to start with non-delim */ 154 | + /* the first token won't be a pointer to the mem otherwise */ 155 | + /*s = strdup(p+strspn(s, DELIM));*/ 156 | + s = strdup(s); 157 | + 158 | + argc = 0; 159 | + (*argv)[argc++] = s; 160 | + for (p=strtok(s, DELIM); p!=NULL; p=strtok(NULL, DELIM)) 161 | + (*argv)[argc++] = p; 162 | + 163 | + return argc; 164 | +} 165 | + 166 | +/* Parse the command line. argv should be untampered, as passed to main. 167 | + * Beep parameters returned in result, subsequent parameters in argv will over- 168 | + * ride previous ones. 169 | + * 170 | + * Currently valid parameters: 171 | + * "-f " 172 | + * "-l " 173 | + * "-r " 174 | + * "-d " 175 | + * "-D " (similar to -d, but delay after last repetition as well) 176 | + * "-s" (beep after each line of input from stdin, echo line to stdout) 177 | + * "-c" (beep after each char of input from stdin, echo char to stdout) 178 | + * "-h/--help" 179 | + * "-v/-V/--version" 180 | + * "-n/--new" 181 | + * 182 | + * March 29, 2002 - Daniel Eisenbud points out that c should be int, not char, 183 | + * for correctness on platforms with unsigned chars. 184 | + */ 185 | +void parse_command_line(int argc, char **argv, beep_parms_t *result) { 186 | + int c; 187 | + optind=0; 188 | + while((c = getopt(argc, argv, "f:l:r:d:D:schvVn")) 189 | + != EOF) { 190 | + int argval = -1; /* handle parsed numbers for various arguments */ 191 | + float argfreq = -1; 192 | + switch(c) { 193 | + case 'f': /* freq */ 194 | + if(!sscanf(optarg, "%f", &argfreq) || (argfreq >= 20000 /* ack! */) || 195 | + (argfreq <= 0)) 196 | + break; 197 | + else 198 | + result->freq = argfreq; 199 | + break; 200 | + case 'l' : /* length */ 201 | + if(!sscanf(optarg, "%d", &argval) || (argval < 0)) 202 | + break; 203 | + else 204 | + result->length = argval; 205 | + break; 206 | + case 'r' : /* repetitions */ 207 | + if(!sscanf(optarg, "%d", &argval) || (argval < 0)) 208 | + break; 209 | + else 210 | + result->reps = argval; 211 | + break; 212 | + case 'd' : /* delay between reps - WITHOUT delay after last beep*/ 213 | + if(!sscanf(optarg, "%d", &argval) || (argval < 0)) 214 | + break; 215 | + else { 216 | + result->delay = argval; 217 | + result->end_delay = NO_END_DELAY; 218 | + } 219 | + break; 220 | + case 'D' : /* delay between reps - WITH delay after last beep */ 221 | + if(!sscanf(optarg, "%d", &argval) || (argval < 0)) 222 | + break; 223 | + else { 224 | + result->delay = argval; 225 | + result->end_delay = YES_END_DELAY; 226 | + } 227 | + break; 228 | + case 's' : 229 | + result->stdin_beep = LINE_STDIN_BEEP; 230 | + break; 231 | + case 'c' : 232 | + result->stdin_beep = CHAR_STDIN_BEEP; 233 | + break; 234 | + case 'v' : 235 | + case 'V' : /* also --version */ 236 | + printf("%s\n",VERSION_STRING); 237 | + exit(0); 238 | + break; 239 | + case 'n' : /* also --new - create another beep */ 240 | + result->next = (beep_parms_t *)malloc(sizeof(beep_parms_t)); 241 | + result->next->freq = DEFAULT_FREQ; 242 | + result->next->length = DEFAULT_LENGTH; 243 | + result->next->reps = DEFAULT_REPS; 244 | + result->next->delay = DEFAULT_DELAY; 245 | + result->next->end_delay = DEFAULT_END_DELAY; 246 | + result->next->stdin_beep = DEFAULT_STDIN_BEEP; 247 | + result->next->next = NULL; 248 | + result = result->next; /* yes, I meant to do that. */ 249 | + break; 250 | + case 'h' : /* notice that this is also --help */ 251 | + default : 252 | + break; 253 | + } 254 | + } 255 | +} 256 | + 257 | +void play_beep(beep_parms_t parms) { 258 | + int console_fd; 259 | + int i; /* loop counter */ 260 | + 261 | + /* try to snag the console */ 262 | + if((console_fd = open("/dev/console", O_WRONLY)) == -1) { 263 | + fprintf(stderr, "Could not open /dev/console for writing.\n"); 264 | + printf("\a"); /* Output the only beep we can, in an effort to fall back on usefulness */ 265 | + perror("open"); 266 | + exit(1); 267 | + } 268 | + 269 | + /* Beep */ 270 | + for (i = 0; i < parms.reps; i++) { /* start beep */ 271 | + if(ioctl(console_fd, KIOCSOUND, (int)(CLOCK_TICK_RATE/parms.freq)) < 0) { 272 | + printf("\a"); /* Output the only beep we can, in an effort to fall back on usefulness */ 273 | + perror("ioctl"); 274 | + } 275 | + /* Look ma, I'm not ansi C compatible! */ 276 | + usleep(1000*parms.length); /* wait... */ 277 | + ioctl(console_fd, KIOCSOUND, 0); /* stop beep */ 278 | + if(parms.end_delay || (i+1 < parms.reps)) 279 | + usleep(1000*parms.delay); /* wait... */ 280 | + } /* repeat. */ 281 | + 282 | + close(console_fd); 283 | +} 284 | + 285 | + 286 | + 287 | +int beepcmd(char *arg) { 288 | + int argc; 289 | + char **argv; 290 | + 291 | + char sin[4096], *ptr; 292 | + 293 | + beep_parms_t *parms = (beep_parms_t *)malloc(sizeof(beep_parms_t)); 294 | + parms->freq = DEFAULT_FREQ; 295 | + parms->length = DEFAULT_LENGTH; 296 | + parms->reps = DEFAULT_REPS; 297 | + parms->delay = DEFAULT_DELAY; 298 | + parms->end_delay = DEFAULT_END_DELAY; 299 | + parms->stdin_beep = DEFAULT_STDIN_BEEP; 300 | + parms->next = NULL; 301 | + 302 | + // arg = strdup("beep -f 440 -l 120 -D 1 -r 3 -n -f 880 -l 240"); 303 | + argc = make_command_line(&argv, arg); 304 | + printf("Beeping: %d with %s, %s, %s, %s, %s,...\n", 305 | + argc, argv[0] , argv[1], argv[2], argv[3], argv[4]); 306 | + 307 | + parse_command_line(argc, argv, parms); 308 | + 309 | + free(*argv); 310 | + free(argv); 311 | + 312 | + /* this outermost while loop handles the possibility that -n/--new has been 313 | + used, i.e. that we have multiple beeps specified. Each iteration will 314 | + play, then free() one parms instance. */ 315 | + while(parms) { 316 | + beep_parms_t *next = parms->next; 317 | + 318 | + play_beep(*parms); 319 | + 320 | + /* Junk each parms struct after playing it */ 321 | + free(parms); 322 | + parms = next; 323 | + } 324 | + 325 | + return EXIT_SUCCESS; 326 | +} 327 | diff -ruN apinger-0.3-pristine/src/beeplib.h apinger-0.3-mod/src/beeplib.h 328 | --- apinger-0.3-pristine/src/beeplib.h Thu Jan 1 01:00:00 1970 329 | +++ apinger-0.3-mod/src/beeplib.h Tue Jul 23 18:52:38 2002 330 | @@ -0,0 +1,19 @@ 331 | +/* beep - just what it sounds like, makes the console beep - but with 332 | + * precision control. See the man page for details. 333 | + * 334 | + * Try beep -h for command line args 335 | + * 336 | + * This code is copyright (C) Johnathan Nightingale, 2000. 337 | + * 338 | + * This code may distributed only under the terms of the GNU Public License 339 | + * which can be found at http://www.gnu.org/copyleft or in the file COPYING 340 | + * supplied with this code. 341 | + * 342 | + * This code is not distributed with warranties of any kind, including implied 343 | + * warranties of merchantability or fitness for a particular use or ability to 344 | + * breed pandas in captivity, it just can't be done. 345 | + * 346 | + * Bug me, I like it: http://johnath.com/ or johnath@johnath.com 347 | + */ 348 | + 349 | +int beepcmd(char *arg); 350 | diff -ruN apinger-0.3-pristine/src/cfgparser1.y apinger-0.3-mod/src/cfgparser1.y 351 | --- apinger-0.3-pristine/src/cfgparser1.y Thu Jul 18 14:32:30 2002 352 | +++ apinger-0.3-mod/src/cfgparser1.y Tue Jul 23 17:35:13 2002 353 | @@ -80,6 +80,7 @@ 354 | %token MAILSUBJECT 355 | %token COMMAND 356 | %token PIPE 357 | +%token BEEP 358 | 359 | %token DOWN 360 | %token LOSS 361 | @@ -222,6 +223,15 @@ 362 | { cur_alarm->pipe_on=$3; } 363 | | PIPE OFF string 364 | { cur_alarm->pipe_off=$3; } 365 | + | BEEP string 366 | + { 367 | + if (cur_alarm->beep_on==NULL) cur_alarm->beep_on=$2; 368 | + if (cur_alarm->beep_off==NULL) cur_alarm->beep_off=$2; 369 | + } 370 | + | BEEP ON string 371 | + { cur_alarm->beep_on=$3; } 372 | + | BEEP OFF string 373 | + { cur_alarm->beep_off=$3; } 374 | 375 | ; 376 | 377 | diff -ruN apinger-0.3-pristine/src/cfgparser2.l apinger-0.3-mod/src/cfgparser2.l 378 | --- apinger-0.3-pristine/src/cfgparser2.l Thu Jul 18 12:35:43 2002 379 | +++ apinger-0.3-mod/src/cfgparser2.l Tue Jul 23 17:33:33 2002 380 | @@ -74,6 +74,7 @@ 381 | avg_delay_samples { LOC; LOCINC; return AVG_DELAY_SAMPLES; } 382 | avg_loss_delay_samples { LOC; LOCINC; return AVG_LOSS_DELAY_SAMPLES; } 383 | avg_loss_samples { LOC; LOCINC; return AVG_LOSS_SAMPLES; } 384 | +beep { LOC; LOCINC; return BEEP; } 385 | command { LOC; LOCINC; return COMMAND; } 386 | debug { LOC; LOCINC; return DEBUG; } 387 | default { LOC; LOCINC; return DEFAULT; } 388 | diff -ruN apinger-0.3-pristine/src/conf.c apinger-0.3-mod/src/conf.c 389 | --- apinger-0.3-pristine/src/conf.c Thu Jul 18 14:32:30 2002 390 | +++ apinger-0.3-mod/src/conf.c Tue Jul 23 18:49:57 2002 391 | @@ -168,6 +168,10 @@ 392 | a->pipe_on=cur_config.alarm_defaults.pipe_on; 393 | if (a->pipe_off==NULL) 394 | a->pipe_off=cur_config.alarm_defaults.pipe_off; 395 | + if (a->beep_on==NULL) 396 | + a->beep_on=cur_config.alarm_defaults.beep_on; 397 | + if (a->beep_off==NULL) 398 | + a->beep_off=cur_config.alarm_defaults.beep_off; 399 | } 400 | for(t=cur_config.targets;t;t=t->next){ 401 | if (t->description==NULL) 402 | diff -ruN apinger-0.3-pristine/src/conf.h apinger-0.3-mod/src/conf.h 403 | --- apinger-0.3-pristine/src/conf.h Thu Jul 18 14:32:30 2002 404 | +++ apinger-0.3-mod/src/conf.h Tue Jul 23 17:33:02 2002 405 | @@ -49,6 +49,8 @@ 406 | char *command_off; 407 | char *pipe_on; 408 | char *pipe_off; 409 | + char *beep_on; 410 | + char *beep_off; 411 | union { 412 | int val; 413 | struct { 414 | diff -ruN apinger-0.3-pristine/src/main.c apinger-0.3-mod/src/main.c 415 | --- apinger-0.3-pristine/src/main.c Thu Jul 18 14:32:30 2002 416 | +++ apinger-0.3-mod/src/main.c Tue Jul 23 19:12:07 2002 417 | @@ -56,6 +56,8 @@ 418 | NULL, /* command off */ 419 | NULL, /* pipe on */ 420 | NULL, /* pipe off */ 421 | + NULL, /* beep on */ 422 | + NULL, /* beep off */ 423 | {}, /* params */ 424 | NULL /* next */ 425 | }, 426 | -------------------------------------------------------------------------------- /src/apinger.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Alarm Pinger (c) 2002 Jacek Konieczny 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License version 2 as published 6 | * by the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | * GNU General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU General Public License 14 | * along with this program; if not, write to the Free Software 15 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 16 | * USA 17 | * 18 | */ 19 | 20 | #include "config.h" 21 | #include "apinger.h" 22 | 23 | #include 24 | #ifdef HAVE_STDLIB_H 25 | # include 26 | #endif 27 | #ifdef HAVE_UNISTD_H 28 | # include 29 | #endif 30 | #ifdef HAVE_STRING_H 31 | # include 32 | #endif 33 | #ifdef HAVE_SYS_WAIT_H 34 | # include 35 | #endif 36 | #ifdef HAVE_SYS_POLL_H 37 | # include 38 | #endif 39 | #ifdef HAVE_ARPA_INET_H 40 | # include 41 | #endif 42 | 43 | #include "debug.h" 44 | #include "tv_macros.h" 45 | #include "rrd.h" 46 | 47 | #ifdef HAVE_ASSERT_H 48 | # include 49 | #else 50 | # define assert(cond) 51 | #endif 52 | 53 | #define MIN(a,b) (((a)<(b))?(a):(b)) 54 | 55 | struct delayed_report { 56 | int on; 57 | char *msgid; 58 | char *lastmsgid; 59 | struct alarm_cfg *a; 60 | struct target t; 61 | struct timeval timestamp; 62 | struct delayed_report *next; 63 | }; 64 | 65 | struct delayed_report *delayed_reports=NULL; 66 | 67 | struct timeval operation_started; 68 | 69 | 70 | int is_alarm_on(struct target *t,struct alarm_cfg *a){ 71 | struct active_alarm_list *al; 72 | 73 | for(al=t->active_alarms;al;al=al->next) 74 | if (al->alarm==a) 75 | return 1; 76 | return 0; 77 | } 78 | 79 | static char msgid_buf[1024]; 80 | static char hostnamebuf[256]="-"; 81 | 82 | char *gen_msgid(struct target *t,char *suff){ 83 | struct timeval cur_time; 84 | 85 | gettimeofday(&cur_time,NULL); 86 | 87 | gethostname(hostnamebuf,sizeof(hostnamebuf)); 88 | sprintf(msgid_buf,"<%p.%li.%s@%s>", 89 | t,(long int)(cur_time.tv_usec/1000+cur_time.tv_sec*1000), 90 | suff,hostnamebuf); 91 | return strdup(msgid_buf); 92 | } 93 | 94 | char *alarm_on(struct target *t,struct alarm_cfg *a){ 95 | struct active_alarm_list *al; 96 | struct timeval cur_time,tv; 97 | 98 | gettimeofday(&cur_time,NULL); 99 | al=NEW(struct active_alarm_list,1); 100 | al->next=t->active_alarms; 101 | al->alarm=a; 102 | al->msgid=gen_msgid(t,"on"); 103 | al->num_repeats=0; 104 | if (a->repeat_interval){ 105 | tv.tv_sec=a->repeat_interval/1000; 106 | tv.tv_usec=(a->repeat_interval%1000)*1000; 107 | timeradd(&cur_time,&tv,&al->next_repeat); 108 | } 109 | t->active_alarms=al; 110 | return strdup(al->msgid); 111 | } 112 | 113 | char *alarm_off(struct target *t,struct alarm_cfg *a){ 114 | struct active_alarm_list *al,*pa,*na; 115 | char *msgid; 116 | 117 | pa=NULL; 118 | for(al=t->active_alarms;al;al=na){ 119 | na=al->next; 120 | if (al->alarm==a){ 121 | if (pa!=NULL) 122 | pa->next=na; 123 | else 124 | t->active_alarms=na; 125 | msgid=al->msgid; 126 | free(al); 127 | return msgid; 128 | } 129 | else pa=al; 130 | } 131 | logit("Alarm '%s' not found in '%s'",a->name,t->name); 132 | return NULL; 133 | } 134 | 135 | static char *macros_buf=NULL; 136 | static int macros_buf_l=0; 137 | const char * subst_macros(const char *string,struct target *t,struct alarm_cfg *a,int on){ 138 | char *p; 139 | int nmacros=0; 140 | int i,sl,l,n; 141 | char **values; 142 | char ps[16],pr[16],al[16],ad[16],ts[100]; 143 | time_t tim; 144 | 145 | if (string==NULL || string[0]=='\000') return ""; 146 | for(i=0;string[i]!='\000';i++){ 147 | if (string[i]!='%') continue; 148 | nmacros++; 149 | i++; 150 | if (string[i]=='\000') break; 151 | } 152 | if (nmacros==0) return string; 153 | values=NEW(char *,(nmacros+1)); 154 | assert(values!=NULL); 155 | l=sl=strlen(string); 156 | n=0; 157 | for(i=0;iname; 163 | break; 164 | case 'T': 165 | values[n]=t->description; 166 | break; 167 | case 'a': 168 | if (a) 169 | values[n]=a->name; 170 | else 171 | values[n]="?"; 172 | break; 173 | case 'A': 174 | if (a) 175 | switch(a->type){ 176 | case AL_DOWN: 177 | values[n]="down"; 178 | break; 179 | case AL_LOSS: 180 | values[n]="loss"; 181 | break; 182 | case AL_DELAY: 183 | values[n]="delay"; 184 | break; 185 | default: 186 | values[n]="unknown"; 187 | break; 188 | } 189 | else 190 | values[n]="?"; 191 | break; 192 | case 'r': 193 | switch(on){ 194 | case -1: 195 | values[n]="alarm canceled (config reload)"; 196 | break; 197 | case 0: 198 | values[n]="alarm canceled"; 199 | break; 200 | default: 201 | values[n]="ALARM"; 202 | break; 203 | } 204 | break; 205 | case 'p': 206 | sprintf(ps,"%i",t->last_sent); 207 | values[n]=ps; 208 | break; 209 | case 'P': 210 | sprintf(pr,"%i",t->received); 211 | values[n]=pr; 212 | break; 213 | case 'l': 214 | if (AVG_LOSS_KNOWN(t)){ 215 | sprintf(al,"%0.1f%%",AVG_LOSS(t)); 216 | values[n]=al; 217 | } 218 | else values[n]="n/a"; 219 | break; 220 | case 'd': 221 | if (AVG_DELAY_KNOWN(t)){ 222 | sprintf(ad,"%0.3fms",AVG_DELAY(t)); 223 | values[n]=ad; 224 | } 225 | else values[n]="n/a"; 226 | break; 227 | case 's': 228 | tim=time(NULL); 229 | strftime(ts,100,config->timestamp_format,localtime(&tim)); 230 | values[n]=ts; 231 | break; 232 | case '%': 233 | values[n]="%"; 234 | break; 235 | default: 236 | values[n]=""; 237 | break; 238 | } 239 | l+=strlen(values[n])-1; 240 | n++; 241 | } 242 | values[n]=NULL; 243 | l+=2; 244 | if (macros_buf == NULL){ 245 | macros_buf=NEW(char,l); 246 | macros_buf_l=l; 247 | }else if (macros_buf_l < l){ 248 | macros_buf=(char *)realloc(macros_buf,l); 249 | macros_buf_l=l; 250 | } 251 | assert(macros_buf!=NULL); 252 | p=macros_buf; 253 | n=0; 254 | for(i=0;iname); 276 | else 277 | fprintf(f,"alarm canceled: %s\n",a->name); 278 | fprintf(f,"Target: %s\n",t->name); 279 | fprintf(f,"Description: %s\n",t->description); 280 | fprintf(f,"Probes sent: %i\n",t->last_sent+1); 281 | fprintf(f,"Replies received: %i\n",t->received); 282 | fprintf(f,"Last reply received: #%i %s",t->last_received, 283 | ctime(&t->last_received_tv.tv_sec)); 284 | if (AVG_DELAY_KNOWN(t)){ 285 | fprintf(f,"Recent avg. delay: %4.3fms\n",AVG_DELAY(t)); 286 | } 287 | if (AVG_LOSS_KNOWN(t)){ 288 | fprintf(f,"Recent avg. packet loss: %5.1f%%\n",AVG_LOSS(t)); 289 | } 290 | } 291 | 292 | void make_reports(struct target *t,struct alarm_cfg *a,int on,char* thisid,char* lastid){ 293 | FILE *p; 294 | char buf[1024]; 295 | char *mailto,*mailfrom,*mailenvfrom; 296 | int ret; 297 | const char *command; 298 | 299 | mailto=a->mailto; 300 | mailenvfrom=a->mailenvfrom; 301 | if (mailenvfrom!=NULL && strpbrk(mailenvfrom,"\\'")!=0) 302 | mailenvfrom=NULL; 303 | mailfrom=a->mailfrom; 304 | if (mailto){ 305 | if (mailenvfrom){ 306 | snprintf(buf,1024,"%s -f'%s'",config->mailer,mailenvfrom); 307 | } 308 | else{ 309 | snprintf(buf,1024,"%s",config->mailer); 310 | } 311 | debug("Popening: %s",buf); 312 | p=popen(buf,"w"); 313 | if (!p){ 314 | myperror("Couldn't send mail, popen:"); 315 | return; 316 | } 317 | fprintf(p,"Subject: %s\n",subst_macros(a->mailsubject,t,a,on)); 318 | fprintf(p,"To: %s\n",subst_macros(mailto,t,a,on)); 319 | if (mailfrom) { 320 | fprintf(p,"From: %s\n",subst_macros(mailfrom,t,a,on)); 321 | } 322 | if (thisid!=NULL) 323 | fprintf(p,"Message-Id: %s\n",thisid); 324 | if (lastid!=NULL && lastid[0]!='\000') 325 | fprintf(p,"References: %s\n",lastid); 326 | 327 | fprintf(p,"\n"); 328 | write_report(p,t,a,on); 329 | ret=pclose(p); 330 | if (!WIFEXITED(ret)){ 331 | logit("Error while sending mail.\n"); 332 | logit("sendmail terminated abnormally.\n"); 333 | } 334 | else if (WEXITSTATUS(ret)!=0){ 335 | logit("Error while sending mail.\n"); 336 | logit("sendmail exited with status: %i\n",WEXITSTATUS(ret)); 337 | } 338 | } 339 | if (on>0) command=a->pipe_on; 340 | else command=a->pipe_off; 341 | if (command){ 342 | command=subst_macros(command,t,a,on); 343 | debug("Popening: %s",command); 344 | p=popen(command,"w"); 345 | if (!p){ 346 | logit("Couldn't pipe report through %s",command); 347 | myperror("popen"); 348 | } 349 | else { 350 | write_report(p,t,a,on); 351 | ret=pclose(p); 352 | if (!WIFEXITED(ret)){ 353 | logit("Error while piping report."); 354 | logit("command (%s) terminated abnormally.",command); 355 | } 356 | else if (WEXITSTATUS(ret)!=0){ 357 | logit("Error while piping report."); 358 | logit("command (%s) exited with status: %i",command,WEXITSTATUS(ret)); 359 | } 360 | } 361 | } 362 | if (on>0) command=a->command_on; 363 | else command=a->command_off; 364 | if (command){ 365 | command=subst_macros(command,t,a,on); 366 | debug("Starting: %s",command); 367 | ret=system(command); 368 | if (!WIFEXITED(ret)){ 369 | logit("Error while starting command."); 370 | logit("command (%s) terminated abnormally.",command); 371 | } 372 | else if (WEXITSTATUS(ret)!=0){ 373 | logit("Error while starting command."); 374 | logit("command (%s) exited with status: %i",command,WEXITSTATUS(ret)); 375 | } 376 | } 377 | } 378 | 379 | void make_delayed_reports(void){ 380 | struct alarm_cfg *alarm; 381 | struct target target; 382 | int on; 383 | char *msgid,*references; 384 | struct delayed_report *dr,*pdr,*ndr; 385 | int names_len,descriptions_len,references_len; 386 | char *names,*descriptions; 387 | 388 | 389 | if (delayed_reports==NULL) return; 390 | on=delayed_reports->on; 391 | msgid=strdup(delayed_reports->msgid); 392 | alarm=delayed_reports->a; 393 | target=delayed_reports->t; 394 | names_len=descriptions_len=references_len=0; 395 | for(dr=delayed_reports;dr;dr=dr->next){ 396 | if (dr->a==alarm && dr->on==on){ 397 | names_len+=strlen(dr->t.name)+1; 398 | descriptions_len+=strlen(dr->t.description)+1; 399 | if (dr->lastmsgid){ 400 | references_len+=strlen(dr->lastmsgid)+1; 401 | } 402 | } 403 | } 404 | 405 | names=NEW(char,names_len+1); 406 | names[0]='\000'; 407 | descriptions=NEW(char,descriptions_len+1); 408 | descriptions[0]='\000'; 409 | references=NEW(char,references_len+1); 410 | references[0]='\000'; 411 | 412 | pdr=NULL; 413 | for(dr=delayed_reports;dr;dr=ndr){ 414 | ndr=dr->next; 415 | if (dr->a==alarm && dr->on==on){ 416 | if (on){ 417 | struct active_alarm_list *al; 418 | struct target *t; 419 | for(t=targets;t;t=t->next){ 420 | if (strcmp(dr->t.name,t->name)) continue; 421 | for(al=t->active_alarms;al;al=al->next){ 422 | if (al->alarm==alarm){ 423 | if (al->msgid!=NULL) free(al->msgid); 424 | al->msgid=strdup(msgid); 425 | } 426 | } 427 | break; 428 | } 429 | } 430 | if (names[0]!='\000') strcat(names,","); 431 | strcat(names,dr->t.name); 432 | if (descriptions[0]!='\000') strcat(descriptions,","); 433 | strcat(descriptions,dr->t.description); 434 | if (dr->lastmsgid){ 435 | if (references[0]!='\000') strcat(references," "); 436 | strcat(references,dr->lastmsgid); 437 | } 438 | if (pdr!=NULL) 439 | pdr->next=ndr; 440 | else 441 | delayed_reports=ndr; 442 | free(dr->msgid); 443 | free(dr->lastmsgid); 444 | free(dr); 445 | } 446 | else pdr=dr; 447 | } 448 | 449 | target.name=names; 450 | target.description=descriptions; 451 | 452 | make_reports(&target,alarm,on,msgid,references); 453 | 454 | free(msgid); 455 | free(names); 456 | free(descriptions); 457 | free(references); 458 | } 459 | 460 | void toggle_alarm(struct target *t,struct alarm_cfg *a,int on){ 461 | char *thisid=NULL,*lastid=NULL; 462 | struct delayed_report *dr,*tdr; 463 | 464 | if (on>0){ 465 | logit("ALARM: %s(%s) *** %s ***",t->description,t->name,a->name); 466 | thisid=alarm_on(t,a); 467 | } 468 | else{ 469 | lastid=alarm_off(t,a); 470 | thisid=gen_msgid(t,"off"); 471 | if (on==0) 472 | logit("alarm canceled: %s(%s) *** %s ***",t->description,t->name,a->name); 473 | else 474 | logit("alarm canceled (config reload): %s(%s) *** %s ***",t->description,t->name,a->name); 475 | } 476 | 477 | if (a->combine_interval>0){ 478 | for(tdr=delayed_reports;tdr!=NULL && tdr->next!=NULL;tdr=tdr->next){ 479 | if (strcmp(tdr->t.name,t->name)==0 && tdr->a==a && tdr->on==on) return; 480 | } 481 | if (tdr!=NULL && strcmp(tdr->t.name,t->name)==0 && tdr->a==a && tdr->on==on) return; 482 | dr=NEW(struct delayed_report,1); 483 | assert(dr!=NULL); 484 | dr->t=*t; 485 | dr->a=a; 486 | dr->msgid=strdup(thisid); 487 | if (lastid) dr->lastmsgid=strdup(lastid); 488 | else dr->lastmsgid=NULL; 489 | dr->on=on; 490 | gettimeofday(&dr->timestamp,NULL); 491 | dr->next=NULL; 492 | if (tdr==NULL) 493 | delayed_reports=dr; 494 | else 495 | tdr->next=dr; 496 | } 497 | else make_reports(t,a,on,thisid,lastid); 498 | free(thisid); 499 | free(lastid); 500 | } 501 | 502 | /* if a time came for the next event schedule next one in given interval and return 1 */ 503 | int scheduled_event(struct timeval *next_event,struct timeval *cur_time,int interval){ 504 | struct timeval ct,tv; 505 | int ret; 506 | 507 | if (cur_time==NULL){ 508 | gettimeofday(&ct,NULL); 509 | cur_time=&ct; 510 | } 511 | if (!timerisset(next_event) || timercmp(next_event,cur_time,<)){ 512 | if (!timerisset(next_event)){ 513 | *next_event=*cur_time; 514 | } 515 | tv.tv_sec=interval/1000; 516 | tv.tv_usec=(interval%1000)*1000; 517 | timeradd(cur_time,&tv,next_event); 518 | ret=1; 519 | } 520 | else { 521 | ret=0; 522 | } 523 | if (!timerisset(&next_probe) || timercmp(next_event,&next_probe,<)) 524 | next_probe=*next_event; 525 | return ret; 526 | } 527 | 528 | void send_probe(struct target *t){ 529 | int i,i1; 530 | char buf[100]; 531 | int seq; 532 | 533 | seq=++t->last_sent; 534 | debug("Sending ping #%i to %s (%s)",seq,t->description,t->name); 535 | strftime(buf,100,"%b %d %H:%M:%S",localtime(&t->next_probe.tv_sec)); 536 | debug("Next one scheduled for %s",buf); 537 | if (t->addr.addr.sa_family==AF_INET) send_icmp_probe(t,seq); 538 | #ifdef HAVE_IPV6 539 | else if (t->addr.addr.sa_family==AF_INET6) send_icmp6_probe(t,seq); 540 | #endif 541 | 542 | i=t->last_sent%(t->config->avg_loss_delay_samples+t->config->avg_loss_samples); 543 | if (t->last_sent>t->config->avg_loss_delay_samples+t->config->avg_loss_samples){ 544 | if (!t->queue[i]) t->recently_lost--; 545 | } 546 | t->queue[i]=0; 547 | 548 | if (t->last_sent>t->config->avg_loss_delay_samples){ 549 | i1=(t->last_sent-t->config->avg_loss_delay_samples) 550 | %(t->config->avg_loss_delay_samples+t->config->avg_loss_samples); 551 | if (!t->queue[i1]) t->recently_lost++; 552 | debug("Recently lost packets: %i",t->recently_lost); 553 | } 554 | 555 | t->upsent++; 556 | } 557 | 558 | 559 | void analyze_reply(struct timeval time_recv,int icmp_seq,struct trace_info *ti){ 560 | struct target *t; 561 | struct timeval tv; 562 | double delay,avg_delay,avg_loss; 563 | double tmp; 564 | int i; 565 | int previous_received; 566 | struct alarm_list *al; 567 | struct active_alarm_list *aal,*paa,*naa; 568 | struct alarm_cfg *a; 569 | 570 | if (icmp_seq!=(ti->seq%65536)){ 571 | debug("Sequence number mismatch."); 572 | return; 573 | } 574 | 575 | for(t=targets;t!=NULL;t=t->next){ 576 | if (t==ti->target_id) break; 577 | } 578 | if (t==NULL){ 579 | logit("Couldn't match any target to the echo reply.\n"); 580 | return; 581 | } 582 | previous_received=t->last_received; 583 | if (ti->seq>t->last_received) t->last_received=ti->seq; 584 | t->last_received_tv=time_recv; 585 | timersub(&time_recv,&ti->timestamp,&tv); 586 | delay=tv.tv_sec*1000.0+((double)tv.tv_usec)/1000.0; 587 | debug("#%i from %s(%s) delay: %4.3fms",ti->seq,t->description,t->name,delay); 588 | if (t->received >= t->config->avg_delay_samples) 589 | tmp=t->rbuf[t->received%t->config->avg_delay_samples]; 590 | else 591 | tmp=0; 592 | t->rbuf[t->received%t->config->avg_delay_samples]=delay; 593 | t->delay_sum+=delay-tmp; 594 | t->received++; 595 | 596 | avg_delay=AVG_DELAY(t); 597 | debug("(avg: %4.3fms)",avg_delay); 598 | 599 | i=ti->seq%(t->config->avg_loss_delay_samples+t->config->avg_loss_samples); 600 | if (!t->queue[i] && ti->seq<=t->last_sent-t->config->avg_loss_delay_samples) 601 | t->recently_lost--; 602 | t->queue[i]=1; 603 | 604 | if (AVG_LOSS_KNOWN(t)){ 605 | avg_loss=AVG_LOSS(t); 606 | }else 607 | avg_loss=0; 608 | 609 | debug("(avg. loss: %5.1f%%)",avg_loss); 610 | 611 | paa=NULL; 612 | for(aal=t->active_alarms;aal;aal=naa){ 613 | naa=aal->next; 614 | a=aal->alarm; 615 | if (a->type==AL_DOWN){ 616 | t->upsent=0; 617 | avg_loss=0; 618 | } 619 | if ((a->type==AL_DOWN) 620 | || (a->type==AL_DELAY && avg_delayp.lh.low) 621 | || (a->type==AL_LOSS && avg_lossp.lh.low) ){ 622 | toggle_alarm(t,a,0); 623 | } 624 | } 625 | 626 | for(al=t->config->alarms;al;al=al->next){ 627 | a=al->alarm; 628 | if (is_alarm_on(t,a)) continue; 629 | switch(a->type){ 630 | case AL_DELAY: 631 | if (AVG_DELAY_KNOWN(t) && avg_delay>a->p.lh.high ) 632 | toggle_alarm(t,a,1); 633 | break; 634 | case AL_LOSS: 635 | if ( avg_loss > a->p.lh.high ) 636 | toggle_alarm(t,a,1); 637 | break; 638 | default: 639 | break; 640 | } 641 | } 642 | } 643 | 644 | void configure_targets(void){ 645 | struct target *t,*pt,*nt; 646 | struct target_cfg *tc; 647 | struct active_alarm_list *al,*nal; 648 | union addr addr; 649 | int r; 650 | int l; 651 | 652 | /* delete all not configured targets */ 653 | pt=NULL; 654 | for(t=targets;t;t=nt){ 655 | for(tc=config->targets;tc;tc=tc->next) 656 | if (strcmp(tc->name,t->name)==0) 657 | break; 658 | nt=t->next; 659 | if (tc==NULL){ 660 | if (pt==NULL) 661 | targets=t; 662 | else 663 | pt->next=nt; 664 | for(al=t->active_alarms;al;al=nal){ 665 | nal=al->next; 666 | free(al); 667 | } 668 | free(t->queue); 669 | free(t->rbuf); 670 | free(t->name); 671 | free(t->description); 672 | free(t); 673 | } 674 | else pt=t; 675 | } 676 | 677 | /* Update target configuration */ 678 | for(tc=config->targets;tc;tc=tc->next){ 679 | for(t=targets;t;t=t->next) 680 | if (!strcmp(t->name,tc->name)) 681 | break; 682 | if (t==NULL) { /* new target */ 683 | memset(&addr,0,sizeof(addr)); 684 | r=inet_pton(AF_INET,tc->name,&addr.addr4.sin_addr); 685 | if (r){ 686 | if (icmp_sock<0){ 687 | logit("Sorry, IPv4 is not available\n"); 688 | logit("Ignoring target %s\n",tc->name); 689 | continue; 690 | } 691 | addr.addr.sa_family=AF_INET; 692 | }else{ 693 | #ifdef HAVE_IPV6 694 | r=inet_pton(AF_INET6,tc->name,&addr.addr6.sin6_addr); 695 | if (r==0){ 696 | #endif 697 | logit("Bad host address: %s\n",tc->name); 698 | logit("Ignoring target %s\n",tc->name); 699 | continue; 700 | #ifdef HAVE_IPV6 701 | } 702 | if (icmp6_sock<0){ 703 | logit("Sorry, IPv6 is not available\n"); 704 | logit("Ignoring target %s\n",tc->name); 705 | continue; 706 | } 707 | addr.addr.sa_family=AF_INET6; 708 | #endif 709 | } 710 | t=NEW(struct target,1); 711 | memset(t,0,sizeof(struct target)); 712 | t->name=strdup(tc->name); 713 | t->description=strdup(tc->description); 714 | t->addr=addr; 715 | t->next=targets; 716 | targets=t; 717 | } 718 | t->config=tc; 719 | l=tc->avg_loss_delay_samples+tc->avg_loss_samples; 720 | if (t->queue) 721 | t->queue=(char *)realloc(t->queue,l); 722 | else 723 | t->queue=NEW(char,l); 724 | assert(t->queue!=NULL); 725 | memset(t->queue,0,l); 726 | /* t->recently_lost=tc->avg_loss_samples; */ 727 | l=tc->avg_delay_samples; 728 | if (t->rbuf) 729 | t->rbuf=(double *)realloc(t->rbuf,l); 730 | else 731 | t->rbuf=NEW(double,l); 732 | assert(t->rbuf!=NULL); 733 | memset(t->rbuf,0,l); 734 | } 735 | if (targets==NULL){ 736 | logit("No usable targets found, exiting"); 737 | exit(1); 738 | } 739 | gettimeofday(&operation_started,NULL); 740 | if (config->rrd_interval) 741 | rrd_create(); 742 | } 743 | 744 | void free_targets(void){ 745 | struct target *t,*nt; 746 | struct active_alarm_list *al,*nal; 747 | 748 | /* delete all not configured targets */ 749 | for(t=targets;t;t=nt){ 750 | nt=t->next; 751 | for(al=t->active_alarms;al;al=nal){ 752 | nal=al->next; 753 | free(al); 754 | } 755 | free(t->queue); 756 | free(t->rbuf); 757 | free(t->name); 758 | free(t->description); 759 | free(t); 760 | } 761 | } 762 | 763 | 764 | void reload_config(void){ 765 | struct target *t; 766 | struct active_alarm_list *al,*an; 767 | struct alarm_cfg *a; 768 | int r; 769 | 770 | while(delayed_reports!=NULL) make_delayed_reports(); 771 | for(t=targets;t;t=t->next) 772 | for(al=t->active_alarms;al;al=an){ 773 | an=al->next; 774 | a=al->alarm; 775 | toggle_alarm(t,a,-1); 776 | } 777 | r=load_config(config_file); 778 | if (r==0) configure_targets(); 779 | } 780 | 781 | void write_status(void){ 782 | FILE *f; 783 | struct target *t; 784 | struct active_alarm_list *al; 785 | struct alarm_cfg *a; 786 | time_t tm; 787 | int i,qp,really_lost; 788 | char *buf1,*buf2; 789 | int err=0; 790 | 791 | if (config->status_file==NULL) return; 792 | 793 | f=fopen(config->status_file,"w"); 794 | if (f==NULL){ 795 | logit("Couldn't open status file"); 796 | myperror(config->status_file); 797 | return; 798 | } 799 | tm=time(NULL); 800 | fprintf(f,"%s\n",ctime(&tm)); 801 | for(t=targets;t;t=t->next){ 802 | fprintf(f,"Target: %s\n",t->name); 803 | fprintf(f,"Description: %s\n",t->description); 804 | fprintf(f,"Last reply received: #%i %s",t->last_received, 805 | ctime(&t->last_received_tv.tv_sec)); 806 | fprintf(f,"Average delay: %0.3fms\n",AVG_DELAY(t)); 807 | if (AVG_LOSS_KNOWN(t)){ 808 | fprintf(f,"Average packet loss: %0.1f%%\n",AVG_LOSS(t)); 809 | } 810 | fprintf(f,"Active alarms:"); 811 | if (t->active_alarms){ 812 | for(al=t->active_alarms;al;al=al->next){ 813 | a=al->alarm; 814 | fprintf(f," \"%s\"",a->name); 815 | } 816 | fprintf(f,"\n"); 817 | } 818 | else fprintf(f," None\n"); 819 | 820 | buf1=NEW(char,t->config->avg_loss_delay_samples+1); 821 | buf2=NEW(char,t->config->avg_loss_samples+1); 822 | i=t->last_sent%(t->config->avg_loss_delay_samples+t->config->avg_loss_samples); 823 | for(i=0;iconfig->avg_loss_delay_samples;i++){ 824 | if (i>=t->last_sent){ 825 | buf1[t->config->avg_loss_delay_samples-i-1]='-'; 826 | continue; 827 | } 828 | qp=(t->last_sent-i)%(t->config->avg_loss_delay_samples+ 829 | t->config->avg_loss_samples); 830 | if (t->queue[qp]) 831 | buf1[t->config->avg_loss_delay_samples-i-1]='#'; 832 | else 833 | buf1[t->config->avg_loss_delay_samples-i-1]='.'; 834 | } 835 | buf1[i]=0; 836 | really_lost=0; 837 | for(i=0;iconfig->avg_loss_samples;i++){ 838 | if (i>=t->last_sent-t->config->avg_loss_delay_samples){ 839 | buf2[t->config->avg_loss_samples-i-1]='-'; 840 | continue; 841 | } 842 | qp=(t->last_sent-i-t->config->avg_loss_delay_samples) 843 | %(t->config->avg_loss_delay_samples+t->config->avg_loss_samples); 844 | if (t->queue[qp]) 845 | buf2[t->config->avg_loss_samples-i-1]='#'; 846 | else{ 847 | buf2[t->config->avg_loss_samples-i-1]='.'; 848 | really_lost++; 849 | } 850 | } 851 | buf2[i]=0; 852 | fprintf(f,"Received packets buffer: %s %s\n",buf2,buf1); 853 | if (t->recently_lost!=really_lost){ 854 | fprintf(f," lost packet count mismatch (%i!=%i)!\n",t->recently_lost,really_lost); 855 | logit("%s: Lost packet count mismatch (%i!=%i)!",t->name,t->recently_lost,really_lost); 856 | logit("%s: Received packets buffer: %s %s\n",t->name,buf2,buf1); 857 | err=1; 858 | } 859 | free(buf1); 860 | free(buf2); 861 | 862 | fprintf(f,"\n"); 863 | } 864 | fclose(f); 865 | if (err) abort(); 866 | } 867 | 868 | #ifdef FORKED_RECEIVER 869 | int receiver_pipe=0; 870 | 871 | void pipe_reply(struct timeval time_recv,int icmp_seq,struct trace_info *ti){ 872 | struct piped_info pi; 873 | 874 | pi.recv_timestamp=time_recv; 875 | pi.icmp_seq=icmp_seq; 876 | pi.ti=*ti; 877 | write(receiver_pipe,&pi,sizeof(pi)); 878 | } 879 | 880 | void receiver_loop(void){ 881 | struct pollfd pfd[2]; 882 | int npfd=0; 883 | int i; 884 | 885 | signal(SIGTERM,SIG_DFL); 886 | signal(SIGINT,SIG_DFL); 887 | signal(SIGHUP,SIG_DFL); 888 | signal(SIGUSR1,SIG_DFL); 889 | signal(SIGPIPE,SIG_DFL); 890 | 891 | if (icmp_sock){ 892 | pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL; 893 | pfd[npfd].revents=0; 894 | pfd[npfd++].fd=icmp_sock; 895 | } 896 | if (icmp6_sock){ 897 | pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL; 898 | pfd[npfd++].fd=icmp6_sock; 899 | pfd[npfd].revents=0; 900 | } 901 | while(1){ 902 | poll(pfd,npfd,-1); 903 | for(i=0;istatus_interval){ 966 | gettimeofday(&cur_time,NULL); 967 | tv.tv_sec=config->status_interval/1000; 968 | tv.tv_usec=(config->status_interval%1000)*1000; 969 | timeradd(&cur_time,&tv,&next_status); 970 | } 971 | while(!interrupted_by){ 972 | gettimeofday(&cur_time,NULL); 973 | if ( !timercmp(&next_probe,&cur_time,>) ) 974 | timerclear(&next_probe); 975 | for(t=targets;t;t=t->next){ 976 | for(al=t->config->alarms;al;al=nal){ 977 | a=al->alarm; 978 | nal=al->next; 979 | if (a->type!=AL_DOWN || is_alarm_on(t,a)) continue; 980 | if (timerisset(&t->last_received_tv)){ 981 | timersub(&cur_time,&t->last_received_tv,&tv); 982 | } 983 | else { 984 | timersub(&cur_time,&operation_started,&tv); 985 | } 986 | downtime=tv.tv_sec*1000+tv.tv_usec/1000; 987 | if ( downtime > a->p.val) 988 | toggle_alarm(t,a,1); 989 | } 990 | if (scheduled_event(&(t->next_probe),&cur_time,t->config->interval)){ 991 | send_probe(t); 992 | } 993 | for(aal=t->active_alarms;aal;aal=aal->next){ 994 | char *msgid; 995 | char buf[100]; 996 | a=aal->alarm; 997 | if (a->repeat_interval<=0) 998 | continue; 999 | if (!scheduled_event(&aal->next_repeat,&cur_time,a->repeat_interval)) 1000 | continue; 1001 | if (a->repeat_max && aal->num_repeats>=a->repeat_max) 1002 | continue; 1003 | aal->num_repeats++; 1004 | debug("Repeating reports..."); 1005 | sprintf(buf,"%i",aal->num_repeats); 1006 | msgid=gen_msgid(t,buf); 1007 | make_reports(t,a,1,msgid,aal->msgid); 1008 | free(msgid); 1009 | } 1010 | } 1011 | if (config->status_interval){ 1012 | if (scheduled_event(&next_status,&cur_time,config->status_interval)){ 1013 | if (config->status_file) write_status(); 1014 | status_request=0; 1015 | } 1016 | } 1017 | if (config->rrd_interval){ 1018 | if (scheduled_event(&next_rrd_update,&cur_time,config->rrd_interval)){ 1019 | rrd_update(); 1020 | } 1021 | } 1022 | if (delayed_reports){ 1023 | if (timerisset(&next_report) && timercmp(&next_report,&cur_time,<)){ 1024 | make_delayed_reports(); 1025 | timerclear(&next_report); 1026 | } 1027 | } 1028 | if (delayed_reports){ 1029 | if (!timerisset(&next_report)){ 1030 | tv.tv_sec=delayed_reports->a->combine_interval/1000; 1031 | tv.tv_usec=(delayed_reports->a->combine_interval%1000)*1000; 1032 | timeradd(&delayed_reports->timestamp,&tv,&next_report); 1033 | } 1034 | if (!timerisset(&next_probe) || timercmp(&next_report,&next_probe,<)) 1035 | next_probe=next_report; 1036 | } 1037 | strftime(buf,100,"%b %d %H:%M:%S",localtime(&next_probe.tv_sec)); 1038 | debug("Next event scheduled for %s",buf); 1039 | gettimeofday(&cur_time,NULL); 1040 | if (timercmp(&next_probe,&cur_time,<)){ 1041 | timeout=0; 1042 | } 1043 | else{ 1044 | timersub(&next_probe,&cur_time,&tv); 1045 | timeout=tv.tv_usec/1000+tv.tv_sec*1000; 1046 | } 1047 | debug("Polling, timeout: %5.3fs",((double)timeout)/1000); 1048 | poll(pfd,npfd,timeout); 1049 | for(i=0;istatus_file){ 1066 | logit("SIGUSR1 received, writing status."); 1067 | write_status(); 1068 | } 1069 | } 1070 | if (reload_request){ 1071 | reload_request=0; 1072 | logit("SIGHUP received, reloading configuration."); 1073 | reload_config(); 1074 | } 1075 | } 1076 | #ifdef FORKED_RECEIVER 1077 | kill(pid,SIGTERM); 1078 | #endif 1079 | while(delayed_reports!=NULL) make_delayed_reports(); 1080 | free_targets(); 1081 | if (macros_buf!=NULL) free(macros_buf); 1082 | } 1083 | 1084 | --------------------------------------------------------------------------------