1 /* $OpenBSD: sysv_msg.c,v 1.7 1999/04/18 18:24:40 deraadt 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 #define MSG_DEBUG 34 #undef MSG_DEBUG_OK 35 36 int nfree_msgmaps; /* # of free map entries */ 37 short free_msgmaps; /* head of linked list of free map entries */ 38 struct msg *free_msghdrs; /* list of free msg headers */ 39 40 static void msg_freehdr __P((struct msg *)); 41 42 void 43 msginit() 44 { 45 register int i; 46 47 /* 48 * msginfo.msgssz should be a power of two for efficiency reasons. 49 * It is also pretty silly if msginfo.msgssz is less than 8 50 * or greater than about 256 so ... 51 */ 52 53 i = 8; 54 while (i < 1024 && i != msginfo.msgssz) 55 i <<= 1; 56 57 if (i != msginfo.msgssz) 58 panic("msginfo.msgssz %d not a small power of 2", msginfo.msgssz); 59 if (msginfo.msgseg > 32767) 60 panic("msginfo.msgseg %d > 32767", msginfo.msgseg); 61 62 if (msgmaps == NULL) 63 panic("msgmaps is NULL"); 64 65 for (i = 0; i < msginfo.msgseg; i++) { 66 if (i > 0) 67 msgmaps[i-1].next = i; 68 msgmaps[i].next = -1; /* implies entry is available */ 69 } 70 free_msgmaps = 0; 71 nfree_msgmaps = msginfo.msgseg; 72 73 if (msghdrs == NULL) 74 panic("msghdrs is NULL"); 75 76 for (i = 0; i < msginfo.msgtql; i++) { 77 msghdrs[i].msg_type = 0; 78 if (i > 0) 79 msghdrs[i-1].msg_next = &msghdrs[i]; 80 msghdrs[i].msg_next = NULL; 81 } 82 free_msghdrs = &msghdrs[0]; 83 84 if (msqids == NULL) 85 panic("msqids is NULL"); 86 87 for (i = 0; i < msginfo.msgmni; i++) { 88 msqids[i].msg_qbytes = 0; /* implies entry is available */ 89 msqids[i].msg_perm.seq = 0; /* reset to a known value */ 90 } 91 } 92 93 static void 94 msg_freehdr(msghdr) 95 struct msg *msghdr; 96 { 97 while (msghdr->msg_ts > 0) { 98 short next; 99 100 #ifdef DIAGNOSTIC 101 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) 102 panic("msghdr->msg_spot out of range"); 103 #endif 104 next = msgmaps[msghdr->msg_spot].next; 105 msgmaps[msghdr->msg_spot].next = free_msgmaps; 106 free_msgmaps = msghdr->msg_spot; 107 nfree_msgmaps++; 108 msghdr->msg_spot = next; 109 if (msghdr->msg_ts >= msginfo.msgssz) 110 msghdr->msg_ts -= msginfo.msgssz; 111 else 112 msghdr->msg_ts = 0; 113 } 114 #ifdef DIAGNOSTIC 115 if (msghdr->msg_spot != -1) 116 panic("msghdr->msg_spot != -1"); 117 #endif 118 msghdr->msg_next = free_msghdrs; 119 free_msghdrs = msghdr; 120 } 121 122 void 123 msqid_n2o(n, o) 124 struct msqid_ds *n; 125 struct omsqid_ds *o; 126 { 127 o->msg_first = n->msg_first; 128 o->msg_last = n->msg_last; 129 o->msg_cbytes = n->msg_cbytes; 130 o->msg_qnum = n->msg_qnum; 131 o->msg_qbytes = n->msg_qbytes; 132 o->msg_lspid = n->msg_lspid; 133 o->msg_lrpid = n->msg_lrpid; 134 o->msg_stime = n->msg_stime; 135 o->msg_pad1 = n->msg_pad1; 136 o->msg_rtime = n->msg_rtime; 137 o->msg_pad2 = n->msg_pad2; 138 o->msg_ctime = n->msg_ctime; 139 o->msg_pad3 = n->msg_pad3; 140 bcopy(n->msg_pad4, o->msg_pad4, sizeof o->msg_pad4); 141 ipc_n2o(&n->msg_perm, &o->msg_perm); 142 } 143 144 int 145 sys_omsgctl(p, v, retval) 146 struct proc *p; 147 void *v; 148 register_t *retval; 149 { 150 register struct sys_msgctl_args /* { 151 syscallarg(int) msqid; 152 syscallarg(int) cmd; 153 syscallarg(struct msqid_ds *) buf; 154 } */ *uap = v; 155 int msqid = SCARG(uap, msqid); 156 int cmd = SCARG(uap, cmd); 157 struct msqid_ds *user_msqptr = SCARG(uap, buf); 158 struct ucred *cred = p->p_ucred; 159 int rval, eval; 160 struct omsqid_ds omsqbuf; 161 register struct msqid_ds *msqptr; 162 163 #ifdef MSG_DEBUG_OK 164 printf("call to msgctl(%d, %d, %p)\n", msqid, cmd, user_msqptr); 165 #endif 166 167 msqid = IPCID_TO_IX(msqid); 168 169 if (msqid < 0 || msqid >= msginfo.msgmni) { 170 #ifdef MSG_DEBUG_OK 171 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 172 msginfo.msgmni); 173 #endif 174 return(EINVAL); 175 } 176 177 msqptr = &msqids[msqid]; 178 179 if (msqptr->msg_qbytes == 0) { 180 #ifdef MSG_DEBUG_OK 181 printf("no such msqid\n"); 182 #endif 183 return(EINVAL); 184 } 185 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { 186 #ifdef MSG_DEBUG_OK 187 printf("wrong sequence number\n"); 188 #endif 189 return(EINVAL); 190 } 191 192 eval = 0; 193 rval = 0; 194 195 switch (cmd) { 196 197 case IPC_RMID: 198 { 199 struct msg *msghdr; 200 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M)) != 0) 201 return(eval); 202 /* Free the message headers */ 203 msghdr = msqptr->msg_first; 204 while (msghdr != NULL) { 205 struct msg *msghdr_tmp; 206 207 /* Free the segments of each message */ 208 msqptr->msg_cbytes -= msghdr->msg_ts; 209 msqptr->msg_qnum--; 210 msghdr_tmp = msghdr; 211 msghdr = msghdr->msg_next; 212 msg_freehdr(msghdr_tmp); 213 } 214 215 #ifdef DIAGNOSTIC 216 if (msqptr->msg_cbytes != 0) 217 panic("msg_cbytes is screwed up"); 218 if (msqptr->msg_qnum != 0) 219 panic("msg_qnum is screwed up"); 220 #endif 221 222 msqptr->msg_qbytes = 0; /* Mark it as free */ 223 224 wakeup((caddr_t)msqptr); 225 } 226 227 break; 228 229 case IPC_SET: 230 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M))) 231 return(eval); 232 if ((eval = copyin(user_msqptr, &omsqbuf, sizeof(omsqbuf))) != 0) 233 return(eval); 234 if (omsqbuf.msg_qbytes > msqptr->msg_qbytes && cred->cr_uid != 0) 235 return(EPERM); 236 if (omsqbuf.msg_qbytes > msginfo.msgmnb) { 237 #ifdef MSG_DEBUG_OK 238 printf("can't increase msg_qbytes beyond %d (truncating)\n", 239 msginfo.msgmnb); 240 #endif 241 omsqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ 242 } 243 if (omsqbuf.msg_qbytes == 0) { 244 #ifdef MSG_DEBUG_OK 245 printf("can't reduce msg_qbytes to 0\n"); 246 #endif 247 return(EINVAL); /* non-standard errno! */ 248 } 249 msqptr->msg_perm.uid = omsqbuf.msg_perm.uid; /* change the owner */ 250 msqptr->msg_perm.gid = omsqbuf.msg_perm.gid; /* change the owner */ 251 msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | 252 (omsqbuf.msg_perm.mode & 0777); 253 msqptr->msg_qbytes = omsqbuf.msg_qbytes; 254 msqptr->msg_ctime = time.tv_sec; 255 break; 256 257 case IPC_STAT: 258 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 259 #ifdef MSG_DEBUG_OK 260 printf("requester doesn't have read access\n"); 261 #endif 262 return(eval); 263 } 264 msqid_n2o(msqptr, &omsqbuf); 265 eval = copyout((caddr_t)&omsqbuf, user_msqptr, sizeof omsqbuf); 266 break; 267 268 default: 269 #ifdef MSG_DEBUG_OK 270 printf("invalid command %d\n", cmd); 271 #endif 272 return(EINVAL); 273 } 274 275 if (eval == 0) 276 *retval = rval; 277 return(eval); 278 } 279 280 int 281 sys_msgctl(p, v, retval) 282 struct proc *p; 283 void *v; 284 register_t *retval; 285 { 286 register struct sys_msgctl_args /* { 287 syscallarg(int) msqid; 288 syscallarg(int) cmd; 289 syscallarg(struct msqid_ds *) buf; 290 } */ *uap = v; 291 int msqid = SCARG(uap, msqid); 292 int cmd = SCARG(uap, cmd); 293 struct msqid_ds *user_msqptr = SCARG(uap, buf); 294 struct ucred *cred = p->p_ucred; 295 int rval, eval; 296 struct msqid_ds msqbuf; 297 register struct msqid_ds *msqptr; 298 299 #ifdef MSG_DEBUG_OK 300 printf("call to msgctl(%d, %d, %p)\n", msqid, cmd, user_msqptr); 301 #endif 302 303 msqid = IPCID_TO_IX(msqid); 304 305 if (msqid < 0 || msqid >= msginfo.msgmni) { 306 #ifdef MSG_DEBUG_OK 307 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 308 msginfo.msgmni); 309 #endif 310 return(EINVAL); 311 } 312 313 msqptr = &msqids[msqid]; 314 315 if (msqptr->msg_qbytes == 0) { 316 #ifdef MSG_DEBUG_OK 317 printf("no such msqid\n"); 318 #endif 319 return(EINVAL); 320 } 321 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { 322 #ifdef MSG_DEBUG_OK 323 printf("wrong sequence number\n"); 324 #endif 325 return(EINVAL); 326 } 327 328 eval = 0; 329 rval = 0; 330 331 switch (cmd) { 332 333 case IPC_RMID: 334 { 335 struct msg *msghdr; 336 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M)) != 0) 337 return(eval); 338 /* Free the message headers */ 339 msghdr = msqptr->msg_first; 340 while (msghdr != NULL) { 341 struct msg *msghdr_tmp; 342 343 /* Free the segments of each message */ 344 msqptr->msg_cbytes -= msghdr->msg_ts; 345 msqptr->msg_qnum--; 346 msghdr_tmp = msghdr; 347 msghdr = msghdr->msg_next; 348 msg_freehdr(msghdr_tmp); 349 } 350 351 #ifdef DIAGNOSTIC 352 if (msqptr->msg_cbytes != 0) 353 panic("msg_cbytes is screwed up"); 354 if (msqptr->msg_qnum != 0) 355 panic("msg_qnum is screwed up"); 356 #endif 357 358 msqptr->msg_qbytes = 0; /* Mark it as free */ 359 360 wakeup((caddr_t)msqptr); 361 } 362 363 break; 364 365 case IPC_SET: 366 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_M))) 367 return(eval); 368 if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0) 369 return(eval); 370 if (msqbuf.msg_qbytes > msqptr->msg_qbytes && cred->cr_uid != 0) 371 return(EPERM); 372 if (msqbuf.msg_qbytes > msginfo.msgmnb) { 373 #ifdef MSG_DEBUG_OK 374 printf("can't increase msg_qbytes beyond %d (truncating)\n", 375 msginfo.msgmnb); 376 #endif 377 msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ 378 } 379 if (msqbuf.msg_qbytes == 0) { 380 #ifdef MSG_DEBUG_OK 381 printf("can't reduce msg_qbytes to 0\n"); 382 #endif 383 return(EINVAL); /* non-standard errno! */ 384 } 385 msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ 386 msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ 387 msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | 388 (msqbuf.msg_perm.mode & 0777); 389 msqptr->msg_qbytes = msqbuf.msg_qbytes; 390 msqptr->msg_ctime = time.tv_sec; 391 break; 392 393 case IPC_STAT: 394 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 395 #ifdef MSG_DEBUG_OK 396 printf("requester doesn't have read access\n"); 397 #endif 398 return(eval); 399 } 400 eval = copyout((caddr_t)msqptr, user_msqptr, 401 sizeof(struct msqid_ds)); 402 break; 403 404 default: 405 #ifdef MSG_DEBUG_OK 406 printf("invalid command %d\n", cmd); 407 #endif 408 return(EINVAL); 409 } 410 411 if (eval == 0) 412 *retval = rval; 413 return(eval); 414 } 415 416 int 417 sys_msgget(p, v, retval) 418 struct proc *p; 419 void *v; 420 register_t *retval; 421 { 422 register struct sys_msgget_args /* { 423 syscallarg(key_t) key; 424 syscallarg(int) msgflg; 425 } */ *uap = v; 426 int msqid, eval; 427 int key = SCARG(uap, key); 428 int msgflg = SCARG(uap, msgflg); 429 struct ucred *cred = p->p_ucred; 430 register struct msqid_ds *msqptr = NULL; 431 432 #ifdef MSG_DEBUG_OK 433 printf("msgget(0x%x, 0%o)\n", key, msgflg); 434 #endif 435 436 if (key != IPC_PRIVATE) { 437 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 438 msqptr = &msqids[msqid]; 439 if (msqptr->msg_qbytes != 0 && 440 msqptr->msg_perm.key == key) 441 break; 442 } 443 if (msqid < msginfo.msgmni) { 444 #ifdef MSG_DEBUG_OK 445 printf("found public key\n"); 446 #endif 447 if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { 448 #ifdef MSG_DEBUG_OK 449 printf("not exclusive\n"); 450 #endif 451 return(EEXIST); 452 } 453 if ((eval = ipcperm(cred, &msqptr->msg_perm, msgflg & 0700 ))) { 454 #ifdef MSG_DEBUG_OK 455 printf("requester doesn't have 0%o access\n", 456 msgflg & 0700); 457 #endif 458 return(eval); 459 } 460 goto found; 461 } 462 } 463 464 #ifdef MSG_DEBUG_OK 465 printf("need to allocate the msqid_ds\n"); 466 #endif 467 if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { 468 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 469 /* 470 * Look for an unallocated and unlocked msqid_ds. 471 * msqid_ds's can be locked by msgsnd or msgrcv while 472 * they are copying the message in/out. We can't 473 * re-use the entry until they release it. 474 */ 475 msqptr = &msqids[msqid]; 476 if (msqptr->msg_qbytes == 0 && 477 (msqptr->msg_perm.mode & MSG_LOCKED) == 0) 478 break; 479 } 480 if (msqid == msginfo.msgmni) { 481 #ifdef MSG_DEBUG_OK 482 printf("no more msqid_ds's available\n"); 483 #endif 484 return(ENOSPC); 485 } 486 #ifdef MSG_DEBUG_OK 487 printf("msqid %d is available\n", msqid); 488 #endif 489 msqptr->msg_perm.key = key; 490 msqptr->msg_perm.cuid = cred->cr_uid; 491 msqptr->msg_perm.uid = cred->cr_uid; 492 msqptr->msg_perm.cgid = cred->cr_gid; 493 msqptr->msg_perm.gid = cred->cr_gid; 494 msqptr->msg_perm.mode = (msgflg & 0777); 495 /* Make sure that the returned msqid is unique */ 496 msqptr->msg_perm.seq++; 497 msqptr->msg_first = NULL; 498 msqptr->msg_last = NULL; 499 msqptr->msg_cbytes = 0; 500 msqptr->msg_qnum = 0; 501 msqptr->msg_qbytes = msginfo.msgmnb; 502 msqptr->msg_lspid = 0; 503 msqptr->msg_lrpid = 0; 504 msqptr->msg_stime = 0; 505 msqptr->msg_rtime = 0; 506 msqptr->msg_ctime = time.tv_sec; 507 } else { 508 #ifdef MSG_DEBUG_OK 509 printf("didn't find it and wasn't asked to create it\n"); 510 #endif 511 return(ENOENT); 512 } 513 514 found: 515 /* Construct the unique msqid */ 516 *retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); 517 return(0); 518 } 519 520 int 521 sys_msgsnd(p, v, retval) 522 struct proc *p; 523 void *v; 524 register_t *retval; 525 { 526 register struct sys_msgsnd_args /* { 527 syscallarg(int) msqid; 528 syscallarg(const void *) msgp; 529 syscallarg(size_t) msgsz; 530 syscallarg(int) msgflg; 531 } */ *uap = v; 532 int msqid = SCARG(uap, msqid); 533 const char *user_msgp = SCARG(uap, msgp); 534 size_t msgsz = SCARG(uap, msgsz); 535 int msgflg = SCARG(uap, msgflg); 536 int segs_needed, eval; 537 struct ucred *cred = p->p_ucred; 538 register struct msqid_ds *msqptr; 539 register struct msg *msghdr; 540 short next; 541 542 #ifdef MSG_DEBUG_OK 543 printf("call to msgsnd(%d, %p, %d, %d)\n", msqid, user_msgp, msgsz, 544 msgflg); 545 #endif 546 547 msqid = IPCID_TO_IX(msqid); 548 549 if (msqid < 0 || msqid >= msginfo.msgmni) { 550 #ifdef MSG_DEBUG_OK 551 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 552 msginfo.msgmni); 553 #endif 554 return(EINVAL); 555 } 556 557 msqptr = &msqids[msqid]; 558 if (msqptr->msg_qbytes == 0) { 559 #ifdef MSG_DEBUG_OK 560 printf("no such message queue id\n"); 561 #endif 562 return(EINVAL); 563 } 564 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { 565 #ifdef MSG_DEBUG_OK 566 printf("wrong sequence number\n"); 567 #endif 568 return(EINVAL); 569 } 570 571 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_W))) { 572 #ifdef MSG_DEBUG_OK 573 printf("requester doesn't have write access\n"); 574 #endif 575 return(eval); 576 } 577 578 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 579 #ifdef MSG_DEBUG_OK 580 printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, 581 segs_needed); 582 #endif 583 for (;;) { 584 int need_more_resources = 0; 585 586 /* 587 * check msgsz [cannot be negative since it is unsigned] 588 * (inside this loop in case msg_qbytes changes while we sleep) 589 */ 590 591 if (msgsz > msqptr->msg_qbytes) { 592 #ifdef MSG_DEBUG_OK 593 printf("msgsz > msqptr->msg_qbytes\n"); 594 #endif 595 return(EINVAL); 596 } 597 598 if (msqptr->msg_perm.mode & MSG_LOCKED) { 599 #ifdef MSG_DEBUG_OK 600 printf("msqid is locked\n"); 601 #endif 602 need_more_resources = 1; 603 } 604 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { 605 #ifdef MSG_DEBUG_OK 606 printf("msgsz + msg_cbytes > msg_qbytes\n"); 607 #endif 608 need_more_resources = 1; 609 } 610 if (segs_needed > nfree_msgmaps) { 611 #ifdef MSG_DEBUG_OK 612 printf("segs_needed > nfree_msgmaps\n"); 613 #endif 614 need_more_resources = 1; 615 } 616 if (free_msghdrs == NULL) { 617 #ifdef MSG_DEBUG_OK 618 printf("no more msghdrs\n"); 619 #endif 620 need_more_resources = 1; 621 } 622 623 if (need_more_resources) { 624 int we_own_it; 625 626 if ((msgflg & IPC_NOWAIT) != 0) { 627 #ifdef MSG_DEBUG_OK 628 printf("need more resources but caller doesn't want to wait\n"); 629 #endif 630 return(EAGAIN); 631 } 632 633 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { 634 #ifdef MSG_DEBUG_OK 635 printf("we don't own the msqid_ds\n"); 636 #endif 637 we_own_it = 0; 638 } else { 639 /* Force later arrivals to wait for our 640 request */ 641 #ifdef MSG_DEBUG_OK 642 printf("we own the msqid_ds\n"); 643 #endif 644 msqptr->msg_perm.mode |= MSG_LOCKED; 645 we_own_it = 1; 646 } 647 #ifdef MSG_DEBUG_OK 648 printf("goodnight\n"); 649 #endif 650 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, 651 "msgwait", 0); 652 #ifdef MSG_DEBUG_OK 653 printf("good morning, eval=%d\n", eval); 654 #endif 655 if (we_own_it) 656 msqptr->msg_perm.mode &= ~MSG_LOCKED; 657 if (eval != 0) { 658 #ifdef MSG_DEBUG_OK 659 printf("msgsnd: interrupted system call\n"); 660 #endif 661 return(EINTR); 662 } 663 664 /* 665 * Make sure that the msq queue still exists 666 */ 667 668 if (msqptr->msg_qbytes == 0) { 669 #ifdef MSG_DEBUG_OK 670 printf("msqid deleted\n"); 671 #endif 672 /* The SVID says to return EIDRM. */ 673 #ifdef EIDRM 674 return(EIDRM); 675 #else 676 /* Unfortunately, BSD doesn't define that code 677 yet! */ 678 return(EINVAL); 679 #endif 680 } 681 682 } else { 683 #ifdef MSG_DEBUG_OK 684 printf("got all the resources that we need\n"); 685 #endif 686 break; 687 } 688 } 689 690 /* 691 * We have the resources that we need. 692 * Make sure! 693 */ 694 695 #ifdef DIAGNOSTIC 696 if (msqptr->msg_perm.mode & MSG_LOCKED) 697 panic("msg_perm.mode & MSG_LOCKED"); 698 if (segs_needed > nfree_msgmaps) 699 panic("segs_needed > nfree_msgmaps"); 700 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) 701 panic("msgsz + msg_cbytes > msg_qbytes"); 702 if (free_msghdrs == NULL) 703 panic("no more msghdrs"); 704 #endif 705 706 /* 707 * Re-lock the msqid_ds in case we page-fault when copying in the 708 * message 709 */ 710 711 #ifdef DIAGNOSTIC 712 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) 713 panic("msqid_ds is already locked"); 714 #endif 715 msqptr->msg_perm.mode |= MSG_LOCKED; 716 717 /* 718 * Allocate a message header 719 */ 720 721 msghdr = free_msghdrs; 722 free_msghdrs = msghdr->msg_next; 723 msghdr->msg_spot = -1; 724 msghdr->msg_ts = msgsz; 725 726 /* 727 * Allocate space for the message 728 */ 729 730 while (segs_needed > 0) { 731 #ifdef DIAGNOSTIC 732 if (nfree_msgmaps <= 0) 733 panic("not enough msgmaps"); 734 if (free_msgmaps == -1) 735 panic("nil free_msgmaps"); 736 #endif 737 next = free_msgmaps; 738 #ifdef DIAGNOSTIC 739 if (next <= -1) 740 panic("next too low #1"); 741 if (next >= msginfo.msgseg) 742 panic("next out of range #1"); 743 #endif 744 #ifdef MSG_DEBUG_OK 745 printf("allocating segment %d to message\n", next); 746 #endif 747 free_msgmaps = msgmaps[next].next; 748 nfree_msgmaps--; 749 msgmaps[next].next = msghdr->msg_spot; 750 msghdr->msg_spot = next; 751 segs_needed--; 752 } 753 754 /* 755 * Copy in the message type 756 */ 757 758 if ((eval = copyin(user_msgp, &msghdr->msg_type, 759 sizeof(msghdr->msg_type))) != 0) { 760 #ifdef MSG_DEBUG_OK 761 printf("error %d copying the message type\n", eval); 762 #endif 763 msg_freehdr(msghdr); 764 msqptr->msg_perm.mode &= ~MSG_LOCKED; 765 wakeup((caddr_t)msqptr); 766 return(eval); 767 } 768 user_msgp += sizeof(msghdr->msg_type); 769 770 /* 771 * Validate the message type 772 */ 773 774 if (msghdr->msg_type < 1) { 775 msg_freehdr(msghdr); 776 msqptr->msg_perm.mode &= ~MSG_LOCKED; 777 wakeup((caddr_t)msqptr); 778 #ifdef MSG_DEBUG_OK 779 printf("mtype (%d) < 1\n", msghdr->msg_type); 780 #endif 781 return(EINVAL); 782 } 783 784 /* 785 * Copy in the message body 786 */ 787 788 next = msghdr->msg_spot; 789 while (msgsz > 0) { 790 size_t tlen; 791 if (msgsz > msginfo.msgssz) 792 tlen = msginfo.msgssz; 793 else 794 tlen = msgsz; 795 #ifdef DIAGNOSTIC 796 if (next <= -1) 797 panic("next too low #2"); 798 if (next >= msginfo.msgseg) 799 panic("next out of range #2"); 800 #endif 801 if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], 802 tlen)) != 0) { 803 #ifdef MSG_DEBUG_OK 804 printf("error %d copying in message segment\n", eval); 805 #endif 806 msg_freehdr(msghdr); 807 msqptr->msg_perm.mode &= ~MSG_LOCKED; 808 wakeup((caddr_t)msqptr); 809 return(eval); 810 } 811 msgsz -= tlen; 812 user_msgp += tlen; 813 next = msgmaps[next].next; 814 } 815 #ifdef DIAGNOSTIC 816 if (next != -1) 817 panic("didn't use all the msg segments"); 818 #endif 819 /* 820 * We've got the message. Unlock the msqid_ds. 821 */ 822 823 msqptr->msg_perm.mode &= ~MSG_LOCKED; 824 825 /* 826 * Make sure that the msqid_ds is still allocated. 827 */ 828 829 if (msqptr->msg_qbytes == 0) { 830 msg_freehdr(msghdr); 831 wakeup((caddr_t)msqptr); 832 /* The SVID says to return EIDRM. */ 833 #ifdef EIDRM 834 return(EIDRM); 835 #else 836 /* Unfortunately, BSD doesn't define that code yet! */ 837 return(EINVAL); 838 #endif 839 } 840 841 /* 842 * Put the message into the queue 843 */ 844 845 if (msqptr->msg_first == NULL) { 846 msqptr->msg_first = msghdr; 847 msqptr->msg_last = msghdr; 848 } else { 849 msqptr->msg_last->msg_next = msghdr; 850 msqptr->msg_last = msghdr; 851 } 852 msqptr->msg_last->msg_next = NULL; 853 854 msqptr->msg_cbytes += msghdr->msg_ts; 855 msqptr->msg_qnum++; 856 msqptr->msg_lspid = p->p_pid; 857 msqptr->msg_stime = time.tv_sec; 858 859 wakeup((caddr_t)msqptr); 860 *retval = 0; 861 return(0); 862 } 863 864 int 865 sys_msgrcv(p, v, retval) 866 struct proc *p; 867 void *v; 868 register_t *retval; 869 { 870 register struct sys_msgrcv_args /* { 871 syscallarg(int) msqid; 872 syscallarg(void *) msgp; 873 syscallarg(size_t) msgsz; 874 syscallarg(long) msgtyp; 875 syscallarg(int) msgflg; 876 } */ *uap = v; 877 int msqid = SCARG(uap, msqid); 878 char *user_msgp = SCARG(uap, msgp); 879 size_t msgsz = SCARG(uap, msgsz); 880 long msgtyp = SCARG(uap, msgtyp); 881 int msgflg = SCARG(uap, msgflg); 882 size_t len; 883 struct ucred *cred = p->p_ucred; 884 register struct msqid_ds *msqptr; 885 register struct msg *msghdr; 886 int eval; 887 short next; 888 889 #ifdef MSG_DEBUG_OK 890 printf("call to msgrcv(%d, %p, %d, %ld, %d)\n", msqid, user_msgp, 891 msgsz, msgtyp, msgflg); 892 #endif 893 894 msqid = IPCID_TO_IX(msqid); 895 896 if (msqid < 0 || msqid >= msginfo.msgmni) { 897 #ifdef MSG_DEBUG_OK 898 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 899 msginfo.msgmni); 900 #endif 901 return(EINVAL); 902 } 903 904 msqptr = &msqids[msqid]; 905 if (msqptr->msg_qbytes == 0) { 906 #ifdef MSG_DEBUG_OK 907 printf("no such message queue id\n"); 908 #endif 909 return(EINVAL); 910 } 911 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { 912 #ifdef MSG_DEBUG_OK 913 printf("wrong sequence number\n"); 914 #endif 915 return(EINVAL); 916 } 917 918 if ((eval = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 919 #ifdef MSG_DEBUG_OK 920 printf("requester doesn't have read access\n"); 921 #endif 922 return(eval); 923 } 924 925 #if 0 926 /* cannot happen, msgsz is unsigned */ 927 if (msgsz < 0) { 928 #ifdef MSG_DEBUG_OK 929 printf("msgsz < 0\n"); 930 #endif 931 return(EINVAL); 932 } 933 #endif 934 935 msghdr = NULL; 936 while (msghdr == NULL) { 937 if (msgtyp == 0) { 938 msghdr = msqptr->msg_first; 939 if (msghdr != NULL) { 940 if (msgsz < msghdr->msg_ts && 941 (msgflg & MSG_NOERROR) == 0) { 942 #ifdef MSG_DEBUG_OK 943 printf("first message on the queue is too big (want %d, got %d)\n", 944 msgsz, msghdr->msg_ts); 945 #endif 946 return(E2BIG); 947 } 948 if (msqptr->msg_first == msqptr->msg_last) { 949 msqptr->msg_first = NULL; 950 msqptr->msg_last = NULL; 951 } else { 952 msqptr->msg_first = msghdr->msg_next; 953 #ifdef DIAGNOSTIC 954 if (msqptr->msg_first == NULL) 955 panic("msg_first/last screwed up #1"); 956 #endif 957 } 958 } 959 } else { 960 struct msg *previous; 961 struct msg **prev; 962 963 for (previous = NULL, prev = &msqptr->msg_first; 964 (msghdr = *prev) != NULL; 965 previous = msghdr, prev = &msghdr->msg_next) { 966 /* 967 * Is this message's type an exact match or is 968 * this message's type less than or equal to 969 * the absolute value of a negative msgtyp? 970 * Note that the second half of this test can 971 * NEVER be true if msgtyp is positive since 972 * msg_type is always positive! 973 */ 974 975 if (msgtyp == msghdr->msg_type || 976 msghdr->msg_type <= -msgtyp) { 977 #ifdef MSG_DEBUG_OK 978 printf("found message type %d, requested %d\n", 979 msghdr->msg_type, msgtyp); 980 #endif 981 if (msgsz < msghdr->msg_ts && 982 (msgflg & MSG_NOERROR) == 0) { 983 #ifdef MSG_DEBUG_OK 984 printf("requested message on the queue is too big (want %d, got %d)\n", 985 msgsz, msghdr->msg_ts); 986 #endif 987 return(E2BIG); 988 } 989 *prev = msghdr->msg_next; 990 if (msghdr == msqptr->msg_last) { 991 if (previous == NULL) { 992 #ifdef DIAGNOSTIC 993 if (prev != 994 &msqptr->msg_first) 995 panic("msg_first/last screwed up #2"); 996 #endif 997 msqptr->msg_first = 998 NULL; 999 msqptr->msg_last = 1000 NULL; 1001 } else { 1002 #ifdef DIAGNOSTIC 1003 if (prev == 1004 &msqptr->msg_first) 1005 panic("msg_first/last screwed up #3"); 1006 #endif 1007 msqptr->msg_last = 1008 previous; 1009 } 1010 } 1011 break; 1012 } 1013 } 1014 } 1015 1016 /* 1017 * We've either extracted the msghdr for the appropriate 1018 * message or there isn't one. 1019 * If there is one then bail out of this loop. 1020 */ 1021 1022 if (msghdr != NULL) 1023 break; 1024 1025 /* 1026 * Hmph! No message found. Does the user want to wait? 1027 */ 1028 1029 if ((msgflg & IPC_NOWAIT) != 0) { 1030 #ifdef MSG_DEBUG_OK 1031 printf("no appropriate message found (msgtyp=%d)\n", 1032 msgtyp); 1033 #endif 1034 /* The SVID says to return ENOMSG. */ 1035 #ifdef ENOMSG 1036 return(ENOMSG); 1037 #else 1038 /* Unfortunately, BSD doesn't define that code yet! */ 1039 return(EAGAIN); 1040 #endif 1041 } 1042 1043 /* 1044 * Wait for something to happen 1045 */ 1046 1047 #ifdef MSG_DEBUG_OK 1048 printf("msgrcv: goodnight\n"); 1049 #endif 1050 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait", 1051 0); 1052 #ifdef MSG_DEBUG_OK 1053 printf("msgrcv: good morning (eval=%d)\n", eval); 1054 #endif 1055 1056 if (eval != 0) { 1057 #ifdef MSG_DEBUG_OK 1058 printf("msgsnd: interrupted system call\n"); 1059 #endif 1060 return(EINTR); 1061 } 1062 1063 /* 1064 * Make sure that the msq queue still exists 1065 */ 1066 1067 if (msqptr->msg_qbytes == 0 || 1068 msqptr->msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) { 1069 #ifdef MSG_DEBUG_OK 1070 printf("msqid deleted\n"); 1071 #endif 1072 /* The SVID says to return EIDRM. */ 1073 #ifdef EIDRM 1074 return(EIDRM); 1075 #else 1076 /* Unfortunately, BSD doesn't define that code yet! */ 1077 return(EINVAL); 1078 #endif 1079 } 1080 } 1081 1082 /* 1083 * Return the message to the user. 1084 * 1085 * First, do the bookkeeping (before we risk being interrupted). 1086 */ 1087 1088 msqptr->msg_cbytes -= msghdr->msg_ts; 1089 msqptr->msg_qnum--; 1090 msqptr->msg_lrpid = p->p_pid; 1091 msqptr->msg_rtime = time.tv_sec; 1092 1093 /* 1094 * Make msgsz the actual amount that we'll be returning. 1095 * Note that this effectively truncates the message if it is too long 1096 * (since msgsz is never increased). 1097 */ 1098 1099 #ifdef MSG_DEBUG_OK 1100 printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz, 1101 msghdr->msg_ts); 1102 #endif 1103 if (msgsz > msghdr->msg_ts) 1104 msgsz = msghdr->msg_ts; 1105 1106 /* 1107 * Return the type to the user. 1108 */ 1109 1110 eval = copyout((caddr_t)&msghdr->msg_type, user_msgp, 1111 sizeof(msghdr->msg_type)); 1112 if (eval != 0) { 1113 #ifdef MSG_DEBUG_OK 1114 printf("error (%d) copying out message type\n", eval); 1115 #endif 1116 msg_freehdr(msghdr); 1117 wakeup((caddr_t)msqptr); 1118 return(eval); 1119 } 1120 user_msgp += sizeof(msghdr->msg_type); 1121 1122 /* 1123 * Return the segments to the user 1124 */ 1125 1126 next = msghdr->msg_spot; 1127 for (len = 0; len < msgsz; len += msginfo.msgssz) { 1128 size_t tlen; 1129 1130 if (msgsz - len > msginfo.msgssz) 1131 tlen = msginfo.msgssz; 1132 else 1133 tlen = msgsz - len; 1134 #ifdef DIAGNOSTIC 1135 if (next <= -1) 1136 panic("next too low #3"); 1137 if (next >= msginfo.msgseg) 1138 panic("next out of range #3"); 1139 #endif 1140 eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz], 1141 user_msgp, tlen); 1142 if (eval != 0) { 1143 #ifdef MSG_DEBUG_OK 1144 printf("error (%d) copying out message segment\n", 1145 eval); 1146 #endif 1147 msg_freehdr(msghdr); 1148 wakeup((caddr_t)msqptr); 1149 return(eval); 1150 } 1151 user_msgp += tlen; 1152 next = msgmaps[next].next; 1153 } 1154 1155 /* 1156 * Done, return the actual number of bytes copied out. 1157 */ 1158 1159 msg_freehdr(msghdr); 1160 wakeup((caddr_t)msqptr); 1161 *retval = msgsz; 1162 return(0); 1163 } 1164