#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/errno.h>
#include <unistd.h>
#include <setjmp.h>
#include <ucontext.h>


/*
 * mascheck at faw.uni-ulm.de, in public domain
 *
 * This program can only be killed with SIGKILL
 *
 * Who is throwing signals at me?
 *
 * If called with arbitrary arguments, then begin with a selftest (SIGUSR2),
 * which is different from being signaled through the tty, btw.
 * Otherwise just wait.
 *
 * currently not reliable, using libc functions in handler
 */


/*
 * When modifying, particularly adding a fork(), pay attention
 * to any possible signals reaching the parent frequently for any
 * reason (not just SIGCHLD).  forkbomb().
 * Although this program doesn't use threads, i received SIGWAITING e.g.
 * on SunOS 5.5
 */

/*
 * Linux 2.4:
 * sigaction(2)
 * signal(2)
 * /usr/include/signal.h
 * /usr/include/bits/siginfo.h
 *
 * siginfo was added in Linux 2.2, but PID/UID were actually not reported
 */


extern int errno;       /* not used */
sigjmp_buf env;         /* sigset/longjmp() */

char cmd[256]
 = "ps -A -ouser,pid,ppid,gid,stime,tty,time,etime,rss,vsz,pcpu,args";

siginfo_t  *siginfo_p; /* would be a copy of the original struct */
ucontext_t *context_p; /* not used */

/*
 * normal signal handler, not used
 */

static void signal_handler() {
    write(1, "in signal_handler()\n", 20);
}


/* ucontext_t *context_p; /* not used */


/*
 * extra 'signal catching function', if SA_SIGINFO is used
 *
 * libc functions shall not be used in signal handler,
 * store interesting variables and inspect them after returning from
 * siglongjmp(). "siginfo_p->si_signo = info_p->si_signo;"
 *
 * Hack, unreliable: i do use printf()
 *
 */

static void signal_catcher(int sig, siginfo_t* info_p, void* c_p) {

    printf("\n\nsignal_catcher() started.\n"); fflush(stdout);

    if (info_p != NULL) {
            switch(info_p->si_code) {
                case SI_KERNEL:
                    printf("    signal generated via kernel\n");
                    break;
                case SI_USER:
                    printf("    signal generated via: kill() or alike\n");
                    break;
                case SI_QUEUE:
                    printf("    signal generated via: sigqueue()\n");
                    break;
                case SI_TIMER:
                    printf("    signal from: timer expiration\n");
                    break;
                case SI_ASYNCIO:
                    printf("    signal from: asynchronous I/O completion\n");
                    break;
                case SI_MESGQ:
                    printf("    signal from: message arrival\n");
                    break;
                case SI_ASYNCNL:
                    printf("    signal from: asynch name lookup completion\n");
                    break;
                default:
                    printf("    see siginfo(2) about 'si_code' for more information\n");
                    break;
            } /* switch */

            printf("    signal number:       %d\n", info_p->si_signo);
            printf("    sender uid:          %d\n", info_p->si_uid);
            printf("    sender pid:          %d\n", info_p->si_pid);
            fflush(stdout);
            printf("    error (errno) was:   %d\n", info_p->si_errno);
            printf("    si_code:             %d\n", info_p->si_code);

            /*
             * process list.
             * When suffering from a SIG storm, this process might get killed.
             * can't protect the child across an exec(2).
             */
/*
            printf("launch[->unreliable] ps(1):\n %s\n", cmd); fflush(stdout);
*/
            /* system(cmd); */

    } else /* info_p == NULL */ {
            printf("    signal from (user/kernel): kernel\n");
            printf("    signal number:       %d\n", sig);
    }

    siglongjmp(env, 1); /* for a correct handler */
}



int main(int argc, char** argv) {
    struct sigaction sig_act;           /* ouselves */
    struct sigaction sig_act_chld;      /* a possible child */
    int i;
    int maxsig;

    printf("\ncurrent PID: [ %d ]\n\n", getpid());

    siginfo_p = (siginfo_t*)malloc(sizeof(siginfo_p));

    sigfillset(&sig_act.sa_mask);
    sig_act.sa_handler = signal_handler; /* not used with SA_SIGINFO */
    sig_act.sa_sigaction = signal_catcher; /* this is ours */
    sig_act.sa_flags = 0;
    sig_act.sa_flags |= SA_RESTART;
    sig_act.sa_flags |= SA_SIGINFO;     /* this is what we want */
    sig_act.sa_flags |= SA_NOCLDWAIT;   /* don't wait for children */

    sigemptyset(&sig_act_chld.sa_mask);
    sig_act_chld.sa_handler = signal_handler;
    sig_act_chld.sa_flags = 0;
    sig_act_chld.sa_flags |= SA_RESTART;


    maxsig = _NSIG;

    /*
     * catching SIGCONT doesn't really make sense, but just for information.
     */

    for (i=1; i<=maxsig; i++) {
        if (i != SIGKILL && i != SIGSTOP && i != SIGCONT) {
            if (sigaction(i, &sig_act, NULL) < 0) {
                fprintf(stderr, "sigaction failed for signal %d\n", i);
                exit(1);
            }
        }
    }


    while (1) {
        /* direkter Aufruf: == '0' , nach siglongjp() !=0 */
        if (sigsetjmp(env, 2) == 0) {
            printf("sigsetjmp() == 0\n"); fflush(stdout);
            if (argc >= 2) {
                /*
                 * start with a selftest
                 */

                kill(getpid(), SIGUSR2); /* */
            }
        } else /* sigsetjmp() != 0 */ {
            printf("handler returned\n"); fflush(stdout); /* */
        }
        pause();
    }
}
