blob: b72349c5435290956da69f233e7371ca7886f292 [file] [log] [blame]
/*
* Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* Further, this software is distributed without any warranty that it is
* free of the rightful claim of any third person regarding infringement
* or the like. Any license provided herein, whether implied or
* otherwise, applies only to this software file. Patent licenses, if
* any, provided herein do not apply to combinations of this program with
* other software, or any other product whatsoever.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
* Mountain View, CA 94043, or:
*
* http://www.sgi.com
*
* For further information regarding this notice, see:
*
* http://oss.sgi.com/projects/GenInfo/NoticeExplan/
*
*/
/* $Id: sighold02.c,v 1.11 2009/08/28 13:53:04 vapier Exp $ */
/*****************************************************************************
* OS Test - Silicon Graphics, Inc. Eagan, Minnesota
*
* TEST IDENTIFIER : sighold02 Holding all signals.
*
* PARENT DOCUMENT : sghtds01 sighold system call (CRAY X-MP and CRAY-1 only)
*
* AUTHOR : Bob Clark
*
* CO-PILOT : Barrie Kletscher
*
* DATE STARTED : 9/26/86
*
* TEST ITEMS
*
* 1. sighold action to turn off the receipt of all signals was done
* without error.
* 2. After signals were held, and sent, no signals were trapped.
*
* SPECIAL PROCEDURAL REQUIRMENTS
*
* The program must be linked with tst_res.o and parse_opts.o.
*
* DETAILED DESCRIPTION
*
* set up pipe for parent/child communications
* fork off a child process
*
* PARENT:
* set up for unexpected signals
* wait for child to send ready message over pipe
* issue a result for sighold
* send all catchable signals to child
* write to pipe, tell child all signals sent
* read pipe to get result from child
* wait for child to terminate
*
* CHILD:
* set up to catch all signals
* hold signals with sighold()
* send parent sighold results via pipe
* wait for signals to arrive while reading pipe
* write to pipe telling parent which signals were received if any.
*
***************************************************************************/
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "test.h"
/* Needed for NPTL */
#define SIGCANCEL 32
#define SIGTIMER 33
#ifdef _CRAYT3E
#define CRAYT3E 1
#else
#define CRAYT3E 0
#endif
#ifdef sgi
#define SGI 1
#else
#define SGI 0
#endif
#ifdef __linux__
/* glibc2.2 definition needs -D_XOPEN_SOURCE, which breaks other things. */
extern int sighold(int __sig);
#endif
/* ensure NUMSIGS is defined */
#ifndef NUMSIGS
#define NUMSIGS NSIG
#endif
#define CHILD_EXIT(VAL) ((VAL >> 8) & 0377) /* exit value of child process */
#define MAXMESG 150 /* the size of the message string */
#define TIMEOUT 2 /* time used in the alarm calls as backup */
char *TCID = "sighold02";
int TST_TOTAL = 2;
char signals_received[MAXMESG];
int pid; /* process id of child */
int Fds1[2]; /* file descriptors for pipe - child 2 parent */
int Fds2[2]; /* file descriptors for pipe - parent 2 child */
#define PARENTSREADFD Fds1[0]
#define CHILDSWRITEFD Fds1[1]
#define CHILDSREADFD Fds2[0]
#define PARENTSWRITEFD Fds2[1]
struct pipe_packet {
int result;
char mesg[MAXMESG];
struct tblock rtimes;
} p_p;
void do_child(void);
void setup(void);
void cleanup(void);
static void getout(void);
static void timeout(int sig);
static int read_pipe(int fd);
static int write_pipe(int fd);
static int setup_sigs(char *mesg);
static void handle_sigs(int sig);
static int set_timeout(char *mesg);
static void clear_timeout(void);
int Timeout = 0;
/***********************************************************************
* MAIN
***********************************************************************/
int main(int ac, char **av)
{
int term_stat; /* child return status */
int sig; /* current signal */
int lc;
const char *msg;
/***************************************************************
* parse standard options, and exit if there is an error
***************************************************************/
if ((msg = parse_opts(ac, av, NULL, NULL)) != NULL) {
tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
}
#ifdef UCLINUX
maybe_run_child(&do_child, "dd", &CHILDSWRITEFD, &CHILDSREADFD);
#endif
/***************************************************************
* perform global setup for test
***************************************************************/
setup();
/***************************************************************
* check looping state if -c option given
***************************************************************/
for (lc = 0; TEST_LOOPING(lc); lc++) {
tst_count = 0;
signals_received[0] = '\0';
/*
* fork off a child process
*/
if ((pid = FORK_OR_VFORK()) < 0) {
tst_brkm(TBROK | TERRNO, cleanup, "fork() failed");
} else if (pid > 0) {
/* PARENT PROCESS - first set up for unexpected signals */
tst_sig(FORK, DEF_HANDLER, getout);
/* wait for "ready" message from child */
if (read_pipe(PARENTSREADFD) != 0) {
/* read_pipe() failed. */
tst_brkm(TBROK, getout, "%s", p_p.mesg);
}
if (STD_TIMING_ON) {
/*
* Insure a running total for multiple loop iterations
*/
tblock.tb_total += p_p.rtimes.tb_total;
if (p_p.rtimes.tb_min < tblock.tb_min)
tblock.tb_min = p_p.rtimes.tb_min;
if (p_p.rtimes.tb_max > tblock.tb_max)
tblock.tb_max = p_p.rtimes.tb_max;
tblock.tb_count += p_p.rtimes.tb_count;
}
/* check for ready message */
if (p_p.result != TPASS) {
/* child setup did not go well */
tst_brkm(p_p.result, getout, "%s", p_p.mesg);
} else {
tst_resm(p_p.result, "%s", p_p.mesg);
}
/*
* send signals to child and see if it holds them
*/
for (sig = 1; sig < NUMSIGS; sig++) {
if ((sig == 41) && !CRAYT3E && !SGI) {
sig = 42; /* skip over SIGPEFAILURE for non-CRAYT3E systems */
}
if ((sig != SIGCLD) && (sig != SIGKILL) &&
(sig != SIGALRM) && (sig != SIGSTOP)
&& (sig != SIGCANCEL)
&& (sig != SIGTIMER)
#ifdef SIGRECOVERY
&& (sig != SIGRECOVERY)
#endif
#ifdef SIGRESTART
&& (sig != SIGRESTART)
#endif
#ifdef SIGNOBDM
&& (sig != SIGNOBDM)
#endif
#ifdef SIGPTINTR
&& (sig != SIGPTINTR)
#endif
) {
if (kill(pid, sig) < 0) {
tst_brkm(TBROK | TERRNO, NULL,
"kill(%d, %d) failed",
pid, sig);
getout();
}
}
}
/*
* Tell child that all signals were sent.
*/
p_p.result = TPASS;
strcpy(p_p.mesg, "All signals were sent");
write_pipe(PARENTSWRITEFD);
/*
* Get childs reply about received signals.
*/
if (read_pipe(PARENTSREADFD) < 0) {
tst_brkm(TBROK, getout, "%s", p_p.mesg);
}
tst_resm(p_p.result, "%s", p_p.mesg);
/*
* wait for child
*/
if (wait(&term_stat) < 0) {
tst_brkm(TBROK, getout, "wait() failed");
}
} else {
/*
* CHILD PROCESS - set up to catch signals.
*/
#ifdef UCLINUX
if (self_exec(av[0], "dd", CHILDSWRITEFD, CHILDSREADFD)
< 0) {
tst_brkm(TBROK | TERRNO, cleanup,
"self_exec() failed");
}
#else
do_child();
#endif
}
}
cleanup();
tst_exit();
}
/*****************************************************************************
* do_child()
****************************************************************************/
void do_child(void)
{
int rv; /* function return value */
int sig; /* current signal */
int cnt;
p_p.result = TPASS;
/* set up signal handlers for the signals */
if (setup_sigs(p_p.mesg) < 0) {
p_p.result = TBROK;
} else {
/* all set up to catch signals, now hold them */
for (cnt = 0, sig = 1; sig < NUMSIGS; sig++) {
if ((sig == 41) && !CRAYT3E && !SGI) {
sig = 42; /* skip over SIGPEFAILURE for non-CRAYT3E systems */
}
if ((sig != SIGCLD) && (sig != SIGKILL) &&
(sig != SIGALRM) && (sig != SIGSTOP)
#ifdef SIGNOBDM
&& (sig != SIGNOBDM)
#endif
&& (sig != SIGCANCEL) && (sig != SIGTIMER)
) {
cnt++;
TEST(sighold(sig));
rv = TEST_RETURN;
if (rv != 0) {
/* THEY say sighold ALWAYS returns 0 */
p_p.result = TFAIL;
(void)sprintf(p_p.mesg,
"sighold(%d) failed, rv:%d, errno:%d",
sig, rv, errno);
break;
}
}
}
if (STD_TIMING_ON) {
p_p.rtimes = tblock;
}
if (p_p.result == TPASS) {
sprintf(p_p.mesg,
"Sighold called without error for %d of %d signals",
cnt, NUMSIGS - 1);
}
}
/*
* write to parent (if not READY, parent will BROK) and
* wait for parent to send signals. The timeout clock is set so
* that we will not wait forever - if sighold() did its job, we
* will not receive the signals. If sighold() blew it we will
* catch a signal and the interrupt handler will exit(1).
*/
#if debug
printf("child: %d writing to parent fd:%d\n", getpid(), CHILDSWRITEFD);
#endif
if (write_pipe(CHILDSWRITEFD) < 0 || p_p.result != TPASS) {
exit(2);
}
/*
* Read pipe from parent, that will tell us that all signals were sent
*/
if (read_pipe(CHILDSREADFD) != 0) {
p_p.result = TBROK;
strcpy(p_p.mesg, "read() pipe failed");
} else if (signals_received[0] == '\0') {
p_p.result = TPASS;
strcpy(p_p.mesg,
"No signals trapped after being sent by parent");
} else {
p_p.result = TFAIL;
sprintf(p_p.mesg, "signals received: %s", signals_received);
}
if (write_pipe(CHILDSWRITEFD) < 0) {
exit(2);
}
/* exit back to parent */
if (p_p.result == TPASS)
exit(0);
else
exit(1);
}
/*****************************************************************************
* read_pipe() : read data from pipe and return in buf. If an error occurs
* put message in mesg and return NULL. Note: this routine sets a
* timeout signal in case the pipe is blocked.
****************************************************************************/
int read_pipe(int fd)
{
int ret = -1;
#ifdef debug
printf("read_pipe: %d entering, fd = %d...\n", getpid(), fd);
#endif
/* set timeout alarm in case the pipe is blocked */
if (set_timeout(p_p.mesg) < 0) {
/* an error occured, message in mesg */
return -1;
}
ret = read(fd, (char *)&p_p, sizeof(struct pipe_packet));
#ifdef debug
printf("read_pipe: %d read() completed ret=%d\n", getpid(), ret);
#endif
clear_timeout();
if (Timeout) {
(void)sprintf(p_p.mesg,
"read() pipe failed -timed out after %d seconds.",
TIMEOUT);
return -1;
}
#ifdef debug
printf("read_pipe: received %s.\n", p_p.mesg);
#endif
return 0;
}
/*****************************************************************************
* write_pipe(msg) : write p_p to pipe. If it fails, put message in
* mesg and return -1, else return 0.
****************************************************************************/
static int write_pipe(int fd)
{
#ifdef debug
printf("write_pipe: sending result:%d, mesg:%s.\n", p_p.result,
p_p.mesg);
#endif
if (write(fd, (char *)&p_p, sizeof(struct pipe_packet)) < 0) {
if (pid)
tst_brkm(TBROK | TERRNO, getout, "write() pipe failed");
return -1;
}
#ifdef debug
printf("write_pipe: %d complete successfully, fd:%d.\n", getpid(), fd);
#endif
return 0;
}
/*****************************************************************************
* set_timeout() : set alarm to signal process after the period of time
* indicated by TIMEOUT. If the signal occurs, the routine timeout()
* will be executed. If all goes ok, return 0, else load message
* into mesg and return -1.
****************************************************************************/
static int set_timeout(char *mesg)
{
if (signal(SIGALRM, timeout) == SIG_ERR) {
(void)sprintf(mesg,
"signal() failed for signal %d. error:%d %s.",
SIGALRM, errno, strerror(errno));
return (-1);
}
#if debug
printf("set_timeout()...\n");
#endif
Timeout = 0;
(void)alarm(TIMEOUT);
return 0;
}
/*****************************************************************************
* clear_timeout() : turn off the alarm so that SIGALRM will not get sent.
****************************************************************************/
static void clear_timeout(void)
{
(void)alarm(0);
Timeout = 0;
}
/*****************************************************************************
* timeout() : this routine is executed when the SIGALRM signal is
* caught. It does nothing but return - if executed during a read()
* system call, a -1 will be returned by the read().
****************************************************************************/
static void timeout(int sig)
{
#ifdef debug
printf("timeout: sigalrm caught.\n");
#endif
Timeout = 1;
}
/*****************************************************************************
* setup_sigs() : set child up to catch all signals. If there is
* trouble, write message in mesg and return -1, else return 0.
****************************************************************************/
static int setup_sigs(char *mesg)
{
int sig;
/* set up signal handler routine */
for (sig = 1; sig < NUMSIGS; sig++) {
if ((sig != SIGCLD) && (sig != SIGKILL) &&
(sig != SIGALRM) && (sig != SIGSTOP)
#ifdef SIGRESTART
&& (sig != SIGRESTART)
#endif
#ifdef SIGRECOVERY
&& (sig != SIGRECOVERY)
#endif
#ifdef SIGSWAP
&& (sig != SIGSWAP)
#endif
&& (sig != SIGCANCEL) && (sig != SIGTIMER)
) {
if (signal(sig, handle_sigs) == SIG_ERR) {
/* set up mesg to send back to parent */
(void)sprintf(mesg,
"signal() failed for signal %d. error:%d %s.",
sig, errno, strerror(errno));
return (-1);
}
}
}
return 0;
}
/*****************************************************************************
* handle_sigs() : interrupt handler for all signals. This will be run
* if the child process catches a signal (resulting in
* a test item FAIL if tst_res has not yet been called). The parent
* detects this situation by a child exit value of 1.
****************************************************************************/
static void handle_sigs(int sig)
{
char string[10];
#ifdef debug
printf("child: handle_sigs: caught signal %d.\n", sig);
#endif
sprintf(string, " %d", sig);
strcat(signals_received, string);
return;
}
/*****************************************************************************
* getout() : attempt to kill child process and call cleanup().
****************************************************************************/
static void getout(void)
{
if (kill(pid, SIGKILL) < 0)
tst_resm(TWARN | TERRNO, "kill(%d) failed", pid);
cleanup();
}
/***************************************************************
* setup() - performs all ONE TIME setup for this test.
***************************************************************/
void setup(void)
{
tst_sig(FORK, DEF_HANDLER, cleanup);
/* set up pipe for child sending to parent communications */
if (pipe(Fds1) < 0)
tst_brkm(TBROK | TERRNO, cleanup, "pipe() failed");
/* set up pipe for parent sending to child communications */
if (pipe(Fds2) < 0)
tst_brkm(TBROK | TERRNO, cleanup, "pipe() failed");
#if debug
printf("child 2 parent Fds1[0] = %d, Fds1[1] = %d\n", Fds1[0], Fds1[1]);
printf("parent 2 child Fds2[0] = %d, Fds2[1] = %d\n", Fds2[0], Fds2[1]);
#endif
TEST_PAUSE;
}
/***************************************************************
* cleanup() - performs all ONE TIME cleanup for this test at
* completion or premature exit.
***************************************************************/
void cleanup(void)
{
}