summaryrefslogtreecommitdiffstats
path: root/seaudit/toplevel.c
diff options
context:
space:
mode:
Diffstat (limited to 'seaudit/toplevel.c')
-rw-r--r--seaudit/toplevel.c1179
1 files changed, 1179 insertions, 0 deletions
diff --git a/seaudit/toplevel.c b/seaudit/toplevel.c
new file mode 100644
index 0000000..d901a99
--- /dev/null
+++ b/seaudit/toplevel.c
@@ -0,0 +1,1179 @@
+/**
+ * @file
+ * Implementation for the main toplevel window.
+ *
+ * @author Jeremy A. Mowery jmowery@tresys.com
+ * @author Jason Tang jtang@tresys.com
+ * @author Jeremy Solt jsolt@tresys.com
+ *
+ * Copyright (C) 2003-2007 Tresys Technology, LLC
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <config.h>
+
+#include "message_view.h"
+#include "open_policy_window.h"
+#include "policy_view.h"
+#include "preferences_view.h"
+#include "report_window.h"
+#include "toplevel.h"
+#include "utilgui.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <apol/util.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <seaudit/parse.h>
+
+struct toplevel
+{
+ seaudit_t *s;
+ policy_view_t *pv;
+ progress_t *progress;
+ /** vector of message_view_t that are in the toplevel's notebook */
+ apol_vector_t *views;
+ GladeXML *xml;
+ /** filename for glade file */
+ char *xml_filename;
+ /** toplevel window widget */
+ GtkWindow *w;
+ GtkNotebook *notebook;
+ /** non-zero if the log file should be polled for changes */
+ int do_monitor_log;
+ /** event id for the monitor callback */
+ guint monitor_id;
+ /** serial number for models created, such that new models
+ * will be named Untitled <number> */
+ int next_model_number;
+ /** filename for most recently opened view */
+ char *view_filename;
+};
+
+/**
+ * Given a view, return its index within the toplevel notebook pages.
+ *
+ * @param top Toplevel containing the notebook.
+ * @param view View to look up.
+ *
+ * @return Index of the view (zero-indexed), or -1 if not found.
+ */
+static gint toplevel_notebook_find_view(toplevel_t * top, message_view_t * view)
+{
+ gint num_pages = gtk_notebook_get_n_pages(top->notebook);
+ while (num_pages >= 1) {
+ GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, num_pages - 1);
+ GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child);
+ message_view_t *v = g_object_get_data(G_OBJECT(tab), "view-object");
+ if (v == view) {
+ return num_pages - 1;
+ }
+ num_pages--;
+ }
+ return -1;
+}
+
+/**
+ * Return the view on the page that is currently raised, or NULL if
+ * there are no views.
+ */
+static message_view_t *toplevel_get_current_view(toplevel_t * top)
+{
+ gint current = gtk_notebook_get_current_page(top->notebook);
+ if (current >= 0) {
+ GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, current);
+ GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child);
+ return g_object_get_data(G_OBJECT(tab), "view-object");
+ }
+ return NULL;
+}
+
+static void toplevel_on_notebook_switch_page(GtkNotebook * notebook __attribute__ ((unused)), GtkNotebookPage * page
+ __attribute__ ((unused)), guint pagenum __attribute__ ((unused)), toplevel_t * top)
+{
+ toplevel_update_selection_menu_item(top);
+ toplevel_update_status_bar(top);
+}
+
+/**
+ * Callback invoked when a tab close button is clicked.
+ */
+static void toplevel_on_tab_close(GtkButton * button, toplevel_t * top)
+{
+ /* disallow the close if this is the last tab */
+ if (top->views == NULL || apol_vector_get_size(top->views) <= 1) {
+ return;
+ } else {
+ message_view_t *view = g_object_get_data(G_OBJECT(button), "view-object");
+ gint idx = toplevel_notebook_find_view(top, view);
+ size_t i;
+ assert(idx >= 0);
+ gtk_notebook_remove_page(top->notebook, idx);
+ apol_vector_get_index(top->views, view, NULL, NULL, &i);
+ message_view_destroy(&view);
+ apol_vector_remove(top->views, i);
+ }
+}
+
+/**
+ * Create a new view associated with the given model, then create a
+ * tab to place that view. The newly created tab will then be raised.
+ *
+ * @param top Toplevel containing notebook to which add the view and tab.
+ * @param model Model from which to create a view.
+ * @param filename Initial filename for the view.
+ */
+static void toplevel_add_new_view(toplevel_t * top, seaudit_model_t * model, const char *filename)
+{
+ message_view_t *view;
+ GtkWidget *tab, *button, *label, *image;
+ gint idx;
+ if ((view = message_view_create(top, model, filename)) == NULL) {
+ return;
+ }
+ if (apol_vector_append(top->views, view) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ message_view_destroy(&view);
+ return;
+ }
+ tab = gtk_hbox_new(FALSE, 5);
+ g_object_set_data(G_OBJECT(tab), "view-object", view);
+ button = gtk_button_new();
+ g_object_set_data(G_OBJECT(button), "view-object", view);
+ image = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
+ gtk_container_add(GTK_CONTAINER(button), image);
+ gtk_widget_set_size_request(image, 8, 8);
+ g_signal_connect(G_OBJECT(button), "pressed", G_CALLBACK(toplevel_on_tab_close), top);
+ label = gtk_label_new(seaudit_model_get_name(model));
+ g_object_set_data(G_OBJECT(tab), "label", label);
+ gtk_box_pack_start(GTK_BOX(tab), label, TRUE, TRUE, 5);
+ gtk_box_pack_end(GTK_BOX(tab), button, FALSE, FALSE, 5);
+ gtk_widget_show(label);
+ gtk_widget_show(button);
+ gtk_widget_show(image);
+ idx = gtk_notebook_append_page(top->notebook, message_view_get_view(view), tab);
+ gtk_notebook_set_current_page(top->notebook, idx);
+}
+
+/**
+ * Create a new model for the currently loaded log file (which could
+ * be NULL), then create a view that watches that model.
+ */
+static void toplevel_add_new_model(toplevel_t * top)
+{
+ seaudit_log_t *log = seaudit_get_log(top->s);
+ char *model_name = NULL;
+ seaudit_model_t *model = NULL;
+ if (asprintf(&model_name, "Untitled %d", top->next_model_number) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ return;
+ }
+ model = seaudit_model_create(model_name, log);
+ free(model_name);
+ if (model == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ return;
+ } else {
+ top->next_model_number++;
+ toplevel_add_new_view(top, model, NULL);
+ }
+}
+
+/**
+ * Callback whenever an item from the recent logs submenu is activated.
+ */
+static void toplevel_on_open_recent_log_activate(GtkWidget * widget, gpointer user_data)
+{
+ GtkWidget *label = gtk_bin_get_child(GTK_BIN(widget));
+ const char *path = gtk_label_get_text(GTK_LABEL(label));
+ toplevel_t *top = (toplevel_t *) user_data;
+ toplevel_open_log(top, path);
+}
+
+/**
+ * Update the entries within recent logs submenu to match those in the
+ * preferences object.
+ */
+static void toplevel_set_recent_logs_submenu(toplevel_t * top)
+{
+ GtkMenuItem *recent = GTK_MENU_ITEM(glade_xml_get_widget(top->xml, "OpenRecentLog"));
+ apol_vector_t *paths = preferences_get_recent_logs(toplevel_get_prefs(top));
+ GtkWidget *submenu, *submenu_item;
+ size_t i;
+
+ gtk_menu_item_remove_submenu(recent);
+ submenu = gtk_menu_new();
+ for (i = 0; i < apol_vector_get_size(paths); i++) {
+ char *path = (char *)apol_vector_get_element(paths, i);
+ submenu_item = gtk_menu_item_new_with_label(path);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(submenu), submenu_item);
+ gtk_widget_show(submenu_item);
+ g_signal_connect(G_OBJECT(submenu_item), "activate", G_CALLBACK(toplevel_on_open_recent_log_activate), top);
+ }
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent), submenu);
+}
+
+/**
+ * Callback whenever an item from the recent policies submenu is
+ * activated.
+ */
+static void toplevel_on_open_recent_policy_activate(GtkMenuItem * menuitem, gpointer user_data)
+{
+ apol_policy_path_t *path = g_object_get_data(G_OBJECT(menuitem), "path");
+ toplevel_t *top = (toplevel_t *) user_data;
+ apol_policy_path_t *dup_path = apol_policy_path_create_from_policy_path(path);
+ if (dup_path == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ return;
+ }
+ toplevel_open_policy(top, dup_path);
+}
+
+/**
+ * Update the entries within recent policies submenu to match those in
+ * the preferences object.
+ */
+static void toplevel_set_recent_policies_submenu(toplevel_t * top)
+{
+ GtkMenuItem *recent = GTK_MENU_ITEM(glade_xml_get_widget(top->xml, "OpenRecentPolicy"));
+ apol_vector_t *paths = preferences_get_recent_policies(toplevel_get_prefs(top));
+ GtkWidget *submenu, *submenu_item;
+ size_t i;
+
+ GtkTooltipsData *tips_data = gtk_tooltips_data_get(GTK_WIDGET(recent));
+ assert(tips_data != NULL);
+ GtkTooltips *tooltips = tips_data->tooltips;
+
+ gtk_menu_item_remove_submenu(recent);
+ submenu = gtk_menu_new();
+ for (i = 0; i < apol_vector_get_size(paths); i++) {
+ apol_policy_path_t *path = apol_vector_get_element(paths, i);
+ char *menu_label = NULL;
+ const char *primary_path = apol_policy_path_get_primary(path);
+ GString *tip = g_string_new(NULL);
+ if ((menu_label = util_policy_path_to_string(path)) == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ g_string_free(tip, TRUE);
+ break;
+ }
+ submenu_item = gtk_menu_item_new_with_label(menu_label);
+ free(menu_label);
+ if (apol_policy_path_get_type(path) == APOL_POLICY_PATH_TYPE_MONOLITHIC) {
+ g_string_append_printf(tip, "monolithic policy: %s", primary_path);
+ } else {
+ char *s = NULL;
+ const apol_vector_t *modules = apol_policy_path_get_modules(path);
+ size_t num_modules = apol_vector_get_size(modules);
+ g_string_append_printf(tip, "base policy: %s", primary_path);
+ if (num_modules > 0) {
+ if ((s = apol_str_join(modules, "\n ")) == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ break;
+ }
+ g_string_append_printf(tip, "\n %s", s);
+ free(s);
+ }
+ }
+ gtk_tooltips_set_tip(tooltips, GTK_WIDGET(submenu_item), tip->str, "");
+ g_string_free(tip, TRUE);
+ g_object_set_data(G_OBJECT(submenu_item), "path", path);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(submenu), submenu_item);
+ gtk_widget_show(submenu_item);
+ g_signal_connect(G_OBJECT(submenu_item), "activate", G_CALLBACK(toplevel_on_open_recent_policy_activate), top);
+ }
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent), submenu);
+}
+
+/**
+ * Enable/disable all items (menus and buttons) that depend upon if a
+ * log is loaded.
+ *
+ * @param top Toplevel object containing menu items.
+ * @param TRUE to enable items, FALSE to disable.
+ */
+static void toplevel_enable_log_items(toplevel_t * top, gboolean sens)
+{
+ static const char *items[] = {
+ "NewView", "OpenView", "SaveView", "SaveViewAs", "ModifyView",
+ "ExportAll", "ExportSelected", "ViewMessage",
+ "CreateReport", "MonitorLog", "ClearView",
+ "ModifyViewButton", "MonitorLogButton", "ClearViewButton",
+ NULL
+ };
+ size_t i;
+ const char *s;
+ for (i = 0, s = items[0]; s != NULL; s = items[++i]) {
+ GtkWidget *w = glade_xml_get_widget(top->xml, s);
+ assert(w != NULL);
+ gtk_widget_set_sensitive(w, sens);
+ }
+}
+
+/**
+ * Enable/disable all items (menus and buttons) that depend upon if a
+ * policy is loaded.
+ *
+ * @param top Toplevel object containing widgets.
+ * @param TRUE to enable items, FALSE to disable.
+ */
+static void toplevel_enable_policy_items(toplevel_t * top, gboolean sens)
+{
+ static const char *items[] = {
+ "FindTERules", "FindTERulesButton",
+ NULL
+ };
+ size_t i;
+ const char *s;
+ for (i = 0, s = items[0]; s != NULL; s = items[++i]) {
+ GtkWidget *w = glade_xml_get_widget(top->xml, s);
+ assert(w != NULL);
+ gtk_widget_set_sensitive(w, sens);
+ }
+}
+
+/**
+ * Update the toplevel's title bar to list the log and policy files
+ * opened.
+ *
+ * @param top Toplevel to modify.
+ */
+static void toplevel_update_title_bar(toplevel_t * top)
+{
+ char *log_path = seaudit_get_log_path(top->s);
+ apol_policy_path_t *policy_path = seaudit_get_policy_path(top->s);
+ char *policy_type_str = "Policy";
+ const char *primary_path;
+ char *s;
+
+ if (log_path == NULL) {
+ log_path = "No Log";
+ }
+ if (policy_path == NULL) {
+ primary_path = "No Policy";
+ } else {
+ if (apol_policy_path_get_type(policy_path) == APOL_POLICY_PATH_TYPE_MODULAR) {
+ policy_type_str = "Base";
+ }
+ primary_path = apol_policy_path_get_primary(policy_path);
+ }
+ if (asprintf(&s, "seaudit - [Log file: %s] [%s file: %s]", log_path, policy_type_str, primary_path) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ return;
+ }
+ gtk_window_set_title(top->w, s);
+ free(s);
+}
+
+/**
+ * Initialize the application icons for the program. These icons are
+ * the ones shown by the window manager within title bars and pagers.
+ * The last icon listed in the array will be displayed in the About
+ * dialog.
+ *
+ * @param top Toplevel whose icon to set. All child windows will
+ * inherit these icons.
+ */
+static void init_icons(toplevel_t * top)
+{
+ static const char *icon_names[] = { "seaudit-small.png", "seaudit.png" };
+ GdkPixbuf *icon;
+ char *path;
+ GList *icon_list = NULL;
+ size_t i;
+ for (i = 0; i < sizeof(icon_names) / sizeof(icon_names[0]); i++) {
+ if ((path = apol_file_find_path(icon_names[i])) == NULL) {
+ continue;
+ }
+ icon = gdk_pixbuf_new_from_file(path, NULL);
+ free(path);
+ if (icon == NULL) {
+ continue;
+ }
+ icon_list = g_list_append(icon_list, icon);
+ }
+ gtk_window_set_default_icon_list(icon_list);
+ gtk_window_set_icon_list(top->w, icon_list);
+}
+
+static void message_view_free(void *elem)
+{
+ message_view_t *view = elem;
+ message_view_destroy(&view);
+}
+
+toplevel_t *toplevel_create(seaudit_t * s)
+{
+ toplevel_t *top;
+ GtkWidget *vbox;
+ int error = 0;
+
+ if ((top = calloc(1, sizeof(*top))) == NULL || (top->views = apol_vector_create(message_view_free)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ top->s = s;
+ top->next_model_number = 1;
+
+ if ((top->xml_filename = apol_file_find_path("seaudit.glade")) == NULL ||
+ (top->xml = glade_xml_new(top->xml_filename, "TopLevel", NULL)) == NULL) {
+ fprintf(stderr, "Could not open seaudit.glade.\n");
+ error = EIO;
+ goto cleanup;
+ }
+ top->w = GTK_WINDOW(glade_xml_get_widget(top->xml, "TopLevel"));
+ g_object_set_data(G_OBJECT(top->w), "toplevel", top);
+ init_icons(top);
+ top->notebook = GTK_NOTEBOOK(gtk_notebook_new());
+ g_signal_connect_after(G_OBJECT(top->notebook), "switch-page", G_CALLBACK(toplevel_on_notebook_switch_page), top);
+ vbox = glade_xml_get_widget(top->xml, "NotebookVBox");
+ gtk_container_add(GTK_CONTAINER(vbox), GTK_WIDGET(top->notebook));
+ gtk_widget_show(GTK_WIDGET(top->notebook));
+ gtk_widget_show(GTK_WIDGET(top->w));
+ toplevel_set_recent_logs_submenu(top);
+ toplevel_set_recent_policies_submenu(top);
+
+ glade_xml_signal_autoconnect(top->xml);
+
+ /* create initial blank tab for the notebook */
+ toplevel_add_new_model(top);
+
+ /* initialize sub-windows, now that glade XML file has been
+ * read */
+ if ((top->pv = policy_view_create(top)) == NULL || (top->progress = progress_create(top)) == NULL) {
+ error = errno;
+ goto cleanup;
+ }
+ cleanup:
+ if (error != 0) {
+ toplevel_destroy(&top);
+ errno = error;
+ return NULL;
+ }
+ return top;
+}
+
+void toplevel_destroy(toplevel_t ** top)
+{
+ if (top != NULL && *top != NULL) {
+ if ((*top)->monitor_id > 0) {
+ g_source_remove((*top)->monitor_id);
+ }
+ policy_view_destroy(&(*top)->pv);
+ apol_vector_destroy(&(*top)->views);
+ free((*top)->xml_filename);
+ g_free((*top)->view_filename);
+ progress_destroy(&(*top)->progress);
+ if ((*top)->w != NULL) {
+ gtk_widget_destroy(GTK_WIDGET((*top)->w));
+ }
+ free(*top);
+ *top = NULL;
+ }
+}
+
+struct log_run_datum
+{
+ toplevel_t *top;
+ FILE *file;
+ const char *filename;
+ seaudit_log_t *log;
+ int result;
+};
+
+/**
+ * Update the seaudit log, then refresh all views as necessary. Note
+ * that this only works in a single-threaded environment; otherwise
+ * there are two possible race conditions:
+ * - monitor is disabled while this function is being executed
+ * - a new log file is loaded while the function is being executed
+ *
+ * But what happens if this function is scheduled and then a new log
+ * is opened? In toplevel_open_log(), do_monitor_log is temporarily
+ * disabled because that function is threaded. It is then re-enabled
+ * afterwards.
+ *
+ * To make this function fully thread-safe requires making this entire
+ * function synchronized, and then employ locking every time
+ * do_monitor_log and monitor_id are set.
+ */
+static gboolean toplevel_monitor_log_timer(gpointer data)
+{
+ toplevel_t *top = (toplevel_t *) data;
+ if (top->do_monitor_log) {
+ int retval;
+ gint i = gtk_notebook_get_n_pages(top->notebook) - 1;
+ uint delay;
+ retval = seaudit_parse_log(top->s);
+ if (retval < 0) {
+ GtkCheckMenuItem *w;
+ toplevel_ERR(top, "Error while monitoring log: %s", strerror(errno));
+ w = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(top->xml, "MonitorLog"));
+ top->monitor_id = 0;
+ gtk_check_menu_item_set_active(w, 0);
+ return FALSE;
+ }
+ while (i >= 0) {
+ GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, i);
+ GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child);
+ message_view_t *v = g_object_get_data(G_OBJECT(tab), "view-object");
+ message_view_update_rows(v);
+ i--;
+ }
+
+ /* reschedule another timer callback */
+ delay = preferences_get_real_time_interval(toplevel_get_prefs(top));
+ top->monitor_id = g_timeout_add(delay, toplevel_monitor_log_timer, top);
+ } else {
+ top->monitor_id = 0;
+ }
+ return FALSE;
+}
+
+/**
+ * Enable or disable the log monitoring feature. While enabled, the
+ * log file will be periodically polled; new lines will be parsed and
+ * inserted into the seaudit log object. All models and their views
+ * will then be notified of the changes.
+ *
+ * @param top Toplevel object whose widgets to update.
+ */
+static void toplevel_monitor_log(toplevel_t * top)
+{
+ GtkLabel *label = GTK_LABEL(glade_xml_get_widget(top->xml, "MonitorLogLabel"));
+ assert(label != NULL);
+ if (top->do_monitor_log) {
+ gtk_label_set_markup(label, "Monitor Status: <span foreground=\"green\">ON</span>");
+ if (top->monitor_id == 0) {
+ uint delay = preferences_get_real_time_interval(toplevel_get_prefs(top));
+ top->monitor_id = g_timeout_add(delay, toplevel_monitor_log_timer, top);
+ }
+ } else {
+ if (top->monitor_id > 0) {
+ g_source_remove(top->monitor_id);
+ top->monitor_id = 0;
+ }
+ gtk_label_set_markup(label, "Monitor Status: <span foreground=\"red\">OFF</span>");
+ }
+}
+
+/**
+ * Thread that loads and parses a log file. It will write to
+ * progress_seaudit_handle_func() its status during the load. Note
+ * that the file handle is not closed upon completion; it is left open
+ * so that subsequent calls to seaudit_log_parse(), such as
+ * forreal-time monitoring.
+ *
+ * @param data Pointer to a struct log_run_datum, for control
+ * information.
+ */
+static gpointer toplevel_open_log_runner(gpointer data)
+{
+ struct log_run_datum *run = (struct log_run_datum *)data;
+ progress_update(run->top->progress, "Parsing %s", run->filename);
+ if ((run->file = fopen(run->filename, "r")) == NULL) {
+ progress_update(run->top->progress, "Could not open %s for reading.", run->filename);
+ run->result = -1;
+ goto cleanup;
+ }
+ if ((run->log = seaudit_log_create(progress_seaudit_handle_func, run->top->progress)) == NULL) {
+ progress_update(run->top->progress, "%s", strerror(errno));
+ run->result = -1;
+ goto cleanup;
+ }
+ run->result = seaudit_log_parse(run->log, run->file);
+ cleanup:
+ if (run->result < 0) {
+ if (run->file != NULL) {
+ fclose(run->file);
+ }
+ run->file = NULL;
+ seaudit_log_destroy(&run->log);
+ progress_abort(run->top->progress, NULL);
+ } else if (run->result > 0) {
+ progress_warn(run->top->progress, NULL);
+ } else {
+ progress_done(run->top->progress);
+ }
+ return NULL;
+}
+
+/**
+ * Destroy all views and their notebook tabs.
+ */
+static void toplevel_destroy_views(toplevel_t * top)
+{
+ gint num_pages = gtk_notebook_get_n_pages(top->notebook);
+ while (num_pages >= 1) {
+ message_view_t *view = apol_vector_get_element(top->views, num_pages - 1);
+ gtk_notebook_remove_page(top->notebook, num_pages - 1);
+ message_view_destroy(&view);
+ apol_vector_remove(top->views, num_pages - 1);
+ num_pages--;
+ }
+}
+
+void toplevel_open_log(toplevel_t * top, const char *filename)
+{
+ struct log_run_datum run = { top, NULL, filename, NULL, 0 };
+ int was_monitor_running;
+ GtkCheckMenuItem *w;
+
+ /* disable monitoring during the threaded part of this code */
+ was_monitor_running = top->do_monitor_log;
+ top->do_monitor_log = 0;
+ toplevel_monitor_log(top);
+
+ util_cursor_wait(GTK_WIDGET(top->w));
+ progress_show(top->progress, "Opening Log");
+ g_thread_create(toplevel_open_log_runner, &run, FALSE, NULL);
+ progress_wait(top->progress);
+ progress_hide(top->progress);
+ util_cursor_clear(GTK_WIDGET(top->w));
+
+ if (run.result < 0) {
+ top->do_monitor_log = was_monitor_running;
+ toplevel_monitor_log(top);
+ return;
+ }
+
+ toplevel_destroy_views(top);
+ top->next_model_number = 1;
+ seaudit_set_log(top->s, run.log, run.file, filename);
+ toplevel_set_recent_logs_submenu(top);
+ toplevel_enable_log_items(top, TRUE);
+ toplevel_add_new_model(top);
+ toplevel_update_title_bar(top);
+ toplevel_update_status_bar(top);
+ toplevel_update_selection_menu_item(top);
+ top->do_monitor_log = preferences_get_real_time_at_startup(toplevel_get_prefs(top));
+
+ w = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(top->xml, "MonitorLog"));
+
+ gtk_check_menu_item_set_active(w, top->do_monitor_log);
+ /* call this again because the check item could have already
+ * been active, thus its handler would not run */
+ toplevel_monitor_log(top);
+}
+
+struct policy_run_datum
+{
+ toplevel_t *top;
+ apol_policy_path_t *path;
+ apol_policy_t *policy;
+ int result;
+};
+
+/**
+ * Thread that loads and parses a policy file. It will write to
+ * progress_seaudit_handle_func() its status during the load.
+ *
+ * @param data Pointer to a struct policy_run_datum, for control
+ * information.
+ */
+static gpointer toplevel_open_policy_runner(gpointer data)
+{
+ struct policy_run_datum *run = (struct policy_run_datum *)data;
+ progress_update(run->top->progress, "Opening policy.");
+ run->policy =
+ apol_policy_create_from_policy_path(run->path, QPOL_POLICY_OPTION_NO_NEVERALLOWS, progress_apol_handle_func,
+ run->top->progress);
+ if (run->policy == NULL) {
+ run->result = -1;
+ progress_abort(run->top->progress, NULL);
+ return NULL;
+ }
+ run->result = 0;
+ progress_done(run->top->progress);
+ return NULL;
+}
+
+int toplevel_open_policy(toplevel_t * top, apol_policy_path_t * path)
+{
+ struct policy_run_datum run = { top, path, NULL, 0 };
+
+ util_cursor_wait(GTK_WIDGET(top->w));
+ progress_show(top->progress, apol_policy_path_get_primary(path));
+ g_thread_create(toplevel_open_policy_runner, &run, FALSE, NULL);
+ progress_wait(top->progress);
+ progress_hide(top->progress);
+ util_cursor_clear(GTK_WIDGET(top->w));
+ if (run.result < 0) {
+ apol_policy_path_destroy(&path);
+ return run.result;
+ }
+ seaudit_set_policy(top->s, run.policy, path);
+ toplevel_set_recent_policies_submenu(top);
+ toplevel_enable_policy_items(top, TRUE);
+ toplevel_update_title_bar(top);
+ toplevel_update_status_bar(top);
+ policy_view_update(top->pv, path);
+ return 0;
+}
+
+void toplevel_update_status_bar(toplevel_t * top)
+{
+ apol_policy_t *policy = seaudit_get_policy(top->s);
+ GtkLabel *policy_version = (GtkLabel *) glade_xml_get_widget(top->xml, "PolicyVersionLabel");
+ GtkLabel *log_num = (GtkLabel *) glade_xml_get_widget(top->xml, "LogNumLabel");
+ GtkLabel *log_dates = (GtkLabel *) glade_xml_get_widget(top->xml, "LogDateLabel");
+ seaudit_log_t *log = toplevel_get_log(top);
+
+ if (policy == NULL) {
+ gtk_label_set_text(policy_version, "Policy: No policy");
+ } else {
+ char *policy_str = apol_policy_get_version_type_mls_str(policy);
+ if (policy_str == NULL) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ } else {
+ char *s;
+ if (asprintf(&s, "Policy: %s", policy_str) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ } else {
+ gtk_label_set_text(policy_version, s);
+ free(s);
+ }
+ free(policy_str);
+ }
+ }
+
+ if (log == NULL) {
+ gtk_label_set_text(log_num, "Log Messages: No log");
+ gtk_label_set_text(log_dates, "Dates: No log");
+ } else {
+ message_view_t *view = toplevel_get_current_view(top);
+ size_t num_messages = seaudit_get_num_log_messages(top->s);
+ size_t num_view_messages;
+ const struct tm *first = seaudit_get_log_first(top->s);
+ const struct tm *last = seaudit_get_log_last(top->s);
+ assert(view != NULL);
+ num_view_messages = message_view_get_num_log_messages(view);
+ char *s, t1[256], t2[256];
+ if (asprintf(&s, "Log Messages: %zd/%zd", num_view_messages, num_messages) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ } else {
+ gtk_label_set_text(log_num, s);
+ free(s);
+ }
+ if (first == NULL || last == NULL) {
+ gtk_label_set_text(log_dates, "Dates: No messages");
+ } else {
+ strftime(t1, 256, "%b %d %H:%M:%S", first);
+ strftime(t2, 256, "%b %d %H:%M:%S", last);
+ if (asprintf(&s, "Dates: %s - %s", t1, t2) < 0) {
+ toplevel_ERR(top, "%s", strerror(errno));
+ } else {
+ gtk_label_set_text(log_dates, s);
+ free(s);
+ }
+ }
+ }
+}
+
+void toplevel_update_tabs(toplevel_t * top)
+{
+ gint i = gtk_notebook_get_n_pages(top->notebook) - 1;
+ while (i >= 0) {
+ GtkWidget *child = gtk_notebook_get_nth_page(top->notebook, i);
+ GtkWidget *tab = gtk_notebook_get_tab_label(top->notebook, child);
+ GtkWidget *label = g_object_get_data(G_OBJECT(tab), "label");
+ message_view_t *v = g_object_get_data(G_OBJECT(tab), "view-object");
+ seaudit_model_t *model = message_view_get_model(v);
+ const char *name = seaudit_model_get_name(model);
+ gtk_label_set_text(GTK_LABEL(label), name);
+ i--;
+ }
+}
+
+void toplevel_update_selection_menu_item(toplevel_t * top)
+{
+ static const char *items[] = {
+ "ExportSelected", "ViewMessage",
+ NULL
+ };
+ message_view_t *view = toplevel_get_current_view(top);
+ gboolean sens = FALSE;
+ size_t i;
+ const char *s;
+ if (view != NULL) {
+ sens = message_view_is_message_selected(view);
+ }
+ for (i = 0, s = items[0]; s != NULL; s = items[++i]) {
+ GtkWidget *w = glade_xml_get_widget(top->xml, s);
+ assert(s != NULL);
+ gtk_widget_set_sensitive(w, sens);
+ }
+}
+
+preferences_t *toplevel_get_prefs(toplevel_t * top)
+{
+ return seaudit_get_prefs(top->s);
+}
+
+seaudit_log_t *toplevel_get_log(toplevel_t * top)
+{
+ return seaudit_get_log(top->s);
+}
+
+apol_vector_t *toplevel_get_log_users(toplevel_t * top)
+{
+ return seaudit_get_log_users(top->s);
+}
+
+apol_vector_t *toplevel_get_log_roles(toplevel_t * top)
+{
+ return seaudit_get_log_roles(top->s);
+}
+
+apol_vector_t *toplevel_get_log_types(toplevel_t * top)
+{
+ return seaudit_get_log_types(top->s);
+}
+
+apol_vector_t *toplevel_get_log_mls_lvl(toplevel_t * top)
+{
+ return seaudit_get_log_mls_lvl(top->s);
+}
+
+apol_vector_t *toplevel_get_log_mls_clr(toplevel_t * top)
+{
+ return seaudit_get_log_mls_clr(top->s);
+}
+
+apol_vector_t *toplevel_get_log_classes(toplevel_t * top)
+{
+ return seaudit_get_log_classes(top->s);
+}
+
+apol_policy_t *toplevel_get_policy(toplevel_t * top)
+{
+ return seaudit_get_policy(top->s);
+}
+
+char *toplevel_get_glade_xml(toplevel_t * top)
+{
+ return top->xml_filename;
+}
+
+progress_t *toplevel_get_progress(toplevel_t * top)
+{
+ return top->progress;
+}
+
+GtkWindow *toplevel_get_window(toplevel_t * top)
+{
+ return top->w;
+}
+
+void toplevel_find_terules(toplevel_t * top, seaudit_message_t * message)
+{
+ policy_view_find_terules(top->pv, message);
+}
+
+/**
+ * Pop-up a dialog with a line of text and wait for the user to
+ * dismiss the dialog.
+ *
+ * @param top Toplevel window; this message dialog will be centered
+ * upon it.
+ * @param msg_type Type of message being displayed.
+ * @param fmt Format string to print, using syntax of printf(3).
+ */
+static void toplevel_message(toplevel_t * top, GtkMessageType msg_type, const char *fmt, va_list ap)
+{
+ GtkWidget *dialog;
+ char *msg;
+ if (vasprintf(&msg, fmt, ap) < 0) {
+ ERR(NULL, "%s", strerror(errno));
+ return;
+ }
+ dialog = gtk_message_dialog_new(top->w, GTK_DIALOG_DESTROY_WITH_PARENT, msg_type, GTK_BUTTONS_CLOSE, msg);
+ free(msg);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+
+void toplevel_ERR(toplevel_t * top, const char *format, ...)
+{
+ va_list(ap);
+ va_start(ap, format);
+ toplevel_message(top, GTK_MESSAGE_ERROR, format, ap);
+ va_end(ap);
+}
+
+void toplevel_WARN(toplevel_t * top, const char *format, ...)
+{
+ va_list(ap);
+ va_start(ap, format);
+ toplevel_message(top, GTK_MESSAGE_WARNING, format, ap);
+ va_end(ap);
+}
+
+/************* below are callbacks for the toplevel menu items *************/
+
+void toplevel_on_destroy(gpointer user_data, GtkObject * object __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ top->w = NULL;
+ gtk_main_quit();
+}
+
+void toplevel_on_open_log_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ apol_vector_t *paths = util_open_file(top->w, "Open Log", seaudit_get_log_path(top->s), 0);
+ if (paths != NULL) {
+ toplevel_open_log(top, apol_vector_get_element(paths, 0));
+ apol_vector_destroy(&paths);
+ }
+}
+
+void toplevel_on_open_policy_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ open_policy_window_run(top, seaudit_get_policy_path(top->s), NULL);
+}
+
+void toplevel_on_preferences_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (preferences_view_run(top, seaudit_get_log_path(top->s), seaudit_get_policy_path(top->s))) {
+ size_t i;
+ for (i = 0; i < apol_vector_get_size(top->views); i++) {
+ message_view_t *v = apol_vector_get_element(top->views, i);
+ message_view_update_visible_columns(v);
+ }
+ }
+}
+
+void toplevel_on_quit_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ top->w = NULL;
+ gtk_main_quit();
+}
+
+void toplevel_on_new_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ toplevel_add_new_model(top);
+}
+
+void toplevel_on_open_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ apol_vector_t *paths = util_open_file(top->w, "Open View", top->view_filename, 0);
+ seaudit_model_t *model = NULL;
+ if (paths == NULL) {
+ return;
+ }
+ free(top->view_filename);
+ top->view_filename = strdup(apol_vector_get_element(paths, 0));
+ apol_vector_destroy(&paths);
+ if (top->view_filename == NULL ||
+ (model = seaudit_model_create_from_file(top->view_filename)) == NULL ||
+ seaudit_model_append_log(model, seaudit_get_log(top->s)) < 0) {
+ toplevel_ERR(top, "Error opening view: %s", strerror(errno));
+ seaudit_model_destroy(&model);
+ } else {
+ toplevel_add_new_view(top, model, top->view_filename);
+ }
+}
+
+void toplevel_on_save_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_save(view);
+}
+
+void toplevel_on_save_viewas_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_saveas(view);
+}
+
+void toplevel_on_modify_view_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_modify(view);
+}
+
+void toplevel_on_export_all_messages_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_export_all_messages(view);
+}
+
+void toplevel_on_export_selected_messages_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_export_selected_messages(view);
+}
+
+void toplevel_on_view_entire_message_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_entire_message(view);
+}
+
+void toplevel_on_find_terules_activate(gpointer user_data, GtkWidget * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ toplevel_find_terules(top, NULL);
+}
+
+void toplevel_on_create_report_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ report_window_run(top, view);
+}
+
+void toplevel_on_monitor_log_activate(gpointer user_data, GtkMenuItem * widget)
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) {
+ top->do_monitor_log = 1;
+ } else {
+ top->do_monitor_log = 0;
+ }
+ toplevel_monitor_log(top);
+}
+
+void toplevel_on_clear_view_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_clear(view);
+}
+
+void toplevel_on_help_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ GtkWidget *window;
+ GtkWidget *scroll;
+ GtkWidget *text_view;
+ GtkTextBuffer *buffer;
+ char *help_text = NULL;
+ size_t len;
+ int rt;
+ char *dir;
+
+ window = gtk_dialog_new_with_buttons("seaudit Help",
+ GTK_WINDOW(top->w),
+ GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(window), GTK_RESPONSE_CLOSE);
+ g_signal_connect_swapped(window, "response", G_CALLBACK(gtk_widget_destroy), window);
+ scroll = gtk_scrolled_window_new(NULL, NULL);
+ text_view = gtk_text_view_new();
+ gtk_window_set_default_size(GTK_WINDOW(window), 520, 300);
+ gtk_container_add(GTK_CONTAINER(GTK_DIALOG(window)->vbox), scroll);
+ gtk_container_add(GTK_CONTAINER(scroll), text_view);
+ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_NONE);
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
+ dir = apol_file_find_path("seaudit_help.txt");
+ if (!dir) {
+ toplevel_ERR(top, "Cannot find help file.");
+ return;
+ }
+ rt = apol_file_read_to_buffer(dir, &help_text, &len);
+ free(dir);
+ if (rt != 0) {
+ free(help_text);
+ return;
+ }
+ gtk_text_buffer_set_text(buffer, help_text, len);
+ gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE);
+ gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_widget_show(text_view);
+ gtk_widget_show(scroll);
+ gtk_widget_show(window);
+}
+
+void toplevel_on_about_seaudit_activate(gpointer user_data, GtkMenuItem * widget __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+#ifdef GTK_2_8
+ gtk_show_about_dialog(top->w,
+ "comments", "Audit Log Analysis Tool for Security Enhanced Linux",
+ "copyright", COPYRIGHT_INFO,
+ "name", "seaudit", "version", VERSION, "website", "http://oss.tresys.com/projects/setools", NULL);
+#else
+ GtkWidget *w = gtk_message_dialog_new(top->w,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "%s %s\n%s\n%s\n%s",
+ "seaudit", VERSION,
+ "Audit Log Analysis Tool for Security Enhanced Linux",
+ COPYRIGHT_INFO,
+ "http://oss.tresys.com/projects/setools");
+ gtk_dialog_run(GTK_DIALOG(w));
+ gtk_widget_destroy(w);
+#endif
+}
+
+void toplevel_on_find_terules_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ toplevel_find_terules(top, NULL);
+}
+
+void toplevel_on_modify_view_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_modify(view);
+}
+
+void toplevel_on_monitor_log_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ GtkCheckMenuItem *w = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(top->xml, "MonitorLog"));
+ gboolean old_state;
+ assert(w != NULL);
+ old_state = gtk_check_menu_item_get_active(w);
+ gtk_check_menu_item_set_active(w, !old_state);
+}
+
+void toplevel_on_clear_view_click(gpointer user_data, GtkWidget * widget __attribute__ ((unused)), GdkEvent * event
+ __attribute__ ((unused)))
+{
+ toplevel_t *top = g_object_get_data(G_OBJECT(user_data), "toplevel");
+ message_view_t *view = toplevel_get_current_view(top);
+ assert(view != NULL);
+ message_view_clear(view);
+}