/*
Usage:
$ ./a.out -s25
<parent> child 5926 created.
<parent> child 5926 terminated by signal 25

$ ./a.out -s25 -c
<parent> child 5936 created.
<child>  child 5936 catches signal 25 (SIGXFSZ)- perform some cleanups.
<parent> child 5936 exited normally with termination status=100

$ ./a.out -s 25 -c -d
<parent> child 5939 created.
<child>  child 5939 catches signal 25 (SIGXFSZ)- perform some cleanups.
<child>  child 5939 calls raise(25)
<parent> child 5939 terminated by signal 25
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <err.h>
#include <errno.h>
#include <sys/wait.h>
#include "../lpi.h"             // toInt() --> ../lpi.c
                                // getSigname --> ../defs.c

void handler(int signo);
int disEstablish;               // disestablish signal's handler?
struct sigaction sa;

int
main(int argc, char *argv[])
{
    int c;
    int signo;
    int catch;
    int status;
    pid_t pid;


    signo = SIGTERM;    // default signal
    catch = 0;          // catch signal?
    while ((c = getopt(argc, argv, ":s:cd")) != -1) {
        switch (c) {
        case 'c':
            catch = 1;
            break;
        case 's':
            if ((toInt(optarg, &signo)) != 0)
                err(EXIT_FAILURE, "toInt('%s')", optarg);
            break;
        case 'd':
            disEstablish = 1;
            break;
        case '?':
            warnx("unknown option -%c", optopt);
            break;
        case ':':
            errx(EXIT_FAILURE, "option -%c requires an argument", optopt);
        default:
            fprintf(stderr, "control must not reach here!\n");
            break;
        }
    }

    switch (pid = fork()) {
    case -1:
        err(EXIT_FAILURE, "fork");

    case 0:
        if (catch) {
            sa.sa_flags = SA_NODEFER;       // #TIP. very important
            sa.sa_handler = handler;
            if (sigaction(signo, &sa, NULL) == -1)
                err(EXIT_FAILURE, "error establish signal handler");
        }
        raise(signo);
        /* control should never reach here */
        _exit(EXIT_SUCCESS);

    default:
        printf("<parent> child %ld created.\n", (long) pid);

        /* wait for any child' status change */
        pid = waitpid(-1, &status, WUNTRACED | WCONTINUED);
        if (WIFEXITED(status)) {
            printf("<parent> child %ld exited normally with termination status=%d\n",
                    (long) pid, WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("<parent> child %ld terminated by signal %d\n",
                    (long) pid, WTERMSIG(status));
        } else if (WIFSTOPPED(status)) {
            printf("<parent> child %ld stopped by signal %d\n",
                    (long) pid, WSTOPSIG(status));
        } else if (WIFCONTINUED(status)) {
            printf("<parent> child %ld continued with SIGCONT\n", (long) pid);
        }
        break;
    }
    exit(EXIT_SUCCESS);
}

void
handler(int signo)
{
    printf("<child>  child %ld catches signal %d (%s)- perform some cleanups.\n",
            (long) getpid(), signo, getSigname(signo));
    if (disEstablish) {
        if (signal(signo, SIG_DFL) == SIG_ERR) {
            fprintf(stdout, "error disestablish signal's handler\n");
            return;
        }
        printf("<child>  child %ld calls raise(%d)\n", (long) getpid(), signo);
        raise(signo);
    }
    _exit(100);
}
