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