123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- /* This file is part of libepistle.
- *
- * libepistle is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * libepistle 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with libepistle. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <sys/epoll.h>
- #include <sys/socket.h>
- #include <unistd.h>
- #include <errno.h>
- #include "polling.h"
- struct pm_extra {
- Pm_react react;
- Server_check s_check;
- void *extra;
- };
- int
- poll_add(int epoll_fd, int fd, Err *err)
- {
- struct epoll_event event = { 0 };
- event.events = EPOLLIN;
- event.data.fd = fd;
- if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0) {
- err_std(err);
- return -1;
- }
- return 0;
- }
- int
- poll_remove(int epoll_fd, int fd, Err *err)
- {
- if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0) {
- err_std(err);
- return -1;
- }
- return 0;
- }
- int
- poll_out(int epoll_fd, int fd, Err *err)
- {
- struct epoll_event event = { 0 };
- event.events = EPOLLIN | EPOLLOUT;
- event.data.fd = fd;
- if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0) {
- err_std(err);
- return -1;
- }
- return 0;
- }
- int
- poll_out_off(int epoll_fd, int fd, Err *err)
- {
- struct epoll_event event = { 0 };
- event.events = EPOLLIN;
- event.data.fd = fd;
- if (epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0) {
- err_std(err);
- return -1;
- }
- return 0;
- }
- int
- polling(int *epoll_fd, Polling_react react, int fds[], Err *err, void *extra)
- {
- struct epoll_event events[16];
- if (*epoll_fd < 0 && (*epoll_fd = epoll_create1(0)) < 0) {
- err_std(err);
- return -1;
- }
- if (fds)
- for (int i = 0; fds[i] >= 0; ++i)
- if (poll_add(*epoll_fd, fds[i], err) < 0)
- return -1;
- while (1) {
- int react_val = POLLING_OKAY;
- int nfds = epoll_wait(*epoll_fd, events,
- sizeof(events) / sizeof(events[0]), -1);
- if (nfds < 0) {
- /* laptop closing can cause this! */
- if (errno == EINTR)
- continue;
- err_std(err);
- return -1;
- }
- for (int i = 0; i < nfds && react_val == POLLING_OKAY; ++i)
- react_val = react(*epoll_fd, events[i].data.fd,
- events[i].events, err, extra);
- if (react_val == POLLING_OKAY)
- continue;
- if (react_val > 0) /* POLLING END */
- break;
- /* POLLING FATAL */
- return -1;
- }
- return 0;
- }
- static int
- pm_react(int epoll_fd, int fd, uint32_t events, Err *err, void *extra)
- {
- int pm_event = 0;
- struct pm_extra *pm_extra = extra;
- int server_fd = -1;
- char check;
- if (pm_extra->s_check &&
- pm_extra->s_check(fd, pm_extra->extra)) {
- int client = accept(fd, NULL, NULL);
- /* We don't do anything, it's the clients problem */
- if (client < 0)
- return POLLING_OKAY;
- server_fd = fd;
- fd = client;
- pm_event = PM_ADD;
- } else if (events & EPOLLIN) {
- pm_event = recv(fd, &check, 1, MSG_PEEK | MSG_DONTWAIT) > 0 ?
- PM_INPUT : PM_REMOVE;
- } else if (events & EPOLLOUT) {
- pm_event = PM_OUTPUT;
- }
- if (!pm_event)
- return POLLING_OKAY;
- return pm_extra->react(epoll_fd, fd, server_fd, pm_event,
- err, pm_extra->extra);
- }
- int
- polling_mode(Pm_react react, int *epoll_fd, int fds[], Server_check s_check,
- Err *err, void *extra)
- {
- struct pm_extra pm_extra = {
- .react = react,
- .s_check = s_check,
- .extra = extra
- };
- return polling(epoll_fd, pm_react, fds, err, &pm_extra);
- }
|