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 msqids[i].msg_perm.mode = 0; 192 } 193 } 194 SYSINIT(sysv_msg, SI_SUB_SYSV_MSG, SI_ORDER_FIRST, msginit, NULL) 195 196 /* 197 * Entry point for all MSG calls 198 */ 199 int 200 msgsys(p, uap) 201 struct proc *p; 202 /* XXX actually varargs. */ 203 struct msgsys_args /* { 204 u_int which; 205 int a2; 206 int a3; 207 int a4; 208 int a5; 209 int a6; 210 } */ *uap; 211 { 212 213 if (uap->which >= sizeof(msgcalls)/sizeof(msgcalls[0])) 214 return (EINVAL); 215 return ((*msgcalls[uap->which])(p, &uap->a2)); 216 } 217 218 static void 219 msg_freehdr(msghdr) 220 struct msg *msghdr; 221 { 222 while (msghdr->msg_ts > 0) { 223 short next; 224 if (msghdr->msg_spot < 0 || msghdr->msg_spot >= msginfo.msgseg) 225 panic("msghdr->msg_spot out of range"); 226 next = msgmaps[msghdr->msg_spot].next; 227 msgmaps[msghdr->msg_spot].next = free_msgmaps; 228 free_msgmaps = msghdr->msg_spot; 229 nfree_msgmaps++; 230 msghdr->msg_spot = next; 231 if (msghdr->msg_ts >= msginfo.msgssz) 232 msghdr->msg_ts -= msginfo.msgssz; 233 else 234 msghdr->msg_ts = 0; 235 } 236 if (msghdr->msg_spot != -1) 237 panic("msghdr->msg_spot != -1"); 238 msghdr->msg_next = free_msghdrs; 239 free_msghdrs = msghdr; 240 } 241 242 #ifndef _SYS_SYSPROTO_H_ 243 struct msgctl_args { 244 int msqid; 245 int cmd; 246 struct msqid_ds *buf; 247 }; 248 #endif 249 250 int 251 msgctl(p, uap) 252 struct proc *p; 253 register struct msgctl_args *uap; 254 { 255 int msqid = uap->msqid; 256 int cmd = uap->cmd; 257 struct msqid_ds *user_msqptr = uap->buf; 258 int rval, eval; 259 struct msqid_ds msqbuf; 260 register struct msqid_ds *msqptr; 261 262 #ifdef MSG_DEBUG_OK 263 printf("call to msgctl(%d, %d, 0x%x)\n", msqid, cmd, user_msqptr); 264 #endif 265 266 msqid = IPCID_TO_IX(msqid); 267 268 if (msqid < 0 || msqid >= msginfo.msgmni) { 269 #ifdef MSG_DEBUG_OK 270 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 271 msginfo.msgmni); 272 #endif 273 return(EINVAL); 274 } 275 276 msqptr = &msqids[msqid]; 277 278 if (msqptr->msg_qbytes == 0) { 279 #ifdef MSG_DEBUG_OK 280 printf("no such msqid\n"); 281 #endif 282 return(EINVAL); 283 } 284 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 285 #ifdef MSG_DEBUG_OK 286 printf("wrong sequence number\n"); 287 #endif 288 return(EINVAL); 289 } 290 291 eval = 0; 292 rval = 0; 293 294 switch (cmd) { 295 296 case IPC_RMID: 297 { 298 struct msg *msghdr; 299 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M))) 300 return(eval); 301 /* Free the message headers */ 302 msghdr = msqptr->msg_first; 303 while (msghdr != NULL) { 304 struct msg *msghdr_tmp; 305 306 /* Free the segments of each message */ 307 msqptr->msg_cbytes -= msghdr->msg_ts; 308 msqptr->msg_qnum--; 309 msghdr_tmp = msghdr; 310 msghdr = msghdr->msg_next; 311 msg_freehdr(msghdr_tmp); 312 } 313 314 if (msqptr->msg_cbytes != 0) 315 panic("msg_cbytes is screwed up"); 316 if (msqptr->msg_qnum != 0) 317 panic("msg_qnum is screwed up"); 318 319 msqptr->msg_qbytes = 0; /* Mark it as free */ 320 321 wakeup((caddr_t)msqptr); 322 } 323 324 break; 325 326 case IPC_SET: 327 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_M))) 328 return(eval); 329 if ((eval = copyin(user_msqptr, &msqbuf, sizeof(msqbuf))) != 0) 330 return(eval); 331 if (msqbuf.msg_qbytes > msqptr->msg_qbytes) { 332 eval = suser(p); 333 if (eval) 334 return(eval); 335 } 336 if (msqbuf.msg_qbytes > msginfo.msgmnb) { 337 #ifdef MSG_DEBUG_OK 338 printf("can't increase msg_qbytes beyond %d (truncating)\n", 339 msginfo.msgmnb); 340 #endif 341 msqbuf.msg_qbytes = msginfo.msgmnb; /* silently restrict qbytes to system limit */ 342 } 343 if (msqbuf.msg_qbytes == 0) { 344 #ifdef MSG_DEBUG_OK 345 printf("can't reduce msg_qbytes to 0\n"); 346 #endif 347 return(EINVAL); /* non-standard errno! */ 348 } 349 msqptr->msg_perm.uid = msqbuf.msg_perm.uid; /* change the owner */ 350 msqptr->msg_perm.gid = msqbuf.msg_perm.gid; /* change the owner */ 351 msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | 352 (msqbuf.msg_perm.mode & 0777); 353 msqptr->msg_qbytes = msqbuf.msg_qbytes; 354 msqptr->msg_ctime = time_second; 355 break; 356 357 case IPC_STAT: 358 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) { 359 #ifdef MSG_DEBUG_OK 360 printf("requester doesn't have read access\n"); 361 #endif 362 return(eval); 363 } 364 eval = copyout((caddr_t)msqptr, user_msqptr, 365 sizeof(struct msqid_ds)); 366 break; 367 368 default: 369 #ifdef MSG_DEBUG_OK 370 printf("invalid command %d\n", cmd); 371 #endif 372 return(EINVAL); 373 } 374 375 if (eval == 0) 376 p->p_retval[0] = rval; 377 return(eval); 378 } 379 380 #ifndef _SYS_SYSPROTO_H_ 381 struct msgget_args { 382 key_t key; 383 int msgflg; 384 }; 385 #endif 386 387 int 388 msgget(p, uap) 389 struct proc *p; 390 register struct msgget_args *uap; 391 { 392 int msqid, eval; 393 int key = uap->key; 394 int msgflg = uap->msgflg; 395 struct ucred *cred = p->p_ucred; 396 register struct msqid_ds *msqptr = NULL; 397 398 #ifdef MSG_DEBUG_OK 399 printf("msgget(0x%x, 0%o)\n", key, msgflg); 400 #endif 401 402 if (key != IPC_PRIVATE) { 403 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 404 msqptr = &msqids[msqid]; 405 if (msqptr->msg_qbytes != 0 && 406 msqptr->msg_perm.key == key) 407 break; 408 } 409 if (msqid < msginfo.msgmni) { 410 #ifdef MSG_DEBUG_OK 411 printf("found public key\n"); 412 #endif 413 if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { 414 #ifdef MSG_DEBUG_OK 415 printf("not exclusive\n"); 416 #endif 417 return(EEXIST); 418 } 419 if ((eval = ipcperm(p, &msqptr->msg_perm, msgflg & 0700 ))) { 420 #ifdef MSG_DEBUG_OK 421 printf("requester doesn't have 0%o access\n", 422 msgflg & 0700); 423 #endif 424 return(eval); 425 } 426 goto found; 427 } 428 } 429 430 #ifdef MSG_DEBUG_OK 431 printf("need to allocate the msqid_ds\n"); 432 #endif 433 if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { 434 for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 435 /* 436 * Look for an unallocated and unlocked msqid_ds. 437 * msqid_ds's can be locked by msgsnd or msgrcv while 438 * they are copying the message in/out. We can't 439 * re-use the entry until they release it. 440 */ 441 msqptr = &msqids[msqid]; 442 if (msqptr->msg_qbytes == 0 && 443 (msqptr->msg_perm.mode & MSG_LOCKED) == 0) 444 break; 445 } 446 if (msqid == msginfo.msgmni) { 447 #ifdef MSG_DEBUG_OK 448 printf("no more msqid_ds's available\n"); 449 #endif 450 return(ENOSPC); 451 } 452 #ifdef MSG_DEBUG_OK 453 printf("msqid %d is available\n", msqid); 454 #endif 455 msqptr->msg_perm.key = key; 456 msqptr->msg_perm.cuid = cred->cr_uid; 457 msqptr->msg_perm.uid = cred->cr_uid; 458 msqptr->msg_perm.cgid = cred->cr_gid; 459 msqptr->msg_perm.gid = cred->cr_gid; 460 msqptr->msg_perm.mode = (msgflg & 0777); 461 /* Make sure that the returned msqid is unique */ 462 msqptr->msg_perm.seq++; 463 msqptr->msg_first = NULL; 464 msqptr->msg_last = NULL; 465 msqptr->msg_cbytes = 0; 466 msqptr->msg_qnum = 0; 467 msqptr->msg_qbytes = msginfo.msgmnb; 468 msqptr->msg_lspid = 0; 469 msqptr->msg_lrpid = 0; 470 msqptr->msg_stime = 0; 471 msqptr->msg_rtime = 0; 472 msqptr->msg_ctime = time_second; 473 } else { 474 #ifdef MSG_DEBUG_OK 475 printf("didn't find it and wasn't asked to create it\n"); 476 #endif 477 return(ENOENT); 478 } 479 480 found: 481 /* Construct the unique msqid */ 482 p->p_retval[0] = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); 483 return(0); 484 } 485 486 #ifndef _SYS_SYSPROTO_H_ 487 struct msgsnd_args { 488 int msqid; 489 void *msgp; 490 size_t msgsz; 491 int msgflg; 492 }; 493 #endif 494 495 int 496 msgsnd(p, uap) 497 struct proc *p; 498 register struct msgsnd_args *uap; 499 { 500 int msqid = uap->msqid; 501 void *user_msgp = uap->msgp; 502 size_t msgsz = uap->msgsz; 503 int msgflg = uap->msgflg; 504 int segs_needed, eval; 505 register struct msqid_ds *msqptr; 506 register struct msg *msghdr; 507 short next; 508 509 #ifdef MSG_DEBUG_OK 510 printf("call to msgsnd(%d, 0x%x, %d, %d)\n", msqid, user_msgp, msgsz, 511 msgflg); 512 #endif 513 514 msqid = IPCID_TO_IX(msqid); 515 516 if (msqid < 0 || msqid >= msginfo.msgmni) { 517 #ifdef MSG_DEBUG_OK 518 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 519 msginfo.msgmni); 520 #endif 521 return(EINVAL); 522 } 523 524 msqptr = &msqids[msqid]; 525 if (msqptr->msg_qbytes == 0) { 526 #ifdef MSG_DEBUG_OK 527 printf("no such message queue id\n"); 528 #endif 529 return(EINVAL); 530 } 531 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 532 #ifdef MSG_DEBUG_OK 533 printf("wrong sequence number\n"); 534 #endif 535 return(EINVAL); 536 } 537 538 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_W))) { 539 #ifdef MSG_DEBUG_OK 540 printf("requester doesn't have write access\n"); 541 #endif 542 return(eval); 543 } 544 545 segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 546 #ifdef MSG_DEBUG_OK 547 printf("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz, 548 segs_needed); 549 #endif 550 for (;;) { 551 int need_more_resources = 0; 552 553 /* 554 * check msgsz 555 * (inside this loop in case msg_qbytes changes while we sleep) 556 */ 557 558 if (msgsz > msqptr->msg_qbytes) { 559 #ifdef MSG_DEBUG_OK 560 printf("msgsz > msqptr->msg_qbytes\n"); 561 #endif 562 return(EINVAL); 563 } 564 565 if (msqptr->msg_perm.mode & MSG_LOCKED) { 566 #ifdef MSG_DEBUG_OK 567 printf("msqid is locked\n"); 568 #endif 569 need_more_resources = 1; 570 } 571 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) { 572 #ifdef MSG_DEBUG_OK 573 printf("msgsz + msg_cbytes > msg_qbytes\n"); 574 #endif 575 need_more_resources = 1; 576 } 577 if (segs_needed > nfree_msgmaps) { 578 #ifdef MSG_DEBUG_OK 579 printf("segs_needed > nfree_msgmaps\n"); 580 #endif 581 need_more_resources = 1; 582 } 583 if (free_msghdrs == NULL) { 584 #ifdef MSG_DEBUG_OK 585 printf("no more msghdrs\n"); 586 #endif 587 need_more_resources = 1; 588 } 589 590 if (need_more_resources) { 591 int we_own_it; 592 593 if ((msgflg & IPC_NOWAIT) != 0) { 594 #ifdef MSG_DEBUG_OK 595 printf("need more resources but caller doesn't want to wait\n"); 596 #endif 597 return(EAGAIN); 598 } 599 600 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { 601 #ifdef MSG_DEBUG_OK 602 printf("we don't own the msqid_ds\n"); 603 #endif 604 we_own_it = 0; 605 } else { 606 /* Force later arrivals to wait for our 607 request */ 608 #ifdef MSG_DEBUG_OK 609 printf("we own the msqid_ds\n"); 610 #endif 611 msqptr->msg_perm.mode |= MSG_LOCKED; 612 we_own_it = 1; 613 } 614 #ifdef MSG_DEBUG_OK 615 printf("goodnight\n"); 616 #endif 617 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, 618 "msgwait", 0); 619 #ifdef MSG_DEBUG_OK 620 printf("good morning, eval=%d\n", eval); 621 #endif 622 if (we_own_it) 623 msqptr->msg_perm.mode &= ~MSG_LOCKED; 624 if (eval != 0) { 625 #ifdef MSG_DEBUG_OK 626 printf("msgsnd: interrupted system call\n"); 627 #endif 628 return(EINTR); 629 } 630 631 /* 632 * Make sure that the msq queue still exists 633 */ 634 635 if (msqptr->msg_qbytes == 0) { 636 #ifdef MSG_DEBUG_OK 637 printf("msqid deleted\n"); 638 #endif 639 return(EIDRM); 640 } 641 642 } else { 643 #ifdef MSG_DEBUG_OK 644 printf("got all the resources that we need\n"); 645 #endif 646 break; 647 } 648 } 649 650 /* 651 * We have the resources that we need. 652 * Make sure! 653 */ 654 655 if (msqptr->msg_perm.mode & MSG_LOCKED) 656 panic("msg_perm.mode & MSG_LOCKED"); 657 if (segs_needed > nfree_msgmaps) 658 panic("segs_needed > nfree_msgmaps"); 659 if (msgsz + msqptr->msg_cbytes > msqptr->msg_qbytes) 660 panic("msgsz + msg_cbytes > msg_qbytes"); 661 if (free_msghdrs == NULL) 662 panic("no more msghdrs"); 663 664 /* 665 * Re-lock the msqid_ds in case we page-fault when copying in the 666 * message 667 */ 668 669 if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) 670 panic("msqid_ds is already locked"); 671 msqptr->msg_perm.mode |= MSG_LOCKED; 672 673 /* 674 * Allocate a message header 675 */ 676 677 msghdr = free_msghdrs; 678 free_msghdrs = msghdr->msg_next; 679 msghdr->msg_spot = -1; 680 msghdr->msg_ts = msgsz; 681 682 /* 683 * Allocate space for the message 684 */ 685 686 while (segs_needed > 0) { 687 if (nfree_msgmaps <= 0) 688 panic("not enough msgmaps"); 689 if (free_msgmaps == -1) 690 panic("nil free_msgmaps"); 691 next = free_msgmaps; 692 if (next <= -1) 693 panic("next too low #1"); 694 if (next >= msginfo.msgseg) 695 panic("next out of range #1"); 696 #ifdef MSG_DEBUG_OK 697 printf("allocating segment %d to message\n", next); 698 #endif 699 free_msgmaps = msgmaps[next].next; 700 nfree_msgmaps--; 701 msgmaps[next].next = msghdr->msg_spot; 702 msghdr->msg_spot = next; 703 segs_needed--; 704 } 705 706 /* 707 * Copy in the message type 708 */ 709 710 if ((eval = copyin(user_msgp, &msghdr->msg_type, 711 sizeof(msghdr->msg_type))) != 0) { 712 #ifdef MSG_DEBUG_OK 713 printf("error %d copying the message type\n", eval); 714 #endif 715 msg_freehdr(msghdr); 716 msqptr->msg_perm.mode &= ~MSG_LOCKED; 717 wakeup((caddr_t)msqptr); 718 return(eval); 719 } 720 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); 721 722 /* 723 * Validate the message type 724 */ 725 726 if (msghdr->msg_type < 1) { 727 msg_freehdr(msghdr); 728 msqptr->msg_perm.mode &= ~MSG_LOCKED; 729 wakeup((caddr_t)msqptr); 730 #ifdef MSG_DEBUG_OK 731 printf("mtype (%d) < 1\n", msghdr->msg_type); 732 #endif 733 return(EINVAL); 734 } 735 736 /* 737 * Copy in the message body 738 */ 739 740 next = msghdr->msg_spot; 741 while (msgsz > 0) { 742 size_t tlen; 743 if (msgsz > msginfo.msgssz) 744 tlen = msginfo.msgssz; 745 else 746 tlen = msgsz; 747 if (next <= -1) 748 panic("next too low #2"); 749 if (next >= msginfo.msgseg) 750 panic("next out of range #2"); 751 if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz], 752 tlen)) != 0) { 753 #ifdef MSG_DEBUG_OK 754 printf("error %d copying in message segment\n", eval); 755 #endif 756 msg_freehdr(msghdr); 757 msqptr->msg_perm.mode &= ~MSG_LOCKED; 758 wakeup((caddr_t)msqptr); 759 return(eval); 760 } 761 msgsz -= tlen; 762 user_msgp = (char *)user_msgp + tlen; 763 next = msgmaps[next].next; 764 } 765 if (next != -1) 766 panic("didn't use all the msg segments"); 767 768 /* 769 * We've got the message. Unlock the msqid_ds. 770 */ 771 772 msqptr->msg_perm.mode &= ~MSG_LOCKED; 773 774 /* 775 * Make sure that the msqid_ds is still allocated. 776 */ 777 778 if (msqptr->msg_qbytes == 0) { 779 msg_freehdr(msghdr); 780 wakeup((caddr_t)msqptr); 781 return(EIDRM); 782 } 783 784 /* 785 * Put the message into the queue 786 */ 787 788 if (msqptr->msg_first == NULL) { 789 msqptr->msg_first = msghdr; 790 msqptr->msg_last = msghdr; 791 } else { 792 msqptr->msg_last->msg_next = msghdr; 793 msqptr->msg_last = msghdr; 794 } 795 msqptr->msg_last->msg_next = NULL; 796 797 msqptr->msg_cbytes += msghdr->msg_ts; 798 msqptr->msg_qnum++; 799 msqptr->msg_lspid = p->p_pid; 800 msqptr->msg_stime = time_second; 801 802 wakeup((caddr_t)msqptr); 803 p->p_retval[0] = 0; 804 return(0); 805 } 806 807 #ifndef _SYS_SYSPROTO_H_ 808 struct msgrcv_args { 809 int msqid; 810 void *msgp; 811 size_t msgsz; 812 long msgtyp; 813 int msgflg; 814 }; 815 #endif 816 817 int 818 msgrcv(p, uap) 819 struct proc *p; 820 register struct msgrcv_args *uap; 821 { 822 int msqid = uap->msqid; 823 void *user_msgp = uap->msgp; 824 size_t msgsz = uap->msgsz; 825 long msgtyp = uap->msgtyp; 826 int msgflg = uap->msgflg; 827 size_t len; 828 register struct msqid_ds *msqptr; 829 register struct msg *msghdr; 830 int eval; 831 short next; 832 833 #ifdef MSG_DEBUG_OK 834 printf("call to msgrcv(%d, 0x%x, %d, %ld, %d)\n", msqid, user_msgp, 835 msgsz, msgtyp, msgflg); 836 #endif 837 838 msqid = IPCID_TO_IX(msqid); 839 840 if (msqid < 0 || msqid >= msginfo.msgmni) { 841 #ifdef MSG_DEBUG_OK 842 printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 843 msginfo.msgmni); 844 #endif 845 return(EINVAL); 846 } 847 848 msqptr = &msqids[msqid]; 849 if (msqptr->msg_qbytes == 0) { 850 #ifdef MSG_DEBUG_OK 851 printf("no such message queue id\n"); 852 #endif 853 return(EINVAL); 854 } 855 if (msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 856 #ifdef MSG_DEBUG_OK 857 printf("wrong sequence number\n"); 858 #endif 859 return(EINVAL); 860 } 861 862 if ((eval = ipcperm(p, &msqptr->msg_perm, IPC_R))) { 863 #ifdef MSG_DEBUG_OK 864 printf("requester doesn't have read access\n"); 865 #endif 866 return(eval); 867 } 868 869 msghdr = NULL; 870 while (msghdr == NULL) { 871 if (msgtyp == 0) { 872 msghdr = msqptr->msg_first; 873 if (msghdr != NULL) { 874 if (msgsz < msghdr->msg_ts && 875 (msgflg & MSG_NOERROR) == 0) { 876 #ifdef MSG_DEBUG_OK 877 printf("first message on the queue is too big (want %d, got %d)\n", 878 msgsz, msghdr->msg_ts); 879 #endif 880 return(E2BIG); 881 } 882 if (msqptr->msg_first == msqptr->msg_last) { 883 msqptr->msg_first = NULL; 884 msqptr->msg_last = NULL; 885 } else { 886 msqptr->msg_first = msghdr->msg_next; 887 if (msqptr->msg_first == NULL) 888 panic("msg_first/last screwed up #1"); 889 } 890 } 891 } else { 892 struct msg *previous; 893 struct msg **prev; 894 895 previous = NULL; 896 prev = &(msqptr->msg_first); 897 while ((msghdr = *prev) != NULL) { 898 /* 899 * Is this message's type an exact match or is 900 * this message's type less than or equal to 901 * the absolute value of a negative msgtyp? 902 * Note that the second half of this test can 903 * NEVER be true if msgtyp is positive since 904 * msg_type is always positive! 905 */ 906 907 if (msgtyp == msghdr->msg_type || 908 msghdr->msg_type <= -msgtyp) { 909 #ifdef MSG_DEBUG_OK 910 printf("found message type %d, requested %d\n", 911 msghdr->msg_type, msgtyp); 912 #endif 913 if (msgsz < msghdr->msg_ts && 914 (msgflg & MSG_NOERROR) == 0) { 915 #ifdef MSG_DEBUG_OK 916 printf("requested message on the queue is too big (want %d, got %d)\n", 917 msgsz, msghdr->msg_ts); 918 #endif 919 return(E2BIG); 920 } 921 *prev = msghdr->msg_next; 922 if (msghdr == msqptr->msg_last) { 923 if (previous == NULL) { 924 if (prev != 925 &msqptr->msg_first) 926 panic("msg_first/last screwed up #2"); 927 msqptr->msg_first = 928 NULL; 929 msqptr->msg_last = 930 NULL; 931 } else { 932 if (prev == 933 &msqptr->msg_first) 934 panic("msg_first/last screwed up #3"); 935 msqptr->msg_last = 936 previous; 937 } 938 } 939 break; 940 } 941 previous = msghdr; 942 prev = &(msghdr->msg_next); 943 } 944 } 945 946 /* 947 * We've either extracted the msghdr for the appropriate 948 * message or there isn't one. 949 * If there is one then bail out of this loop. 950 */ 951 952 if (msghdr != NULL) 953 break; 954 955 /* 956 * Hmph! No message found. Does the user want to wait? 957 */ 958 959 if ((msgflg & IPC_NOWAIT) != 0) { 960 #ifdef MSG_DEBUG_OK 961 printf("no appropriate message found (msgtyp=%d)\n", 962 msgtyp); 963 #endif 964 /* The SVID says to return ENOMSG. */ 965 #ifdef ENOMSG 966 return(ENOMSG); 967 #else 968 /* Unfortunately, BSD doesn't define that code yet! */ 969 return(EAGAIN); 970 #endif 971 } 972 973 /* 974 * Wait for something to happen 975 */ 976 977 #ifdef MSG_DEBUG_OK 978 printf("msgrcv: goodnight\n"); 979 #endif 980 eval = tsleep((caddr_t)msqptr, (PZERO - 4) | PCATCH, "msgwait", 981 0); 982 #ifdef MSG_DEBUG_OK 983 printf("msgrcv: good morning (eval=%d)\n", eval); 984 #endif 985 986 if (eval != 0) { 987 #ifdef MSG_DEBUG_OK 988 printf("msgsnd: interrupted system call\n"); 989 #endif 990 return(EINTR); 991 } 992 993 /* 994 * Make sure that the msq queue still exists 995 */ 996 997 if (msqptr->msg_qbytes == 0 || 998 msqptr->msg_perm.seq != IPCID_TO_SEQ(uap->msqid)) { 999 #ifdef MSG_DEBUG_OK 1000 printf("msqid deleted\n"); 1001 #endif 1002 return(EIDRM); 1003 } 1004 } 1005 1006 /* 1007 * Return the message to the user. 1008 * 1009 * First, do the bookkeeping (before we risk being interrupted). 1010 */ 1011 1012 msqptr->msg_cbytes -= msghdr->msg_ts; 1013 msqptr->msg_qnum--; 1014 msqptr->msg_lrpid = p->p_pid; 1015 msqptr->msg_rtime = time_second; 1016 1017 /* 1018 * Make msgsz the actual amount that we'll be returning. 1019 * Note that this effectively truncates the message if it is too long 1020 * (since msgsz is never increased). 1021 */ 1022 1023 #ifdef MSG_DEBUG_OK 1024 printf("found a message, msgsz=%d, msg_ts=%d\n", msgsz, 1025 msghdr->msg_ts); 1026 #endif 1027 if (msgsz > msghdr->msg_ts) 1028 msgsz = msghdr->msg_ts; 1029 1030 /* 1031 * Return the type to the user. 1032 */ 1033 1034 eval = copyout((caddr_t)&(msghdr->msg_type), user_msgp, 1035 sizeof(msghdr->msg_type)); 1036 if (eval != 0) { 1037 #ifdef MSG_DEBUG_OK 1038 printf("error (%d) copying out message type\n", eval); 1039 #endif 1040 msg_freehdr(msghdr); 1041 wakeup((caddr_t)msqptr); 1042 return(eval); 1043 } 1044 user_msgp = (char *)user_msgp + sizeof(msghdr->msg_type); 1045 1046 /* 1047 * Return the segments to the user 1048 */ 1049 1050 next = msghdr->msg_spot; 1051 for (len = 0; len < msgsz; len += msginfo.msgssz) { 1052 size_t tlen; 1053 1054 if (msgsz - len > msginfo.msgssz) 1055 tlen = msginfo.msgssz; 1056 else 1057 tlen = msgsz - len; 1058 if (next <= -1) 1059 panic("next too low #3"); 1060 if (next >= msginfo.msgseg) 1061 panic("next out of range #3"); 1062 eval = copyout((caddr_t)&msgpool[next * msginfo.msgssz], 1063 user_msgp, tlen); 1064 if (eval != 0) { 1065 #ifdef MSG_DEBUG_OK 1066 printf("error (%d) copying out message segment\n", 1067 eval); 1068 #endif 1069 msg_freehdr(msghdr); 1070 wakeup((caddr_t)msqptr); 1071 return(eval); 1072 } 1073 user_msgp = (char *)user_msgp + tlen; 1074 next = msgmaps[next].next; 1075 } 1076 1077 /* 1078 * Done, return the actual number of bytes copied out. 1079 */ 1080 1081 msg_freehdr(msghdr); 1082 wakeup((caddr_t)msqptr); 1083 p->p_retval[0] = msgsz; 1084 return(0); 1085 } 1086