1101902Salfred/*- 2101902Salfred * Copyright (c) 1999 The NetBSD Foundation, Inc. 3101902Salfred * All rights reserved. 4101902Salfred * 5101902Salfred * This code is derived from software contributed to The NetBSD Foundation 6101902Salfred * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 7101902Salfred * NASA Ames Research Center. 8101902Salfred * 9101902Salfred * Redistribution and use in source and binary forms, with or without 10101902Salfred * modification, are permitted provided that the following conditions 11101902Salfred * are met: 12101902Salfred * 1. Redistributions of source code must retain the above copyright 13101902Salfred * notice, this list of conditions and the following disclaimer. 14101902Salfred * 2. Redistributions in binary form must reproduce the above copyright 15101902Salfred * notice, this list of conditions and the following disclaimer in the 16101902Salfred * documentation and/or other materials provided with the distribution. 17101902Salfred * 18101902Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19101902Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20101902Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21101902Salfred * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22101902Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23101902Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24101902Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25101902Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26101902Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27101902Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28101902Salfred * POSSIBILITY OF SUCH DAMAGE. 29101902Salfred * 30101902Salfred * Obtained from: $NetBSD: semtest.c,v 1.4 2002/07/20 08:36:25 grant Exp $ 31101902Salfred * $FreeBSD$ 32101902Salfred */ 33101902Salfred 34101902Salfred/* 35101902Salfred * Test the SVID-compatible Semaphore facility. 36101902Salfred */ 37101902Salfred 38235719Skevlo#include <sys/types.h> 39101902Salfred#include <sys/ipc.h> 40101902Salfred#include <sys/sem.h> 41101902Salfred#include <sys/wait.h> 42101902Salfred 43101902Salfred#include <err.h> 44101902Salfred#include <errno.h> 45101902Salfred#include <signal.h> 46101902Salfred#include <stdio.h> 47101902Salfred#include <stdlib.h> 48101902Salfred#include <string.h> 49101902Salfred#include <time.h> 50101902Salfred#include <unistd.h> 51101902Salfred 52101902Salfredint main (int, char *[]); 53101902Salfredvoid print_semid_ds (struct semid_ds *, mode_t); 54101902Salfredvoid sigsys_handler (int); 55101902Salfredvoid sigchld_handler(int); 56101902Salfredvoid cleanup (void); 57101902Salfredvoid waiter (void); 58101902Salfredvoid usage (void); 59101902Salfred 60101902Salfredint sender_semid = -1; 61101902Salfredpid_t child_pid; 62101902Salfredint child_count; 63101902Salfredint signal_was_sigchld; 64101902Salfred 65101902Salfredkey_t semkey; 66101902Salfred 67101902Salfred/* 68101902Salfred * This is the original semun union used by the sysvsem utility. 69101902Salfred * It is deliberately kept here under #if 0'ed condition for future 70101902Salfred * reference. PLEASE DO NOT REMOVE. The {SET,GET}ALL in FreeBSD 71101902Salfred * are signed values, so the default version in sys/sem.h suffices. 72101902Salfred */ 73101902Salfred#if 0 74101902Salfredunion semun { 75101902Salfred int val; /* value for SETVAL */ 76101902Salfred struct semid_ds *buf; /* buffer for IPC_{STAT,SET} */ 77101902Salfred u_short *array; /* array for GETALL & SETALL */ 78101902Salfred}; 79101902Salfred#endif 80101902Salfred 81101902Salfredint 82101902Salfredmain(int argc, char *argv[]) 83101902Salfred{ 84101902Salfred struct sigaction sa; 85101902Salfred union semun sun; 86101902Salfred struct semid_ds s_ds; 87101902Salfred sigset_t sigmask; 88101902Salfred int i; 89101902Salfred 90101902Salfred if (argc != 2) 91101902Salfred usage(); 92101902Salfred 93101902Salfred /* 94101902Salfred * Install a SIGSYS handler so that we can exit gracefully if 95101902Salfred * System V Semaphore support isn't in the kernel. 96101902Salfred */ 97101902Salfred sa.sa_handler = sigsys_handler; 98101902Salfred sigemptyset(&sa.sa_mask); 99101902Salfred sa.sa_flags = 0; 100101902Salfred if (sigaction(SIGSYS, &sa, NULL) == -1) 101101902Salfred err(1, "sigaction SIGSYS"); 102101902Salfred 103101902Salfred /* 104101902Salfred * Install and SIGCHLD handler to deal with all possible exit 105101902Salfred * conditions of the receiver. 106101902Salfred */ 107101902Salfred sa.sa_handler = sigchld_handler; 108101902Salfred sigemptyset(&sa.sa_mask); 109101902Salfred sa.sa_flags = 0; 110101902Salfred if (sigaction(SIGCHLD, &sa, NULL) == -1) 111101902Salfred err(1, "sigaction SIGCHLD"); 112101902Salfred 113101902Salfred semkey = ftok(argv[1], 4160); 114101902Salfred 115101902Salfred /* 116101902Salfred * Initialize child_pid to ourselves to that the cleanup function 117101902Salfred * works before we create the receiver. 118101902Salfred */ 119101902Salfred child_pid = getpid(); 120101902Salfred 121101902Salfred /* 122101902Salfred * Make sure that when the sender exits, the message queue is 123101902Salfred * removed. 124101902Salfred */ 125101902Salfred if (atexit(cleanup) == -1) 126101902Salfred err(1, "atexit"); 127101902Salfred 128101902Salfred if ((sender_semid = semget(semkey, 1, IPC_CREAT | 0640)) == -1) 129101902Salfred err(1, "semget"); 130101902Salfred 131101902Salfred 132101902Salfred sun.buf = &s_ds; 133101902Salfred if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 134101902Salfred err(1, "semctl IPC_STAT"); 135101902Salfred 136101902Salfred print_semid_ds(&s_ds, 0640); 137101902Salfred 138101902Salfred s_ds.sem_perm.mode = (s_ds.sem_perm.mode & ~0777) | 0600; 139101902Salfred 140101902Salfred sun.buf = &s_ds; 141101902Salfred if (semctl(sender_semid, 0, IPC_SET, sun) == -1) 142101902Salfred err(1, "semctl IPC_SET"); 143101902Salfred 144101902Salfred memset(&s_ds, 0, sizeof(s_ds)); 145101902Salfred 146101902Salfred sun.buf = &s_ds; 147101902Salfred if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 148101902Salfred err(1, "semctl IPC_STAT"); 149101902Salfred 150101902Salfred if ((s_ds.sem_perm.mode & 0777) != 0600) 151101902Salfred err(1, "IPC_SET of mode didn't hold"); 152101902Salfred 153101902Salfred print_semid_ds(&s_ds, 0600); 154101902Salfred 155297747Sjilles errno = 0; 156297747Sjilles if (semget(semkey, 1, IPC_CREAT | IPC_EXCL | 0600) != -1 || 157297747Sjilles errno != EEXIST) 158297747Sjilles err(1, "semget IPC_EXCL 1 did not fail with [EEXIST]"); 159297747Sjilles errno = 0; 160297747Sjilles if (semget(semkey, 2, IPC_CREAT | IPC_EXCL | 0600) != -1 || 161297747Sjilles errno != EEXIST) 162297747Sjilles err(1, "semget IPC_EXCL 2 did not fail with [EEXIST]"); 163297747Sjilles 164101902Salfred for (child_count = 0; child_count < 5; child_count++) { 165101902Salfred switch ((child_pid = fork())) { 166101902Salfred case -1: 167101902Salfred err(1, "fork"); 168101902Salfred /* NOTREACHED */ 169101902Salfred 170101902Salfred case 0: 171101902Salfred waiter(); 172101902Salfred break; 173101902Salfred 174101902Salfred default: 175101902Salfred break; 176101902Salfred } 177101902Salfred } 178101902Salfred 179101902Salfred /* 180101902Salfred * Wait for all of the waiters to be attempting to acquire the 181101902Salfred * semaphore. 182101902Salfred */ 183101902Salfred for (;;) { 184101902Salfred i = semctl(sender_semid, 0, GETNCNT); 185101902Salfred if (i == -1) 186101902Salfred err(1, "semctl GETNCNT"); 187101902Salfred if (i == 5) 188101902Salfred break; 189101902Salfred } 190101902Salfred 191101902Salfred /* 192101902Salfred * Now set the thundering herd in motion by initializing the 193101902Salfred * semaphore to the value 1. 194101902Salfred */ 195101902Salfred sun.val = 1; 196101902Salfred if (semctl(sender_semid, 0, SETVAL, sun) == -1) 197101902Salfred err(1, "sender: semctl SETVAL to 1"); 198101902Salfred 199101902Salfred /* 200101902Salfred * Suspend forever; when we get SIGCHLD, the handler will exit. 201101902Salfred */ 202101902Salfred sigemptyset(&sigmask); 203101902Salfred for (;;) { 204101902Salfred (void) sigsuspend(&sigmask); 205101902Salfred if (signal_was_sigchld) 206101902Salfred signal_was_sigchld = 0; 207101902Salfred else 208101902Salfred break; 209101902Salfred } 210101902Salfred 211101902Salfred /* 212101902Salfred * ...and any other signal is an unexpected error. 213101902Salfred */ 214101902Salfred errx(1, "sender: received unexpected signal"); 215101902Salfred} 216101902Salfred 217101902Salfredvoid 218101902Salfredsigsys_handler(int signo) 219101902Salfred{ 220101902Salfred 221101902Salfred errx(1, "System V Semaphore support is not present in the kernel"); 222101902Salfred} 223101902Salfred 224101902Salfredvoid 225101902Salfredsigchld_handler(int signo) 226101902Salfred{ 227101902Salfred union semun sun; 228101902Salfred struct semid_ds s_ds; 229101902Salfred int cstatus; 230101902Salfred 231101902Salfred /* 232101902Salfred * Reap the child; if it exited successfully, then we're on the 233101902Salfred * right track! 234101902Salfred */ 235101902Salfred if (wait(&cstatus) == -1) 236101902Salfred err(1, "wait"); 237101902Salfred 238101902Salfred if (WIFEXITED(cstatus) == 0) 239101902Salfred errx(1, "receiver exited abnormally"); 240101902Salfred 241101902Salfred if (WEXITSTATUS(cstatus) != 0) 242101902Salfred errx(1, "receiver exited with status %d", 243101902Salfred WEXITSTATUS(cstatus)); 244101902Salfred 245101902Salfred /* 246101902Salfred * If we get here, the child has exited normally, and we should 247101902Salfred * decrement the child count. If the child_count reaches 0, we 248101902Salfred * should exit. 249101902Salfred */ 250101902Salfred 251101902Salfred sun.buf = &s_ds; 252101902Salfred if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 253101902Salfred err(1, "semctl IPC_STAT"); 254101902Salfred 255101902Salfred print_semid_ds(&s_ds, 0600); 256101902Salfred 257101902Salfred if (--child_count != 0) { 258101902Salfred signal_was_sigchld = 1; 259101902Salfred return; 260101902Salfred } 261101902Salfred 262101902Salfred exit(0); 263101902Salfred} 264101902Salfred 265101902Salfredvoid 266101902Salfredcleanup() 267101902Salfred{ 268101902Salfred 269101902Salfred /* 270101902Salfred * If we're the sender, and it exists, remove the message queue. 271101902Salfred */ 272101902Salfred if (child_pid != 0 && sender_semid != -1) { 273101902Salfred if (semctl(sender_semid, 0, IPC_RMID) == -1) 274101902Salfred warn("semctl IPC_RMID"); 275101902Salfred } 276101902Salfred} 277101902Salfred 278101902Salfredvoid 279101902Salfredprint_semid_ds(struct semid_ds *sp, mode_t mode) 280101902Salfred{ 281101902Salfred uid_t uid = geteuid(); 282101902Salfred gid_t gid = getegid(); 283101902Salfred 284101902Salfred printf("PERM: uid %d, gid %d, cuid %d, cgid %d, mode 0%o\n", 285101902Salfred sp->sem_perm.uid, sp->sem_perm.gid, 286101902Salfred sp->sem_perm.cuid, sp->sem_perm.cgid, 287101902Salfred sp->sem_perm.mode & 0777); 288101902Salfred 289101902Salfred printf("nsems %u\n", sp->sem_nsems); 290101902Salfred 291101902Salfred printf("otime: %s", ctime(&sp->sem_otime)); 292101902Salfred printf("ctime: %s", ctime(&sp->sem_ctime)); 293101902Salfred 294101902Salfred /* 295101902Salfred * Sanity check a few things. 296101902Salfred */ 297101902Salfred 298101902Salfred if (sp->sem_perm.uid != uid || sp->sem_perm.cuid != uid) 299101902Salfred errx(1, "uid mismatch"); 300101902Salfred 301101902Salfred if (sp->sem_perm.gid != gid || sp->sem_perm.cgid != gid) 302101902Salfred errx(1, "gid mismatch"); 303101902Salfred 304101902Salfred if ((sp->sem_perm.mode & 0777) != mode) 305101902Salfred errx(1, "mode mismatch %o != %o", 306101902Salfred (sp->sem_perm.mode & 0777), mode); 307101902Salfred} 308101902Salfred 309101902Salfredvoid 310101902Salfredusage() 311101902Salfred{ 312101902Salfred 313101902Salfred fprintf(stderr, "usage: %s keypath\n", getprogname()); 314101902Salfred exit(1); 315101902Salfred} 316101902Salfred 317101902Salfredvoid 318101902Salfredwaiter() 319101902Salfred{ 320101902Salfred struct sembuf s; 321101902Salfred int semid; 322101902Salfred 323101902Salfred if ((semid = semget(semkey, 1, 0)) == -1) 324101902Salfred err(1, "waiter: semget"); 325101902Salfred 326101902Salfred /* 327101902Salfred * Attempt to acquire the semaphore. 328101902Salfred */ 329101902Salfred s.sem_num = 0; 330101902Salfred s.sem_op = -1; 331101902Salfred s.sem_flg = SEM_UNDO; 332101902Salfred 333101902Salfred if (semop(semid, &s, 1) == -1) 334101902Salfred err(1, "waiter: semop -1"); 335101902Salfred 336101902Salfred printf("WOO! GOT THE SEMAPHORE!\n"); 337101902Salfred sleep(1); 338101902Salfred 339101902Salfred /* 340101902Salfred * Release the semaphore and exit. 341101902Salfred */ 342101902Salfred s.sem_num = 0; 343101902Salfred s.sem_op = 1; 344101902Salfred s.sem_flg = SEM_UNDO; 345101902Salfred 346101902Salfred if (semop(semid, &s, 1) == -1) 347101902Salfred err(1, "waiter: semop +1"); 348101902Salfred 349101902Salfred exit(0); 350101902Salfred} 351