void io_loop_handle_remove(struct io_file *io, bool closed) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; struct kevent ev; i_assert(io->io.condition != 0); if ((io->io.condition & (IO_READ | IO_ERROR)) != 0 && !closed) { MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) i_error("kevent(EV_DELETE, %d) failed: %m", io->fd); } if ((io->io.condition & IO_WRITE) != 0 && !closed) { MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) i_error("kevent(EV_DELETE, %d) failed: %m", io->fd); } io->io.condition = 0; /* since we're not freeing memory in any case, just increase deleted counter so next handle_add() can just decrease it insteading of appending to the events array */ ctx->deleted_count++; i_assert(io->refcount > 0); if (--io->refcount == 0) i_free(io); }
enum io_notify_result io_add_notify(const char *path, const char *source_filename, unsigned int source_linenum, io_callback_t *callback, void *context, struct io **io_r) { struct ioloop_notify_handler_context *ctx = current_ioloop->notify_handler_context; struct kevent ev; struct io_notify *io; int fd; if (ctx == NULL) ctx = io_loop_notify_handler_init(); fd = open(path, O_RDONLY); if (fd == -1) { /* ESTALE could happen with NFS. Don't bother giving an error message then. */ if (errno != ENOENT && errno != ESTALE) i_error("open(%s) for kq notify failed: %m", path); return IO_NOTIFY_NOTFOUND; } fd_close_on_exec(fd, TRUE); io = i_new(struct io_notify, 1); io->io.condition = IO_NOTIFY; io->io.source_filename = source_filename; io->io.source_linenum = source_linenum; io->io.callback = callback; io->io.context = context; io->io.ioloop = current_ioloop; io->refcount = 1; io->fd = fd; /* EV_CLEAR flag is needed because the EVFILT_VNODE filter reports event state transitions and not the current state. With this flag, the same event is only returned once. */ MY_EV_SET(&ev, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_WRITE | NOTE_EXTEND | NOTE_REVOKE, 0, io); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) { i_error("kevent(%d, %s) for notify failed: %m", fd, path); i_close_fd(&fd); i_free(io); return IO_NOTIFY_NOSUPPORT; } if (ctx->event_io == NULL) { ctx->event_io = io_add(ctx->kq, IO_READ, event_callback, io->io.ioloop->notify_handler_context); } DLLIST_PREPEND(&ctx->notifies, io); *io_r = &io->io; return IO_NOTIFY_ADDED; }
void io_loop_handle_add(struct io_file *io) { struct ioloop_handler_context *ctx = io->io.ioloop->handler_context; struct kevent ev; if ((io->io.condition & (IO_READ | IO_ERROR)) != 0) { MY_EV_SET(&ev, io->fd, EVFILT_READ, EV_ADD, 0, 0, io); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) i_fatal("kevent(EV_ADD, READ, %d) failed: %m", io->fd); } if ((io->io.condition & IO_WRITE) != 0) { MY_EV_SET(&ev, io->fd, EVFILT_WRITE, EV_ADD, 0, 0, io); if (kevent(ctx->kq, &ev, 1, NULL, 0, NULL) < 0) i_fatal("kevent(EV_ADD, WRITE, %d) failed: %m", io->fd); } /* allow kevent() to return the maximum number of events by keeping space allocated for each handle */ if (ctx->deleted_count > 0) ctx->deleted_count--; else array_append_zero(&ctx->events); }
void io_loop_notify_remove(struct io *_io) { struct ioloop_notify_handler_context *ctx = _io->ioloop->notify_handler_context; struct io_notify *io = (struct io_notify *)_io; struct kevent ev; MY_EV_SET(&ev, io->fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL); if (kevent(ctx->kq, &ev, 1, NULL, 0, 0) < 0) i_error("kevent(%d) for notify remove failed: %m", io->fd); if (close(io->fd) < 0) i_error("close(%d) for notify remove failed: %m", io->fd); io->fd = -1; if (--io->refcount == 0) i_free(io); }