/* Copyright (C) 2000-2003 Markus Lausser (sgop@users.sf.net) This is free software distributed under the terms of the GNU Public License. See the file COPYING for details. */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ping.h" extern char **environ; typedef struct { int input; char buf[2048]; int cnt; int pos; int pid; int fd; time_t update; // last try int time; // time diff unsigned long ip_long; char *ip; } ping_t; /** @name Local module params */ /*@{*/ /** Time the ping structs are kept */ static int PingLifeTime = 60; /** Max concurrent pings allowed */ static int PingMax = 20; /** Current number of running pings */ static int PingCnt = 0; /** External ping command */ static char* PingCommand = NULL; /** List of queued pings */ static GList* PingQueue = NULL; /** List of running pings */ static GList* PingList = NULL; /** Function that is called whenever a ping terminates */ static ping_update_func UpdateFunc = NULL; /** Function that is called when having to notify anything */ static notify_func NotifyFunc = NULL; /*@}*/ void ping_configure(int life_time, int max_pings, char* command) { if (life_time) PingLifeTime = life_time; if (max_pings) PingMax = max_pings; if (command) { if (PingCommand) g_free(PingCommand); PingCommand = g_strdup(command); } } void ping_set_funcs(ping_update_func func1, notify_func func2) { UpdateFunc = func1; NotifyFunc = func2; } static char *ping_ntoa(unsigned int ip) { struct in_addr a; memset(&a, 0, sizeof(a)); a.s_addr = ip; return (inet_ntoa(a)); } static ping_t *ping_new(unsigned long ip) { ping_t *ping; time_t ctime; ping = g_malloc0(sizeof(ping_t)); ping->cnt = 0; ping->input = -1; ping->pos = 0; ping->pid = -1; ping->ip = g_strdup(ping_ntoa(ip)); time(&ctime); ping->update = ctime; ping->time = 0; ping->ip_long = ip; return ping; } static void ping_close(ping_t * ping) { int status; if (!ping) return; if (ping->pid >= 0) { waitpid(ping->pid, &status, WNOHANG); ping->pid = -1; PingCnt--; if (UpdateFunc) UpdateFunc(ping->ip_long, ping->time); } if (ping->fd >= 0) { close(ping->fd); ping->fd = -1; } if (ping->input >= 0) { gdk_input_remove(ping->input); ping->input = -1; } } static void ping_destroy(ping_t * ping) { if (!ping) return; // only pings in are destroyed ping_close(ping); PingList = g_list_remove(PingList, ping); if (ping->ip) g_free(ping->ip); g_free(ping); } void ping_inc_counter(int value) { GList* dlist; ping_t* ping; dlist = PingList; while (dlist) { ping = dlist->data; dlist = dlist->next; ping->cnt += value; if (ping->cnt > PingLifeTime) ping_destroy(ping); } } static gint get_ping_input(gpointer data, gint source, GdkInputCondition condition) { ping_t *ping = (ping_t *) data; int res; char *pos1; char *pos2; char *t1; char *t2; if ((condition & GDK_INPUT_READ) == 0) { ping_close(ping); return 0; } switch (res = read(source, ping->buf + ping->pos, 2048 - ping->pos - 1)) { case -1: ping_close(ping); return 0; case 0: ping_close(ping); return 0; default: break; } ping->buf[res + ping->pos] = 0; pos1 = ping->buf; while ((pos2 = strchr(pos1, '\n')) != NULL) { *pos2 = 0; // printf("p %s: %s\n", ping->ip, pos1); if (strstr(pos1, "/avg/")) { // printf("%s\n", pos1); t2 = strchr(pos1, '='); if (t2) t1 = strchr(t2, '/'); else t1 = NULL; if (t1) t2 = strchr(t1 + 1, '/'); else t2 = NULL; if (t1 && t2) { *t2 = 0; ping->time = atoi(t1 + 1); // printf("got ping %d\n", ping->time); } } pos1 = pos2 + 1; } strcpy(ping->buf, pos1); ping->pos = strlen(ping->buf); return 0; } static int ping_fire_up(ping_t * ping) { int p1[2], pid; int i; gchar *argv[20]; gchar *pos; char *pcom; int i1; if (!PingCommand) { g_warning("You should specify a ping command"); return 0; } p1[0] = p1[1] = -1; if (pipe(p1)) { NotifyFunc(NULL, "Unable to start new process: %s", strerror(errno)); return 0; } switch ((pid = fork())) { case -1: if (NotifyFunc) NotifyFunc(NULL, "Couldn't start new process!"); close(p1[0]); close(p1[1]); return 0; break; case 0: // child setsid(); setuid(getuid()); setgid(getgid()); dup2(p1[1], 1); // copy stdout close(p1[0]); p1[0] = -1; for (i = 3; i < 256; i++) close(i); pcom = g_strdup(PingCommand); // printf("[%s]\n", pcom); argv[0] = strtok(pcom, " \t\n"); for (i1 = 1; i1 < 18; i1++) { pos = strtok(NULL, " \t\n"); argv[i1] = pos; if (!pos) break; if (!strcasecmp(argv[i1], "$IP")) argv[i1] = ping->ip; // printf("arg: %s\n", argv[i1]); } execve(argv[0], argv, environ); _exit(-1); break; default: // parent close(p1[1]); p1[1] = -1; ping->pid = pid; PingCnt++; ping->fd = p1[0]; ping->input = gdk_input_add(ping->fd, GDK_INPUT_READ, GTK_SIGNAL_FUNC(get_ping_input), ping); break; } return 1; } static void ping_try_dequeue() { while (PingQueue && PingCnt < PingMax) { ping_t *ping = PingQueue->data; PingQueue = g_list_remove(PingQueue, ping); PingList = g_list_append(PingList, ping); if (!ping_fire_up(ping)) ping_destroy(ping); } } static void ping_queue(ping_t* ping) { PingQueue = g_list_append(PingQueue, ping); ping_try_dequeue(); } static ping_t *ping_search_list(unsigned long ip) { GList *dlist; ping_t *ping; for (dlist = PingList; dlist; dlist = dlist->next) { ping = dlist->data; if (ping->ip_long == ip) return ping; } return NULL; } static ping_t *ping_search_queue(unsigned long ip) { GList *dlist; ping_t *ping; for (dlist = PingQueue; dlist; dlist = dlist->next) { ping = dlist->data; if (ping->ip_long == ip) return ping; } return NULL; } int ping(unsigned long addr) { ping_t *ping; if ((ping = ping_search_queue(addr)) != NULL) { return -1; } else if ((ping = ping_search_list(addr)) != NULL) { if (ping->pid == -1) return ping->time; else return -1; } else { ping = ping_new(addr); ping_queue(ping); return -1; } }