proctable.cpp 42.2 KB
Newer Older
1
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
Kevin Vandersloot's avatar
Kevin Vandersloot committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* Procman tree view and process updating
 * Copyright (C) 2001 Kevin Vandersloot
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
16
 * License along with this program; if not, see <http://www.gnu.org/licenses/>.
Kevin Vandersloot's avatar
Kevin Vandersloot committed
17 18 19
 *
 */

Benoît Dejean's avatar
Benoît Dejean committed
20 21 22

#include <config.h>

23

24
#include <string.h>
Benoît Dejean's avatar
Benoît Dejean committed
25
#include <math.h>
26
#include <glib/gi18n.h>
27
#include <glib/gprintf.h>
Kevin Vandersloot's avatar
Kevin Vandersloot committed
28 29 30
#include <glibtop.h>
#include <glibtop/proclist.h>
#include <glibtop/procstate.h>
31
#include <glibtop/procio.h>
Kevin Vandersloot's avatar
Kevin Vandersloot committed
32
#include <glibtop/procmem.h>
Benoît Dejean's avatar
Benoît Dejean committed
33
#include <glibtop/procmap.h>
Kevin Vandersloot's avatar
Kevin Vandersloot committed
34 35
#include <glibtop/proctime.h>
#include <glibtop/procuid.h>
36
#include <glibtop/procargs.h>
Benoît Dejean's avatar
Benoît Dejean committed
37
#include <glibtop/prockernel.h>
Kevin Vandersloot's avatar
Kevin Vandersloot committed
38 39
#include <glibtop/mem.h>
#include <glibtop/swap.h>
40
#include <sys/stat.h>
Kevin Vandersloot's avatar
Kevin Vandersloot committed
41
#include <pwd.h>
42
#include <time.h>
43

44
#include <set>
45
#include <list>
46

47 48 49 50 51
#ifdef HAVE_WNCK
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
#include <libwnck/libwnck.h>
#endif

52
#include "application.h"
Kevin Vandersloot's avatar
Kevin Vandersloot committed
53
#include "proctable.h"
54
#include "prettytable.h"
55
#include "util.h"
56
#include "interface.h"
57
#include "selinux.h"
58
#include "settings-keys.h"
59
#include "cgroups.h"
Ben's avatar
Ben committed
60
#include "legacy/treeview.h"
61
#include "systemd.h"
62

63 64 65 66
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif

67 68 69 70 71 72
ProcInfo* ProcList::find(pid_t pid)
{
    auto it = data.find(pid);
    return (it == data.end() ? nullptr : &it->second);
}

73
static void
74
cb_save_tree_state(gpointer, gpointer data)
75
{
76
    GsmApplication * const app = static_cast<GsmApplication *>(data);
77

78
    gsm_tree_view_save_state (app->tree);
79 80
}

81 82 83
static void
cb_proctree_destroying (GtkTreeView *self, gpointer data)
{
84
    g_signal_handlers_disconnect_by_func (self,
85
                                          (gpointer) cb_save_tree_state,
86 87 88
                                          data);

    g_signal_handlers_disconnect_by_func (gtk_tree_view_get_model (self),
89
                                          (gpointer) cb_save_tree_state,
90
                                          data);
91
}
92

93 94 95
static gboolean
cb_tree_button_pressed (GtkWidget *widget, GdkEventButton *event, gpointer data)
{
96
    GsmApplication *app = (GsmApplication *) data;
97
    GtkTreePath *path;
98

99 100 101 102 103 104 105
    if (!gdk_event_triggers_context_menu ((GdkEvent *) event))
        return FALSE;

    if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (app->tree), event->x, event->y, &path, NULL, NULL, NULL))
        return FALSE;

    if (!gtk_tree_selection_path_is_selected (app->selection, path)) {
106
        if (!(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
107 108
            gtk_tree_selection_unselect_all (app->selection);
        gtk_tree_selection_select_path (app->selection, path);
109 110
    }

111 112
    gtk_tree_path_free (path);

Robert Roth's avatar
Robert Roth committed
113
    gtk_menu_popup_at_pointer (GTK_MENU (app->popup_menu), NULL);
114
    return TRUE;
115 116 117 118 119
}

static gboolean
cb_tree_popup_menu (GtkWidget *widget, gpointer data)
{
120
    GsmApplication *app = (GsmApplication *) data;
121

Robert Roth's avatar
Robert Roth committed
122
    gtk_menu_popup_at_pointer (GTK_MENU (app->popup_menu), NULL);
123 124 125 126

    return TRUE;
}

127
void
128 129 130 131 132 133 134 135 136 137 138
get_last_selected (GtkTreeModel *model, GtkTreePath *path,
                   GtkTreeIter *iter, gpointer data)
{
    ProcInfo **info = (ProcInfo**) data;

    gtk_tree_model_get (model, iter, COL_POINTER, info, -1);
}

void
cb_row_selected (GtkTreeSelection *selection, gpointer data)
{
139
    GsmApplication *app = (GsmApplication *) data;
140 141 142

    app->selection = selection;

143
    ProcInfo *selected_process = NULL;
144 145 146 147 148

    /* get the most recent selected process and determine if there are
    ** no selected processes
    */
    gtk_tree_selection_selected_foreach (app->selection, get_last_selected,
149 150
                                         &selected_process);
    if (selected_process) {
151
        GVariant *priority;
152
        gint nice = selected_process->nice;
153
        if (nice < -7)
154
            priority = g_variant_new_int32 (-20);
155
        else if (nice < -2)
156
            priority = g_variant_new_int32 (-5);
157
        else if (nice < 3)
158
            priority = g_variant_new_int32 (0);
159
        else if (nice < 7)
160
            priority = g_variant_new_int32 (5);
161
        else
162
            priority = g_variant_new_int32 (19);
163 164 165 166 167 168 169 170 171

        GAction *action = g_action_map_lookup_action (G_ACTION_MAP (app->main_window),
                                                      "priority");

        g_action_change_state (action, priority);
    }
    update_sensitivity(app);
}

172 173 174
static gint
cb_timeout (gpointer data)
{
175
    GsmApplication *app = (GsmApplication *) data;
176 177
    guint new_interval;

178
    proctable_update (app);
179 180 181 182 183 184 185 186 187 188 189

    if (app->smooth_refresh->get(new_interval)) {
        app->timeout = g_timeout_add(new_interval,
                                     cb_timeout,
                                     app);
        return G_SOURCE_REMOVE;
    }

    return G_SOURCE_CONTINUE;
}

190 191 192
static void
cb_refresh_icons (GtkIconTheme *theme, gpointer data)
{
193
    GsmApplication *app = (GsmApplication *) data;
194 195 196 197 198

    if(app->timeout) {
        g_source_remove (app->timeout);
    }

199
    for (auto& v : app->processes) {
200
        app->pretty_table->set_icon(v.second);
201 202 203 204 205
    }

    cb_timeout(app);
}

206
static gboolean
207
iter_matches_search_key (GtkTreeModel *model, GtkTreeIter *iter, const gchar *search_text)
208 209 210
{
    char *name;
    char *user;
211 212 213
    pid_t pid;
    char *pids;
    char *args;
214
    gboolean found;
215 216
    char *search_pattern;
    char **keys;
217
    Glib::RefPtr<Glib::Regex> regex;
218 219 220 221

    gtk_tree_model_get (model, iter,
                        COL_NAME, &name,
                        COL_USER, &user,
222 223
                        COL_PID, &pid,
                        COL_ARGS, &args,
224 225
                        -1);

226
    pids = g_strdup_printf ("%d", pid);
227

228 229
    keys = g_strsplit_set(search_text, " |", -1);
    search_pattern = g_strjoinv ("|", keys);
230 231 232 233 234
    try {
        regex = Glib::Regex::create(search_pattern, Glib::REGEX_CASELESS);
    } catch (const Glib::Error& ex) {
        regex = Glib::Regex::create(Glib::Regex::escape_string(search_pattern), Glib::REGEX_CASELESS);
    }
235 236 237 238 239 240

    found = (name && regex->match(name)) || (user && regex->match(user))
            || (pids && regex->match(pids)) || (args && regex->match(args));

    g_strfreev (keys);
    g_free (search_pattern);
241 242
    g_free (name);
    g_free (user);
243 244
    g_free (args);
    g_free (pids);
245 246 247 248 249 250

    return found;
}

static gboolean
process_visibility_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
251
{
252
    GsmApplication * const app = static_cast<GsmApplication *>(data);
253 254
    const gchar * search_text = app->search_entry == NULL ? "" : gtk_entry_get_text (GTK_ENTRY (app->search_entry));
    GtkTreePath *tree_path = gtk_tree_model_get_path (model, iter);
255

256 257
    if (strcmp (search_text, "") == 0) {
        gtk_tree_path_free (tree_path);
258
        return TRUE;
259
    }
260

261 262
	// in case we are in dependencies view, we show (and expand) rows not matching the text, but having a matching child
    gboolean match = false;
Artem Vorotnikov's avatar
Artem Vorotnikov committed
263
    if (app->settings->get_boolean (GSM_SETTING_SHOW_DEPENDENCIES)) {
264 265
        GtkTreeIter child;
        if (gtk_tree_model_iter_children (model, &child, iter)) {
266 267 268 269 270
            gboolean child_match = FALSE;
            do {
                child_match = process_visibility_func (model, &child, data);
            } while (gtk_tree_model_iter_next (model, &child) && !child_match);
            match = child_match;
271
        }
272 273

        match |= iter_matches_search_key (model, iter, search_text);
274 275 276 277 278 279
        // TODO auto-expand items not matching the search string but having matching children
        // complicated because of treestore nested in treemodelfilter nested in treemodelsort
        // expand to path requires the path string in the treemodelsort, but tree_path is the path in the double nested treestore
        //if (match && (strlen (search_text) > 0)) {
        //    gtk_tree_view_expand_to_path (GTK_TREE_VIEW (app->tree), tree_path);
        //}
280 281 282 283

    } else {
        match = iter_matches_search_key (model, iter, search_text);
    }
284 285
        
    gtk_tree_path_free (tree_path);
286

287
    return match;
288
}
289 290

static void
291
proctable_clear_tree (GsmApplication * const app)
292 293 294 295 296 297 298 299 300 301 302 303 304
{
    GtkTreeModel *model;

    model =  gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (
             gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT (
             gtk_tree_view_get_model (GTK_TREE_VIEW(app->tree))))));

    gtk_tree_store_clear (GTK_TREE_STORE (model));

    proctable_free_table (app);

    update_sensitivity(app);
}
Artem Vorotnikov's avatar
Artem Vorotnikov committed
305

306
static void
Artem Vorotnikov's avatar
Artem Vorotnikov committed
307
cb_show_dependencies_changed(Gio::Settings& settings, Glib::ustring key, GsmApplication* app) {
308 309
    if (app->timeout) {
        gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (app->tree),
Artem Vorotnikov's avatar
Artem Vorotnikov committed
310
                                          settings.get_boolean (GSM_SETTING_SHOW_DEPENDENCIES));
311

312 313 314
        proctable_clear_tree (app);
        proctable_update (app);
    }
315 316 317
}

static void
Artem Vorotnikov's avatar
Artem Vorotnikov committed
318
cb_show_whose_processes_changed(Gio::Settings& settings, Glib::ustring key, GsmApplication* app) {
319 320 321 322
    if (app->timeout) {
        proctable_clear_tree (app);
        proctable_update (app);
    }
323 324
}

325
GsmTreeView *
326
proctable_new (GsmApplication * const app)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
327
{
328
    GsmTreeView *proctree;
329
    GtkTreeStore *model;
330 331 332
    GtkTreeModelFilter *model_filter;
    GtkTreeModelSort *model_sort;
    
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
    GtkTreeViewColumn *column;
    GtkCellRenderer *cell_renderer;
    const gchar *titles[] = {
        N_("Process Name"),
        N_("User"),
        N_("Status"),
        N_("Virtual Memory"),
        N_("Resident Memory"),
        N_("Writable Memory"),
        N_("Shared Memory"),
        N_("X Server Memory"),
        /* xgettext:no-c-format */ N_("% CPU"),
        N_("CPU Time"),
        N_("Started"),
        N_("Nice"),
        N_("ID"),
        N_("Security Context"),
        N_("Command Line"),
        N_("Memory"),
352
        /* xgettext: combined noun, the function the process is waiting in, see wchan ps(1) */
353
        N_("Waiting Channel"),
354
        N_("Control Group"),
355 356
        N_("Unit"),
        N_("Session"),
357 358
        /* TRANSLATORS: Seat = i.e. the physical seat the session of the process belongs to, only
	for multi-seat environments. See http://en.wikipedia.org/wiki/Multiseat_configuration */
359 360
        N_("Seat"),
        N_("Owner"),
361 362 363 364
        N_("Disk read total"),
        N_("Disk write total"),
        N_("Disk read"),
        N_("Disk write"),
365
        N_("Priority"),
366 367 368 369 370
        NULL,
        "POINTER"
    };

    gint i;
Artem Vorotnikov's avatar
Artem Vorotnikov committed
371
    auto settings = g_settings_get_child (app->settings->gobj (), GSM_SETTINGS_CHILD_PROCESSES);
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
    model = gtk_tree_store_new (NUM_COLUMNS,
                                G_TYPE_STRING,      /* Process Name */
                                G_TYPE_STRING,      /* User         */
                                G_TYPE_UINT,        /* Status       */
                                G_TYPE_ULONG,       /* VM Size      */
                                G_TYPE_ULONG,       /* Resident Memory */
                                G_TYPE_ULONG,       /* Writable Memory */
                                G_TYPE_ULONG,       /* Shared Memory */
                                G_TYPE_ULONG,       /* X Server Memory */
                                G_TYPE_UINT,        /* % CPU        */
                                G_TYPE_UINT64,      /* CPU time     */
                                G_TYPE_ULONG,       /* Started      */
                                G_TYPE_INT,         /* Nice         */
                                G_TYPE_UINT,        /* ID           */
                                G_TYPE_STRING,      /* Security Context */
                                G_TYPE_STRING,      /* Arguments    */
                                G_TYPE_ULONG,       /* Memory       */
                                G_TYPE_STRING,      /* wchan        */
390
                                G_TYPE_STRING,      /* Cgroup       */
391 392 393 394
                                G_TYPE_STRING,      /* Unit         */
                                G_TYPE_STRING,      /* Session      */
                                G_TYPE_STRING,      /* Seat         */
                                G_TYPE_STRING,      /* Owner        */
395 396 397 398
                                G_TYPE_UINT64,      /* Disk read total */
                                G_TYPE_UINT64,      /* Disk write total*/
                                G_TYPE_UINT64,      /* Disk read    */
                                G_TYPE_UINT64,      /* Disk write   */
399
                                G_TYPE_STRING,      /* Priority     */
400 401 402 403 404
                                GDK_TYPE_PIXBUF,    /* Icon         */
                                G_TYPE_POINTER,     /* ProcInfo     */
                                G_TYPE_STRING       /* Sexy tooltip */
        );

405 406
    model_filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (model), NULL));
        
407
    gtk_tree_model_filter_set_visible_func(model_filter, process_visibility_func, app, NULL);
408 409 410
    
    model_sort = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (model_filter)));
    
411 412 413
    proctree = gsm_tree_view_new (settings, TRUE);
    gtk_tree_view_set_model (GTK_TREE_VIEW (proctree), GTK_TREE_MODEL (model_sort));

414
    gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (proctree), COL_TOOLTIP);
Artem Vorotnikov's avatar
Artem Vorotnikov committed
415
    gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (proctree), app->settings->get_boolean (GSM_SETTING_SHOW_DEPENDENCIES));
416
    gtk_tree_view_set_enable_search (GTK_TREE_VIEW (proctree), FALSE);
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
    g_object_unref (G_OBJECT (model));

    column = gtk_tree_view_column_new ();

    cell_renderer = gtk_cell_renderer_pixbuf_new ();
    gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
    gtk_tree_view_column_set_attributes (column, cell_renderer,
                                         "pixbuf", COL_PIXBUF,
                                         NULL);

    cell_renderer = gtk_cell_renderer_text_new ();
    gtk_tree_view_column_pack_start (column, cell_renderer, FALSE);
    gtk_tree_view_column_set_attributes (column, cell_renderer,
                                         "text", COL_NAME,
                                         NULL);
    gtk_tree_view_column_set_title (column, _(titles[0]));
433

434 435 436 437
    gtk_tree_view_column_set_sort_column_id (column, COL_NAME);
    gtk_tree_view_column_set_resizable (column, TRUE);
    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
    gtk_tree_view_column_set_min_width (column, 1);
438
    gtk_tree_view_column_set_reorderable(column, TRUE);
439

440
    gsm_tree_view_append_and_bind_column (proctree, column);
441 442
    gtk_tree_view_set_expander_column (GTK_TREE_VIEW (proctree), column);

443
    for (i = COL_USER; i <= COL_PRIORITY; i++) {
444
        GtkTreeViewColumn *col;
445
        GtkCellRenderer *cell;
446

447
#ifndef HAVE_WNCK
Stefano Facchini's avatar
Stefano Facchini committed
448
        if (i == COL_MEMXSERVER)
449 450
          continue;
#endif
451 452 453 454

        if (i == COL_MEMWRITABLE)
            continue;

455 456 457 458 459 460 461
        cell = gtk_cell_renderer_text_new();
        col = gtk_tree_view_column_new();
        gtk_tree_view_column_pack_start(col, cell, TRUE);
        gtk_tree_view_column_set_title(col, _(titles[i]));
        gtk_tree_view_column_set_resizable(col, TRUE);
        gtk_tree_view_column_set_sort_column_id(col, i);
        gtk_tree_view_column_set_reorderable(col, TRUE);
462
        gsm_tree_view_append_and_bind_column (proctree, col);
463

464 465
        // type
        switch (i) {
466
#ifdef HAVE_WNCK
467 468 469 470 471 472
            case COL_MEMXSERVER:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::size_cell_data_func,
                                                        GUINT_TO_POINTER(i),
                                                        NULL);
                break;
473
#endif
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
            case COL_VMSIZE:
            case COL_MEMRES:
            case COL_MEMSHARED:
            case COL_MEM:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::size_na_cell_data_func,
                                                        GUINT_TO_POINTER(i),
                                                        NULL);
                break;

            case COL_CPU_TIME:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::duration_cell_data_func,
                                                        GUINT_TO_POINTER(i),
                                                        NULL);
                break;

            case COL_START_TIME:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::time_cell_data_func,
                                                        GUINT_TO_POINTER(i),
                                                        NULL);
                break;

            case COL_STATUS:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::status_cell_data_func,
                                                        GUINT_TO_POINTER(i),
                                                        NULL);
                break;
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
            case COL_DISK_READ_TOTAL:
            case COL_DISK_WRITE_TOTAL:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::size_na_cell_data_func,
                                                        GUINT_TO_POINTER(i),
                                                        NULL);
                break;
            case COL_DISK_READ_CURRENT:
            case COL_DISK_WRITE_CURRENT:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::io_rate_cell_data_func,
                                                        GUINT_TO_POINTER(i),
                                                        NULL);

                break;
519 520 521 522 523
            case COL_PRIORITY:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::priority_cell_data_func,
                                                        GUINT_TO_POINTER(COL_NICE),
                                                        NULL);
524 525 526 527 528 529 530 531
                break;
            default:
                gtk_tree_view_column_set_attributes(col, cell, "text", i, NULL);
                break;
        }

        // sorting
        switch (i) {
532
#ifdef HAVE_WNCK
533
            case COL_MEMXSERVER:
534
#endif
535 536 537 538 539 540
            case COL_VMSIZE:
            case COL_MEMRES:
            case COL_MEMSHARED:
            case COL_MEM:
            case COL_CPU:
            case COL_CPU_TIME:
541 542 543 544
            case COL_DISK_READ_TOTAL:
            case COL_DISK_WRITE_TOTAL:
            case COL_DISK_READ_CURRENT:
            case COL_DISK_WRITE_CURRENT:
545
            case COL_START_TIME:
546 547 548 549
                gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model_sort), i,
                                                 procman::number_compare_func,
                                                 GUINT_TO_POINTER (i),
                                                 NULL);
550 551
                break;
            case COL_PRIORITY:
552 553 554
                gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model_sort), i,
                                                 procman::priority_compare_func,
                                                 GUINT_TO_POINTER (COL_NICE), NULL);
555
                break;
556 557 558 559
            default:
                break;
        }

560
        // xalign
561 562 563 564 565
        switch(i)
        {
            case COL_VMSIZE:
            case COL_MEMRES:
            case COL_MEMSHARED:
566
#ifdef HAVE_WNCK
567
            case COL_MEMXSERVER:
568
#endif
569 570 571
            case COL_CPU:
            case COL_NICE:
            case COL_PID:
572 573 574 575
            case COL_DISK_READ_TOTAL:
            case COL_DISK_WRITE_TOTAL:
            case COL_DISK_READ_CURRENT:
            case COL_DISK_WRITE_CURRENT:
576 577 578 579 580 581
            case COL_CPU_TIME:
            case COL_MEM:
                g_object_set(G_OBJECT(cell), "xalign", 1.0f, NULL);
                break;
        }

582
        gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
583 584 585 586 587 588 589 590 591 592
        // sizing
        switch (i) {
            case COL_ARGS:
                gtk_tree_view_column_set_min_width(col, 150);
                break;
            default:
                gtk_tree_view_column_set_min_width(column, 20);
                break;
        }
    }
593
    app->tree = proctree;
594
    app->top_of_tree = NULL;
595 596
    app->last_vscroll_max = 0;
    app->last_vscroll_value = 0;
597

598
    if (!cgroups_enabled ())
599
        gsm_tree_view_add_excluded_column (proctree, COL_CGROUP);
600

601
    if (!procman::systemd_logind_running())
602
    {
603 604 605 606
        gsm_tree_view_add_excluded_column (proctree, COL_UNIT);
        gsm_tree_view_add_excluded_column (proctree, COL_SESSION);
        gsm_tree_view_add_excluded_column (proctree, COL_SEAT);
        gsm_tree_view_add_excluded_column (proctree, COL_OWNER);
607 608
    }

609
    if (!can_show_security_context_column ())
610
        gsm_tree_view_add_excluded_column (proctree, COL_SECURITYCONTEXT);
611

612
    gsm_tree_view_load_state (proctree);
613

614 615
    GtkIconTheme* theme = gtk_icon_theme_get_default();
    g_signal_connect(G_OBJECT (theme), "changed", G_CALLBACK (cb_refresh_icons), app);
616

617 618 619 620
    app->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (proctree));
    gtk_tree_selection_set_mode (app->selection, GTK_SELECTION_MULTIPLE);
    
    g_signal_connect (G_OBJECT (app->selection),
621
                      "changed",
622
                      G_CALLBACK (cb_row_selected), app);
623
    g_signal_connect (G_OBJECT (proctree), "popup_menu",
624
                      G_CALLBACK (cb_tree_popup_menu), app);
625
    g_signal_connect (G_OBJECT (proctree), "button_press_event",
626 627
                      G_CALLBACK (cb_tree_button_pressed), app);

628 629
    g_signal_connect (G_OBJECT (proctree), "destroy",
                      G_CALLBACK (cb_proctree_destroying),
630
                      app);
631

632
    g_signal_connect (G_OBJECT (proctree), "columns-changed",
633
                      G_CALLBACK (cb_save_tree_state), app);
634

635
    g_signal_connect (G_OBJECT (model_sort), "sort-column-changed",
636
                      G_CALLBACK (cb_save_tree_state), app);
637

Artem Vorotnikov's avatar
Artem Vorotnikov committed
638 639 640
    app->settings->signal_changed(GSM_SETTING_SHOW_DEPENDENCIES).connect([app](const Glib::ustring& key) {
        cb_show_dependencies_changed(*app->settings.operator->(), key, app);
    });
641

Artem Vorotnikov's avatar
Artem Vorotnikov committed
642 643 644
    app->settings->signal_changed(GSM_SETTING_SHOW_WHOSE_PROCESSES).connect([app](const Glib::ustring& key) {
        cb_show_whose_processes_changed(*app->settings.operator->(), key, app);
    });
645

646
    gtk_widget_show (GTK_WIDGET (proctree));
647

648
    return proctree;
Kevin Vandersloot's avatar
Kevin Vandersloot committed
649 650
}

651
static void
Benoît Dejean's avatar
Benoît Dejean committed
652
get_process_name (ProcInfo *info,
653
                  const gchar *cmd, const GStrv args)
654
{
655 656 657 658 659 660 661 662 663
    if (args) {
        // look for /usr/bin/very_long_name
        // and also /usr/bin/interpreter /usr/.../very_long_name
        // which may have use prctl to alter 'cmd' name
        for (int i = 0; i != 2 && args[i]; ++i) {
            char* basename;
            basename = g_path_get_basename(args[i]);

            if (g_str_has_prefix(basename, cmd)) {
Ben's avatar
Ben committed
664
                info->name = make_string(basename);
665 666 667 668 669 670
                return;
            }

            g_free(basename);
        }
    }
671
    info->name = cmd;
672 673
}

674 675
std::string
ProcInfo::lookup_user(guint uid)
676
{
677 678
    static std::map<guint, std::string> users;
    auto p = users.insert({uid, ""});
679

680
    // procman_debug("User lookup for uid %u: %s", uid, (p.second ? "MISS" : "HIT"));
681

682 683 684
    if (p.second) {
        struct passwd* pwd;
        pwd = getpwuid(uid);
685

686 687 688 689 690 691 692 693
        if (pwd && pwd->pw_name)
            p.first->second = pwd->pw_name;
        else {
            char username[16];
            g_sprintf(username, "%u", uid);
            p.first->second = username;
        }
    }
694

695
    return p.first->second;
696 697
}

698 699 700 701 702
void
ProcInfo::set_user(guint uid)
{
    if (G_LIKELY(this->uid == uid))
        return;
703

704 705 706
    this->uid = uid;
    this->user = lookup_user(uid);
}
707

708 709
void
get_process_memory_writable (ProcInfo *info)
Benoît Dejean's avatar
Benoît Dejean committed
710
{
711 712
    glibtop_proc_map buf;
    glibtop_map_entry *maps;
Benoît Dejean's avatar
Benoît Dejean committed
713

714
    maps = glibtop_get_proc_map(&buf, info->pid);
Benoît Dejean's avatar
Benoît Dejean committed
715

716 717
    const bool use_private_dirty = buf.flags & (1 << GLIBTOP_MAP_ENTRY_PRIVATE_DIRTY);

718 719
    gulong memwritable = 0;
    const unsigned number = buf.number;
Benoît Dejean's avatar
Benoît Dejean committed
720

721
    for (unsigned i = 0; i < number; ++i) {
722 723 724 725 726 727
        if (use_private_dirty) {
            // clang++ 3.4 is not smart enough to move this invariant out of the loop
            // but who cares ?
            memwritable += maps[i].private_dirty;
        }
        else if (maps[i].perm & GLIBTOP_MAP_PERM_WRITE) {
728
            memwritable += maps[i].size;
729
        }
730
    }
Benoît Dejean's avatar
Benoît Dejean committed
731

732
    info->memwritable = memwritable;
Benoît Dejean's avatar
Benoît Dejean committed
733

734
    g_free(maps);
Benoît Dejean's avatar
Benoît Dejean committed
735 736
}

Benoît Dejean's avatar
Benoît Dejean committed
737 738
static void
get_process_memory_info(ProcInfo *info)
739
{
740
    glibtop_proc_mem procmem;
741

742
#ifdef HAVE_WNCK
743 744 745 746
    info->memxserver = 0;
#ifdef GDK_WINDOWING_X11
    if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
        WnckResourceUsage xresources;
747

748 749 750
        wnck_pid_read_resource_usage (gdk_display_get_default (),
                                      info->pid,
                                      &xresources);
Benoît Dejean's avatar
Benoît Dejean committed
751

752 753 754
        info->memxserver = xresources.total_bytes_estimate;
    }
#endif
755 756
#endif

757
    glibtop_get_proc_mem(&procmem, info->pid);
Benoît Dejean's avatar
Benoît Dejean committed
758

759 760 761
    info->vmsize    = procmem.vsize;
    info->memres    = procmem.resident;
    info->memshared = procmem.share;
Benoît Dejean's avatar
Benoît Dejean committed
762

763
    info->mem = info->memres - info->memshared;
764 765 766
#ifdef HAVE_WNCK
    info->mem += info->memxserver;
#endif
Benoît Dejean's avatar
Benoît Dejean committed
767
}
768

769
static void
770
update_info_mutable_cols(ProcInfo *info)
771
{
772
    GtkTreeModel *model;
773 774
    model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (
            gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(
775
            gtk_tree_view_get_model (GTK_TREE_VIEW(GsmApplication::get()->tree))))));
776 777 778

    using procman::tree_store_update;

779 780 781 782 783
    tree_store_update(model, &info->node, COL_STATUS, info->status);
    tree_store_update(model, &info->node, COL_USER, info->user.c_str());
    tree_store_update(model, &info->node, COL_VMSIZE, info->vmsize);
    tree_store_update(model, &info->node, COL_MEMRES, info->memres);
    tree_store_update(model, &info->node, COL_MEMSHARED, info->memshared);
784
#ifdef HAVE_WNCK
785
    tree_store_update(model, &info->node, COL_MEMXSERVER, info->memxserver);
786
#endif
787 788
    tree_store_update(model, &info->node, COL_CPU, info->pcpu);
    tree_store_update(model, &info->node, COL_CPU_TIME, info->cpu_time);
789 790 791 792
    tree_store_update(model, &info->node, COL_DISK_READ_TOTAL, info->disk_read_bytes_total);
    tree_store_update(model, &info->node, COL_DISK_WRITE_TOTAL, info->disk_write_bytes_total);
    tree_store_update(model, &info->node, COL_DISK_READ_CURRENT, info->disk_read_bytes_current);
    tree_store_update(model, &info->node, COL_DISK_WRITE_CURRENT, info->disk_write_bytes_current);
793 794 795
    tree_store_update(model, &info->node, COL_START_TIME, info->start_time);
    tree_store_update(model, &info->node, COL_NICE, info->nice);
    tree_store_update(model, &info->node, COL_MEM, info->mem);
796 797 798 799 800
    tree_store_update(model, &info->node, COL_WCHAN, info->wchan.c_str());
    tree_store_update(model, &info->node, COL_CGROUP, info->cgroup_name.c_str());
    tree_store_update(model, &info->node, COL_UNIT, info->unit.c_str());
    tree_store_update(model, &info->node, COL_SESSION, info->session.c_str());
    tree_store_update(model, &info->node, COL_SEAT, info->seat.c_str());
801
    tree_store_update(model, &info->node, COL_OWNER, info->owner.c_str());
802 803
}

804
static void
805
insert_info_to_tree (ProcInfo *info, GsmApplication *app, bool forced = false)
806
{
807
    GtkTreeModel *model;
808 809 810 811 812 813
    GtkTreeModel *filtered;
    GtkTreeModel *sorted;
    sorted = gtk_tree_view_get_model (GTK_TREE_VIEW(app->tree));
    filtered = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(sorted));
    model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filtered));
    
Artem Vorotnikov's avatar
Artem Vorotnikov committed
814
    if (app->settings->get_boolean (GSM_SETTING_SHOW_DEPENDENCIES)) {
Benoît Dejean's avatar
Benoît Dejean committed
815

816
        ProcInfo *parent = 0;
817

818
        if (not forced)
819
            parent = app->processes.find(info->ppid);
Benoît Dejean's avatar
Benoît Dejean committed
820

821 822 823
        if (parent) {
            GtkTreePath *parent_node = gtk_tree_model_get_path(model, &parent->node);
            gtk_tree_store_insert(GTK_TREE_STORE(model), &info->node, &parent->node, 0);
824 825 826 827 828 829 830
            
            GtkTreePath *filtered_parent = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (filtered), parent_node);
            if (filtered_parent != NULL) {
                GtkTreePath *sorted_parent = gtk_tree_model_sort_convert_child_path_to_path (GTK_TREE_MODEL_SORT (sorted), filtered_parent);
            
                if (sorted_parent != NULL) {
                    if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(app->tree), sorted_parent)
831
#ifdef __linux__
832 833
                        // on linuxes we don't want to expand kthreadd by default (always has pid 2)
                        && (parent->pid != 2)
834
#endif
835 836 837 838 839 840 841
                    )
                        gtk_tree_view_expand_row(GTK_TREE_VIEW(app->tree), sorted_parent, FALSE);
                    gtk_tree_path_free (sorted_parent);
                }
                gtk_tree_path_free (filtered_parent);
            }
            gtk_tree_path_free (parent_node);
842 843 844 845 846
        } else
            gtk_tree_store_insert(GTK_TREE_STORE(model), &info->node, NULL, 0);
    }
    else
        gtk_tree_store_insert (GTK_TREE_STORE (model), &info->node, NULL, 0);
Benoît Dejean's avatar
Benoît Dejean committed
847

848 849
    gtk_tree_store_set (GTK_TREE_STORE (model), &info->node,
                        COL_POINTER, info,
850 851 852
                        COL_NAME, info->name.c_str(),
                        COL_ARGS, info->arguments.c_str(),
                        COL_TOOLTIP, info->tooltip.c_str(),
853
                        COL_PID, info->pid,
854
                        COL_SECURITYCONTEXT, info->security_context.c_str(),
855
                        -1);
856

857
    app->pretty_table->set_icon(*info);
858

859
    procman_debug("inserted %d%s", info->pid, (forced ? " (forced)" : ""));
860 861
}

862 863
/* Removing a node with children - make sure the children are queued
** to be readded.
864
*/
865
template<typename List>
866
static void
867
remove_info_from_tree (GsmApplication *app, GtkTreeModel *model,
868
                       ProcInfo& current, List &orphans, unsigned lvl = 0)
869
{
870
    GtkTreeIter child_node;
Benoît Dejean's avatar
Benoît Dejean committed
871

872 873
    if (std::find(orphans.begin(), orphans.end(), &current) != orphans.end()) {
        procman_debug("[%u] %d already removed from tree", lvl, int(current.pid));
874 875
        return;
    }
Benoît Dejean's avatar
Benoît Dejean committed
876

877 878
    procman_debug("[%u] pid %d, %d children", lvl, int(current.pid),
                  gtk_tree_model_iter_n_children(model, &current.node));
879

880 881 882
    // it is not possible to iterate&erase over a treeview so instead we
    // just pop one child after another and recursively remove it and
    // its children
Benoît Dejean's avatar
Benoît Dejean committed
883

884
    while (gtk_tree_model_iter_children(model, &child_node, &current.node)) {
885 886
        ProcInfo *child = 0;
        gtk_tree_model_get(model, &child_node, COL_POINTER, &child, -1);
887
        remove_info_from_tree(app, model, *child, orphans, lvl + 1);
888
    }
889

890
    g_assert(not gtk_tree_model_iter_has_child(model, &current.node));
Benoît Dejean's avatar
Benoît Dejean committed
891

892 893 894
    orphans.push_back(&current);
    gtk_tree_store_remove(GTK_TREE_STORE(model), &current.node);
    procman::poison(current.node, 0x69);
895 896
}

897

898 899 900 901 902 903 904 905
static std::string
get_proc_kernel_wchan(glibtop_proc_kernel& obj) {
    char buf[40] = {0};
    g_strlcpy(buf, obj.wchan, sizeof(buf));
    buf[sizeof(buf)-1] = '\0';
    return buf;
}

906
static void
907
update_info (GsmApplication *app, ProcInfo *info)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
908
{
909 910 911 912
    glibtop_proc_state procstate;
    glibtop_proc_uid procuid;
    glibtop_proc_time proctime;
    glibtop_proc_kernel prockernel;
913 914
    glibtop_proc_io procio;
    gdouble update_interval_seconds = app->config.update_interval / 1000;
915
    glibtop_get_proc_kernel(&prockernel, info->pid);
916
    info->wchan = get_proc_kernel_wchan(prockernel);
917

918 919
    glibtop_get_proc_state (&procstate, info->pid);
    info->status = procstate.state;
920

921 922
    glibtop_get_proc_uid (&procuid, info->pid);
    glibtop_get_proc_time (&proctime, info->pid);
923
    glibtop_get_proc_io (&procio, info->pid);
924

925
    get_process_memory_info(info);
926

927
    info->set_user(procstate.uid);
928

929 930 931 932 933
    // if the cpu time has increased reset the status to running
    // regardless of kernel state (#606579)
    guint64 difference = proctime.rtime - info->cpu_time;
    if (difference > 0) 
        info->status = GLIBTOP_PROCESS_RUNNING;
934

935
    guint cpu_scale = 100;
936
    if (not app->config.solaris_mode)
937 938 939 940
        cpu_scale *= app->config.num_cpus;

    info->pcpu = difference * cpu_scale / app->cpu_total_time;
    info->pcpu = MIN(info->pcpu, cpu_scale);
941

942
    app->processes.cpu_times[info->pid] = info->cpu_time = proctime.rtime;
943
    info->nice = procuid.nice;
944 945 946 947 948 949
    
    info->disk_write_bytes_current = (procio.disk_wbytes - info->disk_write_bytes_total)/update_interval_seconds;
    info->disk_read_bytes_current = (procio.disk_rbytes - info->disk_read_bytes_total)/update_interval_seconds;

    info->disk_write_bytes_total = procio.disk_wbytes;
    info->disk_read_bytes_total = procio.disk_rbytes;
Ben's avatar
Ben committed
950 951 952 953 954 955 956 957 958

    // set the ppid only if one can exist
    // i.e. pid=0 can never have a parent
    if (info->pid > 0) {
        info->ppid = procuid.ppid;
    }

    g_assert(info->pid != info->ppid);
    g_assert(info->ppid != -1 || info->pid == 0);
959 960

    /* get cgroup data */
Artem Vorotnikov's avatar
Artem Vorotnikov committed
961
    get_process_cgroup_info(*info);
962

963
    procman::get_process_systemd_info(info);
Kevin Vandersloot's avatar
Kevin Vandersloot committed
964 965
}

966
ProcInfo::ProcInfo(pid_t pid)
Ben's avatar
Ben committed
967 968
    : node(),
      pixbuf(),
969
      pid(pid),
Ben's avatar
Ben committed
970
      ppid(-1),
971
      uid(-1)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
972
{
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
    ProcInfo * const info = this;
    glibtop_proc_state procstate;
    glibtop_proc_time proctime;
    glibtop_proc_args procargs;
    gchar** arguments;

    glibtop_get_proc_state (&procstate, pid);
    glibtop_get_proc_time (&proctime, pid);
    arguments = glibtop_get_proc_argv (&procargs, pid, 0);

    /* FIXME : wrong. name and arguments may change with exec* */
    get_process_name (info, procstate.cmd, static_cast<const GStrv>(arguments));

    std::string tooltip = make_string(g_strjoinv(" ", arguments));
    if (tooltip.empty())
        tooltip = procstate.cmd;

990
    info->tooltip = make_string(g_markup_escape_text(tooltip.c_str(), -1));
991

992
    info->arguments = make_string(g_strescape(tooltip.c_str(), "\\\""));
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
    g_strfreev(arguments);

    guint64 cpu_time = proctime.rtime;
    auto app = GsmApplication::get();
    auto it = app->processes.cpu_times.find(pid);
    if (it != app->processes.cpu_times.end())
    {
        if (proctime.rtime >= it->second)
            cpu_time = it->second;
    }
    info->cpu_time = cpu_time;
    info->start_time = proctime.start_time;

    get_process_selinux_context (info);
    get_process_cgroup_info(*info);

    get_process_systemd_info(info);
1010
}
1011

Kevin Vandersloot's avatar
Kevin Vandersloot committed
1012
static void
1013
refresh_list (GsmApplication *app, const pid_t* pid_list, const guint n)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
1014
{
1015 1016
    typedef std::list<ProcInfo*> ProcList;
    ProcList addition;
1017

1018 1019 1020
    GtkTreeModel    *model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (
                             gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT (
                             gtk_tree_view_get_model (GTK_TREE_VIEW(app->tree))))));
1021
    guint i;
Benoît Dejean's avatar
Benoît Dejean committed
1022

1023
    // Add or update processes in the process list
1024 1025 1026 1027 1028
    for(i = 0; i < n; ++i) {
        ProcInfo *info = app->processes.find(pid_list[i]);

        if (!info) {
            info = app->processes.add(pid_list[i]);
1029 1030
            addition.push_back(info);
        }
1031

1032
        update_info (app, info);
1033
    }
Benoît Dejean's avatar
Benoît Dejean committed
1034

Benoît Dejean's avatar
Benoît Dejean committed
1035

1036 1037 1038
    // Remove dead processes from the process list and from the
    // tree. children are queued to be readded at the right place
    // in the tree.
Benoît Dejean's avatar
Benoît Dejean committed
1039

1040
    const std::set<pid_t> pids(pid_list, pid_list + n);
Benoît Dejean's avatar
Benoît Dejean committed
1041

1042 1043
    auto it = std::begin(app->processes);
    while (it != std::end(app->processes)) {
1044 1045 1046
        auto& info = it->second;
        if (pids.find(info.pid) == pids.end()) {
            procman_debug("ripping %d", info.pid);
1047
            remove_info_from_tree(app, model, info, addition);
1048
            addition.remove(&info);
1049
            it = app->processes.erase(it);
1050
        } else {
1051
            ++it;
1052 1053
        }
    }
Benoît Dejean's avatar
Benoît Dejean committed
1054

1055 1056
    // INVARIANT
    // pid_list == ProcInfo::all + addition
1057 1058


Artem Vorotnikov's avatar
Artem Vorotnikov committed
1059
    if (app->settings->get_boolean (GSM_SETTING_SHOW_DEPENDENCIES)) {
1060

1061 1062 1063 1064
        // insert process in the tree. walk through the addition list
        // (new process + process that have a new parent). This loop
        // handles the dependencies because we cannot insert a process
        // until its parent is in the tree.
Benoît Dejean's avatar
Benoît Dejean committed
1065

1066
        std::set<pid_t> in_tree(pids);
Benoît Dejean's avatar
Benoît Dejean committed
1067

1068 1069
        for (ProcList::iterator it(addition.begin()); it != addition.end(); ++it)
            in_tree.erase((*it)->pid);
Benoît Dejean's avatar
Benoît Dejean committed
1070

1071

1072 1073 1074
        while (not addition.empty()) {
            procman_debug("looking for %d parents", int(addition.size()));
            ProcList::iterator it(addition.begin());
1075

1076 1077 1078
            while (it != addition.end()) {
                procman_debug("looking for %d's parent with ppid %d",
                              int((*it)->pid), int((*it)->ppid));
1079 1080


1081
                // inserts the process in the treeview if :
Ben's avatar
Ben committed
1082 1083
                // - it has no parent (ppid = -1),
                //   ie it is for example the [kernel] on FreeBSD
1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
                // - it is init
                // - its parent is already in tree
                // - its parent is unreachable
                //
                // rounds == 2 means that addition contains processes with
                // unreachable parents
                //
                // FIXME: this is broken if the unreachable parent becomes active
                // i.e. it gets active or changes ower
                // so we just clear the tree on __each__ update
1094
                // see proctable_update (ProcData * const procdata)
1095 1096


Ben's avatar
Ben committed
1097
                if ((*it)->ppid <= 0 or in_tree.find((*it)->ppid) != in_tree.end()) {
1098
                    insert_info_to_tree(*it, app);
1099 1100 1101 1102
                    in_tree.insert((*it)->pid);
                    it = addition.erase(it);
                    continue;
                }
1103

1104
                ProcInfo *parent = app->processes.find((*it)->ppid);
1105 1106 1107
                // if the parent is unreachable
                if (not parent) {
                    // or std::find(addition.begin(), addition.end(), parent) == addition.end()) {
1108
                    insert_info_to_tree(*it, app, true);
1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
                    in_tree.insert((*it)->pid);
                    it = addition.erase(it);
                    continue;
                }

                ++it;
            }
        }
    }
    else {
        // don't care of the tree
1120
        for (auto& v : addition) insert_info_to_tree(v, app);
1121
    }
1122

1123

1124
    for (auto& v : app->processes) update_info_mutable_cols(&v.second);
Kevin Vandersloot's avatar
Kevin Vandersloot committed
1125 1126
}

1127 1128
void
proctable_update (GsmApplication *app)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
1129
{
1130 1131 1132
    pid_t* pid_list;
    glibtop_proclist proclist;
    glibtop_cpu cpu;
1133 1134
    int which = 0;
    int arg = 0;
Artem Vorotnikov's avatar
Artem Vorotnikov committed
1135 1136
    auto whose_processes = app->settings->get_string(GSM_SETTING_SHOW_WHOSE_PROCESSES);
    if (whose_processes == "all") {
1137 1138
        which = GLIBTOP_KERN_PROC_ALL;
        arg = 0;
Artem Vorotnikov's avatar
Artem Vorotnikov committed
1139
    } else if (whose_processes == "active") {
1140 1141
        which = GLIBTOP_KERN_PROC_ALL | GLIBTOP_EXCLUDE_IDLE;
        arg = 0;
Artem Vorotnikov's avatar
Artem Vorotnikov committed
1142
    } else if (whose_processes == "user") {
1143 1144
      which = GLIBTOP_KERN_PROC_UID;
      arg = getuid ();
1145 1146 1147 1148 1149 1150 1151
    }

    pid_list = glibtop_get_proclist (&proclist, which, arg);

    /* FIXME: total cpu time elapsed should be calculated on an individual basis here
    ** should probably have a total_time_last gint in the ProcInfo structure */
    glibtop_get_cpu (&cpu);
1152 1153
    app->cpu_total_time = MAX(cpu.total - app->cpu_total_time_last, 1);
    app->cpu_total_time_last = cpu.total;
Ben's avatar
Ben committed
1154 1155 1156 1157

    // FIXME: not sure if glibtop always returns a sorted list of pid
    // but it is important otherwise refresh_list won't find the parent
    std::sort(pid_list, pid_list + proclist.number);
1158
    refresh_list (app, pid_list, proclist.number);
1159

1160 1161
    // juggling with tree scroll position to fix https://bugzilla.gnome.org/show_bug.cgi?id=92724
    GtkTreePath* current_top;
1162
    if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(app->tree), 0,0, &current_top, NULL, NULL, NULL)) {
1163
        GtkAdjustment *vadjustment = GTK_ADJUSTMENT (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (app->tree)));
1164 1165
        gdouble current_max = gtk_adjustment_get_upper(vadjustment);
        gdouble current_value = gtk_adjustment_get_value(vadjustment);
1166

Robert Roth's avatar