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