Blame SOURCES/0005-logging-to-the-systemd-journal.patch

9db2f0
From 0621739db9b751c48b8e7a6d67941aeca9fa2154 Mon Sep 17 00:00:00 2001
9db2f0
From: David Teigland <teigland@redhat.com>
9db2f0
Date: Wed, 24 Mar 2021 14:19:54 -0500
9db2f0
Subject: [PATCH 05/11] logging: to the systemd journal
9db2f0
9db2f0
Configure via lvm.conf log/journal or command line --journal.
9db2f0
9db2f0
Possible values:
9db2f0
"command" records command information.
9db2f0
"output" records default command output.
9db2f0
"debug" records full command debugging.
9db2f0
9db2f0
Multiple values can be set in lvm.conf as an array.
9db2f0
One value can be set in --journal which is added to
9db2f0
values set in lvm.conf
9db2f0
---
9db2f0
 lib/commands/toolcontext.c   | 30 +++++++++++++++
9db2f0
 lib/commands/toolcontext.h   |  1 +
9db2f0
 lib/config/config_settings.h |  6 +++
9db2f0
 lib/log/log.c                | 91 ++++++++++++++++++++++++++++++++++++++++++++
9db2f0
 lib/log/log.h                |  4 ++
9db2f0
 lib/log/lvm-logging.h        |  6 +++
9db2f0
 tools/args.h                 |  8 ++++
9db2f0
 tools/command-lines.in       |  2 +-
9db2f0
 tools/lvmcmdline.c           | 12 ++++++
9db2f0
 9 files changed, 159 insertions(+), 1 deletion(-)
9db2f0
9db2f0
diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
9db2f0
index 8991aeb..f939c3e 100644
9db2f0
--- a/lib/commands/toolcontext.c
9db2f0
+++ b/lib/commands/toolcontext.c
9db2f0
@@ -340,6 +340,33 @@ static int _parse_debug_classes(struct cmd_context *cmd)
9db2f0
 	return debug_classes;
9db2f0
 }
9db2f0
 
9db2f0
+static uint32_t _parse_log_journal(struct cmd_context *cmd, int cfg, const char *cfgname)
9db2f0
+{
9db2f0
+	const struct dm_config_node *cn;
9db2f0
+	const struct dm_config_value *cv;
9db2f0
+	uint32_t fields = 0;
9db2f0
+	uint32_t val;
9db2f0
+
9db2f0
+	if (!(cn = find_config_tree_array(cmd, cfg, NULL))) {
9db2f0
+		log_debug("Unable to find configuration for log/%s.", cfgname);
9db2f0
+		return 0;
9db2f0
+	}
9db2f0
+
9db2f0
+	for (cv = cn->v; cv; cv = cv->next) {
9db2f0
+		if (cv->type != DM_CFG_STRING) {
9db2f0
+			log_verbose("log/%s contains a value which is not a string.  Ignoring.", cfgname);
9db2f0
+			continue;
9db2f0
+		}
9db2f0
+
9db2f0
+		if ((val = log_journal_str_to_val(cv->v.str)))
9db2f0
+			fields |= val;
9db2f0
+		else
9db2f0
+			log_verbose("Unrecognised value for log/%s: %s", cfgname, cv->v.str);
9db2f0
+	}
9db2f0
+
9db2f0
+	return fields;
9db2f0
+}
9db2f0
+
9db2f0
 static void _init_logging(struct cmd_context *cmd)
9db2f0
 {
9db2f0
 	int append = 1;
9db2f0
@@ -408,6 +435,9 @@ static void _init_logging(struct cmd_context *cmd)
9db2f0
 	init_debug_file_fields(_parse_debug_fields(cmd, log_debug_file_fields_CFG, "debug_file_fields"));
9db2f0
 	init_debug_output_fields(_parse_debug_fields(cmd, log_debug_output_fields_CFG, "debug_output_fields"));
9db2f0
 
9db2f0
+	cmd->default_settings.journal = _parse_log_journal(cmd, log_journal_CFG, "journal");
9db2f0
+	init_log_journal(cmd->default_settings.journal);
9db2f0
+
9db2f0
 	t = time(NULL);
9db2f0
 	ctime_r(&t, &timebuf[0]);
9db2f0
 	timebuf[24] = '\0';
9db2f0
diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
9db2f0
index 0cb4ad7..2ee6524 100644
9db2f0
--- a/lib/commands/toolcontext.h
9db2f0
+++ b/lib/commands/toolcontext.h
9db2f0
@@ -41,6 +41,7 @@ struct config_info {
9db2f0
 	int udev_sync;
9db2f0
 	int udev_fallback;
9db2f0
 	int issue_discards;
9db2f0
+	uint32_t journal;
9db2f0
 	const char *msg_prefix;
9db2f0
 	const char *fmt_name;
9db2f0
 	const char *dmeventd_executable;
9db2f0
diff --git a/lib/config/config_settings.h b/lib/config/config_settings.h
9db2f0
index 76ebc10..63569bf 100644
9db2f0
--- a/lib/config/config_settings.h
9db2f0
+++ b/lib/config/config_settings.h
9db2f0
@@ -871,6 +871,12 @@ cfg(log_syslog_CFG, "syslog", log_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_B
9db2f0
 cfg(log_file_CFG, "file", log_CFG_SECTION, CFG_DEFAULT_UNDEFINED, CFG_TYPE_STRING, NULL, vsn(1, 0, 0), NULL, 0, NULL,
9db2f0
 	"Write error and debug log messages to a file specified here.\n")
9db2f0
 
9db2f0
+cfg_array(log_journal_CFG, "journal", log_CFG_SECTION, CFG_ALLOW_EMPTY | CFG_DEFAULT_COMMENTED, CFG_TYPE_STRING, NULL, vsn(2, 3, 12), NULL, 0, NULL,
9db2f0
+	  "Record lvm information in the systemd journal.\n"
9db2f0
+	  "command: record commands that are run.\n"
9db2f0
+	  "output: record default output from commands.\n"
9db2f0
+	  "debug: record debug messages from commands.\n")
9db2f0
+
9db2f0
 cfg(log_overwrite_CFG, "overwrite", log_CFG_SECTION, CFG_DEFAULT_COMMENTED, CFG_TYPE_BOOL, DEFAULT_OVERWRITE, vsn(1, 0, 0), NULL, 0, NULL,
9db2f0
 	"Overwrite the log file each time the program is run.\n")
9db2f0
 
9db2f0
diff --git a/lib/log/log.c b/lib/log/log.c
9db2f0
index 53ff5b1..8ebbf94 100644
9db2f0
--- a/lib/log/log.c
9db2f0
+++ b/lib/log/log.c
9db2f0
@@ -25,6 +25,7 @@
9db2f0
 #include <syslog.h>
9db2f0
 #include <ctype.h>
9db2f0
 #include <time.h>
9db2f0
+#include <systemd/sd-journal.h>
9db2f0
 
9db2f0
 static FILE *_log_file;
9db2f0
 static char _log_file_path[PATH_MAX];
9db2f0
@@ -40,6 +41,7 @@ static char _msg_prefix[30] = "  ";
9db2f0
 static int _abort_on_internal_errors_config = 0;
9db2f0
 static uint32_t _debug_file_fields;
9db2f0
 static uint32_t _debug_output_fields;
9db2f0
+static uint32_t _log_journal = 0;
9db2f0
 
9db2f0
 static lvm2_log_fn_t _lvm2_log_fn = NULL;
9db2f0
 
9db2f0
@@ -455,6 +457,11 @@ void init_debug_output_fields(uint32_t debug_fields)
9db2f0
 	_debug_output_fields = debug_fields;
9db2f0
 }
9db2f0
 
9db2f0
+void init_log_journal(uint32_t fields)
9db2f0
+{
9db2f0
+	_log_journal = fields;
9db2f0
+}
9db2f0
+
9db2f0
 static void _set_time_prefix(char *prefix, int buflen)
9db2f0
 {
9db2f0
 
9db2f0
@@ -609,6 +616,33 @@ static void _vprint_log(int level, const char *file, int line, int dm_errno_or_c
9db2f0
 	}
9db2f0
 
9db2f0
       log_it:
9db2f0
+
9db2f0
+	if (_log_journal) {
9db2f0
+		int to_journal = 0;
9db2f0
+
9db2f0
+		/* By default the visible command output is _LOG_WARN or less. */
9db2f0
+
9db2f0
+		if (_log_journal & LOG_JOURNAL_DEBUG)
9db2f0
+			to_journal = 1;
9db2f0
+		if ((_log_journal & LOG_JOURNAL_OUTPUT) && (log_level(level) <= _LOG_WARN))
9db2f0
+			to_journal = 1;
9db2f0
+
9db2f0
+		if (to_journal) {
9db2f0
+			int prio;
9db2f0
+			switch (log_level(level)) {
9db2f0
+			case _LOG_ERR:    prio = LOG_ERR; break;
9db2f0
+			case _LOG_WARN:   prio = LOG_WARNING; break;
9db2f0
+			case _LOG_INFO:   prio = LOG_INFO; break;
9db2f0
+			case _LOG_NOTICE: prio = LOG_NOTICE; break;
9db2f0
+			case _LOG_DEBUG:  prio = LOG_DEBUG; break;
9db2f0
+			default:          prio = LOG_INFO;
9db2f0
+			}
9db2f0
+			va_copy(ap, orig_ap);
9db2f0
+			sd_journal_printv(prio, trformat, ap);
9db2f0
+			va_end(ap);
9db2f0
+		}
9db2f0
+	}
9db2f0
+
9db2f0
 	if (!logged_via_report && ((verbose_level() >= level) && !_log_suppress)) {
9db2f0
 		if (verbose_level() > _LOG_DEBUG) {
9db2f0
 			memset(buf, 0, sizeof(buf));
9db2f0
@@ -792,3 +826,60 @@ void log_set_report_object_name_and_id(const char *name, const char *id)
9db2f0
 	_log_report.object_name = name;
9db2f0
 	_log_report.object_id = id;
9db2f0
 }
9db2f0
+
9db2f0
+/*
9db2f0
+ * TODO: log/journal=["daemon_command"]
9db2f0
+ *       daemon_command: record commands that are run by an lvm daemon.
9db2f0
+ *       (i.e. not commands run directly by a user.)
9db2f0
+ *       For this we need to be able to clearly identify when a command is
9db2f0
+ *       being run by dmeventd/lvmpolld/lvmdbusd.
9db2f0
+ *
9db2f0
+ * TODO: log/journal_commmand_names=["lvcreate","lvconvert"]
9db2f0
+ * This would restrict log/journal=["command"] to the listed command names.
9db2f0
+ * Also allow "!command" to exclude a command, e.g. ["!pvs"]
9db2f0
+ *
9db2f0
+ * TODO: log/journal_daemon_command_names=["lvcreate","lvconvert"]
9db2f0
+ * This would restrict log/journal=["dameon_command"] to the listed command names.
9db2f0
+ *
9db2f0
+ * TODO: log/journal_daemon_names=["dmeventd"]
9db2f0
+ * This would restrict log/journal=["daemon_command"] to commands run by
9db2f0
+ * the named daemon.
9db2f0
+ *
9db2f0
+ * TODO: log/command_to_file=<path> would write this info to the file.
9db2f0
+ *
9db2f0
+ * TODO: log/debug_to_file=<path> would write full debugging to the file.
9db2f0
+ *       (the same effect as log/file=<path> log/level=7)
9db2f0
+ */
9db2f0
+
9db2f0
+void log_command(const char *cmd_line, const char *cmd_name, const char *cmd_id)
9db2f0
+{
9db2f0
+	if (_log_journal & LOG_JOURNAL_COMMAND) {
9db2f0
+
9db2f0
+		/*
9db2f0
+		 * TODO: DAEMON=dmeventd|lvmpolld|lvmdbusd,
9db2f0
+		 * Could we include caller info such as libblkid, udev rule, etc?
9db2f0
+		 * Does systemd already record the caller for us?
9db2f0
+		 */
9db2f0
+
9db2f0
+		/* The command line, pid, and other things are automatically included. */
9db2f0
+
9db2f0
+		sd_journal_send("MESSAGE=lvm command %s", cmd_name,
9db2f0
+				"MESSAGE_ID=3ca432788c374e4ba684b834188eca36",
9db2f0
+				"LVM_CMD_NAME=%s", cmd_name,
9db2f0
+				"LVM_CMD_ID=%s", cmd_id,
9db2f0
+				"PRIORITY=%i", LOG_INFO,
9db2f0
+				NULL);
9db2f0
+	}
9db2f0
+}
9db2f0
+
9db2f0
+uint32_t log_journal_str_to_val(const char *str)
9db2f0
+{
9db2f0
+	if (!strcasecmp(str, "command"))
9db2f0
+		return LOG_JOURNAL_COMMAND;
9db2f0
+	if (!strcasecmp(str, "output"))
9db2f0
+		return LOG_JOURNAL_OUTPUT;
9db2f0
+	if (!strcasecmp(str, "debug"))
9db2f0
+		return LOG_JOURNAL_DEBUG;
9db2f0
+	return 0;
9db2f0
+}
9db2f0
+
9db2f0
diff --git a/lib/log/log.h b/lib/log/log.h
9db2f0
index d3848a4..b5f05f2 100644
9db2f0
--- a/lib/log/log.h
9db2f0
+++ b/lib/log/log.h
9db2f0
@@ -63,6 +63,10 @@
9db2f0
 #define LOG_DEBUG_FIELD_FILELINE	0x0004
9db2f0
 #define LOG_DEBUG_FIELD_MESSAGE		0x0008
9db2f0
 
9db2f0
+#define LOG_JOURNAL_COMMAND             0x0001
9db2f0
+#define LOG_JOURNAL_OUTPUT              0x0002
9db2f0
+#define LOG_JOURNAL_DEBUG               0x0004
9db2f0
+
9db2f0
 
9db2f0
 /*
9db2f0
  * Classes available for debug log messages.
9db2f0
diff --git a/lib/log/lvm-logging.h b/lib/log/lvm-logging.h
9db2f0
index 39108fc..fb18a41 100644
9db2f0
--- a/lib/log/lvm-logging.h
9db2f0
+++ b/lib/log/lvm-logging.h
9db2f0
@@ -62,6 +62,12 @@ void reset_log_duplicated(void);
9db2f0
 void init_syslog(int facility);
9db2f0
 void fin_syslog(void);
9db2f0
 
9db2f0
+void init_log_journal(uint32_t fields);
9db2f0
+uint32_t log_journal_str_to_val(const char *str);
9db2f0
+
9db2f0
+void log_command(const char *cmd_line, const char *cmd_name, const char *cmd_id);
9db2f0
+
9db2f0
+
9db2f0
 int error_message_produced(void);
9db2f0
 void reset_lvm_errno(int store_errmsg);
9db2f0
 int stored_errno(void);
9db2f0
diff --git a/tools/args.h b/tools/args.h
9db2f0
index 9aeec40..71db7f5 100644
9db2f0
--- a/tools/args.h
9db2f0
+++ b/tools/args.h
9db2f0
@@ -323,6 +323,14 @@ arg(ignoreunsupported_ARG, '\0', "ignoreunsupported", 0, 0, 0,
9db2f0
 arg(importdevices_ARG, '\0', "importdevices", 0, 0, 0,
9db2f0
     "Add devices to the devices file.\n")
9db2f0
 
9db2f0
+arg(journal_ARG, '\0', "journal", string_VAL, 0, 0,
9db2f0
+    "Record information in the systemd journal.\n"
9db2f0
+    "This information is in addition to information\n"
9db2f0
+    "enabled by the lvm.conf log/journal setting.\n"
9db2f0
+    "command: record information about the command.\n"
9db2f0
+    "output: record the default command output.\n"
9db2f0
+    "debug: record full command debugging.\n")
9db2f0
+
9db2f0
 arg(labelsector_ARG, '\0', "labelsector", number_VAL, 0, 0,
9db2f0
     "By default the PV is labelled with an LVM2 identifier in its second\n"
9db2f0
     "sector (sector 1). This lets you use a different sector near the\n"
9db2f0
diff --git a/tools/command-lines.in b/tools/command-lines.in
9db2f0
index aa3e3d9..68374c0 100644
9db2f0
--- a/tools/command-lines.in
9db2f0
+++ b/tools/command-lines.in
9db2f0
@@ -204,7 +204,7 @@
9db2f0
 #
9db2f0
 OO_ALL: --commandprofile String, --config String, --debug,
9db2f0
 --driverloaded Bool, --help, --nolocking, --lockopt String, --longhelp, --profile String, --quiet,
9db2f0
---verbose, --version, --yes, --test, --devicesfile String, --devices PV
9db2f0
+--verbose, --version, --yes, --test, --devicesfile String, --devices PV, --journal String
9db2f0
 
9db2f0
 #
9db2f0
 # options for pvs, lvs, vgs, fullreport
9db2f0
diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
9db2f0
index 6ea5487..222cd5b 100644
9db2f0
--- a/tools/lvmcmdline.c
9db2f0
+++ b/tools/lvmcmdline.c
9db2f0
@@ -2016,6 +2016,8 @@ out:
9db2f0
 	log_debug("Recognised command %s (id %d / enum %d).",
9db2f0
 		  commands[best_i].command_id, best_i, commands[best_i].command_enum);
9db2f0
 
9db2f0
+	log_command(cmd->cmd_line, commands[best_i].name, commands[best_i].command_id);
9db2f0
+
9db2f0
 	return &commands[best_i];
9db2f0
 }
9db2f0
 
9db2f0
@@ -2404,6 +2406,15 @@ static void _get_current_output_settings_from_args(struct cmd_context *cmd)
9db2f0
 		cmd->current_settings.verbose = 0;
9db2f0
 		cmd->current_settings.silent = (arg_count(cmd, quiet_ARG) > 1) ? 1 : 0;
9db2f0
 	}
9db2f0
+
9db2f0
+	/*
9db2f0
+	 * default_settings.journal is already set from config and has already been
9db2f0
+	 * applied using init_log_journal().
9db2f0
+	 * current_settings have been set to default_settings.
9db2f0
+	 * now --journal value adds to current_settings.
9db2f0
+	 */
9db2f0
+	if (arg_is_set(cmd, journal_ARG))
9db2f0
+		cmd->current_settings.journal |= log_journal_str_to_val(arg_str_value(cmd, journal_ARG, ""));
9db2f0
 }
9db2f0
 
9db2f0
 static void _apply_current_output_settings(struct cmd_context *cmd)
9db2f0
@@ -2413,6 +2424,7 @@ static void _apply_current_output_settings(struct cmd_context *cmd)
9db2f0
 	init_debug_classes_logged(cmd->default_settings.debug_classes);
9db2f0
 	init_verbose(cmd->current_settings.verbose + VERBOSE_BASE_LEVEL);
9db2f0
 	init_silent(cmd->current_settings.silent);
9db2f0
+	init_log_journal(cmd->current_settings.journal);
9db2f0
 }
9db2f0
 
9db2f0
 static int _read_devices_list(struct cmd_context *cmd)
9db2f0
-- 
9db2f0
1.8.3.1
9db2f0