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