sslbd/certdd.cpp
#include "certdd.hpp"
#include <stdlib.h>
#include <time.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
typedef struct {
pid_t pid;
unsigned spawn_count;
time_t spawn_count_since;
} child_t;
static child_t* childs = NULL;
static int childnum = 0;
static void signal_broadcast(int sig) {
for (int i=0; i< childnum; ++i) {
if (childs[i].pid > 0) {
(void)kill(childs[i].pid, sig);
}
}
}
static pid_t spawn(int i, int argc, char** argv) {
pid_t pid = fork();
if (pid == -1) {
STDERR("cannot fork: %d - %s", errno, strerror(errno));
} else if (pid != 0) {
STDERR(" -> %d", pid);
} else {
++argv;
--argc;
static const char* certd = "./certd";
argv[0] = (char*)certd;
char suff[64];
(void)sprintf(suff, ".%d", i);
strcat(argv[argc-1], suff);
(void)execvp(argv[0], argv);
exit(1);
}
return pid;
}
int main(int argc, char** argv) {
if (argc < 3) {
STDERR("usage: %s num certd-args...", argv[0]);
STDERR(" the last argument gets appended a .#");
return 1;
}
childnum = atoi(argv[1]);
if (childnum <= 0) {
STDERR("invalid number of processes to spawn: %d", childnum);
return 1;
}
childs = (child_t*)calloc(childnum, sizeof(child_t));
static struct sigaction sa = {};
sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
sa.sa_handler = &signal_broadcast;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
STDERR("spawning %d processes...", childnum);
for (int i=0; i<childnum; ++i) {
childs[i].pid = spawn(i, argc, argv);
childs[i].spawn_count = 1;
childs[i].spawn_count_since = time(NULL);
}
while (true) {
int status;
pid_t pid = wait(&status);
if (pid == -1) {
if (errno == ECHILD) {
STDERR("no children to wait for");
break;
}
continue; // ignore other errors, i.e. EINTR
}
unsigned left = 0;
for (int i=0; i<childnum; ++i) {
if (childs[i].pid == pid) {
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
STDERR("%d exited successfully", pid);
childs[i].pid = 0;
} else {
time_t now = time(NULL);
if (childs[i].spawn_count_since < now-10) {
childs[i].spawn_count_since = now;
childs[i].spawn_count = 0;
}
if (childs[i].spawn_count >= 3) {
STDERR("%d exited w/ %d - NOT respawning", pid, status);
childs[i].pid = 0;
} else {
STDERR("%d exited w/ %d - respawning", pid, status);
childs[i].spawn_count++;
childs[i].pid = spawn(i, argc, argv);
}
}
}
if (childs[i].pid > 0) {
left++;
}
}
if (!left) {
STDERR("no children left");
break;
}
}
return 0;
}