adding network !!

- rx/tx graph
- label rx/tx/error
- rewriting some codes
- adding packetin/packetout for each pid
- adding pcap sniffing port -> count
- adding inode mapping to get port <- inode <- pid

# Conflicts:
#	src/main.c
#	src/process-monitor.c
#	src/process-monitor.h
#	src/process-statusbar.c
#	src/process-window.c
#	src/process-window.h
#	src/settings.h
#	src/task-manager-linux.c
#	src/task-manager.c
#	src/task-manager.h

starting freebsd

get mac address trough getifaddrs function, should be more portable
remove mac_get_binary_from_file that use /sys/class/net/%s/address

freebsd adding packetin/packetout/active socket trough sockstat
- move linux code
- adding pseudo inode mapping

OpenBSD suppoort & code cleanup

NetBSD global network usage & per process

hiding of settings and columns on network initialization failure

Apply .clang-format file

# Conflicts:
#	src/main.c
#	src/process-monitor.c
#	src/process-monitor.h
#	src/process-statusbar.c
#	src/process-window.c
#	src/process-window.h
#	src/settings-dialog.c
#	src/settings.h
#	src/task-manager-bsd.c
#	src/task-manager.c
#	src/task-manager.h

use AC_CHECK_LIB and add the corresponding ifdef to disable pcap functionality

fix compilation openbsd

clang format

fix compilation netbsd

clang format ...

disable clang-format on task-manager-bsd, important include order

fix compilation freebsd

disable clang-format on task-manager-freebsd, important include order

typo

openbsd include

fix segfault on openbsd without permissions

fix warning on freebsd : -Wint-to-pointer-cast -Wmissing-declarations -Wshadow

fix compilation for "skel" and "solaris" (implementation todo)

freebsd iterate over all network interface

linux iterate over all network interface and fix some warnings

netbsd iterator over all network interface and fix args issue trough kvm_getargv2

fix ptr issue, clang format

ide change coding style ?

fix close button, hide when "Keep in the notification area" is enable

Discard my changes on the README

Solaris adding mac adresse, rx/tx/error for the network graph
libsocket is in the default package of solaris, it should be linked for getifaddrs

adding packet callback for solaris

solaris pid to socket mapping (tcp, tcp6, udp, udp6) !

final clang format

credit ?

Linux adding udp, udp6, icmp, icmp6, raw, raw6.
Disable virtual network device on linux.

Fix -Wincompatible-pointer-types and -Wdeprecated-declarations
use WNCK_CHECK_VERSION and WnckHandle on version >= 43.0.0

asked changes
This commit is contained in:
Phirxian
2024-05-20 12:04:10 +02:00
committed by Valeriano A.R.
parent b9f85dff85
commit b470c568cf
28 changed files with 2301 additions and 93 deletions

View File

@@ -1,6 +1,7 @@
dnl
dnl xfce4-taskmanager - A small taskmanager based on the Xfce 4 libraries.
dnl
dnl 2024-2024 Jehan-Antoine Vayssade
dnl 2014-2021 Simon Steinbess
dnl 2018-2019 Rozhuk Ivan
dnl 2014 Landry Breuil
@@ -81,6 +82,9 @@ dnl ***********************************
XDT_CHECK_OPTIONAL_PACKAGE([LIBX11], [x11], [1.6.7], [libx11], [Libx11 support])
XDT_CHECK_OPTIONAL_PACKAGE([WNCK], [libwnck-3.0], [3.2], [wnck3], [building with libwnck3 for window icons/names], [yes])
AC_CHECK_LIB(pcap, [pcap_open_live])
AC_CHECK_HEADERS([pcap.h])
dnl ***********************************
dnl ********** Check for skel *********
dnl ***********************************
@@ -104,12 +108,14 @@ else
;;
dragonfly*|netbsd*|openbsd*|darwin*)
ac_os_implementation="bsd"
AC_CHECK_LIB([kvm], [kvm_openfiles])
AC_CHECK_HEADERS([err.h pwd.h stdlib.h string.h sys/param.h sys/sched.h \
sys/swap.h sys/sysctl.h sys/types.h unistd.h])
;;
solaris*)
ac_os_implementation="solaris"
AC_CHECK_LIB([kstat], [kstat_open])
AC_CHECK_LIB([socket], [freeifaddrs])
AC_CHECK_HEADERS([fcntl.h kstat.h procfs.h pwd.h stdlib.h string.h \
sys/procfs.h sys/stat.h sys/swap.h sys/types.h])
;;

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View File

@@ -8,6 +8,7 @@ bin_PROGRAMS = \
xfce4_taskmanager_CFLAGS = \
$(CAIRO_CFLAGS) \
$(LIBPCAP_CFLAGS) \
$(LIBX11_CFLAGS) \
$(LIBXMU_CFLAGS) \
$(GTK3_CFLAGS) \
@@ -18,6 +19,7 @@ xfce4_taskmanager_CFLAGS = \
xfce4_taskmanager_LDADD = \
$(CAIRO_LIBS) \
$(LIBPCAP_LIBS) \
$(LIBX11_LIBS) \
$(LIBXMU_LIBS) \
$(GTK3_LIBS) \
@@ -30,6 +32,8 @@ xfce4_taskmanager_SOURCES = \
main.c \
process-window_ui.h \
process-window.c process-window.h \
inode-to-sock.c inode-to-sock.h \
network-analyzer.c network-analyzer.h \
process-monitor.c process-monitor.h \
process-tree-model.c process-tree-model.h \
process-tree-view.c process-tree-view.h \

View File

@@ -44,7 +44,7 @@ static App *apps_lookup_app (GArray *apps, WnckApplication *application);
static void application_opened (WnckScreen *screen, WnckApplication *application, XtmAppManager *manager);
static void application_closed (WnckScreen *screen, WnckApplication *application, XtmAppManager *manager);
static void scale_factor_changed (GdkMonitor *monitor);
// static WnckHandle *handle = NULL;
static void
@@ -59,10 +59,18 @@ static void
xtm_app_manager_init (XtmAppManager *manager)
{
WnckApplication *application;
WnckScreen *screen = wnck_screen_get_default ();
GdkMonitor *monitor = gdk_display_get_monitor (gdk_display_get_default (), 0);
WnckScreen *screen;
GdkMonitor *monitor;
GList *windows, *l;
// #if WNCK_CHECK_VERSION(43, 0, 0)
// handle = wnck_handle_new (WNCK_CLIENT_TYPE_APPLICATION);
// screen = wnck_handle_get_default_screen (handle);
// #else
screen = wnck_screen_get_default ();
// #endif
monitor = gdk_display_get_monitor (gdk_display_get_default (), 0);
/* Retrieve initial applications */
while (gtk_events_pending ())
gtk_main_iteration ();
@@ -210,7 +218,11 @@ application_closed (WnckScreen *screen __unused, WnckApplication *application, X
static void
scale_factor_changed (GdkMonitor *monitor)
{
// #if WNCK_CHECK_VERSION(43, 0, 0)
// wnck_handle_set_default_mini_icon_size (handle, WNCK_DEFAULT_MINI_ICON_SIZE * gdk_monitor_get_scale_factor (monitor));
// #else
wnck_set_default_mini_icon_size (WNCK_DEFAULT_MINI_ICON_SIZE * gdk_monitor_get_scale_factor (monitor));
// #endif
}

45
src/inode-to-sock.c Normal file
View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2024 Jehan-Antoine Vayssade, <javayss@sleek-think.ovh>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "inode-to-sock.h"
#include "stdio.h"
XtmInodeToSock *
xtm_create_inode_to_sock (void)
{
XtmInodeToSock *its = g_new (XtmInodeToSock, 1);
its->hash = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL);
// freebsd only
its->pid = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL);
return its;
}
void
xtm_destroy_inode_to_sock (XtmInodeToSock *its)
{
if (its == NULL)
return;
g_hash_table_destroy (its->hash);
g_hash_table_destroy (its->pid);
free (its);
}
XtmInodeToSock *
xtm_inode_to_sock_get_default (void)
{
static XtmInodeToSock *its = NULL;
if (its == NULL)
its = xtm_create_inode_to_sock ();
return its;
}

29
src/inode-to-sock.h Normal file
View File

@@ -0,0 +1,29 @@
/*
* Copyright (c) 2024 Jehan-Antoine Vayssade, <javayss@sleek-think.ovh>
*
* 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.
*/
#ifndef INMODE_TO_SOCK_H
#define INMODE_TO_SOCK_H
#include <glib.h>
typedef struct _XtmInodeToSock XtmInodeToSock;
struct _XtmInodeToSock
{
// inode to port
GHashTable *hash;
// inode to pid (freebsd)
GHashTable *pid;
};
XtmInodeToSock *xtm_create_inode_to_sock (void);
void xtm_refresh_inode_to_sock (XtmInodeToSock *);
void xtm_destroy_inode_to_sock (XtmInodeToSock *);
XtmInodeToSock *xtm_inode_to_sock_get_default (void);
#endif

View File

@@ -12,6 +12,7 @@
#include "config.h"
#endif
#include "network-analyzer.h"
#include "process-window.h"
#include "settings.h"
#include "task-manager.h"
@@ -28,6 +29,7 @@ static XtmTaskManager *task_manager;
static guint timer_id;
static gboolean start_hidden = FALSE;
static gboolean standalone = FALSE;
static XtmNetworkAnalyzer *analyzer = NULL;
static GOptionEntry main_entries[] = {
{ "start-hidden", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &start_hidden, "Don't open a task manager window", NULL },
@@ -121,15 +123,14 @@ destroy_window (void)
}
static gboolean
delete_window (void)
delete_window (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
if (!status_icon_get_visible ())
{
if (timer_id > 0)
g_source_remove (timer_id);
gtk_main_quit ();
destroy_window ();
return FALSE;
}
gtk_widget_hide (window);
return TRUE;
}
@@ -140,8 +141,10 @@ collect_data (void)
guint num_processes;
gfloat cpu, memory_percent, swap_percent;
guint64 swap_used, swap_free, swap_total, memory_used, memory_total;
guint64 tcp_rx, tcp_tx, tcp_error;
gchar *used, *total, tooltip[1024], memory_info[64], swap_info[64];
xtm_task_manager_get_network_info (task_manager, &tcp_rx, &tcp_tx, &tcp_error);
xtm_task_manager_get_system_info (task_manager, &num_processes, &cpu, &memory_used, &memory_total, &swap_used, &swap_total);
memory_percent = (memory_total != 0) ? ((memory_used * 100.0f) / (float)memory_total) : 0.0f;
@@ -159,19 +162,23 @@ collect_data (void)
g_free (used);
g_free (total);
xtm_process_window_set_system_info (XTM_PROCESS_WINDOW (window), num_processes, cpu, memory_percent, memory_info, swap_percent, swap_info);
xtm_process_window_set_system_info (XTM_PROCESS_WINDOW (window), num_processes, cpu, memory_percent, memory_info, swap_percent, swap_info, tcp_rx, tcp_tx, tcp_error);
xtm_task_manager_get_swap_usage (task_manager, &swap_free, &swap_total);
xtm_process_window_show_swap_usage (XTM_PROCESS_WINDOW (window), (swap_total > 0));
if (status_icon_get_visible ())
{
g_snprintf (tooltip, sizeof (tooltip),
g_snprintf (
tooltip, sizeof (tooltip),
_("<b>Processes:</b> %u\n"
"<b>CPU:</b> %.0f%%\n"
"<b>Memory:</b> %s\n"
"<b>Swap:</b> %s"),
num_processes, cpu, memory_info, swap_info);
"<b>Swap:</b> %s\n"
"<b>Network:</b> %.2f / %.2f MB/s"),
num_processes, cpu, memory_info, swap_info,
tcp_rx * 1e-5, tcp_tx * 1e-5);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
gtk_status_icon_set_tooltip_markup (GTK_STATUS_ICON (status_icon_or_null), tooltip);
G_GNUC_END_IGNORE_DEPRECATIONS
@@ -253,8 +260,7 @@ main (int argc, char *argv[])
if (!xfconf_init (&error))
{
xfce_message_dialog (NULL, _("Xfce Notify Daemon"),
"dialog-error",
_("Settings daemon is unavailable"),
"dialog-error", _("Settings daemon is unavailable"),
error->message,
"application-exit", GTK_RESPONSE_ACCEPT,
NULL);
@@ -273,6 +279,8 @@ main (int argc, char *argv[])
g_signal_connect_swapped (app, "activate", G_CALLBACK (xtm_process_window_show), window);
//! create net
analyzer = xtm_network_analyzer_get_default ();
task_manager = xtm_task_manager_new (xtm_process_window_get_model (XTM_PROCESS_WINDOW (window)));
collect_data ();
@@ -282,8 +290,11 @@ main (int argc, char *argv[])
g_signal_connect_after (settings, "notify::full-command-line", G_CALLBACK (collect_data), NULL);
g_signal_connect (settings, "notify::show-status-icon", G_CALLBACK (show_hide_status_icon), NULL);
g_signal_connect (window, "destroy", G_CALLBACK (destroy_window), NULL);
g_signal_connect (xtm_process_window_get (XTM_PROCESS_WINDOW (window)), "destroy", G_CALLBACK (destroy_window), NULL);
g_signal_connect (xtm_process_window_get (XTM_PROCESS_WINDOW (window)), "delete-event", G_CALLBACK (delete_window), NULL);
g_signal_connect (window, "delete-event", G_CALLBACK (delete_window), NULL);
g_signal_connect (window, "destroy", G_CALLBACK (destroy_window), NULL);
if (gtk_widget_get_visible (window) || status_icon_get_visible ())
gtk_main ();
@@ -297,5 +308,7 @@ main (int argc, char *argv[])
g_object_unref (status_icon_or_null);
xfconf_shutdown ();
xtm_destroy_network_analyzer (analyzer);
return 0;
}

168
src/network-analyzer.c Normal file
View File

@@ -0,0 +1,168 @@
/*
* Copyright (c) 2024 Jehan-Antoine Vayssade, <javayss@sleek-think.ovh>
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "network-analyzer.h"
#include "task-manager.h"
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <sys/types.h>
void *network_analyzer_thread (void *ptr);
void
increament_packet_count (char *mac, char *direction, GHashTable *hash_table, long int port)
{
gint64 *key = g_new0 (gint64, 1);
*key = port;
gpointer value = g_hash_table_lookup (hash_table, key);
g_hash_table_replace (hash_table, key, (gpointer)(((guint64)value) + 1));
// printf ("%s -> %s %ld: %ld\n", mac, direction, port, ((guint64)value) + 1);
}
void *
network_analyzer_thread (void *ptr)
{
#ifdef HAVE_LIBPCAP
XtmNetworkAnalyzer *analyzer = (XtmNetworkAnalyzer *)ptr;
pcap_loop (analyzer->handle, -1, packet_callback, (void *)analyzer);
#endif
return NULL;
}
XtmNetworkAnalyzer *
xtm_create_network_analyzer (void)
{
XtmNetworkAnalyzer *analyzer = NULL;
#ifdef HAVE_LIBPCAP
XtmNetworkAnalyzer *current = NULL;
char errbuf[PCAP_ERRBUF_SIZE];
pcap_if_t *it = NULL;
if (pcap_findalldevs (&it, errbuf) != 0)
{
fprintf (stderr, "Error finding device: %s\n", errbuf);
return NULL;
}
while (it)
{
guint8 mac[6];
char *device = it->name;
//! todo check pcap lib ?
if (get_mac_address (device, mac) == 0)
{
printf (
"%s, MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
device, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
else
{
fprintf (stderr, "No mac adresse for %s\n", device);
it = it->next;
continue;
}
// Open a pcap session
pcap_t *handle = pcap_open_live (device, BUFSIZ, 1, 1000, errbuf);
if (handle == NULL)
{
fprintf (stderr, "Could not open device %s: %s\n", device, errbuf);
it = it->next;
continue;
}
if (analyzer == NULL)
{
analyzer = (XtmNetworkAnalyzer *)malloc (sizeof (XtmNetworkAnalyzer));
analyzer->next = NULL;
current = analyzer;
}
else
{
current->next = (XtmNetworkAnalyzer *)malloc (sizeof (XtmNetworkAnalyzer));
current = current->next;
current->next = NULL;
}
current->handle = handle;
current->iface = it;
current->packetin = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL);
current->packetout = g_hash_table_new_full (g_int_hash, g_int_equal, g_free, NULL);
memcpy (current->mac, mac, sizeof (uint8_t) * 6);
pthread_mutex_init (&current->lock, NULL);
pthread_create (&current->thread, NULL, network_analyzer_thread, (void *)current);
it = it->next;
}
if (analyzer == NULL)
{
fprintf (stderr, "Could not open any device %s\n", errbuf);
pcap_freealldevs (it);
}
#endif
return analyzer;
}
XtmNetworkAnalyzer *
xtm_network_analyzer_get_default (void)
{
static int initialized = FALSE;
static XtmNetworkAnalyzer *analyzer = NULL;
if (initialized == FALSE)
{
initialized = TRUE;
// analyzer may be NULL if no device can be opened
analyzer = xtm_create_network_analyzer ();
}
return analyzer;
}
void
xtm_destroy_network_analyzer (XtmNetworkAnalyzer *analyzer)
{
#ifdef HAVE_LIBPCAP
if (analyzer == NULL)
return;
XtmNetworkAnalyzer *previous = analyzer;
pcap_if_t *it = analyzer->iface;
while (analyzer)
{
pthread_cancel (analyzer->thread);
pcap_close (analyzer->handle);
g_hash_table_destroy (analyzer->packetin);
g_hash_table_destroy (analyzer->packetout);
pthread_mutex_destroy (&analyzer->lock);
analyzer = analyzer->next;
free (previous);
previous = analyzer;
}
pcap_freealldevs (it);
#endif
}

45
src/network-analyzer.h Normal file
View File

@@ -0,0 +1,45 @@
/*
* Copyright (c) 2024 Jehan-Antoine Vayssade, <javayss@sleek-think.ovh>
*
* 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.
*/
#ifndef NETWORK_ANALYZER_H
#define NETWORK_ANALYZER_H
#include <glib.h>
#ifdef HAVE_LIBPCAP
#include <pcap.h>
#include <pthread.h>
#endif
typedef struct _XtmNetworkAnalyzer XtmNetworkAnalyzer;
struct _XtmNetworkAnalyzer
{
// interface mac adress
guint8 mac[6];
void *iface; // pcap_if_t
void *handle; // pcap_t
pthread_t thread;
pthread_mutex_t lock;
// map port and number of packets
GHashTable *packetin;
GHashTable *packetout;
// next interface
XtmNetworkAnalyzer *next;
};
XtmNetworkAnalyzer *xtm_create_network_analyzer (void);
void increament_packet_count (char *mac, char *direction, GHashTable *hash_table, long int port);
void xtm_destroy_network_analyzer (XtmNetworkAnalyzer *analyzer);
XtmNetworkAnalyzer *xtm_network_analyzer_get_default (void);
#endif

View File

@@ -16,7 +16,7 @@
#include <cairo.h>
#define MAX_GRAPH 3
enum
{
@@ -34,8 +34,8 @@ struct _XtmProcessMonitor
/*<private>*/
gfloat step_size;
gint type;
GArray *history;
GArray *history_swap;
GArray *history[MAX_GRAPH];
gfloat max[MAX_GRAPH];
gboolean dark_mode;
};
G_DEFINE_TYPE (XtmProcessMonitor, xtm_process_monitor, GTK_TYPE_DRAWING_AREA)
@@ -76,8 +76,12 @@ xtm_process_monitor_init (XtmProcessMonitor *monitor)
{
GtkSettings *settings = gtk_settings_get_default ();
monitor->history = g_array_new (FALSE, TRUE, sizeof (gfloat));
monitor->history_swap = g_array_new (FALSE, TRUE, sizeof (gfloat));
for (int graph = 0; graph < MAX_GRAPH; ++graph)
{
monitor->history[graph] = g_array_new (FALSE, TRUE, sizeof (gfloat));
monitor->max[graph] = 1.0;
}
monitor->dark_mode = xtm_gtk_widget_is_dark_mode (GTK_WIDGET (monitor));
g_signal_connect_swapped (settings, "notify::gtk-theme-name", G_CALLBACK (xtm_process_monitor_on_notify_theme_name), monitor);
@@ -89,8 +93,8 @@ xtm_process_monitor_finalize (GObject *object)
{
XtmProcessMonitor *monitor = XTM_PROCESS_MONITOR (object);
g_array_free (monitor->history, TRUE);
g_array_free (monitor->history_swap, TRUE);
for (int graph = 0; graph < MAX_GRAPH; ++graph)
g_array_free (monitor->history[graph], TRUE);
G_OBJECT_CLASS (xtm_process_monitor_parent_class)->finalize (object);
}
@@ -142,13 +146,10 @@ xtm_process_monitor_draw (GtkWidget *widget, cairo_t *cr)
guint minimum_history_length;
minimum_history_length = (guint)(gtk_widget_get_allocated_width (widget) / monitor->step_size);
if (monitor->history->len < minimum_history_length)
{
g_array_set_size (monitor->history, minimum_history_length + 1);
if (monitor->type == 1)
g_array_set_size (monitor->history_swap, minimum_history_length + 1);
}
for (int graph = 0; graph < MAX_GRAPH; ++graph)
if (monitor->history[graph]->len < minimum_history_length)
g_array_set_size (monitor->history[graph], minimum_history_length + 1);
xtm_process_monitor_paint (monitor, cr);
return FALSE;
@@ -172,6 +173,30 @@ xtm_process_monitor_cairo_set_source_rgb (cairo_t *cr, double red, double green,
cairo_set_source_rgb (cr, red, green, blue);
}
static void
xtm_process_monitor_cairo_set_color (XtmProcessMonitor *monitor, cairo_t *cr, gfloat alpha, guint variant)
{
if (monitor->type == 0)
xtm_process_monitor_cairo_set_source_rgba (cr, 1.0, 0.43, 0.0, alpha, monitor->dark_mode);
else if (monitor->type == 1)
{
// xtm_process_monitor_cairo_set_source_rgba (cr, 0.67, 0.09, 0.32, alpha, monitor->dark_mode);
if (variant == 0)
xtm_process_monitor_cairo_set_source_rgba (cr, 0.80, 0.22, 0.42, alpha, monitor->dark_mode);
else
xtm_process_monitor_cairo_set_source_rgba (cr, 0.50, 0.50, 0.50, alpha, monitor->dark_mode);
}
else
{
if (variant == 0)
xtm_process_monitor_cairo_set_source_rgba (cr, 0.00, 0.42, 0.64, alpha, monitor->dark_mode);
else if (variant == 1)
xtm_process_monitor_cairo_set_source_rgba (cr, 0.02, 0.71, 0.81, alpha, monitor->dark_mode);
else
xtm_process_monitor_cairo_set_source_rgba (cr, 0.00, 1.00, 1.00, alpha, monitor->dark_mode);
}
}
static cairo_surface_t *
xtm_process_monitor_graph_surface_create (XtmProcessMonitor *monitor, gint width, gint height)
{
@@ -180,69 +205,39 @@ xtm_process_monitor_graph_surface_create (XtmProcessMonitor *monitor, gint width
gdouble peak, step_size;
gint i;
if (monitor->history->len <= 1)
{
g_warning ("Cannot paint graph with n_peak <= 1");
return NULL;
}
step_size = (gdouble)monitor->step_size;
graph_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
cr = cairo_create (graph_surface);
/* Draw line and area below the line, distinguish between CPU (0) and Mem (1) color-wise */
cairo_translate (cr, step_size, 0);
cairo_set_line_width (cr, 0.85);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
cairo_set_antialias (cr, CAIRO_ANTIALIAS_DEFAULT);
cairo_move_to (cr, width, height);
/* Create a line before the call to cairo_translate,
* to avoid creating a downward sloping line going off the graph */
peak = g_array_index (monitor->history, gfloat, 0);
cairo_line_to (cr, width, (1.0 - peak) * height);
for (int graph = 0; graph < MAX_GRAPH; ++graph)
{
if (monitor->history[graph]->len <= 1)
continue;
cairo_move_to (cr, width, height);
cairo_translate (cr, step_size, 0);
xtm_process_monitor_cairo_set_color (monitor, cr, 0.3, graph);
for (i = 0; (step_size * (i - 1)) <= width; i++)
{
peak = g_array_index (monitor->history, gfloat, i);
peak = g_array_index (monitor->history[graph], gfloat, i) / monitor->max[graph];
cairo_translate (cr, -step_size, 0);
cairo_line_to (cr, width, (1.0 - peak) * height);
}
if (monitor->type == 0)
xtm_process_monitor_cairo_set_source_rgba (cr, 1.0, 0.43, 0.0, 0.3, monitor->dark_mode);
else
xtm_process_monitor_cairo_set_source_rgba (cr, 0.67, 0.09, 0.32, 0.3, monitor->dark_mode);
cairo_line_to (cr, width, height);
cairo_fill_preserve (cr);
if (monitor->type == 0)
xtm_process_monitor_cairo_set_source_rgba (cr, 1.0, 0.43, 0.0, 1.0, monitor->dark_mode);
else
xtm_process_monitor_cairo_set_source_rgba (cr, 0.67, 0.09, 0.32, 1.0, monitor->dark_mode);
xtm_process_monitor_cairo_set_color (monitor, cr, 1.0, graph);
cairo_stroke (cr);
/* Draw Swap graph */
if (monitor->type == 1)
{
cairo_translate (cr, step_size * i, 0);
cairo_move_to (cr, width, height);
peak = g_array_index (monitor->history_swap, gfloat, 0);
cairo_line_to (cr, width, (1.0 - peak) * height);
for (i = 0; (step_size * (i - 1)) <= width; i++)
{
peak = g_array_index (monitor->history_swap, gfloat, i);
cairo_translate (cr, -step_size, 0);
cairo_line_to (cr, width, (1.0 - peak) * height);
}
xtm_process_monitor_cairo_set_source_rgba (cr, 0.33, 0.04, 0.16, 0.3, monitor->dark_mode);
cairo_line_to (cr, width, height);
cairo_fill_preserve (cr);
xtm_process_monitor_cairo_set_source_rgba (cr, 0.33, 0.04, 0.16, 1.0, monitor->dark_mode);
cairo_stroke (cr);
cairo_translate (cr, width, 0);
}
cairo_destroy (cr);
@@ -300,21 +295,17 @@ xtm_process_monitor_new (void)
}
void
xtm_process_monitor_add_peak (XtmProcessMonitor *monitor, gfloat peak, gfloat peak_swap)
xtm_process_monitor_add_peak (XtmProcessMonitor *monitor, gfloat peak, gint index)
{
g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor));
g_return_if_fail (peak >= 0.0f && peak <= 1.0f);
// g_return_if_fail (peak >= 0.0f && peak <= 1.0f);
g_array_prepend_val (monitor->history, peak);
if (monitor->history->len > 1)
g_array_remove_index (monitor->history, monitor->history->len - 1);
if (peak > monitor->max[index])
monitor->max[index] = peak;
if (monitor->type == 1)
{
g_array_prepend_val (monitor->history_swap, peak_swap);
if (monitor->history_swap->len > 1)
g_array_remove_index (monitor->history_swap, monitor->history_swap->len - 1);
}
g_array_prepend_val (monitor->history[index], peak);
if (monitor->history[index]->len > 1)
g_array_remove_index (monitor->history[index], monitor->history[index]->len - 1);
if (GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET (monitor))))
gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (monitor)), NULL, FALSE);
@@ -340,8 +331,8 @@ void
xtm_process_monitor_clear (XtmProcessMonitor *monitor)
{
g_return_if_fail (XTM_IS_PROCESS_MONITOR (monitor));
g_array_set_size (monitor->history, 0);
g_array_set_size (monitor->history_swap, 0);
for (int graph = 0; graph < MAX_GRAPH; ++graph)
g_array_set_size (monitor->history[graph], 0);
if (GDK_IS_WINDOW (gtk_widget_get_window (GTK_WIDGET (monitor))))
gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (monitor)), NULL, FALSE);
}

View File

@@ -1,4 +1,5 @@
/*
* Copyright (c) 2024 Jehan-Antoine Vayssade, <javayss@sleek-think.ovh>
* Copyright (c) 2010 Mike Massonnet, <mmassonnet@xfce.org>
*
* This program is free software; you can redistribute it and/or modify
@@ -24,7 +25,7 @@ typedef struct _XtmProcessMonitor XtmProcessMonitor;
GType xtm_process_monitor_get_type (void);
GtkWidget *xtm_process_monitor_new (void);
void xtm_process_monitor_add_peak (XtmProcessMonitor *monitor, gfloat peak, gfloat peak_swap);
void xtm_process_monitor_add_peak (XtmProcessMonitor *monitor, gfloat peak, gint index);
void xtm_process_monitor_set_step_size (XtmProcessMonitor *monitor, gfloat step_size);
void xtm_process_monitor_set_type (XtmProcessMonitor *monitor, gint type);
void xtm_process_monitor_clear (XtmProcessMonitor *monitor);

View File

@@ -26,7 +26,11 @@ enum
PROP_SWAP,
PROP_SHOW_SWAP,
PROP_NUM_PROCESSES,
PROP_NETWORK_RX,
PROP_NETWORK_TX,
PROP_NETWORK_ERROR,
};
typedef struct _XtmProcessStatusbarClass XtmProcessStatusbarClass;
struct _XtmProcessStatusbarClass
{
@@ -43,11 +47,19 @@ struct _XtmProcessStatusbar
GtkWidget *label_memory;
GtkWidget *label_swap;
GtkWidget *label_net_rx;
GtkWidget *label_net_tx;
GtkWidget *label_net_error;
gfloat cpu;
gchar memory[64];
gchar swap[64];
guint num_processes;
gfloat tcp_rx;
gfloat tcp_tx;
guint64 tcp_error;
gboolean dark_mode;
};
G_DEFINE_TYPE (XtmProcessStatusbar, xtm_process_statusbar, GTK_TYPE_BOX)
@@ -74,6 +86,12 @@ xtm_process_statusbar_class_init (XtmProcessStatusbarClass *klass)
g_param_spec_boolean ("show-swap", "ShowSwap", "Show or hide swap usage", TRUE, G_PARAM_WRITABLE));
g_object_class_install_property (class, PROP_NUM_PROCESSES,
g_param_spec_uint ("num-processes", "NumProcesses", "Number of processes", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT | G_PARAM_WRITABLE));
g_object_class_install_property (class, PROP_NETWORK_RX,
g_param_spec_float ("network-rx", "RX", "Net rx", 0, 100, 0, G_PARAM_CONSTRUCT | G_PARAM_WRITABLE));
g_object_class_install_property (class, PROP_NETWORK_TX,
g_param_spec_float ("network-tx", "TX", "Net tx", 0, 100, 0, G_PARAM_CONSTRUCT | G_PARAM_WRITABLE));
g_object_class_install_property (class, PROP_NETWORK_ERROR,
g_param_spec_uint64 ("network-error", "NetEror", "Number of error since last update", 0, G_MAXUINT64, 0, G_PARAM_CONSTRUCT | G_PARAM_WRITABLE));
}
static void
@@ -99,13 +117,16 @@ xtm_process_statusbar_on_notify_theme_name (XtmProcessStatusbar *statusbar)
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_num_processes);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_memory);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_swap);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_net_rx);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_net_tx);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_net_error);
}
static void
xtm_process_statusbar_init (XtmProcessStatusbar *statusbar)
{
GtkSettings *settings = gtk_settings_get_default ();
GtkWidget *hbox, *hbox_cpu, *hbox_mem;
GtkWidget *hbox, *hbox_cpu, *hbox_net, *hbox_mem;
GtkStyleContext *context;
GtkCssProvider *provider;
statusbar->settings = xtm_settings_get_default ();
@@ -116,6 +137,7 @@ xtm_process_statusbar_init (XtmProcessStatusbar *statusbar)
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 16);
hbox_cpu = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 16);
hbox_net = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 16);
hbox_mem = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 16);
statusbar->label_cpu = gtk_label_new (NULL);
@@ -143,11 +165,39 @@ xtm_process_statusbar_init (XtmProcessStatusbar *statusbar)
gtk_box_pack_start (GTK_BOX (hbox_mem), statusbar->label_swap, TRUE, FALSE, 0);
context = gtk_widget_get_style_context (statusbar->label_swap);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "* { color: #75324d; } .dark { color: #8acdb2; }", -1, NULL);
gtk_css_provider_load_from_data (provider, "* { color: #808080; } .dark { color: #808080; }", -1, NULL);
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider);
statusbar->label_net_rx = gtk_label_new (NULL);
gtk_label_set_ellipsize (GTK_LABEL (statusbar->label_net_rx), PANGO_ELLIPSIZE_END);
gtk_box_pack_start (GTK_BOX (hbox_net), statusbar->label_net_rx, TRUE, FALSE, 0);
context = gtk_widget_get_style_context (statusbar->label_net_rx);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "* { color: #006ca2; } .dark { color: #ff935d; }", -1, NULL);
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider);
statusbar->label_net_tx = gtk_label_new (NULL);
gtk_label_set_ellipsize (GTK_LABEL (statusbar->label_net_tx), PANGO_ELLIPSIZE_END);
gtk_box_pack_start (GTK_BOX (hbox_net), statusbar->label_net_tx, TRUE, FALSE, 0);
context = gtk_widget_get_style_context (statusbar->label_net_tx);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "* { color: #05b6ce; } .dark { color: #fa4931; }", -1, NULL);
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider);
statusbar->label_net_error = gtk_label_new (NULL);
gtk_label_set_ellipsize (GTK_LABEL (statusbar->label_net_error), PANGO_ELLIPSIZE_END);
gtk_box_pack_start (GTK_BOX (hbox_net), statusbar->label_net_error, TRUE, FALSE, 0);
context = gtk_widget_get_style_context (statusbar->label_net_error);
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_data (provider, "* { color: #008080; } .dark { color: #FF0000; }", -1, NULL);
gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (provider);
gtk_box_pack_start (GTK_BOX (hbox), hbox_cpu, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox), hbox_net, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (hbox), hbox_mem, TRUE, TRUE, 0);
gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
@@ -157,6 +207,9 @@ xtm_process_statusbar_init (XtmProcessStatusbar *statusbar)
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_num_processes);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_memory);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_swap);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_net_rx);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_net_tx);
xtm_process_statusbar_set_label_style (statusbar, statusbar->label_net_error);
gtk_widget_show_all (hbox);
}
@@ -224,6 +277,31 @@ xtm_process_statusbar_set_property (GObject *object, guint property_id, const GV
g_free (text);
break;
case PROP_NETWORK_RX:
statusbar->tcp_rx = g_value_get_float (value);
// statusbar->tcp_rx = interval_to_second(statusbar->tcp_rx, statusbar->settings);
float_value = rounded_float_value (statusbar->tcp_rx, statusbar->settings);
text = g_strdup_printf (_("RX: %s MB/s"), float_value);
gtk_label_set_text (GTK_LABEL (statusbar->label_net_rx), text);
g_free (text);
break;
case PROP_NETWORK_TX:
statusbar->tcp_tx = g_value_get_float (value);
// statusbar->tcp_tx = interval_to_second(statusbar->tcp_tx, statusbar->settings);
float_value = rounded_float_value (statusbar->tcp_tx, statusbar->settings);
text = g_strdup_printf (_("TX: %s MB/s"), float_value);
gtk_label_set_text (GTK_LABEL (statusbar->label_net_tx), text);
g_free (text);
break;
case PROP_NETWORK_ERROR:
statusbar->tcp_error = g_value_get_uint64 (value);
text = g_strdup_printf (_("Error: %lu"), statusbar->tcp_error);
gtk_label_set_text (GTK_LABEL (statusbar->label_net_error), text);
g_free (text);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;

View File

@@ -11,6 +11,15 @@
#include "config.h"
#endif
#include <cairo-gobject.h>
#include <gdk/gdkkeysyms.h>
#include <glib-object.h>
#include <glib/gi18n.h>
#include <glib/gprintf.h>
#include <gtk/gtk.h>
#include <unistd.h>
#include "network-analyzer.h"
#include "process-tree-model.h"
#include "process-tree-view.h"
#include "settings.h"
@@ -37,6 +46,9 @@ enum
COLUMN_UID,
COLUMN_CPU,
COLUMN_GROUP_CPU,
COLUMN_PACKET_IN,
COLUMN_PACKET_OUT,
COLUMN_ACTIVE_SOCKET,
COLUMN_PRIORITY,
N_COLUMNS,
};
@@ -88,6 +100,7 @@ xtm_process_tree_view_class_init (XtmProcessTreeViewClass *klass)
static void
xtm_process_tree_view_init (XtmProcessTreeView *treeview)
{
XtmNetworkAnalyzer *network;
GtkCellRenderer *cell_text, *cell_right_aligned;
GtkTreeViewColumn *column;
gboolean visible;
@@ -122,6 +135,9 @@ xtm_process_tree_view_init (XtmProcessTreeView *treeview)
G_TYPE_STRING, /* XTM_PTV_COLUMN_CPU_STR */
G_TYPE_FLOAT, /* XTM_PTV_COLUMN_GROUP_CPU */
G_TYPE_STRING, /* XTM_PTV_COLUMN_GROUP_CPU_STR */
G_TYPE_UINT64, /* XTM_PTV_COLUMN_PACKET_IN */
G_TYPE_UINT64, /* XTM_PTV_COLUMN_PACKET_OUT */
G_TYPE_UINT64, /* XTM_PTV_COLUMN_ACTIVE_SOCKET */
G_TYPE_INT, /* XTM_PTV_COLUMN_PRIORITY */
G_TYPE_STRING, /* XTM_PTV_COLUMN_BACKGROUND */
G_TYPE_STRING, /* XTM_PTV_COLUMN_FOREGROUND */
@@ -245,6 +261,43 @@ xtm_process_tree_view_init (XtmProcessTreeView *treeview)
g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview);
gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_GROUP_CPU]);
/*************/
g_object_get (treeview->settings, "column-packet-in", &visible, NULL);
column = gtk_tree_view_column_new_with_attributes (_("PacketIn"), cell_right_aligned, "text", XTM_PTV_COLUMN_PACKET_IN, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL);
g_object_set (column, COLUMN_PROPERTIES, NULL);
g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_PACKET_IN));
g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_PACKET_IN));
g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview);
gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_PACKET_IN]);
g_object_get (treeview->settings, "column-packet-out", &visible, NULL);
column = gtk_tree_view_column_new_with_attributes (_("PacketOut"), cell_right_aligned, "text", XTM_PTV_COLUMN_PACKET_OUT, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL);
g_object_set (column, COLUMN_PROPERTIES, NULL);
g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_PACKET_OUT));
g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_PACKET_OUT));
g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview);
gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_PACKET_OUT]);
g_object_get (treeview->settings, "column-active-socket", &visible, NULL);
column = gtk_tree_view_column_new_with_attributes (_("ActiveSocket"), cell_right_aligned, "text", XTM_PTV_COLUMN_ACTIVE_SOCKET, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL);
g_object_set (column, COLUMN_PROPERTIES, NULL);
g_object_set_data (G_OBJECT (column), "sort-column-id", GINT_TO_POINTER (XTM_PTV_COLUMN_ACTIVE_SOCKET));
g_object_set_data (G_OBJECT (column), "column-id", GINT_TO_POINTER (COLUMN_ACTIVE_SOCKET));
g_signal_connect (column, "clicked", G_CALLBACK (column_clicked), treeview);
gtk_tree_view_insert_column (GTK_TREE_VIEW (treeview), column, treeview->columns_positions[COLUMN_ACTIVE_SOCKET]);
// insufficient permission
network = xtm_network_analyzer_get_default ();
if (network == NULL)
{
gtk_tree_view_column_set_visible (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), treeview->columns_positions[COLUMN_PACKET_IN]), FALSE);
gtk_tree_view_column_set_visible (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), treeview->columns_positions[COLUMN_PACKET_OUT]), FALSE);
gtk_tree_view_column_set_visible (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), treeview->columns_positions[COLUMN_ACTIVE_SOCKET]), FALSE);
}
/*******************/
g_object_get (treeview->settings, "column-priority", &visible, NULL);
/* TRANSLATORS: “Prio.” is short for Priority, it appears in the tree view header. */
column = gtk_tree_view_column_new_with_attributes (_("Prio."), cell_right_aligned, "text", XTM_PTV_COLUMN_PRIORITY, "cell-background", XTM_PTV_COLUMN_BACKGROUND, "foreground", XTM_PTV_COLUMN_FOREGROUND, NULL);
@@ -842,6 +895,12 @@ settings_changed (GObject *object, GParamSpec *pspec, XtmProcessTreeView *treevi
column_id = COLUMN_GROUP_CPU;
else if (!g_strcmp0 (pspec->name, "column-priority"))
column_id = COLUMN_PRIORITY;
else if (!g_strcmp0 (pspec->name, "column-packet-in"))
column_id = COLUMN_PACKET_IN;
else if (!g_strcmp0 (pspec->name, "column-packet-out"))
column_id = COLUMN_PACKET_OUT;
else if (!g_strcmp0 (pspec->name, "column-active-socket"))
column_id = COLUMN_ACTIVE_SOCKET;
g_object_get (object, pspec->name, &visible, NULL);
gtk_tree_view_column_set_visible (gtk_tree_view_get_column (GTK_TREE_VIEW (treeview), treeview->columns_positions[column_id]), visible);

View File

@@ -39,6 +39,9 @@ enum
XTM_PTV_COLUMN_CPU_STR,
XTM_PTV_COLUMN_GROUP_CPU,
XTM_PTV_COLUMN_GROUP_CPU_STR,
XTM_PTV_COLUMN_PACKET_IN,
XTM_PTV_COLUMN_PACKET_OUT,
XTM_PTV_COLUMN_ACTIVE_SOCKET,
XTM_PTV_COLUMN_PRIORITY,
XTM_PTV_COLUMN_BACKGROUND,
XTM_PTV_COLUMN_FOREGROUND,

View File

@@ -52,6 +52,7 @@ struct _XtmProcessWindow
GtkWidget *filter_searchbar;
GtkWidget *cpu_monitor;
GtkWidget *mem_monitor;
GtkWidget *net_monitor;
GtkWidget *vpaned;
GtkWidget *treeview;
GtkWidget *statusbar;
@@ -347,6 +348,13 @@ xtm_process_window_init (XtmProcessWindow *window)
gtk_widget_show (window->mem_monitor);
gtk_container_add (GTK_CONTAINER (toolitem), window->mem_monitor);
toolitem = GTK_WIDGET (gtk_builder_get_object (window->builder, "graph-net"));
window->net_monitor = xtm_process_monitor_new ();
xtm_process_monitor_set_step_size (XTM_PROCESS_MONITOR (window->net_monitor), refresh_rate / 1000.0f);
xtm_process_monitor_set_type (XTM_PROCESS_MONITOR (window->net_monitor), 2);
gtk_widget_show (window->net_monitor);
gtk_container_add (GTK_CONTAINER (toolitem), window->net_monitor);
g_signal_connect_swapped (window->settings, "notify::refresh-rate", G_CALLBACK (monitor_update_step_size), window);
}
@@ -489,6 +497,7 @@ monitor_update_step_size (XtmProcessWindow *window)
g_object_get (window->settings, "refresh-rate", &refresh_rate, NULL);
g_object_set (window->cpu_monitor, "step-size", refresh_rate / 1000.0, NULL);
g_object_set (window->mem_monitor, "step-size", refresh_rate / 1000.0, NULL);
g_object_set (window->net_monitor, "step-size", refresh_rate / 1000.0, NULL);
}
/**
@@ -511,6 +520,12 @@ xtm_process_window_show (GtkWidget *widget)
GTK_WIDGET_CLASS (xtm_process_window_parent_class)->show (widget);
}
GtkWindow *
xtm_process_window_get (XtmProcessWindow *window)
{
return GTK_WINDOW (window->window);
}
static void
xtm_process_window_hide (GtkWidget *widget)
{
@@ -533,24 +548,40 @@ xtm_process_window_get_model (XtmProcessWindow *window)
}
void
xtm_process_window_set_system_info (XtmProcessWindow *window, guint num_processes, gfloat cpu, gfloat memory, gchar *memory_str, gfloat swap, gchar *swap_str)
xtm_process_window_set_system_info (XtmProcessWindow *window, guint num_processes, gfloat cpu, gfloat memory, gchar *memory_str, gfloat swap, gchar *swap_str, guint64 tcp_rx, guint64 tcp_tx, guint64 tcp_error)
{
gchar text[100];
gchar text[200];
gchar value[4];
g_return_if_fail (XTM_IS_PROCESS_WINDOW (window));
g_return_if_fail (GTK_IS_BOX (window->statusbar));
g_object_set (window->statusbar, "num-processes", num_processes, "cpu", cpu, "memory", memory_str, "swap", swap_str, NULL);
g_object_set (
window->statusbar,
"num-processes", num_processes,
"cpu", cpu,
"memory", memory_str,
"swap", swap_str,
"network-rx", tcp_rx * 1e-5,
"network-tx", tcp_tx * 1e-5,
"network-error", tcp_error,
NULL);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->cpu_monitor), cpu / 100.0f, -1.0);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->cpu_monitor), cpu / 100.0f, 0);
g_snprintf (value, sizeof (value), "%.0f", cpu);
g_snprintf (text, sizeof (text), _("CPU: %s%%"), value);
gtk_widget_set_tooltip_text (window->cpu_monitor, text);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->mem_monitor), memory / 100.0f, swap / 100.0f);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->mem_monitor), memory / 100.0f, 0);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->mem_monitor), swap / 100.0f, 1);
g_snprintf (text, sizeof (text), _("Memory: %s"), memory_str);
gtk_widget_set_tooltip_text (window->mem_monitor, text);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->net_monitor), tcp_rx * 1e-5, 0);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->net_monitor), tcp_tx * 1e-5, 1);
xtm_process_monitor_add_peak (XTM_PROCESS_MONITOR (window->net_monitor), tcp_error, 2);
g_snprintf (text, sizeof (text), _("Network rx=%0.2f MB/s\nNetwork tx=%0.2f MB/s \nNetwork error=%lu error/s"), tcp_rx * 1e-5, tcp_tx * 1e-5, tcp_error);
gtk_widget_set_tooltip_text (window->net_monitor, text);
}
void

View File

@@ -34,10 +34,11 @@ typedef struct _XtmProcessWindow XtmProcessWindow;
GType xtm_process_window_get_type (void);
GtkWidget *xtm_process_window_new (void);
GtkWindow *xtm_process_window_get (XtmProcessWindow *window);
void xtm_process_window_settings_init (XtmProcessWindow *window, XfconfChannel *channel);
void xtm_process_window_show (GtkWidget *widget);
GtkTreeModel *xtm_process_window_get_model (XtmProcessWindow *window);
void xtm_process_window_set_system_info (XtmProcessWindow *window, guint num_processes, gfloat cpu, gfloat memory, gchar *memory_str, gfloat swap, gchar *swap_str);
void xtm_process_window_set_system_info (XtmProcessWindow *window, guint num_processes, gfloat cpu, gfloat memory, gchar *memory_str, gfloat swap, gchar *swap_str, guint64 tcp_rx, guint64 tcp_tx, guint64 tcp_error);
void xtm_process_window_show_swap_usage (XtmProcessWindow *window, gboolean show_swap_usage);
#endif /* !PROCESS_WINDOW_H */

View File

@@ -198,6 +198,27 @@
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkFrame" id="graph-net">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">False</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<placeholder/>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>

View File

@@ -11,6 +11,9 @@
#include "config.h"
#endif
#include <libxfce4ui/libxfce4ui.h>
#include "network-analyzer.h"
#include "settings-dialog.h"
#include "settings-dialog_ui.h"
#include "settings.h"
@@ -19,8 +22,6 @@
#include <gdk/gdkx.h>
#endif
#include <libxfce4ui/libxfce4ui.h>
static void show_about_dialog (GtkWidget *widget, gpointer user_data);
static GtkWidget *xtm_settings_dialog_new (GtkBuilder *builder, GtkWidget *parent_window);
@@ -117,18 +118,22 @@ show_about_dialog (GtkWidget *widget, gpointer user_data)
"(c) 2005-2008 Johannes Zellner",
"",
"FreeBSD",
" \342\200\242 Jehan-Antoine Vayssade",
" \342\200\242 Rozhuk Ivan",
" \342\200\242 Mike Massonnet",
" \342\200\242 Oliver Lehmann",
"",
"OpenBSD",
" \342\200\242 Jehan-Antoine Vayssade",
" \342\200\242 Landry Breuil",
"",
"Linux",
" \342\200\242 Jehan-Antoine Vayssade",
" \342\200\242 Johannes Zellner",
" \342\200\242 Mike Massonnet",
"",
"OpenSolaris",
" \342\200\242 Jehan-Antoine Vayssade",
" \342\200\242 Mike Massonnet",
" \342\200\242 Peter Tribble",
NULL
@@ -174,6 +179,7 @@ xtm_settings_dialog_new (GtkBuilder *builder, GtkWidget *parent_window)
GtkWidget *dialog;
GtkWidget *button;
XtmSettings *settings;
XtmNetworkAnalyzer *network;
settings = xtm_settings_get_default ();
dialog = GTK_WIDGET (gtk_builder_get_object (builder, "settings-dialog"));
@@ -214,8 +220,20 @@ xtm_settings_dialog_new (GtkBuilder *builder, GtkWidget *parent_window)
builder_bind_toggle_button (builder, "uid", settings, "column-uid");
builder_bind_toggle_button (builder, "cpu", settings, "column-cpu");
builder_bind_toggle_button (builder, "group-cpu", settings, "column-group-cpu");
builder_bind_toggle_button (builder, "packet-in", settings, "column-packet-in");
builder_bind_toggle_button (builder, "packet-out", settings, "column-packet-out");
builder_bind_toggle_button (builder, "active-socket", settings, "column-active-socket");
builder_bind_toggle_button (builder, "priority", settings, "column-priority");
// insufficient permission
network = xtm_network_analyzer_get_default ();
if (network == NULL)
{
gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (builder, "packet-in")));
gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (builder, "packet-out")));
gtk_widget_hide (GTK_WIDGET (gtk_builder_get_object (builder, "active-socket")));
}
button = GTK_WIDGET (gtk_builder_get_object (builder, "button-about"));
g_signal_connect (button, "clicked", G_CALLBACK (show_about_dialog), dialog);

View File

@@ -508,6 +508,48 @@
<property name="position">10</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="packet-in">
<property name="label" translatable="yes">Packet in</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">10</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="packet-out">
<property name="label" translatable="yes">Packet out</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">10</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="active-socket">
<property name="label" translatable="yes">Active Socket</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">10</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>

View File

@@ -47,6 +47,9 @@ enum
PROP_COLUMN_CPU,
PROP_COLUMN_GROUP_CPU,
PROP_COLUMN_PRIORITY,
PROP_COLUMN_PACKET_IN,
PROP_COLUMN_PACKET_OUT,
PROP_COLUMN_ACTIVE_SOCKET,
PROP_SORT_COLUMN_ID,
PROP_SORT_TYPE,
PROP_HANDLE_POSITION,
@@ -116,6 +119,14 @@ xtm_settings_class_init (XtmSettingsClass *klass)
g_param_spec_boolean ("column-cpu", "ColumnCPU", "Show column CPU", TRUE, G_PARAM_READWRITE));
g_object_class_install_property (class, PROP_COLUMN_GROUP_CPU,
g_param_spec_boolean ("column-group-cpu", "ColumnGroupCPU", "Show column Group CPU", TRUE, G_PARAM_READWRITE));
g_object_class_install_property (class, PROP_COLUMN_PACKET_IN,
g_param_spec_boolean ("column-packet-in", "ColumnPacketIn", "Show column packet in", TRUE, G_PARAM_READWRITE));
g_object_class_install_property (class, PROP_COLUMN_PACKET_OUT,
g_param_spec_boolean ("column-packet-out", "ColumnPacketOut", "Show column packet out", TRUE, G_PARAM_READWRITE));
g_object_class_install_property (class, PROP_COLUMN_PACKET_OUT,
g_param_spec_boolean ("column-active-socket", "ColumnActiveSocket", "Show number of used socket descriptor", TRUE, G_PARAM_READWRITE));
g_object_class_install_property (class, PROP_COLUMN_PRIORITY,
g_param_spec_boolean ("column-priority", "ColumnPriority", "Show column priority", FALSE, G_PARAM_READWRITE));
g_object_class_install_property (class, PROP_SORT_COLUMN_ID,
@@ -219,6 +230,15 @@ xtm_settings_bind_xfconf (XtmSettings *settings, XfconfChannel *channel)
G_OBJECT (settings), "column-cpu");
xfconf_g_property_bind (channel, SETTING_COLUMN_GROUP_CPU, G_TYPE_BOOLEAN,
G_OBJECT (settings), "column-group-cpu");
xfconf_g_property_bind (channel, SETTING_COLUMN_PACKET_IN, G_TYPE_BOOLEAN,
G_OBJECT (settings), "column-packet-in");
xfconf_g_property_bind (channel, SETTING_COLUMN_PACKET_OUT, G_TYPE_BOOLEAN,
G_OBJECT (settings), "column-packet-out");
xfconf_g_property_bind (channel, SETTING_COLUMN_ACTIVE_SOCKET, G_TYPE_BOOLEAN,
G_OBJECT (settings), "column-active-socket");
xfconf_g_property_bind (channel, SETTING_COLUMN_PRIORITY, G_TYPE_BOOLEAN,
G_OBJECT (settings), "column-priority");

View File

@@ -48,6 +48,9 @@
#define SETTING_COLUMN_UID "/columns/column-uid"
#define SETTING_COLUMN_CPU "/columns/column-cpu"
#define SETTING_COLUMN_GROUP_CPU "/columns/column-group-cpu"
#define SETTING_COLUMN_PACKET_IN "/columns/column-packet-in"
#define SETTING_COLUMN_PACKET_OUT "/columns/column-packet-out"
#define SETTING_COLUMN_ACTIVE_SOCKET "/columns/column-active-socket"
#define SETTING_COLUMN_PRIORITY "/columns/column-priority"
#define SETTING_COLUMN_SORT_ID "/columns/sort-id"
#define SETTING_COLUMN_SORT_TYPE "/columns/sort-type"

View File

@@ -20,6 +20,8 @@
#include "config.h"
#endif
#include "inode-to-sock.h"
#include "network-analyzer.h"
#include "task-manager.h"
#include <err.h>
@@ -40,14 +42,413 @@
/* for struct vmtotal */
#include <sys/vmmeter.h>
// clang-format off
#include <net/if.h>
#include <net/if_dl.h>
#include <net/route.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
// clang-format on
/* errno */
#include <errno.h>
#ifdef __OpenBSD__
extern int errno;
#else
// struct file in NetBSD
// ugly ! where is defined this ?
// plateform dependent
typedef long register_t;
typedef unsigned long paddr_t;
#include <kvm.h>
#include <machine/types.h>
#include <sys/domain.h>
#include <sys/filedesc.h>
#include <sys/protosw.h>
#include <sys/socketvar.h>
#include <sys/unpcb.h>
#endif
#define _KERNEL
#include <sys/file.h>
#include <sys/types.h>
#undef _KERNEL
char *state_abbrev[] = {
"", "start", "run", "sleep", "stop", "zomb", "dead", "onproc"
};
static XtmInodeToSock *inode_to_sock = NULL;
static XtmNetworkAnalyzer *analyzer = NULL;
gboolean get_if_count (int *data);
gboolean get_network_usage_if (int interface, guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error);
struct kinfo_file *get_process_fds (int *nfiles, int kern, int arg);
#ifdef __OpenBSD__
void list_process_fds (Task *task, struct kinfo_proc *kp);
#else
void list_process_fds (Task *task, struct kinfo_proc2 *kp);
#endif
#ifdef HAVE_LIBPCAP
void
packet_callback (u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
// Extract source and destination IP addresses and ports from the packet
struct ether_header eth_header;
struct ip ip_header;
struct tcphdr tcp_header;
XtmNetworkAnalyzer *iface;
long int src_port, dst_port;
char local_mac[18];
char src_mac[18];
char dst_mac[18];
memcpy (&eth_header, packet, sizeof (struct ether_header));
memcpy (&ip_header, packet + sizeof (struct ether_header), sizeof (struct ip));
memcpy (&tcp_header, packet + sizeof (struct ether_header) + sizeof (struct ip), sizeof (struct ip));
// cast -> increases required alignment from 1 to 2 [-Wcast-align]
// const struct ether_header *eth_header = (const struct ether_header *)packet;
// struct ip *ip_header = (struct ip *)(packet + sizeof (struct ether_header));
// struct tcphdr *tcp_header = (struct tcphdr *)(packet + sizeof (struct ether_header) + sizeof (struct ip));
// printf("%d, %d \n", eth_heade.ether_type , ip_header.ip_p);
// Dropped non-ip packet
if (eth_header.ether_type != 8 || ip_header.ip_p != 6)
return;
iface = (XtmNetworkAnalyzer *)args;
src_port = ntohs (tcp_header.th_sport);
dst_port = ntohs (tcp_header.th_dport);
// directly use strcmp on analyzer->mac, eth_header->ether_shost doesnt work
snprintf (local_mac, sizeof (local_mac),
"%02X:%02X:%02X:%02X:%02X:%02X",
iface->mac[0], iface->mac[1],
iface->mac[2], iface->mac[3],
iface->mac[4], iface->mac[5]);
snprintf (src_mac, sizeof (local_mac),
"%02X:%02X:%02X:%02X:%02X:%02X",
eth_header.ether_shost[0], eth_header.ether_shost[1],
eth_header.ether_shost[2], eth_header.ether_shost[3],
eth_header.ether_shost[4], eth_header.ether_shost[5]);
snprintf (dst_mac, sizeof (local_mac),
"%02X:%02X:%02X:%02X:%02X:%02X",
eth_header.ether_dhost[0], eth_header.ether_dhost[1],
eth_header.ether_dhost[2], eth_header.ether_dhost[3],
eth_header.ether_dhost[4], eth_header.ether_dhost[5]);
// Debug
// pthread_mutex_lock(&iface->lock);
if (strcmp (local_mac, src_mac) == 0)
increament_packet_count (local_mac, "in ", iface->packetin, src_port);
if (strcmp (local_mac, dst_mac) == 0)
increament_packet_count (local_mac, "out", iface->packetout, dst_port);
// pthread_mutex_unlock(&iface->lock);
}
#endif
gboolean
get_network_usage (guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error)
{
// can also be get trough tcpstat struct
// int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_STATS }
// sysctl(mib, sizeof(mib) / sizeof(mib[0]), %tcpstat, &len, NULL, 0)
// repeat for IPPROTO_UDP or use IPPROTO_ETHERIP
struct ifaddrs *ifaddr, *ifa;
*tcp_error = 0;
*tcp_rx = 0;
*tcp_tx = 0;
if (getifaddrs (&ifaddr) == -1)
return -1;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
// Check if the interface is a network interface (AF_PACKET for Linux)
if (ifa->ifa_addr->sa_family == AF_LINK)
{
struct if_data *ifdata = (struct if_data *)ifa->ifa_data;
// Check if the interface has a hardware address (MAC address)
if (ifdata != NULL)
{
*tcp_error += ifdata->ifi_oerrors + ifdata->ifi_ierrors;
;
*tcp_rx += ifdata->ifi_ibytes;
*tcp_tx += ifdata->ifi_obytes;
}
// printf("%d, %d \n", *tcp_rx, *tcp_tx);
}
}
freeifaddrs (ifaddr);
return 0;
}
int
get_mac_address (const char *device, uint8_t mac[6])
{
struct ifaddrs *ifaddr, *ifa;
if (getifaddrs (&ifaddr) == -1)
return -1;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
// Check if the interface is a network interface (AF_PACKET for Linux)
if (ifa->ifa_addr->sa_family == AF_LINK && strcmp (device, ifa->ifa_name) == 0)
{
// Check if the interface has a hardware address (MAC address)
if (ifa->ifa_data != NULL)
{
// warning: cast from 'struct sockaddr *' to 'struct sockaddr_dl *' increases required alignment from 1 to 2
// struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
struct sockaddr_dl sdl;
memcpy (&sdl, ifa->ifa_addr, sizeof (struct sockaddr_dl));
memcpy (mac, sdl.sdl_data + sdl.sdl_nlen, sizeof (uint8_t) * 6);
freeifaddrs (ifaddr);
return 0;
}
}
}
freeifaddrs (ifaddr);
return -1;
}
struct kinfo_file *
get_process_fds (int *nfiles, int kern, int arg)
{
// inspired by NetBSD pstat.c
int mib[6];
size_t len;
struct kinfo_file *kf;
// Set the MIB (Management Information Base) for the sysctl call
mib[0] = CTL_KERN;
#ifdef __OpenBSD__
mib[1] = KERN_FILE;
#else
mib[1] = KERN_FILE2;
#endif
mib[2] = kern;
mib[3] = arg;
mib[4] = sizeof (struct kinfo_file);
mib[5] = 0;
// Get the number of open files
if (sysctl (mib, 6, NULL, &len, NULL, 0) < 0)
{
printf ("sysctl Get the number of opened fd");
return NULL;
}
*nfiles = len / sizeof (struct kinfo_file);
// Allocate memory for the kinfo_file structures
kf = malloc (len);
if (kf == NULL)
{
printf ("malloc");
return NULL;
}
mib[5] = len / sizeof (struct kinfo_file);
// Get the list of open files
if (sysctl (mib, 6, kf, &len, NULL, 0) < 0)
{
printf ("sysctl, enable to get kinfo_file *");
free (kf);
return NULL;
}
return kf;
}
void
xtm_refresh_inode_to_sock (XtmInodeToSock *its)
{
#ifdef __OpenBSD__
// unneeded
#else
// unneeded
#endif
}
void
#ifdef __OpenBSD__
list_process_fds (Task *task, struct kinfo_proc *kp)
#else
list_process_fds (Task *task, struct kinfo_proc2 *kp)
#endif
{
#ifdef __OpenBSD__
// inspired by openbsd netstat/inet.c
// inspired by openbsd fstat/fstat.c
long int nfiles, port;
XtmNetworkAnalyzer *current;
struct kinfo_file *kf = get_process_fds (&nfiles, KERN_FILE_BYPID, task->pid);
if (kf == NULL)
return;
// Parse the kinfo_file structures
for (int i = 0; i < nfiles; i++)
{
if (kf[i].f_type == DTYPE_SOCKET)
{
// interesting properties
// task->packet_in += kf[i].f_rxfer;
// task->packet_in += kf[i].f_rbytes;
// task->packet_out += kf[i].f_rwfer;
// task->packet_out += kf[i].f_wbytes;netstat
current = analyzer;
while (current != NULL)
{
port = ntohs (kf[i].inp_lport);
task->packet_in += (guint64)g_hash_table_lookup (current->packetin, &port);
task->packet_out += (guint64)g_hash_table_lookup (current->packetout, &port);
current = current->next;
}
task->active_socket += 1;
}
}
free (kf);
#else
// inspired by netbsd fstat/fstat.c
XtmNetworkAnalyzer *current;
char *memf, *nlistf;
char buf[_POSIX2_LINE_MAX];
kvm_t *kd;
struct filedesc filed;
struct fdtab dt;
size_t len;
fdfile_t **ofiles;
fdfile_t *fp;
struct socket *sock;
struct socket so;
struct file file;
fdfile_t fdfile;
struct protosw proto;
struct domain dom;
struct in4pcb in4pcb;
struct in6pcb in6pcb;
struct inpcb *inp;
long int port;
nlistf = memf = NULL;
kd = kvm_openfiles (nlistf, memf, NULL, O_RDONLY, buf);
kvm_read (kd, kp->p_fd, &filed, sizeof (filed));
kvm_read (kd, (u_long)filed.fd_dt, &dt, sizeof (dt));
len = (filed.fd_lastfile + 1) * sizeof (fdfile_t *);
ofiles = malloc (len);
kvm_read (kd, (u_long)&filed.fd_dt->dt_ff, ofiles, (filed.fd_lastfile + 1) * (sizeof (fdfile_t *)));
for (int i = 0; i <= filed.fd_lastfile; i++)
{
if (ofiles[i] == NULL)
continue;
fp = ofiles[i];
kvm_read (kd, (u_long)fp, &fdfile, sizeof (fdfile));
if (fdfile.ff_file == NULL)
continue;
kvm_read (kd, (u_long)fdfile.ff_file, &file, sizeof (file));
if (file.f_type != DTYPE_SOCKET)
continue;
sock = (struct socket *)file.f_data;
kvm_read (kd, (u_long)sock, &so, sizeof (struct socket));
kvm_read (kd, (u_long)so.so_proto, &proto, sizeof (struct protosw));
kvm_read (kd, (u_long)proto.pr_domain, &dom, sizeof (struct domain));
port = 0;
if (dom.dom_family == AF_INET)
{
if (proto.pr_protocol == IPPROTO_TCP)
{
if (so.so_pcb == NULL)
continue;
kvm_read (kd, (u_long)so.so_pcb, (char *)&in4pcb, sizeof (in4pcb));
inp = (struct inpcb *)&in4pcb;
port = ntohs (inp->inp_lport);
}
}
if (dom.dom_family == AF_INET6)
{
if (proto.pr_protocol == IPPROTO_TCP)
{
if (so.so_pcb == NULL)
continue;
kvm_read (kd, (u_long)so.so_pcb, (char *)&in6pcb, sizeof (in6pcb));
inp = (struct inpcb *)&in6pcb;
port = ntohs (inp->inp_lport);
}
}
if (port == 0)
continue;
task->active_socket += 1;
current = analyzer;
if (current != NULL)
{
task->packet_in += (guint64)g_hash_table_lookup (current->packetin, &port);
task->packet_out += (guint64)g_hash_table_lookup (current->packetout, &port);
current = current->next;
}
}
kvm_close (kd);
free (ofiles);
#endif
}
gboolean
get_task_list (GArray *task_list)
{
@@ -57,11 +458,22 @@ get_task_list (GArray *task_list)
struct kinfo_proc *kp;
#else
struct kinfo_proc2 *kp;
char errbuf[_POSIX2_LINE_MAX];
kvm_t *kdp;
#endif
Task t;
char **args;
gchar *buf;
int nproc, i;
gchar *buf;
#ifdef __OpenBSD__
#else
kdp = kvm_open (NULL, NULL, NULL, KVM_NO_FILES, errbuf);
#endif
analyzer = xtm_network_analyzer_get_default ();
inode_to_sock = xtm_inode_to_sock_get_default ();
xtm_refresh_inode_to_sock (inode_to_sock);
mib[0] = CTL_KERN;
#ifdef __OpenBSD__
@@ -116,6 +528,8 @@ get_task_list (GArray *task_list)
t.rss = p.p_vm_rssize * getpagesize ();
g_snprintf (t.state, sizeof t.state, "%s", state_abbrev[p.p_stat]);
g_strlcpy (t.name, p.p_comm, strlen (p.p_comm) + 1);
#ifdef __OpenBSD__
/* shamelessly stolen from top/machine.c */
if (!P_ZOMBIE (&p))
{
@@ -132,8 +546,10 @@ get_task_list (GArray *task_list)
mib[1] = KERN_PROC_ARGS;
mib[2] = t.pid;
mib[3] = KERN_PROC_ARGV;
if (sysctl (mib, 4, args, &size, NULL, 0) == 0)
break;
if (errno != ENOMEM)
{ /* ESRCH: process disappeared */
/* printf ("process with pid %d disappeared, errno=%d\n", t.pid, errno); */
@@ -142,15 +558,40 @@ get_task_list (GArray *task_list)
break;
}
}
buf = g_strjoinv (" ", args);
g_assert (g_utf8_validate (buf, -1, NULL));
g_strlcpy (t.cmdline, buf, sizeof t.cmdline);
g_free (buf);
free (args);
}
#else
// assuming NetBSD 10.0
// https://github.com/NetBSD/src/blob/trunk/lib/libkvm/kvm_proc.c#L1116
// https://github.com/NetBSD/pkgsrc/blob/trunk/sysutils/xfce4-taskmanager/files/task-manager-netbsd.c
// fixing code used in __OpenBSD__ crash at g_strjoinv due to strlen
if (!(kp[i].p_stat == SDEAD))
{
args = kvm_getargv2 (kdp, &kp[i], BUFSIZ);
if (args != NULL)
{
buf = g_strjoinv (" ", args);
g_strlcpy (t.cmdline, buf, sizeof (t.cmdline));
g_free (buf);
// Memory seem stable without that
// otherwise i get a segfault
// free (args);
}
}
#endif
t.cpu_user = (100.0f * ((gfloat)p.p_pctcpu / FSCALE));
t.cpu_system = 0.0f; /* TODO ? */
if (analyzer != NULL)
list_process_fds (&t, &p);
g_array_append_val (task_list, t);
}
free (kp);
@@ -201,7 +642,15 @@ get_cpu_usage (gushort *cpu_count, gfloat *cpu_user, gfloat *cpu_system)
static gulong cur_user = 0, cur_system = 0, cur_total = 0;
static gulong old_user = 0, old_system = 0, old_total = 0;
#ifdef KERN_CPTIME
int mib[] = { CTL_KERN, KERN_CPTIME };
#elif defined KERN_CPTIME2
int mib[] = { CTL_KERN, KERN_CPTIME2 };
#else
// NetBSD 10.0
int mib[] = { CTL_KERN, KERN_CP_TIME };
#endif
glong cp_time[CPUSTATES];
gsize size = sizeof (cp_time);
if (sysctl (mib, 2, &cp_time, &size, NULL, 0) < 0)

View File

@@ -15,21 +15,40 @@
#include "task-manager.h"
// clang-format off
#include <fcntl.h>
#include <kvm.h>
#include <paths.h>
#include <string.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/user.h>
#include <unistd.h>
#if defined(__FreeBSD_version) && __FreeBSD_version >= 900044
#include <sys/vmmeter.h>
#endif
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_mib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
// clang-format on
#include <glib.h>
#include "inode-to-sock.h"
#include "network-analyzer.h"
#include "task-manager.h"
static const gchar ki_stat2state[] = {
' ', /* - */
'R', /* SIDL */
@@ -41,6 +60,152 @@ static const gchar ki_stat2state[] = {
'L' /* SLOCK */
};
static XtmInodeToSock *inode_to_sock = NULL;
static XtmNetworkAnalyzer *analyzer = NULL;
void list_process_fds (Task *task);
gboolean get_if_count (int *data);
gboolean get_network_usage_if (int interface, guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error);
#ifdef HAVE_LIBPCAP
void
packet_callback (u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
// Extract source and destination IP addresses and ports from the packet
struct ether_header *eth_header = (struct ether_header *)packet;
struct ip *ip_header = (struct ip *)(packet + sizeof (struct ether_header));
struct tcphdr *tcp_header = (struct tcphdr *)(packet + sizeof (struct ether_header) + sizeof (struct ip));
XtmNetworkAnalyzer *iface = (XtmNetworkAnalyzer *)args;
// Dropped non-ip packet
if (eth_header->ether_type != 8 || ip_header->ip_p != 6)
return;
long int src_port = ntohs (tcp_header->th_sport);
long int dst_port = ntohs (tcp_header->th_dport);
// directly use strcmp on analyzer->mac, eth_header->ether_shost doesnt work
char local_mac[18];
char src_mac[18];
char dst_mac[18];
sprintf (local_mac,
"%02X:%02X:%02X:%02X:%02X:%02X",
iface->mac[0], iface->mac[1],
iface->mac[2], iface->mac[3],
iface->mac[4], iface->mac[5]);
sprintf (src_mac,
"%02X:%02X:%02X:%02X:%02X:%02X",
eth_header->ether_shost[0], eth_header->ether_shost[1],
eth_header->ether_shost[2], eth_header->ether_shost[3],
eth_header->ether_shost[4], eth_header->ether_shost[5]);
sprintf (dst_mac,
"%02X:%02X:%02X:%02X:%02X:%02X",
eth_header->ether_dhost[0], eth_header->ether_dhost[1],
eth_header->ether_dhost[2], eth_header->ether_dhost[3],
eth_header->ether_dhost[4], eth_header->ether_dhost[5]);
// Debug
// pthread_mutex_lock(&iface->lock);
if (strcmp (local_mac, src_mac) == 0)
increament_packet_count (local_mac, "in ", iface->packetin, src_port);
if (strcmp (local_mac, dst_mac) == 0)
increament_packet_count (local_mac, "out", iface->packetout, dst_port);
// pthread_mutex_unlock(&iface->lock);
}
#endif
gboolean
get_if_count (int *data)
{
size_t len = sizeof (*data);
static int32_t name[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_SYSTEM, IFMIB_IFCOUNT };
name[0] = CTL_NET;
name[1] = PF_LINK;
name[2] = NETLINK_GENERIC;
name[3] = IFMIB_SYSTEM;
name[4] = IFMIB_IFCOUNT;
return sysctl (name, 5, data, &len, 0, 0) < 0;
}
gboolean
get_network_usage_if (int interface, guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error)
{
struct ifmibdata data;
size_t len = sizeof (data);
static int32_t name[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, 0, IFDATA_GENERAL };
name[4] = interface;
if (sysctl (name, 6, &data, &len, 0, 0) < 0)
return 1;
//*tcp_error = data.ifmd_data.ifi_oerrors + data.ifmd_data.ifi_ierrors;
*tcp_rx += data.ifmd_data.ifi_ibytes;
*tcp_tx += data.ifmd_data.ifi_obytes;
return 0;
}
gboolean
get_network_usage (guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error)
{
int ifcount = 0;
if (get_if_count (&ifcount))
return 1;
*tcp_error = 0;
*tcp_rx = 0;
*tcp_tx = 0;
for (int i = 0; i < ifcount; ++i)
get_network_usage_if (i, tcp_rx, tcp_tx, tcp_error);
return 0;
}
int
get_mac_address (const char *device, uint8_t mac[6])
{
struct ifaddrs *ifaddr, *ifa;
if (getifaddrs (&ifaddr) == -1)
return -1;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
int family = ifa->ifa_addr->sa_family;
// Check if the interface is a network interface (AF_PACKET for Linux)
if (family == AF_LINK && strcmp (device, ifa->ifa_name) == 0)
{
// Check if the interface has a hardware address (MAC address)
if (ifa->ifa_data != NULL)
{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
memcpy (mac, sdl->sdl_data + sdl->sdl_nlen, sizeof (uint8_t) * 6);
freeifaddrs (ifaddr);
return 0;
}
}
}
freeifaddrs (ifaddr);
return -1;
}
static guint64
get_mem_by_bytes (const gchar *name)
@@ -132,6 +297,114 @@ get_cpu_usage (gushort *cpu_count, gfloat *cpu_user, gfloat *cpu_system)
return TRUE;
}
// TODO check the OpenBSD version here
// (struct sockaddr_in*)kinfo_file.kf_sa_local
void
xtm_refresh_inode_to_sock (XtmInodeToSock *its)
{
FILE *fp;
char line[2048];
char *token;
char *delim = " ";
if (analyzer == NULL)
return;
// Execute the sockstat command and get the output
fp = popen ("sockstat -L -P tcp,udp", "r");
if (fp == NULL)
return;
g_hash_table_remove_all (its->hash);
g_hash_table_remove_all (its->pid);
// Skip the header line
fgets (line, 2048, fp);
// Parse the output line by line
while (fgets (line, 2048, fp) != NULL)
{
// Remove the newline character
line[strcspn (line, "\n")] = '\0';
// Split the line into tokens
token = strtok (line, delim);
// Parse the columns
char user[50];
char command[50];
char pid[10];
char fd[10];
char proto[10];
char local_address[50];
char foreign_address[50];
strcpy (user, token);
token = strtok (NULL, delim);
strcpy (command, token);
token = strtok (NULL, delim);
strcpy (pid, token);
token = strtok (NULL, delim);
strcpy (fd, token);
token = strtok (NULL, delim);
strcpy (proto, token);
token = strtok (NULL, delim);
strcpy (local_address, token);
token = strtok (NULL, delim);
strcpy (foreign_address, token);
long int local_port, assos;
gint64 *inode1 = g_new0 (gint64, 1);
gint64 *inode2 = g_new0 (gint64, 1);
*inode1 = atoi (fd);
*inode2 = atoi (fd);
assos = atoi (pid);
sscanf (local_address, "%*[^:]:%d", &local_port);
// (intptr_t) -> cast to pointer from integer of different size [-Wint-to-pointer-cast]
g_hash_table_replace (its->hash, inode1, (gpointer)(intptr_t)local_port);
g_hash_table_replace (its->pid, inode2, (gpointer)(intptr_t)assos);
// Print the parsed values
// printf("%d -> %d\n", *inode, local_port);
}
// Close the pipe
pclose (fp);
}
void
list_process_fds (Task *task)
{
XtmNetworkAnalyzer *current;
GHashTableIter iter;
gpointer key, value;
gint64 key_int, value_int;
long int port;
g_hash_table_iter_init (&iter, inode_to_sock->pid);
while (g_hash_table_iter_next (&iter, &key, &value))
{
key_int = *(gint64 *)(key);
value_int = GPOINTER_TO_INT (value);
if (task->pid == value_int)
{
port = (long int)g_hash_table_lookup (inode_to_sock->hash, &key_int);
task->active_socket += 1;
current = analyzer;
while (current)
{
task->packet_in += (guint64)g_hash_table_lookup (current->packetin, &port);
task->packet_out += (guint64)g_hash_table_lookup (current->packetout, &port);
current = current->next;
}
}
}
}
static gboolean
get_task_details (struct kinfo_proc *kp, Task *task)
{
@@ -233,17 +506,25 @@ get_task_details (struct kinfo_proc *kp, Task *task)
if (kp->ki_flag & P_JAILED)
task->state[i++] = 'J';
if (analyzer != NULL)
list_process_fds (task);
return TRUE;
}
gboolean
get_task_list (GArray *task_list)
{
analyzer = xtm_network_analyzer_get_default ();
kvm_t *kd;
struct kinfo_proc *kp;
int cnt = 0, i;
Task task;
inode_to_sock = xtm_inode_to_sock_get_default ();
xtm_refresh_inode_to_sock (inode_to_sock);
if ((kd = kvm_openfiles (_PATH_DEVNULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL)) == NULL)
return FALSE;

View File

@@ -1,4 +1,5 @@
/*
* Copyright (c) 2024 Jehan-Antoine Vayssade, <javayss@sleek-think.ovh>
* Copyright (c) 2008-2010 Mike Massonnet <mmassonnet@xfce.org>
* Copyright (c) 2006 Johannes Zellner <webmaster@nebulon.de>
*
@@ -12,11 +13,368 @@
#include "config.h"
#endif
#include "inode-to-sock.h"
#include "network-analyzer.h"
#include "task-manager.h"
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <netpacket/packet.h>
#include <glib.h>
static XtmNetworkAnalyzer *analyzer = NULL;
static XtmInodeToSock *inode_to_sock = NULL;
static gushort _cpu_count = 0;
static gulong jiffies_total_delta = 0;
void list_process_fds (Task *task);
void xtm_refresh_inode_to_sock_protocol (XtmInodeToSock *its, char *filename);
void
xtm_refresh_inode_to_sock_protocol (XtmInodeToSock *its, char *filename)
{
char buffer[8192];
char rem_addr[128], local_addr[128];
int local_port, rem_port, inode, count;
gint64 *key;
FILE *procinfo = fopen (filename, "r");
if (procinfo == 0)
{
perror (filename);
return;
}
// skip header
if (fgets (buffer, sizeof (buffer), procinfo) == 0)
{
printf ("%s no header\n", filename);
fclose (procinfo);
return;
}
while (fgets (buffer, sizeof (buffer), procinfo))
{
count = sscanf (
buffer,
"%*d: %64[0-9A-Fa-f]:%X %64[0-9A-Fa-f]:%X %*X %*X:%*X %*X:%*X %*X %*d %*d %d",
local_addr, &local_port, rem_addr, &rem_port, &inode);
if (count != 5)
continue;
key = g_new0 (gint64, 1);
*key = inode;
g_hash_table_replace (its->hash, key, (gpointer)(intptr_t)local_port);
}
fclose (procinfo);
}
void
xtm_refresh_inode_to_sock (XtmInodeToSock *its)
{
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/tcp");
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/tcp6");
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/udp");
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/udp6");
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/udplite");
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/udplite6");
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/raw");
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/raw6");
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/icmp");
xtm_refresh_inode_to_sock_protocol (its, "/proc/net/icmp6");
}
#ifdef HAVE_LIBPCAP
void
packet_callback (u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
struct ether_header *eth_header = (struct ether_header *)packet;
XtmNetworkAnalyzer *iface = (XtmNetworkAnalyzer *)args;
char local_mac[18];
char src_mac[18];
char dst_mac[18];
int32_t src_port = -1;
int32_t dst_port = -1;
sprintf (local_mac, "%02X:%02X:%02X:%02X:%02X:%02X",
iface->mac[0], iface->mac[1], iface->mac[2],
iface->mac[3], iface->mac[4], iface->mac[5]);
sprintf (src_mac, "%02X:%02X:%02X:%02X:%02X:%02X",
eth_header->ether_shost[0], eth_header->ether_shost[1],
eth_header->ether_shost[2], eth_header->ether_shost[3],
eth_header->ether_shost[4], eth_header->ether_shost[5]);
sprintf (dst_mac, "%02X:%02X:%02X:%02X:%02X:%02X",
eth_header->ether_dhost[0], eth_header->ether_dhost[1],
eth_header->ether_dhost[2], eth_header->ether_dhost[3],
eth_header->ether_dhost[4], eth_header->ether_dhost[5]);
// IPv4 handling
if (ntohs (eth_header->ether_type) == ETHERTYPE_IP)
{
struct ip *ip_header = (struct ip *)(packet + sizeof (struct ether_header));
// TCP handling
if (ip_header->ip_p == IPPROTO_TCP)
{
struct tcphdr *tcp_header = (struct tcphdr *)(packet + sizeof (struct ether_header) + sizeof (struct ip));
src_port = ntohs (tcp_header->source);
dst_port = ntohs (tcp_header->dest);
}
// UDP handling
else if (ip_header->ip_p == IPPROTO_UDP)
{
struct udphdr *udp_header = (struct udphdr *)(packet + sizeof (struct ether_header) + sizeof (struct ip));
src_port = ntohs (udp_header->source);
dst_port = ntohs (udp_header->dest);
}
// ICMP handling
else if (ip_header->ip_p == IPPROTO_ICMP)
{
// The Internet Control Message Protocol (ICMP) does not use ports like TCP and UDP
// But the ICMP packet is encapsulated in an IPv4 packet
// 0x1 Reported by /proc/net/raw using ping
src_port = 1;
dst_port = 1;
}
}
// IPv6 handling
else if (ntohs (eth_header->ether_type) == ETHERTYPE_IPV6)
{
struct ip6_hdr *ip6_header = (struct ip6_hdr *)(packet + sizeof (struct ether_header));
// TCP handling
if (ip6_header->ip6_nxt == IPPROTO_TCP)
{
struct tcphdr *tcp_header = (struct tcphdr *)(packet + sizeof (struct ether_header) + sizeof (struct ip6_hdr));
src_port = ntohs (tcp_header->source);
dst_port = ntohs (tcp_header->dest);
}
// UDP handling
else if (ip6_header->ip6_nxt == IPPROTO_UDP)
{
struct udphdr *udp_header = (struct udphdr *)(packet + sizeof (struct ether_header) + sizeof (struct ip6_hdr));
src_port = ntohs (udp_header->source);
dst_port = ntohs (udp_header->dest);
}
// ICMP handling
else if (ip6_header->ip6_nxt == IPPROTO_ICMPV6)
{
// The Internet Control Message Protocol (ICMP) does not use ports like TCP and UDP
// But the ICMP packet is encapsulated in an IPv6 packet
// 0x3A Reported by /proc/net/raw6
src_port = 0x3A;
dst_port = 0x3A;
}
}
// Raw packet handling
else
{
src_port = 1;
dst_port = 1;
}
//! ICMP and RAW packet share the same local port
//! thus the link port -> count is broken in this case
//! since local port become non unique
//! However it still allow to see some unexpected program
if (src_port != -1 && dst_port != -1)
{
if (strcmp (local_mac, src_mac) == 0)
increament_packet_count (local_mac, "in ", iface->packetin, src_port);
if (strcmp (local_mac, dst_mac) == 0)
increament_packet_count (local_mac, "out", iface->packetout, dst_port);
}
}
#endif
int
get_mac_address (const char *device, uint8_t mac[6])
{
struct ifaddrs *ifaddr, *ifa;
char device_path[512];
char link_path[512];
snprintf (device_path, 512, "/sys/class/net/%s", device);
ssize_t len = readlink (device_path, link_path, 511);
// disable localhost, docker, vpn, and other virtual device
// only physical device should remain
if (len == -1 || strstr (link_path, "virtual") != NULL)
return -1;
if (getifaddrs (&ifaddr) == -1)
return -1;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
int family = ifa->ifa_addr->sa_family;
// Check if the interface is a network interface (AF_PACKET for Linux)
if (family == AF_PACKET && strcmp (device, ifa->ifa_name) == 0)
{
// Check if the interface has a hardware address (MAC address)
if (ifa->ifa_data != NULL)
{
struct sockaddr_ll *sll = (struct sockaddr_ll *)ifa->ifa_addr;
memcpy (mac, sll->sll_addr, sizeof (uint8_t) * 6);
freeifaddrs (ifaddr);
return 0;
}
}
}
freeifaddrs (ifaddr);
return -1;
}
gboolean
get_network_usage (guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error)
{
FILE *file;
gchar buffer[256];
char *out;
*tcp_rx = 0;
*tcp_tx = 0;
*tcp_error = 0;
if ((file = fopen ("/proc/net/dev", "r")) == NULL)
return FALSE;
out = fgets (buffer, sizeof (buffer), file);
if (!out)
{
fclose (file);
return FALSE;
}
out = fgets (buffer, sizeof (buffer), file);
if (!out)
{
fclose (file);
return FALSE;
}
while (fgets (buffer, sizeof (buffer), file))
{
unsigned long int dummy = 0;
unsigned long int r_bytes = 0;
unsigned long int t_bytes = 0;
unsigned long int r_packets = 0;
unsigned long int t_packets = 0;
unsigned long int error = 0;
gchar ifname[256];
int count = sscanf (
buffer, "%[^:]: %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
ifname, &r_bytes, &r_packets, &error,
&dummy, &dummy, &dummy, &dummy, &dummy,
&t_bytes, &t_packets);
if (count != 11)
{
printf ("Something went wrong while reading /proc/net/dev -> expected %d\n", count);
break;
}
*tcp_rx += r_bytes;
*tcp_tx += t_bytes;
*tcp_error += error;
}
fclose (file);
return TRUE;
}
void
list_process_fds (Task *task)
{
XtmNetworkAnalyzer *current;
char path[1024];
char link[2048];
char target[2048];
struct dirent *entry;
ssize_t len;
long int port;
DIR *dir;
task->packet_in = 0;
task->packet_out = 0;
snprintf (path, sizeof (path), "/proc/%d/fd", (int)task->pid);
dir = opendir (path);
if (dir == 0)
return;
while ((entry = readdir (dir)) != NULL)
{
if (entry->d_type != DT_LNK)
continue;
snprintf (link, sizeof (link), "%s/%s", path, entry->d_name);
len = readlink (link, target, sizeof (target) - 1);
if (len == -1)
continue;
target[len] = '\0';
if (strncmp (target, "socket:", 7) != 0)
continue;
int inode;
if (sscanf (target, "socket:[%d]", &inode) == 1)
{
task->active_socket += 1;
port = (long int)g_hash_table_lookup (inode_to_sock->hash, &inode);
current = analyzer;
while (current)
{
// pthread_mutex_lock(&analyzer->lock);
task->packet_in += (guint64)g_hash_table_lookup (current->packetin, &port);
task->packet_out += (guint64)g_hash_table_lookup (current->packetout, &port);
// pthread_mutex_lock(&analyzer->lock);
current = current->next;
}
}
}
closedir (dir);
}
gboolean
get_memory_usage (guint64 *memory_total, guint64 *memory_available, guint64 *memory_free, guint64 *memory_cache, guint64 *memory_buffers, guint64 *swap_total, guint64 *swap_free)
{
@@ -304,6 +662,8 @@ get_task_details (GPid pid, Task *task)
fclose (file);
}
list_process_fds (task);
/* Read the full command line */
if (!get_task_cmdline (task))
return FALSE;
@@ -319,6 +679,10 @@ get_task_list (GArray *task_list)
GPid pid;
Task task;
analyzer = xtm_network_analyzer_get_default ();
inode_to_sock = xtm_inode_to_sock_get_default ();
xtm_refresh_inode_to_sock (inode_to_sock);
if ((dir = g_dir_open ("/proc", 0, NULL)) == NULL)
return FALSE;

View File

@@ -26,6 +26,29 @@
static gushort _cpu_count = 0;
*/
int
get_mac_address (const char *device, uint8_t mac[6])
{
memset (mac, 0, sizeof (uint8_t) * 6);
return FALSE;
}
gboolean
get_network_usage (guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error)
{
*tcp_rx = 0;
*tcp_tx = 0;
*tcp_error = 0;
return TRUE;
}
#ifdef HAVE_LIBPCAP
void
packet_callback (u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
}
#endif
gboolean
get_memory_usage (guint64 *memory_total, guint64 *memory_available, guint64 *memory_free, guint64 *memory_cache, guint64 *memory_buffers, guint64 *swap_total, guint64 *swap_free)
{

View File

@@ -1,4 +1,5 @@
/*
* Copyright (c) 2024 Jehan-Antoine Vayssade, <javayss@sleek-think.ovh>
* Copyright (c) 2010 Mike Massonnet <mmassonnet@xfce.org>
* Copyright (c) 2009 Peter Tribble <peter.tribble@gmail.com>
*
@@ -12,29 +13,456 @@
#include "config.h"
#endif
#include "inode-to-sock.h"
#include "network-analyzer.h"
#include "task-manager.h"
#include <dirent.h>
#include <fcntl.h>
#include <inet/common.h> /* typedef int (*pfi_t)() for inet/optcom.h */
#include <inet/optcom.h>
#include <kstat.h>
#include <procfs.h>
#include <stdlib.h>
#include <string.h>
#include <sys/procfs.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/swap.h>
#include <sys/types.h>
#include <unistd.h>
// clang-format off
#include <sys/socket.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/if_ether.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/tcp.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#include <sys/stropts.h>
#include <inet/mib2.h>
#include <sys/tihdr.h>
// clang-format on
static XtmInodeToSock *inode_to_sock = NULL;
static XtmNetworkAnalyzer *analyzer = NULL;
static kstat_ctl_t *kc;
static gushort _cpu_count = 0;
static gulong ticks_total_delta = 0;
void addtoconninode (XtmInodeToSock *its, gint64 pid, char *ip, guint64 port);
static void
init_stats (void)
{
kc = kstat_open ();
}
int
get_mac_address (const char *device, uint8_t mac[6])
{
#ifdef HAVE_LIBSOCKET
struct ifaddrs *ifaddr, *ifa;
if (getifaddrs (&ifaddr) == -1)
return -1;
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
continue;
// Check if the interface is a network interface (AF_PACKET for Linux)
if (ifa->ifa_addr->sa_family == AF_LINK && strcmp (device, ifa->ifa_name) == 0)
{
// Check if the interface has a hardware address (MAC address)
if (ifa->ifa_data != NULL)
{
// warning: cast from 'struct sockaddr *' to 'struct sockaddr_dl *' increases required alignment from 1 to 2
// struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr;
struct sockaddr_dl sdl;
memcpy (&sdl, ifa->ifa_addr, sizeof (struct sockaddr_dl));
memcpy (mac, sdl.sdl_data + sdl.sdl_nlen, sizeof (uint8_t) * 6);
freeifaddrs (ifaddr);
return 0;
}
}
}
freeifaddrs (ifaddr);
return -1;
#else
memset (mac, 0, sizeof (uint8_t) * 6);
return FALSE;
#endif
}
gboolean
get_network_usage (guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error)
{
kstat_named_t *knp;
kstat_t *ksp;
if (!kc)
init_stats ();
if (!(ksp = kstat_lookup (kc, "link", -1, NULL)))
{
printf ("kstat_lookup failed\n");
return FALSE;
}
kstat_read (kc, ksp, NULL);
*tcp_error = 0;
if ((knp = kstat_data_lookup (ksp, "rbytes64")) != NULL)
*tcp_rx = knp->value.ui64;
if ((knp = kstat_data_lookup (ksp, "obytes64")) != NULL)
*tcp_tx = knp->value.ui64;
if ((knp = kstat_data_lookup (ksp, "ierrors")) != NULL)
*tcp_error += knp->value.ui32;
if ((knp = kstat_data_lookup (ksp, "oerrors")) != NULL)
*tcp_error += knp->value.ui32;
return TRUE;
}
#ifdef HAVE_LIBPCAP
void
packet_callback (u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{
// Extract source and destination IP addresses and ports from the packet
struct ether_header eth_header;
struct ip ip_header;
struct tcphdr tcp_header;
XtmNetworkAnalyzer *iface;
long int src_port, dst_port;
char local_mac[18];
char src_mac[18];
char dst_mac[18];
memcpy (&eth_header, packet, sizeof (struct ether_header));
memcpy (&ip_header, packet + sizeof (struct ether_header), sizeof (struct ip));
memcpy (&tcp_header, packet + sizeof (struct ether_header) + sizeof (struct ip), sizeof (struct ip));
// cast -> increases required alignment from 1 to 2 [-Wcast-align]
// const struct ether_header *eth_header = (const struct ether_header *)packet;
// struct ip *ip_header = (struct ip *)(packet + sizeof (struct ether_header));
// struct tcphdr *tcp_header = (struct tcphdr *)(packet + sizeof (struct ether_header) + sizeof (struct ip));
// printf("%d, %d \n", eth_heade.ether_type , ip_header.ip_p);
// Dropped non-ip packet
if (eth_header.ether_type != 8 || ip_header.ip_p != 6)
return;
iface = (XtmNetworkAnalyzer *)args;
src_port = ntohs (tcp_header.th_sport);
dst_port = ntohs (tcp_header.th_dport);
// directly use strcmp on analyzer->mac, eth_header->ether_shost doesnt work
snprintf (local_mac, sizeof (local_mac),
"%02X:%02X:%02X:%02X:%02X:%02X",
iface->mac[0], iface->mac[1],
iface->mac[2], iface->mac[3],
iface->mac[4], iface->mac[5]);
snprintf (src_mac, sizeof (local_mac),
"%02X:%02X:%02X:%02X:%02X:%02X",
eth_header.ether_shost.ether_addr_octet[0], eth_header.ether_shost.ether_addr_octet[1],
eth_header.ether_shost.ether_addr_octet[2], eth_header.ether_shost.ether_addr_octet[3],
eth_header.ether_shost.ether_addr_octet[4], eth_header.ether_shost.ether_addr_octet[5]);
snprintf (dst_mac, sizeof (local_mac),
"%02X:%02X:%02X:%02X:%02X:%02X",
eth_header.ether_dhost.ether_addr_octet[0], eth_header.ether_dhost.ether_addr_octet[1],
eth_header.ether_dhost.ether_addr_octet[2], eth_header.ether_dhost.ether_addr_octet[3],
eth_header.ether_dhost.ether_addr_octet[4], eth_header.ether_dhost.ether_addr_octet[5]);
// Debug
// pthread_mutex_lock(&iface->lock);
if (strcmp (local_mac, src_mac) == 0)
increament_packet_count (local_mac, "in ", iface->packetin, src_port);
if (strcmp (local_mac, dst_mac) == 0)
increament_packet_count (local_mac, "out", iface->packetout, dst_port);
// pthread_mutex_unlock(&iface->lock);
}
#endif
void
set_task_network_data (Task *task, guint64 port)
{
XtmNetworkAnalyzer *current;
task->active_socket += 1;
current = analyzer;
while (current)
{
task->packet_in += (guint64)g_hash_table_lookup (current->packetin, &port);
task->packet_out += (guint64)g_hash_table_lookup (current->packetout, &port);
current = current->next;
}
}
void
list_process_fds (Task *task)
{
XtmNetworkAnalyzer *current;
GHashTableIter iter;
gpointer key, value;
gint64 key_int, value_int;
g_hash_table_iter_init (&iter, inode_to_sock->pid);
while (g_hash_table_iter_next (&iter, &key, &value))
{
key_int = *(gint64 *)(key);
value_int = GPOINTER_TO_INT (value);
if (task->pid == value_int)
set_task_network_data (task, key_int);
}
}
void
addtoconninode (XtmInodeToSock *its, gint64 pid, char *ip, guint64 port)
{
gint64 *inode;
if (its == NULL)
return;
// seem like idle socket are given back to pid 0 (kernel ?)
if (pid <= 0)
return;
if (strcmp (ip, "0.0.0.0") == 0)
return;
if (strcmp (ip, "::") == 0)
return;
if (strcmp (ip, "127.0.0.1") == 0)
return;
inode = g_new0 (gint64, 1);
*inode = port;
g_hash_table_replace (inode_to_sock->pid, inode, (gpointer)(intptr_t)pid);
printf ("PID %ld: Local Address: [%s]:%ld\n", pid, ip, port);
}
void
xtm_refresh_inode_to_sock (XtmInodeToSock *its)
{
// inspired by :
// nxsensor/src/sysdeps/solaris.c
// net-snmp/agent/mibgroup/mibII/tcpTable.c
// net-snmp/agent/mibgroup/mibgroup/kernel_sunos5.c
// psutil/psutil/_psutil_sunos.c
// illumos-joyent/master/usr/src/uts/common/io/tl.c
// nicstat/nicstat.c
int sd, ret, flags, getcode, num_ent, i;
char buf[4096];
char lip[INET6_ADDRSTRLEN];
mib2_tcpConnEntry_t tp;
mib2_udpEntry_t ude;
#if defined(AF_INET6)
mib2_tcp6ConnEntry_t tp6;
mib2_udp6Entry_t ude6;
#endif
struct strbuf ctlbuf, databuf;
struct T_optmgmt_req tor = { 0 };
struct T_optmgmt_ack toa = { 0 };
struct T_error_ack tea = { 0 };
struct opthdr mibhdr = { 0 };
sd = open ("/dev/arp", O_RDWR);
if (sd == -1)
{
perror ("open");
return;
}
ret = ioctl (sd, I_PUSH, "tcp");
if (ret == -1)
{
perror ("ioctl");
close (sd);
return;
}
ret = ioctl (sd, I_PUSH, "udp");
if (ret == -1)
{
perror ("ioctl");
close (sd);
return;
}
// g_hash_table_remove_all (its->pid);
// g_hash_table_remove_all (its->hash);
// Set up the request
tor.PRIM_type = T_SVR4_OPTMGMT_REQ;
tor.OPT_offset = sizeof (struct T_optmgmt_req);
tor.OPT_length = sizeof (struct opthdr);
tor.MGMT_flags = T_CURRENT;
mibhdr.level = MIB2_IP;
mibhdr.name = 0;
#ifdef NEW_MIB_COMPLIANT
mibhdr.len = 1;
#else
mibhdr.len = 0;
#endif
memcpy (buf, &tor, sizeof (tor));
memcpy (buf + tor.OPT_offset, &mibhdr, sizeof (mibhdr));
ctlbuf.buf = buf;
ctlbuf.len = tor.OPT_offset + tor.OPT_length;
flags = 0;
// Send the request
if (putmsg (sd, &ctlbuf, NULL, flags) == -1)
{
perror ("putmsg");
close (sd);
return;
}
ctlbuf.maxlen = sizeof (buf);
for (;;)
{
getcode = getmsg (sd, &ctlbuf, NULL, &flags);
memcpy (&toa, buf, sizeof (toa));
memcpy (&tea, buf, sizeof (tea));
if (getcode != MOREDATA || ctlbuf.len < (int)sizeof (struct T_optmgmt_ack) ||
toa.PRIM_type != T_OPTMGMT_ACK || toa.MGMT_flags != T_SUCCESS)
{
break;
}
if (ctlbuf.len >= (int)sizeof (struct T_error_ack) && tea.PRIM_type == T_ERROR_ACK)
{
fprintf (stderr, "ERROR_ACK\n");
close (sd);
return;
}
if (getcode == 0 && ctlbuf.len >= (int)sizeof (struct T_optmgmt_ack) &&
toa.PRIM_type == T_OPTMGMT_ACK && toa.MGMT_flags == T_SUCCESS)
{
fprintf (stderr, "ERROR_T_OPTMGMT_ACK\n");
close (sd);
return;
}
memset (&mibhdr, 0x0, sizeof (mibhdr));
memcpy (&mibhdr, buf + toa.OPT_offset, toa.OPT_length);
databuf.maxlen = mibhdr.len;
databuf.len = 0;
databuf.buf = (char *)malloc ((int)mibhdr.len);
if (!databuf.buf)
{
fprintf (stderr, "Out of memory\n");
close (sd);
return;
}
flags = 0;
getcode = getmsg (sd, NULL, &databuf, &flags);
if (getcode < 0)
{
perror ("getmsg");
free (databuf.buf);
close (sd);
return;
}
// TCPv4
if (mibhdr.level == MIB2_TCP && mibhdr.name == MIB2_TCP_13)
{
num_ent = mibhdr.len / sizeof (mib2_tcpConnEntry_t);
for (i = 0; i < num_ent; i++)
{
memcpy (&tp, databuf.buf + i * sizeof (tp), sizeof (tp));
inet_ntop (AF_INET, &tp.tcpConnLocalAddress, lip, sizeof (lip));
addtoconninode (inode_to_sock, tp.tcpConnCreationProcess, lip, tp.tcpConnLocalPort);
// interesting properties, allowing to remove pcap
// tp.tcpConnEntryInfo.ce_in_data_inorder_segs
// tp.tcpConnEntryInfo.ce_in_data_unorder_segs
// tp.tcpConnEntryInfo.ce_out_data_segs
// tp.tcpConnEntryInfo.ce_out_retrans_segs
}
}
#if defined(AF_INET6)
// TCPv6
else if (mibhdr.level == MIB2_TCP6 && mibhdr.name == MIB2_TCP6_CONN)
{
num_ent = mibhdr.len / sizeof (mib2_tcp6ConnEntry_t);
for (i = 0; i < num_ent; i++)
{
memcpy (&tp6, databuf.buf + i * sizeof (tp6), sizeof (tp6));
inet_ntop (AF_INET6, &tp6.tcp6ConnLocalAddress, lip, sizeof (lip));
addtoconninode (inode_to_sock, tp6.tcp6ConnCreationProcess, lip, tp6.tcp6ConnLocalPort);
// interesting properties, allowing to remove pcap
// tp.tcpConnEntryInfo.ce_in_data_inorder_segs
// tp.tcpConnEntryInfo.ce_in_data_unorder_segs
// tp.tcpConnEntryInfo.ce_out_data_segs
// tp.tcpConnEntryInfo.ce_out_retrans_segs
}
}
#endif
// UDPv4
else if (mibhdr.level == MIB2_UDP || mibhdr.level == MIB2_UDP_ENTRY)
{
num_ent = mibhdr.len / sizeof (mib2_udpEntry_t);
for (i = 0; i < num_ent; i++)
{
memcpy (&ude, databuf.buf + i * sizeof (ude), sizeof (ude));
inet_ntop (AF_INET, &ude.udpLocalAddress, lip, sizeof (lip));
addtoconninode (inode_to_sock, ude.udpCreationProcess, lip, ude.udpLocalPort);
}
}
#if defined(AF_INET6)
// UDPv6
else if (mibhdr.level == MIB2_UDP6 || mibhdr.level == MIB2_UDP6_ENTRY)
{
num_ent = mibhdr.len / sizeof (mib2_udp6Entry_t);
for (i = 0; i < num_ent; i++)
{
memcpy (&ude6, databuf.buf + i * sizeof (ude6), sizeof (ude6));
inet_ntop (AF_INET6, &ude6.udp6LocalAddress, lip, sizeof (lip));
addtoconninode (inode_to_sock, ude6.udp6CreationProcess, lip, ude6.udp6LocalPort);
}
}
#endif
free (databuf.buf);
}
close (sd);
}
gboolean
get_memory_usage (guint64 *memory_total, guint64 *memory_available, guint64 *memory_free, guint64 *memory_cache, guint64 *memory_buffers, guint64 *swap_total, guint64 *swap_free)
{
@@ -201,6 +629,8 @@ get_task_details (GPid pid, Task *task)
fclose (file);
list_process_fds (task);
return TRUE;
}
@@ -212,6 +642,11 @@ get_task_list (GArray *task_list)
GPid pid;
Task task;
printf ("------------\n");
analyzer = xtm_network_analyzer_get_default ();
inode_to_sock = xtm_inode_to_sock_get_default ();
xtm_refresh_inode_to_sock (inode_to_sock);
if ((dir = g_dir_open ("/proc", 0, NULL)) == NULL)
return FALSE;

View File

@@ -1,4 +1,5 @@
/*
* Copyright (c) 2024 Jehan-Antoine Vayssade, <javayss@sleek-think.ovh>
* Copyright (c) 2010 Mike Massonnet, <mmassonnet@xfce.org>
* Copyright (c) 2018 Rozhuk Ivan <rozhuk.im@gmail.com>
*
@@ -12,6 +13,7 @@
#include "config.h"
#endif
#include "network-analyzer.h"
#include "process-tree-view.h" /* for the columns of the model */
#include "settings.h"
#include "task-manager.h"
@@ -22,6 +24,10 @@
#include <gdk/gdkx.h>
#endif
#ifdef HAVE_LIBPCAP
#include <pcap.h>
#endif
#include <glib/gi18n.h>
#include <pwd.h>
#include <sys/resource.h>
@@ -42,6 +48,7 @@ struct _XtmTaskManagerClass
{
GObjectClass parent_class;
};
struct _XtmTaskManager
{
GObject parent;
@@ -61,6 +68,12 @@ struct _XtmTaskManager
guint64 memory_buffers;
guint64 swap_total;
guint64 swap_free;
guint64 tcp_rx;
guint64 tcp_tx;
guint64 tcp_error;
guint64 old_tcp_rx;
guint64 old_tcp_tx;
guint64 old_tcp_error;
};
G_DEFINE_TYPE (XtmTaskManager, xtm_task_manager, G_TYPE_OBJECT)
@@ -101,6 +114,9 @@ xtm_task_manager_init (XtmTaskManager *manager)
g_object_get (settings, "full-command-line", &full_cmdline, NULL);
g_signal_connect (settings, "notify::more-precision", G_CALLBACK (setting_changed), manager);
g_signal_connect (settings, "notify::full-command-line", G_CALLBACK (setting_changed), manager);
manager->old_tcp_rx = 0;
manager->old_tcp_tx = 0;
manager->old_tcp_error = 0;
}
static void
@@ -108,6 +124,7 @@ xtm_task_manager_finalize (GObject *object)
{
XtmTaskManager *manager = XTM_TASK_MANAGER (object);
g_array_free (manager->tasks, TRUE);
#ifdef HAVE_WNCK
if (manager->app_manager != NULL)
{
@@ -334,6 +351,9 @@ model_update_tree_iter (XtmTaskManager *manager, GtkTreeIter *iter, glong timest
XTM_PTV_COLUMN_CPU_STR, cpu,
XTM_PTV_COLUMN_GROUP_CPU, (task->group_cpu_user + task->group_cpu_system),
XTM_PTV_COLUMN_GROUP_CPU_STR, group_cpu,
XTM_PTV_COLUMN_PACKET_IN, task->packet_in,
XTM_PTV_COLUMN_PACKET_OUT, task->packet_out,
XTM_PTV_COLUMN_ACTIVE_SOCKET, task->active_socket,
XTM_PTV_COLUMN_PRIORITY, task->prio,
XTM_PTV_COLUMN_BACKGROUND, background,
XTM_PTV_COLUMN_FOREGROUND, foreground,
@@ -393,6 +413,33 @@ xtm_task_manager_new (GtkTreeModel *model)
return manager;
}
void
xtm_task_manager_get_network_info (XtmTaskManager *manager, guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error)
{
g_return_if_fail (XTM_IS_TASK_MANAGER (manager));
get_network_usage (&manager->tcp_rx, &manager->tcp_tx, &manager->tcp_error);
if (manager->old_tcp_rx == 0 && manager->old_tcp_tx == 0 && manager->old_tcp_error == 0)
{
*tcp_rx = 0;
*tcp_tx = 0;
*tcp_error = 0;
}
else
{
gint ms;
g_object_get (settings, "refresh-rate", &ms, NULL);
// ugly approximation in guint64
*tcp_rx = (manager->tcp_rx - manager->old_tcp_rx) / ms * 1000;
*tcp_tx = (manager->tcp_tx - manager->old_tcp_tx) / ms * 1000;
*tcp_error = (manager->tcp_error - manager->old_tcp_error) / ms * 1000;
}
manager->old_tcp_rx = manager->tcp_rx;
manager->old_tcp_tx = manager->tcp_tx;
manager->old_tcp_error = manager->tcp_error;
}
void
xtm_task_manager_get_system_info (XtmTaskManager *manager, guint *num_processes, gfloat *cpu,
guint64 *memory_used, guint64 *memory_total,

View File

@@ -10,9 +10,17 @@
#ifndef TASK_MANAGER_H
#define TASK_MANAGER_H
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib-object.h>
#include <gtk/gtk.h>
#ifdef HAVE_LIBPCAP
#include <pcap.h>
#endif
/**
* Legend colors
*/
@@ -45,6 +53,10 @@ struct _Task
gfloat group_cpu_system;
guint64 group_vsz;
guint64 group_rss;
guint64 packet_in;
guint64 packet_out;
guint64 active_socket;
};
/**
@@ -53,6 +65,12 @@ struct _Task
* memory_available = free + cache + buffers + an-OS-specific-value
*/
#ifdef HAVE_LIBPCAP
void packet_callback (u_char *args, const struct pcap_pkthdr *header, const u_char *packet);
#endif
int get_mac_address (const char *device, uint8_t mac[6]);
gboolean get_network_usage (guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error);
gboolean get_memory_usage (guint64 *memory_total, guint64 *memory_available, guint64 *memory_free, guint64 *memory_cache, guint64 *memory_buffers, guint64 *swap_total, guint64 *swap_free);
gboolean get_cpu_usage (gushort *cpu_count, gfloat *cpu_user, gfloat *cpu_system);
gboolean get_task_list (GArray *task_list);
@@ -73,6 +91,7 @@ typedef struct _XtmTaskManager XtmTaskManager;
GType xtm_task_manager_get_type (void);
XtmTaskManager *xtm_task_manager_new (GtkTreeModel *model);
void xtm_task_manager_get_network_info (XtmTaskManager *manager, guint64 *tcp_rx, guint64 *tcp_tx, guint64 *tcp_error);
void xtm_task_manager_get_system_info (XtmTaskManager *manager, guint *num_processes, gfloat *cpu,
guint64 *memory_used, guint64 *memory_total,
guint64 *swap_used, guint64 *swap_total);