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