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
main(argc,argv)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
sigsys_handler(signo)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
sigchld_handler(signo)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
cleanup()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
print_semid_ds(sp,mode)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
waiter()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