1 /* $OpenBSD: sig-stop2.c,v 1.1 2024/10/07 14:01:12 claudio Exp $ */ 2 /* 3 * Written by Artur Grabowski <art@openbsd.org> 2007 Public Domain. 4 * Written by Claudio Jeker <claudio@openbsd.org> 2024 Public Domain. 5 */ 6 #include <sys/types.h> 7 #include <sys/time.h> 8 #include <sys/wait.h> 9 10 #include <err.h> 11 #include <stdlib.h> 12 #include <stdio.h> 13 #include <unistd.h> 14 #include <time.h> 15 #include <err.h> 16 #include <signal.h> 17 #include <pthread.h> 18 19 #define THREAD_COUNT 4 20 21 volatile sig_atomic_t tstp_count, cont_count; 22 pid_t child; 23 24 static void 25 tstp_handler(int sig) 26 { 27 tstp_count++; 28 dprintf(STDERR_FILENO, "SIGTSTP\n"); 29 kill(getpid(), SIGSTOP); 30 } 31 32 static void 33 cont_handler(int sig) 34 { 35 dprintf(STDERR_FILENO, "SIGCONT\n"); 36 cont_count++; 37 } 38 39 static void 40 alrm_handler(int sig) 41 { 42 kill(child, SIGKILL); 43 dprintf(STDERR_FILENO, "timeout\n"); 44 _exit(2); 45 } 46 47 48 static void * 49 thread(void *arg) 50 { 51 struct timespec ts = { .tv_sec = 2 }; 52 53 while (nanosleep(&ts, &ts) != 0) 54 ; 55 56 return NULL; 57 } 58 59 static int 60 child_main(void) 61 { 62 pthread_t self, pthread[THREAD_COUNT]; 63 sigset_t set; 64 int i, r; 65 66 signal(SIGTSTP, tstp_handler); 67 signal(SIGCONT, cont_handler); 68 69 sigemptyset(&set); 70 sigaddset(&set, SIGTSTP); 71 sigaddset(&set, SIGCONT); 72 73 self = pthread_self(); 74 for (i = 0; i < THREAD_COUNT; i++) { 75 if ((r = pthread_create(&pthread[i], NULL, thread, NULL))) { 76 warnc(r, "could not create thread"); 77 pthread[i] = self; 78 } 79 } 80 81 sigprocmask(SIG_BLOCK, &set, NULL); 82 83 for (i = 0; i < THREAD_COUNT; i++) { 84 if (!pthread_equal(pthread[i], self) && 85 (r = pthread_join(pthread[i], NULL))) 86 warnc(r, "could not join thread"); 87 } 88 89 printf("#tstp = %d #cont = %d\n", tstp_count, cont_count); 90 91 return !(tstp_count == 1 && cont_count == 1); 92 } 93 94 int 95 main(int argc, char **argv) 96 { 97 struct timespec ts = { .tv_nsec = 200 * 1000 * 1000 }; 98 int status; 99 100 switch((child = fork())) { 101 case -1: 102 err(1, "fork"); 103 case 0: 104 exit(child_main()); 105 default: 106 break; 107 } 108 109 signal(SIGALRM, alrm_handler); 110 alarm(5); 111 112 nanosleep(&ts, NULL); 113 114 if (kill(child, SIGTSTP) == -1) 115 err(1, "kill"); 116 117 if (waitpid(child, &status, WCONTINUED|WUNTRACED) <= 0) 118 err(1, "waitpid"); 119 120 nanosleep(&ts, NULL); 121 122 if (kill(child, SIGCONT) == -1) 123 err(1, "kill"); 124 125 if (waitpid(child, &status, WCONTINUED|WUNTRACED) <= 0) 126 err(1, "waitpid"); 127 128 nanosleep(&ts, NULL); 129 130 if (waitpid(child, &status, 0) <= 0) 131 err(1, "waitpid"); 132 133 if (!WIFEXITED(status)) 134 err(1, "bad status: %d", status); 135 136 return WEXITSTATUS(status); 137 } 138