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