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