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