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