util/cbfstool/eventlog: Use LocalTime or UTC timestamps

Add a new flag "--utc" to allow the user to choose if
elogtool should print timestamps in Local Time or in UTC.
It is useful for generating automated crash reports
including all system logs when users are located in
various regions (timezones).

Add information about timezone to timestamps printed
on the console.

Signed-off-by: Wojciech Macek <wmacek@google.com>
Change-Id: I30ba0e17c67ab4078e3a7137ece69009a63d68fa
Reviewed-on: https://review.coreboot.org/c/coreboot/+/73201
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Jakub Czapiga <jacz@semihalf.com>
diff --git a/util/cbfstool/elogtool.c b/util/cbfstool/elogtool.c
index 8465757..1dd653b1 100644
--- a/util/cbfstool/elogtool.c
+++ b/util/cbfstool/elogtool.c
@@ -18,6 +18,10 @@
 /* Only refers to the data max size. The "-1" is the checksum byte */
 #define ELOG_MAX_EVENT_DATA_SIZE  (ELOG_MAX_EVENT_SIZE - sizeof(struct event_header) - 1)
 
+enum elogtool_flag {
+	ELOGTOOL_FLAG_UTC = (1 << 0),
+};
+
 enum elogtool_return {
 	ELOGTOOL_EXIT_SUCCESS = 0,
 	ELOGTOOL_EXIT_BAD_ARGS,
@@ -28,13 +32,13 @@
 	ELOGTOOL_EXIT_NOT_ENOUGH_BUFFER_SPACE,
 };
 
-static int cmd_list(const struct buffer *);
-static int cmd_clear(const struct buffer *);
-static int cmd_add(const struct buffer *);
+static int cmd_list(const struct buffer *, enum elogtool_flag);
+static int cmd_clear(const struct buffer *, enum elogtool_flag);
+static int cmd_add(const struct buffer *, enum elogtool_flag);
 
 static const struct {
 	const char *name;
-	int (*func)(const struct buffer *buf);
+	int (*func)(const struct buffer *buf, enum elogtool_flag flags);
 	/* Whether it requires to write the buffer back */
 	bool write_back;
 } cmds[] = {
@@ -49,6 +53,7 @@
 static struct option long_options[] = {
 	{"file", required_argument, 0, 'f'},
 	{"help", no_argument, 0, 'h'},
+	{"utc", no_argument, 0, 'U'},
 	{NULL, 0, 0, 0},
 };
 
@@ -66,6 +71,7 @@
 			"-f, --file <filename>   File that holds event log partition.\n"
 			"                        If empty it will try to read/write from/to\n"
 			"                        the " ELOG_RW_REGION_NAME " using flashrom.\n"
+			"-U, --utc               Print timestamps in UTC time zone\n"
 			"-h, --help              Print this help\n",
 			invoked_as);
 }
@@ -187,11 +193,15 @@
 	return ELOGTOOL_EXIT_SUCCESS;
 }
 
-static int cmd_list(const struct buffer *buf)
+static int cmd_list(const struct buffer *buf, enum elogtool_flag flags)
 {
+	enum eventlog_timezone tz = EVENTLOG_TIMEZONE_LOCALTIME;
 	const struct event_header *event;
 	unsigned int count = 0;
 
+	if (flags & ELOGTOOL_FLAG_UTC)
+		tz = EVENTLOG_TIMEZONE_UTC;
+
 	/* Point to the first event */
 	event = buffer_get(buf) + sizeof(struct elog_header);
 
@@ -203,7 +213,7 @@
 			|| event->type == ELOG_TYPE_EOL)
 			break;
 
-		eventlog_print_event(event, count);
+		eventlog_print_event(event, count, tz);
 		event = elog_get_next_event(event);
 		count++;
 	}
@@ -215,7 +225,8 @@
  * Clears the elog events from the given buffer, which is a valid RW_ELOG region.
  * A LOG_CLEAR event is appended.
  */
-static int cmd_clear(const struct buffer *buf)
+static int cmd_clear(const struct buffer *buf,
+		     enum elogtool_flag flags __maybe_unused)
 {
 	uint32_t used_data_size;
 	struct buffer copy;
@@ -318,7 +329,8 @@
 }
 
 /* Appends an elog entry to EventLog buffer. */
-static int cmd_add(const struct buffer *buf)
+static int cmd_add(const struct buffer *buf,
+		   enum elogtool_flag flags __maybe_unused)
 {
 	uint8_t data[ELOG_MAX_EVENT_DATA_SIZE];
 	size_t data_size = 0;
@@ -358,6 +370,7 @@
 int main(int argc, char **argv)
 {
 	char *filename = NULL;
+	enum elogtool_flag flags = 0;
 	struct buffer buf;
 	unsigned int i;
 	int argflag;
@@ -370,7 +383,7 @@
 
 	while (1) {
 		int option_index;
-		argflag = getopt_long(argc, argv, "hf:", long_options, &option_index);
+		argflag = getopt_long(argc, argv, "Uhf:", long_options, &option_index);
 		if (argflag == -1)
 			break;
 
@@ -388,6 +401,9 @@
 
 			filename = optarg;
 			break;
+		case 'U':
+			flags |= ELOGTOOL_FLAG_UTC;
+			break;
 
 		default:
 			break;
@@ -410,7 +426,7 @@
 			/* For commands that parse their own arguments. */
 			cmd_argv = &argv[optind+1];
 			argv0 = argv[0];
-			ret = cmds[i].func(&buf);
+			ret = cmds[i].func(&buf, flags);
 			break;
 		}
 	}
diff --git a/util/cbfstool/eventlog.c b/util/cbfstool/eventlog.c
index 058a3f7..93590a1 100644
--- a/util/cbfstool/eventlog.c
+++ b/util/cbfstool/eventlog.c
@@ -49,10 +49,12 @@
  *
  * Forms the key-value description pair for the event timestamp.
  */
-static void eventlog_print_timestamp(const struct event_header *event)
+static void eventlog_print_timestamp(const struct event_header *event,
+				     enum eventlog_timezone tz)
 {
 	const char *tm_format = "%y-%m-%d%t%H:%M:%S";
 	char tm_string[40];
+	struct tm *tmptr;
 	struct tm tm;
 	time_t time;
 
@@ -78,7 +80,11 @@
 	time = mktime(&tm);
 	time += tm.tm_gmtoff; /* force adjust for timezone */
 
-	strftime(tm_string, sizeof(tm_string), "%Y-%m-%d %H:%M:%S", localtime(&time));
+	if (tz == EVENTLOG_TIMEZONE_UTC)
+		tmptr = gmtime(&time);
+	else
+		tmptr = localtime(&time);
+	strftime(tm_string, sizeof(tm_string), "%Y-%m-%d %H:%M:%S%z", tmptr);
 
 	eventlog_printf("%s", tm_string);
 }
@@ -648,13 +654,14 @@
 	return 0;
 }
 
-void eventlog_print_event(const struct event_header *event, int count)
+void eventlog_print_event(const struct event_header *event, int count,
+			  enum eventlog_timezone tz)
 {
 	/* Ignore the printf separator at the beginning and end of each line */
 	eventlog_printf_ignore_separator_once = 1;
 
 	eventlog_printf("%d", count);
-	eventlog_print_timestamp(event);
+	eventlog_print_timestamp(event, tz);
 	eventlog_print_type(event);
 	eventlog_print_data(event);
 
diff --git a/util/cbfstool/eventlog.h b/util/cbfstool/eventlog.h
index 1911b77..6cc39fb 100644
--- a/util/cbfstool/eventlog.h
+++ b/util/cbfstool/eventlog.h
@@ -8,7 +8,13 @@
 struct event_header;
 struct buffer;
 
-void eventlog_print_event(const struct event_header *event, int count);
+enum eventlog_timezone {
+	EVENTLOG_TIMEZONE_LOCALTIME = 0,
+	EVENTLOG_TIMEZONE_UTC,
+};
+
+void eventlog_print_event(const struct event_header *event, int count,
+			  enum eventlog_timezone tz);
 int eventlog_init_event(const struct buffer *buf, uint8_t type,
 			const void *data, int data_size);