1 /*
2 * aprsc
3 *
4 * (c) Heikki Hannikainen, OH7LZB <hessu@hes.iki.fi>
5 *
6 * This program is licensed under the BSD license, which can be found
7 * in the file LICENSE.
8 *
9 */
10
11
12 /*
13 * xpoll.c - obfuscate poll(), epoll(), etc under a single API
14 *
15 * Please try to keep this module usable as a self-contained library,
16 * having and API of it's own, so that it can be reused without
17 * aprsc easily. Thanks!
18 *
19 * TODO: add kqueue support
20 */
21
22 #include <string.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include "xpoll.h"
27 #include "hmalloc.h"
28 #include "hlog.h"
29 #include "cellmalloc.h"
30
31 #ifdef XP_USE_EPOLL
32 const char xpoll_implementation[] = "epoll";
33 #endif
34 #ifdef XP_USE_POLL
35 const char xpoll_implementation[] = "poll";
36 #endif
37
38 #ifndef _FOR_VALGRIND_
39 cellarena_t *xpoll_fd_pool;
40 #endif
41
xpoll_init(void)42 void xpoll_init(void) {
43 #ifndef _FOR_VALGRIND_
44 xpoll_fd_pool = cellinit( "xpollfd",
45 sizeof(struct xpoll_fd_t),
46 __alignof__(struct xpoll_fd_t),
47 CELLMALLOC_POLICY_FIFO,
48 128 /* 128 kB */,
49 0 /* minfree */ );
50 #endif
51 }
52
xpoll_initialize(struct xpoll_t * xp,void * tp,int (* handler)(struct xpoll_t * xp,struct xpoll_fd_t * xfd))53 struct xpoll_t *xpoll_initialize(struct xpoll_t *xp, void *tp, int (*handler) (struct xpoll_t *xp, struct xpoll_fd_t *xfd))
54 {
55 xp->fds = NULL;
56 xp->tp = tp;
57 xp->handler = handler;
58
59 #ifdef XP_USE_EPOLL
60 //hlog(LOG_DEBUG, "xpoll: initializing %p using epoll()", (void *)xp);
61 xp->epollfd = epoll_create(1000);
62 if (xp->epollfd < 0) {
63 hlog(LOG_CRIT, "xpoll: epoll_create failed: %s", strerror(errno));
64 return NULL;
65 }
66
67 if (fcntl(xp->epollfd, F_SETFL, FD_CLOEXEC) == -1) {
68 hlog(LOG_ERR, "xpoll: fnctl FD_CLOEXEC on epollfd failed: %s", strerror(errno));
69 }
70 #else
71 #ifdef XP_USE_POLL
72 //hlog(LOG_DEBUG, "xpoll: initializing %p using poll()", (void *)xp);
73 xp->pollfd_len = XP_INCREMENT;
74 xp->pollfd = hmalloc(sizeof(struct pollfd) * xp->pollfd_len);
75 #endif
76 #endif
77 xp->pollfd_used = 0;
78
79 return xp;
80 }
81
xpoll_free(struct xpoll_t * xp)82 int xpoll_free(struct xpoll_t *xp)
83 {
84 struct xpoll_fd_t *xfd;
85
86 #ifdef XP_USE_EPOLL
87 close(xp->epollfd);
88 xp->epollfd = -1;
89 #endif
90 while (xp->fds) {
91 xfd = xp->fds->next;
92 #ifndef _FOR_VALGRIND_
93 cellfree( xpoll_fd_pool, xp->fds );
94 #else
95 hfree(xp->fds);
96 #endif
97 xp->fds = xfd;
98 }
99
100 #ifdef XP_USE_POLL
101 hfree(xp->pollfd);
102 #endif
103 return 0;
104 }
105
xpoll_add(struct xpoll_t * xp,int fd,void * p)106 struct xpoll_fd_t *xpoll_add(struct xpoll_t *xp, int fd, void *p)
107 {
108 struct xpoll_fd_t *xfd;
109
110 #ifndef _FOR_VALGRIND_
111 xfd = (struct xpoll_fd_t*) cellmalloc( xpoll_fd_pool );
112 if (!xfd) {
113 hlog(LOG_ERR, "xpoll: cellmalloc failed, too many FDs");
114 return NULL;
115 }
116 #else
117 xfd = (struct xpoll_fd_t*) hmalloc(sizeof(struct xpoll_fd_t));
118 #endif
119
120 xfd->fd = fd;
121 xfd->p = p;
122 xfd->next = xp->fds;
123 xfd->prevp = &xp->fds;
124 if (xfd->next)
125 xfd->next->prevp = &xfd->next;
126 xp->fds = xfd;
127
128 #ifdef XP_USE_EPOLL
129 xfd->ev.events = EPOLLIN; // | EPOLLET ?
130 // Each event has initialized callback pointer to struct xpoll_fd_t...
131 xfd->ev.data.ptr = xfd;
132 if (epoll_ctl(xp->epollfd, EPOLL_CTL_ADD, fd, &xfd->ev) == -1) {
133 hlog(LOG_ERR, "xpoll: epoll_ctl EPOL_CTL_ADD %d failed: %s", fd, strerror(errno));
134 return NULL;
135 }
136 #else
137 #ifdef XP_USE_POLL
138 if (xp->pollfd_used >= xp->pollfd_len) {
139 /* make the struct longer */
140 xp->pollfd_len += XP_INCREMENT;
141 xp->pollfd = hrealloc(xp->pollfd, sizeof(struct pollfd) * xp->pollfd_len);
142 }
143 /* append the new fd to the struct */
144 xp->pollfd[xp->pollfd_used].fd = fd;
145 xp->pollfd[xp->pollfd_used].events = POLLIN;
146 xfd->pollfd_n = xp->pollfd_used;
147 #endif
148 #endif
149 xp->pollfd_used++;
150
151 return xfd;
152 }
153
xpoll_remove(struct xpoll_t * xp,struct xpoll_fd_t * xfd)154 int xpoll_remove(struct xpoll_t *xp, struct xpoll_fd_t *xfd)
155 {
156 #ifdef XP_USE_EPOLL
157 if (xfd->fd >= 0) {
158 // Remove it from kernel polled events
159 if (epoll_ctl(xp->epollfd, EPOLL_CTL_DEL, xfd->fd, NULL) == -1) {
160 hlog(LOG_ERR, "xpoll: epoll_ctl EPOL_CTL_DEL %d failed: %s", xfd->fd, strerror(errno));
161 }
162 }
163 #else
164 #ifdef XP_USE_POLL
165 /* remove the fd from the pollfd struct by moving the tail of the struct
166 * to the left
167 */
168 if (xp->pollfd != NULL) {
169 void *to = (char *)xp->pollfd + (xfd->pollfd_n * sizeof(struct pollfd));
170 void *from = (char*)to + sizeof(struct pollfd);
171 int len = (xp->pollfd_used - xfd->pollfd_n - 1) * sizeof(struct pollfd);
172 //hlog(LOG_DEBUG, "xpoll_remove fd %d pollfd_n %d sizeof %d: 0x%x -> 0x%x len %d", xfd->fd, xfd->pollfd_n, sizeof(struct pollfd), (unsigned long)from, (unsigned long)to, len);
173 memmove(to, from, len);
174 }
175 /* reduce pollfd_n for fds which where moved */
176 struct xpoll_fd_t *xf;
177 for (xf = xp->fds; (xf); xf = xf->next) {
178 if (xf->pollfd_n > xfd->pollfd_n) {
179 xf->pollfd_n--;
180 //hlog(LOG_DEBUG, " ... fd %d is now pollfd_n %d", xf->fd, xf->pollfd_n);
181 }
182 }
183 #endif
184 #endif
185 xp->pollfd_used--;
186
187 *xfd->prevp = xfd->next;
188 if (xfd->next) {
189 xfd->next->prevp = xfd->prevp;
190 }
191
192 #ifndef _FOR_VALGRIND_
193 cellfree( xpoll_fd_pool, xfd );
194 #else
195 hfree(xfd);
196 #endif
197 return 0;
198 }
199
200 /*
201 * enable / disable polling for writeability of a socket
202 */
203
xpoll_outgoing(struct xpoll_t * xp,struct xpoll_fd_t * xfd,int have_outgoing)204 void xpoll_outgoing(struct xpoll_t *xp, struct xpoll_fd_t *xfd, int have_outgoing)
205 {
206 #ifdef XP_USE_EPOLL
207 if (have_outgoing) {
208 xfd->ev.events |= EPOLLOUT;
209 } else {
210 xfd->ev.events &= EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP;
211 }
212 if (epoll_ctl(xp->epollfd, EPOLL_CTL_MOD, xfd->fd, &xfd->ev) == -1) {
213 hlog(LOG_ERR, "xpoll_outgoing: epoll_ctl EPOL_CTL_MOD %d failed: %s", xfd->fd, strerror(errno));
214 }
215 #else
216 #ifdef XP_USE_POLL
217 if (have_outgoing)
218 xp->pollfd[xfd->pollfd_n].events |= POLLOUT;
219 else
220 xp->pollfd[xfd->pollfd_n].events &= POLLIN|POLLPRI|POLLERR|POLLHUP|POLLNVAL;
221 #endif
222 #endif
223 }
224
225 /*
226 * do a poll
227 */
228
xpoll(struct xpoll_t * xp,int timeout)229 int xpoll(struct xpoll_t *xp, int timeout)
230 {
231 #ifdef XP_USE_EPOLL
232 #define MAX_EPOLL_EVENTS 32
233 struct epoll_event events[MAX_EPOLL_EVENTS];
234
235 int nfds = epoll_wait( xp->epollfd, events, MAX_EPOLL_EVENTS, timeout );
236 int n;
237 for (n = 0; n < nfds; ++n) {
238 // Each event has initialized callback pointer to struct xpoll_fd_t...
239 struct xpoll_fd_t *xfd = (struct xpoll_fd_t*) events[n].data.ptr;
240 xfd->result = 0;
241 if (events[n].events & (EPOLLIN|EPOLLPRI))
242 xfd->result |= XP_IN;
243 if (events[n].events & (EPOLLOUT))
244 xfd->result |= XP_OUT;
245 if (events[n].events & (EPOLLERR|EPOLLHUP))
246 xfd->result |= XP_ERR;
247 (*xp->handler)(xp, xfd);
248 }
249 return nfds;
250 #else
251 #ifdef XP_USE_POLL
252 struct xpoll_fd_t *xfd, *next;
253 int r = poll(xp->pollfd, xp->pollfd_used, timeout);
254 //hlog(LOG_DEBUG, "poll() returned %d", r);
255 if (r <= 0)
256 return r;
257 for (xfd = xp->fds; xfd; xfd = next) {
258 next = xfd->next; /* the current one might be deleted by a handler */
259 //hlog(LOG_DEBUG, "... checking fd %d", xfd->fd);
260 xfd->result = 0;
261 if (xp->pollfd[xfd->pollfd_n].revents) {
262 if (xp->pollfd[xfd->pollfd_n].revents & (POLLIN|POLLPRI))
263 xfd->result |= XP_IN;
264 if (xp->pollfd[xfd->pollfd_n].revents & POLLOUT)
265 xfd->result |= XP_OUT;
266 if (xp->pollfd[xfd->pollfd_n].revents & (POLLERR|POLLHUP|POLLNVAL))
267 xfd->result |= XP_ERR;
268 (*xp->handler)(xp, xfd);
269 }
270 }
271 return r;
272 #endif
273 #endif
274 }
275