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