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