lua.c (12893B)
1 /*
2 * Copyright (c) 2023 Joris Vink <joris@coders.se>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <sys/types.h>
18
19 #include "kore.h"
20
21 #if !defined(KORE_NO_HTTP)
22 #include "http.h"
23 #endif
24
25 #include "lua_api.h"
26 #include "lua_methods.h"
27
28 struct lua_http_request {
29 struct http_request *req;
30 };
31
32 struct lua_symbol {
33 lua_State *L;
34 int ref;
35 LIST_ENTRY(lua_symbol) list;
36 };
37
38 struct lua_module {
39 lua_State *L;
40 LIST_HEAD(, lua_symbol) symbols;
41 };
42
43 static int lua_runtime_resolve(const char *, const struct stat *);
44 static int lua_runtime_http_request(void *, struct http_request *);
45 static void lua_runtime_http_request_free(void *, struct http_request *);
46 static int lua_runtime_http_body_chunk(void *, struct http_request *,
47 const void *, size_t);
48 static int lua_runtime_validator(void *, struct http_request *,
49 const void *);
50 static void lua_runtime_wsmessage(void *, struct connection *,
51 u_int8_t, const void *, size_t);
52 static void lua_runtime_execute(void *);
53 static int lua_runtime_onload(void *, int);
54 static void lua_runtime_signal(void *, int);
55 static void lua_runtime_configure(void *, int, char **);
56 static void lua_runtime_connect(void *, struct connection *);
57
58 static void lua_module_load(struct kore_module *);
59 static void lua_module_free(struct kore_module *);
60 static void lua_module_reload(struct kore_module *);
61 static void *lua_module_getsym(struct kore_module *, const char *);
62
63 static void *lua_mem_alloc(void *, void *, size_t, size_t);
64
65 static int lua_kore_module_init(lua_State *);
66 static void lua_symbol_resolve(struct lua_symbol *, lua_State **);
67
68 static int lua_argument_get_bool(lua_State *, const char *);
69 static const char *lua_argument_get_string(lua_State *, const char *);
70
71 struct kore_module_functions kore_lua_module = {
72 .free = lua_module_free,
73 .load = lua_module_load,
74 .getsym = lua_module_getsym,
75 .reload = lua_module_reload
76 };
77
78 struct kore_runtime kore_lua_runtime = {
79 KORE_RUNTIME_LUA,
80 .resolve = lua_runtime_resolve,
81 .http_request = lua_runtime_http_request,
82 .http_body_chunk = lua_runtime_http_body_chunk,
83 .http_request_free = lua_runtime_http_request_free,
84 .validator = lua_runtime_validator,
85 .wsconnect = lua_runtime_connect,
86 .wsmessage = lua_runtime_wsmessage,
87 .wsdisconnect = lua_runtime_connect,
88 .onload = lua_runtime_onload,
89 .signal = lua_runtime_signal,
90 .connect = lua_runtime_connect,
91 .execute = lua_runtime_execute,
92 .configure = lua_runtime_configure,
93 };
94
95 #define LUA_CONSTANT(x) { #x, x }
96
97 static struct {
98 const char *symbol;
99 int value;
100 } lua_integers[] = {
101 LUA_CONSTANT(LOG_ERR),
102 LUA_CONSTANT(LOG_INFO),
103 LUA_CONSTANT(LOG_NOTICE),
104 LUA_CONSTANT(HTTP_METHOD_GET),
105 LUA_CONSTANT(HTTP_METHOD_PUT),
106 LUA_CONSTANT(HTTP_METHOD_POST),
107 LUA_CONSTANT(HTTP_METHOD_HEAD),
108 LUA_CONSTANT(HTTP_METHOD_PATCH),
109 LUA_CONSTANT(HTTP_METHOD_DELETE),
110 LUA_CONSTANT(HTTP_METHOD_OPTIONS),
111 { NULL, -1 },
112 };
113
114 void
115 kore_lua_init(void)
116 {
117 if (!kore_configure_setting("deployment", "dev"))
118 fatal("failed to set initial deployment to dev");
119 }
120
121 void
122 kore_lua_cleanup(void)
123 {
124 }
125
126 static void *
127 lua_mem_alloc(void *uptr, void *ptr, size_t osize, size_t nsize)
128 {
129 if (nsize == 0) {
130 kore_free(ptr);
131 return (NULL);
132 }
133
134 return (kore_realloc(ptr, nsize));
135 }
136
137 static void
138 lua_symbol_resolve(struct lua_symbol *sym, lua_State **L)
139 {
140 lua_rawgeti(sym->L, LUA_REGISTRYINDEX, sym->ref);
141 *L = sym->L;
142 }
143
144 static int
145 lua_argument_get_bool(lua_State *L, const char *field)
146 {
147 int ret;
148
149 lua_pushstring(L, field);
150 ret = lua_gettable(L, -2);
151
152 if (ret == LUA_TNIL) {
153 lua_pop(L, 1);
154 return (0);
155 }
156
157 luaL_argcheck(L, ret == LUA_TBOOLEAN, 0, field);
158
159 ret = lua_toboolean(L, -1);
160 lua_pop(L, 1);
161
162 return (ret);
163 }
164
165 static const char *
166 lua_argument_get_string(lua_State *L, const char *field)
167 {
168 const char *v;
169 int type;
170
171 lua_pushstring(L, field);
172 type = lua_gettable(L, -2);
173
174 if (type == LUA_TNIL) {
175 lua_pop(L, 1);
176 return (NULL);
177 }
178
179 luaL_argcheck(L, type == LUA_TSTRING, 0, field);
180
181 v = lua_tostring(L, -1);
182 lua_pop(L, 1);
183
184 return (v);
185 }
186
187 static int
188 lua_kore_module_init(lua_State *L)
189 {
190 int i;
191
192 luaL_newlib(L, lua_kore_functions);
193
194 for (i = 0; lua_integers[i].symbol != NULL; i++) {
195 lua_pushstring(L, lua_integers[i].symbol);
196 lua_pushnumber(L, lua_integers[i].value);
197 lua_settable(L, -3);
198 }
199
200 return (1);
201 }
202
203 static void
204 lua_module_free(struct kore_module *module)
205 {
206 struct lua_symbol *sym;
207 struct lua_module *lua;
208
209 lua = module->handle;
210
211 while ((sym = LIST_FIRST(&lua->symbols)) != NULL) {
212 LIST_REMOVE(sym, list);
213 kore_free(sym);
214 }
215
216 kore_free(lua);
217 }
218
219 static void
220 lua_module_reload(struct kore_module *module)
221 {
222 lua_module_free(module);
223 lua_module_load(module);
224 }
225
226 static void
227 lua_module_load(struct kore_module *module)
228 {
229 struct lua_module *lua;
230
231 lua = kore_calloc(1, sizeof(*lua));
232 LIST_INIT(&lua->symbols);
233
234 if ((lua->L = lua_newstate(lua_mem_alloc, NULL)) == NULL)
235 fatal("luaL_newstate");
236
237 luaL_openlibs(lua->L);
238
239 luaL_requiref(lua->L, "kore", lua_kore_module_init, 1);
240 lua_pop(lua->L, 1);
241
242 luaL_newmetatable(lua->L, "http_request");
243 luaL_setfuncs(lua->L, lua_http_request_meta, 0);
244 lua_pop(lua->L, 1);
245
246 lua_pushliteral(lua->L, "http_request_methods");
247 luaL_newlib(lua->L, lua_http_request_methods);
248 lua_settable(lua->L, LUA_REGISTRYINDEX);
249
250 luaL_newlib(lua->L, lua_http_request_methods);
251 lua_pop(lua->L, 1);
252
253 if (luaL_loadfile(lua->L, module->path) != LUA_OK) {
254 fatal("%s: failed to import module (%s)", module->path,
255 lua_tostring(lua->L, -1));
256 }
257
258 if (lua_pcall(lua->L, 0, 0, 0) != LUA_OK) {
259 fatal("%s: failed to import module (%s)", module->path,
260 lua_tostring(lua->L, -1));
261 }
262
263 module->handle = lua;
264 }
265
266 static void *
267 lua_module_getsym(struct kore_module *module, const char *symbol)
268 {
269 int ref;
270 struct lua_module *lua;
271 struct lua_symbol *sym;
272
273 lua = module->handle;
274
275 if (lua_getglobal(lua->L, symbol) != LUA_TFUNCTION)
276 return (NULL);
277
278 if ((ref = luaL_ref(lua->L, LUA_REGISTRYINDEX)) == LUA_REFNIL)
279 return (NULL);
280
281 sym = kore_calloc(1, sizeof(*sym));
282
283 sym->ref = ref;
284 sym->L = lua->L;
285
286 LIST_INSERT_HEAD(&lua->symbols, sym, list);
287
288 return (sym);
289 }
290
291 static int
292 lua_runtime_resolve(const char *module, const struct stat *st)
293 {
294 const char *ext;
295
296 if (!S_ISREG(st->st_mode))
297 return (KORE_RESULT_ERROR);
298
299 ext = strrchr(module, '.');
300
301 if (ext == NULL || strcasecmp(ext, ".lua"))
302 return (KORE_RESULT_ERROR);
303
304 kore_module_load(module, NULL, KORE_MODULE_LUA);
305
306 return (KORE_RESULT_OK);
307 }
308
309 static int
310 lua_runtime_http_request(void *addr, struct http_request *req)
311 {
312 lua_State *L;
313 struct lua_http_request *lreq;
314
315 lua_symbol_resolve(addr, &L);
316
317 lreq = lua_newuserdata(L, sizeof(*lreq));
318 luaL_setmetatable(L, "http_request");
319
320 lreq->req = req;
321
322 if (lua_pcall(L, 1, 0, 0)) {
323 kore_log(LOG_NOTICE, "%s: failed to call handler: %s", __func__,
324 lua_tostring(L, -1));
325 http_response(req, 500, NULL, 0);
326 return (KORE_RESULT_OK);
327 }
328
329 return (KORE_RESULT_OK);
330 }
331
332 static void
333 lua_runtime_http_request_free(void *addr, struct http_request *req)
334 {
335 fatal("%s: not yet implemented", __func__);
336 }
337
338 static int
339 lua_runtime_http_body_chunk(void *addr, struct http_request *req,
340 const void *data, size_t len)
341 {
342 fatal("%s: not yet implemented", __func__);
343
344 return (KORE_RESULT_ERROR);
345 }
346
347 static int
348 lua_runtime_validator(void *addr, struct http_request *req, const void *data)
349 {
350 fatal("%s: not yet implemented", __func__);
351
352 return (KORE_RESULT_ERROR);
353 }
354
355 static void
356 lua_runtime_wsmessage(void *addr, struct connection *c, u_int8_t op,
357 const void *data, size_t len)
358 {
359 fatal("%s: not yet implemented", __func__);
360 }
361
362 static void
363 lua_runtime_execute(void *addr)
364 {
365 lua_State *L;
366
367 lua_symbol_resolve(addr, &L);
368
369 if (lua_pcall(L, 0, 0, 0)) {
370 fatal("failed to execute function: %s",
371 lua_tostring(L, -1));
372 }
373 }
374
375 static void
376 lua_runtime_configure(void *addr, int argc, char **argv)
377 {
378 lua_State *L;
379 int idx;
380
381 lua_symbol_resolve(addr, &L);
382
383 lua_pushinteger(L, argc);
384 lua_newtable(L);
385
386 for (idx = 0; idx < argc; idx++) {
387 lua_pushstring(L, argv[idx]);
388 lua_rawseti(L, -2, idx);
389 }
390
391 if (lua_pcall(L, 2, 0, 0)) {
392 fatal("failed to configure your application (%s)",
393 lua_tostring(L, -1));
394 }
395 }
396
397 static int
398 lua_runtime_onload(void *addr, int action)
399 {
400 fatal("%s: not yet implemented", __func__);
401
402 return (KORE_RESULT_ERROR);
403 }
404
405 static void
406 lua_runtime_connect(void *addr, struct connection *c)
407 {
408 fatal("%s: not yet implemented", __func__);
409 }
410
411 static void
412 lua_runtime_signal(void *addr, int sig)
413 {
414 fatal("%s: not yet implemented", __func__);
415 }
416
417 static int
418 lua_kore_config(lua_State *L)
419 {
420 char *v;
421 const char *opt, *val;
422
423 lua_pushnil(L);
424
425 while (lua_next(L, -2) != 0) {
426 if (!lua_isstring(L, -2))
427 fatal("kore.config: keyword not a string");
428
429 opt = lua_tostring(L, -2);
430
431 if (lua_isinteger(L, -1)) {
432 lua_pushvalue(L, -1);
433 val = lua_tostring(L, -1);
434 lua_pop(L, 1);
435 } else if (lua_isstring(L, -1)) {
436 val = lua_tostring(L, -1);
437 } else {
438 fatal("kore.config: value not a string or integer");
439 }
440
441 v = kore_strdup(val);
442
443 if (!kore_configure_setting(opt, v)) {
444 kore_free(v);
445 luaL_error(L, "kore.config: cannot be set at runtime");
446 lua_pop(L, 1);
447 return (0);
448 }
449
450 kore_free(v);
451 lua_pop(L, 1);
452 }
453
454 return (0);
455 }
456
457 static int
458 lua_kore_server(lua_State *L)
459 {
460 struct kore_server *srv;
461 const char *name, *ip, *port;
462
463 if ((name = lua_argument_get_string(L, "name")) == NULL)
464 name = "default";
465
466 if ((ip = lua_argument_get_string(L, "ip")) == NULL) {
467 luaL_error(L, "kore.server: missing ip keyword");
468 return (0);
469 }
470
471 if ((port = lua_argument_get_string(L, "port")) == NULL) {
472 luaL_error(L, "kore.server: missing port keyword");
473 return (0);
474 }
475
476 if ((srv = kore_server_lookup(name)) != NULL) {
477 luaL_error(L, "kore.server: server '%s' exists", name);
478 return (0);
479 }
480
481 srv = kore_server_create(name);
482 srv->tls = lua_argument_get_bool(L, "tls");
483
484 if (srv->tls && !kore_tls_supported()) {
485 kore_server_free(srv);
486 luaL_error(L, "kore.server: TLS not supported");
487 return (0);
488 }
489
490 if (!kore_server_bind(srv, ip, port, NULL)) {
491 kore_server_free(srv);
492 luaL_error(L, "kore.server: failed to bind %s:%s", ip, port);
493 return (0);
494 }
495
496 kore_server_finalize(srv);
497
498 return (0);
499 }
500
501 static int
502 lua_http_request_gc(lua_State *L)
503 {
504 struct lua_http_request *lreq;
505
506 lreq = luaL_checkudata(L, 1, "http_request");
507 kore_free(lreq);
508
509 return (0);
510 }
511
512 static int
513 lua_http_request_index(lua_State *L)
514 {
515 struct lua_http_request *lreq;
516 const char *field;
517
518 lreq = luaL_checkudata(L, 1, "http_request");
519 field = luaL_checkstring(L, 2);
520
521 lua_getfield(L, LUA_REGISTRYINDEX, "http_request_methods");
522 lua_getfield(L, -1, field);
523
524 if (!lua_isnil(L, -1))
525 return (1);
526
527 lua_pop(L, 2);
528
529 if (!strcmp(field, "path")) {
530 lua_pushstring(L, lreq->req->path);
531 return (1);
532 } else if (!strcmp(field, "host")) {
533 lua_pushstring(L, lreq->req->host);
534 return (1);
535 } else if (!strcmp(field, "agent")) {
536 lua_pushstring(L, lreq->req->agent);
537 return (1);
538 } else if (!strcmp(field, "referer")) {
539 lua_pushstring(L, lreq->req->referer);
540 return (1);
541 } else if (!strcmp(field, "method")) {
542 lua_pushinteger(L, lreq->req->method);
543 return (1);
544 }
545
546 return (0);
547 }
548
549 static int
550 lua_http_response_header(lua_State *L)
551 {
552 struct lua_http_request *lreq;
553 const char *header, *value;
554
555 lreq = luaL_checkudata(L, 1, "http_request");
556 header = luaL_checkstring(L, 2);
557 value = luaL_checkstring(L, 3);
558
559 http_response_header(lreq->req, header, value);
560
561 return (0);
562 }
563
564 static int
565 lua_http_request_header(lua_State *L)
566 {
567 struct lua_http_request *lreq;
568 const char *header, *value;
569
570 lreq = luaL_checkudata(L, 1, "http_request");
571 header = luaL_checkstring(L, 2);
572
573 if (!http_request_header(lreq->req, header, &value)) {
574 lua_pushnil(L);
575 } else {
576 lua_pushstring(L, value);
577 }
578
579 return (1);
580 }
581
582 static int
583 lua_http_response(lua_State *L)
584 {
585 size_t len;
586 struct lua_http_request *lreq;
587 const void *data;
588 int status;
589
590 lreq = luaL_checkudata(L, 1, "http_request");
591 status = luaL_checkinteger(L, 2);
592
593 if (lua_isnil(L, 3)) {
594 len = 0;
595 data = NULL;
596 } else {
597 data = luaL_checklstring(L, 3, &len);
598 }
599
600 http_response(lreq->req, status, data, len);
601
602 return (0);
603 }