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