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
typedef int LifeSign[2];
/* LifeSign owned only by the parent.
* Watched by backends to let them exit once the parent dies
*/
static LifeSign LifeSignParent;
/* LifeSign owned by the parent and backends.
* Watched by utilities to let them exit once the parent and all backends
* have died
*/
static LifeSign LifeSignParentBackends;
static void launch();
static int backend();
static int utility();
static int lifesign(LifeSign lifesign, int wait_ms);
/* 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();
/* Close owning fd of life sign we aren't supposed to own */
if (close(LifeSignParent[LIFESIGN_FD_OWN])) {
fprintf(stderr, "Failed to close writing end of parent life sign due to %s\n", strerror(errno));
exit(1);
}
/* Wait for parent to exit */
printf("Launched backend %d\n", my_pid);
fflush(stdout);
while (lifesign(LifeSignParent, 5 * 1000)) {
printf("Backend %d detected live parent\n", my_pid);
}
printf("Backend %d exiting after parent died\n", my_pid);
fflush(stdout);
return 0;
}
/* utility()
* Wait for parent and backends to exit, then exit too
*/
static int utility()
{
int my_pid = getpid();
/* Close owning fd of life sign we aren't supposed to own */
if (close(LifeSignParent[LIFESIGN_FD_OWN])) {
fprintf(stderr, "Failed to close writing end of parent life sign due to %s\n", strerror(errno));
exit(1);
}
if (close(LifeSignParentBackends[LIFESIGN_FD_OWN])) {
fprintf(stderr, "Failed to close writing end of parent+backens life sign due to %s\n", strerror(errno));
exit(1);
}
/* Wait for parent to exit */
printf("Launched utility %d\n", my_pid);
fflush(stdout);
while (lifesign(LifeSignParentBackends, 5 * 1000)) {
printf("Utility %d detected live parent or backend\n", my_pid);
}
printf("Utility %d exiting after parent and backends died\n", my_pid);
fflush(stdout);
return 0;
}
/* lifesign()
* Waits for wait_ms seconds for all processes owning the lifesign to exit.
* Returns 0 if they did, 1 otherwise
*/
static int lifesign(LifeSign lifesign, int wait_ms)
{
fd_set set_rd;
struct timeval timeout;
int r;
FD_ZERO(&set_rd);
FD_SET(lifesign[LIFESIGN_FD_WATCH], &set_rd);
timeout.tv_sec = wait_ms / 1000;
timeout.tv_usec = (wait_ms % 1000) * 1000;
r = select(FD_SETSIZE, &set_rd, NULL, NULL, &timeout);
if (r < 0) {
fprintf(stderr, "Unable to select from life sign pipe due to %s", strerror(errno));
exit(1);
}
else if ((r == 0) || !FD_ISSET(lifesign[LIFESIGN_FD_WATCH], &set_rd)) {
/* fd is not readable */
return 1;
}
else {
/* fd is readable. Can only mean EOF since nobody ever writes to the channel */
return 0;
}
}
int main(int argc, char** argv)
{
/* Create life signs */
if (pipe(LifeSignParent)) {
fprintf(stderr, "Unable to create parent life sign pipe due to %s", strerror(errno));
return 1;
}
if (pipe(LifeSignParentBackends)) {
fprintf(stderr, "Unable to create parent+backends life sign pipe due to %s", strerror(errno));
return 1;
}
/* Launch utilities */
launch(utility);
launch(utility);
/* Launch backends */
launch(backend);
launch(backend);
launch(backend);
/* Exit after a while */
sleep(7);
fprintf(stdout, "Parent exiting\n");
fflush(stdout);
return 0;
}