- 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
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

161
ifstatd.c
View File

@ -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 <sys/cdefs.h>
@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <libutil.h>
#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 */
FILE *pid_file = fopen(pid_filename, "w");
fprintf(pid_file, "%d\n", getpid());
fclose(pid_file);
pidfile_write(pfh);
}
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();
}
}