/*
 *   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_03                             |
| ==================================================================== |
|                                                                      |
| Description:  Block a critical section from receiving signals.       |
|                                                                      |
| Algorithm:    o  Setup a signal-catching function                    |
|               o  Beginning of Critical section -- Set the process    |
|                  signal mask to block the SIGILL signal              |
|               o  Send SIGILL signal to the process to insure that    |
|                  the signal is blocked during the critical section   |
|               o  Preform the critical section code (merely sleep     |
|                  for testing purposes)                               |
|               o  Verify that the signal was blocked                  |
|               o  End of Critical section -- unblock the signal and   |
|                  suspend he 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:                 |
|                                                                      |
|               sigsetmask () - Sets the current signal mask           |
|               sigblock () - Sets the current signal mask             |
|               sigvec () - Specify the action to take upon delivery   |
|                           of a signal.                               |
|               raise () - Sends a signal to the executing program     |
|                                                                      |
| Usage:        signal_test_03                                         |
|                                                                      |
| To compile:   cc -o signal_test_03 signal_test_03                    |
|                                                                      |
| Last update:   Ver. 1.2, 2/7/94 23:23:34                           |
|                                                                      |
| Change Activity                                                      |
|                                                                      |
|   Version  Date    Name  Reason                                      |
|    0.1     050689  CTU   Initial version                             |
|    1.2     112293  DJK   Rewrite for AIX version 4.1                 |
|                                                                      |
+---------------------------------------------------------------------*/

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

#define MASK(sig)  (1 << ((sig) - 1))
#define MAXTIME	2		/* MAX timeout (minutes) */

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

#include "signals.h"

/* Function prototypes */
void handler(int);
void init_sig_vec();
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)
{
	int timeout = MAXTIME * 60;	/* Number sec to wait for signal */

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

	/* Set up our signal handlers */
	init_sig_vec();

	/*
	 * Critical section - block SIGILL signal
	 *
	 * Block the SIGILL interrupt from interrupting the process
	 * with the sigprocmask () system function call.
	 *
	 * Send the SIGILL interrupt to the process in an attempt to
	 * disrupt the critial section -- the signal should be blocked.
	 * Wait one second to insure that the signal has plenty of time
	 * to reach the process.
	 */
#ifdef _LINUX_
	sigset_t mask;
	sigemptyset(&mask);
	sigaddset(&mask, SIGILL);
	sigprocmask(SIG_BLOCK, &mask, NULL);
#else
	if (sigblock(MASK(SIGILL)) < 0)
		sys_error("sigblock failed", __LINE__);
#endif

	printf("\t(BEGIN) Critial section\n");

	/* Critial section */
	sleep(1);

	/*
	 * End of critical section - ensure SIGILL signal was not received
	 *
	 * Check to insure that the signal handler has not caught any signals,
	 * and then unblock all of the signals with the sigsetmask system
	 * function call.
	 */
	if (signals_received > 0)
		error
		    ("received an unexpected signal during the critical section",
		     __LINE__);

	printf("\n\t(END) Critial section\n");

#ifdef _LINUX_
	sigemptyset(&mask);
	sigprocmask(SIG_SETMASK, &mask, NULL);
#else
	if (sigsetmask(0) < 0)
		sys_error("sigsetmask failed", __LINE__);
#endif
	raise(SIGILL);

	/*
	 * Upon unblocking the signals, should receive the SIGILL signal.
	 * Verify that it indeed is caught.
	 */
	while (signals_received == 0 && --timeout) {
		printf(".");
		fflush(stdout);
		sleep(1);
	}

	if (timeout == 0)
		error
		    ("failed to receive SIGILL signal after unblocking signals",
		     __LINE__);

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

/*---------------------------------------------------------------------+
|                           init_sig_vec ()                            |
| ==================================================================== |
|                                                                      |
| 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:   Nothing                                                   |
|                                                                      |
+---------------------------------------------------------------------*/
void init_sig_vec()
{
	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.  This function is called each   |
|            time a non-blocked signal is received by the process.     |
|                                                                      |
|            Increment the global variable (signals_received) for      |
|            each received signal.                                     |
|                                                                      |
| Returns:   Nothing                                                   |
|                                                                      |
+---------------------------------------------------------------------*/
void handler(int signal)
{
	if (signal == SIGILL)
		signals_received++;
	printf("\treceived signal: (%s)\n", signames[signal]);
}

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