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