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