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