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