1 /* $NetBSD: semtest.c,v 1.3 2001/02/19 22:44:41 cgd Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Test the SVID-compatible Semaphore facility. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/ipc.h> 39 #include <sys/sem.h> 40 #include <sys/wait.h> 41 42 #include <err.h> 43 #include <errno.h> 44 #include <limits.h> 45 #include <signal.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <time.h> 50 #include <unistd.h> 51 52 int main(int, char *[]); 53 void print_semid_ds(struct semid_ds *, mode_t); 54 void sigsys_handler(int); 55 void sigchld_handler(int); 56 void cleanup(void); 57 void waiter(void); 58 59 int sender_semid = -1; 60 pid_t child_pid; 61 int child_count; 62 volatile sig_atomic_t signal_was_sigchld; 63 64 key_t semkey; 65 66 char keyname[] = "/tmp/msgtestXXXXXXXX"; 67 68 int verbose; 69 70 union mysemun { 71 int val; /* value for SETVAL */ 72 struct semid_ds *buf; /* buffer for IPC_{STAT,SET} */ 73 u_short *array; /* array for GETALL & SETALL */ 74 }; 75 76 int 77 main(argc, argv) 78 int argc; 79 char *argv[]; 80 { 81 struct sigaction sa; 82 union mysemun sun; 83 struct semid_ds s_ds; 84 sigset_t sigmask; 85 int i; 86 87 int fd, ch; 88 89 if ((fd = mkstemp(keyname)) < 0) 90 err(1, "mkstemp"); 91 92 close(fd); 93 94 while ((ch = getopt(argc, argv, "v")) != -1) { 95 switch (ch) { 96 case 'v': 97 verbose = 1; 98 break; 99 default: 100 fprintf(stderr, "Usage: semtest [-v]\n"); 101 exit(1); 102 } 103 } 104 105 /* 106 * Install a SIGSYS handler so that we can exit gracefully if 107 * System V Semaphore support isn't in the kernel. 108 */ 109 sa.sa_handler = sigsys_handler; 110 sigemptyset(&sa.sa_mask); 111 sa.sa_flags = 0; 112 if (sigaction(SIGSYS, &sa, NULL) == -1) 113 err(1, "sigaction SIGSYS"); 114 115 /* 116 * Install and SIGCHLD handler to deal with all possible exit 117 * conditions of the receiver. 118 */ 119 sa.sa_handler = sigchld_handler; 120 sigemptyset(&sa.sa_mask); 121 sa.sa_flags = 0; 122 if (sigaction(SIGCHLD, &sa, NULL) == -1) 123 err(1, "sigaction SIGCHLD"); 124 125 semkey = ftok(keyname, arc4random() & INT_MAX); 126 127 /* 128 * Initialize child_pid to ourselves to that the cleanup function 129 * works before we create the receiver. 130 */ 131 child_pid = getpid(); 132 133 /* 134 * Make sure that when the sender exits, the message queue is 135 * removed. 136 */ 137 if (atexit(cleanup) == -1) 138 err(1, "atexit"); 139 140 sender_semid = semget(semkey, 1, IPC_CREAT | IPC_EXCL | 0640); 141 if (sender_semid == -1) 142 err(1, "semget"); 143 144 145 sun.buf = &s_ds; 146 if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 147 err(1, "semctl IPC_STAT"); 148 149 if (verbose) 150 print_semid_ds(&s_ds, 0640); 151 152 s_ds.sem_perm.mode = (s_ds.sem_perm.mode & ~0777) | 0600; 153 154 sun.buf = &s_ds; 155 if (semctl(sender_semid, 0, IPC_SET, sun) == -1) 156 err(1, "semctl IPC_SET"); 157 158 memset(&s_ds, 0, sizeof(s_ds)); 159 160 sun.buf = &s_ds; 161 if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 162 err(1, "semctl IPC_STAT"); 163 164 if ((s_ds.sem_perm.mode & 0777) != 0600) 165 err(1, "IPC_SET of mode didn't hold"); 166 167 if (verbose) 168 print_semid_ds(&s_ds, 0600); 169 170 for (child_count = 0; child_count < 5; child_count++) { 171 switch ((child_pid = fork())) { 172 case -1: 173 err(1, "fork"); 174 /* NOTREACHED */ 175 176 case 0: 177 waiter(); 178 break; 179 180 default: 181 break; 182 } 183 } 184 185 /* 186 * Wait for all of the waiters to be attempting to acquire the 187 * semaphore. 188 */ 189 for (;;) { 190 i = semctl(sender_semid, 0, GETNCNT); 191 if (i == -1) 192 err(1, "semctl GETNCNT"); 193 if (i == 5) 194 break; 195 } 196 197 /* 198 * Now set the thundering herd in motion by initializing the 199 * semaphore to the value 1. 200 */ 201 sun.val = 1; 202 if (semctl(sender_semid, 0, SETVAL, sun) == -1) 203 err(1, "sender: semctl SETVAL to 1"); 204 205 /* 206 * Suspend forever; when we get SIGCHLD, the handler will exit. 207 */ 208 sigemptyset(&sigmask); 209 for (;;) { 210 (void) sigsuspend(&sigmask); 211 if (signal_was_sigchld) 212 signal_was_sigchld = 0; 213 else 214 break; 215 } 216 217 /* 218 * ...and any other signal is an unexpected error. 219 */ 220 errx(1, "sender: received unexpected signal"); 221 } 222 223 void 224 sigsys_handler(signo) 225 int signo; 226 { 227 228 errx(1, "System V Semaphore support is not present in the kernel"); 229 } 230 231 void 232 sigchld_handler(signo) 233 int signo; 234 { 235 union mysemun sun; 236 struct semid_ds s_ds; 237 int cstatus; 238 239 /* 240 * Reap the child; if it exited successfully, then we're on the 241 * right track! 242 */ 243 if (wait(&cstatus) == -1) 244 err(1, "wait"); 245 246 if (WIFEXITED(cstatus) == 0) 247 errx(1, "receiver exited abnormally"); 248 249 if (WEXITSTATUS(cstatus) != 0) 250 errx(1, "receiver exited with status %d", 251 WEXITSTATUS(cstatus)); 252 253 /* 254 * If we get here, the child has exited normally, and we should 255 * decrement the child count. If the child_count reaches 0, we 256 * should exit. 257 */ 258 259 sun.buf = &s_ds; 260 if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 261 err(1, "semctl IPC_STAT"); 262 263 if (verbose) 264 print_semid_ds(&s_ds, 0600); 265 266 if (--child_count != 0) { 267 signal_was_sigchld = 1; 268 return; 269 } 270 271 exit(0); 272 } 273 274 void 275 cleanup() 276 { 277 278 /* 279 * If we're the sender, and it exists, remove the message queue. 280 */ 281 if (child_pid != 0 && sender_semid != -1) { 282 if (semctl(sender_semid, 0, IPC_RMID) == -1) 283 warn("semctl IPC_RMID"); 284 } 285 remove(keyname); 286 } 287 288 void 289 print_semid_ds(sp, mode) 290 struct semid_ds *sp; 291 mode_t mode; 292 { 293 uid_t uid = geteuid(); 294 gid_t gid = getegid(); 295 296 printf("PERM: uid %u, gid %u, cuid %u, cgid %u, mode 0%o\n", 297 sp->sem_perm.uid, sp->sem_perm.gid, 298 sp->sem_perm.cuid, sp->sem_perm.cgid, 299 sp->sem_perm.mode & 0777); 300 301 printf("nsems %u\n", sp->sem_nsems); 302 303 printf("otime: %s", ctime(&sp->sem_otime)); 304 printf("ctime: %s", ctime(&sp->sem_ctime)); 305 306 /* 307 * Sanity check a few things. 308 */ 309 310 if (sp->sem_perm.uid != uid || sp->sem_perm.cuid != uid) 311 errx(1, "uid mismatch"); 312 313 if (sp->sem_perm.gid != gid || sp->sem_perm.cgid != gid) 314 errx(1, "gid mismatch"); 315 316 if ((sp->sem_perm.mode & 0777) != mode) 317 errx(1, "mode mismatch %o != %o", 318 (sp->sem_perm.mode & 0777), mode); 319 } 320 321 void 322 waiter() 323 { 324 struct sembuf s; 325 int semid; 326 327 if ((semid = semget(semkey, 1, 0)) == -1) 328 err(1, "waiter: semget"); 329 330 /* 331 * Attempt to acquire the semaphore. 332 */ 333 s.sem_num = 0; 334 s.sem_op = -1; 335 s.sem_flg = SEM_UNDO; 336 337 if (semop(semid, &s, 1) == -1) 338 err(1, "waiter: semop -1"); 339 340 if (verbose) 341 printf("WOO! GOT THE SEMAPHORE!\n"); 342 sleep(1); 343 344 /* 345 * Release the semaphore and exit. 346 */ 347 s.sem_num = 0; 348 s.sem_op = 1; 349 s.sem_flg = SEM_UNDO; 350 351 if (semop(semid, &s, 1) == -1) 352 err(1, "waiter: semop +1"); 353 354 exit(0); 355 } 356