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