/*
 Detect unexpected termination of mother process, which has many children.

                             +--
                             |
  grandmother --+-- mother --+-- children
                |            |
                |            +--
                +-- uncle

 You can kill all the processes even if mother or grandmother terminates.
 If uncle terminates, grandmother spawns it again.
 About more discussions, please see
	http://www.sra.co.jp/people/katsu/article/#killchild
 Works on POSIX, BSD(4.3,4.4) and SunOS(4.X,5.X).

 Written by WATANABE Katsuhiro <katsu@sra.co.jp>.

 Freely redistributable.
*/


/*
 * Start of environment dependency section.
 *
 *
 * first step: Set default parameters for compilation.
 */
#define HAS_UNISTD_H
#define HAS_PID_T
#define HAS_SETPGID
#define WAITSTATUS_T int 
#define SIGHANDLER_T void

/*
 * 2nd step: Guess compile environment.
 */
#include <sys/param.h>

#ifdef BSD
#if (BSD < 44)
#undef WAITSTATUS_T
#define WAITSTATUS_T union wait
#undef HAS_PID_T
#undef HAS_SETPGID
#ifndef news
#undef HAS_UNISTD_H
#endif /* !news */
#ifndef NeXT
#undef SIGHANDLER_T
#define SIGHANDLER_T int
#endif /* !NeXT */
#endif /* BSD < 44 */
#endif /* BSD */

/*
 * 3rd step: Include header files appropriate for the environment.
 */
#include <sys/types.h>
#include <sys/wait.h>
#ifdef HAS_UNISTD_H
#include <unistd.h>
#endif /* HAS_UNISTD_H */
#include <signal.h>
#include <stdio.h>

/*
 * Final step: Define proper types and macros.
 */
typedef WAITSTATUS_T waitstatus_t;
typedef SIGHANDLER_T sighandler_t;

#ifndef WEXITSTATUS
#define WEXITSTATUS(status) ((status).w_retcode)
#define WTERMSIG(status) ((status).w_termsig)
#define WSTOPSIG(status) ((status).w_stopsig)
#endif /* WEXITSTATUS */

#ifndef HAS_PID_T
typedef int pid_t;
#endif /* !HAS_PID_T */

#ifndef HAS_SETPGID
#define setpgid(pid, pgid) setpgrp(pid, pgid)
#endif /* !HAS_SETPGID */

/*
 * End of environment dependency section.
 */


static pid_t pgid;
static pid_t unclepid, motherpid;

main(argc, argv)
int argc;
char **argv;
{
    pid_t pid;

    pgid = getpid();
    if (setpgid(0, pgid) == -1) {
	perror("setpgid()");
	exit(1);
    }

    if (!(motherpid = fork())) {
	mother();
    } else if (pid == -1) {
	perror("fork() for mother");
	exit(1);
    }
    grandmother();
}

print_exit_reason(pid, status)
pid_t pid;
waitstatus_t status;
{
    if (WIFEXITED(status)) {
        fprintf(stderr,
		"wait() returned because pid %d exited with status %d\n",
		pid, WEXITSTATUS(status));
    } else if (WIFSIGNALED(status)) {
        fprintf(stderr,
		"wait() returned because pid %d terminated with signal %d\n",
		pid, WTERMSIG(status));

    } else if (WIFSTOPPED(status)) {
        fprintf(stderr,
		"wait() returned because pid %d stopped with signal %d\n",
		pid, WSTOPSIG(status));
    } else {
        fprintf(stderr, "wait() returned unknown reason for pid %d", pid);
    }
}

grandmother()
{
    pid_t death;
    waitstatus_t status;

    spawn_uncle();
    while (1) {
	death = wait(&status);
	if (death == motherpid) {
            fprintf(stderr, "Grandmother: wait() detect mother's death\n");
	    killpg(pgid, SIGINT);
	    exit(2);
	} else if (death == unclepid) {
	    fprintf(stderr, "Grandmother: wait() detect uncle's death\n");
	    spawn_uncle();
	} else {
            fprintf(stderr, "Grandmother: ");
            print_exit_reason(death, status);
	}
    }
}

spawn_uncle()
{
    if (!(unclepid = fork())) {
	uncle();
    } else if (unclepid == -1) {
	perror("fork() for uncle");
	exit(1);
    }
}

sighandler_t on_sighup(ignore)
int ignore;
{
    killpg(pgid, SIGKILL);
}

uncle()
{
    signal(SIGHUP, on_sighup);
    kill(getpid(), SIGSTOP);
    pause();
}

/***************************************************************************
 Following mother() and child are prototype of real meaningful program.
 Please notice that you don't need to change the source code of mother
 and child processes at all.
***************************************************************************/

mother()
{
    pid_t childpid, death;
    waitstatus_t status;

    if (!(childpid = fork())) {
	child();
    } else if (childpid == -1) {
	perror("fork() for child");
	exit(1);
    }
    death = wait(&status);
    fprintf(stderr, "Mother: ");
    print_exit_reason(death, status);
    exit(0);
}

child()
{
    pause();
    exit(1);
}

