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