1 /*  Copyright (C) 2015-2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2  *  SPDX-License-Identifier: GPL-3.0-or-later
3  */
4 
5 #include "daemon/bindings/impl.h"
6 
7 #include <unistd.h>
8 #include <uv.h>
9 
event_free(uv_timer_t * timer)10 static void event_free(uv_timer_t *timer)
11 {
12 	lua_State *L = the_worker->engine->L;
13 	int ref = (intptr_t) timer->data;
14 	luaL_unref(L, LUA_REGISTRYINDEX, ref);
15 	free(timer);
16 }
17 
event_callback(uv_timer_t * timer)18 static void event_callback(uv_timer_t *timer)
19 {
20 	lua_State *L = the_worker->engine->L;
21 
22 	/* Retrieve callback and execute */
23 	lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) timer->data);
24 	lua_rawgeti(L, -1, 1);
25 	lua_pushinteger(L, (intptr_t) timer->data);
26 	int ret = execute_callback(L, 1);
27 	/* Free callback if not recurrent or an error */
28 	if (ret != 0 || (uv_timer_get_repeat(timer) == 0 && uv_is_active((uv_handle_t *)timer) == 0)) {
29 		if (!uv_is_closing((uv_handle_t *)timer)) {
30 			uv_close((uv_handle_t *)timer, (uv_close_cb) event_free);
31 		}
32 	}
33 }
34 
event_fdcallback(uv_poll_t * handle,int status,int events)35 static void event_fdcallback(uv_poll_t* handle, int status, int events)
36 {
37 	lua_State *L = the_worker->engine->L;
38 
39 	/* Retrieve callback and execute */
40 	lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) handle->data);
41 	lua_rawgeti(L, -1, 1);
42 	lua_pushinteger(L, (intptr_t) handle->data);
43 	lua_pushinteger(L, status);
44 	lua_pushinteger(L, events);
45 	int ret = execute_callback(L, 3);
46 	/* Free callback if not recurrent or an error */
47 	if (ret != 0) {
48 		if (!uv_is_closing((uv_handle_t *)handle)) {
49 			uv_close((uv_handle_t *)handle, (uv_close_cb) event_free);
50 		}
51 	}
52 }
53 
event_sched(lua_State * L,unsigned timeout,unsigned repeat)54 static int event_sched(lua_State *L, unsigned timeout, unsigned repeat)
55 {
56 	uv_timer_t *timer = malloc(sizeof(*timer));
57 	if (!timer)
58 		lua_error_p(L, "out of memory");
59 
60 	/* Start timer with the reference */
61 	uv_loop_t *loop = uv_default_loop();
62 	uv_timer_init(loop, timer);
63 	int ret = uv_timer_start(timer, event_callback, timeout, repeat);
64 	if (ret != 0) {
65 		free(timer);
66 		lua_error_p(L, "couldn't start the event");
67 	}
68 
69 	/* Save callback and timer in registry */
70 	lua_newtable(L);
71 	lua_pushvalue(L, 2);
72 	lua_rawseti(L, -2, 1);
73 	lua_pushpointer(L, timer);
74 	lua_rawseti(L, -2, 2);
75 	int ref = luaL_ref(L, LUA_REGISTRYINDEX);
76 
77 	/* Save reference to the timer */
78 	timer->data = (void *) (intptr_t)ref;
79 	lua_pushinteger(L, ref);
80 	return 1;
81 }
82 
event_after(lua_State * L)83 static int event_after(lua_State *L)
84 {
85 	/* Check parameters */
86 	int n = lua_gettop(L);
87 	if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2))
88 		lua_error_p(L, "expected 'after(number timeout, function)'");
89 
90 	return event_sched(L, lua_tointeger(L, 1), 0);
91 }
92 
event_recurrent(lua_State * L)93 static int event_recurrent(lua_State *L)
94 {
95 	/* Check parameters */
96 	int n = lua_gettop(L);
97 	if (n < 2 || !lua_isnumber(L, 1)  || lua_tointeger(L, 1) == 0
98 		|| !lua_isfunction(L, 2))
99 		lua_error_p(L, "expected 'recurrent(number interval, function)'");
100 
101 	return event_sched(L, 0, lua_tointeger(L, 1));
102 }
103 
event_cancel(lua_State * L)104 static int event_cancel(lua_State *L)
105 {
106 	int n = lua_gettop(L);
107 	if (n < 1 || !lua_isnumber(L, 1))
108 		lua_error_p(L, "expected 'cancel(number event)'");
109 
110 	/* Fetch event if it exists */
111 	lua_rawgeti(L, LUA_REGISTRYINDEX, lua_tointeger(L, 1));
112 	bool ok = lua_istable(L, -1);
113 
114 	/* Close the timer */
115 	uv_handle_t **timer_pp = NULL;
116 	if (ok) {
117 		lua_rawgeti(L, -1, 2);
118 		timer_pp = lua_touserdata(L, -1);
119 		ok = timer_pp && *timer_pp;
120 		/* That have been sufficient safety checks, hopefully. */
121 	}
122 	if (ok && !uv_is_closing(*timer_pp)) {
123 		uv_close(*timer_pp, (uv_close_cb)event_free);
124 	}
125 	lua_pushboolean(L, ok);
126 	return 1;
127 }
128 
event_reschedule(lua_State * L)129 static int event_reschedule(lua_State *L)
130 {
131 	int n = lua_gettop(L);
132 	if (n < 2 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2))
133 		lua_error_p(L, "expected 'reschedule(number event, number timeout)'");
134 
135 	/* Fetch event if it exists */
136 	lua_rawgeti(L, LUA_REGISTRYINDEX, lua_tointeger(L, 1));
137 	bool ok = lua_istable(L, -1);
138 
139 	/* Reschedule the timer */
140 	uv_handle_t **timer_pp = NULL;
141 	if (ok) {
142 		lua_rawgeti(L, -1, 2);
143 		timer_pp = lua_touserdata(L, -1);
144 		ok = timer_pp && *timer_pp;
145 		/* That have been sufficient safety checks, hopefully. */
146 	}
147 	if (ok && !uv_is_closing(*timer_pp)) {
148 		int ret = uv_timer_start((uv_timer_t *)*timer_pp,
149 				event_callback, lua_tointeger(L, 2), 0);
150 		if (ret != 0) {
151 			uv_close(*timer_pp, (uv_close_cb)event_free);
152 			ok = false;
153 		}
154 	}
155 	lua_pushboolean(L, ok);
156 	return 1;
157 }
158 
event_fdwatch(lua_State * L)159 static int event_fdwatch(lua_State *L)
160 {
161 	/* Check parameters */
162 	int n = lua_gettop(L);
163 	if (n < 2 || !lua_isnumber(L, 1) || !lua_isfunction(L, 2))
164 		lua_error_p(L, "expected 'socket(number fd, function)'");
165 
166 	uv_poll_t *handle = malloc(sizeof(*handle));
167 	if (!handle)
168 		lua_error_p(L, "out of memory");
169 
170 	/* Start timer with the reference */
171 	int sock = lua_tointeger(L, 1);
172 	uv_loop_t *loop = uv_default_loop();
173 	int ret = uv_poll_init(loop, handle, sock);
174 	if (ret == 0)
175 		ret = uv_poll_start(handle, UV_READABLE, event_fdcallback);
176 	if (ret != 0) {
177 		free(handle);
178 		lua_error_p(L, "couldn't start event poller");
179 	}
180 
181 	/* Save callback and timer in registry */
182 	lua_newtable(L);
183 	lua_pushvalue(L, 2);
184 	lua_rawseti(L, -2, 1);
185 	lua_pushpointer(L, handle);
186 	lua_rawseti(L, -2, 2);
187 	int ref = luaL_ref(L, LUA_REGISTRYINDEX);
188 
189 	/* Save reference to the timer */
190 	handle->data = (void *) (intptr_t)ref;
191 	lua_pushinteger(L, ref);
192 	return 1;
193 }
194 
kr_bindings_event(lua_State * L)195 int kr_bindings_event(lua_State *L)
196 {
197 	static const luaL_Reg lib[] = {
198 		{ "after",      event_after },
199 		{ "recurrent",  event_recurrent },
200 		{ "cancel",     event_cancel },
201 		{ "socket",     event_fdwatch },
202 		{ "reschedule", event_reschedule },
203 		{ NULL, NULL }
204 	};
205 
206 	luaL_register(L, "event", lib);
207 	return 1;
208 }
209 
210