• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/log.c

  1 /*
  2  * Copyright (C) 2023 Jo-Philipp Wich <jo@mein.io>
  3  *
  4  * Permission to use, copy, modify, and/or 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 /**
 18  * # System logging functions
 19  *
 20  * The `log` module provides bindings to the POSIX syslog functions `openlog()`,
 21  * `syslog()` and `closelog()` as well as - when available - the OpenWrt
 22  * specific ulog library functions.
 23  *
 24  * Functions can be individually imported and directly accessed using the
 25  * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import}
 26  * syntax:
 27  *
 28  *   ```
 29  *   import { openlog, syslog, LOG_PID, LOG_USER, LOG_ERR } from 'log';
 30  *
 31  *   openlog("my-log-ident", LOG_PID, LOG_USER);
 32  *   syslog(LOG_ERR, "An error occurred!");
 33  *
 34  *   // OpenWrt specific ulog functions
 35  *   import { ulog_open, ulog, ULOG_SYSLOG, LOG_DAEMON, LOG_INFO } from 'log';
 36  *
 37  *   ulog_open(ULOG_SYSLOG, LOG_DAEMON, "my-log-ident");
 38  *   ulog(LOG_INFO, "The current epoch is %d", time());
 39  *   ```
 40  *
 41  * Alternatively, the module namespace can be imported
 42  * using a wildcard import statement:
 43  *
 44  *   ```
 45  *   import * as log from 'log';
 46  *
 47  *   log.openlog("my-log-ident", log.LOG_PID, log.LOG_USER);
 48  *   log.syslog(log.LOG_ERR, "An error occurred!");
 49  *
 50  *   // OpenWrt specific ulog functions
 51  *   log.ulog_open(log.ULOG_SYSLOG, log.LOG_DAEMON, "my-log-ident");
 52  *   log.ulog(log.LOG_INFO, "The current epoch is %d", time());
 53  *   ```
 54  *
 55  * Additionally, the log module namespace may also be imported by invoking the
 56  * `ucode` interpreter with the `-llog` switch.
 57  *
 58  * ## Constants
 59  *
 60  * The `log` module declares a number of numeric constants to specify logging
 61  * facility, priority and option values, as well as ulog specific channels.
 62  *
 63  * ### Syslog Options
 64  *
 65  * | Constant Name | Description                                             |
 66  * |---------------|---------------------------------------------------------|
 67  * | `LOG_PID`     | Include PID with each message.                          |
 68  * | `LOG_CONS`    | Log to console if error occurs while sending to syslog. |
 69  * | `LOG_NDELAY`  | Open the connection to the logger immediately.          |
 70  * | `LOG_ODELAY`  | Delay open until the first message is logged.           |
 71  * | `LOG_NOWAIT`  | Do not wait for child processes created during logging. |
 72  *
 73  * ### Syslog Facilities
 74  *
 75  * | Constant Name  | Description                                      |
 76  * |----------------|--------------------------------------------------|
 77  * | `LOG_AUTH`     | Authentication/authorization messages.           |
 78  * | `LOG_AUTHPRIV` | Private authentication messages.                 |
 79  * | `LOG_CRON`     | Clock daemon (cron and at commands).             |
 80  * | `LOG_DAEMON`   | System daemons without separate facility values. |
 81  * | `LOG_FTP`      | FTP server daemon.                               |
 82  * | `LOG_KERN`     | Kernel messages.                                 |
 83  * | `LOG_LPR`      | Line printer subsystem.                          |
 84  * | `LOG_MAIL`     | Mail system.                                     |
 85  * | `LOG_NEWS`     | Network news subsystem.                          |
 86  * | `LOG_SYSLOG`   | Messages generated internally by syslogd.        |
 87  * | `LOG_USER`     | Generic user-level messages.                     |
 88  * | `LOG_UUCP`     | UUCP subsystem.                                  |
 89  * | `LOG_LOCAL0`   | Local use 0 (custom facility).                   |
 90  * | `LOG_LOCAL1`   | Local use 1 (custom facility).                   |
 91  * | `LOG_LOCAL2`   | Local use 2 (custom facility).                   |
 92  * | `LOG_LOCAL3`   | Local use 3 (custom facility).                   |
 93  * | `LOG_LOCAL4`   | Local use 4 (custom facility).                   |
 94  * | `LOG_LOCAL5`   | Local use 5 (custom facility).                   |
 95  * | `LOG_LOCAL6`   | Local use 6 (custom facility).                   |
 96  * | `LOG_LOCAL7`   | Local use 7 (custom facility).                   |
 97  *
 98  * ### Syslog Priorities
 99  *
100  * | Constant Name | Description                         |
101  * |---------------|-------------------------------------|
102  * | `LOG_EMERG`   | System is unusable.                 |
103  * | `LOG_ALERT`   | Action must be taken immediately.   |
104  * | `LOG_CRIT`    | Critical conditions.                |
105  * | `LOG_ERR`     | Error conditions.                   |
106  * | `LOG_WARNING` | Warning conditions.                 |
107  * | `LOG_NOTICE`  | Normal, but significant, condition. |
108  * | `LOG_INFO`    | Informational message.              |
109  * | `LOG_DEBUG`   | Debug-level message.                |
110  *
111  * ### Ulog channels
112  *
113  * | Constant Name | Description                          |
114  * |---------------|--------------------------------------|
115  * | `ULOG_KMSG`   | Log messages to `/dev/kmsg` (dmesg). |
116  * | `ULOG_STDIO`  | Log messages to stdout.              |
117  * | `ULOG_SYSLOG` | Log messages to syslog.              |
118  *
119  * @module log
120  */
121 
122 #include <syslog.h>
123 #include <errno.h>
124 
125 #ifdef HAVE_ULOG
126 #include <libubox/ulog.h>
127 #endif
128 
129 #include "ucode/module.h"
130 
131 
132 static char log_ident[32];
133 
134 /**
135  * The following log option strings are recognized:
136  *
137  * | Log Option | Description                                                |
138  * |------------|------------------------------------------------------------|
139  * | `"pid"`    | Include PID with each message.                             |
140  * | `"cons"`   | Log to console if an error occurs while sending to syslog. |
141  * | `"ndelay"` | Open the connection to the logger immediately.             |
142  * | `"odelay"` | Delay open until the first message is logged.              |
143  * | `"nowait"` | Do not wait for child processes created during logging.    |
144  *
145  * @typedef {string} module:log.LogOption
146  * @enum {module:log.LogOption}
147  *
148  */
149 static const struct { const char *name; int value; } log_options[] = {
150         { "pid", LOG_PID },
151         { "cons", LOG_CONS },
152         { "ndelay", LOG_NDELAY },
153         { "odelay", LOG_ODELAY },
154         { "nowait", LOG_NOWAIT },
155 };
156 
157 /**
158  * The following log facility strings are recognized:
159  *
160  * | Facility     | Description                                      |
161  * |--------------|--------------------------------------------------|
162  * | `"auth"`     | Authentication/authorization messages.           |
163  * | `"authpriv"` | Private authentication messages.                 |
164  * | `"cron"`     | Clock daemon (cron and at commands).             |
165  * | `"daemon"`   | System daemons without separate facility values. |
166  * | `"ftp"`      | FTP server daemon.                               |
167  * | `"kern"`     | Kernel messages.                                 |
168  * | `"lpr"`      | Line printer subsystem.                          |
169  * | `"mail"`     | Mail system.                                     |
170  * | `"news"`     | Network news subsystem.                          |
171  * | `"syslog"`   | Messages generated internally by syslogd.        |
172  * | `"user"`     | Generic user-level messages.                     |
173  * | `"uucp"`     | UUCP subsystem.                                  |
174  * | `"local0"`   | Local use 0 (custom facility).                   |
175  * | `"local1"`   | Local use 1 (custom facility).                   |
176  * | `"local2"`   | Local use 2 (custom facility).                   |
177  * | `"local3"`   | Local use 3 (custom facility).                   |
178  * | `"local4"`   | Local use 4 (custom facility).                   |
179  * | `"local5"`   | Local use 5 (custom facility).                   |
180  * | `"local6"`   | Local use 6 (custom facility).                   |
181  * | `"local7"`   | Local use 7 (custom facility).                   |
182  *
183  * @typedef {string} module:log.LogFacility
184  * @enum {module:log.LogFacility}
185  */
186 static const struct { const char *name; int value; } log_facilities[] = {
187         { "auth", LOG_AUTH },
188 #ifdef LOG_AUTHPRIV
189         { "authpriv", LOG_AUTHPRIV },
190 #endif
191         { "cron", LOG_CRON },
192         { "daemon", LOG_DAEMON },
193 #ifdef LOG_FTP
194         { "ftp", LOG_FTP },
195 #endif
196         { "kern", LOG_KERN },
197         { "lpr", LOG_LPR },
198         { "mail", LOG_MAIL },
199         { "news", LOG_NEWS },
200         { "syslog", LOG_SYSLOG },
201         { "user", LOG_USER },
202         { "uucp", LOG_UUCP },
203         { "local0", LOG_LOCAL0 },
204         { "local1", LOG_LOCAL1 },
205         { "local2", LOG_LOCAL2 },
206         { "local3", LOG_LOCAL3 },
207         { "local4", LOG_LOCAL4 },
208         { "local5", LOG_LOCAL5 },
209         { "local6", LOG_LOCAL6 },
210         { "local7", LOG_LOCAL7 },
211 };
212 
213 /**
214  * The following log priority strings are recognized:
215  *
216  * | Priority    | Description                         |
217  * |-------------|-------------------------------------|
218  * | `"emerg"`   | System is unusable.                 |
219  * | `"alert"`   | Action must be taken immediately.   |
220  * | `"crit"`    | Critical conditions.                |
221  * | `"err"`     | Error conditions.                   |
222  * | `"warning"` | Warning conditions.                 |
223  * | `"notice"`  | Normal, but significant, condition. |
224  * | `"info"`    | Informational message.              |
225  * | `"debug"`   | Debug-level message.                |
226  *
227  * @typedef {string} module:log.LogPriority
228  * @enum {module:log.LogPriority}
229  */
230 static const struct { const char *name; int value; } log_priorities[] = {
231         { "emerg", LOG_EMERG },
232         { "alert", LOG_ALERT },
233         { "crit", LOG_CRIT },
234         { "err", LOG_ERR },
235         { "warning", LOG_WARNING },
236         { "notice", LOG_NOTICE },
237         { "info", LOG_INFO },
238         { "debug", LOG_DEBUG },
239 };
240 
241 
242 static int
243 parse_facility(uc_value_t *facility)
244 {
245         char *s;
246         int rv;
247 
248         switch (ucv_type(facility)) {
249         case UC_STRING:
250                 s = ucv_string_get(facility);
251 
252                 for (size_t i = 0; i < ARRAY_SIZE(log_facilities); i++)
253                         if (s && !strcasecmp(s, log_facilities[i].name))
254                                 return log_facilities[i].value;
255 
256                 return -1;
257 
258         case UC_INTEGER:
259                 rv = ucv_int64_get(facility);
260 
261                 if (errno == ERANGE || rv < 0)
262                         return -1;
263 
264                 return rv;
265 
266         case UC_NULL:
267                 return 0;
268 
269         default:
270                 return -1;
271         }
272 }
273 
274 static int
275 parse_options(uc_value_t *option)
276 {
277         char *s;
278         int rv;
279 
280         switch (ucv_type(option)) {
281         case UC_ARRAY:
282                 rv = 0;
283 
284                 for (size_t i = 0; i < ucv_array_length(option); i++) {
285                         uc_value_t *opt = ucv_array_get(option, i);
286                         char *s = ucv_string_get(opt);
287 
288                         for (size_t j = 0; j < ARRAY_SIZE(log_options); j++) {
289                                 if (s && !strcasecmp(log_options[j].name, s))
290                                         rv |= log_options[j].value;
291                                 else
292                                         return -1;
293                         }
294                 }
295 
296                 return rv;
297 
298         case UC_STRING:
299                 s = ucv_string_get(option);
300 
301                 for (size_t i = 0; i < ARRAY_SIZE(log_options); i++)
302                         if (s && !strcasecmp(s, log_options[i].name))
303                                 return log_options[i].value;
304 
305                 return -1;
306 
307         case UC_INTEGER:
308                 rv = ucv_int64_get(option);
309 
310                 if (errno == ERANGE || rv < 0)
311                         return -1;
312 
313                 return rv;
314 
315         case UC_NULL:
316                 return 0;
317 
318         default:
319                 return -1;
320         }
321 }
322 
323 static int
324 parse_priority(uc_value_t *priority)
325 {
326         char *s;
327         int rv;
328 
329         switch (ucv_type(priority)) {
330         case UC_STRING:
331                 s = ucv_string_get(priority);
332 
333                 for (size_t i = 0; i < ARRAY_SIZE(log_priorities); i++)
334                         if (s && !strcasecmp(s, log_priorities[i].name))
335                                 return log_priorities[i].value;
336 
337                 return -1;
338 
339         case UC_INTEGER:
340                 rv = ucv_int64_get(priority);
341 
342                 if (errno == ERANGE || rv < 0)
343                         return -1;
344 
345                 return rv;
346 
347         case UC_NULL:
348                 return LOG_INFO;
349 
350         default:
351                 return -1;
352         }
353 }
354 
355 static char *
356 parse_ident(uc_vm_t *vm, uc_value_t *ident)
357 {
358         if (!ident)
359                 return NULL;
360 
361         char *s = ucv_to_string(vm, ident);
362 
363         snprintf(log_ident, sizeof(log_ident), "%s", s ? s : "");
364         free(s);
365 
366         return log_ident[0] ? log_ident : NULL;
367 }
368 
369 /**
370  * Open connection to system logger.
371  *
372  * The `openlog()` function instructs the program to establish a connection to
373  * the system log service and configures the default facility and identification
374  * for use in subsequent log operations. It may be omitted, in which case the
375  * first call to `syslog()` will implicitly call `openlog()` with a default
376  * ident value representing the program name and a default `LOG_USER` facility.
377  *
378  * The log option argument may be either a single string value containing an
379  * option name, an array of option name strings or a numeric value representing
380  * a bitmask of `LOG_*` option constants.
381  *
382  * The facility argument may be either a single string value containing a
383  * facility name or one of the numeric `LOG_*` facility constants in the module
384  * namespace.
385  *
386  * Returns `true` if the system `openlog()` function was invoked.
387  *
388  * Returns `false` if an invalid argument, such as an unrecognized option or
389  * facility name, was provided.
390  *
391  * @function module:log#openlog
392  *
393  * @param {string} [ident]
394  * A string identifying the program name. If omitted, the name of the calling
395  * process is used by default.
396  *
397  * @param {number|module:log.LogOption|module:log.LogOption[]} [options]
398  * Logging options to use.
399  *
400  * See {@link module:log.LogOption|LogOption} for recognized option names.
401  *
402  * @param {number|module:log.LogFacility} [facility="user"]
403  * The facility to use for log messages generated by subsequent syslog calls.
404  *
405  * See {@link module:log.LogFacility|LogFacility} for recognized facility names.
406  *
407  * @returns {boolean}
408  *
409  * @example
410  * // Example usage of openlog function
411  * openlog("myapp", LOG_PID | LOG_NDELAY, LOG_LOCAL0);
412  *
413  * // Using option names instead of bitmask and LOG_USER facility
414  * openlog("myapp", [ "pid", "ndelay" ], "user");
415  */
416 static uc_value_t *
417 uc_openlog(uc_vm_t *vm, size_t nargs)
418 {
419         char *ident = parse_ident(vm, uc_fn_arg(0));
420         int options = parse_options(uc_fn_arg(1));
421         int facility = parse_facility(uc_fn_arg(2));
422 
423         if (options == -1 || facility == -1)
424                 return ucv_boolean_new(false);
425 
426         openlog(ident, options, facility);
427 
428         return ucv_boolean_new(true);
429 }
430 
431 /**
432  * Log a message to the system logger.
433  *
434  * This function logs a message to the system logger. The function behaves in a
435  * sprintf-like manner, allowing the use of format strings and associated
436  * arguments to construct log messages.
437  *
438  * If the `openlog` function has not been called explicitly before, `syslog()`
439  * implicitly calls `openlog()`, using a default ident and `LOG_USER` facility
440  * value before logging the message.
441  *
442  * If the `format` argument is not a string and not `null`, it will be
443  * implicitly converted to a string and logged as-is, without further format
444  * string processing.
445  *
446  * Returns `true` if a message was passed to the system `syslog()` function.
447  *
448  * Returns `false` if an invalid priority value or an empty message was given.
449  *
450  * @function module:log#syslog
451  *
452  * @param {number|module:log.LogPriority} priority
453  * Log message priority. May be either a number value (potentially bitwise OR-ed
454  * with a log facility constant) which is passed as-is to the system `syslog()`
455  * function or a priority name string.
456  *
457  * See {@link module:log.LogPriority|LogPriority} for recognized priority names.
458  *
459  * @param {*} format
460  * The sprintf-like format string for the log message, or any other, non-null,
461  * non-string value type which will be implicitly stringified and logged as-is.
462  *
463  * @param {...*} [args]
464  * In case a format string value was provided in the previous argument, then
465  * all subsequent arguments are used to replace the placeholders in the format
466  * string.
467  *
468  * @returns {boolean}
469  *
470  * @example
471  * // Example usage of syslog function with format string and arguments
472  * const username = "user123";
473  * const errorCode = 404;
474  * syslog(LOG_ERR, "User %s encountered error: %d", username, errorCode);
475  *
476  * // If openlog has not been called explicitly, it is implicitly called with defaults:
477  * syslog(LOG_INFO, "This message will be logged with default settings.");
478  *
479  * // Selectively override used facility by OR-ing numeric constant
480  * const password =" secret";
481  * syslog(LOG_DEBUG|LOG_AUTHPRIV, "The password %s has been wrong", secret);
482  *
483  * // Using priority names for logging
484  * syslog("emerg", "System shutdown imminent!");
485  *
486  * // Implicit stringification
487  * syslog("debug", { foo: 1, bar: true, baz: [1, 2, 3] });
488  */
489 static uc_value_t *
490 uc_syslog(uc_vm_t *vm, size_t nargs)
491 {
492         int priority = parse_priority(uc_fn_arg(0));
493 
494         if (priority == -1 || nargs < 2)
495                 return ucv_boolean_new(false);
496 
497         uc_value_t *fmt = uc_fn_arg(1), *msg;
498         uc_cfn_ptr_t fmtfn;
499         char *s;
500 
501         switch (ucv_type(fmt)) {
502         case UC_STRING:
503                 fmtfn = uc_stdlib_function("sprintf");
504                 msg = fmtfn(vm, nargs - 1);
505 
506                 if (msg) {
507                         syslog(priority, "%s", ucv_string_get(msg));
508                         ucv_put(msg);
509 
510                         return ucv_boolean_new(true);
511                 }
512 
513                 break;
514 
515         case UC_NULL:
516                 break;
517 
518         default:
519                 s = ucv_to_string(vm, fmt);
520 
521                 if (s) {
522                         syslog(priority, "%s", s);
523                         free(s);
524 
525                         return ucv_boolean_new(true);
526                 }
527 
528                 break;
529         }
530 
531         return ucv_boolean_new(false);
532 }
533 
534 /**
535  * Close connection to system logger.
536  *
537  * The usage of this function is optional, and usually an explicit log
538  * connection tear down is not required.
539  *
540  * @function module:log#closelog
541  */
542 static uc_value_t *
543 uc_closelog(uc_vm_t *vm, size_t nargs)
544 {
545         closelog();
546 
547         return NULL;
548 }
549 
550 
551 #ifdef HAVE_ULOG
552 /**
553  * The following ulog channel strings are recognized:
554  *
555  * | Channel    | Description                                       |
556  * |------------|---------------------------------------------------|
557  * | `"kmsg"`   | Log to `/dev/kmsg`, log messages appear in dmesg. |
558  * | `"syslog"` | Use standard `syslog()` mechanism.                |
559  * | `"stdio"`  | Use stderr for log output.                        |
560  *
561  * @typedef {string} module:log.UlogChannel
562  * @enum {module:log.UlogChannel}
563  */
564 static const struct { const char *name; int value; } ulog_channels[] = {
565         { "kmsg", ULOG_KMSG },
566         { "syslog", ULOG_SYSLOG },
567         { "stdio", ULOG_STDIO },
568 };
569 
570 static int
571 parse_channels(uc_value_t *channels)
572 {
573         char *s;
574         int rv;
575 
576         switch (ucv_type(channels)) {
577         case UC_ARRAY:
578                 rv = 0;
579 
580                 for (size_t i = 0; i < ucv_array_length(channels); i++) {
581                         uc_value_t *channel = ucv_array_get(channels, i);
582                         char *s = ucv_string_get(channel);
583 
584                         for (size_t j = 0; j < ARRAY_SIZE(ulog_channels); j++) {
585                                 if (s && !strcasecmp(s, ulog_channels[j].name))
586                                         rv |= ulog_channels[j].value;
587                                 else
588                                         return -1;
589                         }
590                 }
591 
592                 return rv;
593 
594         case UC_STRING:
595                 s = ucv_string_get(channels);
596 
597                 for (size_t i = 0; i < ARRAY_SIZE(ulog_channels); i++)
598                         if (s && !strcasecmp(s, ulog_channels[i].name))
599                                 return ulog_channels[i].value;
600 
601                 return -1;
602 
603         case UC_INTEGER:
604                 rv = ucv_uint64_get(channels);
605 
606                 if (errno == ERANGE)
607                         return -1;
608 
609                 return rv & (ULOG_KMSG|ULOG_STDIO|ULOG_SYSLOG);
610 
611         case UC_NULL:
612                 return 0;
613 
614         default:
615                 return -1;
616         }
617 }
618 
619 /**
620  * Configure ulog logger.
621  *
622  * This functions configures the ulog mechanism and is analogeous to using the
623  * `openlog()` function in conjuncton with `syslog()`.
624  *
625  * The `ulog_open()` function is OpenWrt specific and may not be present on
626  * other systems. Use `openlog()` and `syslog()` instead for portability to
627  * non-OpenWrt environments.
628  *
629  * A program may use multiple channels to simultaneously output messages using
630  * different means. The channel argument may either be a single string value
631  * containing a channel name, an array of channel names or a numeric value
632  * representing a bitmask of `ULOG_*` channel constants.
633  *
634  * The facility argument may be either a single string value containing a
635  * facility name or one of the numeric `LOG_*` facility constants in the module
636  * namespace.
637  *
638  * The default facility value varies, depending on the execution context of the
639  * program. In OpenWrt's preinit boot phase, or when stdout is not connected to
640  * an interactive terminal, the facility defaults to `"daemon"` (`LOG_DAEMON`),
641  * otherwise to `"user"` (`LOG_USER`).
642  *
643  * Likewise, the default channel is selected depending on the context. During
644  * OpenWrt's preinit boot phase, the `"kmsg"` channel is used, for interactive
645  * terminals the `"stdio"` one and for all other cases the `"syslog"` channel
646  * is selected.
647  *
648  * Returns `true` if ulog was configured.
649  *
650  * Returns `false` if an invalid argument, such as an unrecognized channel or
651  * facility name, was provided.
652  *
653  * @function module:log#ulog_open
654  *
655  * @param {number|module:log.UlogChannel|module:log.UlogChannel[]} [channel]
656  * Specifies the log channels to use.
657  *
658  * See {@link module:log.UlogChannel|UlogChannel} for recognized channel names.
659  *
660  * @param {number|module:log.LogFacility} [facility]
661  * The facility to use for log messages generated by subsequent `ulog()` calls.
662  *
663  * See {@link module:log.LogFacility|LogFacility} for recognized facility names.
664  *
665  * @param {string} [ident]
666  * A string identifying the program name. If omitted, the name of the calling
667  * process is used by default.
668  *
669  * @returns {boolean}
670  *
671  * @example
672  * // Log to dmesg and stderr
673  * ulog_open(["stdio", "kmsg"], "daemon", "my-program");
674  *
675  * // Use numeric constants and use implicit default ident
676  * ulog_open(ULOG_SYSLOG, LOG_LOCAL0);
677  */
678 static uc_value_t *
679 uc_ulog_open(uc_vm_t *vm, size_t nargs)
680 {
681         int channels = parse_channels(uc_fn_arg(0));
682         int facility = parse_facility(uc_fn_arg(1));
683         char *ident = parse_ident(vm, uc_fn_arg(2));
684 
685         if (channels == -1 || facility == -1)
686                 return ucv_boolean_new(false);
687 
688         ulog_open(channels, facility, ident);
689 
690         return ucv_boolean_new(true);
691 }
692 
693 static uc_value_t *
694 uc_ulog_log_common(uc_vm_t *vm, size_t nargs, int priority)
695 {
696         uc_value_t *fmt = uc_fn_arg(0), *msg;
697         uc_cfn_ptr_t fmtfn;
698         char *s;
699 
700         switch (ucv_type(fmt)) {
701         case UC_STRING:
702                 fmtfn = uc_stdlib_function("sprintf");
703                 msg = fmtfn(vm, nargs);
704 
705                 if (msg) {
706                         ulog(priority, "%s", ucv_string_get(msg));
707                         ucv_put(msg);
708 
709                         return ucv_boolean_new(true);
710                 }
711 
712                 break;
713 
714         case UC_NULL:
715                 break;
716 
717         default:
718                 s = ucv_to_string(vm, fmt);
719 
720                 if (s) {
721                         ulog(priority, "%s", s);
722                         free(s);
723 
724                         return ucv_boolean_new(true);
725                 }
726 
727                 break;
728         }
729 
730         return ucv_boolean_new(false);
731 }
732 
733 /**
734  * Log a message via the ulog mechanism.
735  *
736  * The `ulog()` function outputs the given log message to all configured ulog
737  * channels unless the given priority level exceeds the globally configured ulog
738  * priority threshold. See {@link module:log#ulog_threshold|ulog_threshold()}
739  * for details.
740  *
741  * The `ulog()` function is OpenWrt specific and may not be present on other
742  * systems. Use `syslog()` instead for portability to non-OpenWrt environments.
743  *
744  * Like `syslog()`, the function behaves in a sprintf-like manner, allowing the
745  * use of format strings and associated arguments to construct log messages.
746  *
747  * If the `ulog_open()` function has not been called explicitly before, `ulog()`
748  * implicitly configures certain defaults, see
749  * {@link module:log#ulog_open|ulog_open()} for a detailled description.
750  *
751  * If the `format` argument is not a string and not `null`, it will be
752  * implicitly converted to a string and logged as-is, without further format
753  * string processing.
754  *
755  * Returns `true` if a message was passed to the underlying `ulog()` function.
756  *
757  * Returns `false` if an invalid priority value or an empty message was given.
758  *
759  * @function module:log#ulog
760  *
761  * @param {number|module:log.LogPriority} priority
762  * Log message priority. May be either a number value or a priority name string.
763  *
764  * See {@link module:log.LogPriority|LogPriority} for recognized priority names.
765  *
766  * @param {*} format
767  * The sprintf-like format string for the log message, or any other, non-null,
768  * non-string value type which will be implicitly stringified and logged as-is.
769  *
770  * @param {...*} [args]
771  * In case a format string value was provided in the previous argument, then
772  * all subsequent arguments are used to replace the placeholders in the format
773  * string.
774  *
775  * @returns {boolean}
776  *
777  * @example
778  * // Example usage of ulog function with format string and arguments
779  * const username = "user123";
780  * const errorCode = 404;
781  * ulog(LOG_ERR, "User %s encountered error: %d", username, errorCode);
782  *
783  * // Using priority names for logging
784  * ulog("err", "General error encountered");
785  *
786  * // Implicit stringification
787  * ulog("debug", { foo: 1, bar: true, baz: [1, 2, 3] });
788  *
789  * @see module:log#ulog_open
790  * @see module:log#ulog_threshold
791  * @see module:log#syslog
792  */
793 static uc_value_t *
794 uc_ulog_log(uc_vm_t *vm, size_t nargs)
795 {
796         int priority = parse_priority(uc_fn_arg(0));
797 
798         if (priority == -1 || nargs < 2)
799                 return ucv_boolean_new(false);
800 
801         return uc_ulog_log_common(vm, nargs - 1, priority);
802 }
803 
804 /**
805  * Close ulog logger.
806  *
807  * Resets the ulog channels, the default facility and the log ident value to
808  * defaults.
809  *
810  * In case the `"syslog"` channel has been configured, the underlying
811  * `closelog()` function will be invoked.
812  *
813  * The usage of this function is optional, and usually an explicit ulog teardown
814  * is not required.
815  *
816  * The `ulog_close()` function is OpenWrt specific and may not be present on
817  * other systems. Use `closelog()` in conjunction with `syslog()` instead for
818  * portability to non-OpenWrt environments.
819  *
820  * @function module:log#ulog_close
821  *
822  * @see module:log#closelog
823  */
824 static uc_value_t *
825 uc_ulog_close(uc_vm_t *vm, size_t nargs)
826 {
827         ulog_close();
828 
829         return NULL;
830 }
831 
832 /**
833  * Set ulog priority threshold.
834  *
835  * This function configures the application wide log message threshold for log
836  * messages emitted with `ulog()`. Any message with a priority higher (= less
837  * severe) than the threshold priority will be discarded. This is useful to
838  * implement application wide verbosity settings without having to wrap `ulog()`
839  * invocations into a helper function or guarding code.
840  *
841  * When no explicit threshold has been set, `LOG_DEBUG` is used by default,
842  * allowing log messages with all known priorities.
843  *
844  * The `ulog_threshold()` function is OpenWrt specific and may not be present on
845  * other systems. There is no syslog equivalent to this ulog specific threshold
846  * mechanism.
847  *
848  * The priority argument may be either a string value containing a priority name
849  * or one of the numeric `LOG_*` priority constants in the module namespace.
850  *
851  * Returns `true` if a threshold was set.
852  *
853  * Returns `false` if an invalid priority value was given.
854  *
855  * @function module:log#ulog_threshold
856  *
857  * @param {number|module:log.LogPriority} [priority]
858  * The priority threshold to configure.
859  *
860  * See {@link module:log.LogPriority|LogPriority} for recognized priority names.
861  *
862  * @returns {boolean}
863  *
864  * @example
865  * // Set threshold to "warning" or more severe
866  * ulog_threshold(LOG_WARNING);
867  *
868  * // This message will be supressed
869  * ulog(LOG_DEBUG, "Testing thresholds");
870  *
871  * // Using priority name
872  * ulog_threshold("debug");
873  */
874 static uc_value_t *
875 uc_ulog_threshold(uc_vm_t *vm, size_t nargs)
876 {
877         int priority = parse_priority(uc_fn_arg(0));
878 
879         if (priority == -1)
880                 return ucv_boolean_new(false);
881 
882         ulog_threshold(priority);
883 
884         return ucv_boolean_new(true);
885 }
886 
887 /**
888  * Invoke ulog with LOG_INFO.
889  *
890  * This function is convenience wrapper for `ulog(LOG_INFO, ...)`.
891  *
892  * See {@link module:log#ulog|ulog()} for details.
893  *
894  * @function module:log#INFO
895  *
896  * @param {*} format
897  * The sprintf-like format string for the log message, or any other, non-null,
898  * non-string value type which will be implicitly stringified and logged as-is.
899  *
900  * @param {...*} [args]
901  * In case a format string value was provided in the previous argument, then
902  * all subsequent arguments are used to replace the placeholders in the format
903  * string.
904  *
905  * @returns {boolean}
906  *
907  * @example
908  * INFO("This is an info log message");
909  */
910 static uc_value_t *
911 uc_ulog_INFO(uc_vm_t *vm, size_t nargs)
912 {
913         return uc_ulog_log_common(vm, nargs, LOG_INFO);
914 }
915 
916 /**
917  * Invoke ulog with LOG_NOTICE.
918  *
919  * This function is convenience wrapper for `ulog(LOG_NOTICE, ...)`.
920  *
921  * See {@link module:log#ulog|ulog()} for details.
922  *
923  * @function module:log#NOTE
924  *
925  * @param {*} format
926  * The sprintf-like format string for the log message, or any other, non-null,
927  * non-string value type which will be implicitly stringified and logged as-is.
928  *
929  * @param {...*} [args]
930  * In case a format string value was provided in the previous argument, then
931  * all subsequent arguments are used to replace the placeholders in the format
932  * string.
933  *
934  * @returns {boolean}
935  *
936  * @example
937  * NOTE("This is a notification log message");
938  */
939 static uc_value_t *
940 uc_ulog_NOTE(uc_vm_t *vm, size_t nargs)
941 {
942         return uc_ulog_log_common(vm, nargs, LOG_NOTICE);
943 }
944 
945 /**
946  * Invoke ulog with LOG_WARNING.
947  *
948  * This function is convenience wrapper for `ulog(LOG_WARNING, ...)`.
949  *
950  * See {@link module:log#ulog|ulog()} for details.
951  *
952  * @function module:log#WARN
953  *
954  * @param {*} format
955  * The sprintf-like format string for the log message, or any other, non-null,
956  * non-string value type which will be implicitly stringified and logged as-is.
957  *
958  * @param {...*} [args]
959  * In case a format string value was provided in the previous argument, then
960  * all subsequent arguments are used to replace the placeholders in the format
961  * string.
962  *
963  * @returns {boolean}
964  *
965  * @example
966  * WARN("This is a warning");
967  */
968 static uc_value_t *
969 uc_ulog_WARN(uc_vm_t *vm, size_t nargs)
970 {
971         return uc_ulog_log_common(vm, nargs, LOG_WARNING);
972 }
973 
974 /**
975  * Invoke ulog with LOG_ERR.
976  *
977  * This function is convenience wrapper for `ulog(LOG_ERR, ...)`.
978  *
979  * See {@link module:log#ulog|ulog()} for details.
980  *
981  * @function module:log#ERR
982  *
983  * @param {*} format
984  * The sprintf-like format string for the log message, or any other, non-null,
985  * non-string value type which will be implicitly stringified and logged as-is.
986  *
987  * @param {...*} [args]
988  * In case a format string value was provided in the previous argument, then
989  * all subsequent arguments are used to replace the placeholders in the format
990  * string.
991  *
992  * @returns {boolean}
993  *
994  * @example
995  * ERR("This is an error!");
996  */
997 static uc_value_t *
998 uc_ulog_ERR(uc_vm_t *vm, size_t nargs)
999 {
1000         return uc_ulog_log_common(vm, nargs, LOG_ERR);
1001 }
1002 #endif
1003 
1004 
1005 static const uc_function_list_t global_fns[] = {
1006         { "openlog",            uc_openlog },
1007         { "syslog",                     uc_syslog },
1008         { "closelog",           uc_closelog },
1009 
1010 #ifdef HAVE_ULOG
1011         { "ulog_open",          uc_ulog_open },
1012         { "ulog",                       uc_ulog_log },
1013         { "ulog_close",         uc_ulog_close },
1014         { "ulog_threshold",     uc_ulog_threshold },
1015         { "INFO",                       uc_ulog_INFO },
1016         { "NOTE",                       uc_ulog_NOTE },
1017         { "WARN",                       uc_ulog_WARN },
1018         { "ERR",                        uc_ulog_ERR },
1019 #endif
1020 };
1021 
1022 
1023 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
1024 {
1025         uc_function_list_register(scope, global_fns);
1026 
1027 #define ADD_CONST(x) ucv_object_add(scope, #x, ucv_int64_new(x))
1028 
1029         ADD_CONST(LOG_PID);
1030         ADD_CONST(LOG_CONS);
1031         ADD_CONST(LOG_NDELAY);
1032         ADD_CONST(LOG_ODELAY);
1033         ADD_CONST(LOG_NOWAIT);
1034 
1035         ADD_CONST(LOG_AUTH);
1036 #ifdef LOG_AUTHPRIV
1037         ADD_CONST(LOG_AUTHPRIV);
1038 #endif
1039         ADD_CONST(LOG_CRON);
1040         ADD_CONST(LOG_DAEMON);
1041 #ifdef LOG_FTP
1042         ADD_CONST(LOG_FTP);
1043 #endif
1044         ADD_CONST(LOG_KERN);
1045         ADD_CONST(LOG_LPR);
1046         ADD_CONST(LOG_MAIL);
1047         ADD_CONST(LOG_NEWS);
1048         ADD_CONST(LOG_SYSLOG);
1049         ADD_CONST(LOG_USER);
1050         ADD_CONST(LOG_UUCP);
1051         ADD_CONST(LOG_LOCAL0);
1052         ADD_CONST(LOG_LOCAL1);
1053         ADD_CONST(LOG_LOCAL2);
1054         ADD_CONST(LOG_LOCAL3);
1055         ADD_CONST(LOG_LOCAL4);
1056         ADD_CONST(LOG_LOCAL5);
1057         ADD_CONST(LOG_LOCAL6);
1058         ADD_CONST(LOG_LOCAL7);
1059 
1060         ADD_CONST(LOG_EMERG);
1061         ADD_CONST(LOG_ALERT);
1062         ADD_CONST(LOG_CRIT);
1063         ADD_CONST(LOG_ERR);
1064         ADD_CONST(LOG_WARNING);
1065         ADD_CONST(LOG_NOTICE);
1066         ADD_CONST(LOG_INFO);
1067         ADD_CONST(LOG_DEBUG);
1068 
1069 #ifdef HAVE_ULOG
1070         ADD_CONST(ULOG_KMSG);
1071         ADD_CONST(ULOG_SYSLOG);
1072         ADD_CONST(ULOG_STDIO);
1073 #endif
1074 }
1075 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt