sysv_msg.c revision 43426
1/* $Id: sysv_msg.c,v 1.18 1998/03/30 09:50:35 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 30static void msginit __P((void *)); 31SYSINIT(sysv_msg, SI_SUB_SYSV_MSG, SI_ORDER_FIRST, msginit, NULL) 32 33#define MSG_DEBUG 34#undef MSG_DEBUG_OK 35 36#ifndef _SYS_SYSPROTO_H_ 37struct msgctl_args; 38int msgctl __P((struct proc *p, struct msgctl_args *uap)); 39struct msgget_args; 40int msgget __P((struct proc *p, struct msgget_args *uap)); 41struct msgsnd_args; 42int msgsnd __P((struct proc *p, struct msgsnd_args *uap)); 43struct msgrcv_args; 44int msgrcv __P((struct proc *p, struct msgrcv_args *uap)); 45#endif 46static void msg_freehdr __P((struct msg *msghdr)); 47 48/* XXX casting to (sy_call_t *) is bogus, as usual. */ 49static sy_call_t *msgcalls[] = { 50 (sy_call_t *)msgctl, (sy_call_t *)msgget, 51 (sy_call_t *)msgsnd, (sy_call_t *)msgrcv 52}; 53 54static int nfree_msgmaps; /* # of free map entries */ 55static short free_msgmaps; /* head of linked list of free map entries */ 56static struct msg *free_msghdrs; /* list of free msg headers */ 57char *msgpool; /* MSGMAX byte long msg buffer pool */ 58struct msgmap *msgmaps; /* MSGSEG msgmap structures */ 59struct msg *msghdrs; /* MSGTQL msg headers */ 60struct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */ 61 62void 63msginit(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 */ 122int 123msgsys(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 141static void 142msg_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_ 166struct msgctl_args { 167 int msqid; 168 int cmd; 169 struct msqid_ds *buf; 170}; 171#endif 172 173int 174msgctl(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) { 256 eval = suser(cred, &p->p_acflag); 257 if (eval) 258 return(eval); 259 } 260 if (msqbuf.msg_qbytes > msginfo.msgmnb) { 261#ifdef MSG_DEBUG_OK 262 printf("can't increase msg_qbytes beyond %d (truncating)\n", 263 msginfo.msgmnb); 264#endif 265 msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ 266 } 267 if (msqbuf.msg_qbytes == 0) { 268#ifdef MSG_DEBUG_OK 269 printf("can't reduce msg_qbytes to 0\n"); 270#endif 271 return(EINVAL); /* non-standard errno! */ 272 } 273 msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ 274 msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ 275 msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | 276 (msqbuf.msg_perm.mode & 0777); 277 msqptr->msg_qbytes = msqbuf.msg_qbytes; 278 msqptr->msg_ctime = time_second; 279 break; 280 281 case IPC_STAT: 282 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 283#ifdef MSG_DEBUG_OK 284 printf("requester doesn't have read access\n"); 285#endif 286 return(eval); 287 } 288 eval = copyout((caddr_t)msqptr, user_msqptr, 289 sizeof(struct msqid_ds)); 290 break; 291 292 default: 293#ifdef MSG_DEBUG_OK 294 printf("invalid command %d\n", cmd); 295#endif 296 return(EINVAL); 297 } 298 299 if (eval == 0) 300 p->p_retval[0] = rval; 301 return(eval); 302} 303 304#ifndef _SYS_SYSPROTO_H_ 305struct msgget_args { 306 key_t key; 307 int msgflg; 308}; 309#endif 310 311int 312msgget(p, uap) 313 struct proc *p; 314 register struct msgget_args *uap; 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_second; 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 404found: 405 /* Construct the unique msqid */ 406 p->p_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); 407 return(0); 408} 409 410#ifndef _SYS_SYSPROTO_H_ 411struct msgsnd_args { 412 int msqid; 413 void *msgp; 414 size_t msgsz; 415 int msgflg; 416}; 417#endif 418 419int 420msgsnd(p, uap) 421 struct proc *p; 422 register struct msgsnd_args *uap; 423{ 424 int msqid = uap->msqid; 425 void *user_msgp = uap->msgp; 426 size_t msgsz = uap->msgsz; 427 int msgflg = uap->msgflg; 428 int segs_needed, eval; 429 struct ucred *cred = p->p_ucred; 430 register struct msqid_ds *msqptr; 431 register struct msg *msghdr; 432 short next; 433 434#ifdef MSG_DEBUG_OK 435 printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz, 436 msgflg); 437#endif 438 439 msqid = IPCID_TO_IX(msqid); 440 441 if (msqid < 0 || msqid >= msginfo.msgmni) { 442#ifdef MSG_DEBUG_OK 443 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 444 msginfo.msgmni); 445#endif 446 return(EINVAL); 447 } 448 449 msqptr = &msqids[msqid]; 450 if (msqptr->msg_qbytes == 0) { 451#ifdef MSG_DEBUG_OK 452 printf("no such message queue id\n"); 453#endif 454 return(EINVAL); 455 } 456 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 457#ifdef MSG_DEBUG_OK 458 printf("wrong sequence number\n"); 459#endif 460 return(EINVAL); 461 } 462 463 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_W))) { 464#ifdef MSG_DEBUG_OK 465 printf("requester doesn't have write access\n"); 466#endif 467 return(eval); 468 } 469 470 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 471#ifdef MSG_DEBUG_OK 472 printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, 473 segs_needed); 474#endif 475 for (;;) { 476 int need_more_resources = 0; 477 478 /* 479 * check msgsz 480 * (inside this loop in case msg_qbytes changes while we sleep) 481 */ 482 483 if (msgsz > msqptr->msg_qbytes) { 484#ifdef MSG_DEBUG_OK 485 printf("msgsz > msqptr->msg_qbytes\n"); 486#endif 487 return(EINVAL); 488 } 489 490 if (msqptr->msg_perm.mode & MSG_LOCKED) { 491#ifdef MSG_DEBUG_OK 492 printf("msqid is locked\n"); 493#endif 494 need_more_resources = 1; 495 } 496 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { 497#ifdef MSG_DEBUG_OK 498 printf("msgsz + msg_cbytes > msg_qbytes\n"); 499#endif 500 need_more_resources = 1; 501 } 502 if (segs_needed > nfree_msgmaps) { 503#ifdef MSG_DEBUG_OK 504 printf("segs_needed > nfree_msgmaps\n"); 505#endif 506 need_more_resources = 1; 507 } 508 if (free_msghdrs == NULL) { 509#ifdef MSG_DEBUG_OK 510 printf("no more msghdrs\n"); 511#endif 512 need_more_resources = 1; 513 } 514 515 if (need_more_resources) { 516 int we_own_it; 517 518 if ((msgflg & IPC_NOWAIT) != 0) { 519#ifdef MSG_DEBUG_OK 520 printf("need more resources but caller doesn't want to wait\n"); 521#endif 522 return(EAGAIN); 523 } 524 525 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { 526#ifdef MSG_DEBUG_OK 527 printf("we don't own the msqid_ds\n"); 528#endif 529 we_own_it = 0; 530 } else { 531 /* Force later arrivals to wait for our 532 request */ 533#ifdef MSG_DEBUG_OK 534 printf("we own the msqid_ds\n"); 535#endif 536 msqptr->msg_perm.mode |= MSG_LOCKED; 537 we_own_it = 1; 538 } 539#ifdef MSG_DEBUG_OK 540 printf("goodnight\n"); 541#endif 542 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, 543 "msgwait", 0); 544#ifdef MSG_DEBUG_OK 545 printf("good morning, eval=%d\n", eval); 546#endif 547 if (we_own_it) 548 msqptr->msg_perm.mode &= ~MSG_LOCKED; 549 if (eval != 0) { 550#ifdef MSG_DEBUG_OK 551 printf("msgsnd: interrupted system call\n"); 552#endif 553 return(EINTR); 554 } 555 556 /* 557 * Make sure that the msq queue still exists 558 */ 559 560 if (msqptr->msg_qbytes == 0) { 561#ifdef MSG_DEBUG_OK 562 printf("msqid deleted\n"); 563#endif 564 /* The SVID says to return EIDRM. */ 565#ifdef EIDRM 566 return(EIDRM); 567#else 568 /* Unfortunately, BSD doesn't define that code 569 yet! */ 570 return(EINVAL); 571#endif 572 } 573 574 } else { 575#ifdef MSG_DEBUG_OK 576 printf("got all the resources that we need\n"); 577#endif 578 break; 579 } 580 } 581 582 /* 583 * We have the resources that we need. 584 * Make sure! 585 */ 586 587 if (msqptr->msg_perm.mode & MSG_LOCKED) 588 panic("msg_perm.mode & MSG_LOCKED"); 589 if (segs_needed > nfree_msgmaps) 590 panic("segs_needed > nfree_msgmaps"); 591 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) 592 panic("msgsz + msg_cbytes > msg_qbytes"); 593 if (free_msghdrs == NULL) 594 panic("no more msghdrs"); 595 596 /* 597 * Re-lock the msqid_ds in case we page-fault when copying in the 598 * message 599 */ 600 601 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) 602 panic("msqid_ds is already locked"); 603 msqptr->msg_perm.mode |= MSG_LOCKED; 604 605 /* 606 * Allocate a message header 607 */ 608 609 msghdr = free_msghdrs; 610 free_msghdrs = msghdr->msg_next; 611 msghdr->msg_spot = -1; 612 msghdr->msg_ts = msgsz; 613 614 /* 615 * Allocate space for the message 616 */ 617 618 while (segs_needed > 0) { 619 if (nfree_msgmaps <= 0) 620 panic("not enough msgmaps"); 621 if (free_msgmaps == -1) 622 panic("nil free_msgmaps"); 623 next = free_msgmaps; 624 if (next <= -1) 625 panic("next too low #1"); 626 if (next >= msginfo.msgseg) 627 panic("next out of range #1"); 628#ifdef MSG_DEBUG_OK 629 printf("allocating segment %d to message\n", next); 630#endif 631 free_msgmaps = msgmaps[next].next; 632 nfree_msgmaps--; 633 msgmaps[next].next = msghdr->msg_spot; 634 msghdr->msg_spot = next; 635 segs_needed--; 636 } 637 638 /* 639 * Copy in the message type 640 */ 641 642 if ((eval = copyin(user_msgp, &msghdr->msg_type, 643 sizeof(msghdr->msg_type))) != 0) { 644#ifdef MSG_DEBUG_OK 645 printf("error %d copying the message type\n", eval); 646#endif 647 msg_freehdr(msghdr); 648 msqptr->msg_perm.mode &= ~MSG_LOCKED; 649 wakeup((caddr_t)msqptr); 650 return(eval); 651 } 652 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); 653 654 /* 655 * Validate the message type 656 */ 657 658 if (msghdr->msg_type < 1) { 659 msg_freehdr(msghdr); 660 msqptr->msg_perm.mode &= ~MSG_LOCKED; 661 wakeup((caddr_t)msqptr); 662#ifdef MSG_DEBUG_OK 663 printf("mtype (%d) < 1\n", msghdr->msg_type); 664#endif 665 return(EINVAL); 666 } 667 668 /* 669 * Copy in the message body 670 */ 671 672 next = msghdr->msg_spot; 673 while (msgsz > 0) { 674 size_t tlen; 675 if (msgsz > msginfo.msgssz) 676 tlen = msginfo.msgssz; 677 else 678 tlen = msgsz; 679 if (next <= -1) 680 panic("next too low #2"); 681 if (next >= msginfo.msgseg) 682 panic("next out of range #2"); 683 if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], 684 tlen)) != 0) { 685#ifdef MSG_DEBUG_OK 686 printf("error %d copying in message segment\n", eval); 687#endif 688 msg_freehdr(msghdr); 689 msqptr->msg_perm.mode &= ~MSG_LOCKED; 690 wakeup((caddr_t)msqptr); 691 return(eval); 692 } 693 msgsz -= tlen; 694 user_msgp = (char *)user_msgp + tlen; 695 next = msgmaps[next].next; 696 } 697 if (next != -1) 698 panic("didn't use all the msg segments"); 699 700 /* 701 * We've got the message. Unlock the msqid_ds. 702 */ 703 704 msqptr->msg_perm.mode &= ~MSG_LOCKED; 705 706 /* 707 * Make sure that the msqid_ds is still allocated. 708 */ 709 710 if (msqptr->msg_qbytes == 0) { 711 msg_freehdr(msghdr); 712 wakeup((caddr_t)msqptr); 713 /* The SVID says to return EIDRM. */ 714#ifdef EIDRM 715 return(EIDRM); 716#else 717 /* Unfortunately, BSD doesn't define that code yet! */ 718 return(EINVAL); 719#endif 720 } 721 722 /* 723 * Put the message into the queue 724 */ 725 726 if (msqptr->msg_first == NULL) { 727 msqptr->msg_first = msghdr; 728 msqptr->msg_last = msghdr; 729 } else { 730 msqptr->msg_last->msg_next = msghdr; 731 msqptr->msg_last = msghdr; 732 } 733 msqptr->msg_last->msg_next = NULL; 734 735 msqptr->msg_cbytes += msghdr->msg_ts; 736 msqptr->msg_qnum++; 737 msqptr->msg_lspid = p->p_pid; 738 msqptr->msg_stime = time_second; 739 740 wakeup((caddr_t)msqptr); 741 p->p_retval[0] = 0; 742 return(0); 743} 744 745#ifndef _SYS_SYSPROTO_H_ 746struct msgrcv_args { 747 int msqid; 748 void *msgp; 749 size_t msgsz; 750 long msgtyp; 751 int msgflg; 752}; 753#endif 754 755int 756msgrcv(p, uap) 757 struct proc *p; 758 register struct msgrcv_args *uap; 759{ 760 int msqid = uap->msqid; 761 void *user_msgp = uap->msgp; 762 size_t msgsz = uap->msgsz; 763 long msgtyp = uap->msgtyp; 764 int msgflg = uap->msgflg; 765 size_t len; 766 struct ucred *cred = p->p_ucred; 767 register struct msqid_ds *msqptr; 768 register struct msg *msghdr; 769 int eval; 770 short next; 771 772#ifdef MSG_DEBUG_OK 773 printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp, 774 msgsz, msgtyp, msgflg); 775#endif 776 777 msqid = IPCID_TO_IX(msqid); 778 779 if (msqid < 0 || msqid >= msginfo.msgmni) { 780#ifdef MSG_DEBUG_OK 781 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 782 msginfo.msgmni); 783#endif 784 return(EINVAL); 785 } 786 787 msqptr = &msqids[msqid]; 788 if (msqptr->msg_qbytes == 0) { 789#ifdef MSG_DEBUG_OK 790 printf("no such message queue id\n"); 791#endif 792 return(EINVAL); 793 } 794 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 795#ifdef MSG_DEBUG_OK 796 printf("wrong sequence number\n"); 797#endif 798 return(EINVAL); 799 } 800 801 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 802#ifdef MSG_DEBUG_OK 803 printf("requester doesn't have read access\n"); 804#endif 805 return(eval); 806 } 807 808 msghdr = NULL; 809 while (msghdr == NULL) { 810 if (msgtyp == 0) { 811 msghdr = msqptr->msg_first; 812 if (msghdr != NULL) { 813 if (msgsz < msghdr->msg_ts && 814 (msgflg & MSG_NOERROR) == 0) { 815#ifdef MSG_DEBUG_OK 816 printf("first message on the queue is too big (want %d, got %d)\n", 817 msgsz, msghdr->msg_ts); 818#endif 819 return(E2BIG); 820 } 821 if (msqptr->msg_first == msqptr->msg_last) { 822 msqptr->msg_first = NULL; 823 msqptr->msg_last = NULL; 824 } else { 825 msqptr->msg_first = msghdr->msg_next; 826 if (msqptr->msg_first == NULL) 827 panic("msg_first/last screwed up #1"); 828 } 829 } 830 } else { 831 struct msg *previous; 832 struct msg **prev; 833 834 previous = NULL; 835 prev = &(msqptr->msg_first); 836 while ((msghdr = *prev) != NULL) { 837 /* 838 * Is this message's type an exact match or is 839 * this message's type less than or equal to 840 * the absolute value of a negative msgtyp? 841 * Note that the second half of this test can 842 * NEVER be true if msgtyp is positive since 843 * msg_type is always positive! 844 */ 845 846 if (msgtyp == msghdr->msg_type || 847 msghdr->msg_type <= -msgtyp) { 848#ifdef MSG_DEBUG_OK 849 printf("found message type %d, requested %d\n", 850 msghdr->msg_type, msgtyp); 851#endif 852 if (msgsz < msghdr->msg_ts && 853 (msgflg & MSG_NOERROR) == 0) { 854#ifdef MSG_DEBUG_OK 855 printf("requested message on the queue is too big (want %d, got %d)\n", 856 msgsz, msghdr->msg_ts); 857#endif 858 return(E2BIG); 859 } 860 *prev = msghdr->msg_next; 861 if (msghdr == msqptr->msg_last) { 862 if (previous == NULL) { 863 if (prev != 864 &msqptr->msg_first) 865 panic("msg_first/last screwed up #2"); 866 msqptr->msg_first = 867 NULL; 868 msqptr->msg_last = 869 NULL; 870 } else { 871 if (prev == 872 &msqptr->msg_first) 873 panic("msg_first/last screwed up #3"); 874 msqptr->msg_last = 875 previous; 876 } 877 } 878 break; 879 } 880 previous = msghdr; 881 prev = &(msghdr->msg_next); 882 } 883 } 884 885 /* 886 * We've either extracted the msghdr for the appropriate 887 * message or there isn't one. 888 * If there is one then bail out of this loop. 889 */ 890 891 if (msghdr != NULL) 892 break; 893 894 /* 895 * Hmph! No message found. Does the user want to wait? 896 */ 897 898 if ((msgflg & IPC_NOWAIT) != 0) { 899#ifdef MSG_DEBUG_OK 900 printf("no appropriate message found (msgtyp=%d)\n", 901 msgtyp); 902#endif 903 /* The SVID says to return ENOMSG. */ 904#ifdef ENOMSG 905 return(ENOMSG); 906#else 907 /* Unfortunately, BSD doesn't define that code yet! */ 908 return(EAGAIN); 909#endif 910 } 911 912 /* 913 * Wait for something to happen 914 */ 915 916#ifdef MSG_DEBUG_OK 917 printf("msgrcv: goodnight\n"); 918#endif 919 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait", 920 0); 921#ifdef MSG_DEBUG_OK 922 printf("msgrcv: good morning (eval=%d)\n", eval); 923#endif 924 925 if (eval != 0) { 926#ifdef MSG_DEBUG_OK 927 printf("msgsnd: interrupted system call\n"); 928#endif 929 return(EINTR); 930 } 931 932 /* 933 * Make sure that the msq queue still exists 934 */ 935 936 if (msqptr->msg_qbytes == 0 || 937 msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 938#ifdef MSG_DEBUG_OK 939 printf("msqid deleted\n"); 940#endif 941 /* The SVID says to return EIDRM. */ 942#ifdef EIDRM 943 return(EIDRM); 944#else 945 /* Unfortunately, BSD doesn't define that code yet! */ 946 return(EINVAL); 947#endif 948 } 949 } 950 951 /* 952 * Return the message to the user. 953 * 954 * First, do the bookkeeping (before we risk being interrupted). 955 */ 956 957 msqptr->msg_cbytes -= msghdr->msg_ts; 958 msqptr->msg_qnum--; 959 msqptr->msg_lrpid = p->p_pid; 960 msqptr->msg_rtime = time_second; 961 962 /* 963 * Make msgsz the actual amount that we'll be returning. 964 * Note that this effectively truncates the message if it is too long 965 * (since msgsz is never increased). 966 */ 967 968#ifdef MSG_DEBUG_OK 969 printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz, 970 msghdr->msg_ts); 971#endif 972 if (msgsz > msghdr->msg_ts) 973 msgsz = msghdr->msg_ts; 974 975 /* 976 * Return the type to the user. 977 */ 978 979 eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp, 980 sizeof(msghdr->msg_type)); 981 if (eval != 0) { 982#ifdef MSG_DEBUG_OK 983 printf("error (%d) copying out message type\n", eval); 984#endif 985 msg_freehdr(msghdr); 986 wakeup((caddr_t)msqptr); 987 return(eval); 988 } 989 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); 990 991 /* 992 * Return the segments to the user 993 */ 994 995 next = msghdr->msg_spot; 996 for (len = 0; len < msgsz; len += msginfo.msgssz) { 997 size_t tlen; 998 999 if (msgsz > msginfo.msgssz) 1000 tlen = msginfo.msgssz; 1001 else 1002 tlen = msgsz; 1003 if (next <= -1) 1004 panic("next too low #3"); 1005 if (next >= msginfo.msgseg) 1006 panic("next out of range #3"); 1007 eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz], 1008 user_msgp, tlen); 1009 if (eval != 0) { 1010#ifdef MSG_DEBUG_OK 1011 printf("error (%d) copying out message segment\n", 1012 eval); 1013#endif 1014 msg_freehdr(msghdr); 1015 wakeup((caddr_t)msqptr); 1016 return(eval); 1017 } 1018 user_msgp = (char *)user_msgp + tlen; 1019 next = msgmaps[next].next; 1020 } 1021 1022 /* 1023 * Done, return the actual number of bytes copied out. 1024 */ 1025 1026 msg_freehdr(msghdr); 1027 wakeup((caddr_t)msqptr); 1028 p->p_retval[0] = msgsz; 1029 return(0); 1030} 1031