From 620e51230e0dad03fca451d5e6c036a28f819d65 Mon Sep 17 00:00:00 2001 From: Babak Farrokhi Date: Tue, 15 Sep 2015 18:15:12 +0430 Subject: [PATCH] - Improve pidfile handling - Clean shutdown if daemon is killed - Prevent daemon from running twice - Minor code cleanup and refactoring --- Makefile | 2 +- ifstatd.c | 163 ++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 117 insertions(+), 48 deletions(-) diff --git a/Makefile b/Makefile index b9e7e4f..862f713 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ PREFIX=/usr/local INC=-I$(PREFIX)/include -LIB=-L$(PREFIX)/lib +LIB=-L$(PREFIX)/lib -lutil FLAGS=-Wall -O2 -pipe -funroll-loops -ffast-math -fno-strict-aliasing -mssse3 CC?=cc diff --git a/ifstatd.c b/ifstatd.c index 1aee170..1fe90c6 100644 --- a/ifstatd.c +++ b/ifstatd.c @@ -1,27 +1,27 @@ -/* -Copyright (c) 2015, Babak Farrokhi -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/*- + * Copyright (c) 2015, Babak Farrokhi + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #define IFA_STAT(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s) #define RESOLUTION 10 @@ -67,12 +68,38 @@ char *program_name = "ifstatd_"; char *interface; char *pid_filename; char *cache_filename; +struct pidfh *pfh; + +/* + * Prepare for a clean shutdown + */ +void daemon_shutdown() +{ + pidfile_remove(pfh); +} + +/* + * Act upon receiving signals + */ +void signal_handler(int sig) +{ + switch(sig) { + + case SIGHUP: + case SIGINT: + case SIGTERM: + daemon_shutdown(); + exit(EXIT_SUCCESS); + break; + default: + break; + } +} /* * Obtain stats for interface(s). */ -static void -fill_iftot(struct iftot *st) +static void fill_iftot(struct iftot *st) { struct ifaddrs *ifap, *ifa; bool found = false; @@ -93,14 +120,9 @@ fill_iftot(struct iftot *st) } st->ift_ip += IFA_STAT(ipackets); - /*st->ift_ie += IFA_STAT(ierrors);*/ - /*st->ift_id += IFA_STAT(iqdrops);*/ st->ift_ib += IFA_STAT(ibytes); st->ift_op += IFA_STAT(opackets); - /*st->ift_oe += IFA_STAT(oerrors);*/ - /* st->ift_od += IFA_STAT(oqdrops);*/ st->ift_ob += IFA_STAT(obytes); - /*st->ift_co += IFA_STAT(collisions);*/ } if (interface && found == false) @@ -109,6 +131,9 @@ fill_iftot(struct iftot *st) freeifaddrs(ifap); } +/* + * Print out munin plugin configuration + */ int config(char *iface) { printf( @@ -135,6 +160,9 @@ int config(char *iface) return(0); } +/* + * Wait for a certain amount of time + */ time_t wait_for(int seconds) { struct timespec tp; @@ -153,17 +181,45 @@ time_t wait_for(int seconds) { return current_epoch + seconds; } -int acquire() +/* + * Daemonize and persist pid + */ +int daemon_start() { struct iftot ift, *tot; time_t epoch; + struct sigaction sig_action; + sigset_t sig_set; + pid_t otherpid; tot = &ift; - /* fork ourselves if not asked otherwise */ char* no_fork = getenv("no_fork"); if (! no_fork || strcmp("1", no_fork)) { - if (fork()) return(0); + + /* Check if parent process id is set */ + if (getppid() == 1) + { + /* PPID exists, therefore we are already a daemon */ + return(EXIT_FAILURE); + } + + /* Check if we can acquire the pid file */ + pfh = pidfile_open(pid_filename, 0600, &otherpid); + + if (pfh == NULL) { + if (errno == EEXIST) { + errx(EXIT_FAILURE, "Daemon already running, pid: %jd.", (intmax_t)otherpid); + } + warn("Cannot open or create pidfile: %s", pid_filename); + } + + /* fork ourselves if not asked otherwise */ + if (fork()) + { + return(EXIT_SUCCESS); + } + /* we are the child, complete the daemonization */ /* Close standard IO */ @@ -171,31 +227,45 @@ int acquire() fclose(stdout); fclose(stderr); + /* Block unnecessary signals */ + sigemptyset(&sig_set); + sigaddset(&sig_set, SIGCHLD); /* ignore child - i.e. we don't need to wait for it */ + sigaddset(&sig_set, SIGTSTP); /* ignore Tty stop signals */ + sigaddset(&sig_set, SIGTTOU); /* ignore Tty background writes */ + sigaddset(&sig_set, SIGTTIN); /* ignore Tty background reads */ + sigprocmask(SIG_BLOCK, &sig_set, NULL); /* Block the above specified signals */ + + /* Catch necessary signals */ + sig_action.sa_handler = signal_handler; + sigemptyset(&sig_action.sa_mask); + sig_action.sa_flags = 0; + + sigaction(SIGTERM, &sig_action, NULL); + sigaction(SIGHUP, &sig_action, NULL); + sigaction(SIGINT, &sig_action, NULL); + /* create new session and process group */ setsid(); - } + + /* persist pid */ + pidfile_write(pfh); - /* persist pid */ - FILE *pid_file = fopen(pid_filename, "w"); - fprintf(pid_file, "%d\n", getpid()); - fclose(pid_file); + } FILE *cache_file = fopen(cache_filename, "a"); - /* looping to collect traffic stat every RESOLUTION seconds */ - while(1) { epoch=wait_for(RESOLUTION); - flock(fileno(cache_file), LOCK_EX); + flockfile(cache_file); fill_iftot(tot); fprintf(cache_file, "obytes.value %ld:%lu\nrbytes.value %ld:%lu\n", epoch, tot->ift_ob, epoch, tot->ift_ib); fflush(cache_file); - flock(fileno(cache_file), LOCK_UN); + funlockfile(cache_file); } fclose(cache_file); @@ -208,7 +278,7 @@ int fetch() FILE* cache_file = fopen(cache_filename, "r+"); /* lock */ - flock(fileno(cache_file), LOCK_EX); + flockfile(cache_file); /* cat the cache_file to stdout */ char buffer[1024]; @@ -222,8 +292,7 @@ int fetch() return(0); } -int -main(int argc, char* argv[]) +int main(int argc, char* argv[]) { if (argv[0] && argv[0][0]) program_name = argv[0]; @@ -260,7 +329,7 @@ main(int argc, char* argv[]) } if (! strcmp(first_arg, "acquire")) { - return acquire(); + return daemon_start(); } }