apinger: improve uci and procd support
authorJaymin Patel <jem.patel@gmail.com>
Wed, 6 Jul 2022 09:45:59 +0000 (15:15 +0530)
committerJaymin Patel <jem.patel@gmail.com>
Tue, 12 Jul 2022 06:57:05 +0000 (12:27 +0530)
- convert apinger into procd instances
- generate instance specific apinger.conf from uci
- hotplug handling for apinger alarms
- restart apinger interface instance on ifup action of interface
- don't exit on packet count mismatch, allows to use apinger as monitor
  for multiple targets handling
- add srcip option to target configuration, allows specifying source ip
  used to monitor target
- allow creating status file in script parseable format

Patches are ported against latest version of apinger and referenced from
https://git.pld-linux.org/?p=packages/apinger.git;a=summary

Signed-off-by: Jaymin Patel <jem.patel@gmail.com>
net/apinger/Makefile
net/apinger/files/apinger-hotplug [new file with mode: 0644]
net/apinger/files/apinger.config [new file with mode: 0644]
net/apinger/files/apinger.init
net/apinger/files/iface.hotplug [new file with mode: 0644]
net/apinger/files/user.hotplug [new file with mode: 0644]
net/apinger/patches/030-apinger-no_exit.patch [new file with mode: 0644]
net/apinger/patches/040-srcip.patch [new file with mode: 0644]
net/apinger/patches/050-statusformat.patch [new file with mode: 0644]

index f6837cec410c192d2fdaad8dc5acb4410351188a..c0b232d15875155f9e5f45434e6003fe3c926bba 100644 (file)
@@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
 PKG_NAME:=apinger
 PKG_SOURCE_DATE:=2015-04-09
 PKG_SOURCE_VERSION:=78eb328721ba1a10571c19df95acddcb5f0c17c8
-PKG_RELEASE:=2
+PKG_RELEASE:=$(AUTORELEASE)
 
 PKG_SOURCE_PROTO:=git
 PKG_SOURCE_URL:=https://github.com/Jajcus/apinger
@@ -46,7 +46,8 @@ define Package/apinger/description
 endef
 
 define Package/apinger/conffiles
-/etc/apinger.conf
+/etc/config/apinger
+/etc/apinger.user
 endef
 
 define Package/apinger/install
@@ -56,6 +57,14 @@ define Package/apinger/install
        $(INSTALL_DATA) $(PKG_BUILD_DIR)/src/apinger.conf $(1)/etc/apinger.conf
        $(INSTALL_DIR) $(1)/etc/init.d
        $(INSTALL_BIN) ./files/apinger.init $(1)/etc/init.d/apinger
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_DATA) ./files/apinger.config $(1)/etc/config/apinger
+       $(INSTALL_DIR) $(1)/usr/libexec
+       $(INSTALL_BIN) ./files/apinger-hotplug $(1)/usr/libexec/apinger-hotplug
+       $(INSTALL_DIR) $(1)/etc/hotplug.d/apinger
+       $(INSTALL_DATA) ./files/user.hotplug $(1)/etc/hotplug.d/apinger/01-user
+       $(INSTALL_DIR) $(1)/etc/hotplug.d/iface
+       $(INSTALL_DATA) ./files/iface.hotplug $(1)/etc/hotplug.d/iface/25-apinger
 endef
 
 $(eval $(call BuildPackage,apinger))
diff --git a/net/apinger/files/apinger-hotplug b/net/apinger/files/apinger-hotplug
new file mode 100644 (file)
index 0000000..70a29da
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+usage_help()
+{
+       echo "$0 <action> <instance> <target> <srcip> <target_desc> <alarm> <alarmtype> <reason> <send> <recieved> <loss> <delay> <timestamp>"
+}
+
+export ACTION=$1
+export INSTANCE=$2
+export APINGER_TARGET=$3
+export APINGER_SRCIP=$4
+export APINGER_TARGET_ID=$5
+export APINGER_ALARM=$6
+export APINGER_ALARM_TYPE=$7
+export APINGER_ALARM_MESSAGE=$8
+export APINGER_PROBES_SENT=$9
+export APINGER_PROBES_RECEIVED=${10}
+export APINGER_LOSS=${11}
+export APINGER_DELAY=${12}
+export APINGER_TIMESTAMP=${13}
+
+exec /sbin/hotplug-call apinger $@
diff --git a/net/apinger/files/apinger.config b/net/apinger/files/apinger.config
new file mode 100644 (file)
index 0000000..40190b3
--- /dev/null
@@ -0,0 +1,28 @@
+config interface 'wan'
+       option debug '0'
+       option status_interval '5'
+
+#config interface 'wan2'
+#      option debug '0'
+#      option status_interval '5'
+
+# delay is in ms
+#config alarm_delay 'delay200'
+#      option delay_low '100'
+#      option delay_high '200'
+
+# loss is in %
+#config alarm_loss 'loss50'
+#      option percent_low '30'
+#      option percent_high '50'
+
+#config target 'target1'
+#      option interface 'wan'
+#      option address '8.8.8.8'
+#      option alarm_delay 'a1'
+#      option alarm_loss 'loss50'
+#      option probe_interval '5'
+
+#config target 'target2'
+#      option interface 'wan2'
+#      option address '8.8.8.8'
index 8caac386976ea7da9f0dcc4b590f22ea292ac54c..51999880cc7b06e661c5d00e9f3ea1673e4fce27 100644 (file)
 # Copyright (C) 2006-2011 OpenWrt.org
 
 START=80
+USE_PROCD=1
 
-SERVICE_USE_PID=1
+. /lib/functions/network.sh
 
-start() {
-       service_start /usr/sbin/apinger
+set_config_file() {
+       export CONFIG_FILE="/var/run/apinger-$instance.conf"
 }
 
-stop() {
-       service_stop /usr/sbin/apinger
+set_status_file() {
+       export STATUS_FILE="/var/run/apinger-$instance.status"
 }
 
-reload() {
-       service_reload /usr/sbin/apinger
+write_config_block() {
+       local cfg_var="CONFIG_BLOCK_$instance"
+       eval echo -e "\$$cfg_var" >> "$CONFIG_FILE"
+}
+
+start_config_block() {
+       eval "export CONFIG_BLOCK_$instance=''"
+       append CONFIG_BLOCK_$instance "$* {" "\n"
+}
+
+close_config_block() {
+       append CONFIG_BLOCK_$instance "}" "\n"
+}
+
+append_config_line() {
+       append CONFIG_BLOCK_$instance "\t$*" "\n"
+}
+
+append_target() {
+       local target=$1
+       local interface address probe_interval srcip
+       local avg_delay_samples avg_loss_samples avg_loss_delay_samples 
+       local alarm_down alarm_delay alarm_loss alarms
+
+       config_get interface              "$target" interface wan
+       [ "$interface" != "$instance" ] && return 0
+
+       config_get address                "$target" address
+       config_get probe_interval         "$target" probe_interval
+       config_get avg_delay_samples      "$target" avg_delay_samples
+       config_get avg_loss_samples       "$target" avg_loss_samples
+       config_get avg_loss_delay_samples "$target" avg_loss_delay_samples
+       config_get alarm_down             "$target" alarm_down
+       config_get alarm_delay            "$target" alarm_delay
+       config_get alarm_loss             "$target" alarm_loss
+
+       [ -z "$address" ] && return 0
+
+       srcip=$(uci_get network "$interface" ipaddr)
+       [ -z "$srcip" ] && network_get_ipaddr srcip "$interface"
+       srcip="${srcip:-0.0.0.0}"
+
+       alarms=${alarm_down:+\"${alarm_down}\"}
+       alarms=${alarm_delay:+${alarms:+${alarms}, }}${alarm_delay:+\"${alarm_delay}\"}
+       alarms=${alarm_loss:+${alarms:+${alarms}, }}${alarm_loss:+\"${alarm_loss}\"}
+
+       start_config_block "target \"$address\""
+       append_config_line "srcip \"$srcip\""
+       append_config_line "description \"$target\""
+
+       [ -n "$probe_interval" ]          && append_config_line "interval ${probe_interval}s"
+       [ -n "$avg_delay_samples" ]       && append_config_line "avg_delay_samples ${avg_delay_samples}"
+       [ -n "$avg_loss_samples" ]        && append_config_line "avg_loss_samples ${avg_loss_samples}"
+       [ -n "$avg_loss_delay_samples" ]  && append_config_line "avg_loss_delay_samples ${avg_loss_delay_samples}"
+       [ -n "$alarms" ]                  && append_config_line "alarms override ${alarms}"
+
+       close_config_block
+       write_config_block
+}
+
+append_alarm_down() {
+       local alarm=$1
+       local time
+
+       config_get time "$alarm" time 5
+
+       [ -z "$time" ] && return
+
+       start_config_block "alarm down \"$alarm\""
+       append_config_line "time ${time}s"
+       close_config_block
+       write_config_block
+}
+
+append_alarm_delay() {
+       local alarm=$1
+       local delay_low delay_high
+
+       config_get delay_low  "$alarm" delay_low
+       config_get delay_high "$alarm" delay_high
+
+       if [ -z "$delay_low" ] || [ -z "$delay_high" ]; then
+               return
+       fi
+
+       start_config_block "alarm delay \"$alarm\""
+       append_config_line "delay_low ${delay_low}ms"
+       append_config_line "delay_high ${delay_high}ms"
+       close_config_block
+       write_config_block
+}
+
+append_alarm_loss() {
+       local alarm=$1
+       local percent_low percent_high
+
+       config_get percent_low  "$alarm" percent_low
+       config_get percent_high "$alarm" percent_low
+
+       if [ -z "$percent_low" ] || [ -z "$percent_high" ]; then
+               return
+       fi
+
+       start_config_block "alarm loss \"$alarm\""
+       append_config_line "percent_low ${percent_low}"
+       append_config_line "percent_high ${percent_high}"
+       close_config_block
+       write_config_block
+}
+
+init_apinger_config() {
+       local debug status_interval instance
+       instance=$1
+
+       config_get_bool debug             apinger debug 0
+       config_get      status_interval   apinger status_interval 1
+
+       [ "$debug" == "1" ] && debug=on || debug=off
+
+       set_config_file
+       set_status_file
+
+       cat << EOF > "$CONFIG_FILE"
+user "root"
+group "root"
+debug ${debug}
+
+status {
+       scriptformat on
+       file "$STATUS_FILE"
+       interval ${status_interval}s
+}
+alarm down "down" {
+       time 30s
+}
+alarm delay "delay" {
+       delay_low 5ms
+       delay_high 20ms
+}
+alarm loss "loss" {
+       percent_low 3
+       percent_high 5
+}
+alarm default {
+       command on "/usr/libexec/apinger-hotplug up $instance '%t' '%i' '%T' '%a' '%A' '%r' '%p' '%P' '%l' '%d' '%s'"
+       command off "/usr/libexec/apinger-hotplug down $instance '%t' '%i' '%T' '%a' '%A' '%r' '%p' '%P' '%l' '%d' '%s'"
+}
+target default {
+       interval 1s
+       avg_delay_samples 10
+       avg_loss_samples 50
+       avg_loss_delay_samples 20
+       alarms "down", "delay", "loss"
+}
+EOF
+}
+
+start_instance() {
+       export instance=$1
+
+       local enabled
+       config_get_bool enabled     "$instance" enabled 1
+       [ "$enabled" != "1" ] && return 0
+
+       init_apinger_config "$instance"
+       config_foreach append_alarm_down alarm_down
+       config_foreach append_alarm_delay alarm_delay
+       config_foreach append_alarm_loss alarm_loss
+       config_foreach append_target target
+
+       procd_open_instance "$instance"
+       procd_set_param command /usr/sbin/apinger -f -c $CONFIG_FILE
+       procd_set_param stderr 1
+       procd_set_param stdout 1
+       procd_close_instance
+}
+
+start_service() {
+       local instance=$1
+
+       config_load apinger
+
+       if [ -n "$instance" ]; then
+               start_instance "$instance"
+       else
+               config_foreach start_instance interface
+       fi
+}
+
+service_triggers() {
+       procd_add_reload_trigger apinger
+}
+
+clean_instance() {
+       local instance=$1
+
+       set_config_file
+       set_status_file
+
+       [ -e $CONFIG_FILE ] && rm -f $CONFIG_FILE
+       [ -e $STATUS_FILE ] && rm -f $STATUS_FILE
+}
+
+service_stopped() {
+       local instance=$1
+
+       config_load apinger
+
+       if [ -n "$instance" ]; then
+               clean_instance "$instance"
+       else
+               config_foreach clean_instance interface
+       fi
+}
+
+reload_service() {
+       restart
 }
diff --git a/net/apinger/files/iface.hotplug b/net/apinger/files/iface.hotplug
new file mode 100644 (file)
index 0000000..6c1930f
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+. /lib/functions.sh
+
+/etc/init.d/apinger enabled && {
+       [ "$(uci_get apinger $INTERFACE)" == "interface" ] || exit 0
+
+       [ "$ACTION" = "ifup" ] && {
+               /etc/init.d/apinger $INTERFACE restart
+       }
+
+}
diff --git a/net/apinger/files/user.hotplug b/net/apinger/files/user.hotplug
new file mode 100644 (file)
index 0000000..50bee5e
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+[ -e /etc/apinger.user ] && {
+       sh /etc/apinger.user
+}
+
+exit 0
diff --git a/net/apinger/patches/030-apinger-no_exit.patch b/net/apinger/patches/030-apinger-no_exit.patch
new file mode 100644 (file)
index 0000000..d058b3d
--- /dev/null
@@ -0,0 +1,34 @@
+--- a/src/apinger.c
++++ b/src/apinger.c
+@@ -786,7 +786,6 @@ struct alarm_cfg *a;
+ time_t tm;
+ int i,qp,really_lost;
+ char *buf1,*buf2;
+-int err=0;
+       if (config->status_file==NULL) return;
+       
+@@ -849,12 +848,10 @@ int err=0;
+                       }
+               }
+               buf2[i]=0;
+-              fprintf(f,"Received packets buffer: %s %s\n",buf2,buf1);
+               if (t->recently_lost!=really_lost){
+-                      fprintf(f,"   lost packet count mismatch (%i!=%i)!\n",t->recently_lost,really_lost);
+-                      logit("%s: Lost packet count mismatch (%i!=%i)!",t->name,t->recently_lost,really_lost);
+-                      logit("%s: Received packets buffer: %s %s\n",t->name,buf2,buf1);
+-                      err=1;
++                      logit("Target \"%s\": Lost packet count mismatch (%i(recently_lost) != %i(really_lost))!",t->name,t->recently_lost,really_lost);
++                      logit("Target \"%s\": Received packets buffer: %s %s\n",t->name,buf2,buf1);
++                      t->recently_lost = really_lost = 0;
+               }
+               free(buf1);
+               free(buf2);
+@@ -862,7 +859,6 @@ int err=0;
+               fprintf(f,"\n");
+       }
+       fclose(f);
+-      if (err) abort();
+ }
+ #ifdef FORKED_RECEIVER
diff --git a/net/apinger/patches/040-srcip.patch b/net/apinger/patches/040-srcip.patch
new file mode 100644 (file)
index 0000000..461201e
--- /dev/null
@@ -0,0 +1,564 @@
+--- a/src/apinger.c
++++ b/src/apinger.c
+@@ -161,6 +161,9 @@ time_t tim;
+               case 't':
+                       values[n]=t->name;
+                       break;
++              case 'i':
++                      values[n]=t->config->srcip;
++                      break;
+               case 'T':
+                       values[n]=t->description;
+                       break;
+@@ -276,6 +279,7 @@ time_t tm;
+       else
+               fprintf(f,"alarm canceled: %s\n",a->name);
+       fprintf(f,"Target: %s\n",t->name);
++      fprintf(f,"Source: %s\n",t->config->srcip);
+       fprintf(f,"Description: %s\n",t->description);
+       fprintf(f,"Probes sent: %i\n",t->last_sent+1);
+       fprintf(f,"Replies received: %i\n",t->received);
+@@ -645,7 +649,7 @@ void configure_targets(void){
+ struct target *t,*pt,*nt;
+ struct target_cfg *tc;
+ struct active_alarm_list *al,*nal;
+-union addr addr;
++union addr addr, srcaddr;
+ int r;
+ int l;
+@@ -665,6 +669,8 @@ int l;
+                               nal=al->next;
+                               free(al);
+                       }
++                      if (t->socket)
++                              close(t->socket);
+                       free(t->queue);
+                       free(t->rbuf);
+                       free(t->name);
+@@ -681,20 +687,16 @@ int l;
+                               break;
+               if (t==NULL) { /* new target */
+                       memset(&addr,0,sizeof(addr));
++                      logit("Checking target IP %s", tc->srcip);
+                       r=inet_pton(AF_INET,tc->name,&addr.addr4.sin_addr);
+                       if (r){
+-                              if (icmp_sock<0){
+-                                      logit("Sorry, IPv4 is not available\n");
+-                                      logit("Ignoring target %s\n",tc->name);
+-                                      continue;
+-                              }
+                               addr.addr.sa_family=AF_INET;
+                       }else{
+ #ifdef HAVE_IPV6
+                               r=inet_pton(AF_INET6,tc->name,&addr.addr6.sin6_addr);
+                               if (r==0){
+ #endif
+-                                      logit("Bad host address: %s\n",tc->name);
++                                      logit("Bad target IP address: %s\n",tc->name);
+                                       logit("Ignoring target %s\n",tc->name);
+                                       continue;
+ #ifdef HAVE_IPV6
+@@ -707,12 +709,38 @@ int l;
+                               addr.addr.sa_family=AF_INET6;
+ #endif
+                       }
++                      memset(&srcaddr,0,sizeof(srcaddr));
++                      logit("Checking source IP %s", tc->srcip);
++                      r=inet_pton(AF_INET,tc->srcip,&srcaddr.addr4.sin_addr);
++                      if (r){
++                              srcaddr.addr.sa_family=AF_INET;
++                      }else{
++#ifdef HAVE_IPV6
++                              r=inet_pton(AF_INET6,tc->srcip,&srcaddr.addr6.sin6_addr);
++                              if (r==0){
++#endif
++                                      logit("Bad source IP address %s for target %s\n", tc->srcip, tc->name);
++                                      logit("Ignoring target %s\n",tc->name);
++                                      continue;
++#ifdef HAVE_IPV6
++                              }
++                              if (icmp6_sock<0){
++                                      logit("Sorry, IPv6 is not available\n");
++                                      logit("Ignoring target %s\n",tc->name);
++                                      continue;
++                              }
++                              srcaddr.addr.sa_family=AF_INET6;
++#endif
++                      }
+                       t=NEW(struct target,1);
+                       memset(t,0,sizeof(struct target));
+                       t->name=strdup(tc->name);
+                       t->description=strdup(tc->description);
+                       t->addr=addr;
++                      t->ifaddr=srcaddr;
+                       t->next=targets;
++                      if(t->addr.addr.sa_family==AF_INET) make_icmp_socket(t);
++                      if(t->addr.addr.sa_family==AF_INET6) make_icmp6_socket(t);
+                       targets=t;
+               }
+               t->config=tc;
+@@ -752,6 +780,8 @@ struct active_alarm_list *al,*nal;
+                       nal=al->next;
+                       free(al);
+               }
++              if (t->socket)
++                      close(t->socket);
+               free(t->queue);
+               free(t->rbuf);
+               free(t->name);
+@@ -799,6 +829,7 @@ char *buf1,*buf2;
+       fprintf(f,"%s\n",ctime(&tm));
+       for(t=targets;t;t=t->next){
+               fprintf(f,"Target: %s\n",t->name);
++              fprintf(f,"Source: %s\n",t->config->srcip);
+               fprintf(f,"Description: %s\n",t->description);
+               fprintf(f,"Last reply received: #%i %s",t->last_received,
+                       ctime(&t->last_received_tv.tv_sec));
+@@ -909,7 +940,7 @@ int i;
+ void main_loop(void){
+ struct target *t;
+ struct timeval cur_time,next_status={0,0},tv,next_report={0,0},next_rrd_update={0,0};
+-struct pollfd pfd[2];
++struct pollfd pfd[1024];
+ int timeout;
+ int npfd=0;
+ int i;
+@@ -946,18 +977,8 @@ struct piped_info pi;
+       pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL;
+       pfd[npfd].revents=0;
+       pfd[npfd++].fd=recv_pipe[0];
+-#else
+-      if (icmp_sock){
+-              pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL;
+-              pfd[npfd].revents=0;
+-              pfd[npfd++].fd=icmp_sock;
+-      }
+-      if (icmp6_sock){
+-              pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL;
+-              pfd[npfd++].fd=icmp6_sock;
+-              pfd[npfd].revents=0;
+-      }
+ #endif
++      memset(&pfd, '\0', sizeof pfd);
+       if (config->status_interval){
+               gettimeofday(&cur_time,NULL);
+               tv.tv_sec=config->status_interval/1000;
+@@ -965,10 +986,16 @@ struct piped_info pi;
+               timeradd(&cur_time,&tv,&next_status);
+       }
+       while(!interrupted_by){
++              npfd = 0;
+               gettimeofday(&cur_time,NULL);
+               if ( !timercmp(&next_probe,&cur_time,>) )
+                       timerclear(&next_probe);
+               for(t=targets;t;t=t->next){
++                      if (t->socket){
++                              pfd[npfd].events=POLLIN|POLLERR|POLLHUP|POLLNVAL;
++                              pfd[npfd].revents=0;
++                              pfd[npfd++].fd=t->socket;
++                      }
+                       for(al=t->config->alarms;al;al=nal){
+                               a=al->alarm;
+                               nal=al->next;
+@@ -1051,8 +1078,20 @@ struct piped_info pi;
+                                       analyze_reply(pi.recv_timestamp,pi.icmp_seq,&pi.ti);
+                       }
+ #else
+-                      if (pfd[i].fd==icmp_sock) recv_icmp();
+-                      else if (pfd[i].fd==icmp6_sock) recv_icmp6();
++                      for(t=targets;t;t=t->next){
++                              if (t->addr.addr.sa_family==AF_INET) {
++                                      if (t->socket == pfd[i].fd) {
++                                              recv_icmp(t);
++                                              break;
++                                      }
++                              }
++                              if (t->addr.addr.sa_family==AF_INET6) {
++                                      if (t->socket == pfd[i].fd) {
++                                              recv_icmp6(t);
++                                              break;
++                                      }
++                              }
++                      }
+ #endif
+                       pfd[i].revents=0;
+               }
+--- a/src/apinger.conf
++++ b/src/apinger.conf
+@@ -47,6 +47,7 @@ alarm default {
+       ## Following "macros" may be used in options below:
+       ##      %t - target name (address)
++      ##      %i - source name (address)
+       ##      %T - target description
+       ##      %a - alarm name
+       ##      %A - alarm type ("down"/"loss"/"delay")
+--- a/src/apinger.h
++++ b/src/apinger.h
+@@ -46,6 +46,8 @@
+ #endif
+ #include "conf.h"
++#include <ifaddrs.h>
++
+ union addr {
+       struct sockaddr addr;
+       struct sockaddr_in addr4;
+@@ -70,6 +72,7 @@ struct target {
+       char *queue;            /*
+                               contains info about recently sent packets
+                               "1" means it was received */
++      int socket;
+       int last_sent;          /* sequence number of the last ping sent */
+       int last_received;      /* sequence number of the last ping received */
+       struct timeval last_received_tv; /* timestamp of the last ping received */
+@@ -89,6 +92,7 @@ struct target {
+       struct target_cfg *config;
+       
+       struct target *next;
++      union addr ifaddr;      /* iface address */
+ };
+ #define AVG_DELAY_KNOWN(t) (t->upsent >= t->config->avg_delay_samples)
+@@ -118,16 +122,16 @@ extern char *config_file;
+ extern int icmp_sock;
+ extern int icmp6_sock;
+-extern int ident;
++extern uint16_t ident;
+ extern struct timeval next_probe;
+-int make_icmp_socket(void);
+-void recv_icmp(void);
++int make_icmp_socket(struct target *t);
++void recv_icmp(struct target *t);
+ void send_icmp_probe(struct target *t,int seq);
+-int make_icmp6_socket(void);
+-void recv_icmp6(void);
++int make_icmp6_socket(struct target *t);
++void recv_icmp6(struct target *t);
+ void send_icmp6_probe(struct target *t,int seq);
+ #ifdef FORKED_RECEIVER
+--- a/src/cfgparser1.y
++++ b/src/cfgparser1.y
+@@ -96,6 +96,7 @@ struct target_cfg *cur_target;
+ %token DELAY_HIGH
+ %token DESCRIPTION
++%token SRCIP
+ %token ALARMS
+ %token INTERVAL
+ %token AVG_DELAY_SAMPLES
+@@ -247,6 +248,8 @@ target:    TARGET getdeftarget DEFAULT '{'
+ targetcfg: /* */ 
+       | DESCRIPTION string 
+               { cur_target->description=$2; }
++      | SRCIP string
++              { cur_target->srcip = $2; }
+       | ALARMS alarmlist
+               { cur_target->alarms=$2; }
+       | ALARMS OVERRIDE alarmlist
+--- a/src/cfgparser2.l
++++ b/src/cfgparser2.l
+@@ -81,6 +81,7 @@ delay                { LOC; LOCINC; return DELAY; }
+ delay_high    { LOC; LOCINC; return DELAY_HIGH; }
+ delay_low     { LOC; LOCINC; return DELAY_LOW; }
+ description   { LOC; LOCINC; return DESCRIPTION; }
++srcip         { LOC; LOCINC; return SRCIP; }
+ down          { LOC; LOCINC; return DOWN; }
+ false         { LOC; LOCINC; return FALSE; }
+ file          { LOC; LOCINC; return FILE_; }
+--- a/src/conf.c
++++ b/src/conf.c
+@@ -174,6 +174,14 @@ int ret;
+                       }
+               }
+               for(t=cur_config.targets;t;t=t->next){
++                      if (t->name==NULL || strlen(t->name)==0){
++                              logit("Target name can't be empty.");
++                              return 1;
++                      }
++                      else if (t->srcip==NULL){
++                              logit("No source IP defined for target \"%s\".", t->name);
++                              return 1;
++                      }
+                       if (t->description==NULL)
+                               t->description=cur_config.target_defaults.description;
+                       if (t->interval<=0)
+--- a/src/conf.h
++++ b/src/conf.h
+@@ -71,6 +71,7 @@ struct alarm_list {
+ struct target_cfg {
+       char *name;
+       char *description;
++      char *srcip;
+       int interval;
+       int avg_delay_samples;
+       int avg_loss_delay_samples;
+--- a/src/icmp6.c
++++ b/src/icmp6.c
+@@ -112,14 +112,14 @@ int ret;
+       memcpy(p+1,&ti,sizeof(ti));
+       size=sizeof(*p)+sizeof(ti);
+-      ret=sendto(icmp6_sock,p,size,MSG_DONTWAIT,
++      ret=sendto(t->socket,p,size,MSG_DONTWAIT,
+                       (struct sockaddr *)&t->addr.addr6,sizeof(t->addr.addr6));
+       if (ret<0){
+               if (config->debug) myperror("sendto");
+       }
+ }
+-void recv_icmp6(void){
++void recv_icmp6(struct target *t){
+ int len,icmplen,datalen;
+ char buf[1024];
+ char abuf[100];
+@@ -133,6 +133,7 @@ char ans_data[4096];
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *c;
++reloophack6:
+       iov.iov_base=buf;
+       iov.iov_len=1000;
+@@ -142,12 +143,13 @@ struct cmsghdr *c;
+       msg.msg_iovlen=1;
+       msg.msg_control=ans_data;
+       msg.msg_controllen=sizeof(ans_data);
+-      len=recvmsg(icmp6_sock, &msg, MSG_DONTWAIT);
++      len=recvmsg(t->socket, &msg, MSG_DONTWAIT);
+ #else
+ socklen_t sl;
++reloophack6:
+       sl=sizeof(from);
+-      len=recvfrom(icmp6_sock,buf,1024,0,(struct sockaddr *)&from,&sl);
++      len=recvfrom(t->socket,buf,1024,0,(struct sockaddr *)&from,&sl);
+ #endif
+       if (len<0){
+               if (errno==EAGAIN) return;
+@@ -169,7 +171,7 @@ socklen_t sl;
+ #endif
+       if (time_recvp==NULL){
+ #ifdef SIOCGSTAMP
+-              if (!ioctl(icmp6_sock, SIOCGSTAMP, &time_recv)){
++              if (!ioctl(t->socket, SIOCGSTAMP, &time_recv)){
+                       debug("Got timestamp from ioctl()");
+               }else
+ #endif
+@@ -182,8 +184,11 @@ socklen_t sl;
+       icmplen=len;
+       icmp=(struct icmp6_hdr *)buf;
+       if (icmp->icmp6_type != ICMP6_ECHO_REPLY) return;
+-      if (icmp->icmp6_id != ident) return;
+-
++      if (icmp->icmp6_id != ident){
++              debug("Alien echo-reply received from xxx. Expected %i, received %i", ident, icmp->icmp6_id);
++              goto reloophack6;
++              return;
++      }
+       name=inet_ntop(AF_INET6,&from.sin6_addr,abuf,100);
+       debug("Ping reply from %s",name);
+       datalen=icmplen-sizeof(*icmp);
+@@ -199,33 +204,36 @@ socklen_t sl;
+ }
+-int make_icmp6_socket(void){
++int make_icmp6_socket(struct target *t){
+ int opt;
+-      icmp6_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+-      if (icmp6_sock<0)
++      t->socket = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
++      if (t->socket <0)
+               myperror("socket");
+       else {
+               opt=2;
+ #if defined(SOL_RAW) && defined(IPV6_CHECKSUM)
+-              if (setsockopt(icmp6_sock, SOL_RAW, IPV6_CHECKSUM, &opt, sizeof(int)))
++              if (setsockopt(t->socket, SOL_RAW, IPV6_CHECKSUM, &opt, sizeof(int)))
+                       myperror("setsockopt(IPV6_CHECKSUM)");
+ #endif
+ #ifdef SO_TIMESTAMP
+               opt=1;
+-              if (setsockopt(icmp6_sock, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)))
++              if (setsockopt(t->socket, SOL_SOCKET, SO_TIMESTAMP, &opt, sizeof(opt)))
+                       myperror("setsockopt(SO_TIMESTAMP)");
+ #endif
+               /*install_filter6();*/
+       }
+-      return icmp6_sock;
++      if (bind(t->socket, (struct sockaddr *)&t->ifaddr.addr6, sizeof(t->ifaddr.addr6)) < 0)
++              myperror("bind socket");
++
++      return t->socket;
+ }
+ #else /*HAVE_IPV6*/
+ #include "apinger.h"
+-int make_icmp6_socket(void){ return -1; }
+-void recv_icmp6(void){}
++int make_icmp6_socket(struct target *t){ return -1; }
++void recv_icmp6(struct target *t){}
+ void send_icmp6_probe(struct target *t,int seq){}
+ #endif /*HAVE_IPV6*/
+--- a/src/icmp.c
++++ b/src/icmp.c
+@@ -150,14 +150,14 @@ int ret;
+       size=sizeof(*p)+sizeof(ti);
+       p->icmp_cksum = in_cksum((u_short *)p,size,0);
+-      ret=sendto(icmp_sock,p,size,MSG_DONTWAIT,
++      ret=sendto(t->socket,p,size,MSG_DONTWAIT,
+                       (struct sockaddr *)&t->addr.addr4,sizeof(t->addr.addr4));
+       if (ret<0){
+               if (config->debug) myperror("sendto");
+       }
+ }
+-void recv_icmp(void){
++void recv_icmp(struct target *t){
+ int len,hlen,icmplen,datalen;
+ char buf[1024];
+ struct sockaddr_in from;
+@@ -170,6 +170,7 @@ char ans_data[4096];
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *c;
++reloophack:
+       iov.iov_base=buf;
+       iov.iov_len=1000;
+@@ -179,12 +180,13 @@ struct cmsghdr *c;
+       msg.msg_iovlen=1;
+       msg.msg_control=ans_data;
+       msg.msg_controllen=sizeof(ans_data);
+-      len=recvmsg(icmp_sock, &msg, MSG_DONTWAIT);
++      len=recvmsg(t->socket, &msg, MSG_DONTWAIT);
+ #else
+ socklen_t sl;
++reloophack:
+       sl=sizeof(from);
+-      len=recvfrom(icmp_sock,buf,1024,MSG_DONTWAIT,(struct sockaddr *)&from,&sl);
++      len=recvfrom(t->socket,buf,1024,MSG_DONTWAIT,(struct sockaddr *)&from,&sl);
+ #endif
+       if (len<0){
+               if (errno==EAGAIN) return;
+@@ -196,7 +198,7 @@ socklen_t sl;
+       debug("checking CMSG...");
+       for (c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) {
+               debug("CMSG level: %i type: %i",c->cmsg_level,c->cmsg_type);
+-              if (c->cmsg_level != SOL_SOCKET || c->cmsg_type != SO_TIMESTAMP)
++              if (c->cmsg_level != SOL_SOCKET || c->cmsg_type != SCM_TIMESTAMP)
+                       continue;
+               if (c->cmsg_len < CMSG_LEN(sizeof(struct timeval)))
+                       continue;
+@@ -206,7 +208,7 @@ socklen_t sl;
+ #endif
+       if (time_recvp==NULL){
+ #ifdef SIOCGSTAMP
+-              if (!ioctl(icmp_sock, SIOCGSTAMP, &time_recv)){
++              if (!ioctl(t->socket, SIOCGSTAMP, &time_recv)){
+                       debug("Got timestampt from ioctl()");
+               }else
+ #endif
+@@ -226,7 +228,8 @@ socklen_t sl;
+               return;
+       }
+       if (icmp->icmp_id != ident){
+-              debug("Alien echo-reply received");
++              debug("Alien echo-reply received from %s. Expected %i, received %i",inet_ntoa(from.sin_addr), ident, icmp->icmp_id);
++              goto reloophack;
+               return;
+       }
+       debug("Ping reply from %s",inet_ntoa(from.sin_addr));
+@@ -242,19 +245,23 @@ socklen_t sl;
+ #endif
+ }
+-int make_icmp_socket(void){
++int make_icmp_socket(struct target *t){
+ int on;
+-      icmp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+-      if (icmp_sock<0)
++      t->socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
++      if (t->socket < 0)
+               myperror("socket");
+ #ifdef SO_TIMESTAMP
+       else{
+               on=1;
+-              if (setsockopt(icmp_sock, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
++              if (setsockopt(t->socket, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(on)))
+                       myperror("setsockopt(SO_TIMESTAMP)");
+       }
+ #endif
+-      return icmp_sock;
++
++      if (bind(t->socket, (struct sockaddr *)&t->ifaddr.addr4, sizeof(t->ifaddr.addr4)) < 0)
++                      myperror("bind socket");
++
++      return t->socket;
+ }
+--- a/src/main.c
++++ b/src/main.c
+@@ -71,6 +71,7 @@ struct config default_config={
+               {               /* target defaults */
+                               "default",      /* name */
+                               "",             /* description */
++                              "",             /* interface */
+                               1000,           /* interval */
+                               20,             /* avg_delay_samples */
+                               5,              /* avg_loss_delay_samples */
+@@ -95,7 +96,7 @@ char *config_file=CONFIG;
+ int icmp_sock;
+ int icmp6_sock;
+-int ident;
++uint16_t ident;
+ struct timeval next_probe={0,0};
+@@ -216,12 +217,6 @@ char *graph_location="/apinger/";
+               }
+       }
+-      make_icmp_socket();
+-      make_icmp6_socket();
+-      if (icmp6_sock<0 && icmp_sock<0){
+-              return 1;
+-      }
+-
+       pw=getpwnam(config->user);
+       if (!pw) {
+               debug("getpwnam(\"%s\") failed.",config->user);
+@@ -276,7 +271,7 @@ char *graph_location="/apinger/";
+               return 1;
+       }
+-      ident=getpid();
++      ident=getpid() & 0xFFFF;
+       signal(SIGTERM,signal_handler);
+       signal(SIGINT,signal_handler);
+       signal(SIGHUP,signal_handler);
+@@ -285,9 +280,8 @@ char *graph_location="/apinger/";
+ #ifdef FORKED_RECEIVER
+       signal(SIGCHLD,sigchld_handler);
+ #endif
++      logit("Starting Alarm Pinger, apinger(%i)", ident);
+       main_loop();
+-      if (icmp_sock>=0) close(icmp_sock);
+-      if (icmp6_sock>=0) close(icmp6_sock);
+       logit("Exiting on signal %i.",interrupted_by);
diff --git a/net/apinger/patches/050-statusformat.patch b/net/apinger/patches/050-statusformat.patch
new file mode 100644 (file)
index 0000000..dee865b
--- /dev/null
@@ -0,0 +1,126 @@
+--- a/src/apinger.c
++++ b/src/apinger.c
+@@ -826,26 +826,49 @@ char *buf1,*buf2;
+               return;
+       }
+       tm=time(NULL);
+-      fprintf(f,"%s\n",ctime(&tm));
++      if(!config->status_format) fprintf(f,"%s\n",ctime(&tm));
+       for(t=targets;t;t=t->next){
+-              fprintf(f,"Target: %s\n",t->name);
+-              fprintf(f,"Source: %s\n",t->config->srcip);
+-              fprintf(f,"Description: %s\n",t->description);
+-              fprintf(f,"Last reply received: #%i %s",t->last_received,
+-                      ctime(&t->last_received_tv.tv_sec));
+-              fprintf(f,"Average delay: %0.3fms\n",AVG_DELAY(t));
++              if(config->status_format){
++                      fprintf(f,"%s|%s|%s|%i|%i|%u|",t->name, t->config->srcip, t->description, t->last_sent+1,
++                               t->received, t->last_received_tv.tv_sec);
++                      fprintf(f,"%0.3fms|", AVG_DELAY(t));
++              }
++              else{
++                      fprintf(f,"Target: %s\n",t->name);
++                      fprintf(f,"Source: %s\n",t->config->srcip);
++                      fprintf(f,"Description: %s\n",t->description);
++                      fprintf(f,"Last reply received: #%i %s",t->last_received,
++                              ctime(&t->last_received_tv.tv_sec));
++                      fprintf(f,"Average delay: %0.3fms\n",AVG_DELAY(t));
++              }
+               if (AVG_LOSS_KNOWN(t)){
+-                      fprintf(f,"Average packet loss: %0.1f%%\n",AVG_LOSS(t));
++                      if(config->status_format){
++                              fprintf(f,"%0.1f%%",AVG_LOSS(t));
++                      }
++                      else{
++                              fprintf(f,"Average packet loss: %0.1f%%\n",AVG_LOSS(t));
++                      }
++              }
++              if(config->status_format){
++                      fprintf(f, "|");
++              }
++              else{
++                      fprintf(f,"Active alarms: ");
+               }
+-              fprintf(f,"Active alarms:");
+               if (t->active_alarms){
+                       for(al=t->active_alarms;al;al=al->next){
+                               a=al->alarm;
+-                              fprintf(f," \"%s\"",a->name);
++                              if(config->status_format){
++                                      fprintf(f,"%s",a->name);
++                              }
++                              else{
++                                      fprintf(f," \"%s\"",a->name);
++                              }
+                       }
+-                      fprintf(f,"\n");
++                      if(!config->status_format) fprintf(f,"\n");
+               }
+-              else fprintf(f," None\n");
++              else fprintf(f,"none");
++              if(!config->status_format) fprintf(f,"\n");
+               buf1=NEW(char,t->config->avg_loss_delay_samples+1);
+               buf2=NEW(char,t->config->avg_loss_samples+1);
+--- a/src/apinger.conf
++++ b/src/apinger.conf
+@@ -29,6 +29,10 @@ group "nogroup"
+ #     ## Interval between file updates
+ #     ## when 0 or not set, file is written only when SIGUSR1 is received
+ #     interval 5m
++#
++#     ## Create status file in script parseable format (on) or human
++#     ## readable format (off)
++#     scriptformat off
+ #}
+ ########################################
+--- a/src/cfgparser1.y
++++ b/src/cfgparser1.y
+@@ -104,6 +104,7 @@ struct target_cfg *cur_target;
+ %token AVG_LOSS_DELAY_SAMPLES
+ %token FILE_
++%token SCRIPTFORMAT
+ %token ERROR
+@@ -282,6 +283,8 @@ statuscfg: /* */
+               { cur_config.status_interval=$2; }
+       | INTERVAL TIME
+               { cur_config.status_interval=$2; }
++      | SCRIPTFORMAT boolean
++              { cur_config.status_format=$2; }
+       | statuscfg separator statuscfg
+ ;
+--- a/src/cfgparser2.l
++++ b/src/cfgparser2.l
+@@ -85,6 +85,7 @@ srcip                { LOC; LOCINC; return SRCIP; }
+ down          { LOC; LOCINC; return DOWN; }
+ false         { LOC; LOCINC; return FALSE; }
+ file          { LOC; LOCINC; return FILE_; }
++scriptformat  { LOC; LOCINC; return SCRIPTFORMAT; }
+ group         { LOC; LOCINC; return GROUP; }
+ interval      { LOC; LOCINC; return INTERVAL; }
+ loss          { LOC; LOCINC; return LOSS; }
+--- a/src/conf.h
++++ b/src/conf.h
+@@ -97,6 +97,7 @@ struct config {
+       char *pid_file;
+       char *status_file;
+       int status_interval;
++      int status_format;
+       char *timestamp_format;
+ };
+--- a/src/main.c
++++ b/src/main.c
+@@ -88,6 +88,7 @@ struct config default_config={
+       "/var/run/apinger.pid", /* pid file */
+       NULL,                   /* status file */
+       0,                      /* status interval */
++      0,                      /* status format */
+       "%b %d %H:%M:%S"        /* timestamp format */
+ };