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