sysv_msg.c revision 68024
1/* $FreeBSD: head/sys/kern/sysv_msg.c 68024 2000-10-31 01:34:00Z rwatson $ */ 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 35static MALLOC_DEFINE(M_MSG, "msg", "SVID compatible message queues"); 36 37static void msginit __P((void *)); 38 39#define MSG_DEBUG 40#undef MSG_DEBUG_OK 41 42static void msg_freehdr __P((struct msg *msghdr)); 43 44/* XXX casting to (sy_call_t *) is bogus, as usual. */ 45static sy_call_t *msgcalls[] = { 46 (sy_call_t *)msgctl, (sy_call_t *)msgget, 47 (sy_call_t *)msgsnd, (sy_call_t *)msgrcv 48}; 49 50struct 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 */ 87struct 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 109struct 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 117static int nfree_msgmaps; /* # of free map entries */ 118static short free_msgmaps; /* head of linked list of free map entries */ 119static struct msg *free_msghdrs;/* list of free msg headers */ 120static char *msgpool; /* MSGMAX byte long msg buffer pool */ 121static struct msgmap *msgmaps; /* MSGSEG msgmap structures */ 122static struct msg *msghdrs; /* MSGTQL msg headers */ 123static struct msqid_ds *msqids; /* MSGMNI msqid_ds struct's */ 124 125static void 126msginit(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} 195SYSINIT(sysv_msg, SI_SUB_SYSV_MSG, SI_ORDER_FIRST, msginit, NULL) 196 197/* 198 * Entry point for all MSG calls 199 */ 200int 201msgsys(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 222static void 223msg_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_ 247struct msgctl_args { 248 int msqid; 249 int cmd; 250 struct msqid_ds *buf; 251}; 252#endif 253 254int 255msgctl(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_ 388struct msgget_args { 389 key_t key; 390 int msgflg; 391}; 392#endif 393 394int 395msgget(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 490found: 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_ 497struct msgsnd_args { 498 int msqid; 499 void *msgp; 500 size_t msgsz; 501 int msgflg; 502}; 503#endif 504 505int 506msgsnd(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_ 821struct msgrcv_args { 822 int msqid; 823 void *msgp; 824 size_t msgsz; 825 long msgtyp; 826 int msgflg; 827}; 828#endif 829 830int 831msgrcv(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