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