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/ipc.h> 38 #include <sys/sem.h> 39 #include <sys/wait.h> 40 41 #include <err.h> 42 #include <errno.h> 43 #include <limits.h> 44 #include <signal.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <string.h> 48 #include <time.h> 49 #include <unistd.h> 50 51 int main(int, char *[]); 52 void print_semid_ds(struct semid_ds *, mode_t); 53 void sigsys_handler(int); 54 void sigchld_handler(int); 55 void cleanup(void); 56 void waiter(void); 57 58 int sender_semid = -1; 59 pid_t child_pid; 60 int child_count; 61 volatile sig_atomic_t signal_was_sigchld; 62 63 key_t semkey; 64 65 char keyname[] = "/tmp/msgtestXXXXXXXX"; 66 67 int verbose; 68 69 union mysemun { 70 int val; /* value for SETVAL */ 71 struct semid_ds *buf; /* buffer for IPC_{STAT,SET} */ 72 u_short *array; /* array for GETALL & SETALL */ 73 }; 74 75 int 76 main(argc, argv) 77 int argc; 78 char *argv[]; 79 { 80 struct sigaction sa; 81 union mysemun sun; 82 struct semid_ds s_ds; 83 sigset_t sigmask; 84 int i; 85 86 int fd, ch; 87 88 if ((fd = mkstemp(keyname)) < 0) 89 err(1, "mkstemp"); 90 91 close(fd); 92 93 while ((ch = getopt(argc, argv, "v")) != -1) { 94 switch (ch) { 95 case 'v': 96 verbose = 1; 97 break; 98 default: 99 fprintf(stderr, "Usage: semtest [-v]\n"); 100 exit(1); 101 } 102 } 103 104 /* 105 * Install a SIGSYS handler so that we can exit gracefully if 106 * System V Semaphore support isn't in the kernel. 107 */ 108 sa.sa_handler = sigsys_handler; 109 sigemptyset(&sa.sa_mask); 110 sa.sa_flags = 0; 111 if (sigaction(SIGSYS, &sa, NULL) == -1) 112 err(1, "sigaction SIGSYS"); 113 114 /* 115 * Install and SIGCHLD handler to deal with all possible exit 116 * conditions of the receiver. 117 */ 118 sa.sa_handler = sigchld_handler; 119 sigemptyset(&sa.sa_mask); 120 sa.sa_flags = 0; 121 if (sigaction(SIGCHLD, &sa, NULL) == -1) 122 err(1, "sigaction SIGCHLD"); 123 124 semkey = ftok(keyname, arc4random() & INT_MAX); 125 126 /* 127 * Initialize child_pid to ourselves to that the cleanup function 128 * works before we create the receiver. 129 */ 130 child_pid = getpid(); 131 132 /* 133 * Make sure that when the sender exits, the message queue is 134 * removed. 135 */ 136 if (atexit(cleanup) == -1) 137 err(1, "atexit"); 138 139 sender_semid = semget(semkey, 1, IPC_CREAT | IPC_EXCL | 0640); 140 if (sender_semid == -1) 141 err(1, "semget"); 142 143 144 sun.buf = &s_ds; 145 if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 146 err(1, "semctl IPC_STAT"); 147 148 if (verbose) 149 print_semid_ds(&s_ds, 0640); 150 151 s_ds.sem_perm.mode = (s_ds.sem_perm.mode & ~0777) | 0600; 152 153 sun.buf = &s_ds; 154 if (semctl(sender_semid, 0, IPC_SET, sun) == -1) 155 err(1, "semctl IPC_SET"); 156 157 memset(&s_ds, 0, sizeof(s_ds)); 158 159 sun.buf = &s_ds; 160 if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 161 err(1, "semctl IPC_STAT"); 162 163 if ((s_ds.sem_perm.mode & 0777) != 0600) 164 err(1, "IPC_SET of mode didn't hold"); 165 166 if (verbose) 167 print_semid_ds(&s_ds, 0600); 168 169 for (child_count = 0; child_count < 5; child_count++) { 170 switch ((child_pid = fork())) { 171 case -1: 172 err(1, "fork"); 173 /* NOTREACHED */ 174 175 case 0: 176 waiter(); 177 break; 178 179 default: 180 break; 181 } 182 } 183 184 /* 185 * Wait for all of the waiters to be attempting to acquire the 186 * semaphore. 187 */ 188 for (;;) { 189 i = semctl(sender_semid, 0, GETNCNT); 190 if (i == -1) 191 err(1, "semctl GETNCNT"); 192 if (i == 5) 193 break; 194 } 195 196 /* 197 * Now set the thundering herd in motion by initializing the 198 * semaphore to the value 1. 199 */ 200 sun.val = 1; 201 if (semctl(sender_semid, 0, SETVAL, sun) == -1) 202 err(1, "sender: semctl SETVAL to 1"); 203 204 /* 205 * Suspend forever; when we get SIGCHLD, the handler will exit. 206 */ 207 sigemptyset(&sigmask); 208 for (;;) { 209 (void) sigsuspend(&sigmask); 210 if (signal_was_sigchld) 211 signal_was_sigchld = 0; 212 else 213 break; 214 } 215 216 /* 217 * ...and any other signal is an unexpected error. 218 */ 219 errx(1, "sender: received unexpected signal"); 220 } 221 222 void 223 sigsys_handler(signo) 224 int signo; 225 { 226 227 errx(1, "System V Semaphore support is not present in the kernel"); 228 } 229 230 void 231 sigchld_handler(signo) 232 int signo; 233 { 234 union mysemun sun; 235 struct semid_ds s_ds; 236 int cstatus; 237 238 /* 239 * Reap the child; if it exited successfully, then we're on the 240 * right track! 241 */ 242 if (wait(&cstatus) == -1) 243 err(1, "wait"); 244 245 if (WIFEXITED(cstatus) == 0) 246 errx(1, "receiver exited abnormally"); 247 248 if (WEXITSTATUS(cstatus) != 0) 249 errx(1, "receiver exited with status %d", 250 WEXITSTATUS(cstatus)); 251 252 /* 253 * If we get here, the child has exited normally, and we should 254 * decrement the child count. If the child_count reaches 0, we 255 * should exit. 256 */ 257 258 sun.buf = &s_ds; 259 if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 260 err(1, "semctl IPC_STAT"); 261 262 if (verbose) 263 print_semid_ds(&s_ds, 0600); 264 265 if (--child_count != 0) { 266 signal_was_sigchld = 1; 267 return; 268 } 269 270 exit(0); 271 } 272 273 void 274 cleanup() 275 { 276 277 /* 278 * If we're the sender, and it exists, remove the message queue. 279 */ 280 if (child_pid != 0 && sender_semid != -1) { 281 if (semctl(sender_semid, 0, IPC_RMID) == -1) 282 warn("semctl IPC_RMID"); 283 } 284 remove(keyname); 285 } 286 287 void 288 print_semid_ds(sp, mode) 289 struct semid_ds *sp; 290 mode_t mode; 291 { 292 uid_t uid = geteuid(); 293 gid_t gid = getegid(); 294 295 printf("PERM: uid %u, gid %u, cuid %u, cgid %u, mode 0%o\n", 296 sp->sem_perm.uid, sp->sem_perm.gid, 297 sp->sem_perm.cuid, sp->sem_perm.cgid, 298 sp->sem_perm.mode & 0777); 299 300 printf("nsems %u\n", sp->sem_nsems); 301 302 printf("otime: %s", ctime(&sp->sem_otime)); 303 printf("ctime: %s", ctime(&sp->sem_ctime)); 304 305 /* 306 * Sanity check a few things. 307 */ 308 309 if (sp->sem_perm.uid != uid || sp->sem_perm.cuid != uid) 310 errx(1, "uid mismatch"); 311 312 if (sp->sem_perm.gid != gid || sp->sem_perm.cgid != gid) 313 errx(1, "gid mismatch"); 314 315 if ((sp->sem_perm.mode & 0777) != mode) 316 errx(1, "mode mismatch %o != %o", 317 (sp->sem_perm.mode & 0777), mode); 318 } 319 320 void 321 waiter() 322 { 323 struct sembuf s; 324 int semid; 325 326 if ((semid = semget(semkey, 1, 0)) == -1) 327 err(1, "waiter: semget"); 328 329 /* 330 * Attempt to acquire the semaphore. 331 */ 332 s.sem_num = 0; 333 s.sem_op = -1; 334 s.sem_flg = SEM_UNDO; 335 336 if (semop(semid, &s, 1) == -1) 337 err(1, "waiter: semop -1"); 338 339 if (verbose) 340 printf("WOO! GOT THE SEMAPHORE!\n"); 341 sleep(1); 342 343 /* 344 * Release the semaphore and exit. 345 */ 346 s.sem_num = 0; 347 s.sem_op = 1; 348 s.sem_flg = SEM_UNDO; 349 350 if (semop(semid, &s, 1) == -1) 351 err(1, "waiter: semop +1"); 352 353 exit(0); 354 } 355