/* * ircd-ratbox: A slightly useful ircd. * kqueue.c: FreeBSD kqueue compatible network routines. * * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center * Copyright (C) 1996-2002 Hybrid Development Team * Copyright (C) 2001 Adrian Chadd * Copyright (C) 2002-2005 ircd-ratbox development team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * */ #include #include #include #include #if defined(HAVE_SYS_EVENT_H) && (HAVE_KEVENT) #include /* jlemon goofed up and didn't add EV_SET until fbsd 4.3 */ #ifndef EV_SET #define EV_SET(kevp, a, b, c, d, e, f) do { \ (kevp)->ident = (a); \ (kevp)->filter = (b); \ (kevp)->flags = (c); \ (kevp)->fflags = (d); \ (kevp)->data = (e); \ (kevp)->udata = (f); \ } while(0) #endif #ifdef EVFILT_TIMER #define KQUEUE_SCHED_EVENT #endif static void kq_update_events(rb_fde_t *, short, PF *); static int kq; static struct timespec zero_timespec; static struct kevent *kqlst; /* kevent buffer */ static struct kevent *kqout; /* kevent output buffer */ static int kqmax; /* max structs to buffer */ static int kqoff; /* offset into the buffer */ int rb_setup_fd_kqueue(rb_fde_t *F) { return 0; } static void kq_update_events(rb_fde_t *F, short filter, PF * handler) { PF *cur_handler; int kep_flags; switch (filter) { case EVFILT_READ: cur_handler = F->read_handler; break; case EVFILT_WRITE: cur_handler = F->write_handler; break; default: /* XXX bad! -- adrian */ return; break; } if((cur_handler == NULL && handler != NULL) || (cur_handler != NULL && handler == NULL)) { struct kevent *kep; kep = kqlst + kqoff; if(handler != NULL) { kep_flags = EV_ADD | EV_ONESHOT; } else { kep_flags = EV_DELETE; } EV_SET(kep, F->fd, filter, kep_flags, 0, 0, F); if(++kqoff == kqmax) { int ret, i; /* Add them one at a time, because there may be * already closed fds in it. The kernel will try * to report invalid fds in the output; if there * is no space, it silently stops processing the * array at that point. We cannot give output space * because that would also return events we cannot * process at this point. */ for(i = 0; i < kqoff; i++) { ret = kevent(kq, kqlst + i, 1, NULL, 0, &zero_timespec); /* jdc -- someone needs to do error checking... */ /* EBADF is normal here -- jilles */ if(ret == -1 && errno != EBADF) rb_lib_log("kq_update_events(): kevent(): %s", strerror(errno)); } kqoff = 0; } } } /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ /* Public functions */ /* * rb_init_netio * * This is a needed exported function which will be called to initialise * the network loop code. */ int rb_init_netio_kqueue(void) { kq = kqueue(); if(kq < 0) { return errno; } kqmax = getdtablesize(); kqlst = rb_malloc(sizeof(struct kevent) * kqmax); kqout = rb_malloc(sizeof(struct kevent) * kqmax); rb_open(kq, RB_FD_UNKNOWN, "kqueue fd"); zero_timespec.tv_sec = 0; zero_timespec.tv_nsec = 0; return 0; } /* * rb_setselect * * This is a needed exported function which will be called to register * and deregister interest in a pending IO state for a given FD. */ void rb_setselect_kqueue(rb_fde_t *F, unsigned int type, PF * handler, void *client_data) { lrb_assert(IsFDOpen(F)); if(type & RB_SELECT_READ) { kq_update_events(F, EVFILT_READ, handler); F->read_handler = handler; F->read_data = client_data; } if(type & RB_SELECT_WRITE) { kq_update_events(F, EVFILT_WRITE, handler); F->write_handler = handler; F->write_data = client_data; } } /* * Check all connections for new connections and input data that is to be * processed. Also check for connections with data queued and whether we can * write it out. */ /* * rb_select * * Called to do the new-style IO, courtesy of squid (like most of this * new IO code). This routine handles the stuff we've hidden in * rb_setselect and fd_table[] and calls callbacks for IO ready * events. */ int rb_select_kqueue(long delay) { int num, i; struct timespec poll_time; struct timespec *pt; rb_fde_t *F; if(delay < 0) { pt = NULL; } else { pt = &poll_time; poll_time.tv_sec = delay / 1000; poll_time.tv_nsec = (delay % 1000) * 1000000; } for(;;) { num = kevent(kq, kqlst, kqoff, kqout, kqmax, pt); kqoff = 0; if(num >= 0) break; if(rb_ignore_errno(errno)) break; rb_set_time(); return RB_ERROR; /* NOTREACHED */ } rb_set_time(); if(num == 0) return RB_OK; /* No error.. */ for(i = 0; i < num; i++) { PF *hdl = NULL; if(kqout[i].flags & EV_ERROR) { errno = kqout[i].data; /* XXX error == bad! -- adrian */ continue; /* XXX! */ } switch (kqout[i].filter) { case EVFILT_READ: F = kqout[i].udata; if((hdl = F->read_handler) != NULL) { F->read_handler = NULL; hdl(F, F->read_data); } break; case EVFILT_WRITE: F = kqout[i].udata; if((hdl = F->write_handler) != NULL) { F->write_handler = NULL; hdl(F, F->write_data); } break; #if defined(EVFILT_TIMER) case EVFILT_TIMER: rb_run_one_event(kqout[i].udata); break; #endif default: /* Bad! -- adrian */ break; } } return RB_OK; } #if defined(KQUEUE_SCHED_EVENT) static int can_do_event = 0; int rb_kqueue_supports_event(void) { struct kevent kv; struct timespec ts; int xkq; if(can_do_event == 1) return 1; if(can_do_event == -1) return 0; xkq = kqueue(); ts.tv_sec = 0; ts.tv_nsec = 1000; EV_SET(&kv, (uintptr_t)0x0, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 1, 0); if(kevent(xkq, &kv, 1, NULL, 0, NULL) < 0) { can_do_event = -1; close(xkq); return 0; } close(xkq); can_do_event = 1; return 1; } int rb_kqueue_sched_event(struct ev_entry *event, int when) { struct kevent kev; int kep_flags; kep_flags = EV_ADD; if(event->frequency == 0) kep_flags |= EV_ONESHOT; EV_SET(&kev, (uintptr_t)event, EVFILT_TIMER, kep_flags, 0, when * 1000, event); if(kevent(kq, &kev, 1, NULL, 0, NULL) < 0) return 0; return 1; } void rb_kqueue_unsched_event(struct ev_entry *event) { struct kevent kev; EV_SET(&kev, (uintptr_t)event, EVFILT_TIMER, EV_DELETE, 0, 0, event); kevent(kq, &kev, 1, NULL, 0, NULL); } void rb_kqueue_init_event(void) { return; } #endif /* KQUEUE_SCHED_EVENT */ #else /* kqueue not supported */ int rb_init_netio_kqueue(void) { errno = ENOSYS; return -1; } void rb_setselect_kqueue(rb_fde_t *F __attribute__((unused)), unsigned int type __attribute__((unused)), PF * handler __attribute__((unused)), void *client_data __attribute__((unused))) { errno = ENOSYS; return; } int rb_select_kqueue(long delay __attribute__((unused))) { errno = ENOSYS; return -1; } int rb_setup_fd_kqueue(rb_fde_t *F __attribute__((unused))) { errno = ENOSYS; return -1; } #endif #if !defined(HAVE_KEVENT) || !defined(KQUEUE_SCHED_EVENT) void rb_kqueue_init_event(void) { return; } int rb_kqueue_sched_event(struct ev_entry *event __attribute__((unused)), int when __attribute__((unused))) { errno = ENOSYS; return -1; } void rb_kqueue_unsched_event(struct ev_entry *event __attribute__((unused))) { return; } int rb_kqueue_supports_event(void) { errno = ENOSYS; return 0; } #endif /* !HAVE_KEVENT || !KQUEUE_SCHED_EVENT */