summaryrefslogtreecommitdiff
path: root/logview/logview-app.c
diff options
context:
space:
mode:
Diffstat (limited to 'logview/logview-app.c')
-rw-r--r--logview/logview-app.c401
1 files changed, 401 insertions, 0 deletions
diff --git a/logview/logview-app.c b/logview/logview-app.c
new file mode 100644
index 00000000..a13f29d4
--- /dev/null
+++ b/logview/logview-app.c
@@ -0,0 +1,401 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* logview-app.c - logview application singleton
+ *
+ * Copyright (C) 2008 Cosimo Cecchi <[email protected]>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* logview-app.c */
+
+#include "logview-app.h"
+
+#include "logview-manager.h"
+#include "logview-window.h"
+#include "logview-prefs.h"
+
+#include <glib/gi18n.h>
+
+struct _LogviewAppPrivate {
+ LogviewPrefs *prefs;
+ LogviewManager *manager;
+ LogviewWindow *window;
+};
+
+enum {
+ APP_QUIT,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static LogviewApp *app_singleton = NULL;
+
+G_DEFINE_TYPE (LogviewApp, logview_app, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), LOGVIEW_TYPE_APP, LogviewAppPrivate))
+
+static gboolean
+main_window_delete_cb (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ LogviewApp *app = user_data;
+
+ g_signal_emit (app, signals[APP_QUIT], 0, NULL);
+
+ return FALSE;
+}
+
+static gboolean
+logview_app_set_window (LogviewApp *app)
+{
+ LogviewWindow *window;
+ gboolean retval = FALSE;
+
+ window = LOGVIEW_WINDOW (logview_window_new ());
+
+ if (window) {
+ app->priv->window = window;
+ g_signal_connect (window, "delete-event",
+ G_CALLBACK (main_window_delete_cb), app);
+ retval = TRUE;
+ }
+
+ gtk_window_set_default_icon_name ("logviewer");
+
+ return retval;
+}
+
+typedef struct {
+ LogviewApp *app;
+ GSList *logs;
+} EnumerateJob;
+
+/* TODO: ideally we should parse configuration files in /etc/logrotate.conf
+ * and all the files in /etc/logrotate.d/ and group all the logs referring
+ * to the same entry under a category. Right now, we just do some
+ * parsing instead, and fill with quasi-sensible defaults.
+ */
+
+/* adapted from sysklogd sources */
+static GSList*
+parse_syslog ()
+{
+ char cbuf[BUFSIZ];
+ char *cline, *p;
+ FILE *cf;
+ GSList *logfiles = NULL;
+
+ if ((cf = fopen ("/etc/syslog.conf", "r")) == NULL) {
+ return NULL;
+ }
+
+ cline = cbuf;
+ while (fgets (cline, sizeof (cbuf) - (cline - cbuf), cf) != NULL) {
+ gchar **list;
+ gint i;
+
+ for (p = cline; g_ascii_isspace (*p); ++p);
+ if (*p == '\0' || *p == '#' || *p == '\n')
+ continue;
+
+ list = g_strsplit_set (p, ", -\t()\n", 0);
+
+ for (i = 0; list[i]; ++i) {
+ if (*list[i] == '/' &&
+ g_slist_find_custom (logfiles, list[i],
+ (GCompareFunc) g_ascii_strcasecmp) == NULL)
+ {
+ logfiles = g_slist_insert (logfiles,
+ g_strdup (list[i]), 0);
+ }
+ }
+
+ g_strfreev (list);
+ }
+
+ fclose (cf);
+
+ return logfiles;
+}
+
+static void
+enumerate_job_finish (EnumerateJob *job)
+{
+ GSList *files = job->logs;
+ LogviewApp *app = job->app;
+
+ logview_manager_add_logs_from_name_list (app->priv->manager, files, files->data);
+
+ g_slist_foreach (files, (GFunc) g_free, NULL);
+ g_slist_free (files);
+
+ g_object_unref (job->app);
+ g_slice_free (EnumerateJob, job);
+}
+
+static void
+enumerate_next_files_async_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ EnumerateJob *job = user_data;
+ GList *enumerated_files, *l;
+ GFileInfo *info;
+ GSList *logs;
+ const char *content_type, *name;
+ char *parse_string, *container_path;
+ GFileType type;
+ GFile *container;
+
+ enumerated_files = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source),
+ res, NULL);
+ if (!enumerated_files) {
+ enumerate_job_finish (job);
+ return;
+ }
+
+ logs = job->logs;
+ container = g_file_enumerator_get_container (G_FILE_ENUMERATOR (source));
+ container_path = g_file_get_path (container);
+
+ /* TODO: we don't support grouping rotated logs yet, skip gzipped files
+ * and those which name contains a formatted date.
+ */
+ for (l = enumerated_files; l; l = l->next) {
+ info = l->data;
+ type = g_file_info_get_file_type (info);
+ content_type = g_file_info_get_content_type (info);
+ name = g_file_info_get_name (info);
+
+ if (!g_file_info_get_attribute_boolean (info, "access::can-read")) {
+ g_object_unref (info);
+ continue;
+ }
+
+ if (type != (G_FILE_TYPE_REGULAR || G_FILE_TYPE_SYMBOLIC_LINK) ||
+ !g_content_type_is_a (content_type, "text/plain"))
+ {
+ g_object_unref (info);
+ continue;
+ }
+
+ if (g_content_type_is_a (content_type, "application/x-gzip")) {
+ g_object_unref (info);
+ continue;
+ }
+
+ if (g_regex_match_simple ("\\d{8}$", name, 0, 0)) {
+ g_object_unref (info);
+ continue;
+ }
+
+ parse_string = g_build_filename (container_path, name, NULL);
+
+ if (g_slist_find_custom (logs, parse_string, (GCompareFunc) g_ascii_strcasecmp) == NULL) {
+ logs = g_slist_append (logs, parse_string);
+ } else {
+ g_free (parse_string);
+ }
+
+ g_object_unref (info);
+ parse_string = NULL;
+ }
+
+ g_list_free (enumerated_files);
+ g_object_unref (container);
+ g_free (container_path);
+
+ job->logs = logs;
+
+ enumerate_job_finish (job);
+}
+
+static void
+enumerate_children_async_cb (GObject *source,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ EnumerateJob *job = user_data;
+ GFileEnumerator *enumerator;
+
+ enumerator = g_file_enumerate_children_finish (G_FILE (source),
+ res, NULL);
+ if (!enumerator) {
+ enumerate_job_finish (job);
+ return;
+ }
+
+ g_file_enumerator_next_files_async (enumerator, G_MAXINT,
+ G_PRIORITY_DEFAULT,
+ NULL, enumerate_next_files_async_cb, job);
+}
+
+static void
+logview_app_first_time_initialize (LogviewApp *app)
+{
+ GSList *logs;
+ GFile *log_dir;
+ EnumerateJob *job;
+
+ /* let's add all accessible files in /var/log and those mentioned
+ * in /etc/syslog.conf.
+ */
+
+ logs = parse_syslog ();
+
+ job = g_slice_new0 (EnumerateJob);
+ job->app = g_object_ref (app);
+ job->logs = logs;
+
+ log_dir = g_file_new_for_path ("/var/log/");
+ g_file_enumerate_children_async (log_dir,
+ "standard::*,access::can-read", 0,
+ G_PRIORITY_DEFAULT, NULL,
+ enumerate_children_async_cb, job);
+
+ g_object_unref (log_dir);
+}
+
+static void
+do_finalize (GObject *obj)
+{
+ LogviewApp *app = LOGVIEW_APP (obj);
+
+ g_object_unref (app->priv->manager);
+ g_object_unref (app->priv->prefs);
+
+ G_OBJECT_CLASS (logview_app_parent_class)->finalize (obj);
+}
+
+static void
+logview_app_class_init (LogviewAppClass *klass)
+{
+ GObjectClass *oclass = G_OBJECT_CLASS (klass);
+
+ oclass->finalize = do_finalize;
+
+ signals[APP_QUIT] =
+ g_signal_new ("app-quit",
+ G_OBJECT_CLASS_TYPE (oclass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (LogviewAppClass, app_quit),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_type_class_add_private (klass, sizeof (LogviewAppPrivate));
+}
+
+static void
+logview_app_init (LogviewApp *self)
+{
+ LogviewAppPrivate *priv = self->priv = GET_PRIVATE (self);
+
+ priv->prefs = logview_prefs_get ();
+ priv->manager = logview_manager_get ();
+}
+
+LogviewApp*
+logview_app_get (void)
+{
+ if (!app_singleton) {
+ app_singleton = g_object_new (LOGVIEW_TYPE_APP, NULL);
+
+ if (!logview_app_set_window (app_singleton)) {
+ g_object_unref (app_singleton);
+ app_singleton = NULL;
+ }
+ }
+
+ return app_singleton;
+}
+
+void
+logview_app_initialize (LogviewApp *app, char **log_files)
+{
+ LogviewAppPrivate *priv;
+
+ g_assert (LOGVIEW_IS_APP (app));
+
+ priv = app->priv;
+
+ /* open regular logs and add each log passed as a parameter */
+
+ if (log_files == NULL) {
+ char *active_log;
+ GSList *logs;
+
+ active_log = logview_prefs_get_active_logfile (priv->prefs);
+ logs = logview_prefs_get_stored_logfiles (priv->prefs);
+
+ if (!logs) {
+ logview_app_first_time_initialize (app);
+ } else {
+ logview_manager_add_logs_from_name_list (priv->manager,
+ logs, active_log);
+
+ g_free (active_log);
+ g_slist_foreach (logs, (GFunc) g_free, NULL);
+ g_slist_free (logs);
+ }
+ } else {
+ logview_manager_add_logs_from_names (priv->manager, log_files);
+ }
+
+ gtk_widget_show (GTK_WIDGET (priv->window));
+}
+
+void
+logview_app_add_error (LogviewApp *app,
+ const char *file_path,
+ const char *secondary)
+{
+ LogviewWindow *window;
+ char *primary;
+
+ g_assert (LOGVIEW_IS_APP (app));
+
+ window = app->priv->window;
+ primary = g_strdup_printf (_("Impossible to open the file %s"), file_path);
+
+ logview_window_add_error (window, primary, secondary);
+
+ g_free (primary);
+}
+
+void
+logview_app_add_errors (LogviewApp *app,
+ GPtrArray *errors)
+{
+ LogviewWindow *window;
+
+ g_assert (LOGVIEW_IS_APP (app));
+
+ window = app->priv->window;
+
+ if (errors->len == 0) {
+ return;
+ } else if (errors->len == 1) {
+ char **err;
+
+ err = g_ptr_array_index (errors, 0);
+ logview_window_add_error (window, err[0], err[1]);
+ } else {
+ logview_window_add_errors (window, errors);
+ }
+}