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