summaryrefslogtreecommitdiffstats
path: root/misc-utils/logger.c
diff options
context:
space:
mode:
authorKarel Zak2015-10-01 14:48:15 +0200
committerKarel Zak2015-10-01 14:48:15 +0200
commit4299ed1ce2f088cced8a5b9420811ddce27eae8e (patch)
treec53451f34d69a575c5754fc9f0a4d02568271884 /misc-utils/logger.c
parentlib/strv: add strv_extendv() (diff)
downloadkernel-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.c246
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;