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