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