/* doscan - Denial Of Service Capable Auditing of Networks -*- C++ -*- * Copyright (C) 2003 Florian Weimer * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "event_queue_epoll.h" #include "opt.h" #include #include #include #if defined(__linux__) && defined(HAVE_SYS_EPOLL_H) #define HAVE_EPOLL #include #ifndef HAVE_EPOLL_WAIT // Some GNU libc versions provide the correct header file, but no // implementations. Some even lack the syscall numbers. extern "C" { #include #ifndef __NR_epoll_create #define __NR_epoll_create 254 #define __NR_epoll_ctl 255 #define __NR_epoll_wait 256 #endif // __NR_epoll_create _syscall1(int, epoll_create, int, size) _syscall4(int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event *, event) _syscall4(int, epoll_wait, int, epfd, struct epoll_event *, pevents, int, maxevents, int, timeout) } #endif // HAVE_EPOLL_WAIT // event_queue_epoll implementation inline unsigned event_queue_epoll::convert_watch(fd_handler::watch_options w) { switch (w) { case fd_handler::watch_read: return EPOLLIN | EPOLLERR; break; case fd_handler::watch_write: return EPOLLOUT | EPOLLERR; break; case fd_handler::watch_read_write: return EPOLLIN | EPOLLOUT | EPOLLERR; break; default: abort(); } } void event_queue_epoll::forget_activity(event_queue::fd_handler* fdh) { for (fd_activities_t::reverse_iterator p = fd_activities.rbegin(); p != fd_activities.rend(); ++p) { if (p->fdh == fdh) { fd_activities.erase(p.base()); break; } } } void event_queue_epoll::add_fd(fd_handler* fdh, fd_handler::watch_options w) { epoll_event event; event.events = convert_watch(w); event.data.ptr = fdh; int result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fdh->fd(), &event); if (result == -1) { fprintf(stderr, "%s: epoll_ctl(EPOLL_CTL_ADD) failed: %s\n", opt_program, strerror(errno)); exit(1); } ++count; } void event_queue_epoll::update_fd(fd_handler* fdh, fd_handler::watch_options w) { epoll_event event; event.events = convert_watch(w); event.data.ptr = fdh; int result = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fdh->fd(), &event); if (result == -1) { fprintf(stderr, "%s: epoll_ctl(EPOLL_CTL_MOD) failed: %s\n", opt_program, strerror(errno)); exit(1); } } void event_queue_epoll::remove_fd(fd_handler* fdh) { forget_activity(fdh); epoll_event event; event.events = 0; event.data.ptr = 0; int result = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fdh->fd(), &event); if (result == -1) { fprintf(stderr, "%s: epoll_ctl(EPOLL_CTL_DEL) failed: %s\n", opt_program, strerror(errno)); exit(1); } --count; } event_queue_epoll::event_queue_epoll(unsigned size_hint) { int result = epoll_create(size_hint); if (result == -1) { fprintf(stderr, "%s: epoll_create failed: %s\n", opt_program, strerror(errno)); exit(1); } epoll_fd = result; count = 0; } event_queue_epoll::~event_queue_epoll() { close(epoll_fd); } void event_queue_epoll::run() { for (;;) { int timeout = next_timeout(); if (count == 0) { if (timeout < 0) { return; // infinite timeout without any fds } usleep(1000 * timeout); dispatch_start(); // No fd activity to dispatch. Timeouts are handled below. } else { static unsigned const max_events = 32; struct epoll_event events[max_events]; int result = epoll_wait(epoll_fd, events, sizeof(max_events), timeout); if (result < 0) { if (errno != EINTR) { fprintf(stderr, "%s: epoll_wait() failure, error was: %s\n", opt_program, strerror(errno)); exit (EXIT_FAILURE); } } dispatch_start(); if (result > 0) { // We have to make a copy of the array so that it's not // corrupted because of remove_fd() calls. fd_activities.clear(); for (int j = 0; j < result; ++j) { fd_activity fa; fa.fdh = static_cast(events[j].data.ptr); if (events[j].events & EPOLLERR) { fa.act = fd_handler::activity_error; } else if (events[j].events & EPOLLIN) { if (events[j].events & EPOLLOUT) { fa.act = fd_handler::activity_read_write; } else { fa.act = fd_handler::activity_read; } } else { if (events[j].events & EPOLLOUT) { fa.act = fd_handler::activity_write; } else { fprintf(stderr, "%s: warning: illegal epoll_wait() state 0x%08X " "received on descriptor %d\n", opt_program, events[j].events, fa.fdh->fd()); fa.act = fd_handler::activity_error; } } fd_activities.push_back(fa); } while (!fd_activities.empty()) { fd_activity fa = *(fd_activities.end() - 1); if (!fa.fdh->on_activity(fa.act)) { forget_activity(fa.fdh); delete fa.fdh; } else { forget_activity(fa.fdh); } } } } dispatch_end(); } } #else // __linux__ #undef HAVE_EPOLL #endif //__linux__ // event_queue::create() implementation. We dynamically switch // between event_queue_poll and event_queue_epoll. #include "event_queue_poll.h" event_queue* event_queue::create(unsigned size_hint) { #ifdef HAVE_EPOLL if (!opt_no_epoll) { static int have_epoll = -1; if (have_epoll < 0) { int result = epoll_create(1); if (result >= 0) { close(result); have_epoll = 1; if (opt_verbose) { fprintf(stderr, "%s: using epoll interface\n", opt_program); } } else { have_epoll = 0; if (opt_verbose) { fprintf(stderr, "%s: using poll interface\n", opt_program); } } } if (have_epoll > 0) { return new event_queue_epoll(size_hint); } } #endif return new event_queue_poll(); } // arch-tag: 0e7efd23-7dbb-4264-906d-084184e5add3