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/param.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 for (child_count = 0; child_count < 5; child_count++) { 156 switch ((child_pid = fork())) { 157 case -1: 158 err(1, "fork"); 159 /* NOTREACHED */ 160 161 case 0: 162 waiter(); 163 break; 164 165 default: 166 break; 167 } 168 } 169 170 /* 171 * Wait for all of the waiters to be attempting to acquire the 172 * semaphore. 173 */ 174 for (;;) { 175 i = semctl(sender_semid, 0, GETNCNT); 176 if (i == -1) 177 err(1, "semctl GETNCNT"); 178 if (i == 5) 179 break; 180 } 181 182 /* 183 * Now set the thundering herd in motion by initializing the 184 * semaphore to the value 1. 185 */ 186 sun.val = 1; 187 if (semctl(sender_semid, 0, SETVAL, sun) == -1) 188 err(1, "sender: semctl SETVAL to 1"); 189 190 /* 191 * Suspend forever; when we get SIGCHLD, the handler will exit. 192 */ 193 sigemptyset(&sigmask); 194 for (;;) { 195 (void) sigsuspend(&sigmask); 196 if (signal_was_sigchld) 197 signal_was_sigchld = 0; 198 else 199 break; 200 } 201 202 /* 203 * ...and any other signal is an unexpected error. 204 */ 205 errx(1, "sender: received unexpected signal"); 206 } 207 208 void 209 sigsys_handler(int signo) 210 { 211 212 errx(1, "System V Semaphore support is not present in the kernel"); 213 } 214 215 void 216 sigchld_handler(int signo) 217 { 218 union semun sun; 219 struct semid_ds s_ds; 220 int cstatus; 221 222 /* 223 * Reap the child; if it exited successfully, then we're on the 224 * right track! 225 */ 226 if (wait(&cstatus) == -1) 227 err(1, "wait"); 228 229 if (WIFEXITED(cstatus) == 0) 230 errx(1, "receiver exited abnormally"); 231 232 if (WEXITSTATUS(cstatus) != 0) 233 errx(1, "receiver exited with status %d", 234 WEXITSTATUS(cstatus)); 235 236 /* 237 * If we get here, the child has exited normally, and we should 238 * decrement the child count. If the child_count reaches 0, we 239 * should exit. 240 */ 241 242 sun.buf = &s_ds; 243 if (semctl(sender_semid, 0, IPC_STAT, sun) == -1) 244 err(1, "semctl IPC_STAT"); 245 246 print_semid_ds(&s_ds, 0600); 247 248 if (--child_count != 0) { 249 signal_was_sigchld = 1; 250 return; 251 } 252 253 exit(0); 254 } 255 256 void 257 cleanup() 258 { 259 260 /* 261 * If we're the sender, and it exists, remove the message queue. 262 */ 263 if (child_pid != 0 && sender_semid != -1) { 264 if (semctl(sender_semid, 0, IPC_RMID) == -1) 265 warn("semctl IPC_RMID"); 266 } 267 } 268 269 void 270 print_semid_ds(struct semid_ds *sp, mode_t mode) 271 { 272 uid_t uid = geteuid(); 273 gid_t gid = getegid(); 274 275 printf("PERM: uid %d, gid %d, cuid %d, cgid %d, mode 0%o\n", 276 sp->sem_perm.uid, sp->sem_perm.gid, 277 sp->sem_perm.cuid, sp->sem_perm.cgid, 278 sp->sem_perm.mode & 0777); 279 280 printf("nsems %u\n", sp->sem_nsems); 281 282 printf("otime: %s", ctime(&sp->sem_otime)); 283 printf("ctime: %s", ctime(&sp->sem_ctime)); 284 285 /* 286 * Sanity check a few things. 287 */ 288 289 if (sp->sem_perm.uid != uid || sp->sem_perm.cuid != uid) 290 errx(1, "uid mismatch"); 291 292 if (sp->sem_perm.gid != gid || sp->sem_perm.cgid != gid) 293 errx(1, "gid mismatch"); 294 295 if ((sp->sem_perm.mode & 0777) != mode) 296 errx(1, "mode mismatch %o != %o", 297 (sp->sem_perm.mode & 0777), mode); 298 } 299 300 void 301 usage() 302 { 303 304 fprintf(stderr, "usage: %s keypath\n", getprogname()); 305 exit(1); 306 } 307 308 void 309 waiter() 310 { 311 struct sembuf s; 312 int semid; 313 314 if ((semid = semget(semkey, 1, 0)) == -1) 315 err(1, "waiter: semget"); 316 317 /* 318 * Attempt to acquire the semaphore. 319 */ 320 s.sem_num = 0; 321 s.sem_op = -1; 322 s.sem_flg = SEM_UNDO; 323 324 if (semop(semid, &s, 1) == -1) 325 err(1, "waiter: semop -1"); 326 327 printf("WOO! GOT THE SEMAPHORE!\n"); 328 sleep(1); 329 330 /* 331 * Release the semaphore and exit. 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 exit(0); 341 } 342