/*
 *   Copyright (C) Bull S.A. 1996
 *   Copyright (c) International Business Machines  Corp., 2001
 *
 *   This program is free software;  you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program;  if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
/*---------------------------------------------------------------------+
|                          signal_test_02                              |
| ==================================================================== |
|                                                                      |
| Description:  Simplistic test to verify the signal system function   |
|               calls:                                                 |
|                                                                      |
|               o  Setup a signal-catching function for every possible |
|                  signal.                                             |
|               o  Send signals to the process and verify that they    |
|                  were received by the signal-catching function.      |
|               o  Block a few signals by changing the process signal  |
|                  mask.  Send signals to the process and verify that  |
|                  they indeed were blocked.                           |
|               o  Add additional signals to the process signal mask.  |
|                  Verify that they were blocked too.                  |
|               o  Change the process signal mask to unblock one       |
|                  signal and suspend execution of the process until   |
|                  the signal is received.  Verify that the unblocked  |
|                  signal is received.                                 |
|                                                                      |
| System calls: The following system calls are tested:                 |
|                                                                      |
|               sigprocmask () - Sets the current signal mask          |
|               sigemptyset () - Creates and manipulates signal masks  |
|               sigfillset () - Creates and manipulates signal masks   |
|               sigaddset () - Creates and manipulates signal masks    |
|               sigdelset () - Creates and manipulates signal masks    |
|               sigsuspend () - Atomically changes the set of blocked  |
|                               signals and waits for a signal         |
|               sigaction () - Specifies the action to take upon       |
|                              delivery of a signal                    |
|               kill () - Sends a signal to a process                  |
|                                                                      |
| Usage:        signal_test_03                                         |
|                                                                      |
| To compile:   cc -o signal_test_03 signal_test_03                    |
|                                                                      |
| Last update:   Ver. 1.2, 2/7/94 23:17:48                             |
|                                                                      |
| Change Activity                                                      |
|                                                                      |
|   Version  Date    Name  Reason                                      |
|    0.1     050689  CTU   Initial version                             |
|    0.2     112293  DJK   Rewrite for AIX version 4.1                 |
|    1.2     020794  DJK   Move to "prod" directory                    |
|    1.3     060501  VHM   Port to work in linux                       |
|                                                                      |
+---------------------------------------------------------------------*/

#define SIGMAX 64		/* What should this number really be? _NSIG from bits/signum.h maybe? */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

/* Macro for specifying signal masks */
#define MASK(sig)  (1 << ((sig) - 1))

#include "signals.h"

/* Function prototypes */
void init_sig();
void handler(int sig);		//, int code, struct sigcontext *);
void reset_valid_sig();
void sys_error(const char *, int);
void error(const char *, int);

/* Define an array for verifying received signals */
int valid_sig[SIGMAX + 1];

/*---------------------------------------------------------------------+
|                               main ()                                |
| ==================================================================== |
|                                                                      |
| Function:  Main program  (see prolog for more details)               |
|                                                                      |
+---------------------------------------------------------------------*/
int main(int argc, char **argv)
{
	sigset_t setsig,	/* Initial signal mask */
	 newsetsig;		/* Second signal mask */
	pid_t pid = getpid();	/* Process ID (of this process) */

	/* Print out program header */
	printf("%s: IPC TestSuite program\n\n", *argv);

	/*
	 * Establish signal handler for each signal & reset "valid signals"
	 * array
	 */
	init_sig();
	reset_valid_sig();

	sigemptyset(&setsig);
	if (sigprocmask(SIG_SETMASK, &setsig, (sigset_t *) NULL) < 0)
		sys_error("sigprocmask failed", __LINE__);

	/*
	 * Send SIGILL, SIGALRM & SIGIOT signals to this process:
	 *
	 * First indicate which signals the signal handler should expect
	 * by setting the corresponding valid_sig[] array fields.
	 *
	 * Then send the signals to this process.
	 *
	 * And finally verify that the signals were caught by the signal
	 * handler by checking to see if the corresponding valid_sig[] array
	 * fields were reset.
	 */
	printf("\tSend SIGILL, SIGALRM, SIGIOT signals to process\n");
	valid_sig[SIGILL] = 1;
	valid_sig[SIGALRM] = 1;
	valid_sig[SIGIOT] = 1;

	kill(pid, SIGILL);
	kill(pid, SIGALRM);
	kill(pid, SIGIOT);

	if (valid_sig[SIGILL])
		error("failed to receive SIGILL signal!", __LINE__);
	if (valid_sig[SIGALRM])
		error("failed to receive SIGALRM signal!", __LINE__);
	if (valid_sig[SIGIOT])
		error("failed to receive SIGIOT signal!", __LINE__);

	/*
	 * Block SIGILL, SIGALRM & SIGIOT signals:
	 *
	 * First initialize the signal set so that all signals are excluded,
	 * then individually add the signals to block to the signal set.
	 *
	 * Then change the process signal mask with sigprocmask (SIG_SETMASK).
	 *
	 * Verify that the desired signals are blocked from interrupting the
	 * process, by sending both blocked and unblocked signals to the
	 * process.  Only the unblocked signals should interrupt the process.
	 */
	printf("\n\tBlock SIGILL, SIGALRM, SIGIOT signals, "
	       "and resend signals + others\n");
	sigemptyset(&setsig);

	if (sigaddset(&setsig, SIGIOT) < 0)
		sys_error("sigaddset (SIGIOT) failed", __LINE__);
	if (sigaddset(&setsig, SIGILL) < 0)
		sys_error("sigaddset (SIGILL) failed", __LINE__);
	if (sigaddset(&setsig, SIGALRM) < 0)
		sys_error("sigaddset (SIGALRM) failed", __LINE__);

	if (sigprocmask(SIG_SETMASK, &setsig, (sigset_t *) NULL) < 0)
		sys_error("sigaddset (SIGALRM) failed", __LINE__);

	valid_sig[SIGFPE] = 1;
	valid_sig[SIGTERM] = 1;
	valid_sig[SIGINT] = 1;

	kill(pid, SIGILL);
	kill(pid, SIGALRM);
	kill(pid, SIGIOT);
	kill(pid, SIGFPE);
	kill(pid, SIGTERM);
	kill(pid, SIGINT);

	if (valid_sig[SIGFPE])
		sys_error("failed to receive SIGFPE signal!", __LINE__);
	if (valid_sig[SIGTERM])
		sys_error("failed to receive SIGTERM signal!", __LINE__);
	if (valid_sig[SIGINT])
		sys_error("failed to receive SIGINT signal!", __LINE__);

	/*
	 * Block additional SIGFPE, SIGTERM & SIGINT signals:
	 *
	 * Create an other signal set to contain the additional signals to block
	 * and add the signals to block to the signal set.
	 *
	 * Change the process signal mask to block the additional signals
	 * with the sigprocmask (SIG_BLOCK) function.
	 *
	 * Verify that all of the desired signals are now blocked from
	 * interrupting the process.  None of the specified signals should
	 * interrupt the process until the process signal mask is changed.
	 */
	printf("\n\tBlock rest of signals\n");
	sigemptyset(&newsetsig);

	sigaddset(&newsetsig, SIGFPE);
	sigaddset(&newsetsig, SIGTERM);
	sigaddset(&newsetsig, SIGINT);

	if (sigprocmask(SIG_BLOCK, &newsetsig, &setsig) < 0)
		sys_error("sigprocmask failed", __LINE__);

	kill(pid, SIGILL);
	kill(pid, SIGALRM);
	kill(pid, SIGIOT);
	kill(pid, SIGFPE);
	kill(pid, SIGTERM);
	kill(pid, SIGINT);

	/*
	 * Wait two seconds just to make sure that none of the specified
	 * signals interrupt the process (They should all be blocked).
	 */
	sleep(2);

	/*
	 * Change the process signal mask:
	 *
	 * Now specifiy a new process signal set to allow the SIGINT signal
	 * to interrupt the process.  Create the signal set by initializing
	 * the signal set with sigfillset () so that all signals are included
	 * in the signal set, then remove the SIGINT signal from the set with
	 * sigdelset ().
	 *
	 * Force the  process to suspend execution until delivery of an
	 * unblocked signal (SIGINT in this case) with sigsuspend ().
	 *
	 * Additionally, verify that the SIGINT signal was received.
	 */
	valid_sig[SIGINT] = 1;

	printf
	    ("\n\tChange signal mask & wait until signal interrupts process\n");
	if (sigfillset(&setsig) < 0)
		sys_error("sigfillset failed", __LINE__);
	if (sigdelset(&setsig, SIGINT) < 0)
		sys_error("sigdelset failed", __LINE__);
	if (sigsuspend(&setsig) != -1 || errno != 4)
		sys_error("sigsuspend failed", __LINE__);

	if (valid_sig[SIGINT])
		error("failed to receive SIGIOT signal!", __LINE__);

	/* Program completed successfully -- exit */
	printf("\nsuccessful!\n");

	return (0);
}

/*---------------------------------------------------------------------+
|                             init_sig ()                              |
| ==================================================================== |
|                                                                      |
| Function:  Initialize the signal vector for ALL possible signals     |
|            (as defined in /usr/include/sys/signal.h) except for      |
|            the following signals which cannot be caught or ignored:  |
|                                                                      |
|              o  SIGKILL (9)                                          |
|              o  SIGSTOP (17)                                         |
|              o  SIGCONT (19)                                         |
|                                                                      |
| Returns:   n/a                                                       |
|                                                                      |
+---------------------------------------------------------------------*/
void init_sig()
{
	struct sigaction invec;
	char msg[256];		/* Buffer for error message */
	int i;

	for (i = 1; i <= SIGMAX; i++) {

		/* Cannot catch or ignore the following signals */
#ifdef _IA64			/* SIGWAITING not supported, RESERVED */
		if ((i == SIGKILL) || (i == SIGSTOP) ||
		    (i == SIGCONT) || (i == SIGWAITING))
			continue;
#else
#ifdef _LINUX_
		if ((i == SIGKILL) || (i == SIGSTOP)
		    || ((i >= 32) && (i <= 34)))
			continue;
#else
		if (i == SIGKILL || i == SIGSTOP || i == SIGCONT)
			continue;
#endif
#endif

		invec.sa_handler = (void (*)(int))handler;
		sigemptyset(&invec.sa_mask);
		invec.sa_flags = 0;

		if (sigaction(i, &invec, (struct sigaction *)NULL) < 0) {
			sprintf(msg, "sigaction failed on signal %d", i);
			error(msg, __LINE__);
		}
	}
}

/*---------------------------------------------------------------------+
|                             handler ()                               |
| ==================================================================== |
|                                                                      |
| Function:  Signal catching function.  As specified in init_sig_vec() |
|            this function is automatically called each time a signal  |
|            is received by the process.                               |
|                                                                      |
|            Once receiving the signal, verify that the corresponding  |
|            signal was expected.                                      |
|                                                                      |
| Returns:   Aborts program if an unexpected signal was received.      |
|                                                                      |
+---------------------------------------------------------------------*/
void handler(int sig)		//, int code, struct sigcontext *scp)
{
	char msg[256];

	/* Check to insure that expected signal was received */
	if (valid_sig[sig]) {
		valid_sig[sig] = 0;
		printf("\treceived signal: (%s)\n", signames[sig]);
	} else {
		sprintf(msg, "unexpected signal (%d,%s)", sig,
			(sig < 32) ? signames[sig] : "unknown signal");
		error(msg, __LINE__);
	}
}

/*---------------------------------------------------------------------+
|                         reset_valid_sig ()                           |
| ==================================================================== |
|                                                                      |
| Function:  Reset the valid "signal" array                            |
|                                                                      |
| Returns:   n/a                                                       |
|                                                                      |
+---------------------------------------------------------------------*/
void reset_valid_sig()
{
	int i;

	for (i = 0; i < (SIGMAX + 1); i++)
		valid_sig[i] = 0;
}

/*---------------------------------------------------------------------+
|                             sys_error ()                             |
| ==================================================================== |
|                                                                      |
| Function:  Creates system error message and calls error ()           |
|                                                                      |
+---------------------------------------------------------------------*/
void sys_error(const char *msg, int line)
{
	char syserr_msg[256];

	sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
	error(syserr_msg, line);
}

/*---------------------------------------------------------------------+
|                               error ()                               |
| ==================================================================== |
|                                                                      |
| Function:  Prints out message and exits...                           |
|                                                                      |
+---------------------------------------------------------------------*/
void error(const char *msg, int line)
{
	fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
	exit(-1);
}
