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, sizeof(m), 0) == -1) 181 err(1, "sender: msgsnd 1"); 182 183 if (msgrcv(sender_msqid, &m, sizeof(m), MTYPE_1_ACK, 0) != sizeof(m)) 184 err(1, "sender: msgrcv 1 ack"); 185 186 print_msqid_ds(&m_ds, 0600); 187 188 /* 189 * Send the second message to the receiver and wait for the ACK. 190 */ 191 m.mtype = MTYPE_2; 192 strcpy(m.mtext, m2_str); 193 if (msgsnd(sender_msqid, &m, sizeof(m), 0) == -1) 194 err(1, "sender: msgsnd 2"); 195 196 if (msgrcv(sender_msqid, &m, sizeof(m), MTYPE_2_ACK, 0) != sizeof(m)) 197 err(1, "sender: msgrcv 2 ack"); 198 199 /* 200 * Suspend forever; when we get SIGCHLD, the handler will exit. 201 */ 202 sigemptyset(&sigmask); 203 (void) sigsuspend(&sigmask); 204 205 /* 206 * ...and any other signal is an unexpected error. 207 */ 208 errx(1, "sender: received unexpected signal"); 209 } 210 211 void 212 sigsys_handler(int signo) 213 { 214 215 errx(1, "System V Message Queue support is not present in the kernel"); 216 } 217 218 void 219 sigchld_handler(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 251 void 252 cleanup() 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 264 void 265 print_msqid_ds(struct msqid_ds *mp, mode_t mode) 266 { 267 uid_t uid = geteuid(); 268 gid_t gid = getegid(); 269 270 printf("PERM: uid %d, gid %d, cuid %d, cgid %d, mode 0%o\n", 271 mp->msg_perm.uid, mp->msg_perm.gid, 272 mp->msg_perm.cuid, mp->msg_perm.cgid, 273 mp->msg_perm.mode & 0777); 274 275 printf("qnum %lu, qbytes %lu, lspid %d, lrpid %d\n", 276 mp->msg_qnum, (u_long)mp->msg_qbytes, mp->msg_lspid, 277 mp->msg_lrpid); 278 279 printf("stime: %s", ctime(&mp->msg_stime)); 280 printf("rtime: %s", ctime(&mp->msg_rtime)); 281 printf("ctime: %s", ctime(&mp->msg_ctime)); 282 283 /* 284 * Sanity check a few things. 285 */ 286 287 if (mp->msg_perm.uid != uid || mp->msg_perm.cuid != uid) 288 errx(1, "uid mismatch"); 289 290 if (mp->msg_perm.gid != gid || mp->msg_perm.cgid != gid) 291 errx(1, "gid mismatch"); 292 293 if ((mp->msg_perm.mode & 0777) != mode) 294 errx(1, "mode mismatch"); 295 } 296 297 void 298 usage() 299 { 300 301 fprintf(stderr, "usage: %s keypath\n", getprogname()); 302 exit(1); 303 } 304 305 void 306 receiver() 307 { 308 struct test_mymsg m; 309 int msqid; 310 311 if ((msqid = msgget(msgkey, 0)) == -1) 312 err(1, "receiver: msgget"); 313 314 /* 315 * Receive the first message, print it, and send an ACK. 316 */ 317 318 if (msgrcv(msqid, &m, sizeof(m), MTYPE_1, 0) != sizeof(m)) 319 err(1, "receiver: msgrcv 1"); 320 321 printf("%s\n", m.mtext); 322 if (strcmp(m.mtext, m1_str) != 0) 323 err(1, "receiver: message 1 data isn't correct"); 324 325 m.mtype = MTYPE_1_ACK; 326 327 if (msgsnd(msqid, &m, sizeof(m), 0) == -1) 328 err(1, "receiver: msgsnd ack 1"); 329 330 /* 331 * Receive the second message, print it, and send an ACK. 332 */ 333 334 if (msgrcv(msqid, &m, sizeof(m), MTYPE_2, 0) != sizeof(m)) 335 err(1, "receiver: msgrcv 2"); 336 337 printf("%s\n", m.mtext); 338 if (strcmp(m.mtext, m2_str) != 0) 339 err(1, "receiver: message 2 data isn't correct"); 340 341 m.mtype = MTYPE_2_ACK; 342 343 if (msgsnd(msqid, &m, sizeof(m), 0) == -1) 344 err(1, "receiver: msgsnd ack 2"); 345 346 exit(0); 347 } 348