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