/*
 *  sig_purgechilds.c
 *  
 *  The parent process pulls a child exit status whenever the OS notifies
 *  a child status change.
 *
 *  Created by Mij <mij@bitchx.it> on 04/01/05.
 *  Original source file available at http://mij.oltrelinux.com/devel/unixprg/
 *
 */


/* for printf() and fgetc() */
#include <stdio.h>
/* for fork() */
#include <sys/types.h>
#include <unistd.h>
/* for srandom() and random() */
#include <stdlib.h>
/* for time() [seeding srandom()] */
#include <time.h>
/* for waitpid() */
#include <sys/wait.h>
/* for signal(), kill() and raise() */
#include <signal.h>

/* how many childs to raise */
#define NUM_PROCS   5


/* handler prototype for SIGCHLD */
void child_handler(int);

int main(int argc, char *argv[])
{
    int i, exit_status;
    
    
    /* execute child_handler() when receiving a signal of type SIGCHLD */
    signal(SIGCHLD, &child_handler);

    /* initialize the random num generator */
    srandom(time(NULL));
    
    
    printf("Try to issue a \'ps\' while the process is running...\n");
    
    
    /* produce NUM_PROCS childs */
    for (i = 0; i < NUM_PROCS; i++) {
        if (! fork()) {
            /* child */
            
            /* choosing a random exit status between 0 and 99 */
            exit_status = (int)(random() % 100);
            printf(" -> New child %d, will exit with %d.\n", (int)getpid(), exit_status);
            
            /* try to skip signals overlapping */
            sleep((unsigned int)(random() % 3));
            
            /* choosing a value to exit between 0 and 99 */
            exit(exit_status);
        }
        
        /* father */
        sleep((unsigned int)(random() % 2));
    }

    /* checkpoint */
    printf("parent: done with fork()ing.\n");

    /* why this is not equivalent to sleep(20) here? */
    for (i = 0; i < 10; i++) {
        sleep(1);
    }
    /* all the child processes should be done now */
    printf("I did not purge all the childs. Timeout; exiting.\n");
    
    /* terminate myself => exit */
    kill(getpid(), 15);
    
    /* this won't be actually executed */
    return 0;
}


/* handler definition for SIGCHLD */
void child_handler(int sig_type)
{
    int child_status;
    pid_t child;
    static int call_num = 0;

    /* getting the child's exit status */
    child = waitpid(0, &child_status, 0);
    
    printf("<-  Child %d exited with status %d.\n", child, WEXITSTATUS(child_status));
    
    /* did we get all the childs? */
    if (++call_num >= NUM_PROCS) {
        printf("I got all the childs this time. Going to exit.\n");
        exit (0);
    }
    
    return;
}