/*                 Physics 673 Assignment Number 9                   */
/*                 Bryan L. Caron  November 27, 1996                 */
/*                                                                   */
/* This program demonstrates the catching of five signals sent and   */
/* received in a variety of manners:                                 */
/*                                                                   */
/*     -terminal generated signal SIGINT                             */
/*     -hardware exception generated signal _SIGBUS                  */
/*     -kill(2) function sending _SIGTSTP                            */
/*     -kill(1) function sending SIGABRT                             */
/*     -software condition generated signal _SIGCHD                  */


#include      /* header file for standard i/o               */
#include  /* header file for fork(), waitpid() commands */
#include     /* header file for fork() command             */
#include   /* header file for waitpid() command          */
#include     /* header file for signal handling            */
#include     /* header file for string manipulation        */

int kill1fnc();
int kill2fnc();
int terminal();
int hardware();
int software();

main()
{
  printf( "\n*** TERMINAL GENERATED SIGNAL HANDLING ...***\n" );

  terminal();

  printf( "\n*** KILL(2) FUNCTION SIGNAL HANDLING ... ***\n" );

  kill2fnc();

  printf( "\n*** KILL(1) FUNCTION SIGNAL HANDLING ... ***\n" );

  kill1fnc();

  printf( "\n*** SOFTWARE CONDITION SIGNAL HANDLING ... ***\n" );

  software();

  printf( "\n*** HARDWARE EXCEPTION SIGNAL HANDLING ... ***\n" );

  hardware();
}


int terminal()
{
  void sighandler();

  signal( SIGINT , sighandler );
  printf( "\nGenerate a terminal interrupt signal by hitting 'Control C'\n");

  /* pause until a signal is received */

  pause();
}


int kill2fnc()
{
  void sighandler();

  int i;
  int j;
  int k;
  int status;
  int r;

  printf( "\nPre-fork Process ID:%d", getpid() );
  printf( "\nPre-fork Parent Process ID:%d",getppid() );

  printf( "\n\nFork a new process ...\n" );

  /* flush stdout stream and fork a new process */

  fflush( stdout );

  i = fork();

  if( i <0 ) {

    printf( "\nReturn code = %d Fork failed. Clean up and exit.", i );
    exit( 1 );

  }
  else { 

    if( i == 0 ) {

      signal( _SIGTSTP , sighandler );

      printf( "\n\tfork() return value = %d",i );
      printf( "\n\tThis process is a Child with Process ID:%d", getpid() );
      printf( "\n\tChild's Parent Process ID:%d\n",getppid() );

      /* pause until a signal is received */

      pause();

      printf( "\n\tReturn from Signal Handler." );
      printf( "\n\tChild exits of its own accord.\n" );

      /* exit necessary as child will execute remaining main functions */

      exit( 1 );

    }

    else {

      system( "ps -l | grep 'a.out' " );

      printf( "\n\tfork return value =  %d",i );
      printf( "\n\tThis process is a Parent with Process ID:%d\n", getpid());

      printf( "\n\tPARENT: Send SIGSTP signal to Child via kill(2)" );

      j = kill ( i , SIGTSTP );

      if( j < 0 ) {
	printf( "\n\tError sending kill(2) SIGTSTP.  Return code = %d\n", j );
	exit( 2 );
      }

      printf( " \n\tPARENT: kill(2) return code = %d\n\n", j );

      fflush( stdout );

      system( "ps -l | grep 'a.out' " );

      /* while loop statements occur for whenever child still running */

      while ( ! (j = waitpid ( i , &status , WNOHANG )) ) {
	fflush(stdout);
	printf( "." );
	sleep(1);
      }

      printf( "\n\n\tPARENT: waitpid() return value = %d",  status );
      printf( "\n\tPARENT: Child Process ID %d has terminated.\n", j );

      fflush ( stdout );

    }

  }

} /* end of kill2fnc() */



int kill1fnc()
{
  void itoa();
  void sighandler();

  int i;
  int j;
  int k;
  int status;

  char kill1sig[] = "kill -SIGABRT ";
  char Pid[10];

  printf( "\nPre-fork Process ID:%d", getpid() );
  printf( "\nPre-fork Parent Process ID:%d",getppid() );

  printf( "\n\nFork a new process ...\n" );

  /* flush stdout stream and fork a new process */

  fflush( stdout );

  i = fork();

  if( i <0 ) {

    printf( "\nReturn code = %d Fork failed. Clean up and exit.", i );
    exit( 1 );

  }
  else { 

    if( i == 0 ) {

      signal( SIGABRT , sighandler );

      printf( "\n\tfork() return value = %d",i );
      printf( "\n\tThis process is a Child with Process ID:%d", getpid() );
      printf( "\n\tChild's Parent Process ID:%d\n",getppid() );

      /* pause until a signal is received */

      pause();

      printf( "\n\tReturn from Signal Handler." );
      printf( "\n\tChild exits of its own accord.\n" );

      /* exit necessary as child will execute remaining main functions */

      exit( 1 );

    }

    else {

      system( "ps -l | grep 'a.out' " );

      printf( "\n\tfork return value =  %d",i );
      printf( "\n\tThis process is a Parent with Process ID:%d\n", getpid());

      /* convert integer to ascii character and concatenate */

      itoa( i , Pid );
      strcat( kill1sig , Pid );

      printf( "\n\tPARENT: kill1 signal being sent is: %s", kill1sig );
      printf( "\n\tPARENT: Send SIGABRT signal to Child via kill(1)\n\n" );

      system( kill1sig );

      fflush( stdout );

      system( "ps -l | grep 'a.out' " );

      /* while loop statements occur for whenever child still running */

      while ( ! (j = waitpid ( i , &status , WNOHANG )) ) {
	fflush(stdout);
	printf( "." );
	sleep(1);
      }

      printf( "\n\n\tPARENT: waitpid() return value = %d",  status );
      printf( "\n\tPARENT: Child Process ID %d has terminated.\n", j );

      fflush ( stdout );

    }

  }

} /* end of kill1fnc() */



software()
{

  void sighandler();

  int i;
  int j;
  int k;
  int status;

  printf( "\nPre-fork Process ID:%d", getpid() );
  printf( "\nPre-fork Parent Process ID:%d\n",getppid() );

  printf( "\nFork a new process ...\n" );

  /* flush stdout stream and fork a new process */

  fflush( stdout );

  i = fork();

  if( i <0 ) {

    printf( "Return code = %d Fork failed. Clean up and exit.\n", i );
    exit( 1 );

  }
  else { 

    if( i == 0 ) {

      printf( "\n\tfork() return value = %d\n",i );
      printf( "\tThis process is a Child with Process ID:%d\n", getpid() );
      printf( "\tChild's Parent Process ID:%d\n",getppid() );

      sleep(20);

      printf( "\n\tLet the Child exit, which will signal the Parent.\n\n" );

      exit(1);

    }

    else {

      signal( _SIGCHLD , sighandler );

      printf( "\n\t fork return value =  %d",i );
      printf( "\n\t This process is a Parent with Process ID:%d\n", getpid() );

      system( "ps -l | grep 'a.out' " );

      /* while loop statements occur for whenever child still running */

      while ( ! (j = waitpid ( i , &status , WNOHANG )) ) {
	fflush(stdout);
	sleep(1);
	system( "ps -l | grep 'a.out' | grep 'Z' " );
      }

      system( "ps -l | grep 'a.out' " );

      printf( "\nwaitpid() return value = %d\n",  status );
      printf( "\nChild Process ID %d has terminated.\n", j );
    }

  }

} /* end of software() */



int hardware()
{

  int i;
  int j;
  int k;
  int status;

  printf( "\nPre-fork Process ID:%d\n", getpid() );
  printf( "\nPre-fork Parent Process ID:%d\n",getppid() );

  printf( "\nFork a new process ...\n" );

  /* flush stdout stream and fork a new process */

  fflush( stdout );

  i = fork();

  if( i <0 ) {

    printf( "\nReturn code = %d Fork failed. Clean up and exit.\n", i );
    exit( 1 );

  }
  else { 

    if( i == 0 ) {

      void sighandler();

      signal( _SIGBUS , sighandler );

      printf( "\n\tfork() return value = %d",i );
      printf( "\n\tThis process is a Child with Process ID:%d", getpid() );
      printf( "\n\tChild's Parent Process ID:%d\n",getppid() );

      /* wait for signal to arrive */

      pause();

      printf( "\n\tChild continues after receiving signal. Exit cleanly.\n");

    }

    else {
      printf( "\n\tfork return value =  %d",i );
      printf( "\n\tThis process is a Parent with Process ID:%d\n", getpid());

      fflush( stdout );

      system( "ps -l | grep 'a.out' " );

      j = kill ( i , _SIGBUS );

      if( j < 0 ) {
	printf( "\n\tError sending kill(2) _SIGBUS.  Return code = %d\n", j );
	exit( 2 );
      }

      printf( " \n\tPARENT: kill(2) return code = %d\n", j );

      /* while loop statements occur for whenever child still running */

      system( "ps -l | grep 'a.out' " );

      while ( ! (j = waitpid ( i , &status , WNOHANG )) ) {
	fflush(stdout);
	sleep(1);
      }

      printf( "\n\twaitpid() return value = %d",  status );
      printf( "\n\nChild Process ID %d has terminated.\n", j );

    }
  }
} /* end of hardware() */




void sighandler(sig)
{

  printf( "\n\tSIGNAL HANDLER: Signal Value = %d\n", sig );

  if(sig == SIGINT ) {
    printf( "\n\tTerminal interrupt has been caught.\n");
  }

  if(sig == _SIGTSTP ) {
    printf( "\n\tKill(2) SIGTSTP interrupt has been caught.\n");
  }

  if(sig == SIGABRT ) {
    printf( "\n\tKill(1) SIGABRT interrupt has been caught.\n");
  }

  if(sig == _SIGCHLD ) {
    printf( "\n\tSoftware interrupt _SIGCHLD has been caught.\n");
  }

  if(sig == _SIGBUS ) {
    printf( "\n\t_SIGBUS hardware bus error interrupt has been caught.\n");
  }

  fflush (stdout);
}


/* this routine to convert integer to char type courtesy N. Buchanan */

void itoa (int pid, char id[]) { 
  
  int i=0, j, k, temp;
  
  do {  /* generate digits in reverse order */
    id[i++] = pid % 10 + '0';  /* get next digit */
  } while (( pid /= 10) > 0);
  id[i] = '\0';
  
  /* Now reverse the string */
  
  for (j=0, k = strlen(id)-1; j