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 return (mevp->me_state); 217 } 218 219 static int 220 mevent_kq_fflags(struct mevent *mevp) 221 { 222 int retval; 223 224 retval = 0; 225 226 switch (mevp->me_type) { 227 case EVF_VNODE: 228 if ((mevp->me_fflags & EVFF_ATTRIB) != 0) 229 retval |= NOTE_ATTRIB; 230 break; 231 } 232 233 return (retval); 234 } 235 236 static void 237 mevent_populate(struct mevent *mevp, struct kevent *kev) 238 { 239 if (mevp->me_type == EVF_TIMER) { 240 kev->ident = mevp->me_timid; 241 kev->data = mevp->me_msecs; 242 } else { 243 kev->ident = mevp->me_fd; 244 kev->data = 0; 245 } 246 kev->filter = mevent_kq_filter(mevp); 247 kev->flags = mevent_kq_flags(mevp); 248 kev->fflags = mevent_kq_fflags(mevp); 249 kev->udata = mevp; 250 } 251 252 static int 253 mevent_build(struct kevent *kev) 254 { 255 struct mevent *mevp, *tmpp; 256 int i; 257 258 i = 0; 259 260 mevent_qlock(); 261 262 LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) { 263 if (mevp->me_closefd) { 264 /* 265 * A close of the file descriptor will remove the 266 * event 267 */ 268 close(mevp->me_fd); 269 } else { 270 assert((mevp->me_state & EV_ADD) == 0); 271 mevent_populate(mevp, &kev[i]); 272 i++; 273 } 274 275 mevp->me_cq = 0; 276 LIST_REMOVE(mevp, me_list); 277 278 if (mevp->me_state & EV_DELETE) { 279 free(mevp); 280 } else { 281 LIST_INSERT_HEAD(&global_head, mevp, me_list); 282 } 283 284 assert(i < MEVENT_MAX); 285 } 286 287 mevent_qunlock(); 288 289 return (i); 290 } 291 292 static void 293 mevent_handle(struct kevent *kev, int numev) 294 { 295 struct mevent *mevp; 296 int i; 297 298 for (i = 0; i < numev; i++) { 299 mevp = kev[i].udata; 300 301 /* XXX check for EV_ERROR ? */ 302 303 (*mevp->me_func)(mevp->me_fd, mevp->me_type, mevp->me_param); 304 } 305 } 306 307 #else /* __FreeBSD__ */ 308 309 static boolean_t 310 mevent_clarify_state(struct mevent *mevp) 311 { 312 const int state = mevp->me_state; 313 314 if ((state & EV_DELETE) != 0) { 315 /* All other intents are overriden by delete. */ 316 mevp->me_state = EV_DELETE; 317 return (B_TRUE); 318 } 319 320 /* 321 * Without a distinction between EV_ADD and EV_ENABLE in our emulation, 322 * handling the add-disabled case means eliding the portfs operation 323 * when both flags are present. 324 * 325 * This is not a concern for subsequent enable/disable operations, as 326 * mevent_update() toggles the flags properly so they are not left in 327 * conflict. 328 */ 329 if (state == (EV_ENABLE|EV_DISABLE)) { 330 mevp->me_state = EV_DISABLE; 331 return (B_FALSE); 332 } 333 334 return (B_TRUE); 335 } 336 337 static char * 338 mevent_fdpath(int fd) 339 { 340 prfdinfo_t *fdinfo; 341 char *path; 342 size_t len; 343 344 fdinfo = proc_get_fdinfo(getpid(), fd); 345 if (fdinfo == NULL) { 346 (void) fprintf(stderr, "%s: proc_get_fdinfo(%d) failed: %s\n", 347 __func__, fd, strerror(errno)); 348 path = NULL; 349 } else { 350 path = (char *)proc_fdinfo_misc(fdinfo, PR_PATHNAME, &len); 351 } 352 353 if (path == NULL) { 354 (void) fprintf(stderr, "%s: Fall back to /proc/self/fd/%d\n", 355 __func__, fd); 356 (void) asprintf(&path, "/proc/self/fd/%d", fd); 357 } else { 358 path = strdup(path); 359 } 360 361 proc_fdinfo_free(fdinfo); 362 363 if (path == NULL) { 364 (void) fprintf(stderr, 365 "%s: Error building path for fd %d: %s\n", __func__, 366 fd, strerror(errno)); 367 } 368 369 return (path); 370 } 371 372 static void 373 mevent_update_one(struct mevent *mevp) 374 { 375 int portfd = mevp->me_notify.portnfy_port; 376 377 switch (mevp->me_type) { 378 case EVF_READ: 379 case EVF_WRITE: 380 mevp->me_auto_requeue = B_FALSE; 381 382 switch (mevp->me_state) { 383 case EV_ENABLE: 384 { 385 int events; 386 387 events = (mevp->me_type == EVF_READ) ? POLLIN : POLLOUT; 388 389 if (port_associate(portfd, PORT_SOURCE_FD, mevp->me_fd, 390 events, mevp) != 0) { 391 (void) fprintf(stderr, 392 "port_associate fd %d %p failed: %s\n", 393 mevp->me_fd, mevp, strerror(errno)); 394 } 395 return; 396 } 397 case EV_DISABLE: 398 case EV_DELETE: 399 /* 400 * A disable that comes in while an event is being 401 * handled will result in an ENOENT. 402 */ 403 if (port_dissociate(portfd, PORT_SOURCE_FD, 404 mevp->me_fd) != 0 && errno != ENOENT) { 405 (void) fprintf(stderr, "port_dissociate " 406 "portfd %d fd %d mevp %p failed: %s\n", 407 portfd, mevp->me_fd, mevp, strerror(errno)); 408 } 409 return; 410 default: 411 goto abort; 412 } 413 414 case EVF_TIMER: 415 mevp->me_auto_requeue = B_TRUE; 416 417 switch (mevp->me_state) { 418 case EV_ENABLE: 419 { 420 struct itimerspec it = { 0 }; 421 422 mevp->me_sigev.sigev_notify = SIGEV_PORT; 423 mevp->me_sigev.sigev_value.sival_ptr = &mevp->me_notify; 424 425 if (timer_create(CLOCK_REALTIME, &mevp->me_sigev, 426 &mevp->me_timid) != 0) { 427 (void) fprintf(stderr, 428 "timer_create failed: %s", strerror(errno)); 429 return; 430 } 431 432 /* The first timeout */ 433 it.it_value.tv_sec = mevp->me_msecs / MILLISEC; 434 it.it_value.tv_nsec = 435 MSEC2NSEC(mevp->me_msecs % MILLISEC); 436 /* Repeat at the same interval */ 437 it.it_interval = it.it_value; 438 439 if (timer_settime(mevp->me_timid, 0, &it, NULL) != 0) { 440 (void) fprintf(stderr, "timer_settime failed: " 441 "%s", strerror(errno)); 442 } 443 return; 444 } 445 case EV_DISABLE: 446 case EV_DELETE: 447 if (timer_delete(mevp->me_timid) != 0) { 448 (void) fprintf(stderr, "timer_delete failed: " 449 "%s", strerror(errno)); 450 } 451 return; 452 default: 453 goto abort; 454 } 455 456 case EVF_VNODE: 457 mevp->me_auto_requeue = B_FALSE; 458 459 switch (mevp->me_state) { 460 case EV_ENABLE: 461 { 462 int events = 0; 463 464 if ((mevp->me_fflags & EVFF_ATTRIB) != 0) 465 events |= FILE_ATTRIB; 466 467 assert(events != 0); 468 469 if (mevp->me_fname == NULL) { 470 mevp->me_fname = mevent_fdpath(mevp->me_fd); 471 if (mevp->me_fname == NULL) 472 return; 473 } 474 475 bzero(&mevp->me_fobj, sizeof (mevp->me_fobj)); 476 mevp->me_fobj.fo_name = mevp->me_fname; 477 478 if (port_associate(portfd, PORT_SOURCE_FILE, 479 (uintptr_t)&mevp->me_fobj, events, mevp) != 0) { 480 (void) fprintf(stderr, 481 "port_associate fd %d (%s) %p failed: %s\n", 482 mevp->me_fd, mevp->me_fname, mevp, 483 strerror(errno)); 484 } 485 return; 486 } 487 case EV_DISABLE: 488 case EV_DELETE: 489 /* 490 * A disable that comes in while an event is being 491 * handled will result in an ENOENT. 492 */ 493 if (port_dissociate(portfd, PORT_SOURCE_FILE, 494 (uintptr_t)&mevp->me_fobj) != 0 && 495 errno != ENOENT) { 496 (void) fprintf(stderr, "port_dissociate " 497 "portfd %d fd %d mevp %p failed: %s\n", 498 portfd, mevp->me_fd, mevp, strerror(errno)); 499 } 500 free(mevp->me_fname); 501 mevp->me_fname = NULL; 502 return; 503 default: 504 goto abort; 505 } 506 507 default: 508 /* EVF_SIGNAL not yet implemented. */ 509 goto abort; 510 } 511 512 abort: 513 (void) fprintf(stderr, "%s: unhandled type %d state %d\n", __func__, 514 mevp->me_type, mevp->me_state); 515 abort(); 516 } 517 518 static void 519 mevent_populate(struct mevent *mevp) 520 { 521 mevp->me_notify.portnfy_port = mfd; 522 mevp->me_notify.portnfy_user = mevp; 523 } 524 525 static void 526 mevent_update_pending() 527 { 528 struct mevent *mevp, *tmpp; 529 530 mevent_qlock(); 531 532 LIST_FOREACH_SAFE(mevp, &change_head, me_list, tmpp) { 533 mevent_populate(mevp); 534 if (mevp->me_closefd) { 535 /* 536 * A close of the file descriptor will remove the 537 * event 538 */ 539 (void) close(mevp->me_fd); 540 mevp->me_fd = -1; 541 } else { 542 if (mevent_clarify_state(mevp)) { 543 mevent_update_one(mevp); 544 } 545 } 546 547 mevp->me_cq = 0; 548 LIST_REMOVE(mevp, me_list); 549 550 if (mevp->me_state & EV_DELETE) { 551 free(mevp->me_fname); 552 free(mevp); 553 } else { 554 LIST_INSERT_HEAD(&global_head, mevp, me_list); 555 } 556 } 557 558 mevent_qunlock(); 559 } 560 561 static void 562 mevent_handle_pe(port_event_t *pe) 563 { 564 struct mevent *mevp = pe->portev_user; 565 566 mevent_qunlock(); 567 568 (*mevp->me_func)(mevp->me_fd, mevp->me_type, mevp->me_param); 569 570 mevent_qlock(); 571 if (!mevp->me_cq && !mevp->me_auto_requeue) { 572 mevent_update_one(mevp); 573 } 574 mevent_qunlock(); 575 } 576 #endif 577 578 static struct mevent * 579 mevent_add_state(int tfd, enum ev_type type, 580 void (*func)(int, enum ev_type, void *), void *param, 581 int state, int fflags) 582 { 583 #ifdef __FreeBSD__ 584 struct kevent kev; 585 #endif 586 struct mevent *lp, *mevp; 587 #ifdef __FreeBSD__ 588 int ret; 589 #endif 590 591 if (tfd < 0 || func == NULL) { 592 return (NULL); 593 } 594 595 mevp = NULL; 596 597 pthread_once(&mevent_once, mevent_init); 598 599 mevent_qlock(); 600 601 /* 602 * Verify that the fd/type tuple is not present in any list 603 */ 604 LIST_FOREACH(lp, &global_head, me_list) { 605 if (type != EVF_TIMER && lp->me_fd == tfd && 606 lp->me_type == type) { 607 goto exit; 608 } 609 } 610 611 LIST_FOREACH(lp, &change_head, me_list) { 612 if (type != EVF_TIMER && lp->me_fd == tfd && 613 lp->me_type == type) { 614 goto exit; 615 } 616 } 617 618 /* 619 * Allocate an entry and populate it. 620 */ 621 mevp = calloc(1, sizeof(struct mevent)); 622 if (mevp == NULL) { 623 goto exit; 624 } 625 626 if (type == EVF_TIMER) { 627 mevp->me_msecs = tfd; 628 mevp->me_timid = mevent_timid++; 629 } else 630 mevp->me_fd = tfd; 631 mevp->me_type = type; 632 mevp->me_func = func; 633 mevp->me_param = param; 634 635 mevp->me_state = state; 636 mevp->me_fflags = fflags; 637 638 /* 639 * Try to add the event. If this fails, report the failure to 640 * the caller. 641 */ 642 #ifdef __FreeBSD__ 643 mevent_populate(mevp, &kev); 644 ret = kevent(mfd, &kev, 1, NULL, 0, NULL); 645 if (ret == -1) { 646 free(mevp); 647 mevp = NULL; 648 goto exit; 649 } 650 mevp->me_state &= ~EV_ADD; 651 #else 652 mevent_populate(mevp); 653 if (mevent_clarify_state(mevp)) 654 mevent_update_one(mevp); 655 #endif 656 657 LIST_INSERT_HEAD(&global_head, mevp, me_list); 658 659 exit: 660 mevent_qunlock(); 661 662 return (mevp); 663 } 664 665 struct mevent * 666 mevent_add(int tfd, enum ev_type type, 667 void (*func)(int, enum ev_type, void *), void *param) 668 { 669 670 return (mevent_add_state(tfd, type, func, param, EV_ADD, 0)); 671 } 672 673 struct mevent * 674 mevent_add_flags(int tfd, enum ev_type type, int fflags, 675 void (*func)(int, enum ev_type, void *), void *param) 676 { 677 678 return (mevent_add_state(tfd, type, func, param, EV_ADD, fflags)); 679 } 680 681 struct mevent * 682 mevent_add_disabled(int tfd, enum ev_type type, 683 void (*func)(int, enum ev_type, void *), void *param) 684 { 685 686 return (mevent_add_state(tfd, type, func, param, EV_ADD | EV_DISABLE, 0)); 687 } 688 689 static int 690 mevent_update(struct mevent *evp, bool enable) 691 { 692 int newstate; 693 694 mevent_qlock(); 695 696 /* 697 * It's not possible to enable/disable a deleted event 698 */ 699 assert((evp->me_state & EV_DELETE) == 0); 700 701 newstate = evp->me_state; 702 if (enable) { 703 newstate |= EV_ENABLE; 704 newstate &= ~EV_DISABLE; 705 } else { 706 newstate |= EV_DISABLE; 707 newstate &= ~EV_ENABLE; 708 } 709 710 /* 711 * No update needed if state isn't changing 712 */ 713 if (evp->me_state != newstate) { 714 evp->me_state = newstate; 715 716 /* 717 * Place the entry onto the changed list if not 718 * already there. 719 */ 720 if (evp->me_cq == 0) { 721 evp->me_cq = 1; 722 LIST_REMOVE(evp, me_list); 723 LIST_INSERT_HEAD(&change_head, evp, me_list); 724 mevent_notify(); 725 } 726 } 727 728 mevent_qunlock(); 729 730 return (0); 731 } 732 733 int 734 mevent_enable(struct mevent *evp) 735 { 736 737 return (mevent_update(evp, true)); 738 } 739 740 int 741 mevent_disable(struct mevent *evp) 742 { 743 744 return (mevent_update(evp, false)); 745 } 746 747 static int 748 mevent_delete_event(struct mevent *evp, int closefd) 749 { 750 mevent_qlock(); 751 752 /* 753 * Place the entry onto the changed list if not already there, and 754 * mark as to be deleted. 755 */ 756 if (evp->me_cq == 0) { 757 evp->me_cq = 1; 758 LIST_REMOVE(evp, me_list); 759 LIST_INSERT_HEAD(&change_head, evp, me_list); 760 mevent_notify(); 761 } 762 evp->me_state = EV_DELETE; 763 764 if (closefd) 765 evp->me_closefd = 1; 766 767 mevent_qunlock(); 768 769 return (0); 770 } 771 772 int 773 mevent_delete(struct mevent *evp) 774 { 775 776 return (mevent_delete_event(evp, 0)); 777 } 778 779 int 780 mevent_delete_close(struct mevent *evp) 781 { 782 783 return (mevent_delete_event(evp, 1)); 784 } 785 786 static void 787 mevent_set_name(void) 788 { 789 790 pthread_set_name_np(mevent_tid, "mevent"); 791 } 792 793 void 794 mevent_dispatch(void) 795 { 796 #ifdef __FreeBSD__ 797 struct kevent changelist[MEVENT_MAX]; 798 struct kevent eventlist[MEVENT_MAX]; 799 struct mevent *pipev; 800 int numev; 801 #else 802 struct mevent *pipev; 803 #endif 804 int ret; 805 #ifndef WITHOUT_CAPSICUM 806 cap_rights_t rights; 807 #endif 808 809 mevent_tid = pthread_self(); 810 mevent_set_name(); 811 812 pthread_once(&mevent_once, mevent_init); 813 814 /* 815 * Open the pipe that will be used for other threads to force 816 * the blocking kqueue call to exit by writing to it. Set the 817 * descriptor to non-blocking. 818 */ 819 ret = pipe(mevent_pipefd); 820 if (ret < 0) { 821 perror("pipe"); 822 exit(0); 823 } 824 825 #ifndef WITHOUT_CAPSICUM 826 cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE); 827 if (caph_rights_limit(mevent_pipefd[0], &rights) == -1) 828 errx(EX_OSERR, "Unable to apply rights for sandbox"); 829 if (caph_rights_limit(mevent_pipefd[1], &rights) == -1) 830 errx(EX_OSERR, "Unable to apply rights for sandbox"); 831 #endif 832 833 /* 834 * Add internal event handler for the pipe write fd 835 */ 836 pipev = mevent_add(mevent_pipefd[0], EVF_READ, mevent_pipe_read, NULL); 837 assert(pipev != NULL); 838 839 for (;;) { 840 #ifdef __FreeBSD__ 841 /* 842 * Build changelist if required. 843 * XXX the changelist can be put into the blocking call 844 * to eliminate the extra syscall. Currently better for 845 * debug. 846 */ 847 numev = mevent_build(changelist); 848 if (numev) { 849 ret = kevent(mfd, changelist, numev, NULL, 0, NULL); 850 if (ret == -1) { 851 perror("Error return from kevent change"); 852 } 853 } 854 855 /* 856 * Block awaiting events 857 */ 858 ret = kevent(mfd, NULL, 0, eventlist, MEVENT_MAX, NULL); 859 if (ret == -1 && errno != EINTR) { 860 perror("Error return from kevent monitor"); 861 } 862 863 /* 864 * Handle reported events 865 */ 866 mevent_handle(eventlist, ret); 867 868 #else /* __FreeBSD__ */ 869 port_event_t pev; 870 871 /* Handle any pending updates */ 872 mevent_update_pending(); 873 874 /* Block awaiting events */ 875 ret = port_get(mfd, &pev, NULL); 876 if (ret != 0) { 877 if (errno != EINTR) 878 perror("Error return from port_get"); 879 continue; 880 } 881 882 /* Handle reported event */ 883 mevent_handle_pe(&pev); 884 #endif /* __FreeBSD__ */ 885 } 886 } 887