summaryrefslogtreecommitdiffstats
path: root/src/lsof.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lsof.cpp')
-rw-r--r--src/lsof.cpp316
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));
+}
+