void sigchld_handler(int signo) { union semun sun; struct semid_ds s_ds; int cstatus; /* * Reap the child; if it exited successfully, then we're on the * right track! */ if (wait(&cstatus) == -1) err(1, "wait"); if (WIFEXITED(cstatus) == 0) errx(1, "receiver exited abnormally"); if (WEXITSTATUS(cstatus) != 0) errx(1, "receiver exited with status %d", WEXITSTATUS(cstatus)); /* * If we get here, the child has exited normally, and we should * decrement the child count. If the child_count reaches 0, we * should exit. */ sun.buf = &s_ds; if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) err(1, "semctl IPC_STAT"); print_semid_ds(&s_ds, 0600); if (--child_count != 0) { signal_was_sigchld = 1; return; } exit(0); }
int main(int argc, char *argv[]) { struct sigaction sa; union semun sun; struct semid_ds s_ds; sigset_t sigmask; int i; if (argc != 2) usage(); /* * Install a SIGSYS handler so that we can exit gracefully if * System V Semaphore support isn't in the kernel. */ sa.sa_handler = sigsys_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGSYS, &sa, NULL) == -1) err(1, "sigaction SIGSYS"); /* * Install and SIGCHLD handler to deal with all possible exit * conditions of the receiver. */ sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGCHLD, &sa, NULL) == -1) err(1, "sigaction SIGCHLD"); semkey = ftok(argv[1], 4160); /* * Initialize child_pid to ourselves to that the cleanup function * works before we create the receiver. */ child_pid = getpid(); /* * Make sure that when the sender exits, the message queue is * removed. */ if (atexit(cleanup) == -1) err(1, "atexit"); if ((sender_semid = semget(semkey, 1, IPC_CREAT | 0640)) == -1) err(1, "semget"); sun.buf = &s_ds; if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) err(1, "semctl IPC_STAT"); print_semid_ds(&s_ds, 0640); s_ds.sem_perm.mode = (s_ds.sem_perm.mode & ~0777) | 0600; sun.buf = &s_ds; if (semctl(sender_semid, 0, IPC_SET, sun) == -1) err(1, "semctl IPC_SET"); memset(&s_ds, 0, sizeof(s_ds)); sun.buf = &s_ds; if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) err(1, "semctl IPC_STAT"); if ((s_ds.sem_perm.mode & 0777) != 0600) err(1, "IPC_SET of mode didn't hold"); print_semid_ds(&s_ds, 0600); for (child_count = 0; child_count < 5; child_count++) { switch ((child_pid = fork())) { case -1: err(1, "fork"); /* NOTREACHED */ case 0: waiter(); break; default: break; } } /* * Wait for all of the waiters to be attempting to acquire the * semaphore. */ for (;;) { i = semctl(sender_semid, 0, GETNCNT); if (i == -1) err(1, "semctl GETNCNT"); if (i == 5) break; } /* * Now set the thundering herd in motion by initializing the * semaphore to the value 1. */ sun.val = 1; if (semctl(sender_semid, 0, SETVAL, sun) == -1) err(1, "sender: semctl SETVAL to 1"); /* * Suspend forever; when we get SIGCHLD, the handler will exit. */ sigemptyset(&sigmask); for (;;) { (void) sigsuspend(&sigmask); if (signal_was_sigchld) signal_was_sigchld = 0; else break; } /* * ...and any other signal is an unexpected error. */ errx(1, "sender: received unexpected signal"); }
ATF_TC_BODY(sem, tc) { struct sigaction sa; union semun sun; struct semid_ds s_ds; sigset_t sigmask; int sender_semid; int i; int c_status; /* * Install a SIGSYS handler so that we can exit gracefully if * System V Semaphore support isn't in the kernel. */ did_sigsys = 0; sa.sa_handler = sigsys_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; ATF_REQUIRE_MSG(sigaction(SIGSYS, &sa, NULL) != -1, "sigaction SIGSYS: %d", errno); /* * Install a SIGCHLD handler to deal with all possible exit * conditions of the receiver. */ did_sigchild = 0; child_count = 0; sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; ATF_REQUIRE_MSG(sigaction(SIGCHLD, &sa, NULL) != -1, "sigaction SIGCHLD: %d", errno); semkey = get_ftok(4160); ATF_REQUIRE_MSG(semkey != (key_t)-1, "get_ftok failed"); sender_semid = semget(semkey, 1, IPC_CREAT | 0640); ATF_REQUIRE_MSG(sender_semid != -1, "semget: %d", errno); write_int("sender_semid", sender_semid); if (did_sigsys) { atf_tc_skip("SYSV Semaphore not supported"); return; } sun.buf = &s_ds; ATF_REQUIRE_MSG(semctl(sender_semid, 0, IPC_STAT, sun) != -1, "semctl IPC_STAT: %d", errno); print_semid_ds(&s_ds, 0640); s_ds.sem_perm.mode = (s_ds.sem_perm.mode & ~0777) | 0600; sun.buf = &s_ds; ATF_REQUIRE_MSG(semctl(sender_semid, 0, IPC_SET, sun) != -1, "semctl IPC_SET: %d", errno); memset(&s_ds, 0, sizeof(s_ds)); sun.buf = &s_ds; ATF_REQUIRE_MSG(semctl(sender_semid, 0, IPC_STAT, sun) != -1, "semctl IPC_STAT: %d", errno); ATF_REQUIRE_MSG((s_ds.sem_perm.mode & 0777) == 0600, "IPC_SET of mode didn't hold"); print_semid_ds(&s_ds, 0600); for (child_count = 0; child_count < 5; child_count++) { switch ((child_pid = fork())) { case -1: atf_tc_fail("fork: %d", errno); return; case 0: waiter(); break; default: break; } } /* * Wait for all of the waiters to be attempting to acquire the * semaphore. */ for (;;) { i = semctl(sender_semid, 0, GETNCNT); if (i == -1) atf_tc_fail("semctl GETNCNT: %d", i); if (i == 5) break; } /* * Now set the thundering herd in motion by initializing the * semaphore to the value 1. */ sun.val = 1; ATF_REQUIRE_MSG(semctl(sender_semid, 0, SETVAL, sun) != -1, "sender: semctl SETVAL to 1: %d", errno); /* * Wait for all children to finish */ sigemptyset(&sigmask); for (;;) { (void) sigsuspend(&sigmask); if (did_sigchild) { c_status = child_status; if (c_status < 0) atf_tc_fail("waitpid: %d", -c_status); else if (WIFEXITED(c_status) == 0) atf_tc_fail("c abnormal exit: %d", c_status); else if (WEXITSTATUS(c_status) != 0) atf_tc_fail("c status: %d", WEXITSTATUS(c_status)); else { sun.buf = &s_ds; ATF_REQUIRE_MSG(semctl(sender_semid, 0, IPC_STAT, sun) != -1, "semctl IPC_STAT: %d", errno); print_semid_ds(&s_ds, 0600); atf_tc_pass(); } if (child_count <= 0) break; did_sigchild = 0; } else { atf_tc_fail("sender: received unexpected signal"); break; } } }