/* An example of using signal handling to produce an orderly shutdown */

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>

#define NUMKIDS 4

void do_child(int num);
void child_term (int code);

/* global vars needed by the child's signal handler */
int child_num;
int work_index;
int block;

int
main(int argc, char *argv[])
{
  extern char *optarg;
  extern int optind;
  int ch;

  int i, pid[NUMKIDS];
  int status;

  while((ch = getopt(argc, argv, "b")) != -1) {
    switch(ch) {
    case 'b' :
      block = 1;
      break;
    default : 
      block = 0;
      break;
    }
  }

  for(i = 0; i< NUMKIDS; i++) {
    if ((pid[i] = fork()) == 0) {
      do_child(i);
    }
  }

  sleep(5);
  for(i = 0; i < NUMKIDS-1; i++) {
    kill(pid[i], SIGINT);
    sleep(2);
  }
  for(i = 0; i < NUMKIDS; i++) {
    if((wait(&status)) > 0) {
      if(WIFEXITED(status)) {
	if(WEXITSTATUS(status) == 1) {
	  printf("Child %d terminated prematurely\n", i);
	} 
	else {
	  printf("Child %d finished its work\n", i);
	}
      }
    }
  }
}

void
do_child(int num)
{
  struct sigaction newact;
  sigset_t sigset;
  sigemptyset(&newact.sa_mask);
  newact.sa_handler = child_term;
  newact.sa_flags = 0;

  if(sigaction(SIGINT, &newact, NULL) == -1)
    perror("Could not install handler for SIGINT\n");

  sigemptyset(&sigset);
  sigaddset(&sigset, SIGINT);

  child_num = num;
  for(work_index = 0; work_index < 5; work_index++) {

    if(block) {
      /* block signals while working on a "unit" of work */
      if((sigprocmask(SIG_BLOCK, &sigset, NULL)) == -1) {
	perror("sigprocmask:");
      }
    }
    fprintf(stdout, "%d: starting unit %d\n", num, work_index);
    sleep(3);  /* use the sleep call to pretend to do work */
    fprintf(stdout, "%d: finishing unit %d\n", num, work_index);

    if(block) {
      if((sigprocmask(SIG_UNBLOCK, &sigset, NULL)) == -1) {
	perror("sigprocmask:");
      }
    }

    /* unblock signals so that the process can receive a signal */
  }
  fprintf(stdout, "Child %d produced %d units of work\n", 
	  child_num, work_index);
  exit(0);
}

void
child_term(int code)
{
  fprintf(stdout, "Child %d produced %d units of work\n", 
	  child_num, work_index);
  exit(1);
}
