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