#include <config.h> #include <glib/gi18n.h> #include <glibtop/procopenfiles.h> #include <sys/stat.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include "procman.h" #include "openfiles.h" #include "proctable.h" #include "util.h" #include "settings-keys.h" #ifndef NI_IDN #define NI_IDN 0 #endif enum { COL_FD, COL_TYPE, COL_OBJECT, COL_OPENFILE_STRUCT, NUM_OPENFILES_COL }; static const char* get_type_name(enum glibtop_file_type t) { switch(t) { case GLIBTOP_FILE_TYPE_FILE: return _("file"); case GLIBTOP_FILE_TYPE_PIPE: return _("pipe"); case GLIBTOP_FILE_TYPE_INET6SOCKET: return _("IPv6 network connection"); case GLIBTOP_FILE_TYPE_INETSOCKET: return _("IPv4 network connection"); case GLIBTOP_FILE_TYPE_LOCALSOCKET: return _("local socket"); default: return _("unknown type"); } } static char * friendlier_hostname(const char *addr_str, int port) { struct addrinfo hints = { }; struct addrinfo *res = NULL; char hostname[NI_MAXHOST]; char service[NI_MAXSERV]; char port_str[6]; if (!addr_str[0]) return g_strdup(""); snprintf(port_str, sizeof port_str, "%d", port); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if (getaddrinfo(addr_str, port_str, &hints, &res)) goto failsafe; if (getnameinfo(res->ai_addr, res->ai_addrlen, hostname, sizeof hostname, service, sizeof service, NI_IDN)) goto failsafe; if (res) freeaddrinfo(res); return g_strdup_printf("%s, TCP port %d (%s)", hostname, port, service); failsafe: if (res) freeaddrinfo(res); return g_strdup_printf("%s, TCP port %d", addr_str, port); } static void add_new_files (gpointer key, gpointer value, gpointer data) { glibtop_open_files_entry *openfiles = static_cast<glibtop_open_files_entry*>(value); GtkTreeModel *model = static_cast<GtkTreeModel*>(data); GtkTreeIter row; char *object; switch(openfiles->type) { case GLIBTOP_FILE_TYPE_FILE: object = g_strdup(openfiles->info.file.name); break; case GLIBTOP_FILE_TYPE_INET6SOCKET: case GLIBTOP_FILE_TYPE_INETSOCKET: object = friendlier_hostname(openfiles->info.sock.dest_host, openfiles->info.sock.dest_port); break; case GLIBTOP_FILE_TYPE_LOCALSOCKET: object = g_strdup(openfiles->info.localsock.name); break; default: object = g_strdup(""); } gtk_list_store_insert (GTK_LIST_STORE (model), &row, 0); gtk_list_store_set (GTK_LIST_STORE (model), &row, COL_FD, openfiles->fd, COL_TYPE, get_type_name(static_cast<glibtop_file_type>(openfiles->type)), COL_OBJECT, object, COL_OPENFILE_STRUCT, g_memdup(openfiles, sizeof(*openfiles)), -1); g_free(object); } static GList *old_maps = NULL; static gboolean classify_openfiles (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { GHashTable *new_maps = static_cast<GHashTable*>(data); GtkTreeIter *old_iter; glibtop_open_files_entry *openfiles; gchar *old_name; gtk_tree_model_get (model, iter, 1, &old_name, -1); openfiles = static_cast<glibtop_open_files_entry*>(g_hash_table_lookup (new_maps, old_name)); if (openfiles) { g_hash_table_remove (new_maps, old_name); g_free (old_name); return FALSE; } old_iter = gtk_tree_iter_copy (iter); old_maps = g_list_append (old_maps, old_iter); g_free (old_name); return FALSE; } static gboolean compare_open_files(gconstpointer a, gconstpointer b) { const glibtop_open_files_entry *o1 = static_cast<const glibtop_open_files_entry *>(a); const glibtop_open_files_entry *o2 = static_cast<const glibtop_open_files_entry *>(b); /* Falta manejar los diferentes tipos! */ return (o1->fd == o2->fd) && (o1->type == o2->type); /* XXX! */ } static void update_openfiles_dialog (GtkWidget *tree) { ProcInfo *info; GtkTreeModel *model; glibtop_open_files_entry *openfiles; glibtop_proc_open_files procmap; GHashTable *new_maps; guint i; pid_t pid = GPOINTER_TO_UINT(static_cast<pid_t*>(g_object_get_data (G_OBJECT (tree), "selected_info"))); info = ProcInfo::find(pid); if (!info) return; model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree)); openfiles = glibtop_get_proc_open_files (&procmap, info->pid); if (!openfiles) return; new_maps = static_cast<GHashTable *>(g_hash_table_new_full (g_str_hash, compare_open_files, NULL, NULL)); for (i=0; i < procmap.number; i++) g_hash_table_insert (new_maps, openfiles + i, openfiles + i); gtk_tree_model_foreach (model, classify_openfiles, new_maps); g_hash_table_foreach (new_maps, add_new_files, model); while (old_maps) { GtkTreeIter *iter = static_cast<GtkTreeIter*>(old_maps->data); glibtop_open_files_entry *openfiles = NULL; gtk_tree_model_get (model, iter, COL_OPENFILE_STRUCT, &openfiles, -1); gtk_list_store_remove (GTK_LIST_STORE (model), iter); gtk_tree_iter_free (iter); g_free (openfiles); old_maps = g_list_next (old_maps); } g_hash_table_destroy (new_maps); g_free (openfiles); } static void close_openfiles_dialog (GtkDialog *dialog, gint id, gpointer data) { GtkWidget *tree = static_cast<GtkWidget*>(data); GSettings *settings; guint timer; settings = static_cast<GSettings*>(g_object_get_data (G_OBJECT (tree), "settings")); procman_save_tree_state (settings, tree, procman::settings::open_files_tree_prefix.c_str()); timer = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (tree), "timer")); g_source_remove (timer); gtk_widget_destroy (GTK_WIDGET (dialog)); return ; } static GtkWidget * create_openfiles_tree (ProcData *procdata) { GtkWidget *tree; GtkListStore *model; GtkTreeViewColumn *column; GtkCellRenderer *cell; gint i; const gchar * const titles[] = { /* Translators: "FD" here means "File Descriptor". Please use a very short translation if possible, and at most 2-3 characters for it to be able to fit in the UI. */ N_("FD"), N_("Type"), N_("Object") }; model = gtk_list_store_new (NUM_OPENFILES_COL, G_TYPE_INT, /* FD */ G_TYPE_STRING, /* Type */ G_TYPE_STRING, /* Object */ G_TYPE_POINTER /* open_files_entry */ ); tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model)); g_object_unref (G_OBJECT (model)); for (i = 0; i < NUM_OPENFILES_COL-1; i++) { cell = gtk_cell_renderer_text_new (); switch (i) { case COL_FD: g_object_set(cell, "xalign", 1.0f, NULL); break; } column = gtk_tree_view_column_new_with_attributes (_(titles[i]), cell, "text", i, NULL); gtk_tree_view_column_set_sort_column_id (column, i); gtk_tree_view_column_set_resizable (column, TRUE); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); } procman_get_tree_state (procdata->settings, tree, procman::settings::open_files_tree_prefix.c_str()); return tree; } static gboolean openfiles_timer (gpointer data) { GtkWidget *tree = static_cast<GtkWidget*>(data); GtkTreeModel *model; model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree)); g_assert(model); update_openfiles_dialog (tree); return TRUE; } static void create_single_openfiles_dialog (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { ProcData *procdata = static_cast<ProcData*>(data); GtkWidget *openfilesdialog; GtkWidget *dialog_vbox, *vbox; GtkWidget *cmd_hbox; GtkWidget *label; GtkWidget *scrolled; GtkWidget *tree; ProcInfo *info; guint timer; gtk_tree_model_get (model, iter, COL_POINTER, &info, -1); if (!info) return; openfilesdialog = gtk_dialog_new_with_buttons (_("Open Files"), NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); gtk_window_set_resizable (GTK_WINDOW (openfilesdialog), TRUE); gtk_window_set_default_size (GTK_WINDOW (openfilesdialog), 575, 400); gtk_container_set_border_width (GTK_CONTAINER (openfilesdialog), 5); vbox = gtk_dialog_get_content_area (GTK_DIALOG (openfilesdialog)); gtk_box_set_spacing (GTK_BOX (vbox), 2); gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); dialog_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox), 5); gtk_box_pack_start (GTK_BOX (vbox), dialog_vbox, TRUE, TRUE, 0); cmd_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12); gtk_box_pack_start (GTK_BOX (dialog_vbox), cmd_hbox, FALSE, FALSE, 0); label = procman_make_label_for_mmaps_or_ofiles ( _("_Files opened by process \"%s\" (PID %u):"), info->name, info->pid); gtk_box_pack_start (GTK_BOX (cmd_hbox),label, FALSE, FALSE, 0); scrolled = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), GTK_SHADOW_IN); tree = create_openfiles_tree (procdata); gtk_container_add (GTK_CONTAINER (scrolled), tree); g_object_set_data (G_OBJECT (tree), "selected_info", GUINT_TO_POINTER (info->pid)); g_object_set_data (G_OBJECT (tree), "settings", procdata->settings); gtk_box_pack_start (GTK_BOX (dialog_vbox), scrolled, TRUE, TRUE, 0); gtk_widget_show_all (scrolled); g_signal_connect (G_OBJECT (openfilesdialog), "response", G_CALLBACK (close_openfiles_dialog), tree); gtk_widget_show_all (openfilesdialog); timer = g_timeout_add_seconds (5, openfiles_timer, tree); g_object_set_data (G_OBJECT (tree), "timer", GUINT_TO_POINTER (timer)); update_openfiles_dialog (tree); } void create_openfiles_dialog (ProcData *procdata) { gtk_tree_selection_selected_foreach (procdata->selection, create_single_openfiles_dialog, procdata); }