- Improve pidfile handling

- Clean shutdown if daemon is killed
- Prevent daemon from running twice
- Minor code cleanup and refactoring
This commit is contained in:
Babak Farrokhi 2015-09-15 18:15:12 +04:30
parent 49202f073a
commit 620e51230e
2 changed files with 117 additions and 48 deletions

View File

@ -1,6 +1,6 @@
PREFIX=/usr/local PREFIX=/usr/local
INC=-I$(PREFIX)/include 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 FLAGS=-Wall -O2 -pipe -funroll-loops -ffast-math -fno-strict-aliasing -mssse3
CC?=cc CC?=cc

163
ifstatd.c
View File

@ -1,27 +1,27 @@
/* /*-
Copyright (c) 2015, Babak Farrokhi * Copyright (c) 2015, Babak Farrokhi
All rights reserved. * All rights reserved.
*
Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string.h> #include <string.h>
#include <sysexits.h> #include <sysexits.h>
#include <unistd.h> #include <unistd.h>
#include <libutil.h>
#define IFA_STAT(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s) #define IFA_STAT(s) (((struct if_data *)ifa->ifa_data)->ifi_ ## s)
#define RESOLUTION 10 #define RESOLUTION 10
@ -67,12 +68,38 @@ char *program_name = "ifstatd_";
char *interface; char *interface;
char *pid_filename; char *pid_filename;
char *cache_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). * Obtain stats for interface(s).
*/ */
static void static void fill_iftot(struct iftot *st)
fill_iftot(struct iftot *st)
{ {
struct ifaddrs *ifap, *ifa; struct ifaddrs *ifap, *ifa;
bool found = false; bool found = false;
@ -93,14 +120,9 @@ fill_iftot(struct iftot *st)
} }
st->ift_ip += IFA_STAT(ipackets); 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_ib += IFA_STAT(ibytes);
st->ift_op += IFA_STAT(opackets); 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_ob += IFA_STAT(obytes);
/*st->ift_co += IFA_STAT(collisions);*/
} }
if (interface && found == false) if (interface && found == false)
@ -109,6 +131,9 @@ fill_iftot(struct iftot *st)
freeifaddrs(ifap); freeifaddrs(ifap);
} }
/*
* Print out munin plugin configuration
*/
int config(char *iface) int config(char *iface)
{ {
printf( printf(
@ -135,6 +160,9 @@ int config(char *iface)
return(0); return(0);
} }
/*
* Wait for a certain amount of time
*/
time_t wait_for(int seconds) { time_t wait_for(int seconds) {
struct timespec tp; struct timespec tp;
@ -153,17 +181,45 @@ time_t wait_for(int seconds) {
return current_epoch + seconds; return current_epoch + seconds;
} }
int acquire() /*
* Daemonize and persist pid
*/
int daemon_start()
{ {
struct iftot ift, *tot; struct iftot ift, *tot;
time_t epoch; time_t epoch;
struct sigaction sig_action;
sigset_t sig_set;
pid_t otherpid;
tot = &ift; tot = &ift;
/* fork ourselves if not asked otherwise */
char* no_fork = getenv("no_fork"); char* no_fork = getenv("no_fork");
if (! no_fork || strcmp("1", 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 */ /* we are the child, complete the daemonization */
/* Close standard IO */ /* Close standard IO */
@ -171,31 +227,45 @@ int acquire()
fclose(stdout); fclose(stdout);
fclose(stderr); 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 */ /* create new session and process group */
setsid(); 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"); FILE *cache_file = fopen(cache_filename, "a");
/* looping to collect traffic stat every RESOLUTION seconds */ /* looping to collect traffic stat every RESOLUTION seconds */
while(1) { while(1) {
epoch=wait_for(RESOLUTION); epoch=wait_for(RESOLUTION);
flock(fileno(cache_file), LOCK_EX); flockfile(cache_file);
fill_iftot(tot); fill_iftot(tot);
fprintf(cache_file, "obytes.value %ld:%lu\nrbytes.value %ld:%lu\n", epoch, tot->ift_ob, epoch, tot->ift_ib); fprintf(cache_file, "obytes.value %ld:%lu\nrbytes.value %ld:%lu\n", epoch, tot->ift_ob, epoch, tot->ift_ib);
fflush(cache_file); fflush(cache_file);
flock(fileno(cache_file), LOCK_UN); funlockfile(cache_file);
} }
fclose(cache_file); fclose(cache_file);
@ -208,7 +278,7 @@ int fetch()
FILE* cache_file = fopen(cache_filename, "r+"); FILE* cache_file = fopen(cache_filename, "r+");
/* lock */ /* lock */
flock(fileno(cache_file), LOCK_EX); flockfile(cache_file);
/* cat the cache_file to stdout */ /* cat the cache_file to stdout */
char buffer[1024]; char buffer[1024];
@ -222,8 +292,7 @@ int fetch()
return(0); return(0);
} }
int int main(int argc, char* argv[])
main(int argc, char* argv[])
{ {
if (argv[0] && argv[0][0]) if (argv[0] && argv[0][0])
program_name = argv[0]; program_name = argv[0];
@ -260,7 +329,7 @@ main(int argc, char* argv[])
} }
if (! strcmp(first_arg, "acquire")) { if (! strcmp(first_arg, "acquire")) {
return acquire(); return daemon_start();
} }
} }