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