1 /* Procman tree view and process updating
2  * Copyright (C) 2001 Kevin Vandersloot
3  * Copyright (C) 2012-2021 MATE Developers
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  */
20 
21 
22 #include <config.h>
23 
24 #include <cairo-gobject.h>
25 #include <string.h>
26 #include <math.h>
27 #include <glib/gi18n.h>
28 #include <glib/gprintf.h>
29 #include <glibtop.h>
30 #include <glibtop/loadavg.h>
31 #include <glibtop/proclist.h>
32 #include <glibtop/procio.h>
33 #include <glibtop/procstate.h>
34 #include <glibtop/procmem.h>
35 #include <glibtop/procmap.h>
36 #include <glibtop/proctime.h>
37 #include <glibtop/procuid.h>
38 #include <glibtop/procargs.h>
39 #include <glibtop/prockernel.h>
40 #include <glibtop/mem.h>
41 #include <glibtop/swap.h>
42 #include <sys/stat.h>
43 #include <pwd.h>
44 #include <time.h>
45 
46 #include <set>
47 #include <list>
48 
49 #ifdef HAVE_SYSTEMD
50 #include <systemd/sd-login.h>
51 #endif
52 
53 #include "procman.h"
54 #include "selection.h"
55 #include "proctable.h"
56 #include "callbacks.h"
57 #include "prettytable.h"
58 #include "util.h"
59 #include "interface.h"
60 #include "selinux.h"
61 #include "cgroups.h"
62 
63 #ifdef GDK_WINDOWING_X11
64 #include <gdk/gdkx.h>
65 #endif
66 
67 ProcInfo::UserMap ProcInfo::users;
68 ProcInfo::List ProcInfo::all;
69 std::map<pid_t, guint64> ProcInfo::cpu_times;
70 
71 
find(pid_t pid)72 ProcInfo* ProcInfo::find(pid_t pid)
73 {
74     Iterator it(ProcInfo::all.find(pid));
75     return (it == ProcInfo::all.end() ? NULL : it->second);
76 }
77 
78 void
get_last_selected(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)79 get_last_selected (GtkTreeModel *model, GtkTreePath *path,
80            GtkTreeIter *iter, gpointer data)
81 {
82     ProcInfo **info = static_cast<ProcInfo**>(data);
83 
84     gtk_tree_model_get (model, iter, COL_POINTER, info, -1);
85 }
86 
87 static void
cb_columns_changed(GtkTreeView * treeview,gpointer user_data)88 cb_columns_changed(GtkTreeView *treeview, gpointer user_data)
89 {
90     ProcData * const procdata = static_cast<ProcData*>(user_data);
91 
92     procman_save_tree_state(procdata->settings,
93                             GTK_WIDGET(treeview),
94                             "proctree");
95 }
96 
97 static void
cb_sort_changed(GtkTreeSortable * model,gpointer user_data)98 cb_sort_changed (GtkTreeSortable *model, gpointer user_data)
99 {
100     ProcData * const procdata = static_cast<ProcData*>(user_data);
101 
102     procman_save_tree_state (procdata->settings,
103                              GTK_WIDGET (procdata->tree),
104                              "proctree");
105 }
106 
107 
108 static GtkTreeViewColumn*
my_gtk_tree_view_get_column_with_sort_column_id(GtkTreeView * treeview,int id)109 my_gtk_tree_view_get_column_with_sort_column_id(GtkTreeView *treeview, int id)
110 {
111     GList *columns, *it;
112     GtkTreeViewColumn *col = NULL;
113 
114     columns = gtk_tree_view_get_columns(treeview);
115 
116     for(it = columns; it; it = it->next)
117     {
118         if(gtk_tree_view_column_get_sort_column_id(static_cast<GtkTreeViewColumn*>(it->data)) == id)
119         {
120             col = static_cast<GtkTreeViewColumn*>(it->data);
121             break;
122         }
123     }
124 
125     g_list_free(columns);
126 
127     return col;
128 }
129 
130 
131 void
proctable_set_columns_order(GtkTreeView * treeview,GSList * order)132 proctable_set_columns_order(GtkTreeView *treeview, GSList *order)
133 {
134     GtkTreeViewColumn* last = NULL;
135     GSList *it;
136 
137     for(it = order; it; it = it->next)
138     {
139         int id;
140         GtkTreeViewColumn *cur;
141 
142         id = GPOINTER_TO_INT(it->data);
143 
144         cur = my_gtk_tree_view_get_column_with_sort_column_id(treeview, id);
145 
146         if(cur && cur != last)
147         {
148             gtk_tree_view_move_column_after(treeview, cur, last);
149             last = cur;
150         }
151     }
152 }
153 
154 
155 GSList*
proctable_get_columns_order(GtkTreeView * treeview)156 proctable_get_columns_order(GtkTreeView *treeview)
157 {
158     GList *columns, *col;
159     GSList *order = NULL;
160 
161     columns = gtk_tree_view_get_columns(treeview);
162 
163     for(col = columns; col; col = col->next)
164     {
165         int id;
166 
167         id = gtk_tree_view_column_get_sort_column_id(static_cast<GtkTreeViewColumn*>(col->data));
168         order = g_slist_prepend(order, GINT_TO_POINTER(id));
169     }
170 
171     g_list_free(columns);
172 
173     order = g_slist_reverse(order);
174 
175     return order;
176 }
177 
178 static guint timeout_id = 0;
179 static GtkTreeViewColumn *current_column;
180 
181 static gboolean
save_column_width(gpointer data)182 save_column_width (gpointer data)
183 {
184     gint width;
185     gchar *key;
186     int id;
187     GSettings *settings;
188 
189     settings = g_settings_get_child (G_SETTINGS (data), "proctree");
190     id = gtk_tree_view_column_get_sort_column_id (current_column);
191     width = gtk_tree_view_column_get_width (current_column);
192 
193     key = g_strdup_printf ("col-%d-width", id);
194     g_settings_set_int(settings, key, width);
195     g_free (key);
196 
197     if (timeout_id) {
198         g_source_remove (timeout_id);
199         timeout_id = 0;
200     }
201 
202     return FALSE;
203 }
204 
205 static void
cb_proctable_column_resized(GtkWidget * widget,GParamSpec * pspec,gpointer data)206 cb_proctable_column_resized(GtkWidget *widget, GParamSpec *pspec, gpointer data)
207 {
208     current_column = GTK_TREE_VIEW_COLUMN(widget);
209 
210     if (timeout_id)
211         g_source_remove (timeout_id);
212 
213     timeout_id = g_timeout_add (250, save_column_width, data);
214 }
215 
216 static gboolean
search_equal_func(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer search_data)217 search_equal_func(GtkTreeModel *model,
218                   gint column,
219                   const gchar *key,
220                   GtkTreeIter *iter,
221                   gpointer search_data)
222 {
223     char* name;
224     char* user;
225     gboolean found;
226 
227     gtk_tree_model_get(model, iter,
228                        COL_NAME, &name,
229                        COL_USER, &user,
230                        -1);
231 
232     found = !((name && strcasestr(name, key))
233               || (user && strcasestr(user, key)));
234 
235     g_free(name);
236     g_free(user);
237 
238     return found;
239 }
240 
241 
242 
243 GtkWidget *
proctable_new(ProcData * const procdata)244 proctable_new (ProcData * const procdata)
245 {
246     GtkWidget *proctree;
247     GtkWidget *scrolled;
248     GtkTreeStore *model;
249     GtkTreeSelection *selection;
250     GtkTreeViewColumn *column;
251     GtkCellRenderer *cell_renderer;
252 
253     const gchar *titles[] = {
254         N_("Process Name"),
255         N_("User"),
256         N_("Status"),
257         N_("Virtual Memory"),
258         N_("Resident Memory"),
259         N_("Writable Memory"),
260         N_("Shared Memory"),
261         N_("X Server Memory"),
262         /* xgettext:no-c-format */ N_("% CPU"),
263         N_("CPU Time"),
264         N_("Started"),
265         N_("Nice"),
266         N_("ID"),
267         N_("Security Context"),
268         N_("Command Line"),
269         N_("Memory"),
270         /* xgettext: combined noun, the function the process is waiting in, see wchan ps(1) */
271         N_("Waiting Channel"),
272         N_("Control Group"),
273         N_("Unit"),
274         N_("Session"),
275         /* TRANSLATORS: Seat = i.e. the physical seat the session of the process belongs to, only
276 	for multi-seat environments. See http://en.wikipedia.org/wiki/Multiseat_configuration */
277         N_("Seat"),
278         N_("Owner"),
279         N_("Disk Read Total"),
280         N_("Disk Write Total"),
281         N_("Disk Read"),
282         N_("Disk Write"),
283         N_("Priority"),
284         NULL,
285         "POINTER"
286     };
287 
288     gint i;
289 
290     scrolled = gtk_scrolled_window_new (NULL, NULL);
291     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
292                                     GTK_POLICY_AUTOMATIC,
293                                     GTK_POLICY_AUTOMATIC);
294 
295     model = gtk_tree_store_new (NUM_COLUMNS,
296                                 G_TYPE_STRING,              /* Process Name */
297                                 G_TYPE_STRING,              /* User         */
298                                 G_TYPE_UINT,                /* Status       */
299                                 G_TYPE_ULONG,               /* VM Size      */
300                                 G_TYPE_ULONG,               /* Resident Memory */
301                                 G_TYPE_ULONG,               /* Writable Memory */
302                                 G_TYPE_ULONG,               /* Shared Memory */
303                                 G_TYPE_ULONG,               /* X Server Memory */
304                                 G_TYPE_UINT,                /* % CPU        */
305                                 G_TYPE_UINT64,              /* CPU time     */
306                                 G_TYPE_ULONG,               /* Started      */
307                                 G_TYPE_INT,                 /* Nice         */
308                                 G_TYPE_UINT,                /* ID           */
309                                 G_TYPE_STRING,              /* Security Context */
310                                 G_TYPE_STRING,              /* Arguments    */
311                                 G_TYPE_ULONG,               /* Memory       */
312                                 G_TYPE_STRING,              /* wchan        */
313                                 G_TYPE_STRING,              /* Cgroup       */
314                                 G_TYPE_STRING,              /* Unit         */
315                                 G_TYPE_STRING,              /* Session      */
316                                 G_TYPE_STRING,              /* Seat         */
317                                 G_TYPE_STRING,              /* Owner        */
318                                 G_TYPE_UINT64,              /* Disk read total */
319                                 G_TYPE_UINT64,              /* Disk write total*/
320                                 G_TYPE_UINT64,              /* Disk read    */
321                                 G_TYPE_UINT64,              /* Disk write   */
322                                 G_TYPE_STRING,              /* Priority     */
323                                 CAIRO_GOBJECT_TYPE_SURFACE, /* Icon         */
324                                 G_TYPE_POINTER,             /* ProcInfo     */
325                                 G_TYPE_STRING               /* Sexy tooltip */
326         );
327 
328     proctree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
329     gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (proctree), COL_TOOLTIP);
330     g_object_set(G_OBJECT(proctree),
331                  "show-expanders", procdata->config.show_tree,
332                  NULL);
333     gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (proctree),
334                                          search_equal_func,
335                                          NULL,
336                                          NULL);
337     g_object_unref (G_OBJECT (model));
338 
339     selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (proctree));
340     gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
341 
342     column = gtk_tree_view_column_new ();
343 
344     cell_renderer = gtk_cell_renderer_pixbuf_new ();
345     gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
346     gtk_tree_view_column_set_attributes (column, cell_renderer,
347                                          "surface", COL_SURFACE,
348                                          NULL);
349 
350     cell_renderer = gtk_cell_renderer_text_new ();
351     gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
352     gtk_tree_view_column_set_attributes (column, cell_renderer,
353                                          "text", COL_NAME,
354                                          NULL);
355     gtk_tree_view_column_set_title (column, _(titles[0]));
356     gtk_tree_view_column_set_sort_column_id (column, COL_NAME);
357     gtk_tree_view_column_set_resizable (column, TRUE);
358     gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
359     gtk_tree_view_column_set_min_width (column, 20);
360     gtk_tree_view_column_set_reorderable (column, TRUE);
361     g_signal_connect(G_OBJECT(column), "notify::fixed-width", G_CALLBACK(cb_proctable_column_resized), procdata->settings);
362     gtk_tree_view_append_column (GTK_TREE_VIEW (proctree), column);
363     gtk_tree_view_set_expander_column (GTK_TREE_VIEW (proctree), column);
364 
365 
366     for (i = COL_USER; i <= COL_PRIORITY; i++) {
367 
368         GtkCellRenderer *cell;
369         GtkTreeViewColumn *col;
370 
371 #ifndef HAVE_WNCK
372         if (i == COL_MEMXSERVER) {
373           continue;
374         }
375 #endif
376 
377         cell = gtk_cell_renderer_text_new();
378         col = gtk_tree_view_column_new();
379         gtk_tree_view_column_pack_start(col, cell, TRUE);
380         gtk_tree_view_column_set_title(col, _(titles[i]));
381         gtk_tree_view_column_set_sort_column_id(col, i);
382         gtk_tree_view_column_set_resizable(col, TRUE);
383         gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
384         g_signal_connect(G_OBJECT(col), "notify::fixed-width", G_CALLBACK(cb_proctable_column_resized), procdata->settings);
385         gtk_tree_view_column_set_reorderable(col, TRUE);
386         gtk_tree_view_append_column(GTK_TREE_VIEW(proctree), col);
387 
388         // type
389         switch (i) {
390 #ifdef HAVE_WNCK
391             case COL_MEMXSERVER:
392                 gtk_tree_view_column_set_cell_data_func(col, cell,
393                                                         &procman::memory_size_cell_data_func,
394                                                         GUINT_TO_POINTER(i),
395                                                         NULL);
396                 break;
397 #endif
398             case COL_VMSIZE:
399             case COL_MEMRES:
400             case COL_MEMSHARED:
401             case COL_MEM:
402             case COL_MEMWRITABLE:
403                 gtk_tree_view_column_set_cell_data_func(col, cell,
404                                                         &procman::memory_size_na_cell_data_func,
405                                                         GUINT_TO_POINTER(i),
406                                                         NULL);
407                 break;
408 
409             case COL_CPU_TIME:
410                 gtk_tree_view_column_set_cell_data_func(col, cell,
411                                                         &procman::duration_cell_data_func,
412                                                         GUINT_TO_POINTER(i),
413                                                         NULL);
414                 break;
415 
416             case COL_START_TIME:
417                 gtk_tree_view_column_set_cell_data_func(col, cell,
418                                                         &procman::time_cell_data_func,
419                                                         GUINT_TO_POINTER(i),
420                                                         NULL);
421                 break;
422 
423             case COL_STATUS:
424                 gtk_tree_view_column_set_cell_data_func(col, cell,
425                                                         &procman::status_cell_data_func,
426                                                         GUINT_TO_POINTER(i),
427                                                         NULL);
428                 break;
429 
430             case COL_DISK_READ_TOTAL:
431             case COL_DISK_WRITE_TOTAL:
432                 gtk_tree_view_column_set_cell_data_func(col, cell,
433                                                         &procman::storage_size_na_cell_data_func,
434                                                         GUINT_TO_POINTER(i),
435                                                         NULL);
436                 break;
437 
438             case COL_DISK_READ_CURRENT:
439             case COL_DISK_WRITE_CURRENT:
440                 gtk_tree_view_column_set_cell_data_func(col, cell,
441                                                         &procman::io_rate_cell_data_func,
442                                                         GUINT_TO_POINTER(i),
443                                                         NULL);
444                 break;
445 
446             case COL_PRIORITY:
447                 gtk_tree_view_column_set_cell_data_func(col, cell,
448                                                         &procman::priority_cell_data_func,
449                                                         GUINT_TO_POINTER(COL_NICE),
450                                                         NULL);
451                 break;
452 
453             default:
454                 gtk_tree_view_column_set_attributes(col, cell, "text", i, NULL);
455         }
456 
457         // sorting
458         switch (i) {
459 #ifdef HAVE_WNCK
460             case COL_MEMXSERVER:
461 #endif
462             case COL_VMSIZE:
463             case COL_MEMRES:
464             case COL_MEMSHARED:
465             case COL_MEM:
466             case COL_MEMWRITABLE:
467             case COL_CPU:
468             case COL_CPU_TIME:
469             case COL_DISK_READ_TOTAL:
470             case COL_DISK_WRITE_TOTAL:
471             case COL_DISK_READ_CURRENT:
472             case COL_DISK_WRITE_CURRENT:
473             case COL_START_TIME:
474                 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), i,
475                                                 procman::number_compare_func, GUINT_TO_POINTER(i),
476                                                 NULL);
477                 break;
478             case COL_PRIORITY:
479                 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(model), i,
480                                                 procman::priority_compare_func,
481                                                 GUINT_TO_POINTER(COL_NICE), NULL);
482                 break;
483             default:
484                 break;
485         }
486 
487         // xalign
488         switch(i)
489         {
490             case COL_VMSIZE:
491             case COL_MEMRES:
492             case COL_MEMWRITABLE:
493             case COL_MEMSHARED:
494 #ifdef HAVE_WNCK
495             case COL_MEMXSERVER:
496 #endif
497             case COL_CPU:
498             case COL_NICE:
499             case COL_PID:
500             case COL_DISK_READ_TOTAL:
501             case COL_DISK_WRITE_TOTAL:
502             case COL_DISK_READ_CURRENT:
503             case COL_DISK_WRITE_CURRENT:
504             case COL_CPU_TIME:
505             case COL_MEM:
506                 g_object_set(G_OBJECT(cell), "xalign", 1.0f, NULL);
507                 break;
508         }
509 
510         // sizing
511         switch (i) {
512             case COL_ARGS:
513                 gtk_tree_view_column_set_min_width(col, 150);
514                 break;
515             default:
516                 gtk_tree_view_column_set_min_width(col, 20);
517                 break;
518         }
519     }
520 
521     gtk_container_add (GTK_CONTAINER (scrolled), proctree);
522 
523     procdata->tree = proctree;
524 
525     procman_get_tree_state (procdata->settings, proctree, "proctree");
526 
527     /* Override column settings by hiding this column if it's meaningless: */
528     if (!can_show_security_context_column ()) {
529         column = my_gtk_tree_view_get_column_with_sort_column_id (GTK_TREE_VIEW (proctree), COL_SECURITYCONTEXT);
530         gtk_tree_view_column_set_visible (column, FALSE);
531     }
532 
533     if (!cgroups_enabled()) {
534         column = my_gtk_tree_view_get_column_with_sort_column_id(GTK_TREE_VIEW(proctree), COL_CGROUP);
535         gtk_tree_view_column_set_visible(column, FALSE);
536     }
537 
538 #ifdef HAVE_SYSTEMD
539     if (!LOGIND_RUNNING())
540 #endif
541     {
542         for (i = COL_UNIT; i <= COL_OWNER; i++) {
543             column = my_gtk_tree_view_get_column_with_sort_column_id(GTK_TREE_VIEW(proctree), i);
544             gtk_tree_view_column_set_visible(column, FALSE);
545         }
546     }
547 
548     g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (proctree))),
549                       "changed",
550                       G_CALLBACK (cb_row_selected), procdata);
551     g_signal_connect (G_OBJECT (proctree), "popup_menu",
552                       G_CALLBACK (cb_tree_popup_menu), procdata);
553     g_signal_connect (G_OBJECT (proctree), "button_press_event",
554                       G_CALLBACK (cb_tree_button_pressed), procdata);
555 
556     g_signal_connect (G_OBJECT(proctree), "columns-changed",
557                       G_CALLBACK(cb_columns_changed), procdata);
558 
559     g_signal_connect (G_OBJECT (model), "sort-column-changed",
560                       G_CALLBACK (cb_sort_changed), procdata);
561 
562     return scrolled;
563 }
564 
565 
~ProcInfo()566 ProcInfo::~ProcInfo()
567 {
568     g_free(this->name);
569     g_free(this->tooltip);
570     g_free(this->arguments);
571     g_free(this->security_context);
572     g_free(this->cgroup_name);
573     g_free(this->unit);
574     g_free(this->session);
575     g_free(this->seat);
576     cairo_surface_destroy(this->surface);
577 }
578 
579 
580 static void
get_process_name(ProcInfo * info,const gchar * cmd,const GStrv args)581 get_process_name (ProcInfo *info,
582                   const gchar *cmd, const GStrv args)
583 {
584     if (args) {
585         // look for /usr/bin/very_long_name
586         // and also /usr/bin/interpreter /usr/.../very_long_name
587         // which may have use prctl to alter 'cmd' name
588         for (int i = 0; i != 2 && args[i]; ++i) {
589             char* basename;
590             basename = g_path_get_basename(args[i]);
591 
592             if (g_str_has_prefix(basename, cmd)) {
593                 info->name = basename;
594                 return;
595             }
596 
597             g_free(basename);
598         }
599     }
600 
601     info->name = g_strdup (cmd);
602 }
603 
604 std::string
lookup_user(guint uid)605 ProcInfo::lookup_user(guint uid)
606 {
607     typedef std::pair<ProcInfo::UserMap::iterator, bool> Pair;
608     ProcInfo::UserMap::value_type hint(uid, "");
609     Pair p(ProcInfo::users.insert(hint));
610 
611     // procman_debug("User lookup for uid %u: %s", uid, (p.second ? "MISS" : "HIT"));
612 
613     if (p.second) {
614         struct passwd* pwd;
615         pwd = getpwuid(uid);
616 
617         if (pwd && pwd->pw_name)
618             p.first->second = pwd->pw_name;
619         else {
620             char username[16];
621             g_sprintf(username, "%u", uid);
622             p.first->second = username;
623         }
624     }
625 
626     return p.first->second;
627 }
628 
629 void
set_user(guint uid)630 ProcInfo::set_user(guint uid)
631 {
632     if (G_LIKELY(this->uid == uid))
633         return;
634 
635     this->uid = uid;
636     this->user = lookup_user(uid);
637 }
638 
get_process_memory_writable(ProcInfo * info)639 static void get_process_memory_writable(ProcInfo *info)
640 {
641     glibtop_proc_map buf;
642     glibtop_map_entry *maps;
643 
644     maps = glibtop_get_proc_map(&buf, info->pid);
645 
646     gulong memwritable = 0;
647     const unsigned number = buf.number;
648 
649     for (unsigned i = 0; i < number; ++i) {
650 #ifdef __linux__
651         memwritable += maps[i].private_dirty;
652 #else
653         if (maps[i].perm & GLIBTOP_MAP_PERM_WRITE)
654             memwritable += maps[i].size;
655 #endif
656     }
657 
658     info->memwritable = memwritable;
659 
660     g_free(maps);
661 }
662 
663 static void
get_process_memory_info(ProcInfo * info)664 get_process_memory_info(ProcInfo *info)
665 {
666     glibtop_proc_mem procmem;
667 
668 #ifdef HAVE_WNCK
669     info->memxserver = 0;
670 #ifdef GDK_WINDOWING_X11
671     if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
672         WnckResourceUsage xresources;
673 
674         wnck_pid_read_resource_usage (gdk_screen_get_display (gdk_screen_get_default ()),
675                                       info->pid,
676                                       &xresources);
677 
678         info->memxserver = xresources.total_bytes_estimate;
679     }
680 #endif // GDK_WINDOWING_X11
681 #endif // HAVE_WNCK
682 
683     glibtop_get_proc_mem(&procmem, info->pid);
684 
685     info->vmsize    = procmem.vsize;
686     info->memres    = procmem.resident;
687     info->memshared = procmem.share;
688 
689     get_process_memory_writable(info);
690 
691     // fake the smart memory column if writable is not available
692     info->mem = info->memwritable ? info->memwritable : info->memres;
693 #ifdef HAVE_WNCK
694     info->mem += info->memxserver;
695 #endif
696 }
697 
698 
699 static void
update_info_mutable_cols(ProcInfo * info)700 update_info_mutable_cols(ProcInfo *info)
701 {
702     ProcData * const procdata = ProcData::get_instance();
703     GtkTreeModel *model;
704     model = gtk_tree_view_get_model(GTK_TREE_VIEW(procdata->tree));
705 
706     using procman::tree_store_update;
707 
708     tree_store_update(model, &info->node, COL_STATUS, info->status);
709     tree_store_update(model, &info->node, COL_USER, info->user.c_str());
710     tree_store_update(model, &info->node, COL_VMSIZE, info->vmsize);
711     tree_store_update(model, &info->node, COL_MEMRES, info->memres);
712     tree_store_update(model, &info->node, COL_MEMWRITABLE, info->memwritable);
713     tree_store_update(model, &info->node, COL_MEMSHARED, info->memshared);
714 #ifdef HAVE_WNCK
715     tree_store_update(model, &info->node, COL_MEMXSERVER, info->memxserver);
716 #endif
717     tree_store_update(model, &info->node, COL_CPU, info->pcpu);
718     tree_store_update(model, &info->node, COL_CPU_TIME, info->cpu_time);
719     tree_store_update(model, &info->node, COL_DISK_READ_TOTAL, info->disk_read_bytes_total);
720     tree_store_update(model, &info->node, COL_DISK_WRITE_TOTAL, info->disk_write_bytes_total);
721     tree_store_update(model, &info->node, COL_DISK_READ_CURRENT, info->disk_read_bytes_current);
722     tree_store_update(model, &info->node, COL_DISK_WRITE_CURRENT, info->disk_write_bytes_current);
723     tree_store_update(model, &info->node, COL_START_TIME, info->start_time);
724     tree_store_update(model, &info->node, COL_NICE, info->nice);
725     tree_store_update(model, &info->node, COL_MEM, info->mem);
726     tree_store_update(model, &info->node, COL_WCHAN, info->wchan);
727     tree_store_update(model, &info->node, COL_CGROUP, info->cgroup_name);
728     tree_store_update(model, &info->node, COL_UNIT, info->unit);
729     tree_store_update(model, &info->node, COL_SESSION, info->session);
730     tree_store_update(model, &info->node, COL_SEAT, info->seat);
731     tree_store_update(model, &info->node, COL_OWNER, info->owner.c_str());
732 }
733 
734 
735 
736 static void
insert_info_to_tree(ProcInfo * info,ProcData * procdata,bool forced=false)737 insert_info_to_tree (ProcInfo *info, ProcData *procdata, bool forced = false)
738 {
739     GtkTreeModel *model;
740 
741     model = gtk_tree_view_get_model (GTK_TREE_VIEW (procdata->tree));
742 
743     if (procdata->config.show_tree) {
744 
745         ProcInfo *parent = 0;
746 
747         if (not forced)
748             parent = ProcInfo::find(info->ppid);
749 
750         if (parent) {
751             GtkTreePath *parent_node = gtk_tree_model_get_path(model, &parent->node);
752             gtk_tree_store_insert(GTK_TREE_STORE(model), &info->node, &parent->node, 0);
753 
754             if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(procdata->tree), parent_node)
755 #ifdef __linux__
756                 // on linuxes we don't want to expand kthreadd by default (always has pid 2)
757                 && (parent->pid != 2)
758 #endif
759             )
760                 gtk_tree_view_expand_row(GTK_TREE_VIEW(procdata->tree), parent_node, FALSE);
761             gtk_tree_path_free(parent_node);
762         } else
763             gtk_tree_store_insert(GTK_TREE_STORE(model), &info->node, NULL, 0);
764     }
765     else
766         gtk_tree_store_insert (GTK_TREE_STORE (model), &info->node, NULL, 0);
767 
768     gtk_tree_store_set (GTK_TREE_STORE (model), &info->node,
769                         COL_POINTER, info,
770                         COL_NAME, info->name,
771                         COL_ARGS, info->arguments,
772                         COL_TOOLTIP, info->tooltip,
773                         COL_PID, info->pid,
774                         COL_SECURITYCONTEXT, info->security_context,
775                         -1);
776 
777     procdata->pretty_table.set_icon(*info);
778 
779     procman_debug("inserted %d%s", info->pid, (forced ? " (forced)" : ""));
780 }
781 
782 
783 /* Removing a node with children - make sure the children are queued
784 ** to be readded.
785 */
786 template<typename List>
787 static void
remove_info_from_tree(ProcData * procdata,GtkTreeModel * model,ProcInfo * current,List & orphans,unsigned lvl=0)788 remove_info_from_tree (ProcData *procdata, GtkTreeModel *model,
789                        ProcInfo *current, List &orphans, unsigned lvl = 0)
790 {
791     GtkTreeIter child_node;
792 
793     if (std::find(orphans.begin(), orphans.end(), current) != orphans.end()) {
794         procman_debug("[%u] %d already removed from tree", lvl, int(current->pid));
795         return;
796     }
797 
798     procman_debug("[%u] pid %d, %d children", lvl, int(current->pid),
799                   gtk_tree_model_iter_n_children(model, &current->node));
800 
801     // it is not possible to iterate&erase over a treeview so instead we
802     // just pop one child after another and recursively remove it and
803     // its children
804 
805     while (gtk_tree_model_iter_children(model, &child_node, &current->node)) {
806         ProcInfo *child = 0;
807         gtk_tree_model_get(model, &child_node, COL_POINTER, &child, -1);
808         remove_info_from_tree(procdata, model, child, orphans, lvl + 1);
809     }
810 
811     g_assert(not gtk_tree_model_iter_has_child(model, &current->node));
812 
813     if (procdata->selected_process == current)
814         procdata->selected_process = NULL;
815 
816     orphans.push_back(current);
817     gtk_tree_store_remove(GTK_TREE_STORE(model), &current->node);
818     procman::poison(current->node, 0x69);
819 }
820 
821 static void
get_process_systemd_info(ProcInfo * info)822 get_process_systemd_info(ProcInfo *info)
823 {
824 #ifdef HAVE_SYSTEMD
825     uid_t uid;
826 
827     if (!LOGIND_RUNNING())
828         return;
829 
830     free(info->unit);
831     info->unit = NULL;
832     sd_pid_get_unit(info->pid, &info->unit);
833 
834     free(info->session);
835     info->session = NULL;
836     sd_pid_get_session(info->pid, &info->session);
837 
838     free(info->seat);
839     info->seat = NULL;
840 
841     if (info->session != NULL)
842         sd_session_get_seat(info->session, &info->seat);
843 
844     if (sd_pid_get_owner_uid(info->pid, &uid) >= 0)
845         info->owner = info->lookup_user(uid);
846     else
847         info->owner = "";
848 #endif
849 }
850 
851 static void
update_info(ProcData * procdata,ProcInfo * info)852 update_info (ProcData *procdata, ProcInfo *info)
853 {
854     glibtop_proc_state procstate;
855     glibtop_proc_uid procuid;
856     glibtop_proc_time proctime;
857     glibtop_proc_kernel prockernel;
858     glibtop_proc_io procio;
859 
860     glibtop_get_proc_kernel(&prockernel, info->pid);
861     g_strlcpy(info->wchan, prockernel.wchan, sizeof info->wchan);
862 
863     glibtop_get_proc_state (&procstate, info->pid);
864     info->status = procstate.state;
865 
866     glibtop_get_proc_uid (&procuid, info->pid);
867     glibtop_get_proc_time (&proctime, info->pid);
868     glibtop_get_proc_io (&procio, info->pid);
869 
870     get_process_memory_info(info);
871 
872     info->set_user(procstate.uid);
873 
874     // if the cpu time has increased reset the status to running
875     // regardless of kernel state (https://bugzilla.gnome.org/606579)
876     guint64 difference = proctime.rtime - info->cpu_time;
877     if (difference > 0)
878         info->status = GLIBTOP_PROCESS_RUNNING;
879     info->pcpu = difference * 100 / procdata->cpu_total_time;
880     info->pcpu = MIN(info->pcpu, 100);
881 
882     if (not procdata->config.solaris_mode)
883       info->pcpu *= procdata->config.num_cpus;
884 
885     ProcInfo::cpu_times[info->pid] = info->cpu_time = proctime.rtime;
886     info->nice = procuid.nice;
887 
888     gdouble update_interval_seconds = procdata->config.update_interval / 1000.0;
889     difference = procio.disk_wbytes - info->disk_write_bytes_total;
890     info->disk_write_bytes_current = difference > update_interval_seconds ? difference/update_interval_seconds : 0ULL;
891     difference = procio.disk_rbytes - info->disk_read_bytes_total;
892     info->disk_read_bytes_current = difference > update_interval_seconds ? difference/update_interval_seconds : 0ULL;
893 
894     info->disk_write_bytes_total = procio.disk_wbytes;
895     info->disk_read_bytes_total = procio.disk_rbytes;
896 
897     // set the ppid only if one can exist
898     // i.e. pid=0 can never have a parent
899     if (info->pid > 0) {
900         info->ppid = procuid.ppid;
901     }
902 
903     g_assert(info->pid != info->ppid);
904     g_assert(info->ppid != -1 || info->pid == 0);
905 
906     /* get cgroup data */
907     get_process_cgroup_info(info);
908 
909     get_process_systemd_info(info);
910 }
911 
912 
ProcInfo(pid_t pid)913 ProcInfo::ProcInfo(pid_t pid)
914     : node(),
915       surface(),
916       tooltip(NULL),
917       name(NULL),
918       arguments(NULL),
919       security_context(NULL),
920       pid(pid),
921       ppid(-1),
922       uid(-1)
923 {
924     ProcInfo * const info = this;
925     glibtop_proc_state procstate;
926     glibtop_proc_time proctime;
927     glibtop_proc_args procargs;
928     gchar** arguments;
929 
930     glibtop_get_proc_state (&procstate, pid);
931     glibtop_get_proc_time (&proctime, pid);
932     arguments = glibtop_get_proc_argv (&procargs, pid, 0);
933 
934     /* FIXME : wrong. name and arguments may change with exec* */
935     get_process_name (info, procstate.cmd, static_cast<const GStrv>(arguments));
936 
937     std::string tooltip = make_string(g_strjoinv(" ", arguments));
938     if (tooltip.empty())
939         tooltip = procstate.cmd;
940 
941     info->tooltip = g_markup_escape_text(tooltip.c_str(), -1);
942 
943     info->arguments = g_strescape(tooltip.c_str(), "\\\"");
944     g_strfreev(arguments);
945 
946     guint64 cpu_time = proctime.rtime;
947     std::map<pid_t, guint64>::iterator it(ProcInfo::cpu_times.find(pid));
948     if (it != ProcInfo::cpu_times.end())
949     {
950         if (proctime.rtime >= it->second)
951           cpu_time = it->second;
952     }
953     info->cpu_time = cpu_time;
954     info->start_time = proctime.start_time;
955 
956     get_process_selinux_context (info);
957     info->cgroup_name = NULL;
958     get_process_cgroup_info(info);
959 
960     info->unit = info->session = info->seat = NULL;
961     get_process_systemd_info(info);
962 }
963 
964 static void
refresh_list(ProcData * procdata,const pid_t * pid_list,const guint n)965 refresh_list (ProcData *procdata, const pid_t* pid_list, const guint n)
966 {
967     typedef std::list<ProcInfo*> ProcList;
968     ProcList addition;
969 
970     GtkTreeModel *model = gtk_tree_view_get_model (GTK_TREE_VIEW (procdata->tree));
971     guint i;
972 
973     // Add or update processes in the process list
974     for(i = 0; i < n; ++i) {
975         ProcInfo *info = ProcInfo::find(pid_list[i]);
976 
977         if (!info) {
978             info = new ProcInfo(pid_list[i]);
979             ProcInfo::all[info->pid] = info;
980             addition.push_back(info);
981         }
982 
983         update_info (procdata, info);
984     }
985 
986 
987     // Remove dead processes from the process list and from the
988     // tree. children are queued to be readded at the right place
989     // in the tree.
990 
991     const std::set<pid_t> pids(pid_list, pid_list + n);
992 
993     ProcInfo::Iterator it(ProcInfo::begin());
994 
995     while (it != ProcInfo::end()) {
996         ProcInfo * const info = it->second;
997         ProcInfo::Iterator next(it);
998         ++next;
999 
1000         if (pids.find(info->pid) == pids.end()) {
1001             procman_debug("ripping %d", info->pid);
1002             remove_info_from_tree(procdata, model, info, addition);
1003             addition.remove(info);
1004             ProcInfo::all.erase(it);
1005             delete info;
1006         }
1007 
1008         it = next;
1009     }
1010 
1011     // INVARIANT
1012     // pid_list == ProcInfo::all + addition
1013 
1014 
1015     if (procdata->config.show_tree) {
1016 
1017         // insert process in the tree. walk through the addition list
1018         // (new process + process that have a new parent). This loop
1019         // handles the dependencies because we cannot insert a process
1020         // until its parent is in the tree.
1021 
1022         std::set<pid_t> in_tree(pids);
1023 
1024         for (ProcList::iterator it(addition.begin()); it != addition.end(); ++it)
1025             in_tree.erase((*it)->pid);
1026 
1027 
1028         while (not addition.empty()) {
1029             procman_debug("looking for %d parents", int(addition.size()));
1030             ProcList::iterator it(addition.begin());
1031 
1032             while (it != addition.end()) {
1033                 procman_debug("looking for %d's parent with ppid %d",
1034                               int((*it)->pid), int((*it)->ppid));
1035 
1036 
1037                 // inserts the process in the treeview if :
1038                 // - it has no parent (ppid = -1),
1039                 //   ie it is for example the [kernel] on FreeBSD
1040                 // - it is init
1041                 // - its parent is already in tree
1042                 // - its parent is unreachable
1043                 //
1044                 // rounds == 2 means that addition contains processes with
1045                 // unreachable parents
1046                 //
1047                 // FIXME: this is broken if the unreachable parent becomes active
1048                 // i.e. it gets active or changes ower
1049                 // so we just clear the tree on __each__ update
1050                 // see proctable_update_list (ProcData * const procdata)
1051 
1052 
1053                 if ((*it)->ppid <= 0 or in_tree.find((*it)->ppid) != in_tree.end()) {
1054                     insert_info_to_tree(*it, procdata);
1055                     in_tree.insert((*it)->pid);
1056                     it = addition.erase(it);
1057                     continue;
1058                 }
1059 
1060                 ProcInfo *parent = ProcInfo::find((*it)->ppid);
1061                 // if the parent is unreachable
1062                 if (not parent) {
1063                     // or std::find(addition.begin(), addition.end(), parent) == addition.end()) {
1064                     insert_info_to_tree(*it, procdata, true);
1065                     in_tree.insert((*it)->pid);
1066                     it = addition.erase(it);
1067                     continue;
1068                 }
1069 
1070                 ++it;
1071             }
1072         }
1073     }
1074     else {
1075         // don't care of the tree
1076         for (ProcList::iterator it(addition.begin()); it != addition.end(); ++it)
1077             insert_info_to_tree(*it, procdata);
1078     }
1079 
1080 
1081     for (ProcInfo::Iterator it(ProcInfo::begin()); it != ProcInfo::end(); ++it)
1082         update_info_mutable_cols(it->second);
1083 }
1084 
1085 
1086 static void
proctable_update_list(ProcData * const procdata)1087 proctable_update_list (ProcData * const procdata)
1088 {
1089     pid_t* pid_list;
1090     glibtop_proclist proclist;
1091     glibtop_cpu cpu;
1092     gint which, arg;
1093     procman::SelectionMemento selection;
1094 
1095     switch (procdata->config.whose_process) {
1096         case ALL_PROCESSES:
1097             which = GLIBTOP_KERN_PROC_ALL;
1098             arg = 0;
1099             break;
1100 
1101         case ACTIVE_PROCESSES:
1102             which = GLIBTOP_KERN_PROC_ALL | GLIBTOP_EXCLUDE_IDLE;
1103             arg = 0;
1104             if (procdata->config.show_tree)
1105             {
1106                 selection.save(procdata->tree);
1107             }
1108             break;
1109 
1110         default:
1111             which = GLIBTOP_KERN_PROC_UID;
1112             arg = getuid ();
1113             if (procdata->config.show_tree)
1114             {
1115                 selection.save(procdata->tree);
1116             }
1117             break;
1118     }
1119 
1120     pid_list = glibtop_get_proclist (&proclist, which, arg);
1121 
1122     /* FIXME: total cpu time elapsed should be calculated on an individual basis here
1123     ** should probably have a total_time_last gint in the ProcInfo structure */
1124     glibtop_get_cpu (&cpu);
1125     procdata->cpu_total_time = MAX(cpu.total - procdata->cpu_total_time_last, 1);
1126     procdata->cpu_total_time_last = cpu.total;
1127 
1128     // FIXME: not sure if glibtop always returns a sorted list of pid
1129     // but it is important otherwise refresh_list won't find the parent
1130     std::sort(pid_list, pid_list + proclist.number);
1131 
1132     refresh_list (procdata, pid_list, proclist.number);
1133 
1134     selection.restore(procdata->tree);
1135 
1136     g_free (pid_list);
1137 
1138     /* proclist.number == g_list_length(procdata->info) == g_hash_table_size(procdata->pids) */
1139 }
1140 
1141 
1142 void
proctable_update(ProcData * const procdata)1143 proctable_update (ProcData * const procdata)
1144 {
1145     char* string;
1146 
1147     string = make_loadavg_string();
1148     gtk_label_set_text (GTK_LABEL(procdata->loadavg), string);
1149     g_free (string);
1150 
1151     proctable_update_list (procdata);
1152 }
1153 
1154 
1155 void
proctable_free_table(ProcData * const procdata)1156 proctable_free_table (ProcData * const procdata)
1157 {
1158     for (ProcInfo::Iterator it(ProcInfo::begin()); it != ProcInfo::end(); ++it)
1159         delete it->second;
1160 
1161     ProcInfo::all.clear();
1162 }
1163 
1164 
1165 void
proctable_clear_tree(ProcData * const procdata)1166 proctable_clear_tree (ProcData * const procdata)
1167 {
1168     GtkTreeModel *model;
1169 
1170     model = gtk_tree_view_get_model (GTK_TREE_VIEW (procdata->tree));
1171 
1172     gtk_tree_store_clear (GTK_TREE_STORE (model));
1173 
1174     proctable_free_table (procdata);
1175 
1176     update_sensitivity(procdata);
1177 }
1178 
1179 
1180 char*
make_loadavg_string(void)1181 make_loadavg_string(void)
1182 {
1183     glibtop_loadavg buf;
1184 
1185     glibtop_get_loadavg(&buf);
1186 
1187     return g_strdup_printf(
1188         _("Load averages for the last 1, 5, 15 minutes: "
1189           "%0.2f, %0.2f, %0.2f"),
1190         buf.loadavg[0],
1191         buf.loadavg[1],
1192         buf.loadavg[2]);
1193 }
1194 
1195 
1196 
1197 void
set_icon(Glib::RefPtr<Gdk::Pixbuf> icon)1198 ProcInfo::set_icon(Glib::RefPtr<Gdk::Pixbuf> icon)
1199 {
1200   this->surface = gdk_cairo_surface_create_from_pixbuf (icon->gobj(), 0, NULL);
1201 
1202   GtkTreeModel *model;
1203   model = gtk_tree_view_get_model(GTK_TREE_VIEW(ProcData::get_instance()->tree));
1204   gtk_tree_store_set(GTK_TREE_STORE(model), &this->node,
1205                      COL_SURFACE, this->surface,
1206                     -1);
1207 }
1208