diff options
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | pingu.c | 180 | ||||
-rw-r--r-- | pingu.conf | 22 | ||||
-rw-r--r-- | xlib.c | 19 | ||||
-rw-r--r-- | xlib.h | 1 |
5 files changed, 178 insertions, 47 deletions
@@ -1,5 +1,5 @@ -TARGETS = mtu +TARGETS = mtu pingu CFLAGS ?= -g prefix = /usr @@ -10,6 +10,7 @@ INSTALL = install INSTALLDIR = $(INSTALL) -d pingu_OBJS = \ + icmp.o \ log.o \ pingu.o \ xlib.o @@ -1,20 +1,32 @@ +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> #include <sys/queue.h> +#include <sys/types.h> +#include <sys/stat.h> #include <err.h> #include <errno.h> +#include <fcntl.h> +#include <libgen.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include "icmp.h" #include "pingu.h" #include "xlib.h" -int pingu_verbose = 0; +#define DEFAULT_CONFIG "/etc/pingu.conf" +#define DEFAULT_PIDFILE "/var/run/pingu.pid" + +int pingu_verbose = 0, pid_file_fd = 0; +char *pid_file = DEFAULT_PIDFILE; +int interval = 30; struct provider { - char *router; + struct sockaddr_in address; char *name; char *interface; char *up_action; @@ -95,13 +107,14 @@ int read_config(const char *file, struct provider_list *head) parse_line(line, &key, &value); if (key == NULL) continue; -// printf("DEBUG: lineno=%i, key='%s', val='%s'\n", -// lineno, key, value); - if (strcmp(key, "router") == 0) { + if (p == NULL && strcmp(key, "interval") == 0) { + interval = atoi(value); + } else if (strcmp(key, "router") == 0) { p = xmalloc(sizeof(struct provider)); memset(p, 0, sizeof(struct provider)); - p->router = xstrdup(value); + if (init_sockaddr(&p->address, value) < 0) + return 1; p->status = 1; /* online by default */ SLIST_INSERT_HEAD(head, p, provider_list); } else if (p && strcmp(key, "interface") == 0) { @@ -121,19 +134,50 @@ int read_config(const char *file, struct provider_list *head) return 0; } -static int ping(const char *host) +int do_ping(struct sockaddr_in *to, int seq, int retries) { - char cmd[280]; - snprintf(cmd, sizeof(cmd), "ping -c 1 -q %s >/dev/null 2>&1", host); - return system(cmd); + __u8 buf[1500]; + struct iphdr *ip = (struct iphdr *) buf; + struct icmphdr *icp; + struct sockaddr_in from; + int retry; + int len = sizeof(struct iphdr) + sizeof(struct icmphdr); + int fd = icmp_open(); + + for (retry = 0; retry < retries; retry++) { + icmp_send_ping(fd, (struct sockaddr *) to, sizeof(*to), + seq, len); + + if ((len = icmp_read_reply(fd, (struct sockaddr *) &from, + sizeof(from), buf, sizeof(buf))) <= 0) + continue; + + icp = (struct icmphdr *) &buf[ip->ihl * 4]; + if (icp->type == ICMP_ECHOREPLY && icp->un.echo.id == getpid()) { + icmp_close(fd); + return 0; + } + } + icmp_close(fd); + return -1; } -void usage(int retcode) +int usage(const char *program) { - fprintf(stderr, "usage:\n"); - exit(retcode); + fprintf(stderr, "usage: %s [-dh] [-c CONFIG] [-p PIDFILE]\n" + "options:\n" + " -c Read configuration from FILE (default is " + DEFAULT_CONFIG ")\n" + " -d Debug mode. Stay in foreground\n" + " -h Show this help\n" + " -p Use PIDFILE as pidfile (default is " + DEFAULT_PIDFILE ")\n" + "\n", + program); + return 1; } +#if 0 void dump_provider(struct provider *p) { printf("router: %s\n" @@ -143,47 +187,111 @@ void dump_provider(struct provider *p) "down-action: %s\n" "p->status: %i\n" "\n", - p->router, p->name, p->interface, + inet_ntoa(p->address.sin_addr), p->name, p->interface, p->up_action, p->down_action, p->status); } +#endif + + void ping_loop(struct provider_list *head, int interval) { struct provider *p; + int seq = 0; while (1) { + seq++; SLIST_FOREACH(p, head, provider_list) { - int status = (ping(p->router) != 0); + int status = (do_ping(&p->address, seq, 3) == 0); if (status != p->status) { p->status = status; - printf("status changed for %s to %i\n", - p->router, status); + if (status) + system(p->up_action); + else + system(p->down_action); } } sleep(interval); + seq &= 0xffff; + } +} + +static void remove_pid_file(void) +{ + if (pid_file_fd != 0) { + close(pid_file_fd); + pid_file_fd = 0; + remove(pid_file); + } +} + +static int daemonize(void) +{ + char tmp[16]; + pid_t pid; + + pid = fork(); + if (pid < 0) + return -1; + if (pid > 0) + exit(0); + + if (setsid() < 0) + return -1; + + pid = fork(); + if (pid < 0) + return -1; + if (pid > 0) + exit(0); + + if (chdir("/") < 0) + return -1; + + pid_file_fd = open(pid_file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); + if (pid_file_fd < 0) { + log_error("Unable to open %s: %s.", pid_file, strerror(errno)); + return -1; + } + + if (flock(pid_file_fd, LOCK_EX | LOCK_NB) < 0) { + log_error("Unable to lock pid file (already running?)."); + close(pid_file_fd); + pid_file_fd = 0; + return -1; } + + ftruncate(pid_file_fd, 0); + write(pid_file_fd, tmp, sprintf(tmp, "%d\n", getpid())); + atexit(remove_pid_file); + + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + + umask(0); + + return 0; } int main(int argc, char *argv[]) { - int c; - char *config = "/etc/pingu.conf"; - char **hosts; - int *offline; - int hosts_count; - int interval = 30; - const char *script = NULL; + int c, debug_mode = 0; struct provider_list providers; + char *config_file = DEFAULT_CONFIG; - while ((c = getopt(argc, argv, "c:i:s:")) != -1) { + while ((c = getopt(argc, argv, "c:dhp:")) != -1) { switch (c) { case 'c': - config = optarg; + config_file = optarg; break; - case 's': - script = optarg; + case 'd': + debug_mode++; break; - case 'i': - interval = atoi(optarg); + case 'h': + return usage(basename(argv[0])); + return; + case 'p': + pid_file = optarg; break; } } @@ -192,17 +300,15 @@ int main(int argc, char *argv[]) argv += optind; SLIST_INIT(&providers); - if (read_config(config, &providers) == -1) + if (read_config(config_file, &providers) == -1) return 1; -// if (argc == 0) -// usage(EXIT_FAILURE); - - offline = xmalloc(sizeof(int) * argc); - memset(offline, 0, sizeof(int) * argc); + if (!debug_mode) { + if (daemonize() == -1) + return 1; + } ping_loop(&providers, interval); - free(hosts); return 0; } @@ -1,14 +1,18 @@ # comments are prefixed with # -interface eth0 -provider ISP-1 -pinghost 10.2.0.3 -up-action /etc/pingu/isp1 up -down-action /etc/pingu/isp1 down +# global option +interval 3 +router 10.65.67.11 interface eth1 -provider ISP-2 -pinghost 192.168.0.1 -up-action echo "isp 2 went up" >> /var/log/isp2.log -down-action echo "iso 2 went down" >> /var/log/isp2.log +provider ISP-1 +up-action echo "isp 1 went up" >> /tmp/pingu.log +down-action echo "iso 1 went down" >> /tmp/pingu.log + +#router 10.2.0.3 +#interface eth0 +#provider ISP-1 +#up-action /etc/pingu/isp1 up +#down-action /etc/pingu/isp1 down + @@ -1,4 +1,9 @@ +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> + +#include <netdb.h> #include <stdlib.h> #include <string.h> #include <unistd.h> @@ -29,3 +34,17 @@ char *xstrdup(const char *str) return s; } +int init_sockaddr(struct sockaddr_in *addr, const char *host) +{ + memset((char *) addr, 0, sizeof(struct sockaddr_in)); + addr->sin_family = AF_INET; + if (inet_aton(host, &addr->sin_addr) == 0) { + struct hostent *hp; + hp = gethostbyname(host); + if (!hp) + return -1; + memcpy(&addr->sin_addr, hp->h_addr, 4); + } + return 0; +} + @@ -5,4 +5,5 @@ void *xmalloc(size_t size); void *xrealloc(void *ptr, size_t size); char *xstrdup(const char *str); +int init_sockaddr(struct sockaddr_in *addr, const char *host); #endif |