1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2011 NetApp, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * Copyright 2018 Joyent, Inc. 31 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. 32 */ 33 34 /* 35 * Micro event library for FreeBSD, designed for a single i/o thread 36 * using kqueue, and having events be persistent by default. 37 */ 38 39 #include <sys/cdefs.h> 40 41 #include <assert.h> 42 #ifndef WITHOUT_CAPSICUM 43 #include <capsicum_helpers.h> 44 #endif 45 #include <err.h> 46 #include <errno.h> 47 #include <stdbool.h> 48 #include <stdlib.h> 49 #include <stdio.h> 50 #include <string.h> 51 #include <sysexits.h> 52 #include <unistd.h> 53 54 #include <sys/types.h> 55 #ifndef WITHOUT_CAPSICUM 56 #include <sys/capsicum.h> 57 #endif 58 #ifdef __FreeBSD__ 59 #include <sys/event.h> 60 #else 61 #include <port.h> 62 #include <sys/poll.h> 63 #include <sys/siginfo.h> 64 #include <sys/queue.h> 65 #include <sys/debug.h> 66 #include <sys/stat.h> 67 #endif 68 #include <sys/time.h> 69 70 #include <pthread.h> 71 #include <pthread_np.h> 72 73 #include "mevent.h" 74 75 #define MEVENT_MAX 64 76 77 #ifndef __FreeBSD__ 78 #define EV_ENABLE 0x01 79 #define EV_ADD EV_ENABLE 80 #define EV_DISABLE 0x02 81 #define EV_DELETE 0x04 82 83 static int mevent_file_poll_interval_ms = 5000; 84 #endif 85 86 static pthread_t mevent_tid; 87 static pthread_once_t mevent_once = PTHREAD_ONCE_INIT; 88 #ifdef __FreeBSD__ 89 static int mevent_timid = 43; 90 #endif 91 static int mevent_pipefd[2]; 92 static int mfd; 93 static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER; 94 95 struct mevent { 96 void (*me_func)(int, enum ev_type, void *); 97 #define me_msecs me_fd 98 int me_fd; 99 #ifdef __FreeBSD__ 100 int me_timid; 101 #else 102 timer_t me_timid; 103 #endif 104 enum ev_type me_type; 105 void *me_param; 106 int me_cq; 107 int me_state; /* Desired kevent flags. */ 108 int me_closefd; 109 int me_fflags; 110 #ifndef __FreeBSD__ 111 port_notify_t me_notify; 112 struct sigevent me_sigev; 113 boolean_t me_auto_requeue; 114 struct { 115 int mp_fd; 116 off_t mp_size; 117 void (*mp_func)(int, enum ev_type, void *); 118 void *mp_param; 119 } me_poll; 120 #endif 121 LIST_ENTRY(mevent) me_list; 122 }; 123 124 static LIST_HEAD(listhead, mevent) global_head, change_head; 125 126 static void 127 mevent_qlock(void) 128 { 129 pthread_mutex_lock(&mevent_lmutex); 130 } 131 132 static void 133 mevent_qunlock(void) 134 { 135 pthread_mutex_unlock(&mevent_lmutex); 136 } 137 138 static void 139 mevent_pipe_read(int fd, enum ev_type type __unused, void *param __unused) 140 { 141 char buf[MEVENT_MAX]; 142 int status; 143 144 /* 145 * Drain the pipe read side. The fd is non-blocking so this is 146 * safe to do. 147 */ 148 do { 149 status = read(fd, buf, sizeof(buf)); 150 } while (status == MEVENT_MAX); 151 } 152 153 static void 154 mevent_notify(void) 155 { 156 char c = '\0'; 157 158 /* 159 * If calling from outside the i/o thread, write a byte on the 160 * pipe to force the i/o thread to exit the blocking kevent call. 161 */ 162 if (mevent_pipefd[1] != 0 && pthread_self() != mevent_tid) { 163 write(mevent_pipefd[1], &c, 1); 164 } 165 } 166 167 static void 168 mevent_init(void) 169 { 170 #ifndef WITHOUT_CAPSICUM 171 cap_rights_t rights; 172 #endif 173 174 #ifdef __FreeBSD__ 175 mfd = kqueue(); 176 #else 177 mfd = port_create(); 178 #endif 179 assert(mfd > 0); 180 181 #ifndef WITHOUT_CAPSICUM 182 cap_rights_init(&rights, CAP_KQUEUE); 183 if (caph_rights_limit(mfd, &rights) == -1) 184 errx(EX_OSERR, "Unable to apply rights for sandbox"); 185 #endif 186 187 LIST_INIT(&change_head); 188 LIST_INIT(&global_head); 189 } 190 191 192 #ifdef __FreeBSD__ 193 static int 194 mevent_kq_filter(struct mevent *mevp) 195 { 196 int retval; 197 198 retval = 0; 199 200 if (mevp->me_type == EVF_READ) 201 retval = EVFILT_READ; 202 203 if (mevp->me_type == EVF_WRITE) 204 retval = EVFILT_WRITE; 205 206 if (mevp->me_type == EVF_TIMER) 207 retval = EVFILT_TIMER; 208 209 if (mevp->me_type == EVF_SIGNAL) 210 retval = EVFILT_SIGNAL; 211 212 if (mevp->me_type == EVF_VNODE) 213 retval = EVFILT_VNODE; 214 215 return (retval); 216 } 217 218 static int 219 mevent_kq_flags(struct mevent *mevp) 220 { 221 int retval; 222 223 retval = mevp->me_state; 224 225 if (mevp->me_type == EVF_VNODE) 226 retval |= EV_CLEAR; 227 228 return (retval); 229 } 230 231 static int 232 mevent_kq_fflags(struct mevent *mevp) 233 { 234 int retval; 235 236 retval = 0; 237 238 switch (mevp->me_type) { 239 case EVF_VNODE: 240 if ((mevp->me_fflags & EVFF_ATTRIB) != 0) 241 retval |= NOTE_ATTRIB; 242 break; 243 case EVF_READ: 244 case EVF_WRITE: 245 case EVF_TIMER: 246 case EVF_SIGNAL: 247 break; 248 } 249 250 return (retval); 251 } 252 253 static void 254 mevent_populate(struct mevent *mevp, struct kevent *kev) 255 { 256 if (mevp->me_type == EVF_TIMER) { 257 kev->ident = mevp->me_timid; 258 kev->data = mevp->me_msecs; 259 } else { 260 kev->ident = mevp->me_fd; 261 kev->data = 0; 262 } 263 kev->filter = mevent_kq_filter(mevp); 264 kev->flags = mevent_kq_flags(mevp); 265 kev->fflags = mevent_kq_fflags(mevp); 266 kev->udata = mevp; 267 } 268 269 static int 270 mevent_build(struct kevent *kev) 271 { 272 struct mevent *mevp, *tmpp; 273 int i; 274 275 i = 0; 276 277 mevent_qlock(); 278 279 LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) { 280 if (mevp->me_closefd) { 281 /* 282 * A close of the file descriptor will remove the 283 * event 284 */ 285 close(mevp->me_fd); 286 } else { 287 assert((mevp->me_state & EV_ADD) == 0); 288 mevent_populate(mevp, &kev[i]); 289 i++; 290 } 291 292 mevp->me_cq = 0; 293 LIST_REMOVE(mevp, me_list); 294 295 if (mevp->me_state & EV_DELETE) { 296 free(mevp); 297 } else { 298 LIST_INSERT_HEAD(&global_head, mevp, me_list); 299 } 300 301 assert(i < MEVENT_MAX); 302 } 303 304 mevent_qunlock(); 305 306 return (i); 307 } 308 309 static void 310 mevent_handle(struct kevent *kev, int numev) 311 { 312 struct mevent *mevp; 313 int i; 314 315 for (i = 0; i < numev; i++) { 316 mevp = kev[i].udata; 317 318 /* XXX check for EV_ERROR ? */ 319 320 (*mevp->me_func)(mevp->me_fd, mevp->me_type, mevp->me_param); 321 } 322 } 323 324 #else /* __FreeBSD__ */ 325 326 static boolean_t 327 mevent_clarify_state(struct mevent *mevp) 328 { 329 const int state = mevp->me_state; 330 331 if ((state & EV_DELETE) != 0) { 332 /* All other intents are overriden by delete. */ 333 mevp->me_state = EV_DELETE; 334 return (B_TRUE); 335 } 336 337 /* 338 * Without a distinction between EV_ADD and EV_ENABLE in our emulation, 339 * handling the add-disabled case means eliding the portfs operation 340 * when both flags are present. 341 * 342 * This is not a concern for subsequent enable/disable operations, as 343 * mevent_update() toggles the flags properly so they are not left in 344 * conflict. 345 */ 346 if (state == (EV_ENABLE|EV_DISABLE)) { 347 mevp->me_state = EV_DISABLE; 348 return (B_FALSE); 349 } 350 351 return (B_TRUE); 352 } 353 354 static void 355 mevent_poll_file_attrib(int fd, enum ev_type type, void *param) 356 { 357 struct mevent *mevp = param; 358 struct stat st; 359 360 if (fstat(mevp->me_poll.mp_fd, &st) != 0) { 361 (void) fprintf(stderr, "%s: fstat(%d) failed: %s\n", 362 __func__, fd, strerror(errno)); 363 return; 364 } 365 366 /* 367 * The only current consumer of file attribute monitoring is 368 * blockif, which wants to know about size changes. 369 */ 370 if (mevp->me_poll.mp_size != st.st_size) { 371 mevp->me_poll.mp_size = st.st_size; 372 373 (*mevp->me_poll.mp_func)(mevp->me_poll.mp_fd, EVF_VNODE, 374 mevp->me_poll.mp_param); 375 } 376 } 377 378 static void 379 mevent_update_one_readwrite(struct mevent *mevp) 380 { 381 int portfd = mevp->me_notify.portnfy_port; 382 383 mevp->me_auto_requeue = B_FALSE; 384 385 switch (mevp->me_state) { 386 case EV_ENABLE: 387 { 388 const int events = (mevp->me_type == EVF_READ) ? 389 POLLIN : POLLOUT; 390 391 if (port_associate(portfd, PORT_SOURCE_FD, mevp->me_fd, 392 events, mevp) != 0) { 393 (void) fprintf(stderr, 394 "port_associate fd %d %p failed: %s\n", 395 mevp->me_fd, mevp, strerror(errno)); 396 } 397 return; 398 } 399 case EV_DISABLE: 400 case EV_DELETE: 401 /* 402 * A disable that comes in while an event is being 403 * handled will result in an ENOENT. 404 */ 405 if (port_dissociate(portfd, PORT_SOURCE_FD, 406 mevp->me_fd) != 0 && errno != ENOENT) { 407 (void) fprintf(stderr, "port_dissociate " 408 "portfd %d fd %d mevp %p failed: %s\n", 409 portfd, mevp->me_fd, mevp, strerror(errno)); 410 } 411 return; 412 default: 413 (void) fprintf(stderr, "%s: unhandled state %d\n", __func__, 414 mevp->me_state); 415 abort(); 416 } 417 } 418 419 static void 420 mevent_update_one_timer(struct mevent *mevp) 421 { 422 mevp->me_auto_requeue = B_TRUE; 423 424 switch (mevp->me_state) { 425 case EV_ENABLE: 426 { 427 struct itimerspec it = { 0 }; 428 429 mevp->me_sigev.sigev_notify = SIGEV_PORT; 430 mevp->me_sigev.sigev_value.sival_ptr = &mevp->me_notify; 431 432 if (timer_create(CLOCK_REALTIME, &mevp->me_sigev, 433 &mevp->me_timid) != 0) { 434 (void) fprintf(stderr, "timer_create failed: %s", 435 strerror(errno)); 436 return; 437 } 438 439 /* The first timeout */ 440 it.it_value.tv_sec = mevp->me_msecs / MILLISEC; 441 it.it_value.tv_nsec = 442 MSEC2NSEC(mevp->me_msecs % MILLISEC); 443 /* Repeat at the same interval */ 444 it.it_interval = it.it_value; 445 446 if (timer_settime(mevp->me_timid, 0, &it, NULL) != 0) { 447 (void) fprintf(stderr, "timer_settime failed: %s", 448 strerror(errno)); 449 } 450 return; 451 } 452 case EV_DISABLE: 453 case EV_DELETE: 454 if (timer_delete(mevp->me_timid) != 0) { 455 (void) fprintf(stderr, "timer_delete failed: %s", 456 strerror(errno)); 457 } 458 mevp->me_timid = -1; 459 return; 460 default: 461 (void) fprintf(stderr, "%s: unhandled state %d\n", __func__, 462 mevp->me_state); 463 abort(); 464 } 465 } 466 467 static void 468 mevent_update_one_vnode(struct mevent *mevp) 469 { 470 switch (mevp->me_state) { 471 case EV_ENABLE: 472 { 473 struct stat st; 474 int events = 0; 475 476 if ((mevp->me_fflags & EVFF_ATTRIB) != 0) 477 events |= FILE_ATTRIB; 478 479 assert(events != 0); 480 481 /* 482 * It is tempting to use the PORT_SOURCE_FILE type for this in 483 * conjunction with the FILE_ATTRIB event type. Unfortunately 484 * this event type triggers on any change to the file's 485 * ctime, and therefore for every write as well as attribute 486 * changes. It also does not work for ZVOLs. 487 * 488 * Convert this to a timer event and poll for the file 489 * attribute changes that we care about. 490 */ 491 492 if (fstat(mevp->me_fd, &st) != 0) { 493 (void) fprintf(stderr, "fstat(%d) failed: %s\n", 494 mevp->me_fd, strerror(errno)); 495 return; 496 } 497 498 mevp->me_poll.mp_fd = mevp->me_fd; 499 mevp->me_poll.mp_size = st.st_size; 500 501 mevp->me_poll.mp_func = mevp->me_func; 502 mevp->me_poll.mp_param = mevp->me_param; 503 mevp->me_func = mevent_poll_file_attrib; 504 mevp->me_param = mevp; 505 506 mevp->me_type = EVF_TIMER; 507 mevp->me_timid = -1; 508 mevp->me_msecs = mevent_file_poll_interval_ms; 509 mevent_update_one_timer(mevp); 510 511 return; 512 } 513 case EV_DISABLE: 514 case EV_DELETE: 515 /* 516 * These events do not really exist as they are converted to 517 * timers; fall through to abort. 518 */ 519 default: 520 (void) fprintf(stderr, "%s: unhandled state %d\n", __func__, 521 mevp->me_state); 522 abort(); 523 } 524 } 525 526 static void 527 mevent_update_one(struct mevent *mevp) 528 { 529 switch (mevp->me_type) { 530 case EVF_READ: 531 case EVF_WRITE: 532 mevent_update_one_readwrite(mevp); 533 break; 534 case EVF_TIMER: 535 mevent_update_one_timer(mevp); 536 break; 537 case EVF_VNODE: 538 mevent_update_one_vnode(mevp); 539 break; 540 case EVF_SIGNAL: /* EVF_SIGNAL not yet implemented. */ 541 default: 542 (void) fprintf(stderr, "%s: unhandled event type %d\n", 543 __func__, mevp->me_type); 544 abort(); 545 } 546 } 547 548 static void 549 mevent_populate(struct mevent *mevp) 550 { 551 mevp->me_notify.portnfy_port = mfd; 552 mevp->me_notify.portnfy_user = mevp; 553 } 554 555 static void 556 mevent_update_pending() 557 { 558 struct mevent *mevp, *tmpp; 559 560 mevent_qlock(); 561 562 LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) { 563 mevent_populate(mevp); 564 if (mevp->me_closefd) { 565 /* 566 * A close of the file descriptor will remove the 567 * event 568 */ 569 (void) close(mevp->me_fd); 570 mevp->me_fd = -1; 571 } else { 572 if (mevent_clarify_state(mevp)) { 573 mevent_update_one(mevp); 574 } 575 } 576 577 mevp->me_cq = 0; 578 LIST_REMOVE(mevp, me_list); 579 580 if (mevp->me_state & EV_DELETE) { 581 free(mevp); 582 } else { 583 LIST_INSERT_HEAD(&global_head, mevp, me_list); 584 } 585 } 586 587 mevent_qunlock(); 588 } 589 590 static void 591 mevent_handle_pe(port_event_t *pe) 592 { 593 struct mevent *mevp = pe->portev_user; 594 595 (*mevp->me_func)(mevp->me_fd, mevp->me_type, mevp->me_param); 596 597 mevent_qlock(); 598 if (!mevp->me_cq && !mevp->me_auto_requeue) { 599 mevent_update_one(mevp); 600 } 601 mevent_qunlock(); 602 } 603 #endif 604 605 static struct mevent * 606 mevent_add_state(int tfd, enum ev_type type, 607 void (*func)(int, enum ev_type, void *), void *param, 608 int state, int fflags) 609 { 610 #ifdef __FreeBSD__ 611 struct kevent kev; 612 #endif 613 struct mevent *lp, *mevp; 614 #ifdef __FreeBSD__ 615 int ret; 616 #endif 617 618 if (tfd < 0 || func == NULL) { 619 return (NULL); 620 } 621 622 mevp = NULL; 623 624 pthread_once(&mevent_once, mevent_init); 625 626 mevent_qlock(); 627 628 /* 629 * Verify that the fd/type tuple is not present in any list 630 */ 631 LIST_FOREACH(lp, &global_head, me_list) { 632 if (type != EVF_TIMER && lp->me_fd == tfd && 633 lp->me_type == type) { 634 goto exit; 635 } 636 } 637 638 LIST_FOREACH(lp, &change_head, me_list) { 639 if (type != EVF_TIMER && lp->me_fd == tfd && 640 lp->me_type == type) { 641 goto exit; 642 } 643 } 644 645 /* 646 * Allocate an entry and populate it. 647 */ 648 mevp = calloc(1, sizeof(struct mevent)); 649 if (mevp == NULL) { 650 goto exit; 651 } 652 653 if (type == EVF_TIMER) { 654 mevp->me_msecs = tfd; 655 #ifdef __FreeBSD__ 656 mevp->me_timid = mevent_timid++; 657 #else 658 mevp->me_timid = -1; 659 #endif 660 } else 661 mevp->me_fd = tfd; 662 mevp->me_type = type; 663 mevp->me_func = func; 664 mevp->me_param = param; 665 mevp->me_state = state; 666 mevp->me_fflags = fflags; 667 668 /* 669 * Try to add the event. If this fails, report the failure to 670 * the caller. 671 */ 672 #ifdef __FreeBSD__ 673 mevent_populate(mevp, &kev); 674 ret = kevent(mfd, &kev, 1, NULL, 0, NULL); 675 if (ret == -1) { 676 free(mevp); 677 mevp = NULL; 678 goto exit; 679 } 680 mevp->me_state &= ~EV_ADD; 681 #else 682 mevent_populate(mevp); 683 if (mevent_clarify_state(mevp)) 684 mevent_update_one(mevp); 685 #endif 686 687 LIST_INSERT_HEAD(&global_head, mevp, me_list); 688 689 exit: 690 mevent_qunlock(); 691 692 return (mevp); 693 } 694 695 struct mevent * 696 mevent_add(int tfd, enum ev_type type, 697 void (*func)(int, enum ev_type, void *), void *param) 698 { 699 700 return (mevent_add_state(tfd, type, func, param, EV_ADD, 0)); 701 } 702 703 struct mevent * 704 mevent_add_flags(int tfd, enum ev_type type, int fflags, 705 void (*func)(int, enum ev_type, void *), void *param) 706 { 707 708 return (mevent_add_state(tfd, type, func, param, EV_ADD, fflags)); 709 } 710 711 struct mevent * 712 mevent_add_disabled(int tfd, enum ev_type type, 713 void (*func)(int, enum ev_type, void *), void *param) 714 { 715 716 return (mevent_add_state(tfd, type, func, param, EV_ADD | EV_DISABLE, 0)); 717 } 718 719 static int 720 mevent_update(struct mevent *evp, bool enable) 721 { 722 int newstate; 723 724 mevent_qlock(); 725 726 /* 727 * It's not possible to enable/disable a deleted event 728 */ 729 assert((evp->me_state & EV_DELETE) == 0); 730 731 newstate = evp->me_state; 732 if (enable) { 733 newstate |= EV_ENABLE; 734 newstate &= ~EV_DISABLE; 735 } else { 736 newstate |= EV_DISABLE; 737 newstate &= ~EV_ENABLE; 738 } 739 740 /* 741 * No update needed if state isn't changing 742 */ 743 if (evp->me_state != newstate) { 744 evp->me_state = newstate; 745 746 /* 747 * Place the entry onto the changed list if not 748 * already there. 749 */ 750 if (evp->me_cq == 0) { 751 evp->me_cq = 1; 752 LIST_REMOVE(evp, me_list); 753 LIST_INSERT_HEAD(&change_head, evp, me_list); 754 mevent_notify(); 755 } 756 } 757 758 mevent_qunlock(); 759 760 return (0); 761 } 762 763 int 764 mevent_enable(struct mevent *evp) 765 { 766 767 return (mevent_update(evp, true)); 768 } 769 770 int 771 mevent_disable(struct mevent *evp) 772 { 773 774 return (mevent_update(evp, false)); 775 } 776 777 static int 778 mevent_delete_event(struct mevent *evp, int closefd) 779 { 780 mevent_qlock(); 781 782 /* 783 * Place the entry onto the changed list if not already there, and 784 * mark as to be deleted. 785 */ 786 if (evp->me_cq == 0) { 787 evp->me_cq = 1; 788 LIST_REMOVE(evp, me_list); 789 LIST_INSERT_HEAD(&change_head, evp, me_list); 790 mevent_notify(); 791 } 792 evp->me_state = EV_DELETE; 793 794 if (closefd) 795 evp->me_closefd = 1; 796 797 mevent_qunlock(); 798 799 return (0); 800 } 801 802 int 803 mevent_delete(struct mevent *evp) 804 { 805 806 return (mevent_delete_event(evp, 0)); 807 } 808 809 int 810 mevent_delete_close(struct mevent *evp) 811 { 812 813 return (mevent_delete_event(evp, 1)); 814 } 815 816 static void 817 mevent_set_name(void) 818 { 819 820 pthread_set_name_np(mevent_tid, "mevent"); 821 } 822 823 void 824 mevent_dispatch(void) 825 { 826 #ifdef __FreeBSD__ 827 struct kevent changelist[MEVENT_MAX]; 828 struct kevent eventlist[MEVENT_MAX]; 829 struct mevent *pipev; 830 int numev; 831 #else 832 struct mevent *pipev; 833 #endif 834 int ret; 835 #ifndef WITHOUT_CAPSICUM 836 cap_rights_t rights; 837 #endif 838 839 mevent_tid = pthread_self(); 840 mevent_set_name(); 841 842 pthread_once(&mevent_once, mevent_init); 843 844 /* 845 * Open the pipe that will be used for other threads to force 846 * the blocking kqueue call to exit by writing to it. Set the 847 * descriptor to non-blocking. 848 */ 849 ret = pipe(mevent_pipefd); 850 if (ret < 0) { 851 perror("pipe"); 852 exit(0); 853 } 854 855 #ifndef WITHOUT_CAPSICUM 856 cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE); 857 if (caph_rights_limit(mevent_pipefd[0], &rights) == -1) 858 errx(EX_OSERR, "Unable to apply rights for sandbox"); 859 if (caph_rights_limit(mevent_pipefd[1], &rights) == -1) 860 errx(EX_OSERR, "Unable to apply rights for sandbox"); 861 #endif 862 863 /* 864 * Add internal event handler for the pipe write fd 865 */ 866 pipev = mevent_add(mevent_pipefd[0], EVF_READ, mevent_pipe_read, NULL); 867 assert(pipev != NULL); 868 869 for (;;) { 870 #ifdef __FreeBSD__ 871 /* 872 * Build changelist if required. 873 * XXX the changelist can be put into the blocking call 874 * to eliminate the extra syscall. Currently better for 875 * debug. 876 */ 877 numev = mevent_build(changelist); 878 if (numev) { 879 ret = kevent(mfd, changelist, numev, NULL, 0, NULL); 880 if (ret == -1) { 881 perror("Error return from kevent change"); 882 } 883 } 884 885 /* 886 * Block awaiting events 887 */ 888 ret = kevent(mfd, NULL, 0, eventlist, MEVENT_MAX, NULL); 889 if (ret == -1 && errno != EINTR) { 890 perror("Error return from kevent monitor"); 891 } 892 893 /* 894 * Handle reported events 895 */ 896 mevent_handle(eventlist, ret); 897 898 #else /* __FreeBSD__ */ 899 port_event_t pev; 900 901 /* Handle any pending updates */ 902 mevent_update_pending(); 903 904 /* Block awaiting events */ 905 ret = port_get(mfd, &pev, NULL); 906 if (ret != 0) { 907 if (errno != EINTR) 908 perror("Error return from port_get"); 909 continue; 910 } 911 912 /* Handle reported event */ 913 mevent_handle_pe(&pev); 914 #endif /* __FreeBSD__ */ 915 } 916 } 917