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