diff options
author | Karel Zak | 2015-10-01 14:48:15 +0200 |
---|---|---|
committer | Karel Zak | 2015-10-01 14:48:15 +0200 |
commit | 4299ed1ce2f088cced8a5b9420811ddce27eae8e (patch) | |
tree | c53451f34d69a575c5754fc9f0a4d02568271884 /misc-utils/logger.c | |
parent | lib/strv: add strv_extendv() (diff) | |
download | kernel-qcow2-util-linux-4299ed1ce2f088cced8a5b9420811ddce27eae8e.tar.gz kernel-qcow2-util-linux-4299ed1ce2f088cced8a5b9420811ddce27eae8e.tar.xz kernel-qcow2-util-linux-4299ed1ce2f088cced8a5b9420811ddce27eae8e.zip |
logger: add --sd-id and -sd-param
This patch add support for RFC 5424 structured data elements. For
example:
logger --rfc5424 --sd-id zoo@123 \
--sd-param tiger=\"hungry\" \
--sd-param zebra=\"running\" \
--sd-id manager@123 \
--sd-param onMeeting=\"yes\" \
"this is message"
produces:
<13>1 2015-10-01T14:07:59.168662+02:00 ws kzak - - [timeQuality tzKnown="1" isSynced="1" syncAccuracy="218616"][zoo@123 tiger="hungry" zebra="running"][manager@123 onMeeting="yes"] this is message
Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'misc-utils/logger.c')
-rw-r--r-- | misc-utils/logger.c | 246 |
1 files changed, 232 insertions, 14 deletions
diff --git a/misc-utils/logger.c b/misc-utils/logger.c index e4a6c5aa1..c8ec8fa2d 100644 --- a/misc-utils/logger.c +++ b/misc-utils/logger.c @@ -59,6 +59,8 @@ #include "pathnames.h" #include "strutils.h" #include "xalloc.h" +#include "strv.h" +#include "list.h" #define SYSLOG_NAMES #include <syslog.h> @@ -93,9 +95,19 @@ enum { OPT_MSGID, OPT_NOACT, OPT_ID, + OPT_STRUCTURED_DATA_ID, + OPT_STRUCTURED_DATA_PARAM, OPT_OCTET_COUNT }; +/* rfc5424 structured data */ +struct structured_data { + char *id; /* SD-ID */ + char **params; /* array with SD-PARAMs */ + + struct list_head sds; +}; + struct logger_ctl { int fd; int pri; @@ -108,7 +120,11 @@ struct logger_ctl { char *port; int socket_type; size_t max_message_size; + struct list_head user_sds; /* user defined rfc5424 structured data */ + struct list_head reserved_sds; /* standard rfc5424 structured data */ + void (*syslogfp)(struct logger_ctl *ctl); + unsigned int unix_socket_errors:1, /* whether to report or not errors */ noact:1, /* do not write to sockets */ @@ -430,7 +446,177 @@ static void syslog_rfc3164_header(struct logger_ctl *const ctl) free(hostname); } -/* Some field mappings may be controversal, thus I give the reason +static inline struct list_head *get_user_structured_data(struct logger_ctl *ctl) +{ + return &ctl->user_sds; +} + +static inline struct list_head *get_reserved_structured_data(struct logger_ctl *ctl) +{ + return &ctl->reserved_sds; +} + +static int has_structured_data_id(struct list_head *ls, const char *id) +{ + struct list_head *p; + + if (!ls || list_empty(ls)) + return 0; + + list_for_each(p, ls) { + struct structured_data *sd = list_entry(p, struct structured_data, sds); + if (sd->id && strcmp(sd->id, id) == 0) + return 1; + } + + return 0; +} + +static void add_structured_data_id(struct list_head *ls, const char *id) +{ + struct structured_data *sd; + + assert(id); + + if (has_structured_data_id(ls, id)) + errx(EXIT_FAILURE, _("structured data ID '%s' is not unique"), id); + + sd = xcalloc(1, sizeof(*sd)); + INIT_LIST_HEAD(&sd->sds); + sd->id = xstrdup(id); + + list_add_tail(&sd->sds, ls); +} + +static void add_structured_data_param(struct list_head *ls, const char *param) +{ + struct structured_data *sd; + + if (list_empty(ls)) + errx(EXIT_FAILURE, _("--sd-id no specified for --sd-param %s"), param); + + assert(param); + + sd = list_last_entry(ls, struct structured_data, sds); + + if (strv_extend(&sd->params, param)) + err_oom(); +} + +static void add_structured_data_paramf(struct list_head *ls, const char *fmt, ...) +{ + struct structured_data *sd; + va_list ap; + int x; + + assert(!list_empty(ls)); + assert(fmt); + + sd = list_last_entry(ls, struct structured_data, sds); + va_start(ap, fmt); + x = strv_extendv(&sd->params, fmt, ap); + va_end(ap); + + if (x) + err_oom(); +} + +static char *strdup_structured_data(struct structured_data *sd) +{ + char *res, *tmp; + + if (strv_isempty(sd->params)) + return NULL; + + xasprintf(&res, "[%s %s]", sd->id, + (tmp = strv_join(sd->params, " "))); + free(tmp); + return res; +} + +static char *strdup_structured_data_list(struct list_head *ls) +{ + struct list_head *p; + char *res = NULL; + + list_for_each(p, ls) { + struct structured_data *sd = list_entry(p, struct structured_data, sds); + char *one = strdup_structured_data(sd); + char *tmp = res; + + if (!one) + continue; + res = strappend(tmp, one); + free(tmp); + free(one); + } + + return res; +} + +static char *get_structured_data_string(struct logger_ctl *ctl) +{ + char *sys = NULL, *usr = NULL, *res; + + if (!list_empty(&ctl->reserved_sds)) + sys = strdup_structured_data_list(&ctl->reserved_sds); + if (!list_empty(&ctl->user_sds)) + usr = strdup_structured_data_list(&ctl->user_sds); + + if (sys && usr) { + res = strappend(sys, usr); + free(sys); + free(usr); + } else + res = sys ? sys : usr; + + return res; +} + +static int valid_structured_data_param(const char *str) +{ + char *eq = strchr(str, '='), + *qm1 = strchr(str, '"'), + *qm2 = qm1 ? strchr(qm1 + 1, '"') : NULL; + + if (!eq || !qm1 || !qm2) /* something is missing */ + return 0; + + /* foo="bar" */ + return eq > str && eq < qm1 && eq + 1 == qm1 && qm1 < qm2 && *(qm2 + 1) == '\0'; +} + +/* SD-ID format: + * name@<private enterprise number>, e.g., "ourSDID@32473" + */ +static int valid_structured_data_id(const char *str) +{ + char *at = strchr(str, '@'); + const char *p; + + /* standardized IDs without @<digits> */ + if (!at && (strcmp(str, "timeQuality") == 0 || + strcmp(str, "origin") == 0 || + strcmp(str, "meta") == 0)) + return 1; + + if (!at || at == str || !*(at + 1)) + return 0; + if (!isdigit_string(at + 1)) + return 0; + + /* check for forbidden chars in the <name> */ + for (p = str; p < at; p++) { + if (*p == '[' || *p == '=' || *p == '"' || *p == '@') + return 0; + if (isblank((unsigned int) *p) || iscntrl((unsigned int) *p)) + return 0; + } + return 1; +} + + +/* Some field mappings may be controversial, thus I give the reason * why this specific mapping was used: * APP-NAME <-- tag * Some may argue that "logger" is a better fit, but we think @@ -457,7 +643,8 @@ static void syslog_rfc5424_header(struct logger_ctl *const ctl) char *const app_name = ctl->tag; char *procid; char *const msgid = xstrdup(ctl->msgid ? ctl->msgid : NILVALUE); - char *structured_data; + char *structured = NULL; + struct list_head *sd; if (ctl->fd < 0) return; @@ -500,20 +687,29 @@ static void syslog_rfc5424_header(struct logger_ctl *const ctl) else procid = xstrdup(NILVALUE); - if (ctl->rfc5424_tq) { + sd = get_reserved_structured_data(ctl); + + /* time quality structured data (maybe overwriten by --sd-id timeQuality) */ + if (ctl->rfc5424_tq && !has_structured_data_id(sd, "timeQuality")) { + + add_structured_data_id(sd, "timeQuality"); + add_structured_data_param(sd, "tzKnown=\"1\""); + #ifdef HAVE_NTP_GETTIME struct ntptimeval ntptv; - if (ntp_gettime(&ntptv) == TIME_OK) - xasprintf(&structured_data, - "[timeQuality tzKnown=\"1\" isSynced=\"1\" syncAccuracy=\"%ld\"]", - ntptv.maxerror); - else + if (ntp_gettime(&ntptv) == TIME_OK) { + add_structured_data_param(sd, "isSynced=\"1\""); + add_structured_data_paramf(sd, "syncAccuracy=\"%ld\"", ntptv.maxerror); + } else #endif - xasprintf(&structured_data, - "[timeQuality tzKnown=\"1\" isSynced=\"0\"]"); - } else - structured_data = xstrdup(NILVALUE); + add_structured_data_paramf(sd, "isSynced=\"0\""); + } + + /* convert all structured data to string */ + structured = get_structured_data_string(ctl); + if (!structured) + structured = xstrdup(NILVALUE); xasprintf(&ctl->hdr, "<%d>1 %s %s %s %s %s %s ", ctl->pri, @@ -522,14 +718,14 @@ static void syslog_rfc5424_header(struct logger_ctl *const ctl) app_name, procid, msgid, - structured_data); + structured); free(time); free(hostname); /* app_name points to ctl->tag, do NOT free! */ free(procid); free(msgid); - free(structured_data); + free(structured); } static void parse_rfc5424_flags(struct logger_ctl *ctl, char *optarg) @@ -723,6 +919,8 @@ static void __attribute__ ((__noreturn__)) usage(FILE *out) fputs(_(" --rfc3164 use the obsolete BSD syslog protocol\n"), out); fputs(_(" --rfc5424[=<snip>] use the syslog protocol (the default for remote);\n" " <snip> can be notime, or notq, and/or nohost\n"), out); + fputs(_(" --sd-id <id> rfc5424 structured data ID\n"), out); + fputs(_(" --sd-param <data> rfc5424 structured data name=value\n"), out); fputs(_(" --msgid <msgid> set rfc5424 message id field\n"), out); fputs(_(" -u, --socket <socket> write to this Unix socket\n"), out); fputs(_(" --socket-errors[=<on|off|auto>]\n" @@ -794,6 +992,8 @@ int main(int argc, char **argv) { "size", required_argument, 0, 'S' }, { "msgid", required_argument, 0, OPT_MSGID }, { "skip-empty", no_argument, 0, 'e' }, + { "sd-id", required_argument, 0, OPT_STRUCTURED_DATA_ID }, + { "sd-param", required_argument, 0, OPT_STRUCTURED_DATA_PARAM }, #ifdef HAVE_LIBSYSTEMD { "journald", optional_argument, 0, OPT_JOURNALD }, #endif @@ -805,6 +1005,9 @@ int main(int argc, char **argv) textdomain(PACKAGE); atexit(close_stdout); + INIT_LIST_HEAD(&ctl.user_sds); + INIT_LIST_HEAD(&ctl.reserved_sds); + while ((ch = getopt_long(argc, argv, "ef:ip:S:st:u:dTn:P:Vh", longopts, NULL)) != -1) { switch (ch) { @@ -898,6 +1101,16 @@ int main(int argc, char **argv) case OPT_NOACT: ctl.noact = 1; break; + case OPT_STRUCTURED_DATA_ID: + if (!valid_structured_data_id(optarg)) + errx(EXIT_FAILURE, _("invalid structured data ID: '%s'"), optarg); + add_structured_data_id(get_user_structured_data(&ctl), optarg); + break; + case OPT_STRUCTURED_DATA_PARAM: + if (!valid_structured_data_param(optarg)) + errx(EXIT_FAILURE, _("invalid structured data parameter: '%s'"), optarg); + add_structured_data_param(get_user_structured_data(&ctl), optarg); + break; case '?': default: usage(stderr); @@ -917,6 +1130,11 @@ int main(int argc, char **argv) return EXIT_SUCCESS; } #endif + + /* user overwrites build-in SD-ELEMENT */ + if (has_structured_data_id(get_user_structured_data(&ctl), "timeQuality")) + ctl.rfc5424_tq = 0; + switch (unix_socket_errors_mode) { case AF_UNIX_ERRORS_OFF: ctl.unix_socket_errors = 0; |