1 /**
2  * \file libevent.c
3  * \brief Eventloop extension for libevent
4  *
5  */
6 
7 /*
8  * Copyright (c) 2013, NLNet Labs, Verisign, Inc.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions are met:
13  * * Redistributions of source code must retain the above copyright
14  *   notice, this list of conditions and the following disclaimer.
15  * * Redistributions in binary form must reproduce the above copyright
16  *   notice, this list of conditions and the following disclaimer in the
17  *   documentation and/or other materials provided with the distribution.
18  * * Neither the names of the copyright holders nor the
19  *   names of its contributors may be used to endorse or promote products
20  *   derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25  * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY
26  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "config.h"
35 #include "types-internal.h"
36 #include <sys/time.h>
37 #include "getdns/getdns_ext_libevent.h"
38 
39 #ifdef HAVE_EVENT2_EVENT_H
40 #  include <event2/event.h>
41 #else
42 #  include <event.h>
43 #  define evutil_socket_t int
44 #  define event_free free
45 #  define evtimer_new(b, cb, arg) event_new((b), -1, 0, (cb), (arg))
46 #endif
47 
48 #ifndef HAVE_EVENT_BASE_FREE
49 #define event_base_free(x) /* nop */
50 #endif
51 #ifndef HAVE_EVENT_BASE_NEW
52 #define event_base_new event_init
53 #endif
54 
55 #ifndef HAVE_EVENT2_EVENT_H
56 static struct event *
event_new(struct event_base * b,evutil_socket_t fd,short ev,void (* cb)(int,short,void *),void * arg)57 event_new(struct event_base *b, evutil_socket_t fd, short ev, void (*cb)(int, short, void*), void *arg)
58 {
59 	struct event* e = (struct event*)calloc(1, sizeof(struct event));
60 
61 	if (!e) return NULL;
62 	event_set(e, fd, ev, cb, arg);
63 	event_base_set(b, e);
64 	return e;
65 }
66 #endif /* no event2 */
67 
68 typedef struct getdns_libevent {
69 	getdns_eventloop_vmt *vmt;
70         struct event_base    *base;
71 	struct mem_funcs      mf;
72 } getdns_libevent;
73 
74 static void
getdns_libevent_run(getdns_eventloop * loop)75 getdns_libevent_run(getdns_eventloop *loop)
76 {
77 	(void) event_base_dispatch(((getdns_libevent *)loop)->base);
78 }
79 
80 static void
getdns_libevent_run_once(getdns_eventloop * loop,int blocking)81 getdns_libevent_run_once(getdns_eventloop *loop, int blocking)
82 {
83 	(void) event_base_loop(((getdns_libevent *)loop)->base,
84 	    EVLOOP_ONCE | (blocking ? EVLOOP_NONBLOCK : 0));
85 }
86 
87 static void
getdns_libevent_cleanup(getdns_eventloop * loop)88 getdns_libevent_cleanup(getdns_eventloop *loop)
89 {
90 	getdns_libevent *ext = (getdns_libevent *)loop;
91 	GETDNS_FREE(ext->mf, ext);
92 }
93 
94 static getdns_return_t
getdns_libevent_clear(getdns_eventloop * loop,getdns_eventloop_event * el_ev)95 getdns_libevent_clear(getdns_eventloop *loop, getdns_eventloop_event *el_ev)
96 {
97 	struct event *my_ev = (struct event *)el_ev->ev;
98 	(void)loop;
99 
100 	assert(my_ev);
101 
102 	if (event_del(my_ev) != 0)
103 		return GETDNS_RETURN_GENERIC_ERROR;
104 
105 	event_free(my_ev);
106 	el_ev->ev = NULL;
107 	return GETDNS_RETURN_GOOD;
108 }
109 
110 static void
getdns_libevent_callback(evutil_socket_t fd,short bits,void * arg)111 getdns_libevent_callback(evutil_socket_t fd, short bits, void *arg)
112 {
113 	getdns_eventloop_event *el_ev = (getdns_eventloop_event *)arg;
114 	(void)fd;
115 
116 	if (bits & EV_READ) {
117 		assert(el_ev->read_cb);
118 		el_ev->read_cb(el_ev->userarg);
119 	} else if (bits & EV_WRITE) {
120 		assert(el_ev->write_cb);
121 		el_ev->write_cb(el_ev->userarg);
122 	} else if (bits & EV_TIMEOUT) {
123 		assert(el_ev->timeout_cb);
124 		el_ev->timeout_cb(el_ev->userarg);
125 	} else
126 		assert(ASSERT_UNREACHABLE);
127 }
128 
129 static getdns_return_t
getdns_libevent_schedule(getdns_eventloop * loop,int fd,uint64_t timeout,getdns_eventloop_event * el_ev)130 getdns_libevent_schedule(getdns_eventloop *loop,
131     int fd, uint64_t timeout, getdns_eventloop_event *el_ev)
132 {
133 	getdns_libevent *ext = (getdns_libevent *)loop;
134 	struct event *my_ev;
135 	struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 };
136 
137 	assert(el_ev);
138 	assert(!(el_ev->read_cb || el_ev->write_cb) || fd >= 0);
139 	assert(  el_ev->read_cb || el_ev->write_cb  || el_ev->timeout_cb);
140 
141 	my_ev = event_new(ext->base, fd, (
142 	    (el_ev->read_cb ? EV_READ|EV_PERSIST : 0) |
143 	    (el_ev->write_cb ? EV_WRITE|EV_PERSIST : 0) |
144 	    (el_ev->timeout_cb ? EV_TIMEOUT : 0)),
145 	    getdns_libevent_callback, el_ev);
146 	if (!my_ev)
147 		return GETDNS_RETURN_MEMORY_ERROR;
148 
149 	el_ev->ev = my_ev;
150 
151 	if (event_add(my_ev, el_ev->timeout_cb ? &tv : NULL)) {
152 		event_free(my_ev);
153 		el_ev->ev = NULL;
154 		return GETDNS_RETURN_GENERIC_ERROR;
155 	}
156 	return GETDNS_RETURN_GOOD;
157 }
158 
159 getdns_return_t
getdns_extension_set_libevent_base(getdns_context * context,struct event_base * base)160 getdns_extension_set_libevent_base(getdns_context *context,
161     struct event_base *base)
162 {
163 	static getdns_eventloop_vmt getdns_libevent_vmt = {
164 		getdns_libevent_cleanup,
165 		getdns_libevent_schedule,
166 		getdns_libevent_clear,
167 		getdns_libevent_run,
168 		getdns_libevent_run_once
169 	};
170 	getdns_libevent *ext;
171 
172 	if (!context)
173 		return GETDNS_RETURN_BAD_CONTEXT;
174 	if (!base)
175 		return GETDNS_RETURN_INVALID_PARAMETER;
176 
177 	ext = GETDNS_MALLOC(*priv_getdns_context_mf(context), getdns_libevent);
178 	if (!ext)
179 		return GETDNS_RETURN_MEMORY_ERROR;
180 
181 	ext->vmt  = &getdns_libevent_vmt;
182 	ext->base = base;
183 	ext->mf   = *priv_getdns_context_mf(context);
184 
185 	return getdns_context_set_eventloop(context, (getdns_eventloop *)ext);
186 }
187