1101902Salfred/*-
2101902Salfred * Copyright (c) 1999 The NetBSD Foundation, Inc.
3101902Salfred * All rights reserved.
4101902Salfred *
5101902Salfred * This code is derived from software contributed to The NetBSD Foundation
6101902Salfred * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
7101902Salfred * NASA Ames Research Center.
8101902Salfred *
9101902Salfred * Redistribution and use in source and binary forms, with or without
10101902Salfred * modification, are permitted provided that the following conditions
11101902Salfred * are met:
12101902Salfred * 1. Redistributions of source code must retain the above copyright
13101902Salfred *    notice, this list of conditions and the following disclaimer.
14101902Salfred * 2. Redistributions in binary form must reproduce the above copyright
15101902Salfred *    notice, this list of conditions and the following disclaimer in the
16101902Salfred *    documentation and/or other materials provided with the distribution.
17101902Salfred *
18101902Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19101902Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20101902Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21101902Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22101902Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23101902Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24101902Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25101902Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26101902Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27101902Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28101902Salfred * POSSIBILITY OF SUCH DAMAGE.
29101902Salfred *
30101902Salfred * Obtained from: $NetBSD: msgtest.c,v 1.7 2002/07/20 08:36:25 grant Exp $
31101902Salfred * $FreeBSD$
32101902Salfred */
33101902Salfred
34101902Salfred/*
35101902Salfred * Test the SVID-compatible Message Queue facility.
36101902Salfred */
37101902Salfred
38235719Skevlo#include <sys/types.h>
39101902Salfred#include <sys/ipc.h>
40101902Salfred#include <sys/msg.h>
41101902Salfred#include <sys/wait.h>
42101902Salfred
43101902Salfred#include <err.h>
44101902Salfred#include <errno.h>
45101902Salfred#include <signal.h>
46101902Salfred#include <stdio.h>
47101902Salfred#include <stdlib.h>
48101902Salfred#include <string.h>
49101902Salfred#include <time.h>
50101902Salfred#include <unistd.h>
51101902Salfred
52101902Salfredvoid	print_msqid_ds (struct msqid_ds *, mode_t);
53101902Salfredvoid	sigsys_handler(int);
54101902Salfredvoid	sigchld_handler (int);
55101902Salfredvoid	cleanup (void);
56101902Salfredvoid	receiver (void);
57101902Salfredvoid	usage (void);
58101902Salfred
59101902Salfred#define	MESSAGE_TEXT_LEN	255
60101902Salfred
61101902Salfred/*
62101902Salfred * Define it as test_mymsg because we already have struct mymsg and we dont
63101902Salfred * want to conflict with it.  Also, regression fails when the default mymsg
64101902Salfred * struct is used, because mtext[] array is '1', so the passed string cannot
65101902Salfred * be processed.
66101902Salfred */
67101902Salfredstruct test_mymsg {
68101902Salfred	long	mtype;
69101902Salfred	char	mtext[MESSAGE_TEXT_LEN];
70101902Salfred};
71101902Salfred
72101902Salfredconst char *m1_str = "California is overrated.";
73101902Salfredconst char *m2_str = "The quick brown fox jumped over the lazy dog.";
74101902Salfred
75101902Salfred#define	MTYPE_1		1
76101902Salfred#define	MTYPE_1_ACK	2
77101902Salfred
78101902Salfred#define	MTYPE_2		3
79101902Salfred#define	MTYPE_2_ACK	4
80101902Salfred
81101902Salfredint	sender_msqid = -1;
82101902Salfredpid_t	child_pid;
83101902Salfred
84101902Salfredkey_t	msgkey;
85101902Salfred
86101902Salfredint
87101902Salfredmain(int argc, char *argv[])
88101902Salfred{
89101902Salfred	struct sigaction sa;
90101902Salfred	struct msqid_ds m_ds;
91101902Salfred	struct test_mymsg m;
92101902Salfred	sigset_t sigmask;
93101902Salfred
94101902Salfred	if (argc != 2)
95101902Salfred		usage();
96101902Salfred
97101902Salfred	/*
98101902Salfred	 * Install a SIGSYS handler so that we can exit gracefully if
99101902Salfred	 * System V Message Queue support isn't in the kernel.
100101902Salfred	 */
101101902Salfred	sa.sa_handler = sigsys_handler;
102101902Salfred	sigemptyset(&sa.sa_mask);
103101902Salfred	sa.sa_flags = 0;
104101902Salfred	if (sigaction(SIGSYS, &sa, NULL) == -1)
105101902Salfred		err(1, "sigaction SIGSYS");
106101902Salfred
107101902Salfred	/*
108101902Salfred	 * Install and SIGCHLD handler to deal with all possible exit
109101902Salfred	 * conditions of the receiver.
110101902Salfred	 */
111101902Salfred	sa.sa_handler = sigchld_handler;
112101902Salfred	sigemptyset(&sa.sa_mask);
113101902Salfred	sa.sa_flags = 0;
114101902Salfred	if (sigaction(SIGCHLD, &sa, NULL) == -1)
115101902Salfred		err(1, "sigaction SIGCHLD");
116101902Salfred
117101902Salfred	msgkey = ftok(argv[1], 4160);
118101902Salfred
119101902Salfred	/*
120101902Salfred	 * Initialize child_pid to ourselves to that the cleanup function
121101902Salfred	 * works before we create the receiver.
122101902Salfred	 */
123101902Salfred	child_pid = getpid();
124101902Salfred
125101902Salfred	/*
126101902Salfred	 * Make sure that when the sender exits, the message queue is
127101902Salfred	 * removed.
128101902Salfred	 */
129101902Salfred	if (atexit(cleanup) == -1)
130101902Salfred		err(1, "atexit");
131101902Salfred
132101902Salfred	if ((sender_msqid = msgget(msgkey, IPC_CREAT | 0640)) == -1)
133101902Salfred		err(1, "msgget");
134101902Salfred
135101902Salfred	if (msgctl(sender_msqid, IPC_STAT, &m_ds) == -1)
136101902Salfred		err(1, "msgctl IPC_STAT");
137101902Salfred
138101902Salfred	print_msqid_ds(&m_ds, 0640);
139101902Salfred
140101902Salfred	m_ds.msg_perm.mode = (m_ds.msg_perm.mode & ~0777) | 0600;
141101902Salfred
142101902Salfred	if (msgctl(sender_msqid, IPC_SET, &m_ds) == -1)
143101902Salfred		err(1, "msgctl IPC_SET");
144101902Salfred
145101902Salfred	bzero(&m_ds, sizeof m_ds);
146101902Salfred
147101902Salfred	if (msgctl(sender_msqid, IPC_STAT, &m_ds) == -1)
148101902Salfred		err(1, "msgctl IPC_STAT");
149101902Salfred
150101902Salfred	if ((m_ds.msg_perm.mode & 0777) != 0600)
151101902Salfred		err(1, "IPC_SET of mode didn't hold");
152101902Salfred
153101902Salfred	print_msqid_ds(&m_ds, 0600);
154101902Salfred
155101902Salfred	switch ((child_pid = fork())) {
156101902Salfred	case -1:
157101902Salfred		err(1, "fork");
158101902Salfred		/* NOTREACHED */
159101902Salfred
160101902Salfred	case 0:
161101902Salfred		receiver();
162101902Salfred		break;
163101902Salfred
164101902Salfred	default:
165101902Salfred		break;
166101902Salfred	}
167101902Salfred
168101902Salfred	/*
169101902Salfred	 * Send the first message to the receiver and wait for the ACK.
170101902Salfred	 */
171101902Salfred	m.mtype = MTYPE_1;
172101902Salfred	strcpy(m.mtext, m1_str);
173164342Sjkim	if (msgsnd(sender_msqid, &m, strlen(m1_str) + 1, 0) == -1)
174101902Salfred		err(1, "sender: msgsnd 1");
175101902Salfred
176164342Sjkim	if (msgrcv(sender_msqid, &m, sizeof(m.mtext), MTYPE_1_ACK, 0) !=
177164342Sjkim	    strlen(m1_str) + 1)
178101902Salfred		err(1, "sender: msgrcv 1 ack");
179101902Salfred
180101902Salfred	print_msqid_ds(&m_ds, 0600);
181101902Salfred
182101902Salfred	/*
183101902Salfred	 * Send the second message to the receiver and wait for the ACK.
184101902Salfred	 */
185101902Salfred	m.mtype = MTYPE_2;
186101902Salfred	strcpy(m.mtext, m2_str);
187164342Sjkim	if (msgsnd(sender_msqid, &m, strlen(m2_str) + 1, 0) == -1)
188101902Salfred		err(1, "sender: msgsnd 2");
189101902Salfred
190164342Sjkim	if (msgrcv(sender_msqid, &m, sizeof(m.mtext), MTYPE_2_ACK, 0) !=
191164342Sjkim	    strlen(m2_str) + 1)
192101902Salfred		err(1, "sender: msgrcv 2 ack");
193101902Salfred
194101902Salfred	/*
195101902Salfred	 * Suspend forever; when we get SIGCHLD, the handler will exit.
196101902Salfred	 */
197101902Salfred	sigemptyset(&sigmask);
198101902Salfred	(void) sigsuspend(&sigmask);
199101902Salfred
200101902Salfred	/*
201101902Salfred	 * ...and any other signal is an unexpected error.
202101902Salfred	 */
203101902Salfred	errx(1, "sender: received unexpected signal");
204101902Salfred}
205101902Salfred
206101902Salfredvoid
207101902Salfredsigsys_handler(int signo)
208101902Salfred{
209101902Salfred
210101902Salfred	errx(1, "System V Message Queue support is not present in the kernel");
211101902Salfred}
212101902Salfred
213101902Salfredvoid
214101902Salfredsigchld_handler(int signo)
215101902Salfred{
216101902Salfred	struct msqid_ds m_ds;
217101902Salfred	int cstatus;
218101902Salfred
219101902Salfred	/*
220101902Salfred	 * Reap the child; if it exited successfully, then the test passed!
221101902Salfred	 */
222101902Salfred	if (waitpid(child_pid, &cstatus, 0) != child_pid)
223101902Salfred		err(1, "waitpid");
224101902Salfred
225101902Salfred	if (WIFEXITED(cstatus) == 0)
226101902Salfred		errx(1, "receiver exited abnormally");
227101902Salfred
228101902Salfred	if (WEXITSTATUS(cstatus) != 0)
229101902Salfred		errx(1, "receiver exited with status %d",
230101902Salfred		    WEXITSTATUS(cstatus));
231101902Salfred
232101902Salfred	/*
233101902Salfred	 * If we get here, the child has exited normally, and thus
234101902Salfred	 * we should exit normally too.  First, tho, we print out
235101902Salfred	 * the final stats for the message queue.
236101902Salfred	 */
237101902Salfred
238101902Salfred	if (msgctl(sender_msqid, IPC_STAT, &m_ds) == -1)
239101902Salfred		err(1, "msgctl IPC_STAT");
240101902Salfred
241101902Salfred	print_msqid_ds(&m_ds, 0600);
242101902Salfred
243101902Salfred	exit(0);
244101902Salfred}
245101902Salfred
246101902Salfredvoid
247101902Salfredcleanup()
248101902Salfred{
249101902Salfred
250101902Salfred	/*
251101902Salfred	 * If we're the sender, and it exists, remove the message queue.
252101902Salfred	 */
253101902Salfred	if (child_pid != 0 && sender_msqid != -1) {
254101902Salfred		if (msgctl(sender_msqid, IPC_RMID, NULL) == -1)
255101902Salfred			warn("msgctl IPC_RMID");
256101902Salfred	}
257101902Salfred}
258101902Salfred
259101902Salfredvoid
260101902Salfredprint_msqid_ds(struct msqid_ds *mp, mode_t mode)
261101902Salfred{
262101902Salfred	uid_t uid = geteuid();
263101902Salfred	gid_t gid = getegid();
264101902Salfred
265101902Salfred	printf("PERM: uid %d, gid %d, cuid %d, cgid %d, mode 0%o\n",
266101902Salfred	    mp->msg_perm.uid, mp->msg_perm.gid,
267101902Salfred	    mp->msg_perm.cuid, mp->msg_perm.cgid,
268101902Salfred	    mp->msg_perm.mode & 0777);
269101902Salfred
270101902Salfred	printf("qnum %lu, qbytes %lu, lspid %d, lrpid %d\n",
271101902Salfred	    mp->msg_qnum, (u_long)mp->msg_qbytes, mp->msg_lspid,
272101902Salfred	    mp->msg_lrpid);
273101902Salfred
274101902Salfred	printf("stime: %s", ctime(&mp->msg_stime));
275101902Salfred	printf("rtime: %s", ctime(&mp->msg_rtime));
276101902Salfred	printf("ctime: %s", ctime(&mp->msg_ctime));
277101902Salfred
278101902Salfred	/*
279101902Salfred	 * Sanity check a few things.
280101902Salfred	 */
281101902Salfred
282101902Salfred	if (mp->msg_perm.uid != uid || mp->msg_perm.cuid != uid)
283101902Salfred		errx(1, "uid mismatch");
284101902Salfred
285101902Salfred	if (mp->msg_perm.gid != gid || mp->msg_perm.cgid != gid)
286101902Salfred		errx(1, "gid mismatch");
287101902Salfred
288101902Salfred	if ((mp->msg_perm.mode & 0777) != mode)
289101902Salfred		errx(1, "mode mismatch");
290101902Salfred}
291101902Salfred
292101902Salfredvoid
293101902Salfredusage()
294101902Salfred{
295101902Salfred
296101902Salfred	fprintf(stderr, "usage: %s keypath\n", getprogname());
297101902Salfred	exit(1);
298101902Salfred}
299101902Salfred
300101902Salfredvoid
301101902Salfredreceiver()
302101902Salfred{
303101902Salfred	struct test_mymsg m;
304101902Salfred	int msqid;
305101902Salfred
306101902Salfred	if ((msqid = msgget(msgkey, 0)) == -1)
307101902Salfred		err(1, "receiver: msgget");
308101902Salfred
309101902Salfred	/*
310101902Salfred	 * Receive the first message, print it, and send an ACK.
311101902Salfred	 */
312101902Salfred
313164342Sjkim	if (msgrcv(msqid, &m, sizeof(m.mtext), MTYPE_1, 0) !=
314164342Sjkim	    strlen(m1_str) + 1)
315101902Salfred		err(1, "receiver: msgrcv 1");
316101902Salfred
317101902Salfred	printf("%s\n", m.mtext);
318101902Salfred	if (strcmp(m.mtext, m1_str) != 0)
319101902Salfred		err(1, "receiver: message 1 data isn't correct");
320101902Salfred
321101902Salfred	m.mtype = MTYPE_1_ACK;
322101902Salfred
323164342Sjkim	if (msgsnd(msqid, &m, strlen(m1_str) + 1, 0) == -1)
324101902Salfred		err(1, "receiver: msgsnd ack 1");
325101902Salfred
326101902Salfred	/*
327101902Salfred	 * Receive the second message, print it, and send an ACK.
328101902Salfred	 */
329101902Salfred
330164342Sjkim	if (msgrcv(msqid, &m, sizeof(m.mtext), MTYPE_2, 0) !=
331164342Sjkim	    strlen(m2_str) + 1)
332101902Salfred		err(1, "receiver: msgrcv 2");
333101902Salfred
334101902Salfred	printf("%s\n", m.mtext);
335101902Salfred	if (strcmp(m.mtext, m2_str) != 0)
336101902Salfred		err(1, "receiver: message 2 data isn't correct");
337101902Salfred
338101902Salfred	m.mtype = MTYPE_2_ACK;
339101902Salfred
340164342Sjkim	if (msgsnd(msqid, &m, strlen(m2_str) + 1, 0) == -1)
341101902Salfred		err(1, "receiver: msgsnd ack 2");
342101902Salfred
343101902Salfred	exit(0);
344101902Salfred}
345