aboutsummaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..3d38ec7
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,297 @@
+/* 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 <http://www.gnu.org/licenses/>.
+*/
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+#include "ping903.h"
+#include "json.h"
+
+static char *config_file = "/etc/ping903.conf";
+static int verbose;
+
+
+void
+emalloc_die(void)
+{
+ fatal("not enough memory");
+ exit(2);
+}
+
+void *
+emalloc(size_t s)
+{
+ void *p = malloc(s);
+ if (!p)
+ emalloc_die();
+ return p;
+}
+
+void *
+e2nrealloc(void *p, size_t *pn, size_t s)
+{
+ extern void *json_2nrealloc(void *p, size_t *pn, size_t s);
+ char *n = json_2nrealloc(p, pn, s);
+ if (!n)
+ emalloc_die();
+ return n;
+}
+
+char *
+estrdup(char const *s)
+{
+ return strcpy(emalloc(strlen(s) + 1), s);
+}
+
+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);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int foreground = 0;
+ int single_process = 0;
+
+ set_progname(argv[0]);
+
+ while ((c = getopt(argc, argv, "c:fsv")) != EOF) {
+ switch (c) {
+ case 'c':
+ config_file = optarg;
+ break;
+ case 'f':
+ foreground = 1;
+ break;
+ case 's':
+ single_process = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ if (readconfig(config_file))
+ exit(1);
+
+ pidfile_check();
+
+ if (!foreground) {
+ if (daemon(0, 0)) {
+ error("daemon failed: %s", strerror(errno));
+ exit(1);
+ }
+ syslog_enable();
+ }
+ pidfile_create();
+ if (single_process)
+ ping903();
+ else
+ sentinel();
+
+ pidfile_remove();
+ return 0;
+}

Return to:

Send suggestions and report system problems to the System administrator.