sigaction.c

/**
   @file      sigaction.c
   @author    Mitch Richling <http://www.mitchr.me/>
   @Copyright Copyright 1993 by Mitch Richling.  All rights reserved.
   @brief     UNIX sigaction function.@EOL
   @Keywords  UNIX signal handler sleep sigaction POSIX
   @Std       ISOC POSIX
   
              The POSIX sigaction(2) function is more complex than
              required for most uses, but the simple, non-POSIX
              function signal() is not "safe" for SRV based UNIX
              variants.  In System V a signal handler is unregistered
              after a signal is received if the signal() function was
              used to register that signal in the first place.  So the
              first thing most signal handlers do is to re-register
              themselves.  This leaves open a possible race condition
              because new signals are not blocked. This behavior is
              not present in BSD 4.3.  Thus in BSD, signal(2) is a
              safe signal function.  The POSIX sigaction(2) function
              provides the safety of the BSD signal(2) function to all
              platforms.  This C program is intended to illustrate how
              to bring the simplicity of the signal() function call to
              the POSIX signal handling API; thus providing all POSIX
              compliant platforms with a simple way to catch signals.
              
              This program also illustrates several signal concepts:
              1) How to write a signal handler, 2) How to have a
              signal handler handle more than one signal, 3) How to
              chain signal handlers, 4) How to set a signal handler,
              5) how to ignore signals, 6) how to reset signal
              handlers to the default, and 7) how to set signal
              handlers back to the last handler.
              
              Note the sleep(2) function sleeps until ANY signal is
              received, not just a SIGALARM.

   @Tested    
              - Solaris 2.8
              - MacOS X.2
              - Linux (RH 7.3)
 */

#include <sys/types.h>          /* UNIX types      POSIX */
#include <signal.h>             /* UNIX signals    POSIX */
#include <string.h>             /* Strings         ISOC  */
#include <stdlib.h>             /* Standard Lib    ISOC  */
#include <unistd.h>             /* UNIX std stf    POSIX */
#include <stdio.h>              /* I/O lib         ISOC  */

void (*mjrSignal(int sigcatch, void (*func) (int sigraised))) (int);

int main(int argc, char *argv[]);
void firstUserSignalAction(int sigNum);
void secondUserSignalAction(int sigNum);

/* The following declares a pointer to a function taking an int and returning void */
void (*oldUserSignalAction1) (int);
void (*oldUserSignalAction2) (int);

int main(int argc, char *argv[]) {

  /* mjrSignal() takes a signal number and a pointer to a signal
     handler.  It registers that function for that signal, and returns
     a pointer to the previous signal handler.  SIG_DFL is a pointer
     to the default signal handler.  SIG_IGN is a pointer to the
     "ignore" signal handler.  SIG_ERR is a dummy pointer that
     indicates an error condition.  The function can error for two
     reasons: 
        -# errno=EINVAL if the given integer is not a valid signal number or 
        -# errno=EINVAL if one tries to give a new handler for, or 
           tries to ignore, SIGKILL or SIGSTOP. */
  oldUserSignalAction1 = mjrSignal(SIGUSR1, &firstUserSignalAction);

  if(oldUserSignalAction1 == SIG_ERR) {
    printf("ERROR: mjrSignal() call failed.\n");
    return 1;
  } /* end if */

  /* This is a typical call sequence you might see in real code.  Note
     that it is common, but not the best.  It doesn't check for
     previous signal handlers. */
  if(SIG_ERR == mjrSignal(SIGUSR2, &firstUserSignalAction)) {
    printf("ERROR: mjrSignal() call failed.\n");
    return 1;
  } /* end if */

  printf("My PID: %ld\n", (long)getpid());

  printf("SIGUSR1,SIGUSR2=First.  Hit me.\n");
  sleep(600);
  printf("SIGUSR1,SIGUSR2=First.  Hit me.\n");
  sleep(600);
  printf("SIGUSR1,SIGUSR2=First.  Hit me.\n");
  sleep(600);

  /* Note, with the oldUserSignalAction1 value, we can check to see
     what was the handler before.  This probably should be done in
     good code because child processes inherit signal handlers.  */
  if(oldUserSignalAction1 == SIG_DFL)
    printf("Old signal action was the default.\n");
  else if(oldUserSignalAction1 == SIG_IGN)
    printf("Old signal action was to ignore.\n");
  else
    printf("Old signal action was a custom signal handler.\n");

  /* One common thing to do with old signal handlers is to "chain"
     them.  For example we redefine the SIGUSR1 handler, but the first
     thing that the handler does is to call the old handler.
     oldUserSignalAction2 is global so that firstUserSignalAction can
     be used for chaining.  Note that one should NOT chain outside of
     the handler definition because */
  oldUserSignalAction2 = mjrSignal(SIGUSR1, &secondUserSignalAction);

  printf("SIGUSR1=Second,SIGUSR2=First.  Hit me.\n");
  sleep(600);
  printf("SIGUSR1=Second,SIGUSR2=First.  Hit me.\n");
  sleep(600);
  printf("SIGUSR1=Second,SIGUSR2=First.  Hit me.\n");
  sleep(600);

  /* One common thing to do with the return of mjrSignal() is to set the signal handler
     back to what it was before you mucked with it.  This is the method used inside the
     sleep() system call.  Here we set SIGUSR1 back to what it was before we set it
     the second time. */
  mjrSignal(SIGUSR1, oldUserSignalAction2);

  printf("SIGUSR1,SIGUSR2=First.  Hit me.\n");
  sleep(600);
  printf("SIGUSR1,SIGUSR2=First.  Hit me.\n");
  sleep(600);
  printf("SIGUSR1,SIGUSR2=First.  Hit me.\n");
  sleep(600);

  /* We now set SIGUSR1 to the default and SIGUSR2 to ignore. */
  mjrSignal(SIGUSR1, SIG_DFL);
  mjrSignal(SIGUSR2, SIG_IGN);

  printf("SIGUSR1=deflt, SIGUSR2=ignore.  Hit me.\n");
  sleep(600);
  printf("SIGUSR1=deflt, SIGUSR2=ignore.  Hit me.\n");
  sleep(600);
  printf("SIGUSR1=deflt, SIGUSR2=ignore.  Hit me.\n");
  sleep(600);

  return 0;
} /* end func main */

/* All signal handlers take a single integer argument and return void.
   The integer argument has the signal number.  This lets one handler
   handle multiple signals as demonstrated here. */
void firstUserSignalAction(int sigNum) {
  /* We make SIGUSR1 & SIGUSR2 both handled by this function.  We can
     tell what signal we got from the argument. */
  if(sigNum == SIGUSR1)
    printf("First handler. This is sigusr1(%d)\n", sigNum);
  else
    printf("First handler. This is sigusr2(%d)\n", sigNum);
} /* end func firstUserSignalAction */

void secondUserSignalAction(int sigNum) {
  (*oldUserSignalAction2) (sigNum);
  printf("Second handler.  We got signal: %d\n", sigNum);
} /* end func secondUserSignalAction */

/* This is a simple wrapper function for sigaction(2) that returns the
   simple syntax of signal(2) and the robustness of POSIX/BSD4.3
   signals.  This is a nice way to get BSD4.3 signal handling inside
   of SRV as well.  The signal(2) function call has slightly different
   behavior between modern BSD based systems and modern SRV based
   systems.  This function, because it uses the POSIX sigaction API,
   gives both systems the same behavior--that of BSD4.3 and POSIX.
   This function will return SIG_ERR on error and set errno.  Upon
   success it will return the previous action.  EFAULT is one possible
   value for errno that this funciton may set, but that the origonal
   signal(2) function will not.  See the errno values possible for the
   sigaction(2) function for more information.
*/
void (*mjrSignal(int sigcatch, void (*func) (int sigraised))) (int) {
  struct sigaction newSigAction;
  struct sigaction oldSigAction;

/* For platforms with a sig mask that is not a simple integer, we must
   take care and fill the field with zeros using memset instead of
   simply setting the quantity to the zero integer. For a BSD system,
   'newSigAction.sa_mask = 0;', would be enough to get the job
   done. */
  memset(&newSigAction.sa_mask, 0, sizeof(newSigAction.sa_mask)); 

  newSigAction.sa_flags = 0;
  newSigAction.sa_handler = func;
  if(sigaction(sigcatch, &newSigAction, &oldSigAction) < 0) {
    return SIG_ERR;
  } else {
    return oldSigAction.sa_handler;
  } /* end if/else */
} /* end func mjrSignal */

Generated by GNU Enscript 1.6.5.2.