sysv_msg.c revision 12866
1/* $Id: sysv_msg.c,v 1.10 1995/12/14 08:31:51 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, int *retval)); 39struct msgget_args; 40int msgget __P((struct proc *p, struct msgget_args *uap, int *retval)); 41struct msgsnd_args; 42int msgsnd __P((struct proc *p, struct msgsnd_args *uap, int *retval)); 43struct msgrcv_args; 44int msgrcv __P((struct proc *p, struct msgrcv_args *uap, int *retval)); 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, 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 142static void 143msg_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_ 167struct msgctl_args { 168 int msqid; 169 int cmd; 170 struct msqid_ds *buf; 171}; 172#endif 173 174int 175msgctl(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_ 304struct msgget_args { 305 key_t key; 306 int msgflg; 307}; 308#endif 309 310int 311msgget(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 404found: 405 /* Construct the unique msqid */ 406 *retval = 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, 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 += 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 += 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_ 747struct msgrcv_args { 748 int msqid; 749 void *msgp; 750 size_t msgsz; 751 long msgtyp; 752 int msgflg; 753}; 754#endif 755 756int 757msgrcv(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 += 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 += 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