/* This file is part of Ping903 Copyright (C) 2020 Sergey Poznyakoff Ping903 is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. Ping903 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Ping903. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "ping903.h" #include "json.h" #include "defs.h" static char *config_file = DEFAULT_CONFIG_FILE; int verbose; enum state { RUNNING, /* Program is running */ TERMINATING, /* Program is terminating */ CHILD_TERM, /* SIGTERM has been sent to the child */ CHILD_KILL }; volatile enum state state; static void sigterm(int sig) { if (state == RUNNING) state = TERMINATING; } static void sigalrm(int sig) { if (state == CHILD_TERM) state = CHILD_KILL; } int fatal_signals[] = { SIGHUP, SIGINT, SIGQUIT, SIGTERM, 0 }; static void sentinel(void) { pid_t pid = 0; int i; struct sigaction act; pid_t child_pid = 0; int status; act.sa_flags = 0; sigemptyset(&act.sa_mask); act.sa_handler = sigalrm; sigaction(SIGALRM, &act, NULL); for (i = 0; fatal_signals[i]; i++) sigaddset(&act.sa_mask, fatal_signals[i]); act.sa_handler = sigterm; for (i = 0; fatal_signals[i]; i++) sigaction(fatal_signals[i], &act, NULL); while (1) { if (pid == 0) { if (state != RUNNING) break; pid = fork(); if (pid == -1) { error("fork: %s", strerror(errno)); break; } if (pid == 0) { ping903(); exit(0); } } if (child_pid > 0) { child_pid = 0; if (WIFEXITED(status)) { int code = WEXITSTATUS(status); if (code || verbose > 1) error("child exited with status %d", code); } else if (WIFSIGNALED(status)) { char const *coremsg = ""; #ifdef WCOREDUMP if (WCOREDUMP(status)) coremsg = " (core dumped)"; #endif error("child terminated on signal %d%s", WTERMSIG(status), coremsg); } else if (WIFSTOPPED(status)) { error("child stopped on signal %d", WSTOPSIG(status)); continue; } else { error("child terminated with unrecognized status %d", status); } /* restart the child */ pid = 0; continue; } switch (state) { case RUNNING: break; case TERMINATING: kill(pid, SIGTERM); alarm(5); state = CHILD_TERM; break; case CHILD_TERM: break; case CHILD_KILL: kill(pid, SIGKILL); return; } child_pid = wait(&status); if (child_pid == -1) { if (errno != EINTR || verbose > 1) error("wait: %s", strerror(errno)); } } } char *pidfile; void pidfile_remove(void) { if (pidfile && unlink(pidfile)) error("cannot unlink pidfile `%s': %s", pidfile, strerror(errno)); } void pidfile_create(void) { FILE *fp; if (!pidfile) return; fp = fopen(pidfile, "w"); if (!fp) { error("cannot create pidfile `%s': %s", pidfile, strerror(errno)); exit(1); } fprintf(fp, "%lu", (unsigned long) getpid()); fclose(fp); } /* Check whether pidfile exists and if so, whether its PID is still active. Exit if it is. */ void pidfile_check(void) { unsigned long pid; FILE *fp; if (!pidfile) return; fp = fopen(pidfile, "r"); if (fp) { if (fscanf(fp, "%lu", &pid) != 1) { error("cannot get pid from pidfile `%s'", pidfile); } else { if (kill(pid, 0) == 0) { error("%s appears to run with pid %lu. " "If it does not, remove `%s' and retry.", progname, pid, pidfile); exit(1); } } fclose(fp); if (unlink(pidfile)) { error("cannot unlink pidfile `%s': %s", pidfile, strerror(errno)); exit(1); } } else if (errno != ENOENT) { error("cannot open pidfile `%s': %s", pidfile, strerror(errno)); exit(1); } } void usage(void) { printf("Usage: %s [-fhsVv] [-c FILE]\n", progname); printf("High-performance ICMP monitoring daemon.\n"); printf("\n"); printf("Options:\n\n"); printf(" -c FILE read configuration from FILE\n"); printf(" -f remain in foreground\n"); printf(" -h print this help test\n"); printf(" -s don't start supervisor process\n"); printf(" -V print program version and exit\n"); printf(" -v additional verbosity\n"); printf("\n"); printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT); printf("%s home page: <%s>\n", PACKAGE_NAME, PACKAGE_URL); } void version(void) { printf("%s\n", PACKAGE_STRING); printf("%s", COPYLEFT); } int main(int argc, char **argv) { int c; int foreground = 0; int single_process = 0; set_progname(argv[0]); if (argc == 2) { if (strcmp(argv[1], "--help") == 0) { usage(); exit(0); } if (strcmp(argv[1], "--version") == 0) { version(); exit(0); } } while ((c = getopt(argc, argv, "c:fhsVv")) != EOF) { switch (c) { case 'c': config_file = optarg; break; case 'f': foreground = 1; break; case 'h': usage(); exit(0); case 's': single_process = 1; break; case 'v': verbose++; break; case 'V': version(); exit(0); default: exit(EX_USAGE); } } pinger_setup(); if (readconfig(config_file)) exit(EX_CONFIG); pidfile_check(); if (!foreground) { if (daemon(0, 0)) { error("daemon failed: %s", strerror(errno)); exit(EX_OSERR); } syslog_enable(); } pidfile_create(); if (single_process) ping903(); else sentinel(); pidfile_remove(); return 0; }