1 /*-
2 * Copyright 2016 Vsevolod Stakhov
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "lua_common.h"
17 #include "libserver/maps/map.h"
18 #include "libserver/maps/map_private.h"
19
20 /***
21 * @module rspamd_logger
22 * Rspamd logger module is used to log messages from LUA API to the main rspamd logger.
23 * It supports legacy and modern interfaces allowing highly customized an convenient log functions.
24 * Here is an example of logger usage:
25 * @example
26 local rspamd_logger = require "rspamd_logger"
27
28 local a = 'string'
29 local b = 1.5
30 local c = 1
31 local d = {
32 'aa',
33 1,
34 'bb'
35 }
36 local e = {
37 key = 'value',
38 key2 = 1.0
39 }
40
41 -- New extended interface
42 -- %<number> means numeric arguments and %s means the next argument
43 -- for example %1, %2, %s: %s would mean the third argument
44
45 rspamd_logger.infox('a=%1, b=%2, c=%3, d=%4, e=%s', a, b, c, d, e)
46 -- Output: a=string, b=1.50000, c=1, d={[1] = aa, [2] = 1, [3] = bb} e={[key]=value, [key2]=1.0}
47
48 -- Legacy interface (can handle merely strings)
49 rspamd_logger.info('Old stupid API')
50
51 -- Create string using logger API
52 local str = rspamd_logger.slog('a=%1, b=%2, c=%3, d=%4, e=%5', a, b, c, d, e)
53
54 print(str)
55 -- Output: a=string, b=1.50000, c=1, d={[1] = aa, [2] = 1, [3] = bb} e={[key]=value, [key2]=1.0}
56 */
57
58 /* Logger methods */
59 /***
60 * @function logger.err(msg)
61 * Log message as an error
62 * @param {string} msg string to be logged
63 */
64 LUA_FUNCTION_DEF (logger, err);
65 /***
66 * @function logger.warn(msg)
67 * Log message as a warning
68 * @param {string} msg string to be logged
69 */
70 LUA_FUNCTION_DEF (logger, warn);
71 /***
72 * @function logger.info(msg)
73 * Log message as an informational message
74 * @param {string} msg string to be logged
75 */
76 LUA_FUNCTION_DEF (logger, info);
77 /***
78 * @function logger.message(msg)
79 * Log message as an notice message
80 * @param {string} msg string to be logged
81 */
82 LUA_FUNCTION_DEF (logger, message);
83 /***
84 * @function logger.debug(msg)
85 * Log message as a debug message
86 * @param {string} msg string to be logged
87 */
88 LUA_FUNCTION_DEF (logger, debug);
89 /***
90 * @function logger.errx(fmt[, args)
91 * Extended interface to make an error log message
92 * @param {string} fmt format string, arguments are encoded as %<number>
93 * @param {any} args list of arguments to be replaced in %<number> positions
94 */
95 LUA_FUNCTION_DEF (logger, errx);
96 /***
97 * @function logger.warn(fmt[, args)
98 * Extended interface to make a warning log message
99 * @param {string} fmt format string, arguments are encoded as %<number>
100 * @param {any} args list of arguments to be replaced in %<number> positions
101 */
102 LUA_FUNCTION_DEF (logger, warnx);
103 /***
104 * @function logger.infox(fmt[, args)
105 * Extended interface to make an informational log message
106 * @param {string} fmt format string, arguments are encoded as %<number>
107 * @param {any} args list of arguments to be replaced in %<number> positions
108 */
109 LUA_FUNCTION_DEF (logger, infox);
110 /***
111 * @function logger.infox(fmt[, args)
112 * Extended interface to make an informational log message
113 * @param {string} fmt format string, arguments are encoded as %<number>
114 * @param {any} args list of arguments to be replaced in %<number> positions
115 */
116 LUA_FUNCTION_DEF (logger, messagex);
117 /***
118 * @function logger.debugx(fmt[, args)
119 * Extended interface to make a debug log message
120 * @param {string} fmt format string, arguments are encoded as %<number>
121 * @param {any} args list of arguments to be replaced in %<number> positions
122 */
123 LUA_FUNCTION_DEF (logger, debugx);
124
125 /***
126 * @function logger.debugm(module, id, fmt[, args)
127 * Extended interface to make a debug log message
128 * @param {string} module debug module
129 * @param {task|cfg|pool|string} id id to log
130 * @param {string} fmt format string, arguments are encoded as %<number>
131 * @param {any} args list of arguments to be replaced in %<number> positions
132 */
133 LUA_FUNCTION_DEF (logger, debugm);
134 /***
135 * @function logger.slog(fmt[, args)
136 * Create string replacing percent params with corresponding arguments
137 * @param {string} fmt format string, arguments are encoded as %<number>
138 * @param {any} args list of arguments to be replaced in %<number> positions
139 * @return {string} string with percent parameters substituted
140 */
141 LUA_FUNCTION_DEF (logger, slog);
142
143 /***
144 * @function logger.logx(level, module, id, fmt[, args)
145 * Extended interface to make a generic log message on any level
146 * @param {number} log level as a number (see GLogLevelFlags enum for values)
147 * @param {task|cfg|pool|string} id id to log
148 * @param {string} fmt format string, arguments are encoded as %<number>
149 * @param {any} args list of arguments to be replaced in %<number> positions
150 */
151 LUA_FUNCTION_DEF (logger, logx);
152
153 /***
154 * @function logger.log_level()
155 * Returns log level for a logger
156 * @return {string} current log level
157 */
158 LUA_FUNCTION_DEF (logger, log_level);
159
160 static const struct luaL_reg loggerlib_f[] = {
161 LUA_INTERFACE_DEF (logger, err),
162 LUA_INTERFACE_DEF (logger, warn),
163 LUA_INTERFACE_DEF (logger, message),
164 {"msg", lua_logger_message},
165 LUA_INTERFACE_DEF (logger, info),
166 LUA_INTERFACE_DEF (logger, debug),
167 LUA_INTERFACE_DEF (logger, errx),
168 LUA_INTERFACE_DEF (logger, warnx),
169 LUA_INTERFACE_DEF (logger, infox),
170 LUA_INTERFACE_DEF (logger, messagex),
171 {"msgx", lua_logger_messagex},
172 LUA_INTERFACE_DEF (logger, debugx),
173 LUA_INTERFACE_DEF (logger, debugm),
174 LUA_INTERFACE_DEF (logger, slog),
175 LUA_INTERFACE_DEF (logger, logx),
176 LUA_INTERFACE_DEF (logger, log_level),
177 {"__tostring", rspamd_lua_class_tostring},
178 {NULL, NULL}
179 };
180
181 static void
lua_common_log_line(GLogLevelFlags level,lua_State * L,const gchar * msg,const gchar * uid,const gchar * module,gint stack_level)182 lua_common_log_line (GLogLevelFlags level,
183 lua_State *L,
184 const gchar *msg,
185 const gchar *uid,
186 const gchar *module,
187 gint stack_level)
188 {
189 lua_Debug d;
190 gchar func_buf[128], *p;
191
192 if (lua_getstack (L, stack_level, &d) == 1) {
193 (void) lua_getinfo (L, "Sl", &d);
194 if ((p = strrchr (d.short_src, '/')) == NULL) {
195 p = d.short_src;
196 }
197 else {
198 p++;
199 }
200
201 if (strlen (p) > 30) {
202 rspamd_snprintf (func_buf, sizeof (func_buf), "%27s...:%d", p,
203 d.currentline);
204 }
205 else {
206 rspamd_snprintf (func_buf, sizeof (func_buf), "%s:%d", p,
207 d.currentline);
208 }
209
210 rspamd_common_log_function (NULL,
211 level,
212 module,
213 uid,
214 func_buf,
215 "%s",
216 msg);
217 }
218 else {
219 rspamd_common_log_function (NULL,
220 level,
221 module,
222 uid,
223 G_STRFUNC,
224 "%s",
225 msg);
226 }
227 }
228
229 /*** Logger interface ***/
230 static gint
lua_logger_err(lua_State * L)231 lua_logger_err (lua_State *L)
232 {
233 LUA_TRACE_POINT;
234 const gchar *msg;
235 msg = luaL_checkstring (L, 1);
236 lua_common_log_line (G_LOG_LEVEL_CRITICAL, L, msg, NULL, NULL, 1);
237 return 0;
238 }
239
240 static gint
lua_logger_warn(lua_State * L)241 lua_logger_warn (lua_State *L)
242 {
243 LUA_TRACE_POINT;
244 const gchar *msg;
245 msg = luaL_checkstring (L, 1);
246 lua_common_log_line (G_LOG_LEVEL_WARNING, L, msg, NULL, NULL, 1);
247 return 0;
248 }
249
250 static gint
lua_logger_info(lua_State * L)251 lua_logger_info (lua_State *L)
252 {
253 LUA_TRACE_POINT;
254 const gchar *msg;
255 msg = luaL_checkstring (L, 1);
256 lua_common_log_line (G_LOG_LEVEL_INFO, L, msg, NULL, NULL, 1);
257 return 0;
258 }
259
260 static gint
lua_logger_message(lua_State * L)261 lua_logger_message (lua_State *L)
262 {
263 LUA_TRACE_POINT;
264 const gchar *msg;
265 msg = luaL_checkstring (L, 1);
266 lua_common_log_line (G_LOG_LEVEL_MESSAGE, L, msg, NULL, NULL, 1);
267 return 0;
268 }
269
270 static gint
lua_logger_debug(lua_State * L)271 lua_logger_debug (lua_State *L)
272 {
273 LUA_TRACE_POINT;
274 const gchar *msg;
275 msg = luaL_checkstring (L, 1);
276 lua_common_log_line (G_LOG_LEVEL_DEBUG, L, msg, NULL, NULL, 1);
277 return 0;
278 }
279
280 static inline bool
lua_logger_char_safe(int t,unsigned int esc_type)281 lua_logger_char_safe (int t, unsigned int esc_type)
282 {
283 if (t & 0x80) {
284 if (esc_type & LUA_ESCAPE_8BIT) {
285 return false;
286 }
287
288 return true;
289 }
290
291 if (esc_type & LUA_ESCAPE_UNPRINTABLE) {
292 if (!g_ascii_isprint (t) && !g_ascii_isspace (t)) {
293 return false;
294 }
295 }
296
297 if (esc_type & LUA_ESCAPE_NEWLINES) {
298 if (t == '\r' || t == '\n') {
299 return false;
300 }
301 }
302
303 return true;
304 }
305
306 static gsize
lua_logger_out_str(lua_State * L,gint pos,gchar * outbuf,gsize len,struct lua_logger_trace * trace,enum lua_logger_escape_type esc_type)307 lua_logger_out_str (lua_State *L, gint pos,
308 gchar *outbuf, gsize len,
309 struct lua_logger_trace *trace,
310 enum lua_logger_escape_type esc_type)
311 {
312 gsize slen, flen;
313 const gchar *str = lua_tolstring (L, pos, &slen);
314 static const gchar hexdigests[16] = "0123456789abcdef";
315 gsize r = 0, s;
316
317 if (str) {
318 gboolean normal = TRUE;
319 flen = MIN (slen, len - 1);
320
321 for (r = 0; r < flen; r ++) {
322 if (!lua_logger_char_safe (str[r], esc_type)) {
323 normal = FALSE;
324 break;
325 }
326 }
327
328 if (normal) {
329 r = rspamd_strlcpy (outbuf, str, flen + 1);
330 }
331 else {
332 /* Need to escape non printed characters */
333 r = 0;
334 s = 0;
335
336 while (slen > 0 && len > 1) {
337 if (!lua_logger_char_safe (str[s], esc_type)) {
338 if (len >= 3) {
339 outbuf[r++] = '\\';
340 outbuf[r++] = hexdigests[((str[s] >> 4) & 0xF)];
341 outbuf[r++] = hexdigests[((str[s]) & 0xF)];
342
343 len -= 2;
344 }
345 else {
346 outbuf[r++] = '?';
347 }
348 }
349 else {
350 outbuf[r++] = str[s];
351 }
352
353 s++;
354 slen --;
355 len --;
356 }
357
358 outbuf[r] = '\0';
359 }
360 }
361
362 return r;
363 }
364
365 static gsize
lua_logger_out_num(lua_State * L,gint pos,gchar * outbuf,gsize len,struct lua_logger_trace * trace)366 lua_logger_out_num (lua_State *L, gint pos, gchar *outbuf, gsize len,
367 struct lua_logger_trace *trace)
368 {
369 gdouble num = lua_tonumber (L, pos);
370 glong inum;
371 gsize r = 0;
372
373 if ((gdouble) (glong) num == num) {
374 inum = num;
375 r = rspamd_snprintf (outbuf, len + 1, "%l", inum);
376 }
377 else {
378 r = rspamd_snprintf (outbuf, len + 1, "%f", num);
379 }
380
381 return r;
382 }
383
384 static gsize
lua_logger_out_boolean(lua_State * L,gint pos,gchar * outbuf,gsize len,struct lua_logger_trace * trace)385 lua_logger_out_boolean (lua_State *L, gint pos, gchar *outbuf, gsize len,
386 struct lua_logger_trace *trace)
387 {
388 gboolean val = lua_toboolean (L, pos);
389 gsize r = 0;
390
391 r = rspamd_strlcpy (outbuf, val ? "true" : "false", len + 1);
392
393 return r;
394 }
395
396 static gsize
lua_logger_out_userdata(lua_State * L,gint pos,gchar * outbuf,gsize len,struct lua_logger_trace * trace)397 lua_logger_out_userdata (lua_State *L, gint pos, gchar *outbuf, gsize len,
398 struct lua_logger_trace *trace)
399 {
400 gint r = 0, top;
401 const gchar *str = NULL;
402 gboolean converted_to_str = FALSE;
403
404 top = lua_gettop (L);
405
406 if (!lua_getmetatable (L, pos)) {
407 return 0;
408 }
409
410 lua_pushstring (L, "__index");
411 lua_gettable (L, -2);
412
413 if (!lua_istable (L, -1)) {
414
415 if (lua_isfunction (L, -1)) {
416 /* Functional metatable, try to get __tostring directly */
417 lua_pushstring (L, "__tostring");
418 lua_gettable (L, -3);
419
420 if (lua_isfunction (L, -1)) {
421 lua_pushvalue (L, pos);
422
423 if (lua_pcall (L, 1, 1, 0) != 0) {
424 lua_settop (L, top);
425
426 return 0;
427 }
428
429 str = lua_tostring (L, -1);
430
431 if (str) {
432 r = rspamd_snprintf (outbuf, len, "%s", str);
433 }
434
435 lua_settop (L, top);
436
437 return r;
438 }
439 }
440 lua_settop (L, top);
441
442 return 0;
443 }
444
445 lua_pushstring (L, "__tostring");
446 lua_gettable (L, -2);
447
448 if (lua_isfunction (L, -1)) {
449 lua_pushvalue (L, pos);
450
451 if (lua_pcall (L, 1, 1, 0) != 0) {
452 lua_settop (L, top);
453
454 return 0;
455 }
456
457 str = lua_tostring (L, -1);
458
459 if (str) {
460 converted_to_str = TRUE;
461 }
462 }
463 else {
464 lua_pop (L, 1);
465 lua_pushstring (L, "class");
466 lua_gettable (L, -2);
467
468 if (lua_isstring (L, -1)) {
469 str = lua_tostring (L, -1);
470 converted_to_str = TRUE;
471 }
472 }
473
474 if (converted_to_str) {
475 r = rspamd_snprintf (outbuf, len, "%s", str);
476 }
477 else {
478 /* Print raw pointer */
479 r = rspamd_snprintf (outbuf, len, "%s(%p)", str, lua_touserdata (L, pos));
480 }
481
482 lua_settop (L, top);
483
484 return r;
485 }
486
487 #define MOVE_BUF(d, remain, r) \
488 (d) += (r); (remain) -= (r); \
489 if ((remain) == 0) { lua_pop (L, 1); break; }
490
491 static gsize
lua_logger_out_table(lua_State * L,gint pos,gchar * outbuf,gsize len,struct lua_logger_trace * trace,enum lua_logger_escape_type esc_type)492 lua_logger_out_table (lua_State *L, gint pos, gchar *outbuf, gsize len,
493 struct lua_logger_trace *trace,
494 enum lua_logger_escape_type esc_type)
495 {
496 gchar *d = outbuf;
497 gsize remain = len, r;
498 gboolean first = TRUE;
499 gconstpointer self = NULL;
500 gint i, tpos, last_seq = -1;
501
502 if (!lua_istable (L, pos) || remain == 0) {
503 return 0;
504 }
505
506 self = lua_topointer (L, pos);
507
508 /* Check if we have seen this pointer */
509 for (i = 0; i < TRACE_POINTS; i ++) {
510 if (trace->traces[i] == self) {
511 r = rspamd_snprintf (d, remain + 1, "ref(%p)", self);
512
513 d += r;
514
515 return (d - outbuf);
516 }
517 }
518
519 trace->traces[trace->cur_level % TRACE_POINTS] = self;
520
521 lua_pushvalue (L, pos);
522 r = rspamd_snprintf (d, remain + 1, "{");
523 remain -= r;
524 d += r;
525
526 /* Get numeric keys (ipairs) */
527 for (i = 1; ; i++) {
528 lua_rawgeti (L, -1, i);
529
530 if (lua_isnil (L, -1)) {
531 lua_pop (L, 1);
532 break;
533 }
534
535 last_seq = i;
536
537 if (!first) {
538 r = rspamd_snprintf (d, remain + 1, ", ");
539 MOVE_BUF(d, remain, r);
540 }
541
542 r = rspamd_snprintf (d, remain + 1, "[%d] = ", i);
543 MOVE_BUF(d, remain, r);
544 tpos = lua_gettop (L);
545
546 if (lua_topointer (L, tpos) == self) {
547 r = rspamd_snprintf (d, remain + 1, "__self");
548 }
549 else {
550 r = lua_logger_out_type (L, tpos, d, remain, trace, esc_type);
551 }
552 MOVE_BUF(d, remain, r);
553
554 first = FALSE;
555 lua_pop (L, 1);
556 }
557
558 /* Get string keys (pairs) */
559 for (lua_pushnil (L); lua_next (L, -2); lua_pop (L, 1)) {
560 /* 'key' is at index -2 and 'value' is at index -1 */
561
562 if (lua_type (L, -2) == LUA_TNUMBER) {
563 if (last_seq > 0) {
564 lua_pushvalue (L, -2);
565
566 if (lua_tonumber (L, -1) <= last_seq + 1) {
567 lua_pop (L, 1);
568 /* Already seen */
569 continue;
570 }
571
572 lua_pop (L, 1);
573 }
574 }
575
576 if (!first) {
577 r = rspamd_snprintf (d, remain + 1, ", ");
578 MOVE_BUF(d, remain, r);
579 }
580
581 /* Preserve key */
582 lua_pushvalue (L, -2);
583 r = rspamd_snprintf (d, remain + 1, "[%s] = ",
584 lua_tostring (L, -1));
585 lua_pop (L, 1); /* Remove key */
586 MOVE_BUF(d, remain, r);
587 tpos = lua_gettop (L);
588
589 if (lua_topointer (L, tpos) == self) {
590 r = rspamd_snprintf (d, remain + 1, "__self");
591 }
592 else {
593 r = lua_logger_out_type (L, tpos, d, remain, trace, esc_type);
594 }
595 MOVE_BUF(d, remain, r);
596
597 first = FALSE;
598 }
599
600 lua_pop (L, 1);
601
602 r = rspamd_snprintf (d, remain + 1, "}");
603 d += r;
604
605 return (d - outbuf);
606 }
607
608 #undef MOVE_BUF
609
610 gsize
lua_logger_out_type(lua_State * L,gint pos,gchar * outbuf,gsize len,struct lua_logger_trace * trace,enum lua_logger_escape_type esc_type)611 lua_logger_out_type (lua_State *L, gint pos,
612 gchar *outbuf, gsize len,
613 struct lua_logger_trace *trace,
614 enum lua_logger_escape_type esc_type)
615 {
616 gint type;
617 gsize r = 0;
618
619 if (len == 0) {
620 return 0;
621 }
622
623 type = lua_type (L, pos);
624 trace->cur_level ++;
625
626 switch (type) {
627 case LUA_TNUMBER:
628 r = lua_logger_out_num (L, pos, outbuf, len, trace);
629 break;
630 case LUA_TBOOLEAN:
631 r = lua_logger_out_boolean (L, pos, outbuf, len, trace);
632 break;
633 case LUA_TTABLE:
634 r = lua_logger_out_table (L, pos, outbuf, len, trace, esc_type);
635 break;
636 case LUA_TUSERDATA:
637 r = lua_logger_out_userdata (L, pos, outbuf, len, trace);
638 break;
639 case LUA_TFUNCTION:
640 r = rspamd_snprintf (outbuf, len + 1, "function");
641 break;
642 case LUA_TLIGHTUSERDATA:
643 r = rspamd_snprintf (outbuf, len + 1, "0x%p", lua_topointer (L, pos));
644 break;
645 case LUA_TNIL:
646 r = rspamd_snprintf (outbuf, len + 1, "nil");
647 break;
648 case LUA_TNONE:
649 r = rspamd_snprintf (outbuf, len + 1, "no value");
650 break;
651 default:
652 /* Try to push everything as string using tostring magic */
653 r = lua_logger_out_str (L, pos, outbuf, len, trace, esc_type);
654 break;
655 }
656
657 trace->cur_level --;
658
659 return r;
660 }
661
662 static const gchar *
lua_logger_get_id(lua_State * L,gint pos,GError ** err)663 lua_logger_get_id (lua_State *L, gint pos, GError **err)
664 {
665 const gchar *uid = NULL, *clsname;
666
667 if (lua_getmetatable (L, pos) != 0) {
668 uid = "";
669 lua_pushstring (L, "__index");
670 lua_gettable (L, -2);
671
672 lua_pushstring (L, "class");
673 lua_gettable (L, -2);
674
675 clsname = lua_tostring (L, -1);
676
677 if (strcmp (clsname, "rspamd{task}") == 0) {
678 struct rspamd_task *task = lua_check_task (L, pos);
679
680 if (task) {
681 uid = task->task_pool->tag.uid;
682 }
683 else {
684 g_set_error (err, g_quark_from_static_string ("lua_logger"),
685 EINVAL, "invalid rspamd{task}");
686 }
687 }
688 else if (strcmp (clsname, "rspamd{mempool}") == 0) {
689 rspamd_mempool_t *pool;
690
691 pool = rspamd_lua_check_mempool (L, pos);
692
693 if (pool) {
694 uid = pool->tag.uid;
695 }
696 else {
697 g_set_error (err, g_quark_from_static_string ("lua_logger"),
698 EINVAL, "invalid rspamd{mempool}");
699 }
700 }
701 else if (strcmp (clsname, "rspamd{config}") == 0) {
702 struct rspamd_config *cfg;
703
704 cfg = lua_check_config (L, pos);
705
706 if (cfg) {
707 if (cfg->checksum) {
708 uid = cfg->checksum;
709 }
710 }
711 else {
712 g_set_error (err, g_quark_from_static_string ("lua_logger"),
713 EINVAL, "invalid rspamd{config}");
714 }
715 }
716 else if (strcmp (clsname, "rspamd{map}") == 0) {
717 struct rspamd_lua_map *map;
718
719 map = lua_check_map (L, pos);
720
721 if (map) {
722 if (map->map) {
723 uid = map->map->tag;
724 }
725 else {
726 uid = "embedded";
727 }
728 }
729 else {
730 g_set_error (err, g_quark_from_static_string ("lua_logger"),
731 EINVAL, "invalid rspamd{map}");
732 }
733 }
734 else {
735 g_set_error (err, g_quark_from_static_string ("lua_logger"),
736 EINVAL, "unknown class: %s", clsname);
737 }
738
739
740 /* Metatable, __index, classname */
741 lua_pop (L, 3);
742 }
743 else {
744 g_set_error (err, g_quark_from_static_string ("lua_logger"),
745 EINVAL, "no metatable found for userdata");
746 }
747
748 return uid;
749 }
750
751 static gboolean
lua_logger_log_format(lua_State * L,gint fmt_pos,gboolean is_string,gchar * logbuf,gsize remain)752 lua_logger_log_format (lua_State *L, gint fmt_pos, gboolean is_string,
753 gchar *logbuf, gsize remain)
754 {
755 gchar *d;
756 const gchar *s, *c;
757 gsize r, cpylen = 0;
758 guint arg_num = 0, cur_arg;
759 bool num_arg = false;
760 struct lua_logger_trace tr;
761 enum {
762 copy_char = 0,
763 got_percent,
764 parse_arg_num
765 } state = copy_char;
766
767 d = logbuf;
768 s = lua_tostring (L, fmt_pos);
769 c = s;
770 cur_arg = fmt_pos;
771
772 if (s == NULL) {
773 return FALSE;
774 }
775
776 while (remain > 0 && *s != '\0') {
777 switch (state) {
778 case copy_char:
779 if (*s == '%') {
780 state = got_percent;
781 s++;
782 if (cpylen > 0) {
783 memcpy (d, c, cpylen);
784 d += cpylen;
785 }
786 cpylen = 0;
787 }
788 else {
789 s++;
790 cpylen ++;
791 remain--;
792 }
793 break;
794 case got_percent:
795 if (g_ascii_isdigit (*s) || *s == 's') {
796 state = parse_arg_num;
797 c = s;
798 }
799 else {
800 *d++ = *s++;
801 c = s;
802 state = copy_char;
803 }
804 break;
805 case parse_arg_num:
806 if (g_ascii_isdigit (*s)) {
807 s++;
808 num_arg = true;
809 }
810 else {
811 if (num_arg) {
812 arg_num = strtoul (c, NULL, 10);
813 arg_num += fmt_pos - 1;
814 /* Update the current argument */
815 cur_arg = arg_num;
816 }
817 else {
818 /* We have non numeric argument, e.g. %s */
819 arg_num = cur_arg ++;
820 s ++;
821 }
822
823 if (arg_num < 1 || arg_num > (guint) lua_gettop (L) + 1) {
824 msg_err ("wrong argument number: %ud", arg_num);
825
826 return FALSE;
827 }
828
829 memset (&tr, 0, sizeof (tr));
830 r = lua_logger_out_type (L, arg_num + 1, d, remain, &tr,
831 is_string ? LUA_ESCAPE_UNPRINTABLE : LUA_ESCAPE_LOG);
832 g_assert (r <= remain);
833 remain -= r;
834 d += r;
835 state = copy_char;
836 c = s;
837 }
838 break;
839 }
840 }
841
842 if (state == parse_arg_num) {
843 if (num_arg) {
844 arg_num = strtoul (c, NULL, 10);
845 arg_num += fmt_pos - 1;
846 }
847 else {
848 /* We have non numeric argument, e.g. %s */
849 arg_num = cur_arg;
850 }
851
852 if (arg_num < 1 || arg_num > (guint) lua_gettop (L) + 1) {
853 msg_err ("wrong argument number: %ud", arg_num);
854
855 return FALSE;
856 }
857
858 memset (&tr, 0, sizeof (tr));
859 r = lua_logger_out_type (L, arg_num + 1, d, remain, &tr,
860 is_string ? LUA_ESCAPE_UNPRINTABLE : LUA_ESCAPE_LOG);
861 g_assert (r <= remain);
862 remain -= r;
863 d += r;
864 }
865 else if (state == copy_char) {
866 if (cpylen > 0 && remain > 0) {
867 memcpy (d, c, cpylen);
868 d += cpylen;
869 }
870 }
871
872 *d = '\0';
873
874
875 return TRUE;
876 }
877
878 static gint
lua_logger_do_log(lua_State * L,GLogLevelFlags level,gboolean is_string,gint start_pos)879 lua_logger_do_log (lua_State *L,
880 GLogLevelFlags level,
881 gboolean is_string,
882 gint start_pos)
883 {
884 gchar logbuf[RSPAMD_LOGBUF_SIZE - 128];
885 const gchar *uid = NULL;
886 gint fmt_pos = start_pos;
887 gint ret;
888 GError *err = NULL;
889
890 if (lua_type (L, start_pos) == LUA_TSTRING) {
891 fmt_pos = start_pos;
892 }
893 else if (lua_type (L, start_pos) == LUA_TUSERDATA) {
894 fmt_pos = start_pos + 1;
895
896 uid = lua_logger_get_id (L, start_pos, &err);
897
898 if (uid == NULL) {
899 ret = luaL_error (L, "bad userdata for logging: %s",
900 err ? err->message : "unknown error");
901
902 if (err) {
903 g_error_free (err);
904 }
905
906 return ret;
907 }
908 }
909 else {
910 /* Bad argument type */
911 return luaL_error (L, "bad format string type: %s",
912 lua_typename (L, lua_type (L, start_pos)));
913 }
914
915 ret = lua_logger_log_format (L, fmt_pos, is_string,
916 logbuf, sizeof (logbuf) - 1);
917
918 if (ret) {
919 if (is_string) {
920 lua_pushstring (L, logbuf);
921 return 1;
922 }
923 else {
924 lua_common_log_line (level, L, logbuf, uid, "lua", 1);
925 }
926 }
927 else {
928 if (is_string) {
929 lua_pushnil (L);
930
931 return 1;
932 }
933 }
934
935 return 0;
936 }
937
938 static gint
lua_logger_errx(lua_State * L)939 lua_logger_errx (lua_State *L)
940 {
941 LUA_TRACE_POINT;
942 return lua_logger_do_log (L, G_LOG_LEVEL_CRITICAL, FALSE, 1);
943 }
944
945 static gint
lua_logger_warnx(lua_State * L)946 lua_logger_warnx (lua_State *L)
947 {
948 LUA_TRACE_POINT;
949 return lua_logger_do_log (L, G_LOG_LEVEL_WARNING, FALSE, 1);
950 }
951
952 static gint
lua_logger_infox(lua_State * L)953 lua_logger_infox (lua_State *L)
954 {
955 LUA_TRACE_POINT;
956 return lua_logger_do_log (L, G_LOG_LEVEL_INFO, FALSE, 1);
957 }
958
959 static gint
lua_logger_messagex(lua_State * L)960 lua_logger_messagex (lua_State *L)
961 {
962 LUA_TRACE_POINT;
963 return lua_logger_do_log (L, G_LOG_LEVEL_MESSAGE, FALSE, 1);
964 }
965
966 static gint
lua_logger_debugx(lua_State * L)967 lua_logger_debugx (lua_State *L)
968 {
969 LUA_TRACE_POINT;
970 return lua_logger_do_log (L, G_LOG_LEVEL_DEBUG, FALSE, 1);
971 }
972
973 static gint
lua_logger_logx(lua_State * L)974 lua_logger_logx (lua_State *L)
975 {
976 LUA_TRACE_POINT;
977 GLogLevelFlags flags = lua_tonumber (L, 1);
978 const gchar *modname = lua_tostring (L, 2), *uid = NULL;
979 gchar logbuf[RSPAMD_LOGBUF_SIZE - 128];
980 gboolean ret;
981 gint stack_pos = 1;
982
983 if (lua_type (L, 3) == LUA_TSTRING) {
984 uid = luaL_checkstring (L, 3);
985 }
986 else if (lua_type (L, 3) == LUA_TUSERDATA) {
987 uid = lua_logger_get_id (L, 3, NULL);
988 }
989 else {
990 uid = "???";
991 }
992
993 if (uid && modname) {
994 if (lua_type (L, 4) == LUA_TSTRING) {
995 ret = lua_logger_log_format (L, 4, FALSE, logbuf, sizeof (logbuf) - 1);
996 }
997 else if (lua_type (L, 4) == LUA_TNUMBER) {
998 stack_pos = lua_tonumber (L, 4);
999 ret = lua_logger_log_format (L, 5, FALSE, logbuf, sizeof (logbuf) - 1);
1000 }
1001 else {
1002 return luaL_error (L, "invalid argument on pos 4");
1003 }
1004
1005 if (ret) {
1006 lua_common_log_line (flags, L, logbuf, uid, modname, stack_pos);
1007 }
1008 }
1009 else {
1010 return luaL_error (L, "invalid arguments");
1011 }
1012
1013 return 0;
1014 }
1015
1016
1017 static gint
lua_logger_debugm(lua_State * L)1018 lua_logger_debugm (lua_State *L)
1019 {
1020 LUA_TRACE_POINT;
1021 gchar logbuf[RSPAMD_LOGBUF_SIZE - 128];
1022 const gchar *uid = NULL, *module = NULL;
1023 gint stack_pos = 1;
1024 gboolean ret;
1025
1026 module = luaL_checkstring (L, 1);
1027
1028 if (lua_type (L, 2) == LUA_TSTRING) {
1029 uid = luaL_checkstring (L, 2);
1030 }
1031 else {
1032 uid = lua_logger_get_id (L, 2, NULL);
1033 }
1034
1035 if (uid && module) {
1036 if (lua_type (L, 3) == LUA_TSTRING) {
1037 ret = lua_logger_log_format (L, 3, FALSE, logbuf, sizeof (logbuf) - 1);
1038 }
1039 else if (lua_type (L, 3) == LUA_TNUMBER) {
1040 stack_pos = lua_tonumber (L, 3);
1041 ret = lua_logger_log_format (L, 4, FALSE, logbuf, sizeof (logbuf) - 1);
1042 }
1043 else {
1044 return luaL_error (L, "invalid argument on pos 3");
1045 }
1046
1047 if (ret) {
1048 lua_common_log_line (G_LOG_LEVEL_DEBUG, L, logbuf, uid, module, stack_pos);
1049 }
1050 }
1051 else {
1052 return luaL_error (L, "invalid arguments");
1053 }
1054
1055 return 0;
1056 }
1057
1058
1059
1060 static gint
lua_logger_slog(lua_State * L)1061 lua_logger_slog (lua_State *L)
1062 {
1063 return lua_logger_do_log (L, 0, TRUE, 1);
1064 }
1065
1066 static gint
lua_logger_log_level(lua_State * L)1067 lua_logger_log_level (lua_State *L)
1068 {
1069 gint log_level = rspamd_log_get_log_level (NULL);
1070
1071 lua_pushstring (L, rspamd_get_log_severity_string(log_level));
1072
1073 return 1;
1074 }
1075
1076 /*** Init functions ***/
1077
1078 static gint
lua_load_logger(lua_State * L)1079 lua_load_logger (lua_State *L)
1080 {
1081 lua_newtable (L);
1082 luaL_register (L, NULL, loggerlib_f);
1083
1084 return 1;
1085 }
1086
1087 void
luaopen_logger(lua_State * L)1088 luaopen_logger (lua_State *L)
1089 {
1090 rspamd_lua_add_preload (L, "rspamd_logger", lua_load_logger);
1091 }
1092