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