liveness.c
application/octet-stream
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#define LIFESIGN_FD_WATCH 0
#define LIFESIGN_FD_OWN 1
/*
* The pipe representing the life sign and the infrastructure
* which turns SIGIO into SIGTERM for processes who watch the
* life sign
*/
static int LifeSignFDs[2];
static int LifeSignSendSIGTERM = 0;
static void LifeSignSIGIOHandler(int signal)
{
if (LifeSignSendSIGTERM)
kill(getpid(), SIGTERM);
}
/*
* LifeSignCreate - Create a life sign object.
*
* The calling process and all its future child processes become owner
* of the life sign.
*/
void LifeSignInitialize()
{
int flags;
/* Create pipe. The life sign is deemed to have vanished of all
* no process has the writing end (LIFESIGN_FD_OWN) open
*/
if (pipe(LifeSignFDs)) {
fprintf(stderr, "Failed to create life sign due to %s\n", strerror(errno));
exit(1);
}
/* Set FNONBLOCK to allow checking for the life sign's presence by a simply
* read() and FASYNC to deliver a signal to our process group if the life
* sign vanishes
*/
flags = fcntl(LifeSignFDs[LIFESIGN_FD_WATCH], F_GETFL);
if (flags < 0) {
fprintf(stderr, "Failed to get the life sign's watching end's flags due to %s\n", strerror(errno));
exit(1);
}
flags |= FNONBLOCK | FASYNC;
if (fcntl(LifeSignFDs[LIFESIGN_FD_WATCH], F_SETFL, flags)) {
fprintf(stderr, "Failed to set the life sign's watching end's flags due to %s\n", strerror(errno));
exit(1);
}
/* Send SIGIO signal to the whole process group */
if (fcntl(LifeSignFDs[LIFESIGN_FD_WATCH], F_SETOWN, -getpgrp())) {
fprintf(stderr, "Failed to set the life sign's watching end's notification pid due to %s\n", strerror(errno));
exit(1);
}
/* Register signal handler */
if (signal(SIGIO, LifeSignSIGIOHandler)) {
fprintf(stderr, "Failed to register SIGIO handler %s\n", strerror(errno));
exit(1);
}
}
/*
* LifeSignPresent - Check if the life sign vanished
*
* Returns 1 if the life sign is present, 0 otherwise. Doesn't require the
* calling process to watch the life sign
*/
int LifeSignPresent()
{
int r, b;
r = read(LifeSignFDs[LIFESIGN_FD_WATCH], &b, sizeof(b));
if ((r < 0) && (errno == EAGAIN))
/* Life sign present */
return 1;
else if (r == 0)
/* EOF. Life sign vanished */
return 0;
else {
fprintf(stderr, "Unexpected state of life sign's reading end. read() returned %d\n", r);
exit(1);
}
}
/*
* LifeSignWatch - Make the calling process a watcher of the life sign
*
* Arranges for the calling process to receive a SIGTERM once the
* last owner of the life sign vanishes.
*/
void LifeSignWatch()
{
LifeSignSendSIGTERM = 1;
/* Prevent race condition */
if (!LifeSignPresent())
kill(getpid(), SIGTERM);
}
/*
* LifeSignRelease - The calling process gives up ownership of the life sign
*/
void LifeSignRelease()
{
if (close(LifeSignFDs[LIFESIGN_FD_OWN])) {
fprintf(stderr, "Failed to release life sign due to %s\n", strerror(errno));
exit(1);
}
LifeSignFDs[LIFESIGN_FD_OWN] = -1;
}
static void launch();
static int backend();
/*
* launch - Run function in new process, exit when done
*/
static void launch(int (*proc)())
{
pid_t pid;
pid = fork();
if (pid < 0) {
fprintf(stderr, "Failed to fork due to %s\n", strerror(errno));
exit(1);
}
else if (pid > 0) {
/* Parent. Do nothing */
}
else {
/* Child. */
exit(proc());
}
}
/*
* backend - Wait for parent to exit, then exit too
*/
static int backend()
{
int my_pid = getpid();
int first_run = 1;
printf("Launched backend %d\n", my_pid);
fflush(stdout);
/* Ignore SIGTERM */
if (signal(SIGTERM, SIG_IGN)) {
fprintf(stderr, "Unable to ignore SIGTERM due to %s",strerror(errno));
exit(1);
}
/* Release life sign and watch it to receive a SIGTERM if it vanishes */
LifeSignRelease();
LifeSignWatch();
/* To make the output more deterministic */
sleep(1);
/* Wait for parent to exit */
while (LifeSignPresent()) {
printf("Backend %d detected live parent\n", my_pid);
sleep(first_run ? 2: 10);
first_run = 0;
}
printf("Backend %d exiting after parent died\n", my_pid);
fflush(stdout);
return 0;
}
int main(int argc, char** argv)
{
/* Create life signs */
LifeSignInitialize();
/* Launch backends */
launch(backend);
launch(backend);
launch(backend);
/* Exit after a while */
sleep(5);
fprintf(stdout, "Parent exiting\n");
fflush(stdout);
return 0;
}