xref: /openbsd/regress/sys/kern/sysvsem/semtest.c (revision 49a6e16f)
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