blob: 2d37d9b1e8ed10acb8a2daef3386e9ae845ea989 [file] [log] [blame]
Thierry FOURNIER6908c952018-02-23 15:20:55 +01001/* spoa-server: processing Lua
2 *
3 * Copyright 2018 OZON / Thierry Fournier <thierry.fournier@ozon.io>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 *
10 */
11
12#include <arpa/inet.h>
13
14#include <errno.h>
15#include <string.h>
16
17#include <lauxlib.h>
18#include <lua.h>
19#include <lualib.h>
20
21#include "spoa.h"
22
23static lua_State *L = NULL;
24static struct worker *worker;
25
26static int ps_lua_start_worker(struct worker *w);
27static int ps_lua_load_file(struct worker *w, const char *file);
28static int ps_lua_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args);
29
30static struct ps ps_lua_bindings_1 = {
31 .init_worker = ps_lua_start_worker,
32 .load_file = ps_lua_load_file,
33 .exec_message = ps_lua_exec_message,
34 .ext = ".lua",
35};
36
37static struct ps ps_lua_bindings_2 = {
38 .init_worker = ps_lua_start_worker,
39 .load_file = ps_lua_load_file,
40 .exec_message = ps_lua_exec_message,
41 .ext = ".luac",
42};
43
44/* Imported from Lua-5.3.4 */
45static int typeerror (lua_State *L, int arg, const char *tname)
46{
47 const char *msg;
48 const char *typearg; /* name for the type of the actual argument */
49 if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
50 typearg = lua_tostring(L, -1); /* use the given type name */
51 else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA)
52 typearg = "light userdata"; /* special name for messages */
53 else
54 typearg = luaL_typename(L, arg); /* standard name */
55 msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg);
56 return luaL_argerror(L, arg, msg);
57}
58
59/* Imported from Lua-5.3.4 */
60static void tag_error (lua_State *L, int arg, int tag) {
61 typeerror(L, arg, lua_typename(L, tag));
62}
63
64#ifndef luaL_checkboolean
65static int luaL_checkboolean(lua_State *L, int index)
66{
67 if (!lua_isboolean(L, index)) {
68 tag_error(L, index, LUA_TBOOLEAN);
69 }
70 return lua_toboolean(L, index);
71}
72#endif
73
74static int ps_lua_register_message(lua_State *L)
75{
76 const char *name;
77 long ref;
78
79 /* First argument is a message name */
80 name = luaL_checkstring(L, 1);
81
82 /* Second argument is a function */
83 if (!lua_isfunction(L, 2)) {
84 const char *msg = lua_pushfstring(L, "function expected, got %s", luaL_typename(L, 2));
85 luaL_argerror(L, 2, msg);
86 }
87 lua_pushvalue(L, 2);
88 ref = luaL_ref(L, LUA_REGISTRYINDEX);
89
90 /* Register the message processor */
91 ps_register_message(&ps_lua_bindings_1, name, (void *)ref);
92
93 return 1;
94}
95
96static int ps_lua_set_var_null(lua_State *L)
97{
98 const char *name;
99 size_t name_len;
100 unsigned char scope;
101
102 name = luaL_checklstring(L, 1, &name_len);
103 scope = (unsigned char)luaL_checkinteger(L, 2);
104
105 if (!set_var_null(worker, name, name_len, scope)) {
106 luaL_error(L, "No space left available");
107 }
108 return 0;
109}
110
111static int ps_lua_set_var_boolean(lua_State *L)
112{
113 const char *name;
114 size_t name_len;
115 unsigned char scope;
116 int64_t value;
117
118 name = luaL_checklstring(L, 1, &name_len);
119 scope = (unsigned char)luaL_checkinteger(L, 2);
120 value = luaL_checkboolean(L, 3);
121
122 if (!set_var_bool(worker, name, name_len, scope, value))
123 luaL_error(L, "No space left available");
124 return 0;
125}
126
127static int ps_lua_set_var_uint32(lua_State *L)
128{
129 const char *name;
130 size_t name_len;
131 unsigned char scope;
132 int64_t value;
133
134 name = luaL_checklstring(L, 1, &name_len);
135 scope = (unsigned char)luaL_checkinteger(L, 2);
136 value = luaL_checkinteger(L, 3);
137
138 if (value < 0 || value > UINT_MAX)
139 luaL_error(L, "Integer '%lld' out of range for 'uint32' type", value);
140
141 if (!set_var_uint32(worker, name, name_len, scope, value))
142 luaL_error(L, "No space left available");
143 return 0;
144}
145
146static int ps_lua_set_var_int32(lua_State *L)
147{
148 const char *name;
149 size_t name_len;
150 unsigned char scope;
151 int64_t value;
152
153 name = luaL_checklstring(L, 1, &name_len);
154 scope = (unsigned char)luaL_checkinteger(L, 2);
155 value = luaL_checkinteger(L, 3);
156
157 if (value < INT_MIN || value > INT_MAX)
158 luaL_error(L, "Integer '%lld' out of range for 'int32' type", value);
159
160 if (!set_var_int32(worker, name, name_len, scope, value))
161 luaL_error(L, "No space left available");
162 return 0;
163}
164
165static int ps_lua_set_var_uint64(lua_State *L)
166{
167 const char *name;
168 size_t name_len;
169 unsigned char scope;
170 int64_t value;
171
172 name = luaL_checklstring(L, 1, &name_len);
173 scope = (unsigned char)luaL_checkinteger(L, 2);
174 value = luaL_checkinteger(L, 3);
175
176 if (value < 0)
177 luaL_error(L, "Integer '%lld' out of range for 'uint64' type", value);
178
179 if (!set_var_uint64(worker, name, name_len, scope, value))
180 luaL_error(L, "No space left available");
181 return 0;
182}
183
184static int ps_lua_set_var_int64(lua_State *L)
185{
186 const char *name;
187 size_t name_len;
188 unsigned char scope;
189 int64_t value;
190
191 name = luaL_checklstring(L, 1, &name_len);
192 scope = (unsigned char)luaL_checkinteger(L, 2);
193 value = luaL_checkinteger(L, 3);
194
195 if (!set_var_int64(worker, name, name_len, scope, value))
196 luaL_error(L, "No space left available");
197 return 0;
198}
199
200static int ps_lua_set_var_ipv4(lua_State *L)
201{
202 const char *name;
203 size_t name_len;
204 unsigned char scope;
205 const char *value;
206 struct in_addr ipv4;
207 int ret;
208
209 name = luaL_checklstring(L, 1, &name_len);
210 scope = (unsigned char)luaL_checkinteger(L, 2);
211 value = luaL_checkstring(L, 3);
212
213 ret = inet_pton(AF_INET, value, &ipv4);
214 if (ret == 0)
215 luaL_error(L, "IPv4 '%s': invalid format", value);
216 if (ret == -1)
217 luaL_error(L, "IPv4 '%s': %s", value, strerror(errno));
218
219 if (!set_var_ipv4(worker, name, name_len, scope, &ipv4))
220 luaL_error(L, "No space left available");
221 return 0;
222}
223
224static int ps_lua_set_var_ipv6(lua_State *L)
225{
226 const char *name;
227 size_t name_len;
228 unsigned char scope;
229 const char *value;
230 struct in6_addr ipv6;
231 int ret;
232
233 name = luaL_checklstring(L, 1, &name_len);
234 scope = (unsigned char)luaL_checkinteger(L, 2);
235 value = luaL_checkstring(L, 3);
236
237 ret = inet_pton(AF_INET6, value, &ipv6);
238 if (ret == 0)
239 luaL_error(L, "IPv6 '%s': invalid format", value);
240 if (ret == -1)
241 luaL_error(L, "IPv6 '%s': %s", value, strerror(errno));
242
243 if (!set_var_ipv6(worker, name, name_len, scope, &ipv6))
244 luaL_error(L, "No space left available");
245 return 0;
246}
247
248static int ps_lua_set_var_str(lua_State *L)
249{
250 const char *name;
251 size_t name_len;
252 unsigned char scope;
253 const char *value;
254 size_t value_len;
255
256 name = luaL_checklstring(L, 1, &name_len);
257 scope = (unsigned char)luaL_checkinteger(L, 2);
258 value = luaL_checklstring(L, 3, &value_len);
259
260 if (!set_var_string(worker, name, name_len, scope, value, value_len))
261 luaL_error(L, "No space left available");
262 return 0;
263}
264
265static int ps_lua_set_var_bin(lua_State *L)
266{
267 const char *name;
268 size_t name_len;
269 unsigned char scope;
270 const char *value;
271 size_t value_len;
272
273 name = luaL_checklstring(L, 1, &name_len);
274 scope = (unsigned char)luaL_checkinteger(L, 2);
275 value = luaL_checklstring(L, 3, &value_len);
276
277 if (!set_var_bin(worker, name, name_len, scope, value, value_len))
278 luaL_error(L, "No space left available");
279 return 0;
280}
281
282static int ps_lua_start_worker(struct worker *w)
283{
284 if (L != NULL)
285 return 1;
286
287 worker = w;
288
289 L = luaL_newstate();
290 luaL_openlibs(L);
291
292 lua_newtable(L);
293
294 lua_pushstring(L, "register_message");
295 lua_pushcclosure(L, ps_lua_register_message, 0);
296 lua_rawset(L, -3);
297
298 lua_pushstring(L, "set_var_null");
299 lua_pushcclosure(L, ps_lua_set_var_null, 0);
300 lua_rawset(L, -3);
301
302 lua_pushstring(L, "set_var_boolean");
303 lua_pushcclosure(L, ps_lua_set_var_boolean, 0);
304 lua_rawset(L, -3);
305
306 lua_pushstring(L, "set_var_uint32");
307 lua_pushcclosure(L, ps_lua_set_var_uint32, 0);
308 lua_rawset(L, -3);
309
310 lua_pushstring(L, "set_var_int32");
311 lua_pushcclosure(L, ps_lua_set_var_int32, 0);
312 lua_rawset(L, -3);
313
314 lua_pushstring(L, "set_var_uint64");
315 lua_pushcclosure(L, ps_lua_set_var_uint64, 0);
316 lua_rawset(L, -3);
317
318 lua_pushstring(L, "set_var_int64");
319 lua_pushcclosure(L, ps_lua_set_var_int64, 0);
320 lua_rawset(L, -3);
321
322 lua_pushstring(L, "set_var_ipv4");
323 lua_pushcclosure(L, ps_lua_set_var_ipv4, 0);
324 lua_rawset(L, -3);
325
326 lua_pushstring(L, "set_var_ipv6");
327 lua_pushcclosure(L, ps_lua_set_var_ipv6, 0);
328 lua_rawset(L, -3);
329
330 lua_pushstring(L, "set_var_str");
331 lua_pushcclosure(L, ps_lua_set_var_str, 0);
332 lua_rawset(L, -3);
333
334 lua_pushstring(L, "set_var_bin");
335 lua_pushcclosure(L, ps_lua_set_var_bin, 0);
336 lua_rawset(L, -3);
337
338 lua_pushstring(L, "scope");
339 lua_newtable(L);
340
341 lua_pushstring(L, "proc");
342 lua_pushinteger(L, SPOE_SCOPE_PROC);
343 lua_rawset(L, -3);
344
345 lua_pushstring(L, "sess");
346 lua_pushinteger(L, SPOE_SCOPE_SESS);
347 lua_rawset(L, -3);
348
349 lua_pushstring(L, "txn");
350 lua_pushinteger(L, SPOE_SCOPE_TXN);
351 lua_rawset(L, -3);
352
353 lua_pushstring(L, "req");
354 lua_pushinteger(L, SPOE_SCOPE_REQ);
355 lua_rawset(L, -3);
356
357 lua_pushstring(L, "res");
358 lua_pushinteger(L, SPOE_SCOPE_RES);
359 lua_rawset(L, -3);
360
361 lua_rawset(L, -3); /* scope */
362
363 lua_setglobal(L, "spoa");
364 return 1;
365}
366
367static int ps_lua_load_file(struct worker *w, const char *file)
368{
369 int error;
370
371 /* Load the file and check syntax */
372 error = luaL_loadfile(L, file);
373 if (error) {
374 fprintf(stderr, "lua syntax error: %s\n", lua_tostring(L, -1));
375 return 0;
376 }
377
378 /* If no syntax error where detected, execute the code. */
379 error = lua_pcall(L, 0, LUA_MULTRET, 0);
380 switch (error) {
381 case LUA_OK:
382 break;
383 case LUA_ERRRUN:
384 fprintf(stderr, "lua runtime error: %s\n", lua_tostring(L, -1));
385 lua_pop(L, 1);
386 return 0;
387 case LUA_ERRMEM:
388 fprintf(stderr, "lua out of memory error\n");
389 return 0;
390 case LUA_ERRERR:
391 fprintf(stderr, "lua message handler error: %s\n", lua_tostring(L, 0));
392 lua_pop(L, 1);
393 return 0;
394 case LUA_ERRGCMM:
395 fprintf(stderr, "lua garbage collector error: %s\n", lua_tostring(L, 0));
396 lua_pop(L, 1);
397 return 0;
398 default:
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500399 fprintf(stderr, "lua unknown error: %s\n", lua_tostring(L, 0));
Thierry FOURNIER6908c952018-02-23 15:20:55 +0100400 lua_pop(L, 1);
401 return 0;
402 }
403 return 1;
404}
405
406static int ps_lua_exec_message(struct worker *w, void *ref, int nargs, struct spoe_kv *args)
407{
408 long lua_ref = (long)ref;
409 int ret;
410 char *msg_fmt = NULL;
411 const char *msg;
412 int i;
413 char ipbuf[64];
414
415 /* Restore function in the stack */
416 lua_rawgeti(L, LUA_REGISTRYINDEX, lua_ref);
417
418 /* convert args in lua mode */
419 lua_newtable(L);
420 for (i = 0; i < nargs; i++) {
421 lua_newtable(L);
422 lua_pushstring(L, "name");
423 lua_pushlstring(L, args[i].name.str, args[i].name.len);
424 lua_rawset(L, -3); /* Push name */
425 lua_pushstring(L, "value");
426 switch (args[i].value.type) {
427 case SPOE_DATA_T_NULL:
428 lua_pushnil(L);
429 break;
430 case SPOE_DATA_T_BOOL:
431 lua_pushboolean(L, args[i].value.u.boolean);
432 break;
433 case SPOE_DATA_T_INT32:
434 lua_pushinteger(L, args[i].value.u.sint32);
435 break;
436 case SPOE_DATA_T_UINT32:
437 lua_pushinteger(L, args[i].value.u.uint32);
438 break;
439 case SPOE_DATA_T_INT64:
440 lua_pushinteger(L, args[i].value.u.sint64);
441 break;
442 case SPOE_DATA_T_UINT64:
443 if (args[i].value.u.uint64 > LLONG_MAX)
444 lua_pushnil(L);
445 else
446 lua_pushinteger(L, args[i].value.u.uint64);
447 break;
448 case SPOE_DATA_T_IPV4:
449 if (inet_ntop(AF_INET, &args[i].value.u.ipv4, ipbuf, 64) == NULL)
450 lua_pushnil(L);
451 else
452 lua_pushstring(L, ipbuf);
453 break;
454 case SPOE_DATA_T_IPV6:
455 if (inet_ntop(AF_INET6, &args[i].value.u.ipv4, ipbuf, 64) == NULL)
456 lua_pushnil(L);
457 else
458 lua_pushstring(L, ipbuf);
459 break;
460 case SPOE_DATA_T_STR:
461 case SPOE_DATA_T_BIN:
462 lua_pushlstring(L, args[i].value.u.buffer.str, args[i].value.u.buffer.len);
463 break;
464 default:
465 lua_pushnil(L);
466 break;
467 }
468 lua_rawset(L, -3); /* Push name */
469 lua_rawseti(L, -2, i + 1); /* Pusg table in globale table */
470 }
471
472 /* execute lua function */
473 while (1) {
474 ret = lua_resume(L, L, 1);
475 switch (ret) {
476 case LUA_OK:
477 return 1;
478 case LUA_YIELD:
479 DEBUG("Lua yield");
480 continue;
481 case LUA_ERRMEM:
482 LOG("Lua: Out of memory error");
483 return 0;
484 case LUA_ERRRUN:
485 msg_fmt = "Lua runtime error";
486 case LUA_ERRGCMM:
487 msg_fmt = msg_fmt ? msg_fmt : "Lua garbage collector error";
488 case LUA_ERRERR:
489 msg_fmt = msg_fmt ? msg_fmt : "Lua message handler error";
490 default:
Ilya Shipitsince7b00f2020-03-23 22:28:40 +0500491 msg_fmt = msg_fmt ? msg_fmt : "Lua unknown error";
Thierry FOURNIER6908c952018-02-23 15:20:55 +0100492 msg = lua_tostring(L, -1);
493 if (msg == NULL)
494 msg = "Unknown error";
495 LOG("%s: %s", msg_fmt, msg);
496 lua_settop(L, 0);
497 return 0;
498 }
499 }
500
501 return 1;
502}
503
504__attribute__((constructor))
505static void __ps_lua_init(void)
506{
507 ps_register(&ps_lua_bindings_1);
508 ps_register(&ps_lua_bindings_2);
509}