/* 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_poll.h" #include "opt.h" #include #include #include void event_queue_poll::update_cache(int fd) { if (fd != cache_poll_fd->fd) { for (poll_fds_t::iterator p = poll_fds.begin(); p != poll_fds.end(); ++p) { if (fd == p->fd) { cache_poll_fd = p; break; } } if (fd != cache_poll_fd->fd) { abort(); } } // If this is not the last fd (which is about to be removed from the // array anyway), we have to hide the pending poll event. if (fd_activities.size() > 1) { fd_activities_t::iterator last = fd_activities.end() - 1; if (last->fd != fd) { // We have to search the entire (short) array. for (fd_activities_t::iterator p = fd_activities.begin(); p != last; ++p) { if (p->fd == fd) { fprintf(stderr, "%s: warning: removing pending poll event on %d\n", opt_program, fd); fd_activities.erase(p); break; } } } } } inline unsigned short event_queue_poll::convert_watch(fd_handler::watch_options w) { switch (w) { case fd_handler::watch_read: return POLLIN; break; case fd_handler::watch_write: return POLLOUT; break; case fd_handler::watch_read_write: return POLLIN | POLLOUT; break; default: abort(); } } void event_queue_poll::add_fd(fd_handler* fdh, fd_handler::watch_options w) { fds[fdh->fd()] = fdh; pollfd pfd; pfd.fd = fdh->fd(); pfd.events = convert_watch(w); poll_fds.push_back(pfd); cache_poll_fd = poll_fds.end() - 1; } void event_queue_poll::update_fd(fd_handler* fdh, fd_handler::watch_options w) { update_cache(fdh->fd()); cache_poll_fd->events = convert_watch(w); } void event_queue_poll::remove_fd(fd_handler* fdh) { fds.erase(fdh->fd()); update_cache(fdh->fd()); poll_fds.erase(cache_poll_fd); } event_queue_poll::~event_queue_poll() { // We have to deallocate fds while *this is still an // event_queue_poll object, otherwise the remove_fd() method is // unavailable. while (!fds.empty()) { delete fds.begin()->second; } } void event_queue_poll::run() { for (;;) { int timeout = next_timeout(); if (poll_fds.empty()) { if (timeout < 0) { return; // infinite timeout without any fds } usleep(1000 * timeout); dispatch_start(); // No fd activity to dispatch. Timeouts are handled below. } else { int result = poll(&*poll_fds.begin(), poll_fds.size(), timeout); if (result < 0) { if (errno != EINTR) { fprintf(stderr, "%s: internal polling 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 forget_fd() calls. unsigned pos = 0; fd_activities.clear(); for (poll_fds_t::iterator p = poll_fds.begin(); p != poll_fds.end(); ++p, ++pos) { if (p->revents == 0) { continue; } fd_activity fa; fa.fd = p->fd; if (p->revents & POLLERR) { fa.act = fd_handler::activity_error; } else if (p->revents & POLLIN) { if (p->revents & POLLOUT) { fa.act = fd_handler::activity_read_write; } else { fa.act = fd_handler::activity_read; } } else { if (p->revents & POLLOUT) { fa.act = fd_handler::activity_write; } else { fprintf(stderr, "%s: warning: illegal poll() state 0x%08X " "received on descriptor %d\n", opt_program, p->revents, p->fd); fa.act = fd_handler::activity_error; } } fd_activities.push_back(fa); } while (!fd_activities.empty()) { fd_activities_t::iterator last = fd_activities.end() - 1; int fd = last->fd; fds_t::iterator p = fds.find(fd); if (p == fds.end()) { abort(); // FIXME: maybe too drastic } if (!p->second->on_activity(last->act)) { delete p->second; } // Might have changed. last = fd_activities.end() - 1; if (last->fd == fd) { fd_activities.erase(last); } } } } dispatch_end(); } } event_queue_poll t; // arch-tag: 79fa52fa-ae51-421e-9a49-d3335c820a4e