1 /***
2   This file is part of avahi.
3 
4   avahi is free software; you can redistribute it and/or modify it
5   under the terms of the GNU Lesser General Public License as
6   published by the Free Software Foundation; either version 2.1 of the
7   License, or (at your option) any later version.
8 
9   avahi is distributed in the hope that it will be useful, but WITHOUT
10   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12   Public License for more details.
13 
14   You should have received a copy of the GNU Lesser General Public
15   License along with avahi; if not, write to the Free Software
16   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17   USA.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include <stdlib.h>
25 #include <event2/event.h>
26 #include <event2/event_struct.h>
27 
28 #include <avahi-common/llist.h>
29 #include <avahi-common/malloc.h>
30 #include <avahi-common/timeval.h>
31 #include <avahi-common/watch.h>
32 
33 #include "libevent-watch.h"
34 
35 #ifndef HOST_NAME_MAX
36 # include <limits.h>
37 # define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
38 #endif
39 
40 struct AvahiWatch {
41 	AvahiLibeventPoll *eventpoll;
42 
43 	struct event ev;
44 
45 	AvahiWatchCallback cb;
46 	void *userdata;
47 
48 	AVAHI_LLIST_FIELDS(AvahiWatch, watches);
49 };
50 
51 struct AvahiTimeout {
52 	AvahiLibeventPoll *eventpoll;
53 
54 	struct event ev;
55 
56 	AvahiTimeoutCallback cb;
57 	void *userdata;
58 
59 	AVAHI_LLIST_FIELDS(AvahiTimeout, timeouts);
60 };
61 
62 struct AvahiLibeventPoll {
63 	AvahiPoll api;
64 
65 	struct event_base *base;
66 
67 	AVAHI_LLIST_HEAD(AvahiWatch, watches);
68 	AVAHI_LLIST_HEAD(AvahiTimeout, timeouts);
69 };
70 
71 /* AvahiPoll implementation for libevent */
72 
73 static void
watch_cb(evutil_socket_t fd,short what,void * arg)74 watch_cb(evutil_socket_t fd, short what, void *arg)
75 {
76 	AvahiWatch *w = arg;
77 	AvahiWatchEvent events = 0;
78 
79 	if (what & EV_READ)
80 		events |= AVAHI_WATCH_IN;
81 	if (what & EV_WRITE)
82 		events |= AVAHI_WATCH_OUT;
83 
84 	w->cb(w, fd, events, w->userdata);
85 }
86 
87 static int
watch_add(AvahiWatch * w,int fd,AvahiWatchEvent events)88 watch_add(AvahiWatch *w, int fd, AvahiWatchEvent events)
89 {
90 	AvahiLibeventPoll *ep = w->eventpoll;
91 	short ev_events = 0;
92 
93 	if (events & AVAHI_WATCH_IN)
94 		ev_events |= EV_READ;
95 	if (events & AVAHI_WATCH_OUT)
96 		ev_events |= EV_WRITE;
97 
98 	event_assign(&w->ev, ep->base, fd, ev_events | EV_PERSIST, watch_cb, w);
99 
100 	return event_add(&w->ev, NULL);
101 }
102 
103 static AvahiWatch *
watch_new(const AvahiPoll * api,int fd,AvahiWatchEvent events,AvahiWatchCallback cb,void * userdata)104 watch_new(const AvahiPoll *api, int fd, AvahiWatchEvent events, AvahiWatchCallback cb, void *userdata)
105 {
106 	AvahiLibeventPoll *ep;
107 	AvahiWatch *w;
108 	int ret;
109 
110 	assert(api);
111 	assert(fd >= 0);
112 	assert(cb);
113 
114 	ep = api->userdata;
115 	assert(ep);
116 
117 	w = avahi_new(AvahiWatch, 1);
118 	if (!w)
119 		return NULL;
120 
121 	w->eventpoll = ep;
122 	w->cb = cb;
123 	w->userdata = userdata;
124 
125 	ret = watch_add(w, fd, events);
126 	if (ret != 0) {
127 		free(w);
128 		return NULL;
129 	}
130 
131 	AVAHI_LLIST_PREPEND(AvahiWatch, watches, ep->watches, w);
132 
133 	return w;
134 }
135 
136 static void
watch_update(AvahiWatch * w,AvahiWatchEvent events)137 watch_update(AvahiWatch *w, AvahiWatchEvent events)
138 {
139 	event_del(&w->ev);
140 
141 	watch_add(w, (int)event_get_fd(&w->ev), events);
142 }
143 
144 static AvahiWatchEvent
watch_get_events(AvahiWatch * w)145 watch_get_events(AvahiWatch *w)
146 {
147 	AvahiWatchEvent events = 0;
148 
149 	if (event_pending(&w->ev, EV_READ, NULL))
150 		events |= AVAHI_WATCH_IN;
151 	if (event_pending(&w->ev, EV_WRITE, NULL))
152 		events |= AVAHI_WATCH_OUT;
153 
154 	return events;
155 }
156 
157 static void
watch_free(AvahiWatch * w)158 watch_free(AvahiWatch *w)
159 {
160 	AvahiLibeventPoll *ep = w->eventpoll;
161 
162 	event_del(&w->ev);
163 
164 	AVAHI_LLIST_REMOVE(AvahiWatch, watches, ep->watches, w);
165 
166 	free(w);
167 }
168 
169 static void
timeout_cb(AVAHI_GCC_UNUSED evutil_socket_t fd,AVAHI_GCC_UNUSED short events,void * arg)170 timeout_cb(AVAHI_GCC_UNUSED evutil_socket_t fd, AVAHI_GCC_UNUSED short events, void *arg)
171 {
172 	AvahiTimeout *t = arg;
173 
174 	t->cb(t, t->userdata);
175 }
176 
177 static int
timeout_add(AvahiTimeout * t,const struct timeval * tv)178 timeout_add(AvahiTimeout *t, const struct timeval *tv)
179 {
180 	AvahiLibeventPoll *ep = t->eventpoll;
181 	struct timeval now, e_tv;
182 
183 	event_assign(&t->ev, ep->base, -1, EV_TIMEOUT, timeout_cb, t);
184 
185 	if (!tv || ((tv->tv_sec == 0) && (tv->tv_usec == 0)))
186 		evutil_timerclear(&e_tv);
187 	else {
188 		(void)gettimeofday(&now, NULL);
189 		evutil_timersub(tv, &now, &e_tv);
190 	}
191 
192 	return evtimer_add(&t->ev, &e_tv);
193 }
194 
195 static AvahiTimeout *
timeout_new(const AvahiPoll * api,const struct timeval * tv,AvahiTimeoutCallback cb,void * userdata)196 timeout_new(const AvahiPoll *api, const struct timeval *tv, AvahiTimeoutCallback cb, void *userdata)
197 {
198 	AvahiLibeventPoll *ep;
199 	AvahiTimeout *t;
200 	int ret;
201 
202 	assert(api);
203 	assert(cb);
204 
205 	ep = api->userdata;
206 
207 	assert(ep);
208 
209 	t = avahi_new(AvahiTimeout, 1);
210 	if (!t)
211 		return NULL;
212 
213 	t->eventpoll = ep;
214 	t->cb = cb;
215 	t->userdata = userdata;
216 
217 	ret = timeout_add(t, tv);
218 	if (ret != 0) {
219 		free(t);
220 		return NULL;
221 	}
222 
223 	AVAHI_LLIST_PREPEND(AvahiTimeout, timeouts, ep->timeouts, t);
224 
225 	return t;
226 }
227 
228 static void
timeout_update(AvahiTimeout * t,const struct timeval * tv)229 timeout_update(AvahiTimeout *t, const struct timeval *tv)
230 {
231 	struct timeval now, e_tv;
232 
233 	event_del(&t->ev);
234 
235 	if (!tv)
236 		return;
237 
238 	(void)gettimeofday(&now, NULL);
239 	evutil_timersub(tv, &now, &e_tv);
240 
241 	event_add(&t->ev, &e_tv);
242 }
243 
244 static void
timeout_free(AvahiTimeout * t)245 timeout_free(AvahiTimeout *t)
246 {
247 	AvahiLibeventPoll *ep = t->eventpoll;
248 
249 	event_del(&t->ev);
250 
251 	AVAHI_LLIST_REMOVE(AvahiTimeout, timeouts, ep->timeouts, t);
252 
253 	free(t);
254 }
255 
256 AvahiLibeventPoll *
avahi_libevent_poll_new(struct event_base * base)257 avahi_libevent_poll_new(struct event_base *base)
258 {
259 	AvahiLibeventPoll *ep = avahi_new(AvahiLibeventPoll, 1);
260 
261 	ep->base = base;
262 
263 	ep->api.userdata = ep;
264 
265 	ep->api.watch_new = watch_new;
266 	ep->api.watch_free = watch_free;
267 	ep->api.watch_update = watch_update;
268 	ep->api.watch_get_events = watch_get_events;
269 
270 	ep->api.timeout_new = timeout_new;
271 	ep->api.timeout_free = timeout_free;
272 	ep->api.timeout_update = timeout_update;
273 
274 	AVAHI_LLIST_HEAD_INIT(AvahiWatch, ep->watches);
275 	AVAHI_LLIST_HEAD_INIT(AvahiTimeout, ep->timeouts);
276 
277 	return ep;
278 }
279 
280 void
avahi_libevent_poll_free(AvahiLibeventPoll * ep)281 avahi_libevent_poll_free(AvahiLibeventPoll *ep)
282 {
283 	assert(ep);
284 
285 	for (AvahiWatch *w_next, *w = ep->watches; w; w = w_next) {
286 		w_next = w->watches_next;
287 
288 		watch_free(w);
289 	}
290 
291 	for (AvahiTimeout *t_next, *t = ep->timeouts; t; t = t_next) {
292 		t_next = t->timeouts_next;
293 
294 		timeout_free(t);
295 	}
296 
297 	free(ep);
298 }
299 
300 void
avahi_libevent_poll_quit(AvahiLibeventPoll * ep)301 avahi_libevent_poll_quit(AvahiLibeventPoll *ep)
302 {
303 	assert(ep);
304 
305 	/* we don't actually have anything to do, since events are
306 	 * associated with watches and timeouts, not with this
307 	 * polling object itself.
308 	 */
309 }
310 
311 const AvahiPoll *
avahi_libevent_poll_get(AvahiLibeventPoll * ep)312 avahi_libevent_poll_get(AvahiLibeventPoll *ep)
313 {
314 	assert(ep);
315 
316 	return &ep->api;
317 }
318 
319