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