From 7d4a15e53a19dcc073b7037638d811eba3a85edf Mon Sep 17 00:00:00 2001
From: Peter Rajnoha <prajnoha@redhat.com>
Date: Tue, 3 May 2016 11:46:52 +0200
Subject: [PATCH] log: log warnings and errors via report if set; add
 log_set_report* fns

This patch adds structures and functions to reroute error and warning
logs to log report, if it's set.

There are 5 new functions:
  - log_set_report
    Set log report where logging will be rerouted.

  - log_set_report_context
    Set context globally so any report_cmdlog call will use it.

  - log_set_report_object_type
    Set object type globally so any report_cmdlog call will use it.

  - log_set_report_object_name_and_id
    Set object ID and name globally so any report_cmdlog call will use it.

  - log_set_report_object_group_and_group_id
    Set object group ID and name globally so any report_cmdlog call will use it.

These functions will be called during LVM command processing so any logs
which are rerouted to log report contain proper information about current
processing state.
---
 WHATS_NEW             |   1 +
 lib/log/log.c         | 101 +++++++++++++++++++++++++++++++++++++++++-
 lib/log/lvm-logging.h |  37 ++++++++++++++++
 lib/report/report.h   |   3 +-
 tools/reporter.c      |   8 +++-
 tools/toollib.c       |   8 +++-
 tools/toollib.h       |   1 +
 7 files changed, 154 insertions(+), 5 deletions(-)

diff --git a/WHATS_NEW b/WHATS_NEW
index edbc0f3d5..88e785a8d 100644
--- a/WHATS_NEW
+++ b/WHATS_NEW
@@ -1,5 +1,6 @@
 Version 2.02.158 - 
 =================================
+  Log warnings and errors via report during cmd processing if this is enabled.
   Make it possible to iterate over internal 'orphan' VGs in process_each_vg fn.
   Make -S|--select option groupable that allows this option to be repeated.
   Make -O|--sort option groupable that allows this option to be repeated.
diff --git a/lib/log/log.c b/lib/log/log.c
index 7b0026f22..3181fdd61 100644
--- a/lib/log/log.c
+++ b/lib/log/log.c
@@ -17,6 +17,7 @@
 #include "device.h"
 #include "memlock.h"
 #include "defaults.h"
+#include "report.h"
 
 #include <stdio.h>
 #include <stdarg.h>
@@ -47,6 +48,15 @@ static size_t _lvm_errmsg_size = 0;
 static size_t _lvm_errmsg_len = 0;
 #define MAX_ERRMSG_LEN (512 * 1024)  /* Max size of error buffer 512KB */
 
+static log_report_t _log_report = {
+	.report = NULL,
+	.context = LOG_REPORT_CONTEXT_NULL,
+	.object_type = LOG_REPORT_OBJECT_TYPE_NULL,
+	.object_id = NULL,
+	.object_name = NULL,
+	.object_group = NULL
+};
+
 void init_log_fn(lvm2_log_fn_t log_fn)
 {
 	_lvm2_log_fn = log_fn;
@@ -260,6 +270,38 @@ void reset_log_duplicated(void) {
 	}
 }
 
+static const char *_get_log_level_name(int level)
+{
+	static const char *log_level_names[] = {"",      /* unassigned */
+						"",      /* unassigned */
+						"fatal", /* _LOG_FATAL */
+						"error", /* _LOG_ERROR */
+						"warn",  /* _LOG_WARN */
+						"notice" /* _LOG_NOTICE */
+						"info",  /* _LOG_INFO */
+						"debug"  /* _LOG_DEBUG */
+						};
+	level &= ~_LOG_STDERR;
+	return log_level_names[level];
+}
+
+const char *log_get_report_context_name(log_report_context_t context)
+{
+	static const char *log_context_names[LOG_REPORT_CONTEXT_COUNT] = {[LOG_REPORT_CONTEXT_NULL] = "",
+									  [LOG_REPORT_CONTEXT_PROCESSING] = "processing"};
+	return log_context_names[context];
+}
+
+
+const char *log_get_report_object_type_name(log_report_object_type_t object_type)
+{
+	static const char *log_object_type_names[LOG_REPORT_OBJECT_TYPE_COUNT] = {[LOG_REPORT_OBJECT_TYPE_NULL] = "",
+										  [LOG_REPORT_OBJECT_TYPE_PV] = "pv",
+										  [LOG_REPORT_OBJECT_TYPE_VG] = "vg",
+										  [LOG_REPORT_OBJECT_TYPE_LV] = "lv"};
+	return log_object_type_names[object_type];
+}
+
 void print_log(int level, const char *file, int line, int dm_errno_or_class,
 	       const char *format, ...)
 {
@@ -277,6 +319,8 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
 	static int _abort_on_internal_errors_env_present = -1;
 	static int _abort_on_internal_errors_env = 0;
 	char *env_str;
+	struct dm_report *orig_report;
+	int logged_via_report = 0;
 
 	level &= ~(_LOG_STDERR|_LOG_ONCE);
 
@@ -309,6 +353,7 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
 
 	if (_lvm2_log_fn ||
 	    (_store_errmsg && (level <= _LOG_ERR)) ||
+	    (_log_report.report && (use_stderr || (level <=_LOG_ERR))) ||
 	    log_once) {
 		va_start(ap, format);
 		n = vsnprintf(message, sizeof(message), trformat, ap);
@@ -356,6 +401,23 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
 		}
 	}
 
+	if (_log_report.report && (use_stderr || (level <= _LOG_ERR))) {
+		orig_report = _log_report.report;
+		_log_report.report = NULL;
+
+		if (!report_cmdlog(orig_report, _get_log_level_name(level),
+				   log_get_report_context_name(_log_report.context),
+				   log_get_report_object_type_name(_log_report.object_type),
+				   _log_report.object_name, _log_report.object_id,
+				   _log_report.object_group, _log_report.object_group_id,
+				   message, _lvm_errno, 0))
+			fprintf(stderr, _("failed to report cmdstatus"));
+		else
+			logged_via_report = 1;
+
+		_log_report.report = orig_report;
+	}
+
 	if (_lvm2_log_fn) {
 		_lvm2_log_fn(level, file, line, 0, message);
 		if (fatal_internal_error)
@@ -364,7 +426,7 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
 	}
 
       log_it:
-	if ((verbose_level() >= level) && !_log_suppress) {
+	if (!logged_via_report && ((verbose_level() >= level) && !_log_suppress)) {
 		if (verbose_level() > _LOG_DEBUG) {
 			(void) dm_snprintf(buf, sizeof(buf), "#%s:%d ",
 					   file, line);
@@ -462,3 +524,40 @@ void print_log(int level, const char *file, int line, int dm_errno_or_class,
 		_already_logging = 0;
 	}
 }
+
+log_report_t log_get_report_state(void)
+{
+	return _log_report;
+}
+
+void log_restore_report_state(log_report_t log_report)
+{
+	_log_report = log_report;
+}
+
+void log_set_report(struct dm_report *report)
+{
+	_log_report.report = report;
+}
+
+void log_set_report_context(log_report_context_t context)
+{
+	_log_report.context = context;
+}
+
+void log_set_report_object_type(log_report_object_type_t object_type)
+{
+	_log_report.object_type = object_type;
+}
+
+void log_set_report_object_group_and_group_id(const char *group, const char *id)
+{
+	_log_report.object_group = group;
+	_log_report.object_group_id = id;
+}
+
+void log_set_report_object_name_and_id(const char *name, const char *id)
+{
+	_log_report.object_name = name;
+	_log_report.object_id = id;
+}
diff --git a/lib/log/lvm-logging.h b/lib/log/lvm-logging.h
index 56e95fff1..5789c0bf8 100644
--- a/lib/log/lvm-logging.h
+++ b/lib/log/lvm-logging.h
@@ -65,4 +65,41 @@ int log_suppress(int suppress);
 /* Suppress messages to syslog */
 void syslog_suppress(int suppress);
 
+/* Hooks to handle logging through report. */
+typedef enum {
+	LOG_REPORT_CONTEXT_NULL,
+	LOG_REPORT_CONTEXT_PROCESSING,
+	LOG_REPORT_CONTEXT_COUNT
+} log_report_context_t;
+
+typedef enum {
+	LOG_REPORT_OBJECT_TYPE_NULL,
+	LOG_REPORT_OBJECT_TYPE_PV,
+	LOG_REPORT_OBJECT_TYPE_VG,
+	LOG_REPORT_OBJECT_TYPE_LV,
+	LOG_REPORT_OBJECT_TYPE_COUNT
+} log_report_object_type_t;
+
+typedef struct log_report {
+	struct dm_report *report;
+	log_report_context_t context;
+	log_report_object_type_t object_type;
+	const char *object_name;
+	const char *object_id;
+	const char *object_group;
+	const char *object_group_id;
+} log_report_t;
+
+log_report_t log_get_report_state(void);
+void log_restore_report_state(log_report_t log_report);
+
+void log_set_report(struct dm_report *report);
+void log_set_report_context(log_report_context_t context);
+void log_set_report_object_type(log_report_object_type_t object_type);
+void log_set_report_object_group_and_group_id(const char *group, const char *group_id);
+void log_set_report_object_name_and_id(const char *name, const char *id);
+
+const char *log_get_report_context_name(log_report_context_t context);
+const char *log_get_report_object_type_name(log_report_object_type_t object_type);
+
 #endif
diff --git a/lib/report/report.h b/lib/report/report.h
index f5df51eb2..37e106158 100644
--- a/lib/report/report.h
+++ b/lib/report/report.h
@@ -82,7 +82,8 @@ typedef int (*field_report_fn) (struct report_handle * dh, struct field * field,
 				const void *data);
 
 int report_format_init(struct cmd_context *cmd, dm_report_group_type_t *report_group_type,
-		       struct dm_report_group **report_group, struct dm_report **log_rh);
+		       struct dm_report_group **report_group, struct dm_report **log_rh,
+		       log_report_t *saved_log_report_state);
 
 void *report_init(struct cmd_context *cmd, const char *format, const char *keys,
 		  report_type_t *report_type, const char *separator,
diff --git a/tools/reporter.c b/tools/reporter.c
index 0509c88c9..d37694733 100644
--- a/tools/reporter.c
+++ b/tools/reporter.c
@@ -1418,7 +1418,8 @@ int devtypes(struct cmd_context *cmd, int argc, char **argv)
 #define REPORT_FORMAT_NAME_JSON "json"
 
 int report_format_init(struct cmd_context *cmd, dm_report_group_type_t *report_group_type,
-		       struct dm_report_group **report_group, struct dm_report **log_rh)
+		       struct dm_report_group **report_group, struct dm_report **log_rh,
+		       log_report_t *saved_log_report_state)
 {
 	int config_set = find_config_tree_node(cmd, report_output_format_CFG, NULL) != NULL;
 	const char *config_format_str = find_config_tree_str(cmd, report_output_format_CFG, NULL);
@@ -1483,6 +1484,11 @@ int report_format_init(struct cmd_context *cmd, dm_report_group_type_t *report_g
 	*report_group = new_report_group;
 	if (tmp_log_rh)
 		*log_rh = tmp_log_rh;
+
+	if (saved_log_report_state) {
+		*saved_log_report_state = log_get_report_state();
+		log_set_report(*log_rh);
+	}
 	return 1;
 bad:
 	if (!dm_report_group_destroy(new_report_group))
diff --git a/tools/toollib.c b/tools/toollib.c
index 925157d0b..fe162d1e2 100644
--- a/tools/toollib.c
+++ b/tools/toollib.c
@@ -1729,11 +1729,12 @@ struct processing_handle *init_processing_handle(struct cmd_context *cmd, struct
 
 	if (!parent_handle) {
 		if (!report_format_init(cmd, &handle->report_group_type, &handle->report_group,
-					&handle->log_rh)) {
+					&handle->log_rh, &handle->saved_log_report_state)) {
 			dm_pool_free(cmd->mem, handle);
 			return NULL;
 		}
-	}
+	} else
+		handle->saved_log_report_state = log_get_report_state();
 
 	return handle;
 }
@@ -1767,6 +1768,9 @@ void destroy_processing_handle(struct cmd_context *cmd, struct processing_handle
 	if (handle) {
 		if (handle->selection_handle && handle->selection_handle->selection_rh)
 			dm_report_free(handle->selection_handle->selection_rh);
+
+		log_restore_report_state(handle->saved_log_report_state);
+
 		if (!dm_report_group_destroy(handle->report_group))
 			stack;
 		if (handle->log_rh)
diff --git a/tools/toollib.h b/tools/toollib.h
index 179efe551..114479310 100644
--- a/tools/toollib.h
+++ b/tools/toollib.h
@@ -76,6 +76,7 @@ struct processing_handle {
 	dm_report_group_type_t report_group_type;
 	struct dm_report_group *report_group;
 	struct dm_report *log_rh;
+	log_report_t saved_log_report_state;
 	void *custom_handle;
 };