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