1 /*
2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31 *
32 */
33 /* $Id: sigrelse01.c,v 1.14 2009/08/28 14:10:16 vapier Exp $ */
34 /*****************************************************************************
35 * OS Test - Silicon Graphics, Inc. Eagan, Minnesota
36 *
37 * TEST IDENTIFIER : sigrelse01 Releasing held signals.
38 *
39 * PARENT DOCUMENT : sgrtds01 sigrelse system call
40 *
41 * AUTHOR : Bob Clark
42 * : Rewrote 12/92 by Richard Logan
43 *
44 * CO-PILOT : Dave Baumgartner
45 *
46 * DATE STARTED : 10/08/86
47 *
48 * TEST ITEMS
49 *
50 * 1. sigrelse turns on the receipt of signals held by sighold.
51 *
52 * SPECIAL PROCEDURAL REQUIRMENTS
53 * None
54 *
55 * DETAILED DESCRIPTION
56 * set up pipe for parent/child communications
57 * fork off a child process
58 *
59 * parent():
60 * set up for unexpected signals
61 * wait for child to send ready message over pipe
62 * send all catchable signals to child process
63 * send alarm signal to speed up timeout
64 * wait for child to terminate and check exit value
65 *
66 * if exit value is EXIT_OK
67 * get message from pipe (contains array of signal counters)
68 * loop through array of signal counters and record any
69 * signals which were not caught once.
70 * record PASS or FAIL depending on what was found in the array.
71 *
72 * else if exit is SIG_CAUGHT then BROK (signal caught
73 * before released)
74 * else if exit is WRITE_BROK then BROK (write() to pipe failed)
75 * else if exit is HANDLE_ERR then BROK (error in child's
76 * signal handler)
77 * else unexpected exit value - BROK
78 *
79 * child():
80 * phase 1:
81 * set up to catch all catchable signals (exit SIG_CAUGHT
82 * if caught)
83 * hold each signal with sighold()
84 * send parent ready message if setup went ok.
85 * wait for signals to arrive - timeout if they don't
86 *
87 * phase 2:
88 * release each signal and wait a second for the handler to
89 * catch it.
90 * (the handler will record each signal it catches in an array
91 * and exit HANDLE_ERR if an error occurs)
92 *
93 * send array of counters back to parent for processing.
94 * exit EXIT_OK
95 * NOTES
96 * since child is executing system calls under test, no
97 * system call times are printed.
98 *
99 ***************************************************************************/
100
101 #include <sys/types.h>
102 #include <sys/wait.h>
103 #include <errno.h>
104 #include <fcntl.h>
105 #include <signal.h>
106 #include <stdlib.h>
107 #include <string.h>
108 #include <time.h>
109 #include <unistd.h>
110 #include "test.h"
111 #include "safe_macros.h"
112
113 #ifdef __linux__
114 /* glibc2.2 definition needs -D_XOPEN_SOURCE, which breaks other things. */
115 extern int sighold(int __sig);
116 extern int sigrelse(int __sig);
117 #endif
118
119 /* Needed for NPTL */
120 #define SIGCANCEL 32
121 #define SIGTIMER 33
122
123 void setup(void);
124 void cleanup(void);
125 static void parent(void);
126 static void child(void);
127 static void timeout(int sig);
128 static int setup_sigs(void);
129 static void handler(int sig);
130 static void wait_a_while(void);
131 static char *read_pipe(int fd);
132 static int write_pipe(int fd, char *msg);
133 static int set_timeout(void);
134 static void clear_timeout(void);
135 static void getout(void);
136 int choose_sig(int sig);
137
138 #define TRUE 1
139 #define FALSE 0
140
141 #ifndef DEBUG
142 #define DEBUG 0
143 #endif
144
145 #define CHILD_EXIT(VAL) ((VAL >> 8) & 0377) /* exit value of child process */
146 #define CHILD_SIG(VAL) (VAL & 0377) /* signal value of child proc */
147
148 #define MAXMESG 512 /* the size of the message string */
149
150 #define READY "ready" /* signal to parent that child is set up */
151
152 #define TIMEOUT 30 /* time (sec) used in the alarm calls */
153
154 /* child exit values */
155 #define EXIT_OK 0
156 #define SIG_CAUGHT 8
157 #define WRITE_BROK 16
158 #define HANDLE_ERR 32
159
160 int TST_TOTAL = 1; /* number of test items */
161
162 char *TCID = "sigrelse01"; /* test case identifier */
163 static char mesg[MAXMESG]; /* message buffer for tst_res */
164 static int pid; /* process id of child */
165 static int pipe_fd[2]; /* file descriptors for pipe parent read */
166 static int pipe_fd2[2]; /* file descriptors for pipe child read */
167 static int phase; /* flag for phase1 or phase2 of */
168 /* signal handler */
169 static int sig_caught; /* flag TRUE if signal caught */
170 /* (see wait_a_while ()) */
171
172 /* ensure that NUMSIGS is defined. */
173 #ifndef NUMSIGS
174 #define NUMSIGS NSIG
175 #endif
176
177 /* array of counters for signals caught by handler() */
178 static int sig_array[NUMSIGS];
179
180 /***********************************************************************
181 * M A I N
182 ***********************************************************************/
main(int argc,char ** argv)183 int main(int argc, char **argv)
184 {
185 int lc;
186
187 /* gcc -Wall complains about sig_caught not being ref'd because of the
188 external declarations. */
189 sig_caught = FALSE;
190
191 /*
192 * parse standard options
193 */
194 tst_parse_opts(argc, argv, NULL, NULL);
195
196 /*
197 * perform global setup for test
198 */
199 setup();
200
201 for (lc = 0; TEST_LOOPING(lc); lc++) {
202
203 tst_count = 0;
204
205 /*
206 * fork off a child process
207 */
208 if ((pid = tst_fork()) < 0)
209 tst_brkm(TBROK | TERRNO, cleanup, "fork() failed");
210 else if (pid > 0)
211 parent();
212 else
213 child();
214
215 }
216
217 cleanup();
218 tst_exit();
219
220 } /* end main */
221
222 /****************************************************************************
223 * parent() : wait for "ready" from child, send signals to child, wait for
224 * child to exit and report what happened.
225 ***************************************************************************/
parent(void)226 static void parent(void)
227 {
228 int term_stat; /* child return status */
229 int rv; /* function return value */
230 int sig; /* current signal number */
231 char *str; /* string returned from read_pipe() */
232 int *array; /* pointer to sig_array returned from child */
233 int fail = FALSE; /* flag indicating test item failure */
234 char big_mesg[MAXMESG * 6]; /* storage for big failure message */
235 int caught_sigs;
236
237 /* wait for "ready" message from child */
238 if ((str = read_pipe(pipe_fd[0])) == NULL) {
239 /* read_pipe() failed. */
240 tst_brkm(TBROK, getout, "%s", mesg);
241 }
242
243 if (strcmp(str, READY) != 0) {
244 /* child setup did not go well */
245 tst_brkm(TBROK, getout, "%s", str);
246 }
247
248 /*
249 * send signals to child and see if it holds them
250 */
251
252 for (sig = 1; sig < NUMSIGS; sig++) {
253 if (choose_sig(sig)) {
254 if (kill(pid, sig) < 0) {
255 if (errno == ESRCH) {
256 if (kill(pid, SIGTERM) < 0)
257 tst_brkm(TBROK | TERRNO, getout,
258 "kill(%d, %d) and kill(%d, SIGTERM) failed",
259 pid, sig, pid);
260 else
261 tst_brkm(TBROK | TERRNO, getout,
262 "kill(%d, %d) failed, but kill(%d, SIGTERM) worked",
263 pid, sig, pid);
264 } else
265 tst_brkm(TBROK | TERRNO, getout,
266 "kill(%d, %d) failed", pid,
267 sig);
268 }
269 }
270 }
271
272 if (write_pipe(pipe_fd2[1], READY) < 0) {
273 tst_brkm(TBROK | TERRNO, getout,
274 "Unable to tell child to go, write to pipe failed");
275 }
276
277 /*
278 * child is now releasing signals, wait and check exit value
279 */
280 SAFE_WAIT(getout, &term_stat);
281
282 /* check child's signal exit value */
283 if ((sig = CHILD_SIG(term_stat)) != 0)
284 /* the child was zapped by a signal */
285 tst_brkm(TBROK, cleanup, "Unexpected signal %d killed child",
286 sig);
287
288 /* get child exit value */
289
290 rv = CHILD_EXIT(term_stat);
291
292 switch (rv) {
293 case EXIT_OK:
294 /* sig_array sent back on pipe, check it out */
295 if ((array = (int *)read_pipe(pipe_fd[0])) == NULL) {
296 /* read_pipe() failed. */
297 tst_resm(TBROK, "%s", mesg);
298 break;
299 }
300 #if DEBUG > 1
301 for (sig = 1; sig < NUMSIGS; sig++) {
302 printf("array[%d] = %d\n", sig, array[sig]);
303 }
304 #endif
305 caught_sigs = 0;
306 for (sig = 1; sig < NUMSIGS; sig++) {
307 if (choose_sig(sig)) {
308 if (array[sig] != 1) {
309 /* sig was not caught or caught too many times */
310 (void)sprintf(mesg,
311 "\tsignal %d caught %d times (expected 1).\n",
312 sig, array[sig]);
313 (void)strcat(big_mesg, mesg);
314 fail = TRUE;
315 } else {
316 caught_sigs++;
317 }
318 }
319 } /* endfor */
320
321 if (fail == TRUE)
322 tst_resm(TFAIL, "%s", big_mesg);
323 else
324 tst_resm(TPASS,
325 "sigrelse() released all %d signals under test.",
326 caught_sigs);
327 break;
328
329 case TBROK:
330 /* get BROK message from pipe */
331 if ((str = read_pipe(pipe_fd[0])) == NULL) {
332 /* read_pipe() failed. */
333 tst_resm(TBROK, "%s", mesg);
334 break;
335 }
336
337 /* call tst_res: str contains the message */
338 tst_resm(TBROK, "%s", str);
339 break;
340 case SIG_CAUGHT:
341 /* a signal was caught before it was released */
342 tst_resm(TBROK, "A signal was caught before being released.");
343 break;
344 case WRITE_BROK:
345 /* the write() call failed in child's write_pipe */
346 tst_resm(TBROK, "write() pipe failed for child.");
347 break;
348 case HANDLE_ERR:
349 /* more than one signal tried to be handled at the same time */
350 tst_resm(TBROK, "Error occured in signal handler.");
351 break;
352 default:
353 tst_resm(TBROK, "Unexpected exit code %d from child", rv);
354 break;
355 }
356
357 } /* end of parent */
358
359 /****************************************************************************
360 * child() : hold signals, notify parent and wait for parent to send signals.
361 * If none were caught (sighold worked), release the signals one at a time
362 * and wait for them to be caught. Send results back to parent
363 * for processing.
364 ***************************************************************************/
child(void)365 static void child(void)
366 {
367 int rv; /* return value from sighold() and sigrelse() */
368 int sig; /* signal value */
369 int exit_val; /* exit value to send to parent */
370 char note[MAXMESG]; /* message buffer for pipe */
371 char *str;
372
373 phase = 1; /* tell handler that we do not want to catch signals */
374
375 /* set note to READY and if an error occurs, overwrite it */
376 (void)strcpy(note, READY);
377
378 /* set alarm in case something hangs */
379 if (set_timeout() < 0) {
380 /* an error occured - put mesg in note and send it back to parent */
381 (void)strcpy(note, mesg);
382
383 } else if (setup_sigs() < 0) {
384 /* an error occured - put mesg in note and send it back to parent */
385 (void)strcpy(note, mesg);
386
387 } else {
388 /* all set up to catch signals, now hold them */
389
390 for (sig = 1; sig < NUMSIGS; sig++) {
391 if (choose_sig(sig)) {
392 if ((rv = sighold(sig)) != 0) {
393 /* THEY say sighold ALWAYS returns 0 */
394 (void)sprintf(note,
395 "sighold did not return 0. rv:%d",
396 rv);
397 break;
398 }
399 }
400 }
401
402 }
403
404 /*
405 * send note to parent (if not READY, parent will BROK) and
406 * wait for parent to send signals. The timeout clock is set so
407 * that we will not wait forever - if sighold() did its job, we
408 * will not receive the signals. If sighold() blew it we will
409 * catch a signal and the interrupt handler will exit with a
410 * value of SIG_CAUGHT.
411 */
412 if (write_pipe(pipe_fd[1], note) < 0) {
413 /*
414 * write_pipe() failed. Set exit value to WRITE_BROK to let
415 * parent know what happened
416 */
417 clear_timeout();
418 exit(WRITE_BROK);
419 }
420
421 /*
422 * if we get to this point, all signals have been held and the
423 * timer has expired. Now what we want to do is release each
424 * signal and see if we catch it. If we catch all signals,
425 * sigrelse passed, else it failed.
426 */
427
428 phase = 2; /* let handler know we are now expecting signals */
429
430 #if DEBUG > 0
431 printf("child: PHASE II\n");
432 #endif
433
434 /* assume success and overwrite exit_val if an error occurs */
435 exit_val = EXIT_OK;
436
437 #if DEBUG > 0
438 printf("child: pid=%d waiting for parent's ready...\n", getpid());
439 #endif
440
441 /*
442 * wait for parent to tell us that sigals were all sent
443 */
444
445 /* wait for "ready" message from parent */
446 if ((str = read_pipe(pipe_fd2[0])) == NULL) {
447 /* read_pipe() failed. */
448 printf(" child: read_pipe failed\n");
449 exit(TBROK);
450 }
451
452 if (strcmp(str, READY) != 0) {
453 /* parent/pipe problem */
454 printf("child: didn't proper ready message\n");
455 exit(TBROK);
456 }
457
458 for (sig = 1; sig < NUMSIGS; sig++) {
459 if (choose_sig(sig)) {
460
461 /* all set up, release and catch a signal */
462
463 sig_caught = FALSE; /* handler sets it to TRUE when caught */
464 #if DEBUG > 1
465 printf("child: releasing sig %d...\n", sig);
466 #endif
467 if ((rv = sigrelse(sig)) != 0) {
468 /* THEY say sigrelse ALWAYS returns 0 */
469 (void)sprintf(note,
470 "sigrelse did not return 0. rv:%d",
471 rv);
472 exit_val = TBROK;
473 break;
474 }
475
476 /* give signal handler some time to process signal */
477 wait_a_while();
478 }
479
480 } /* endfor */
481
482 /*
483 * If we are error free so far...
484 * check the sig_array array for one occurence of
485 * each of the catchable signals. If this is true,
486 * then PASS, otherwise FAIL.
487 */
488
489 if (exit_val == EXIT_OK) {
490 (void)memcpy(note, (char *)sig_array,
491 sizeof(note) < sizeof(sig_array) ?
492 sizeof(note) : sizeof(sig_array));
493 }
494
495 /* send note to parent and exit */
496 if (write_pipe(pipe_fd[1], note) < 0) {
497 /*
498 * write_pipe() failed. Set exit value to WRITE_BROK to let
499 * parent know what happened
500 */
501 exit(WRITE_BROK);
502 }
503
504 exit(exit_val);
505
506 } /* end of child */
507
508 /*****************************************************************************
509 * setup_sigs() : set child up to catch all signals. If there is
510 * trouble, write message in mesg and return -1, else return 0.
511 * The signal handler has two functions depending on which phase
512 * of the test we are in. The first section is executed after the
513 * signals have been held (should not ever be used). The second
514 * section is executed after the signals have been released (should
515 * be executed for each signal).
516 ****************************************************************************/
setup_sigs(void)517 static int setup_sigs(void)
518 {
519 int sig;
520
521 /* set up signal handler routine */
522 for (sig = 1; sig < NUMSIGS; sig++) {
523 if (choose_sig(sig)) {
524 if (signal(sig, handler) == SIG_ERR) {
525 /* set up mesg to send back to parent */
526 (void)sprintf(mesg,
527 "signal() failed for signal %d. error:%d %s.",
528 sig, errno, strerror(errno));
529 return (-1);
530 }
531 }
532 }
533 return 0;
534
535 } /* end of setup_sigs */
536
537 /*****************************************************************************
538 * handler() : child's interrupt handler for all signals. The phase variable
539 * is set in the child process indicating what action is to be taken.
540 * The phase 1 section will be run if the child process catches a signal
541 * after the signal has been held resulting in a test item BROK.
542 * The parent detects this situation by a child exit value of SIG_CAUGHT.
543 * The phase 2 section will be run if the child process catches a
544 * signal after the signal has been released. All signals must be
545 * caught in order for a PASS.
546 ****************************************************************************/
handler(int sig)547 static void handler(int sig)
548 {
549 static int s = 0; /* semaphore so that we don't handle 2 */
550 /* sigs at once */
551 #if DEBUG > 1
552 printf("child: handler phase%d: caught signal %d.\n", phase, sig);
553 #endif
554
555 if (phase == 1) {
556 /* exit the child process with a value of -1 */
557 exit(SIG_CAUGHT);
558
559 } else {
560 /* phase 2 (error if s gets incremented twice) */
561 ++s;
562
563 if (s > 1) {
564 exit(HANDLE_ERR);
565 }
566
567 /* increment the array element for this signal */
568 ++sig_array[sig];
569 sig_caught = TRUE; /* flag for wait_a_while () */
570 --s;
571 }
572
573 return;
574
575 } /* end of handler */
576
577 /*****************************************************************************
578 * read_pipe() : read data from pipe and return in buf. If an error occurs
579 * put message in mesg and return NULL. Note: this routine sets a
580 * timeout signal in case the pipe is blocked.
581 ****************************************************************************/
read_pipe(int fd)582 static char *read_pipe(int fd)
583 {
584 static char buf[MAXMESG]; /* buffer for pipe read */
585 int ret;
586
587 #if DEBUG > 0
588 printf("read_pipe: pid=%d waiting...\n", getpid());
589 #endif
590
591 /* set timeout alarm in case the pipe is blocked */
592 if (set_timeout() < 0) {
593 /* an error occured, message in mesg */
594 return NULL;
595 }
596
597 ret = -1;
598 while (ret == -1) { /* while empty reads */
599 if ((ret = read(fd, buf, MAXMESG)) == 0) {
600 (void)sprintf(mesg, "read() pipe failed. error:%d %s.",
601 errno, strerror(errno));
602
603 clear_timeout();
604 return NULL;
605 }
606 }
607 clear_timeout();
608
609 #if DEBUG > 0
610 printf("read_pipe: pid=%d received: %s.\n", getpid(), buf);
611 #endif
612 return (buf);
613
614 } /* end of read_pipe */
615
616 /*****************************************************************************
617 * write_pipe(msg) : write msg to pipe. If it fails, put message in
618 * mesg and return -1, else return 0.
619 ****************************************************************************/
write_pipe(int fd,char * msg)620 static int write_pipe(int fd, char *msg)
621 {
622
623 #if DEBUG > 0
624 printf("write_pipe: pid=%d, sending %s.\n", getpid(), msg);
625 #endif
626
627 if (write(fd, msg, MAXMESG) < 0) {
628 (void)sprintf(mesg, "write() pipe failed. error:%d %s.",
629 errno, strerror(errno));
630
631 return (-1);
632 }
633 return 0;
634
635 } /* end of write_pipe */
636
637 /*****************************************************************************
638 * set_timeout() : set alarm to signal process after the period of time
639 * indicated by TIMEOUT. If the signal occurs, the routine timeout()
640 * will be executed. If all goes ok, return 0, else load message
641 * into mesg and return -1.
642 ****************************************************************************/
set_timeout(void)643 static int set_timeout(void)
644 {
645 if (signal(SIGALRM, timeout) == SIG_ERR) {
646 (void)sprintf(mesg,
647 "signal() failed for signal %d. error:%d %s.",
648 SIGALRM, errno, strerror(errno));
649 return (-1);
650 }
651
652 (void)alarm(TIMEOUT);
653 return 0;
654
655 } /* end of set_timeout */
656
657 /*****************************************************************************
658 * clear_timeout() : turn off the alarm so that SIGALRM will not get sent.
659 ****************************************************************************/
clear_timeout(void)660 static void clear_timeout(void)
661 {
662 (void)alarm(0);
663
664 } /* end of clear_timeout */
665
666 /*****************************************************************************
667 * timeout() : this routine is executed when the SIGALRM signal is
668 * caught. It does nothing but return - the read() on the pipe
669 * will fail.
670 ****************************************************************************/
timeout(int sig)671 static void timeout(int sig)
672 {
673 #if DEBUG > 0
674 printf("timeout: pid=%d sigalrm caught.\n", getpid());
675 #endif
676 }
677
678 /*****************************************************************************
679 * wait_a_while () : wait a while before returning.
680 ****************************************************************************/
wait_a_while(void)681 static void wait_a_while(void)
682 {
683 long btime;
684
685 btime = time(NULL);
686 while (time(NULL) - btime < TIMEOUT) {
687 if (sig_caught == TRUE)
688 break;
689 }
690 } /* end of wait_a_while */
691
getout(void)692 static void getout(void)
693 {
694 if (pid > 0 && kill(pid, SIGKILL) < 0)
695 tst_resm(TWARN, "kill(%d, SIGKILL) failed", pid);
696 cleanup();
697
698 } /* end of getout */
699
700 #ifdef VAX
sighold(int signo)701 static int sighold(int signo)
702 {
703 return 0;
704 }
705
sigrelse(signo)706 static int sigrelse(signo)
707 int signo;
708 {
709 return 0;
710 }
711 #endif
712
choose_sig(int sig)713 int choose_sig(int sig)
714 {
715 switch (sig) {
716
717 case SIGKILL:
718 case SIGSTOP:
719 case SIGTSTP:
720 case SIGCONT:
721 case SIGALRM:
722 case SIGCANCEL:
723 case SIGTIMER:
724 #ifdef SIGNOBDM
725 case SIGNOBDM:
726 #endif
727 #ifdef SIGTTIN
728 case SIGTTIN:
729 #endif
730 #ifdef SIGTTOU
731 case SIGTTOU:
732 #endif
733 #ifdef SIGPTINTR
734 case SIGPTINTR:
735 #endif
736 #ifdef SIGSWAP
737 case SIGSWAP:
738 #endif
739 return 0;
740
741 }
742
743 return 1;
744
745 }
746
setup(void)747 void setup(void)
748 {
749
750 tst_sig(FORK, DEF_HANDLER, cleanup);
751
752 TEST_PAUSE;
753
754 tst_tmpdir();
755
756 /* set up pipe for parent/child communications */
757 SAFE_PIPE(cleanup, pipe_fd);
758
759 /*
760 * Cause the read to return 0 once EOF is encountered and the
761 * read to return -1 if pipe is empty.
762 */
763 if (fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK) == -1)
764 tst_brkm(TBROK | TERRNO, cleanup,
765 "fcntl(Fds[0], F_SETFL, O_NONBLOCK) failed");
766
767 /* set up pipe for parent/child communications */
768 SAFE_PIPE(cleanup, pipe_fd2);
769
770 /*
771 * Cause the read to return 0 once EOF is encountered and the
772 * read to return -1 if pipe is empty.
773 */
774 if (fcntl(pipe_fd2[0], F_SETFL, O_NONBLOCK) == -1)
775 tst_brkm(TBROK | TERRNO, cleanup,
776 "fcntl(Fds[0], F_SETFL, O_NONBLOCK) failed");
777 }
778
cleanup(void)779 void cleanup(void)
780 {
781 tst_rmdir();
782
783 }
784