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