From 77c2cf65f29d77910eabe179eaef24fb2c0e49f3 Mon Sep 17 00:00:00 2001
From: Daniel Sungju Kwon <dkwon@redhat.com>
Date: Fri, 21 Jun 2019 12:08:18 +1000
Subject: [PATCH] Allows to change the error output direction

Currently, the error() is always printing the output to the console
through 'stdout'. This does not follow redirection which is good when
you want to know error while redirecting commands output to a file.
However, there are situations that you want to hide error messages or
redirect it into somewhere else.

Using 'set stderr' command, it can be changed to three different
destination - fixed 'stdout', following redirection (fp), or a custom
file path.

        crash> set stderr
        stderr: stdout
        crash> sym 0x523 > /dev/null
        sym: invalid address: 0x523
        crash> set stderr fp
        stderr: fp
        crash> sym 0x523 > /dev/null
        crash> set stderr /tmp/err.log
        stderr: /tmp/err.log
        crash> sym 0x523 > /dev/null
        crash> set stderr stdout
        stderr: stdout
        crash> sym 0x523 > /dev/null
        sym: invalid address: 0x523
---
 defs.h  |  2 ++
 help.c  |  5 +++++
 main.c  |  2 ++
 tools.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 61 insertions(+), 3 deletions(-)

diff --git a/defs.h b/defs.h
index ccffe58..57850c6 100644
--- a/defs.h
+++ b/defs.h
@@ -553,6 +553,8 @@ struct program_context {
 	ulong scope;			/* optional text context address */
 	ulong nr_hash_queues;		/* hash queue head count */
 	char *(*read_vmcoreinfo)(const char *);
+        FILE *stderr;                   /* error() message direction */
+        char stderr_path[PATH_MAX];     /* stderr path information */
 };
 
 #define READMEM  pc->readmem
diff --git a/help.c b/help.c
index 581e616..ddc8e86 100644
--- a/help.c
+++ b/help.c
@@ -1093,6 +1093,10 @@ char *help_set[] = {
 "         redzone  on | off     if on, CONFIG_SLUB object addresses displayed by",
 "                               the kmem command will point to the SLAB_RED_ZONE",
 "                               padding inserted at the beginning of the object.", 
+" stderr  stdout | fp | <path>  set the direction of error put. 'stdout' always",
+"                               print on console. 'fp' follows the redirection",
+"                               or pipe command. '<path>' can be any file path",
+"                               in the filesystem which can save the output",
 " ",
 "  Internal variables may be set in four manners:\n",
 "    1. entering the set command in $HOME/.%src.",
@@ -1144,6 +1148,7 @@ char *help_set[] = {
 "             scope: (not set)",
 "           offline: show",
 "           redzone: on",
+"            stderr: stdout",
 " ",
 "  Show the current context:\n",
 "    %s> set",
diff --git a/main.c b/main.c
index 83ccd31..68bdec4 100644
--- a/main.c
+++ b/main.c
@@ -1085,6 +1085,8 @@ setup_environment(int argc, char **argv)
 	 *  to pipes or output files.
 	 */
 	fp = stdout;
+        pc->stderr = stdout;
+        strcpy(pc->stderr_path, "stdout");
 
 	/*
 	 *  Start populating the program_context structure.  It's used so
diff --git a/tools.c b/tools.c
index 2d95c3a..840d07c 100644
--- a/tools.c
+++ b/tools.c
@@ -58,6 +58,9 @@ __error(int type, char *fmt, ...)
         void *retaddr[NUMBER_STACKFRAMES] = { 0 };
 	va_list ap;
 
+        if (!strcmp(pc->stderr_path, "fp"))
+                pc->stderr = fp;
+
 	if (CRASHDEBUG(1) || (pc->flags & DROP_CORE)) {
 		SAVE_RETURN_ADDRESS(retaddr);
 		console("error() trace: %lx => %lx => %lx => %lx\n",
@@ -69,7 +72,7 @@ __error(int type, char *fmt, ...)
         va_end(ap);
 
 	if (!fmt && FATAL_ERROR(type)) {
-		fprintf(stdout, "\n");
+		fprintf(pc->stderr, "\n");
 		clean_exit(1);
 	}
 
@@ -95,14 +98,14 @@ __error(int type, char *fmt, ...)
 			buf);
 		fflush(pc->stdpipe);
 	} else { 
-		fprintf(stdout, "%s%s%s %s%s", 
+		fprintf(pc->stderr, "%s%s%s %s%s", 
 			new_line || end_of_line ? "\n" : "",
 			type == WARNING ? "WARNING" : 
 			type == NOTE ? "NOTE" : 
 			type == CONT ? spacebuf : pc->curcmd,
 			type == CONT ? " " : ":",
 			buf, end_of_line ? "\n" : "");
-		fflush(stdout);
+		fflush(pc->stderr);
 	}
 
         if ((fp != stdout) && (fp != pc->stdpipe) && (fp != pc->tmpfile)) {
@@ -2483,6 +2486,51 @@ cmd_set(void)
 			}
 			return;
 
+                } else if (STREQ(args[optind], "stderr")) {
+                        if (args[optind+1]) {
+                                FILE *tmp_fp = NULL;
+                                char tmp_path[PATH_MAX];
+
+                                optind++;
+                                if (STREQ(args[optind], "stdout")) {
+                                        tmp_fp = stdout;
+                                        strcpy(tmp_path, "stdout");
+                                } else if (STREQ(args[optind], "fp")) {
+                                        tmp_fp = fp;
+                                        strcpy(tmp_path, "fp");
+                                } else {
+                                        if (strlen(args[optind]) >= PATH_MAX) {
+                                                error(INFO, "path length %d is too long. (max=%d)\n",
+                                                        strlen(args[optind]), PATH_MAX);
+                                                return;
+                                        }
+                                        tmp_fp = fopen(args[optind], "a");
+                                        if (tmp_fp != NULL) {
+                                                strcpy(tmp_path, args[optind]);
+                                        } else {
+                                                error(INFO, "invalid path: %s\n",
+                                                      args[optind]);
+                                                return;
+                                        }
+
+                                }
+
+                                if (strcmp(pc->stderr_path, tmp_path)) {
+                                        if (strcmp(pc->stderr_path, "stdout")
+                                            && strcmp(pc->stderr_path, "fp")) {
+                                                fclose(pc->stderr);
+                                        }
+                                        pc->stderr = tmp_fp;
+                                        strcpy(pc->stderr_path, tmp_path);
+                                }
+                        }
+
+                        if (runtime) {
+                                fprintf(fp, "stderr: %s\n",
+                                        pc->stderr_path);
+                        }
+                        return;
+
 		} else if (XEN_HYPER_MODE()) {
 			error(FATAL, "invalid argument for the Xen hypervisor\n");
 		} else if (pc->flags & MINIMAL_MODE) {
@@ -2590,6 +2638,7 @@ show_options(void)
 		fprintf(fp, "(not set)\n");
 	fprintf(fp, "       offline: %s\n", pc->flags2 & OFFLINE_HIDE ? "hide" : "show");
 	fprintf(fp, "       redzone: %s\n", pc->flags2 & REDZONE ? "on" : "off");
+	fprintf(fp, "        stderr: %s\n", pc->stderr_path);
 }
 
 
-- 
1.8.3.1

