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