xref: /openbsd/regress/sys/kern/sysvmsg/msgtest.c (revision 49a6e16f)
1 /*	$OpenBSD: msgtest.c,v 1.7 2021/12/13 16:56:50 deraadt Exp $	*/
2 /*	$NetBSD: msgtest.c,v 1.6 2001/02/19 22:44:41 cgd Exp $	*/
3 
4 /*-
5  * Copyright (c) 1999 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10  * NASA Ames Research Center.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Test the SVID-compatible Message Queue facility.
36  */
37 
38 #include <sys/ipc.h>
39 #include <sys/msg.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 
51 int	main(int, char *[]);
52 void	print_msqid_ds(struct msqid_ds *, mode_t);
53 void	sigsys_handler(int);
54 void	sigchld_handler(int);
55 void	cleanup(void);
56 void	receiver(void);
57 
58 #define	MESSAGE_TEXT_LEN	256
59 
60 struct thismsg {
61 	long	mtype;
62 	char	mtext[MESSAGE_TEXT_LEN];
63 };
64 
65 const char *m1_str = "California is overrated.";
66 const char *m2_str = "The quick brown fox jumped over the lazy dog.";
67 
68 #define	MTYPE_1		1
69 #define	MTYPE_1_ACK	2
70 
71 #define	MTYPE_2		3
72 #define	MTYPE_2_ACK	4
73 
74 int	sender_msqid = -1;
75 pid_t	child_pid;
76 
77 key_t	msgkey;
78 
79 char keyname[] = "/tmp/msgtestXXXXXXXX";
80 
81 int verbose;
82 
83 int
main(int argc,char ** argv)84 main(int argc, char **argv)
85 {
86 	struct sigaction sa;
87 	struct msqid_ds m_ds;
88 	struct thismsg m;
89 	sigset_t sigmask;
90 	int fd, ch;
91 
92 	if ((fd = mkstemp(keyname)) < 0)
93 		err(1, "mkstemp");
94 
95 	close(fd);
96 
97 	while ((ch = getopt(argc, argv, "v")) != -1) {
98 		switch (ch) {
99 		case 'v':
100 			verbose = 1;
101 			break;
102 		default:
103 			fprintf(stderr, "Usage: msgtest [-v]\n");
104 			exit(1);
105 		}
106 	}
107 
108 	/*
109 	 * Install a SIGSYS handler so that we can exit gracefully if
110 	 * System V Message Queue support isn't in the kernel.
111 	 */
112 	sa.sa_handler = sigsys_handler;
113 	sigemptyset(&sa.sa_mask);
114 	sa.sa_flags = 0;
115 	if (sigaction(SIGSYS, &sa, NULL) == -1)
116 		err(1, "sigaction SIGSYS");
117 
118 	/*
119 	 * Install and SIGCHLD handler to deal with all possible exit
120 	 * conditions of the receiver.
121 	 */
122 	sa.sa_handler = sigchld_handler;
123 	sigemptyset(&sa.sa_mask);
124 	sa.sa_flags = 0;
125 	if (sigaction(SIGCHLD, &sa, NULL) == -1)
126 		err(1, "sigaction SIGCHLD");
127 
128 	msgkey = ftok(keyname, 4160);
129 
130 	/*
131 	 * Initialize child_pid to ourselves to that the cleanup function
132 	 * works before we create the receiver.
133 	 */
134 	child_pid = getpid();
135 
136 	/*
137 	 * Make sure that when the sender exits, the message queue is
138 	 * removed.
139 	 */
140 	if (atexit(cleanup) == -1)
141 		err(1, "atexit");
142 
143 	if ((sender_msqid = msgget(msgkey, IPC_CREAT | 0640)) == -1)
144 		err(1, "msgget");
145 
146 	if (msgctl(sender_msqid, IPC_STAT, &m_ds) == -1)
147 		err(1, "msgctl IPC_STAT");
148 
149 	if (verbose)
150 		print_msqid_ds(&m_ds, 0640);
151 
152 	m_ds.msg_perm.mode = (m_ds.msg_perm.mode & ~0777) | 0600;
153 
154 	if (msgctl(sender_msqid, IPC_SET, &m_ds) == -1)
155 		err(1, "msgctl IPC_SET");
156 
157 	memset(&m_ds, 0, sizeof(m_ds));
158 
159 	if (msgctl(sender_msqid, IPC_STAT, &m_ds) == -1)
160 		err(1, "msgctl IPC_STAT");
161 
162 	if ((m_ds.msg_perm.mode & 0777) != 0600)
163 		err(1, "IPC_SET of mode didn't hold");
164 
165 	if (verbose)
166 		print_msqid_ds(&m_ds, 0600);
167 
168 	switch ((child_pid = fork())) {
169 	case -1:
170 		err(1, "fork");
171 		/* NOTREACHED */
172 
173 	case 0:
174 		receiver();
175 		break;
176 
177 	default:
178 		break;
179 	}
180 
181 	/*
182 	 * Send the first message to the receiver and wait for the ACK.
183 	 */
184 	m.mtype = MTYPE_1;
185 	strlcpy(m.mtext, m1_str, sizeof m.mtext);
186 	if (msgsnd(sender_msqid, &m, sizeof(m), 0) == -1)
187 		err(1, "sender: msgsnd 1");
188 
189 	if (msgrcv(sender_msqid, &m, sizeof(m), MTYPE_1_ACK, 0) != sizeof(m))
190 		err(1, "sender: msgrcv 1 ack");
191 
192 	if (verbose)
193 		print_msqid_ds(&m_ds, 0600);
194 
195 	/*
196 	 * Send the second message to the receiver and wait for the ACK.
197 	 */
198 	m.mtype = MTYPE_2;
199 	strlcpy(m.mtext, m2_str, sizeof m.mtext);
200 	if (msgsnd(sender_msqid, &m, sizeof(m), 0) == -1)
201 		err(1, "sender: msgsnd 2");
202 
203 	if (msgrcv(sender_msqid, &m, sizeof(m), MTYPE_2_ACK, 0) != sizeof(m))
204 		err(1, "sender: msgrcv 2 ack");
205 
206 	/*
207 	 * Suspend forever; when we get SIGCHLD, the handler will exit.
208 	 */
209 	sigemptyset(&sigmask);
210 	(void) sigsuspend(&sigmask);
211 
212 	/*
213 	 * ...and any other signal is an unexpected error.
214 	 */
215 	errx(1, "sender: received unexpected signal");
216 }
217 
218 void
sigsys_handler(signo)219 sigsys_handler(signo)
220 	int signo;
221 {
222 
223 	errx(1, "System V Message Queue support is not present in the kernel");
224 }
225 
226 void
sigchld_handler(signo)227 sigchld_handler(signo)
228 	int signo;
229 {
230 	struct msqid_ds m_ds;
231 	int cstatus;
232 
233 	/*
234 	 * Reap the child; if it exited successfully, then the test passed!
235 	 */
236 	if (waitpid(child_pid, &cstatus, 0) != child_pid)
237 		err(1, "waitpid");
238 
239 	if (WIFEXITED(cstatus) == 0)
240 		errx(1, "receiver exited abnormally");
241 
242 	if (WEXITSTATUS(cstatus) != 0)
243 		errx(1, "receiver exited with status %d",
244 		    WEXITSTATUS(cstatus));
245 
246 	/*
247 	 * If we get here, the child has exited normally, and thus
248 	 * we should exit normally too.  First, tho, we print out
249 	 * the final stats for the message queue.
250 	 */
251 
252 	if (msgctl(sender_msqid, IPC_STAT, &m_ds) == -1)
253 		err(1, "msgctl IPC_STAT");
254 
255 	if (verbose)
256 		print_msqid_ds(&m_ds, 0600);
257 
258 	exit(0);
259 }
260 
261 void
cleanup()262 cleanup()
263 {
264 
265 	/*
266 	 * If we're the sender, and it exists, remove the message queue.
267 	 */
268 	if (child_pid != 0 && sender_msqid != -1) {
269 		if (msgctl(sender_msqid, IPC_RMID, NULL) == -1)
270 			warn("msgctl IPC_RMID");
271 	}
272 
273 	remove(keyname);
274 }
275 
276 void
print_msqid_ds(mp,mode)277 print_msqid_ds(mp, mode)
278 	struct msqid_ds *mp;
279 	mode_t mode;
280 {
281 	uid_t uid = geteuid();
282 	gid_t gid = getegid();
283 
284 	printf("PERM: uid %u, gid %u, cuid %u, cgid %u, mode 0%o\n",
285 	    mp->msg_perm.uid, mp->msg_perm.gid,
286 	    mp->msg_perm.cuid, mp->msg_perm.cgid,
287 	    mp->msg_perm.mode & 0777);
288 
289 	printf("qnum %lu, qbytes %lu, lspid %d, lrpid %d\n",
290 	    mp->msg_qnum, (u_long)mp->msg_qbytes, mp->msg_lspid,
291 	    mp->msg_lrpid);
292 
293 	printf("stime: %s", ctime(&mp->msg_stime));
294 	printf("rtime: %s", ctime(&mp->msg_rtime));
295 	printf("ctime: %s", ctime(&mp->msg_ctime));
296 
297 	/*
298 	 * Sanity check a few things.
299 	 */
300 
301 	if (mp->msg_perm.uid != uid || mp->msg_perm.cuid != uid)
302 		errx(1, "uid mismatch");
303 
304 	if (mp->msg_perm.gid != gid || mp->msg_perm.cgid != gid)
305 		errx(1, "gid mismatch");
306 
307 	if ((mp->msg_perm.mode & 0777) != mode)
308 		errx(1, "mode mismatch");
309 }
310 
311 void
receiver()312 receiver()
313 {
314 	struct thismsg m;
315 	int msqid;
316 
317 	if ((msqid = msgget(msgkey, 0)) == -1)
318 		err(1, "receiver: msgget");
319 
320 	/*
321 	 * Receive the first message, print it, and send an ACK.
322 	 */
323 
324 	if (msgrcv(msqid, &m, sizeof(m), MTYPE_1, 0) != sizeof(m))
325 		err(1, "receiver: msgrcv 1");
326 
327 	if (verbose)
328 		printf("%s\n", m.mtext);
329 	if (strcmp(m.mtext, m1_str) != 0)
330 		err(1, "receiver: message 1 data isn't correct");
331 
332 	m.mtype = MTYPE_1_ACK;
333 
334 	if (msgsnd(msqid, &m, sizeof(m), 0) == -1)
335 		err(1, "receiver: msgsnd ack 1");
336 
337 	/*
338 	 * Receive the second message, print it, and send an ACK.
339 	 */
340 
341 	if (msgrcv(msqid, &m, sizeof(m), MTYPE_2, 0) != sizeof(m))
342 		err(1, "receiver: msgrcv 2");
343 
344 	if (verbose)
345 		printf("%s\n", m.mtext);
346 	if (strcmp(m.mtext, m2_str) != 0)
347 		err(1, "receiver: message 2 data isn't correct");
348 
349 	m.mtype = MTYPE_2_ACK;
350 
351 	if (msgsnd(msqid, &m, sizeof(m), 0) == -1)
352 		err(1, "receiver: msgsnd ack 2");
353 
354 	exit(0);
355 }
356