/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ #include #include #include #include #include #include #include #include #include #include #include "application.h" #include "lsof.h" #include "util.h" using std::string; namespace { class Lsof { Glib::RefPtr re; bool matches(const string &filename) const { return this->re->match(filename); } public: Lsof(const string &pattern, bool caseless) { Glib::RegexCompileFlags flags = static_cast(0); if (caseless) flags |= Glib::REGEX_CASELESS; this->re = Glib::Regex::create(pattern, flags); } template 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 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(data)->search(); } static void close_button_clicked(GtkButton *, gpointer data) { GUI *gui = static_cast(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(data)->case_insensitive = state; } static gboolean window_delete_event(GtkWidget *, GdkEvent *, gpointer data) { delete static_cast(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)); }