xref: /openbsd/regress/sys/kern/sysvshm/shmtest.c (revision 3cab2bb3)
1 /*	$NetBSD: shmtest.c,v 1.2 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 Shared Memory facility.
35  */
36 
37 #include <sys/param.h>
38 #include <sys/ipc.h>
39 #include <sys/mman.h>
40 #include <sys/shm.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_shmid_ds(struct shmid_ds *, mode_t);
54 void	sigsys_handler(int);
55 void	sigchld_handler(int);
56 void	cleanup(void);
57 void	receiver(void);
58 
59 const char *m_str = "The quick brown fox jumped over the lazy dog.";
60 
61 int	sender_shmid = -1;
62 pid_t	child_pid;
63 
64 key_t	shmkey;
65 
66 char keyname[] = "/tmp/msgtestXXXXXXXX";
67 
68 int verbose;
69 
70 size_t	pgsize;
71 
72 int
73 main(argc, argv)
74 	int argc;
75 	char *argv[];
76 {
77 	struct sigaction sa;
78 	struct shmid_ds s_ds;
79 	sigset_t sigmask;
80 	char *shm_buf;
81 	int fd, ch;
82 
83 	if ((fd = mkstemp(keyname)) < 0)
84 		err(1, "mkstemp");
85 
86 	close(fd);
87 
88 	while ((ch = getopt(argc, argv, "v")) != -1) {
89 		switch (ch) {
90 		case 'v':
91 			verbose = 1;
92 			break;
93 		default:
94 			fprintf(stderr, "Usage: shmtest [-v]\n");
95 			exit(1);
96 		}
97 	}
98 
99 	/*
100 	 * Install a SIGSYS handler so that we can exit gracefully if
101 	 * System V Shared Memory support isn't in the kernel.
102 	 */
103 	sa.sa_handler = sigsys_handler;
104 	sigemptyset(&sa.sa_mask);
105 	sa.sa_flags = 0;
106 	if (sigaction(SIGSYS, &sa, NULL) == -1)
107 		err(1, "sigaction SIGSYS");
108 
109 	/*
110 	 * Install and SIGCHLD handler to deal with all possible exit
111 	 * conditions of the receiver.
112 	 */
113 	sa.sa_handler = sigchld_handler;
114 	sigemptyset(&sa.sa_mask);
115 	sa.sa_flags = 0;
116 	if (sigaction(SIGCHLD, &sa, NULL) == -1)
117 		err(1, "sigaction SIGCHLD");
118 
119 	pgsize = sysconf(_SC_PAGESIZE);
120 
121 	shmkey = ftok(argv[1], 4160);
122 
123 	/*
124 	 * Initialize child_pid to ourselves to that the cleanup function
125 	 * works before we create the receiver.
126 	 */
127 	child_pid = getpid();
128 
129 	/*
130 	 * Make sure that when the sender exits, the message queue is
131 	 * removed.
132 	 */
133 	if (atexit(cleanup) == -1)
134 		err(1, "atexit");
135 
136 	if ((sender_shmid = shmget(shmkey, pgsize, IPC_CREAT | 0640)) == -1)
137 		err(1, "shmget");
138 
139 	if (shmctl(sender_shmid, IPC_STAT, &s_ds) == -1)
140 		err(1, "shmctl IPC_STAT");
141 
142 	if (verbose)
143 		print_shmid_ds(&s_ds, 0640);
144 
145 	s_ds.shm_perm.mode = (s_ds.shm_perm.mode & ~0777) | 0600;
146 
147 	if (shmctl(sender_shmid, IPC_SET, &s_ds) == -1)
148 		err(1, "shmctl IPC_SET");
149 
150 	memset(&s_ds, 0, sizeof(s_ds));
151 
152 	if (shmctl(sender_shmid, IPC_STAT, &s_ds) == -1)
153 		err(1, "shmctl IPC_STAT");
154 
155 	if ((s_ds.shm_perm.mode & 0777) != 0600)
156 		err(1, "IPC_SET of mode didn't hold");
157 
158 	if (verbose)
159 		print_shmid_ds(&s_ds, 0600);
160 
161 	if ((shm_buf = shmat(sender_shmid, NULL, 0)) == (void *) -1)
162 		err(1, "sender: shmat");
163 
164 	/*
165 	 * Write the test pattern into the shared memory buffer.
166 	 */
167 	strlcpy(shm_buf, m_str, pgsize);
168 
169 	switch ((child_pid = fork())) {
170 	case -1:
171 		err(1, "fork");
172 		/* NOTREACHED */
173 
174 	case 0:
175 		receiver();
176 		break;
177 
178 	default:
179 		break;
180 	}
181 
182 	/*
183 	 * Suspend forever; when we get SIGCHLD, the handler will exit.
184 	 */
185 	sigemptyset(&sigmask);
186 	(void) sigsuspend(&sigmask);
187 
188 	/*
189 	 * ...and any other signal is an unexpected error.
190 	 */
191 	errx(1, "sender: received unexpected signal");
192 }
193 
194 void
195 sigsys_handler(signo)
196 	int signo;
197 {
198 
199 	errx(1, "System V Shared Memory support is not present in the kernel");
200 }
201 
202 void
203 sigchld_handler(signo)
204 	int signo;
205 {
206 	struct shmid_ds s_ds;
207 	int cstatus;
208 
209 	/*
210 	 * Reap the child; if it exited successfully, then the test passed!
211 	 */
212 	if (waitpid(child_pid, &cstatus, 0) != child_pid)
213 		err(1, "waitpid");
214 
215 	if (WIFEXITED(cstatus) == 0)
216 		errx(1, "receiver exited abnormally");
217 
218 	if (WEXITSTATUS(cstatus) != 0)
219 		errx(1, "receiver exited with status %d",
220 		    WEXITSTATUS(cstatus));
221 
222 	/*
223 	 * If we get here, the child has exited normally, and thus
224 	 * we should exit normally too.  First, tho, we print out
225 	 * the final stats for the message queue.
226 	 */
227 
228 	if (shmctl(sender_shmid, IPC_STAT, &s_ds) == -1)
229 		err(1, "shmctl IPC_STAT");
230 
231 	if (verbose)
232 		print_shmid_ds(&s_ds, 0600);
233 
234 	exit(0);
235 }
236 
237 void
238 cleanup()
239 {
240 
241 	/*
242 	 * If we're the sender, and it exists, remove the shared memory area.
243 	 */
244 	if (child_pid != 0 && sender_shmid != -1) {
245 		if (shmctl(sender_shmid, IPC_RMID, NULL) == -1)
246 			warn("shmctl IPC_RMID");
247 	}
248 
249 	remove(keyname);
250 }
251 
252 void
253 print_shmid_ds(sp, mode)
254 	struct shmid_ds *sp;
255 	mode_t mode;
256 {
257 	uid_t uid = geteuid();
258 	gid_t gid = getegid();
259 
260 	printf("PERM: uid %u, gid %u, cuid %u, cgid %u, mode 0%o\n",
261 	    sp->shm_perm.uid, sp->shm_perm.gid,
262 	    sp->shm_perm.cuid, sp->shm_perm.cgid,
263 	    sp->shm_perm.mode & 0777);
264 
265 	printf("segsz %lu, lpid %d, cpid %d, nattch %u\n",
266 	    (u_long)sp->shm_segsz, sp->shm_lpid, sp->shm_cpid,
267 	    sp->shm_nattch);
268 
269 	printf("atime: %s", ctime(&sp->shm_atime));
270 	printf("dtime: %s", ctime(&sp->shm_dtime));
271 	printf("ctime: %s", ctime(&sp->shm_ctime));
272 
273 	/*
274 	 * Sanity check a few things.
275 	 */
276 
277 	if (sp->shm_perm.uid != uid || sp->shm_perm.cuid != uid)
278 		errx(1, "uid mismatch");
279 
280 	if (sp->shm_perm.gid != gid || sp->shm_perm.cgid != gid)
281 		errx(1, "gid mismatch");
282 
283 	if ((sp->shm_perm.mode & 0777) != mode)
284 		errx(1, "mode mismatch");
285 }
286 
287 void
288 receiver()
289 {
290 	int shmid;
291 	void *shm_buf;
292 	void *block;
293 
294 	if ((shmid = shmget(shmkey, pgsize, 0)) == -1)
295 		err(1, "receiver: shmget");
296 
297 	if ((shm_buf = shmat(shmid, NULL, 0)) == (void *) -1)
298 		err(1, "receiver: shmat");
299 
300 	if (verbose)
301 		printf("%.*s\n", (int)pgsize, (const char *)shm_buf);
302 	if (strcmp((const char *)shm_buf, m_str) != 0)
303 		errx(1, "receiver: data isn't correct");
304 
305 	/* mmap() a page to get a distinct, freeable address */
306 	block = mmap(NULL, pgsize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,
307 	    -1, 0);
308 	if (block == MAP_FAILED)
309 		err(1, "receiver: mmap");
310 
311 	/* detach, then try to attach, conflicting with the mmap() */
312 	if (shmdt(shm_buf) == -1)
313 		err(1, "receiver: shmdt");
314 	if ((shm_buf = shmat(shmid, block, 0)) != (void *) -1)
315 		errx(1, "receiver: shmat(conflict) succeeded!");
316 	if (errno != ENOMEM)
317 		err(1, "receiver: shmat(conflict) wrong error");
318 
319 	/* free up that address and try again */
320 	if (munmap(block, pgsize) == -1)
321 		err(1, "receiver: munmap");
322 	if ((shm_buf = shmat(shmid, block, 0)) == (void *) -1)
323 		err(1, "receiver: shmat(fixed)");
324 
325 	if (shm_buf != block)
326 		errx(1, "receiver: shmat not at expected address: %p != %p",
327 		    shm_buf, block);
328 
329 	if (verbose)
330 		printf("%.*s\n", (int)pgsize, (const char *)shm_buf);
331 	if (strcmp((const char *)shm_buf, m_str) != 0)
332 		errx(1, "receiver: data isn't correct second time");
333 
334 	exit(0);
335 }
336