From 85e41c8412681719e5be1f3d29b069b84d07a226 Mon Sep 17 00:00:00 2001 From: German Service Network Date: Sat, 17 Jan 2026 09:53:31 +0100 Subject: [PATCH] Move printing functions and stats functions (global stats) to a separate file --- CHANGELOG.md | 3 +- src/Makefile.am | 2 +- src/fping.c | 781 +----------------------------------------------- src/fping.h | 108 +++++-- src/output.c | 485 ++++++++++++++++++++++++++++++ src/output.h | 16 + src/stats.c | 226 ++++++++++++++ src/stats.h | 10 + 8 files changed, 832 insertions(+), 799 deletions(-) create mode 100644 src/output.c create mode 100644 src/output.h create mode 100644 src/stats.c create mode 100644 src/stats.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d5743a..0bdb3df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,8 @@ Next recvmsg with MSG_DONTWAIT instead (#449) - Improved compatibility with NetBSD (#452, thanks @auerswal) - Consistent variable names for command-line options (#453, thanks @gsnw-sebast) -- Move the most function declarations from fping.c to fping.h (#454, thanks @gsnw-sebast) +- Move printing functions, stats functions (global stats) and + function declarations to a separate file (#454, thanks @gsnw-sebast) - Avoid crash with option `-0` (#457, thanks @auerswal) fping 5.5 (2025-12-31) diff --git a/src/Makefile.am b/src/Makefile.am index c58e474..7336192 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ AM_CFLAGS = -Wall -Wextra -Wno-sign-compare sbin_PROGRAMS = fping -fping_SOURCES = fping.c seqmap.c socket4.c fping.h options.h seqmap.h optparse.c optparse.h +fping_SOURCES = fping.c output.c stats.c seqmap.c socket4.c fping.h options.h output.h stats.h seqmap.h optparse.c optparse.h fping_DEPENDENCIES = ../config.h if IPV6 diff --git a/src/fping.c b/src/fping.c index 3623ce0..ab674a1 100644 --- a/src/fping.c +++ b/src/fping.c @@ -49,6 +49,8 @@ extern "C" { #include #include "seqmap.h" +#include "output.h" +#include "stats.h" #ifdef HAVE_UNISTD_H #include @@ -263,80 +265,11 @@ char *icmp6_param_prob_str[ICMP6_PARAM_PROB_MAXCODE + 1] = { }; #endif -typedef struct ip_header_result { - int tos; - int ttl; - uint32_t otime_ms; - uint32_t rtime_ms; - uint32_t ttime_ms; -} IP_HEADER_RESULT; - IP_HEADER_RESULT default_ip_header_result() { return (IP_HEADER_RESULT){-1, -1, 0x80000000U, 0x80000000U, 0x80000000U}; } -struct event; -typedef struct host_entry { - int i; /* index into array */ - char *name; /* name as given by user */ - char *host; /* text description of host */ - struct sockaddr_storage saddr; /* internet address */ - socklen_t saddr_len; - int64_t timeout; /* time to wait for response */ - int64_t last_send_time; /* time of last packet sent */ - int num_sent; /* number of ping packets sent (for statistics) */ - int num_recv; /* number of pings received (duplicates ignored) */ - int num_recv_total; /* number of pings received, including duplicates */ - int64_t max_reply; /* longest response time */ - int64_t min_reply; /* shortest response time */ - int64_t total_time; /* sum of response times */ - /* _i -> splits (reset on every report interval) */ - int num_sent_i; /* number of ping packets sent */ - int num_recv_i; /* number of pings received */ - int64_t max_reply_i; /* longest response time */ - int64_t min_reply_i; /* shortest response time */ - int64_t total_time_i; /* sum of response times */ - int64_t *resp_times; /* individual response times */ - - /* to avoid allocating two struct events each time that we send a ping, we - * preallocate here two struct events for each ping that we might send for - * this host. */ - struct event *event_storage_ping; - struct event *event_storage_timeout; -} HOST_ENTRY; - -int event_storage_count; /* how many events can be stored in host_entry->event_storage_xxx */ - -/* basic algorithm to ensure that we have correct data at all times: - * - * 1. when a ping is sent: - * - two events get added into event_queue: - * - t+PERIOD: ping event - * - t+TIMEOUT: timeout event - * - * 2. when a ping is received: - * - record statistics (increase num_sent and num_received) - * - remove timeout event (we store the event in seqmap, so that we can retrieve it when the response is received) - * - * 3. when a timeout happens: - * - record statistics (increase num_sent only) - */ - -#define EV_TYPE_PING 1 -#define EV_TYPE_TIMEOUT 2 - -struct event { - struct event *ev_prev; - struct event *ev_next; - int64_t ev_time; - struct host_entry *host; - int ping_index; -}; - -struct event_queue { - struct event *first; - struct event *last; -}; +int event_storage_count; /*** globals ***/ @@ -457,9 +390,6 @@ unsigned int fwmark = 0; char *filename = NULL; /* file containing hosts to ping */ -/*** forward declarations ***/ -static uint32_t ms_since_midnight_utc(int64_t time_val); - /************************************************************ Function: p_setsockopt @@ -2023,605 +1953,6 @@ void finish() exit(0); } -/************************************************************ - - Function: print_recv - -************************************************************* - - Inputs: HOST_ENTRY *h, int64_t recv_time, int result, - int this_count, int64_t this_reply, int avg - - Description: - -************************************************************/ - -void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) { - if (opt_print_json_on) { - printf("{\"resp\": {"); - - if (opt_timestamp_on) - print_timestamp_format(recv_time, opt_timestamp_format); - - printf("\"host\": \"%s\", ", h->host); - printf("\"seq\": %d, ", this_count); - printf("\"size\": %d, ", result); - printf("\"rtt\": %s", sprint_tm(this_reply)); - return; - } - - /* Normal Output */ - if (opt_timestamp_on) - print_timestamp_format(recv_time, opt_timestamp_format); - - printf("%-*s : [%d], %d bytes, %s ms", - max_hostname_len, h->host, this_count, result, sprint_tm(this_reply)); - - printf(" (%s avg, ", sprint_tm(avg)); - - if (h->num_recv <= h->num_sent) { - printf("%d%% loss)", - ((h->num_sent - h->num_recv) * 100) / h->num_sent); - } - else { - printf("%d%% return)", - (h->num_recv_total * 100) / h->num_sent); - } -} - -/************************************************************ - - Function: print_timeout - -************************************************************* - - Inputs: HOST_ENTRY *h, int ping_index - - Description: - -************************************************************/ - -void print_timeout(HOST_ENTRY *h, int ping_index) { - if (opt_print_json_on) { - printf("{\"timeout\": {"); - if (opt_timestamp_on) - print_timestamp_format(current_time_ns, opt_timestamp_format); - - printf("\"host\": \"%s\", ", h->host); - printf("\"seq\": %d", ping_index); - printf("}}\n"); - return; - } - - /* Normal Output */ - if (opt_timestamp_on) - print_timestamp_format(current_time_ns, opt_timestamp_format); - - printf("%-*s : [%d], timed out", - max_hostname_len, h->host, ping_index); - - if (h->num_recv > 0) { - printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv)); - } - else { - printf(" (NaN avg, "); - } - - if (h->num_recv <= h->num_sent) { - printf("%d%% loss)", - ((h->num_sent - h->num_recv) * 100) / h->num_sent); - } - else { - printf("%d%% return)", - (h->num_recv_total * 100) / h->num_sent); - } - printf("\n"); -} - -/************************************************************ - - Function: print_recv_ext - -************************************************************* - - Inputs: IP_HEADER_RESULT *ip_header_res, - int64_t recv_time, int64_t this_reply - - Description: - -************************************************************/ - -void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { - if (opt_icmp_request_typ == 13) { - printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u", - opt_alive_on ? "" : ",", - ip_header_res->otime_ms, ip_header_res->rtime_ms, ip_header_res->ttime_ms, - ms_since_midnight_utc(recv_time)); - } - - if(opt_print_tos_on) { - if(ip_header_res->tos != -1) { - printf(" (TOS %d)", ip_header_res->tos); - } - else { - printf(" (TOS unknown)"); - } - } - - if (opt_print_ttl_on) { - if(ip_header_res->ttl != -1) { - printf(" (TTL %d)", ip_header_res->ttl); - } - else { - printf(" (TTL unknown)"); - } - } - - if (opt_elapsed_on && !opt_per_recv_on) - printf(" (%s ms)", sprint_tm(this_reply)); - - printf("\n"); -} - -/************************************************************ - - Function: print_recv_ext_json - -************************************************************* - - Inputs: IP_HEADER_RESULT *ip_header_res, - int64_t recv_time, int64_t this_reply - - Description: - -************************************************************/ - -void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { - if (opt_icmp_request_typ == 13) { - printf(", \"timestamps\": {"); - printf("\"originate\": %u, ", ip_header_res->otime_ms); - printf("\"receive\": %u, ", ip_header_res->rtime_ms); - printf("\"transmit\": %u, ", ip_header_res->ttime_ms); - printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time)); - } - - if(opt_print_tos_on) { - if(ip_header_res->tos != -1) { - printf(", \"tos\": %d", ip_header_res->tos); - } - else { - printf(", \"tos\": -1"); - } - } - - if (opt_print_ttl_on) { - if(ip_header_res->ttl != -1) { - printf(", \"ttl\": %d", ip_header_res->ttl); - } - else { - printf(", \"ttl\": -1"); - } - } - - if (opt_elapsed_on && !opt_per_recv_on) - printf(" (%s ms)", sprint_tm(this_reply)); - - printf("}}"); - printf("\n"); -} - -/************************************************************ - - Function: print_per_system_stats - -************************************************************* - - Inputs: void (none) - - Description: - - -************************************************************/ - -void print_per_system_stats(void) -{ - int i, j, avg, outage_ms; - HOST_ENTRY *h; - int64_t resp; - - if (opt_verbose_on || opt_per_recv_on) - fprintf(stderr, "\n"); - - for (i = 0; i < num_hosts; i++) { - h = table[i]; - fprintf(stderr, "%-*s :", max_hostname_len, h->host); - - if (opt_report_all_rtts_on) { - for (j = 0; j < h->num_sent; j++) { - if ((resp = h->resp_times[j]) >= 0) - fprintf(stderr, " %s", sprint_tm(resp)); - else - fprintf(stderr, " -"); - } - - fprintf(stderr, "\n"); - } - else { - if (h->num_recv <= h->num_sent) { - fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%", - h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0); - - if (opt_outage_on) { - /* Time outage total */ - outage_ms = (h->num_sent - h->num_recv) * opt_perhost_interval / 1e6; - fprintf(stderr, ", outage(ms) = %d", outage_ms); - } - } - else { - fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%", - h->num_sent, h->num_recv, - h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0); - } - - if (h->num_recv) { - avg = h->total_time / h->num_recv; - fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply)); - fprintf(stderr, "/%s", sprint_tm(avg)); - fprintf(stderr, "/%s", sprint_tm(h->max_reply)); - } - - fprintf(stderr, "\n"); - } - } -} - -/************************************************************ - - Function: print_per_system_stats_json - -************************************************************* - - Inputs: void (none) - - Description: - - -************************************************************/ - -void print_per_system_stats_json(void) -{ - int i, j, avg, outage_ms; - HOST_ENTRY *h; - int64_t resp; - - for (i = 0; i < num_hosts; i++) { - h = table[i]; - - if (opt_report_all_rtts_on) - fprintf(stdout, "{\"vSum\": {"); - else - fprintf(stdout, "{\"summary\": {"); - - fprintf(stdout, "\"host\": \"%s\", ", h->host); - - if (opt_report_all_rtts_on) { - fprintf(stdout, "\"values\": ["); - for (j = 0; j < h->num_sent; j++) { - if (j > 0) - fprintf(stdout, ", "); - - if ((resp = h->resp_times[j]) >= 0) - fprintf(stdout, "%s", sprint_tm(resp)); - else - fprintf(stdout, "null"); - } - - fprintf(stdout, "]}"); - } - else { - if (h->num_recv <= h->num_sent) { - fprintf(stdout, "\"xmt\": %d, ", h->num_sent); - fprintf(stdout, "\"rcv\": %d, ", h->num_recv); - fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0); - - if (opt_outage_on) { - /* Time outage total */ - outage_ms = (h->num_sent - h->num_recv) * opt_perhost_interval / 1e6; - fprintf(stdout, ", \"outage(ms)\": %d", outage_ms); - } - } - else { - fprintf(stdout, "\"xmt\": %d, ", h->num_sent); - fprintf(stdout, "\"rcv\": %d, ", h->num_recv); - fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0); - } - - if (h->num_recv) { - avg = h->total_time / h->num_recv; - fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply)); - fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg)); - fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply)); - } - - fprintf(stdout, "}"); - } - fprintf(stdout, "}\n"); - } -} - -/************************************************************ - - Function: print_netdata - -************************************************************* - - Inputs: void (none) - - Description: - - -************************************************************/ - -void print_netdata(void) -{ - static int sent_charts = 0; - - int i; - int64_t avg; - HOST_ENTRY *h; - - for (i = 0; i < num_hosts; i++) { - h = table[i]; - - if (!sent_charts) { - printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9); - printf("DIMENSION xmt sent absolute 1 1\n"); - printf("DIMENSION rcv received absolute 1 1\n"); - } - - printf("BEGIN fping.%s_packets\n", h->name); - printf("SET xmt = %d\n", h->num_sent_i); - printf("SET rcv = %d\n", h->num_recv_i); - printf("END\n"); - - if (!sent_charts) { - printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9); - printf("DIMENSION returned '' absolute 1 1\n"); - /* printf("DIMENSION lost '' absolute 1 1\n"); */ - } - - printf("BEGIN fping.%s_quality\n", h->name); - /* - if( h->num_recv_i <= h->num_sent_i ) - printf("SET lost = %d\n", h->num_sent_i > 0 ? ( ( h->num_sent_i - h->num_recv_i ) * 100 ) / h->num_sent_i : 0 ); - else - printf("SET lost = 0\n"); -*/ - - printf("SET returned = %d\n", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0); - printf("END\n"); - - if (!sent_charts) { - printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9); - printf("DIMENSION min minimum absolute 1 1000000\n"); - printf("DIMENSION max maximum absolute 1 1000000\n"); - printf("DIMENSION avg average absolute 1 1000000\n"); - } - - printf("BEGIN fping.%s_latency\n", h->name); - if (h->num_recv_i) { - avg = h->total_time_i / h->num_recv_i; - printf("SET min = %" PRId64 "\n", h->min_reply_i); - printf("SET avg = %" PRId64 "\n", avg); - printf("SET max = %" PRId64 "\n", h->max_reply_i); - } - printf("END\n"); - - stats_reset_interval(h); - } - - sent_charts = 1; -} - -/************************************************************ - - Function: print_per_system_splits - -************************************************************* - - Inputs: void (none) - - Description: - - -************************************************************/ - -void print_per_system_splits(void) -{ - int i, avg, outage_ms_i; - HOST_ENTRY *h; - struct tm *curr_tm; - - if (opt_verbose_on || opt_per_recv_on) - fprintf(stderr, "\n"); - - update_current_time(); - curr_tm = localtime((time_t *)¤t_time.tv_sec); - fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour, - curr_tm->tm_min, curr_tm->tm_sec); - - for (i = 0; i < num_hosts; i++) { - h = table[i]; - fprintf(stderr, "%-*s :", max_hostname_len, h->host); - - if (h->num_recv_i <= h->num_sent_i) { - fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%", - h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0); - - if (opt_outage_on) { - /* Time outage */ - outage_ms_i = (h->num_sent_i - h->num_recv_i) * opt_perhost_interval / 1e6; - fprintf(stderr, ", outage(ms) = %d", outage_ms_i); - } - } - else { - fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%", - h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0); - } - - if (h->num_recv_i) { - avg = h->total_time_i / h->num_recv_i; - fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i)); - fprintf(stderr, "/%s", sprint_tm(avg)); - fprintf(stderr, "/%s", sprint_tm(h->max_reply_i)); - } - - fprintf(stderr, "\n"); - if (!opt_cumulative_stats_on) { - stats_reset_interval(h); - } - } -} - -/************************************************************ - - Function: print_per_system_splits_json - -************************************************************* - - Inputs: void (none) - - Description: - - -************************************************************/ - -void print_per_system_splits_json(void) -{ - int i, avg, outage_ms_i; - HOST_ENTRY *h; - - update_current_time(); - - for (i = 0; i < num_hosts; i++) { - h = table[i]; - fprintf(stdout, "{\"intSum\": {"); - fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec); - fprintf(stdout, "\"host\": \"%s\", ", h->host); - - if (h->num_recv_i <= h->num_sent_i) { - fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i); - fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i); - fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0); - - if (opt_outage_on) { - /* Time outage */ - outage_ms_i = (h->num_sent_i - h->num_recv_i) * opt_perhost_interval / 1e6; - fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i); - } - } - else { - fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i); - fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i); - fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0); - } - - if (h->num_recv_i) { - avg = h->total_time_i / h->num_recv_i; - fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i)); - fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg)); - fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i)); - } - - fprintf(stdout, "}}\n"); - if (!opt_cumulative_stats_on) { - stats_reset_interval(h); - } - } -} - -/************************************************************ - - Function: print_global_stats - -************************************************************* - - Inputs: void (none) - - Description: - - -************************************************************/ - -void print_global_stats(void) -{ - fprintf(stderr, "\n"); - fprintf(stderr, " %7d targets\n", num_hosts); - fprintf(stderr, " %7d alive\n", num_alive); - fprintf(stderr, " %7d unreachable\n", num_unreachable); - fprintf(stderr, " %7d unknown addresses\n", num_noaddress); - fprintf(stderr, "\n"); - fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout); - fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent); - fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived); - fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd); - fprintf(stderr, "\n"); - - if (total_replies == 0) { - min_reply = 0; - max_reply = 0; - total_replies = 1; - sum_replies = 0; - } - - fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply)); - fprintf(stderr, " %s ms (avg round trip time)\n", - sprint_tm(sum_replies / total_replies)); - fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply)); - fprintf(stderr, " %12.3f sec (elapsed real time)\n", - (end_time - start_time) / 1e9); - fprintf(stderr, "\n"); -} - -/************************************************************ - - Function: print_global_stats_json - -************************************************************* - - Inputs: void (none) - - Description: - - -************************************************************/ - -void print_global_stats_json(void) -{ - fprintf(stdout, "{\"stats\": {"); - fprintf(stdout, "\"targets\": %d, ", num_hosts); - fprintf(stdout, "\"alive\": %d, ", num_alive); - fprintf(stdout, "\"unreachable\": %d, ", num_unreachable); - fprintf(stdout, "\"unknownAddresses\": %d, ", num_noaddress); - fprintf(stdout, "\"timeouts\": %d, ", num_timeout); - fprintf(stdout, "\"icmpEchosSent\": %d, ", num_pingsent); - fprintf(stdout, "\"icmpEchoRepliesReceived\": %d, ", num_pingreceived); - fprintf(stdout, "\"otherIcmpReceived\": %d, ", num_othericmprcvd); - - if (total_replies == 0) { - min_reply = 0; - max_reply = 0; - total_replies = 1; - sum_replies = 0; - } - - fprintf(stdout, "\"rttMin\": %s, ", sprint_tm(min_reply)); - fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(sum_replies / total_replies)); - fprintf(stdout, "\"rttMax\": %s, ", sprint_tm(max_reply)); - fprintf(stdout, "\"elapsed\": %.3f", (end_time - start_time) / 1e9); - fprintf(stdout, "}}\n"); -} - /************************************************************ Function: send_ping @@ -3601,48 +2932,7 @@ void print_warning(char *format, ...) } } -/************************************************************ - Function: sprint_tm - -************************************************************* - - render nanosecond int64_t value into milliseconds string with three digits of - precision. - -************************************************************/ - -const char *sprint_tm(int64_t ns) -{ - static char buf[10]; - double t = (double)ns / 1e6; - - if (t < 0.0) { - /* negative (unexpected) */ - snprintf(buf, sizeof(buf), "%.2g", t); - } - else if (t < 1.0) { - /* <= 0.99 ms */ - snprintf(buf, sizeof(buf), "%.3f", t); - } - else if (t < 10.0) { - /* 1.00 - 9.99 ms */ - snprintf(buf, sizeof(buf), "%.2f", t); - } - else if (t < 100.0) { - /* 10.0 - 99.9 ms */ - snprintf(buf, sizeof(buf), "%.1f", t); - } - else if (t < 1000000.0) { - /* 100 - 1'000'000 ms */ - snprintf(buf, sizeof(buf), "%.0f", t); - } - else { - snprintf(buf, sizeof(buf), "%.3e", t); - } - - return (buf); -} /************************************************************ @@ -3795,72 +3085,7 @@ void ev_remove(struct event_queue *queue, struct event *event) event->ev_next = NULL; } -/************************************************************ - - Function: print_human_readable_time from current_time_ns - -*************************************************************/ -void print_timestamp_format(int64_t current_time_ns, int timestamp_format) -{ - char time_buffer[100]; - time_t current_time_s; - struct tm *local_time; - - current_time_s = current_time_ns / 1000000000; - local_time = localtime(¤t_time_s); - switch(timestamp_format) { - case 1: - // timestamp-format ctime - strftime(time_buffer, sizeof(time_buffer), "%c", local_time); - if (opt_print_json_on) - printf("\"timestamp\": \"%s\", ", time_buffer); - else - printf("[%s] ", time_buffer); - break; - case 2: - // timestamp-format iso - strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time); - if (opt_print_json_on) - printf("\"timestamp\": \"%s\", ", time_buffer); - else - printf("[%s] ", time_buffer); - break; - case 3: - // timestamp-format rfc3339 - strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time); - if (opt_print_json_on) - printf("\"timestamp\": \"%s\", ", time_buffer); - else - printf("[%s] ", time_buffer); - break; - default: - if (opt_print_json_on) - printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9); - else - printf("[%.5f] ", (double)current_time_ns / 1e9); - } -} - -/************************************************************ - - Function: ms_since_midnight_utc - -************************************************************* - - Input: int64_t: current UTC time in ns - - Output: uint32_t: current time in ms since midnight UTC - Description: - - Return ICMP Timestamp value corresponding to the given time value. - The given time value must be in UTC. - -*************************************************************/ -static uint32_t ms_since_midnight_utc(int64_t time_val) -{ - return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000)); -} /************************************************************ diff --git a/src/fping.h b/src/fping.h index 419b56e..fad6e7e 100644 --- a/src/fping.h +++ b/src/fping.h @@ -7,10 +7,98 @@ #include #include #include +#include +#include +#include + +/* Structure definitions */ +typedef struct ip_header_result { + int tos; + int ttl; + uint32_t otime_ms; + uint32_t rtime_ms; + uint32_t ttime_ms; +} IP_HEADER_RESULT; + +typedef struct host_entry { + int i; /* index into array */ + char *name; /* name as given by user */ + char *host; /* text description of host */ + struct sockaddr_storage saddr; /* internet address */ + socklen_t saddr_len; + int64_t timeout; /* time to wait for response */ + int64_t last_send_time; /* time of last packet sent */ + int num_sent; /* number of ping packets sent (for statistics) */ + int num_recv; /* number of pings received (duplicates ignored) */ + int num_recv_total; /* number of pings received, including duplicates */ + int64_t max_reply; /* longest response time */ + int64_t min_reply; /* shortest response time */ + int64_t total_time; /* sum of response times */ + /* _i -> splits (reset on every report interval) */ + int num_sent_i; /* number of ping packets sent */ + int num_recv_i; /* number of pings received */ + int64_t max_reply_i; /* longest response time */ + int64_t min_reply_i; /* shortest response time */ + int64_t total_time_i; /* sum of response times */ + int64_t *resp_times; /* individual response times */ + + /* to avoid allocating two struct events each time that we send a ping, we + * preallocate here two struct events for each ping that we might send for + * this host. */ + struct event *event_storage_ping; + struct event *event_storage_timeout; +} HOST_ENTRY; + +struct event { + HOST_ENTRY *host; /* pointer to associated host */ + int ping_index; /* index/sequence for this ping within the host */ + int64_t ev_time; /* time of the event */ + struct event *ev_next; /* next event in linked list */ + struct event *ev_prev; /* previous event in linked list */ +}; + +struct event_queue { + struct event *first; + struct event *last; +}; + +/* Global variables */ +extern HOST_ENTRY **table; +extern int num_hosts; +extern int max_hostname_len; +extern int64_t current_time_ns; +extern struct timespec current_time; +extern int64_t start_time; +extern int64_t end_time; +extern int64_t opt_perhost_interval; +extern int64_t report_interval; + +// Stats globals +extern int num_alive, num_unreachable, num_noaddress, num_timeout; +extern int num_pingsent, num_pingreceived, num_othericmprcvd; +extern int64_t max_reply, min_reply, total_replies, sum_replies; + +// Options +extern int opt_print_json_on; +extern int opt_timestamp_on; +extern int opt_timestamp_format; +extern int opt_alive_on; +extern int opt_quiet_on; +extern int opt_per_recv_on; +extern int opt_verbose_on; +extern int opt_print_tos_on; +extern int opt_print_ttl_on; +extern int opt_elapsed_on; +extern int opt_icmp_request_typ; +extern int opt_report_all_rtts_on; +extern int opt_outage_on; +extern int opt_cumulative_stats_on; +extern int opt_print_netdata_on; +extern int opt_random_data_on; + /* this requires variadic macros, part of C99 */ #if (defined(DEBUG) || defined(_DEBUG)) -extern int64_t current_time_ns; extern int opt_debug_trace_on; #define dbg_printf(fmt, ...) do { if (opt_debug_trace_on) { fprintf(stderr, "[%10.5f] ", (double)(current_time_ns / 1000)/1000000); fprintf(stderr, fmt, __VA_ARGS__); } } while (0) @@ -18,14 +106,7 @@ extern int opt_debug_trace_on; #define dbg_printf(fmt, ...) #endif - /* fping.c */ -typedef struct host_entry HOST_ENTRY; -struct event; -struct event_queue; -typedef struct ip_header_result IP_HEADER_RESULT; -extern int opt_random_data_on; - void add_name(char *name); void add_addr(char *name, char *host, struct sockaddr *ipaddr, socklen_t ipaddr_len); char *na_cat(char *name, struct in_addr ipaddr); @@ -33,18 +114,7 @@ char *get_host_by_address(struct in_addr in); int send_ping(HOST_ENTRY *h, int index); void usage(int); int wait_for_reply(int64_t); -void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg); -void print_timeout(HOST_ENTRY *h, int ping_index); -void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply); -void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply); -void print_per_system_stats(void); -void print_per_system_stats_json(void); -void print_per_system_splits(void); -void print_per_system_splits_json(void); void stats_reset_interval(HOST_ENTRY *h); -void print_netdata(void); -void print_global_stats(void); -void print_global_stats_json(void); void main_loop(); void signal_handler(int); void finish(); diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..c8eec90 --- /dev/null +++ b/src/output.c @@ -0,0 +1,485 @@ +#include "output.h" +#include "fping.h" +#include +#include +#include + +/************************************************************ + + Function: ms_since_midnight_utc + +************************************************************* + + Input: int64_t: current UTC time in ns + + Output: uint32_t: current time in ms since midnight UTC + + Description: + + Return ICMP Timestamp value corresponding to the given time value. + The given time value must be in UTC. + +*************************************************************/ +static uint32_t ms_since_midnight_utc(int64_t time_val) +{ + return (uint32_t)((time_val / 1000000) % (24 * 60 * 60 * 1000)); +} + +/************************************************************ + + Function: sprint_tm + +************************************************************* + + render nanosecond int64_t value into milliseconds string with three digits of + precision. + +************************************************************/ + +const char *sprint_tm(int64_t ns) +{ + static char buf[10]; + double t = (double)ns / 1e6; + + if (t < 0.0) { + /* negative (unexpected) */ + snprintf(buf, sizeof(buf), "%.2g", t); + } + else if (t < 1.0) { + /* <= 0.99 ms */ + snprintf(buf, sizeof(buf), "%.3f", t); + } + else if (t < 10.0) { + /* 1.00 - 9.99 ms */ + snprintf(buf, sizeof(buf), "%.2f", t); + } + else if (t < 100.0) { + /* 10.0 - 99.9 ms */ + snprintf(buf, sizeof(buf), "%.1f", t); + } + else if (t < 1000000.0) { + /* 100 - 1'000'000 ms */ + snprintf(buf, sizeof(buf), "%.0f", t); + } + else { + snprintf(buf, sizeof(buf), "%.3e", t); + } + + return (buf); +} + +/************************************************************ + + Function: print_human_readable_time from current_time_ns + +*************************************************************/ +void print_timestamp_format(int64_t current_time_ns, int timestamp_format) +{ + char time_buffer[100]; + time_t current_time_s; + struct tm *local_time; + + current_time_s = current_time_ns / 1000000000; + local_time = localtime(¤t_time_s); + switch(timestamp_format) { + case 1: + // timestamp-format ctime + strftime(time_buffer, sizeof(time_buffer), "%c", local_time); + if (opt_print_json_on) + printf("\"timestamp\": \"%s\", ", time_buffer); + else + printf("[%s] ", time_buffer); + break; + case 2: + // timestamp-format iso + strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%dT%T%z", local_time); + if (opt_print_json_on) + printf("\"timestamp\": \"%s\", ", time_buffer); + else + printf("[%s] ", time_buffer); + break; + case 3: + // timestamp-format rfc3339 + strftime(time_buffer, sizeof(time_buffer), "%Y-%m-%d %H:%M:%S", local_time); + if (opt_print_json_on) + printf("\"timestamp\": \"%s\", ", time_buffer); + else + printf("[%s] ", time_buffer); + break; + default: + if (opt_print_json_on) + printf("\"timestamp\": \"%.5f\", ", (double)current_time_ns / 1e9); + else + printf("[%.5f] ", (double)current_time_ns / 1e9); + } +} + +/************************************************************ + + Function: print_recv + +************************************************************* + + Inputs: HOST_ENTRY *h, int64_t recv_time, int result, + int this_count, int64_t this_reply, int avg + + Description: + +************************************************************/ + +void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg) { + if (opt_print_json_on) { + printf("{\"resp\": {"); + + if (opt_timestamp_on) + print_timestamp_format(recv_time, opt_timestamp_format); + + printf("\"host\": \"%s\", ", h->host); + printf("\"seq\": %d, ", this_count); + printf("\"size\": %d, ", result); + printf("\"rtt\": %s", sprint_tm(this_reply)); + return; + } + + /* Normal Output */ + if (opt_timestamp_on) + print_timestamp_format(recv_time, opt_timestamp_format); + + printf("%-*s : [%d], %d bytes, %s ms", + max_hostname_len, h->host, this_count, result, sprint_tm(this_reply)); + + printf(" (%s avg, ", sprint_tm(avg)); + + if (h->num_recv <= h->num_sent) { + printf("%d%% loss)", + ((h->num_sent - h->num_recv) * 100) / h->num_sent); + } + else { + printf("%d%% return)", + (h->num_recv_total * 100) / h->num_sent); + } +} + +/************************************************************ + + Function: print_timeout + +************************************************************* + + Inputs: HOST_ENTRY *h, int ping_index + + Description: + +************************************************************/ + +void print_timeout(HOST_ENTRY *h, int ping_index) { + if (opt_print_json_on) { + printf("{\"timeout\": {"); + if (opt_timestamp_on) + print_timestamp_format(current_time_ns, opt_timestamp_format); + + printf("\"host\": \"%s\", ", h->host); + printf("\"seq\": %d", ping_index); + printf("}}\n"); + return; + } + + /* Normal Output */ + if (opt_timestamp_on) + print_timestamp_format(current_time_ns, opt_timestamp_format); + + printf("%-*s : [%d], timed out", + max_hostname_len, h->host, ping_index); + + if (h->num_recv > 0) { + printf(" (%s avg, ", sprint_tm(h->total_time / h->num_recv)); + } + else { + printf(" (NaN avg, "); + } + + if (h->num_recv <= h->num_sent) { + printf("%d%% loss)", + ((h->num_sent - h->num_recv) * 100) / h->num_sent); + } + else { + printf("%d%% return)", + (h->num_recv_total * 100) / h->num_sent); + } + printf("\n"); +} + +/************************************************************ + + Function: print_recv_ext + +************************************************************* + + Inputs: IP_HEADER_RESULT *ip_header_res, + int64_t recv_time, int64_t this_reply + + Description: + +************************************************************/ + +void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { + if (opt_icmp_request_typ == 13) { + printf("%s timestamps: Originate=%u Receive=%u Transmit=%u Localreceive=%u", + opt_alive_on ? "" : ",", + ip_header_res->otime_ms, ip_header_res->rtime_ms, ip_header_res->ttime_ms, + ms_since_midnight_utc(recv_time)); + } + + if(opt_print_tos_on) { + if(ip_header_res->tos != -1) { + printf(" (TOS %d)", ip_header_res->tos); + } + else { + printf(" (TOS unknown)"); + } + } + + if (opt_print_ttl_on) { + if(ip_header_res->ttl != -1) { + printf(" (TTL %d)", ip_header_res->ttl); + } + else { + printf(" (TTL unknown)"); + } + } + + if (opt_elapsed_on && !opt_per_recv_on) + printf(" (%s ms)", sprint_tm(this_reply)); + + printf("\n"); +} + +/************************************************************ + + Function: print_recv_ext_json + +************************************************************* + + Inputs: IP_HEADER_RESULT *ip_header_res, + int64_t recv_time, int64_t this_reply + + Description: + +************************************************************/ + +void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply) { + if (opt_icmp_request_typ == 13) { + printf(", \"timestamps\": {"); + printf("\"originate\": %u, ", ip_header_res->otime_ms); + printf("\"receive\": %u, ", ip_header_res->rtime_ms); + printf("\"transmit\": %u, ", ip_header_res->ttime_ms); + printf("\"localreceive\": %u}", ms_since_midnight_utc(recv_time)); + } + + if(opt_print_tos_on) { + if(ip_header_res->tos != -1) { + printf(", \"tos\": %d", ip_header_res->tos); + } + else { + printf(", \"tos\": -1"); + } + } + + if (opt_print_ttl_on) { + if(ip_header_res->ttl != -1) { + printf(", \"ttl\": %d", ip_header_res->ttl); + } + else { + printf(", \"ttl\": -1"); + } + } + + if (opt_elapsed_on && !opt_per_recv_on) + printf(" (%s ms)", sprint_tm(this_reply)); + + printf("}}"); + printf("\n"); +} + +/************************************************************ + + Function: print_netdata + +************************************************************* + + Inputs: void (none) + + Description: + + +************************************************************/ + +void print_netdata(void) +{ + static int sent_charts = 0; + + int i; + int64_t avg; + HOST_ENTRY *h; + + for (i = 0; i < num_hosts; i++) { + h = table[i]; + + if (!sent_charts) { + printf("CHART fping.%s_packets '' 'FPing Packets' packets '%s' fping.packets line 110020 %.0f\n", h->name, h->host, report_interval / 1e9); + printf("DIMENSION xmt sent absolute 1 1\n"); + printf("DIMENSION rcv received absolute 1 1\n"); + } + + printf("BEGIN fping.%s_packets\n", h->name); + printf("SET xmt = %d\n", h->num_sent_i); + printf("SET rcv = %d\n", h->num_recv_i); + printf("END\n"); + + if (!sent_charts) { + printf("CHART fping.%s_quality '' 'FPing Quality' percentage '%s' fping.quality area 110010 %.0f\n", h->name, h->host, report_interval / 1e9); + printf("DIMENSION returned '' absolute 1 1\n"); + } + + printf("BEGIN fping.%s_quality\n", h->name); + printf("SET returned = %d\n", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0); + printf("END\n"); + + if (!sent_charts) { + printf("CHART fping.%s_latency '' 'FPing Latency' ms '%s' fping.latency area 110000 %.0f\n", h->name, h->host, report_interval / 1e9); + printf("DIMENSION min minimum absolute 1 1000000\n"); + printf("DIMENSION max maximum absolute 1 1000000\n"); + printf("DIMENSION avg average absolute 1 1000000\n"); + } + + printf("BEGIN fping.%s_latency\n", h->name); + if (h->num_recv_i) { + avg = h->total_time_i / h->num_recv_i; + printf("SET min = %" PRId64 "\n", h->min_reply_i); + printf("SET avg = %" PRId64 "\n", avg); + printf("SET max = %" PRId64 "\n", h->max_reply_i); + } + printf("END\n"); + + stats_reset_interval(h); + } + + sent_charts = 1; +} + +/************************************************************ + + Function: print_per_system_splits + +************************************************************* + + Inputs: void (none) + + Description: + + +************************************************************/ + +void print_per_system_splits(void) +{ + int i, avg, outage_ms_i; + HOST_ENTRY *h; + struct tm *curr_tm; + + if (opt_verbose_on || opt_per_recv_on) + fprintf(stderr, "\n"); + + update_current_time(); + curr_tm = localtime((time_t *)¤t_time.tv_sec); + fprintf(stderr, "[%2.2d:%2.2d:%2.2d]\n", curr_tm->tm_hour, + curr_tm->tm_min, curr_tm->tm_sec); + + for (i = 0; i < num_hosts; i++) { + h = table[i]; + fprintf(stderr, "%-*s :", max_hostname_len, h->host); + + if (h->num_recv_i <= h->num_sent_i) { + fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%", + h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0); + + if (opt_outage_on) { + /* Time outage */ + outage_ms_i = (h->num_sent_i - h->num_recv_i) * opt_perhost_interval / 1e6; + fprintf(stderr, ", outage(ms) = %d", outage_ms_i); + } + } + else { + fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%", + h->num_sent_i, h->num_recv_i, h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0); + } + + if (h->num_recv_i) { + avg = h->total_time_i / h->num_recv_i; + fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply_i)); + fprintf(stderr, "/%s", sprint_tm(avg)); + fprintf(stderr, "/%s", sprint_tm(h->max_reply_i)); + } + + fprintf(stderr, "\n"); + if (!opt_cumulative_stats_on) { + stats_reset_interval(h); + } + } +} + +/************************************************************ + + Function: print_per_system_splits_json + +************************************************************* + + Inputs: void (none) + + Description: + + +************************************************************/ + +void print_per_system_splits_json(void) +{ + int i, avg, outage_ms_i; + HOST_ENTRY *h; + + update_current_time(); + + for (i = 0; i < num_hosts; i++) { + h = table[i]; + fprintf(stdout, "{\"intSum\": {"); + fprintf(stdout, "\"time\": %" PRId64 ",", current_time.tv_sec); + fprintf(stdout, "\"host\": \"%s\", ", h->host); + + if (h->num_recv_i <= h->num_sent_i) { + fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i); + fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i); + fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_sent_i - h->num_recv_i) * 100) / h->num_sent_i : 0); + + if (opt_outage_on) { + /* Time outage */ + outage_ms_i = (h->num_sent_i - h->num_recv_i) * opt_perhost_interval / 1e6; + fprintf(stdout, ", \"outage(ms)\": %d", outage_ms_i); + } + } + else { + fprintf(stdout, "\"xmt\": %d, ", h->num_sent_i); + fprintf(stdout, "\"rcv\": %d, ", h->num_recv_i); + fprintf(stdout, "\"loss\": %d", h->num_sent_i > 0 ? ((h->num_recv_i * 100) / h->num_sent_i) : 0); + } + + if (h->num_recv_i) { + avg = h->total_time_i / h->num_recv_i; + fprintf(stdout, ", \"rttMin\": %s, ", sprint_tm(h->min_reply_i)); + fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(avg)); + fprintf(stdout, "\"rttMax\": %s", sprint_tm(h->max_reply_i)); + } + + fprintf(stdout, "}}\n"); + if (!opt_cumulative_stats_on) { + stats_reset_interval(h); + } + } +} diff --git a/src/output.h b/src/output.h new file mode 100644 index 0000000..c5d4043 --- /dev/null +++ b/src/output.h @@ -0,0 +1,16 @@ +#ifndef _OUTPUT_H +#define _OUTPUT_H + +#include "fping.h" + +void print_recv(HOST_ENTRY *h, int64_t recv_time, int result, int this_count, int64_t this_reply, int avg); +void print_timeout(HOST_ENTRY *h, int ping_index); +void print_recv_ext(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply); +void print_recv_ext_json(IP_HEADER_RESULT *ip_header_res, int64_t recv_time, int64_t this_reply); +void print_netdata(void); +void print_per_system_splits(void); +void print_per_system_splits_json(void); + +const char *sprint_tm(int64_t ns); +void print_timestamp_format(int64_t current_time_ns, int timestamp_format); +#endif diff --git a/src/stats.c b/src/stats.c new file mode 100644 index 0000000..fd9bae0 --- /dev/null +++ b/src/stats.c @@ -0,0 +1,226 @@ +#include "stats.h" +#include "output.h" +#include "fping.h" +#include +#include + +/************************************************************ + + Function: print_per_system_stats + +************************************************************* + + Inputs: void (none) + + Description: + + +************************************************************/ + +void print_per_system_stats(void) +{ + int i, j, avg, outage_ms; + HOST_ENTRY *h; + int64_t resp; + + if (opt_verbose_on || opt_per_recv_on) + fprintf(stderr, "\n"); + + for (i = 0; i < num_hosts; i++) { + h = table[i]; + fprintf(stderr, "%-*s :", max_hostname_len, h->host); + + if (opt_report_all_rtts_on) { + for (j = 0; j < h->num_sent; j++) { + if ((resp = h->resp_times[j]) >= 0) + fprintf(stderr, " %s", sprint_tm(resp)); + else + fprintf(stderr, " -"); + } + + fprintf(stderr, "\n"); + } + else { + if (h->num_recv <= h->num_sent) { + fprintf(stderr, " xmt/rcv/%%loss = %d/%d/%d%%", + h->num_sent, h->num_recv, h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0); + + if (opt_outage_on) { + /* Time outage total */ + outage_ms = (h->num_sent - h->num_recv) * opt_perhost_interval / 1e6; + fprintf(stderr, ", outage(ms) = %d", outage_ms); + } + } + else { + fprintf(stderr, " xmt/rcv/%%return = %d/%d/%d%%", + h->num_sent, h->num_recv, + h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0); + } + + if (h->num_recv) { + avg = h->total_time / h->num_recv; + fprintf(stderr, ", min/avg/max = %s", sprint_tm(h->min_reply)); + fprintf(stderr, "/%s", sprint_tm(avg)); + fprintf(stderr, "/%s", sprint_tm(h->max_reply)); + } + + fprintf(stderr, "\n"); + } + } +} + +/************************************************************ + + Function: print_per_system_stats_json + +************************************************************* + + Inputs: void (none) + + Description: + + +************************************************************/ + +void print_per_system_stats_json(void) +{ + int i, j, avg, outage_ms; + HOST_ENTRY *h; + int64_t resp; + + for (i = 0; i < num_hosts; i++) { + h = table[i]; + + if (opt_report_all_rtts_on) + fprintf(stdout, "{\"vSum\": {"); + else + fprintf(stdout, "{\"summary\": {"); + + fprintf(stdout, "\"host\": \"%s\", ", h->host); + + if (opt_report_all_rtts_on) { + fprintf(stdout, "\"values\": ["); + for (j = 0; j < h->num_sent; j++) { + if (j > 0) + fprintf(stdout, ", "); + + if ((resp = h->resp_times[j]) >= 0) + fprintf(stdout, "%s", sprint_tm(resp)); + else + fprintf(stdout, "null"); + } + + fprintf(stdout, "]}"); + } + else { + if (h->num_recv <= h->num_sent) { + fprintf(stdout, "\"xmt\": %d, ", h->num_sent); + fprintf(stdout, "\"rcv\": %d, ", h->num_recv); + fprintf(stdout, "\"loss\": %d", h->num_sent > 0 ? ((h->num_sent - h->num_recv) * 100) / h->num_sent : 0); + + if (opt_outage_on) { + /* Time outage total */ + outage_ms = (h->num_sent - h->num_recv) * opt_perhost_interval / 1e6; + fprintf(stdout, ", \"outage(ms)\": %d", outage_ms); + } + } + else { + fprintf(stdout, "\"xmt\": %d, ", h->num_sent); + fprintf(stdout, "\"rcv\": %d, ", h->num_recv); + fprintf(stdout, "\"return\": %d", h->num_sent > 0 ? ((h->num_recv * 100) / h->num_sent) : 0); + } + + if (h->num_recv) { + avg = h->total_time / h->num_recv; + fprintf(stdout, ", \"rttMin\": %s", sprint_tm(h->min_reply)); + fprintf(stdout, ", \"rttAvg\": %s", sprint_tm(avg)); + fprintf(stdout, ", \"rttMax\": %s", sprint_tm(h->max_reply)); + } + + fprintf(stdout, "}"); + } + fprintf(stdout, "}\n"); + } +} + +/************************************************************ + + Function: print_global_stats + +************************************************************* + + Inputs: void (none) + + Description: + + +************************************************************/ + +void print_global_stats(void) +{ + fprintf(stderr, "\n"); + fprintf(stderr, " %7d targets\n", num_hosts); + fprintf(stderr, " %7d alive\n", num_alive); + fprintf(stderr, " %7d unreachable\n", num_unreachable); + fprintf(stderr, " %7d unknown addresses\n", num_noaddress); + fprintf(stderr, "\n"); + fprintf(stderr, " %7d timeouts (waiting for response)\n", num_timeout); + fprintf(stderr, " %7d ICMP Echos sent\n", num_pingsent); + fprintf(stderr, " %7d ICMP Echo Replies received\n", num_pingreceived); + fprintf(stderr, " %7d other ICMP received\n", num_othericmprcvd); + fprintf(stderr, "\n"); + + if (total_replies == 0) { + min_reply = 0; + max_reply = 0; + total_replies = 1; + sum_replies = 0; + } + + fprintf(stderr, " %s ms (min round trip time)\n", sprint_tm(min_reply)); + fprintf(stderr, " %s ms (avg round trip time)\n", + sprint_tm(sum_replies / total_replies)); + fprintf(stderr, " %s ms (max round trip time)\n", sprint_tm(max_reply)); + fprintf(stderr, " %12.3f sec (elapsed real time)\n", + (end_time - start_time) / 1e9); + fprintf(stderr, "\n"); +} + +/************************************************************ + + Function: print_global_stats_json + +************************************************************* + + Inputs: void (none) + + Description: + + +************************************************************/ + +void print_global_stats_json(void) +{ + fprintf(stdout, "{\"stats\": {"); + fprintf(stdout, "\"targets\": %d, ", num_hosts); + fprintf(stdout, "\"alive\": %d, ", num_alive); + fprintf(stdout, "\"unreachable\": %d, ", num_unreachable); + fprintf(stdout, "\"unknownAddresses\": %d, ", num_noaddress); + fprintf(stdout, "\"timeouts\": %d, ", num_timeout); + fprintf(stdout, "\"icmpEchosSent\": %d, ", num_pingsent); + fprintf(stdout, "\"icmpEchoRepliesReceived\": %d, ", num_pingreceived); + fprintf(stdout, "\"otherIcmpReceived\": %d, ", num_othericmprcvd); + + if (total_replies == 0) { + min_reply = 0; + max_reply = 0; + total_replies = 1; + sum_replies = 0; + } + + fprintf(stdout, "\"rttMin\": %s, ", sprint_tm(min_reply)); + fprintf(stdout, "\"rttAvg\": %s, ", sprint_tm(sum_replies / total_replies)); + fprintf(stdout, "\"rttMax\": %s, ", sprint_tm(max_reply)); + fprintf(stdout, "\"elapsed\": %.3f", (end_time - start_time) / 1e9); + fprintf(stdout, "}}\n"); +} diff --git a/src/stats.h b/src/stats.h new file mode 100644 index 0000000..de59940 --- /dev/null +++ b/src/stats.h @@ -0,0 +1,10 @@ +#ifndef _STATS_H +#define _STATS_H + +#include "fping.h" + +void print_per_system_stats(void); +void print_per_system_stats_json(void); +void print_global_stats(void); +void print_global_stats_json(void); +#endif -- 2.43.0