/*
 *   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_06                            |
| ==================================================================== |
|                                                                      |
| Description:  Verifies that when multiple identical signals are sent |
|               to a process, at least one is queued.                  |
|                                                                      |
| Algorithm:    o  Block all signals from interrupting the process     |
|               o  Send MAXSIG signals to the current process          |
|               o  Sleep for a short time                              |
|               o  Verify that signals are pending                     |
|               o  Change the signal mask and suspend execution until  |
|                  a signal interrupts the process                     |
|               o  Verify that at least one signal was received        |
|                                                                      |
| System calls: The following system calls are tested:                 |
|                                                                      |
|               sigprocmask () - Sets the current signal mask          |
|               sigismember () - Creates and manipulates signal masks  |
|               sigfillset () - Creates and manipulates signal masks   |
|               sigpending () - Returns a set of signals that are      |
|                               blocked from delivery                  |
|               sigsuspend () - Automatically changes the set of       |
|                               blocked signals and waits for a signal |
|               raise () - Sends a signal to the executing program     |
|                                                                      |
| Usage:        signal_test_06                                         |
|                                                                      |
| To compile:   cc -o signal_test_06 signal_test_06                    |
|                                                                      |
| Last update:   Ver. 1.2, 2/7/94 23:24:14                           |
|                                                                      |
| Change Activity                                                      |
|                                                                      |
|   Version  Date    Name  Reason                                      |
|    0.1     122193  DJK   Wrote initial test for AIX version 4.1      |
|    1.2     020794  DJK   Move to "prod" directory                    |
|                                                                      |
+---------------------------------------------------------------------*/

#define MAXSIG 1024*1024	/* Number of signals sent to process */

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

#ifdef _LINUX_
// bits/signum.h defines _NSIG as 64
#define SIGMAX 64
#endif

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

/* Global variables */
int signals_received = 0;

/*---------------------------------------------------------------------+
|                               main ()                                |
| ==================================================================== |
|                                                                      |
| Function:  Main program  (see prolog for more details)               |
|                                                                      |
+---------------------------------------------------------------------*/
int main(int argc, char **argv)
{
	sigset_t newmask,	/* New signal mask */
	 oldmask,		/* Initial signal mask */
	 pendmask;		/* Pending signal mask */
	int i;			/* Loop index */

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

	/* Set up our signal handler */
	init_sig();

	/*
	 * Block ALL signals from interrupting the process
	 */
	printf("\tBlock all signals from interrupting the process\n");
	if (sigfillset(&newmask) < 0)
		error("sigfillset failed", __LINE__);
	if (sigprocmask(SIG_SETMASK, &newmask, &oldmask) < 0)
		error("sigprocmask failed", __LINE__);

	/*
	 * Send MAXSIG signals to the current process -- since ALL of the
	 * signals are blocked, none of the signals should interrupt the
	 * process
	 */
	printf("\n\tSend MAX (%d) SIGUSR1 signals to the process...\n", MAXSIG);
	for (i = 0; i < MAXSIG; i++)
		raise(SIGUSR1);

	/*
	 *  Sleep for a short time and the check to ensure that a SIGUSR1
	 *  signal is pending
	 */
	sleep(2);

	printf("\n\tEnsure at least one SIGUSR1 signal is pending\n");
	if (sigpending(&pendmask) < 0)
		error("sigpending failed", __LINE__);

	if (sigismember(&pendmask, SIGUSR1) == 0)
		error("sent multiple SIGUSR1 signals to process, "
		      "yet none are pending!", __LINE__);

	/*
	 * Change the signal mask to allow signals to interrupt the process
	 * and then suspend execution until a signal reaches the process
	 *
	 * Then verify that at least one signal was received
	 */
	printf("\n\tChange signal mask & wait for SIGUSR1 signal\n");
	if (sigsuspend(&oldmask) != -1 || errno != 4)
		error("sigsuspend failed", __LINE__);

	if (signals_received != 1) {
		printf("Signals are queued!  Sent %d signals, "
		       "while %d were queued\n", MAXSIG, signals_received);
	}

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

/*---------------------------------------------------------------------+
|                               handler ()                             |
| ==================================================================== |
|                                                                      |
| Function:  Catches signals and aborts the program if a signal other  |
|            than SIGUSR1 was received                                 |
|                                                                      |
| Updates:   signals_received - global variable indicating the number  |
|                               of SIGUSR1 signals received            |
|                                                                      |
| Returns:   Aborts if an unexpected signal is caught                  |
|                                                                      |
+---------------------------------------------------------------------*/
void handler(int signal, int code, struct sigcontext *scp)
{
	char msg[256];		/* Buffer for error message */

	if (signal != SIGUSR1) {
		sprintf(msg, "unexpected signal (%d)", signal);
		error(msg, __LINE__);
	}

	printf("\tcaught SIGUSR1 (%d) signal\n", signal);
	signals_received++;
}

/*---------------------------------------------------------------------+
|                             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__);
		}
	}
}

/*---------------------------------------------------------------------+
|                             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);
}
