shmtest.c revision 280494
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: shmtest.c,v 1.3 2002/07/20 08:36:26 grant Exp $
31 * $FreeBSD: stable/10/tools/regression/sysvshm/shmtest.c 280494 2015-03-25 08:23:08Z kib $
32 */
33
34/*
35 * Test the SVID-compatible Shared Memory facility.
36 */
37
38#include <sys/types.h>
39#include <sys/ipc.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
52static void print_shmid_ds(struct shmid_ds *, mode_t);
53static void sigsys_handler(int);
54static void sigchld_handler(int);
55static void cleanup(void);
56static void receiver(void);
57static void usage(void);
58
59static const char *m_str = "The quick brown fox jumped over the lazy dog.";
60
61static int sender_shmid = -1;
62static pid_t child_pid;
63static key_t shmkey;
64static size_t pgsize;
65
66int
67main(int argc, char *argv[])
68{
69	struct sigaction sa;
70	struct shmid_ds s_ds;
71	sigset_t sigmask;
72	char *shm_buf;
73
74	if (argc != 2)
75		usage();
76
77	/*
78	 * Install a SIGSYS handler so that we can exit gracefully if
79	 * System V Shared Memory support isn't in the kernel.
80	 */
81	sa.sa_handler = sigsys_handler;
82	sigemptyset(&sa.sa_mask);
83	sa.sa_flags = 0;
84	if (sigaction(SIGSYS, &sa, NULL) == -1)
85		err(1, "sigaction SIGSYS");
86
87	/*
88	 * Install and SIGCHLD handler to deal with all possible exit
89	 * conditions of the receiver.
90	 */
91	sa.sa_handler = sigchld_handler;
92	sigemptyset(&sa.sa_mask);
93	sa.sa_flags = 0;
94	if (sigaction(SIGCHLD, &sa, NULL) == -1)
95		err(1, "sigaction SIGCHLD");
96
97	pgsize = sysconf(_SC_PAGESIZE);
98
99	shmkey = ftok(argv[1], 4160);
100
101	/*
102	 * Initialize child_pid to ourselves to that the cleanup function
103	 * works before we create the receiver.
104	 */
105	child_pid = getpid();
106
107	/*
108	 * Make sure that when the sender exits, the message queue is
109	 * removed.
110	 */
111	if (atexit(cleanup) == -1)
112		err(1, "atexit");
113
114	if ((sender_shmid = shmget(shmkey, pgsize, IPC_CREAT | 0640)) == -1)
115		err(1, "shmget");
116
117	if (shmctl(sender_shmid, IPC_STAT, &s_ds) == -1)
118		err(1, "shmctl IPC_STAT");
119
120	print_shmid_ds(&s_ds, 0640);
121
122	s_ds.shm_perm.mode = (s_ds.shm_perm.mode & ~0777) | 0600;
123
124	if (shmctl(sender_shmid, IPC_SET, &s_ds) == -1)
125		err(1, "shmctl IPC_SET");
126
127	memset(&s_ds, 0, sizeof(s_ds));
128
129	if (shmctl(sender_shmid, IPC_STAT, &s_ds) == -1)
130		err(1, "shmctl IPC_STAT");
131
132	if ((s_ds.shm_perm.mode & 0777) != 0600)
133		err(1, "IPC_SET of mode didn't hold");
134
135	print_shmid_ds(&s_ds, 0600);
136
137	if ((shm_buf = shmat(sender_shmid, NULL, 0)) == (void *) -1)
138		err(1, "sender: shmat");
139
140	/*
141	 * Write the test pattern into the shared memory buffer.
142	 */
143	strcpy(shm_buf, m_str);
144
145	switch ((child_pid = fork())) {
146	case -1:
147		err(1, "fork");
148		/* NOTREACHED */
149
150	case 0:
151		receiver();
152		break;
153
154	default:
155		break;
156	}
157
158	/*
159	 * Suspend forever; when we get SIGCHLD, the handler will exit.
160	 */
161	sigemptyset(&sigmask);
162	(void) sigsuspend(&sigmask);
163
164	/*
165	 * ...and any other signal is an unexpected error.
166	 */
167	errx(1, "sender: received unexpected signal");
168}
169
170static void
171sigsys_handler(int signo __unused)
172{
173
174	errx(1, "System V Shared Memory support is not present in the kernel");
175}
176
177static void
178sigchld_handler(int signo __unused)
179{
180	struct shmid_ds s_ds;
181	int cstatus;
182
183	/*
184	 * Reap the child; if it exited successfully, then the test passed!
185	 */
186	if (waitpid(child_pid, &cstatus, 0) != child_pid)
187		err(1, "waitpid");
188
189	if (WIFEXITED(cstatus) == 0)
190		errx(1, "receiver exited abnormally");
191
192	if (WEXITSTATUS(cstatus) != 0)
193		errx(1, "receiver exited with status %d",
194		    WEXITSTATUS(cstatus));
195
196	/*
197	 * If we get here, the child has exited normally, and thus
198	 * we should exit normally too.  First, tho, we print out
199	 * the final stats for the message queue.
200	 */
201
202	if (shmctl(sender_shmid, IPC_STAT, &s_ds) == -1)
203		err(1, "shmctl IPC_STAT");
204
205	print_shmid_ds(&s_ds, 0600);
206
207	exit(0);
208}
209
210static void
211cleanup(void)
212{
213
214	/*
215	 * If we're the sender, and it exists, remove the shared memory area.
216	 */
217	if (child_pid != 0 && sender_shmid != -1) {
218		if (shmctl(sender_shmid, IPC_RMID, NULL) == -1)
219			warn("shmctl IPC_RMID");
220	}
221}
222
223static void
224print_shmid_ds(struct shmid_ds *sp, mode_t mode)
225{
226	uid_t uid = geteuid();
227	gid_t gid = getegid();
228
229	printf("PERM: uid %d, gid %d, cuid %d, cgid %d, mode 0%o\n",
230	    sp->shm_perm.uid, sp->shm_perm.gid,
231	    sp->shm_perm.cuid, sp->shm_perm.cgid,
232	    sp->shm_perm.mode & 0777);
233
234	printf("segsz %lu, lpid %d, cpid %d, nattch %u\n",
235	    (u_long)sp->shm_segsz, sp->shm_lpid, sp->shm_cpid,
236	    sp->shm_nattch);
237
238	printf("atime: %s", ctime(&sp->shm_atime));
239	printf("dtime: %s", ctime(&sp->shm_dtime));
240	printf("ctime: %s", ctime(&sp->shm_ctime));
241
242	/*
243	 * Sanity check a few things.
244	 */
245
246	if (sp->shm_perm.uid != uid || sp->shm_perm.cuid != uid)
247		errx(1, "uid mismatch");
248
249	if (sp->shm_perm.gid != gid || sp->shm_perm.cgid != gid)
250		errx(1, "gid mismatch");
251
252	if ((sp->shm_perm.mode & 0777) != mode)
253		errx(1, "mode mismatch");
254}
255
256static void
257usage(void)
258{
259
260	fprintf(stderr, "usage: %s keypath\n", getprogname());
261	exit(1);
262}
263
264static void
265receiver(void)
266{
267	int shmid;
268	void *shm_buf;
269
270	if ((shmid = shmget(shmkey, pgsize, 0)) == -1)
271		err(1, "receiver: shmget");
272
273	if ((shm_buf = shmat(shmid, NULL, 0)) == (void *) -1)
274		err(1, "receiver: shmat");
275
276	printf("%s\n", (const char *)shm_buf);
277	if (strcmp((const char *)shm_buf, m_str) != 0)
278		err(1, "receiver: data isn't correct");
279
280	exit(0);
281}
282