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