/*
 * 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 <inttypes.h>

#include <pthread.h>

#include <linux/genetlink.h>
#include <libmnl/libmnl.h>


#ifndef TIPC_SERVICE_ADDR
#define TIPC_SERVICE_ADDR TIPC_ADDR_NAME
#endif

#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(%" PRIu64 "), link_down(%" PRIu64 ")",
		(*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);
		}
		memcpy(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;
		memcpy(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;
		memcpy(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:%-10" PRIu64 " ", (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 != 0x0) {
		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);
			}
			memcpy(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);
			}
			memcpy(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);
			}
			memcpy(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);
			}
			memcpy(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_SERVICE_ADDR,
		.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);
}
