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