summaryrefslogtreecommitdiff
path: root/src/viewer
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2008-07-29 14:01:09 -0400
committerRay Strode <rstrode@redhat.com>2008-08-12 13:18:51 -0400
commit8ecb83969405df9c57f8532986ea636d1a4a2cc5 (patch)
treef57f894e6d62de16de485419da1a345412e77408 /src/viewer
parent77247462fa52dc0b5e333395fd289b2f6d4a73d7 (diff)
Add initial boot log viewer from Matthias
Since plymouth conceals boot messages from the user during boot up, it should provide a way for users to get at the boot messages after login. In particular, if there was a problem during boot up, the user should get notified at the login screen. This commit adds the first cut at a log viewer without any of the login screen integration bits.
Diffstat (limited to 'src/viewer')
-rw-r--r--src/viewer/Makefile.am12
-rw-r--r--src/viewer/plymouth-log-viewer.c379
2 files changed, 391 insertions, 0 deletions
diff --git a/src/viewer/Makefile.am b/src/viewer/Makefile.am
new file mode 100644
index 00000000..333a4aa9
--- /dev/null
+++ b/src/viewer/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS = .
+
+INCLUDES = -I$(top_srcdir) \
+ -I$(srcdir)
+plymouth_log_viewerdir = $(bindir)
+plymouth_log_viewer_PROGRAMS = plymouth-log-viewer
+
+plymouth_log_viewer_CFLAGS = $(GTK_CFLAGS) -DPLYMOUTH_LOG_DIRECTORY=\"$(localstatedir)/log\"
+plymouth_log_viewer_LDADD = $(GTK_LIBS)
+plymouth_log_viewer_SOURCES = $(srcdir)/plymouth-log-viewer.c
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/src/viewer/plymouth-log-viewer.c b/src/viewer/plymouth-log-viewer.c
new file mode 100644
index 00000000..f44becd1
--- /dev/null
+++ b/src/viewer/plymouth-log-viewer.c
@@ -0,0 +1,379 @@
+/*
+ * bootmessages - Display boot messages
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * Author: Matthias Clasen
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+/* Compile with:
+ *
+ * cc -o bootmessages bootmessages.c `pkg-config --cflags --libs gtk+-2.0`
+ */
+
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#ifndef GETTEXT_PACKAGE
+#define GETTEXT_PACKAGE "bootmessages"
+#endif
+
+#ifndef DEFAULT_LOG
+#define DEFAULT_LOG "/var/log/boot.log"
+#endif
+
+static gboolean show_icon = FALSE;
+static gboolean force = FALSE;
+
+static GOptionEntry entries[] =
+{
+ { "icon", 0, 0, G_OPTION_ARG_NONE, &show_icon, N_("Show a status icon if there are errors"), NULL },
+ { "force", 0, 0, G_OPTION_ARG_NONE, &force, N_("Show the icon even without errors"), NULL },
+ { NULL }
+};
+
+static void
+popup_menu (GtkStatusIcon *icon,
+ guint button,
+ guint activate_time,
+ gpointer user_data)
+{
+ GtkWidget *menu = user_data;
+
+ gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
+ gtk_status_icon_position_menu, icon,
+ button, activate_time);
+}
+
+static void
+activate_icon (GtkStatusIcon *icon,
+ gpointer user_data)
+{
+ GtkWidget *window = user_data;
+
+ gtk_window_present (GTK_WINDOW (window));
+}
+
+static char *
+parse_etc_sysconfig_i18n (void)
+{
+ char *content;
+ gsize length;
+ char *lang;
+ char *p, *q;
+
+ lang = NULL;
+
+ if (!g_file_get_contents ("/etc/sysconfig/i18n", &content, &length, NULL))
+ return NULL;
+
+ p = strstr (content, "LANG=");
+ if (!p)
+ goto out;
+
+ p = strchr (p, '"');
+ if (!p)
+ goto out;
+
+ p++;
+ q = strchr (p, '"');
+
+ if (!q)
+ goto out;
+
+ lang = g_strndup (p, q - p);
+
+out:
+ g_free (content);
+
+ g_print ("boot lang: %s\n", lang);
+ return lang;
+}
+
+static GtkTextBuffer *
+read_boot_log (const char *file,
+ int *seen_errors,
+ GError **error)
+{
+ char *content;
+ char *content_utf8;
+ gsize length;
+ char *p, *q;
+ GtkTextBuffer *buffer;
+ GtkTextTag *blue;
+ GtkTextIter iter;
+ const char *failed;
+ const char *warning;
+ GString *partial;
+ char *boot_lang;
+ char *lang;
+
+ if (!g_file_get_contents (file, &content, &length, error))
+ return NULL;
+
+ /*
+ * We need to briefly change to the locale from /etc/sysconfig/i18n to:
+ * - pick the right initscripts translations
+ * - convert boot.log to UTF-8
+ */
+
+ boot_lang = parse_etc_sysconfig_i18n ();
+ lang = setlocale (LC_ALL, NULL);
+
+ if (boot_lang == NULL)
+ boot_lang = g_strdup (lang);
+
+ setlocale (LC_ALL, boot_lang);
+
+ bind_textdomain_codeset ("initscripts", "UTF-8");
+ failed = dgettext ("initscripts", "FAILED");
+ warning = dgettext ("initscripts", "WARNING");
+
+ content_utf8 = g_locale_to_utf8 (content, length, NULL, NULL, NULL);
+ if (content_utf8)
+ {
+ g_free (content);
+ content = content_utf8;
+ }
+
+ setlocale (LC_ALL, lang);
+
+ if (strstr (content, failed) != NULL)
+ *seen_errors = 2;
+ else if (strstr (content, warning) != NULL)
+ *seen_errors = 1;
+ else
+ *seen_errors = 0;
+
+ buffer = gtk_text_buffer_new (NULL);
+ gtk_text_buffer_create_tag (buffer, "blue", "foreground", "blue", NULL);
+ gtk_text_buffer_create_tag (buffer, "green", "foreground", "green", NULL);
+ gtk_text_buffer_create_tag (buffer, "red", "foreground", "red", NULL);
+ gtk_text_buffer_create_tag (buffer, "yellow", "foreground", "yellow", NULL);
+
+ partial = g_string_new ("");
+
+ p = content;
+ while (*p)
+ {
+ switch (*p)
+ {
+ case '\r':
+ /* keep isolated \r */
+ if (p[1] != '\r' && p[-1] != '\r' &&
+ p[1] != '\n' && p[-1] != '\n')
+ {
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert (buffer, &iter, p, 1);
+ }
+ p++;
+ break;
+ case '\t':
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert (buffer, &iter, " ", 8);
+ p++;
+ break;
+ case '\033':
+ if (strncmp (p, "\033[0;34m", 7) == 0 && (q = strstr (p, "\033[0;39m")))
+ {
+ p += 7;
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, p, q - p, "blue", NULL);
+ p = q + 7;
+ }
+ else if (strncmp (p, "\033[60G", 5) == 0)
+ {
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert (buffer, &iter, "\t", 1);
+ p += 5;
+ }
+ else if (strncmp (p, "\033[0;31m", 7) == 0 && (q = strstr (p, "\033[0;39m")))
+ {
+ p += 7;
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, p, q - p, "red", NULL);
+ p = q + 7;
+ }
+ else if (strncmp (p, "\033[0;32m", 7) == 0 && (q = strstr (p, "\033[0;39m")))
+ {
+ p += 7;
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, p, q - p, "green", NULL);
+ p = q + 7;
+ }
+ else if (strncmp (p, "\033[0;33m", 7) == 0 && (q = strstr (p, "\033[0;39m")))
+ {
+ p += 7;
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert_with_tags_by_name (buffer, &iter, p, q - p, "yellow", NULL);
+ p = q + 7;
+ }
+ else if (strncmp (p, "\033%G", 3) == 0)
+ p += 3;
+ else
+ p++;
+ break;
+ default:
+ /* GtkTextBuffer doesn't let us insert partial utf-8 characters */
+ g_string_append_c (partial, *p);
+ if (g_utf8_get_char_validated (partial->str, partial->len) != (gunichar)-2)
+ {
+ gtk_text_buffer_get_end_iter (buffer, &iter);
+ gtk_text_buffer_insert (buffer, &iter, partial->str, partial->len);
+ g_string_truncate (partial, 0);
+ }
+ p++;
+ break;
+ }
+ }
+
+ g_string_free (partial, TRUE);
+ g_free (content);
+
+ return buffer;
+}
+
+static void
+close_window (GtkWidget *window)
+{
+ if (show_icon)
+ gtk_widget_hide (window);
+ else
+ gtk_main_quit ();
+}
+
+static GtkWidget *
+create_window (GtkTextBuffer *buffer)
+{
+ GtkWidget *window;
+ GtkWidget *scrolledwin;
+ GtkWidget *box;
+ GtkWidget *terminal;
+ GtkWidget *bbox;
+ GtkWidget *close_button;
+ PangoTabArray *tabs;
+ int width, height;
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
+ gtk_window_set_title (GTK_WINDOW (window), _("Boot messages"));
+ gtk_container_set_border_width (GTK_CONTAINER (window), 12);
+
+ width = MIN (800, 0.75 * gdk_screen_get_width (gdk_screen_get_default ()));
+ height = MIN (600, 0.75 * gdk_screen_get_height (gdk_screen_get_default ()));
+ gtk_window_set_default_size (GTK_WINDOW (window), width, height);
+
+ box = gtk_vbox_new (FALSE, 0);
+ scrolledwin = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwin),
+ GTK_SHADOW_IN);
+ terminal = gtk_text_view_new_with_buffer (buffer);
+ gtk_text_view_set_editable (GTK_TEXT_VIEW (terminal), FALSE);
+ tabs = pango_tab_array_new_with_positions (1, TRUE, PANGO_TAB_LEFT, width - 130);
+ gtk_text_view_set_tabs (GTK_TEXT_VIEW (terminal), tabs);
+ gtk_text_view_set_left_margin (GTK_TEXT_VIEW (terminal), 12);
+ gtk_text_view_set_right_margin (GTK_TEXT_VIEW (terminal), 12);
+
+ bbox = gtk_hbutton_box_new ();
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_END);
+ close_button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+
+ gtk_container_add (GTK_CONTAINER (window), box);
+ gtk_box_pack_start (GTK_BOX (box), scrolledwin, TRUE, TRUE, 6);
+ gtk_container_add (GTK_CONTAINER (scrolledwin), terminal);
+ gtk_box_pack_start (GTK_BOX (box), bbox, FALSE, TRUE, 6);
+ gtk_box_pack_start (GTK_BOX (bbox), close_button, FALSE, TRUE, 6);
+
+ g_signal_connect (window, "delete-event",
+ G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+ g_signal_connect_swapped (close_button, "clicked",
+ G_CALLBACK (close_window), window);
+
+ gtk_widget_show_all (box);
+
+ return window;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkStatusIcon *icon;
+ GtkWidget *menu;
+ GtkWidget *quit_item;
+ GtkWidget *window;
+ GtkTextBuffer *buffer;
+ GError *error = NULL;
+ int seen_errors;
+ gchar *file;
+
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ if (!gtk_init_with_args (&argc, &argv,
+ _("[FILE]"), entries, GETTEXT_PACKAGE, &error))
+ {
+ g_print ("%s\n", error ? error->message : "Beep");
+ exit (1);
+ }
+
+ if (argc > 1)
+ file = argv[1];
+ else
+ file = DEFAULT_LOG;
+
+ buffer = read_boot_log (file, &seen_errors, &error);
+ if (buffer == NULL)
+ {
+ g_print ("%s\n", error ? error->message : "Blop");
+ exit (1);
+ }
+
+ window = create_window (buffer);
+
+ if (show_icon)
+ {
+ menu = gtk_menu_new ();
+ quit_item = gtk_image_menu_item_new_from_stock (GTK_STOCK_QUIT, NULL);
+ gtk_menu_append (GTK_MENU (menu), quit_item);
+ gtk_widget_show_all (menu);
+ g_signal_connect (quit_item, "activate", G_CALLBACK (gtk_main_quit), NULL);
+
+ icon = gtk_status_icon_new ();
+ if (seen_errors == 2)
+ gtk_status_icon_set_from_stock (icon, GTK_STOCK_DIALOG_WARNING);
+ else if (seen_errors == 1 || force)
+ gtk_status_icon_set_from_stock (icon, GTK_STOCK_INFO);
+ else
+ exit (0);
+
+ gtk_status_icon_set_tooltip (icon, _("Boot messages"));
+
+ g_signal_connect (icon, "activate", G_CALLBACK (activate_icon), window);
+ g_signal_connect (icon, "popup-menu", G_CALLBACK (popup_menu), menu);
+ }
+ else
+ gtk_window_present (GTK_WINDOW (window));
+
+ gtk_main ();
+
+ return 0;
+}