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