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