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