proctable.cpp 40.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 16 17 18 19 20
/* 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
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 *
 */

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

#include <config.h>

24

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

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

48 49 50 51
#ifdef HAVE_SYSTEMD
#include <systemd/sd-login.h>
#endif

52 53 54 55 56
#ifdef HAVE_WNCK
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
#include <libwnck/libwnck.h>
#endif

57
#include "application.h"
Kevin Vandersloot's avatar
Kevin Vandersloot committed
58
#include "proctable.h"
59
#include "prettytable.h"
60
#include "util.h"
61
#include "interface.h"
62
#include "selinux.h"
63
#include "settings-keys.h"
64
#include "cgroups.h"
65
#include "treeview.h"
66

67
ProcInfo::UserMap ProcInfo::users;
68
ProcInfo::List ProcInfo::all;
69
std::map<pid_t, guint64> ProcInfo::cpu_times;
70 71 72 73


ProcInfo* ProcInfo::find(pid_t pid)
{
74 75
    Iterator it(ProcInfo::all.find(pid));
    return (it == ProcInfo::all.end() ? NULL : it->second);
76 77
}

78
static void
79
cb_save_tree_state(gpointer, gpointer data)
80
{
81
    GsmApplication * const app = static_cast<GsmApplication *>(data);
82

83
    gsm_tree_view_save_state (GSM_TREE_VIEW (app->tree));
84 85
}

86 87 88
static void
cb_proctree_destroying (GtkTreeView *self, gpointer data)
{
89
    g_signal_handlers_disconnect_by_func (self,
90
                                          (gpointer) cb_save_tree_state,
91 92 93
                                          data);

    g_signal_handlers_disconnect_by_func (gtk_tree_view_get_model (self),
94
                                          (gpointer) cb_save_tree_state,
95
                                          data);
96
}
97

98 99 100
static gboolean
cb_tree_button_pressed (GtkWidget *widget, GdkEventButton *event, gpointer data)
{
101
    GsmApplication *app = (GsmApplication *) data;
102 103

    if (gdk_event_triggers_context_menu ((GdkEvent *) event)) {
104 105 106
        gtk_menu_popup (GTK_MENU (app->popup_menu),
                        NULL, NULL, NULL, NULL,
                        event->button, event->time);
107 108 109 110 111 112 113 114 115
        return TRUE;
    }

    return FALSE;
}

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

118 119 120
    gtk_menu_popup (GTK_MENU (app->popup_menu),
                    NULL, NULL, NULL, NULL,
                    0, gtk_get_current_event_time ());
121 122 123 124

    return TRUE;
}

125
void
126 127 128 129 130 131 132 133 134 135 136
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)
{
137
    GsmApplication *app = (GsmApplication *) data;
138 139 140

    app->selection = selection;

141
    ProcInfo *selected_process = NULL;
142 143 144 145 146

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

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

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

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

176
    proctable_update (app);
177 178 179 180 181 182 183 184 185 186 187

    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;
}

188 189 190
static void
cb_refresh_icons (GtkIconTheme *theme, gpointer data)
{
191
    GsmApplication *app = (GsmApplication *) data;
192 193 194 195 196 197 198 199 200 201 202 203

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

    for (ProcInfo::Iterator it(ProcInfo::begin()); it != ProcInfo::end(); ++it) {
        app->pretty_table->set_icon(*(it->second));
    }

    cb_timeout(app);
}

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
static gboolean
iter_matches_search_key (GtkTreeModel *model, GtkTreeIter *iter, const gchar *key)
{
    char *name;
    char *user;
    gboolean found;

    gtk_tree_model_get (model, iter,
                        COL_NAME, &name,
                        COL_USER, &user,
                        -1);

    found = (name && strcasestr (name, key)) || (user && strcasestr (user, key));

    g_free (name);
    g_free (user);

    return found;
}

static gboolean
process_visibility_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
226
{
227
    GsmApplication * const app = static_cast<GsmApplication *>(data);
228 229
    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);
230 231 232 233

    if (strcmp (search_text, "") == 0)
        return TRUE;

234 235
	// in case we are in dependencies view, we show (and expand) rows not matching the text, but having a matching child
    gboolean match = false;
236
    if (g_settings_get_boolean (app->settings, GSM_SETTING_SHOW_DEPENDENCIES)) {
237 238
        GtkTreeIter child;
        if (gtk_tree_model_iter_children (model, &child, iter)) {
239 240 241 242 243
            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;
244
        }
245 246

        match |= iter_matches_search_key (model, iter, search_text);
247 248 249 250 251 252
        // 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);
        //}
253 254 255 256

    } else {
        match = iter_matches_search_key (model, iter, search_text);
    }
257 258
        
    gtk_tree_path_free (tree_path);
259

260
    return match;
261
}
262 263

static void
264
proctable_clear_tree (GsmApplication * const app)
265 266 267 268 269 270 271 272 273 274 275 276 277
{
    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);
}
278
                                                        
279 280 281
static void
cb_show_dependencies_changed (GSettings *settings, const gchar *key, gpointer data)
{
282
    GsmApplication *app = (GsmApplication *) data;
283 284

    gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (app->tree),
285
                                      g_settings_get_boolean (settings, GSM_SETTING_SHOW_DEPENDENCIES));
286 287

    proctable_clear_tree (app);
288
    proctable_update (app);
289 290 291 292 293
}

static void
cb_show_whose_processes_changed (GSettings *settings, const gchar *key, gpointer data)
{
294
    GsmApplication *app = (GsmApplication *) data;
295 296

    proctable_clear_tree (app);
297
    proctable_update (app);
298 299
}

Kevin Vandersloot's avatar
Kevin Vandersloot committed
300
GtkWidget *
301
proctable_new (GsmApplication * const app)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
302
{
303 304
    GtkWidget *proctree;
    GtkTreeStore *model;
305 306 307
    GtkTreeModelFilter *model_filter;
    GtkTreeModelSort *model_sort;
    
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    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"),
327
        /* xgettext: combined noun, the function the process is waiting in, see wchan ps(1) */
328
        N_("Waiting Channel"),
329
        N_("Control Group"),
330 331
        N_("Unit"),
        N_("Session"),
332 333
        /* 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 */
334 335
        N_("Seat"),
        N_("Owner"),
336
        N_("Priority"),
337 338 339 340 341
        NULL,
        "POINTER"
    };

    gint i;
342
    GSettings * settings = g_settings_get_child (app->settings, GSM_SETTINGS_CHILD_PROCESSES);
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    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        */
361
                                G_TYPE_STRING,      /* Cgroup       */
362 363 364 365
                                G_TYPE_STRING,      /* Unit         */
                                G_TYPE_STRING,      /* Session      */
                                G_TYPE_STRING,      /* Seat         */
                                G_TYPE_STRING,      /* Owner        */
366
                                G_TYPE_STRING,      /* Priority     */
367 368 369 370 371
                                GDK_TYPE_PIXBUF,    /* Icon         */
                                G_TYPE_POINTER,     /* ProcInfo     */
                                G_TYPE_STRING       /* Sexy tooltip */
        );

372 373
    model_filter = GTK_TREE_MODEL_FILTER (gtk_tree_model_filter_new (GTK_TREE_MODEL (model), NULL));
        
374
    gtk_tree_model_filter_set_visible_func(model_filter, process_visibility_func, app, NULL);
375 376 377
    
    model_sort = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (model_filter)));
    
378 379 380
    proctree = gsm_tree_view_new (settings, TRUE);
    gtk_tree_view_set_model (GTK_TREE_VIEW (proctree), GTK_TREE_MODEL (model_sort));

381
    gtk_tree_view_set_tooltip_column (GTK_TREE_VIEW (proctree), COL_TOOLTIP);
382
    gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (proctree),
383
                                      g_settings_get_boolean (app->settings, GSM_SETTING_SHOW_DEPENDENCIES));
384
    gtk_tree_view_set_enable_search (GTK_TREE_VIEW (proctree), FALSE);
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
    gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (proctree), TRUE);
    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]));
402

403 404 405 406
    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);
407
    gtk_tree_view_column_set_reorderable(column, TRUE);
408 409

    gsm_tree_view_append_and_bind_column (GSM_TREE_VIEW (proctree), column);
410 411
    gtk_tree_view_set_expander_column (GTK_TREE_VIEW (proctree), column);

412
    for (i = COL_USER; i <= COL_PRIORITY; i++) {
413
        GtkTreeViewColumn *col;
414
        GtkCellRenderer *cell;
415

416
#ifndef HAVE_WNCK
Stefano Facchini's avatar
Stefano Facchini committed
417
        if (i == COL_MEMXSERVER)
418 419
          continue;
#endif
420 421 422 423

        if (i == COL_MEMWRITABLE)
            continue;

424 425 426 427 428 429 430
        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);
431
        gsm_tree_view_append_and_bind_column (GSM_TREE_VIEW (proctree), col);
432

433 434
        // type
        switch (i) {
435
#ifdef HAVE_WNCK
436 437 438 439 440 441
            case COL_MEMXSERVER:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::size_cell_data_func,
                                                        GUINT_TO_POINTER(i),
                                                        NULL);
                break;
442
#endif
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
            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;
473 474 475 476 477
            case COL_PRIORITY:
                gtk_tree_view_column_set_cell_data_func(col, cell,
                                                        &procman::priority_cell_data_func,
                                                        GUINT_TO_POINTER(COL_NICE),
                                                        NULL);
478 479 480 481 482 483 484 485
                break;
            default:
                gtk_tree_view_column_set_attributes(col, cell, "text", i, NULL);
                break;
        }

        // sorting
        switch (i) {
486
#ifdef HAVE_WNCK
487
            case COL_MEMXSERVER:
488
#endif
489 490 491 492 493 494 495
            case COL_VMSIZE:
            case COL_MEMRES:
            case COL_MEMSHARED:
            case COL_MEM:
            case COL_CPU:
            case COL_CPU_TIME:
            case COL_START_TIME:
496 497 498 499
                gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model_sort), i,
                                                 procman::number_compare_func,
                                                 GUINT_TO_POINTER (i),
                                                 NULL);
500 501
                break;
            case COL_PRIORITY:
502 503 504
                gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model_sort), i,
                                                 procman::priority_compare_func,
                                                 GUINT_TO_POINTER (COL_NICE), NULL);
505
                break;
506 507 508 509
            default:
                break;
        }

510
        // xalign
511 512 513 514 515
        switch(i)
        {
            case COL_VMSIZE:
            case COL_MEMRES:
            case COL_MEMSHARED:
516
#ifdef HAVE_WNCK
517
            case COL_MEMXSERVER:
518
#endif
519 520 521 522 523 524 525 526 527
            case COL_CPU:
            case COL_NICE:
            case COL_PID:
            case COL_CPU_TIME:
            case COL_MEM:
                g_object_set(G_OBJECT(cell), "xalign", 1.0f, NULL);
                break;
        }

528
        gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
529 530 531 532 533 534 535 536 537 538
        // 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;
        }
    }
539
    app->tree = proctree;
540
    app->top_of_tree = NULL;
541 542
    app->last_vscroll_max = 0;
    app->last_vscroll_value = 0;
543

544 545 546 547
    if (!cgroups_enabled ())
        gsm_tree_view_add_excluded_column (GSM_TREE_VIEW (proctree), COL_CGROUP);

#ifdef HAVE_SYSTEMD
548
    if (!LOGIND_RUNNING ())
549
#endif
550
    {
551 552 553 554 555 556
        gsm_tree_view_add_excluded_column (GSM_TREE_VIEW (proctree), COL_UNIT);
        gsm_tree_view_add_excluded_column (GSM_TREE_VIEW (proctree), COL_SESSION);
        gsm_tree_view_add_excluded_column (GSM_TREE_VIEW (proctree), COL_SEAT);
        gsm_tree_view_add_excluded_column (GSM_TREE_VIEW (proctree), COL_OWNER);
    }

557 558
    if (!can_show_security_context_column ())
        gsm_tree_view_add_excluded_column (GSM_TREE_VIEW (proctree), COL_SECURITYCONTEXT);
559

560
    gsm_tree_view_load_state (GSM_TREE_VIEW (proctree));
561

562 563
    GtkIconTheme* theme = gtk_icon_theme_get_default();
    g_signal_connect(G_OBJECT (theme), "changed", G_CALLBACK (cb_refresh_icons), app);
564

565 566 567 568
    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),
569
                      "changed",
570
                      G_CALLBACK (cb_row_selected), app);
571
    g_signal_connect (G_OBJECT (proctree), "popup_menu",
572
                      G_CALLBACK (cb_tree_popup_menu), app);
573
    g_signal_connect (G_OBJECT (proctree), "button_press_event",
574 575
                      G_CALLBACK (cb_tree_button_pressed), app);

576 577
    g_signal_connect (G_OBJECT (proctree), "destroy",
                      G_CALLBACK (cb_proctree_destroying),
578
                      app);
579

580
    g_signal_connect (G_OBJECT (proctree), "columns-changed",
581
                      G_CALLBACK (cb_save_tree_state), app);
582

583
    g_signal_connect (G_OBJECT (model_sort), "sort-column-changed",
584
                      G_CALLBACK (cb_save_tree_state), app);
585

586
    g_signal_connect (app->settings, "changed::" GSM_SETTING_SHOW_DEPENDENCIES,
587 588
                      G_CALLBACK (cb_show_dependencies_changed), app);

589
    g_signal_connect (app->settings, "changed::" GSM_SETTING_SHOW_WHOSE_PROCESSES,
590 591
                      G_CALLBACK (cb_show_whose_processes_changed), app);

592 593
    gtk_widget_show (proctree);

594
    return proctree;
Kevin Vandersloot's avatar
Kevin Vandersloot committed
595 596
}

597
ProcInfo::~ProcInfo()
Kevin Vandersloot's avatar
Kevin Vandersloot committed
598
{
599 600 601 602
    g_free(this->name);
    g_free(this->tooltip);
    g_free(this->arguments);
    g_free(this->security_context);
603
    g_free(this->cgroup_name);
604 605 606 607 608 609
    // The following are allocated inside of the sd_pid_get_*
    // functions using malloc(). Free with free() instead of g_free()
    // to insure proper clean up.
    free(this->unit);
    free(this->session);
    free(this->seat);
Kevin Vandersloot's avatar
Kevin Vandersloot committed
610 611
}

612
static void
Benoît Dejean's avatar
Benoît Dejean committed
613
get_process_name (ProcInfo *info,
614
                  const gchar *cmd, const GStrv args)
615
{
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
    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)) {
                info->name = basename;
                return;
            }

            g_free(basename);
        }
    }

    info->name = g_strdup (cmd);
634 635
}

636 637
std::string
ProcInfo::lookup_user(guint uid)
638
{
639 640 641
    typedef std::pair<ProcInfo::UserMap::iterator, bool> Pair;
    ProcInfo::UserMap::value_type hint(uid, "");
    Pair p(ProcInfo::users.insert(hint));
642

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

645 646 647
    if (p.second) {
        struct passwd* pwd;
        pwd = getpwuid(uid);
648

649 650 651 652 653 654 655 656
        if (pwd && pwd->pw_name)
            p.first->second = pwd->pw_name;
        else {
            char username[16];
            g_sprintf(username, "%u", uid);
            p.first->second = username;
        }
    }
657

658
    return p.first->second;
659 660
}

661 662 663 664 665
void
ProcInfo::set_user(guint uid)
{
    if (G_LIKELY(this->uid == uid))
        return;
666

667 668 669
    this->uid = uid;
    this->user = lookup_user(uid);
}
670

671 672
void
get_process_memory_writable (ProcInfo *info)
Benoît Dejean's avatar
Benoît Dejean committed
673
{
674 675
    glibtop_proc_map buf;
    glibtop_map_entry *maps;
Benoît Dejean's avatar
Benoît Dejean committed
676

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

679 680
    gulong memwritable = 0;
    const unsigned number = buf.number;
Benoît Dejean's avatar
Benoît Dejean committed
681

682
    for (unsigned i = 0; i < number; ++i) {
683
#ifdef __linux__
684
        memwritable += maps[i].private_dirty;
685
#else
686 687
        if (maps[i].perm & GLIBTOP_MAP_PERM_WRITE)
            memwritable += maps[i].size;
688
#endif
689
    }
Benoît Dejean's avatar
Benoît Dejean committed
690

691
    info->memwritable = memwritable;
Benoît Dejean's avatar
Benoît Dejean committed
692

693
    g_free(maps);
Benoît Dejean's avatar
Benoît Dejean committed
694 695
}

Benoît Dejean's avatar
Benoît Dejean committed
696 697
static void
get_process_memory_info(ProcInfo *info)
698
{
699
    glibtop_proc_mem procmem;
700
#ifdef HAVE_WNCK
701
    WnckResourceUsage xresources;
702

703 704 705
    wnck_pid_read_resource_usage (gdk_screen_get_display (gdk_screen_get_default ()),
                                  info->pid,
                                  &xresources);
Benoît Dejean's avatar
Benoît Dejean committed
706

707 708 709
    info->memxserver = xresources.total_bytes_estimate;
#endif

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

712 713 714
    info->vmsize    = procmem.vsize;
    info->memres    = procmem.resident;
    info->memshared = procmem.share;
Benoît Dejean's avatar
Benoît Dejean committed
715

716
    info->mem = info->memres - info->memshared;
717 718 719
#ifdef HAVE_WNCK
    info->mem += info->memxserver;
#endif
Benoît Dejean's avatar
Benoît Dejean committed
720
}
721

722
static void
723
update_info_mutable_cols(ProcInfo *info)
724
{
725
    GtkTreeModel *model;
726 727
    model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (
            gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(
728
            gtk_tree_view_get_model (GTK_TREE_VIEW(GsmApplication::get()->tree))))));
729 730 731

    using procman::tree_store_update;

732 733 734 735 736
    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);
737
#ifdef HAVE_WNCK
738
    tree_store_update(model, &info->node, COL_MEMXSERVER, info->memxserver);
739
#endif
740 741 742 743 744 745 746 747 748 749 750
    tree_store_update(model, &info->node, COL_CPU, info->pcpu);
    tree_store_update(model, &info->node, COL_CPU_TIME, info->cpu_time);
    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);
    tree_store_update(model, &info->node, COL_WCHAN, info->wchan);
    tree_store_update(model, &info->node, COL_CGROUP, info->cgroup_name);
    tree_store_update(model, &info->node, COL_UNIT, info->unit);
    tree_store_update(model, &info->node, COL_SESSION, info->session);
    tree_store_update(model, &info->node, COL_SEAT, info->seat);
    tree_store_update(model, &info->node, COL_OWNER, info->owner.c_str());
751 752
}

753
static void
754
insert_info_to_tree (ProcInfo *info, GsmApplication *app, bool forced = false)
755
{
756
    GtkTreeModel *model;
757 758 759 760 761 762
    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));
    
763
    if (g_settings_get_boolean (app->settings, GSM_SETTING_SHOW_DEPENDENCIES)) {
Benoît Dejean's avatar
Benoît Dejean committed
764

765
        ProcInfo *parent = 0;
766

767 768
        if (not forced)
            parent = ProcInfo::find(info->ppid);
Benoît Dejean's avatar
Benoît Dejean committed
769

770 771 772
        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);
773 774 775 776 777 778 779
            
            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)
780
#ifdef __linux__
781 782
                        // on linuxes we don't want to expand kthreadd by default (always has pid 2)
                        && (parent->pid != 2)
783
#endif
784 785 786 787 788 789 790
                    )
                        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);
791 792 793 794 795
        } 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
796

797 798 799 800 801 802 803 804
    gtk_tree_store_set (GTK_TREE_STORE (model), &info->node,
                        COL_POINTER, info,
                        COL_NAME, info->name,
                        COL_ARGS, info->arguments,
                        COL_TOOLTIP, info->tooltip,
                        COL_PID, info->pid,
                        COL_SECURITYCONTEXT, info->security_context,
                        -1);
805

806
    app->pretty_table->set_icon(*info);
807

808
    procman_debug("inserted %d%s", info->pid, (forced ? " (forced)" : ""));
809 810
}

811 812
/* Removing a node with children - make sure the children are queued
** to be readded.
813
*/
814
template<typename List>
815
static void
816
remove_info_from_tree (GsmApplication *app, GtkTreeModel *model,
817
                       ProcInfo *current, List &orphans, unsigned lvl = 0)
818
{
819
    GtkTreeIter child_node;
Benoît Dejean's avatar
Benoît Dejean committed
820

821 822 823 824
    if (std::find(orphans.begin(), orphans.end(), current) != orphans.end()) {
        procman_debug("[%u] %d already removed from tree", lvl, int(current->pid));
        return;
    }
Benoît Dejean's avatar
Benoît Dejean committed
825

826 827
    procman_debug("[%u] pid %d, %d children", lvl, int(current->pid),
                  gtk_tree_model_iter_n_children(model, &current->node));
828

829 830 831
    // 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
832

833 834 835
    while (gtk_tree_model_iter_children(model, &child_node, &current->node)) {
        ProcInfo *child = 0;
        gtk_tree_model_get(model, &child_node, COL_POINTER, &child, -1);
836
        remove_info_from_tree(app, model, child, orphans, lvl + 1);
837
    }
838

839
    g_assert(not gtk_tree_model_iter_has_child(model, &current->node));
Benoît Dejean's avatar
Benoît Dejean committed
840

841 842 843
    orphans.push_back(current);
    gtk_tree_store_remove(GTK_TREE_STORE(model), &current->node);
    procman::poison(current->node, 0x69);
844 845
}

846 847 848 849 850 851
static void
get_process_systemd_info(ProcInfo *info)
{
#ifdef HAVE_SYSTEMD
    uid_t uid;

852
    if (!LOGIND_RUNNING())
853 854 855 856 857 858 859 860 861
        return;

    free(info->unit);
    info->unit = NULL;
    sd_pid_get_unit(info->pid, &info->unit);

    free(info->session);
    info->session = NULL;
    sd_pid_get_session(info->pid, &info->session);
Benoît Dejean's avatar
Benoît Dejean committed
862

863 864 865 866 867 868 869 870 871 872 873 874
    free(info->seat);
    info->seat = NULL;

    if (info->session != NULL)
        sd_session_get_seat(info->session, &info->seat);

    if (sd_pid_get_owner_uid(info->pid, &uid) >= 0)
        info->owner = info->lookup_user(uid);
    else
        info->owner = "";
#endif
}
875

876
static void
877
update_info (GsmApplication *app, ProcInfo *info)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
878
{
879 880 881 882
    glibtop_proc_state procstate;
    glibtop_proc_uid procuid;
    glibtop_proc_time proctime;
    glibtop_proc_kernel prockernel;
Benoît Dejean's avatar
Benoît Dejean committed
883

884 885
    glibtop_get_proc_kernel(&prockernel, info->pid);
    g_strlcpy(info->wchan, prockernel.wchan, sizeof info->wchan);
886

887 888
    glibtop_get_proc_state (&procstate, info->pid);
    info->status = procstate.state;
889

890 891
    glibtop_get_proc_uid (&procuid, info->pid);
    glibtop_get_proc_time (&proctime, info->pid);
892

893
    get_process_memory_info(info);
894

895
    info->set_user(procstate.uid);
896

897 898 899 900 901 902
    // 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;
    info->pcpu = difference * 100 / app->cpu_total_time;
903
    info->pcpu = MIN(info->pcpu, 100);
904

905 906
    if (not app->config.solaris_mode)
        info->pcpu *= app->config.num_cpus;
907

908 909 910
    ProcInfo::cpu_times[info->pid] = info->cpu_time = proctime.rtime;
    info->nice = procuid.nice;
    info->ppid = procuid.ppid;
911 912 913

    /* get cgroup data */
    get_process_cgroup_info(info);
914 915

    get_process_systemd_info(info);
Kevin Vandersloot's avatar
Kevin Vandersloot committed
916 917
}

918
ProcInfo::ProcInfo(pid_t pid)
919 920 921 922 923 924
    : tooltip(NULL),
      name(NULL),
      arguments(NULL),
      security_context(NULL),
      pid(pid),
      uid(-1)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
925
{
926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
    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;

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

    info->arguments = g_strescape(tooltip.c_str(), "\\\"");
    g_strfreev(arguments);

    guint64 cpu_time = proctime.rtime;
    std::map<pid_t, guint64>::iterator it(ProcInfo::cpu_times.find(pid));
    if (it != ProcInfo::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);
959 960
    info->cgroup_name = NULL;
    get_process_cgroup_info(info);
Benoît Dejean's avatar
Benoît Dejean committed
961

962 963 964
    info->unit = info->session = info->seat = NULL;
    get_process_systemd_info(info);
}
965

Kevin Vandersloot's avatar
Kevin Vandersloot committed
966
static void
967
refresh_list (GsmApplication *app, const pid_t* pid_list, const guint n)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
968
{
969 970
    typedef std::list<ProcInfo*> ProcList;
    ProcList addition;
971

972 973 974
    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))))));
975
    guint i;
Benoît Dejean's avatar
Benoît Dejean committed
976

977 978 979 980 981 982 983 984 985
    // Add or update processes in the process list
    for(i = 0; i < n; ++i) {
        ProcInfo *info = ProcInfo::find(pid_list[i]);

        if (!info) {
            info = new ProcInfo(pid_list[i]);
            ProcInfo::all[info->pid] = info;
            addition.push_back(info);
        }
986

987
        update_info (app, info);
988
    }
Benoît Dejean's avatar
Benoît Dejean committed
989

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

991 992 993
    // 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
994

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

997
    ProcInfo::Iterator it(ProcInfo::begin());
Benoît Dejean's avatar
Benoît Dejean committed
998

999 1000 1001 1002
    while (it != ProcInfo::end()) {
        ProcInfo * const info = it->second;
        ProcInfo::Iterator next(it);
        ++next;
1003

1004 1005
        if (pids.find(info->pid) == pids.end()) {
            procman_debug("ripping %d", info->pid);
1006
            remove_info_from_tree(app, model, info, addition);
1007 1008 1009 1010
            addition.remove(info);
            ProcInfo::all.erase(it);
            delete info;
        }
1011

1012 1013
        it = next;
    }
Benoît Dejean's avatar
Benoît Dejean committed
1014

1015 1016
    // INVARIANT
    // pid_list == ProcInfo::all + addition
1017 1018


1019
    if (g_settings_get_boolean (app->settings, GSM_SETTING_SHOW_DEPENDENCIES)) {
1020

1021 1022 1023 1024
        // 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
1025

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

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

1031

1032 1033 1034
        while (not addition.empty()) {
            procman_debug("looking for %d parents", int(addition.size()));
            ProcList::iterator it(addition.begin());
1035

1036 1037 1038
            while (it != addition.end()) {
                procman_debug("looking for %d's parent with ppid %d",
                              int((*it)->pid), int((*it)->ppid));
1039 1040


1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
                // inserts the process in the treeview if :
                // - 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
                // see proctable_update_list (ProcData * const procdata)
1053 1054


1055
                if ((*it)->ppid == 0 or in_tree.find((*it)->ppid) != in_tree.end()) {
1056
                    insert_info_to_tree(*it, app);
1057 1058 1059 1060
                    in_tree.insert((*it)->pid);
                    it = addition.erase(it);
                    continue;
                }
1061

1062 1063 1064 1065
                ProcInfo *parent = ProcInfo::find((*it)->ppid);
                // if the parent is unreachable
                if (not parent) {
                    // or std::find(addition.begin(), addition.end(), parent) == addition.end()) {
1066
                    insert_info_to_tree(*it, app, true);
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
                    in_tree.insert((*it)->pid);
                    it = addition.erase(it);
                    continue;
                }

                ++it;
            }
        }
    }
    else {
        // don't care of the tree
        for (ProcList::iterator it(addition.begin()); it != addition.end(); ++it)
1079
            insert_info_to_tree(*it, app);
1080
    }
1081

1082

1083 1084
    for (ProcInfo::Iterator it(ProcInfo::begin()); it != ProcInfo::end(); ++it)
        update_info_mutable_cols(it->second);
Kevin Vandersloot's avatar
Kevin Vandersloot committed
1085 1086
}

1087
static void
1088
proctable_update_list (GsmApplication *app)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
1089
{
1090 1091 1092
    pid_t* pid_list;
    glibtop_proclist proclist;
    glibtop_cpu cpu;
1093 1094
    int which = 0;
    int arg = 0;
1095

1096
    const char* whose_processes = g_settings_get_string (app->settings, GSM_SETTING_SHOW_WHOSE_PROCESSES);
1097 1098 1099 1100 1101 1102 1103 1104 1105
    if (strcmp (whose_processes, "all") == 0) {
        which = GLIBTOP_KERN_PROC_ALL;
        arg = 0;
    } else if (strcmp (whose_processes, "active") == 0) {
        which = GLIBTOP_KERN_PROC_ALL | GLIBTOP_EXCLUDE_IDLE;
        arg = 0;
    } else if (strcmp (whose_processes, "user") == 0) {
      which = GLIBTOP_KERN_PROC_UID;
      arg = getuid ();
1106 1107 1108 1109 1110 1111 1112
    }

    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);
1113 1114
    app->cpu_total_time = MAX(cpu.total - app->cpu_total_time_last, 1);
    app->cpu_total_time_last = cpu.total;
1115
    
1116
    refresh_list (app, pid_list, proclist.number);
1117

1118 1119
    // juggling with tree scroll position to fix https://bugzilla.gnome.org/show_bug.cgi?id=92724
    GtkTreePath* current_top;
1120
    if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(app->tree), 0,0, &current_top, NULL, NULL, NULL)) {
1121
        GtkAdjustment *vadjustment = GTK_ADJUSTMENT (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (app->tree)));
1122 1123
        gdouble current_max = gtk_adjustment_get_upper(vadjustment);
        gdouble current_value = gtk_adjustment_get_value(vadjustment);
1124

1125 1126
        if (app->top_of_tree) {
            // if the visible cell from the top of the tree is still the same, as last time 
1127
            if (gtk_tree_path_compare (app->top_of_tree, current_top) == 0) {
1128
                //but something from the scroll parameters has changed compared to the last values
1129
                if (app->last_vscroll_value == 0 && current_value != 0) {
1130 1131
                    // the tree was scrolled to top, and something has been added above the current top row
                    gtk_tree_view_scroll_to_point(GTK_TREE_VIEW(app->tree), -1, 0);
1132
                } else if (current_max > app->last_vscroll_max && app->last_vscroll_max == app->last_vscroll_value) {
1133 1134 1135
                    // the tree was scrolled to bottom, something has been added below the current bottom row
                    gtk_tree_view_scroll_to_point(GTK_TREE_VIEW(app->tree), -1, current_max);
                }
1136
            }
1137

1138
            gtk_tree_path_free(app->top_of_tree);
1139
        }
1140

1141 1142 1143
        app->top_of_tree = current_top;
        app->last_vscroll_value = current_value;
        app->last_vscroll_max = current_max;
1144
    }
1145 1146 1147 1148

    g_free (pid_list);

    /* proclist.number == g_list_length(procdata->info) == g_hash_table_size(procdata->pids) */
Kevin Vandersloot's avatar
Kevin Vandersloot committed
1149 1150
}

1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
static char *
make_loadavg_string (void)
{
    glibtop_loadavg buf;

    glibtop_get_loadavg (&buf);

    return g_strdup_printf (_("Load averages for the last 1, 5, 15 minutes: "
                              "%0.2f, %0.2f, %0.2f"),
                            buf.loadavg[0],
                            buf.loadavg[1],
                            buf.loadavg[2]);
}

Kevin Vandersloot's avatar
Kevin Vandersloot committed
1165
void
1166
proctable_update (GsmApplication * const app)
Kevin Vandersloot's avatar
Kevin Vandersloot committed
1167
{
1168
    char* string;
1169

1170
    string = make_loadavg_string();
1171
    gtk_label_set_text (GTK_LABEL(app->loadavg), string);
1172
    g_free (string);
1173

1174
    proctable_update_list (app);
Kevin Vandersloot's avatar
Kevin Vandersloot committed
1175 1176
}

Benoît Dejean's avatar
Benoît Dejean committed
1177
void
1178
proctable_free_table (GsmApplication * const app)
1179
{
1180 1181
    for (ProcInfo::Iterator it(ProcInfo::begin()); it != ProcInfo::end(); ++it)
        delete it->second;
Benoît Dejean's avatar
Benoît Dejean committed
1182

1183
    ProcInfo::all.clear();
1184
}
1185

1186
void
Benoît Dejean's avatar
Benoît Dejean committed
1187
ProcInfo::set_icon(Glib::RefPtr<Gdk::Pixbuf> icon)
1188
{
1189
    this->pixbuf = icon;
1190

1191
    GtkTreeModel *model;
1192 1193
    model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (
            gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT(
1194
            gtk_tree_view_get_model (GTK_TREE_VIEW(GsmApplication::get()->tree))))));
1195 1196 1197
    gtk_tree_store_set(GTK_TREE_STORE(model), &this->node,
                       COL_PIXBUF, (this->pixbuf ? this->pixbuf->gobj() : NULL),
                       -1);
1198
}
1199 1200

void
1201
proctable_freeze (GsmApplication *app)
1202 1203 1204 1205 1206 1207 1208 1209
{
    if (app->timeout) {
      g_source_remove (app->timeout);
      app->timeout = 0;
    }
}

void
1210
proctable_thaw (GsmApplication *app)
1211
{
1212 1213 1214
    if (app->timeout)
        return;

1215 1216 1217 1218 1219 1220
    app->timeout = g_timeout_add (app->config.update_interval,
                                  cb_timeout,
                                  app);
}

void