Code

noping: Print host name in bold.
[liboping.git] / src / oping.c
index e4584d71cc12b526fca4d87cc2c01a505ced4997..3934b4a65db0bfc8b4ad0d320c465077d287a235 100644 (file)
 #include <sys/types.h>
 #endif
 
+#if USE_NCURSES
+# include <ncurses.h>
+#endif
+
 #include "oping.h"
 
 #ifndef _POSIX_SAVED_IDS
@@ -80,6 +84,10 @@ typedef struct ping_context
        double latency_max;
        double latency_total;
        double latency_total_square;
+
+#if USE_NCURSES
+       WINDOW *window;
+#endif
 } ping_context_t;
 
 static double  opt_interval   = 1.0;
@@ -90,13 +98,17 @@ static char   *opt_filename   = NULL;
 static int     opt_count      = -1;
 static int     opt_send_ttl   = 64;
 
-static void sigint_handler (int signal)
+#if USE_NCURSES
+static WINDOW *main_win = NULL;
+#endif
+
+static void sigint_handler (int signal) /* {{{ */
 {
        /* Make compiler happy */
        signal = 0;
        /* Exit the loop */
        opt_count = 0;
-}
+} /* }}} void sigint_handler */
 
 static ping_context_t *context_create (void)
 {
@@ -112,6 +124,10 @@ static ping_context_t *context_create (void)
        ret->latency_total = 0.0;
        ret->latency_total_square = 0.0;
 
+#if USE_NCURSES
+       ret->window = NULL;
+#endif
+
        return (ret);
 }
 
@@ -120,9 +136,61 @@ static void context_destroy (ping_context_t *context)
        if (context == NULL)
                return;
 
+#if USE_NCURSES
+       if (context->window != NULL)
+       {
+               delwin (context->window);
+               context->window = NULL;
+       }
+#endif
+
        free (context);
 }
 
+static double context_get_average (ping_context_t *ctx) /* {{{ */
+{
+       double num_total;
+
+       if (ctx == NULL)
+               return (-1.0);
+
+       if (ctx->req_rcvd < 1)
+               return (-0.0);
+
+       num_total = (double) ctx->req_rcvd;
+       return (ctx->latency_total / num_total);
+} /* }}} double context_get_average */
+
+static double context_get_stddev (ping_context_t *ctx) /* {{{ */
+{
+       double num_total;
+
+       if (ctx == NULL)
+               return (-1.0);
+
+       if (ctx->req_rcvd < 1)
+               return (-0.0);
+       else if (ctx->req_rcvd < 2)
+               return (0.0);
+
+       num_total = (double) ctx->req_rcvd;
+       return (sqrt (((num_total * ctx->latency_total_square)
+                                       - (ctx->latency_total * ctx->latency_total))
+                               / (num_total * (num_total - 1.0))));
+} /* }}} double context_get_stddev */
+
+static double context_get_packet_loss (const ping_context_t *ctx) /* {{{ */
+{
+       if (ctx == NULL)
+               return (-1.0);
+
+       if (ctx->req_sent < 1)
+               return (0.0);
+
+       return (100.0 * (ctx->req_sent - ctx->req_rcvd)
+                       / ((double) ctx->req_sent));
+} /* }}} double context_get_packet_loss */
+
 static void usage_exit (const char *name, int status)
 {
        int name_length;
@@ -148,7 +216,7 @@ static void usage_exit (const char *name, int status)
        exit (status);
 }
 
-static int read_options (int argc, char **argv)
+static int read_options (int argc, char **argv) /* {{{ */
 {
        int optchar;
 
@@ -230,9 +298,162 @@ static int read_options (int argc, char **argv)
        }
 
        return (optind);
-}
+} /* }}} read_options */
+
+static void time_normalize (struct timespec *ts) /* {{{ */
+{
+       while (ts->tv_nsec < 0)
+       {
+               if (ts->tv_sec == 0)
+               {
+                       ts->tv_nsec = 0;
+                       return;
+               }
+
+               ts->tv_sec  -= 1;
+               ts->tv_nsec += 1000000000;
+       }
+
+       while (ts->tv_nsec >= 1000000000)
+       {
+               ts->tv_sec  += 1;
+               ts->tv_nsec -= 1000000000;
+       }
+} /* }}} void time_normalize */
 
-static void print_host (pingobj_iter_t *iter)
+static void time_calc (struct timespec *ts_dest, /* {{{ */
+               const struct timespec *ts_int,
+               const struct timeval  *tv_begin,
+               const struct timeval  *tv_end)
+{
+       ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec;
+       ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec;
+       time_normalize (ts_dest);
+
+       /* Assure that `(begin + interval) > end'.
+        * This may seem overly complicated, but `tv_sec' is of type `time_t'
+        * which may be `unsigned. *sigh* */
+       if ((tv_end->tv_sec > ts_dest->tv_sec)
+                       || ((tv_end->tv_sec == ts_dest->tv_sec)
+                               && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec)))
+       {
+               ts_dest->tv_sec  = 0;
+               ts_dest->tv_nsec = 0;
+               return;
+       }
+
+       ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec;
+       ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000);
+       time_normalize (ts_dest);
+} /* }}} void time_calc */
+
+#if USE_NCURSES
+static int context_window_repaint (ping_context_t *ctx, /* {{{ */
+               int index)
+{
+       if (ctx == NULL)
+               return (EINVAL);
+
+       if (ctx->window == NULL)
+       {
+               ctx->window = newwin (/* height = */ 4, /* width = */ 0,
+                               /* start y = */ 4 * index, /* start x = */ 0);
+       }
+       else /* if (ctx->window != NULL) */
+       {
+               werase (ctx->window);
+       }
+
+       box (ctx->window, 0, 0);
+       wattron (ctx->window, A_BOLD);
+       mvwprintw (ctx->window, /* y = */ 0, /* x = */ 5,
+                       " %s ", ctx->host);
+       wattroff (ctx->window, A_BOLD);
+       wprintw (ctx->window, "ping statistics ");
+       mvwprintw (ctx->window, /* y = */ 1, /* x = */ 2,
+                       "%i packets transmitted, %i received, %.2f%% packet "
+                       "loss, time %.1fms",
+                       ctx->req_sent, ctx->req_rcvd,
+                       context_get_packet_loss (ctx),
+                       ctx->latency_total);
+       if (ctx->req_rcvd != 0)
+       {
+               double average;
+               double deviation;
+
+               average = context_get_average (ctx);
+               deviation = context_get_stddev (ctx);
+                       
+               mvwprintw (ctx->window, /* y = */ 2, /* x = */ 2,
+                               "rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms",
+                               ctx->latency_min,
+                               average,
+                               ctx->latency_max,
+                               deviation);
+       }
+
+       wrefresh (ctx->window);
+
+       return (0);
+} /* }}} int context_window_repaint */
+
+static int resize_windows (pingobj_t *ping) /* {{{ */
+{
+       int index;
+       pingobj_iter_t *iter;
+       int width = 0;
+       int height = 0;
+       int need_resize = 0;
+
+       while (42)
+       {
+               int key = wgetch (stdscr);
+               if (key == ERR)
+                       break;
+               else if (key == KEY_RESIZE)
+                       need_resize = 1;
+       }
+
+       if (!need_resize)
+               return (0);
+
+       getmaxyx (stdscr, height, width);
+       if ((height < 1) || (width < 1))
+               return (EINVAL);
+
+       index = 0;
+       for (iter = ping_iterator_get (ping);
+                       iter != NULL;
+                       iter = ping_iterator_next (iter))
+       {
+               ping_context_t *ctx = ping_iterator_get_context (iter);
+
+               if (ctx->window == NULL)
+               {
+                       index++;
+                       continue;
+               }
+
+               wresize (ctx->window, 4, width);
+               context_window_repaint (ctx, index);
+
+               index++;
+       }
+
+       if (main_win != NULL)
+       {
+               wresize (main_win, height - (4 * index), width);
+               /* touchwin (main_win); */
+               /* wrefresh (main_win); */
+               clearok (main_win, TRUE);
+       }
+
+       return (0);
+} /* }}} int resize_windows */
+#endif
+
+static void print_host (pingobj_iter_t *iter, /* {{{ */
+               int index)
 {
        double          latency;
        unsigned int    sequence;
@@ -262,6 +483,12 @@ static void print_host (pingobj_iter_t *iter)
 
        context = (ping_context_t *) ping_iterator_get_context (iter);
 
+#if USE_NCURSES
+# define HOST_PRINTF(...) wprintw(main_win, __VA_ARGS__)
+#else
+# define HOST_PRINTF(...) printf(__VA_ARGS__)
+#endif
+
        context->req_sent++;
        if (latency > 0.0)
        {
@@ -274,72 +501,38 @@ static void print_host (pingobj_iter_t *iter)
                if ((context->latency_min < 0.0) || (context->latency_min > latency))
                        context->latency_min = latency;
 
-               printf ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i time=%.2f ms\n",
+               HOST_PRINTF ("%zu bytes from %s (%s): icmp_seq=%u ttl=%i "
+                               "time=%.2f ms\n",
                                data_len,
                                context->host, context->addr,
                                sequence, recv_ttl, latency);
        }
        else
        {
-               printf ("echo reply from %s (%s): icmp_seq=%u timeout\n",
+               HOST_PRINTF ("echo reply from %s (%s): icmp_seq=%u timeout\n",
                                context->host, context->addr,
                                sequence);
        }
-}
-
-static void time_normalize (struct timespec *ts)
-{
-       while (ts->tv_nsec < 0)
-       {
-               if (ts->tv_sec == 0)
-               {
-                       ts->tv_nsec = 0;
-                       return;
-               }
-
-               ts->tv_sec  -= 1;
-               ts->tv_nsec += 1000000000;
-       }
 
-       while (ts->tv_nsec >= 1000000000)
-       {
-               ts->tv_sec  += 1;
-               ts->tv_nsec -= 1000000000;
-       }
-}
-
-static void time_calc (struct timespec *ts_dest, /* {{{ */
-               const struct timespec *ts_int,
-               const struct timeval  *tv_begin,
-               const struct timeval  *tv_end)
-{
-       ts_dest->tv_sec = tv_begin->tv_sec + ts_int->tv_sec;
-       ts_dest->tv_nsec = (tv_begin->tv_usec * 1000) + ts_int->tv_nsec;
-       time_normalize (ts_dest);
-
-       /* Assure that `(begin + interval) > end'.
-        * This may seem overly complicated, but `tv_sec' is of type `time_t'
-        * which may be `unsigned. *sigh* */
-       if ((tv_end->tv_sec > ts_dest->tv_sec)
-                       || ((tv_end->tv_sec == ts_dest->tv_sec)
-                               && ((tv_end->tv_usec * 1000) > ts_dest->tv_nsec)))
-       {
-               ts_dest->tv_sec  = 0;
-               ts_dest->tv_nsec = 0;
-               return;
-       }
-
-       ts_dest->tv_sec = ts_dest->tv_sec - tv_end->tv_sec;
-       ts_dest->tv_nsec = ts_dest->tv_nsec - (tv_end->tv_usec * 1000);
-       time_normalize (ts_dest);
-} /* }}} void time_calc */
+#if USE_NCURSES
+       context_window_repaint (context, index);
+       wrefresh (main_win);
+#endif
+} /* }}} void print_host */
 
 static int print_header (pingobj_t *ping) /* {{{ */
 {
        pingobj_iter_t *iter;
-       int i;
+       int index;
+
+#if USE_NCURSES
+       initscr ();
+       cbreak ();
+       noecho ();
+       nodelay (stdscr, TRUE);
+#endif
 
-       i = 0;
+       index = 0;
        for (iter = ping_iterator_get (ping);
                        iter != NULL;
                        iter = ping_iterator_next (iter))
@@ -358,21 +551,48 @@ static int print_header (pingobj_t *ping) /* {{{ */
                buffer_size = 0;
                ping_iterator_get_info (iter, PING_INFO_DATA, NULL, &buffer_size);
 
+#if USE_NCURSES
+               context_window_repaint (context, index);
+#else /* !USE_NCURSES */
                printf ("PING %s (%s) %zu bytes of data.\n",
                                context->host, context->addr, buffer_size);
+#endif
 
                ping_iterator_set_context (iter, (void *) context);
 
-               i++;
+               index++;
+       }
+
+#if USE_NCURSES
+       main_win = newwin (/* height = */ 0, /* width = */ 0,
+                       /* y = */ 4 * index, /* x = */ 0);
+       /* Allow scrolling */
+       scrollok (main_win, TRUE);
+       /* Allow hardware accelerated scrolling. */
+       idlok (main_win, TRUE);
+
+       /* Don't know what good this does exactly, but without this code
+        * "resize_windows" will be called right after startup and *somehow*
+        * this leads to display errors. If we purge all initial characters
+        * here, the problem goes away. "wgetch" is non-blocking due to
+        * "nodelay" (see above). */
+       while (wgetch (stdscr) != ERR)
+       {
+               /* eat up characters */;
        }
+#endif
 
        return (0);
 } /* }}} int print_header */
 
-static int print_footer (pingobj_t *ping)
+static int print_footer (pingobj_t *ping) /* {{{ */
 {
        pingobj_iter_t *iter;
 
+#if USE_NCURSES
+       endwin ();
+#endif
+
        for (iter = ping_iterator_get (ping);
                        iter != NULL;
                        iter = ping_iterator_next (iter))
@@ -384,20 +604,16 @@ static int print_footer (pingobj_t *ping)
                printf ("\n--- %s ping statistics ---\n"
                                "%i packets transmitted, %i received, %.2f%% packet loss, time %.1fms\n",
                                context->host, context->req_sent, context->req_rcvd,
-                               100.0 * (context->req_sent - context->req_rcvd) / ((double) context->req_sent),
+                               context_get_packet_loss (context),
                                context->latency_total);
 
                if (context->req_rcvd != 0)
                {
-                       double num_total;
                        double average;
                        double deviation;
 
-                       num_total = (double) context->req_rcvd;
-
-                       average = context->latency_total / num_total;
-                       deviation = sqrt (((num_total * context->latency_total_square) - (context->latency_total * context->latency_total))
-                                       / (num_total * (num_total - 1.0)));
+                       average = context_get_average (context);
+                       deviation = context_get_stddev (context);
 
                        printf ("rtt min/avg/max/sdev = %.3f/%.3f/%.3f/%.3f ms\n",
                                        context->latency_min,
@@ -623,6 +839,7 @@ int main (int argc, char **argv) /* {{{ */
 
        while (opt_count != 0)
        {
+               int index;
                int status;
 
                if (gettimeofday (&tv_begin, NULL) < 0)
@@ -638,13 +855,17 @@ int main (int argc, char **argv) /* {{{ */
                        return (1);
                }
 
+               index = 0;
                for (iter = ping_iterator_get (ping);
                                iter != NULL;
                                iter = ping_iterator_next (iter))
                {
-                       print_host (iter);
+                       print_host (iter, index);
+                       index++;
                }
+#if !USE_NCURSES
                fflush (stdout);
+#endif
 
                /* Don't sleep in the last iteration */
                if (opt_count == 1)
@@ -671,8 +892,16 @@ int main (int argc, char **argv) /* {{{ */
                                /* sigint */
                                break;
                        }
+
+#if USE_NCURSES
+                       resize_windows (ping);
+#endif
                }
 
+#if USE_NCURSES
+               resize_windows (ping);
+#endif
+
                if (opt_count > 0)
                        opt_count--;
        } /* while (opt_count != 0) */