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