Hi Ying,
Sorry for the delay.
Here it is (Pls see below):
Ciao//Anthony
From 11155e72babf481b526e3aaf1d9427c41d718dd5 Mon Sep 17 00:00:00 2001
From: Akbar Rezvanpour <akbrez...@gmail.com>
Date: Thu, 1 Jun 2017 17:58:57 +0200
Subject: [PATCH] Introducing tipc-link-watcher
In this commit we add tipc-link-watcher which monitors the tipc link
status and reports in foreground mode as shown below.
Sample output of the program when invoked by following options:
./tipc-link-watcher -d -p 2 -s 2
1.1.2:data1-1.1.1:data1: link reset:0 rx_packets:0
tx_packets:0 rx_loss:0 tx_loss:0 Working :)
1.1.2:data1-1.1.3:data1: link reset:0 rx_packets:0
tx_packets:0 rx_loss:0 tx_loss:0 Working :)
1.1.2:data1-1.1.4:data1: link reset:0 rx_packets:0
tx_packets:0 rx_loss:0 tx_loss:0 Working :)
1.1.2:data0-1.1.1:data0: link reset:4 rx_packets:0
tx_packets:0 rx_loss:0 tx_loss:0 Working :)
1.1.2:data0-1.1.4:data0: link reset:5 rx_packets:0
tx_packets:0 rx_loss:0 tx_loss:0 Working :)
1.1.2:data0-1.1.3:data0: link reset:5 rx_packets:0
tx_packets:0 rx_loss:0 tx_loss:0 Working :)
1.1.2:data0-1.1.1:data1: link reset:0 rx_packets:0
tx_packets:0 rx_loss:0 tx_loss:0 Working :)
1.1.2:data0-1.1.3:data1: link reset:0 rx_packets:0
tx_packets:0 rx_loss:0 tx_loss:0 Working :)
The user is always able to dump the tipc link statistics.
Further information is available in the README.
Signed-off-by: Akbar Rezvanpour <akbrez...@gmail.com>
---
.gitignore | 1 +
Makefile.am | 2 +-
configure.ac | 1 +
tipc-link-watcher/Makefile.am | 4 +
tipc-link-watcher/README | 233 ++++
tipc-link-watcher/tipc-link-watcher.c | 2143
+++++++++++++++++++++++++++++++++
6 files changed, 2383 insertions(+), 1 deletion(-)
create mode 100644 tipc-link-watcher/Makefile.am
create mode 100644 tipc-link-watcher/README
create mode 100644 tipc-link-watcher/tipc-link-watcher.c
diff --git a/.gitignore b/.gitignore
index f544ddfceff9..0ff143dea8ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,3 +48,4 @@ tipc-pipe/tipc-pipe
tipclog/tipclog
multicast_blast/mcast_tipc
multicast_blast/group_cast
+tipc-link-watcher/tipc-link-watcher
diff --git a/Makefile.am b/Makefile.am
index 0643d78e03b5..5f0d5e979b15 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,4 +1,4 @@
SUBDIRS=tipc-pipe ptts demos man scripts
if TIPC_LINK_STATE_SUBSCRITION
-SUBDIRS+=tipclog multicast_blast
+SUBDIRS+=tipclog multicast_blast tipc-link-watcher
endif
diff --git a/configure.ac b/configure.ac
index 0602878bada7..b466d56cc522 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,6 +40,7 @@ AC_CONFIG_FILES([
man/Makefile
scripts/Makefile
tipclog/Makefile
+ tipc-link-watcher/Makefile
])
AM_CONDITIONAL(WITH_SCRIPTS, false)
AC_ARG_ENABLE(scripts,
diff --git a/tipc-link-watcher/Makefile.am b/tipc-link-watcher/Makefile.am
new file mode 100644
index 000000000000..f8484d07ff45
--- /dev/null
+++ b/tipc-link-watcher/Makefile.am
@@ -0,0 +1,4 @@
+sbin_PROGRAMS=tipc-link-watcher
+tipc_link_watcher_SOURCES=tipc-link-watcher.c
+tipc_link_watcher_CFLAGS=-Werror $(shell $(PKG_CONFIG) libmnl --cflags)
+tipc_link_watcher_LDFLAGS=$(shell $(PKG_CONFIG) libmnl --libs)
diff --git a/tipc-link-watcher/README b/tipc-link-watcher/README
new file mode 100644
index 000000000000..5c8a7b901b80
--- /dev/null
+++ b/tipc-link-watcher/README
@@ -0,0 +1,233 @@
+#The following commits are pre-requisite for this feature.
+1.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=78acb1f9b898e85fa2c1e28e700b54b66b288e8d
+2. https://lwn.net/Articles/611994/
+#The former commit is implemented in kernel v3.16 and the later in v3.19.
So this tool will work only from v3.19.
+
+#Install the shared library libmnl-dev on your build environment.
+
+=== To Build tipc-link-watcher ===
+
+#To build the daemon (SuSE environment):
+
+#First install libmnl on your build host: (libmnl-1.0.4)
+
+"zypper install libmnl-devel"
+
+#and run:
+
+"gcc -o tipc-link-watcher $(pkg-config --libs --cflags libmnl) *.c"
+
+=== 2Do ===
+- Possible bug in current code or TIPC regarding values of rx_packets and
tx_packets.
+ When a link is reset, rx_packets and tx_packets contain rubbish values
until the link is reestablished.
+
+=== Couple of notes ===
+
+-Option "-x" and "-a" should not be used at the same time.
+
+-One shot (-x):
+You will get one single "layer 1" sample when you run with option "-x".
+Setting "-a" option will not change this behaviour.
+
+-Run forever:
+When all layer 0 samples (s) are taken every p second, the window
+layer 0 start to slide. Afterwards, every p seconds "mean of samples of
layer 0"
+ are calculated. The calculated mean value will be the sample that is
inserted into layer 1 window.
+The procedure above will continue to fill the layer 1 window until the
layer 1 window begin to slide.
+When layer 1 window begin to slide the oldest value will be deleted from
that window.
+The size of layer 1 window is set by option "-a"
+
+=== So what is "layer 0 and layer 1"? ===
+Note:
+ -layer 0 samples are statistic values obtained from TIPC.
+ -layer 1 samples are "mean of samples of full layer 0 window".
+
+When Layer 0 is complete, it starts to "slide" and the first sample (mean
of layer 0 samples) of layer 1 is
+calculated and inserted in layer 1 samples.
+
+-P 4 : Sample period (how often) in secounds (Default:60 secounds)
+-s 5 : Number of samples in the window(layer 0) which must be gt 1
+ Default:60 samples
+-a 4 : Number of samples in the window layer 1 which must be gt 1
+ These samples are mean of layer zero samples over full window
+ size defined by -s option
+ Default value is 3600 samples.
+ __________ _______________
+ | 3 | 2 |2 |1 |2 |
+ |____|____|____|____|____|
+ <-p->
+ <----------- s ---------->
+
+3+2+2+1+2=10
+The mean value = 10 / 5(-s) = 2
+"2" is mean of layer 0 samples, which is sample number one in layer 1.
+
+ layer 1
+ _____________________
+ | 2 | | | |
+ |____|____|____|____|
+ <-p->
+ <------------ a ---->
+
+Layer 0 received a new sample (8) and the second
+sample (mean of layer 0 samples) of layer 1 is calculated and inserted in
layer 1 samples.!
+
+ layer 0
+ __________ _______________
+ | 8 | 3 |2 |2 |1 |
+ |____|____|____|____|____|
+ <-p->
+ <----------- s ---------->
+
+8+3+2+2+1=15
+The mean value = 15 / 5(-s) = 3
+"3" is mean of layer 0 samples, which is sample number two in layer 1.
+
+ layer 1
+ _____________________
+ | 2 | 3 | | |
+ |____|____|____|____|
+ <-p->
+ <----------- a ---->
+
+Layer 1 windown mean samples are generated at the same rate as the -p
value.
+=== Examples how to run the Deamon ===
+
+#3.1: Run forever(absence of -x option); do not run as Daemon(-d);
+ every second(-p); 60 samples (-s: window size layer 0) and
+ 3600 mean samples (-a:Default, window size layer 1).
+
+"tipc-link-watcher -d -p 1 -s 60"
+
+To make the process generate samples into a file run:
+"kill -HUP $(pidof tipc-link-watcher)"
+
+#3.2: Run forever as a daemon, every second(-p),
+ 60 samples (-s) and 3600 mean samples (-a:Default).
+
+"tipc-link-watcher -p 1 -s 60"
+
+To request samples run:
+"kill -HUP $(pidof tipc-link-watcher)"
+
+#3.3: One shot(-x), do not run as daemon(-d), every 1 second (-p), 60
samples (-s) and dump the samples in "/my/print/path" when the daemon exits.
+
+"tipc-link-watcher -dx -p 1 -s 60 -f /my/print/path"
+
+To request additional samples before the daemon exist:
+"kill -HUP $(pidof tipc-link-watcher)"
+
+#3.4: Run forever (last 24 hour) and do not run as daemon(-d),
+ every 60 seconds (-p:Default), 60 samples (-s:Default). 3600 mean
samples (-a:Default).
+
+ "-tipc-link-watcher -d"
+
+ To request samples run:
+ "kill -HUP $(pidof tipc-link-watcher)"
+
+Be aware that after 24 hours window layer 1 begins to slide and oldest
mean value are removed from the window.
+
+#3.5:Run forever (latest 24 hour) as a daemon,
+ every 60 seconds (-p:Default), 60 samples (-s:Default). 3600 mean
samples (-a:Default).
+
+ "-tipc-link-watcher"
+
+ To request samples run:
+ "kill -HUP $(pidof tipc-link-watcher)"
+
+Be aware that after 24 hours window layer 1 begins to slide and oldest
mean value are removed from the window.
+
+
+#3.5:Run for one hour (-x), as daemon, every 60 seconds (-p:Default), 60
samples (-s:Default)
+ dump the samples in "tmp" directory at exit.
+ "-tipc-link-watcher -x"
+
+ To request samples run:
+ "kill -HUP $(pidof tipc-link-watcher)"
+
+#3.6: Run forever; do not run as Daemon(-d), every second(-p), 2 samlpes
(-s) and set thresholds (-o).
+
+"tipc-link-watcher -d -p 1 -s 2 -o t1_rx_loss_rate=10 -o rx_packets=666"
+
+The thresholds warnings will be written in syslog.
+
+#3.7: Run forever, do not run as daemon(-d), every second(-p) and 60
samples (-s), 3600 mean samples (-a:Default), with selected links to watch
(-l)
+
+"tipc-link-watcher -d -p 1 -s 60 -l 1.1.1:bond0-1.1.3:bond0 -l
1.1.1:bond0-1.1.4:bond0"
+
+The program will print the quality levels of selected links: (looks like
this for a link )
+"1.1.1:bond0-1.1.2:bond0: link reset:0 rx_packets:11 tx_packets:71
rx_loss:0 tx_loss:0 Working :)"
+Possible quality levels are: Working, Degraded, Faulty and Down
+
+Watching(-l) links quality is only possible in "non" daemon mode.
+
+The sample files are NOT effected by "-l" option. That means that you will
still get statistic for all published links.
+
+Note: "rx_packets and tx_packets" are mean values of window layer 0!
+ "rx_loss and tx_loss" are presented in precent.
+ "link reset" is number of reset of the link!
+
+
+Attention: There is some bug in TIPC or this code regarding values of
+ rx_packets and tx_packets.
+ When any link is reset, rx_packets and tx_packets are rabish values until
+ the link is restablished.
+=== Tipc link quality ===
+
+t1: Threshold one! 10%
+t2: Threshold two! 30%
+
+This is how the link quality is calculated:
+Down == (Tipc link is not up)
+
+Working == (Tipc link is up) &&
+ (Tipc link not reset during a size of window layer 0, which is -s)
&&
+ ((rx loss rate < t1) && (tx loss rate < t1))
+
+Degraded == (Tipc link is up) &&
+ ((link rested: up->Down->up) ||
+ !((rx loss rate < 10 %) && (tx loss rate < 10 %))) &&
+ ((rx loss rate < t2) && (tx loss rate < t2))
+
+Faulty == (Tipc link is up) &&
+ ((link rested: up->Down->up) ||
+ !((rx loss rate < 10 %) && (tx loss rate < 10 %))) &&
+ !((rx loss rate < t2) && (tx loss rate < t2))
+
+
+=== Couple of notes ===
+
+-Option "-x" and "-a" should not be used at the same time.
+
+-One shot (-x):
+ 1-One full set of samples which are represented by values in layer 0
window of samples. The first sample in
+ the file represents the accumulated staticstic since last time the
link statistics were reset by TIPC.
+ 2-And One single "layer 1" sample which is mean of samples in layer 0
window.
+ Setting "-a" option will not change this behaviour.
+
+-Run forever(absence of -x):
+ When all layer 0 samples (s) are taken every p second, the window
+ layer 0 start to slide. Afterwards, every p seconds "mean of samples of
+ layer 0" are calculated. The calculated mean value will be the sample
+ that is inserted into layer 1 window. The procedure above will continue
+ to fill the layer 1 window until the layer 1 window begin to slide.
+ When layer 1 window begin to slide the oldest value will be deleted
+ from that window.
+ The size of layer 1 window is set by option "-a"
+
+-The dumped sample files name look like this: (in csv format)
+ 2017-05-30.14.26.28_tipc (layer 0 samples)
+ 2017-05-30.14.26.28_tipc_l1 (layer 1 samples; which are mean values of
+ layer 0 samples for every "-p" over every "-s" samples)
+
+-The link "up" and "active" statistics in *_tipc_l1 are the sum
+ of (not mean of) the link "up" and "active" statistics in window layer 0.
+
+-"Mean of" some of properties; such as, "dest" and "mtu" in "*_tipc_l1"
file; it does
+ not make sence.
+ The advantage of mean calculation for these properties is, to reveal any
changes of
+ these values during sampling time.
+ For example: if "mtu" is configured to the value "1500" the mean value of
"mtu"
+ in window layer 0 should of course be equal to "1500".
+
+=== That's all folks ===
diff --git a/tipc-link-watcher/tipc-link-watcher.c
b/tipc-link-watcher/tipc-link-watcher.c
new file mode 100644
index 000000000000..c7c36fce7397
--- /dev/null
+++ b/tipc-link-watcher/tipc-link-watcher.c
@@ -0,0 +1,2143 @@
+/*
+ * tipc-link-watcher.c: TIPC link Fault detection tool
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/timerfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <linux/kernel.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <linux/tipc.h>
+#include <linux/tipc_netlink.h>
+
+#include <pthread.h>
+
+#include <linux/genetlink.h>
+#include <libmnl/libmnl.h>
+
+#define RUN "/run"
+#define LOCKMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
+static char LOCKFILE[512] = {};
+
+#define RESET "\x1b[0m"
+#define RED "\x1b[31m"
+#define GREEN "\x1b[32m"
+#define YELLOW "\x1b[33m"
+
+#define MAX_MY_TIPC_RESOURCE 0x400
+#define SLIDING_WINDOW_L1_SIZE 3600
+
+#define DEFUALT_THRESHOLD_ONE_STATS_RESET_RATE 0
+#define DEFUALT_THRESHOLD_ONE_STATS_RX_LOSS_RATE 10
+#define DEFUALT_THRESHOLD_ONE_STATS_TX_LOSS_RATE 10
+#define DEFUALT_THRESHOLD_TWO_STATS_RESET_RATE 0
+#define DEFUALT_THRESHOLD_TWO_STATS_RX_LOSS_RATE 10
+#define DEFUALT_THRESHOLD_TWO_STATS_TX_LOSS_RATE 10
+
+static int first_sample[MAX_MY_TIPC_RESOURCE];
+static int stat_index, l1_stat_index;
+static int topsrv_sd, timer_fd;
+static unsigned int this_node;
+static char start_time[64];
+static bool take_sample = false;
+
+#define IF_WATCH(W, IN_LIST, FUNC) \
+ do { \
+ if (W) { if (IN_LIST) FUNC; } else {FUNC; } \
+ } \
+ while (0)
+
+#define TIPC_ALARM(EXP, S, T, V, N) \
+ do { if (EXP) \
+ syslog (LOG_WARNING, " %s"" %s" ":%d" " crossed the threshold value
" "%d", N, S, V, T); } \
+ while (0)
+
+#define SET_OFFSET_AND_STEP(OFFSETPOINTER, STEP, OFFSET, STRUCT) \
+ do { \
+ OFFSETPOINTER = (uintptr_t *)((char *)OFFSET +
offsetof(tipceLinkStatistics, STRUCT)); \
+ int i = STEP; \
+ while (i > 0 ) { \
+ OFFSETPOINTER++; \
+ i--; \
+ } \
+ } \
+ while (0)
+
+#define SET_MEM_AND_STEP(OFFSETPOINTER, STEP_SIZE, MEM) \
+ do { \
+ *OFFSETPOINTER = (uintptr_t)MEM; \
+ OFFSETPOINTER++; \
+ MEM += STEP_SIZE; \
+ } \
+ while (0)
+
+enum {
+ THRESHOLD_UNSPEC,
+ THRESHOLD_ONE_STATS_RESET_RATE,
+ THRESHOLD_ONE_STATS_RX_LOSS_RATE,
+ THRESHOLD_ONE_STATS_TX_LOSS_RATE,
+ THRESHOLD_TWO_STATS_RESET_RATE,
+ THRESHOLD_TWO_STATS_RX_LOSS_RATE,
+ THRESHOLD_TWO_STATS_TX_LOSS_RATE,
+ THRESHOLD_MAX
+};
+
+char * const thresholds[] = {
+ [THRESHOLD_UNSPEC] = "thresholed_unspec",
+ [THRESHOLD_ONE_STATS_RESET_RATE] = "t1_reset_rate",
+ [THRESHOLD_ONE_STATS_RX_LOSS_RATE] = "t1_rx_loss_rate",
+ [THRESHOLD_ONE_STATS_TX_LOSS_RATE] = "t1_rx_loss_rate",
+ [THRESHOLD_TWO_STATS_RESET_RATE] = "t2_reset_rate",
+ [THRESHOLD_TWO_STATS_RX_LOSS_RATE] = "t2_rx_loss_rate",
+ [THRESHOLD_TWO_STATS_TX_LOSS_RATE] = "t2_tx_loss_rate",
+ [THRESHOLD_MAX] = NULL,
+};
+
+enum {
+ PRIV_TIPC_NLA_STATS_LINK_STATE,
+ PRIV_TIPC_NLA_STATS_RX_PACKETS,
+ PRIV_TIPC_NLA_STATS_TX_PACKETS,
+ PRIV_TIPC_NLA_STATS_RX_LOSS_RATE,
+ PRIV_TIPC_NLA_STATS_TX_LOSS_RATE,
+ __PRIV_TIPC_NLA_STATS_MAX
+};
+
+char * const stat_opts[] = {
+ [TIPC_NLA_STATS_UNSPEC] = "unspec",
+ [TIPC_NLA_STATS_RX_INFO] = "rx_info",
+ [TIPC_NLA_STATS_RX_FRAGMENTS] = "rx_fragments",
+ [TIPC_NLA_STATS_RX_FRAGMENTED] = "rx_fragmented",
+ [TIPC_NLA_STATS_RX_BUNDLES] = "rx_bundles",
+ [TIPC_NLA_STATS_RX_BUNDLED] = "rx_bundled",
+ [TIPC_NLA_STATS_TX_INFO] = "tx_info",
+ [TIPC_NLA_STATS_TX_FRAGMENTS] = "tx_fragments",
+ [TIPC_NLA_STATS_TX_FRAGMENTED] = "tx_fragmented",
+ [TIPC_NLA_STATS_TX_BUNDLES] = "tx_bundles",
+ [TIPC_NLA_STATS_TX_BUNDLED] = "tx_bundled",
+ [TIPC_NLA_STATS_MSG_PROF_TOT] = "msg_prof_tot",
+ [TIPC_NLA_STATS_MSG_LEN_CNT] = "msg_len_cnt",
+ [TIPC_NLA_STATS_MSG_LEN_TOT] = "msg_len_tot",
+ [TIPC_NLA_STATS_MSG_LEN_P0] = "msg_len_0",
+ [TIPC_NLA_STATS_MSG_LEN_P1] = "msg_len_1",
+ [TIPC_NLA_STATS_MSG_LEN_P2] = "msg_len_2",
+ [TIPC_NLA_STATS_MSG_LEN_P3] = "msg_len_3",
+ [TIPC_NLA_STATS_MSG_LEN_P4] = "msg_len_4",
+ [TIPC_NLA_STATS_MSG_LEN_P5] = "msg_len_5",
+ [TIPC_NLA_STATS_MSG_LEN_P6] = "msg_len_6",
+ [TIPC_NLA_STATS_RX_STATES] = "rx_states",
+ [TIPC_NLA_STATS_RX_PROBES] = "rx_probes",
+ [TIPC_NLA_STATS_RX_NACKS] = "rx_nacks",
+ [TIPC_NLA_STATS_RX_DEFERRED] = "rx_defs",
+ [TIPC_NLA_STATS_TX_STATES] = "tx_states",
+ [TIPC_NLA_STATS_TX_PROBES] = "tx_probes",
+ [TIPC_NLA_STATS_TX_NACKS] = "tx_nacks",
+ [TIPC_NLA_STATS_TX_ACKS] = "tx_acks",
+ [TIPC_NLA_STATS_RETRANSMITTED] = "retrans",
+ [TIPC_NLA_STATS_DUPLICATES] = "dups",
+ [TIPC_NLA_STATS_LINK_CONGS] = "link_congestion",
+ [TIPC_NLA_STATS_MAX_QUEUE] = "send_queue_max",
+ [TIPC_NLA_STATS_AVG_QUEUE] = "send_queue_avg",
+ [__TIPC_NLA_STATS_MAX] = NULL
+};
+
+char * const stat_opts_priv[] = {
+ /* EXTENDED PART*/
+ [PRIV_TIPC_NLA_STATS_LINK_STATE] = "link_state",
+ [PRIV_TIPC_NLA_STATS_RX_PACKETS] = "rx_packets",
+ [PRIV_TIPC_NLA_STATS_TX_PACKETS] = "tx_packets",
+ [PRIV_TIPC_NLA_STATS_RX_LOSS_RATE] = "rx_loss",
+ [PRIV_TIPC_NLA_STATS_TX_LOSS_RATE] = "tx_loss",
+ [__PRIV_TIPC_NLA_STATS_MAX] = NULL
+};
+
+char * const stat_opts_link[] = {
+ [TIPC_NLA_LINK_UNSPEC] = "unspec",
+ [TIPC_NLA_LINK_NAME] = "link",
+ [TIPC_NLA_LINK_DEST] = "dest",
+ [TIPC_NLA_LINK_MTU] = "mtu",
+ [TIPC_NLA_LINK_BROADCAST] = "broadcast",
+ [TIPC_NLA_LINK_UP] = "up",
+ [TIPC_NLA_LINK_ACTIVE] = "active",
+ [TIPC_NLA_LINK_PROP] = "prop",
+ [TIPC_NLA_LINK_STATS] = "stats",
+ [TIPC_NLA_LINK_RX] = "link_rx",
+ [TIPC_NLA_LINK_TX] = "link_tx",
+ [__TIPC_NLA_LINK_MAX] = NULL
+};
+
+uint32_t link_stat_threshold[__TIPC_NLA_STATS_MAX];
+uint32_t link_stat_threshold_priv[__PRIV_TIPC_NLA_STATS_MAX];
+uint32_t link_info_threshold[__TIPC_NLA_LINK_MAX];
+
+uint32_t thresholds_link[THRESHOLD_MAX+1] =
+ {0,
+ DEFUALT_THRESHOLD_ONE_STATS_RESET_RATE,
+ DEFUALT_THRESHOLD_ONE_STATS_RX_LOSS_RATE,
+ DEFUALT_THRESHOLD_ONE_STATS_TX_LOSS_RATE,
+ DEFUALT_THRESHOLD_TWO_STATS_RESET_RATE,
+ DEFUALT_THRESHOLD_TWO_STATS_RX_LOSS_RATE,
+ DEFUALT_THRESHOLD_TWO_STATS_TX_LOSS_RATE
+ };
+
+typedef struct link_miscellaneous {
+ char *link_state; /* TIPC_NLA_LINK_ACTIVE, TIPC_NLA_LINK_UP */
+ uint32_t *rx_packets; /* TIPC_NLA_LINK_RX - TIPC_NLA_STATS_RX_INFO */
+ uint32_t *tx_packets; /* TIPC_NLA_LINK_TX - TIPC_NLA_STATS_TX_INFO */
+ uint32_t *rx_loss; /* perc(TIPC_NLA_STATS_RX_NACKS,
+ PRIV_TIPC_NLA_STATS_TX_PACKETS) */
+ uint32_t *tx_loss; /* prec(TIPC_NLA_STATS_TX_NACKS,
+ PRIV_TIPC_NLA_STATS_RX_PACKETS) */
+}link_miscellaneous;
+
+/* Link info */
+typedef struct link_information {
+ uint32_t *unspec;
+ char *link; /* TIPC_NLA_LINK_NAME */
+ uint32_t *dest; /* TIPC_NLA_LINK_DEST */
+ uint32_t *mtu; /* TIPC_NLA_LINK_MTU */
+ uint32_t *broadcast; /* TIPC_NLA_LINK_BROADCAST */
+ uint32_t *up; /* TIPC_NLA_LINK_UP */
+ uint32_t *active; /* TIPC_NLA_LINK_ACTIVE */
+ uint32_t *prop; /* TIPC_NLA_LINK_PROP */
+ uint32_t *stats; /* TIPC_NLA_LINK_STATS */
+ uint32_t *link_rx; /* TIPC_NLA_LINK_RX */
+ uint32_t *link_tx; /* TIPC_NLA_LINK_TX */
+}link_information;
+
+/* Nest, link propreties. Valid for link, media and bearer */
+typedef struct nest_link_propreties {
+ uint32_t *unspec;
+ uint32_t *priority; /* TIPC_NLA_PROP_PRIO */
+ uint32_t *tolerance; /* TIPC_NLA_PROP_TOL */
+ uint32_t *window; /* TIPC_NLA_PROP_WIN */
+}link_propreties;
+
+/* Nest, statistics info */
+typedef struct nest_stat_information {
+ uint32_t *unspec;
+ uint32_t *rx_info; /* TIPC_NLA_STATS_RX_INFO */
+ uint32_t *rx_fragments; /* TIPC_NLA_STATS_RX_FRAGMENTS */
+ uint32_t *rx_fragmented; /* TIPC_NLA_STATS_RX_FRAGMENTED */
+ uint32_t *rx_bundles; /* TIPC_NLA_STATS_RX_BUNDLED */
+ uint32_t *rx_bundled; /* TIPC_NLA_STATS_RX_BUNDLED */
+ uint32_t *tx_info; /* TIPC_NLA_STATS_TX_INFO */
+ uint32_t *tx_fragments; /* TIPC_NLA_STATS_TX_FRAGMENTS */
+ uint32_t *tx_fragmented; /* TIPC_NLA_STATS_TX_FRAGMENTED */
+ uint32_t *tx_bundles; /* TIPC_NLA_STATS_TX_BUNDLES */
+ uint32_t *tx_bundled; /* TIPC_NLA_STATS_TX_BUNDLED */
+ uint32_t *msg_prof_tot; /* TIPC_NLA_STATS_MSG_PROF_TOT */
+ uint32_t *len_cnt; /* TIPC_NLA_STATS_MSG_LEN_CNT */
+ uint32_t *len_tot; /* TIPC_NLA_STATS_MSG_LEN_TOT */
+ uint32_t *len_0; /* TIPC_NLA_STATS_MSG_LEN_P0 */
+ uint32_t *len_1; /* TIPC_NLA_STATS_MSG_LEN_P1 */
+ uint32_t *len_2; /* TIPC_NLA_STATS_MSG_LEN_P2 */
+ uint32_t *len_3; /* TIPC_NLA_STATS_MSG_LEN_P3 */
+ uint32_t *len_4; /* TIPC_NLA_STATS_MSG_LEN_P4 */
+ uint32_t *len_5; /* TIPC_NLA_STATS_MSG_LEN_P5 */
+ uint32_t *len_6; /* TIPC_NLA_STATS_MSG_LEN_P6 */
+ uint32_t *rx_states; /* TIPC_NLA_STATS_RX_STATES */
+ uint32_t *rx_probes; /* TIPC_NLA_STATS_RX_PROBES */
+ uint32_t *rx_nacks; /* TIPC_NLA_STATS_RX_NACKS */
+ uint32_t *rx_defs; /* TIPC_NLA_STATS_RX_DEFERRED */
+ uint32_t *tx_states; /* TIPC_NLA_STATS_TX_STATES */
+ uint32_t *tx_probes; /* TIPC_NLA_STATS_TX_PROBES */
+ uint32_t *tx_nacks; /* TIPC_NLA_STATS_TX_NACKS */
+ uint32_t *tx_acks ; /* TIPC_NLA_STATS_TX_ACKS */
+ uint32_t *retrans; /* TIPC_NLA_STATS_RETRANSMITTED */
+ uint32_t *dups; /* TIPC_NLA_STATS_DUPLICATES */
+ uint32_t *link_congestion;/* TIPC_NLA_STATS_LINK_CONGS */
+ uint32_t *send_queue_max; /* TIPC_NLA_STATS_MAX_QUEUE */
+ uint32_t *send_queue_avg; /* TIPC_NLA_STATS_AVG_QUEUE */
+}stat_information;
+
+typedef struct link_status {
+ uint64_t up;
+ uint64_t down;
+ uint64_t count_down;
+}link_status;
+
+typedef struct linkStatistics {
+ /* link misc */
+ link_miscellaneous link_misc;
+
+ /* Link info */
+ link_information link_info;
+
+ /* Nest, link propreties. Valid for link, media and bearer */
+ link_propreties link_props;
+
+ /* Nest, statistics info */
+ stat_information stat_info;
+
+ /* Current link status */
+ link_status link_state;
+}tipceLinkStatistics;
+
+
+/* FIXME: This is a copy from struct from socket.c libmnl*/
+struct mnl_socket {
+ int fd;
+ struct sockaddr_nl addr;
+};
+
+struct tipc_link_name_map {
+ struct tipc_sioc_ln_req req;
+ SLIST_ENTRY(tipc_link_name_map) entries;
+};
+
+SLIST_HEAD(listhead, tipc_link_name_map) head =
SLIST_HEAD_INITIALIZER(head);
+
+typedef struct event_status {
+ char *resource;
+ uint32_t event;
+}event_status;
+
+event_status tipc_events[2] =
+ {{.resource = "l_event", .event = TIPC_LINK_STATE},
+ {.resource = "n_event", .event = TIPC_CFG_SRV}};
+
+typedef struct resource_status {
+ char *name;
+ uint64_t up;
+ uint64_t down;
+}resource_status;
+
+resource_status *node_status = NULL;
+
+typedef struct data {
+ int links;
+ int sample_limit;
+ char *mem;
+ tipceLinkStatistics *samples;
+ int counter[1000];
+}data;
+
+data uc_d = {
+ .links = 0,
+ .sample_limit = 0,
+ .mem = NULL,
+ .samples = NULL
+};
+data *uc_dp = &uc_d;
+
+data l1_d = {
+ .links = 0,
+ .sample_limit = 0,
+ .mem = NULL,
+ .samples = NULL
+};
+data *l1_dp = &l1_d;
+
+#define uc_links uc_dp->links
+#define uc_samples uc_dp->samples
+#define uc_tot_mem uc_dp->mem
+
+typedef struct action_resource {
+ int action;
+ char *action_list[MAX_MY_TIPC_RESOURCE];
+}action_resource;
+
+action_resource w_link, w_node, r_link;
+
+action_resource *w_linkp = &w_link;
+action_resource *w_nodep = &w_node;
+action_resource *r_linkp = &r_link;
+
+char *user_dir = NULL;
+typedef struct l0_default_conf {
+ int period;
+ int nos;
+ bool window_passed;
+ int nod;
+ int one_shot;
+}l0_default_conf;
+
+/* default config */
+/* Run as Daemon every minute in one hour and quit without dumping any
file*/
+l0_default_conf l0_w_conf = {
+ .period = 60, /* Number of secounds between samples*/
+ .nos = 60, /* Number of samples*/
+ .window_passed = false,
+ .nod = 0, /* Daemonize, 1 => Not Daemonize*/
+ .one_shot = 0, /* Just run one layer zero window */
+};
+
+typedef struct l1_default_conf {
+ int counter; /* Counter to discover when the window start to slid*/
+ int nos; /* The size of the sliding window*/
+ bool window_passed;
+
+}l1_default_conf;
+
+/* default l1 config */
+l1_default_conf l1_w_conf = {
+ .counter = 0,
+ .nos = SLIDING_WINDOW_L1_SIZE, /* Maximum Number of collected means */
+ .window_passed = false
+};
+
+typedef void (*add_link) (const char *,
+ const char *,
+ int,
+ data **);
+typedef void(*print_sample_to_file) (char *, int, char *,
+ char *, int, int,
+ tipceLinkStatistics **);
+
+static void usage(char *pName)
+{
+ int i;
+ fprintf(stdout,
+ "usage: %s [options]\n"
+ "options:\n"
+ " -h Print this message\n"
+ " -a [value] Number of samples in the window layer 1 which must be gt 0
\n"
+ " These samples are mean of layer 0 full window samples
(Default: 3600 samples)\n"
+ " -d Do not daemonize; i.e. non daemon mode\n"
+ " -f [value] The directory to save the sample files in csv format with
following postfixes:\n"
+ " \"_tipc\" and \"_tipc_l1\" (Default: \"%s\" directory)\n"
+ " -l [value] Link(s) to watch, only in non daemon mode\n"
+ " -n [value] Node(s) to watch, only in non daemon mode\n"
+ " -p [value] Sample period (how often) in secounds (Default: 60
secounds)\n"
+ " -o [option=value] Threshold options: Threshold option value must be gt
0\n"
+ " -s [value] Number of samples in the window layer 0 which must be gt 1
(Default: 60 samples)\n"
+ " -x Run only one window of samples and exit the program; i.e.
\"-s\" samples that are \n"
+ " written to \"_tipc\" file; and one single \"mean of layer 0
window\" sample;\n"
+ " which is written to \"_tipc_l1\" file\n"
+ "\n"
+ " Only tipc \"unicast\" messages are sampled!\n"
+ " Threshold options are only valid for following statistics:\n",
+ pName, P_tmpdir);
+ for (i = THRESHOLD_ONE_STATS_RESET_RATE; i < THRESHOLD_MAX; i ++)
+ fprintf(stdout, " %s \n", thresholds[i]);
+
+ for (i = TIPC_NLA_STATS_RX_INFO; i < (TIPC_NLA_STATS_MAX + 1); i ++)
+ fprintf(stdout, " %s \n", stat_opts[i]);
+
+ for (i = PRIV_TIPC_NLA_STATS_RX_PACKETS; i < __PRIV_TIPC_NLA_STATS_MAX;
i ++)
+ fprintf(stdout, " %s \n", stat_opts_priv[i]);
+ fprintf(stdout, " link_rx\n link_tx \n\n");
+ fprintf(stdout,"-o option example: %s -d -o t1_rx_loss_rate=10 -o
rx_packets=666 -o tx_packets=123117\n", pName);
+ fprintf(stdout,"**************************\n");
+}
+
+static void sigs_term_int(int signo)
+{
+ syslog(LOG_INFO, "The TIPC Network Daemon has terminated by %s signal",
+ strsignal(signo));
+ exit(0);
+}
+
+static void sig_hup(int signo)
+{
+ syslog(LOG_INFO, "Create the csv sample file(s)");
+ take_sample = true;
+}
+
+static void handle_signals (void)
+{
+ struct sigaction sa;
+
+ sa.sa_handler = sigs_term_int;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sa.sa_flags = 0;
+
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ {
+ syslog(LOG_ERR, "sigaction: Failed to catch SIGINT, %s",
strerror(errno));
+ exit(-1);
+ }
+ if (sigaction(SIGTERM, &sa, NULL) < 0)
+ {
+ syslog(LOG_ERR, "sigaction: Failed to catch SIGTERM, %s",
+ strerror(errno));
+ exit(-1);
+ }
+
+ sa.sa_handler = sig_hup;
+ sigemptyset(&sa.sa_mask);
+
+ sigaddset(&sa.sa_mask, SIGINT);
+ sa.sa_flags = SA_RESTART;
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ {
+ syslog(LOG_ERR, "sigaction: Failed to catch SIGHUP, %s",
strerror(errno));
+ exit(-1);
+ }
+}
+
+static uint32_t calc_sum(int samples, uint32_t *array)
+{
+ int sample;
+ uint32_t sum = 0;
+
+ for (sample = 0; sample < samples; sample++)
+ sum = sum + array[sample];
+
+ return sum;
+}
+
+static uint32_t calc_mean(int index, int samples, uint32_t **array, int
first)
+{
+ int sample, skip = 0;
+ uint32_t sum = 0, *data = *array;
+
+ if (samples == 0 )
+ return 0;
+
+ for (sample = 0; sample < samples; sample++) {
+ index = (index + 1) % samples;
+ if (first == 0 && index == 0)
+ {
+ skip = 1;
+ } else {
+ sum = sum + data[index];
+ }
+ }
+ if (skip)
+ {
+ if ((samples - 1) == 0 )
+ return 0;
+ return (sum/(samples - 1));
+ } else {
+ return(sum/samples);
+ }
+}
+
+static uint32_t perc(uint32_t count, uint32_t total)
+{
+ if(total > 0)
+ return (count * 100 + (total / 2)) / total;
+ return 0;
+}
+
+static void print_samples(FILE *csv_fp,
+ const char *opt,
+ uint32_t *stat,
+ int nos,
+ int s_index)
+{
+ int sample=0;
+ char *buf = malloc(strlen(opt) + 2);
+ if (!buf)
+ {
+ syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ }
+ sprintf(buf, "%s,", opt);
+ fprintf(csv_fp,"%s %u", buf, stat[0]);
+ for (sample = 1 ; sample < nos; sample++) {
+ s_index = (s_index + 1 ) % nos;
+ fprintf(csv_fp,"%s %u", ",", stat[s_index]);
+ }
+ fprintf(csv_fp,"\n");
+ free(buf);
+};
+
+static void print_uc_samples_to_file(char *filename,
+ int link,
+ char *start_time, char *end_time,
+ int s_index,
+ int nos,
+ tipceLinkStatistics **samples)
+{
+ FILE *csv_fp;
+ uintptr_t *offset_p;
+ int i;
+ uint32_t *stat_p;
+
+ csv_fp=fopen(filename,"a+");
+ fprintf(csv_fp,"stime: %s , etime: %s,", start_time, end_time);
+ fprintf(csv_fp,"\n");
+ fprintf(csv_fp,"%s, ", (*samples)[link].link_info.link);
+ fprintf(csv_fp,"mtu(%u), ", (*samples)[link].link_info.mtu[link]);
+ fprintf(csv_fp,"priority(%u), ",
(*samples)[link].link_props.priority[link]);
+ fprintf(csv_fp,"tolerance(%u ms), ",
(*samples)[link].link_props.tolerance[link]);
+ fprintf(csv_fp,"window(%u packets), ",
(*samples)[link].link_props.window[link]);
+ fprintf(csv_fp,"link_up(%lu), link_down(%lu)",
+ (*samples)[link].link_state.up, (*samples)[link].link_state.down);
+ fprintf(csv_fp,"\n");
+ /* skip link_state*/
+ SET_OFFSET_AND_STEP(offset_p, 1, &(*samples)[link], link_misc);
+ for (i = PRIV_TIPC_NLA_STATS_RX_PACKETS; i < __PRIV_TIPC_NLA_STATS_MAX;
i++) {
+ stat_p = (uint32_t *)((uintptr_t)*offset_p);
+ print_samples(csv_fp, stat_opts_priv[i], stat_p, nos, s_index);
+ offset_p++;
+ }
+ SET_OFFSET_AND_STEP(offset_p, 2, &(*samples)[link], link_info);
+ for (i = TIPC_NLA_LINK_DEST; i < __TIPC_NLA_LINK_MAX; i++) {
+ stat_p = (uint32_t *)((uintptr_t)*offset_p);
+ if( i == TIPC_NLA_LINK_UP || i == TIPC_NLA_LINK_ACTIVE ||
+ i == TIPC_NLA_LINK_RX || i == TIPC_NLA_LINK_TX)
+ print_samples(csv_fp, stat_opts_link[i], stat_p, nos, s_index);
+ offset_p++;
+ }
+ SET_OFFSET_AND_STEP(offset_p, 1, &(*samples)[link], stat_info);
+ for (i = TIPC_NLA_STATS_RX_INFO; i < __TIPC_NLA_STATS_MAX; i++) {
+ stat_p = (uint32_t *)((uintptr_t)*offset_p);
+ print_samples(csv_fp, stat_opts[i], stat_p, nos, s_index);
+ offset_p++;
+ }
+ fclose(csv_fp);
+};
+
+static char * get_full_path_file_name(const char * post_str)
+{
+ time_t the_time = time(NULL);
+ /* If threadsafe is wished, use localtime_r*/
+ struct tm *tm = localtime(&the_time);
+ char tmp[300], *fullpath, *csv_file;
+
+ strftime(tmp, sizeof(tmp),"%Y-%m-%d.%H.%M.%S",tm);
+ fullpath = malloc(300*strlen(tmp) + 2);
+ if (!fullpath) {
+ syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ }
+ sprintf(fullpath, "%s/%s", user_dir ? user_dir : P_tmpdir, tmp);
+ strcat(fullpath, post_str);
+ csv_file = malloc(150*strlen(tmp) + 2);
+ if (!csv_file)
+ {
+ syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ }
+ strcpy (csv_file, fullpath);
+ free(fullpath);
+ return csv_file;
+}
+
+static void print_to_file(char* csv_file_post_str,
+ int s_index,
+ int nos,
+ print_sample_to_file print2file,
+ data **dp)
+{
+ int j;
+ char end_time[64];
+ time_t the_time = time(NULL);
+ /* If threadsafe is wished, use localtime_r*/
+ struct tm *tm = localtime(&the_time);
+ strftime(end_time, sizeof(end_time),"%Y-%B-%d:%H:%M:%S",tm);
+ char *csv_file = get_full_path_file_name(csv_file_post_str);
+
+ for (j = 0; j < (*dp)->links; j++) {
+ (*print2file)(csv_file, j, start_time, end_time, s_index, nos,
&((*dp)->samples));
+ }
+ free(csv_file);
+}
+
+static void handle_print_to_file(void)
+{
+ print_to_file("_tipc",
+ stat_index,
+ l0_w_conf.window_passed ? l0_w_conf.nos : stat_index,
+ print_uc_samples_to_file,
+ &uc_dp);
+
+ if(l0_w_conf.window_passed)
+ print_to_file("_tipc_l1",
+ l1_stat_index,
+ l1_w_conf.window_passed ? l1_w_conf.nos : l1_stat_index,
+ print_uc_samples_to_file,
+ &l1_dp);
+}
+
+static bool check_link_reset(int link,
+ data *dp)
+{
+ return ((dp->samples)[link].link_state.count_down == 0 );
+}
+
+static bool check_link_up(int link, data *dp, int s_index)
+{
+ return ((dp->samples)[link].link_info.up[s_index] == 0 );
+}
+
+static bool check_threshold_one(int link, data *dp, int s_index)
+{
+ return (((dp->samples)[link].link_misc.rx_loss[s_index] <
+ thresholds_link[THRESHOLD_ONE_STATS_RX_LOSS_RATE]) &&
+ ((dp->samples)[link].link_misc.tx_loss[s_index] <
+ thresholds_link[THRESHOLD_ONE_STATS_TX_LOSS_RATE]));
+}
+
+static bool check_threshold_two_lt(int link, data *dp, int s_index)
+{
+ return (((dp->samples)[link].link_misc.rx_loss[s_index] <
+ thresholds_link[THRESHOLD_TWO_STATS_RX_LOSS_RATE]) &&
+ ((dp->samples)[link].link_misc.tx_loss[s_index] <
+ thresholds_link[THRESHOLD_TWO_STATS_TX_LOSS_RATE]));
+}
+
+static bool check_threshold_two_gt(int link, data *dp, int s_index)
+{
+ return (((dp->samples)[link].link_misc.rx_loss[s_index] >
+ thresholds_link[THRESHOLD_TWO_STATS_RX_LOSS_RATE]) ||
+ ((dp->samples)[link].link_misc.tx_loss[s_index] >
+ thresholds_link[THRESHOLD_TWO_STATS_TX_LOSS_RATE]));
+}
+
+static void link_quality(int link, data *l1_dp, data *uc_dp)
+{
+ if (!check_link_up(link, uc_dp, stat_index))
+ {
+ printf("Down!\n");
+ } else if (check_link_up(link, uc_dp, stat_index) &&
+ check_link_reset(link, uc_dp) &&
+ check_threshold_one(link, l1_dp, l1_stat_index))
+ {
+ printf(GREEN "Working :)" RESET "\n");
+ } else if (check_link_up(link, uc_dp, stat_index) &&
+ (!check_link_reset(link, uc_dp) ||
+ !check_threshold_one(link, l1_dp, l1_stat_index)) &&
+ check_threshold_two_lt(link, l1_dp, l1_stat_index))
+ {
+ printf(YELLOW "Degraded :(" RESET "\n");
+ } else if (check_link_up(link, uc_dp, stat_index) &&
+ (!check_link_reset(link, uc_dp) ||
+ !check_threshold_one(link, l1_dp, l1_stat_index)) &&
+ !check_threshold_two_gt(link, l1_dp, l1_stat_index))
+ {
+ printf(RED "Faulty!!!" RESET "\n");
+ } else {
+ printf(RED "Faulty .... going down!!!" RESET "\n");
+ }
+}
+
+static int already_pid_running(char *cmd)
+{
+ int fd;
+ char buf[16];
+ struct flock fl;
+ memset(&fl, 0, sizeof fl);
+
+ snprintf(LOCKFILE, sizeof(LOCKFILE), "%s/%s.pid", RUN, cmd);
+ fd = open(LOCKFILE, O_RDWR|O_CREAT, LOCKMODE);
+ if (fd < 0)
+ {
+ syslog(LOG_ERR, "open: Failed to open %s -> %s",
+ LOCKFILE, strerror(errno));
+ exit(-1);
+ }
+
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ if (fcntl(fd, F_SETLK, &fl) < 0)
+ {
+ if (errno == EACCES || errno == EAGAIN) {
+ close(fd);
+ return(1);
+ }
+ syslog(LOG_ERR, "fcntl: Failed to lock %s -> %s",
+ LOCKFILE, strerror(errno));
+ exit(-1);
+ }
+
+
+ if (ftruncate(fd, 0) < 0 )
+ {
+ syslog(LOG_ERR, "ftruncate: Failed to truncate %d, %s", fd,
strerror(errno));
+ exit(-1);
+ }
+
+ sprintf(buf, "%ld", (long)getpid());
+
+ if (write(fd, buf, strlen(buf)+1) < 0 )
+ {
+ syslog(LOG_ERR, "write: Failed to write to %d, %s",
+ fd, strerror(errno));
+ exit(-1);
+ }
+ return(0);
+}
+
+static void daemonise(const char *cmd)
+{
+ int i, fd0, fd1, fd2;
+ pid_t pid;
+ struct rlimit rl;
+
+ /*
+ * Clear file creation mask.
+ */
+ umask(0);
+
+ /*
+ * Get maximum number of file descriptors.
+ */
+ if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
+ {
+ syslog(LOG_ERR, "getrlimit: Failed to get file limit: %s",
+ strerror(errno));
+ exit(-1);
+ }
+
+ /*
+ * Become a session leader to lose controlling TTY.
+ */
+ if ((pid = fork()) < 0)
+ {
+ syslog(LOG_ERR, "fork: Failed to fork: %s", strerror(errno));
+ exit(-1);
+ }
+ else if (pid != 0) /* parent */
+ exit(0);
+ setsid();
+
+ /*
+ * Ensure future opens won't allocate controlling TTYs.
+ */
+
+ /* Second fork */
+ if ((pid = fork()) < 0)
+ {
+ syslog(LOG_ERR, "fork: Failed to fork: %s", strerror(errno));
+ exit(-1);
+ }
+ else if (pid != 0) /* parent */
+ exit(0);
+
+ /*
+ * Second child:
+ * 1-The second child is a child of init now.
+ * 2-That the child will never acquire a controlling TTY.
+ */
+
+ /*
+ * Change the current working directory to the root so
+ * we won't prevent file systems from being unmounted.
+ */
+ if (chdir("/") < 0)
+ {
+ syslog(LOG_ERR, "chdir: Failed to change directory to /: %s",
+ strerror(errno));
+ exit(-1);
+ }
+ /*
+ * Close all open file descriptors.
+ */
+ if (rl.rlim_max == RLIM_INFINITY)
+ rl.rlim_max = 1024;
+ for (i = 0; i < rl.rlim_max; i++)
+ close(i);
+
+ /*
+ * Attach file descriptors 0, 1, and 2 to /dev/null.
+ */
+ fd0 = open("/dev/null", O_RDWR);
+ fd1 = dup(0);
+ fd2 = dup(0);
+
+ /*
+ * Initialize the log file.
+ */
+ openlog(cmd, LOG_CONS, LOG_DAEMON);
+ if (fd0 != 0 || fd1 != 1 || fd2 != 2)
+ {
+ syslog(LOG_ERR, "openlog: unexpected file descriptors %d %d %d: %s",
+ fd0, fd1, fd2, strerror(errno));
+ exit(-1);
+ }
+}
+
+static void daemonize_me(char *cmd)
+{
+
+ if(!l0_w_conf.nod)
+ {
+ daemonise(cmd);
+ if (already_pid_running(cmd))
+ {
+ syslog(LOG_ERR, "daemonize_me: daemon already running");
+ exit(-1);
+ }
+ }
+};
+
+static void add_resource(const char *name,
+ resource_status **resource,
+ int *limit,
+ int *count)
+{
+ (*limit)++;
+ resource_status new_resource;
+ memset(&new_resource, 0, sizeof(new_resource));
+ resource_status *resource_p = &new_resource;
+
+ resource_status *newArray = realloc(*resource,
+ (*limit)*sizeof(resource_status));
+ if(!newArray)
+ {
+ free(*resource);
+ syslog(LOG_ERR, "realloc: Failed to realloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ } else {
+ resource_p->name = (char*)malloc(strlen(name) + 1);
+ if (!resource_p->name)
+ {
+ syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ }
+ strncpy(resource_p->name, name, strlen(name));
+ *(resource) = newArray;
+ (*(resource))[(*count)++] = *resource_p;
+ }
+ return;
+}
+
+static int look_up_node_index(char * name,
+ resource_status *node,
+ int len)
+{
+ int i;
+ for (i = 0; i < len ; i++) {
+ if (!strncmp(name, (node)[i].name, strlen(name)))
+ return i;
+ }
+ return -1;
+}
+
+static int look_up_link_index(char *name, char **links, int len)
+{
+ int link;
+ for (link = 0; link < len; link++) {
+ if (!strncmp(name, links[link], strlen(name)))
+ return link;
+ }
+ return -1;
+}
+
+static int insert_link(const char *name,
+ const char *link_state,
+ add_link link2add,
+ int nos,
+ data *dp)
+{
+ int link;
+ for (link = 0; link < dp->links ; link++) {
+ if (!strncmp(name, dp->samples[link].link_info.link, strlen(name)))
+ return link;
+ }
+ (*link2add)(name, link_state, nos, &dp);
+ return (dp->links - 1);
+}
+
+static void uc_add_link(const char *name,
+ const char * link_state,
+ int nos,
+ data **uc_dp)
+{
+ /* "nos + 1" in the function allocates memory for last sampled value!
+ * 2bused in the delta calculation!
+ */
+ ((*uc_dp)->sample_limit)++;
+ int i;
+ uint16_t size_tot_chars, size_tot_chars_2b_alligend;
+ uint16_t size_tot_u32s;
+
+ uint16_t step_size = (nos + 1)*sizeof(uint32_t);
+ tipceLinkStatistics new_l;
+ tipceLinkStatistics *new_link = &new_l;
+ tipceLinkStatistics *new_samples = realloc((*uc_dp)->samples,
((*uc_dp)->sample_limit)*sizeof(tipceLinkStatistics));
+ char *tot_mem;
+
+ if(!new_samples)
+ {
+ free((*uc_dp)->samples);
+ syslog(LOG_ERR, "uc realloc: Failed to realloc buffer, %s",
+ strerror(errno));
+ exit(-1);
+ } else {
+ uintptr_t *offset_p;
+
+ /* Memory to 2b allocted for nos*linkstate and link name */
+ size_tot_chars_2b_alligend = strlen(link_state)*(nos + 1 ) + (nos + 1
) + strlen(name) + 1 ; /* null characters*/
+ size_tot_chars = (size_tot_chars_2b_alligend + (4 - 1)) & - 4; /*
Round up to 4-byte boundary */
+ size_tot_u32s = (sizeof(tipceLinkStatistics) / sizeof(uintptr_t)) -
(2); /* const strings*/
+ (*uc_dp)->mem = calloc((nos + 1), sizeof(uint32_t)*size_tot_u32s +
size_tot_chars);
+ tot_mem = (*uc_dp)->mem;
+ /* linkstate miscellaneous */
+ new_link->link_misc.link_state = tot_mem;
+ strncpy(new_link->link_misc.link_state, link_state,
strlen(link_state));
+ *(new_link->link_misc.link_state + strlen(link_state)) = '\0';
+
+ /* Make space for nos of linkstate string and null character!*/
+ tot_mem = tot_mem + (((strlen(link_state)*nos + nos) + (4 - 1)) & -
4);
+ SET_OFFSET_AND_STEP(offset_p, 1, new_link, link_misc);
+
+ for ( i = PRIV_TIPC_NLA_STATS_RX_PACKETS; i <
__PRIV_TIPC_NLA_STATS_MAX; i++ )
+ SET_MEM_AND_STEP(offset_p, step_size, tot_mem);
+
+ new_link->link_info.link = tot_mem;
+ strncpy(new_link->link_info.link, name, strlen(name));
+ *(new_link->link_info.link + strlen(name)) = '\0';
+ tot_mem = tot_mem + (((strlen(name) + 1) + (4 - 1)) & - 4);
+ SET_OFFSET_AND_STEP(offset_p, 0, new_link, link_info);
+ /* skip unspec*/
+ SET_MEM_AND_STEP(offset_p, step_size, tot_mem);
+ /* skip link pointer */
+ offset_p++;
+ for ( i = TIPC_NLA_LINK_DEST; i < __TIPC_NLA_LINK_MAX; i++ )
+ SET_MEM_AND_STEP(offset_p, step_size, tot_mem);
+
+ /* nest link propreties */
+ SET_OFFSET_AND_STEP(offset_p, 0, new_link, link_props);
+ /* skip unspec */
+ SET_MEM_AND_STEP(offset_p, step_size, tot_mem);
+ for ( i = TIPC_NLA_PROP_PRIO; i < __TIPC_NLA_PROP_MAX; i++ )
+ SET_MEM_AND_STEP(offset_p, step_size, tot_mem);
+
+ /* nest stat information */
+ SET_OFFSET_AND_STEP(offset_p, 0, new_link, stat_info);
+ SET_MEM_AND_STEP(offset_p, step_size, tot_mem);
+ for ( i = TIPC_NLA_STATS_RX_INFO; i < __TIPC_NLA_STATS_MAX; i++ )
+ SET_MEM_AND_STEP(offset_p, step_size, tot_mem);
+
+ /* link status FIXME: dubble check" if tot_mem includes this part
too*/
+ SET_OFFSET_AND_STEP(offset_p ,0 ,new_link , link_state);
+ *offset_p = (uintptr_t)tot_mem;
+ memset((link_status *)offset_p, 0, sizeof(link_status));
+
+ (*uc_dp)->samples = new_samples;
+ ((*uc_dp)->samples)[((*uc_dp)->links)++] = *new_link;
+ }
+ return;
+}
+
+static void log_event(int topsrv_sd,
+ struct tipc_event *evt)
+{
+
+ static int count = 0;
+ static int limit = 0;
+ int node;
+
+ char r_name[TIPC_MAX_LINK_NAME];
+ char full_name[TIPC_MAX_LINK_NAME + 100];
+ struct tipc_link_name_map *link;
+ struct tipc_sioc_ln_req lnr = {
+ .peer = ntohl(evt->found_lower),
+ .bearer_id = ntohl(evt->port.ref)
+ };
+
+ if (ntohl(evt->event) == TIPC_PUBLISHED)
+ {
+ /* UP*/
+ if ((ntohl((*evt).s.seq.type)) == TIPC_LINK_STATE)
+ {
+ if (ioctl(topsrv_sd, SIOCGETLINKNAME, &lnr) < 0)
+ {
+ syslog(LOG_ERR, "ioctl: Failed, %s", strerror(errno));
+ exit(-1);
+ } else {
+ strcpy(r_name, lnr.linkname);
+ sprintf(full_name, "<%s> on network plane %c",
+ r_name, lnr.bearer_id + 'A');
+ }
+ link = malloc(sizeof(struct tipc_link_name_map));
+ if (!link)
+ {
+ syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ }
+ link->req.peer = lnr.peer;
+ link->req.bearer_id = lnr.bearer_id;
+ strcpy(link->req.linkname, lnr.linkname);
+
+ SLIST_INSERT_HEAD(&head, link, entries);
+
+ int uc_index = insert_link(r_name, "UP", uc_add_link,
+ l0_w_conf.nos, uc_dp);
+ (uc_dp->samples)[uc_index].link_state.up++;
+ IF_WATCH(w_linkp->action,
+ ((look_up_link_index(r_name, w_linkp->action_list, w_linkp->action)) >=
0),
+ syslog(LOG_INFO, "Published link: %s\n", full_name));
+
+ if (l0_w_conf.window_passed)
+ if((uc_dp->samples)[uc_index].link_state.down)
+ IF_WATCH(w_linkp->action,
+ ((look_up_link_index((uc_dp->samples)[uc_index].link_info.link,
+ w_linkp->action_list, w_linkp->action)) >= 0),
+ link_quality(uc_index, l1_dp, uc_dp));
+ } else { /* TIPC_CFG_SRV */
+ node = ntohl(evt->port.node);
+ sprintf(r_name, "%u.%u.%u",
+ tipc_zone(node), tipc_cluster(node), tipc_node(node));
+ IF_WATCH(w_nodep->action,
+ ((look_up_link_index(r_name, w_nodep->action_list, w_nodep->action)) >=
0),
+ syslog(LOG_INFO, "Published node: %s\n", r_name));
+
+ node = look_up_node_index(r_name, node_status, count);
+ if(node < 0)
+ {
+ add_resource(r_name,
+ &node_status,
+ &limit,
+ &count);
+ node = count - 1;
+ }
+ (node_status)[node].up++;
+ }
+
+ } else if (evt->event == htonl(TIPC_WITHDRAWN))
+ {
+ int found = 0;
+ SLIST_FOREACH(link, &head, entries) {
+ if ((link->req.peer == lnr.peer) &&
+ (link->req.bearer_id == lnr.bearer_id)) {
+ strcpy(r_name, link->req.linkname);
+ SLIST_REMOVE(&head, link, tipc_link_name_map, entries);
+ free(link);
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ syslog(LOG_ERR, "Unknown link with peer:%x bearer_id:%x withdrawn!",
+ lnr.peer, lnr.bearer_id);
+ return;
+ }
+ /* Down*/
+ if ((ntohl((*evt).s.seq.type)) == TIPC_LINK_STATE)
+ {
+ int link = -1;
+ for ( link = 0; link < uc_dp->links ; link++ ) {
+ if (!strncmp(r_name, uc_dp->samples[link].link_info.link,
strlen(r_name)))
+ break;
+ }
+
+ if (link < 0 )
+ {
+ syslog(LOG_ERR, "Did I? missed to insert the link!");
+ exit(-1);
+ }
+ (uc_dp->samples)[link].link_state.down++;
+ (uc_dp->samples)[link].link_state.count_down = l0_w_conf.nos;
+
+ if (l0_w_conf.window_passed)
+ IF_WATCH(w_linkp->action,
+ ((look_up_link_index(((uc_dp)->samples)[link].link_info.link,
+ w_linkp->action_list, w_linkp->action)) >= 0),
+ link_quality(link, l1_dp, uc_dp));
+ IF_WATCH(w_linkp->action,
+ ((look_up_link_index(r_name, w_linkp->action_list, w_linkp->action))
= 0),
+ syslog(LOG_INFO, "Withdrawn link: %s\n", full_name));
+ } else {
+ if(look_up_node_index(r_name, node_status, count) < 0)
+ {
+ syslog(LOG_ERR, "Did I? missed to insert the node!");
+ exit(-1);
+ }
+ printf("Withdrawn node: %s\n", r_name);
+ }
+ }
+ else if (evt->event == htonl(TIPC_SUBSCR_TIMEOUT))
+ syslog(LOG_WARNING, "TIPC_SUBSCR_TIMEOUT event");
+ else
+ syslog(LOG_WARNING, "unknown event type (%u)\n", ntohl(evt->event));
+ return;
+}
+
+static int unicast_sample(struct nlattr *attrs[],
+ struct nlattr *prop[],
+ struct nlattr *stats[],
+ int index,
+ data *dp,
+ const char *name)
+{
+ int i;
+ uint32_t *last_p;
+ uint32_t *stat_p;
+ uintptr_t *offset_p;
+
+ uint32_t threshold = 0;
+ stat_information *offset_stat_info_p;
+ link_information *offset_link_info_p;
+ link_miscellaneous *offset_link_misc_p;
+ /*skip unspec and link*/
+ SET_OFFSET_AND_STEP(offset_p, 2, &(dp->samples)[index], link_info);
+ for ( i = TIPC_NLA_LINK_DEST; i < __TIPC_NLA_LINK_MAX; i++) {
+ stat_p = (uint32_t *)(uintptr_t)*offset_p;
+ last_p = &stat_p[l0_w_conf.nos];
+ stat_p += stat_index;
+ if ((i < TIPC_NLA_LINK_BROADCAST) || (i > TIPC_NLA_LINK_STATS))
+ {
+ if( i > TIPC_NLA_LINK_STATS)
+ {
+ if(dp->counter[index] < (l0_w_conf.nos) )
+ {
+ /* link_rx, link_tx */
+ if(stat_index)
+ {
+ *stat_p = mnl_attr_get_u32(attrs[i]) - *last_p;
+ } else {
+ *stat_p = mnl_attr_get_u32(attrs[i]);
+ }
+ } else {
+ *stat_p = mnl_attr_get_u32(attrs[i]) - *last_p;
+ }
+ *last_p = mnl_attr_get_u32(attrs[i]);
+ threshold = link_info_threshold[i];
+ TIPC_ALARM(((threshold > 0) && (*stat_p > threshold)),
stat_opts_link[i], threshold, *stat_p, name);
+ } else {
+ /*dest mtu*/
+ *stat_p = mnl_attr_get_u32(attrs[i]);
+ }
+ } else {
+ /*bc, up and active*/
+ if(i < TIPC_NLA_LINK_PROP)
+ {
+ *stat_p = attrs[i] ? 0: 1;
+ }
+ }
+ offset_p++;
+ }
+ /*skip unspec*/
+ SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], link_props);
+ for ( i = TIPC_NLA_PROP_PRIO; i < __TIPC_NLA_PROP_MAX; i++) {
+ stat_p = (uint32_t *)(uintptr_t)*offset_p;
+ stat_p += stat_index;
+ *stat_p = mnl_attr_get_u32(prop[i]);
+ offset_p++;
+ }
+ /*skip unspec*/
+ SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], stat_info);
+ for ( i = TIPC_NLA_STATS_RX_INFO; i < __TIPC_NLA_STATS_MAX; i++) {
+ stat_p = (uint32_t *)(uintptr_t)*offset_p;
+ last_p = &stat_p[l0_w_conf.nos];
+ stat_p += stat_index;
+
+ if(dp->counter[index] < (l0_w_conf.nos))
+ {
+ if(stat_index)
+ {
+ *stat_p = mnl_attr_get_u32(stats[i]) - *last_p;
+ } else {
+ *stat_p = mnl_attr_get_u32(stats[i]);
+ }
+ } else {
+ *stat_p = mnl_attr_get_u32(stats[i]) - *last_p;
+ }
+ *last_p = mnl_attr_get_u32(stats[i]);
+ offset_p++;
+ threshold = link_stat_threshold[i];
+ TIPC_ALARM(((threshold > 0) && (*stat_p > threshold)), stat_opts[i],
threshold, *stat_p, name);
+ }
+
+ offset_stat_info_p = (stat_information *)(uintptr_t *)((char
*)&(dp->samples)[index] + offsetof(tipceLinkStatistics, stat_info));
+ offset_link_info_p = (link_information *)(uintptr_t *)((char
*)&(dp->samples)[index] + offsetof(tipceLinkStatistics, link_info));
+ offset_link_misc_p = (link_miscellaneous *)(uintptr_t *)((char
*)&(dp->samples)[index] + offsetof(tipceLinkStatistics, link_misc));
+
+ offset_link_misc_p->rx_packets[stat_index] =
+ offset_link_info_p->link_rx[stat_index] -
offset_stat_info_p->rx_info[stat_index];
+ threshold = link_stat_threshold_priv[PRIV_TIPC_NLA_STATS_RX_PACKETS];
+ TIPC_ALARM(((threshold > 0) &&
(offset_link_misc_p->rx_packets[stat_index] > threshold)),
+ "rx_packets", threshold, offset_link_misc_p->rx_packets[stat_index],
name);
+
+ offset_link_misc_p->tx_packets[stat_index] =
+ offset_link_info_p->link_tx[stat_index] -
offset_stat_info_p->tx_info[stat_index];
+ threshold = link_stat_threshold_priv[PRIV_TIPC_NLA_STATS_TX_PACKETS];
+ TIPC_ALARM(((threshold > 0) &&
+ ( offset_link_misc_p->tx_packets[stat_index] > threshold)),
+ "tx_packets", threshold, offset_link_misc_p->tx_packets[stat_index],
name);
+
+ offset_link_misc_p->rx_loss[stat_index] = perc(
offset_stat_info_p->rx_nacks[stat_index],
+ offset_link_misc_p->tx_packets[stat_index]);
+ threshold = link_stat_threshold_priv[PRIV_TIPC_NLA_STATS_RX_LOSS_RATE];
+ TIPC_ALARM(((threshold > 0) &&
+ (offset_link_misc_p->rx_loss[stat_index] > threshold)),
+ "rx_loss", threshold , offset_link_misc_p->rx_loss[stat_index], name);
+
+ offset_link_misc_p->tx_loss[stat_index] =
perc(offset_stat_info_p->tx_nacks[stat_index],
+ offset_link_misc_p->rx_packets[stat_index]);
+ threshold = link_stat_threshold_priv[PRIV_TIPC_NLA_STATS_TX_LOSS_RATE];
+ TIPC_ALARM(((threshold > 0) &&
+ (offset_link_misc_p->tx_loss[stat_index] > threshold)),
+ "tx_loss", threshold, offset_link_misc_p->tx_loss[stat_index], name);
+
+ dp->counter[index]++;
+ return MNL_CB_OK;
+}
+
+static int l1_unicast_sample(int index,
+ int uc_index,
+ data *uc_dp,
+ data *dp)
+{
+ int i;
+ uint32_t *uc_stat_p;
+ uint32_t *stat_p;
+ uintptr_t *offset_p;
+ uintptr_t *uc_offset_p;
+
+ SET_OFFSET_AND_STEP(offset_p, 2, &(dp->samples)[index], link_info);
+ SET_OFFSET_AND_STEP(uc_offset_p, 2, &(uc_dp->samples)[uc_index],
link_info);
+
+ /* FIXME: fix the print to csv file b4 rewrite this part!
+ it does not make sence to calculate some of following propreties*/
+ for ( i = TIPC_NLA_LINK_DEST; i < __TIPC_NLA_LINK_MAX; i++)
+ {
+ stat_p = (uint32_t *)(uintptr_t)*offset_p;
+ stat_p += l1_stat_index;
+ uc_stat_p = (uint32_t *)(uintptr_t)*uc_offset_p;
+
+ if ((i < TIPC_NLA_LINK_BROADCAST) || (i > TIPC_NLA_LINK_STATS))
+ {
+ /* link_rx, link_tx
+ * dest, mtu: should be the same value as dest and mtu values
+ * in layer 0 window.
+ */
+ *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p,
first_sample[uc_index]);
+ } else {
+ /* up and active*/
+ if((i < TIPC_NLA_LINK_PROP) && (i > TIPC_NLA_LINK_BROADCAST))
+ /* *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p,
first_sample[uc_index]); */
+ *stat_p = calc_sum(l0_w_conf.nos, uc_stat_p);
+ }
+ offset_p++;
+ uc_offset_p++;
+ }
+
+ SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], link_props);
+ SET_OFFSET_AND_STEP(uc_offset_p, 1, &(uc_dp->samples)[index], link_props);
+ for (i = TIPC_NLA_PROP_PRIO; i < __TIPC_NLA_PROP_MAX; i++) {
+ stat_p = (uint32_t *)(uintptr_t)*offset_p;
+ stat_p += l1_stat_index;
+ uc_stat_p = (uint32_t *)(uintptr_t)*uc_offset_p;
+ *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p,
first_sample[uc_index]);
+ offset_p++;
+ uc_offset_p++;
+ }
+ SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], stat_info);
+ SET_OFFSET_AND_STEP(uc_offset_p, 1, &(uc_dp->samples)[index], stat_info);
+ for (i = TIPC_NLA_STATS_RX_INFO; i < __TIPC_NLA_STATS_MAX; i++) {
+ stat_p = (uint32_t *)(uintptr_t)*offset_p;
+ stat_p += l1_stat_index;
+ uc_stat_p = (uint32_t *)(uintptr_t)*uc_offset_p;
+ *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p,
first_sample[uc_index]);
+ offset_p++;
+ uc_offset_p++;
+ }
+ SET_OFFSET_AND_STEP(offset_p, 1, &(dp->samples)[index], link_misc);
+ SET_OFFSET_AND_STEP( uc_offset_p, 1, &(uc_dp->samples)[index], link_misc);
+
+ IF_WATCH(w_linkp->action,
+ (look_up_link_index(uc_dp->samples[index].link_info.link,
+ w_linkp->action_list, w_linkp->action) >= 0),
+ printf("\n%s: link reset:%-10lu ",
(uc_dp)->samples[index].link_info.link,
(uc_dp)->samples[index].link_state.down));
+
+ for (i = PRIV_TIPC_NLA_STATS_RX_PACKETS; i < __PRIV_TIPC_NLA_STATS_MAX;
i++) {
+ stat_p = (uint32_t *)(uintptr_t)*offset_p;
+ stat_p += l1_stat_index;
+ uc_stat_p = (uint32_t *)(uintptr_t)*uc_offset_p;
+ *stat_p = calc_mean(stat_index, l0_w_conf.nos, &uc_stat_p,
first_sample[uc_index]);
+ /*
+ * Attention: There is some bug in TIPC or this code regarding values of
+ * rx_packets and tx_packets.
+ * When any link is reset, rx_packets and tx_packets are rabish values
until the link
+ * is restablished.
+ */
+ IF_WATCH(w_linkp->action,
+ (look_up_link_index(uc_dp->samples[index].link_info.link,
w_linkp->action_list, w_linkp->action) >= 0),
+ printf("%s:%-10u ", stat_opts_priv[i], *stat_p));
+ offset_p++;
+ uc_offset_p++;
+ }
+ dp->counter[index]++;
+ return MNL_CB_OK;
+}
+
+static int parse_attrs(const struct nlattr *attr, void *data)
+{
+ const struct nlattr **tb = data;
+
+ tb[ mnl_attr_get_type(attr)] = attr;
+ return MNL_CB_OK;
+}
+
+static int family_id_cb(const struct nlmsghdr *nlh, void *data)
+{
+ struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ int *id = data;
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, tb);
+ if (!tb[CTRL_ATTR_FAMILY_ID])
+ return MNL_CB_ERROR;
+
+ *id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
+ return MNL_CB_OK;
+}
+
+
+static void nl_prep_msg(struct nlmsghdr **nlhp,
+ void *buf,
+ uint16_t nlmsg_type,
+ uint16_t nlmsg_flags,
+ uint16_t cmd, /* uint16_t? */
+ time_t *nlSeq)
+{
+ /*
+ Allocate a buffer of MNL_SOCKET_BUFFER_SIZE
+ (which is 8KB, see linux/netlink.h for more information).
+ Using this buffer size ensures that the buffer is big enough
+ to store the netlink message without truncating it.
+ */
+
+ /* Pointer to Generic Netlink message header */
+ struct genlmsghdr *genlh;
+ struct nlmsghdr *nlh;
+ nlh = mnl_nlmsg_put_header(buf);
+ nlh->nlmsg_type = nlmsg_type;
+ nlh->nlmsg_flags = nlmsg_flags;
+
+ (void)time(nlSeq);
+ nlh->nlmsg_seq = *nlSeq;
+
+ genlh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
+ genlh->cmd = cmd;
+ genlh->version = 1;
+ *nlhp = nlh;
+ return;
+}
+
+static void nl_send_msg(struct mnl_socket **nls,
+ struct nlmsghdr *nlh,
+ int nlSocket,
+ pid_t portId)
+{
+ /* R we getting the right errno when mnl_*_* funcs returns?!*/
+ if ((*nls = mnl_socket_open(nlSocket) ) == NULL)
+ {
+ syslog(LOG_ERR, "mnl_socket_open: Failed, %s", strerror(errno));
+ exit(-1);
+ }
+
+ if (mnl_socket_bind(*nls, 0, portId) < 0)
+ {
+ mnl_socket_close(*nls);
+ syslog(LOG_ERR, "mnl_socket_bind: Failed, %s", strerror(errno));
+ exit(-1);
+ }
+
+ /* Maybe check return value equal to nlh->nlmsg_len? */
+ if (mnl_socket_sendto(*nls, nlh, nlh->nlmsg_len) < 0)
+ {
+ mnl_socket_close(*nls);
+ syslog(LOG_ERR, "mnl_socket_sendto: Failed, %s", strerror(errno));
+ exit(-1);
+ }
+ return;
+}
+
+static void nl_rcv_msg(struct mnl_socket *nls,
+ mnl_cb_t cb,
+ void *cb_data,
+ time_t *nlSeq)
+{
+ int ret;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+ uint32_t portId = mnl_socket_get_portid(nls);
+
+ if ((ret = mnl_socket_recvfrom(nls, buf, sizeof(buf))) < 0)
+ {
+ mnl_socket_close(nls);
+ syslog(LOG_ERR, "mnl_socket_recvfrom: Failed, %s", strerror(errno));
+ exit(-1);
+ }
+
+ while (ret > 0) {
+ ret = mnl_cb_run(buf,
+ ret,
+ (uint32_t)*nlSeq,
+ portId,
+ cb,
+ cb_data);
+ if (ret <= 0) {
+ if (ret == MNL_CB_ERROR)
+ {
+ mnl_socket_close(nls);
+ syslog(LOG_ERR, "mnl_cb_run: Failed, (MNL_CB_ERROR) %s",
strerror(errno));
+ exit(-1);
+ }
+ break;
+ }
+
+ if ((ret = mnl_socket_recvfrom(nls, buf, sizeof(buf))) < 0)
+ {
+ mnl_socket_close(nls);
+ syslog(LOG_ERR, "mnl_socket_recvfrom: Failed, %s", strerror(errno));
+ exit(-1);
+ }
+ }
+ return;
+}
+
+static int get_tipc_family(void)
+{
+ int nlFamily;
+ time_t nlSeq;
+ struct mnl_socket *nls;
+ struct nlmsghdr *nlh;
+ char buf[MNL_SOCKET_BUFFER_SIZE];
+
+ nl_prep_msg(&nlh,
+ buf,
+ GENL_ID_CTRL,
+ NLM_F_REQUEST | NLM_F_ACK,
+ CTRL_CMD_GETFAMILY,
+ &nlSeq);
+
+ mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
+ mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, TIPC_GENL_V2_NAME);
+
+ nl_send_msg(&nls,
+ nlh,
+ NETLINK_GENERIC,
+ MNL_SOCKET_AUTOPID);
+
+ nl_rcv_msg(nls,
+ family_id_cb,
+ &nlFamily,
+ &nlSeq);
+ mnl_socket_close(nls);
+
+ return nlFamily;
+}
+
+static int validate_opt(const char* str)
+{
+ size_t i, len = strlen(str);
+
+ for (i = 0; i < len; i++) {
+ if(!isdigit(str[i]))
+ return 0;
+ }
+ return 1;
+}
+
+static bool handle_getsubopt(char **optionp, char *const *tokens,
+ uint32_t *t_link, int t_low, int t_high)
+{
+ int sub_opt;
+ int tmp;
+ char *valuep;
+ while (*optionp != '\0') {
+ sub_opt = getsubopt (optionp, tokens, &valuep);
+ if ((t_low < sub_opt) && (sub_opt < t_high) )
+ {
+ if ( *valuep )
+ {
+ tmp = atoi (valuep);
+ if (tmp > 0)
+ {
+ t_link[sub_opt] = atoi (valuep);
+ return true;
+ }
+ }
+ fprintf(stderr, "The suboption `%s is not given any valid value!\n",
+ tokens[sub_opt]);
+ fflush(stderr);
+ abort ();
+ }
+ else
+ return false;
+ }
+ return false;
+}
+
+static int handle_opts(int argc, char *const argv[])
+{
+
+ int opt;
+ bool found =false;
+ char *pName = strrchr(argv[0], '/');
+ pName = pName ? 1+pName : argv[0];
+
+ while (EOF != (opt = getopt(argc, argv, "hdxs:a:p:f:l:n:r:o:"))) {
+
+ switch (opt) {
+ case 'r':
+ /* Reset following link(s) */
+ r_linkp->action_list[r_linkp->action] = malloc(strlen(optarg) + 1);
+ if (!r_linkp->action_list[r_linkp->action])
+ {
+ syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ }
+ strncpy(r_linkp->action_list[r_linkp->action], optarg,
strlen(optarg));
+ r_linkp->action++;
+ break;
+ case 'l':
+ /* Watch only following link(s) */
+ w_linkp->action_list[w_linkp->action] = malloc(strlen(optarg) + 1);
+ if (!w_linkp->action_list[w_linkp->action])
+ {
+ syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ }
+ strncpy(w_linkp->action_list[w_linkp->action], optarg,
strlen(optarg));
+ w_linkp->action++;
+ break;
+ case 'n':
+ /* Watch only following nodes(s) */
+ w_nodep->action_list[w_nodep->action] = malloc(strlen(optarg) + 1);
+ if (!w_nodep->action_list[w_nodep->action])
+ {
+ syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ }
+ strncpy(w_nodep->action_list[w_nodep->action], optarg,
strlen(optarg));
+ w_nodep->action++;
+ break;
+ case 'f':
+ printf(" %d %s \n", __LINE__, optarg);
+ fflush(stdout);
+ user_dir = malloc(strlen(optarg) + 1);
+ if (!user_dir)
+ {
+ syslog(LOG_ERR, "malloc: Failed to alloc buffer , %s",
+ strerror(errno));
+ exit(-1);
+ }
+ strncpy(user_dir, optarg, strlen(optarg));
+ printf(" %d \n", __LINE__);
+ fflush(stdout);
+ break;
+ case 'x':
+ /* Only one sample */
+ l0_w_conf.one_shot = 1;
+ break;
+ case 'd':
+ /* Do not daemonize */
+ l0_w_conf.nod = 1;
+ break;
+ case 's':
+ /* Number of samples, The size of sliding window level zero*/
+ if(validate_opt(optarg))
+ {
+ l0_w_conf.nos = atoi(optarg);
+ if(l0_w_conf.nos < 2)
+ {
+ printf("The size of the window must be gt one!\n");
+ abort();
+ }
+ } else {
+ printf("option requires an argument\n");
+ abort();
+ }
+ break;
+ case 'a':
+ /* The size of sliding window level one; mean of samples of window
level zero*/
+ if(validate_opt(optarg))
+ {
+ l1_w_conf.nos = atoi(optarg);
+ if(l1_w_conf.nos < 1)
+ {
+ printf("The size of the window must be gt zero!\n");
+ abort();
+ }
+ }
+ else {
+ printf("option requires an argument\n");
+ abort();
+ }
+ break;
+ case 'p':
+ /* How often*/
+ if(validate_opt(optarg))
+ l0_w_conf.period = atoi(optarg);
+ else {
+ printf("option requires an argument\n");
+ abort();
+ }
+ break;
+ case 'o':
+ {
+ char *sub_opts;
+ sub_opts = optarg;
+ found = handle_getsubopt(&sub_opts, thresholds,
+ thresholds_link,
+ THRESHOLD_UNSPEC,
+ THRESHOLD_MAX);
+ if(!found)
+ {
+ sub_opts = optarg;
+ found = handle_getsubopt(&sub_opts, stat_opts_link,
+ link_info_threshold,
+ TIPC_NLA_LINK_STATS,
+ __TIPC_NLA_LINK_MAX);
+ }
+ if(!found)
+ {
+ sub_opts = optarg;
+ found = handle_getsubopt(&sub_opts, stat_opts,
+ link_stat_threshold,
+ TIPC_NLA_STATS_UNSPEC,
+ __TIPC_NLA_STATS_MAX);
+ }
+ if(!found)
+ {
+ sub_opts = optarg;
+ found = handle_getsubopt(&sub_opts, stat_opts_priv,
+ link_stat_threshold_priv,
+ PRIV_TIPC_NLA_STATS_LINK_STATE,
+ __PRIV_TIPC_NLA_STATS_MAX);
+ }
+ if(found)
+ found=false;
+ else {
+ fprintf(stderr,"Unknown suboption\n");
+ abort();
+ }
+ }
+ break;
+ case 'h':
+ usage(pName);
+ exit(0);
+ case ':':
+ case '?':
+ default:
+ usage(pName);
+ exit(-1);
+ }
+ }
+ return 0;
+}
+
+static void topsrv_subscibe(event_status *events, int len)
+{
+ int i;
+ struct sockaddr_tipc sa = {
+ .family = AF_TIPC,
+ .addrtype = TIPC_ADDR_NAME,
+ .addr.name.name.type = TIPC_TOP_SRV,
+ .addr.name.name.instance = TIPC_TOP_SRV,
+ .addr.name.domain = 0
+ };
+ socklen_t sa_len = sizeof(sa);
+
+ if (!(topsrv_sd = socket(AF_TIPC, SOCK_SEQPACKET, 0)))
+ {
+ syslog(LOG_ERR, "socket: Failed to open TIPC socket: %s",
+ strerror(errno));
+ exit(-1);
+ }
+
+ if ( 0 > connect(topsrv_sd, (struct sockaddr*)&sa, sizeof(sa)))
+ {
+ syslog(LOG_ERR, "connect: Failed to connect to TIPC topology server:
%s",
+ strerror(errno));
+ exit(-1);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa_len = sizeof(sa);
+
+ if (0 > getsockname(topsrv_sd, (struct sockaddr*)&sa, &sa_len))
+ {
+ syslog(LOG_ERR, "getsockname: Failed to get this node TIPC address:
%s",
+ strerror(errno));
+ exit(-1);
+ }
+
+ this_node = sa.addr.id.node;
+
+ for (i = 0; i < len; i++) {
+
+ struct tipc_subscr link_sub = {
+ .seq.type = htonl(events[i].event), /* topology service name type */
+ .seq.lower = htonl(0),
+ .seq.upper = htonl(~0),
+ /* subscription duration set 24ever*/
+ .timeout = htonl(TIPC_WAIT_FOREVER),
+ .filter = htonl(TIPC_SUB_PORTS),
+ };
+ strcpy(link_sub.usr_handle, events[i].resource);
+ if (send(topsrv_sd, &link_sub, sizeof(link_sub), 0) !=
sizeof(link_sub))
+ {
+ syslog(LOG_ERR,
+ "Failed to subscribe to \"TIPC Link\" Or \"Node State\" event(s): %s",
+ strerror(errno));
+ exit(-1);
+ }
+ }
+ return;
+}
+
+static void setup_sample_timer(void)
+{
+ int err = 0;
+ timer_fd = timerfd_create (CLOCK_REALTIME, 0);
+ struct itimerspec new_value = {
+ /* new_value.it_value specifies the initial expiration of the timer, in
+ seconds and nanoseconds. Setting either field of new_value.it_value
+ to a nonzero value arms the timer. Setting both fields of
+ new_value.it_value to zero disarms the timer.
+ */
+
+ .it_value.tv_sec = 1,
+ .it_value.tv_nsec = 0,
+
+ /* Setting one or both fields of new_value.it_interval to nonzero
values
+ specifies the period, in seconds and nanoseconds, for repeated timer
+ expirations after the initial expiration. If both fields of
+ new_value.it_interval are zero, the timer expires just once, at the
+ time specified by new_value.it_value.
+ */
+
+ .it_interval.tv_sec = l0_w_conf.period,
+ .it_interval.tv_nsec = 0
+ };
+
+ if ((err = timerfd_settime(timer_fd, 0, &new_value, 0)) < 0)
+ {
+ syslog(LOG_ERR, "timerfd_settime: Failed with error: %d", err);
+ exit(-1);
+ }
+ return;
+}
+
+static void reset_tipc_stat(char *link)
+{
+ struct mnl_socket *nls;
+ struct nlmsghdr *nlh;
+ time_t nlSeq;
+ char mnl_s_buffer[MNL_SOCKET_BUFFER_SIZE];
+ struct nlattr *nest;
+ syslog(LOG_INFO, "start to reset the link %s \n", link);
+ nl_prep_msg(&nlh,
+ mnl_s_buffer,
+ get_tipc_family(),
+ NLM_F_REQUEST | NLM_F_ACK,
+ TIPC_NL_LINK_RESET_STATS,
+ &nlSeq);
+ nest = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
+ mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, link);
+ mnl_attr_nest_end(nlh, nest);
+ nl_send_msg(&nls,
+ nlh,
+ NETLINK_GENERIC,
+ MNL_SOCKET_AUTOPID);
+ nl_rcv_msg(nls,
+ NULL,
+ NULL,
+ &nlSeq);
+ mnl_socket_close(nls);
+ syslog(LOG_INFO, "reset link %s is done \n", link);
+}
+
+static void reset_link_statistic(void)
+{
+ if (r_linkp->action)
+ {
+ int r;
+ for (r = 0; r < r_linkp->action ; r++) {
+ reset_tipc_stat(r_linkp->action_list[r]);
+ }
+ }
+}
+
+static void request_link_statistic(char *mnl_s_buffer,
+ struct mnl_socket **nls,
+ struct nlmsghdr **nlh,
+ time_t *nlSeq)
+{
+
+ nl_prep_msg(nlh,
+ mnl_s_buffer,
+ get_tipc_family(),
+ NLM_F_REQUEST | NLM_F_DUMP,
+ TIPC_NL_LINK_GET,
+ nlSeq);
+ nl_send_msg(nls,
+ *nlh,
+ NETLINK_GENERIC,
+ MNL_SOCKET_AUTOPID);
+ }
+
+static void handle_l1_uc(void)
+{
+
+ int i;
+ l1_w_conf.counter++;
+
+ for (i = 0; i < uc_links; i++) {
+ int j = insert_link(uc_samples[i].link_info.link,
+ "L1_W", /*Not used*/
+ uc_add_link,
+ l1_w_conf.nos,
+ l1_dp);
+
+ l1_unicast_sample(j, i, uc_dp, l1_dp);
+
+ if ( (first_sample[i] == 0) && ( (stat_index + 1) % l0_w_conf.nos ) ==
0 )
+ first_sample[i] = 1;
+
+ if (l0_w_conf.window_passed)
+ IF_WATCH(w_linkp->action,
+ ((look_up_link_index(uc_dp->samples[i].link_info.link,
+ w_linkp->action_list, w_linkp->action)) >= 0),
+ link_quality(i, l1_dp, uc_dp));
+ }
+
+ if (l0_w_conf.one_shot)
+ l1_stat_index++;
+ else
+ l1_stat_index = ( l1_stat_index + 1 ) % l1_w_conf.nos;
+
+ if (l1_w_conf.counter > l1_w_conf.nos)
+ l1_w_conf.window_passed = true;
+}
+
+static int link_get_stat_cb(const struct nlmsghdr *nlh, void *data)
+{
+ const char *name;
+ const char *link_state;
+ struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
+ struct nlattr *attrs[TIPC_NLA_MAX + 1] = {};
+ struct nlattr *info[TIPC_NLA_LINK_MAX + 1] = {};
+ struct nlattr *prop[TIPC_NLA_PROP_MAX + 1] = {};
+ struct nlattr *stats[TIPC_NLA_STATS_MAX + 1] = {};
+ int link = 0;
+
+ mnl_attr_parse(nlh, sizeof(*genl), parse_attrs, info);
+ if (!info[TIPC_NLA_LINK])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(info[TIPC_NLA_LINK], parse_attrs, attrs);
+
+ if (!attrs[TIPC_NLA_LINK_NAME] ||
+ !attrs[TIPC_NLA_LINK_PROP] ||
+ !attrs[TIPC_NLA_LINK_STATS])
+ return MNL_CB_ERROR;
+
+ mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_PROP], parse_attrs, prop);
+ mnl_attr_parse_nested(attrs[TIPC_NLA_LINK_STATS], parse_attrs, stats);
+
+ name = mnl_attr_get_str(attrs[TIPC_NLA_LINK_NAME]);
+
+ if (attrs[TIPC_NLA_LINK_BROADCAST])
+ return 1;
+
+ if (attrs[TIPC_NLA_LINK_ACTIVE])
+ link_state = "ACTIVE";
+ else if (attrs[TIPC_NLA_LINK_UP])
+ link_state = "STANDBY";
+ else
+ link_state = "DEFUNCT";
+
+ link = insert_link(name, link_state, uc_add_link, l0_w_conf.nos, uc_dp);
+
+ return unicast_sample(attrs, prop,
+ stats, link,
+ uc_dp, name);
+}
+
+static void handle_statistic_fd(struct mnl_socket **nls,
+ time_t **nlSeq)
+{
+ static int counter=0;
+
+ nl_rcv_msg(*nls, link_get_stat_cb, NULL, *nlSeq);
+ counter++;
+ printf("Total # of samples: %d \n", counter);
+ if( counter > ( l0_w_conf.nos - 1 ))
+ {
+ l0_w_conf.window_passed = true;
+ handle_l1_uc();
+ }
+ else
+ {
+ /*skip so far*/
+ ;
+ }
+}
+
+static void handle_topsrv_sd(struct tipc_event *tipc_evt)
+{
+
+ if (recv(topsrv_sd, tipc_evt, sizeof(*tipc_evt), 0) == sizeof(*tipc_evt))
+ {
+ switch (ntohl((*tipc_evt).s.seq.type))
+ {
+ case TIPC_CFG_SRV:
+ if (ntohl(tipc_evt->port.node) == this_node)
+ return;
+ /* fall through*/
+ case TIPC_LINK_STATE:
+ log_event(topsrv_sd, tipc_evt);
+ break;
+ default:
+ syslog(LOG_WARNING, "Unknown event received: %d", (*tipc_evt).s.seq.type);
+ break;
+ }
+ }
+}
+
+static void handle_select(time_t *nlSeq,
+ struct nlmsghdr **nlh,
+ struct mnl_socket *nls)
+{
+ int link, err = 0;
+ fd_set fds;
+ struct tipc_event tipc_evt = {0};
+ time_t the_time = time(NULL);
+ /* If threadsafe is wished, use localtime_r*/
+ struct tm *tm = localtime(&the_time);
+ strftime(start_time, sizeof(start_time),"%Y-%B-%d:%H:%M:%S",tm);
+ stat_index = 0;
+ l1_stat_index = 0;
+
+ while (stat_index < l0_w_conf.nos ) {
+ FD_ZERO(&fds);
+ FD_SET((nls)->fd, &fds);
+ FD_SET(timer_fd, &fds);
+ FD_SET(topsrv_sd, &fds);
+ if ((err = select(FD_SETSIZE, &fds, NULL, NULL, NULL)) < 0)
+ {
+ if ( err == -1 && errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: Failed, %s", strerror(errno));
+ exit(-1);
+ }
+ /* Accept only one print at a time, dirty?*/
+ if(take_sample)
+ {
+ handle_print_to_file();
+ take_sample = false;
+ }
+ if (FD_ISSET((nls)->fd, &fds))
+ {
+ printf("\033[2J\033[;H");
+
+ handle_statistic_fd(&nls,
+ &nlSeq);
+
+ if (l0_w_conf.one_shot)
+ {
+ stat_index++;
+ }
+ else
+ stat_index = (stat_index + 1) % l0_w_conf.nos;
+ }
+
+ if (FD_ISSET(topsrv_sd, &fds))
+ {
+ handle_topsrv_sd(&tipc_evt);
+ }
+
+ if (FD_ISSET(timer_fd, &fds))
+ {
+ /*
+ If the timer has expired one or more times since its
+ settings were last modified using timerfd_settime(), or since
+ the last successful read(2), then the buffer given to read(2)
+ returns an unsigned 8-byte integer (uint64_t) containing the
+ number of expirations that have occurred.
+ Do this dummy read to get select happy!!!
+ */
+ uint64_t dummy;
+ if (read(timer_fd, &dummy, sizeof(dummy)) < 0) {
+ syslog(LOG_ERR, "read: Failed to read %d, %s",
+ timer_fd, strerror(errno));
+ exit(-1);
+ }
+
+ if (mnl_socket_sendto(nls, *nlh, (*nlh)->nlmsg_len) < 0)
+ {
+ syslog(LOG_ERR, "mnl_socket_sendto: Failed, %s", strerror(errno));
+ exit(-1);
+ }
+
+ for (link = 0; link < uc_links; link++) {
+ if ((uc_dp->samples)[link].link_state.count_down)
+ {
+ (uc_dp->samples)[link].link_state.count_down--;
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*FIXME: call the function when process terminats with non zero exit()*/
+static void free_resources(struct mnl_socket *nls)
+{
+ if (nls)
+ mnl_socket_close(nls);
+ if (topsrv_sd)
+ shutdown(topsrv_sd, SHUT_RDWR);
+ if (uc_samples)
+ free(uc_samples);
+ if (uc_tot_mem)
+ free(uc_tot_mem);
+ //if(LOCKFILE) /*FIXME the check of lockfile*/
+ unlink(LOCKFILE);
+}
+
+/* Thank you a lots W. Richie S.! */
+int main(int argc, char *argv[])
+{
+ struct mnl_socket *nls;
+ struct nlmsghdr *nlh;
+ char mnl_s_buffer[MNL_SOCKET_BUFFER_SIZE];
+ time_t nlSeq;
+ char *cmd = strrchr(argv[0], '/');
+ cmd = cmd ? 1+cmd : argv[0];
+
+ handle_opts(argc, argv);
+
+ daemonize_me(cmd);
+
+ syslog(LOG_INFO, "The TIPC Network Daemon has started");
+
+ printf("\033[2J\033[;H");
+
+ handle_signals();
+ /* Subsrcibe for all link and node events! No Zero div problem?!*/
+ topsrv_subscibe(tipc_events, sizeof(tipc_events)/sizeof(tipc_events[0]));
+
+ setup_sample_timer();
+
+ reset_link_statistic();
+
+ request_link_statistic(mnl_s_buffer, &nls, &nlh, &nlSeq);
+
+ handle_select(&nlSeq, &nlh, nls);
+
+ handle_print_to_file();
+
+ free_resources(nls);
+ syslog(LOG_INFO, "The TIPC Network Daemon has terminated");
+ exit(0);
+}