]> git.ipfire.org Git - thirdparty/FORT-validator.git/commitdiff
Rethink logging for Fort 2
authorAlberto Leiva Popper <ydahhrk@gmail.com>
Mon, 29 Sep 2025 19:28:28 +0000 (13:28 -0600)
committerAlberto Leiva Popper <ydahhrk@gmail.com>
Mon, 29 Sep 2025 19:28:28 +0000 (13:28 -0600)
- Rename "operation logs" to just "logs"
- Rename "validation logs" to "report"

Rationale: Logging is meant for sysadmin consumption.
Sysadmins don't care if some RPKI objects need to be thrown away.

- Logs can be sent to std streams, files or syslog. Reports only go to files.
  Logs and reports go to different files (because they're different things).
- Subdivide DEBUG into TRC, CLT and DBG.

"TRC"s (traces) are algorithm steps as they happen.
"CLT"s (clutters) are detailed information about what's happening.
"DBG"s are just temporal debugging prints.

- Rename CRIT into PANIC.
- Subdivide ERR ento ERR and CRIT.

"ERR"s are normal, expected errors.
"CRIT"s (criticals) are unexpected (but recoverable) errors.

- Apply #121: Redirect WRNs and ERRs to reports during validation cycles.

Logs actually still get WRNs and ERRs, but they're downgraded to TRCs.

Still TODO:

- Configuration layer has not been updated
- Reports don't print offending file names

19 files changed:
src/Makefile.am
src/config.c
src/config.h
src/config/filename_format.c [deleted file]
src/config/filename_format.h [deleted file]
src/config/types.h
src/daemon.c
src/daemon.h
src/log.c
src/log.h
src/main.c
src/object/certificate.c
src/object/tal.c
src/report.c [new file with mode: 0644]
src/report.h [new file with mode: 0644]
src/thread_var.c
src/types/map.c
src/types/map.h
test/mock.c

index f1e79c40c8c829117604a67010860ee83a5d93c4..7f2a50dd7fa166dbc1e2c7386d69122c7c2a3010 100644 (file)
@@ -14,7 +14,6 @@ fort_SOURCES += cachetmp.c cachetmp.h
 fort_SOURCES += common.c common.h
 fort_SOURCES += config/boolean.c config/boolean.h
 fort_SOURCES += config/curl_offset.c config/curl_offset.h
-fort_SOURCES += config/filename_format.h config/filename_format.c
 fort_SOURCES += config/file_type.h config/file_type.c
 fort_SOURCES += config.h config.c
 fort_SOURCES += config/incidences.h config/incidences.c
@@ -51,6 +50,7 @@ fort_SOURCES += output_printer.h output_printer.c
 fort_SOURCES += print_file.h print_file.c
 fort_SOURCES += prometheus.h prometheus.c
 fort_SOURCES += relax_ng.c relax_ng.h
+fort_SOURCES += report.c report.h
 fort_SOURCES += resource/asn.h resource/asn.c
 fort_SOURCES += resource.h resource.c
 fort_SOURCES += resource/ip4.h resource/ip4.c
index e898f3559f6a1ad89d36f841b9635cd979a98442..ed9515b11a9d92d8a49befab1d320efe35c522cc 100644 (file)
@@ -156,8 +156,6 @@ struct rpki_config {
                char *tag;
                /** Print ANSI color codes? */
                bool color;
-               /** Format in which file names will be printed. */
-               enum filename_format filename_format;
                /* Log level */
                uint8_t level;
                /* Log output */
@@ -167,22 +165,8 @@ struct rpki_config {
        } log;
 
        struct {
-               /** Enables validation Logs **/
-               bool enabled;
-               bool print_times;
-               /** String tag to identify validation logs **/
-               char *tag;
-               /** Print ANSI color codes? */
-               bool color;
-               /** Format in which file names will be printed. */
-               enum filename_format filename_format;
-               /* Log level */
-               uint8_t level;
-               /* Log output */
-               enum log_output output;
-               /** facilities for syslog if output is syslog **/
-               uint32_t facility;
-       } validation_log;
+               char *path;
+       } report;
 
        struct {
                /** File where the validated ROAs will be stored */
@@ -679,12 +663,6 @@ static const struct option_field options[] = {
                .type = &gt_log_facility,
                .offset = offsetof(struct rpki_config, log.facility),
                .doc = "Facility for syslog if output is syslog",
-       }, {
-               .id = 4005,
-               .name = "log.file-name-format",
-               .type = &gt_filename_format,
-               .offset = offsetof(struct rpki_config, log.filename_format),
-               .doc = "File name variant to print during debug/error messages",
        }, {
                .id = 'c',
                .name = "log.color-output",
@@ -695,55 +673,9 @@ static const struct option_field options[] = {
 
        {
                .id = 4010,
-               .name = "validation-log.enabled",
-               .type = &gt_bool,
-               .offset = offsetof(struct rpki_config, validation_log.enabled),
-               .doc = "Enables validation logs",
-       }, {
-               .id = 4011,
-               .name = "validation-log.output",
-               .type = &gt_log_output,
-               .offset = offsetof(struct rpki_config, validation_log.output),
-               .doc = "Output where validation log messages will be printed",
-       }, {
-               .id = 4012,
-               .name = "validation-log.level",
-               .type = &gt_log_level,
-               .offset = offsetof(struct rpki_config, validation_log.level),
-               .doc = "Log level to print message of equal or higher importance",
-       }, {
-               .id = 4017,
-               .name = "validation-log.print-times",
-               .type = &gt_bool,
-               .offset = offsetof(struct rpki_config, validation_log.print_times),
-               .doc = "(Console output only)",
-       }, {
-               .id = 4013,
-               .name = "validation-log.tag",
+               .name = "report.path",
                .type = &gt_string,
-               .offset = offsetof(struct rpki_config, validation_log.tag),
-               .doc = "Text tag to identify validation logs",
-               .arg_doc = "<string>",
-               .json_null_allowed = true,
-       }, {
-               .id = 4014,
-               .name = "validation-log.facility",
-               .type = &gt_log_facility,
-               .offset = offsetof(struct rpki_config, validation_log.facility),
-               .doc = "Facility for syslog if output is syslog",
-       }, {
-               .id = 4015,
-               .name = "validation-log.file-name-format",
-               .type = &gt_filename_format,
-               .offset = offsetof(struct rpki_config,
-                   validation_log.filename_format),
-               .doc = "File name variant to print during debug/error messages",
-       }, {
-               .id = 4016,
-               .name = "validation-log.color-output",
-               .type = &gt_bool,
-               .offset = offsetof(struct rpki_config, validation_log.color),
-               .doc = "Print ANSI color codes",
+               .offset = offsetof(struct rpki_config, report.path),
        },
 
        /* Incidences */
@@ -852,10 +784,9 @@ static const struct option_field options[] = {
        { 0 },
 };
 
-/**
+/*
  * Returns true if @field is the descriptor of one of the members of the
  * struct rpki_config structure, false otherwise.
- * (Alternatively: Returns true if @field->offset is valid, false otherwise.)
  */
 static bool
 is_rpki_config_field(struct option_field const *field)
@@ -1042,18 +973,11 @@ set_default_values(void)
        rpki_config.log.enabled = true;
        rpki_config.log.tag = NULL;
        rpki_config.log.color = false;
-       rpki_config.log.filename_format = FNF_GLOBAL;
        rpki_config.log.level = LOG_WARNING;
        rpki_config.log.output = CONSOLE;
        rpki_config.log.facility = LOG_DAEMON;
 
-       rpki_config.validation_log.enabled = false;
-       rpki_config.validation_log.tag = pstrdup("Validation");
-       rpki_config.validation_log.color = false;
-       rpki_config.validation_log.filename_format = FNF_GLOBAL;
-       rpki_config.validation_log.level = LOG_WARNING;
-       rpki_config.validation_log.output = CONSOLE;
-       rpki_config.validation_log.facility = LOG_DAEMON;
+       rpki_config.report.path = NULL;
 
        rpki_config.output.roa = NULL;
        rpki_config.output.bgpsec = NULL;
@@ -1171,25 +1095,72 @@ handle_opt(int opt)
        return ESRCH;
 }
 
+static void
+become_absolute_path(char *cwd, char **_path)
+{
+       char *relative, *absolute;
+
+       relative = *_path;
+       if (relative != NULL) {
+               absolute = path_join(cwd, relative);
+               free(relative);
+               *_path = absolute;
+       }
+}
+
 static int
-become_absolute_path(char **_path)
+become_absolute_paths(void)
 {
-       char *relative = *_path;
-       char *absolute;
+       char *buf, *cwd;
+       int error;
 
-       if (relative == NULL)
-               return 0;
+       buf = pmalloc(1024);
+
+       cwd = getcwd(buf, 1024);
+       if (!cwd) {
+               error = errno;
+               pr_err("Cannot get the current directory: %s", strerror(error));
+               free(buf);
+               return error;
+       }
 
-       absolute = realpath(relative, NULL);
-       if (!absolute)
-               return pr_err("Cannot resolve the absolute path of %s: %s",
-                   relative, strerror(errno));
+       become_absolute_path(cwd, &rpki_config.tal);
+       become_absolute_path(cwd, &rpki_config.report.path);
+       become_absolute_path(cwd, &rpki_config.slurm);
+       become_absolute_path(cwd, &rpki_config.http.ca_path);
 
-       free(relative);
-       *_path = absolute;
+       free(buf);
        return 0;
 }
 
+static void
+set_logger_syslog(void)
+{
+       struct log_listeners list = TAILQ_HEAD_INITIALIZER(list);
+       struct log_listener node = { 0 };
+
+       node.type = "syslog";
+       node.level = "info";
+       node.facility = LOG_DAEMON;
+
+       TAILQ_INSERT_TAIL(&list, &node, lh);
+       log_init(&list);
+}
+
+static void
+set_logger_console(void)
+{
+       struct log_listeners list = TAILQ_HEAD_INITIALIZER(list);
+       struct log_listener node = { 0 };
+
+       node.type = "console";
+       node.level = "info";
+       node.color = true;
+
+       TAILQ_INSERT_TAIL(&list, &node, lh);
+       log_init(&list);
+}
+
 int
 handle_flags_config(int argc, char **argv)
 {
@@ -1219,13 +1190,7 @@ handle_flags_config(int argc, char **argv)
        if (error)
                goto end;
 
-       error = become_absolute_path(&rpki_config.tal);
-       if (error)
-               goto end;
-       error = become_absolute_path(&rpki_config.slurm);
-       if (error)
-               goto end;
-       error = become_absolute_path(&rpki_config.http.ca_path);
+       error = become_absolute_paths();
        if (error)
                goto end;
 
@@ -1242,14 +1207,12 @@ handle_flags_config(int argc, char **argv)
 
        if (rpki_config.daemon) {
                pr_inf("Executing as daemon, all logs will be sent to syslog.");
-               /* Send all logs to syslog */
-               rpki_config.log.output = SYSLOG;
-               rpki_config.validation_log.output = SYSLOG;
-               error = daemonize(log_start);
-               goto end;
+               set_logger_syslog(); /* XXX hardcoded */
+               error = daemonize();
+       } else {
+               set_logger_console(); /* XXX hardcoded */
        }
 
-       log_start();
 end:
        if (error) {
                free_rpki_config();
@@ -1261,7 +1224,6 @@ end:
        free(long_opts);
        free(short_opts);
        return error;
-
 }
 
 struct option_field const *
@@ -1385,12 +1347,6 @@ config_get_op_log_color_output(void)
        return rpki_config.log.color;
 }
 
-enum filename_format
-config_get_op_log_file_format(void)
-{
-       return rpki_config.log.filename_format;
-}
-
 uint8_t
 config_get_op_log_level(void)
 {
@@ -1409,60 +1365,10 @@ config_get_op_log_facility(void)
        return rpki_config.log.facility;
 }
 
-bool
-config_get_val_log_enabled(void)
-{
-       return rpki_config.validation_log.enabled;
-}
-
-bool
-config_get_val_print_times(void)
-{
-       return rpki_config.validation_log.print_times;
-}
-
-char const *
-config_get_val_log_tag(void)
-{
-       return rpki_config.validation_log.tag;
-}
-
-bool
-config_get_val_log_color_output(void)
-{
-       return rpki_config.validation_log.color;
-}
-
-enum filename_format
-config_get_val_log_file_format(void)
-{
-       return rpki_config.validation_log.filename_format;
-}
-
-char const *
-logv_filename(char const *path)
-{
-       return (rpki_config.validation_log.filename_format == FNF_NAME)
-            ? path_filename(path)
-            : path;
-}
-
-uint8_t
-config_get_val_log_level(void)
-{
-       return rpki_config.validation_log.level;
-}
-
-enum log_output
-config_get_val_log_output(void)
-{
-       return rpki_config.validation_log.output;
-}
-
-uint32_t
-config_get_val_log_facility(void)
+char *
+config_get_report(void)
 {
-       return rpki_config.validation_log.facility;
+       return rpki_config.report.path;
 }
 
 bool
index 68aaaf2c0b3a744b97d725e0924f573946e18f50..637e8e599936d845c9a6db1efe094aafe2c5bcad 100644 (file)
@@ -10,7 +10,6 @@
 #include <sys/types.h>
 
 #include "config/file_type.h"
-#include "config/filename_format.h"
 #include "config/log_conf.h"
 #include "config/mode.h"
 #include "config/output_format.h"
@@ -66,20 +65,11 @@ bool config_get_op_log_enabled(void);
 bool config_get_op_print_times(void);
 char const * config_get_op_log_tag(void);
 bool config_get_op_log_color_output(void);
-enum filename_format config_get_op_log_file_format(void);
 uint8_t config_get_op_log_level(void);
 enum log_output config_get_op_log_output(void);
 uint32_t config_get_op_log_facility(void);
 
-bool config_get_val_log_enabled(void);
-bool config_get_val_print_times(void);
-char const * config_get_val_log_tag(void);
-bool config_get_val_log_color_output(void);
-enum filename_format config_get_val_log_file_format(void);
-char const *logv_filename(char const *);
-uint8_t config_get_val_log_level(void);
-enum log_output config_get_val_log_output(void);
-uint32_t config_get_val_log_facility(void);
+char *config_get_report(void);
 
 /* Public, so --work-offline can override them. */
 void config_set_rsync_enabled(bool);
diff --git a/src/config/filename_format.c b/src/config/filename_format.c
deleted file mode 100644 (file)
index 25d27a6..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "config/filename_format.h"
-
-#include <getopt.h>
-#include <string.h>
-
-#include "config/str.h"
-#include "log.h"
-
-#define FNF_VALUE_GLOBAL "global-url"
-#define FNF_VALUE_LOCAL "local-path"
-#define FNF_VALUE_NAME "file-name"
-
-#define DEREFERENCE(void_value) (*((enum filename_format *) void_value))
-
-static void
-print_filename_format(struct option_field const *field, void *value)
-{
-       char const *str = "<unknown>";
-
-       switch (DEREFERENCE(value)) {
-       case FNF_GLOBAL:
-               str = FNF_VALUE_GLOBAL;
-               break;
-       case FNF_LOCAL:
-               str = FNF_VALUE_LOCAL;
-               break;
-       case FNF_NAME:
-               str = FNF_VALUE_NAME;
-               break;
-       }
-
-       pr_inf("%s: %s", field->name, str);
-}
-
-static int
-parse_argv_filename_format(struct option_field const *field, char const *str,
-    void *result)
-{
-       if (strcmp(str, FNF_VALUE_GLOBAL) == 0)
-               DEREFERENCE(result) = FNF_GLOBAL;
-       else if (strcmp(str, FNF_VALUE_LOCAL) == 0)
-               DEREFERENCE(result) = FNF_LOCAL;
-       else if (strcmp(str, FNF_VALUE_NAME) == 0)
-               DEREFERENCE(result) = FNF_NAME;
-       else
-               return pr_err("Unknown file name format %s: '%s'",
-                   field->name, str);
-
-       return 0;
-}
-
-static int
-parse_json_filename_format(struct option_field const *opt, json_t *json,
-    void *result)
-{
-       char const *string;
-       int error;
-
-       error = parse_json_string(json, opt->name, &string);
-       return error ? error : parse_argv_filename_format(opt, string, result);
-}
-
-const struct global_type gt_filename_format = {
-       .has_arg = required_argument,
-       .size = sizeof(enum filename_format),
-       .print = print_filename_format,
-       .parse.argv = parse_argv_filename_format,
-       .parse.json = parse_json_filename_format,
-       .arg_doc = FNF_VALUE_GLOBAL "|" FNF_VALUE_LOCAL "|" FNF_VALUE_NAME,
-};
diff --git a/src/config/filename_format.h b/src/config/filename_format.h
deleted file mode 100644 (file)
index 9074851..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef SRC_CONFIG_FILENAME_FORMAT_H_
-#define SRC_CONFIG_FILENAME_FORMAT_H_
-
-#include "config/types.h"
-
-enum filename_format {
-       /** Example: "rsync://repository.lacnic.net/rpki/foo/bar/baz.cer" */
-       FNF_GLOBAL,
-       /** Example: "/tmp/repo/repository.lacnic.net/rpki/foo/bar/baz.cer" */
-       FNF_LOCAL,
-       /** Example: "baz.cer" */
-       FNF_NAME,
-};
-
-extern const struct global_type gt_filename_format;
-
-#endif /* SRC_CONFIG_FILENAME_FORMAT_H_ */
index 50152797c7bf9c4ae87e8ffc1684c294300d97fa..23d45360a3eb0a25d182d9ea9b09c312be5c06ec 100644 (file)
@@ -92,7 +92,7 @@ struct global_type {
        int has_arg;
        /**
         * Number of bytes this data type uses in the rpki_config structure.
-        * Optional. Defaults to zero, obviously.
+        * Optional; Defaults to zero.
         */
        size_t size;
 
index b424620a195aff6c76851385c311cef993b29897..4ef7bda97138053a36beada60118a7e664112391 100644 (file)
 #include "log.h"
 
 /*
- * Daemonize fort execution. The function "daemon()" from unistd header isn't
- * utilized since isn't standardized (too bad).
- *
- * The logs must be sent to syslog before this call, the @log_cb is called to
- * initialize logging after the first fork() call.
+ * Daemonize fort execution. "daemon()" from unistd.h isn't used since it's not
+ * portable.
  *
  * This function exits on any error once the first fork is successfully done.
  */
 int
-daemonize(daemon_log_cb log_cb)
+daemonize(void)
 {
        char *pwd;
        pid_t pid;
@@ -55,9 +52,6 @@ daemonize(daemon_log_cb log_cb)
        if (pid > 0)
                exit(0);
 
-       /* Activate logs */
-       log_cb();
-
        /* Child goes on from here */
        if (setsid() < 0) {
                error = errno;
@@ -74,7 +68,7 @@ daemonize(daemon_log_cb log_cb)
         */
        signal(SIGHUP, SIG_IGN);
 
-       /* Assure this is not a session leader */
+       /* Ensure this is not a session leader */
        pid = fork();
        if (pid < 0) {
                error = errno;
@@ -99,7 +93,7 @@ daemonize(daemon_log_cb log_cb)
 
        if (chdir(pwd) < 0) {
                error = errno;
-               pr_err("Couldn't chdir() of daemon, ending execution: %s",
+               pr_err("Couldn't chdir() daemon, ending execution: %s",
                    strerror(error));
                exit(error);
        }
index 94e796715f286d031caf1dc07c7878dd3763486d..6432b4dba746523a3950038d4b650acfc104bb3c 100644 (file)
@@ -1,7 +1,6 @@
 #ifndef SRC_DAEMON_H_
 #define SRC_DAEMON_H_
 
-typedef void (*daemon_log_cb)(void);
-int daemonize(daemon_log_cb);
+int daemonize(void);
 
 #endif /* SRC_DAEMON_H_ */
index 77295b1f33ee953a4d029dc5085793dad349a284..c5e9d14d6a71cbfb627632db08e662285a787dda 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,3 +1,5 @@
+#define _DEFAULT_SOURCE 1
+
 #include "log.h"
 
 #include <errno.h>
 #include <unistd.h>
 
 #include "config.h"
+#include "report.h"
 #include "thread_var.h"
 #include "types/path.h"
 
 struct level {
-       char const *label;
+       int id;
+       char const *tag;
+       char const *name;
        char const *color;
-       FILE *stream;
+       char const *rst;
 };
 
-static struct level DBG = { "DBG", PR_COLOR_DBG };
-static struct level INF = { "INF", PR_COLOR_INF };
-static struct level WRN = { "WRN", PR_COLOR_WRN };
-static struct level ERR = { "ERR", PR_COLOR_ERR };
-static struct level CRT = { "CRT", PR_COLOR_CRT };
-static struct level UNK = { "UNK", "" };
-
-struct log_config {
-       bool fprintf_enabled; /* Print on the standard streams? */
-       bool syslog_enabled; /* Print on syslog? */
-
-       uint8_t level;
-       bool print_times;
-       char const *tag;
-       bool color;
-       int facility;
-       bool rm_filepath;
+#ifdef PR_CLUTTER_ENABLED
+static struct level CLT = { LOG_DEBUG, "CLT", "clutter", CLR_CLT, CLR_RST };
+#endif
+static struct level TRC = { LOG_DEBUG, "TRC", "trace", CLR_TRC, CLR_RST };
+static struct level INF = { LOG_INFO, "INF", "info", "", "" };
+static struct level WRN = { LOG_WARNING, "WRN", "warning", CLR_WRN, CLR_RST };
+static struct level ERR = { LOG_ERR, "ERR", "error", CLR_ERR, CLR_RST };
+static struct level CRT = { LOG_CRIT, "CRT", "critical", CLR_CRT, CLR_RST };
+static struct level PNC = { LOG_EMERG, "PNC", "panic", CLR_PNC, CLR_RST };
+
+struct logger {
+       void (*cb)(struct logger *, struct level *, char const *, va_list);
+
+       struct level *lvl;
+       FILE *stream;           /* file only */
+       bool print_times;       /* console and file only */
+       bool color;             /* console and file only */
+       int facility;           /* syslog only */
+       bool free;
+
+       pthread_mutex_t *lock;  /* console (except init) and file only */
+       SLIST_ENTRY(logger) lh;
 };
 
-/* Configuration for the operation logs. */
-static struct log_config op_config;
-/* Configuration for the validation logs. */
-static struct log_config val_config;
+SLIST_HEAD(loggers, logger);
 
-/*
- * fprintf and syslog are rarely enabled at the same time, so I reused the
- * mutex.
- *
- * "log" + "lock" = "logck"
- */
-static pthread_mutex_t logck;
+/* Constant after init */
+static struct loggers listeners = SLIST_HEAD_INITIALIZER(listeners);
 
-/**
- * Important: -rdynamic needs to be enabled, otherwise this does not print
- * function names. See LDFLAGS_DEBUG in Makefile.am.
- * Also: Only non-static functions will be labeled.
- *
- * The first printed entry is probably not meaningful. (But I'm printing
- * everything anyway due to paranoia.)
- *
- * @title is allowed to be NULL. If you need locking, do it outside. (And be
- * aware that pthread_mutex_lock() can return error codes, which shouldn't
- * prevent critical stack traces from printing.)
- */
 static void
-print_stack_trace(char const *title)
+vcb(struct logger *lgr, struct level *lvl, char const *fmt, ...)
 {
-#ifdef BACKTRACE_ENABLED
-#define STACK_SIZE 64
+       va_list vl;
 
-       void *array[STACK_SIZE];
-       size_t size;
-       char **strings;
-       size_t i;
-       int fp;
+       va_start(vl, fmt);
+       lgr->cb(lgr, lvl, fmt, vl);
+       va_end(vl);
+}
 
-       size = backtrace(array, STACK_SIZE);
-       strings = backtrace_symbols(array, size);
+#ifdef BACKTRACE_ENABLED
 
-       if (op_config.fprintf_enabled) {
-               if (title != NULL)
-                       fprintf(ERR.stream, "%s\n", title);
-               fprintf(ERR.stream, "Stack trace:\n");
-               for (i = 0; i < size; i++)
-                       fprintf(ERR.stream, "  %s\n", strings[i]);
-               fprintf(ERR.stream, "(End of stack trace)\n");
-       }
+#define STACK_SIZE 64
 
-       if (op_config.syslog_enabled) {
-               fp = LOG_ERR | op_config.facility;
-               if (title != NULL)
-                       syslog(fp, "%s", title);
-               syslog(fp, "Stack trace:");
-               for (i = 0; i < size; i++)
-                       syslog(fp, "  %s", strings[i]);
-               syslog(fp, "(End of stack trace)");
-       }
+/*
+ * Needs -rdynamic, otherwise function names don't show up.
+ * Only non-static functions can be labeled.
+ */
+static void
+__pr_stack_trace(struct logger *lgr, struct level *lvl,
+    char const *title, size_t size, char **strings)
+{
+       size_t i;
 
-       free(strings);
-#endif /* BACKTRACE_ENABLED */
+       if (title != NULL)
+               vcb(lgr, lvl, "%s", title);
+       vcb(lgr, lvl, "Stack trace:");
+       for (i = 0; i < size; i++)
+               vcb(lgr, lvl, "  %s", strings[i]);
+       vcb(lgr, lvl, "(End of stack trace)");
 }
 
-static void init_config(struct log_config *cfg)
-{
-       cfg->fprintf_enabled = true;
-       cfg->syslog_enabled = true;
-       cfg->level = LOG_DEBUG;
-       cfg->print_times = true;
-       cfg->tag = NULL;
-       cfg->color = false;
-       cfg->facility = LOG_DAEMON;
-}
+#endif /* BACKTRACE_ENABLED */
 
-int
-log_setup(void)
+static void
+init_mutex(struct logger *lgr)
 {
-       /*
-        * Remember not to use any actual logging functions until logging has
-        * been properly initialized.
-        */
-
        int error;
 
-       DBG.stream = stdout;
-       INF.stream = stdout;
-       WRN.stream = stderr;
-       ERR.stream = stderr;
-       CRT.stream = stderr;
-       UNK.stream = stdout;
+       lgr->lock = pmalloc(sizeof(pthread_mutex_t));
 
-       init_config(&op_config);
-       init_config(&val_config);
-
-       error = pthread_mutex_init(&logck, NULL);
+       error = pthread_mutex_init(lgr->lock, NULL);
        if (error) {
-               fprintf(ERR.stream,
-                   "pthread_mutex_init() failure: %s\n",
-                   strerror(error));
-               syslog(LOG_ERR | op_config.facility,
-                   "pthread_mutex_init() failure: %s",
-                   strerror(error));
-               return error;
+               pr_wrn("Cannot initialize logging mutex: %s. "
+                   "Logs might overlap sometimes.", strerror(error));
+               free(lgr->lock);
+               lgr->lock = NULL;
        }
-
-       return 0;
 }
 
 static void
-log_disable_syslog(void)
+get_time(char *buf /* Must length 16 */)
 {
-       if (op_config.syslog_enabled || val_config.syslog_enabled) {
-               closelog();
-               op_config.syslog_enabled = false;
-               val_config.syslog_enabled = false;
-       }
+       time_t now;
+       struct tm components;
+
+       now = time(NULL);
+       if (now == ((time_t) -1))
+               return;
+       if (localtime_r(&now, &components) == NULL)
+               return;
+       if (strftime(buf, sizeof(buf), "%m-%d %H:%M:%S ", &components) == 0)
+               buf[0] = 0;
 }
 
-void
-log_start(void)
+static void
+stream_print(FILE *out, struct logger *lgr, struct level *lvl,
+    char const *fmt, va_list vl)
 {
-       if (config_get_val_log_enabled()) {
-               switch (config_get_val_log_output()) {
-               case SYSLOG:
-                       val_config.fprintf_enabled = false;
-                       break;
-               case CONSOLE:
-                       val_config.syslog_enabled = false;
-                       break;
-               }
-       } else {
-               val_config.fprintf_enabled = false;
-               val_config.syslog_enabled = false;
-       }
+       char time[16];
 
-       if (config_get_op_log_enabled()) {
-               switch (config_get_op_log_output()) {
-               case SYSLOG:
-                       op_config.fprintf_enabled = false;
-                       break;
-               case CONSOLE:
-                       if (val_config.syslog_enabled)
-                               op_config.syslog_enabled = false;
-                       else
-                               log_disable_syslog();
-                       break;
-               }
-       } else {
-               op_config.fprintf_enabled = false;
-               if (val_config.syslog_enabled)
-                       op_config.syslog_enabled = false;
-               else
-                       log_disable_syslog();
-       }
+       time[0] = 0;
+       if (lgr->print_times)
+               get_time(time);
 
-       op_config.level = config_get_op_log_level();
-       op_config.print_times = config_get_op_print_times();
-       op_config.tag = config_get_op_log_tag();
-       op_config.color = config_get_op_log_color_output();
-       op_config.facility = config_get_op_log_facility();
-       op_config.rm_filepath = config_get_op_log_file_format() == FNF_NAME;
-       val_config.level = config_get_val_log_level();
-       val_config.print_times = config_get_val_print_times();
-       val_config.tag = config_get_val_log_tag();
-       val_config.color = config_get_val_log_color_output();
-       val_config.facility = config_get_val_log_facility();
-       val_config.rm_filepath = config_get_val_log_file_format() == FNF_NAME;
-}
+       if (lgr->lock)
+               pthread_mutex_lock(lgr->lock);
 
-void
-log_teardown(void)
-{
-       log_disable_syslog();
-       pthread_mutex_destroy(&logck);
+       fprintf(out, "%s%s%s: ", lgr->color ? lvl->color : "", time, lvl->tag);
+       vfprintf(out, fmt, vl);
+       fprintf(out, "%s\n", lgr->color ? lvl->rst : "");
+
+       if (lgr->lock)
+               pthread_mutex_unlock(lgr->lock);
 }
 
-static struct level const *
-level2struct(int level)
+static void
+console_cb(struct logger *lgr, struct level *lvl, char const *fmt, va_list vl)
 {
-       switch (level) {
-       case LOG_CRIT:
-               return &CRT;
-       case LOG_ERR:
-               return &ERR;
-       case LOG_WARNING:
-               return &WRN;
-       case LOG_INFO:
-               return &INF;
-       case LOG_DEBUG:
-               return &DBG;
-       }
+       FILE *stream;
 
-       return &UNK;
+       stream = (lvl->id <= LOG_ERR) ? stderr : stdout;
+       stream_print(stream, lgr, lvl, fmt, vl);
+       fflush(stream);
 }
 
 static void
-lock_mutex(void)
+file_cb(struct logger *lgr, struct level *lvl, char const *fmt, va_list vl)
 {
-       int error;
-
-       error = pthread_mutex_lock(&logck);
-       if (error) {
-               /*
-                * Despite being supposed to be impossible, failing to lock the
-                * mutex is not fatal; it just means we might log some mixed
-                * messages, which is better than dying.
-                *
-                * Furthermore, this might have been called while logging
-                * another critical. We must absolutely not get in the way of
-                * that critical's print.
-                */
-               print_stack_trace(strerror(error));
-       }
+       stream_print(lgr->stream, lgr, lvl, fmt, vl);
 }
 
 static void
-unlock_mutex(void)
+syslog_cb(struct logger *lgr, struct level *lvl, char const *fmt, va_list vl)
 {
-       int error;
-
-       error = pthread_mutex_unlock(&logck);
-       if (error)
-               print_stack_trace(strerror(error)); /* Same as above. */
+       vsyslog(lvl->id | lgr->facility, fmt, vl);
 }
 
-static void
-print_time(struct level const *lvl)
+void
+log_setup(void)
 {
-       time_t now;
-       struct tm components;
-       char str[16];
+       static struct logger init_node = { 0 };
 
-       now = time(NULL);
-       if (now == ((time_t) -1))
-               return;
-       if (localtime_r(&now, &components) == NULL)
-               return;
-       if (strftime(str, sizeof(str), "%m-%d %H:%M:%S", &components) == 0)
-               return;
+       init_node.cb = console_cb;
+       init_node.lvl = &INF;
 
-       fprintf(lvl->stream, "%s ", str);
+       SLIST_INSERT_HEAD(&listeners, &init_node, lh);
 }
 
-static void
-__vfprintf(int level, struct log_config *cfg, char const *format, va_list args)
+static int
+add_listener(struct loggers *list, struct log_listener *new)
 {
-       struct level const *lvl;
-       char const *file_name;
-
-       lvl = level2struct(level);
-
-       lock_mutex();
+       struct logger *node;
+
+       node = pzalloc(sizeof(struct logger));
+       node->free = true;
+
+       if (strcmp(new->level, ERR.name) == 0)
+               node->lvl = &ERR;
+       else if (strcmp(new->level, WRN.name) == 0)
+               node->lvl = &WRN;
+       else if (strcmp(new->level, INF.name) == 0)
+               node->lvl = &INF;
+       else if (strcmp(new->level, TRC.name) == 0)
+               node->lvl = &TRC;
+       else {
+               free(node);
+               return pr_err("Unknown log level: %s", new->level);
+       }
 
-       if (cfg->color)
-               fprintf(lvl->stream, "%s", lvl->color);
+       if (strcmp(new->type, "console") == 0) {
+               node->cb = console_cb;
+               node->print_times = new->print_times;
+               node->color = new->color;
+               init_mutex(node);
+       } else if (strcmp(new->type, "file") == 0) {
+               node->cb = file_cb;
+               node->stream = fopen(new->filename, "a");
+               node->print_times = new->print_times;
+               node->color = new->color;
+               init_mutex(node);
+       } else if (strcmp(new->type, "syslog") == 0) {
+               node->cb = syslog_cb;
+               node->facility = new->facility;
+       } else {
+               free(node);
+               return pr_err("Unknown log type: %s", new->type);
+       }
 
-       if (cfg->print_times)
-               print_time(lvl);
+       SLIST_INSERT_HEAD(list, node, lh);
+       return 0;
+}
 
-       fprintf(lvl->stream, "%s", lvl->label);
-       if (cfg->tag)
-               fprintf(lvl->stream, " [%s]", cfg->tag);
-       fprintf(lvl->stream, ": ");
+static void
+clear_loggers(struct loggers *list)
+{
+       struct logger *lgr;
+
+       while ((lgr = SLIST_FIRST(list)) != NULL) {
+               if (lgr->stream)
+                       fclose(lgr->stream);
+               if (lgr->lock) {
+                       pthread_mutex_destroy(lgr->lock);
+                       free(lgr->lock);
+               }
 
-       file_name = fnstack_peek();
-       if (file_name != NULL) {
-               if (cfg->rm_filepath)
-                       file_name = path_filename(file_name);
-               fprintf(lvl->stream, "%s: ", file_name);
+               SLIST_REMOVE_HEAD(list, lh);
+               if (lgr->free)
+                       free(lgr);
        }
+}
 
-       vfprintf(lvl->stream, format, args);
+int
+log_init(struct log_listeners *descriptors)
+{
+       struct loggers newlist;
+       struct log_listener *descriptor;
+       int error;
 
-       if (cfg->color)
-               fprintf(lvl->stream, PR_COLOR_RST);
-       fprintf(lvl->stream, "\n");
+       SLIST_INIT(&newlist);
+       TAILQ_FOREACH(descriptor, descriptors, lh) {
+               error = add_listener(&newlist, descriptor);
+               if (error) {
+                       clear_loggers(&newlist);
+                       return error;
+               }
+       }
 
-       /* Force flush */
-       if (lvl->stream == stdout)
-               fflush(lvl->stream);
+       clear_loggers(&listeners);
+       listeners = newlist;
+       return 0;
+}
 
-       unlock_mutex();
+void
+log_teardown(void)
+{
+       clear_loggers(&listeners);
 }
 
-/*
- * TODO (fine) Optimize. Notice the buffer is static, which seems to be the
- * reason why it's (probably ill-advisedly) mutexing.
- */
-#define MSG_LEN 512
+#ifdef PR_CLUTTER_ENABLED
 
-static void
-__syslog(int level, struct log_config *cfg, const char *format, va_list args)
+void
+pr_clutter(const char *fmt, ...)
 {
-       static char msg[MSG_LEN];
-       char const *file;
-       int res;
-
-       level |= cfg->facility;
-       file = fnstack_peek();
-       if (file && cfg->rm_filepath)
-               file = path_filename(file);
-
-       lock_mutex();
-
-       /* Can't use vsyslog(); it's not portable. */
-       res = vsnprintf(msg, MSG_LEN, format, args);
-       if (res < 0)
-               goto end;
-       if (res >= MSG_LEN)
-               msg[MSG_LEN - 1] = '\0';
-
-       if (file != NULL) {
-               if (cfg->tag != NULL)
-                       syslog(level, "[%s] %s: %s", cfg->tag, file, msg);
-               else
-                       syslog(level, "%s: %s", file, msg);
-       } else {
-               if (cfg->tag != NULL)
-                       syslog(level, "[%s] %s", cfg->tag, msg);
-               else
-                       syslog(level, "%s", msg);
+       struct logger *lgr;
+       va_list args;
+
+       SLIST_FOREACH(lgr, &listeners, lh) {
+               if (LOG_DEBUG <= lgr->lvl->id) {
+                       va_start(args, fmt);
+                       lgr->cb(lgr, &CLT, fmt, args);
+                       va_end(args);
+               }
        }
-
-end:   unlock_mutex();
 }
 
-#define PR_SIMPLE(lvl, config)                                         \
-       do {                                                            \
-               va_list args;                                           \
-                                                                       \
-               if (lvl > config.level)                                 \
-                       break;                                          \
-                                                                       \
-               if (config.syslog_enabled) {                            \
-                       va_start(args, format);                         \
-                       __syslog(lvl, &config, format, args);           \
-                       va_end(args);                                   \
-               }                                                       \
-                                                                       \
-               if (config.fprintf_enabled) {                           \
-                       va_start(args, format);                         \
-                       __vfprintf(lvl, &config, format, args);         \
-                       va_end(args);                                   \
-               }                                                       \
-       } while (0)
+#endif
 
 void
-pr_trc(const char *format, ...)
+pr_trc(const char *fmt, ...)
 {
-       PR_SIMPLE(LOG_DEBUG, op_config);
+       struct logger *lgr;
+       va_list args;
+
+       SLIST_FOREACH(lgr, &listeners, lh) {
+               if (LOG_DEBUG <= lgr->lvl->id) {
+                       va_start(args, fmt);
+                       lgr->cb(lgr, &TRC, fmt, args);
+                       va_end(args);
+               }
+       }
 }
 
 void
-pr_inf(const char *format, ...)
+pr_inf(const char *fmt, ...)
 {
-       PR_SIMPLE(LOG_INFO, op_config);
+       struct logger *lgr;
+       va_list args;
+
+       SLIST_FOREACH(lgr, &listeners, lh) {
+               if (LOG_INFO <= lgr->lvl->id) {
+                       va_start(args, fmt);
+                       lgr->cb(lgr, &INF, fmt, args);
+                       va_end(args);
+               }
+       }
 }
 
-/**
- * Always returs 0. (So you can interrupt whatever you're doing without failing
- * validation.)
- */
 int
-pr_wrn(const char *format, ...)
+pr_wrn(const char *fmt, ...)
 {
-       PR_SIMPLE(LOG_WARNING, op_config);
+       struct level *lvl = &WRN;
+       struct logger *lgr;
+       va_list args;
+
+       if (report_enabled()) {
+               va_start(args, fmt);
+               report(lvl->tag, fmt, args);
+               va_end(args);
+               lvl = &TRC;
+       }
+
+       SLIST_FOREACH(lgr, &listeners, lh) {
+               if (lvl->id <= lgr->lvl->id) {
+                       va_start(args, fmt);
+                       lgr->cb(lgr, lvl, fmt, args);
+                       va_end(args);
+               }
+       }
+
        return 0;
 }
 
 int
-pr_err(const char *format, ...)
+pr_err(const char *fmt, ...)
 {
-       PR_SIMPLE(LOG_ERR, op_config);
-       return EINVAL;
-}
+       struct level *lvl = &ERR;
+       struct logger *lgr;
+       va_list args;
+
+       if (report_enabled()) {
+               va_start(args, fmt);
+               report(lvl->tag, fmt, args);
+               va_end(args);
+               lvl = &TRC;
+       }
+
+       SLIST_FOREACH(lgr, &listeners, lh) {
+               if (lvl->id <= lgr->lvl->id) {
+                       va_start(args, fmt);
+                       lgr->cb(lgr, lvl, fmt, args);
+                       va_end(args);
+               }
+       }
 
-int
-pr_crit(const char *format, ...)
-{
-       PR_SIMPLE(LOG_ERR, op_config);
-       lock_mutex();
-       print_stack_trace(NULL);
-       unlock_mutex();
        return EINVAL;
 }
 
-struct crypto_cb_arg {
-       unsigned int stack_size;
-       int (*error_fn)(const char *, ...);
-};
-
 static int
-log_crypto_error(const char *str, size_t len, void *_arg)
+log_crypto_error(const char *str, size_t len, void *_stack_size)
 {
-       struct crypto_cb_arg *arg = _arg;
-       arg->error_fn("-> %s", str);
-       arg->stack_size++;
+       unsigned int *stack_size = _stack_size;
+       pr_err("-> %s", str);
+       (*stack_size)++;
        return 1;
 }
 
-static int
-crypto_err(struct log_config *cfg, int (*error_fn)(const char *, ...))
-{
-       struct crypto_cb_arg arg;
-
-       error_fn("libcrypto error stack:");
-
-       arg.stack_size = 0;
-       arg.error_fn = error_fn;
-       ERR_print_errors_cb(log_crypto_error, &arg);
-       if (arg.stack_size == 0)
-               error_fn("   <Empty>");
-       else
-               error_fn("End of libcrypto stack.");
-
-       return EINVAL;
-}
-
 /**
- * This is like pr_err() and pr_errno(), except meant to log an error made
- * during a libcrypto routine.
+ * This is like pr_err(), except meant to log an error made during a libcrypto
+ * routine.
  *
  * This differs from usual printf-like functions:
  *
  * - It returns EINVAL, not bytes written.
  * - It prints a newline.
  * - Also prints the cryptolib's error message stack.
- *
- * Always appends a newline at the end.
  */
 int
-pr_crypto_err(const char *format, ...)
+pr_crypto_err(const char *fmt, ...)
+{
+       struct level *lvl = &ERR;
+       struct logger *lgr;
+       va_list args;
+       unsigned int stack_size;
+
+       if (report_enabled()) {
+               va_start(args, fmt);
+               report(lvl->tag, fmt, args);
+               va_end(args);
+               lvl = &TRC;
+       }
+
+       SLIST_FOREACH(lgr, &listeners, lh) {
+               if (lvl->id <= lgr->lvl->id) {
+                       va_start(args, fmt);
+                       lgr->cb(lgr, lvl, fmt, args);
+                       va_end(args);
+               }
+       }
+
+       pr_err("libcrypto error stack:");
+       stack_size = 0;
+       ERR_print_errors_cb(log_crypto_error, &stack_size);
+       if (stack_size == 0)
+               pr_err("   <Empty>");
+       else
+               pr_err("End of libcrypto stack.");
+
+       return EINVAL;
+}
+
+int
+pr_crit(const char *fmt, ...)
 {
-       PR_SIMPLE(LOG_ERR, op_config);
-       return crypto_err(&op_config, pr_err);
+       struct logger *lgr;
+       va_list args;
+
+#ifdef BACKTRACE_ENABLED
+       void *array[STACK_SIZE];
+       size_t size;
+       char **strings;
+
+       size = backtrace(array, STACK_SIZE);
+       strings = backtrace_symbols(array, size);
+#endif
+
+       SLIST_FOREACH(lgr, &listeners, lh) {
+               if (CRT.id <= lgr->lvl->id) {
+                       va_start(args, fmt);
+                       lgr->cb(lgr, &CRT, fmt, args);
+                       va_end(args);
+
+#ifdef BACKTRACE_ENABLED
+                       __pr_stack_trace(lgr, &CRT, NULL, size, strings);
+#endif
+               }
+       }
+
+#ifdef BACKTRACE_ENABLED
+       free(strings);
+#endif
+
+       return EINVAL;
 }
 
 __dead void
-enomem_panic(void)
+pr_panic(const char *fmt, ...)
 {
-       static char const *ENOMEM_MSG = "Out of memory.\n";
-       ssize_t garbage;
-
-       /*
-        * I'm not using PR_SIMPLE and friends, because those allocate.
-        * We want to minimize allocations after a memory allocation failure.
-        */
-
-       if (LOG_ERR > op_config.level)
-               goto done;
-
-       if (op_config.fprintf_enabled) {
-               lock_mutex();
-               /*
-                * write() is AS-Safe, which implies it doesn't allocate,
-                * unlike printf().
-                *
-                * "garbage" prevents write()'s warn_unused_result (compiler
-                * warning).
-                */
-               garbage = write(STDERR_FILENO, ENOMEM_MSG, strlen(ENOMEM_MSG));
-               unlock_mutex();
-               /* Prevents "set but not used" warning. */
-               garbage += garbage;
-       }
+       struct logger *lgr;
+       va_list args;
+
+#ifdef BACKTRACE_ENABLED
+       void *array[STACK_SIZE];
+       size_t size;
+       char **strings;
+
+       size = backtrace(array, STACK_SIZE);
+       strings = backtrace_symbols(array, size);
+#endif
 
-       if (op_config.syslog_enabled) {
-               lock_mutex();
-               /* This allocates, but I don't think I have more options. */
-               syslog(LOG_ERR | op_config.facility, "Out of memory.");
-               unlock_mutex();
+       SLIST_FOREACH(lgr, &listeners, lh) {
+               if (LOG_CRIT <= lgr->lvl->id) {
+                       va_start(args, fmt);
+                       lgr->cb(lgr, &PNC, fmt, args);
+                       va_end(args);
+
+#ifdef BACKTRACE_ENABLED
+                       __pr_stack_trace(lgr, &PNC, NULL, size, strings);
+#endif
+               }
        }
 
-done:  exit(ENOMEM);
+#ifdef BACKTRACE_ENABLED
+       free(strings);
+#endif
+
+       exit(-1);
 }
 
 __dead void
-pr_panic(const char *format, ...)
+enomem_panic(void)
 {
-       PR_SIMPLE(LOG_ERR, op_config);
-       print_stack_trace(NULL);
-       exit(-1);
+       struct logger *lgr;
+
+       SLIST_FOREACH(lgr, &listeners, lh)
+               if (LOG_CRIT <= lgr->lvl->id)
+                       vcb(lgr, &CRT, "Out of memory");
+
+       exit(ENOMEM);
 }
index fc82c8e7812f807191135042b706a959062ef3a1..59a5cb776077c997b78c678e16f3a72ceb699605 100644 (file)
--- a/src/log.h
+++ b/src/log.h
@@ -3,15 +3,16 @@
 
 #include <stdbool.h>
 #include <stdio.h>
+#include <sys/queue.h>
 
-#define PR_COLOR_DBG   "\x1B[36m"      /* Cyan */
-#define PR_COLOR_CLT   "\x1B[34m"      /* Blue */
-#define PR_COLOR_TRC   "\x1B[32m"      /* Green */
-#define PR_COLOR_INF   "\x1B[37m"      /* White */
-#define PR_COLOR_WRN   "\x1B[33m"      /* Yellow */
-#define PR_COLOR_ERR   "\x1B[31m"      /* Red */
-#define PR_COLOR_CRT   "\x1B[35m"      /* Purple */
-#define PR_COLOR_RST   "\x1B[0m"
+#define CLR_DBG        "\x1B[36m"      /* Cyan */
+#define CLR_CLT        "\x1B[34m"      /* Blue */
+#define CLR_TRC        "\x1B[32m"      /* Green */
+#define CLR_WRN        "\x1B[33m"      /* Yellow */
+#define CLR_ERR        "\x1B[31m"      /* Red */
+#define CLR_CRT        "\x1B[35m"      /* Magenta */
+#define CLR_PNC        "\x1B[35m"      /* Magenta */
+#define CLR_RST        "\x1B[0m"
 
 /*
  * According to BSD style, __dead is supposed to be defined in sys/cdefs.h,
 #define CHECK_FORMAT(str, args) /* Nothing */
 #endif
 
+struct log_listener {
+       char const *type;
+       char const *level;
+       char const *filename;   /* file only */
+       bool print_times;       /* console and file only */
+       bool color;             /* console and file only */
+       int facility;           /* syslog only */
+
+       TAILQ_ENTRY(log_listener) lh;
+};
+
+TAILQ_HEAD(log_listeners, log_listener);
+
+void log_setup(void);                  /* Enables pr_* functions */
+int log_init(struct log_listeners *);  /* Loads configuration */
+void log_teardown(void);
+
 /*
- * Only call this group of functions while you know there's only one thread.
+ * DBG (Debug) = Dirty lazy (debuggerless) bug hunting prints.
+ * These should be removed after the fix, and are always purged during releases.
+ * They go straight to standard output, bypassing listeners (and their levels).
+ *
+ * CLT (Clutter) = Low-level "I'm doing this now."
+ * TRC (Trace) = High-level "I'm doing this now."
+ * TRCs contextualize errors; CLTs give detailed information once the dev
+ * understands the context.
+ * TRCs are permanent; CLTs need to be compiled in. This is because I've
+ * extremely rarely found CLTs useful, even when debugging, and I got tired of
+ * them.
+ * In syslog, both are mapped to LOG_DEBUG.
+ *
+ * INF (Info) = Rare significant benign event.
  *
- * log_setup() is an incomplete initialization meant to be called when the
- * program starts. Logging can be performed after log_setup(), but it will use
- * default values.
- * log_init() finishes initialization by loading the user's intended config.
- * log_teardown() reverts initialization.
+ * WRN (Warning) = Weirdness found, object not rejected, validation recovers.
+ * Means "this doesn't look rational, but I'll humor you anyway."
+ *
+ * ERR (Error) = Error, object rejected, validation recovers.
+ * Typical nonfatal expected error. A "checked exception," if you will.
+ *
+ * CRIT (Critical) = Apalling error, object rejected, validation recovers.
+ * A somehow nonfatal programming error.
+ * These include stack traces, and are not meant for reports.
+ *
+ * PNC (Panic) = Crippling error, validation dies.
+ * This is what CRIT used to be; Fort dies on the spot. Also includes a stack
+ * trace. Mapped to LOG_EMERG.
+ * With the exception of ENOMEMs, PNCs should never happen in production.
+ *
+ * WRNs and ERRs are redirected to reports during validations.
  */
-int log_setup(void);
-void log_start(void);
-void log_teardown(void);
 
+#ifdef PR_CLUTTER_ENABLED
+#define pr_clutter_enabled() true
+void pr_clutter(const char *, ...) CHECK_FORMAT(1, 2);
+#else
 #define pr_clutter_enabled() false
 #define pr_clutter(...)
+#endif
 
 void pr_trc(const char *, ...) CHECK_FORMAT(1, 2);
 void pr_inf(const char *, ...) CHECK_FORMAT(1, 2);
@@ -57,11 +101,15 @@ int pr_crit(const char *format, ...) CHECK_FORMAT(1, 2);
 __dead void pr_panic(const char *, ...) CHECK_FORMAT(1, 2);
 __dead void enomem_panic(void); /* Out of memory */
 
-#define PR_DBG(msg, ...) \
-    printf(PR_COLOR_DBG "%s:%d (%s()): " msg PR_COLOR_RST "\n", \
-        __FILE__, __LINE__, __func__, ##__VA_ARGS__)
-#define PR_HELLO \
-    printf(PR_COLOR_DBG "%s:%d (%s())" PR_COLOR_RST "\n", \
-        __FILE__, __LINE__, __func__)
+#define PR_DBG(msg, ...) do {                                          \
+               printf(CLR_DBG "%s:%d (%s()): " msg CLR_RST "\n",       \
+                   __FILE__, __LINE__, __func__, ##__VA_ARGS__);       \
+               fflush(stdout);                                         \
+       } while (0)
+#define PR_HELLO do {                                                  \
+               printf(CLR_DBG "%s:%d (%s())" CLR_RST "\n",             \
+                   __FILE__, __LINE__, __func__);                      \
+               fflush(stdout);                                         \
+       } while (0)
 
 #endif /* SRC_LOG_H_ */
index 3ebef4124d1f249b5b132c50a7d368f534788ed4..2d5fe535c537b4273905450db96fff3fc46ec386 100644 (file)
@@ -121,9 +121,7 @@ main(int argc, char **argv)
        /* Initializations */
        /* (Do not start any threads until after rsync_setup() has forked.) */
 
-       error = log_setup();
-       if (error)
-               goto just_quit;
+       log_setup();
        error = handle_flags_config(argc, argv);
        if (error)
                goto revert_log;
@@ -206,6 +204,5 @@ revert_config:
        free_rpki_config();
 revert_log:
        log_teardown();
-just_quit:
        return convert_to_result(error);
 }
index b8559d6be1be7d521ce29b19c56df40ce84305b2..393a9d33efd994c2b75bbff213ac46d596dc85e9 100644 (file)
@@ -935,6 +935,9 @@ pr_clutter_x509_dates(X509 *x509)
 {
        char *nb, *na;
 
+       if (!pr_clutter_enabled())
+               return;
+
        nb = asn1time2str(X509_get0_notBefore(x509));
        na = asn1time2str(X509_get0_notAfter(x509));
 
@@ -1023,8 +1026,7 @@ validate_chain(struct rpki_certificate *cert)
        }
        X509_STORE_CTX_set0_crls(ctx, crls);
 
-       if (pr_clutter_enabled())
-               pr_clutter_x509_dates(cert->x509);
+       pr_clutter_x509_dates(cert->x509);
 
        /*
         * HERE'S THE MEAT OF LIBCRYPTO'S VALIDATION.
index aa4447d40bd2e2d57d1eaeefa494f1cd4794266e..4d601fefdf31d6b6f56e49200062917d5ba3281c 100644 (file)
@@ -7,6 +7,7 @@
 #include "file.h"
 #include "log.h"
 #include "object/certificate.h"
+#include "report.h"
 #include "task.h"
 #include "thread_var.h"
 #include "types/path.h"
@@ -283,13 +284,16 @@ perform_standalone_validation(void)
        error = cache_prepare();
        if (error)
                return error;
-       fnstack_init();
-       task_start();
 
+       task_start();
        error = foreach_file(config_get_tal(), ".tal", true, queue_tal, NULL);
        if (error)
                goto end;
 
+       error = report_enable();
+       if (error)
+               goto end;
+
        /*
         * From now on, the trees should be considered valid, even if subsequent
         * certificates fail.
@@ -310,6 +314,8 @@ perform_standalone_validation(void)
                            t, strerror(error));
        }
 
+       report_disable();
+
 //     // FIXME
 //     stats_set_tal_vrps(thread->tal_file, "ipv4",
 //         db_table_roa_count_v4(thread->db));
diff --git a/src/report.c b/src/report.c
new file mode 100644 (file)
index 0000000..5b53107
--- /dev/null
@@ -0,0 +1,66 @@
+#include "report.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "config.h"
+#include "log.h"
+
+static FILE *stream;   /* Constant during multithreaded */
+static bool enabled;
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+int
+report_enable(void)
+{
+       char const *path;
+       int error;
+
+       path = config_get_report();
+       if (!path)
+               /* Will not write report, but pr_wrn & pr_err still need to
+                * be sent to TRC */
+               goto done;
+
+       stream = fopen(path, "wb");
+       if (!stream) {
+               error = errno;
+               if (error != EEXIST)
+                       return pr_err("fopen(%s): %s", path, strerror(error));
+       }
+
+done:  enabled = true;
+       return 0;
+}
+
+bool
+report_enabled(void)
+{
+       return enabled;
+}
+
+void
+report(char const *tag, char const *fmt, va_list vl)
+{
+       if (!stream)
+               return;
+
+       mutex_lock(&lock);
+       fprintf(stream, "Severity: %s\n", tag);
+       fprintf(stream, "File: %s\n", ""); // XXX
+       vfprintf(stream, fmt, vl);
+       fprintf(stream, "\n\n");
+       mutex_unlock(&lock);
+}
+
+void
+report_disable(void)
+{
+       if (stream) {
+               fclose(stream);
+               stream = NULL;
+       }
+       enabled = false;
+}
diff --git a/src/report.h b/src/report.h
new file mode 100644 (file)
index 0000000..3c984c1
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef SRC_REPORT_H_
+#define SRC_REPORT_H_
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+int report_enable(void);       /* pr_wrn and pr_errs now send to report */
+bool report_enabled(void);
+void report(char const *, char const *, va_list);
+void report_disable(void);     /* pr_wrn and pr_errs go back to logs */
+
+#endif /* SRC_REPORT_H_ */
index 45de81e64d19d4c2274f54bad1d4e410b4e4ecd5..ae80d5b4ddcce11b73ec7ea8636c4c56a1c21673 100644 (file)
@@ -127,7 +127,7 @@ fnstack_push(char const *file)
 void
 fnstack_push_map(struct cache_mapping const *map)
 {
-       fnstack_push(map_val_get_printable(map));
+       fnstack_push(uri_str(&map->url));
 }
 
 /* Returns the file name on the top of the file name stack. */
index 76820fdae764d8eada6462d0db6e186f38b628bd..a840c63b2e216f22e5dcc3e14897a8032f1daf9b 100644 (file)
@@ -1,37 +1,5 @@
 #include "types/map.h"
 
-#include "config.h"
-#include "log.h"
-#include "types/path.h"
-
-static char const *
-map_get_printable(struct cache_mapping const *map, enum filename_format format)
-{
-       switch (format) {
-       case FNF_GLOBAL:
-               return uri_str(&map->url);
-       case FNF_LOCAL:
-               return map->path;
-       case FNF_NAME:
-               return path_filename(uri_str(&map->url));
-       }
-
-       pr_panic("Unknown file name format: %u", format);
-       return NULL;
-}
-
-char const *
-map_val_get_printable(struct cache_mapping const *map)
-{
-       return map_get_printable(map, config_get_val_log_file_format());
-}
-
-char const *
-map_op_get_printable(struct cache_mapping const *map)
-{
-       return map_get_printable(map, config_get_op_log_file_format());
-}
-
 void
 map_copy(struct cache_mapping *dst, struct cache_mapping const *src)
 {
index 4dac978cc94f098d71c9789c88ed29bb9dcd157d..9ee59e76989a7ea7aed9f2f9bfd155eb4dcb20b2 100644 (file)
@@ -10,9 +10,6 @@ struct cache_mapping {
        char *path;
 };
 
-char const *map_val_get_printable(struct cache_mapping const *);
-char const *map_op_get_printable(struct cache_mapping const *);
-
 void map_copy(struct cache_mapping *, struct cache_mapping const *);
 void map_cleanup(struct cache_mapping *);
 
index 7d4676eb7379bf4eb12765ef196e02ad0b39565e..ff32f98860e3f34b6ce07ecfc79a443de8a35588 100644 (file)
@@ -77,31 +77,25 @@ log_crypto_error(const char *str, size_t len, void *_arg)
        return 1;
 }
 
-static int
-crypto_err(int (*error_fn)(const char *, ...))
+int
+pr_crypto_err(const char *format, ...)
 {
        struct crypto_cb_arg arg;
 
-       error_fn("libcrypto error stack:");
+       MOCK_PRINT(PR_COLOR_ERR);
 
+       pr_err("libcrypto error stack:");
        arg.stack_size = 0;
-       arg.error_fn = error_fn;
+       arg.error_fn = pr_err;
        ERR_print_errors_cb(log_crypto_error, &arg);
        if (arg.stack_size == 0)
-               error_fn("   <Empty>");
+               pr_err("   <Empty>");
        else
-               error_fn("End of libcrypto stack.");
+               pr_err("End of libcrypto stack.");
 
        return EINVAL;
 }
 
-int
-pr_crypto_err(const char *format, ...)
-{
-       MOCK_PRINT(PR_COLOR_ERR);
-       return crypto_err(pr_err);
-}
-
 void
 enomem_panic(void)
 {
@@ -164,7 +158,6 @@ MOCK_VOID(free_rpki_config, void)
 
 MOCK_VOID(fnstack_init, void)
 MOCK_VOID(fnstack_push, char const *file)
-MOCK_VOID(fnstack_push_map, struct cache_mapping const *map)
 MOCK_VOID(fnstack_pop, void)
 MOCK_VOID(fnstack_cleanup, void)