Blob Blame History Raw
From bfb7c70e7eaa1311cadc716c303ca694163f9e2f Mon Sep 17 00:00:00 2001
From: Peter Jones <pjones@redhat.com>
Date: Tue, 19 Feb 2013 18:00:36 -0500
Subject: [PATCH] Add logging when things fail.

Actually also logs when things succeed.

Signed-off-by: Peter Jones <pjones@redhat.com>
---
 Makefile                |   2 +-
 grubby.c                |  92 ++++++++++++++++++++++++++++----------
 log.c                   | 114 ++++++++++++++++++++++++++++++++++++++++++++++++
 log.h                   |  27 ++++++++++++
 test/results/debug/g2.1 |   1 +
 5 files changed, 212 insertions(+), 24 deletions(-)
 create mode 100644 log.c
 create mode 100644 log.h

diff --git a/Makefile b/Makefile
index fcf2dd2..325ffd8 100644
--- a/Makefile
+++ b/Makefile
@@ -20,7 +20,7 @@
 VERSION=8.22
 
 TARGETS = grubby
-OBJECTS = grubby.o
+OBJECTS = grubby.o log.o
 
 CC = gcc
 RPM_OPT_FLAGS := -O2 -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fstack-protector
diff --git a/grubby.c b/grubby.c
index edbeb64..408e3ca 100644
--- a/grubby.c
+++ b/grubby.c
@@ -36,6 +36,8 @@
 #include <signal.h>
 #include <blkid/blkid.h>
 
+#include "log.h"
+
 #ifndef DEBUG
 #define DEBUG 0
 #endif
@@ -58,6 +60,8 @@ int debug = 0;	/* Currently just for template debugging */
 
 int isEfi = 0;
 
+char *saved_command_line = NULL;
+
 /* comments get lumped in with indention */
 struct lineElement {
     char * item;
@@ -1533,43 +1537,67 @@ static char *findDiskForRoot()
     return NULL;
 }
 
-void printEntry(struct singleEntry * entry) {
+void printEntry(struct singleEntry * entry, FILE *f) {
     int i;
     struct singleLine * line;
 
     for (line = entry->lines; line; line = line->next) {
-	fprintf(stderr, "DBG: %s", line->indent);
+	log_message(f, "DBG: %s", line->indent);
 	for (i = 0; i < line->numElements; i++) {
 	    /* Need to handle this, because we strip the quotes from
 	     * menuentry when read it. */
 	    if (line->type == LT_MENUENTRY && i == 1) {
 		if(!isquote(*line->elements[i].item))
-		    fprintf(stderr, "\'%s\'", line->elements[i].item);
+		    log_message(f, "\'%s\'", line->elements[i].item);
 		else
-		    fprintf(stderr, "%s", line->elements[i].item);
-		fprintf(stderr, "%s", line->elements[i].indent);
+		    log_message(f, "%s", line->elements[i].item);
+		log_message(f, "%s", line->elements[i].indent);
 		
 		continue;
 	    }
 	    
-	    fprintf(stderr, "%s%s",
+	    log_message(f, "%s%s",
 		    line->elements[i].item, line->elements[i].indent);
 	}
-	fprintf(stderr, "\n");
+	log_message(f, "\n");
     }
 }
 
-void notSuitablePrintf(struct singleEntry * entry, const char *fmt, ...)
+void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...)
 {
-    va_list argp;
+    static int once;
+    va_list argp, argq;
 
-    if (!debug)
+    va_start(argp, fmt);
+
+    va_copy(argq, argp);
+    if (!once) {
+	log_time(NULL);
+	log_message(NULL, "command line: %s\n", saved_command_line);
+    }
+    log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed");
+    log_vmessage(NULL, fmt, argq);
+	
+    printEntry(entry, NULL);
+    va_end(argq);
+
+    if (!debug) {
+	once = 1;
+    	va_end(argp);
 	return;
+    }
 
-    va_start(argp, fmt);
+    if (okay) {
+	va_end(argp);
+	return;
+    }
+
+    if (!once)
+	log_message(stderr, "DBG: command line: %s\n", saved_command_line);
+    once = 1;
     fprintf(stderr, "DBG: Image entry failed: ");
     vfprintf(stderr, fmt, argp);
-    printEntry(entry);
+    printEntry(entry, stderr);
     va_end(argp);
 }
 
@@ -1596,22 +1624,25 @@ int suitableImage(struct singleEntry * entry, const char * bootPrefix,
     char * rootdev;
 
     if (skipRemoved && entry->skip) {
-	notSuitablePrintf(entry, "marked to skip\n");
+	notSuitablePrintf(entry, 0, "marked to skip\n");
 	return 0;
     }
 
     line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines);
     if (!line) {
-	notSuitablePrintf(entry, "no line found\n");
+	notSuitablePrintf(entry, 0, "no line found\n");
 	return 0;
     }
     if (line->numElements < 2) {
-	notSuitablePrintf(entry, "line has only %d elements\n",
+	notSuitablePrintf(entry, 0, "line has only %d elements\n",
 	    line->numElements);
 	return 0;
     }
 
-    if (flags & GRUBBY_BADIMAGE_OKAY) return 1;
+    if (flags & GRUBBY_BADIMAGE_OKAY) {
+	    notSuitablePrintf(entry, 1, "\n");
+	    return 1;
+    }
 
     fullName = alloca(strlen(bootPrefix) + 
 		      strlen(line->elements[1].item) + 1);
@@ -1622,7 +1653,7 @@ int suitableImage(struct singleEntry * entry, const char * bootPrefix,
     sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
             line->elements[1].item + rootspec_offset);
     if (access(fullName, R_OK)) {
-	notSuitablePrintf(entry, "access to %s failed\n", fullName);
+	notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
 	return 0;
     }
     for (i = 2; i < line->numElements; i++) 
@@ -1643,7 +1674,7 @@ int suitableImage(struct singleEntry * entry, const char * bootPrefix,
 
             /* failed to find one */
             if (!line) {
-		notSuitablePrintf(entry, "no line found\n");
+		notSuitablePrintf(entry, 0, "no line found\n");
 		return 0;
             }
 
@@ -1652,7 +1683,7 @@ int suitableImage(struct singleEntry * entry, const char * bootPrefix,
 	    if (i < line->numElements)
 	        dev = line->elements[i].item + 5;
 	    else {
-		notSuitablePrintf(entry, "no root= entry found\n");
+		notSuitablePrintf(entry, 0, "no root= entry found\n");
 		/* it failed too...  can't find root= */
 	        return 0;
             }
@@ -1661,32 +1692,33 @@ int suitableImage(struct singleEntry * entry, const char * bootPrefix,
 
     dev = getpathbyspec(dev);
     if (!getpathbyspec(dev)) {
-        notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev);
+        notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n", dev);
         return 0;
     } else
 	dev = getpathbyspec(dev);
 
     rootdev = findDiskForRoot();
     if (!rootdev) {
-        notSuitablePrintf(entry, "can't find root device\n");
+        notSuitablePrintf(entry, 0, "can't find root device\n");
 	return 0;
     }
 
     if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
-        notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n",
+        notSuitablePrintf(entry, 0, "uuid missing: rootdev %s, dev %s\n",
 		getuuidbydev(rootdev), getuuidbydev(dev));
         free(rootdev);
         return 0;
     }
 
     if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
-        notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n",
+        notSuitablePrintf(entry, 0, "uuid mismatch: rootdev %s, dev %s\n",
 		getuuidbydev(rootdev), getuuidbydev(dev));
 	free(rootdev);
         return 0;
     }
 
     free(rootdev);
+    notSuitablePrintf(entry, 1, "\n");
 
     return 1;
 }
@@ -3898,6 +3930,20 @@ int main(int argc, const char ** argv) {
 
     signal(SIGSEGV, traceback);
 
+    int i = 0;
+    for (int j = 1; j < argc; j++)
+	i += strlen(argv[j]) + 1;
+    saved_command_line = malloc(i);
+    if (!saved_command_line) {
+	fprintf(stderr, "grubby: %m\n");
+	exit(1);
+    }
+    saved_command_line[0] = '\0';
+    for (int j = 1; j < argc; j++) {
+	strcat(saved_command_line, argv[j]);
+	strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
+    }
+
     optCon = poptGetContext("grubby", argc, argv, options, 0);
     poptReadDefaultConfig(optCon, 1);
 
diff --git a/log.c b/log.c
new file mode 100644
index 0000000..1ddb8c1
--- /dev/null
+++ b/log.c
@@ -0,0 +1,114 @@
+/*
+ * log.c
+ *
+ * Copyright 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "log.h"
+
+static int log_fd = -1;
+static FILE *f = NULL;
+
+static int
+open_log(void)
+{
+	if (log_fd > -1)
+		return 0;
+	log_fd = open("/var/log/grubby", O_RDWR|O_APPEND|O_CREAT|O_CLOEXEC, 0600);
+	if (log_fd < 0)
+		return log_fd;
+
+	f = fdopen(log_fd, "a+");
+	if (f == NULL) {
+		typeof(errno) saved_errno = errno;
+		close(log_fd);
+		log_fd = -1;
+		errno = saved_errno;
+		return -1;
+	}
+
+	setbuf(f, NULL);
+	return 0;
+}
+
+int
+log_time(FILE *log)
+{
+	if (!log) {
+		int rc = open_log();
+		if (rc < 0)
+			return rc;
+	}
+
+	time_t t = time(NULL);
+	char timestr[27];
+
+	ctime_r(&t, timestr);
+	timestr[26] = '\0';
+	for (int i = 26; i >= 0; i--)
+		if (timestr[i] == '\n')
+			timestr[i] = '\0';
+
+	return log_message(log, "DBG: %d: %s: ", getpid(), timestr);
+}
+
+int
+log_vmessage(FILE *log, const char *msg, va_list ap)
+{
+	int rc;
+
+	if (!msg)
+		return -1;
+	if (msg[0] == '\0')
+		return 0;
+
+	if (!log) {
+		rc = open_log();
+		if (rc < 0)
+			return rc;
+	}
+
+	vfprintf(log ? log : f, msg, ap);
+	fdatasync(log ? fileno(log) : log_fd);
+
+	return 0;
+}
+
+int
+log_message(FILE *log, const char *msg, ...)
+{
+	va_list argp;
+
+	va_start(argp, msg);
+	int rc = log_vmessage(log, msg, argp);
+	va_end(argp);
+	return rc;
+}
diff --git a/log.h b/log.h
new file mode 100644
index 0000000..082a59e
--- /dev/null
+++ b/log.h
@@ -0,0 +1,27 @@
+/*
+ * log.h
+ *
+ * Copyright 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef GRUBBY_LOG_H
+#define GRUBBY_LOG_H 1
+
+extern int log_time(FILE *log);
+extern int log_vmessage(FILE *log, const char *msg, va_list ap);
+extern int log_message(FILE *log, const char *msg, ...);
+
+#endif /* GRUBBY_LOG_H */
diff --git a/test/results/debug/g2.1 b/test/results/debug/g2.1
index 9de4595..45c64ae 100644
--- a/test/results/debug/g2.1
+++ b/test/results/debug/g2.1
@@ -1,3 +1,4 @@
+DBG: command line: --grub2 -c test/grub2.1 --boot-filesystem=/boot --default-kernel --debug
 DBG: Image entry failed: access to /boot/vmlinuz-2.6.38.8-32.fc15.x86_64 failed
 DBG: menuentry 'Linux, with Fedora 2.6.38.8-32.fc15.x86_64' --class gnu-linux --class gnu --class os { 
 DBG: 	load_video
-- 
1.8.1.2