Line data Source code
1 : /*
2 : START OF LICENSE STUB
3 : DeDOS: Declarative Dispersion-Oriented Software
4 : Copyright (C) 2017 University of Pennsylvania, Georgetown University
5 :
6 : This program is free software: you can redistribute it and/or modify
7 : it under the terms of the GNU General Public License as published by
8 : the Free Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : This program is distributed in the hope that it will be useful,
12 : but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : GNU General Public License for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>.
18 : END OF LICENSE STUB
19 : */
20 : /**
21 : * @file epollops.c
22 : *
23 : * Wrapper functions for epoll to manage event-based communication
24 : */
25 :
26 : #include "epollops.h"
27 : #include "logging.h"
28 : #include <inttypes.h>
29 : #include <netinet/in.h>
30 : #include <sys/epoll.h>
31 : #include <unistd.h>
32 : #include <fcntl.h>
33 : #include <stdbool.h>
34 :
35 : /**
36 : * The maximum number of events that can be responded to
37 : * in a single call to epoll_wait().
38 : * NOT the maximum number of events that can be stored in the epoll.
39 : */
40 : #define MAX_EPOLL_EVENTS 512
41 :
42 0 : int enable_epoll(int epoll_fd, int new_fd, uint32_t events) {
43 : struct epoll_event event;
44 0 : memset(&event, 0, sizeof(event));
45 0 : event.data.fd = new_fd;
46 0 : event.events = events | EPOLLONESHOT;
47 :
48 0 : int rtn = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, new_fd, &event);
49 :
50 0 : if (rtn == -1) {
51 : log(LOG_EPOLL_OPS, "failed to enable fd %d on epoll %d: %s",
52 : new_fd, epoll_fd, strerror(errno));
53 0 : return -1;
54 : }
55 : log(LOG_EPOLL_OPS, "enabled fd %d on epoll", new_fd);
56 0 : return 0;
57 : }
58 :
59 0 : int add_to_epoll(int epoll_fd, int new_fd, uint32_t events, bool oneshot) {
60 : struct epoll_event event;
61 0 : memset(&event, 0, sizeof(event));
62 0 : event.data.fd = new_fd;
63 0 : if (oneshot) {
64 0 : event.events = events | EPOLLONESHOT;
65 : } else {
66 0 : event.events = events;
67 : }
68 :
69 0 : int rtn = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, new_fd, &event);
70 :
71 0 : if (rtn == -1) {
72 : log(LOG_EPOLL_OPS, "failed to add fd %d on epoll %d: %s",
73 : new_fd, epoll_fd, strerror(errno));
74 0 : return -1;
75 : }
76 : log(LOG_EPOLL_OPS, "Added fd %d to epoll", new_fd);
77 0 : return 0;
78 : }
79 :
80 : /**
81 : * Accepts a new connection and adds it to the epoll instance.
82 : * @param socketfd The new file descriptor to accept and add to the epoll
83 : * @param epoll_fd The epoll file descriptor
84 : * @param oneshot Whether the new connection should have EPOLLONESHOT enabled by default
85 : * @return 0 on success, -1 on error
86 : */
87 0 : static int accept_new_connection(int socketfd, int epoll_fd, int oneshot) {
88 : int rtn;
89 : log(LOG_EPOLL_OPS, "Accepting a new connection");
90 :
91 : struct sockaddr_in client_addr;
92 0 : socklen_t addr_len = sizeof(client_addr);
93 :
94 0 : int new_fd = accept(socketfd, (struct sockaddr*) &client_addr, &addr_len);
95 0 : if (new_fd == -1) {
96 0 : log_perror("Error accepting new connection from socket handler");
97 0 : return -1;
98 : }
99 :
100 : /** If GET_NAME_INFO is defined, prints out the source of the connection */
101 : #ifdef GET_NAME_INFO
102 : char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
103 : rtn = getnameinfo((struct sockaddr*)&client_addr, addr_len,
104 : hbuf, sizeof(hbuf),
105 : sbuf, sizeof(sbuf),
106 : NI_NUMERICHOST| NI_NUMERICSERV);
107 : if ( rtn == 0) {
108 : log(LOG_EPOLL_OPS, "Accepted connection on descriptor %d"
109 : "host=%s, port=%s",
110 : new_fd, hbuf, sbuf);
111 : }
112 : #endif
113 :
114 0 : if (oneshot) {
115 0 : int flags = O_NONBLOCK;;
116 0 : rtn = fcntl(new_fd, F_SETFL, flags);
117 0 : if (rtn == -1) {
118 0 : log_perror("Error setting O_NONBLOCK");
119 : // TODO: Error handling :?
120 0 : return -1;
121 : }
122 : }
123 0 : rtn = add_to_epoll(epoll_fd, new_fd, EPOLLIN, oneshot);
124 0 : if (rtn < 0) {
125 0 : return -1;
126 : } else {
127 0 : return new_fd;
128 : }
129 : }
130 :
131 :
132 0 : int epoll_loop(int socket_fd, int epoll_fd, int batch_size, int timeout, bool oneshot,
133 : int (*connection_handler)(int, void*),
134 : int (*accept_handler)(int, void*),
135 : void *data) {
136 : struct epoll_event events[MAX_EPOLL_EVENTS];
137 :
138 0 : for (int j=0; j<batch_size || batch_size == -1; j++) {
139 : log(LOG_EPOLL_OPS, "Waiting on epoll");
140 0 : int n = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, timeout);
141 0 : if (n == 0) {
142 0 : break;
143 : }
144 : log(LOG_EPOLL_OPS, "Epoll returned %d events", n);
145 0 : for (int i=0; i < n; ++i) {
146 0 : if (socket_fd == events[i].data.fd) {
147 : log(LOG_EPOLL_OPS, "Accepting connection on %d", socket_fd);
148 0 : int new_fd = accept_new_connection(socket_fd, epoll_fd, oneshot);
149 0 : if ( new_fd < 0) {
150 : //log_error("Failed accepting new connection on epoll %d", epoll_fd);
151 0 : return -1;
152 : } else {
153 0 : if (accept_handler) {
154 0 : accept_handler(new_fd, data);
155 : }
156 : log(LOG_EPOLL_OPS, "Accepted new connection");
157 : }
158 : } else {
159 : log(LOG_EPOLL_OPS, "Processing connection (fd: %d)",
160 : events[i].data.fd);
161 0 : int rtn = connection_handler(events[i].data.fd, data);
162 0 : if (rtn != 0) {
163 0 : if (rtn < 0) {
164 0 : log_error("Failed processing existing connection (fd: %d)",
165 : events[i].data.fd);
166 : } else {
167 : log(LOG_EPOLL_OPS, "Got exit code %d from fd %d",
168 : rtn, events[i].data.fd);
169 : }
170 0 : return rtn;
171 : } else {
172 : log(LOG_EPOLL_OPS, "Processed connection (fd: %d)",
173 : events[i].data.fd);
174 : }
175 : }
176 : }
177 : }
178 0 : return 0;
179 : }
180 :
181 :
182 0 : int init_epoll(int socket_fd) {
183 0 : int epoll_fd = epoll_create1(0);
184 0 : if (epoll_fd == -1) {
185 0 : log_perror("epoll_create1() failed");
186 0 : return -1;
187 : }
188 :
189 0 : if (socket_fd > 0) {
190 : struct epoll_event event;
191 0 : memset(&event, 0, sizeof(event));
192 0 : event.data.fd = socket_fd;
193 0 : event.events = EPOLLIN;
194 :
195 0 : if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, &event) == -1) {
196 0 : log_perror("epoll_ctl() failed to add socket");
197 0 : close(epoll_fd);
198 0 : return -1;
199 : }
200 : }
201 : log(LOG_EPOLL_OPS, "Created epoll fd %d",epoll_fd);
202 0 : return epoll_fd;
203 : }
|