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