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