1 /* $OpenBSD: sysv_msg.c,v 1.20 2007/10/14 23:08:04 fgsch Exp $ */ 2 /* $NetBSD: sysv_msg.c,v 1.19 1996/02/09 19:00:18 christos Exp $ */ 3 4 /* 5 * Implementation of SVID messages 6 * 7 * Author: Daniel Boulet 8 * 9 * Copyright 1993 Daniel Boulet and RTMX Inc. 10 * 11 * This system call was implemented by Daniel Boulet under contract from RTMX. 12 * 13 * Redistribution and use in source forms, with and without modification, 14 * are permitted provided that this entire comment appears intact. 15 * 16 * Redistribution in binary form may occur without any restrictions. 17 * Obviously, it would be nice if you gave credit where credit is due 18 * but requiring it would be too onerous. 19 * 20 * This software is provided ``AS IS'' without any warranties of any kind. 21 */ 22 23 #include <sys/param.h> 24 #include <sys/systm.h> 25 #include <sys/kernel.h> 26 #include <sys/proc.h> 27 #include <sys/msg.h> 28 #include <sys/malloc.h> 29 30 #include <sys/mount.h> 31 #include <sys/syscallargs.h> 32 33 #ifdef MSG_DEBUG 34 #define DPRINTF(x) printf x 35 #else 36 #define DPRINTF(x) 37 #endif 38 39 int nfree_msgmaps; /* # of free map entries */ 40 short free_msgmaps; /* head of linked list of free map entries */ 41 struct msg *free_msghdrs; /* list of free msg headers */ 42 char *msgpool; /* MSGMAX byte long msg buffer pool */ 43 struct msgmap *msgmaps; /* MSGSEG msgmap structures */ 44 struct msg *msghdrs; /* MSGTQL msg headers */ 45 struct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */ 46 47 void msg_freehdr(struct msg *); 48 49 void 50 msginit(void) 51 { 52 int i; 53 54 /* 55 * msginfo.msgssz should be a power of two for efficiency reasons. 56 * It is also pretty silly if msginfo.msgssz is less than 8 57 * or greater than about 256 so ... 58 */ 59 60 i = 8; 61 while (i < 1024 && i != msginfo.msgssz) 62 i <<= 1; 63 64 if (i != msginfo.msgssz) 65 panic("msginfo.msgssz %d not a small power of 2", msginfo.msgssz); 66 if (msginfo.msgseg > 32767) 67 panic("msginfo.msgseg %d > 32767", msginfo.msgseg); 68 69 if (msgmaps == NULL) 70 panic("msgmaps is NULL"); 71 72 for (i = 0; i < msginfo.msgseg; i++) { 73 if (i > 0) 74 msgmaps[i-1].next = i; 75 msgmaps[i].next = -1; /* implies entry is available */ 76 } 77 free_msgmaps = 0; 78 nfree_msgmaps = msginfo.msgseg; 79 80 if (msghdrs == NULL) 81 panic("msghdrs is NULL"); 82 83 for (i = 0; i < msginfo.msgtql; i++) { 84 msghdrs[i].msg_type = 0; 85 if (i > 0) 86 msghdrs[i-1].msg_next = &msghdrs[i]; 87 msghdrs[i].msg_next = NULL; 88 } 89 free_msghdrs = &msghdrs[0]; 90 91 if (msqids == NULL) 92 panic("msqids is NULL"); 93 94 for (i = 0; i < msginfo.msgmni; i++) { 95 msqids[i].msg_qbytes = 0; /* implies entry is available */ 96 msqids[i].msg_perm.seq = 0; /* reset to a known value */ 97 } 98 } 99 100 void 101 msg_freehdr(struct msg *msghdr) 102 { 103 while (msghdr->msg_ts > 0) { 104 short next; 105 106 #ifdef DIAGNOSTIC 107 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) 108 panic("msghdr->msg_spot out of range"); 109 #endif 110 next = msgmaps[msghdr->msg_spot].next; 111 msgmaps[msghdr->msg_spot].next = free_msgmaps; 112 free_msgmaps = msghdr->msg_spot; 113 nfree_msgmaps++; 114 msghdr->msg_spot = next; 115 if (msghdr->msg_ts >= msginfo.msgssz) 116 msghdr->msg_ts -= msginfo.msgssz; 117 else 118 msghdr->msg_ts = 0; 119 } 120 #ifdef DIAGNOSTIC 121 if (msghdr->msg_spot != -1) 122 panic("msghdr->msg_spot != -1"); 123 #endif 124 msghdr->msg_next = free_msghdrs; 125 free_msghdrs = msghdr; 126 } 127 128 int 129 sys_msgctl(struct proc *p, void *v, register_t *retval) 130 { 131 struct sys_msgctl_args /* { 132 syscallarg(int) msqid; 133 syscallarg(int) cmd; 134 syscallarg(struct msqid_ds *) buf; 135 } */ *uap = v; 136 137 return (msgctl1(p, SCARG(uap, msqid), SCARG(uap, cmd), 138 (caddr_t)SCARG(uap, buf), copyin, copyout)); 139 } 140 141 int 142 msgctl1(struct proc *p, int msqid, int cmd, caddr_t buf, 143 int (*ds_copyin)(const void *, void *, size_t), 144 int (*ds_copyout)(const void *, void *, size_t)) 145 { 146 struct ucred *cred = p->p_ucred; 147 struct msqid_ds msqbuf, *msqptr; 148 struct msg *msghdr; 149 int ix, error = 0; 150 151 DPRINTF(("call to msgctl(%d, %d, %p)\n", msqid, cmd, buf)); 152 153 ix = IPCID_TO_IX(msqid); 154 155 if (ix < 0 || ix >= msginfo.msgmni) { 156 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", ix, 157 msginfo.msgmni)); 158 return (EINVAL); 159 } 160 161 msqptr = &msqids[ix]; 162 163 if (msqptr->msg_qbytes == 0) { 164 DPRINTF(("no such msqid\n")); 165 return (EINVAL); 166 } 167 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(msqid)) { 168 DPRINTF(("wrong sequence number\n")); 169 return (EINVAL); 170 } 171 172 switch (cmd) { 173 case IPC_RMID: 174 if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M)) != 0) 175 return (error); 176 /* Free the message headers */ 177 msghdr = msqptr->msg_first; 178 while (msghdr != NULL) { 179 struct msg *msghdr_tmp; 180 181 /* Free the segments of each message */ 182 msqptr->msg_cbytes -= msghdr->msg_ts; 183 msqptr->msg_qnum--; 184 msghdr_tmp = msghdr; 185 msghdr = msghdr->msg_next; 186 msg_freehdr(msghdr_tmp); 187 } 188 189 #ifdef DIAGNOSTIC 190 if (msqptr->msg_cbytes != 0) 191 panic("sys_msgctl: msg_cbytes is screwed up"); 192 if (msqptr->msg_qnum != 0) 193 panic("sys_msgctl: msg_qnum is screwed up"); 194 #endif 195 msqptr->msg_qbytes = 0; /* Mark it as free */ 196 wakeup(msqptr); 197 break; 198 199 case IPC_SET: 200 if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M))) 201 return (error); 202 if ((error = ds_copyin(buf, &msqbuf, sizeof(msqbuf))) != 0) 203 return (error); 204 if (msqbuf.msg_qbytes > msqptr->msg_qbytes && 205 cred->cr_uid != 0) 206 return (EPERM); 207 if (msqbuf.msg_qbytes > msginfo.msgmnb) { 208 DPRINTF(("can't increase msg_qbytes beyond %d " 209 "(truncating)\n", msginfo.msgmnb)); 210 /* silently restrict qbytes to system limit */ 211 msqbuf.msg_qbytes = msginfo.msgmnb; 212 } 213 if (msqbuf.msg_qbytes == 0) { 214 DPRINTF(("can't reduce msg_qbytes to 0\n")); 215 return (EINVAL); /* non-standard errno! */ 216 } 217 msqptr->msg_perm.uid = msqbuf.msg_perm.uid; 218 msqptr->msg_perm.gid = msqbuf.msg_perm.gid; 219 msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | 220 (msqbuf.msg_perm.mode & 0777); 221 msqptr->msg_qbytes = msqbuf.msg_qbytes; 222 msqptr->msg_ctime = time_second; 223 break; 224 225 case IPC_STAT: 226 if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 227 DPRINTF(("requester doesn't have read access\n")); 228 return (error); 229 } 230 error = ds_copyout(msqptr, buf, sizeof(struct msqid_ds)); 231 break; 232 233 default: 234 DPRINTF(("invalid command %d\n", cmd)); 235 return (EINVAL); 236 } 237 return (error); 238 } 239 240 int 241 sys_msgget(struct proc *p, void *v, register_t *retval) 242 { 243 struct sys_msgget_args /* { 244 syscallarg(key_t) key; 245 syscallarg(int) msgflg; 246 } */ *uap = v; 247 int msqid, eval; 248 int key = SCARG(uap, key); 249 int msgflg = SCARG(uap, msgflg); 250 struct ucred *cred = p->p_ucred; 251 struct msqid_ds *msqptr = NULL; 252 253 DPRINTF(("msgget(0x%x, 0%o)\n", key, msgflg)); 254 255 if (key != IPC_PRIVATE) { 256 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 257 msqptr = &msqids[msqid]; 258 if (msqptr->msg_qbytes != 0 && 259 msqptr->msg_perm.key == key) 260 break; 261 } 262 if (msqid < msginfo.msgmni) { 263 DPRINTF(("found public key\n")); 264 if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { 265 DPRINTF(("not exclusive\n")); 266 return (EEXIST); 267 } 268 if ((eval = ipcperm(cred, &msqptr->msg_perm, msgflg & 0700 ))) { 269 DPRINTF(("requester doesn't have 0%o access\n", 270 msgflg & 0700)); 271 return (eval); 272 } 273 goto found; 274 } 275 } 276 277 DPRINTF(("need to allocate the msqid_ds\n")); 278 if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { 279 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 280 /* 281 * Look for an unallocated and unlocked msqid_ds. 282 * msqid_ds's can be locked by msgsnd or msgrcv while 283 * they are copying the message in/out. We can't 284 * re-use the entry until they release it. 285 */ 286 msqptr = &msqids[msqid]; 287 if (msqptr->msg_qbytes == 0 && 288 (msqptr->msg_perm.mode & MSG_LOCKED) == 0) 289 break; 290 } 291 if (msqid == msginfo.msgmni) { 292 DPRINTF(("no more msqid_ds's available\n")); 293 return (ENOSPC); 294 } 295 DPRINTF(("msqid %d is available\n", msqid)); 296 msqptr->msg_perm.key = key; 297 msqptr->msg_perm.cuid = cred->cr_uid; 298 msqptr->msg_perm.uid = cred->cr_uid; 299 msqptr->msg_perm.cgid = cred->cr_gid; 300 msqptr->msg_perm.gid = cred->cr_gid; 301 msqptr->msg_perm.mode = (msgflg & 0777); 302 /* Make sure that the returned msqid is unique */ 303 msqptr->msg_perm.seq = (msqptr->msg_perm.seq + 1) & 0x7fff; 304 msqptr->msg_first = NULL; 305 msqptr->msg_last = NULL; 306 msqptr->msg_cbytes = 0; 307 msqptr->msg_qnum = 0; 308 msqptr->msg_qbytes = msginfo.msgmnb; 309 msqptr->msg_lspid = 0; 310 msqptr->msg_lrpid = 0; 311 msqptr->msg_stime = 0; 312 msqptr->msg_rtime = 0; 313 msqptr->msg_ctime = time_second; 314 } else { 315 DPRINTF(("didn't find it and wasn't asked to create it\n")); 316 return (ENOENT); 317 } 318 319 found: 320 /* Construct the unique msqid */ 321 *retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); 322 return (0); 323 } 324 325 int 326 sys_msgsnd(struct proc *p, void *v, register_t *retval) 327 { 328 struct sys_msgsnd_args /* { 329 syscallarg(int) msqid; 330 syscallarg(const void *) msgp; 331 syscallarg(size_t) msgsz; 332 syscallarg(int) msgflg; 333 } */ *uap = v; 334 int msqid = SCARG(uap, msqid); 335 const char *user_msgp = SCARG(uap, msgp); 336 size_t msgsz = SCARG(uap, msgsz); 337 int msgflg = SCARG(uap, msgflg); 338 int segs_needed, eval; 339 struct ucred *cred = p->p_ucred; 340 struct msqid_ds *msqptr; 341 struct msg *msghdr; 342 short next; 343 344 DPRINTF(("call to msgsnd(%d, %p, %d, %d)\n", msqid, user_msgp, msgsz, 345 msgflg)); 346 347 msqid = IPCID_TO_IX(msqid); 348 349 if (msqid < 0 || msqid >= msginfo.msgmni) { 350 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 351 msginfo.msgmni)); 352 return (EINVAL); 353 } 354 355 msqptr = &msqids[msqid]; 356 if (msqptr->msg_qbytes == 0) { 357 DPRINTF(("no such message queue id\n")); 358 return (EINVAL); 359 } 360 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { 361 DPRINTF(("wrong sequence number\n")); 362 return (EINVAL); 363 } 364 365 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_W))) { 366 DPRINTF(("requester doesn't have write access\n")); 367 return (eval); 368 } 369 370 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 371 DPRINTF(("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, 372 msginfo.msgssz, segs_needed)); 373 for (;;) { 374 int need_more_resources = 0; 375 376 /* 377 * check msgsz [cannot be negative since it is unsigned] 378 * (inside this loop in case msg_qbytes changes while we sleep) 379 */ 380 381 if (msgsz > msqptr->msg_qbytes) { 382 DPRINTF(("msgsz > msqptr->msg_qbytes\n")); 383 return (EINVAL); 384 } 385 386 if (msqptr->msg_perm.mode & MSG_LOCKED) { 387 DPRINTF(("msqid is locked\n")); 388 need_more_resources = 1; 389 } 390 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { 391 DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n")); 392 need_more_resources = 1; 393 } 394 if (segs_needed > nfree_msgmaps) { 395 DPRINTF(("segs_needed > nfree_msgmaps\n")); 396 need_more_resources = 1; 397 } 398 if (free_msghdrs == NULL) { 399 DPRINTF(("no more msghdrs\n")); 400 need_more_resources = 1; 401 } 402 403 if (need_more_resources) { 404 int we_own_it; 405 406 if ((msgflg & IPC_NOWAIT) != 0) { 407 DPRINTF(("need more resources but caller " 408 "doesn't want to wait\n")); 409 return (EAGAIN); 410 } 411 412 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { 413 DPRINTF(("we don't own the msqid_ds\n")); 414 we_own_it = 0; 415 } else { 416 /* Force later arrivals to wait for our 417 request */ 418 DPRINTF(("we own the msqid_ds\n")); 419 msqptr->msg_perm.mode |= MSG_LOCKED; 420 we_own_it = 1; 421 } 422 DPRINTF(("goodnight\n")); 423 eval = tsleep(msqptr, (PZERO - 4) | PCATCH, 424 "msgwait", 0); 425 DPRINTF(("good morning, eval=%d\n", eval)); 426 if (we_own_it) 427 msqptr->msg_perm.mode &= ~MSG_LOCKED; 428 if (eval != 0) { 429 DPRINTF(("msgsnd: interrupted system call\n")); 430 return (EINTR); 431 } 432 433 /* 434 * Make sure that the msq queue still exists 435 */ 436 437 if (msqptr->msg_qbytes == 0) { 438 DPRINTF(("msqid deleted\n")); 439 return (EIDRM); 440 } 441 442 } else { 443 DPRINTF(("got all the resources that we need\n")); 444 break; 445 } 446 } 447 448 /* 449 * We have the resources that we need. 450 * Make sure! 451 */ 452 453 #ifdef DIAGNOSTIC 454 if (msqptr->msg_perm.mode & MSG_LOCKED) 455 panic("msg_perm.mode & MSG_LOCKED"); 456 if (segs_needed > nfree_msgmaps) 457 panic("segs_needed > nfree_msgmaps"); 458 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) 459 panic("msgsz + msg_cbytes > msg_qbytes"); 460 if (free_msghdrs == NULL) 461 panic("no more msghdrs"); 462 #endif 463 464 /* 465 * Re-lock the msqid_ds in case we page-fault when copying in the 466 * message 467 */ 468 469 #ifdef DIAGNOSTIC 470 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) 471 panic("msqid_ds is already locked"); 472 #endif 473 msqptr->msg_perm.mode |= MSG_LOCKED; 474 475 /* 476 * Allocate a message header 477 */ 478 479 msghdr = free_msghdrs; 480 free_msghdrs = msghdr->msg_next; 481 msghdr->msg_spot = -1; 482 msghdr->msg_ts = msgsz; 483 484 /* 485 * Allocate space for the message 486 */ 487 488 while (segs_needed > 0) { 489 #ifdef DIAGNOSTIC 490 if (nfree_msgmaps <= 0) 491 panic("not enough msgmaps"); 492 if (free_msgmaps == -1) 493 panic("nil free_msgmaps"); 494 #endif 495 next = free_msgmaps; 496 #ifdef DIAGNOSTIC 497 if (next <= -1) 498 panic("next too low #1"); 499 if (next >= msginfo.msgseg) 500 panic("next out of range #1"); 501 #endif 502 DPRINTF(("allocating segment %d to message\n", next)); 503 free_msgmaps = msgmaps[next].next; 504 nfree_msgmaps--; 505 msgmaps[next].next = msghdr->msg_spot; 506 msghdr->msg_spot = next; 507 segs_needed--; 508 } 509 510 /* 511 * Copy in the message type 512 */ 513 514 if ((eval = copyin(user_msgp, &msghdr->msg_type, 515 sizeof(msghdr->msg_type))) != 0) { 516 DPRINTF(("error %d copying the message type\n", eval)); 517 msg_freehdr(msghdr); 518 msqptr->msg_perm.mode &= ~MSG_LOCKED; 519 wakeup(msqptr); 520 return (eval); 521 } 522 user_msgp += sizeof(msghdr->msg_type); 523 524 /* 525 * Validate the message type 526 */ 527 528 if (msghdr->msg_type < 1) { 529 msg_freehdr(msghdr); 530 msqptr->msg_perm.mode &= ~MSG_LOCKED; 531 wakeup(msqptr); 532 DPRINTF(("mtype (%d) < 1\n", msghdr->msg_type)); 533 return (EINVAL); 534 } 535 536 /* 537 * Copy in the message body 538 */ 539 540 next = msghdr->msg_spot; 541 while (msgsz > 0) { 542 size_t tlen; 543 if (msgsz > msginfo.msgssz) 544 tlen = msginfo.msgssz; 545 else 546 tlen = msgsz; 547 #ifdef DIAGNOSTIC 548 if (next <= -1) 549 panic("next too low #2"); 550 if (next >= msginfo.msgseg) 551 panic("next out of range #2"); 552 #endif 553 if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], 554 tlen)) != 0) { 555 DPRINTF(("error %d copying in message segment\n", 556 eval)); 557 msg_freehdr(msghdr); 558 msqptr->msg_perm.mode &= ~MSG_LOCKED; 559 wakeup(msqptr); 560 return (eval); 561 } 562 msgsz -= tlen; 563 user_msgp += tlen; 564 next = msgmaps[next].next; 565 } 566 #ifdef DIAGNOSTIC 567 if (next != -1) 568 panic("didn't use all the msg segments"); 569 #endif 570 /* 571 * We've got the message. Unlock the msqid_ds. 572 */ 573 574 msqptr->msg_perm.mode &= ~MSG_LOCKED; 575 576 /* 577 * Make sure that the msqid_ds is still allocated. 578 */ 579 580 if (msqptr->msg_qbytes == 0) { 581 msg_freehdr(msghdr); 582 wakeup(msqptr); 583 return (EIDRM); 584 } 585 586 /* 587 * Put the message into the queue 588 */ 589 590 if (msqptr->msg_first == NULL) { 591 msqptr->msg_first = msghdr; 592 msqptr->msg_last = msghdr; 593 } else { 594 msqptr->msg_last->msg_next = msghdr; 595 msqptr->msg_last = msghdr; 596 } 597 msqptr->msg_last->msg_next = NULL; 598 599 msqptr->msg_cbytes += msghdr->msg_ts; 600 msqptr->msg_qnum++; 601 msqptr->msg_lspid = p->p_pid; 602 msqptr->msg_stime = time_second; 603 604 wakeup(msqptr); 605 *retval = 0; 606 return (0); 607 } 608 609 int 610 sys_msgrcv(struct proc *p, void *v, register_t *retval) 611 { 612 struct sys_msgrcv_args /* { 613 syscallarg(int) msqid; 614 syscallarg(void *) msgp; 615 syscallarg(size_t) msgsz; 616 syscallarg(long) msgtyp; 617 syscallarg(int) msgflg; 618 } */ *uap = v; 619 int msqid = SCARG(uap, msqid); 620 char *user_msgp = SCARG(uap, msgp); 621 size_t msgsz = SCARG(uap, msgsz); 622 long msgtyp = SCARG(uap, msgtyp); 623 int msgflg = SCARG(uap, msgflg); 624 size_t len; 625 struct ucred *cred = p->p_ucred; 626 struct msqid_ds *msqptr; 627 struct msg *msghdr; 628 int eval; 629 short next; 630 631 DPRINTF(("call to msgrcv(%d, %p, %d, %ld, %d)\n", msqid, user_msgp, 632 msgsz, msgtyp, msgflg)); 633 634 msqid = IPCID_TO_IX(msqid); 635 636 if (msqid < 0 || msqid >= msginfo.msgmni) { 637 DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 638 msginfo.msgmni)); 639 return (EINVAL); 640 } 641 642 msqptr = &msqids[msqid]; 643 if (msqptr->msg_qbytes == 0) { 644 DPRINTF(("no such message queue id\n")); 645 return (EINVAL); 646 } 647 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { 648 DPRINTF(("wrong sequence number\n")); 649 return (EINVAL); 650 } 651 652 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 653 DPRINTF(("requester doesn't have read access\n")); 654 return (eval); 655 } 656 657 #if 0 658 /* cannot happen, msgsz is unsigned */ 659 if (msgsz < 0) { 660 DPRINTF(("msgsz < 0\n")); 661 return (EINVAL); 662 } 663 #endif 664 665 msghdr = NULL; 666 while (msghdr == NULL) { 667 if (msgtyp == 0) { 668 msghdr = msqptr->msg_first; 669 if (msghdr != NULL) { 670 if (msgsz < msghdr->msg_ts && 671 (msgflg & MSG_NOERROR) == 0) { 672 DPRINTF(("first message on the queue " 673 "is too big (want %d, got %d)\n", 674 msgsz, msghdr->msg_ts)); 675 return (E2BIG); 676 } 677 if (msqptr->msg_first == msqptr->msg_last) { 678 msqptr->msg_first = NULL; 679 msqptr->msg_last = NULL; 680 } else { 681 msqptr->msg_first = msghdr->msg_next; 682 #ifdef DIAGNOSTIC 683 if (msqptr->msg_first == NULL) 684 panic("msg_first/last screwed up #1"); 685 #endif 686 } 687 } 688 } else { 689 struct msg *previous; 690 struct msg **prev; 691 692 for (previous = NULL, prev = &msqptr->msg_first; 693 (msghdr = *prev) != NULL; 694 previous = msghdr, prev = &msghdr->msg_next) { 695 /* 696 * Is this message's type an exact match or is 697 * this message's type less than or equal to 698 * the absolute value of a negative msgtyp? 699 * Note that the second half of this test can 700 * NEVER be true if msgtyp is positive since 701 * msg_type is always positive! 702 */ 703 704 if (msgtyp == msghdr->msg_type || 705 msghdr->msg_type <= -msgtyp) { 706 DPRINTF(("found message type %d, " 707 "requested %d\n", msghdr->msg_type, 708 msgtyp)); 709 if (msgsz < msghdr->msg_ts && 710 (msgflg & MSG_NOERROR) == 0) { 711 DPRINTF(("requested message on " 712 "the queue is too big " 713 "(want %d, got %d)\n", 714 msgsz, msghdr->msg_ts)); 715 return (E2BIG); 716 } 717 *prev = msghdr->msg_next; 718 if (msghdr == msqptr->msg_last) { 719 if (previous == NULL) { 720 #ifdef DIAGNOSTIC 721 if (prev != 722 &msqptr->msg_first) 723 panic("msg_first/last screwed up #2"); 724 #endif 725 msqptr->msg_first = 726 NULL; 727 msqptr->msg_last = 728 NULL; 729 } else { 730 #ifdef DIAGNOSTIC 731 if (prev == 732 &msqptr->msg_first) 733 panic("msg_first/last screwed up #3"); 734 #endif 735 msqptr->msg_last = 736 previous; 737 } 738 } 739 break; 740 } 741 } 742 } 743 744 /* 745 * We've either extracted the msghdr for the appropriate 746 * message or there isn't one. 747 * If there is one then bail out of this loop. 748 */ 749 750 if (msghdr != NULL) 751 break; 752 753 /* 754 * Hmph! No message found. Does the user want to wait? 755 */ 756 757 if ((msgflg & IPC_NOWAIT) != 0) { 758 DPRINTF(("no appropriate message found (msgtyp=%d)\n", 759 msgtyp)); 760 return (ENOMSG); 761 } 762 763 /* 764 * Wait for something to happen 765 */ 766 767 DPRINTF(("msgrcv: goodnight\n")); 768 eval = tsleep(msqptr, (PZERO - 4) | PCATCH, "msgwait", 769 0); 770 DPRINTF(("msgrcv: good morning (eval=%d)\n", eval)); 771 772 if (eval != 0) { 773 DPRINTF(("msgsnd: interrupted system call\n")); 774 return (EINTR); 775 } 776 777 /* 778 * Make sure that the msq queue still exists 779 */ 780 781 if (msqptr->msg_qbytes == 0 || 782 msqptr->msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { 783 DPRINTF(("msqid deleted\n")); 784 return (EIDRM); 785 } 786 } 787 788 /* 789 * Return the message to the user. 790 * 791 * First, do the bookkeeping (before we risk being interrupted). 792 */ 793 794 msqptr->msg_cbytes -= msghdr->msg_ts; 795 msqptr->msg_qnum--; 796 msqptr->msg_lrpid = p->p_pid; 797 msqptr->msg_rtime = time_second; 798 799 /* 800 * Make msgsz the actual amount that we'll be returning. 801 * Note that this effectively truncates the message if it is too long 802 * (since msgsz is never increased). 803 */ 804 805 DPRINTF(("found a message, msgsz=%d, msg_ts=%d\n", msgsz, 806 msghdr->msg_ts)); 807 if (msgsz > msghdr->msg_ts) 808 msgsz = msghdr->msg_ts; 809 810 /* 811 * Return the type to the user. 812 */ 813 814 eval = copyout(&msghdr->msg_type, user_msgp, 815 sizeof(msghdr->msg_type)); 816 if (eval != 0) { 817 DPRINTF(("error (%d) copying out message type\n", eval)); 818 msg_freehdr(msghdr); 819 wakeup(msqptr); 820 return (eval); 821 } 822 user_msgp += sizeof(msghdr->msg_type); 823 824 /* 825 * Return the segments to the user 826 */ 827 828 next = msghdr->msg_spot; 829 for (len = 0; len < msgsz; len += msginfo.msgssz) { 830 size_t tlen; 831 832 if (msgsz - len > msginfo.msgssz) 833 tlen = msginfo.msgssz; 834 else 835 tlen = msgsz - len; 836 #ifdef DIAGNOSTIC 837 if (next <= -1) 838 panic("next too low #3"); 839 if (next >= msginfo.msgseg) 840 panic("next out of range #3"); 841 #endif 842 eval = copyout(&msgpool[next * msginfo.msgssz], 843 user_msgp, tlen); 844 if (eval != 0) { 845 DPRINTF(("error (%d) copying out message segment\n", 846 eval)); 847 msg_freehdr(msghdr); 848 wakeup(msqptr); 849 return (eval); 850 } 851 user_msgp += tlen; 852 next = msgmaps[next].next; 853 } 854 855 /* 856 * Done, return the actual number of bytes copied out. 857 */ 858 859 msg_freehdr(msghdr); 860 wakeup(msqptr); 861 *retval = msgsz; 862 return (0); 863 } 864