1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /* logview-app.c - logview application singleton
4 * Copyright (C) 2008 Cosimo Cecchi <cosimoc@gnome.org>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "logview-app.h"
29 #include "logview-manager.h"
30 #include "logview-window.h"
31 #include "logview-prefs.h"
33 #include <glib/gi18n.h>
35 struct _LogviewAppPrivate
{
37 LogviewManager
*manager
;
38 LogviewWindow
*window
;
46 static guint signals
[LAST_SIGNAL
] = { 0 };
48 static LogviewApp
*app_singleton
= NULL
;
50 G_DEFINE_TYPE (LogviewApp
, logview_app
, G_TYPE_OBJECT
);
52 #define GET_PRIVATE(o) \
53 (G_TYPE_INSTANCE_GET_PRIVATE ((o), LOGVIEW_TYPE_APP, LogviewAppPrivate))
56 main_window_delete_cb (GtkWidget
*widget
,
60 LogviewApp
*app
= user_data
;
62 g_signal_emit (app
, signals
[APP_QUIT
], 0, NULL
);
68 logview_app_set_window (LogviewApp
*app
)
70 LogviewWindow
*window
;
71 gboolean retval
= FALSE
;
73 window
= LOGVIEW_WINDOW (logview_window_new ());
76 app
->priv
->window
= window
;
77 g_signal_connect (window
, "delete-event",
78 G_CALLBACK (main_window_delete_cb
), app
);
82 gtk_window_set_default_icon_name ("logview");
92 /* TODO: ideally we should parse configuration files in /etc/logrotate.conf
93 * and all the files in /etc/logrotate.d/ and group all the logs referring
94 * to the same entry under a category. Right now, we just do some
95 * parsing instead, and fill with quasi-sensible defaults.
98 /* adapted from sysklogd sources */
105 GSList
*logfiles
= NULL
;
107 if ((cf
= fopen ("/etc/syslog.conf", "r")) == NULL
) {
112 while (fgets (cline
, sizeof (cbuf
) - (cline
- cbuf
), cf
) != NULL
) {
116 for (p
= cline
; g_ascii_isspace (*p
); ++p
);
117 if (*p
== '\0' || *p
== '#' || *p
== '\n')
120 list
= g_strsplit_set (p
, ", -\t()\n", 0);
122 for (i
= 0; list
[i
]; ++i
) {
123 if (*list
[i
] == '/' &&
124 g_slist_find_custom (logfiles
, list
[i
],
125 (GCompareFunc
) g_ascii_strcasecmp
) == NULL
)
127 logfiles
= g_slist_insert (logfiles
,
128 g_strdup (list
[i
]), 0);
141 enumerate_job_finish (EnumerateJob
*job
)
143 GSList
*files
= job
->logs
;
144 LogviewApp
*app
= job
->app
;
146 logview_manager_add_logs_from_name_list (app
->priv
->manager
, files
, files
->data
);
148 g_slist_foreach (files
, (GFunc
) g_free
, NULL
);
149 g_slist_free (files
);
151 g_object_unref (job
->app
);
152 g_slice_free (EnumerateJob
, job
);
156 enumerate_next_files_async_cb (GObject
*source
,
160 EnumerateJob
*job
= user_data
;
161 GList
*enumerated_files
, *l
;
164 const char *content_type
, *name
;
165 char *parse_string
, *container_path
;
169 enumerated_files
= g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source
),
171 if (!enumerated_files
) {
172 enumerate_job_finish (job
);
177 container
= g_file_enumerator_get_container (G_FILE_ENUMERATOR (source
));
178 container_path
= g_file_get_path (container
);
180 /* TODO: we don't support grouping rotated logs yet, skip gzipped files
181 * and those which name contains a formatted date.
183 for (l
= enumerated_files
; l
; l
= l
->next
) {
185 type
= g_file_info_get_file_type (info
);
186 content_type
= g_file_info_get_content_type (info
);
187 name
= g_file_info_get_name (info
);
189 if (!g_file_info_get_attribute_boolean (info
, "access::can-read")) {
190 g_object_unref (info
);
194 if (type
!= (G_FILE_TYPE_REGULAR
|| G_FILE_TYPE_SYMBOLIC_LINK
) ||
195 !g_content_type_is_a (content_type
, "text/plain"))
197 g_object_unref (info
);
201 if (g_content_type_is_a (content_type
, "application/x-gzip")) {
202 g_object_unref (info
);
206 if (g_regex_match_simple ("\\d{8}$", name
, 0, 0)) {
207 g_object_unref (info
);
211 parse_string
= g_build_filename (container_path
, name
, NULL
);
213 if (g_slist_find_custom (logs
, parse_string
, (GCompareFunc
) g_ascii_strcasecmp
) == NULL
) {
214 logs
= g_slist_append (logs
, parse_string
);
216 g_free (parse_string
);
219 g_object_unref (info
);
223 g_list_free (enumerated_files
);
224 g_object_unref (container
);
225 g_free (container_path
);
229 enumerate_job_finish (job
);
233 enumerate_children_async_cb (GObject
*source
,
237 EnumerateJob
*job
= user_data
;
238 GFileEnumerator
*enumerator
;
240 enumerator
= g_file_enumerate_children_finish (G_FILE (source
),
243 enumerate_job_finish (job
);
247 g_file_enumerator_next_files_async (enumerator
, G_MAXINT
,
249 NULL
, enumerate_next_files_async_cb
, job
);
253 logview_app_first_time_initialize (LogviewApp
*app
)
259 /* let's add all accessible files in /var/log and those mentioned
260 * in /etc/syslog.conf.
263 logs
= parse_syslog ();
265 job
= g_slice_new0 (EnumerateJob
);
266 job
->app
= g_object_ref (app
);
269 log_dir
= g_file_new_for_path ("/var/log/");
270 g_file_enumerate_children_async (log_dir
,
271 "standard::*,access::can-read", 0,
272 G_PRIORITY_DEFAULT
, NULL
,
273 enumerate_children_async_cb
, job
);
275 g_object_unref (log_dir
);
279 do_finalize (GObject
*obj
)
281 LogviewApp
*app
= LOGVIEW_APP (obj
);
283 g_object_unref (app
->priv
->manager
);
284 g_object_unref (app
->priv
->prefs
);
286 G_OBJECT_CLASS (logview_app_parent_class
)->finalize (obj
);
290 logview_app_class_init (LogviewAppClass
*klass
)
292 GObjectClass
*oclass
= G_OBJECT_CLASS (klass
);
294 oclass
->finalize
= do_finalize
;
297 g_signal_new ("app-quit",
298 G_OBJECT_CLASS_TYPE (oclass
),
300 G_STRUCT_OFFSET (LogviewAppClass
, app_quit
),
302 g_cclosure_marshal_VOID__VOID
,
305 g_type_class_add_private (klass
, sizeof (LogviewAppPrivate
));
309 logview_app_init (LogviewApp
*self
)
311 LogviewAppPrivate
*priv
= self
->priv
= GET_PRIVATE (self
);
313 priv
->prefs
= logview_prefs_get ();
314 priv
->manager
= logview_manager_get ();
318 logview_app_get (void)
320 if (!app_singleton
) {
321 app_singleton
= g_object_new (LOGVIEW_TYPE_APP
, NULL
);
323 if (!logview_app_set_window (app_singleton
)) {
324 g_object_unref (app_singleton
);
325 app_singleton
= NULL
;
329 return app_singleton
;
333 logview_app_initialize (LogviewApp
*app
, char **log_files
)
335 LogviewAppPrivate
*priv
;
337 g_assert (LOGVIEW_IS_APP (app
));
341 /* open regular logs and add each log passed as a parameter */
343 if (log_files
== NULL
) {
347 active_log
= logview_prefs_get_active_logfile (priv
->prefs
);
348 logs
= logview_prefs_get_stored_logfiles (priv
->prefs
);
351 logview_app_first_time_initialize (app
);
353 logview_manager_add_logs_from_names (priv
->manager
,
360 logview_manager_add_logs_from_names (priv
->manager
, log_files
, NULL
);
363 gtk_widget_show (GTK_WIDGET (priv
->window
));
367 logview_app_add_error (LogviewApp
*app
,
368 const char *file_path
,
369 const char *secondary
)
371 LogviewWindow
*window
;
374 g_assert (LOGVIEW_IS_APP (app
));
376 window
= app
->priv
->window
;
377 primary
= g_strdup_printf (_("Impossible to open the file %s"), file_path
);
379 logview_window_add_error (window
, primary
, secondary
);
385 logview_app_add_errors (LogviewApp
*app
,
388 LogviewWindow
*window
;
390 g_assert (LOGVIEW_IS_APP (app
));
392 window
= app
->priv
->window
;
394 if (errors
->len
== 0) {
396 } else if (errors
->len
== 1) {
399 err
= g_ptr_array_index (errors
, 0);
400 logview_window_add_error (window
, err
[0], err
[1]);
402 logview_window_add_errors (window
, errors
);