My Lab 5 Answer
/****************************  Sigaction Demo  ********************************/

#include <stdio.h>
#include <signal.h>
#include <string.h>
main()
{
  void handler(int signum);
  sigset_t allsigs;
  struct sigaction sa_new, sa_old;
  
  sigfillset(&allsigs);
  sa_new.sa_handler = handler;
  sa_new.sa_mask    = allsigs;
  sa_new.sa_flags   = 0;    /***** Value not used  *****/
  sigaction(SIGINT, &sa_new, &sa_old);
  printf("%s\n", (sa_old.sa_handler == SIG_DFL) ? "Default!"  :  "I dunno!");
  sigaction(SIGTSTP, &sa_new, &sa_old);
  sigaction(SIGINT, NULL, &sa_old);
  printf("%s\n", (sa_old.sa_handler == handler) ?  "Handler!"  :  "I dunno!");
  sleep(5);
}


void handler(int signum)
{
   sleep(3);
   switch (signum)
   {
      case SIGINT:  printf("SIGINT activated!\n");
                    break;
      case SIGTSTP: printf("SIGTSTP activated!\n");
   }
   printf("Leaving handler!\n");
}
/**************************  Program Execution Below  ***********************/

$ sigdemo1     /*  I hit two CTRL-C's and two CTRL-Z's below. */

Default!
Handler!
SIGINT activated!
Leaving handler!
SIGINT activated!
Leaving handler!
SIGTSTP activated!
Leaving handler!

The million-dollar question: why are TWO CTRL-C's processed but only ONE CTRL-Z?

/*************************  How to Trap Malloc Errors  ***********************/

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
main()
{
   void handler(int signum);
   char *p;
   struct sigaction sa;

   sa.sa_handler = handler;
   sa.sa_flags   = 0;
   sigfillset(&sa.sa_mask);
   sigaction(SIGBUS, &sa, NULL);
   sigaction(SIGSEGV, &sa, NULL);
   p = (char *) malloc(2000000000);
   printf("I have not dereferenced p yet!\n");
   *p = 'x';
   printf("P points at %c\n", *p);
}




void handler(int signum)
{
   switch (signum)
   {
     case SIGBUS: printf("SIGBUS error\n");
                  break;
     case SIGSEGV: printf("Segmentation violation\n");
   }
   exit(1);
}
/***************************  Program Output Below  ***************************/   
I have not dereferenced p yet!
Segmentation violation   /*******  On some machines SIGBUS traps memory  ******/
                         /*******  errors.  On others it's SIGSEGV.      ******/

/**************************  Demo of Signal Blocking  ************************/
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
main()
{
  sigset_t newmask, oldmask, pendmask;
  void handler(int signum);
  struct sigaction sa;

  sa.sa_handler = handler;
  sigfillset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGINT, &sa, NULL);
  sigemptyset(&newmask);
  sigaddset(&newmask, SIGINT);
  sigaddset(&newmask, SIGQUIT);
  if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
            printf("SIG_BLOCK error!\n");
  printf("Going to sleep for 5 seconds!\n");
  sleep(5);
  printf("I'm back from first sleep!\n");

  if (sigpending(&pendmask) < 0) 
  {
     printf("Sigpending error!\n");
     exit(1);
  }
  if (sigismember(&pendmask, SIGINT)) printf("Yup, SIG_INT pending!\n");
  if (sigismember(&pendmask, SIGQUIT))printf("Yup, SIG_QUIT pending!\n");
  if (sigprocmask(SIG_UNBLOCK, &pendmask, NULL) < 0)
      printf("SIG_SETMASK error!\n");
}


void handler(int signum)
{
   if (signum == SIGINT) printf("CTRL-C got me here!\n");
   else printf("I don't know how I got here!\n");
   printf("Sleeping in handler for 5 seconds!\n");
   sleep(5);
}
/**************************  Example Execution Below  ***********************/

$ a.out
Going to sleep for 5 seconds!
I'm back from first sleep!
Yup, SIG_INT pending!
Yup, SIG_QUIT pending!
CTRL-C got me here!
Quit(coredump)
/******************************************************************************/

/***************************  Alarm Clock Demo  ****************************/

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf env;
main()
{
  int num;
  void myalarm(void);
  struct sigaction sa;

  sa.sa_handler = myalarm;
  sigfillset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGALRM, &sa, NULL);
  setjmp(env);
  alarm(5);
  printf("You have 5 seconds to enter a number! ");
  scanf("%d",&num);
  printf("\nGood kid -- you did it!\n");
}

void myalarm(void)
{
  printf("\n\nWhat a slow poke!\n");
  printf("Try again but speed it up!\n\n");
  longjmp(env,1);
}
/*****************************  Example Output Below  *************************/

$ a.out
You have 5 seconds to enter a number! 

What a slow poke!
Try again but speed it up!

You have 5 seconds to enter a number!  /****  It hangs here but why?  ****/ 

$ a.out
You have 5 seconds to enter a number! 

What a slow poke!
Try again but speed it up!

You have 5 seconds to enter a number! 10

Good kid -- you did it!
$ 

/**********************  Alarm Clock Demo  Using Sigsetjmp  *******************/

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
sigjmp_buf env;
main()
{
  int num;
  void myalarm(void);
  struct sigaction sa;

  sa.sa_handler = myalarm;
  sigfillset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGALRM, &sa, NULL);
  sigsetjmp(env, 1);   /****  Zero as second arg causes infinite block! ****/
  alarm(5);
  printf("You have 5 seconds to enter a number! ");
  scanf("%d",&num);
  printf("\nGood kid -- you did it!\n");
}
 

void myalarm(void)
{
  printf("\n\nWhat a slow poke!\n");
  printf("Try again but speed it up!\n\n");
  siglongjmp(env,1);
}

/*************************  Program Output Below  ****************************/

$ a.out
You have 5 seconds to enter a number! 

What a slow poke!
Try again but speed it up!

You have 5 seconds to enter a number! 

What a slow poke!
Try again but speed it up!

You have 5 seconds to enter a number! 

What a slow poke!
Try again but speed it up!

You have 5 seconds to enter a number! 10

Good kid -- you did it!
$ 
/**************  Creating and Raising Your Own Interrupts  *******************/
#include <stdio.h>
#include <signal.h>
main()
{
  void sig_usr(int signum);
  struct sigaction sa;
  int i;
  
  sa.sa_handler = sig_usr;
  sigfillset(&sa.sa_mask);
  sa.sa_flags = 0;
  sigaction(SIGUSR1, &sa, NULL);
  sigaction(SIGUSR2, &sa, NULL);
  sigaction(SIGTERM, &sa, NULL);
  raise(SIGUSR1);
  raise(SIGUSR2);
  raise(SIGTERM);
  printf("End of program!\n");
}

void sig_usr(int signum)
{
  switch (signum)
  {
     case SIGUSR1:  printf("SIGUSR1 caught!\n");
                    break;
     case SIGUSR2:  printf("SIGUSR2 caught!\n");
                    break;
     case SIGTERM:  printf("SIGTERM caught!\n");
  }
}
/***************************  Program Output Below  **************************/
SIGUSR1 caught!
SIGUSR2 caught!
SIGTERM caught!
End of program!
/*********************  Blocking Critical Sections  *************************/
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
int main()
{
   int i;
   sigset_t sig_set, pending_set, empty, full;
   void handler(int signum);
   struct sigaction sa;

   sa.sa_handler = handler;
   sigfillset(&sa.sa_mask);
   sa.sa_flags = 0;
   sigaction(SIGINT, &sa, NULL);  
   sigaction(SIGQUIT, &sa, NULL);
   sigaction(SIGTSTP, &sa, NULL);   
   sigemptyset(&sig_set);
   sigaddset(&sig_set, SIGINT);
   sigaddset(&sig_set, SIGQUIT);
   sigaddset(&sig_set, SIGTSTP);
   sigprocmask(SIG_BLOCK, &sig_set, NULL);
/********  Start of critical section (or let's pretend it's critical!?) ******/

   for (i = 0; i < 5; i++) sleep(1);

/****************  End of "Make Believe" Critical Section  *******************/
   sigpending(&pending_set);
   if (sigprocmask(SIG_UNBLOCK, &pending_set, 0) < 0) exit(0);
   if (sigismember(&pending_set, SIGINT))  printf("SIGINT\n");
   if (sigismember(&pending_set, SIGQUIT)) printf("SIGQUIT\n");
   if (sigismember(&pending_set, SIGTSTP)) printf("SIGTSTP\n"); 
}


void handler(int signum)
{
  switch (signum)
  {
      case SIGINT:  printf("SIGINT caught!\n");
                    break;
      case SIGQUIT: printf("SIGQUIT caught!\n");
                    break;
      case SIGTSTP: printf("SIGTSTP caught!\n");
                    break;
  }
  sleep(2);
}
/******************************  Program Output Below  ***********************/
$ a.out
SIGTSTP caught!
SIGQUIT caught!
SIGINT caught!
SIGINT
SIGQUIT
SIGTSTP
$ 

/******************  Signal Handling Summary Sheet  *************************/

Function:   sigaction(int signum, struct sigaction *new, struct sigaction *old)

Action:     Trap signal given as "signum".  Perform signal handling as defined
            in "new".  This structure contains: 1) handler to go to if the
            signal is trapped (or SIG_IGN or SIG_DFL), 2) blocking mask for
            the handler (combined with any previously-defined blocking masks),
            and 3) certain flags which we will ignore for now.

            The "old" structure will contain the handler, blocking mask, and
            flags which were in effect prior to the current sigaction call.

Special notes:  1) Sigaction atomically delivers signal and re-installs the
                   signal handler immediately.
                2) Signal which was trapped is AUTOMATICALLY blocked in the
                   handler!  While in the handler, signals are NOT queued.
                   If SIGINT is handled and user hits 10 CTRL-C's, only one
                   is processed AFTER handler is exited.
                3) If second argument is NULL and third is non-NULL, this is
                   a request for the current handling status.
/*****************************************************************************/

Function:   sigprocmask(int action, sigset_t *block_mask, sigset_t *oldmask)

Action:     Set blocking mask according to "action".  If action is SIG_SETMASK,
            then set process blocking mask to "*block_mask".  If action is
            SIG_BLOCK, then add signals in "*block_mask" to current process
            blocking mask.  If action is SIG_UNBLOCK, then unblock signals in
            "*block_mask".

Special notes:  1) If second argument is NULL and third is non-NULL, this gets
                   status of current blocks.
                2) If second argument is non-NULL and third is NULL, then this
                   indicates that the caller is not interested in the old block-
                   ing status. 
/******************************************************************************/
 
Function:  sigsetjmp(sigjmp_buf env, int savemask)

Action:    This statement holds a place to be returned to when a siglong_jmp is
           called.  Savemask should be non-zero to return to old blocking mask
           after handler terminates via siglongjmp.  If savemask is zero, then
           this function acts like ANSI C setjmp -- which is dangerous!
/******************************************************************************/
      signal           when invoked
      ------           ------------

      SIGINT           When CTRL-C is typed.
      SIGTSTP          When CTRL-Z is typed.
      SIGFPE           When floating point overflow/underflow occur.
      SIGQUIT          When CTRL-\ is typed.
      SIGBUS or        When memory errors occur i.e. bad malloc, array index...
      SIGSEGV
      SIGALRM          When an alarm clock set via the "alarm" system call
                       expires.
      SIGUSR1 or       When these signals are explicitly raised via the "raise"
      SIGUSR2          system call.
/****************  Miscellaneous Signal-Related Functions  *******************/

   1.  sleep(seconds)  -- stop process for "seconds" wall-clock seconds.
   2.  pause()         -- stop process until a signal is delivered.
   3.  alarm(seconds)  -- invoke SIGALRM if "seconds" seconds elapse and the
                          alarm clock is not deactivated via alarm(0).
   4.  kill(pid_t process_id, int signum)  -- deliver signum to a process.
   5.  getpid()        -- obtain current process_id.
   6.  raise(signum)   -- explicitly invoke the signal given by "signum" (e.g.
                          SIGINT or SIGQUIT).  If no sigaction exists for that
                          signal then the DEFAULT action is taken.  If the
                          signal raised is BLOCKED,  raising the signal does
                          NOTHING!!
   7.  sigemptyset(sigset_t *)  -- Initialize a signal set by turning all bits
                                   off.
   8.  sigfillset(sigset_t  *)  -- Initialize a signal set by turning all bits
                                   on.
   9.  sigaddset(sigset_t *, int signum)    -- Add "signum" to the set.
   10. sigdelset(sigset_t *, int signum)    -- Clear "signum" from the set.
   11. sigismember(sigset_t *, int signum)  -- True or false depending upon
                                               whether "signum" is in the set.
   12. sigpending(sigset_t *)   --  Fills up the set with signals which are
                                    blocked and not yet delivered.

More on kill:

1.  The Kill Function

    retval = kill(pid, signal-type);

    If retval is -1, kill failed.  Notice that this function can be used to
    send ANY SIGNAL to ANY member of a process group (i.e. a parent or any
    of its children).  To send, for example, SIGINT to the parent of a process
    you would do this:

    kill(getppid(), SIGINT);

    To send a signal to the CURRENT PROCESS, such as a SIGKILL signal, you
    would do this:

    kill(getpid(), SIGKILL);  /* SIGKILL terminates a process PROMPTLY!! */

    To send a signal to ALL MEMBERS OF A PROCESS GROUP, such as SIGINT, you
    would do this:

    kill(0, SIGINT);  /* Sends SIGINT to parent and all of its children!! */
                      /* It is vital to do this if "stray" processes are  */
                      /* still out there computing when a fatal error has */
                      /* occured. */

    Finally, if you have a pid stored in some variable, you can simply pass
    that as the first argument to kill.  
*****************************************************************************
struct sigaction
{
   int sa_flags;
   sigset_t sa_mask;
   void (*sa_handler)(int signo);
};