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