diff options
Diffstat (limited to 'src/lsof.cpp')
-rw-r--r-- | src/lsof.cpp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/src/lsof.cpp b/src/lsof.cpp new file mode 100644 index 0000000..15c3a59 --- /dev/null +++ b/src/lsof.cpp @@ -0,0 +1,316 @@ +/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +#include <config.h> + +#include <gtkmm/messagedialog.h> +#include <glib/gi18n.h> +#include <glibtop/procopenfiles.h> + +#include <sys/wait.h> + +#include <set> +#include <string> +#include <sstream> +#include <iterator> + +#include <glibmm/regex.h> + +#include "application.h" +#include "lsof.h" +#include "util.h" + + +using std::string; + + +namespace +{ + + class Lsof + { + Glib::RefPtr<Glib::Regex> re; + + bool matches(const string &filename) const + { + return this->re->match(filename); + } + + public: + + Lsof(const string &pattern, bool caseless) + { + Glib::RegexCompileFlags flags = static_cast<Glib::RegexCompileFlags>(0); + + if (caseless) + flags |= Glib::REGEX_CASELESS; + + this->re = Glib::Regex::create(pattern, flags); + } + + + template<typename OutputIterator> + void search(const ProcInfo &info, OutputIterator out) const + { + glibtop_open_files_entry *entries; + glibtop_proc_open_files buf; + + entries = glibtop_get_proc_open_files(&buf, info.pid); + + for (unsigned i = 0; i != buf.number; ++i) { + if (entries[i].type & GLIBTOP_FILE_TYPE_FILE) { + const string filename(entries[i].info.file.name); + if (this->matches(filename)) + *out++ = filename; + } + } + + g_free(entries); + } + }; + + + + // GUI Stuff + + + enum ProcmanLsof { + PROCMAN_LSOF_COL_PIXBUF, + PROCMAN_LSOF_COL_PROCESS, + PROCMAN_LSOF_COL_PID, + PROCMAN_LSOF_COL_FILENAME, + PROCMAN_LSOF_NCOLS + }; + + + struct GUI : private procman::NonCopyable { + + GtkListStore *model; + GtkSearchEntry *entry; + GtkWindow *window; + GtkLabel *count; + GsmApplication *app; + bool case_insensitive; + bool regex_error_displayed; + + + GUI() + : model(NULL), + entry(NULL), + window(NULL), + count(NULL), + app(NULL), + case_insensitive(), + regex_error_displayed(false) + { + procman_debug("New Lsof GUI %p", this); + } + + + ~GUI() + { + procman_debug("Destroying Lsof GUI %p", this); + } + + + void update_count(unsigned count) + { + gchar *title; + if (this->pattern().length() == 0) { + title = g_strdup_printf (ngettext("%d open file", "%d open files", count), count); + } else { + title = g_strdup_printf (ngettext("%d matching open file", "%d matching open files", count), count); + } + gtk_window_set_title(this->window, title); + g_free (title); + } + + + string pattern() const + { + return gtk_entry_get_text(GTK_ENTRY (this->entry)); + } + + + void search() + { + typedef std::set<string> MatchSet; + + bool regex_error = false; + + gtk_list_store_clear(this->model); + try { + Lsof lsof(this->pattern(), this->case_insensitive); + + unsigned count = 0; + + for (const auto& v : app->processes) { + const auto& info = v.second; + MatchSet matches; + lsof.search(info, std::inserter(matches, matches.begin())); + count += matches.size(); + + for (const auto& match : matches) { + GtkTreeIter file; + gtk_list_store_append(this->model, &file); + gtk_list_store_set(this->model, &file, + PROCMAN_LSOF_COL_PIXBUF, info.pixbuf->gobj(), + PROCMAN_LSOF_COL_PROCESS, info.name.c_str(), + PROCMAN_LSOF_COL_PID, info.pid, + PROCMAN_LSOF_COL_FILENAME, match.c_str(), + -1); + } + } + + this->update_count(count); + } + catch (Glib::RegexError& error) { + regex_error = true; + } + + if (regex_error && !this->regex_error_displayed) { + this->regex_error_displayed = true; + gtk_style_context_add_class(gtk_widget_get_style_context(GTK_WIDGET(entry)), GTK_STYLE_CLASS_ERROR); + } + else if (!regex_error && this->regex_error_displayed) { + this->regex_error_displayed = false; + gtk_style_context_remove_class(gtk_widget_get_style_context(GTK_WIDGET(entry)), GTK_STYLE_CLASS_ERROR); + } + } + + + static void search_changed(GtkSearchEntry *, gpointer data) + { + static_cast<GUI*>(data)->search(); + } + + + static void close_button_clicked(GtkButton *, gpointer data) + { + GUI *gui = static_cast<GUI*>(data); + gtk_widget_destroy(GTK_WIDGET(gui->window)); + delete gui; + } + + + static void case_button_toggled(GtkToggleButton *button, gpointer data) + { + bool state = gtk_toggle_button_get_active(button); + static_cast<GUI*>(data)->case_insensitive = state; + } + + + static gboolean window_delete_event(GtkWidget *, GdkEvent *, gpointer data) + { + delete static_cast<GUI*>(data); + return FALSE; + } + + }; +} + + + + +void procman_lsof(GsmApplication *app) +{ + GtkListStore *model = \ + gtk_list_store_new(PROCMAN_LSOF_NCOLS, + GDK_TYPE_PIXBUF, // PROCMAN_LSOF_COL_PIXBUF + G_TYPE_STRING, // PROCMAN_LSOF_COL_PROCESS + G_TYPE_UINT, // PROCMAN_LSOF_COL_PID + G_TYPE_STRING // PROCMAN_LSOF_COL_FILENAME + ); + + GtkTreeView *tree = GTK_TREE_VIEW (gtk_tree_view_new_with_model(GTK_TREE_MODEL(model))); + g_object_unref(model); + + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + + // PIXBUF / PROCESS + + column = gtk_tree_view_column_new(); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, + "pixbuf", PROCMAN_LSOF_COL_PIXBUF, + NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, + "text", PROCMAN_LSOF_COL_PROCESS, + NULL); + + gtk_tree_view_column_set_title(column, _("Process")); + gtk_tree_view_column_set_sort_column_id(column, PROCMAN_LSOF_COL_PROCESS); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_min_width(column, 10); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), PROCMAN_LSOF_COL_PROCESS, + GTK_SORT_ASCENDING); + + + // PID + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("PID"), renderer, + "text", PROCMAN_LSOF_COL_PID, + NULL); + gtk_tree_view_column_set_sort_column_id(column, PROCMAN_LSOF_COL_PID); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + + + // FILENAME + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Filename"), renderer, + "text", PROCMAN_LSOF_COL_FILENAME, + NULL); + gtk_tree_view_column_set_sort_column_id(column, PROCMAN_LSOF_COL_FILENAME); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); + + + GtkWindow *dialog; + + GtkBuilder *builder = gtk_builder_new (); + gtk_builder_add_from_resource (builder, "/org/gnome/gnome-system-monitor/data/lsof.ui", NULL); + + dialog = GTK_WINDOW (gtk_builder_get_object (builder, "lsof_dialog")); + + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (app->main_window)); + + GtkSearchEntry *entry = GTK_SEARCH_ENTRY (gtk_builder_get_object (builder, "entry")); + + GtkCheckButton *case_button = GTK_CHECK_BUTTON (gtk_builder_get_object (builder, "case_button")); + + // Scrolled TreeView + GtkScrolledWindow *scrolled = GTK_SCROLLED_WINDOW (gtk_builder_get_object (builder, "scrolled")); + + gtk_container_add(GTK_CONTAINER(scrolled), GTK_WIDGET (tree)); + + GUI *gui = new GUI; // wil be deleted by the close button or delete-event + gui->app = app; + gui->model = model; + gui->window = dialog; + gui->entry = entry; + gui->case_insensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (case_button)); + + g_signal_connect(G_OBJECT(entry), "search-changed", + G_CALLBACK(GUI::search_changed), gui); + g_signal_connect(G_OBJECT(case_button), "toggled", + G_CALLBACK(GUI::case_button_toggled), gui); + g_signal_connect(G_OBJECT(dialog), "delete-event", + G_CALLBACK(GUI::window_delete_event), gui); + + gtk_builder_connect_signals (builder, NULL); + + gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (GsmApplication::get()->main_window)); + gtk_widget_show_all(GTK_WIDGET (dialog)); + gui->search (); + + g_object_unref (G_OBJECT (builder)); +} + |