#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <math.h>
#include <fcntl.h>
#include <string.h>

#define SIG_MUTEX  1 // prevent mutal interrupt
//#undef SIG_MUTEX

// Print a given set of signals
void affiche_set(sigset_t set,char *msg) {
  puts(msg);
  for (int sig=0; sig<NSIG; sig++) {
    if (sigismember(&set,sig)) { printf("c: \tSignal %s présent\n",strsignal(sig)); }
  }
}

// To be called in a signal handler.
//  Protect code from being interrupted.
//  Show signal mask installed at signal delivery
//  Show pending signals while delivery
void common() {
  sigset_t toblock, old;
  sigfillset(&toblock);
  sigprocmask(SIG_SETMASK,&toblock,&old);
  affiche_set(old,"c: Blocked signals");
  sigset_t set;
  sigpending(&set);
  affiche_set(set,"c: Pending signals");
  sigprocmask(SIG_SETMASK,&old,NULL);
}

// First handler (SIGUSR1)
void f1(int sig) {
  printf("c: In %s\n",__FUNCTION__);
  common();
  printf("c: Out %s\n",__FUNCTION__);
}

// Second handler (SIGUSR2)
void f2(int sig) {
  printf("c: In %s\n",__FUNCTION__);
  common();
  printf("c: Out %s\n",__FUNCTION__);
}

int main(int argc,char *argv[]) {
  struct sigaction action;
  struct sigaction action2;
  struct sigaction old1, old2;

  // Install catching before forking to ensure no race condition
  action.sa_handler = f1;
  sigemptyset(&(action.sa_mask));
#ifdef SIG_MUTEX
  sigaddset(&(action.sa_mask),SIGUSR2);
#endif // SIG_MUTEX
  action.sa_flags = 0;
  sigaction(SIGUSR1,&action,&old1);

  action2.sa_handler = f2;
  sigemptyset(&(action2.sa_mask));
#ifdef SIG_MUTEX
  sigaddset(&(action2.sa_mask),SIGUSR1);
#endif // SIG_MUTEX
  action2.sa_flags = 0;
  sigaction(SIGUSR2,&action2,&old2);

  pid_t pid = fork();
  printf("%d\n",pid);
  int statut;
  switch(pid) {
  case 0: // child
    while (1) {
      printf("c: Do be do be doo\n");
      sleep(1);
    }
    exit(0);
  case -1:
    fprintf(stderr,"Gosh! Can't demonstrate anything...\n");
    exit(1);
  default: // ancestor
    // restore old behavior
    sigaction(SIGUSR1,&old1,NULL);
    sigaction(SIGUSR2,&old2,NULL);
    sleep(2);
    printf("a: sending USR1 %d\n",kill(pid,SIGUSR1));
    printf("a: sending USR2 %d\n",kill(pid,SIGUSR2)); 
    sleep(10);
    kill(pid,SIGTERM);
    pid = waitpid(0,&statut,0);
    if (pid==-1) {
      fprintf(stderr,"Gosh! something's wrong\n");
      exit(1);
    }
    if (WIFEXITED(statut)) {
      printf("a: child %d exit status %d\n",pid,WEXITSTATUS(statut));
    }
    if (WIFSIGNALED(statut)) {
      printf("a: signal %d received by child %d\n",WTERMSIG(statut),pid);
    }
    break;
  }
  exit(0);
}