diff options
-rw-r--r-- | CMakeLists.txt | 8 | ||||
-rw-r--r-- | config.h.cmake | 1 | ||||
-rw-r--r-- | src/irc/irc_client.cpp | 21 | ||||
-rw-r--r-- | src/irc/irc_client.hpp | 25 | ||||
-rw-r--r-- | src/network/poller.cpp | 73 | ||||
-rw-r--r-- | src/network/poller.hpp | 24 |
6 files changed, 135 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 21e82b4..2c01ee3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,12 @@ include_directories(${ICONV_INCLUDE_DIR}) # coming from these headers. include_directories(SYSTEM ${CRYPTO++_INCLUDE_DIR}) +set(POLLER "POLL" CACHE STRING + "Choose the poller between POLL and EPOLL (Linux-only)") +if((NOT ${POLLER} MATCHES "POLL") AND + (NOT ${POLLER} MATCHES "EPOLL")) + message(FATAL_ERROR "POLLER must be either POLL or EPOLL") +endif() # ## utils # @@ -95,4 +101,4 @@ target_link_libraries(test utils config) -CONFIGURE_FILE(config.h.cmake src/config.h @ONLY) +configure_file(config.h.cmake src/config.h) diff --git a/config.h.cmake b/config.h.cmake index 5f0a3cb..e17cc80 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -1 +1,2 @@ #cmakedefine ICONV_SECOND_ARGUMENT_IS_CONST +#cmakedefine POLLER ${POLLER} diff --git a/src/irc/irc_client.cpp b/src/irc/irc_client.cpp index 82abbd9..f339580 100644 --- a/src/irc/irc_client.cpp +++ b/src/irc/irc_client.cpp @@ -222,6 +222,27 @@ void IrcClient::on_channel_message(const IrcMessage& message) this->bridge->send_message(iid, nick, body, muc); } +void IrcClient::empty_motd(const IrcMessage& message) +{ + (void)message; + this->motd.erase(); +} + +void IrcClient::on_motd_line(const IrcMessage& message) +{ + const std::string body = message.arguments[1]; + // We could send the MOTD without a line break between each IRC-message, + // but sometimes it contains some ASCII art, we use line breaks to keep + // them intact. + this->motd += body+"\n"; +} + +void IrcClient::send_motd(const IrcMessage& message) +{ + (void)message; + this->bridge->send_xmpp_message(this->hostname, "", this->motd); +} + void IrcClient::on_topic_received(const IrcMessage& message) { const std::string chan_name = message.arguments[1]; diff --git a/src/irc/irc_client.hpp b/src/irc/irc_client.hpp index 3b22fa0..aa420f4 100644 --- a/src/irc/irc_client.hpp +++ b/src/irc/irc_client.hpp @@ -100,6 +100,18 @@ public: */ void forward_server_message(const IrcMessage& message); /** + * Just empty the motd we kept as a string + */ + void empty_motd(const IrcMessage& message); + /** + * Send the MOTD string as one single "big" message + */ + void send_motd(const IrcMessage& message); + /** + * Append this line to the MOTD + */ + void on_motd_line(const IrcMessage& message); + /** * Forward the join of an other user into an IRC channel, and save the * IrcUsers in the IrcChannel */ @@ -172,6 +184,11 @@ private: */ std::vector<std::string> channels_to_join; bool welcomed; + /** + * Each motd line received is appended to this string, which we send when + * the motd is completely received + */ + std::string motd; IrcClient(const IrcClient&) = delete; IrcClient(IrcClient&&) = delete; IrcClient& operator=(const IrcClient&) = delete; @@ -186,8 +203,12 @@ typedef void (IrcClient::*irc_callback_t)(const IrcMessage&); static const std::unordered_map<std::string, irc_callback_t> irc_callbacks = { {"NOTICE", &IrcClient::forward_server_message}, - {"375", &IrcClient::forward_server_message}, - {"372", &IrcClient::forward_server_message}, + {"RPL_MOTDSTART", &IrcClient::empty_motd}, + {"375", &IrcClient::empty_motd}, + {"RPL_MOTD", &IrcClient::on_motd_line}, + {"372", &IrcClient::on_motd_line}, + {"RPL_MOTDEND", &IrcClient::send_motd}, + {"376", &IrcClient::send_motd}, {"JOIN", &IrcClient::on_channel_join}, {"PRIVMSG", &IrcClient::on_channel_message}, {"353", &IrcClient::set_and_forward_user_list}, diff --git a/src/network/poller.cpp b/src/network/poller.cpp index e790e60..023fb12 100644 --- a/src/network/poller.cpp +++ b/src/network/poller.cpp @@ -12,6 +12,13 @@ Poller::Poller() #if POLLER == POLL memset(this->fds, 0, sizeof(this->fds)); this->nfds = 0; +#elif POLLER == EPOLL + this->epfd = ::epoll_create1(0); + if (this->epfd == -1) + { + perror("epoll"); + throw std::runtime_error("Could not create epoll instance"); + } #endif } @@ -36,6 +43,17 @@ void Poller::add_socket_handler(std::shared_ptr<SocketHandler> socket_handler) this->fds[this->nfds].events = POLLIN; this->nfds++; #endif +#if POLLER == EPOLL + struct epoll_event event; + event.data.ptr = socket_handler.get(); + event.events = EPOLLIN; + const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_ADD, socket_handler->get_socket(), &event); + if (res == -1) + { + perror("epoll_ctl"); + throw std::runtime_error("Could not add socket to epoll"); + } +#endif } void Poller::remove_socket_handler(const socket_t socket) @@ -44,6 +62,8 @@ void Poller::remove_socket_handler(const socket_t socket) if (it == this->socket_handlers.end()) throw std::runtime_error("Trying to remove a SocketHandler that is not managed"); this->socket_handlers.erase(it); + +#if POLLER == POLL for (size_t i = 0; i < this->nfds; i++) { if (this->fds[i].fd == socket) @@ -58,9 +78,17 @@ void Poller::remove_socket_handler(const socket_t socket) this->nfds--; } } +#elif POLLER == EPOLL + const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_DEL, socket, nullptr); + if (res == -1) + { + perror("epoll_ctl"); + throw std::runtime_error("Could not remove socket from epoll"); + } +#endif } -void Poller::watch_send_events(const SocketHandler* const socket_handler) +void Poller::watch_send_events(SocketHandler* socket_handler) { #if POLLER == POLL for (size_t i = 0; i <= this->nfds; ++i) @@ -71,11 +99,21 @@ void Poller::watch_send_events(const SocketHandler* const socket_handler) return; } } -#endif throw std::runtime_error("Cannot watch a non-registered socket for send events"); +#elif POLLER == EPOLL + struct epoll_event event; + event.data.ptr = socket_handler; + event.events = EPOLLIN|EPOLLOUT; + const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event); + if (res == -1) + { + perror("epoll_ctl"); + throw std::runtime_error("Could not modify socket flags in epoll"); + } +#endif } -void Poller::stop_watching_send_events(const SocketHandler* const socket_handler) +void Poller::stop_watching_send_events(SocketHandler* socket_handler) { #if POLLER == POLL for (size_t i = 0; i <= this->nfds; ++i) @@ -86,8 +124,18 @@ void Poller::stop_watching_send_events(const SocketHandler* const socket_handler return; } } -#endif throw std::runtime_error("Cannot watch a non-registered socket for send events"); +#elif POLLER == EPOLL + struct epoll_event event; + event.data.ptr = socket_handler; + event.events = EPOLLIN; + const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event); + if (res == -1) + { + perror("epoll_ctl"); + throw std::runtime_error("Could not modify socket flags in epoll"); + } +#endif } bool Poller::poll() @@ -121,6 +169,23 @@ bool Poller::poll() res--; } } +#elif POLLER == EPOLL + static const size_t max_events = 12; + struct epoll_event revents[max_events]; + const int nb_events = epoll_wait(this->epfd, revents, max_events, -1); + if (nb_events == -1) + { + perror("epoll_wait"); + throw std::runtime_error("Epoll_wait failed"); + } + for (int i = 0; i < nb_events; ++i) + { + auto socket_handler = static_cast<SocketHandler*>(revents[i].data.ptr); + if (revents[i].events & EPOLLIN) + socket_handler->on_recv(); + if (revents[i].events & EPOLLOUT) + socket_handler->on_send(); + } #endif return true; } diff --git a/src/network/poller.hpp b/src/network/poller.hpp index 319236b..fe52fda 100644 --- a/src/network/poller.hpp +++ b/src/network/poller.hpp @@ -9,27 +9,29 @@ #define POLL 1 #define EPOLL 2 #define KQUEUE 3 - -#define POLLER POLL +#include <config.h> +#ifndef POLLER + #define POLLER POLL +#endif #if POLLER == POLL #include <poll.h> - // TODO, dynamic size, without artificial limit #define MAX_POLL_FD_NUMBER 4096 +#elif POLLER == EPOLL + #include <sys/epoll.h> +#else + #error Invalid POLLER value #endif /** - * We pass some SocketHandlers to this the Poller, which uses + * We pass some SocketHandlers to this Poller, which uses * poll/epoll/kqueue/select etc to wait for events on these SocketHandlers, * and call the callbacks when event occurs. * - * TODO: support for all these pollers: - * - poll(2) (mandatory) - * - epoll(7) + * TODO: support these pollers: * - kqueue(2) */ - class Poller { public: @@ -48,12 +50,12 @@ public: * Signal the poller that he needs to watch for send events for the given * SocketHandler. */ - void watch_send_events(const SocketHandler* const socket_handler); + void watch_send_events(SocketHandler* socket_handler); /** * Signal the poller that he needs to stop watching for send events for * this SocketHandler. */ - void stop_watching_send_events(const SocketHandler* const socket_handler); + void stop_watching_send_events(SocketHandler* socket_handler); /** * Wait for all watched events, and call the SocketHandlers' callbacks * when one is ready. @@ -72,6 +74,8 @@ private: #if POLLER == POLL struct pollfd fds[MAX_POLL_FD_NUMBER]; nfds_t nfds; +#elif POLLER == EPOLL + int epfd; #endif Poller(const Poller&) = delete; |