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