1 /* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 #include <config.h>
3 
4 #include <glib/gi18n.h>
5 #include <glibtop/procopenfiles.h>
6 #include <sys/stat.h>
7 #include <netdb.h>
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <netinet/in.h>
11 #include <arpa/inet.h>
12 
13 #include "application.h"
14 #include "openfiles.h"
15 #include "proctable.h"
16 #include "util.h"
17 #include "settings-keys.h"
18 #include "legacy/treeview.h"
19 
20 #ifndef NI_IDN
21 const int NI_IDN = 0;
22 #endif
23 
24 enum
25 {
26     COL_FD,
27     COL_TYPE,
28     COL_OBJECT,
29     COL_OPENFILE_STRUCT,
30     NUM_OPENFILES_COL
31 };
32 
33 
34 static const char*
get_type_name(enum glibtop_file_type t)35 get_type_name(enum glibtop_file_type t)
36 {
37     switch(t)
38     {
39         case GLIBTOP_FILE_TYPE_FILE:
40             return _("file");
41         case GLIBTOP_FILE_TYPE_PIPE:
42             return _("pipe");
43         case GLIBTOP_FILE_TYPE_INET6SOCKET:
44             return _("IPv6 network connection");
45         case GLIBTOP_FILE_TYPE_INETSOCKET:
46             return _("IPv4 network connection");
47         case GLIBTOP_FILE_TYPE_LOCALSOCKET:
48             return _("local socket");
49         default:
50             return _("unknown type");
51     }
52 }
53 
54 
55 
56 static char *
friendlier_hostname(const char * addr_str,int port)57 friendlier_hostname(const char *addr_str, int port)
58 {
59     struct addrinfo hints = { };
60     struct addrinfo *res = NULL;
61     char hostname[NI_MAXHOST];
62     char service[NI_MAXSERV];
63     char port_str[6];
64 
65     if (!addr_str[0]) return g_strdup("");
66 
67     snprintf(port_str, sizeof port_str, "%d", port);
68 
69     hints.ai_family = AF_UNSPEC;
70     hints.ai_socktype = SOCK_STREAM;
71 
72     if (getaddrinfo(addr_str, port_str, &hints, &res))
73         goto failsafe;
74 
75     if (getnameinfo(res->ai_addr, res->ai_addrlen, hostname,
76 #ifdef NI_IDN
77                     sizeof hostname, service, sizeof service, NI_IDN))
78 #else
79                     sizeof hostname, service, sizeof service, 0))
80 #endif
81         goto failsafe;
82 
83     if (res) freeaddrinfo(res);
84     return g_strdup_printf("%s, TCP port %d (%s)", hostname, port, service);
85 
86   failsafe:
87     if (res) freeaddrinfo(res);
88     return g_strdup_printf("%s, TCP port %d", addr_str, port);
89 }
90 
91 
92 
93 static void
add_new_files(gpointer key,gpointer value,gpointer data)94 add_new_files (gpointer key, gpointer value, gpointer data)
95 {
96     glibtop_open_files_entry *openfiles = static_cast<glibtop_open_files_entry*>(value);
97 
98     GtkTreeModel *model = static_cast<GtkTreeModel*>(data);
99     GtkTreeIter row;
100 
101     char *object;
102 
103     switch(openfiles->type)
104     {
105         case GLIBTOP_FILE_TYPE_FILE:
106             object = g_strdup(openfiles->info.file.name);
107             break;
108 
109         case GLIBTOP_FILE_TYPE_INET6SOCKET:
110         case GLIBTOP_FILE_TYPE_INETSOCKET:
111             object = friendlier_hostname(openfiles->info.sock.dest_host,
112                                          openfiles->info.sock.dest_port);
113             break;
114 
115         case GLIBTOP_FILE_TYPE_LOCALSOCKET:
116             object = g_strdup(openfiles->info.localsock.name);
117             break;
118 
119         default:
120             object = g_strdup("");
121     }
122 
123     gtk_list_store_insert (GTK_LIST_STORE (model), &row, 0);
124     gtk_list_store_set (GTK_LIST_STORE (model), &row,
125                         COL_FD, openfiles->fd,
126                         COL_TYPE, get_type_name(static_cast<glibtop_file_type>(openfiles->type)),
127                         COL_OBJECT, object,
128                         COL_OPENFILE_STRUCT, g_memdup(openfiles, sizeof(*openfiles)),
129                         -1);
130 
131     g_free(object);
132 }
133 
134 static GList *old_maps = NULL;
135 
136 static gboolean
classify_openfiles(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)137 classify_openfiles (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
138 {
139     GHashTable *new_maps = static_cast<GHashTable*>(data);
140     GtkTreeIter *old_iter;
141     glibtop_open_files_entry *openfiles;
142     gchar *old_name;
143 
144     gtk_tree_model_get (model, iter, 1, &old_name, -1);
145 
146     openfiles = static_cast<glibtop_open_files_entry*>(g_hash_table_lookup (new_maps, old_name));
147     if (openfiles) {
148         g_hash_table_remove (new_maps, old_name);
149         g_free (old_name);
150         return FALSE;
151 
152     }
153 
154     old_iter = gtk_tree_iter_copy (iter);
155     old_maps = g_list_append (old_maps, old_iter);
156     g_free (old_name);
157     return FALSE;
158 
159 }
160 
161 
162 static gboolean
compare_open_files(gconstpointer a,gconstpointer b)163 compare_open_files(gconstpointer a, gconstpointer b)
164 {
165     const glibtop_open_files_entry *o1 = static_cast<const glibtop_open_files_entry *>(a);
166     const glibtop_open_files_entry *o2 = static_cast<const glibtop_open_files_entry *>(b);
167 
168     /* Falta manejar los diferentes tipos! */
169     return (o1->fd == o2->fd) && (o1->type == o2->type); /* XXX! */
170 }
171 
172 
173 static void
update_openfiles_dialog(GsmTreeView * tree)174 update_openfiles_dialog (GsmTreeView *tree)
175 {
176     ProcInfo *info;
177     GtkTreeModel *model;
178     glibtop_open_files_entry *openfiles;
179     glibtop_proc_open_files procmap;
180     GHashTable *new_maps;
181     guint i;
182 
183     pid_t pid = GPOINTER_TO_UINT(static_cast<pid_t*>(g_object_get_data (G_OBJECT (tree), "selected_info")));
184     info = GsmApplication::get()->processes.find(pid);
185 
186 
187     if (!info)
188         return;
189 
190     model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
191 
192     openfiles = glibtop_get_proc_open_files (&procmap, info->pid);
193 
194     if (!openfiles)
195         return;
196 
197     new_maps = static_cast<GHashTable *>(g_hash_table_new_full (g_str_hash, compare_open_files,
198                                                                 NULL, NULL));
199     for (i=0; i < procmap.number; i++)
200         g_hash_table_insert (new_maps, openfiles + i, openfiles + i);
201 
202     gtk_tree_model_foreach (model, classify_openfiles, new_maps);
203 
204     g_hash_table_foreach (new_maps, add_new_files, model);
205 
206     while (old_maps) {
207         GtkTreeIter *iter = static_cast<GtkTreeIter*>(old_maps->data);
208         glibtop_open_files_entry *openfiles = NULL;
209 
210         gtk_tree_model_get (model, iter,
211                             COL_OPENFILE_STRUCT, &openfiles,
212                             -1);
213 
214         gtk_list_store_remove (GTK_LIST_STORE (model), iter);
215         gtk_tree_iter_free (iter);
216         g_free (openfiles);
217 
218         old_maps = g_list_next (old_maps);
219 
220     }
221 
222     g_hash_table_destroy (new_maps);
223     g_free (openfiles);
224 }
225 
226 static void
close_openfiles_dialog(GtkDialog * dialog,gint id,gpointer data)227 close_openfiles_dialog (GtkDialog *dialog, gint id, gpointer data)
228 {
229     GsmTreeView *tree = static_cast<GsmTreeView*>(data);
230     guint timer;
231 
232     gsm_tree_view_save_state (tree);
233 
234     timer = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (tree), "timer"));
235     g_source_remove (timer);
236 
237     gtk_widget_destroy (GTK_WIDGET (dialog));
238 
239     return ;
240 }
241 
242 
243 static GsmTreeView *
create_openfiles_tree(GsmApplication * app)244 create_openfiles_tree (GsmApplication *app)
245 {
246     GsmTreeView *tree;
247     GtkListStore *model;
248     GtkTreeViewColumn *column;
249     GtkCellRenderer *cell;
250     gint i;
251 
252     const gchar * const titles[] = {
253         /* Translators: "FD" here means "File Descriptor". Please use
254            a very short translation if possible, and at most
255            2-3 characters for it to be able to fit in the UI. */
256         N_("FD"),
257         N_("Type"),
258         N_("Object")
259     };
260 
261     model = gtk_list_store_new (NUM_OPENFILES_COL,
262                                 G_TYPE_INT,         /* FD */
263                                 G_TYPE_STRING,      /* Type */
264                                 G_TYPE_STRING,      /* Object */
265                                 G_TYPE_POINTER      /* open_files_entry */
266         );
267 
268     auto settings = g_settings_get_child (app->settings->gobj (), GSM_SETTINGS_CHILD_OPEN_FILES);
269 
270     tree = gsm_tree_view_new (settings, FALSE);
271     gtk_tree_view_set_model (GTK_TREE_VIEW (tree), GTK_TREE_MODEL (model));
272     g_object_unref (G_OBJECT (model));
273 
274     for (i = 0; i < NUM_OPENFILES_COL-1; i++) {
275         cell = gtk_cell_renderer_text_new ();
276 
277         switch (i) {
278             case COL_FD:
279                 g_object_set(cell, "xalign", 1.0f, NULL);
280                 break;
281         }
282 
283         column = gtk_tree_view_column_new_with_attributes (_(titles[i]),
284                                                            cell,
285                                                            "text", i,
286                                                            NULL);
287         gtk_tree_view_column_set_sort_column_id (column, i);
288         gtk_tree_view_column_set_resizable (column, TRUE);
289         gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
290     }
291 
292     gsm_tree_view_load_state (GSM_TREE_VIEW (tree));
293 
294     return tree;
295 
296 }
297 
298 
299 static gboolean
openfiles_timer(gpointer data)300 openfiles_timer (gpointer data)
301 {
302     GsmTreeView* tree = static_cast<GsmTreeView*>(data);
303     GtkTreeModel *model;
304 
305     model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
306     g_assert(model);
307 
308     update_openfiles_dialog (tree);
309 
310     return TRUE;
311 }
312 
313 
314 static void
create_single_openfiles_dialog(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)315 create_single_openfiles_dialog (GtkTreeModel *model, GtkTreePath *path,
316                                 GtkTreeIter *iter, gpointer data)
317 {
318     GsmApplication *app = static_cast<GsmApplication *>(data);
319     GtkDialog *openfilesdialog;
320     GtkGrid *cmd_grid;
321     GtkLabel *label;
322     GtkScrolledWindow *scrolled;
323     GsmTreeView *tree;
324     ProcInfo *info;
325     guint timer;
326 
327     gtk_tree_model_get (model, iter, COL_POINTER, &info, -1);
328 
329     if (!info)
330         return;
331 
332     GtkBuilder *builder = gtk_builder_new();
333     gtk_builder_add_from_resource (builder, "/org/gnome/gnome-system-monitor/data/openfiles.ui", NULL);
334 
335     openfilesdialog = GTK_DIALOG (gtk_builder_get_object (builder, "openfiles_dialog"));
336 
337     cmd_grid = GTK_GRID (gtk_builder_get_object (builder, "cmd_grid"));
338 
339 
340     label = procman_make_label_for_mmaps_or_ofiles (
341         _("_Files opened by process “%s” (PID %u):"),
342         info->name.c_str(),
343         info->pid);
344 
345     gtk_container_add (GTK_CONTAINER (cmd_grid), GTK_WIDGET (label));
346 
347     scrolled = GTK_SCROLLED_WINDOW (gtk_builder_get_object (builder, "scrolled"));
348 
349     tree = create_openfiles_tree (app);
350     gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (tree));
351     g_object_set_data (G_OBJECT (tree), "selected_info", GUINT_TO_POINTER (info->pid));
352 
353     g_signal_connect (G_OBJECT (openfilesdialog), "response",
354                       G_CALLBACK (close_openfiles_dialog), tree);
355 
356     gtk_builder_connect_signals (builder, NULL);
357 
358     gtk_window_set_transient_for (GTK_WINDOW (openfilesdialog), GTK_WINDOW (GsmApplication::get()->main_window));
359     gtk_widget_show_all (GTK_WIDGET (openfilesdialog));
360 
361     timer = g_timeout_add_seconds (5, openfiles_timer, tree);
362     g_object_set_data (G_OBJECT (tree), "timer", GUINT_TO_POINTER (timer));
363 
364     update_openfiles_dialog (tree);
365 
366     g_object_unref (G_OBJECT (builder));
367 }
368 
369 
370 void
create_openfiles_dialog(GsmApplication * app)371 create_openfiles_dialog (GsmApplication *app)
372 {
373     gtk_tree_selection_selected_foreach (app->selection, create_single_openfiles_dialog,
374                                          app);
375 }
376