/* signal.c - A program to showcase signal trapping */
/* Programmed for Physics 673 - Dr. D.M. Gingrich */
/* Author: R.A. Davis  November 26, 1996 */
/*                                       */
/* Features:  */
/*    - blocks correctly using sigsuspend when waiting for signal */
/*      hence is free to react to other signals and still wait. */
/*    - recovers correctly from div by zero by jumping to a non-local */
/*      error recovery point and fixing the divison */
/*                                                  */
/* Signals trapped: */
/*     1) SIGINT (terminal signal) */
/*     2) SIGFPE (hardware signal) */
/*     3) SIGCLD (software signal) */
/*     4) SIGTERM (kill(1) signal) */
/*     5) SIGQUIT (kill(2) signal  */

#include 
#include 
#include 
#include 

    jmp_buf return_to_error_recover;
    volatile sig_atomic_t int_flag = 0;
    volatile sig_atomic_t cld_flag = 0;

main(void)
{
    int a,b,c,Status,ChildId;
    long MyPID;
    void inthandler();
    void fpehandler();
    void termhandler();
    void quithandler();
    void cldhandler();
    sigset_t mask,oldmask;
    char KillCommand[] = "kill -s SIGTERM ";
    char *strPID;

    /* Set up signal handler routines with error catching */

    if (signal(SIGINT,*inthandler) == SIG_ERR) {
      printf("Error setting up signal trapping.\n");
      exit(10);
    }
    if (signal(SIGFPE,*fpehandler) == SIG_ERR) {
      printf("Error setting up signal trapping.\n");
      exit(10);
    }
    if (signal(SIGTERM,*termhandler) == SIG_ERR) {
      printf("Error setting up signal trapping.\n");
      exit(10);
    }
    if (signal(SIGQUIT,*quithandler) == SIG_ERR) {
      printf("Error setting up signal trapping.\n");
      exit(10);
    }
    if (signal(SIGCLD,*cldhandler) == SIG_ERR) {
      printf("Error setting up signal trapping.\n");
      exit(10);
    }


    /* Wait for a terminal signal - use sigsuspend to wait */

    sigemptyset(&mask);
    sigaddset(&mask,SIGINT);
    printf("Please enter a terminal interrupt (CTRL-C) to continue.\n");
    fflush(stdout);
    sigprocmask(SIG_BLOCK,&mask,&oldmask);
    while (!int_flag) {
      sigsuspend(&oldmask);
    }
    sigprocmask(SIG_UNBLOCK,&mask,NULL); /* Reset signal block mask */

    /* Now do a divide by zero (hardware exception) */

    a=5;
    b=0;

    /* Set error recovery jump point */

    Status = setjmp(return_to_error_recover); 

    if (Status == 0) {
      a=5;
      b=0;
  } else if (Status == 1) { /* If Status = 1, we have divided by zero */
      a=5;
      b=1;
  } else {
      printf("Problem setting jump point ErrNo %i\n",errno);
      exit(1);
    }

    printf("Dividing: Numerator = %i\n",a);
    printf("Dividing: Denominator = %i\n",b);   
    c = a / b;
    printf("The result of the division is %i\n",c);

    /* Now spawn a child to get the SIGCLD signal */

    printf("Forking child.\n");
    fflush(stdout);
    ChildId = fork();

    /* This block is executed if there is a problem with the fork */

    if (ChildId < 0) {
        printf("Error in forking child - terminating.");
        exit(4); 
      }

    /* Parent blocks on SIGCLD and waits */

    if (ChildId > 0) {

    printf("PARENT: Blocking SIGCLD waiting for child to die.\n");
    fflush(stdout);
    sigemptyset(&mask);
    sigaddset(&mask,SIGCLD);
    sigprocmask(SIG_BLOCK,&mask,&oldmask);
    while(!cld_flag) {
      sigsuspend(&oldmask);
    }
    sigprocmask(SIG_UNBLOCK,&mask,NULL); /* Reset signal block mask */
  

    /* Now try suicide - first with the UNIX call */


    MyPID = getpid();
    printf("My PID is %i\n",MyPID);
    strPID = ltoa(MyPID); /* Convert PID to string */
    strcat(KillCommand,strPID); /* Make command line */
    printf("Issuing UNIX kill command: %s\n",KillCommand);
    Status = system(KillCommand);
    if (Status != 0) {
      printf("Problem issuing UNIX kill command.  Exiting.\n");
      exit(2);
    }

    /* Now kill with the C function (and SIGQUIT) */

    printf("Issuing a SIGQUIT with the C kill function.\n");
    Status = kill(MyPID,SIGQUIT);
    if (Status != 0) {
      printf("Problem issuing C kill function.  Exiting.\n");
      exit(3);
    }

   /* End of program */

    printf("Normal termination of main program.\n");
  }

    /* Child sleeps and dies */

    if (ChildId == 0) {
      printf("CHILD: Going to sleep.\n");
      fflush(stdout);
      sleep(10);
      printf("CHILD: Terminating now.\n");
      return(0);
    }

    return(0);
  }

void inthandler(sig)
{
    printf("Caught the SIGINT! Local signal number is %i\n",sig);
    printf("Returning to main program.\n");
    int_flag = 1; /* Unblock sigsuspend call */
    return;
  }

void fpehandler(sig)
{

    printf("Caught the floating point exception! Local signal number is %i\n",sig);
    printf("Going to error recovery point.\n");

    /* Fix divide by zero condition and return to error recovery point */

    longjmp (return_to_error_recover,1); /* Returns setjmp with value 1 */

    return;
  }

void termhandler(sig)
{
    printf("Caught the SIGTERM! Local signal number is %i\n",sig);
    printf("Returning to main program.\n");
    return;
  }

void quithandler(sig)
{
    printf("Caught the SIGQUIT! Local signal number is %i\n",sig);
    printf("Returning to main program.\n");
    return;
  }

void cldhandler(sig)
{
    printf("Caught the SIGCLD! Local signal number is %i\n",sig);
    printf("Child process has died.\n");
    printf("Returning to main program.\n");
    cld_flag = 1; /* Unblock sigsuspend call */
    return;
  }