/******************************************************************/
/* child is a program which demonstartes the effects of forking a */
/* process on the parent and child processes. Three different     */
/* forking routines are used to demonstrate these effects         */
/*                                                                */
/* Physics 673: Real Time Computing                               */
/* Norm Buchanan           96/11/08                               */
/******************************************************************/

#include 
#include 
#include 
#include 
#include 

int level=0;
char kill[7] = {'k','i','l','l',' ','\0'};

void basic_fork(void);
void chain_fork(void);
void itoa(int pid, char id[]);
void menu(void);
void ps(void);
void assgn_fork(void);

main () {
  
  menu();
}

/****************************************************************/
/* This pasic fork routine allows the user to observe the child */
/* process and then cleanly kill it.  This is done as the child */
/* process is kept running until the parent sends the kill sig  */
/* (using the UNIX kill command) which is sent to the shell.    */
/****************************************************************/

void basic_fork(void){
  
  int ret_val, ans, syscall;
  char id[8], *k;
  
  ps();
  
  k = kill;
  printf("Creating child process\n");
  ret_val = fork();
  if (ret_val == -1){  /* no child process forked */
    printf("\a::Error--Unsuccessful fork::");
    exit(1);
  }
  else if (ret_val == 0){  /* child sleeps for 1000 secs */
    sleep(1000);
  }
  else { /* parent waits for prompt to kill child process */
    printf("PID of child: %d\n", ret_val);
    ps();
    itoa(ret_val, id); /* convert integer to ascii */
    k = strcat(k, id); /* create appropriate kill command */
    printf("\"1\"  to kill child process:");
    scanf("%d", &ans);
    syscall = system(k);
    ps();
    printf("\"1\"  to kill parent process:");
    scanf("%d", &ans);
  }
}

/********************************************************************/
/* This routine is to demonstrate the assigned problem of having a  */
/* child process created which terminates prior to the parent       */
/* process. There are ps calls along the way to demonstrate the     */
/* current state of the system.  The waitpid function call is used  */
/* to wait for the child to die at which time the parent dies.      */
/* There is no zombie as the waitpid function calls to the getpid   */
/* function which liberates the child from the zombie state.        */
/********************************************************************/

void assgn_fork(void){
  
  int ret_val, ans, syscall, status;
  char id[8], *k;
  
  ps();
  
  k = kill;
  printf("Creating child process\n");
  ret_val = fork();
  if (ret_val == -1){  /* no child process forked */
    printf("\a::Error--Unsuccessful fork::");
    exit(1);
  }
  else if (ret_val == 0){  /* child sleeps for 1 sec */
    sleep(1);
    printf("Child dying normally now\n");
  }
  else { /* parent waits for prompt to kill child process */
    printf("PID of child: %d\n", ret_val);
    ps();
    status = waitpid(ret_val,0,WNOHANG);
    while(!(status)){                        /* Polling child status */
      ps();
      status = waitpid(ret_val,0,WNOHANG);
    }
    printf("Going back into main routine\n");
    ps();
  }
}


/******************************************************************/
/* This function forks a child process which does the same etc... */
/* The parent lives twice as long as the child and thus creates a */
/* chain of processes where the parents die after leaving a child */
/* and grandchild (load dependent).  When the last child is       */ 
/* created and dies before it's parent it will become a zombie    */
/* process until it's parent dies a short time later.  This       */
/* demonstrates how children behave as the parent terminates, and */
/* how the child becomes a zombie if it dies before it's parent.  */
/******************************************************************/

void chain_fork(void){
  
  int ret_val;
  static int forknum = 0;
  
  forknum++;
  level++;
  
  ps();
  printf("Creating child process\n");
  ret_val = fork();
  if (ret_val == -1){  /* no child process forked */
    printf("\a::Error--Unsuccessful fork::");
    exit(1);
  }
  else if (ret_val == 0){  /* child sleeps for 5 secs */
    sleep(5);
    if (level < 5){
      chain_fork(); /* create child of child process */
    }
  }
  else { /* parent sleeps for 10 seconds */
    printf("Fork Number: %d\n", forknum);
    printf("PID of child: %d\n", ret_val);
    sleep(10);
  }
}

     
/**********************************************************/
/* A nifty little utility function shamefully taken from  */
/* "The C Programming Language" by Kernighan and Ritchie  */
/* which converts an integer to a character string        */
/**********************************************************/
     
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 ");
  scanf("%d", &choice);
  if (choice == 1) {
    printf("\n\nRunning basic fork routine\n");
    basic_fork();
  }
  else if (choice == 2) {
    printf("\n\nRunning chained fork routine\n");
    chain_fork();
    ps();
  }
  else if (choice == 3) {
    printf("\n\nRunning assigned fork routine\n");
    assgn_fork();
  }
}

/****************************************************************/
/* Function to send ps command to system shell to show child    */
/* processes.                                                   */
/****************************************************************/

void ps(void){
  
  system("ps -lu buchanan | grep child | grep -v grep");
   
}