1 /* $OpenBSD: msgtest.c,v 1.6 2008/06/26 05:42:06 ray 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/param.h> 39 #include <sys/ipc.h> 40 #include <sys/msg.h> 41 #include <sys/wait.h> 42 43 #include <err.h> 44 #include <errno.h> 45 #include <signal.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <time.h> 50 #include <unistd.h> 51 52 int main(int, char *[]); 53 void print_msqid_ds(struct msqid_ds *, mode_t); 54 void sigsys_handler(int); 55 void sigchld_handler(int); 56 void cleanup(void); 57 void receiver(void); 58 59 #define MESSAGE_TEXT_LEN 256 60 61 struct thismsg { 62 long mtype; 63 char mtext[MESSAGE_TEXT_LEN]; 64 }; 65 66 const char *m1_str = "California is overrated."; 67 const 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 75 int sender_msqid = -1; 76 pid_t child_pid; 77 78 key_t msgkey; 79 80 char keyname[] = "/tmp/msgtestXXXXXXXX"; 81 82 int verbose; 83 84 int 85 main(int argc, char **argv) 86 { 87 struct sigaction sa; 88 struct msqid_ds m_ds; 89 struct thismsg m; 90 sigset_t sigmask; 91 int fd, ch; 92 93 if ((fd = mkstemp(keyname)) < 0) 94 err(1, "mkstemp"); 95 96 close(fd); 97 98 while ((ch = getopt(argc, argv, "v")) != -1) { 99 switch (ch) { 100 case 'v': 101 verbose = 1; 102 break; 103 default: 104 fprintf(stderr, "Usage: msgtest [-v]\n"); 105 exit(1); 106 } 107 } 108 109 /* 110 * Install a SIGSYS handler so that we can exit gracefully if 111 * System V Message Queue support isn't in the kernel. 112 */ 113 sa.sa_handler = sigsys_handler; 114 sigemptyset(&sa.sa_mask); 115 sa.sa_flags = 0; 116 if (sigaction(SIGSYS, &sa, NULL) == -1) 117 err(1, "sigaction SIGSYS"); 118 119 /* 120 * Install and SIGCHLD handler to deal with all possible exit 121 * conditions of the receiver. 122 */ 123 sa.sa_handler = sigchld_handler; 124 sigemptyset(&sa.sa_mask); 125 sa.sa_flags = 0; 126 if (sigaction(SIGCHLD, &sa, NULL) == -1) 127 err(1, "sigaction SIGCHLD"); 128 129 msgkey = ftok(keyname, 4160); 130 131 /* 132 * Initialize child_pid to ourselves to that the cleanup function 133 * works before we create the receiver. 134 */ 135 child_pid = getpid(); 136 137 /* 138 * Make sure that when the sender exits, the message queue is 139 * removed. 140 */ 141 if (atexit(cleanup) == -1) 142 err(1, "atexit"); 143 144 if ((sender_msqid = msgget(msgkey, IPC_CREAT | 0640)) == -1) 145 err(1, "msgget"); 146 147 if (msgctl(sender_msqid, IPC_STAT, &m_ds) == -1) 148 err(1, "msgctl IPC_STAT"); 149 150 if (verbose) 151 print_msqid_ds(&m_ds, 0640); 152 153 m_ds.msg_perm.mode = (m_ds.msg_perm.mode & ~0777) | 0600; 154 155 if (msgctl(sender_msqid, IPC_SET, &m_ds) == -1) 156 err(1, "msgctl IPC_SET"); 157 158 memset(&m_ds, 0, sizeof(m_ds)); 159 160 if (msgctl(sender_msqid, IPC_STAT, &m_ds) == -1) 161 err(1, "msgctl IPC_STAT"); 162 163 if ((m_ds.msg_perm.mode & 0777) != 0600) 164 err(1, "IPC_SET of mode didn't hold"); 165 166 if (verbose) 167 print_msqid_ds(&m_ds, 0600); 168 169 switch ((child_pid = fork())) { 170 case -1: 171 err(1, "fork"); 172 /* NOTREACHED */ 173 174 case 0: 175 receiver(); 176 break; 177 178 default: 179 break; 180 } 181 182 /* 183 * Send the first message to the receiver and wait for the ACK. 184 */ 185 m.mtype = MTYPE_1; 186 strlcpy(m.mtext, m1_str, sizeof m.mtext); 187 if (msgsnd(sender_msqid, &m, sizeof(m), 0) == -1) 188 err(1, "sender: msgsnd 1"); 189 190 if (msgrcv(sender_msqid, &m, sizeof(m), MTYPE_1_ACK, 0) != sizeof(m)) 191 err(1, "sender: msgrcv 1 ack"); 192 193 if (verbose) 194 print_msqid_ds(&m_ds, 0600); 195 196 /* 197 * Send the second message to the receiver and wait for the ACK. 198 */ 199 m.mtype = MTYPE_2; 200 strlcpy(m.mtext, m2_str, sizeof m.mtext); 201 if (msgsnd(sender_msqid, &m, sizeof(m), 0) == -1) 202 err(1, "sender: msgsnd 2"); 203 204 if (msgrcv(sender_msqid, &m, sizeof(m), MTYPE_2_ACK, 0) != sizeof(m)) 205 err(1, "sender: msgrcv 2 ack"); 206 207 /* 208 * Suspend forever; when we get SIGCHLD, the handler will exit. 209 */ 210 sigemptyset(&sigmask); 211 (void) sigsuspend(&sigmask); 212 213 /* 214 * ...and any other signal is an unexpected error. 215 */ 216 errx(1, "sender: received unexpected signal"); 217 } 218 219 void 220 sigsys_handler(signo) 221 int signo; 222 { 223 224 errx(1, "System V Message Queue support is not present in the kernel"); 225 } 226 227 void 228 sigchld_handler(signo) 229 int signo; 230 { 231 struct msqid_ds m_ds; 232 int cstatus; 233 234 /* 235 * Reap the child; if it exited successfully, then the test passed! 236 */ 237 if (waitpid(child_pid, &cstatus, 0) != child_pid) 238 err(1, "waitpid"); 239 240 if (WIFEXITED(cstatus) == 0) 241 errx(1, "receiver exited abnormally"); 242 243 if (WEXITSTATUS(cstatus) != 0) 244 errx(1, "receiver exited with status %d", 245 WEXITSTATUS(cstatus)); 246 247 /* 248 * If we get here, the child has exited normally, and thus 249 * we should exit normally too. First, tho, we print out 250 * the final stats for the message queue. 251 */ 252 253 if (msgctl(sender_msqid, IPC_STAT, &m_ds) == -1) 254 err(1, "msgctl IPC_STAT"); 255 256 if (verbose) 257 print_msqid_ds(&m_ds, 0600); 258 259 exit(0); 260 } 261 262 void 263 cleanup() 264 { 265 266 /* 267 * If we're the sender, and it exists, remove the message queue. 268 */ 269 if (child_pid != 0 && sender_msqid != -1) { 270 if (msgctl(sender_msqid, IPC_RMID, NULL) == -1) 271 warn("msgctl IPC_RMID"); 272 } 273 274 remove(keyname); 275 } 276 277 void 278 print_msqid_ds(mp, mode) 279 struct msqid_ds *mp; 280 mode_t mode; 281 { 282 uid_t uid = geteuid(); 283 gid_t gid = getegid(); 284 285 printf("PERM: uid %u, gid %u, cuid %u, cgid %u, mode 0%o\n", 286 mp->msg_perm.uid, mp->msg_perm.gid, 287 mp->msg_perm.cuid, mp->msg_perm.cgid, 288 mp->msg_perm.mode & 0777); 289 290 printf("qnum %lu, qbytes %lu, lspid %d, lrpid %d\n", 291 mp->msg_qnum, (u_long)mp->msg_qbytes, mp->msg_lspid, 292 mp->msg_lrpid); 293 294 printf("stime: %s", ctime(&mp->msg_stime)); 295 printf("rtime: %s", ctime(&mp->msg_rtime)); 296 printf("ctime: %s", ctime(&mp->msg_ctime)); 297 298 /* 299 * Sanity check a few things. 300 */ 301 302 if (mp->msg_perm.uid != uid || mp->msg_perm.cuid != uid) 303 errx(1, "uid mismatch"); 304 305 if (mp->msg_perm.gid != gid || mp->msg_perm.cgid != gid) 306 errx(1, "gid mismatch"); 307 308 if ((mp->msg_perm.mode & 0777) != mode) 309 errx(1, "mode mismatch"); 310 } 311 312 void 313 receiver() 314 { 315 struct thismsg m; 316 int msqid; 317 318 if ((msqid = msgget(msgkey, 0)) == -1) 319 err(1, "receiver: msgget"); 320 321 /* 322 * Receive the first message, print it, and send an ACK. 323 */ 324 325 if (msgrcv(msqid, &m, sizeof(m), MTYPE_1, 0) != sizeof(m)) 326 err(1, "receiver: msgrcv 1"); 327 328 if (verbose) 329 printf("%s\n", m.mtext); 330 if (strcmp(m.mtext, m1_str) != 0) 331 err(1, "receiver: message 1 data isn't correct"); 332 333 m.mtype = MTYPE_1_ACK; 334 335 if (msgsnd(msqid, &m, sizeof(m), 0) == -1) 336 err(1, "receiver: msgsnd ack 1"); 337 338 /* 339 * Receive the second message, print it, and send an ACK. 340 */ 341 342 if (msgrcv(msqid, &m, sizeof(m), MTYPE_2, 0) != sizeof(m)) 343 err(1, "receiver: msgrcv 2"); 344 345 if (verbose) 346 printf("%s\n", m.mtext); 347 if (strcmp(m.mtext, m2_str) != 0) 348 err(1, "receiver: message 2 data isn't correct"); 349 350 m.mtype = MTYPE_2_ACK; 351 352 if (msgsnd(msqid, &m, sizeof(m), 0) == -1) 353 err(1, "receiver: msgsnd ack 2"); 354 355 exit(0); 356 } 357