Jindrich Makovicka 23 роки тому
коміт
66b3e15543
7 змінених файлів з 1841 додано та 0 видалено
  1. 1 0
      AUTHORS
  2. 22 0
      ChangeLog
  3. 33 0
      Makefile
  4. 12 0
      Themes
  5. 197 0
      decal_multiping_status.xpm
  6. 780 0
      multiping.c
  7. 796 0
      pinger.c

+ 1 - 0
AUTHORS

@@ -0,0 +1 @@
+(c) 2002 Jindrich Makovicka <makovick@kmlinux.fjfi.cvut.cz>

+ 22 - 0
ChangeLog

@@ -0,0 +1,22 @@
+2.0.3	* patches by Gerard W. Paterson <gerry.patterson@computalog.com>
+	  - per-host ping frequency setup
+	  - various bugfixes
+	* pinger helper restricted to one ping per second to spread load
+
+2.0.2   * trip time display is now optional
+        * DNS resolving
+
+2.0.1
+	* Makefile fix (install -d)
+
+2.0
+	* ported to Gkrellm 2
+
+0.1.2
+	* fixed the pinger helper to handle sigpipe
+	* minor fixes in multiping.c
+	* added an .rc option to adjust time x-offset
+
+0.1.1
+	* changed the status LED pixmap
+	* added options for better theme-ability

+ 33 - 0
Makefile

@@ -0,0 +1,33 @@
+INSTALLDIR = /usr/local/lib/gkrellm2/plugins
+VERSION = 2.0.3
+PKGNAME = gkrellm-multiping
+#OPT = -march=athlon -O2
+#CC = gcc-3.0
+CC = gcc
+
+all: pinger multiping.so
+
+pinger: pinger.c
+	$(CC) `pkg-config glib-2.0 --cflags` $(OPT) -lpthread `pkg-config glib-2.0 --libs` -Wall -o pinger pinger.c
+
+multiping.o: multiping.c decal_multiping_status.xpm
+	$(CC) -Wall -fPIC -Wall `pkg-config gtk+-2.0 --cflags` $(OPT) -DVERSION=\"$(VERSION)\" -c multiping.c
+
+multiping.so: multiping.o
+	$(CC) -shared -Wl -ggdb `pkg-config gtk+-2.0 --libs`-o multiping.so multiping.o
+
+clean:
+	rm -f *.o *.so core
+
+install: pinger multiping.so
+	install -d $(INSTALLDIR)
+	install -c -s -m 644 multiping.so $(INSTALLDIR)
+	install -c -s -m 4755 pinger $(INSTALLDIR)
+	echo "pinger helper is installed suid root"
+
+dist:
+	rm -rf $(PKGNAME)-$(VERSION)
+	mkdir $(PKGNAME)-$(VERSION)
+	cp AUTHORS Makefile multiping.c pinger.c decal_multiping_status.xpm Themes ChangeLog $(PKGNAME)-$(VERSION)/
+	tar zcf $(PKGNAME)-$(VERSION).tgz $(PKGNAME)-$(VERSION)
+	rm -rf $(PKGNAME)-$(VERSION)

+ 12 - 0
Themes

@@ -0,0 +1,12 @@
+Style name: multiping
+
+decal_multiping_status.png contains the images of LEDs (dark, green, red)
+bg_panel.png can contain an alternative background
+
+The following additional integer options control the layout:
+
+multiping_vspacing - spacing between the entries in the table
+multiping_hspacing - spacing between the LED and the label
+multiping_text_xoffset - correction of the x-position of timer
+
+use eg. "set_integer multiping_hspacing = 4" in your gkrellmrc file

+ 197 - 0
decal_multiping_status.xpm

@@ -0,0 +1,197 @@
+/* XPM */
+static char * decal_multiping_status_xpm[] = {
+"10 33 161 2",
+"  	c None",
+". 	c #6A6A6A",
+"+ 	c #4A4A4A",
+"@ 	c #3B3B3B",
+"# 	c #535454",
+"$ 	c #717171",
+"% 	c #131313",
+"& 	c #0C0C0C",
+"* 	c #1C1C1C",
+"= 	c #242524",
+"- 	c #646363",
+"; 	c #444444",
+"> 	c #333233",
+", 	c #2A2A2A",
+"' 	c #322D2F",
+") 	c #949393",
+"! 	c #838282",
+"~ 	c #5B5B5B",
+"{ 	c #AAA9A9",
+"] 	c #9A9A9A",
+"^ 	c #7C7C7C",
+"/ 	c #A5A4A5",
+"( 	c #C2C2C2",
+"_ 	c #8C8C8C",
+": 	c #BDBDBD",
+"< 	c #3F423A",
+"[ 	c #444942",
+"} 	c #6C7269",
+"| 	c #1F241A",
+"1 	c #161912",
+"2 	c #1F2616",
+"3 	c #2C3A1E",
+"4 	c #566347",
+"5 	c #91928B",
+"6 	c #496134",
+"7 	c #5A7738",
+"8 	c #466324",
+"9 	c #325215",
+"0 	c #375219",
+"a 	c #75855A",
+"b 	c #617749",
+"c 	c #CFDDB6",
+"d 	c #C5DA9A",
+"e 	c #7CA93C",
+"f 	c #58852C",
+"g 	c #3C6E12",
+"h 	c #587C30",
+"i 	c #939885",
+"j 	c #8AA565",
+"k 	c #E6F0CD",
+"l 	c #DDF0AD",
+"m 	c #9ACA42",
+"n 	c #69AB21",
+"o 	c #529616",
+"p 	c #548F1C",
+"q 	c #B0BF9C",
+"r 	c #233216",
+"s 	c #67883A",
+"t 	c #A7CB5E",
+"u 	c #AFD950",
+"v 	c #93CE30",
+"w 	c #76C21B",
+"x 	c #65B617",
+"y 	c #67AF19",
+"z 	c #D9E3C3",
+"A 	c #44671F",
+"B 	c #69A12A",
+"C 	c #7ABD21",
+"D 	c #88CB22",
+"E 	c #7CCA19",
+"F 	c #9ED934",
+"G 	c #E6EDD5",
+"H 	c #727C72",
+"I 	c #304221",
+"J 	c #4F831D",
+"K 	c #9CE62A",
+"L 	c #A9EC31",
+"M 	c #E6F8B4",
+"N 	c #DBE0D2",
+"O 	c #5C6E49",
+"P 	c #427C12",
+"Q 	c #58A514",
+"R 	c #9AE126",
+"S 	c #D6F875",
+"T 	c #F3FAE5",
+"U 	c #B0B4AF",
+"V 	c #7C8A5E",
+"W 	c #589A19",
+"X 	c #CBEB80",
+"Y 	c #D5D9CE",
+"Z 	c #A5AD9A",
+"` 	c #CDD5BD",
+" .	c #E8EDDA",
+"..	c #E0E5D7",
+"+.	c #B6B9B2",
+"@.	c #928E91",
+"#.	c #544B4C",
+"$.	c #5F5658",
+"%.	c #261D20",
+"&.	c #38282D",
+"*.	c #422331",
+"=.	c #4D2D3B",
+"-.	c #7E5D6B",
+";.	c #79485E",
+">.	c #934D6F",
+",.	c #7F3556",
+"'.	c #6B2843",
+").	c #9D738D",
+"!.	c #936179",
+"~.	c #E8C9DB",
+"{.	c #E5B2D0",
+"].	c #BD518E",
+"^.	c #9C2C5E",
+"/.	c #871E49",
+"(.	c #9E3F6C",
+"_.	c #9B9298",
+":.	c #38232D",
+"<.	c #B980A1",
+"[.	c #F6DAEC",
+"}.	c #F5C0E4",
+"|.	c #D958A7",
+"1.	c #C22877",
+"2.	c #B22665",
+"3.	c #A62A62",
+"4.	c #C7ADBB",
+"5.	c #A14D79",
+"6.	c #DA79B5",
+"7.	c #E368B9",
+"8.	c #DB42A1",
+"9.	c #C62470",
+"0.	c #BF2673",
+"a.	c #EBD2E2",
+"b.	c #802F54",
+"c.	c #B73B79",
+"d.	c #CF398E",
+"e.	c #DA3293",
+"f.	c #DA2A8B",
+"g.	c #D82887",
+"h.	c #ECDCE6",
+"i.	c #58303F",
+"j.	c #6A2140",
+"k.	c #AD235D",
+"l.	c #EE3BA3",
+"m.	c #F144B1",
+"n.	c #FAC5EA",
+"o.	c #D4CDD2",
+"p.	c #886171",
+"q.	c #77264A",
+"r.	c #951E4D",
+"s.	c #B92065",
+"t.	c #D02580",
+"u.	c #EA36A3",
+"v.	c #FA90DB",
+"w.	c #FBECF5",
+"x.	c #A37990",
+"y.	c #CF3088",
+"z.	c #F29CD3",
+"A.	c #C8C3C6",
+"B.	c #AFA4A9",
+"C.	c #DCCBD5",
+"D.	c #DCD4D8",
+"    . + @ @ # $     ",
+"  # % & * * = + -   ",
+". * * @ ; > , ' # . ",
+"+ % + ) ! ~ @ > ; . ",
+"= * - { ] - + @ @ ^ ",
+"* * + . $ - + + + ] ",
+"@ * @ + # # # # - { ",
+"~ , , @ + + . $ / ] ",
+"$ + > > ; + - ) ( ! ",
+"  . ~ ; ; + _ : )   ",
+"    . $ ) { / !     ",
+"    ) # < [ } ]     ",
+"  . | 1 2 2 3 4 5   ",
+") | 2 6 7 8 9 0 a _ ",
+"# 1 b c d e f g h i ",
+", | j k l m n o p q ",
+"= r s t u v w x y z ",
+"< r A B C D E E F G ",
+"H I 9 J y E K L M N ",
+"] O g P Q x R S T U ",
+"  ) V f W C X T Y   ",
+"    ) Z `  ...+.    ",
+"    @.- #.$.^ )     ",
+"  $ , %.&.*.=.-.@.  ",
+") ' &.;.>.,.'.'.)._ ",
+"- %.!.~.{.].^./.(._.",
+"@ :.<.[.}.|.1.2.3.4.",
+"' *.5.6.7.8.1.9.0.a.",
+"#.*.b.c.d.e.f.g.8.h.",
+"! i.j.k.1.g.l.m.n.o.",
+") p.q.r.s.t.u.v.w.{ ",
+"  ) x.(.2.y.z.w.A.  ",
+"    ) B.C.[.D.{     "};

+ 780 - 0
multiping.c

@@ -0,0 +1,780 @@
+/*____________________________________________________________________________
+        
+        gkrellm multiping plugin
+
+        Copyright (C) 2002 Jindrich Makovicka
+
+        This program is free software; you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation; either version 2 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with this program; if not, Write to the Free Software
+        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+____________________________________________________________________________*/
+
+#include <stdio.h>
+#include <string.h>
+#include <gkrellm2/gkrellm.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "decal_multiping_status.xpm"
+
+#define	CONFIG_NAME	"Multiping"
+#define	STYLE_NAME	"multiping"
+
+#define COMMAND "/usr/local/lib/gkrellm2/plugins/pinger"
+
+static GkrellmMonitor *monitor;
+static GkrellmPanel *panel;
+
+static gint style_id;
+
+static FILE *pinger_pipe = NULL;
+static pid_t pinger_pid;
+
+static gshort selected_row;
+
+static gboolean delete_list;
+static gboolean list_modified;
+
+static GtkWidget *plugin_vbox;
+static GtkWidget *label_entry;
+static GtkWidget *url_entry;
+static GtkWidget *updatefreq_spin;
+static GtkWidget *multiping_clist;
+static GtkWidget *show_trip_checkbutton;
+
+static GkrellmPiximage *decal_status_image;
+static GdkPixmap *status_pixmap;
+static GdkBitmap *status_mask;
+
+static gint vspacing, hspacing, time_xoffset;
+
+typedef struct _host_data {
+    GString *name, *ip, *percentage, *sent_str, *recv_str, *msg, *shortmsg, *updatefreq;
+    GkrellmDecal *name_text, *msg_text, *decal_pix;
+    gboolean show_trip;
+} host_data;
+
+static GList *hosts;
+
+static host_data *host_malloc()
+{
+    host_data *h = (host_data *) g_malloc(sizeof(host_data));
+    h->name_text = NULL;
+    h->msg_text = NULL;
+    h->decal_pix = NULL;
+    h->name = g_string_new(NULL);
+    h->ip = g_string_new(NULL);
+    h->percentage = g_string_new(NULL);
+    h->sent_str = g_string_new(NULL);
+    h->recv_str = g_string_new(NULL);
+    h->msg = g_string_new(NULL);
+    h->shortmsg = g_string_new("wait");
+    h->updatefreq = g_string_new(NULL);
+    h->show_trip = 0;
+    return h;
+}
+
+static void host_free(host_data * h)
+{
+    if (h->name_text) {
+	gkrellm_destroy_decal(h->name_text);
+    }
+    if (h->msg_text) {
+	gkrellm_destroy_decal(h->msg_text);
+    }
+    if (h->decal_pix) {
+	gkrellm_destroy_decal(h->decal_pix);
+    }
+    g_string_free(h->name, TRUE);
+    g_string_free(h->ip, TRUE);
+    g_string_free(h->percentage, TRUE);
+    g_string_free(h->sent_str, TRUE);
+    g_string_free(h->recv_str, TRUE);
+    g_string_free(h->msg, TRUE);
+    g_string_free(h->shortmsg, TRUE);
+    g_string_free(h->updatefreq, TRUE);
+    g_free(h);
+}
+
+static void strip_nl(gchar *buf)
+{
+    if (buf[strlen(buf) - 1] == '\n')
+	buf[strlen(buf) - 1] = 0;
+}
+
+
+static void host_read_pipe(host_data * h)
+{
+    gchar buf[512];
+
+    fgets(buf, 512, pinger_pipe);
+    strip_nl(buf);
+    g_string_assign(h->percentage, buf);
+    fgets(buf, 512, pinger_pipe);
+    strip_nl(buf);
+    g_string_assign(h->sent_str, buf);
+    fgets(buf, 512, pinger_pipe);
+    strip_nl(buf);
+    g_string_assign(h->recv_str, buf);
+    fgets(buf, 512, pinger_pipe);
+    strip_nl(buf);
+    g_string_assign(h->msg, buf);
+    fgets(buf, 512, pinger_pipe);
+    strip_nl(buf);
+    g_string_assign(h->shortmsg, buf);
+}
+
+static void kill_pinger()
+{
+    if (pinger_pipe) {
+	kill(pinger_pid, SIGTERM);
+	waitpid(pinger_pid, NULL, 0);
+	fclose(pinger_pipe);
+	pinger_pipe = NULL;
+    }
+}
+
+static void launch_pipe()
+{
+    GString *s = g_string_new(COMMAND);
+    gint i;
+    GList *list;
+    host_data *h;
+    pid_t pid;
+    gint mypipe[2];
+
+    for (i = 0, list = hosts; list; list = list->next, i++) {
+	h = (host_data *) list->data;
+	g_string_append(s, " ");
+	g_string_append(s, h->ip->str);
+	g_string_append(s, " ");
+	g_string_append(s, h->updatefreq->str);
+    }
+
+    if (pipe(mypipe)) {
+	fprintf(stderr, "Pipe failed.\n");
+	return;
+    }
+
+    pid = fork();
+    if (pid == 0) {
+	/* This is the child process.  Execute the shell command. */
+	close(mypipe[0]);
+	dup2(mypipe[1], 1);
+	execl("/bin/sh", "/bin/sh", "-c", s->str, NULL);
+	_exit(EXIT_FAILURE);
+    } else if (pid < 0) {
+	/* The fork failed.  Report failure.  */
+	fprintf(stderr, "failed to fork\n");
+    } else {
+	/* This is the parent process. Prepare the pipe stream. */
+	close(mypipe[1]);
+	pinger_pipe = fdopen(mypipe[0], "r");
+	pinger_pid = pid;
+    }
+}
+
+/*
+static void
+host_append_info(host_data *h, GString *str)
+{
+  GString *s = g_string_new(NULL);
+
+  g_string_sprintf(s, "%s: %s, %s\n",
+		   h->name->str, h->percentage->str, h->msg->str);
+  g_string_append(str, s->str);
+
+  g_string_free(s, TRUE);
+}
+*/
+
+static void host_draw_name(host_data * h)
+{
+    gkrellm_draw_decal_text(panel, h->name_text, h->name->str, -1);
+}
+
+static void host_draw_msg(host_data * h)
+{
+    gint n, p;
+
+    if (h->show_trip) {
+	gkrellm_draw_decal_text(panel, h->msg_text, h->shortmsg->str, -1);
+    }
+    n = sscanf(h->percentage->str, "%d", &p);
+    if (n != 1 || p == 0) {
+	gkrellm_draw_decal_pixmap(panel, h->decal_pix, 0);
+    } else if (p < 100) {
+	gkrellm_draw_decal_pixmap(panel, h->decal_pix, 2);
+    } else {
+	gkrellm_draw_decal_pixmap(panel, h->decal_pix, 1);
+    }
+}
+
+static GList *append_host(GList * list, gchar * name, gchar * ip, gboolean show_trip, gchar * updatefreq)
+{
+    host_data *h = host_malloc();
+    g_string_assign(h->name, name);
+    g_string_assign(h->ip, ip);
+    h->show_trip = show_trip;
+    g_string_assign(h->updatefreq, updatefreq);
+    return g_list_append(list, h);
+}
+
+static gint
+display_host(host_data * h, GkrellmStyle * style, GkrellmTextstyle * ts,
+	     GkrellmTextstyle * ts_alt, gint y)
+{
+    if (h->show_trip) {
+	h->msg_text =
+	    gkrellm_create_decal_text(panel, "999", ts_alt, style, 0, y, 0);
+
+	h->msg_text->x = gkrellm_chart_width() - h->msg_text->w + time_xoffset;
+    }
+    
+    h->decal_pix =
+	gkrellm_create_decal_pixmap(panel, status_pixmap, status_mask, 3,
+				    style, -1, y);
+    h->name_text =
+	gkrellm_create_decal_text(panel, "Ay", ts, style,
+				  h->decal_pix->x+h->decal_pix->w+hspacing, y, -1);
+
+    if (h->name_text->h < h->decal_pix->h) {
+	h->name_text->y += (h->decal_pix->h-h->name_text->h)/2;
+	if (h->show_trip) {
+	    h->msg_text->y = h->name_text->y;
+	}
+	return h->decal_pix->y + h->decal_pix->h + vspacing;
+    } else {
+	h->decal_pix->y += (h->name_text->h-h->decal_pix->h)/2;
+	return h->name_text->y + h->name_text->h + vspacing;
+    }
+}
+
+static gint panel_click_event(GtkWidget * widget, GdkEventButton * ev)
+{
+    if (ev->button == 3)
+	gkrellm_open_config_window(monitor);
+
+    return 1;
+}
+
+static gint panel_expose_event(GtkWidget * widget, GdkEventExpose * ev)
+{
+    gdk_draw_pixmap(widget->window,
+		    widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
+		    panel->pixmap, ev->area.x, ev->area.y, ev->area.x,
+		    ev->area.y, ev->area.width, ev->area.height);
+    return FALSE;
+}
+
+
+static void update_plugin()
+{
+    fd_set fds;
+    struct timeval tv;
+    gint ret;
+    GString *str = g_string_new(NULL);
+
+    FD_ZERO(&fds);
+    FD_SET(fileno(pinger_pipe), &fds);
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+    ret = select(fileno(pinger_pipe) + 1, &fds, 0, 0, &tv);
+
+    if (ret) {
+	g_list_foreach(hosts, (GFunc) host_read_pipe, NULL);
+	g_list_foreach(hosts, (GFunc) host_draw_msg, NULL);
+	gkrellm_draw_panel_layers(panel);
+    }
+
+    /*  
+       if (GK.minute_tick && tooltip) {
+       //    if (tooltip->tip_window == NULL || !GTK_WIDGET_VISIBLE(tooltip->tip_window)) {
+       g_list_foreach(hosts, (GFunc)host_append_info, str);
+       gtk_tooltips_set_tip(tooltip, panel->drawing_area, str->str, NULL);
+       gtk_tooltips_set_delay(tooltip, 750);
+       gtk_tooltips_enable(tooltip);
+       //    }
+       }
+     */
+    g_string_free(str, TRUE);
+}
+
+static void setup_display(gboolean first_create)
+{
+    gshort i;
+    gint y;
+    GkrellmStyle *style;
+    GkrellmTextstyle *ts, *ts_alt;
+    GList *list;
+    host_data *h;
+
+    if (first_create) {
+	panel = gkrellm_panel_new0();
+    }
+
+    style = gkrellm_panel_style(style_id);
+    ts = gkrellm_meter_textstyle(style_id);
+    ts_alt = gkrellm_meter_alt_textstyle(style_id);
+
+    y = 3;
+//    y = style->top_margin;
+    
+    for (i = 0, list = hosts; list; list = list->next, i++) {
+	h = (host_data *) list->data;
+	y = display_host(h, style, ts, ts_alt, y);
+    }
+
+    gkrellm_panel_configure(panel, NULL, style);
+    gkrellm_panel_create(plugin_vbox, monitor, panel);
+
+    if (first_create) {
+	gtk_signal_connect(GTK_OBJECT(panel->drawing_area), "expose_event",
+			   (GtkSignalFunc) panel_expose_event, NULL);
+	gtk_signal_connect(GTK_OBJECT(panel->drawing_area),
+			   "button_release_event",
+			   (GtkSignalFunc) panel_click_event, NULL);
+    }
+
+    g_list_foreach(hosts, (GFunc) host_draw_name, NULL);
+    g_list_foreach(hosts, (GFunc) host_draw_msg, NULL);
+
+    gkrellm_draw_panel_layers(panel);
+}
+
+static void create_plugin(GtkWidget * vbox, gint first_create)
+{
+    plugin_vbox = vbox;
+
+    gkrellm_load_piximage("decal_multiping_status", decal_multiping_status_xpm,
+			  &decal_status_image, STYLE_NAME);
+
+    gkrellm_scale_piximage_to_pixmap(decal_status_image, &status_pixmap,
+				     &status_mask, 0, 0);
+
+    if (!gkrellm_get_gkrellmrc_integer("multiping_vspacing", &vspacing))
+	vspacing = 2;
+    if (!gkrellm_get_gkrellmrc_integer("multiping_hspacing", &hspacing))
+	hspacing = 2;
+    if (!gkrellm_get_gkrellmrc_integer("multiping_time_xoffset", &time_xoffset))
+	time_xoffset = 0;
+
+    kill_pinger();
+    launch_pipe();
+    setup_display(first_create);
+}
+
+static void cb_up(GtkWidget * widget, gpointer drawer)
+{
+    GtkWidget *clist;
+    gshort row;
+
+    clist = multiping_clist;
+    row = selected_row;
+    if (row > 0) {
+	gtk_clist_row_move(GTK_CLIST(clist), row, row - 1);
+	gtk_clist_select_row(GTK_CLIST(clist), row - 1, -1);
+	if (gtk_clist_row_is_visible(GTK_CLIST(clist), row - 1) !=
+	    GTK_VISIBILITY_FULL)
+	    gtk_clist_moveto(GTK_CLIST(clist), row - 1, -1, 0.0, 0.0);
+	selected_row = row - 1;
+	list_modified = TRUE;
+    }
+}
+
+
+static void cb_down(GtkWidget * widget, gpointer drawer)
+{
+    GtkWidget *clist;
+    gshort row;
+
+
+    clist = multiping_clist;
+    row = selected_row;
+    if (row >= 0 && row < GTK_CLIST(clist)->rows - 1) {
+	gtk_clist_row_move(GTK_CLIST(clist), row, row + 1);
+	gtk_clist_select_row(GTK_CLIST(clist), row + 1, -1);
+	if (gtk_clist_row_is_visible(GTK_CLIST(clist), row + 1) !=
+	    GTK_VISIBILITY_FULL)
+	    gtk_clist_moveto(GTK_CLIST(clist), row + 1, -1, 1.0, 0.0);
+	selected_row = row + 1;
+	list_modified = TRUE;
+    }
+}
+
+
+static void reset_entries()
+{
+    gtk_entry_set_text(GTK_ENTRY(label_entry), "");
+    gtk_entry_set_text(GTK_ENTRY(url_entry), "");
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_trip_checkbutton), TRUE);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(updatefreq_spin),60);
+    return;
+}
+
+
+static void cb_selected(GtkWidget * clist, gint row, gint column,
+			GdkEventButton * bevent, gpointer data)
+{
+    gchar *s;
+
+    gtk_clist_get_text(GTK_CLIST(multiping_clist), row, 0, &s);
+    gtk_entry_set_text(GTK_ENTRY(label_entry), s);
+    gtk_clist_get_text(GTK_CLIST(multiping_clist), row, 1, &s);
+    gtk_entry_set_text(GTK_ENTRY(url_entry), s);
+    gtk_clist_get_text(GTK_CLIST(multiping_clist), row, 2, &s);
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_trip_checkbutton), strcmp(s, "yes") == 0);
+    gtk_clist_get_text(GTK_CLIST(multiping_clist), row, 3, &s);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(updatefreq_spin),atoi(s));
+
+    selected_row = row;
+
+    return;
+}
+
+
+static void cb_unselected(GtkWidget * clist, gint row, gint column,
+			  GdkEventButton * bevent, gpointer data)
+{
+    selected_row = -1;
+    reset_entries();
+
+    return;
+}
+
+
+static void cb_enter(GtkWidget * widget, gpointer data)
+{
+    gchar *buf[4];
+    gboolean active;
+
+    active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(show_trip_checkbutton));
+    buf[0] = gkrellm_gtk_entry_get_text(&label_entry);
+    buf[1] = gkrellm_gtk_entry_get_text(&url_entry);
+    buf[2] = active ? "yes" : "no";
+    buf[3] = gkrellm_gtk_entry_get_text(&updatefreq_spin);
+    if ((strlen(buf[0]) == 0) || (strlen(buf[1]) == 0))
+	return;
+
+    if (selected_row >= 0) {
+	gtk_clist_set_text(GTK_CLIST(multiping_clist), selected_row, 0,
+			   buf[0]);
+	gtk_clist_set_text(GTK_CLIST(multiping_clist), selected_row, 1,
+			   buf[1]);
+	gtk_clist_set_text(GTK_CLIST(multiping_clist), selected_row, 2,
+			   buf[2]);
+	gtk_clist_set_text(GTK_CLIST(multiping_clist), selected_row, 3,
+			   buf[3]);
+	gtk_clist_unselect_row(GTK_CLIST(multiping_clist), selected_row,
+			       0);
+	selected_row = -1;
+    } else
+	gtk_clist_append(GTK_CLIST(multiping_clist), buf);
+
+    reset_entries();
+    list_modified = TRUE;
+
+    return;
+}
+
+
+static void cb_delete(GtkWidget * widget, gpointer data)
+{
+    reset_entries();
+
+    if (selected_row >= 0) {
+	gtk_clist_remove(GTK_CLIST(multiping_clist), selected_row);
+	list_modified = TRUE;
+	selected_row = -1;
+    }
+
+    return;
+}
+
+static gchar plugin_about_text[] =
+    "GKrellM Multiping version " VERSION "\n\n\n"
+    "Copyright (C) 2002 by Jindrich Makovicka\n"
+    "makovick@kmlinux.fjfi.cvut.cz\n"
+    "Released under the GPL.\n"
+    "Portions (C) 2001 Tilman \"Trillian\" Sauerbeck";
+
+static void create_plugin_config(GtkWidget * tab_vbox)
+{
+    GtkWidget *tabs;
+    GtkWidget *vbox;
+    GtkWidget *hbox;
+    GtkWidget *hbox2;
+    GtkWidget *label;
+    GtkWidget *scrolled;
+    GtkWidget *button;
+    GtkWidget *arrow;
+    GtkAdjustment *spin_adjust;
+    GList *list;
+    GtkWidget *info_label;
+    gchar *buf[2];
+    gchar *titles[4] = { "Label", "Hostname / IP Address", "Trip", "Ping Freq" };
+    gshort i;
+    host_data *nt;
+
+    list_modified = FALSE;
+    selected_row = -1;
+
+/* Make a couple of tabs */
+    tabs = gtk_notebook_new();
+    gtk_notebook_set_tab_pos(GTK_NOTEBOOK(tabs), GTK_POS_TOP);
+    gtk_box_pack_start(GTK_BOX(tab_vbox), tabs, TRUE, TRUE, 0);
+
+/* Sources tab */
+    vbox = gkrellm_gtk_framed_notebook_page(tabs, "Hosts");
+
+    hbox = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 2);
+    label = gtk_label_new("Label:");
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 2);
+    label_entry = gtk_entry_new();
+    gtk_entry_set_max_length(GTK_ENTRY(label_entry), 25);
+//    gtk_widget_set_usize(label_entry, 180, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), label_entry, FALSE, TRUE, 0);
+
+    hbox = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 2);
+    label = gtk_label_new("Hostname / IP Address:");
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 2);
+    url_entry = gtk_entry_new();
+    gtk_entry_set_max_length(GTK_ENTRY(url_entry), 75);
+    gtk_widget_set_usize(url_entry, 475, 0);
+    gtk_box_pack_start(GTK_BOX(hbox), url_entry, FALSE, TRUE, 2);
+
+    hbox = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 5);
+    label = gtk_label_new("Ping Frequency:");
+    gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
+    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 2);
+    spin_adjust = (GtkAdjustment *) gtk_adjustment_new(0,10,100,1.0,0,0);
+    updatefreq_spin = gtk_spin_button_new(spin_adjust,1.0,0);
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(updatefreq_spin),60);
+    gtk_box_pack_start(GTK_BOX(hbox), updatefreq_spin, FALSE, TRUE, 0);
+    show_trip_checkbutton = gtk_check_button_new_with_label("Display trip time");
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_trip_checkbutton),TRUE);
+    gtk_box_pack_start(GTK_BOX(hbox), show_trip_checkbutton, FALSE, TRUE, 0);
+
+    hbox = gtk_hbox_new(TRUE, 100);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 5);
+
+    hbox2 = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox2), GTK_BUTTONBOX_START);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(hbox2), 5);
+    gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 5);
+
+    button = gtk_button_new_with_label("Enter");
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       (GtkSignalFunc) cb_enter, NULL);
+    gtk_box_pack_start(GTK_BOX(hbox2), button, TRUE, TRUE, 0);
+
+    button = gtk_button_new_with_label("Delete");
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       (GtkSignalFunc) cb_delete, NULL);
+    gtk_box_pack_start(GTK_BOX(hbox2), button, TRUE, TRUE, 0);
+
+    hbox2 = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox2), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(hbox2), 5);
+    gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 5);
+
+    button = gtk_button_new();
+    arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_ETCHED_OUT);
+    gtk_container_add(GTK_CONTAINER(button), arrow);
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       (GtkSignalFunc) cb_up, NULL);
+    gtk_box_pack_start(GTK_BOX(hbox2), button, TRUE, TRUE, 0);
+
+    button = gtk_button_new();
+    arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_ETCHED_OUT);
+    gtk_container_add(GTK_CONTAINER(button), arrow);
+    gtk_signal_connect(GTK_OBJECT(button), "clicked",
+		       (GtkSignalFunc) cb_down, NULL);
+    gtk_box_pack_start(GTK_BOX(hbox2), button, TRUE, TRUE, 0);
+
+    scrolled = gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+				   GTK_POLICY_AUTOMATIC,
+				   GTK_POLICY_AUTOMATIC);
+    gtk_box_pack_start(GTK_BOX(vbox), scrolled, TRUE, TRUE, 0);
+
+    multiping_clist = gtk_clist_new_with_titles(4, titles);
+    gtk_clist_set_shadow_type(GTK_CLIST(multiping_clist), GTK_SHADOW_OUT);
+    gtk_clist_column_titles_passive(GTK_CLIST(multiping_clist));
+    gtk_clist_set_column_justification(GTK_CLIST(multiping_clist), 0,
+				       GTK_JUSTIFY_LEFT);
+    gtk_clist_set_column_justification(GTK_CLIST(multiping_clist), 1,
+				       GTK_JUSTIFY_LEFT);
+
+    gtk_clist_set_column_width(GTK_CLIST(multiping_clist), 0, 100);
+    gtk_clist_set_column_width(GTK_CLIST(multiping_clist), 1, 200);
+
+    gtk_signal_connect(GTK_OBJECT(multiping_clist), "select_row",
+		       (GtkSignalFunc) cb_selected, NULL);
+    gtk_signal_connect(GTK_OBJECT(multiping_clist), "unselect_row",
+		       (GtkSignalFunc) cb_unselected, NULL);
+    gtk_container_add(GTK_CONTAINER(scrolled), multiping_clist);
+
+    for (i = 0, list = hosts; list; i++, list = list->next) {
+	nt = (host_data *) list->data;
+	buf[0] = nt->name->str;
+	buf[1] = nt->ip->str;
+	buf[2] = nt->show_trip ? "yes" : "no";
+	buf[3] = nt->updatefreq->str;
+	gtk_clist_append(GTK_CLIST(multiping_clist), buf);
+	gtk_clist_set_row_data(GTK_CLIST(multiping_clist), i, nt);
+    }
+
+/* Setup */
+    //  vbox = gkrellm_create_framed_tab(tabs, "Setup");
+
+/* Info tab */
+//    vbox = gkrellm_create_framed_tab(tabs, "Info");
+    //    text = gkrellm_scrolled_text(vbox, NULL, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+    //    for (i = 0; i < sizeof(plugin_info_text)/sizeof(gchar *); i++)
+    //  gkrellm_add_info_text_string(text, plugin_info_text[i]);
+
+/* About tab */
+    info_label = gtk_label_new(plugin_about_text);
+    label = gtk_label_new("About");
+    gtk_notebook_append_page(GTK_NOTEBOOK(tabs), info_label, label);
+}
+
+static void apply_plugin_config()
+{
+    gshort row;
+    gchar *s1, *s2, *s3, *s4;
+    GList *new_hosts;
+
+    if (list_modified) {
+	kill_pinger();
+
+	new_hosts = NULL;
+
+	for (row = 0; row < GTK_CLIST(multiping_clist)->rows; row++) {
+	    gtk_clist_get_text(GTK_CLIST(multiping_clist), row, 0, &s1);
+	    gtk_clist_get_text(GTK_CLIST(multiping_clist), row, 1, &s2);
+	    gtk_clist_get_text(GTK_CLIST(multiping_clist), row, 2, &s3);
+	    gtk_clist_get_text(GTK_CLIST(multiping_clist), row, 3, &s4);
+	    new_hosts = append_host(new_hosts, s1, s2, strcmp(s3, "yes") == 0,s4);
+	}
+
+	g_list_foreach(hosts, (GFunc) host_free, NULL);
+	g_list_free(hosts);
+	gkrellm_panel_destroy(panel);
+	panel = gkrellm_panel_new0();
+
+	hosts = new_hosts;
+	setup_display(1);
+
+	list_modified = FALSE;
+
+	launch_pipe();
+    }
+}
+
+static void save_plugin_config(FILE * f)
+{
+    gchar *label;
+    gchar *pt;
+    GList *list;
+    host_data *h;
+
+    for (list = hosts; list; list = list->next) {
+	h = (host_data *) list->data;
+
+	//when saving, we convert spaces in labels to underscores
+	label = g_strdup(h->name->str);
+	for (pt = label; *pt; pt++)
+	    if (*pt == ' ')
+		*pt = '_';
+
+	fprintf(f, "multiping host %s %s %d %s\n", label, h->ip->str, h->show_trip,
+         h->updatefreq->str);
+	g_free(label);
+    }
+}
+
+static void load_plugin_config(gchar * arg)
+{
+    gchar plugin_config[64];
+    gchar item[256];
+    gchar label[26];
+    gchar ip[76];
+    gchar updatefreq[4];
+    gchar *pt;
+    gshort n;
+    gboolean show_trip;
+
+    n = sscanf(arg, "%s %[^\n]", plugin_config, item);
+
+    if (n == 2) {
+	if (!strcmp(plugin_config, "host")) {
+	    if (delete_list) {
+		g_list_foreach(hosts, (GFunc) host_free, NULL);
+		g_list_free(hosts);
+		delete_list = FALSE;
+	    }
+
+	    label[0] = '\0';
+	    ip[0] = '\0';
+	    updatefreq[0] = '\0';
+	    sscanf(item, "%25s %75s %d %3s", label, ip, &show_trip, updatefreq);
+
+	    //when loading, we convert underscores in labels to spaces
+	    for (pt = label; *pt; pt++)
+		if (*pt == '_')
+		    *pt = ' ';
+
+	    hosts = append_host(hosts, label, ip, show_trip, updatefreq);
+	}
+    }
+}
+
+/* The monitor structure tells GKrellM how to call the plugin routines.
+*/
+static GkrellmMonitor plugin_mon = {
+    CONFIG_NAME,		/* Name, for config tab.    */
+    0,				/* Id,  0 if a plugin       */
+    create_plugin,		/* The create function      */
+    update_plugin,		/* The update function      */
+    create_plugin_config,	/* The config tab create function   */
+    apply_plugin_config,	/* Apply the config function        */
+
+    save_plugin_config,		/* Save user config */
+    load_plugin_config,		/* Load user config */
+    "multiping",		/* config keyword                       */
+
+    NULL,			/* Undefined 2  */
+    NULL,			/* Undefined 1  */
+    NULL,			/* private              */
+
+    MON_MAIL,			/* Insert plugin before this monitor                    */
+
+    NULL,			/* Handle if a plugin, filled in by GKrellM     */
+    NULL			/* path if a plugin, filled in by GKrellM       */
+};
+
+GkrellmMonitor *gkrellm_init_plugin()
+{
+    list_modified = FALSE;
+    delete_list = TRUE;
+    style_id = gkrellm_add_meter_style(&plugin_mon, STYLE_NAME);
+    monitor = &plugin_mon;
+    return &plugin_mon;
+}

+ 796 - 0
pinger.c

@@ -0,0 +1,796 @@
+/*____________________________________________________________________________
+        
+        pinger - gkrellm multiping helper app
+
+        Copyright (C) 2002 Jindrich Makovicka
+
+        This program is free software; you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation; either version 2 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful,
+        but WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+        GNU General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with this program; if not, Write to the Free Software
+        Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+____________________________________________________________________________*/
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <pthread.h>
+#include <glib.h>
+
+#define STORM_PHASE 0
+#define STANDBY_PHASE 1
+
+#define	DEFDATALEN	(64 - 8)	/* default data length */
+#define	MAXIPLEN	60
+#define	MAXICMPLEN	76
+#define	MAXPACKET	(65536 - 60 - 8)	/* max packet size */
+
+#define	MAX_DUP_CHK	(8 * 128)
+
+#define	A(bit)		h->rcvd_tbl[(bit)>>3]	/* identify byte in array */
+#define	B(bit)		(1 << ((bit) & 0x07))	/* identify bit in byte */
+#define	SET(bit)	(A(bit) |= B(bit))
+#define	CLR(bit)	(A(bit) &= (~B(bit)))
+#define	TST(bit)	(A(bit) & B(bit))
+
+int icmp_socket;
+static int ident;		/* process id to identify our packets */
+static int datalen = DEFDATALEN;
+static long ntransmitted;	/* sequence # for outbound packets = #sent */
+u_char outpack[MAXPACKET];
+u_char packet[DEFDATALEN + MAXIPLEN + MAXICMPLEN];
+int packlen = DEFDATALEN + MAXIPLEN + MAXICMPLEN;
+
+int hostcnt = 0;
+
+pthread_t receiver;
+int terminate = 0;
+int has_pinged;
+
+typedef struct _host_data {
+    pthread_mutex_t mutex;
+
+    int nhost;			// cislo poce
+    GString *percentage, *sent_str, *recv_str, *msg, *shortmsg;
+    struct sockaddr addr;
+    int sent, recv, rep;
+    int tmp_sent, tmp_recv, tmp_rep;
+    int dupflag, error_flag;
+    long tsum, tmp_tsum;
+    struct icmp icp;
+    char rcvd_tbl[MAX_DUP_CHK / 8];
+    int phase;
+    int counter;
+    int updatefreq;
+    int delay;
+} host_data;
+
+GList *hosts;
+
+void update_host_stats(host_data * h);
+void update_host_packinfo(host_data * h);
+
+static host_data *host_malloc()
+{
+    host_data *h = (host_data *) g_malloc(sizeof(host_data));
+    h->percentage = g_string_new(NULL);
+    h->sent_str = g_string_new(NULL);
+    h->recv_str = g_string_new(NULL);
+    h->msg = g_string_new(NULL);
+    h->shortmsg = g_string_new(NULL);
+    return h;
+}
+
+static void host_free(host_data * h)
+{
+    g_string_free(h->percentage, TRUE);
+    g_string_free(h->sent_str, TRUE);
+    g_string_free(h->recv_str, TRUE);
+    g_string_free(h->msg, TRUE);
+    g_string_free(h->shortmsg, TRUE);
+    pthread_mutex_destroy(&h->mutex);
+    g_free(h);
+}
+
+static gint compare_nhost(gconstpointer a, gconstpointer b)
+{
+    return ((host_data *) a)->nhost - *(int *) b;
+}
+
+static gint compare_nhost2(gconstpointer a, gconstpointer b)
+{
+    return ((host_data *) a)->nhost - ((host_data *) b)->nhost;
+}
+
+static gint compare_delay(gconstpointer a, gconstpointer b)
+{
+    return ((host_data *) b)->delay - ((host_data *) a)->delay;
+}
+
+/*
+ * in_cksum --
+ *	Checksum routine for Internet Protocol family headers (C Version)
+ */
+static int in_cksum(u_short * addr, int len)
+{
+    int nleft = len;
+    u_short *w = addr;
+    int sum = 0;
+    u_short answer = 0;
+
+    /*
+     * Our algorithm is simple, using a 32 bit accumulator (sum), we add
+     * sequential 16 bit words to it, and at the end, fold back all the
+     * carry bits from the top 16 bits into the lower 16 bits.
+     */
+    while (nleft > 1) {
+	sum += *w++;
+	nleft -= 2;
+    }
+
+    /* mop up an odd byte, if necessary */
+    if (nleft == 1) {
+	*(u_char *) (&answer) = *(u_char *) w;
+	sum += answer;
+    }
+
+    /* add back carry outs from top 16 bits to low 16 bits */
+    sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
+    sum += (sum >> 16);		/* add carry */
+    answer = ~sum;		/* truncate to 16 bits */
+    return (answer);
+}
+
+static void write_result(host_data * h, gchar * msg, gchar * shortmsg)
+{
+    g_string_assign(h->msg, msg);
+    g_string_assign(h->shortmsg, shortmsg);
+}
+
+/*
+ * pinger --
+ * 	Compose and transmit an ICMP ECHO REQUEST packet.  The IP packet
+ * will be added on by the kernel.  The ID field is our UNIX process ID,
+ * and the sequence number is an ascending integer.  The first 8 bytes
+ * of the data portion are used to hold a UNIX "timeval" struct in VAX
+ * byte-order, to compute the round-trip time.
+ *
+ * Another 4 bytes are the index of the host being pinged -JM
+ */
+static void pinger(host_data * h)
+{
+    struct icmp *icp;
+    int cc;
+    int i;
+
+    has_pinged = 1;
+
+//    fprintf(stderr, "pinger: pinging host No. %d\n", h->nhost);
+
+    icp = (struct icmp *) outpack;
+    icp->icmp_type = ICMP_ECHO;
+    icp->icmp_code = 0;
+    icp->icmp_cksum = 0;
+    icp->icmp_seq = ntransmitted++;
+    icp->icmp_id = ident;	/* ID */
+
+    h->sent++;
+    h->tmp_sent++;
+
+    CLR(icp->icmp_seq % MAX_DUP_CHK);
+
+    (void) gettimeofday((struct timeval *) &outpack[8],
+			(struct timezone *) NULL);
+
+    *(int *) &outpack[8 + sizeof(struct timeval)] = h->nhost;
+
+    cc = datalen + 8;		/* skips ICMP portion */
+
+    /* compute ICMP checksum here */
+    icp->icmp_cksum = in_cksum((u_short *) icp, cc);
+
+    i = sendto(icmp_socket, (char *) outpack, cc, 0, &h->addr,
+	       sizeof(struct sockaddr));
+
+    if (i < 0 || i != cc) {
+	write_result(h, "Error sending packet", "Err");
+    }
+}
+
+/*
+ * pr_icmph --
+ *	Print a descriptive string about an ICMP header.
+ */
+static gchar *pr_icmph(struct icmp *icp)
+{
+    GString *s = g_string_new(NULL);
+    gchar *c;
+
+    switch (icp->icmp_type) {
+    case ICMP_ECHOREPLY:
+	g_string_assign(s, "Echo Reply");
+	/* XXX ID + Seq + Data */
+	break;
+    case ICMP_DEST_UNREACH:
+	switch (icp->icmp_code) {
+	case ICMP_NET_UNREACH:
+	    g_string_assign(s, "Destination Net Unreachable");
+	    break;
+	case ICMP_HOST_UNREACH:
+	    g_string_assign(s, "Destination Host Unreachable");
+	    break;
+	case ICMP_PROT_UNREACH:
+	    g_string_assign(s, "Destination Protocol Unreachable");
+	    break;
+	case ICMP_PORT_UNREACH:
+	    g_string_assign(s, "Destination Port Unreachable");
+	    break;
+	case ICMP_FRAG_NEEDED:
+	    g_string_assign(s, "Frag needed and DF set");
+	    break;
+	case ICMP_SR_FAILED:
+	    g_string_assign(s, "Source Route Failed");
+	    break;
+	case ICMP_NET_UNKNOWN:
+	    g_string_assign(s, "Network Unknown");
+	    break;
+	case ICMP_HOST_UNKNOWN:
+	    g_string_assign(s, "Host Unknown");
+	    break;
+	case ICMP_HOST_ISOLATED:
+	    g_string_assign(s, "Host Isolated");
+	    break;
+	case ICMP_NET_UNR_TOS:
+	    g_string_assign(s,
+			    "Destination Network Unreachable At This TOS");
+	    break;
+	case ICMP_HOST_UNR_TOS:
+	    g_string_assign(s, "Destination Host Unreachable At This TOS");
+	    break;
+#ifdef ICMP_PKT_FILTERED
+	case ICMP_PKT_FILTERED:
+	    g_string_assign(s, "Packet Filtered");
+	    break;
+#endif
+#ifdef ICMP_PREC_VIOLATION
+	case ICMP_PREC_VIOLATION:
+	    g_string_assign(s, "Precedence Violation");
+	    break;
+#endif
+#ifdef ICMP_PREC_CUTOFF
+	case ICMP_PREC_CUTOFF:
+	    g_string_assign(s, "Precedence Cutoff");
+	    break;
+#endif
+	default:
+	    g_string_sprintf(s, "Dest Unreachable, Unknown Code: %d",
+			     icp->icmp_code);
+	    break;
+	}
+	break;
+    case ICMP_SOURCE_QUENCH:
+	g_string_assign(s, "Source Quench");
+	break;
+    case ICMP_REDIRECT:
+	switch (icp->icmp_code) {
+	case ICMP_REDIR_NET:
+	    g_string_assign(s, "Redirect Network");
+	    break;
+	case ICMP_REDIR_HOST:
+	    g_string_assign(s, "Redirect Host");
+	    break;
+	case ICMP_REDIR_NETTOS:
+	    g_string_assign(s, "Redirect Type of Service and Network");
+	    break;
+	case ICMP_REDIR_HOSTTOS:
+	    g_string_assign(s, "Redirect Type of Service and Host");
+	    break;
+	default:
+	    g_string_sprintf(s, "Redirect, Bad Code: %d", icp->icmp_code);
+	    break;
+	}
+	g_string_sprintfa(s, " (New addr: %s)",
+			  inet_ntoa(icp->icmp_gwaddr));
+	break;
+    case ICMP_ECHO:
+	g_string_assign(s, "Echo Request");
+	/* XXX ID + Seq + Data */
+	break;
+    case ICMP_TIME_EXCEEDED:
+	switch (icp->icmp_code) {
+	case ICMP_EXC_TTL:
+	    g_string_assign(s, "Time to live exceeded");
+	    break;
+	case ICMP_EXC_FRAGTIME:
+	    g_string_assign(s, "Frag reassembly time exceeded");
+	    break;
+	default:
+	    g_string_sprintf(s, "Time exceeded, Bad Code: %d",
+			     icp->icmp_code);
+	    break;
+	}
+	break;
+    case ICMP_PARAMETERPROB:
+	g_string_sprintf(s, "Parameter problem: IP address = %s",
+			 inet_ntoa(icp->icmp_gwaddr));
+	break;
+    case ICMP_TIMESTAMP:
+	g_string_assign(s, "Timestamp");
+	/* XXX ID + Seq + 3 timestamps */
+	break;
+    case ICMP_TIMESTAMPREPLY:
+	g_string_assign(s, "Timestamp Reply");
+	/* XXX ID + Seq + 3 timestamps */
+	break;
+    case ICMP_INFO_REQUEST:
+	g_string_assign(s, "Information Request");
+	/* XXX ID + Seq */
+	break;
+    case ICMP_INFO_REPLY:
+	g_string_assign(s, "Information Reply");
+	/* XXX ID + Seq */
+	break;
+#ifdef ICMP_MASKREQ
+    case ICMP_MASKREQ:
+	g_string_assign(s, "Address Mask Request");
+	break;
+#endif
+#ifdef ICMP_MASKREPLY
+    case ICMP_MASKREPLY:
+	g_string_assign(s, "Address Mask Reply");
+	break;
+#endif
+    default:
+	g_string_sprintf(s, "Bad ICMP type: %d", icp->icmp_type);
+    }
+
+    c = s->str;
+    g_string_free(s, FALSE);
+    return c;
+}
+
+/*
+ * tvsub --
+ *	Subtract 2 timeval structs:  out = out - in.  Out is assumed to
+ * be >= in.
+ */
+static void tvsub(struct timeval *out, struct timeval *in)
+{
+    if ((out->tv_usec -= in->tv_usec) < 0) {
+	--out->tv_sec;
+	out->tv_usec += 1000000;
+    }
+    out->tv_sec -= in->tv_sec;
+}
+
+// process a received packet
+void pr_pack(char *buf, int cc, struct sockaddr_in *from)
+{
+    struct icmp *icp;
+    struct ip *ip;
+    struct timeval tv, *tp;
+    long triptime = 0;
+    int hlen;
+    host_data *h;
+
+    (void) gettimeofday(&tv, (struct timezone *) NULL);
+
+    /* Check the IP header */
+    ip = (struct ip *) buf;
+    hlen = ip->ip_hl << 2;
+    if (cc < datalen + ICMP_MINLEN)
+	return;
+
+    /* Now the ICMP part */
+    cc -= hlen;
+    icp = (struct icmp *) (buf + hlen);
+
+    if (icp->icmp_type == ICMP_ECHOREPLY) {
+	if (icp->icmp_id != ident)
+	    return;		/* 'Twas not our ECHO */
+
+	h = (host_data *) g_list_find_custom(hosts,
+					     (int *) &icp->
+					     icmp_data[sizeof
+						       (struct timeval)],
+					     compare_nhost)->data;
+
+	pthread_mutex_lock(&h->mutex);
+
+	++h->recv;
+	++h->tmp_recv;
+
+	tp = (struct timeval *) icp->icmp_data;
+	tvsub(&tv, tp);
+	triptime = tv.tv_sec * 1000000 + tv.tv_usec;
+	h->tsum += triptime;
+	h->tmp_tsum += triptime;
+
+	if (TST(icp->icmp_seq % MAX_DUP_CHK)) {
+	    ++h->rep;
+	    ++h->tmp_rep;
+	    --h->recv;
+	    --h->tmp_recv;
+	    h->dupflag = 1;
+	} else {
+	    SET(icp->icmp_seq % MAX_DUP_CHK);
+	    h->dupflag = 0;
+	}
+	pthread_mutex_unlock(&h->mutex);
+    } else {
+	switch (icp->icmp_type) {
+	case ICMP_ECHO:
+	    return;
+	case ICMP_SOURCE_QUENCH:
+	case ICMP_REDIRECT:
+	case ICMP_DEST_UNREACH:
+	case ICMP_TIME_EXCEEDED:
+	case ICMP_PARAMETERPROB:
+	    {
+		struct ip *iph = (struct ip *) (&icp->icmp_data);
+		struct icmp *icp1 =
+		    (struct icmp *) ((unsigned char *) iph +
+				     iph->ip_hl * 4);
+		int error_pkt;
+
+		if (icp1->icmp_type != ICMP_ECHO ||
+		    iph->ip_src.s_addr != ip->ip_dst.s_addr ||
+		    icp1->icmp_id != ident)
+		    return;
+		error_pkt = (icp->icmp_type != ICMP_REDIRECT &&
+			     icp->icmp_type != ICMP_SOURCE_QUENCH);
+
+		h = (host_data *) g_list_find_custom(hosts,
+						     (int *) &icp1->
+						     icmp_data[sizeof
+							       (struct
+								timeval)],
+						     compare_nhost)->data;
+		if (h) {
+		    pthread_mutex_lock(&h->mutex);
+		    h->icp = *icp;
+		    h->error_flag = 1;
+		    pthread_mutex_unlock(&h->mutex);
+		}
+
+	    }
+	}
+    }
+}
+
+void clear_tmp_flags(host_data * h)
+{
+    h->tmp_tsum = 0;
+    h->tmp_sent = 0;
+    h->tmp_recv = 0;
+    h->tmp_rep = 0;
+    h->dupflag = 0;
+    h->error_flag = 0;
+}
+
+void dump_host(host_data * h)
+{
+//    fprintf(stderr, "dump_host: %d\n", h->nhost);
+    printf("%s\n", h->percentage->str);
+    printf("%s\n", h->sent_str->str);
+    printf("%s\n", h->recv_str->str);
+    printf("%s\n", h->msg->str);
+    printf("%s\n", h->shortmsg->str);
+}
+
+void ping_host(host_data * h)
+{
+    gchar *msg;
+
+    pthread_mutex_lock(&h->mutex);
+
+    if (h->error_flag) {
+	msg = pr_icmph(&h->icp);
+	write_result(h, msg, "Err");
+	g_free(msg);
+    }
+
+//    fprintf(stderr, "pinger: ping_host, No. %d, delay = %d\n", h->nhost, h->delay);
+
+    switch (h->phase) {
+    case STORM_PHASE:
+	if (h->counter == 7 || h->counter == 15 || h->counter == 23) {
+	    update_host_stats(h);
+	    clear_tmp_flags(h);
+	}
+	if ((h->counter >= 0 && h->counter < 4)
+	    || (h->counter >= 8 && h->counter < 12)
+	    || (h->counter >= 16 && h->counter < 20)) {
+	    if (has_pinged) {
+		h->delay++;
+		goto dontpingyet;
+	    }
+	    pinger(h);
+	    h->delay = 0;
+	}
+	
+	if (h->counter == 59) {
+	    h->counter = -1;
+	    h->phase = STANDBY_PHASE;
+	}
+	break;
+    case STANDBY_PHASE:
+	if (h->counter == 7) {
+	    update_host_stats(h);
+	    clear_tmp_flags(h);
+	}
+	if (h->counter >= 0 && h->counter < 4) {
+	    if (has_pinged) {
+		h->delay++;
+		goto dontpingyet;
+	    }
+	    pinger(h);
+	    h->delay = 0;
+	}
+	if (h->counter == h->updatefreq) {
+	    h->counter = -1;
+	    h->phase = STANDBY_PHASE;
+	}
+	break;
+    }
+
+    h->counter++;
+dontpingyet:
+    update_host_packinfo(h);
+    pthread_mutex_unlock(&h->mutex);
+}
+
+void *receiver_routine(void *data)
+{
+    int cc;
+    struct sockaddr_in from;
+    size_t fromlen;
+    struct timeval tv;
+    fd_set rfds;
+    int avail;
+
+    FD_ZERO(&rfds);
+    FD_SET(icmp_socket, &rfds);
+
+    tv.tv_usec = 500000;
+    tv.tv_sec = 0;
+
+    fromlen = sizeof(from);
+    for (; !terminate;) {
+	FD_ZERO(&rfds);
+	FD_SET(icmp_socket, &rfds);
+
+	tv.tv_usec = 500000;
+	tv.tv_sec = 0;
+
+	avail = select(icmp_socket + 1, &rfds, NULL, NULL, &tv);
+
+	if (!avail)
+	    continue;
+
+	if ((cc = recvfrom(icmp_socket, (char *) packet, packlen, 0,
+			   (struct sockaddr *) &from, &fromlen)) < 0) {
+	    perror("ping: recvfrom");
+	} else {
+	    pr_pack((char *) packet, cc, &from);
+	}
+    }
+    return NULL;
+}
+
+gint timeout_callback()
+{
+    static int first = 1;
+
+    if (first) {
+	pthread_create(&receiver, NULL, receiver_routine, NULL);
+	first = 0;
+    }
+
+    has_pinged = 0;
+
+    hosts = g_list_sort(hosts, compare_delay);
+//    fprintf(stderr, "pinger: =========================================\n");
+    g_list_foreach(hosts, (GFunc) ping_host, NULL);
+    hosts = g_list_sort(hosts, compare_nhost2);
+    g_list_foreach(hosts, (GFunc) dump_host, NULL);
+    fflush(stdout);
+    return TRUE;
+}
+
+
+void update_host_packinfo(host_data * h)
+{
+    g_string_sprintf(h->sent_str, "%d", h->sent);
+    g_string_sprintf(h->recv_str, "%d", h->recv);
+}
+
+void update_host_stats(host_data * h)
+{
+    long trip;
+    GString *s = g_string_new(NULL);
+    GString *s2 = g_string_new(NULL);
+
+    if (h->tmp_sent > 0) {
+	g_string_sprintf(s, "%d", h->tmp_recv * 100 / h->tmp_sent);
+	g_string_assign(h->percentage, s->str);
+    } else {
+	g_string_assign(h->percentage, "");
+    }
+
+    if (h->tmp_recv > 0) {
+	trip = h->tmp_tsum / (h->tmp_recv + h->tmp_rep);
+	if (trip >= 1000000) {
+	    g_string_sprintf(s, "%ld.%03ld s", trip / 1000000,
+			     (trip % 1000000) / 1000);
+	    g_string_sprintf(s2, ">s");
+	} else if (trip >= 10000) {
+	    g_string_sprintf(s, "%ld.%03ld ms", trip / 1000, trip % 1000);
+	    g_string_sprintf(s2, "%ld", trip / 1000);
+	} else if (trip >= 1000) {
+	    g_string_sprintf(s, "%ld.%03ld ms", trip / 1000, trip % 1000);
+	    g_string_sprintf(s2, "%ld.%01ld", trip / 1000,
+			     (trip % 1000) / 100);
+	} else {
+	    g_string_sprintf(s, "0.%01ld ms", trip / 100);
+	    g_string_sprintf(s2, "0.%01ld", trip / 100);
+	}
+	write_result(h, s->str, s2->str);
+    }
+
+    g_string_free(s, TRUE);
+    g_string_free(s2, TRUE);
+
+    if (!h->error_flag) {
+	if (h->tmp_recv == 0)
+	    write_result(h, "Request timed out", "TO");
+    }
+
+}
+
+
+void append_host(struct in_addr ip, char * updatefreq)
+{
+    host_data *h = host_malloc();
+
+    ((struct sockaddr_in *) &h->addr)->sin_addr = ip;
+    ((struct sockaddr_in *) &h->addr)->sin_family = AF_INET;
+
+    h->nhost = hostcnt++;
+
+    h->sent = 0;
+    h->recv = 0;
+    h->rep = 0;
+    h->dupflag = 0;
+    h->error_flag = 0;
+    h->tsum = 0;
+
+    h->tmp_sent = 0;
+    h->tmp_recv = 0;
+    h->tmp_rep = 0;
+    h->tmp_tsum = 0;
+
+    h->phase = STORM_PHASE;
+    h->counter = 0;
+    h->delay = 0;
+    if (updatefreq) {
+	h->updatefreq = atoi(updatefreq);
+	if (h->updatefreq < 30) {
+	    h->updatefreq = 30;
+	}
+	if (h->updatefreq > 3600) {
+	    h->updatefreq = 3600;
+	}
+    } else {
+	h->updatefreq = 59;
+    }
+
+    pthread_mutex_init(&h->mutex, NULL);
+
+    hosts = g_list_append(hosts, h);
+}
+
+void free_hosts()
+{
+    g_list_foreach(hosts, (GFunc) host_free, NULL);
+    g_list_free(hosts);
+}
+
+void term_signal(int i)
+{
+    terminate = 1;
+    pthread_join(receiver, NULL);
+    exit(0);
+}
+
+void pipe_signal(int i)
+{
+    terminate = 1;
+    pthread_join(receiver, NULL);\
+    free_hosts();
+    exit(0);
+}
+
+void hup_signal(int i)
+{
+    terminate = 1;
+    pthread_join(receiver, NULL);
+    free_hosts();
+    exit(0);
+}
+
+int main(int argc, char **argv)
+{
+    struct in_addr ip;
+    struct hostent *h;
+    int i;
+
+    struct protoent *proto;
+    if (!(proto = getprotobyname("icmp"))) {
+	(void) fprintf(stderr, "pinger: unknown protocol icmp.\n");
+	exit(2);
+    }
+    if ((icmp_socket = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) {
+	if (errno == EPERM) {
+	    fprintf(stderr, "pinger: must run as root\n");
+	} else
+	    perror("pinger: socket");
+	exit(2);
+    }
+
+    setuid(getuid());
+
+    fcntl(icmp_socket, F_SETFL, O_NONBLOCK);
+    ident = getpid() & 0xFFFF;
+
+    for (i = 1; i < argc; i++) {
+	h = gethostbyname(argv[i]);
+	if (h && h->h_addr_list[0]) {
+	    i++;
+	    if (i <= argc) {
+		append_host(*(struct in_addr*)h->h_addr_list[0],argv[i]);
+	    }
+	} else {
+	    memset(&ip, sizeof(ip), 0);
+	    append_host((struct in_addr)ip, 0);
+	}
+    }
+
+    signal(SIGTERM, term_signal);
+    signal(SIGPIPE, pipe_signal);
+    signal(SIGHUP, hup_signal);
+
+    for (;;) {
+	timeout_callback();
+	sleep(1);
+    }
+
+    free_hosts();
+    return 0;
+}