aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Lange <DLange@git.local>2020-12-07 10:26:02 +0100
committerDaniel Lange <DLange@git.local>2020-12-07 10:26:02 +0100
commitd67ee86091f9e07f8d85c1fb77c7c58989e676bb (patch)
tree900f5e62bfd8e8c082be75a94f5348dea819beba
parent3cbc50cde37fee19ce98ee8260b0a6095b829c4c (diff)
parent65357c8c46154de4e4eca14075bfe5523bb5fc14 (diff)
downloaddebian_htop-d67ee86091f9e07f8d85c1fb77c7c58989e676bb.tar.gz
debian_htop-d67ee86091f9e07f8d85c1fb77c7c58989e676bb.tar.bz2
debian_htop-d67ee86091f9e07f8d85c1fb77c7c58989e676bb.zip
Update upstream source from tag 'upstream/3.0.3'
Update to upstream version '3.0.3' with Debian dir 8a3a733dc80e45b1cde220f88fcfd815869742f0
-rw-r--r--.github/workflows/ci.yml79
-rw-r--r--.gitignore5
-rw-r--r--.travis.yml16
-rw-r--r--AUTHORS12
-rw-r--r--Action.c266
-rw-r--r--Action.h15
-rw-r--r--Affinity.c26
-rw-r--r--Affinity.h20
-rw-r--r--AffinityPanel.c106
-rw-r--r--AffinityPanel.h4
-rw-r--r--AvailableColumnsPanel.c30
-rw-r--r--AvailableColumnsPanel.h4
-rw-r--r--AvailableMetersPanel.c35
-rw-r--r--AvailableMetersPanel.h9
-rw-r--r--BatteryMeter.c28
-rw-r--r--BatteryMeter.h6
-rw-r--r--CONTRIBUTING.md11
-rw-r--r--CPUMeter.c371
-rw-r--r--CPUMeter.h31
-rw-r--r--CRT.c991
-rw-r--r--CRT.h93
-rw-r--r--CategoriesPanel.c27
-rw-r--r--CategoriesPanel.h9
-rw-r--r--ChangeLog77
-rw-r--r--CheckItem.c69
-rw-r--r--CheckItem.h29
-rw-r--r--ClockMeter.c17
-rw-r--r--ClockMeter.h6
-rw-r--r--ColorsPanel.c22
-rw-r--r--ColorsPanel.h6
-rw-r--r--ColumnsPanel.c37
-rw-r--r--ColumnsPanel.h8
-rw-r--r--CommandScreen.c68
-rw-r--r--CommandScreen.h19
-rw-r--r--Compat.c119
-rw-r--r--Compat.h59
-rw-r--r--DateMeter.c50
-rw-r--r--DateMeter.h14
-rw-r--r--DateTimeMeter.c50
-rw-r--r--DateTimeMeter.h14
-rw-r--r--DiskIOMeter.c124
-rw-r--r--DiskIOMeter.h20
-rw-r--r--DisplayOptionsPanel.c106
-rw-r--r--DisplayOptionsPanel.h6
-rw-r--r--EnvScreen.c26
-rw-r--r--EnvScreen.h4
-rw-r--r--FunctionBar.c74
-rw-r--r--FunctionBar.h13
-rw-r--r--Hashtable.c323
-rw-r--r--Hashtable.h41
-rw-r--r--Header.c52
-rw-r--r--Header.h11
-rw-r--r--HostnameMeter.c17
-rw-r--r--HostnameMeter.h6
-rw-r--r--IncSet.c77
-rw-r--r--IncSet.h17
-rw-r--r--InfoScreen.c65
-rw-r--r--InfoScreen.h35
-rw-r--r--ListItem.c58
-rw-r--r--ListItem.h12
-rw-r--r--LoadAverageMeter.c33
-rw-r--r--LoadAverageMeter.h10
-rw-r--r--Macros.h52
-rw-r--r--MainPanel.c52
-rw-r--r--MainPanel.h16
-rw-r--r--Makefile.am208
-rw-r--r--MemoryMeter.c35
-rw-r--r--MemoryMeter.h6
-rw-r--r--Meter.c194
-rw-r--r--Meter.h85
-rw-r--r--MetersPanel.c23
-rw-r--r--MetersPanel.h13
-rw-r--r--NetworkIOMeter.c125
-rw-r--r--NetworkIOMeter.h8
-rw-r--r--Object.c22
-rw-r--r--Object.h43
-rw-r--r--OpenFilesScreen.c194
-rw-r--r--OpenFilesScreen.h29
-rw-r--r--OptionItem.c191
-rw-r--r--OptionItem.h70
-rw-r--r--Panel.c144
-rw-r--r--Panel.h32
-rw-r--r--Process.c349
-rw-r--r--Process.h115
-rw-r--r--ProcessList.c478
-rw-r--r--ProcessList.h32
-rw-r--r--ProcessLocksScreen.c105
-rw-r--r--ProcessLocksScreen.h52
-rw-r--r--ProvideCurses.h34
-rw-r--r--README50
-rw-r--r--RichString.c38
-rw-r--r--RichString.h51
-rw-r--r--ScreenManager.c157
-rw-r--r--ScreenManager.h19
-rw-r--r--Settings.c135
-rw-r--r--Settings.h30
-rw-r--r--SignalsPanel.c19
-rw-r--r--SignalsPanel.h6
-rw-r--r--StringUtils.c142
-rw-r--r--StringUtils.h34
-rw-r--r--SwapMeter.c31
-rw-r--r--SwapMeter.h6
-rw-r--r--TasksMeter.c30
-rw-r--r--TasksMeter.h6
-rw-r--r--TraceScreen.c145
-rw-r--r--TraceScreen.h15
-rw-r--r--UptimeMeter.c19
-rw-r--r--UptimeMeter.h6
-rw-r--r--UsersTable.c22
-rw-r--r--UsersTable.h4
-rw-r--r--Vector.c166
-rw-r--r--Vector.h52
-rw-r--r--XAlloc.c70
-rw-r--r--XAlloc.h44
-rw-r--r--XUtils.c263
-rw-r--r--XUtils.h71
-rwxr-xr-xautogen.sh3
-rw-r--r--configure.ac189
-rw-r--r--darwin/Battery.c74
-rw-r--r--darwin/Battery.h6
-rw-r--r--darwin/DarwinCRT.c34
-rw-r--r--darwin/DarwinCRT.h12
-rw-r--r--darwin/DarwinProcess.c73
-rw-r--r--darwin/DarwinProcess.h23
-rw-r--r--darwin/DarwinProcessList.c253
-rw-r--r--darwin/DarwinProcessList.h29
-rw-r--r--darwin/Platform.c194
-rw-r--r--darwin/Platform.h40
-rw-r--r--docs/images/screenshot.pngbin0 -> 80937 bytes
-rw-r--r--docs/styleguide.md224
-rw-r--r--dragonflybsd/Battery.c26
-rw-r--r--dragonflybsd/Battery.h13
-rw-r--r--dragonflybsd/DragonFlyBSDCRT.c34
-rw-r--r--dragonflybsd/DragonFlyBSDCRT.h13
-rw-r--r--dragonflybsd/DragonFlyBSDProcess.c46
-rw-r--r--dragonflybsd/DragonFlyBSDProcess.h10
-rw-r--r--dragonflybsd/DragonFlyBSDProcessList.c146
-rw-r--r--dragonflybsd/DragonFlyBSDProcessList.h18
-rw-r--r--dragonflybsd/Platform.c114
-rw-r--r--dragonflybsd/Platform.h34
-rw-r--r--freebsd/Battery.c25
-rw-r--r--freebsd/Battery.h12
-rw-r--r--freebsd/FreeBSDCRT.c20
-rw-r--r--freebsd/FreeBSDCRT.h12
-rw-r--r--freebsd/FreeBSDProcess.c93
-rw-r--r--freebsd/FreeBSDProcess.h32
-rw-r--r--freebsd/FreeBSDProcessList.c312
-rw-r--r--freebsd/FreeBSDProcessList.h32
-rw-r--r--freebsd/Platform.c244
-rw-r--r--freebsd/Platform.h37
-rw-r--r--htop.1.in65
-rw-r--r--htop.c221
-rw-r--r--htop.pngbin3657 -> 3537 bytes
-rw-r--r--htop.svg112
-rw-r--r--iwyu/htop.imp18
-rwxr-xr-xiwyu/run_iwyu.sh14
-rw-r--r--linux/Battery.c322
-rw-r--r--linux/Battery.h14
-rw-r--r--linux/IOPriority.h4
-rw-r--r--linux/IOPriorityPanel.c32
-rw-r--r--linux/IOPriorityPanel.h3
-rw-r--r--linux/LibSensors.c104
-rw-r--r--linux/LibSensors.h16
-rw-r--r--linux/LinuxCRT.c36
-rw-r--r--linux/LinuxCRT.h12
-rw-r--r--linux/LinuxProcess.c739
-rw-r--r--linux/LinuxProcess.h119
-rw-r--r--linux/LinuxProcessList.c1564
-rw-r--r--linux/LinuxProcessList.h20
-rw-r--r--linux/Platform.c707
-rw-r--r--linux/Platform.h39
-rw-r--r--linux/PressureStallMeter.c92
-rw-r--r--linux/PressureStallMeter.h12
-rw-r--r--linux/SELinuxMeter.c94
-rw-r--r--linux/SELinuxMeter.h14
-rw-r--r--linux/SystemdMeter.c335
-rw-r--r--linux/SystemdMeter.h15
-rw-r--r--linux/ZramMeter.c67
-rw-r--r--linux/ZramMeter.h8
-rw-r--r--linux/ZramStats.h10
-rw-r--r--openbsd/Battery.c72
-rw-r--r--openbsd/Battery.h13
-rw-r--r--openbsd/OpenBSDCRT.c21
-rw-r--r--openbsd/OpenBSDCRT.h13
-rw-r--r--openbsd/OpenBSDProcess.c147
-rw-r--r--openbsd/OpenBSDProcess.h19
-rw-r--r--openbsd/OpenBSDProcessList.c210
-rw-r--r--openbsd/OpenBSDProcessList.h13
-rw-r--r--openbsd/Platform.c266
-rw-r--r--openbsd/Platform.h37
-rw-r--r--scripts/htop_suppressions.valgrind63
-rwxr-xr-xscripts/run_valgrind.sh6
-rw-r--r--solaris/Battery.c7
-rw-r--r--solaris/Battery.h6
-rw-r--r--solaris/Platform.c147
-rw-r--r--solaris/Platform.h45
-rw-r--r--solaris/SolarisCRT.c32
-rw-r--r--solaris/SolarisCRT.h13
-rw-r--r--solaris/SolarisProcess.c60
-rw-r--r--solaris/SolarisProcess.h10
-rw-r--r--solaris/SolarisProcessList.c217
-rw-r--r--solaris/SolarisProcessList.h10
-rw-r--r--unsupported/Battery.c7
-rw-r--r--unsupported/Battery.h6
-rw-r--r--unsupported/Platform.c79
-rw-r--r--unsupported/Platform.h33
-rw-r--r--unsupported/UnsupportedCRT.c20
-rw-r--r--unsupported/UnsupportedCRT.h12
-rw-r--r--unsupported/UnsupportedProcess.c2
-rw-r--r--unsupported/UnsupportedProcess.h2
-rw-r--r--unsupported/UnsupportedProcessList.c88
-rw-r--r--unsupported/UnsupportedProcessList.h4
-rw-r--r--zfs/ZfsArcMeter.c42
-rw-r--r--zfs/ZfsArcMeter.h8
-rw-r--r--zfs/ZfsArcStats.c20
-rw-r--r--zfs/ZfsArcStats.h2
-rw-r--r--zfs/ZfsCompressedArcMeter.c36
-rw-r--r--zfs/ZfsCompressedArcMeter.h8
-rw-r--r--zfs/openzfs_sysctl.c73
-rw-r--r--zfs/openzfs_sysctl.h6
220 files changed, 11712 insertions, 5724 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d95d0af..e416c67 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,7 +3,7 @@ name: CI
on: [ push, pull_request ]
jobs:
- build-ubuntu-latest:
+ build-ubuntu-latest-minimal-gcc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -12,48 +12,95 @@ jobs:
- name: Bootstrap
run: ./autogen.sh
- name: Configure
- run: ./configure --enable-werror
+ run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors
+ - name: Enable compatibility modes
+ run: |
+ sed -i 's/#define HAVE_FSTATAT 1/#undef HAVE_FSTATAT/g' config.h
+ sed -i 's/#define HAVE_OPENAT 1/#undef HAVE_OPENAT/g' config.h
+ sed -i 's/#define HAVE_READLINKAT 1/#undef HAVE_READLINKAT/g' config.h
- name: Build
- run: make
+ run: make -k
- name: Distcheck
- run: make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
+ run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors"
- build-ubuntu-clang-latest:
+ build-ubuntu-latest-minimal-clang:
runs-on: ubuntu-latest
env:
- CC: clang-10
+ CC: clang-11
steps:
- uses: actions/checkout@v2
- name: install clang repo
run: |
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
- sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' -y
+ sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
sudo apt-get update -q
- name: Install Dependencies
- run: sudo apt-get install clang-10 libncursesw5-dev
+ run: sudo apt-get install clang-11 libncursesw5-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
- run: ./configure --enable-werror
+ run: ./configure --enable-werror --enable-linux-affinity --disable-unicode --without-sensors
- name: Build
- run: make
+ run: make -k
- name: Distcheck
- run: make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
+ run: make distcheck DISTCHECK_CONFIGURE_FLAGS="--enable-werror --enable-linux-affinity --disable-unicode --without-sensors"
- build-ubuntu-latest-full-featured:
+ build-ubuntu-latest-full-featured-gcc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
- run: sudo apt-get install libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev
+ run: sudo apt-get install libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
+ - name: Bootstrap
+ run: ./autogen.sh
+ - name: Configure
+ run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors
+ - name: Build
+ run: make -k
+ - name: Distcheck
+ run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors'
+
+ build-ubuntu-latest-full-featured-clang:
+ runs-on: ubuntu-latest
+ env:
+ CC: clang-11
+ steps:
+ - uses: actions/checkout@v2
+ - name: install clang repo
+ run: |
+ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
+ sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
+ sudo apt-get update -q
+ - name: Install Dependencies
+ run: sudo apt-get install clang-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
- name: Bootstrap
run: ./autogen.sh
- name: Configure
- run: ./configure --enable-werror --enable-openvz --enable-cgroup --enable-vserver --enable-ancient-vserver --enable-taskstats --enable-unicode --enable-linux-affinity --enable-hwloc --enable-setuid --enable-delayacct
+ run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors
- name: Build
- run: make
+ run: make -k
- name: Distcheck
- run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-cgroup --enable-vserver --enable-ancient-vserver --enable-taskstats --enable-unicode --enable-linux-affinity --enable-hwloc --enable-setuid --enable-delayacct'
+ run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors'
+
+ build-ubuntu-latest-clang-analyzer:
+ runs-on: ubuntu-latest
+ env:
+ CC: clang-11
+ steps:
+ - uses: actions/checkout@v2
+ - name: install clang repo
+ run: |
+ wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key 2>/dev/null | sudo apt-key add -
+ sudo add-apt-repository 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-11 main' -y
+ sudo apt-get update -q
+ - name: Install Dependencies
+ run: sudo apt-get install clang-11 clang-tools-11 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev
+ - name: Bootstrap
+ run: ./autogen.sh
+ - name: Configure
+ run: scan-build-11 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-setuid --enable-delayacct --with-sensors
+ - name: Build
+ run: scan-build-11 -analyze-headers --status-bugs make -j"$(nproc)"
whitespace_check:
runs-on: ubuntu-latest
diff --git a/.gitignore b/.gitignore
index f94f3f5..3d522b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@ htop
*.h.gch
*/.dirstamp
+# automake/autoconf related files
.deps/
Makefile
Makefile.in
@@ -24,6 +25,7 @@ INSTALL
aclocal.m4
autom4te.cache/
compile
+conf*/
config.guess
config.h
config.h.in
@@ -40,3 +42,6 @@ ltmain.sh
m4/
missing
stamp-h1
+
+# files related to valgrind/callgrind
+callgrind.out.*
diff --git a/.travis.yml b/.travis.yml
index b3e358d..bb79560 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -9,4 +9,18 @@ os:
- linux
- osx
-script: ./autogen.sh && ./configure && make
+arch:
+ - amd64
+ - s390x
+
+before_script:
+ if [[ ${TRAVIS_CPU_ARCH} == 's390x' ]]; then
+ sudo apt-get update && sudo apt-get install -y libncursesw5-dev ;
+ fi
+
+script:
+ - ./autogen.sh
+ # clang might warn about C11 generic selections in isnan()
+ - CFLAGS=-Wno-c11-extensions ./configure --enable-werror
+ - make -k
+ - CFLAGS=-Wno-c11-extensions make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-werror
diff --git a/AUTHORS b/AUTHORS
index de8bf3f..a4cc7c6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1 +1,11 @@
-Hisham H. Muhammad
+Originally authored by:
+ Hisham H. Muhammad
+
+Currently maintained by the htop dev team:
+ Benny Baumann
+ Christian Göttsche
+ Daniel Lange
+ Nathan Scott
+
+For the full list of contributors see:
+ git log --format="%aN" | sort -u
diff --git a/Action.c b/Action.c
index 90d6bfe..66934be 100644
--- a/Action.c
+++ b/Action.c
@@ -1,34 +1,44 @@
/*
htop - Action.c
(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "config.h"
+#include "config.h" // IWYU pragma: keep
#include "Action.h"
-#include "Affinity.h"
-#include "AffinityPanel.h"
+
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
#include "CategoriesPanel.h"
+#include "CommandScreen.h"
#include "CRT.h"
#include "EnvScreen.h"
+#include "FunctionBar.h"
+#include "Hashtable.h"
+#include "IncSet.h"
+#include "InfoScreen.h"
+#include "ListItem.h"
+#include "Macros.h"
#include "MainPanel.h"
#include "OpenFilesScreen.h"
#include "Process.h"
+#include "ProcessLocksScreen.h"
+#include "ProvideCurses.h"
#include "ScreenManager.h"
#include "SignalsPanel.h"
-#include "StringUtils.h"
#include "TraceScreen.h"
-#include "Platform.h"
+#include "Vector.h"
+#include "XUtils.h"
+
+#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
+#include "Affinity.h"
+#include "AffinityPanel.h"
+#endif
-#include <ctype.h>
-#include <math.h>
-#include <pwd.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <sys/param.h>
-#include <sys/time.h>
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess) {
Panel* panel = st->panel;
@@ -36,7 +46,7 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
Settings* settings = st->settings;
int y = panel->y;
- ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, false);
+ ScreenManager* scr = ScreenManager_new(header, settings, st, false);
scr->allowFocusChange = false;
ScreenManager_add(scr, list, x - 1);
ScreenManager_add(scr, panel, -1);
@@ -54,59 +64,59 @@ Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess)
}
ScreenManager_delete(scr);
Panel_move(panel, 0, y);
- Panel_resize(panel, COLS, LINES-y-1);
+ Panel_resize(panel, COLS, LINES - y - 1);
if (panelFocus == list && ch == 13) {
if (followProcess) {
Process* selected = (Process*)Panel_getSelected(panel);
if (selected && selected->pid == pid)
return Panel_getSelected(list);
- else
- beep();
+
+ beep();
} else {
return Panel_getSelected(list);
}
-
}
+
return NULL;
}
// ----------------------------------------
-static void Action_runSetup(Settings* settings, const Header* header, ProcessList* pl) {
- ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true);
- CategoriesPanel* panelCategories = CategoriesPanel_new(scr, settings, (Header*) header, pl);
+static void Action_runSetup(State* st) {
+ ScreenManager* scr = ScreenManager_new(st->header, st->settings, st, true);
+ CategoriesPanel* panelCategories = CategoriesPanel_new(scr, st->settings, st->header, st->pl);
ScreenManager_add(scr, (Panel*) panelCategories, 16);
CategoriesPanel_makeMetersPage(panelCategories);
Panel* panelFocus;
int ch;
ScreenManager_run(scr, &panelFocus, &ch);
ScreenManager_delete(scr);
- if (settings->changed) {
- Header_writeBackToSettings(header);
+ if (st->settings->changed) {
+ Header_writeBackToSettings(st->header);
}
}
static bool changePriority(MainPanel* panel, int delta) {
bool anyTagged;
- bool ok = MainPanel_foreachProcess(panel, (MainPanel_ForeachProcessFn) Process_changePriorityBy, (Arg){ .i = delta }, &anyTagged);
+ bool ok = MainPanel_foreachProcess(panel, Process_changePriorityBy, (Arg) { .i = delta }, &anyTagged);
if (!ok)
beep();
return anyTagged;
}
-static void addUserToVector(int key, void* userCast, void* panelCast) {
- char* user = (char*) userCast;
- Panel* panel = (Panel*) panelCast;
+static void addUserToVector(hkey_t key, void* userCast, void* panelCast) {
+ const char* user = userCast;
+ Panel* panel = panelCast;
Panel_add(panel, (Object*) ListItem_new(user, key));
}
bool Action_setUserOnly(const char* userName, uid_t* userId) {
- struct passwd* user = getpwnam(userName);
+ const struct passwd* user = getpwnam(userName);
if (user) {
*userId = user->pw_uid;
return true;
}
- *userId = -1;
+ *userId = (uid_t)-1;
return false;
}
@@ -123,14 +133,18 @@ static void tagAllChildren(Panel* panel, Process* parent) {
static bool expandCollapse(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
- if (!p) return false;
+ if (!p)
+ return false;
+
p->showChildren = !p->showChildren;
return true;
}
static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
- if (!p) return false;
+ if (!p)
+ return false;
+
pid_t ppid = Process_getParentPid(p);
for (int i = 0; i < Panel_size(panel); i++) {
Process* q = (Process*) Panel_get(panel, i);
@@ -146,7 +160,6 @@ static bool collapseIntoParent(Panel* panel) {
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey;
settings->direction = 1;
- settings->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
@@ -160,6 +173,7 @@ static Htop_Reaction sortBy(State* st) {
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey)
Panel_setSelected(sortPanel, i);
+
free(name);
}
ListItem* field = (ListItem*) Action_pickFromVector(st, sortPanel, 15, false);
@@ -167,6 +181,10 @@ static Htop_Reaction sortBy(State* st) {
reaction |= Action_setSortKey(st->settings, field->key);
}
Object_delete(sortPanel);
+
+ if (st->pauseProcessUpdate)
+ ProcessList_sort(st->pl);
+
return reaction | HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
@@ -174,7 +192,7 @@ static Htop_Reaction sortBy(State* st) {
static Htop_Reaction actionResize(State* st) {
clear();
- Panel_resize(st->panel, COLS, LINES-(st->panel->y)-1);
+ Panel_resize(st->panel, COLS, LINES - (st->panel->y) - 1);
return HTOP_REDRAW_BAR;
}
@@ -197,7 +215,6 @@ static Htop_Reaction actionToggleKernelThreads(State* st) {
static Htop_Reaction actionToggleUserlandThreads(State* st) {
st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads;
- st->settings->hideThreads = st->settings->hideUserlandThreads;
return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS;
}
@@ -206,9 +223,17 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
+static Htop_Reaction actionToggleMergedCommand(State* st) {
+ st->settings->showMergedCommand = !st->settings->showMergedCommand;
+ return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
+}
+
static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView;
- if (st->settings->treeView) st->settings->direction = 1;
+ if (st->settings->treeView) {
+ st->settings->direction = 1;
+ }
+
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
@@ -272,20 +297,25 @@ static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
-static Htop_Reaction actionQuit() {
+static Htop_Reaction actionQuit(ATTR_UNUSED State* st) {
return HTOP_QUIT;
}
static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1)
return HTOP_OK;
-#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
+
+#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
Panel* panel = st->panel;
Process* p = (Process*) Panel_getSelected(panel);
- if (!p) return HTOP_OK;
+ if (!p)
+ return HTOP_OK;
+
Affinity* affinity1 = Affinity_get(p, st->pl);
- if (!affinity1) return HTOP_OK;
+ if (!affinity1)
+ return HTOP_OK;
+
int width;
Panel* affinityPanel = AffinityPanel_new(st->pl, affinity1, &width);
width += 1; /* we add a gap between the panels */
@@ -294,24 +324,25 @@ static Htop_Reaction actionSetAffinity(State* st) {
void* set = Action_pickFromVector(st, affinityPanel, width, true);
if (set) {
Affinity* affinity2 = AffinityPanel_getAffinity(affinityPanel, st->pl);
- bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) Affinity_set, (Arg){ .v = affinity2 }, NULL);
- if (!ok) beep();
+ bool ok = MainPanel_foreachProcess((MainPanel*)panel, Affinity_set, (Arg) { .v = affinity2 }, NULL);
+ if (!ok)
+ beep();
Affinity_delete(affinity2);
}
- Panel_delete((Object*)affinityPanel);
+ Object_delete(affinityPanel);
#endif
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
static Htop_Reaction actionKill(State* st) {
- Panel* signalsPanel = (Panel*) SignalsPanel_new();
+ Panel* signalsPanel = SignalsPanel_new();
ListItem* sgn = (ListItem*) Action_pickFromVector(st, signalsPanel, 15, true);
if (sgn) {
if (sgn->key != 0) {
Panel_setHeader(st->panel, "Sending...");
- Panel_draw(st->panel, true);
+ Panel_draw(st->panel, true, true);
refresh();
- MainPanel_foreachProcess((MainPanel*)st->panel, (MainPanel_ForeachProcessFn) Process_sendSignal, (Arg){ .i = sgn->key }, NULL);
+ MainPanel_foreachProcess((MainPanel*)st->panel, Process_sendSignal, (Arg) { .i = sgn->key }, NULL);
napms(500);
}
}
@@ -329,7 +360,7 @@ static Htop_Reaction actionFilterByUser(State* st) {
ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 20, false);
if (picked) {
if (picked == allUsers) {
- st->pl->userId = -1;
+ st->pl->userId = (uid_t)-1;
} else {
Action_setUserOnly(ListItem_getRef(picked), &(st->pl->userId));
}
@@ -345,7 +376,7 @@ Htop_Reaction Action_follow(State* st) {
}
static Htop_Reaction actionSetup(State* st) {
- Action_runSetup(st->settings, st->header, st->pl);
+ Action_runSetup(st);
// TODO: shouldn't need this, colors should be dynamic
int headerHeight = Header_calculateHeight(st->header);
Panel_move(st->panel, 0, headerHeight);
@@ -355,7 +386,9 @@ static Htop_Reaction actionSetup(State* st) {
static Htop_Reaction actionLsof(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
- if (!p) return HTOP_OK;
+ if (!p)
+ return HTOP_OK;
+
OpenFilesScreen* ofs = OpenFilesScreen_new(p);
InfoScreen_run((InfoScreen*)ofs);
OpenFilesScreen_delete((Object*)ofs);
@@ -364,9 +397,22 @@ static Htop_Reaction actionLsof(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
-static Htop_Reaction actionStrace(State* st) {
+static Htop_Reaction actionShowLocks(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
if (!p) return HTOP_OK;
+ ProcessLocksScreen* pls = ProcessLocksScreen_new(p);
+ InfoScreen_run((InfoScreen*)pls);
+ ProcessLocksScreen_delete((Object*)pls);
+ clear();
+ CRT_enableDelay();
+ return HTOP_REFRESH | HTOP_REDRAW_BAR;
+}
+
+static Htop_Reaction actionStrace(State* st) {
+ Process* p = (Process*) Panel_getSelected(st->panel);
+ if (!p)
+ return HTOP_OK;
+
TraceScreen* ts = TraceScreen_new(p);
bool ok = TraceScreen_forkTracer(ts);
if (ok) {
@@ -380,24 +426,36 @@ static Htop_Reaction actionStrace(State* st) {
static Htop_Reaction actionTag(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
- if (!p) return HTOP_OK;
+ if (!p)
+ return HTOP_OK;
+
Process_toggleTag(p);
Panel_onKey(st->panel, KEY_DOWN);
return HTOP_OK;
}
-static Htop_Reaction actionRedraw() {
+static Htop_Reaction actionRedraw(ATTR_UNUSED State* st) {
clear();
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
-static const struct { const char* key; const char* info; } helpLeft[] = {
+static Htop_Reaction actionTogglePauseProcessUpdate(State* st) {
+ st->pauseProcessUpdate = !st->pauseProcessUpdate;
+ return HTOP_REFRESH | HTOP_REDRAW_BAR;
+}
+
+static const struct {
+ const char* key;
+ const char* info;
+} helpLeft[] = {
{ .key = " Arrows: ", .info = "scroll process list" },
{ .key = " Digits: ", .info = "incremental PID search" },
{ .key = " F3 /: ", .info = "incremental name search" },
{ .key = " F4 \\: ",.info = "incremental name filtering" },
{ .key = " F5 t: ", .info = "tree view" },
{ .key = " p: ", .info = "toggle program path" },
+ { .key = " m: ", .info = "toggle merged command" },
+ { .key = " Z: ", .info = "pause/resume process updates" },
{ .key = " u: ", .info = "show processes of a single user" },
{ .key = " H: ", .info = "hide/show user process threads" },
{ .key = " K: ", .info = "hide/show kernel threads" },
@@ -409,42 +467,54 @@ static const struct { const char* key; const char* info; } helpLeft[] = {
{ .key = NULL, .info = NULL }
};
-static const struct { const char* key; const char* info; } helpRight[] = {
+static const struct {
+ const char* key;
+ const char* info;
+} helpRight[] = {
{ .key = " Space: ", .info = "tag process" },
{ .key = " c: ", .info = "tag process and its children" },
{ .key = " U: ", .info = "untag all processes" },
{ .key = " F9 k: ", .info = "kill process/tagged processes" },
{ .key = " F7 ]: ", .info = "higher priority (root only)" },
{ .key = " F8 [: ", .info = "lower priority (+ nice)" },
-#if (HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY)
+#if (defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY))
{ .key = " a: ", .info = "set CPU affinity" },
#endif
{ .key = " e: ", .info = "show process environment" },
{ .key = " i: ", .info = "set IO priority" },
{ .key = " l: ", .info = "list open files with lsof" },
+ { .key = " x: ", .info = "list file locks of process" },
{ .key = " s: ", .info = "trace syscalls with strace" },
- { .key = " ", .info = "" },
+ { .key = " w: ", .info = "wrap process command in multiple lines" },
{ .key = " F2 C S: ", .info = "setup" },
{ .key = " F1 h: ", .info = "show this help screen" },
{ .key = " F10 q: ", .info = "quit" },
{ .key = NULL, .info = NULL }
};
+static inline void addattrstr( int attr, const char* str) {
+ attrset(attr);
+ addstr(str);
+}
+
static Htop_Reaction actionHelp(State* st) {
Settings* settings = st->settings;
clear();
attrset(CRT_colors[HELP_BOLD]);
- for (int i = 0; i < LINES-1; i++)
+ for (int i = 0; i < LINES - 1; i++)
mvhline(i, 0, ' ', COLS);
- mvaddstr(0, 0, "htop " VERSION " - " COPYRIGHT);
- mvaddstr(1, 0, "Released under the GNU GPL. See 'man' page for more info.");
+ int line = 0;
+
+ mvaddstr(line++, 0, "htop " VERSION " - " COPYRIGHT);
+ mvaddstr(line++, 0, "Released under the GNU GPLv2. See 'man' page for more info.");
attrset(CRT_colors[DEFAULT_COLOR]);
- mvaddstr(3, 0, "CPU usage bar: ");
- #define addattrstr(a,s) attrset(a);addstr(s)
+ line++;
+ mvaddstr(line++, 0, "CPU usage bar: ");
+
addattrstr(CRT_colors[BAR_BORDER], "[");
if (settings->detailedCPUTime) {
addattrstr(CRT_colors[CPU_NICE_TEXT], "low"); addstr("/");
@@ -465,7 +535,7 @@ static Htop_Reaction actionHelp(State* st) {
}
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
- mvaddstr(4, 0, "Memory bar: ");
+ mvaddstr(line++, 0, "Memory bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[MEMORY_USED], "used"); addstr("/");
addattrstr(CRT_colors[MEMORY_BUFFERS_TEXT], "buffers"); addstr("/");
@@ -473,29 +543,49 @@ static Htop_Reaction actionHelp(State* st) {
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
- mvaddstr(5, 0, "Swap bar: ");
+ mvaddstr(line++, 0, "Swap bar: ");
addattrstr(CRT_colors[BAR_BORDER], "[");
addattrstr(CRT_colors[SWAP], "used");
addattrstr(CRT_colors[BAR_SHADOW], " used/total");
addattrstr(CRT_colors[BAR_BORDER], "]");
attrset(CRT_colors[DEFAULT_COLOR]);
- mvaddstr(6,0, "Type and layout of header meters are configurable in the setup screen.");
+ mvaddstr(line++, 0, "Type and layout of header meters are configurable in the setup screen.");
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
- mvaddstr(7, 0, "In monochrome, meters display as different chars, in order: |#*@$%&.");
+ mvaddstr(line, 0, "In monochrome, meters display as different chars, in order: |#*@$%&.");
}
- mvaddstr( 8, 0, " Status: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
- for (int i = 0; helpLeft[i].info; i++) { mvaddstr(9+i, 9, helpLeft[i].info); }
- for (int i = 0; helpRight[i].info; i++) { mvaddstr(9+i, 49, helpRight[i].info); }
- attrset(CRT_colors[HELP_BOLD]);
- for (int i = 0; helpLeft[i].key; i++) { mvaddstr(9+i, 0, helpLeft[i].key); }
- for (int i = 0; helpRight[i].key; i++) { mvaddstr(9+i, 40, helpRight[i].key); }
- attrset(CRT_colors[PROCESS_THREAD]);
- mvaddstr(16, 32, "threads");
- mvaddstr(17, 26, "threads");
- attrset(CRT_colors[DEFAULT_COLOR]);
+ line++;
+
+ mvaddstr(line++, 0, "Process state: R: running; S: sleeping; T: traced/stopped; Z: zombie; D: disk sleep");
+
+ line++;
+
+ int item;
+ for (item = 0; helpLeft[item].key; item++) {
+ attrset(CRT_colors[DEFAULT_COLOR]);
+ mvaddstr(line + item, 9, helpLeft[item].info);
+ attrset(CRT_colors[HELP_BOLD]);
+ mvaddstr(line + item, 0, helpLeft[item].key);
+ if (String_eq(helpLeft[item].key, " H: ")) {
+ attrset(CRT_colors[PROCESS_THREAD]);
+ mvaddstr(line + item, 32, "threads");
+ } else if (String_eq(helpLeft[item].key, " K: ")) {
+ attrset(CRT_colors[PROCESS_THREAD]);
+ mvaddstr(line + item, 26, "threads");
+ }
+ }
+ int leftHelpItems = item;
+
+ for (item = 0; helpRight[item].key; item++) {
+ attrset(CRT_colors[HELP_BOLD]);
+ mvaddstr(line + item, 40, helpRight[item].key);
+ attrset(CRT_colors[DEFAULT_COLOR]);
+ mvaddstr(line + item, 49, helpRight[item].info);
+ }
+ line += MAXIMUM(leftHelpItems, item);
+ line++;
attrset(CRT_colors[HELP_BOLD]);
- mvaddstr(23,0, "Press any key to return.");
+ mvaddstr(line++, 0, "Press any key to return.");
attrset(CRT_colors[DEFAULT_COLOR]);
refresh();
CRT_readKey();
@@ -514,14 +604,18 @@ static Htop_Reaction actionUntagAll(State* st) {
static Htop_Reaction actionTagAllChildren(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
- if (!p) return HTOP_OK;
+ if (!p)
+ return HTOP_OK;
+
tagAllChildren(st->panel, p);
return HTOP_OK;
}
static Htop_Reaction actionShowEnvScreen(State* st) {
Process* p = (Process*) Panel_getSelected(st->panel);
- if (!p) return HTOP_OK;
+ if (!p)
+ return HTOP_OK;
+
EnvScreen* es = EnvScreen_new(p);
InfoScreen_run((InfoScreen*)es);
EnvScreen_delete((Object*)es);
@@ -530,6 +624,18 @@ static Htop_Reaction actionShowEnvScreen(State* st) {
return HTOP_REFRESH | HTOP_REDRAW_BAR;
}
+static Htop_Reaction actionShowCommandScreen(State* st) {
+ Process* p = (Process*) Panel_getSelected(st->panel);
+ if (!p)
+ return HTOP_OK;
+
+ CommandScreen* cmdScr = CommandScreen_new(p);
+ InfoScreen_run((InfoScreen*)cmdScr);
+ CommandScreen_delete((Object*)cmdScr);
+ clear();
+ CRT_enableDelay();
+ return HTOP_REFRESH | HTOP_REDRAW_BAR;
+}
void Action_setBindings(Htop_Action* keys) {
keys[KEY_RESIZE] = actionResize;
@@ -539,6 +645,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['H'] = actionToggleUserlandThreads;
keys['K'] = actionToggleKernelThreads;
keys['p'] = actionToggleProgramPath;
+ keys['m'] = actionToggleMergedCommand;
keys['t'] = actionToggleTreeView;
keys[KEY_F(5)] = actionToggleTreeView;
keys[KEY_F(4)] = actionIncFilter;
@@ -553,7 +660,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['['] = actionLowerPriority;
keys[KEY_F(8)] = actionLowerPriority;
keys['I'] = actionInvertSortOrder;
- keys[KEY_F(6)] = actionExpandCollapseOrSortColumn;
+ keys[KEY_F(6)] = actionSetSortColumn;
keys[KEY_F(18)] = actionExpandCollapseOrSortColumn;
keys['<'] = actionSetSortColumn;
keys[','] = actionSetSortColumn;
@@ -574,6 +681,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['S'] = actionSetup;
keys['C'] = actionSetup;
keys[KEY_F(2)] = actionSetup;
+ keys['x'] = actionShowLocks;
keys['l'] = actionLsof;
keys['s'] = actionStrace;
keys[' '] = actionTag;
@@ -584,4 +692,6 @@ void Action_setBindings(Htop_Action* keys) {
keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
+ keys['w'] = actionShowCommandScreen;
+ keys['Z'] = actionTogglePauseProcessUpdate;
}
diff --git a/Action.h b/Action.h
index 0ec0537..1251911 100644
--- a/Action.h
+++ b/Action.h
@@ -3,12 +3,19 @@
/*
htop - Action.h
(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
+#include <stdbool.h>
+#include <sys/types.h>
+
#include "Header.h"
+#include "Object.h"
#include "Panel.h"
+#include "Process.h"
#include "ProcessList.h"
#include "Settings.h"
#include "UsersTable.h"
@@ -24,16 +31,18 @@ typedef enum {
HTOP_UPDATE_PANELHDR = 0x41, // implies HTOP_REFRESH
} Htop_Reaction;
-typedef Htop_Reaction (*Htop_Action)();
-
typedef struct State_ {
Settings* settings;
UsersTable* ut;
ProcessList* pl;
Panel* panel;
Header* header;
+ bool pauseProcessUpdate;
+ bool hideProcessSelection;
} State;
+typedef Htop_Reaction (*Htop_Action)(State* st);
+
Object* Action_pickFromVector(State* st, Panel* list, int x, bool followProcess);
bool Action_setUserOnly(const char* userName, uid_t* userId);
diff --git a/Affinity.c b/Affinity.c
index 3ea45a9..c157885 100644
--- a/Affinity.c
+++ b/Affinity.c
@@ -2,22 +2,27 @@
htop - Affinity.c
(C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "Affinity.h"
#include <stdlib.h>
+#include "XUtils.h"
+
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
-#if __linux__
+#include <hwloc/bitmap.h>
+#ifdef __linux__
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_THREAD
#else
#define HTOP_HWLOC_CPUBIND_FLAG HWLOC_CPUBIND_PROCESS
#endif
-#elif HAVE_LINUX_AFFINITY
+#elif defined(HAVE_LINUX_AFFINITY)
#include <sched.h>
#endif
@@ -60,7 +65,7 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
} else {
unsigned int id;
hwloc_bitmap_foreach_begin(id, cpuset);
- Affinity_add(affinity, id);
+ Affinity_add(affinity, id);
hwloc_bitmap_foreach_end();
}
}
@@ -69,7 +74,7 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl) {
}
bool Affinity_set(Process* proc, Arg arg) {
- Affinity *this = arg.v;
+ Affinity* this = arg.v;
hwloc_cpuset_t cpuset = hwloc_bitmap_alloc();
for (int i = 0; i < this->used; i++) {
hwloc_bitmap_set(cpuset, this->cpus[i]);
@@ -79,22 +84,25 @@ bool Affinity_set(Process* proc, Arg arg) {
return ok;
}
-#elif HAVE_LINUX_AFFINITY
+#elif defined(HAVE_LINUX_AFFINITY)
Affinity* Affinity_get(Process* proc, ProcessList* pl) {
cpu_set_t cpuset;
bool ok = (sched_getaffinity(proc->pid, sizeof(cpu_set_t), &cpuset) == 0);
- if (!ok) return NULL;
+ if (!ok)
+ return NULL;
+
Affinity* affinity = Affinity_new(pl);
for (int i = 0; i < pl->cpuCount; i++) {
- if (CPU_ISSET(i, &cpuset))
+ if (CPU_ISSET(i, &cpuset)) {
Affinity_add(affinity, i);
+ }
}
return affinity;
}
bool Affinity_set(Process* proc, Arg arg) {
- Affinity *this = arg.v;
+ Affinity* this = arg.v;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
for (int i = 0; i < this->used; i++) {
diff --git a/Affinity.h b/Affinity.h
index 20d0047..97c8e46 100644
--- a/Affinity.h
+++ b/Affinity.h
@@ -4,13 +4,27 @@
htop - Affinity.h
(C) 2004-2011 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Process.h"
+#include "config.h" // IWYU pragma: keep
+
#include "ProcessList.h"
+#if defined(HAVE_LIBHWLOC) || defined(HAVE_LINUX_AFFINITY)
+#include <stdbool.h>
+
+#include "Object.h"
+#include "Process.h"
+#endif
+
+
+#if defined(HAVE_LIBHWLOC) && defined(HAVE_LINUX_AFFINITY)
+#error hwloc and linux affinity are mutual exclusive.
+#endif
+
+
typedef struct Affinity_ {
ProcessList* pl;
int size;
@@ -30,6 +44,6 @@ Affinity* Affinity_get(Process* proc, ProcessList* pl);
bool Affinity_set(Process* proc, Arg arg);
-#endif
+#endif /* HAVE_LIBHWLOC || HAVE_LINUX_AFFINITY */
#endif
diff --git a/AffinityPanel.c b/AffinityPanel.c
index d0a9e6d..e491b52 100644
--- a/AffinityPanel.c
+++ b/AffinityPanel.c
@@ -1,29 +1,41 @@
/*
htop - AffinityPanel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "AffinityPanel.h"
-#include "CRT.h"
+#include "config.h" // IWYU pragma: keep
-#include "Vector.h"
+#include "AffinityPanel.h"
#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
#include <string.h>
+#include "CRT.h"
+#include "FunctionBar.h"
+#include "Object.h"
+#include "ProvideCurses.h"
+#include "RichString.h"
+#include "Settings.h"
+#include "Vector.h"
+#include "XUtils.h"
+
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
+#include <hwloc/bitmap.h>
#endif
+
typedef struct MaskItem_ {
Object super;
- const char* text;
- const char* indent; /* used also as an condition whether this is a tree node */
+ char* text;
+ char* indent; /* used also as an condition whether this is a tree node */
int value; /* tri-state: 0 - off, 1 - some set, 2 - all set */
int sub_tree; /* tri-state: 0 - no sub-tree, 1 - open sub-tree, 2 - closed sub-tree */
- Vector *children;
+ Vector* children;
#ifdef HAVE_LIBHWLOC
bool ownCpuset;
hwloc_bitmap_t cpuset;
@@ -34,9 +46,8 @@ typedef struct MaskItem_ {
static void MaskItem_delete(Object* cast) {
MaskItem* this = (MaskItem*) cast;
- free((void*)this->text);
- if (this->indent)
- free((void*)this->indent);
+ free(this->text);
+ free(this->indent);
Vector_delete(this->children);
#ifdef HAVE_LIBHWLOC
if (this->ownCpuset)
@@ -45,16 +56,17 @@ static void MaskItem_delete(Object* cast) {
free(this);
}
-static void MaskItem_display(Object* cast, RichString* out) {
- MaskItem* this = (MaskItem*)cast;
+static void MaskItem_display(const Object* cast, RichString* out) {
+ const MaskItem* this = (const MaskItem*)cast;
assert (this != NULL);
RichString_append(out, CRT_colors[CHECK_BOX], "[");
- if (this->value == 2)
+ if (this->value == 2) {
RichString_append(out, CRT_colors[CHECK_MARK], "x");
- else if (this->value == 1)
+ } else if (this->value == 1) {
RichString_append(out, CRT_colors[CHECK_MARK], "o");
- else
+ } else {
RichString_append(out, CRT_colors[CHECK_MARK], " ");
+ }
RichString_append(out, CRT_colors[CHECK_BOX], "]");
RichString_append(out, CRT_colors[CHECK_TEXT], " ");
if (this->indent) {
@@ -68,7 +80,7 @@ static void MaskItem_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
}
-static ObjectClass MaskItem_class = {
+static const ObjectClass MaskItem_class = {
.display = MaskItem_display,
.delete = MaskItem_delete
};
@@ -100,11 +112,10 @@ static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) {
this->ownCpuset = true;
this->cpuset = hwloc_bitmap_alloc();
hwloc_bitmap_set(this->cpuset, cpu);
- (void)isSet;
#else
this->cpu = cpu;
#endif
- this->value = 2 * isSet;
+ this->value = isSet ? 2 : 0;
return this;
}
@@ -113,11 +124,11 @@ typedef struct AffinityPanel_ {
Panel super;
ProcessList* pl;
bool topoView;
- Vector *cpuids;
+ Vector* cpuids;
unsigned width;
#ifdef HAVE_LIBHWLOC
- MaskItem *topoRoot;
+ MaskItem* topoRoot;
hwloc_const_cpuset_t allCpuset;
hwloc_bitmap_t workCpuset;
#endif
@@ -162,17 +173,18 @@ static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
Panel* super = (Panel*) this;
FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
- FunctionBar_draw(super->currentBar, NULL);
+ FunctionBar_draw(super->currentBar);
int oldSelected = Panel_getSelectedIndex(super);
Panel_prune(super);
#ifdef HAVE_LIBHWLOC
- if (this->topoView)
+ if (this->topoView) {
AffinityPanel_updateTopo(this, this->topoRoot);
- else {
- for (int i = 0; i < Vector_size(this->cpuids); i++)
+ } else {
+ for (int i = 0; i < Vector_size(this->cpuids); i++) {
AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i));
+ }
}
#else
Panel_splice(super, this->cpuids);
@@ -206,7 +218,7 @@ static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) {
selected->value = 2;
}
#else
- selected->value = 2 * !selected->value; /* toggle between 0 and 2 */
+ selected->value = selected->value ? 0 : 2; /* toggle between 0 and 2 */
#endif
result = HANDLED;
@@ -252,7 +264,7 @@ static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) {
#ifdef HAVE_LIBHWLOC
-static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem *parent) {
+static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
const char* type_name = hwloc_obj_type_string(obj->type);
const char* index_prefix = "#";
unsigned depth = obj->depth;
@@ -271,17 +283,20 @@ static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
for (unsigned i = 1; i < depth; i++) {
xSnprintf(&indent_buf[off], left, "%s ", (indent & (1u << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
size_t len = strlen(&indent_buf[off]);
- off += len, left -= len;
+ off += len;
+ left -= len;
}
xSnprintf(&indent_buf[off], left, "%s",
- obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]);
- size_t len = strlen(&indent_buf[off]);
- off += len, left -= len;
+ obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]);
+ // Uncomment when further appending to indent_buf
+ //size_t len = strlen(&indent_buf[off]);
+ //off += len;
+ //left -= len;
}
- xSnprintf(buf, 64, "%s %s%u", type_name, index_prefix, index);
+ xSnprintf(buf, sizeof(buf), "%s %s%u", type_name, index_prefix, index);
- MaskItem *item = MaskItem_newMask(buf, indent_buf, obj->complete_cpuset, false);
+ MaskItem* item = MaskItem_newMask(buf, indent_buf, obj->complete_cpuset, false);
if (parent)
Vector_add(parent->children, item);
@@ -291,34 +306,38 @@ static MaskItem *AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, u
hwloc_bitmap_and(result, obj->complete_cpuset, this->workCpuset);
int weight = hwloc_bitmap_weight(result);
hwloc_bitmap_free(result);
- if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset)))
+ if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset))) {
item->sub_tree = 2;
+ }
}
/* "[x] " + "|- " * depth + ("- ")?(if root node) + name */
unsigned width = 4 + 3 * depth + (2 * !depth) + strlen(buf);
- if (width > this->width)
+ if (width > this->width) {
this->width = width;
+ }
return item;
}
-static MaskItem *AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem *parent) {
- MaskItem *item = AffinityPanel_addObject(this, obj, indent, parent);
+static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
+ MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
if (obj->next_sibling) {
indent |= (1u << obj->depth);
} else {
indent &= ~(1u << obj->depth);
}
- for (unsigned i = 0; i < obj->arity; i++)
+
+ for (unsigned i = 0; i < obj->arity; i++) {
AffinityPanel_buildTopology(this, obj->children[i], indent, item);
+ }
return parent == NULL ? item : NULL;
}
#endif
-PanelClass AffinityPanel_class = {
+const PanelClass AffinityPanel_class = {
.super = {
.extends = Class(Panel),
.delete = AffinityPanel_delete
@@ -369,8 +388,9 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
char number[16];
xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i));
unsigned cpu_width = 4 + strlen(number);
- if (cpu_width > this->width)
+ if (cpu_width > this->width) {
this->width = cpu_width;
+ }
bool isSet = false;
if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
@@ -389,8 +409,9 @@ Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width) {
this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(pl->topology), 0, NULL);
#endif
- if (width)
+ if (width) {
*width = this->width;
+ }
AffinityPanel_update(this, false);
@@ -404,13 +425,14 @@ Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) {
#ifdef HAVE_LIBHWLOC
int i;
hwloc_bitmap_foreach_begin(i, this->workCpuset)
- Affinity_add(affinity, i);
+ Affinity_add(affinity, i);
hwloc_bitmap_foreach_end();
#else
for (int i = 0; i < this->pl->cpuCount; i++) {
MaskItem* item = (MaskItem*)Vector_get(this->cpuids, i);
- if (item->value)
+ if (item->value) {
Affinity_add(affinity, item->cpu);
+ }
}
#endif
diff --git a/AffinityPanel.h b/AffinityPanel.h
index 61e4287..fdefeae 100644
--- a/AffinityPanel.h
+++ b/AffinityPanel.h
@@ -3,7 +3,7 @@
/*
htop - AffinityPanel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -11,7 +11,7 @@ in the source distribution for its full text.
#include "Affinity.h"
#include "ProcessList.h"
-extern PanelClass AffinityPanel_class;
+extern const PanelClass AffinityPanel_class;
Panel* AffinityPanel_new(ProcessList* pl, Affinity* affinity, int* width);
diff --git a/AvailableColumnsPanel.c b/AvailableColumnsPanel.c
index 8aedde2..8945bd2 100644
--- a/AvailableColumnsPanel.c
+++ b/AvailableColumnsPanel.c
@@ -1,20 +1,24 @@
/*
htop - AvailableColumnsPanel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "AvailableColumnsPanel.h"
-#include "Platform.h"
-
-#include "Header.h"
-#include "ColumnsPanel.h"
-#include <assert.h>
-#include <stdlib.h>
#include <ctype.h>
-#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "ColumnsPanel.h"
+#include "FunctionBar.h"
+#include "ListItem.h"
+#include "Object.h"
+#include "Platform.h"
+#include "Process.h"
+#include "ProvideCurses.h"
+#include "XUtils.h"
static const char* const AvailableColumnsFunctions[] = {" ", " ", " ", " ", "Add ", " ", " ", " ", " ", "Done ", NULL};
@@ -28,7 +32,6 @@ static void AvailableColumnsPanel_delete(Object* object) {
static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
AvailableColumnsPanel* this = (AvailableColumnsPanel*) super;
- int key = ((ListItem*) Panel_getSelected(super))->key;
HandlerResult result = IGNORED;
switch(ch) {
@@ -36,6 +39,11 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
case KEY_ENTER:
case KEY_F(5):
{
+ const ListItem* selected = (ListItem*) Panel_getSelected(super);
+ if (!selected)
+ break;
+
+ int key = selected->key;
int at = Panel_getSelectedIndex(this->columns);
Panel_insert(this->columns, at, (Object*) ListItem_new(Process_fields[key].name, key));
Panel_setSelected(this->columns, at+1);
@@ -45,7 +53,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
}
default:
{
- if (ch < 255 && isalpha(ch))
+ if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
result = Panel_selectByTyping(super, ch);
break;
}
@@ -53,7 +61,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) {
return result;
}
-PanelClass AvailableColumnsPanel_class = {
+const PanelClass AvailableColumnsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = AvailableColumnsPanel_delete
diff --git a/AvailableColumnsPanel.h b/AvailableColumnsPanel.h
index 6292805..8672eb9 100644
--- a/AvailableColumnsPanel.h
+++ b/AvailableColumnsPanel.h
@@ -3,7 +3,7 @@
/*
htop - AvailableColumnsPanel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -14,7 +14,7 @@ typedef struct AvailableColumnsPanel_ {
Panel* columns;
} AvailableColumnsPanel;
-extern PanelClass AvailableColumnsPanel_class;
+extern const PanelClass AvailableColumnsPanel_class;
AvailableColumnsPanel* AvailableColumnsPanel_new(Panel* columns);
diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c
index 0533cfb..ce01255 100644
--- a/AvailableMetersPanel.c
+++ b/AvailableMetersPanel.c
@@ -1,20 +1,26 @@
/*
htop - AvailableMetersPanel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "AvailableMetersPanel.h"
-#include "MetersPanel.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdlib.h>
#include "CPUMeter.h"
+#include "FunctionBar.h"
#include "Header.h"
#include "ListItem.h"
+#include "Meter.h"
+#include "MetersPanel.h"
+#include "Object.h"
#include "Platform.h"
-
-#include <assert.h>
-#include <stdlib.h>
+#include "ProvideCurses.h"
+#include "XUtils.h"
static void AvailableMetersPanel_delete(Object* object) {
@@ -24,19 +30,22 @@ static void AvailableMetersPanel_delete(Object* object) {
free(this);
}
-static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, MeterClass* type, int param, int column) {
- Meter* meter = (Meter*) Header_addMeterByClass(header, type, param, column);
+static inline void AvailableMetersPanel_addMeter(Header* header, Panel* panel, const MeterClass* type, int param, int column) {
+ Meter* meter = Header_addMeterByClass(header, type, param, column);
Panel_add(panel, (Object*) Meter_toListItem(meter, false));
Panel_setSelected(panel, Panel_size(panel) - 1);
MetersPanel_setMoving((MetersPanel*)panel, true);
- FunctionBar_draw(panel->currentBar, NULL);
+ FunctionBar_draw(panel->currentBar);
}
static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
AvailableMetersPanel* this = (AvailableMetersPanel*) super;
Header* header = this->header;
- ListItem* selected = (ListItem*) Panel_getSelected(super);
+ const ListItem* selected = (ListItem*) Panel_getSelected(super);
+ if (!selected)
+ return IGNORED;
+
int param = selected->key & 0xff;
int type = selected->key >> 16;
HandlerResult result = IGNORED;
@@ -74,7 +83,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) {
return result;
}
-PanelClass AvailableMetersPanel_class = {
+const PanelClass AvailableMetersPanel_class = {
.super = {
.extends = Class(Panel),
.delete = AvailableMetersPanel_delete
@@ -98,19 +107,19 @@ AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* heade
// Platform_meterTypes[0] should be always (&CPUMeter_class), which we will
// handle separately in the code below.
for (int i = 1; Platform_meterTypes[i]; i++) {
- MeterClass* type = Platform_meterTypes[i];
+ const MeterClass* type = Platform_meterTypes[i];
assert(type != &CPUMeter_class);
const char* label = type->description ? type->description : type->uiName;
Panel_add(super, (Object*) ListItem_new(label, i << 16));
}
// Handle (&CPUMeter_class)
- MeterClass* type = &CPUMeter_class;
+ const MeterClass* type = &CPUMeter_class;
int cpus = pl->cpuCount;
if (cpus > 1) {
Panel_add(super, (Object*) ListItem_new("CPU average", 0));
for (int i = 1; i <= cpus; i++) {
char buffer[50];
- xSnprintf(buffer, 50, "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1));
+ xSnprintf(buffer, sizeof(buffer), "%s %d", type->uiName, Settings_cpuId(this->settings, i - 1));
Panel_add(super, (Object*) ListItem_new(buffer, i));
}
} else {
diff --git a/AvailableMetersPanel.h b/AvailableMetersPanel.h
index ecebb28..f735936 100644
--- a/AvailableMetersPanel.h
+++ b/AvailableMetersPanel.h
@@ -3,14 +3,15 @@
/*
htop - AvailableMetersPanel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Settings.h"
+#include "Header.h"
#include "Panel.h"
-#include "ScreenManager.h"
#include "ProcessList.h"
+#include "ScreenManager.h"
+#include "Settings.h"
typedef struct AvailableMetersPanel_ {
Panel super;
@@ -22,7 +23,7 @@ typedef struct AvailableMetersPanel_ {
Panel* rightPanel;
} AvailableMetersPanel;
-extern PanelClass AvailableMetersPanel_class;
+extern const PanelClass AvailableMetersPanel_class;
AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, Panel* leftMeters, Panel* rightMeters, ScreenManager* scr, ProcessList* pl);
diff --git a/BatteryMeter.c b/BatteryMeter.c
index 859df9d..4836809 100644
--- a/BatteryMeter.c
+++ b/BatteryMeter.c
@@ -1,7 +1,7 @@
/*
htop - BatteryMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
@@ -9,29 +9,27 @@ This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
#include "BatteryMeter.h"
-#include "Battery.h"
-#include "ProcessList.h"
+#include <math.h>
+
#include "CRT.h"
-#include "StringUtils.h"
+#include "Object.h"
#include "Platform.h"
-
-#include <string.h>
-#include <stdlib.h>
+#include "XUtils.h"
-int BatteryMeter_attributes[] = {
+static const int BatteryMeter_attributes[] = {
BATTERY
};
-static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
+static void BatteryMeter_updateValues(Meter* this, char* buffer, size_t len) {
ACPresence isOnAC;
double percent;
- Battery_getData(&percent, &isOnAC);
+ Platform_getBattery(&percent, &isOnAC);
- if (percent == -1) {
- this->values[0] = 0;
- xSnprintf(buffer, len, "n/a");
+ if (isnan(percent)) {
+ this->values[0] = NAN;
+ xSnprintf(buffer, len, "N/A");
return;
}
@@ -55,11 +53,9 @@ static void BatteryMeter_updateValues(Meter * this, char *buffer, int len) {
} else {
xSnprintf(buffer, len, unknownText, percent);
}
-
- return;
}
-MeterClass BatteryMeter_class = {
+const MeterClass BatteryMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
diff --git a/BatteryMeter.h b/BatteryMeter.h
index 658cd3f..b6e8c52 100644
--- a/BatteryMeter.h
+++ b/BatteryMeter.h
@@ -3,7 +3,7 @@
/*
htop - BatteryMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
This meter written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
@@ -17,8 +17,6 @@ typedef enum ACPresence_ {
AC_ERROR
} ACPresence;
-extern int BatteryMeter_attributes[];
-
-extern MeterClass BatteryMeter_class;
+extern const MeterClass BatteryMeter_class;
#endif
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 7110e44..65eb95c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -32,3 +32,14 @@ Feature Requests
Please label Github issues that are feature requests with the [`feature
request`](https://github.com/htop-dev/htop/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22feature+request%22+)
label.
+
+Style Guide
+-----------
+
+To make working with the code easier a set of guidelines have evolved in
+the past that new contributions should try to follow. While they are not set
+in stone and always up for changes should the need arise they still provide
+a first orientation to go by when contributing to this repository.
+
+The details of the coding style as well as what to take care about with your
+contributions can be found in our [style guide](docs/styleguide.md).
diff --git a/CPUMeter.c b/CPUMeter.c
index cb77e71..97fc3f0 100644
--- a/CPUMeter.c
+++ b/CPUMeter.c
@@ -1,67 +1,107 @@
/*
htop - CPUMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "CPUMeter.h"
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "CRT.h"
-#include "Settings.h"
+#include "Object.h"
#include "Platform.h"
+#include "ProcessList.h"
+#include "RichString.h"
+#include "Settings.h"
+#include "XUtils.h"
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-int CPUMeter_attributes[] = {
- CPU_NICE, CPU_NORMAL, CPU_SYSTEM, CPU_IRQ, CPU_SOFTIRQ, CPU_STEAL, CPU_GUEST, CPU_IOWAIT
+static const int CPUMeter_attributes[] = {
+ CPU_NICE,
+ CPU_NORMAL,
+ CPU_SYSTEM,
+ CPU_IRQ,
+ CPU_SOFTIRQ,
+ CPU_STEAL,
+ CPU_GUEST,
+ CPU_IOWAIT
};
+typedef struct CPUMeterData_ {
+ int cpus;
+ Meter** meters;
+} CPUMeterData;
+
static void CPUMeter_init(Meter* this) {
int cpu = this->param;
if (this->pl->cpuCount > 1) {
char caption[10];
- xSnprintf(caption, sizeof(caption), "%-3d", Settings_cpuId(this->pl->settings, cpu - 1));
+ xSnprintf(caption, sizeof(caption), "%3d", Settings_cpuId(this->pl->settings, cpu - 1));
Meter_setCaption(this, caption);
}
if (this->param == 0)
Meter_setCaption(this, "Avg");
}
-static void CPUMeter_updateValues(Meter* this, char* buffer, int size) {
+static void CPUMeter_updateValues(Meter* this, char* buffer, size_t size) {
int cpu = this->param;
if (cpu > this->pl->cpuCount) {
xSnprintf(buffer, size, "absent");
+ for (uint8_t i = 0; i < this->curItems; i++)
+ this->values[i] = 0;
return;
}
memset(this->values, 0, sizeof(double) * CPU_METER_ITEMCOUNT);
+
+ char cpuUsageBuffer[8] = { 0 };
+ char cpuFrequencyBuffer[16] = { 0 };
+ char cpuTemperatureBuffer[16] = { 0 };
+
double percent = Platform_setCPUValues(this, cpu);
+
+ if (this->pl->settings->showCPUUsage) {
+ xSnprintf(cpuUsageBuffer, sizeof(cpuUsageBuffer), "%5.1f%%", percent);
+ }
+
if (this->pl->settings->showCPUFrequency) {
double cpuFrequency = this->values[CPU_METER_FREQUENCY];
- char cpuFrequencyBuffer[16];
- if (cpuFrequency < 0) {
+ if (isnan(cpuFrequency)) {
xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A");
} else {
- xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%.0fMHz", cpuFrequency);
- }
- if (this->pl->settings->showCPUUsage) {
- xSnprintf(buffer, size, "%5.1f%% %s", percent, cpuFrequencyBuffer);
- } else {
- xSnprintf(buffer, size, "%s", cpuFrequencyBuffer);
+ xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%4uMHz", (unsigned)cpuFrequency);
}
- } else if (this->pl->settings->showCPUUsage) {
- xSnprintf(buffer, size, "%5.1f%%", percent);
- } else if (size > 0) {
- buffer[0] = '\0';
}
+
+ #ifdef HAVE_SENSORS_SENSORS_H
+ if (this->pl->settings->showCPUTemperature) {
+ double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
+ if (isnan(cpuTemperature))
+ xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A");
+ else if (this->pl->settings->degreeFahrenheit)
+ xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%3d%sF", (int)(cpuTemperature * 9 / 5 + 32), CRT_degreeSign);
+ else
+ xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%d%sC", (int)cpuTemperature, CRT_degreeSign);
+ }
+ #endif
+
+ xSnprintf(buffer, size, "%s%s%s%s%s",
+ cpuUsageBuffer,
+ (cpuUsageBuffer[0] && (cpuFrequencyBuffer[0] || cpuTemperatureBuffer[0])) ? " " : "",
+ cpuFrequencyBuffer,
+ (cpuFrequencyBuffer[0] && cpuTemperatureBuffer[0]) ? " " : "",
+ cpuTemperatureBuffer);
}
-static void CPUMeter_display(Object* cast, RichString* out) {
+static void CPUMeter_display(const Object* cast, RichString* out) {
char buffer[50];
- Meter* this = (Meter*)cast;
+ const Meter* this = (const Meter*)cast;
RichString_prune(out);
if (this->param > this->pl->cpuCount) {
RichString_append(out, CRT_colors[METER_TEXT], "absent");
@@ -83,12 +123,12 @@ static void CPUMeter_display(Object* cast, RichString* out) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_SOFTIRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "si:");
RichString_append(out, CRT_colors[CPU_SOFTIRQ], buffer);
- if (this->values[CPU_METER_STEAL]) {
+ if (!isnan(this->values[CPU_METER_STEAL])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_STEAL]);
RichString_append(out, CRT_colors[METER_TEXT], "st:");
RichString_append(out, CRT_colors[CPU_STEAL], buffer);
}
- if (this->values[CPU_METER_GUEST]) {
+ if (!isnan(this->values[CPU_METER_GUEST])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_GUEST]);
RichString_append(out, CRT_colors[METER_TEXT], "gu:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
@@ -103,16 +143,33 @@ static void CPUMeter_display(Object* cast, RichString* out) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_NICE]);
RichString_append(out, CRT_colors[METER_TEXT], "low:");
RichString_append(out, CRT_colors[CPU_NICE_TEXT], buffer);
- if (this->values[CPU_METER_IRQ]) {
+ if (!isnan(this->values[CPU_METER_IRQ])) {
xSnprintf(buffer, sizeof(buffer), "%5.1f%% ", this->values[CPU_METER_IRQ]);
RichString_append(out, CRT_colors[METER_TEXT], "vir:");
RichString_append(out, CRT_colors[CPU_GUEST], buffer);
}
}
+
+ #ifdef HAVE_SENSORS_SENSORS_H
+ if (this->pl->settings->showCPUTemperature) {
+ char cpuTemperatureBuffer[10];
+ double cpuTemperature = this->values[CPU_METER_TEMPERATURE];
+ if (isnan(cpuTemperature)) {
+ xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "N/A");
+ } else if (this->pl->settings->degreeFahrenheit) {
+ xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sF", cpuTemperature * 9 / 5 + 32, CRT_degreeSign);
+ } else {
+ xSnprintf(cpuTemperatureBuffer, sizeof(cpuTemperatureBuffer), "%5.1f%sC", cpuTemperature, CRT_degreeSign);
+ }
+ RichString_append(out, CRT_colors[METER_TEXT], "temp:");
+ RichString_append(out, CRT_colors[METER_VALUE], cpuTemperatureBuffer);
+ }
+ #endif
}
static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
- int cpus = this->pl->cpuCount;
+ CPUMeterData* data = this->meterData;
+ int cpus = data->cpus;
switch(Meter_name(this)[0]) {
default:
case 'A': // All
@@ -130,75 +187,119 @@ static void AllCPUsMeter_getRange(Meter* this, int* start, int* count) {
}
}
-static int MapClassnameToColumncount(Meter* this){
- if (strchr(Meter_name(this), '4'))
- return 4;
- else if (strchr(Meter_name(this), '2'))
- return 2;
- else
- return 1;
-}
-
-static void AllCPUsMeter_init(Meter* this) {
+static void CPUMeterCommonInit(Meter* this, int ncol) {
int cpus = this->pl->cpuCount;
- if (!this->drawData)
- this->drawData = xCalloc(cpus, sizeof(Meter*));
- Meter** meters = (Meter**) this->drawData;
+ CPUMeterData* data = this->meterData;
+ if (!data) {
+ data = this->meterData = xMalloc(sizeof(CPUMeterData));
+ data->cpus = cpus;
+ data->meters = xCalloc(cpus, sizeof(Meter*));
+ }
+ Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++) {
if (!meters[i])
- meters[i] = Meter_new(this->pl, start+i+1, (MeterClass*) Class(CPUMeter));
+ meters[i] = Meter_new(this->pl, start + i + 1, (const MeterClass*) Class(CPUMeter));
+
Meter_init(meters[i]);
}
+
if (this->mode == 0)
this->mode = BAR_METERMODE;
+
int h = Meter_modes[this->mode]->h;
- int ncol = MapClassnameToColumncount(this);
- this->h = h * ((count + ncol - 1)/ ncol);
+ this->h = h * ((count + ncol - 1) / ncol);
+}
+
+static void CPUMeterCommonUpdateMode(Meter* this, int mode, int ncol) {
+ CPUMeterData* data = this->meterData;
+ Meter** meters = data->meters;
+ this->mode = mode;
+ int h = Meter_modes[mode]->h;
+ int start, count;
+ AllCPUsMeter_getRange(this, &start, &count);
+ for (int i = 0; i < count; i++) {
+ Meter_setMode(meters[i], mode);
+ }
+ this->h = h * ((count + ncol - 1) / ncol);
}
static void AllCPUsMeter_done(Meter* this) {
- Meter** meters = (Meter**) this->drawData;
+ CPUMeterData* data = this->meterData;
+ Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++)
Meter_delete((Object*)meters[i]);
+ free(data->meters);
+ free(data);
}
-static void AllCPUsMeter_updateMode(Meter* this, int mode) {
- Meter** meters = (Meter**) this->drawData;
- this->mode = mode;
- int h = Meter_modes[mode]->h;
+static void SingleColCPUsMeter_init(Meter* this) {
+ CPUMeterCommonInit(this, 1);
+}
+
+static void SingleColCPUsMeter_updateMode(Meter* this, int mode) {
+ CPUMeterCommonUpdateMode(this, mode, 1);
+}
+
+static void DualColCPUsMeter_init(Meter* this) {
+ CPUMeterCommonInit(this, 2);
+}
+
+static void DualColCPUsMeter_updateMode(Meter* this, int mode) {
+ CPUMeterCommonUpdateMode(this, mode, 2);
+}
+
+static void QuadColCPUsMeter_init(Meter* this) {
+ CPUMeterCommonInit(this, 4);
+}
+
+static void QuadColCPUsMeter_updateMode(Meter* this, int mode) {
+ CPUMeterCommonUpdateMode(this, mode, 4);
+}
+
+static void OctoColCPUsMeter_init(Meter* this) {
+ CPUMeterCommonInit(this, 8);
+}
+
+static void OctoColCPUsMeter_updateMode(Meter* this, int mode) {
+ CPUMeterCommonUpdateMode(this, mode, 8);
+}
+
+static void CPUMeterCommonDraw(Meter* this, int x, int y, int w, int ncol) {
+ CPUMeterData* data = this->meterData;
+ Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
+ int colwidth = (w - ncol) / ncol + 1;
+ int diff = (w - (colwidth * ncol));
+ int nrows = (count + ncol - 1) / ncol;
for (int i = 0; i < count; i++) {
- Meter_setMode(meters[i], mode);
+ int d = (i / nrows) > diff ? diff : (i / nrows); // dynamic spacer
+ int xpos = x + ((i / nrows) * colwidth) + d;
+ int ypos = y + ((i % nrows) * meters[0]->h);
+ meters[i]->draw(meters[i], xpos, ypos, colwidth);
}
- int ncol = MapClassnameToColumncount(this);
- this->h = h * ((count + ncol - 1)/ ncol);
}
static void DualColCPUsMeter_draw(Meter* this, int x, int y, int w) {
- Meter** meters = (Meter**) this->drawData;
- int start, count;
- int pad = this->pl->settings->headerMargin ? 2 : 0;
- AllCPUsMeter_getRange(this, &start, &count);
- int height = (count+1)/2;
- int startY = y;
- for (int i = 0; i < height; i++) {
- meters[i]->draw(meters[i], x, y, (w-pad)/2);
- y += meters[i]->h;
- }
- y = startY;
- for (int i = height; i < count; i++) {
- meters[i]->draw(meters[i], x+(w-1)/2+1+(pad/2), y, (w-pad)/2);
- y += meters[i]->h;
- }
+ CPUMeterCommonDraw(this, x, y, w, 2);
+}
+
+static void QuadColCPUsMeter_draw(Meter* this, int x, int y, int w) {
+ CPUMeterCommonDraw(this, x, y, w, 4);
}
+static void OctoColCPUsMeter_draw(Meter* this, int x, int y, int w) {
+ CPUMeterCommonDraw(this, x, y, w, 8);
+}
+
+
static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) {
- Meter** meters = (Meter**) this->drawData;
+ CPUMeterData* data = this->meterData;
+ Meter** meters = data->meters;
int start, count;
AllCPUsMeter_getRange(this, &start, &count);
for (int i = 0; i < count; i++) {
@@ -207,23 +308,8 @@ static void SingleColCPUsMeter_draw(Meter* this, int x, int y, int w) {
}
}
-static void MultiColCPUsMeter_draw(Meter* this, int x, int y, int w){
- Meter** meters = (Meter**) this->drawData;
- int start, count;
- AllCPUsMeter_getRange(this, &start, &count);
- int ncol = MapClassnameToColumncount(this);
- int colwidth = (w-ncol)/ncol + 1;
- int diff = (w - (colwidth * ncol));
- int nrows = (count + ncol - 1) / ncol;
- for (int i = 0; i < count; i++){
- int d = (i/nrows) > diff ? diff : (i / nrows) ; // dynamic spacer
- int xpos = x + ((i / nrows) * colwidth) + d;
- int ypos = y + ((i % nrows) * meters[0]->h);
- meters[i]->draw(meters[i], xpos, ypos, colwidth);
- }
-}
-MeterClass CPUMeter_class = {
+const MeterClass CPUMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -240,7 +326,7 @@ MeterClass CPUMeter_class = {
.init = CPUMeter_init
};
-MeterClass AllCPUsMeter_class = {
+const MeterClass AllCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -254,12 +340,12 @@ MeterClass AllCPUsMeter_class = {
.description = "CPUs (1/1): all CPUs",
.caption = "CPU",
.draw = SingleColCPUsMeter_draw,
- .init = AllCPUsMeter_init,
- .updateMode = AllCPUsMeter_updateMode,
+ .init = SingleColCPUsMeter_init,
+ .updateMode = SingleColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
-MeterClass AllCPUs2Meter_class = {
+const MeterClass AllCPUs2Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -273,12 +359,12 @@ MeterClass AllCPUs2Meter_class = {
.description = "CPUs (1&2/2): all CPUs in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
- .init = AllCPUsMeter_init,
- .updateMode = AllCPUsMeter_updateMode,
+ .init = DualColCPUsMeter_init,
+ .updateMode = DualColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
-MeterClass LeftCPUsMeter_class = {
+const MeterClass LeftCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -292,12 +378,12 @@ MeterClass LeftCPUsMeter_class = {
.description = "CPUs (1/2): first half of list",
.caption = "CPU",
.draw = SingleColCPUsMeter_draw,
- .init = AllCPUsMeter_init,
- .updateMode = AllCPUsMeter_updateMode,
+ .init = SingleColCPUsMeter_init,
+ .updateMode = SingleColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
-MeterClass RightCPUsMeter_class = {
+const MeterClass RightCPUsMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -311,12 +397,12 @@ MeterClass RightCPUsMeter_class = {
.description = "CPUs (2/2): second half of list",
.caption = "CPU",
.draw = SingleColCPUsMeter_draw,
- .init = AllCPUsMeter_init,
- .updateMode = AllCPUsMeter_updateMode,
+ .init = SingleColCPUsMeter_init,
+ .updateMode = SingleColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
-MeterClass LeftCPUs2Meter_class = {
+const MeterClass LeftCPUs2Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -330,12 +416,12 @@ MeterClass LeftCPUs2Meter_class = {
.description = "CPUs (1&2/4): first half in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
- .init = AllCPUsMeter_init,
- .updateMode = AllCPUsMeter_updateMode,
+ .init = DualColCPUsMeter_init,
+ .updateMode = DualColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
-MeterClass RightCPUs2Meter_class = {
+const MeterClass RightCPUs2Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -349,12 +435,12 @@ MeterClass RightCPUs2Meter_class = {
.description = "CPUs (3&4/4): second half in 2 shorter columns",
.caption = "CPU",
.draw = DualColCPUsMeter_draw,
- .init = AllCPUsMeter_init,
- .updateMode = AllCPUsMeter_updateMode,
+ .init = DualColCPUsMeter_init,
+ .updateMode = DualColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
-MeterClass AllCPUs4Meter_class = {
+const MeterClass AllCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -367,13 +453,13 @@ MeterClass AllCPUs4Meter_class = {
.uiName = "CPUs (1&2&3&4/4)",
.description = "CPUs (1&2&3&4/4): all CPUs in 4 shorter columns",
.caption = "CPU",
- .draw = MultiColCPUsMeter_draw,
- .init = AllCPUsMeter_init,
- .updateMode = AllCPUsMeter_updateMode,
+ .draw = QuadColCPUsMeter_draw,
+ .init = QuadColCPUsMeter_init,
+ .updateMode = QuadColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
-MeterClass LeftCPUs4Meter_class = {
+const MeterClass LeftCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -386,13 +472,13 @@ MeterClass LeftCPUs4Meter_class = {
.uiName = "CPUs (1-4/8)",
.description = "CPUs (1-4/8): first half in 4 shorter columns",
.caption = "CPU",
- .draw = MultiColCPUsMeter_draw,
- .init = AllCPUsMeter_init,
- .updateMode = AllCPUsMeter_updateMode,
+ .draw = QuadColCPUsMeter_draw,
+ .init = QuadColCPUsMeter_init,
+ .updateMode = QuadColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
-MeterClass RightCPUs4Meter_class = {
+const MeterClass RightCPUs4Meter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -405,8 +491,65 @@ MeterClass RightCPUs4Meter_class = {
.uiName = "CPUs (5-8/8)",
.description = "CPUs (5-8/8): second half in 4 shorter columns",
.caption = "CPU",
- .draw = MultiColCPUsMeter_draw,
- .init = AllCPUsMeter_init,
- .updateMode = AllCPUsMeter_updateMode,
+ .draw = QuadColCPUsMeter_draw,
+ .init = QuadColCPUsMeter_init,
+ .updateMode = QuadColCPUsMeter_updateMode,
+ .done = AllCPUsMeter_done
+};
+
+const MeterClass AllCPUs8Meter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = CPUMeter_display
+ },
+ .defaultMode = CUSTOM_METERMODE,
+ .total = 100.0,
+ .attributes = CPUMeter_attributes,
+ .name = "AllCPUs8",
+ .uiName = "CPUs (1-8/8)",
+ .description = "CPUs (1-8/8): all CPUs in 8 shorter columns",
+ .caption = "CPU",
+ .draw = OctoColCPUsMeter_draw,
+ .init = OctoColCPUsMeter_init,
+ .updateMode = OctoColCPUsMeter_updateMode,
+ .done = AllCPUsMeter_done
+};
+
+const MeterClass LeftCPUs8Meter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = CPUMeter_display
+ },
+ .defaultMode = CUSTOM_METERMODE,
+ .total = 100.0,
+ .attributes = CPUMeter_attributes,
+ .name = "LeftCPUs8",
+ .uiName = "CPUs (1-8/16)",
+ .description = "CPUs (1-8/16): first half in 8 shorter columns",
+ .caption = "CPU",
+ .draw = OctoColCPUsMeter_draw,
+ .init = OctoColCPUsMeter_init,
+ .updateMode = OctoColCPUsMeter_updateMode,
+ .done = AllCPUsMeter_done
+};
+
+const MeterClass RightCPUs8Meter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = CPUMeter_display
+ },
+ .defaultMode = CUSTOM_METERMODE,
+ .total = 100.0,
+ .attributes = CPUMeter_attributes,
+ .name = "RightCPUs8",
+ .uiName = "CPUs (9-16/16)",
+ .description = "CPUs (9-16/16): second half in 8 shorter columns",
+ .caption = "CPU",
+ .draw = OctoColCPUsMeter_draw,
+ .init = OctoColCPUsMeter_init,
+ .updateMode = OctoColCPUsMeter_updateMode,
.done = AllCPUsMeter_done
};
diff --git a/CPUMeter.h b/CPUMeter.h
index d66eefb..989451a 100644
--- a/CPUMeter.h
+++ b/CPUMeter.h
@@ -3,7 +3,7 @@
/*
htop - CPUMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -19,29 +19,34 @@ typedef enum {
CPU_METER_GUEST = 6,
CPU_METER_IOWAIT = 7,
CPU_METER_FREQUENCY = 8,
- CPU_METER_ITEMCOUNT = 9, // number of entries in this enum
+ CPU_METER_TEMPERATURE = 9,
+ CPU_METER_ITEMCOUNT = 10, // number of entries in this enum
} CPUMeterValues;
-extern int CPUMeter_attributes[];
+extern const MeterClass CPUMeter_class;
-extern MeterClass CPUMeter_class;
+extern const MeterClass AllCPUsMeter_class;
-extern MeterClass AllCPUsMeter_class;
+extern const MeterClass AllCPUs2Meter_class;
-extern MeterClass AllCPUs2Meter_class;
+extern const MeterClass LeftCPUsMeter_class;
-extern MeterClass LeftCPUsMeter_class;
+extern const MeterClass RightCPUsMeter_class;
-extern MeterClass RightCPUsMeter_class;
+extern const MeterClass LeftCPUs2Meter_class;
-extern MeterClass LeftCPUs2Meter_class;
+extern const MeterClass RightCPUs2Meter_class;
-extern MeterClass RightCPUs2Meter_class;
+extern const MeterClass AllCPUs4Meter_class;
-extern MeterClass AllCPUs4Meter_class;
+extern const MeterClass LeftCPUs4Meter_class;
-extern MeterClass LeftCPUs4Meter_class;
+extern const MeterClass RightCPUs4Meter_class;
-extern MeterClass RightCPUs4Meter_class;
+extern const MeterClass AllCPUs8Meter_class;
+
+extern const MeterClass LeftCPUs8Meter_class;
+
+extern const MeterClass RightCPUs8Meter_class;
#endif
diff --git a/CRT.c b/CRT.c
index 7776c4e..68f6405 100644
--- a/CRT.c
+++ b/CRT.c
@@ -1,45 +1,47 @@
/*
htop - CRT.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "config.h"
-#include "CRT.h"
+#include "config.h" // IWYU pragma: keep
-#include "StringUtils.h"
-#include "RichString.h"
+#include "CRT.h"
-#include <stdio.h>
#include <errno.h>
+#include <langinfo.h>
#include <signal.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <locale.h>
-#include <langinfo.h>
-#if HAVE_SETUID_ENABLED
#include <unistd.h>
-#include <sys/types.h>
+
+#include "ProvideCurses.h"
+#include "XUtils.h"
+
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
#endif
-#define ColorIndex(i,j) ((7-i)*8+j)
+
+#define ColorIndex(i,j) ((7-(i))*8+(j))
#define ColorPair(i,j) COLOR_PAIR(ColorIndex(i,j))
-#define Black COLOR_BLACK
-#define Red COLOR_RED
-#define Green COLOR_GREEN
-#define Yellow COLOR_YELLOW
-#define Blue COLOR_BLUE
+#define Black COLOR_BLACK
+#define Red COLOR_RED
+#define Green COLOR_GREEN
+#define Yellow COLOR_YELLOW
+#define Blue COLOR_BLUE
#define Magenta COLOR_MAGENTA
-#define Cyan COLOR_CYAN
-#define White COLOR_WHITE
+#define Cyan COLOR_CYAN
+#define White COLOR_WHITE
-#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
+#define ColorPairGrayBlack ColorPair(Magenta,Magenta)
#define ColorIndexGrayBlack ColorIndex(Magenta,Magenta)
-const char *CRT_treeStrAscii[TREE_STR_COUNT] = {
+static const char* const CRT_treeStrAscii[TREE_STR_COUNT] = {
"-", // TREE_STR_HORZ
"|", // TREE_STR_VERT
"`", // TREE_STR_RTEE
@@ -51,7 +53,7 @@ const char *CRT_treeStrAscii[TREE_STR_COUNT] = {
#ifdef HAVE_LIBNCURSESW
-const char *CRT_treeStrUtf8[TREE_STR_COUNT] = {
+static const char* const CRT_treeStrUtf8[TREE_STR_COUNT] = {
"\xe2\x94\x80", // TREE_STR_HORZ ─
"\xe2\x94\x82", // TREE_STR_VERT │
"\xe2\x94\x9c", // TREE_STR_RTEE ├
@@ -67,83 +69,113 @@ bool CRT_utf8 = false;
#endif
-const char **CRT_treeStr = CRT_treeStrAscii;
+const char* const* CRT_treeStr = CRT_treeStrAscii;
-static bool CRT_hasColors;
+static const int* CRT_delay;
+
+const char* CRT_degreeSign;
+
+static const char* initDegreeSign(void) {
+#ifdef HAVE_LIBNCURSESW
+ if (CRT_utf8)
+ return "\xc2\xb0";
+
+ static char buffer[4];
+ // this might fail if the current locale does not support wide characters
+ int r = snprintf(buffer, sizeof(buffer), "%lc", 176);
+ if (r > 0)
+ return buffer;
+#endif
-int CRT_delay = 0;
+ return "";
+}
-int* CRT_colors;
+const int* CRT_colors;
int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[COLORSCHEME_DEFAULT] = {
- [RESET_COLOR] = ColorPair(White,Black),
- [DEFAULT_COLOR] = ColorPair(White,Black),
- [FUNCTION_BAR] = ColorPair(Black,Cyan),
- [FUNCTION_KEY] = ColorPair(White,Black),
- [PANEL_HEADER_FOCUS] = ColorPair(Black,Green),
- [PANEL_HEADER_UNFOCUS] = ColorPair(Black,Green),
- [PANEL_SELECTION_FOCUS] = ColorPair(Black,Cyan),
- [PANEL_SELECTION_FOLLOW] = ColorPair(Black,Yellow),
- [PANEL_SELECTION_UNFOCUS] = ColorPair(Black,White),
- [FAILED_SEARCH] = ColorPair(Red,Cyan),
- [UPTIME] = A_BOLD | ColorPair(Cyan,Black),
- [BATTERY] = A_BOLD | ColorPair(Cyan,Black),
- [LARGE_NUMBER] = A_BOLD | ColorPair(Red,Black),
- [METER_TEXT] = ColorPair(Cyan,Black),
- [METER_VALUE] = A_BOLD | ColorPair(Cyan,Black),
- [LED_COLOR] = ColorPair(Green,Black),
- [TASKS_RUNNING] = A_BOLD | ColorPair(Green,Black),
+ [RESET_COLOR] = ColorPair(White, Black),
+ [DEFAULT_COLOR] = ColorPair(White, Black),
+ [FUNCTION_BAR] = ColorPair(Black, Cyan),
+ [FUNCTION_KEY] = ColorPair(White, Black),
+ [PANEL_HEADER_FOCUS] = ColorPair(Black, Green),
+ [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green),
+ [PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan),
+ [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
+ [PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White),
+ [FAILED_SEARCH] = ColorPair(Red, Cyan),
+ [FAILED_READ] = A_BOLD | ColorPair(Red, Black),
+ [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
+ [UPTIME] = A_BOLD | ColorPair(Cyan, Black),
+ [BATTERY] = A_BOLD | ColorPair(Cyan, Black),
+ [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
+ [METER_TEXT] = ColorPair(Cyan, Black),
+ [METER_VALUE] = A_BOLD | ColorPair(Cyan, Black),
+ [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
+ [METER_VALUE_IOREAD] = ColorPair(Green, Black),
+ [METER_VALUE_IOWRITE] = ColorPair(Blue, Black),
+ [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black),
+ [METER_VALUE_OK] = ColorPair(Green, Black),
+ [LED_COLOR] = ColorPair(Green, Black),
+ [TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
[PROCESS] = A_NORMAL,
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
- [PROCESS_TAG] = A_BOLD | ColorPair(Yellow,Black),
- [PROCESS_MEGABYTES] = ColorPair(Cyan,Black),
- [PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan,Black),
- [PROCESS_TREE] = ColorPair(Cyan,Black),
- [PROCESS_R_STATE] = ColorPair(Green,Black),
- [PROCESS_D_STATE] = A_BOLD | ColorPair(Red,Black),
- [PROCESS_HIGH_PRIORITY] = ColorPair(Red,Black),
- [PROCESS_LOW_PRIORITY] = ColorPair(Green,Black),
- [PROCESS_THREAD] = ColorPair(Green,Black),
- [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green,Black),
+ [PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Black),
+ [PROCESS_MEGABYTES] = ColorPair(Cyan, Black),
+ [PROCESS_GIGABYTES] = ColorPair(Green, Black),
+ [PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Black),
+ [PROCESS_TREE] = ColorPair(Cyan, Black),
+ [PROCESS_R_STATE] = ColorPair(Green, Black),
+ [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
+ [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
+ [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
+ [PROCESS_NEW] = ColorPair(Black, Green),
+ [PROCESS_TOMB] = ColorPair(Black, Red),
+ [PROCESS_THREAD] = ColorPair(Green, Black),
+ [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black),
+ [PROCESS_COMM] = ColorPair(Magenta, Black),
+ [PROCESS_THREAD_COMM] = ColorPair(Blue, Black),
[BAR_BORDER] = A_BOLD,
[BAR_SHADOW] = A_BOLD | ColorPairGrayBlack,
- [SWAP] = ColorPair(Red,Black),
- [GRAPH_1] = A_BOLD | ColorPair(Cyan,Black),
- [GRAPH_2] = ColorPair(Cyan,Black),
- [MEMORY_USED] = ColorPair(Green,Black),
- [MEMORY_BUFFERS] = ColorPair(Blue,Black),
- [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue,Black),
- [MEMORY_CACHE] = ColorPair(Yellow,Black),
- [LOAD_AVERAGE_FIFTEEN] = ColorPair(Cyan,Black),
- [LOAD_AVERAGE_FIVE] = A_BOLD | ColorPair(Cyan,Black),
- [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White,Black),
+ [SWAP] = ColorPair(Red, Black),
+ [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black),
+ [GRAPH_2] = ColorPair(Cyan, Black),
+ [MEMORY_USED] = ColorPair(Green, Black),
+ [MEMORY_BUFFERS] = ColorPair(Blue, Black),
+ [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black),
+ [MEMORY_CACHE] = ColorPair(Yellow, Black),
+ [LOAD_AVERAGE_FIFTEEN] = ColorPair(Cyan, Black),
+ [LOAD_AVERAGE_FIVE] = A_BOLD | ColorPair(Cyan, Black),
+ [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Black),
[LOAD] = A_BOLD,
- [HELP_BOLD] = A_BOLD | ColorPair(Cyan,Black),
+ [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black),
[CLOCK] = A_BOLD,
- [CHECK_BOX] = ColorPair(Cyan,Black),
+ [DATE] = A_BOLD,
+ [DATETIME] = A_BOLD,
+ [CHECK_BOX] = ColorPair(Cyan, Black),
[CHECK_MARK] = A_BOLD,
[CHECK_TEXT] = A_NORMAL,
[HOSTNAME] = A_BOLD,
- [CPU_NICE] = ColorPair(Blue,Black),
- [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black),
- [CPU_NORMAL] = ColorPair(Green,Black),
- [CPU_SYSTEM] = ColorPair(Red,Black),
+ [CPU_NICE] = ColorPair(Blue, Black),
+ [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue, Black),
+ [CPU_NORMAL] = ColorPair(Green, Black),
+ [CPU_SYSTEM] = ColorPair(Red, Black),
[CPU_IOWAIT] = A_BOLD | ColorPairGrayBlack,
- [CPU_IRQ] = ColorPair(Yellow,Black),
- [CPU_SOFTIRQ] = ColorPair(Magenta,Black),
- [CPU_STEAL] = ColorPair(Cyan,Black),
- [CPU_GUEST] = ColorPair(Cyan,Black),
- [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan,Black),
- [PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan,Black),
- [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White,Black),
- [ZFS_MFU] = ColorPair(Blue,Black),
- [ZFS_MRU] = ColorPair(Yellow,Black),
- [ZFS_ANON] = ColorPair(Magenta,Black),
- [ZFS_HEADER] = ColorPair(Cyan,Black),
- [ZFS_OTHER] = ColorPair(Magenta,Black),
- [ZFS_COMPRESSED] = ColorPair(Blue,Black),
- [ZFS_RATIO] = ColorPair(Magenta,Black),
+ [CPU_IRQ] = ColorPair(Yellow, Black),
+ [CPU_SOFTIRQ] = ColorPair(Magenta, Black),
+ [CPU_STEAL] = ColorPair(Cyan, Black),
+ [CPU_GUEST] = ColorPair(Cyan, Black),
+ [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Cyan, Black),
+ [PRESSURE_STALL_SIXTY] = A_BOLD | ColorPair(Cyan, Black),
+ [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Black),
+ [ZFS_MFU] = ColorPair(Blue, Black),
+ [ZFS_MRU] = ColorPair(Yellow, Black),
+ [ZFS_ANON] = ColorPair(Magenta, Black),
+ [ZFS_HEADER] = ColorPair(Cyan, Black),
+ [ZFS_OTHER] = ColorPair(Magenta, Black),
+ [ZFS_COMPRESSED] = ColorPair(Blue, Black),
+ [ZFS_RATIO] = ColorPair(Magenta, Black),
+ [ZRAM] = ColorPair(Yellow, Black),
},
[COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL,
@@ -156,25 +188,37 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[PANEL_SELECTION_FOLLOW] = A_REVERSE,
[PANEL_SELECTION_UNFOCUS] = A_BOLD,
[FAILED_SEARCH] = A_REVERSE | A_BOLD,
+ [FAILED_READ] = A_BOLD,
+ [PAUSED] = A_BOLD | A_REVERSE,
[UPTIME] = A_BOLD,
[BATTERY] = A_BOLD,
[LARGE_NUMBER] = A_BOLD,
[METER_TEXT] = A_NORMAL,
[METER_VALUE] = A_BOLD,
+ [METER_VALUE_ERROR] = A_BOLD,
+ [METER_VALUE_IOREAD] = A_NORMAL,
+ [METER_VALUE_IOWRITE] = A_NORMAL,
+ [METER_VALUE_NOTICE] = A_BOLD,
+ [METER_VALUE_OK] = A_NORMAL,
[LED_COLOR] = A_NORMAL,
[TASKS_RUNNING] = A_BOLD,
[PROCESS] = A_NORMAL,
[PROCESS_SHADOW] = A_DIM,
[PROCESS_TAG] = A_BOLD,
[PROCESS_MEGABYTES] = A_BOLD,
+ [PROCESS_GIGABYTES] = A_BOLD,
[PROCESS_BASENAME] = A_BOLD,
[PROCESS_TREE] = A_BOLD,
[PROCESS_R_STATE] = A_BOLD,
[PROCESS_D_STATE] = A_BOLD,
[PROCESS_HIGH_PRIORITY] = A_BOLD,
[PROCESS_LOW_PRIORITY] = A_DIM,
+ [PROCESS_NEW] = A_BOLD,
+ [PROCESS_TOMB] = A_DIM,
[PROCESS_THREAD] = A_BOLD,
[PROCESS_THREAD_BASENAME] = A_REVERSE,
+ [PROCESS_COMM] = A_BOLD,
+ [PROCESS_THREAD_COMM] = A_REVERSE,
[BAR_BORDER] = A_BOLD,
[BAR_SHADOW] = A_DIM,
[SWAP] = A_BOLD,
@@ -190,6 +234,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[LOAD] = A_BOLD,
[HELP_BOLD] = A_BOLD,
[CLOCK] = A_BOLD,
+ [DATE] = A_BOLD,
+ [DATETIME] = A_BOLD,
[CHECK_BOX] = A_BOLD,
[CHECK_MARK] = A_NORMAL,
[CHECK_TEXT] = A_NORMAL,
@@ -201,8 +247,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_IOWAIT] = A_NORMAL,
[CPU_IRQ] = A_BOLD,
[CPU_SOFTIRQ] = A_BOLD,
- [CPU_STEAL] = A_REVERSE,
- [CPU_GUEST] = A_REVERSE,
+ [CPU_STEAL] = A_DIM,
+ [CPU_GUEST] = A_DIM,
[PRESSURE_STALL_THREEHUNDRED] = A_DIM,
[PRESSURE_STALL_SIXTY] = A_NORMAL,
[PRESSURE_STALL_TEN] = A_BOLD,
@@ -213,282 +259,341 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[ZFS_OTHER] = A_DIM,
[ZFS_COMPRESSED] = A_BOLD,
[ZFS_RATIO] = A_BOLD,
+ [ZRAM] = A_NORMAL,
},
[COLORSCHEME_BLACKONWHITE] = {
- [RESET_COLOR] = ColorPair(Black,White),
- [DEFAULT_COLOR] = ColorPair(Black,White),
- [FUNCTION_BAR] = ColorPair(Black,Cyan),
- [FUNCTION_KEY] = ColorPair(Black,White),
- [PANEL_HEADER_FOCUS] = ColorPair(Black,Green),
- [PANEL_HEADER_UNFOCUS] = ColorPair(Black,Green),
- [PANEL_SELECTION_FOCUS] = ColorPair(Black,Cyan),
- [PANEL_SELECTION_FOLLOW] = ColorPair(Black,Yellow),
- [PANEL_SELECTION_UNFOCUS] = ColorPair(Blue,White),
- [FAILED_SEARCH] = ColorPair(Red,Cyan),
- [UPTIME] = ColorPair(Yellow,White),
- [BATTERY] = ColorPair(Yellow,White),
- [LARGE_NUMBER] = ColorPair(Red,White),
- [METER_TEXT] = ColorPair(Blue,White),
- [METER_VALUE] = ColorPair(Black,White),
- [LED_COLOR] = ColorPair(Green,White),
- [TASKS_RUNNING] = ColorPair(Green,White),
- [PROCESS] = ColorPair(Black,White),
- [PROCESS_SHADOW] = A_BOLD | ColorPair(Black,White),
- [PROCESS_TAG] = ColorPair(White,Blue),
- [PROCESS_MEGABYTES] = ColorPair(Blue,White),
- [PROCESS_BASENAME] = ColorPair(Blue,White),
- [PROCESS_TREE] = ColorPair(Green,White),
- [PROCESS_R_STATE] = ColorPair(Green,White),
- [PROCESS_D_STATE] = A_BOLD | ColorPair(Red,White),
- [PROCESS_HIGH_PRIORITY] = ColorPair(Red,White),
- [PROCESS_LOW_PRIORITY] = ColorPair(Green,White),
- [PROCESS_THREAD] = ColorPair(Blue,White),
- [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue,White),
- [BAR_BORDER] = ColorPair(Blue,White),
- [BAR_SHADOW] = ColorPair(Black,White),
- [SWAP] = ColorPair(Red,White),
- [GRAPH_1] = A_BOLD | ColorPair(Blue,White),
- [GRAPH_2] = ColorPair(Blue,White),
- [MEMORY_USED] = ColorPair(Green,White),
- [MEMORY_BUFFERS] = ColorPair(Cyan,White),
- [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan,White),
- [MEMORY_CACHE] = ColorPair(Yellow,White),
- [LOAD_AVERAGE_FIFTEEN] = ColorPair(Black,White),
- [LOAD_AVERAGE_FIVE] = ColorPair(Black,White),
- [LOAD_AVERAGE_ONE] = ColorPair(Black,White),
- [LOAD] = ColorPair(Black,White),
- [HELP_BOLD] = ColorPair(Blue,White),
- [CLOCK] = ColorPair(Black,White),
- [CHECK_BOX] = ColorPair(Blue,White),
- [CHECK_MARK] = ColorPair(Black,White),
- [CHECK_TEXT] = ColorPair(Black,White),
- [HOSTNAME] = ColorPair(Black,White),
- [CPU_NICE] = ColorPair(Cyan,White),
- [CPU_NICE_TEXT] = ColorPair(Cyan,White),
- [CPU_NORMAL] = ColorPair(Green,White),
- [CPU_SYSTEM] = ColorPair(Red,White),
- [CPU_IOWAIT] = A_BOLD | ColorPair(Black,White),
- [CPU_IRQ] = ColorPair(Blue,White),
- [CPU_SOFTIRQ] = ColorPair(Blue,White),
- [CPU_STEAL] = ColorPair(Cyan,White),
- [CPU_GUEST] = ColorPair(Cyan,White),
- [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black,White),
- [PRESSURE_STALL_SIXTY] = ColorPair(Black,White),
- [PRESSURE_STALL_TEN] = ColorPair(Black,White),
- [ZFS_MFU] = ColorPair(Cyan,White),
- [ZFS_MRU] = ColorPair(Yellow,White),
- [ZFS_ANON] = ColorPair(Magenta,White),
- [ZFS_HEADER] = ColorPair(Yellow,White),
- [ZFS_OTHER] = ColorPair(Magenta,White),
- [ZFS_COMPRESSED] = ColorPair(Cyan,White),
- [ZFS_RATIO] = ColorPair(Magenta,White),
+ [RESET_COLOR] = ColorPair(Black, White),
+ [DEFAULT_COLOR] = ColorPair(Black, White),
+ [FUNCTION_BAR] = ColorPair(Black, Cyan),
+ [FUNCTION_KEY] = ColorPair(Black, White),
+ [PANEL_HEADER_FOCUS] = ColorPair(Black, Green),
+ [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green),
+ [PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan),
+ [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
+ [PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, White),
+ [FAILED_SEARCH] = ColorPair(Red, Cyan),
+ [FAILED_READ] = ColorPair(Red, White),
+ [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
+ [UPTIME] = ColorPair(Yellow, White),
+ [BATTERY] = ColorPair(Yellow, White),
+ [LARGE_NUMBER] = ColorPair(Red, White),
+ [METER_TEXT] = ColorPair(Blue, White),
+ [METER_VALUE] = ColorPair(Black, White),
+ [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, White),
+ [METER_VALUE_IOREAD] = ColorPair(Green, White),
+ [METER_VALUE_IOWRITE] = ColorPair(Yellow, White),
+ [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, White),
+ [METER_VALUE_OK] = ColorPair(Green, White),
+ [LED_COLOR] = ColorPair(Green, White),
+ [TASKS_RUNNING] = ColorPair(Green, White),
+ [PROCESS] = ColorPair(Black, White),
+ [PROCESS_SHADOW] = A_BOLD | ColorPair(Black, White),
+ [PROCESS_TAG] = ColorPair(White, Blue),
+ [PROCESS_MEGABYTES] = ColorPair(Blue, White),
+ [PROCESS_GIGABYTES] = ColorPair(Green, White),
+ [PROCESS_BASENAME] = ColorPair(Blue, White),
+ [PROCESS_TREE] = ColorPair(Green, White),
+ [PROCESS_R_STATE] = ColorPair(Green, White),
+ [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, White),
+ [PROCESS_HIGH_PRIORITY] = ColorPair(Red, White),
+ [PROCESS_LOW_PRIORITY] = ColorPair(Green, White),
+ [PROCESS_NEW] = ColorPair(White, Green),
+ [PROCESS_TOMB] = ColorPair(White, Red),
+ [PROCESS_THREAD] = ColorPair(Blue, White),
+ [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, White),
+ [PROCESS_COMM] = ColorPair(Magenta, White),
+ [PROCESS_THREAD_COMM] = ColorPair(Green, White),
+ [BAR_BORDER] = ColorPair(Blue, White),
+ [BAR_SHADOW] = ColorPair(Black, White),
+ [SWAP] = ColorPair(Red, White),
+ [GRAPH_1] = A_BOLD | ColorPair(Blue, White),
+ [GRAPH_2] = ColorPair(Blue, White),
+ [MEMORY_USED] = ColorPair(Green, White),
+ [MEMORY_BUFFERS] = ColorPair(Cyan, White),
+ [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, White),
+ [MEMORY_CACHE] = ColorPair(Yellow, White),
+ [LOAD_AVERAGE_FIFTEEN] = ColorPair(Black, White),
+ [LOAD_AVERAGE_FIVE] = ColorPair(Black, White),
+ [LOAD_AVERAGE_ONE] = ColorPair(Black, White),
+ [LOAD] = ColorPair(Black, White),
+ [HELP_BOLD] = ColorPair(Blue, White),
+ [CLOCK] = ColorPair(Black, White),
+ [DATE] = ColorPair(Black, White),
+ [DATETIME] = ColorPair(Black, White),
+ [CHECK_BOX] = ColorPair(Blue, White),
+ [CHECK_MARK] = ColorPair(Black, White),
+ [CHECK_TEXT] = ColorPair(Black, White),
+ [HOSTNAME] = ColorPair(Black, White),
+ [CPU_NICE] = ColorPair(Cyan, White),
+ [CPU_NICE_TEXT] = ColorPair(Cyan, White),
+ [CPU_NORMAL] = ColorPair(Green, White),
+ [CPU_SYSTEM] = ColorPair(Red, White),
+ [CPU_IOWAIT] = A_BOLD | ColorPair(Black, White),
+ [CPU_IRQ] = ColorPair(Blue, White),
+ [CPU_SOFTIRQ] = ColorPair(Blue, White),
+ [CPU_STEAL] = ColorPair(Cyan, White),
+ [CPU_GUEST] = ColorPair(Cyan, White),
+ [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, White),
+ [PRESSURE_STALL_SIXTY] = ColorPair(Black, White),
+ [PRESSURE_STALL_TEN] = ColorPair(Black, White),
+ [ZFS_MFU] = ColorPair(Cyan, White),
+ [ZFS_MRU] = ColorPair(Yellow, White),
+ [ZFS_ANON] = ColorPair(Magenta, White),
+ [ZFS_HEADER] = ColorPair(Yellow, White),
+ [ZFS_OTHER] = ColorPair(Magenta, White),
+ [ZFS_COMPRESSED] = ColorPair(Cyan, White),
+ [ZFS_RATIO] = ColorPair(Magenta, White),
+ [ZRAM] = ColorPair(Yellow, White)
},
[COLORSCHEME_LIGHTTERMINAL] = {
- [RESET_COLOR] = ColorPair(Black,Black),
- [DEFAULT_COLOR] = ColorPair(Black,Black),
- [FUNCTION_BAR] = ColorPair(Black,Cyan),
- [FUNCTION_KEY] = ColorPair(Black,Black),
- [PANEL_HEADER_FOCUS] = ColorPair(Black,Green),
- [PANEL_HEADER_UNFOCUS] = ColorPair(Black,Green),
- [PANEL_SELECTION_FOCUS] = ColorPair(Black,Cyan),
- [PANEL_SELECTION_FOLLOW] = ColorPair(Black,Yellow),
- [PANEL_SELECTION_UNFOCUS] = ColorPair(Blue,Black),
- [FAILED_SEARCH] = ColorPair(Red,Cyan),
- [UPTIME] = ColorPair(Yellow,Black),
- [BATTERY] = ColorPair(Yellow,Black),
- [LARGE_NUMBER] = ColorPair(Red,Black),
- [METER_TEXT] = ColorPair(Blue,Black),
- [METER_VALUE] = ColorPair(Black,Black),
- [LED_COLOR] = ColorPair(Green,Black),
- [TASKS_RUNNING] = ColorPair(Green,Black),
- [PROCESS] = ColorPair(Black,Black),
+ [RESET_COLOR] = ColorPair(Blue, Black),
+ [DEFAULT_COLOR] = ColorPair(Blue, Black),
+ [FUNCTION_BAR] = ColorPair(Black, Cyan),
+ [FUNCTION_KEY] = ColorPair(Blue, Black),
+ [PANEL_HEADER_FOCUS] = ColorPair(Black, Green),
+ [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green),
+ [PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan),
+ [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
+ [PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, Black),
+ [FAILED_SEARCH] = ColorPair(Red, Cyan),
+ [FAILED_READ] = ColorPair(Red, Black),
+ [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
+ [UPTIME] = ColorPair(Yellow, Black),
+ [BATTERY] = ColorPair(Yellow, Black),
+ [LARGE_NUMBER] = ColorPair(Red, Black),
+ [METER_TEXT] = ColorPair(Blue, Black),
+ [METER_VALUE] = ColorPair(Blue, Black),
+ [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
+ [METER_VALUE_IOREAD] = ColorPair(Green, Black),
+ [METER_VALUE_IOWRITE] = ColorPair(Yellow, Black),
+ [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, Black),
+ [METER_VALUE_OK] = ColorPair(Green, Black),
+ [LED_COLOR] = ColorPair(Green, Black),
+ [TASKS_RUNNING] = ColorPair(Green, Black),
+ [PROCESS] = ColorPair(Blue, Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
- [PROCESS_TAG] = ColorPair(White,Blue),
- [PROCESS_MEGABYTES] = ColorPair(Blue,Black),
- [PROCESS_BASENAME] = ColorPair(Green,Black),
- [PROCESS_TREE] = ColorPair(Blue,Black),
- [PROCESS_R_STATE] = ColorPair(Green,Black),
- [PROCESS_D_STATE] = A_BOLD | ColorPair(Red,Black),
- [PROCESS_HIGH_PRIORITY] = ColorPair(Red,Black),
- [PROCESS_LOW_PRIORITY] = ColorPair(Green,Black),
- [PROCESS_THREAD] = ColorPair(Blue,Black),
- [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue,Black),
- [BAR_BORDER] = ColorPair(Blue,Black),
+ [PROCESS_TAG] = ColorPair(Yellow, Blue),
+ [PROCESS_MEGABYTES] = ColorPair(Blue, Black),
+ [PROCESS_GIGABYTES] = ColorPair(Green, Black),
+ [PROCESS_BASENAME] = ColorPair(Green, Black),
+ [PROCESS_TREE] = ColorPair(Blue, Black),
+ [PROCESS_R_STATE] = ColorPair(Green, Black),
+ [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
+ [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
+ [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
+ [PROCESS_NEW] = ColorPair(Black, Green),
+ [PROCESS_TOMB] = ColorPair(Black, Red),
+ [PROCESS_THREAD] = ColorPair(Blue, Black),
+ [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
+ [PROCESS_COMM] = ColorPair(Magenta, Black),
+ [PROCESS_THREAD_COMM] = ColorPair(Yellow, Black),
+ [BAR_BORDER] = ColorPair(Blue, Black),
[BAR_SHADOW] = ColorPairGrayBlack,
- [SWAP] = ColorPair(Red,Black),
- [GRAPH_1] = A_BOLD | ColorPair(Cyan,Black),
- [GRAPH_2] = ColorPair(Cyan,Black),
- [MEMORY_USED] = ColorPair(Green,Black),
- [MEMORY_BUFFERS] = ColorPair(Cyan,Black),
- [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan,Black),
- [MEMORY_CACHE] = ColorPair(Yellow,Black),
- [LOAD_AVERAGE_FIFTEEN] = ColorPair(Black,Black),
- [LOAD_AVERAGE_FIVE] = ColorPair(Black,Black),
- [LOAD_AVERAGE_ONE] = ColorPair(Black,Black),
- [LOAD] = ColorPair(White,Black),
- [HELP_BOLD] = ColorPair(Blue,Black),
- [CLOCK] = ColorPair(White,Black),
- [CHECK_BOX] = ColorPair(Blue,Black),
- [CHECK_MARK] = ColorPair(Black,Black),
- [CHECK_TEXT] = ColorPair(Black,Black),
- [HOSTNAME] = ColorPair(White,Black),
- [CPU_NICE] = ColorPair(Cyan,Black),
- [CPU_NICE_TEXT] = ColorPair(Cyan,Black),
- [CPU_NORMAL] = ColorPair(Green,Black),
- [CPU_SYSTEM] = ColorPair(Red,Black),
- [CPU_IOWAIT] = A_BOLD | ColorPair(Black,Black),
- [CPU_IRQ] = A_BOLD | ColorPair(Blue,Black),
- [CPU_SOFTIRQ] = ColorPair(Blue,Black),
- [CPU_STEAL] = ColorPair(Black,Black),
- [CPU_GUEST] = ColorPair(Black,Black),
- [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black,Black),
- [PRESSURE_STALL_SIXTY] = ColorPair(Black,Black),
- [PRESSURE_STALL_TEN] = ColorPair(Black,Black),
- [ZFS_MFU] = ColorPair(Cyan,Black),
- [ZFS_MRU] = ColorPair(Yellow,Black),
- [ZFS_ANON] = A_BOLD | ColorPair(Magenta,Black),
- [ZFS_HEADER] = ColorPair(Black,Black),
- [ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Black),
- [ZFS_COMPRESSED] = ColorPair(Cyan,Black),
- [ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Black),
+ [SWAP] = ColorPair(Red, Black),
+ [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black),
+ [GRAPH_2] = ColorPair(Cyan, Black),
+ [MEMORY_USED] = ColorPair(Green, Black),
+ [MEMORY_BUFFERS] = ColorPair(Cyan, Black),
+ [MEMORY_BUFFERS_TEXT] = ColorPair(Cyan, Black),
+ [MEMORY_CACHE] = ColorPair(Yellow, Black),
+ [LOAD_AVERAGE_FIFTEEN] = ColorPair(Blue, Black),
+ [LOAD_AVERAGE_FIVE] = ColorPair(Blue, Black),
+ [LOAD_AVERAGE_ONE] = ColorPair(Yellow, Black),
+ [LOAD] = ColorPair(Yellow, Black),
+ [HELP_BOLD] = ColorPair(Blue, Black),
+ [CLOCK] = ColorPair(Yellow, Black),
+ [DATE] = ColorPair(White, Black),
+ [DATETIME] = ColorPair(White, Black),
+ [CHECK_BOX] = ColorPair(Blue, Black),
+ [CHECK_MARK] = ColorPair(Blue, Black),
+ [CHECK_TEXT] = ColorPair(Blue, Black),
+ [HOSTNAME] = ColorPair(Yellow, Black),
+ [CPU_NICE] = ColorPair(Cyan, Black),
+ [CPU_NICE_TEXT] = ColorPair(Cyan, Black),
+ [CPU_NORMAL] = ColorPair(Green, Black),
+ [CPU_SYSTEM] = ColorPair(Red, Black),
+ [CPU_IOWAIT] = A_BOLD | ColorPair(Blue, Black),
+ [CPU_IRQ] = A_BOLD | ColorPair(Blue, Black),
+ [CPU_SOFTIRQ] = ColorPair(Blue, Black),
+ [CPU_STEAL] = ColorPair(Blue, Black),
+ [CPU_GUEST] = ColorPair(Blue, Black),
+ [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Blue, Black),
+ [PRESSURE_STALL_SIXTY] = ColorPair(Blue, Black),
+ [PRESSURE_STALL_TEN] = ColorPair(Blue, Black),
+ [ZFS_MFU] = ColorPair(Cyan, Black),
+ [ZFS_MRU] = ColorPair(Yellow, Black),
+ [ZFS_ANON] = A_BOLD | ColorPair(Magenta, Black),
+ [ZFS_HEADER] = ColorPair(Blue, Black),
+ [ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Black),
+ [ZFS_COMPRESSED] = ColorPair(Cyan, Black),
+ [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Black),
+ [ZRAM] = ColorPair(Yellow, Black),
},
[COLORSCHEME_MIDNIGHT] = {
- [RESET_COLOR] = ColorPair(White,Blue),
- [DEFAULT_COLOR] = ColorPair(White,Blue),
- [FUNCTION_BAR] = ColorPair(Black,Cyan),
+ [RESET_COLOR] = ColorPair(White, Blue),
+ [DEFAULT_COLOR] = ColorPair(White, Blue),
+ [FUNCTION_BAR] = ColorPair(Black, Cyan),
[FUNCTION_KEY] = A_NORMAL,
- [PANEL_HEADER_FOCUS] = ColorPair(Black,Cyan),
- [PANEL_HEADER_UNFOCUS] = ColorPair(Black,Cyan),
- [PANEL_SELECTION_FOCUS] = ColorPair(Black,White),
- [PANEL_SELECTION_FOLLOW] = ColorPair(Black,Yellow),
- [PANEL_SELECTION_UNFOCUS] = A_BOLD | ColorPair(Yellow,Blue),
- [FAILED_SEARCH] = ColorPair(Red,Cyan),
- [UPTIME] = A_BOLD | ColorPair(Yellow,Blue),
- [BATTERY] = A_BOLD | ColorPair(Yellow,Blue),
- [LARGE_NUMBER] = A_BOLD | ColorPair(Red,Blue),
- [METER_TEXT] = ColorPair(Cyan,Blue),
- [METER_VALUE] = A_BOLD | ColorPair(Cyan,Blue),
- [LED_COLOR] = ColorPair(Green,Blue),
- [TASKS_RUNNING] = A_BOLD | ColorPair(Green,Blue),
- [PROCESS] = ColorPair(White,Blue),
- [PROCESS_SHADOW] = A_BOLD | ColorPair(Black,Blue),
- [PROCESS_TAG] = A_BOLD | ColorPair(Yellow,Blue),
- [PROCESS_MEGABYTES] = ColorPair(Cyan,Blue),
- [PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan,Blue),
- [PROCESS_TREE] = ColorPair(Cyan,Blue),
- [PROCESS_R_STATE] = ColorPair(Green,Blue),
- [PROCESS_D_STATE] = A_BOLD | ColorPair(Red,Blue),
- [PROCESS_HIGH_PRIORITY] = ColorPair(Red,Blue),
- [PROCESS_LOW_PRIORITY] = ColorPair(Green,Blue),
- [PROCESS_THREAD] = ColorPair(Green,Blue),
- [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green,Blue),
- [BAR_BORDER] = A_BOLD | ColorPair(Yellow,Blue),
- [BAR_SHADOW] = ColorPair(Cyan,Blue),
- [SWAP] = ColorPair(Red,Blue),
- [GRAPH_1] = A_BOLD | ColorPair(Cyan,Blue),
- [GRAPH_2] = ColorPair(Cyan,Blue),
- [MEMORY_USED] = A_BOLD | ColorPair(Green,Blue),
- [MEMORY_BUFFERS] = A_BOLD | ColorPair(Cyan,Blue),
- [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Cyan,Blue),
- [MEMORY_CACHE] = A_BOLD | ColorPair(Yellow,Blue),
- [LOAD_AVERAGE_FIFTEEN] = A_BOLD | ColorPair(Black,Blue),
- [LOAD_AVERAGE_FIVE] = A_NORMAL | ColorPair(White,Blue),
- [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White,Blue),
- [LOAD] = A_BOLD | ColorPair(White,Blue),
- [HELP_BOLD] = A_BOLD | ColorPair(Cyan,Blue),
- [CLOCK] = ColorPair(White,Blue),
- [CHECK_BOX] = ColorPair(Cyan,Blue),
- [CHECK_MARK] = A_BOLD | ColorPair(White,Blue),
- [CHECK_TEXT] = A_NORMAL | ColorPair(White,Blue),
- [HOSTNAME] = ColorPair(White,Blue),
- [CPU_NICE] = A_BOLD | ColorPair(Cyan,Blue),
- [CPU_NICE_TEXT] = A_BOLD | ColorPair(Cyan,Blue),
- [CPU_NORMAL] = A_BOLD | ColorPair(Green,Blue),
- [CPU_SYSTEM] = A_BOLD | ColorPair(Red,Blue),
- [CPU_IOWAIT] = A_BOLD | ColorPair(Blue,Blue),
- [CPU_IRQ] = A_BOLD | ColorPair(Black,Blue),
- [CPU_SOFTIRQ] = ColorPair(Black,Blue),
- [CPU_STEAL] = ColorPair(White,Blue),
- [CPU_GUEST] = ColorPair(White,Blue),
- [PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black,Blue),
- [PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White,Blue),
- [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White,Blue),
- [ZFS_MFU] = A_BOLD | ColorPair(White,Blue),
- [ZFS_MRU] = A_BOLD | ColorPair(Yellow,Blue),
- [ZFS_ANON] = A_BOLD | ColorPair(Magenta,Blue),
- [ZFS_HEADER] = A_BOLD | ColorPair(Yellow,Blue),
- [ZFS_OTHER] = A_BOLD | ColorPair(Magenta,Blue),
- [ZFS_COMPRESSED] = A_BOLD | ColorPair(White,Blue),
- [ZFS_RATIO] = A_BOLD | ColorPair(Magenta,Blue),
+ [PANEL_HEADER_FOCUS] = ColorPair(Black, Cyan),
+ [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Cyan),
+ [PANEL_SELECTION_FOCUS] = ColorPair(Black, White),
+ [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
+ [PANEL_SELECTION_UNFOCUS] = A_BOLD | ColorPair(Yellow, Blue),
+ [FAILED_SEARCH] = ColorPair(Red, Cyan),
+ [FAILED_READ] = A_BOLD | ColorPair(Red, Blue),
+ [PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
+ [UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
+ [BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
+ [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Blue),
+ [METER_TEXT] = ColorPair(Cyan, Blue),
+ [METER_VALUE] = A_BOLD | ColorPair(Cyan, Blue),
+ [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Blue),
+ [METER_VALUE_IOREAD] = ColorPair(Green, Blue),
+ [METER_VALUE_IOWRITE] = ColorPair(Black, Blue),
+ [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Blue),
+ [METER_VALUE_OK] = ColorPair(Green, Blue),
+ [LED_COLOR] = ColorPair(Green, Blue),
+ [TASKS_RUNNING] = A_BOLD | ColorPair(Green, Blue),
+ [PROCESS] = ColorPair(White, Blue),
+ [PROCESS_SHADOW] = A_BOLD | ColorPair(Black, Blue),
+ [PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Blue),
+ [PROCESS_MEGABYTES] = ColorPair(Cyan, Blue),
+ [PROCESS_GIGABYTES] = ColorPair(Green, Blue),
+ [PROCESS_BASENAME] = A_BOLD | ColorPair(Cyan, Blue),
+ [PROCESS_TREE] = ColorPair(Cyan, Blue),
+ [PROCESS_R_STATE] = ColorPair(Green, Blue),
+ [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Blue),
+ [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Blue),
+ [PROCESS_LOW_PRIORITY] = ColorPair(Green, Blue),
+ [PROCESS_NEW] = ColorPair(Blue, Green),
+ [PROCESS_TOMB] = ColorPair(Blue, Red),
+ [PROCESS_THREAD] = ColorPair(Green, Blue),
+ [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Blue),
+ [PROCESS_COMM] = ColorPair(Magenta, Blue),
+ [PROCESS_THREAD_COMM] = ColorPair(Black, Blue),
+ [BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue),
+ [BAR_SHADOW] = ColorPair(Cyan, Blue),
+ [SWAP] = ColorPair(Red, Blue),
+ [GRAPH_1] = A_BOLD | ColorPair(Cyan, Blue),
+ [GRAPH_2] = ColorPair(Cyan, Blue),
+ [MEMORY_USED] = A_BOLD | ColorPair(Green, Blue),
+ [MEMORY_BUFFERS] = A_BOLD | ColorPair(Cyan, Blue),
+ [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Cyan, Blue),
+ [MEMORY_CACHE] = A_BOLD | ColorPair(Yellow, Blue),
+ [LOAD_AVERAGE_FIFTEEN] = A_BOLD | ColorPair(Black, Blue),
+ [LOAD_AVERAGE_FIVE] = A_NORMAL | ColorPair(White, Blue),
+ [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Blue),
+ [LOAD] = A_BOLD | ColorPair(White, Blue),
+ [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Blue),
+ [CLOCK] = ColorPair(White, Blue),
+ [DATE] = ColorPair(White, Blue),
+ [DATETIME] = ColorPair(White, Blue),
+ [CHECK_BOX] = ColorPair(Cyan, Blue),
+ [CHECK_MARK] = A_BOLD | ColorPair(White, Blue),
+ [CHECK_TEXT] = A_NORMAL | ColorPair(White, Blue),
+ [HOSTNAME] = ColorPair(White, Blue),
+ [CPU_NICE] = A_BOLD | ColorPair(Cyan, Blue),
+ [CPU_NICE_TEXT] = A_BOLD | ColorPair(Cyan, Blue),
+ [CPU_NORMAL] = A_BOLD | ColorPair(Green, Blue),
+ [CPU_SYSTEM] = A_BOLD | ColorPair(Red, Blue),
+ [CPU_IOWAIT] = A_BOLD | ColorPair(Black, Blue),
+ [CPU_IRQ] = A_BOLD | ColorPair(Black, Blue),
+ [CPU_SOFTIRQ] = ColorPair(Black, Blue),
+ [CPU_STEAL] = ColorPair(White, Blue),
+ [CPU_GUEST] = ColorPair(White, Blue),
+ [PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black, Blue),
+ [PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White, Blue),
+ [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Blue),
+ [ZFS_MFU] = A_BOLD | ColorPair(White, Blue),
+ [ZFS_MRU] = A_BOLD | ColorPair(Yellow, Blue),
+ [ZFS_ANON] = A_BOLD | ColorPair(Magenta, Blue),
+ [ZFS_HEADER] = A_BOLD | ColorPair(Yellow, Blue),
+ [ZFS_OTHER] = A_BOLD | ColorPair(Magenta, Blue),
+ [ZFS_COMPRESSED] = A_BOLD | ColorPair(White, Blue),
+ [ZFS_RATIO] = A_BOLD | ColorPair(Magenta, Blue),
+ [ZRAM] = A_BOLD | ColorPair(Yellow, Blue),
},
[COLORSCHEME_BLACKNIGHT] = {
- [RESET_COLOR] = ColorPair(Cyan,Black),
- [DEFAULT_COLOR] = ColorPair(Cyan,Black),
- [FUNCTION_BAR] = ColorPair(Black,Green),
- [FUNCTION_KEY] = ColorPair(Cyan,Black),
- [PANEL_HEADER_FOCUS] = ColorPair(Black,Green),
- [PANEL_HEADER_UNFOCUS] = ColorPair(Black,Green),
- [PANEL_SELECTION_FOCUS] = ColorPair(Black,Cyan),
- [PANEL_SELECTION_FOLLOW] = ColorPair(Black,Yellow),
- [PANEL_SELECTION_UNFOCUS] = ColorPair(Black,White),
- [FAILED_SEARCH] = ColorPair(Red,Cyan),
- [UPTIME] = ColorPair(Green,Black),
- [BATTERY] = ColorPair(Green,Black),
- [LARGE_NUMBER] = A_BOLD | ColorPair(Red,Black),
- [METER_TEXT] = ColorPair(Cyan,Black),
- [METER_VALUE] = ColorPair(Green,Black),
- [LED_COLOR] = ColorPair(Green,Black),
- [TASKS_RUNNING] = A_BOLD | ColorPair(Green,Black),
- [PROCESS] = ColorPair(Cyan,Black),
+ [RESET_COLOR] = ColorPair(Cyan, Black),
+ [DEFAULT_COLOR] = ColorPair(Cyan, Black),
+ [FUNCTION_BAR] = ColorPair(Black, Green),
+ [FUNCTION_KEY] = ColorPair(Cyan, Black),
+ [PANEL_HEADER_FOCUS] = ColorPair(Black, Green),
+ [PANEL_HEADER_UNFOCUS] = ColorPair(Black, Green),
+ [PANEL_SELECTION_FOCUS] = ColorPair(Black, Cyan),
+ [PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
+ [PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White),
+ [FAILED_SEARCH] = ColorPair(Red, Green),
+ [FAILED_READ] = A_BOLD | ColorPair(Red, Black),
+ [PAUSED] = A_BOLD | ColorPair(Yellow, Green),
+ [UPTIME] = ColorPair(Green, Black),
+ [BATTERY] = ColorPair(Green, Black),
+ [LARGE_NUMBER] = A_BOLD | ColorPair(Red, Black),
+ [METER_TEXT] = ColorPair(Cyan, Black),
+ [METER_VALUE] = ColorPair(Green, Black),
+ [METER_VALUE_ERROR] = A_BOLD | ColorPair(Red, Black),
+ [METER_VALUE_IOREAD] = ColorPair(Green, Black),
+ [METER_VALUE_IOWRITE] = ColorPair(Blue, Black),
+ [METER_VALUE_NOTICE] = A_BOLD | ColorPair(Yellow, Black),
+ [METER_VALUE_OK] = ColorPair(Green, Black),
+ [LED_COLOR] = ColorPair(Green, Black),
+ [TASKS_RUNNING] = A_BOLD | ColorPair(Green, Black),
+ [PROCESS] = ColorPair(Cyan, Black),
[PROCESS_SHADOW] = A_BOLD | ColorPairGrayBlack,
- [PROCESS_TAG] = A_BOLD | ColorPair(Yellow,Black),
- [PROCESS_MEGABYTES] = A_BOLD | ColorPair(Green,Black),
- [PROCESS_BASENAME] = A_BOLD | ColorPair(Green,Black),
- [PROCESS_TREE] = ColorPair(Cyan,Black),
- [PROCESS_THREAD] = ColorPair(Green,Black),
- [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue,Black),
- [PROCESS_R_STATE] = ColorPair(Green,Black),
- [PROCESS_D_STATE] = A_BOLD | ColorPair(Red,Black),
- [PROCESS_HIGH_PRIORITY] = ColorPair(Red,Black),
- [PROCESS_LOW_PRIORITY] = ColorPair(Green,Black),
- [BAR_BORDER] = A_BOLD | ColorPair(Green,Black),
- [BAR_SHADOW] = ColorPair(Cyan,Black),
- [SWAP] = ColorPair(Red,Black),
- [GRAPH_1] = A_BOLD | ColorPair(Green,Black),
- [GRAPH_2] = ColorPair(Green,Black),
- [MEMORY_USED] = ColorPair(Green,Black),
- [MEMORY_BUFFERS] = ColorPair(Blue,Black),
- [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue,Black),
- [MEMORY_CACHE] = ColorPair(Yellow,Black),
- [LOAD_AVERAGE_FIFTEEN] = ColorPair(Green,Black),
- [LOAD_AVERAGE_FIVE] = ColorPair(Green,Black),
- [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(Green,Black),
+ [PROCESS_TAG] = A_BOLD | ColorPair(Yellow, Black),
+ [PROCESS_MEGABYTES] = A_BOLD | ColorPair(Green, Black),
+ [PROCESS_GIGABYTES] = A_BOLD | ColorPair(Yellow, Black),
+ [PROCESS_BASENAME] = A_BOLD | ColorPair(Green, Black),
+ [PROCESS_TREE] = ColorPair(Cyan, Black),
+ [PROCESS_THREAD] = ColorPair(Green, Black),
+ [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
+ [PROCESS_COMM] = ColorPair(Magenta, Black),
+ [PROCESS_THREAD_COMM] = ColorPair(Yellow, Black),
+ [PROCESS_R_STATE] = ColorPair(Green, Black),
+ [PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
+ [PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
+ [PROCESS_LOW_PRIORITY] = ColorPair(Green, Black),
+ [PROCESS_NEW] = ColorPair(Black, Green),
+ [PROCESS_TOMB] = ColorPair(Black, Red),
+ [BAR_BORDER] = A_BOLD | ColorPair(Green, Black),
+ [BAR_SHADOW] = ColorPair(Cyan, Black),
+ [SWAP] = ColorPair(Red, Black),
+ [GRAPH_1] = A_BOLD | ColorPair(Green, Black),
+ [GRAPH_2] = ColorPair(Green, Black),
+ [MEMORY_USED] = ColorPair(Green, Black),
+ [MEMORY_BUFFERS] = ColorPair(Blue, Black),
+ [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black),
+ [MEMORY_CACHE] = ColorPair(Yellow, Black),
+ [LOAD_AVERAGE_FIFTEEN] = ColorPair(Green, Black),
+ [LOAD_AVERAGE_FIVE] = ColorPair(Green, Black),
+ [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(Green, Black),
[LOAD] = A_BOLD,
- [HELP_BOLD] = A_BOLD | ColorPair(Cyan,Black),
- [CLOCK] = ColorPair(Green,Black),
- [CHECK_BOX] = ColorPair(Green,Black),
- [CHECK_MARK] = A_BOLD | ColorPair(Green,Black),
- [CHECK_TEXT] = ColorPair(Cyan,Black),
- [HOSTNAME] = ColorPair(Green,Black),
- [CPU_NICE] = ColorPair(Blue,Black),
- [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue,Black),
- [CPU_NORMAL] = ColorPair(Green,Black),
- [CPU_SYSTEM] = ColorPair(Red,Black),
- [CPU_IOWAIT] = ColorPair(Yellow,Black),
- [CPU_IRQ] = A_BOLD | ColorPair(Blue,Black),
- [CPU_SOFTIRQ] = ColorPair(Blue,Black),
- [CPU_STEAL] = ColorPair(Cyan,Black),
- [CPU_GUEST] = ColorPair(Cyan,Black),
- [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green,Black),
- [PRESSURE_STALL_SIXTY] = ColorPair(Green,Black),
- [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green,Black),
- [ZFS_MFU] = ColorPair(Blue,Black),
- [ZFS_MRU] = ColorPair(Yellow,Black),
- [ZFS_ANON] = ColorPair(Magenta,Black),
- [ZFS_HEADER] = ColorPair(Yellow,Black),
- [ZFS_OTHER] = ColorPair(Magenta,Black),
- [ZFS_COMPRESSED] = ColorPair(Blue,Black),
- [ZFS_RATIO] = ColorPair(Magenta,Black),
+ [HELP_BOLD] = A_BOLD | ColorPair(Cyan, Black),
+ [CLOCK] = ColorPair(Green, Black),
+ [CHECK_BOX] = ColorPair(Green, Black),
+ [CHECK_MARK] = A_BOLD | ColorPair(Green, Black),
+ [CHECK_TEXT] = ColorPair(Cyan, Black),
+ [HOSTNAME] = ColorPair(Green, Black),
+ [CPU_NICE] = ColorPair(Blue, Black),
+ [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue, Black),
+ [CPU_NORMAL] = ColorPair(Green, Black),
+ [CPU_SYSTEM] = ColorPair(Red, Black),
+ [CPU_IOWAIT] = ColorPair(Yellow, Black),
+ [CPU_IRQ] = A_BOLD | ColorPair(Blue, Black),
+ [CPU_SOFTIRQ] = ColorPair(Blue, Black),
+ [CPU_STEAL] = ColorPair(Cyan, Black),
+ [CPU_GUEST] = ColorPair(Cyan, Black),
+ [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green, Black),
+ [PRESSURE_STALL_SIXTY] = ColorPair(Green, Black),
+ [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green, Black),
+ [ZFS_MFU] = ColorPair(Blue, Black),
+ [ZFS_MRU] = ColorPair(Yellow, Black),
+ [ZFS_ANON] = ColorPair(Magenta, Black),
+ [ZFS_HEADER] = ColorPair(Yellow, Black),
+ [ZFS_OTHER] = ColorPair(Magenta, Black),
+ [ZFS_COMPRESSED] = ColorPair(Blue, Black),
+ [ZFS_RATIO] = ColorPair(Magenta, Black),
+ [ZRAM] = ColorPair(Yellow, Black),
},
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
};
@@ -499,91 +604,85 @@ int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10;
-char* CRT_termType;
-
-// TODO move color scheme to Settings, perhaps?
+const char* CRT_termType;
int CRT_colorScheme = 0;
-void *backtraceArray[128];
+long CRT_pageSize = -1;
+long CRT_pageSizeKB = -1;
+ATTR_NORETURN
static void CRT_handleSIGTERM(int sgn) {
(void) sgn;
CRT_done();
exit(0);
}
-#if HAVE_SETUID_ENABLED
+#ifdef HAVE_SETUID_ENABLED
static int CRT_euid = -1;
static int CRT_egid = -1;
-#define DIE(msg) do { CRT_done(); fprintf(stderr, msg); exit(1); } while(0)
-
void CRT_dropPrivileges() {
CRT_egid = getegid();
CRT_euid = geteuid();
if (setegid(getgid()) == -1) {
- DIE("Fatal error: failed dropping group privileges.\n");
+ CRT_fatalError("Fatal error: failed dropping group privileges");
}
if (seteuid(getuid()) == -1) {
- DIE("Fatal error: failed dropping user privileges.\n");
+ CRT_fatalError("Fatal error: failed dropping user privileges");
}
}
void CRT_restorePrivileges() {
if (CRT_egid == -1 || CRT_euid == -1) {
- DIE("Fatal error: internal inconsistency.\n");
+ CRT_fatalError("Fatal error: internal inconsistency");
}
if (setegid(CRT_egid) == -1) {
- DIE("Fatal error: failed restoring group privileges.\n");
+ CRT_fatalError("Fatal error: failed restoring group privileges");
}
if (seteuid(CRT_euid) == -1) {
- DIE("Fatal error: failed restoring user privileges.\n");
+ CRT_fatalError("Fatal error: failed restoring user privileges");
}
}
-#else
-
-// In this case, the setuid operations are defined as macros in CRT.h
+#endif /* HAVE_SETUID_ENABLED */
-#endif
+static struct sigaction old_sig_handler[32];
// TODO: pass an instance of Settings instead.
-void CRT_init(int delay, int colorScheme, bool allowUnicode) {
+void CRT_init(const int* delay, int colorScheme, bool allowUnicode) {
initscr();
noecho();
CRT_delay = delay;
- if (CRT_delay == 0) {
- CRT_delay = 1;
- }
CRT_colors = CRT_colorSchemes[colorScheme];
CRT_colorScheme = colorScheme;
for (int i = 0; i < LAST_COLORELEMENT; i++) {
unsigned int color = CRT_colorSchemes[COLORSCHEME_DEFAULT][i];
- CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White,Black) : color;
+ CRT_colorSchemes[COLORSCHEME_BROKENGRAY][i] = color == (A_BOLD | ColorPairGrayBlack) ? ColorPair(White, Black) : color;
}
- halfdelay(CRT_delay);
+ halfdelay(*CRT_delay);
nonl();
intrflush(stdscr, false);
keypad(stdscr, true);
mouseinterval(0);
curs_set(0);
+
if (has_colors()) {
start_color();
- CRT_hasColors = true;
- } else {
- CRT_hasColors = false;
}
+
CRT_termType = getenv("TERM");
- if (String_eq(CRT_termType, "linux"))
+ if (String_eq(CRT_termType, "linux")) {
CRT_scrollHAmount = 20;
- else
+ } else {
CRT_scrollHAmount = 5;
+ }
+
if (String_startsWith(CRT_termType, "xterm") || String_eq(CRT_termType, "vt220")) {
define_key("\033[H", KEY_HOME);
define_key("\033[F", KEY_END);
@@ -604,24 +703,33 @@ void CRT_init(int delay, int colorScheme, bool allowUnicode) {
define_key(sequence, KEY_ALT('A' + (c - 'a')));
}
}
-#ifndef DEBUG
- signal(11, CRT_handleSIGSEGV);
-#endif
+
+ struct sigaction act;
+ sigemptyset (&act.sa_mask);
+ act.sa_flags = (int)SA_RESETHAND | SA_NODEFER;
+ act.sa_handler = CRT_handleSIGSEGV;
+ sigaction (SIGSEGV, &act, &old_sig_handler[SIGSEGV]);
+ sigaction (SIGFPE, &act, &old_sig_handler[SIGFPE]);
+ sigaction (SIGILL, &act, &old_sig_handler[SIGILL]);
+ sigaction (SIGBUS, &act, &old_sig_handler[SIGBUS]);
+ sigaction (SIGPIPE, &act, &old_sig_handler[SIGPIPE]);
+ sigaction (SIGSYS, &act, &old_sig_handler[SIGSYS]);
+ sigaction (SIGABRT, &act, &old_sig_handler[SIGABRT]);
+
signal(SIGTERM, CRT_handleSIGTERM);
signal(SIGQUIT, CRT_handleSIGTERM);
+
use_default_colors();
if (!has_colors())
- CRT_colorScheme = 1;
+ CRT_colorScheme = COLORSCHEME_MONOCHROME;
CRT_setColors(CRT_colorScheme);
- /* initialize locale */
- setlocale(LC_CTYPE, "");
-
#ifdef HAVE_LIBNCURSESW
- if (allowUnicode && strcmp(nl_langinfo(CODESET), "UTF-8") == 0)
+ if (allowUnicode && String_eq(nl_langinfo(CODESET), "UTF-8")) {
CRT_utf8 = true;
- else
+ } else {
CRT_utf8 = false;
+ }
#else
(void) allowUnicode;
#endif
@@ -638,6 +746,12 @@ void CRT_init(int delay, int colorScheme, bool allowUnicode) {
mousemask(BUTTON1_RELEASED, NULL);
#endif
+ CRT_pageSize = sysconf(_SC_PAGESIZE);
+ if (CRT_pageSize == -1)
+ CRT_fatalError("Fatal error: Can not get PAGE_SIZE by sysconf(_SC_PAGESIZE)");
+ CRT_pageSizeKB = CRT_pageSize / 1024;
+
+ CRT_degreeSign = initDegreeSign();
}
void CRT_done() {
@@ -657,7 +771,7 @@ int CRT_readKey() {
cbreak();
nodelay(stdscr, FALSE);
int ret = getch();
- halfdelay(CRT_delay);
+ halfdelay(*CRT_delay);
return ret;
}
@@ -668,28 +782,115 @@ void CRT_disableDelay() {
}
void CRT_enableDelay() {
- halfdelay(CRT_delay);
+ halfdelay(*CRT_delay);
}
void CRT_setColors(int colorScheme) {
CRT_colorScheme = colorScheme;
- for (int i = 0; i < 8; i++) {
- for (int j = 0; j < 8; j++) {
- if (ColorIndex(i,j) != ColorPairGrayBlack) {
- int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
- ? (j==0 ? -1 : j)
+ for (short int i = 0; i < 8; i++) {
+ for (short int j = 0; j < 8; j++) {
+ if (ColorIndex(i, j) != ColorIndexGrayBlack) {
+ short int bg = (colorScheme != COLORSCHEME_BLACKNIGHT)
+ ? (j == 0 ? -1 : j)
: j;
- init_pair(ColorIndex(i,j), i, bg);
+ init_pair(ColorIndex(i, j), i, bg);
}
}
}
- int grayBlackFg = COLORS > 8 ? 8 : 0;
- int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT)
- ? -1
- : 0;
+ short int grayBlackFg = COLORS > 8 ? 8 : 0;
+ short int grayBlackBg = (colorScheme != COLORSCHEME_BLACKNIGHT) ? -1 : 0;
init_pair(ColorIndexGrayBlack, grayBlackFg, grayBlackBg);
CRT_colors = CRT_colorSchemes[colorScheme];
}
+
+void CRT_handleSIGSEGV(int signal) {
+ CRT_done();
+
+ fprintf(stderr, "\n\n"
+ "FATAL PROGRAM ERROR DETECTED\n"
+ "============================\n"
+ "Please check at https://htop.dev/issues whether this issue has already been reported.\n"
+ "If no similar issue has been reported before, please create a new issue with the following information:\n"
+ "\n"
+ "- Your htop version (htop --version)\n"
+ "- Your OS and kernel version (uname -a)\n"
+ "- Your distribution and release (lsb_release -a)\n"
+ "- Likely steps to reproduce (How did it happened?)\n"
+#ifdef HAVE_EXECINFO_H
+ "- Backtrace of the issue (see below)\n"
+#endif
+ "\n"
+ );
+
+ const char* signal_str = strsignal(signal);
+ if (!signal_str) {
+ signal_str = "unknown reason";
+ }
+ fprintf(stderr,
+ "Error information:\n"
+ "------------------\n"
+ "A signal %d (%s) was received.\n"
+ "\n",
+ signal, signal_str
+ );
+
+#ifdef HAVE_EXECINFO_H
+ fprintf(stderr,
+ "Backtrace information:\n"
+ "----------------------\n"
+ "The following function calls were active when the issue was detected:\n"
+ "---\n"
+ );
+
+ void *backtraceArray[256];
+
+ size_t size = backtrace(backtraceArray, ARRAYSIZE(backtraceArray));
+ backtrace_symbols_fd(backtraceArray, size, 2);
+ fprintf(stderr,
+ "---\n"
+ "\n"
+ "To make the above information more practical to work with,\n"
+ "you should provide a disassembly of your binary.\n"
+ "This can usually be done by running the following command:\n"
+ "\n"
+#ifdef HTOP_DARWIN
+ " otool -tvV `which htop` > ~/htop.otool\n"
+#else
+ " objdump -d -S -w `which htop` > ~/htop.objdump\n"
+#endif
+ "\n"
+ "Please include the generated file in your report.\n"
+ "\n"
+ );
+#endif
+
+ fprintf(stderr,
+ "Running this program with debug symbols or inside a debugger may provide further insights.\n"
+ "\n"
+ "Thank you for helping to improve htop!\n"
+ "\n"
+ "htop " VERSION " aborting.\n"
+ "\n"
+ );
+
+ /* Call old sigsegv handler; may be default exit or third party one (e.g. ASAN) */
+ if (sigaction (signal, &old_sig_handler[signal], NULL) < 0) {
+ /* This avoids an infinite loop in case the handler could not be reset. */
+ fprintf(stderr,
+ "!!! Chained handler could not be restored. Forcing exit.\n"
+ );
+ _exit(1);
+ }
+
+ /* Trigger the previous signal handler. */
+ raise(signal);
+
+ // Always terminate, even if installed handler returns
+ fprintf(stderr,
+ "!!! Chained handler did not exit. Forcing exit.\n"
+ );
+ _exit(1);
+}
diff --git a/CRT.h b/CRT.h
index 8a5d6ac..ec3fdaf 100644
--- a/CRT.h
+++ b/CRT.h
@@ -3,15 +3,17 @@
/*
htop - CRT.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h"
+
#include <stdbool.h>
-#define KEY_WHEELUP KEY_F(20)
-#define KEY_WHEELDOWN KEY_F(21)
-#define KEY_RECLICK KEY_F(22)
+#include "Macros.h"
+#include "ProvideCurses.h"
+
typedef enum TreeStr_ {
TREE_STR_HORZ,
@@ -26,13 +28,13 @@ typedef enum TreeStr_ {
typedef enum ColorSchemes_ {
COLORSCHEME_DEFAULT = 0,
- COLORSCHEME_MONOCHROME = 1,
- COLORSCHEME_BLACKONWHITE = 2,
- COLORSCHEME_LIGHTTERMINAL = 3,
- COLORSCHEME_MIDNIGHT = 4,
- COLORSCHEME_BLACKNIGHT = 5,
- COLORSCHEME_BROKENGRAY = 6,
- LAST_COLORSCHEME = 7,
+ COLORSCHEME_MONOCHROME,
+ COLORSCHEME_BLACKONWHITE,
+ COLORSCHEME_LIGHTTERMINAL,
+ COLORSCHEME_MIDNIGHT,
+ COLORSCHEME_BLACKNIGHT,
+ COLORSCHEME_BROKENGRAY,
+ LAST_COLORSCHEME,
} ColorSchemes;
typedef enum ColorElements_ {
@@ -41,6 +43,8 @@ typedef enum ColorElements_ {
FUNCTION_BAR,
FUNCTION_KEY,
FAILED_SEARCH,
+ FAILED_READ,
+ PAUSED,
PANEL_HEADER_FOCUS,
PANEL_HEADER_UNFOCUS,
PANEL_SELECTION_FOCUS,
@@ -49,6 +53,11 @@ typedef enum ColorElements_ {
LARGE_NUMBER,
METER_TEXT,
METER_VALUE,
+ METER_VALUE_ERROR,
+ METER_VALUE_IOREAD,
+ METER_VALUE_IOWRITE,
+ METER_VALUE_NOTICE,
+ METER_VALUE_OK,
LED_COLOR,
UPTIME,
BATTERY,
@@ -58,14 +67,19 @@ typedef enum ColorElements_ {
PROCESS_SHADOW,
PROCESS_TAG,
PROCESS_MEGABYTES,
+ PROCESS_GIGABYTES,
PROCESS_TREE,
PROCESS_R_STATE,
PROCESS_D_STATE,
PROCESS_BASENAME,
PROCESS_HIGH_PRIORITY,
PROCESS_LOW_PRIORITY,
+ PROCESS_NEW,
+ PROCESS_TOMB,
PROCESS_THREAD,
PROCESS_THREAD_BASENAME,
+ PROCESS_COMM,
+ PROCESS_THREAD_COMM,
BAR_BORDER,
BAR_SHADOW,
GRAPH_1,
@@ -82,6 +96,8 @@ typedef enum ColorElements_ {
CHECK_MARK,
CHECK_TEXT,
CLOCK,
+ DATE,
+ DATETIME,
HELP_BOLD,
HOSTNAME,
CPU_NICE,
@@ -103,31 +119,30 @@ typedef enum ColorElements_ {
ZFS_OTHER,
ZFS_COMPRESSED,
ZFS_RATIO,
+ ZRAM,
LAST_COLORELEMENT
} ColorElements;
-void CRT_fatalError(const char* note) __attribute__ ((noreturn));
-
-void CRT_handleSIGSEGV(int sgn);
+void CRT_fatalError(const char* note) ATTR_NORETURN;
-#define KEY_ALT(x) (KEY_F(64 - 26) + (x - 'A'))
+void CRT_handleSIGSEGV(int signal) ATTR_NORETURN;
+#define KEY_WHEELUP KEY_F(20)
+#define KEY_WHEELDOWN KEY_F(21)
+#define KEY_RECLICK KEY_F(22)
+#define KEY_ALT(x) (KEY_F(64 - 26) + ((x) - 'A'))
-extern const char *CRT_treeStrAscii[TREE_STR_COUNT];
+extern const char* CRT_degreeSign;
#ifdef HAVE_LIBNCURSESW
-extern const char *CRT_treeStrUtf8[TREE_STR_COUNT];
-
extern bool CRT_utf8;
#endif
-extern const char **CRT_treeStr;
+extern const char* const* CRT_treeStr;
-extern int CRT_delay;
-
-extern int* CRT_colors;
+extern const int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
@@ -137,40 +152,36 @@ extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount;
-extern char* CRT_termType;
+extern const char* CRT_termType;
extern int CRT_colorScheme;
-extern void *backtraceArray[128];
+extern long CRT_pageSize;
+extern long CRT_pageSizeKB;
-#if HAVE_SETUID_ENABLED
+#ifdef HAVE_SETUID_ENABLED
-void CRT_dropPrivileges();
+void CRT_dropPrivileges(void);
-void CRT_restorePrivileges();
+void CRT_restorePrivileges(void);
-#else
+#else /* HAVE_SETUID_ENABLED */
/* Turn setuid operations into NOPs */
+static inline void CRT_dropPrivileges(void) { }
+static inline void CRT_restorePrivileges(void) { }
-#ifndef CRT_dropPrivileges
-#define CRT_dropPrivileges()
-#define CRT_restorePrivileges()
-#endif
-
-#endif
-
-void CRT_init(int delay, int colorScheme, bool allowUnicode);
+#endif /* HAVE_SETUID_ENABLED */
-void CRT_done();
+void CRT_init(const int* delay, int colorScheme, bool allowUnicode);
-void CRT_fatalError(const char* note);
+void CRT_done(void);
-int CRT_readKey();
+int CRT_readKey(void);
-void CRT_disableDelay();
+void CRT_disableDelay(void);
-void CRT_enableDelay();
+void CRT_enableDelay(void);
void CRT_setColors(int colorScheme);
diff --git a/CategoriesPanel.c b/CategoriesPanel.c
index 8a1e16d..4ee1ad4 100644
--- a/CategoriesPanel.c
+++ b/CategoriesPanel.c
@@ -1,21 +1,27 @@
/*
htop - CategoriesPanel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "CategoriesPanel.h"
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "AvailableColumnsPanel.h"
#include "AvailableMetersPanel.h"
-#include "MetersPanel.h"
-#include "DisplayOptionsPanel.h"
-#include "ColumnsPanel.h"
#include "ColorsPanel.h"
-#include "AvailableColumnsPanel.h"
-
-#include <assert.h>
-#include <stdlib.h>
+#include "ColumnsPanel.h"
+#include "DisplayOptionsPanel.h"
+#include "FunctionBar.h"
+#include "ListItem.h"
+#include "MetersPanel.h"
+#include "Object.h"
+#include "ProvideCurses.h"
+#include "Vector.h"
static const char* const CategoriesFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@@ -81,7 +87,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
break;
}
default:
- if (ch < 255 && isalpha(ch))
+ if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
@@ -91,6 +97,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
int size = ScreenManager_size(this->scr);
for (int i = 1; i < size; i++)
ScreenManager_remove(this->scr, 1);
+
switch (selected) {
case 0:
CategoriesPanel_makeMetersPage(this);
@@ -109,7 +116,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
return result;
}
-PanelClass CategoriesPanel_class = {
+const PanelClass CategoriesPanel_class = {
.super = {
.extends = Class(Panel),
.delete = CategoriesPanel_delete
diff --git a/CategoriesPanel.h b/CategoriesPanel.h
index fefc3e9..0582c64 100644
--- a/CategoriesPanel.h
+++ b/CategoriesPanel.h
@@ -3,14 +3,15 @@
/*
htop - CategoriesPanel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "Header.h"
#include "Panel.h"
-#include "Settings.h"
-#include "ScreenManager.h"
#include "ProcessList.h"
+#include "ScreenManager.h"
+#include "Settings.h"
typedef struct CategoriesPanel_ {
Panel super;
@@ -23,7 +24,7 @@ typedef struct CategoriesPanel_ {
void CategoriesPanel_makeMetersPage(CategoriesPanel* this);
-extern PanelClass CategoriesPanel_class;
+extern const PanelClass CategoriesPanel_class;
CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Header* header, ProcessList* pl);
diff --git a/ChangeLog b/ChangeLog
index 4df88d5..0b70458 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,80 @@
+What's new in version 3.0.3
+
+* Process sorting in 'tree' mode
+ (thanks to Maxim Zhiburt)
+* Improved command display/sort functionality
+ (thanks to Narendran Gopalakrishnan)
+* Add screen for active file locks
+ (thanks to Fynn J. Wulf)
+* Calculate library size (M_LRS column) from maps file
+ (thanks to Fynn J. Wulf)
+* Add a Zram meter
+ (thanks to Murloc Knight)
+* Add Linux cwd process column
+* Dynamically load libsensors at runtime
+* Improve PressureStall Meter display strings
+* Hide process selection on ESC
+* Fully support non-ascii characters in Meter-Bar
+* Add support to change numeric options in settings screen
+* Rename virtual memory column from M_SIZE to M_VIRT
+* Add process column for normalized CPU usage
+* Show CPU temperature in CPU meter
+* Drop hideThreads Setting
+* Add a systemd meter
+* Add a network IO meter
+* Add a SELinux meter
+* Compress size of default FunctionBar
+* Updates to the OpenFiles screen
+* Continue updating header data in paused mode
+* BUGFIX: Handle data wraparounds in IO meters
+* BUGFIX: Update InfoScreen content on resize
+* Add security attribute process column
+* Add DiskIOMeter for IO read/write usage
+* Read CPU frequency from sysfs by default
+* Add Linux process column for context switches
+* Several FreeBSD and Mac OS X platform updates
+ (thanks to Christian Göttsche)
+* Add process environment for FreeBSD
+ (thanks to Ross Williams)
+* Parse POWER_SUPPLY_CAPACITY for Linux Battery meter
+ (thanks to Jan Palus)
+* Add octuple-column CPU meters.
+* BUGFIX: On Linux consider ZFS ARC to be cache
+ (thanks to @multi)
+* BUGFIX: Limit screen title length to window width
+* Show selected command wrapped in a separate window
+ (thanks to @ryenus)
+* Allow to pass '/' for item search
+* Document implicit incremental search
+* Handle 'q' as quit if first character
+* Avoid expensive build of process tree when not using it
+* Include documentation for COMM and EXE
+* Distinguish display of no permissions for reading M_LRS
+* Only calculate M_LRS size every 2 seconds
+* Improvements to comm / cmdline display functionality
+* Merged view for COMM, EXE and cmdline
+ (thanks to Narendran Gopalakrishnan and Benny Baumann)
+* Consistent kernel thread display for COMM/EXE columns
+* Central fault handling for all platforms
+* Handle parsing envID & VPid from process status file
+* Use threshold for display of guest/steal/irq meters
+* Enhance highlighting of semi-large and large numbers
+* Documentation on the repository style guide
+ (thanks to Benny Baumann)
+* Align processor identifier to the right
+ (thanks to Christian Hesse)
+* Document M_PSS, M_PSSWP, M_SWAP in man page
+* Add Date and DateTime meters
+ (thanks to Michael F. Schönitzer)
+* BUGFIX: Fix Solaris 11.4 due to missing ZFS ARC kstats
+ (thanks to @senjan)
+* Code hardening, speedups, fd and memory leak fixes
+ (thanks to Christian Göttsche and Benny Baumann)
+* Number CPUs from zero by default
+ (thanks to Zev Weiss)
+* Remove residual python checks during the build process
+ (thanks to Stephen Gregoratto)
+
What's new in version 3.0.2
* BUGFIX: Drop 'vim_mode' - several issues, needs rethink
diff --git a/CheckItem.c b/CheckItem.c
deleted file mode 100644
index d14149e..0000000
--- a/CheckItem.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-htop - CheckItem.c
-(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "CheckItem.h"
-
-#include "CRT.h"
-
-#include <assert.h>
-#include <stdlib.h>
-
-
-static void CheckItem_delete(Object* cast) {
- CheckItem* this = (CheckItem*)cast;
- assert (this != NULL);
-
- free(this->text);
- free(this);
-}
-
-static void CheckItem_display(Object* cast, RichString* out) {
- CheckItem* this = (CheckItem*)cast;
- assert (this != NULL);
- RichString_write(out, CRT_colors[CHECK_BOX], "[");
- if (CheckItem_get(this))
- RichString_append(out, CRT_colors[CHECK_MARK], "x");
- else
- RichString_append(out, CRT_colors[CHECK_MARK], " ");
- RichString_append(out, CRT_colors[CHECK_BOX], "] ");
- RichString_append(out, CRT_colors[CHECK_TEXT], this->text);
-}
-
-ObjectClass CheckItem_class = {
- .display = CheckItem_display,
- .delete = CheckItem_delete
-};
-
-CheckItem* CheckItem_newByRef(char* text, bool* ref) {
- CheckItem* this = AllocThis(CheckItem);
- this->text = text;
- this->value = false;
- this->ref = ref;
- return this;
-}
-
-CheckItem* CheckItem_newByVal(char* text, bool value) {
- CheckItem* this = AllocThis(CheckItem);
- this->text = text;
- this->value = value;
- this->ref = NULL;
- return this;
-}
-
-void CheckItem_set(CheckItem* this, bool value) {
- if (this->ref)
- *(this->ref) = value;
- else
- this->value = value;
-}
-
-bool CheckItem_get(CheckItem* this) {
- if (this->ref)
- return *(this->ref);
- else
- return this->value;
-}
diff --git a/CheckItem.h b/CheckItem.h
deleted file mode 100644
index 11ac35f..0000000
--- a/CheckItem.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef HEADER_CheckItem
-#define HEADER_CheckItem
-/*
-htop - CheckItem.h
-(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "Object.h"
-
-typedef struct CheckItem_ {
- Object super;
- char* text;
- bool* ref;
- bool value;
-} CheckItem;
-
-extern ObjectClass CheckItem_class;
-
-CheckItem* CheckItem_newByRef(char* text, bool* ref);
-
-CheckItem* CheckItem_newByVal(char* text, bool value);
-
-void CheckItem_set(CheckItem* this, bool value);
-
-bool CheckItem_get(CheckItem* this);
-
-#endif
diff --git a/ClockMeter.c b/ClockMeter.c
index 3168b3e..023ca42 100644
--- a/ClockMeter.c
+++ b/ClockMeter.c
@@ -1,30 +1,33 @@
/*
htop - ClockMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "ClockMeter.h"
+#include "config.h" // IWYU pragma: keep
-#include "CRT.h"
+#include "ClockMeter.h"
#include <time.h>
+#include "CRT.h"
+#include "Object.h"
+
-int ClockMeter_attributes[] = {
+static const int ClockMeter_attributes[] = {
CLOCK
};
-static void ClockMeter_updateValues(Meter* this, char* buffer, int size) {
+static void ClockMeter_updateValues(Meter* this, char* buffer, size_t size) {
time_t t = time(NULL);
struct tm result;
- struct tm *lt = localtime_r(&t, &result);
+ struct tm* lt = localtime_r(&t, &result);
this->values[0] = lt->tm_hour * 60 + lt->tm_min;
strftime(buffer, size, "%H:%M:%S", lt);
}
-MeterClass ClockMeter_class = {
+const MeterClass ClockMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
diff --git a/ClockMeter.h b/ClockMeter.h
index e888d58..ecd4b6a 100644
--- a/ClockMeter.h
+++ b/ClockMeter.h
@@ -3,14 +3,12 @@
/*
htop - ClockMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
-extern int ClockMeter_attributes[];
-
-extern MeterClass ClockMeter_class;
+extern const MeterClass ClockMeter_class;
#endif
diff --git a/ColorsPanel.c b/ColorsPanel.c
index cfd975d..2fb1c92 100644
--- a/ColorsPanel.c
+++ b/ColorsPanel.c
@@ -1,18 +1,24 @@
/*
htop - ColorsPanel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "ColorsPanel.h"
+#include <stdbool.h>
+#include <stdlib.h>
+
#include "CRT.h"
-#include "CheckItem.h"
+#include "FunctionBar.h"
+#include "Header.h"
+#include "Object.h"
+#include "OptionItem.h"
+#include "ProvideCurses.h"
+#include "RichString.h"
+#include "Vector.h"
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
// TO ADD A NEW SCHEME:
// * Increment the size of bool check in ColorsPanel.h
@@ -57,6 +63,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
for (int i = 0; ColorSchemeNames[i] != NULL; i++)
CheckItem_set((CheckItem*)Panel_get(super, i), false);
CheckItem_set((CheckItem*)Panel_get(super, mark), true);
+
this->settings->colorScheme = mark;
result = HANDLED;
}
@@ -68,6 +75,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
clear();
Panel* menu = (Panel*) Vector_get(this->scr->panels, 0);
Header_draw(header);
+ FunctionBar_draw(super->currentBar);
RichString_setAttr(&(super->header), CRT_colors[PANEL_HEADER_FOCUS]);
RichString_setAttr(&(menu->header), CRT_colors[PANEL_HEADER_UNFOCUS]);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
@@ -75,7 +83,7 @@ static HandlerResult ColorsPanel_eventHandler(Panel* super, int ch) {
return result;
}
-PanelClass ColorsPanel_class = {
+const PanelClass ColorsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ColorsPanel_delete
@@ -94,7 +102,7 @@ ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr) {
Panel_setHeader(super, "Colors");
for (int i = 0; ColorSchemeNames[i] != NULL; i++) {
- Panel_add(super, (Object*) CheckItem_newByVal(xStrdup(ColorSchemeNames[i]), false));
+ Panel_add(super, (Object*) CheckItem_newByVal(ColorSchemeNames[i], false));
}
CheckItem_set((CheckItem*)Panel_get(super, settings->colorScheme), true);
return this;
diff --git a/ColorsPanel.h b/ColorsPanel.h
index 479fad0..f63ca35 100644
--- a/ColorsPanel.h
+++ b/ColorsPanel.h
@@ -3,13 +3,13 @@
/*
htop - ColorsPanel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
-#include "Settings.h"
#include "ScreenManager.h"
+#include "Settings.h"
typedef struct ColorsPanel_ {
Panel super;
@@ -18,7 +18,7 @@ typedef struct ColorsPanel_ {
ScreenManager* scr;
} ColorsPanel;
-extern PanelClass ColorsPanel_class;
+extern const PanelClass ColorsPanel_class;
ColorsPanel* ColorsPanel_new(Settings* settings, ScreenManager* scr);
diff --git a/ColumnsPanel.c b/ColumnsPanel.c
index 587175c..b2a8246 100644
--- a/ColumnsPanel.c
+++ b/ColumnsPanel.c
@@ -1,20 +1,22 @@
/*
htop - ColumnsPanel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "ColumnsPanel.h"
-#include "Platform.h"
-#include "StringUtils.h"
-#include "ListItem.h"
-#include "CRT.h"
-
-#include <assert.h>
-#include <stdlib.h>
#include <ctype.h>
+#include <stdlib.h>
+
+#include "CRT.h"
+#include "FunctionBar.h"
+#include "ListItem.h"
+#include "Object.h"
+#include "Process.h"
+#include "ProvideCurses.h"
+#include "XUtils.h"
static const char* const ColumnsFunctions[] = {" ", " ", " ", " ", " ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
@@ -43,7 +45,9 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
if (selected < size - 1) {
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
- ((ListItem*)Panel_getSelected(super))->moving = this->moving;
+ ListItem* selectedItem = (ListItem*) Panel_getSelected(super);
+ if (selectedItem)
+ selectedItem->moving = this->moving;
result = HANDLED;
}
break;
@@ -91,7 +95,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
}
default:
{
- if (ch < 255 && isalpha(ch))
+ if (0 < ch && ch < 255 && isgraph((unsigned char)ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
@@ -103,7 +107,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) {
return result;
}
-PanelClass ColumnsPanel_class = {
+const PanelClass ColumnsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ColumnsPanel_delete
@@ -130,20 +134,11 @@ ColumnsPanel* ColumnsPanel_new(Settings* settings) {
return this;
}
-int ColumnsPanel_fieldNameToIndex(const char* name) {
- for (int j = 1; j <= Platform_numberOfFields; j++) {
- if (String_eq(name, Process_fields[j].name)) {
- return j;
- }
- }
- return -1;
-}
-
void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
- this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size+1));
+ this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size + 1));
this->settings->flags = 0;
for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key;
diff --git a/ColumnsPanel.h b/ColumnsPanel.h
index be139a6..173e039 100644
--- a/ColumnsPanel.h
+++ b/ColumnsPanel.h
@@ -3,10 +3,12 @@
/*
htop - ColumnsPanel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+
#include "Panel.h"
#include "Settings.h"
@@ -17,12 +19,10 @@ typedef struct ColumnsPanel_ {
bool moving;
} ColumnsPanel;
-extern PanelClass ColumnsPanel_class;
+extern const PanelClass ColumnsPanel_class;
ColumnsPanel* ColumnsPanel_new(Settings* settings);
-int ColumnsPanel_fieldNameToIndex(const char* name);
-
void ColumnsPanel_update(Panel* super);
#endif
diff --git a/CommandScreen.c b/CommandScreen.c
new file mode 100644
index 0000000..d342829
--- /dev/null
+++ b/CommandScreen.c
@@ -0,0 +1,68 @@
+#include "config.h" // IWYU pragma: keep
+
+#include "CommandScreen.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "Macros.h"
+#include "Panel.h"
+#include "ProvideCurses.h"
+#include "XUtils.h"
+
+
+static void CommandScreen_scan(InfoScreen* this) {
+ Panel* panel = this->display;
+ int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
+ Panel_prune(panel);
+
+ const char* p = Process_getCommand(this->process);
+ char* line = xMalloc(COLS + 1);
+ int line_offset = 0, last_spc = -1, len;
+ for (; *p != '\0'; p++, line_offset++) {
+ line[line_offset] = *p;
+ if (*p == ' ') {
+ last_spc = line_offset;
+ }
+
+ if (line_offset == COLS) {
+ len = (last_spc == -1) ? line_offset : last_spc;
+ line[len] = '\0';
+ InfoScreen_addLine(this, line);
+
+ line_offset -= len;
+ last_spc = -1;
+ memcpy(line, p - line_offset, line_offset + 1);
+ }
+ }
+
+ if (line_offset > 0) {
+ line[line_offset] = '\0';
+ InfoScreen_addLine(this, line);
+ }
+
+ free(line);
+ Panel_setSelected(panel, idx);
+}
+
+static void CommandScreen_draw(InfoScreen* this) {
+ InfoScreen_drawTitled(this, "Command of process %d - %s", this->process->pid, Process_getCommand(this->process));
+}
+
+const InfoScreenClass CommandScreen_class = {
+ .super = {
+ .extends = Class(Object),
+ .delete = CommandScreen_delete
+ },
+ .scan = CommandScreen_scan,
+ .draw = CommandScreen_draw
+};
+
+CommandScreen* CommandScreen_new(Process* process) {
+ CommandScreen* this = AllocThis(CommandScreen);
+ return (CommandScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
+}
+
+void CommandScreen_delete(Object* this) {
+ free(InfoScreen_done((InfoScreen*)this));
+}
diff --git a/CommandScreen.h b/CommandScreen.h
new file mode 100644
index 0000000..e56982b
--- /dev/null
+++ b/CommandScreen.h
@@ -0,0 +1,19 @@
+#ifndef HEADER_CommandScreen
+#define HEADER_CommandScreen
+
+#include "InfoScreen.h"
+#include "Object.h"
+#include "Process.h"
+
+
+typedef struct CommandScreen_ {
+ InfoScreen super;
+} CommandScreen;
+
+extern const InfoScreenClass CommandScreen_class;
+
+CommandScreen* CommandScreen_new(Process* process);
+
+void CommandScreen_delete(Object* this);
+
+#endif
diff --git a/Compat.c b/Compat.c
new file mode 100644
index 0000000..43d02ec
--- /dev/null
+++ b/Compat.c
@@ -0,0 +1,119 @@
+/*
+htop - Compat.c
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include "Compat.h"
+
+#include <errno.h>
+#include <fcntl.h> // IWYU pragma: keep
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h> // IWYU pragma: keep
+
+#include "XUtils.h" // IWYU pragma: keep
+
+
+int Compat_faccessat(int dirfd,
+ const char* pathname,
+ int mode,
+ int flags) {
+ int ret;
+
+#ifdef HAVE_FACCESSAT
+
+ // Implementation note: AT_SYMLINK_NOFOLLOW unsupported on FreeBSD, fallback to lstat in that case
+
+ errno = 0;
+
+ ret = faccessat(dirfd, pathname, mode, flags);
+ if (!ret || errno != EINVAL)
+ return ret;
+
+#endif
+
+ // Error out on unsupported configurations
+ if (dirfd != AT_FDCWD || mode != F_OK) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ // Fallback to stat(2)/lstat(2) depending on flags
+ struct stat statinfo;
+ if(flags) {
+ ret = lstat(pathname, &statinfo);
+ } else {
+ ret = stat(pathname, &statinfo);
+ }
+
+ return ret;
+}
+
+int Compat_fstatat(int dirfd,
+ const char* dirpath,
+ const char* pathname,
+ struct stat* statbuf,
+ int flags) {
+
+#ifdef HAVE_FSTATAT
+
+ (void)dirpath;
+
+ return fstatat(dirfd, pathname, statbuf, flags);
+
+#else
+
+ (void)dirfd;
+
+ char path[4096];
+ xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname);
+
+ if (flags & AT_SYMLINK_NOFOLLOW)
+ return lstat(path, statbuf);
+
+ return stat(path, statbuf);
+
+#endif
+}
+
+#ifndef HAVE_OPENAT
+
+int Compat_openat(const char* dirpath,
+ const char* pathname,
+ int flags) {
+
+ char path[4096];
+ xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname);
+
+ return open(path, flags);
+}
+
+#endif /* !HAVE_OPENAT */
+
+ssize_t Compat_readlinkat(int dirfd,
+ const char* dirpath,
+ const char* pathname,
+ char* buf,
+ size_t bufsize) {
+
+#ifdef HAVE_READLINKAT
+
+ (void)dirpath;
+
+ return readlinkat(dirfd, pathname, buf, bufsize);
+
+#else
+
+ (void)dirfd;
+
+ char path[4096];
+ xSnprintf(path, sizeof(path), "%s/%s", dirpath, pathname);
+
+ return readlink(path, buf, bufsize);
+
+#endif
+}
diff --git a/Compat.h b/Compat.h
new file mode 100644
index 0000000..94c2ee2
--- /dev/null
+++ b/Compat.h
@@ -0,0 +1,59 @@
+#ifndef HEADER_Compat
+#define HEADER_Compat
+/*
+htop - Compat.h
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/stat.h> // IWYU pragma: keep
+
+
+int Compat_faccessat(int dirfd,
+ const char* pathname,
+ int mode,
+ int flags);
+
+int Compat_fstatat(int dirfd,
+ const char* dirpath,
+ const char* pathname,
+ struct stat* statbuf,
+ int flags);
+
+#ifdef HAVE_OPENAT
+
+typedef int openat_arg_t;
+
+static inline void Compat_openatArgClose(openat_arg_t dirfd) {
+ close(dirfd);
+}
+
+static inline int Compat_openat(openat_arg_t dirfd, const char* pathname, int flags) {
+ return openat(dirfd, pathname, flags);
+}
+
+#else /* HAVE_OPENAT */
+
+typedef const char* openat_arg_t;
+
+static inline void Compat_openatArgClose(openat_arg_t dirpath) {
+ (void)dirpath;
+}
+
+int Compat_openat(openat_arg_t dirpath, const char* pathname, int flags);
+
+#endif /* HAVE_OPENAT */
+
+ssize_t Compat_readlinkat(int dirfd,
+ const char* dirpath,
+ const char* pathname,
+ char* buf,
+ size_t bufsize);
+
+#endif /* HEADER_Compat */
diff --git a/DateMeter.c b/DateMeter.c
new file mode 100644
index 0000000..bd6a306
--- /dev/null
+++ b/DateMeter.c
@@ -0,0 +1,50 @@
+/*
+htop - DateMeter.c
+(C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer
+Released under the GNU GPL, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include "DateMeter.h"
+
+#include <time.h>
+
+#include "CRT.h"
+#include "Object.h"
+
+
+static const int DateMeter_attributes[] = {
+ DATE
+};
+
+static void DateMeter_updateValues(Meter* this, char* buffer, size_t size) {
+ time_t t = time(NULL);
+ struct tm result;
+ struct tm* lt = localtime_r(&t, &result);
+ this->values[0] = lt->tm_yday;
+ int year = lt->tm_year + 1900;
+ if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
+ this->total = 366;
+ }
+ else {
+ this->total = 365;
+ }
+ strftime(buffer, size, "%F", lt);
+}
+
+const MeterClass DateMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete
+ },
+ .updateValues = DateMeter_updateValues,
+ .defaultMode = TEXT_METERMODE,
+ .maxItems = 1,
+ .total = 365,
+ .attributes = DateMeter_attributes,
+ .name = "Date",
+ .uiName = "Date",
+ .caption = "Date: ",
+};
diff --git a/DateMeter.h b/DateMeter.h
new file mode 100644
index 0000000..6345576
--- /dev/null
+++ b/DateMeter.h
@@ -0,0 +1,14 @@
+#ifndef HEADER_DateMeter
+#define HEADER_DateMeter
+/*
+htop - DateMeter.h
+(C) 2004-2011 Hisham H. Muhammad
+Released under the GNU GPL, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "Meter.h"
+
+extern const MeterClass DateMeter_class;
+
+#endif
diff --git a/DateTimeMeter.c b/DateTimeMeter.c
new file mode 100644
index 0000000..0d231cd
--- /dev/null
+++ b/DateTimeMeter.c
@@ -0,0 +1,50 @@
+/*
+htop - DateTimeMeter.c
+(C) 2004-2020 Hisham H. Muhammad, Michael Schönitzer
+Released under the GNU GPL, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include "DateTimeMeter.h"
+
+#include <time.h>
+
+#include "CRT.h"
+#include "Object.h"
+
+
+static const int DateTimeMeter_attributes[] = {
+ DATETIME
+};
+
+static void DateTimeMeter_updateValues(Meter* this, char* buffer, size_t size) {
+ time_t t = time(NULL);
+ struct tm result;
+ struct tm* lt = localtime_r(&t, &result);
+ int year = lt->tm_year + 1900;
+ if (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) {
+ this->total = 366;
+ }
+ else {
+ this->total = 365;
+ }
+ this->values[0] = lt->tm_yday;
+ strftime(buffer, size, "%F %H:%M:%S", lt);
+}
+
+const MeterClass DateTimeMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete
+ },
+ .updateValues = DateTimeMeter_updateValues,
+ .defaultMode = TEXT_METERMODE,
+ .maxItems = 1,
+ .total = 365,
+ .attributes = DateTimeMeter_attributes,
+ .name = "DateTime",
+ .uiName = "Date and Time",
+ .caption = "Date & Time: ",
+};
diff --git a/DateTimeMeter.h b/DateTimeMeter.h
new file mode 100644
index 0000000..6cb73c2
--- /dev/null
+++ b/DateTimeMeter.h
@@ -0,0 +1,14 @@
+#ifndef HEADER_DateTimeMeter
+#define HEADER_DateTimeMeter
+/*
+htop - DateTimeMeter.h
+(C) 2004-2011 Hisham H. Muhammad
+Released under the GNU GPL, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "Meter.h"
+
+extern const MeterClass DateTimeMeter_class;
+
+#endif
diff --git a/DiskIOMeter.c b/DiskIOMeter.c
new file mode 100644
index 0000000..0105ce3
--- /dev/null
+++ b/DiskIOMeter.c
@@ -0,0 +1,124 @@
+/*
+htop - DiskIOMeter.c
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "DiskIOMeter.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "Object.h"
+#include "Platform.h"
+#include "RichString.h"
+#include "XUtils.h"
+
+
+static const int DiskIOMeter_attributes[] = {
+ METER_VALUE_NOTICE,
+ METER_VALUE_IOREAD,
+ METER_VALUE_IOWRITE,
+};
+
+static bool hasData = false;
+static unsigned long int cached_read_diff = 0;
+static unsigned long int cached_write_diff = 0;
+static double cached_utilisation_diff = 0.0;
+
+static void DiskIOMeter_updateValues(Meter* this, char* buffer, size_t len) {
+ static unsigned long long int cached_last_update = 0;
+
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000;
+ unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update;
+
+ /* update only every 500ms */
+ if (passedTimeInMs > 500) {
+ static unsigned long int cached_read_total = 0;
+ static unsigned long int cached_write_total = 0;
+ static unsigned long int cached_msTimeSpend_total = 0;
+
+ cached_last_update = timeInMilliSeconds;
+
+ DiskIOData data;
+
+ hasData = Platform_getDiskIO(&data);
+ if (!hasData) {
+ this->values[0] = 0;
+ xSnprintf(buffer, len, "no data");
+ return;
+ }
+
+ if (data.totalBytesRead > cached_read_total) {
+ cached_read_diff = (data.totalBytesRead - cached_read_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
+ } else {
+ cached_read_diff = 0;
+ }
+ cached_read_total = data.totalBytesRead;
+
+ if (data.totalBytesWritten > cached_write_total) {
+ cached_write_diff = (data.totalBytesWritten - cached_write_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
+ } else {
+ cached_write_diff = 0;
+ }
+ cached_write_total = data.totalBytesWritten;
+
+ if (data.totalMsTimeSpend > cached_msTimeSpend_total) {
+ cached_utilisation_diff = 100 * (double)(data.totalMsTimeSpend - cached_msTimeSpend_total) / passedTimeInMs;
+ } else {
+ cached_utilisation_diff = 0.0;
+ }
+ cached_msTimeSpend_total = data.totalMsTimeSpend;
+ }
+
+ this->values[0] = cached_utilisation_diff;
+ this->total = MAXIMUM(this->values[0], 100.0); /* fix total after (initial) spike */
+
+ char bufferRead[12], bufferWrite[12];
+ Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead));
+ Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite));
+ snprintf(buffer, len, "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff);
+}
+
+static void DIskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
+ if (!hasData) {
+ RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
+ return;
+ }
+
+ char buffer[16];
+
+ int color = cached_utilisation_diff > 40.0 ? METER_VALUE_NOTICE : METER_VALUE;
+ xSnprintf(buffer, sizeof(buffer), "%.1f%%", cached_utilisation_diff);
+ RichString_write(out, CRT_colors[color], buffer);
+
+ RichString_append(out, CRT_colors[METER_TEXT], " read: ");
+ Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer));
+ RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
+
+ RichString_append(out, CRT_colors[METER_TEXT], " write: ");
+ Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer));
+ RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
+}
+
+const MeterClass DiskIOMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = DIskIOMeter_display
+ },
+ .updateValues = DiskIOMeter_updateValues,
+ .defaultMode = TEXT_METERMODE,
+ .maxItems = 1,
+ .total = 100.0,
+ .attributes = DiskIOMeter_attributes,
+ .name = "DiskIO",
+ .uiName = "Disk IO",
+ .caption = "Disk IO: "
+};
diff --git a/DiskIOMeter.h b/DiskIOMeter.h
new file mode 100644
index 0000000..b2b3e8d
--- /dev/null
+++ b/DiskIOMeter.h
@@ -0,0 +1,20 @@
+#ifndef HEADER_DiskIOMeter
+#define HEADER_DiskIOMeter
+/*
+htop - DiskIOMeter.h
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "Meter.h"
+
+typedef struct DiskIOData_ {
+ unsigned long int totalBytesRead;
+ unsigned long int totalBytesWritten;
+ unsigned long int totalMsTimeSpend;
+} DiskIOData;
+
+extern const MeterClass DiskIOMeter_class;
+
+#endif /* HEADER_DiskIOMeter */
diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c
index 6978e23..ed37319 100644
--- a/DisplayOptionsPanel.c
+++ b/DisplayOptionsPanel.c
@@ -1,18 +1,23 @@
/*
htop - DisplayOptionsPanel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "DisplayOptionsPanel.h"
+#include "config.h" // IWYU pragma: keep
-#include "CheckItem.h"
-#include "CRT.h"
+#include "DisplayOptionsPanel.h"
-#include <assert.h>
+#include <stdbool.h>
#include <stdlib.h>
-#include <string.h>
+
+#include "CRT.h"
+#include "FunctionBar.h"
+#include "Header.h"
+#include "Object.h"
+#include "OptionItem.h"
+#include "ProvideCurses.h"
static const char* const DisplayOptionsFunctions[] = {" ", " ", " ", " ", " ", " ", " ", " ", " ", "Done ", NULL};
@@ -28,31 +33,52 @@ static HandlerResult DisplayOptionsPanel_eventHandler(Panel* super, int ch) {
DisplayOptionsPanel* this = (DisplayOptionsPanel*) super;
HandlerResult result = IGNORED;
- CheckItem* selected = (CheckItem*) Panel_getSelected(super);
+ OptionItem* selected = (OptionItem*) Panel_getSelected(super);
- switch(ch) {
- case 0x0a:
- case 0x0d:
+ switch (ch) {
+ case '\n':
+ case '\r':
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
case ' ':
- CheckItem_set(selected, ! (CheckItem_get(selected)) );
- result = HANDLED;
+ switch (OptionItem_kind(selected)) {
+ case OPTION_ITEM_CHECK:
+ CheckItem_toggle((CheckItem*)selected);
+ result = HANDLED;
+ break;
+ case OPTION_ITEM_NUMBER:
+ NumberItem_toggle((NumberItem*)selected);
+ result = HANDLED;
+ break;
+ }
+ break;
+ case '-':
+ if (OptionItem_kind(selected) == OPTION_ITEM_NUMBER) {
+ NumberItem_decrease((NumberItem*)selected);
+ result = HANDLED;
+ }
+ break;
+ case '+':
+ if (OptionItem_kind(selected) == OPTION_ITEM_NUMBER) {
+ NumberItem_increase((NumberItem*)selected);
+ result = HANDLED;
+ }
+ break;
}
if (result == HANDLED) {
this->settings->changed = true;
- const Header* header = this->scr->header;
- Header_calculateHeight((Header*) header);
- Header_reinit((Header*) header);
+ Header* header = this->scr->header;
+ Header_calculateHeight(header);
+ Header_reinit(header);
Header_draw(header);
ScreenManager_resize(this->scr, this->scr->x1, header->height, this->scr->x2, this->scr->y2);
}
return result;
}
-PanelClass DisplayOptionsPanel_class = {
+const PanelClass DisplayOptionsPanel_class = {
.super = {
.extends = Class(Panel),
.delete = DisplayOptionsPanel_delete
@@ -64,31 +90,41 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
DisplayOptionsPanel* this = AllocThis(DisplayOptionsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(DisplayOptionsFunctions, NULL, NULL);
- Panel_init(super, 1, 1, 1, 1, Class(CheckItem), true, fuBar);
+ Panel_init(super, 1, 1, 1, 1, Class(OptionItem), true, fuBar);
this->settings = settings;
this->scr = scr;
Panel_setHeader(super, "Display options");
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Display threads in a different color"), &(settings->highlightThreads)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show custom thread names"), &(settings->showThreadNames)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show program path"), &(settings->showProgramPath)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight program \"basename\""), &(settings->highlightBaseName)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight large numbers in memory counters"), &(settings->highlightMegabytes)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Leave a margin around header"), &(settings->headerMargin)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)"), &(settings->detailedCPUTime)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Count CPUs from 0 instead of 1"), &(settings->countCPUsFromZero)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Update process names on every refresh"), &(settings->updateProcessNames)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Add guest time in CPU meter percentage"), &(settings->accountGuestInCPUMeter)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU percentage numerically"), &(settings->showCPUUsage)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Also show CPU frequency"), &(settings->showCPUFrequency)));
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Enable the mouse"), &(settings->enableMouse)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Tree view", &(settings->treeView)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Shadow other users' processes", &(settings->shadowOtherUsers)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Hide kernel threads", &(settings->hideKernelThreads)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Hide userland process threads", &(settings->hideUserlandThreads)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Display threads in a different color", &(settings->highlightThreads)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Show custom thread names", &(settings->showThreadNames)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Show program path", &(settings->showProgramPath)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Highlight program \"basename\"", &(settings->highlightBaseName)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Merge exe, comm and cmdline in Command", &(settings->showMergedCommand)));
+ Panel_add(super, (Object*) CheckItem_newByRef("- Try to find comm in cmdline (when Command is merged)", &(settings->findCommInCmdline)));
+ Panel_add(super, (Object*) CheckItem_newByRef("- Try to strip exe from cmdline (when Command is merged)", &(settings->stripExeFromCmdline)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Highlight large numbers in memory counters", &(settings->highlightMegabytes)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Leave a margin around header", &(settings->headerMargin)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)", &(settings->detailedCPUTime)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Count CPUs from 1 instead of 0", &(settings->countCPUsFromOne)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Update process names on every refresh", &(settings->updateProcessNames)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Add guest time in CPU meter percentage", &(settings->accountGuestInCPUMeter)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU percentage numerically", &(settings->showCPUUsage)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU frequency", &(settings->showCPUFrequency)));
+ #ifdef HAVE_SENSORS_SENSORS_H
+ Panel_add(super, (Object*) CheckItem_newByRef("Also show CPU temperature (requires libsensors)", &(settings->showCPUTemperature)));
+ Panel_add(super, (Object*) CheckItem_newByRef("- Show temperature in degree Fahrenheit instead of Celsius", &(settings->degreeFahrenheit)));
+ #endif
+ Panel_add(super, (Object*) CheckItem_newByRef("Enable the mouse", &(settings->enableMouse)));
+ Panel_add(super, (Object*) NumberItem_newByRef("Update interval (in seconds)", &(settings->delay), -1, 1, 255));
+ Panel_add(super, (Object*) CheckItem_newByRef("Highlight new and old processes", &(settings->highlightChanges)));
+ Panel_add(super, (Object*) NumberItem_newByRef("- Highlight time (in seconds)", &(settings->highlightDelaySecs), 0, 1, 24*60*60));
#ifdef HAVE_LIBHWLOC
- Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show topology when selecting affinity by default"), &(settings->topologyAffinity)));
+ Panel_add(super, (Object*) CheckItem_newByRef("Show topology when selecting affinity by default", &(settings->topologyAffinity)));
#endif
return this;
}
diff --git a/DisplayOptionsPanel.h b/DisplayOptionsPanel.h
index 499b2fc..02b67a0 100644
--- a/DisplayOptionsPanel.h
+++ b/DisplayOptionsPanel.h
@@ -3,13 +3,13 @@
/*
htop - DisplayOptionsPanel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
-#include "Settings.h"
#include "ScreenManager.h"
+#include "Settings.h"
typedef struct DisplayOptionsPanel_ {
Panel super;
@@ -18,7 +18,7 @@ typedef struct DisplayOptionsPanel_ {
ScreenManager* scr;
} DisplayOptionsPanel;
-extern PanelClass DisplayOptionsPanel_class;
+extern const PanelClass DisplayOptionsPanel_class;
DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* scr);
diff --git a/EnvScreen.c b/EnvScreen.c
index 479a45d..ae63d3e 100644
--- a/EnvScreen.c
+++ b/EnvScreen.c
@@ -1,18 +1,20 @@
-#include "EnvScreen.h"
+#include "config.h" // IWYU pragma: keep
-#include "config.h"
-#include "CRT.h"
-#include "IncSet.h"
-#include "ListItem.h"
-#include "Platform.h"
-#include "StringUtils.h"
+#include "EnvScreen.h"
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "Panel.h"
+#include "Platform.h"
+#include "ProvideCurses.h"
+#include "Vector.h"
+#include "XUtils.h"
-InfoScreenClass EnvScreen_class = {
+const InfoScreenClass EnvScreen_class = {
.super = {
.extends = Class(Object),
.delete = EnvScreen_delete
@@ -24,7 +26,7 @@ InfoScreenClass EnvScreen_class = {
EnvScreen* EnvScreen_new(Process* process) {
EnvScreen* this = xMalloc(sizeof(EnvScreen));
Object_setClass(this, Class(EnvScreen));
- return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " ");
+ return (EnvScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " ");
}
void EnvScreen_delete(Object* this) {
@@ -32,7 +34,7 @@ void EnvScreen_delete(Object* this) {
}
void EnvScreen_draw(InfoScreen* this) {
- InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, this->process->comm);
+ InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process));
}
void EnvScreen_scan(InfoScreen* this) {
@@ -45,7 +47,7 @@ void EnvScreen_scan(InfoScreen* this) {
char* env = Platform_getProcessEnv(this->process->pid);
CRT_restorePrivileges();
if (env) {
- for (char *p = env; *p; p = strrchr(p, 0)+1)
+ for (char* p = env; *p; p = strrchr(p, 0) + 1)
InfoScreen_addLine(this, p);
free(env);
}
diff --git a/EnvScreen.h b/EnvScreen.h
index 54dfa41..bf38580 100644
--- a/EnvScreen.h
+++ b/EnvScreen.h
@@ -2,12 +2,14 @@
#define HEADER_EnvScreen
#include "InfoScreen.h"
+#include "Object.h"
+#include "Process.h"
typedef struct EnvScreen_ {
InfoScreen super;
} EnvScreen;
-extern InfoScreenClass EnvScreen_class;
+extern const InfoScreenClass EnvScreen_class;
EnvScreen* EnvScreen_new(Process* process);
diff --git a/FunctionBar.c b/FunctionBar.c
index e05f72e..627bc77 100644
--- a/FunctionBar.c
+++ b/FunctionBar.c
@@ -1,18 +1,21 @@
/*
htop - FunctionBar.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "FunctionBar.h"
-#include "CRT.h"
-#include "RichString.h"
-#include "XAlloc.h"
-#include <assert.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "ProvideCurses.h"
+#include "XUtils.h"
static const char* const FunctionBar_FKeys[] = {"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", NULL};
@@ -24,6 +27,8 @@ static int FunctionBar_FEvents[] = {KEY_F(1), KEY_F(2), KEY_F(3), KEY_F(4), KEY_
static const char* const FunctionBar_EnterEscKeys[] = {"Enter", "Esc", NULL};
static const int FunctionBar_EnterEscEvents[] = {13, 27};
+static int currentLen = 0;
+
FunctionBar* FunctionBar_newEnterEsc(const char* enter, const char* esc) {
const char* functions[] = {enter, esc, NULL};
return FunctionBar_new(functions, FunctionBar_EnterEscKeys, FunctionBar_EnterEscEvents);
@@ -40,20 +45,20 @@ FunctionBar* FunctionBar_new(const char* const* functions, const char* const* ke
}
if (keys && events) {
this->staticData = false;
- this->keys = xCalloc(15, sizeof(char*));
+ this->keys.keys = xCalloc(15, sizeof(char*));
this->events = xCalloc(15, sizeof(int));
int i = 0;
while (i < 15 && functions[i]) {
- this->keys[i] = xStrdup(keys[i]);
+ this->keys.keys[i] = xStrdup(keys[i]);
this->events[i] = events[i];
i++;
}
this->size = i;
} else {
this->staticData = true;
- this->keys = (char**) FunctionBar_FKeys;
+ this->keys.constKeys = FunctionBar_FKeys;
this->events = FunctionBar_FEvents;
- this->size = 10;
+ this->size = ARRAYSIZE(FunctionBar_FEvents);
}
return this;
}
@@ -65,9 +70,9 @@ void FunctionBar_delete(FunctionBar* this) {
free(this->functions);
if (!this->staticData) {
for (int i = 0; i < this->size; i++) {
- free(this->keys[i]);
+ free(this->keys.keys[i]);
}
- free(this->keys);
+ free(this->keys.keys);
free(this->events);
}
free(this);
@@ -83,37 +88,60 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
}
}
-void FunctionBar_draw(const FunctionBar* this, char* buffer) {
- FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
+void FunctionBar_draw(const FunctionBar* this) {
+ FunctionBar_drawExtra(this, NULL, -1, false);
}
-void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
+void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor) {
attrset(CRT_colors[FUNCTION_BAR]);
- mvhline(LINES-1, 0, ' ', COLS);
+ mvhline(LINES - 1, 0, ' ', COLS);
int x = 0;
for (int i = 0; i < this->size; i++) {
attrset(CRT_colors[FUNCTION_KEY]);
- mvaddstr(LINES-1, x, this->keys[i]);
- x += strlen(this->keys[i]);
+ mvaddstr(LINES - 1, x, this->keys.constKeys[i]);
+ x += strlen(this->keys.constKeys[i]);
attrset(CRT_colors[FUNCTION_BAR]);
- mvaddstr(LINES-1, x, this->functions[i]);
+ mvaddstr(LINES - 1, x, this->functions[i]);
x += strlen(this->functions[i]);
}
+
if (buffer) {
- attrset(attr);
- mvaddstr(LINES-1, x, buffer);
- CRT_cursorX = x + strlen(buffer);
+ if (attr == -1) {
+ attrset(CRT_colors[FUNCTION_BAR]);
+ } else {
+ attrset(attr);
+ }
+ mvaddstr(LINES - 1, x, buffer);
+ attrset(CRT_colors[RESET_COLOR]);
+ x += strlen(buffer);
+ }
+
+ if (setCursor) {
+ CRT_cursorX = x;
curs_set(1);
} else {
curs_set(0);
}
+
+ currentLen = x;
+}
+
+void FunctionBar_append(const char* buffer, int attr) {
+ if (attr == -1) {
+ attrset(CRT_colors[FUNCTION_BAR]);
+ } else {
+ attrset(attr);
+ }
+ mvaddstr(LINES - 1, currentLen, buffer);
attrset(CRT_colors[RESET_COLOR]);
+
+ currentLen += strlen(buffer);
}
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {
int x = 0;
for (int i = 0; i < this->size; i++) {
- x += strlen(this->keys[i]);
+ x += strlen(this->keys.constKeys[i]);
x += strlen(this->functions[i]);
if (pos < x) {
return this->events[i];
diff --git a/FunctionBar.h b/FunctionBar.h
index 486990f..925e323 100644
--- a/FunctionBar.h
+++ b/FunctionBar.h
@@ -3,7 +3,7 @@
/*
htop - FunctionBar.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -12,7 +12,10 @@ in the source distribution for its full text.
typedef struct FunctionBar_ {
int size;
char** functions;
- char** keys;
+ union {
+ char** keys;
+ const char* const* constKeys;
+ } keys;
int* events;
bool staticData;
} FunctionBar;
@@ -25,9 +28,11 @@ void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
-void FunctionBar_draw(const FunctionBar* this, char* buffer);
+void FunctionBar_draw(const FunctionBar* this);
-void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
+void FunctionBar_drawExtra(const FunctionBar* this, const char* buffer, int attr, bool setCursor);
+
+void FunctionBar_append(const char* buffer, int attr);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);
diff --git a/Hashtable.c b/Hashtable.c
index bb9517a..1beb2bb 100644
--- a/Hashtable.c
+++ b/Hashtable.c
@@ -1,152 +1,305 @@
/*
htop - Hashtable.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "Hashtable.h"
-#include "XAlloc.h"
-#include <stdlib.h>
#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "Macros.h"
+#include "XUtils.h"
+
+
+#ifndef NDEBUG
+static void Hashtable_dump(const Hashtable* this) {
+ fprintf(stderr, "Hashtable %p: size=%u items=%u owner=%s\n",
+ (const void*)this,
+ this->size,
+ this->items,
+ this->owner ? "yes" : "no");
-#ifdef DEBUG
+ unsigned int items = 0;
+ for (unsigned int i = 0; i < this->size; i++) {
+ fprintf(stderr, " item %5u: key = %5u probe = %2u value = %p\n",
+ i,
+ this->buckets[i].key,
+ this->buckets[i].probe,
+ this->buckets[i].value ? (const void*)this->buckets[i].value : "(nil)");
-static bool Hashtable_isConsistent(Hashtable* this) {
- int items = 0;
- for (int i = 0; i < this->size; i++) {
- HashtableItem* bucket = this->buckets[i];
- while (bucket) {
+ if (this->buckets[i].value)
items++;
- bucket = bucket->next;
- }
}
- return items == this->items;
+
+ fprintf(stderr, "Hashtable %p: items=%u counted=%u\n",
+ (const void*)this,
+ this->items,
+ items);
}
-int Hashtable_count(Hashtable* this) {
- int items = 0;
- for (int i = 0; i < this->size; i++) {
- HashtableItem* bucket = this->buckets[i];
- while (bucket) {
+static bool Hashtable_isConsistent(const Hashtable* this) {
+ unsigned int items = 0;
+ for (unsigned int i = 0; i < this->size; i++) {
+ if (this->buckets[i].value)
+ items++;
+ }
+ bool res = items == this->items;
+ if (!res)
+ Hashtable_dump(this);
+ return res;
+}
+
+unsigned int Hashtable_count(const Hashtable* this) {
+ unsigned int items = 0;
+ for (unsigned int i = 0; i < this->size; i++) {
+ if (this->buckets[i].value)
items++;
- bucket = bucket->next;
- }
}
assert(items == this->items);
return items;
}
-#endif
+#endif /* NDEBUG */
-static HashtableItem* HashtableItem_new(unsigned int key, void* value) {
- HashtableItem* this;
+/* https://oeis.org/A014234 */
+static const uint64_t OEISprimes[] = {
+ 2, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191,
+ 16381, 32749, 65521, 131071, 262139, 524287, 1048573,
+ 2097143, 4194301, 8388593, 16777213, 33554393,
+ 67108859, 134217689, 268435399, 536870909, 1073741789,
+ 2147483647, 4294967291, 8589934583, 17179869143,
+ 34359738337, 68719476731, 137438953447
+};
- this = xMalloc(sizeof(HashtableItem));
- this->key = key;
- this->value = value;
- this->next = NULL;
- return this;
+static uint64_t nextPrime(unsigned int n) {
+ assert(n <= OEISprimes[ARRAYSIZE(OEISprimes) - 1]);
+
+ for (unsigned int i = 0; i < ARRAYSIZE(OEISprimes); i++) {
+ if (n <= OEISprimes[i])
+ return OEISprimes[i];
+ }
+
+ return OEISprimes[ARRAYSIZE(OEISprimes) - 1];
}
-Hashtable* Hashtable_new(int size, bool owner) {
+Hashtable* Hashtable_new(unsigned int size, bool owner) {
Hashtable* this;
this = xMalloc(sizeof(Hashtable));
this->items = 0;
- this->size = size;
- this->buckets = (HashtableItem**) xCalloc(size, sizeof(HashtableItem*));
+ this->size = size ? nextPrime(size) : 13;
+ this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
this->owner = owner;
+
assert(Hashtable_isConsistent(this));
return this;
}
void Hashtable_delete(Hashtable* this) {
- assert(Hashtable_isConsistent(this));
- for (int i = 0; i < this->size; i++) {
- HashtableItem* walk = this->buckets[i];
- while (walk != NULL) {
- if (this->owner)
- free(walk->value);
- HashtableItem* savedWalk = walk;
- walk = savedWalk->next;
- free(savedWalk);
- }
- }
+ Hashtable_clear(this);
+
free(this->buckets);
free(this);
}
-void Hashtable_put(Hashtable* this, unsigned int key, void* value) {
+void Hashtable_clear(Hashtable* this) {
+ assert(Hashtable_isConsistent(this));
+
+ if (this->owner)
+ for (unsigned int i = 0; i < this->size; i++)
+ free(this->buckets[i].value);
+
+ memset(this->buckets, 0, this->size * sizeof(HashtableItem));
+ this->items = 0;
+
+ assert(Hashtable_isConsistent(this));
+}
+
+static void insert(Hashtable* this, hkey_t key, void* value) {
unsigned int index = key % this->size;
- HashtableItem** bucketPtr = &(this->buckets[index]);
- while (true)
- if (*bucketPtr == NULL) {
- *bucketPtr = HashtableItem_new(key, value);
+ unsigned int probe = 0;
+#ifndef NDEBUG
+ unsigned int origIndex = index;
+#endif
+
+ for (;;) {
+ if (!this->buckets[index].value) {
this->items++;
- break;
- } else if ((*bucketPtr)->key == key) {
- if (this->owner)
- free((*bucketPtr)->value);
- (*bucketPtr)->value = value;
- break;
- } else
- bucketPtr = &((*bucketPtr)->next);
+ this->buckets[index].key = key;
+ this->buckets[index].probe = probe;
+ this->buckets[index].value = value;
+ return;
+ }
+
+ if (this->buckets[index].key == key) {
+ if (this->owner && this->buckets[index].value != value)
+ free(this->buckets[index].value);
+ this->buckets[index].value = value;
+ return;
+ }
+
+ /* Robin Hood swap */
+ if (probe > this->buckets[index].probe) {
+ HashtableItem tmp = this->buckets[index];
+
+ this->buckets[index].key = key;
+ this->buckets[index].probe = probe;
+ this->buckets[index].value = value;
+
+ key = tmp.key;
+ probe = tmp.probe;
+ value = tmp.value;
+ }
+
+ index = (index + 1) % this->size;
+ probe++;
+
+ assert(index != origIndex);
+ }
+}
+
+void Hashtable_setSize(Hashtable* this, unsigned int size) {
+
assert(Hashtable_isConsistent(this));
+
+ if (size <= this->items)
+ return;
+
+ HashtableItem* oldBuckets = this->buckets;
+ unsigned int oldSize = this->size;
+
+ this->size = nextPrime(size);
+ this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem));
+ this->items = 0;
+
+ /* rehash */
+ for (unsigned int i = 0; i < oldSize; i++) {
+ if (!oldBuckets[i].value)
+ continue;
+
+ insert(this, oldBuckets[i].key, oldBuckets[i].value);
+ }
+
+ free(oldBuckets);
+
+ assert(Hashtable_isConsistent(this));
+}
+
+void Hashtable_put(Hashtable* this, hkey_t key, void* value) {
+
+ assert(Hashtable_isConsistent(this));
+ assert(this->size > 0);
+ assert(value);
+
+ /* grow on load-factor > 0.7 */
+ if (10 * this->items > 7 * this->size)
+ Hashtable_setSize(this, 2 * this->size);
+
+ insert(this, key, value);
+
+ assert(Hashtable_isConsistent(this));
+ assert(Hashtable_get(this, key) != NULL);
+ assert(this->size > this->items);
}
-void* Hashtable_remove(Hashtable* this, unsigned int key) {
+void* Hashtable_remove(Hashtable* this, hkey_t key) {
unsigned int index = key % this->size;
+ unsigned int probe = 0;
+#ifndef NDEBUG
+ unsigned int origIndex = index;
+#endif
assert(Hashtable_isConsistent(this));
- HashtableItem** bucket;
- for (bucket = &(this->buckets[index]); *bucket; bucket = &((*bucket)->next) ) {
- if ((*bucket)->key == key) {
- void* value = (*bucket)->value;
- HashtableItem* next = (*bucket)->next;
- free(*bucket);
- (*bucket) = next;
- this->items--;
+ void* res = NULL;
+
+ while (this->buckets[index].value) {
+ if (this->buckets[index].key == key) {
if (this->owner) {
- free(value);
- assert(Hashtable_isConsistent(this));
- return NULL;
+ free(this->buckets[index].value);
} else {
- assert(Hashtable_isConsistent(this));
- return value;
+ res = this->buckets[index].value;
+ }
+
+ unsigned int next = (index + 1) % this->size;
+
+ while (this->buckets[next].value && this->buckets[next].probe > 0) {
+ this->buckets[index] = this->buckets[next];
+ this->buckets[index].probe -= 1;
+
+ index = next;
+ next = (index + 1) % this->size;
}
+
+ /* set empty after backward shifting */
+ this->buckets[index].value = NULL;
+ this->items--;
+
+ break;
}
+
+ if (this->buckets[index].probe < probe)
+ break;
+
+ index = (index + 1) % this->size;
+ probe++;
+
+ assert(index != origIndex);
}
+
assert(Hashtable_isConsistent(this));
- return NULL;
+ assert(Hashtable_get(this, key) == NULL);
+
+ /* shrink on load-factor < 0.125 */
+ if (8 * this->items < this->size)
+ Hashtable_setSize(this, this->size / 2);
+
+ return res;
}
-inline void* Hashtable_get(Hashtable* this, unsigned int key) {
+void* Hashtable_get(Hashtable* this, hkey_t key) {
unsigned int index = key % this->size;
- HashtableItem* bucketPtr = this->buckets[index];
- while (true) {
- if (bucketPtr == NULL) {
- assert(Hashtable_isConsistent(this));
- return NULL;
- } else if (bucketPtr->key == key) {
- assert(Hashtable_isConsistent(this));
- return bucketPtr->value;
- } else
- bucketPtr = bucketPtr->next;
+ unsigned int probe = 0;
+ void* res = NULL;
+#ifndef NDEBUG
+ unsigned int origIndex = index;
+#endif
+
+ assert(Hashtable_isConsistent(this));
+
+ while (this->buckets[index].value) {
+ if (this->buckets[index].key == key) {
+ res = this->buckets[index].value;
+ break;
+ }
+
+ if (this->buckets[index].probe < probe)
+ break;
+
+ index = (index + 1) != this->size ? (index + 1) : 0;
+ probe++;
+
+ assert(index != origIndex);
}
+
+ return res;
}
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) {
assert(Hashtable_isConsistent(this));
- for (int i = 0; i < this->size; i++) {
- HashtableItem* walk = this->buckets[i];
- while (walk != NULL) {
+ for (unsigned int i = 0; i < this->size; i++) {
+ HashtableItem* walk = &this->buckets[i];
+ if (walk->value)
f(walk->key, walk->value, userData);
- walk = walk->next;
- }
}
assert(Hashtable_isConsistent(this));
}
diff --git a/Hashtable.h b/Hashtable.h
index 144f01c..6d93478 100644
--- a/Hashtable.h
+++ b/Hashtable.h
@@ -3,44 +3,49 @@
/*
htop - Hashtable.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <stdbool.h>
-typedef struct Hashtable_ Hashtable;
-typedef void(*Hashtable_PairFunction)(int, void*, void*);
+typedef unsigned int hkey_t;
-typedef struct HashtableItem {
- unsigned int key;
+typedef void(*Hashtable_PairFunction)(hkey_t key, void* value, void* userdata);
+
+typedef struct HashtableItem_ {
+ hkey_t key;
+ unsigned int probe;
void* value;
- struct HashtableItem* next;
} HashtableItem;
-struct Hashtable_ {
- int size;
- HashtableItem** buckets;
- int items;
+typedef struct Hashtable_ {
+ unsigned int size;
+ HashtableItem* buckets;
+ unsigned int items;
bool owner;
-};
+} Hashtable;
-#ifdef DEBUG
+#ifndef NDEBUG
-int Hashtable_count(Hashtable* this);
+unsigned int Hashtable_count(const Hashtable* this);
-#endif
+#endif /* NDEBUG */
-Hashtable* Hashtable_new(int size, bool owner);
+Hashtable* Hashtable_new(unsigned int size, bool owner);
void Hashtable_delete(Hashtable* this);
-void Hashtable_put(Hashtable* this, unsigned int key, void* value);
+void Hashtable_clear(Hashtable* this);
+
+void Hashtable_setSize(Hashtable* this, unsigned int size);
+
+void Hashtable_put(Hashtable* this, hkey_t key, void* value);
-void* Hashtable_remove(Hashtable* this, unsigned int key);
+void* Hashtable_remove(Hashtable* this, hkey_t key);
-void* Hashtable_get(Hashtable* this, unsigned int key);
+void* Hashtable_get(Hashtable* this, hkey_t key);
void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData);
diff --git a/Header.c b/Header.c
index d2f3b88..24c4077 100644
--- a/Header.c
+++ b/Header.c
@@ -1,20 +1,24 @@
/*
htop - Header.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Header.h"
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "CRT.h"
-#include "StringUtils.h"
+#include "Macros.h"
+#include "Object.h"
#include "Platform.h"
+#include "ProvideCurses.h"
+#include "XUtils.h"
-#include <assert.h>
-#include <time.h>
-#include <string.h>
-#include <stdlib.h>
Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns) {
Header* this = xCalloc(1, sizeof(Header));
@@ -59,17 +63,17 @@ void Header_writeBackToSettings(const Header* this) {
Vector* vec = this->columns[col];
int len = Vector_size(vec);
- colSettings->names = xCalloc(len+1, sizeof(char*));
+ colSettings->names = xCalloc(len + 1, sizeof(char*));
colSettings->modes = xCalloc(len, sizeof(int));
colSettings->len = len;
for (int i = 0; i < len; i++) {
Meter* meter = (Meter*) Vector_get(vec, i);
- char* name = xCalloc(64, sizeof(char));
+ char* name;
if (meter->param) {
- xSnprintf(name, 63, "%s(%d)", As_Meter(meter)->name, meter->param);
+ xAsprintf(&name, "%s(%d)", As_Meter(meter)->name, meter->param);
} else {
- xSnprintf(name, 63, "%s", As_Meter(meter)->name);
+ xAsprintf(&name, "%s", As_Meter(meter)->name);
}
colSettings->names[i] = name;
colSettings->modes[i] = meter->mode;
@@ -84,11 +88,12 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
int param = 0;
if (paren) {
int ok = sscanf(paren, "(%10d)", &param);
- if (!ok) param = 0;
+ if (!ok)
+ param = 0;
*paren = '\0';
}
MeterModeId mode = TEXT_METERMODE;
- for (MeterClass** type = Platform_meterTypes; *type; type++) {
+ for (const MeterClass* const* type = Platform_meterTypes; *type; type++) {
if (String_eq(name, (*type)->name)) {
Meter* meter = Meter_new(this->pl, param, *type);
Vector_add(meters, meter);
@@ -96,8 +101,10 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column) {
break;
}
}
+
if (paren)
*paren = '(';
+
return mode;
}
@@ -106,11 +113,12 @@ void Header_setMode(Header* this, int i, MeterModeId mode, int column) {
if (i >= Vector_size(meters))
return;
+
Meter* meter = (Meter*) Vector_get(meters, i);
Meter_setMode(meter, mode);
}
-Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column) {
+Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column) {
Vector* meters = this->columns[column];
Meter* meter = Meter_new(this->pl, param, type);
@@ -123,21 +131,6 @@ int Header_size(Header* this, int column) {
return Vector_size(meters);
}
-char* Header_readMeterName(Header* this, int i, int column) {
- Vector* meters = this->columns[column];
- Meter* meter = (Meter*) Vector_get(meters, i);
-
- int nameLen = strlen(Meter_name(meter));
- int len = nameLen + 100;
- char* name = xMalloc(len);
- memcpy(name, Meter_name(meter), nameLen);
- name[nameLen] = '\0';
- if (meter->param)
- xSnprintf(name + nameLen, len - nameLen, "(%d)", meter->param);
-
- return name;
-}
-
MeterModeId Header_readMeterMode(Header* this, int i, int column) {
Vector* meters = this->columns[column];
@@ -149,8 +142,9 @@ void Header_reinit(Header* this) {
Header_forEachColumn(this, col) {
for (int i = 0; i < Vector_size(this->columns[col]); i++) {
Meter* meter = (Meter*) Vector_get(this->columns[col], i);
- if (Meter_initFn(meter))
+ if (Meter_initFn(meter)) {
Meter_init(meter);
+ }
}
}
}
diff --git a/Header.h b/Header.h
index b221bec..870fbd8 100644
--- a/Header.h
+++ b/Header.h
@@ -3,18 +3,19 @@
/*
htop - Header.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
+#include "ProcessList.h"
#include "Settings.h"
#include "Vector.h"
typedef struct Header_ {
Vector** columns;
Settings* settings;
- struct ProcessList_* pl;
+ ProcessList* pl;
int nrColumns;
int pad;
int height;
@@ -22,7 +23,7 @@ typedef struct Header_ {
#define Header_forEachColumn(this_, i_) for (int (i_)=0; (i_) < (this_)->nrColumns; ++(i_))
-Header* Header_new(struct ProcessList_* pl, Settings* settings, int nrColumns);
+Header* Header_new(ProcessList* pl, Settings* settings, int nrColumns);
void Header_delete(Header* this);
@@ -34,12 +35,10 @@ MeterModeId Header_addMeterByName(Header* this, char* name, int column);
void Header_setMode(Header* this, int i, MeterModeId mode, int column);
-Meter* Header_addMeterByClass(Header* this, MeterClass* type, int param, int column);
+Meter* Header_addMeterByClass(Header* this, const MeterClass* type, int param, int column);
int Header_size(Header* this, int column);
-char* Header_readMeterName(Header* this, int i, int column);
-
MeterModeId Header_readMeterMode(Header* this, int i, int column);
void Header_reinit(Header* this);
diff --git a/HostnameMeter.c b/HostnameMeter.c
index 60bd812..af8e349 100644
--- a/HostnameMeter.c
+++ b/HostnameMeter.c
@@ -1,27 +1,30 @@
/*
htop - HostnameMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "HostnameMeter.h"
+#include "config.h" // IWYU pragma: keep
-#include "CRT.h"
+#include "HostnameMeter.h"
#include <unistd.h>
+#include "CRT.h"
+#include "Object.h"
+
-int HostnameMeter_attributes[] = {
+static const int HostnameMeter_attributes[] = {
HOSTNAME
};
-static void HostnameMeter_updateValues(Meter* this, char* buffer, int size) {
+static void HostnameMeter_updateValues(Meter* this, char* buffer, size_t size) {
(void) this;
- gethostname(buffer, size-1);
+ gethostname(buffer, size - 1);
}
-MeterClass HostnameMeter_class = {
+const MeterClass HostnameMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
diff --git a/HostnameMeter.h b/HostnameMeter.h
index ecfab1a..77fe3da 100644
--- a/HostnameMeter.h
+++ b/HostnameMeter.h
@@ -3,14 +3,12 @@
/*
htop - HostnameMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
-extern int HostnameMeter_attributes[];
-
-extern MeterClass HostnameMeter_class;
+extern const MeterClass HostnameMeter_class;
#endif
diff --git a/IncSet.c b/IncSet.c
index e9e2893..d280caf 100644
--- a/IncSet.c
+++ b/IncSet.c
@@ -1,17 +1,24 @@
/*
htop - IncSet.c
(C) 2005-2012 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "IncSet.h"
-#include "StringUtils.h"
-#include "ListItem.h"
-#include "CRT.h"
+
+#include <ctype.h>
#include <string.h>
#include <stdlib.h>
+#include "CRT.h"
+#include "ListItem.h"
+#include "Object.h"
+#include "ProvideCurses.h"
+#include "XUtils.h"
+
static void IncMode_reset(IncMode* mode) {
mode->index = 0;
@@ -72,7 +79,10 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
ListItem* line = (ListItem*)Vector_get(lines, i);
if (String_contains_i(line->value, incFilter)) {
Panel_add(panel, (Object*)line);
- if (selected == (Object*)line) Panel_setSelected(panel, n);
+ if (selected == (Object*)line) {
+ Panel_setSelected(panel, n);
+ }
+
n++;
}
}
@@ -80,7 +90,9 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
for (int i = 0; i < Vector_size(lines); i++) {
Object* line = Vector_get(lines, i);
Panel_add(panel, line);
- if (selected == line) Panel_setSelected(panel, i);
+ if (selected == line) {
+ Panel_setSelected(panel, i);
+ }
}
}
}
@@ -95,10 +107,11 @@ static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelVa
break;
}
}
- if (found)
- FunctionBar_draw(mode->bar, mode->buffer);
- else
- FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
+
+ FunctionBar_drawExtra(mode->bar,
+ mode->buffer,
+ found ? -1 : CRT_colors[FAILED_SEARCH],
+ true);
return found;
}
@@ -106,11 +119,18 @@ static bool IncMode_find(IncMode* mode, Panel* panel, IncMode_GetPanelValue getP
int size = Panel_size(panel);
int here = Panel_getSelectedIndex(panel);
int i = here;
- for(;;) {
- i+=step;
- if (i == size) i = 0;
- if (i == -1) i = size - 1;
- if (i == here) return false;
+ for (;;) {
+ i += step;
+ if (i == size) {
+ i = 0;
+ }
+ if (i == -1) {
+ i = size - 1;
+ }
+ if (i == here) {
+ return false;
+ }
+
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
Panel_setSelected(panel, i);
return true;
@@ -129,22 +149,27 @@ bool IncSet_prev(IncSet* this, IncType type, Panel* panel, IncMode_GetPanelValue
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
if (ch == ERR)
return true;
+
IncMode* mode = this->active;
int size = Panel_size(panel);
bool filterChanged = false;
bool doSearch = true;
if (ch == KEY_F(3)) {
- if (size == 0) return true;
+ if (size == 0)
+ return true;
+
IncMode_find(mode, panel, getPanelValue, 1);
doSearch = false;
- } else if (ch < 255 && isprint((char)ch)) {
+ } else if (0 < ch && ch < 255 && isprint((unsigned char)ch)) {
if (mode->index < INCMODE_MAX) {
mode->buffer[mode->index] = ch;
mode->index++;
mode->buffer[mode->index] = 0;
if (mode->isFilter) {
filterChanged = true;
- if (mode->index == 1) this->filtering = true;
+ if (mode->index == 1) {
+ this->filtering = true;
+ }
}
}
} else if ((ch == KEY_BACKSPACE || ch == 127)) {
@@ -162,7 +187,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
doSearch = false;
}
} else if (ch == KEY_RESIZE) {
- Panel_resize(panel, COLS, LINES-panel->y-1);
+ Panel_resize(panel, COLS, LINES - panel->y - 1);
} else {
if (mode->isFilter) {
filterChanged = true;
@@ -177,7 +202,7 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
}
this->active = NULL;
Panel_setDefaultBar(panel);
- FunctionBar_draw(this->defaultBar, NULL);
+ FunctionBar_draw(this->defaultBar);
doSearch = false;
}
if (doSearch) {
@@ -191,22 +216,20 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
const char* IncSet_getListItemValue(Panel* panel, int i) {
ListItem* l = (ListItem*) Panel_get(panel, i);
- if (l)
- return l->value;
- return "";
+ return l ? l->value : "";
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
- FunctionBar_draw(this->active->bar, this->active->buffer);
+ FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
panel->currentBar = this->active->bar;
}
-void IncSet_drawBar(IncSet* this) {
+void IncSet_drawBar(const IncSet* this) {
if (this->active) {
- FunctionBar_draw(this->active->bar, this->active->buffer);
+ FunctionBar_drawExtra(this->active->bar, this->active->buffer, -1, true);
} else {
- FunctionBar_draw(this->defaultBar, NULL);
+ FunctionBar_draw(this->defaultBar);
}
}
diff --git a/IncSet.h b/IncSet.h
index 2fb22c4..b07840f 100644
--- a/IncSet.h
+++ b/IncSet.h
@@ -3,13 +3,16 @@
/*
htop - IncSet.h
(C) 2005-2012 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+#include <stddef.h>
+
#include "FunctionBar.h"
#include "Panel.h"
-#include <stdbool.h>
+#include "Vector.h"
#define INCMODE_MAX 40
@@ -18,10 +21,8 @@ typedef enum {
INC_FILTER = 1
} IncType;
-#define IncSet_filter(inc_) (inc_->filtering ? inc_->modes[INC_FILTER].buffer : NULL)
-
typedef struct IncMode_ {
- char buffer[INCMODE_MAX+1];
+ char buffer[INCMODE_MAX + 1];
int index;
FunctionBar* bar;
bool isFilter;
@@ -35,6 +36,10 @@ typedef struct IncSet_ {
bool found;
} IncSet;
+static inline const char* IncSet_filter(const IncSet* this) {
+ return this->filtering ? this->modes[INC_FILTER].buffer : NULL;
+}
+
typedef const char* (*IncMode_GetPanelValue)(Panel*, int);
void IncSet_reset(IncSet* this, IncType type);
@@ -53,7 +58,7 @@ const char* IncSet_getListItemValue(Panel* panel, int i);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
-void IncSet_drawBar(IncSet* this);
+void IncSet_drawBar(const IncSet* this);
int IncSet_synthesizeEvent(IncSet* this, int x);
diff --git a/InfoScreen.c b/InfoScreen.c
index 8a6a3be..ceb29f7 100644
--- a/InfoScreen.c
+++ b/InfoScreen.c
@@ -1,17 +1,18 @@
+#include "config.h" // IWYU pragma: keep
+
#include "InfoScreen.h"
-#include "config.h"
-#include "Object.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "CRT.h"
#include "IncSet.h"
#include "ListItem.h"
-#include "Platform.h"
-#include "StringUtils.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <stdarg.h>
+#include "Object.h"
+#include "ProvideCurses.h"
+#include "XUtils.h"
static const char* const InfoScreenFunctions[] = {"Search ", "Filter ", "Refresh", "Done ", NULL};
@@ -20,7 +21,7 @@ static const char* const InfoScreenKeys[] = {"F3", "F4", "F5", "Esc"};
static int InfoScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(5), 27};
-InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, const char* panelHeader) {
+InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader) {
this->process = process;
if (!bar) {
bar = FunctionBar_new(InfoScreenFunctions, InfoScreenKeys, InfoScreenEvents);
@@ -42,45 +43,56 @@ InfoScreen* InfoScreen_done(InfoScreen* this) {
void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
+
+ char* title = xMalloc(COLS + 1);
+ int len = vsnprintf(title, COLS + 1, fmt, ap);
+ if (len > COLS) {
+ memset(&title[COLS - 3], '.', 3);
+ }
+
attrset(CRT_colors[METER_TEXT]);
mvhline(0, 0, ' ', COLS);
- (void) wmove(stdscr, 0, 0);
- vw_printw(stdscr, fmt, ap);
+ mvwprintw(stdscr, 0, 0, title);
attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
- Panel_draw(this->display, true);
+ Panel_draw(this->display, true, true);
IncSet_drawBar(this->inc);
+ free(title);
va_end(ap);
}
void InfoScreen_addLine(InfoScreen* this, const char* line) {
Vector_add(this->lines, (Object*) ListItem_new(line, 0));
const char* incFilter = IncSet_filter(this->inc);
- if (!incFilter || String_contains_i(line, incFilter))
- Panel_add(this->display, (Object*)Vector_get(this->lines, Vector_size(this->lines)-1));
+ if (!incFilter || String_contains_i(line, incFilter)) {
+ Panel_add(this->display, Vector_get(this->lines, Vector_size(this->lines) - 1));
+ }
}
void InfoScreen_appendLine(InfoScreen* this, const char* line) {
- ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines)-1);
+ ListItem* last = (ListItem*)Vector_get(this->lines, Vector_size(this->lines) - 1);
ListItem_append(last, line);
const char* incFilter = IncSet_filter(this->inc);
- if (incFilter && Panel_get(this->display, Panel_size(this->display)-1) != (Object*)last && String_contains_i(line, incFilter))
+ if (incFilter && Panel_get(this->display, Panel_size(this->display) - 1) != (Object*)last && String_contains_i(line, incFilter)) {
Panel_add(this->display, (Object*)last);
+ }
}
void InfoScreen_run(InfoScreen* this) {
Panel* panel = this->display;
- if (As_InfoScreen(this)->scan) InfoScreen_scan(this);
+ if (As_InfoScreen(this)->scan)
+ InfoScreen_scan(this);
+
InfoScreen_draw(this);
bool looping = true;
while (looping) {
- Panel_draw(panel, true);
+ Panel_draw(panel, true, true);
if (this->inc->active) {
- (void) move(LINES-1, CRT_cursorX);
+ (void) move(LINES - 1, CRT_cursorX);
}
set_escdelay(25);
int ch = getch();
@@ -102,7 +114,7 @@ void InfoScreen_run(InfoScreen* this) {
} else if (mevent.y == LINES - 1) {
ch = IncSet_synthesizeEvent(this->inc, mevent.x);
}
- }
+ }
}
if (this->inc->active) {
@@ -123,20 +135,25 @@ void InfoScreen_run(InfoScreen* this) {
break;
case KEY_F(5):
clear();
- if (As_InfoScreen(this)->scan) InfoScreen_scan(this);
+ if (As_InfoScreen(this)->scan)
+ InfoScreen_scan(this);
+
InfoScreen_draw(this);
break;
case '\014': // Ctrl+L
clear();
InfoScreen_draw(this);
break;
- case 'q':
case 27:
+ case 'q':
case KEY_F(10):
looping = false;
break;
case KEY_RESIZE:
- Panel_resize(panel, COLS, LINES-2);
+ Panel_resize(panel, COLS, LINES - 2);
+ if (As_InfoScreen(this)->scan)
+ InfoScreen_scan(this);
+
InfoScreen_draw(this);
break;
default:
diff --git a/InfoScreen.h b/InfoScreen.h
index 93665c1..0d80367 100644
--- a/InfoScreen.h
+++ b/InfoScreen.h
@@ -1,12 +1,25 @@
#ifndef HEADER_InfoScreen
#define HEADER_InfoScreen
-#include "Process.h"
-#include "Panel.h"
+#include <stdbool.h>
+
#include "FunctionBar.h"
#include "IncSet.h"
+#include "Macros.h"
+#include "Object.h"
+#include "Panel.h"
+#include "Process.h"
+#include "Vector.h"
-typedef struct InfoScreen_ InfoScreen;
+
+typedef struct InfoScreen_ {
+ Object super;
+ const Process* process;
+ Panel* display;
+ FunctionBar* bar;
+ IncSet* inc;
+ Vector* lines;
+} InfoScreen;
typedef void(*InfoScreen_Scan)(InfoScreen*);
typedef void(*InfoScreen_Draw)(InfoScreen*);
@@ -14,32 +27,24 @@ typedef void(*InfoScreen_OnErr)(InfoScreen*);
typedef bool(*InfoScreen_OnKey)(InfoScreen*, int);
typedef struct InfoScreenClass_ {
- ObjectClass super;
+ const ObjectClass super;
const InfoScreen_Scan scan;
const InfoScreen_Draw draw;
const InfoScreen_OnErr onErr;
const InfoScreen_OnKey onKey;
} InfoScreenClass;
-#define As_InfoScreen(this_) ((InfoScreenClass*)(((InfoScreen*)(this_))->super.klass))
+#define As_InfoScreen(this_) ((const InfoScreenClass*)(((InfoScreen*)(this_))->super.klass))
#define InfoScreen_scan(this_) As_InfoScreen(this_)->scan((InfoScreen*)(this_))
#define InfoScreen_draw(this_) As_InfoScreen(this_)->draw((InfoScreen*)(this_))
#define InfoScreen_onErr(this_) As_InfoScreen(this_)->onErr((InfoScreen*)(this_))
#define InfoScreen_onKey(this_, ch_) As_InfoScreen(this_)->onKey((InfoScreen*)(this_), ch_)
-struct InfoScreen_ {
- Object super;
- Process* process;
- Panel* display;
- FunctionBar* bar;
- IncSet* inc;
- Vector* lines;
-};
-
-InfoScreen* InfoScreen_init(InfoScreen* this, Process* process, FunctionBar* bar, int height, const char* panelHeader);
+InfoScreen* InfoScreen_init(InfoScreen* this, const Process* process, FunctionBar* bar, int height, const char* panelHeader);
InfoScreen* InfoScreen_done(InfoScreen* this);
+ATTR_FORMAT(printf, 2, 3)
void InfoScreen_drawTitled(InfoScreen* this, const char* fmt, ...);
void InfoScreen_addLine(InfoScreen* this, const char* line);
diff --git a/ListItem.c b/ListItem.c
index 73129fa..c3c1a7d 100644
--- a/ListItem.c
+++ b/ListItem.c
@@ -1,19 +1,21 @@
/*
htop - ListItem.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "ListItem.h"
+#include "config.h" // IWYU pragma: keep
-#include "CRT.h"
-#include "StringUtils.h"
-#include "RichString.h"
+#include "ListItem.h"
-#include <string.h>
#include <assert.h>
#include <stdlib.h>
+#include <string.h>
+
+#include "CRT.h"
+#include "RichString.h"
+#include "XUtils.h"
static void ListItem_delete(Object* cast) {
@@ -22,32 +24,22 @@ static void ListItem_delete(Object* cast) {
free(this);
}
-static void ListItem_display(Object* cast, RichString* out) {
- ListItem* const this = (ListItem*)cast;
+static void ListItem_display(const Object* cast, RichString* out) {
+ const ListItem* const this = (const ListItem*)cast;
assert (this != NULL);
- /*
- int len = strlen(this->value)+1;
- char buffer[len+1];
- xSnprintf(buffer, len, "%s", this->value);
- */
+
if (this->moving) {
RichString_write(out, CRT_colors[DEFAULT_COLOR],
#ifdef HAVE_LIBNCURSESW
- CRT_utf8 ? "↕ " :
+ CRT_utf8 ? "↕ " :
#endif
- "+ ");
+ "+ ");
} else {
RichString_prune(out);
}
- RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value/*buffer*/);
+ RichString_append(out, CRT_colors[DEFAULT_COLOR], this->value);
}
-ObjectClass ListItem_class = {
- .display = ListItem_display,
- .delete = ListItem_delete,
- .compare = ListItem_compare
-};
-
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
this->value = xStrdup(value);
@@ -57,20 +49,22 @@ ListItem* ListItem_new(const char* value, int key) {
}
void ListItem_append(ListItem* this, const char* text) {
- int oldLen = strlen(this->value);
- int textLen = strlen(text);
- int newLen = strlen(this->value) + textLen;
+ size_t oldLen = strlen(this->value);
+ size_t textLen = strlen(text);
+ size_t newLen = oldLen + textLen;
this->value = xRealloc(this->value, newLen + 1);
memcpy(this->value + oldLen, text, textLen);
this->value[newLen] = '\0';
}
-const char* ListItem_getRef(ListItem* this) {
- return this->value;
-}
-
-long ListItem_compare(const void* cast1, const void* cast2) {
- ListItem* obj1 = (ListItem*) cast1;
- ListItem* obj2 = (ListItem*) cast2;
+static long ListItem_compare(const void* cast1, const void* cast2) {
+ const ListItem* obj1 = (const ListItem*) cast1;
+ const ListItem* obj2 = (const ListItem*) cast2;
return strcmp(obj1->value, obj2->value);
}
+
+const ObjectClass ListItem_class = {
+ .display = ListItem_display,
+ .delete = ListItem_delete,
+ .compare = ListItem_compare
+};
diff --git a/ListItem.h b/ListItem.h
index 3fea725..87a7c07 100644
--- a/ListItem.h
+++ b/ListItem.h
@@ -3,10 +3,12 @@
/*
htop - ListItem.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+
#include "Object.h"
typedef struct ListItem_ {
@@ -16,14 +18,14 @@ typedef struct ListItem_ {
bool moving;
} ListItem;
-extern ObjectClass ListItem_class;
+extern const ObjectClass ListItem_class;
ListItem* ListItem_new(const char* value, int key);
void ListItem_append(ListItem* this, const char* text);
-const char* ListItem_getRef(ListItem* this);
-
-long ListItem_compare(const void* cast1, const void* cast2);
+static inline const char* ListItem_getRef(const ListItem* this) {
+ return this->value;
+}
#endif
diff --git a/LoadAverageMeter.c b/LoadAverageMeter.c
index db397b6..d5424cd 100644
--- a/LoadAverageMeter.c
+++ b/LoadAverageMeter.c
@@ -1,29 +1,36 @@
/*
htop - LoadAverageMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "LoadAverageMeter.h"
#include "CRT.h"
+#include "Object.h"
#include "Platform.h"
+#include "RichString.h"
+#include "XUtils.h"
-int LoadAverageMeter_attributes[] = {
- LOAD_AVERAGE_ONE, LOAD_AVERAGE_FIVE, LOAD_AVERAGE_FIFTEEN
+static const int LoadAverageMeter_attributes[] = {
+ LOAD_AVERAGE_ONE,
+ LOAD_AVERAGE_FIVE,
+ LOAD_AVERAGE_FIFTEEN
};
-int LoadMeter_attributes[] = { LOAD };
+static const int LoadMeter_attributes[] = {
+ LOAD
+};
-static void LoadAverageMeter_updateValues(Meter* this, char* buffer, int size) {
+static void LoadAverageMeter_updateValues(Meter* this, char* buffer, size_t size) {
Platform_getLoadAverage(&this->values[0], &this->values[1], &this->values[2]);
xSnprintf(buffer, size, "%.2f/%.2f/%.2f", this->values[0], this->values[1], this->values[2]);
}
-static void LoadAverageMeter_display(Object* cast, RichString* out) {
- Meter* this = (Meter*)cast;
+static void LoadAverageMeter_display(const Object* cast, RichString* out) {
+ const Meter* this = (const Meter*)cast;
char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD_AVERAGE_ONE], buffer);
@@ -33,7 +40,7 @@ static void LoadAverageMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
}
-static void LoadMeter_updateValues(Meter* this, char* buffer, int size) {
+static void LoadMeter_updateValues(Meter* this, char* buffer, size_t size) {
double five, fifteen;
Platform_getLoadAverage(&this->values[0], &five, &fifteen);
if (this->values[0] > this->total) {
@@ -42,14 +49,14 @@ static void LoadMeter_updateValues(Meter* this, char* buffer, int size) {
xSnprintf(buffer, size, "%.2f", this->values[0]);
}
-static void LoadMeter_display(Object* cast, RichString* out) {
- Meter* this = (Meter*)cast;
+static void LoadMeter_display(const Object* cast, RichString* out) {
+ const Meter* this = (const Meter*)cast;
char buffer[20];
- xSnprintf(buffer, sizeof(buffer), "%.2f ", ((Meter*)this)->values[0]);
+ xSnprintf(buffer, sizeof(buffer), "%.2f ", this->values[0]);
RichString_write(out, CRT_colors[LOAD], buffer);
}
-MeterClass LoadAverageMeter_class = {
+const MeterClass LoadAverageMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -66,7 +73,7 @@ MeterClass LoadAverageMeter_class = {
.caption = "Load average: "
};
-MeterClass LoadMeter_class = {
+const MeterClass LoadMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
diff --git a/LoadAverageMeter.h b/LoadAverageMeter.h
index 83d3c8f..776c8bf 100644
--- a/LoadAverageMeter.h
+++ b/LoadAverageMeter.h
@@ -3,18 +3,14 @@
/*
htop - LoadAverageMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
-extern int LoadAverageMeter_attributes[];
+extern const MeterClass LoadAverageMeter_class;
-extern int LoadMeter_attributes[];
-
-extern MeterClass LoadAverageMeter_class;
-
-extern MeterClass LoadMeter_class;
+extern const MeterClass LoadMeter_class;
#endif
diff --git a/Macros.h b/Macros.h
index cb84b29..64aaefa 100644
--- a/Macros.h
+++ b/Macros.h
@@ -1,16 +1,62 @@
#ifndef HEADER_Macros
#define HEADER_Macros
+#include <assert.h> // IWYU pragma: keep
+
#ifndef MINIMUM
-#define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
+#define MINIMUM(a, b) ((a) < (b) ? (a) : (b))
#endif
#ifndef MAXIMUM
-#define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
+#define MAXIMUM(a, b) ((a) > (b) ? (a) : (b))
#endif
#ifndef CLAMP
-#define CLAMP(x, low, high) (((x) > (high)) ? (high) : MAXIMUM(x, low))
+#define CLAMP(x, low, high) (assert((low) <= (high)), ((x) > (high)) ? (high) : MAXIMUM(x, low))
+#endif
+
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#ifndef SPACESHIP_NUMBER
+#define SPACESHIP_NUMBER(a, b) (((a) > (b)) - ((a) < (b)))
+#endif
+
+#ifndef SPACESHIP_NULLSTR
+#define SPACESHIP_NULLSTR(a, b) strcmp((a) ? (a) : "", (b) ? (b) : "")
+#endif
+
+#ifdef __GNUC__ // defined by GCC and Clang
+
+#define ATTR_FORMAT(type, index, check) __attribute__((format (type, index, check)))
+#define ATTR_NONNULL __attribute__((nonnull))
+#define ATTR_NORETURN __attribute__((noreturn))
+#define ATTR_UNUSED __attribute__((unused))
+
+#else /* __GNUC__ */
+
+#define ATTR_FORMAT(type, index, check)
+#define ATTR_NONNULL
+#define ATTR_NORETURN
+#define ATTR_UNUSED
+
+#endif /* __GNUC__ */
+
+// ignore casts discarding const specifier, e.g.
+// const char [] -> char * / void *
+// const char *[2]' -> char *const *
+#ifdef __clang__
+#define IGNORE_WCASTQUAL_BEGIN _Pragma("clang diagnostic push") \
+ _Pragma("clang diagnostic ignored \"-Wcast-qual\"")
+#define IGNORE_WCASTQUAL_END _Pragma("clang diagnostic pop")
+#elif defined(__GNUC__)
+#define IGNORE_WCASTQUAL_BEGIN _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
+#define IGNORE_WCASTQUAL_END _Pragma("GCC diagnostic pop")
+#else
+#define IGNORE_WCASTQUAL_BEGIN
+#define IGNORE_WCASTQUAL_END
#endif
#endif
diff --git a/MainPanel.c b/MainPanel.c
index eb7e663..5e3a54c 100644
--- a/MainPanel.c
+++ b/MainPanel.c
@@ -2,33 +2,35 @@
htop - ColumnsPanel.c
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "MainPanel.h"
-#include "Process.h"
-#include "Platform.h"
-#include "CRT.h"
+#include <ctype.h>
#include <stdlib.h>
-static const char* const MainFunctions[] = {"Help ", "Setup ", "Search ", "Filter ", "Tree ", "SortBy ", "Nice - ", "Nice + ", "Kill ", "Quit ", NULL};
+#include "CRT.h"
+#include "FunctionBar.h"
+#include "Platform.h"
+#include "Process.h"
+#include "ProcessList.h"
+#include "ProvideCurses.h"
+#include "Settings.h"
+#include "XUtils.h"
+
+
+static const char* const MainFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
void MainPanel_updateTreeFunctions(MainPanel* this, bool mode) {
FunctionBar* bar = MainPanel_getFunctionBar(this);
- if (mode) {
- FunctionBar_setLabel(bar, KEY_F(5), "Sorted");
- FunctionBar_setLabel(bar, KEY_F(6), "Collap");
- } else {
- FunctionBar_setLabel(bar, KEY_F(5), "Tree ");
- FunctionBar_setLabel(bar, KEY_F(6), "SortBy");
- }
+ FunctionBar_setLabel(bar, KEY_F(5), mode ? "Sorted" : "Tree ");
}
void MainPanel_pidSearch(MainPanel* this, int ch) {
Panel* super = (Panel*) this;
- pid_t pid = ch-48 + this->pidSearch;
+ pid_t pid = ch - 48 + this->pidSearch;
for (int i = 0; i < Panel_size(super); i++) {
Process* p = (Process*) Panel_get(super, i);
if (p && p->pid == pid) {
@@ -49,15 +51,17 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK;
+ if (ch != ERR)
+ this->state->hideProcessSelection = false;
+
if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch);
- ProcessList* pl = this->state->pl;
+ const ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
- settings->treeView = false;
} else {
reaction |= Action_setSortKey(settings, field);
}
@@ -75,11 +79,12 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
}
result = HANDLED;
} else if (ch == 27) {
+ this->state->hideProcessSelection = true;
return HANDLED;
} else if (ch != ERR && ch > 0 && ch < KEY_MAX && this->keys[ch]) {
reaction |= (this->keys[ch])(this->state);
result = HANDLED;
- } else if (isdigit(ch)) {
+ } else if (0 < ch && ch < 255 && isdigit((unsigned char)ch)) {
MainPanel_pidSearch(this, ch);
} else {
if (ch != ERR) {
@@ -92,6 +97,9 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
+ if (this->state->pauseProcessUpdate) {
+ FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
+ }
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));
@@ -125,9 +133,7 @@ int MainPanel_selectedPid(MainPanel* this) {
const char* MainPanel_getValue(MainPanel* this, int i) {
Process* p = (Process*) Panel_get((Panel*)this, i);
- if (p)
- return p->comm;
- return "";
+ return Process_getCommand(p);
}
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
@@ -143,14 +149,18 @@ bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Ar
}
if (!anyTagged) {
Process* p = (Process*) Panel_getSelected(super);
- if (p) ok = fn(p, arg) && ok;
+ if (p) {
+ ok &= fn(p, arg);
+ }
}
+
if (wasAnyTagged)
*wasAnyTagged = anyTagged;
+
return ok;
}
-PanelClass MainPanel_class = {
+const PanelClass MainPanel_class = {
.super = {
.extends = Class(Panel),
.delete = MainPanel_delete
diff --git a/MainPanel.h b/MainPanel.h
index ca06472..03a1aff 100644
--- a/MainPanel.h
+++ b/MainPanel.h
@@ -4,19 +4,27 @@
htop - ColumnsPanel.h
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
+#include <stdbool.h>
+#include <sys/types.h>
+
#include "Action.h"
#include "IncSet.h"
+#include "Object.h"
#include "Panel.h"
+#include "Process.h"
+
typedef struct MainPanel_ {
Panel super;
State* state;
IncSet* inc;
- Htop_Action *keys;
+ Htop_Action* keys;
pid_t pidSearch;
} MainPanel;
@@ -34,9 +42,9 @@ const char* MainPanel_getValue(MainPanel* this, int i);
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged);
-extern PanelClass MainPanel_class;
+extern const PanelClass MainPanel_class;
-MainPanel* MainPanel_new();
+MainPanel* MainPanel_new(void);
void MainPanel_setState(MainPanel* this, State* state);
diff --git a/Makefile.am b/Makefile.am
index 50fb586..09790fa 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,63 +1,161 @@
-
-ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = subdir-objects
bin_PROGRAMS = htop
dist_man_MANS = htop.1
-EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png \
+EXTRA_DIST = $(dist_man_MANS) htop.desktop htop.png htop.svg \
install-sh autogen.sh missing
applicationsdir = $(datadir)/applications
applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png
+appicondir = $(datadir)/icons/hicolor/scalable/apps
+appicon_DATA = htop.svg
-AM_CFLAGS += -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
+AM_CFLAGS += -pedantic -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS =
-AM_CPPFLAGS = -DNDEBUG
-
-myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \
-ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \
-DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
-LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
-BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
-SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \
-TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
-HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
-InfoScreen.c XAlloc.c
-
-myhtopheaders = AvailableColumnsPanel.h AvailableMetersPanel.h \
-CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
-CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
-Hashtable.h Header.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
-BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
-ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \
-TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
-AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
-EnvScreen.h InfoScreen.h XAlloc.h Macros.h
+
+myhtopsources = \
+ Action.c \
+ Affinity.c \
+ AffinityPanel.c \
+ AvailableColumnsPanel.c \
+ AvailableMetersPanel.c \
+ BatteryMeter.c \
+ CategoriesPanel.c \
+ ClockMeter.c \
+ ColorsPanel.c \
+ ColumnsPanel.c \
+ CommandScreen.c \
+ Compat.c \
+ CPUMeter.c \
+ CRT.c \
+ DateMeter.c \
+ DateTimeMeter.c \
+ DiskIOMeter.c \
+ DisplayOptionsPanel.c \
+ EnvScreen.c \
+ FunctionBar.c \
+ Hashtable.c \
+ Header.c \
+ HostnameMeter.c \
+ htop.c \
+ IncSet.c \
+ InfoScreen.c \
+ ListItem.c \
+ LoadAverageMeter.c \
+ MainPanel.c \
+ MemoryMeter.c \
+ Meter.c \
+ MetersPanel.c \
+ NetworkIOMeter.c \
+ Object.c \
+ OpenFilesScreen.c \
+ OptionItem.c \
+ Panel.c \
+ Process.c \
+ ProcessList.c \
+ ProcessLocksScreen.c \
+ RichString.c \
+ ScreenManager.c \
+ Settings.c \
+ SignalsPanel.c \
+ SwapMeter.c \
+ TasksMeter.c \
+ TraceScreen.c \
+ UptimeMeter.c \
+ UsersTable.c \
+ Vector.c \
+ XUtils.c
+
+myhtopheaders = \
+ Action.h \
+ Affinity.h \
+ AffinityPanel.h \
+ AvailableColumnsPanel.h \
+ AvailableMetersPanel.h \
+ BatteryMeter.h \
+ CPUMeter.h \
+ CRT.h \
+ CategoriesPanel.h \
+ ClockMeter.h \
+ ColorsPanel.h \
+ ColumnsPanel.h \
+ CommandScreen.h \
+ Compat.h \
+ DateMeter.h \
+ DateTimeMeter.h \
+ DiskIOMeter.h \
+ DisplayOptionsPanel.h \
+ EnvScreen.h \
+ FunctionBar.h \
+ Hashtable.h \
+ Header.h \
+ HostnameMeter.h \
+ IncSet.h \
+ InfoScreen.h \
+ ListItem.h \
+ LoadAverageMeter.h \
+ Macros.h \
+ MainPanel.h \
+ MemoryMeter.h \
+ Meter.h \
+ MetersPanel.h \
+ NetworkIOMeter.h \
+ Object.h \
+ OpenFilesScreen.h \
+ OptionItem.h \
+ Panel.h \
+ Process.h \
+ ProcessList.h \
+ ProcessLocksScreen.h \
+ ProvideCurses.h \
+ RichString.h \
+ ScreenManager.h \
+ Settings.h \
+ SignalsPanel.h \
+ SwapMeter.h \
+ TasksMeter.h \
+ TraceScreen.h \
+ UptimeMeter.h \
+ UsersTable.h \
+ Vector.h \
+ XUtils.h
# Linux
# -----
linux_platform_headers = \
- linux/Platform.h \
- linux/IOPriorityPanel.h \
linux/IOPriority.h \
+ linux/IOPriorityPanel.h \
+ linux/LibSensors.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.h \
- linux/LinuxCRT.h \
- linux/Battery.h \
+ linux/Platform.h \
linux/PressureStallMeter.h \
+ linux/SELinuxMeter.h \
+ linux/SystemdMeter.h \
+ linux/ZramMeter.h \
+ linux/ZramStats.h \
zfs/ZfsArcMeter.h \
- zfs/ZfsCompressedArcMeter.h \
- zfs/ZfsArcStats.h
+ zfs/ZfsArcStats.h \
+ zfs/ZfsCompressedArcMeter.h
if HTOP_LINUX
AM_LDFLAGS += -rdynamic
-myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c \
-linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
-linux/PressureStallMeter.c \
-zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
+myhtopplatsources = \
+ linux/IOPriorityPanel.c \
+ linux/LibSensors.c \
+ linux/LinuxProcess.c \
+ linux/LinuxProcessList.c \
+ linux/Platform.c \
+ linux/PressureStallMeter.c \
+ linux/SELinuxMeter.c \
+ linux/SystemdMeter.c \
+ linux/ZramMeter.c \
+ zfs/ZfsArcMeter.c \
+ zfs/ZfsArcStats.c \
+ zfs/ZfsCompressedArcMeter.c
myhtopplatheaders = $(linux_platform_headers)
endif
@@ -69,8 +167,6 @@ freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
- freebsd/FreeBSDCRT.h \
- freebsd/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
@@ -78,7 +174,7 @@ freebsd_platform_headers = \
if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
-freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c \
+freebsd/FreeBSDProcess.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(freebsd_platform_headers)
@@ -90,14 +186,12 @@ endif
dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \
- dragonflybsd/DragonFlyBSDProcess.h \
- dragonflybsd/DragonFlyBSDCRT.h \
- dragonflybsd/Battery.h
+ dragonflybsd/DragonFlyBSDProcess.h
if HTOP_DRAGONFLYBSD
-AM_LDFLAGS += -lkvm -lkinfo -lexecinfo
+AM_LDFLAGS += -lkvm -lkinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
-dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
+dragonflybsd/DragonFlyBSDProcess.c
myhtopplatheaders = $(dragonflybsd_platform_headers)
endif
@@ -108,13 +202,11 @@ endif
openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \
- openbsd/OpenBSDProcess.h \
- openbsd/OpenBSDCRT.h \
- openbsd/Battery.h
+ openbsd/OpenBSDProcess.h
if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
-openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
+openbsd/OpenBSDProcess.c
myhtopplatheaders = $(openbsd_platform_headers)
endif
@@ -126,8 +218,6 @@ darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
- darwin/DarwinCRT.h \
- darwin/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h \
@@ -136,7 +226,7 @@ darwin_platform_headers = \
if HTOP_DARWIN
AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
-darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c \
+darwin/DarwinProcessList.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c zfs/openzfs_sysctl.c
myhtopplatheaders = $(darwin_platform_headers)
@@ -149,8 +239,6 @@ solaris_platform_headers = \
solaris/Platform.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
- solaris/SolarisCRT.h \
- solaris/Battery.h \
zfs/ZfsArcMeter.h \
zfs/ZfsCompressedArcMeter.h \
zfs/ZfsArcStats.h
@@ -158,7 +246,6 @@ solaris_platform_headers = \
if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
-solaris/SolarisCRT.c solaris/Battery.c \
zfs/ZfsArcMeter.c zfs/ZfsCompressedArcMeter.c zfs/ZfsArcStats.c
myhtopplatheaders = $(solaris_platform_headers)
@@ -170,14 +257,11 @@ endif
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/UnsupportedProcess.h \
- unsupported/UnsupportedProcessList.h \
- unsupported/UnsupportedCRT.h \
- unsupported/Battery.h
+ unsupported/UnsupportedProcessList.h
if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \
-unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \
-unsupported/UnsupportedCRT.c unsupported/Battery.c
+unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c
myhtopplatheaders = $(unsupported_platform_headers)
endif
@@ -191,16 +275,16 @@ target:
echo $(htop_SOURCES)
profile:
- $(MAKE) all CFLAGS="-pg" AM_CPPFLAGS="-pg -O2 -DNDEBUG"
+ $(MAKE) all AM_CPPFLAGS="-pg -O2 -DNDEBUG"
debug:
- $(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
+ $(MAKE) all AM_CPPFLAGS="-ggdb -DDEBUG"
coverage:
- $(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"
+ $(MAKE) all AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" AM_LDFLAGS="-lgcov"
cppcheck:
- cppcheck -q -v . --enable=all -DHAVE_CGROUP -DHAVE_OPENVZ -DHAVE_TASKSTATS
+ cppcheck -q -v . --enable=all -DHAVE_OPENVZ
dist-hook: $(top_distdir)/configure
@if grep 'pkg_m4_absent' '$(top_distdir)/configure'; then \
diff --git a/MemoryMeter.c b/MemoryMeter.c
index 91f75b9..9830bf5 100644
--- a/MemoryMeter.c
+++ b/MemoryMeter.c
@@ -1,42 +1,39 @@
/*
htop - MemoryMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "MemoryMeter.h"
#include "CRT.h"
+#include "Object.h"
#include "Platform.h"
+#include "RichString.h"
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <sys/param.h>
-#include <assert.h>
-
-int MemoryMeter_attributes[] = {
- MEMORY_USED, MEMORY_BUFFERS, MEMORY_CACHE
+static const int MemoryMeter_attributes[] = {
+ MEMORY_USED,
+ MEMORY_BUFFERS,
+ MEMORY_CACHE
};
-static void MemoryMeter_updateValues(Meter* this, char* buffer, int size) {
+static void MemoryMeter_updateValues(Meter* this, char* buffer, size_t size) {
int written;
Platform_setMemoryValues(this);
written = Meter_humanUnit(buffer, this->values[0], size);
- buffer += written;
- if ((size -= written) > 0) {
- *buffer++ = '/';
- size--;
- Meter_humanUnit(buffer, this->total, size);
- }
+ METER_BUFFER_CHECK(buffer, size, written);
+
+ METER_BUFFER_APPEND_CHR(buffer, size, '/');
+
+ Meter_humanUnit(buffer, this->total, size);
}
-static void MemoryMeter_display(Object* cast, RichString* out) {
+static void MemoryMeter_display(const Object* cast, RichString* out) {
char buffer[50];
- Meter* this = (Meter*)cast;
+ const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
@@ -51,7 +48,7 @@ static void MemoryMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[MEMORY_CACHE], buffer);
}
-MeterClass MemoryMeter_class = {
+const MeterClass MemoryMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
diff --git a/MemoryMeter.h b/MemoryMeter.h
index 771aa73..d299483 100644
--- a/MemoryMeter.h
+++ b/MemoryMeter.h
@@ -3,14 +3,12 @@
/*
htop - MemoryMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
-extern int MemoryMeter_attributes[];
-
-extern MeterClass MemoryMeter_class;
+extern const MeterClass MemoryMeter_class;
#endif
diff --git a/Meter.c b/Meter.c
index 00c49ce..945911c 100644
--- a/Meter.c
+++ b/Meter.c
@@ -1,56 +1,60 @@
/*
htop - Meter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Meter.h"
+#include "config.h" // IWYU pragma: keep
-#include "RichString.h"
-#include "Object.h"
-#include "CRT.h"
-#include "StringUtils.h"
-#include "Settings.h"
+#include "Meter.h"
+#include <assert.h>
#include <math.h>
-#include <string.h>
#include <stdlib.h>
-#include <stdarg.h>
-#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "Object.h"
+#include "ProvideCurses.h"
+#include "RichString.h"
+#include "Settings.h"
+#include "XUtils.h"
-#define GRAPH_DELAY (DEFAULT_DELAY/2)
#define GRAPH_HEIGHT 4 /* Unit: rows (lines) */
-MeterClass Meter_class = {
+const MeterClass Meter_class = {
.super = {
.extends = Class(Object)
}
};
-Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type) {
+Meter* Meter_new(const struct ProcessList_* pl, int param, const MeterClass* type) {
Meter* this = xCalloc(1, sizeof(Meter));
Object_setClass(this, type);
this->h = 1;
this->param = param;
this->pl = pl;
- type->curItems = type->maxItems;
- this->values = xCalloc(type->maxItems, sizeof(double));
+ this->curItems = type->maxItems;
+ this->values = type->maxItems ? xCalloc(type->maxItems, sizeof(double)) : NULL;
this->total = type->total;
this->caption = xStrdup(type->caption);
- if (Meter_initFn(this))
+ if (Meter_initFn(this)) {
Meter_init(this);
+ }
Meter_setMode(this, type->defaultMode);
return this;
}
-int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
- const char * prefix = "KMGTPEZY";
+int Meter_humanUnit(char* buffer, unsigned long int value, size_t size) {
+ const char* prefix = "KMGTPEZY";
unsigned long int powi = 1;
- unsigned int written, powj = 1, precision = 2;
+ unsigned int powj = 1, precision = 2;
- for(;;) {
+ for (;;) {
if (value / 1024 < powi)
break;
@@ -70,15 +74,13 @@ int Meter_humanUnit(char* buffer, unsigned long int value, int size) {
break;
}
- written = snprintf(buffer, size, "%.*f%c",
- precision, (double) value / powi, *prefix);
-
- return written;
+ return snprintf(buffer, size, "%.*f%c", precision, (double) value / powi, *prefix);
}
void Meter_delete(Object* cast) {
if (!cast)
return;
+
Meter* this = (Meter*) cast;
if (Meter_doneFn(this)) {
Meter_done(this);
@@ -94,7 +96,7 @@ void Meter_setCaption(Meter* this, const char* caption) {
this->caption = xStrdup(caption);
}
-static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) {
+static inline void Meter_displayBuffer(const Meter* this, const char* buffer, RichString* out) {
if (Object_displayFn(this)) {
Object_display(this, out);
} else {
@@ -103,21 +105,26 @@ static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* ou
}
void Meter_setMode(Meter* this, int modeIndex) {
- if (modeIndex > 0 && modeIndex == this->mode)
+ if (modeIndex > 0 && modeIndex == this->mode) {
return;
- if (!modeIndex)
+ }
+
+ if (!modeIndex) {
modeIndex = 1;
+ }
+
assert(modeIndex < LAST_METERMODE);
if (Meter_defaultMode(this) == CUSTOM_METERMODE) {
this->draw = Meter_drawFn(this);
- if (Meter_updateModeFn(this))
+ if (Meter_updateModeFn(this)) {
Meter_updateMode(this, modeIndex);
+ }
} else {
assert(modeIndex >= 1);
free(this->drawData);
this->drawData = NULL;
- MeterMode* mode = Meter_modes[modeIndex];
+ const MeterMode* mode = Meter_modes[modeIndex];
this->draw = mode->draw;
this->h = mode->h;
}
@@ -125,18 +132,20 @@ void Meter_setMode(Meter* this, int modeIndex) {
}
ListItem* Meter_toListItem(Meter* this, bool moving) {
- char mode[21];
- if (this->mode)
- xSnprintf(mode, 20, " [%s]", Meter_modes[this->mode]->uiName);
- else
+ char mode[20];
+ if (this->mode) {
+ xSnprintf(mode, sizeof(mode), " [%s]", Meter_modes[this->mode]->uiName);
+ } else {
mode[0] = '\0';
- char number[11];
- if (this->param > 0)
- xSnprintf(number, 10, " %d", this->param);
- else
+ }
+ char number[10];
+ if (this->param > 0) {
+ xSnprintf(number, sizeof(number), " %d", this->param);
+ } else {
number[0] = '\0';
- char buffer[51];
- xSnprintf(buffer, 50, "%s%s%s", Meter_uiName(this), number, mode);
+ }
+ char buffer[50];
+ xSnprintf(buffer, sizeof(buffer), "%s%s%s", Meter_uiName(this), number, mode);
ListItem* li = ListItem_new(buffer, 0);
li->moving = moving;
return li;
@@ -146,7 +155,7 @@ ListItem* Meter_toListItem(Meter* this, bool moving) {
static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN];
- Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
+ Meter_updateValues(this, buffer, sizeof(buffer));
(void) w;
attrset(CRT_colors[METER_TEXT]);
@@ -166,7 +175,7 @@ static const char BarMeterMode_characters[] = "|#*@$%&.";
static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
char buffer[METER_BUFFER_LEN];
- Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
+ Meter_updateValues(this, buffer, sizeof(buffer));
w -= 2;
attrset(CRT_colors[METER_TEXT]);
@@ -177,28 +186,33 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
attrset(CRT_colors[BAR_BORDER]);
mvaddch(y, x, '[');
mvaddch(y, x + w, ']');
+ attrset(CRT_colors[RESET_COLOR]);
w--;
x++;
- if (w < 1) {
- attrset(CRT_colors[RESET_COLOR]);
+ if (w < 1)
return;
- }
- char bar[w + 1];
- int blockSizes[10];
+ // The text in the bar is right aligned;
+ // calculate needed padding and generate leading spaces
+ const int textLen = mbstowcs(NULL, buffer, 0);
+ const int padding = CLAMP(w - textLen, 0, w);
- xSnprintf(bar, w + 1, "%*.*s", w, w, buffer);
+ RichString_begin(bar);
+ RichString_appendChr(&bar, ' ', padding);
+ RichString_append(&bar, 0, buffer);
+ assert(RichString_sizeVal(bar) >= w);
+
+ int blockSizes[10];
// First draw in the bar[] buffer...
int offset = 0;
- int items = Meter_getItems(this);
- for (int i = 0; i < items; i++) {
+ for (uint8_t i = 0; i < this->curItems; i++) {
double value = this->values[i];
value = CLAMP(value, 0.0, this->total);
if (value > 0) {
- blockSizes[i] = ceil((value/this->total) * w);
+ blockSizes[i] = ceil((value / this->total) * w);
} else {
blockSizes[i] = 0;
}
@@ -206,11 +220,11 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// (Control against invalid values)
nextOffset = CLAMP(nextOffset, 0, w);
for (int j = offset; j < nextOffset; j++)
- if (bar[j] == ' ') {
+ if (RichString_getCharVal(bar, j) == ' ') {
if (CRT_colorScheme == COLORSCHEME_MONOCHROME) {
- bar[j] = BarMeterMode_characters[i];
+ RichString_setChar(&bar, j, BarMeterMode_characters[i]);
} else {
- bar[j] = '|';
+ RichString_setChar(&bar, j, '|');
}
}
offset = nextOffset;
@@ -218,17 +232,19 @@ static void BarMeterMode_draw(Meter* this, int x, int y, int w) {
// ...then print the buffer.
offset = 0;
- for (int i = 0; i < items; i++) {
- attrset(CRT_colors[Meter_attributes(this)[i]]);
- mvaddnstr(y, x + offset, bar + offset, blockSizes[i]);
+ for (uint8_t i = 0; i < this->curItems; i++) {
+ RichString_setAttrn(&bar, CRT_colors[Meter_attributes(this)[i]], offset, offset + blockSizes[i] - 1);
+ RichString_printoffnVal(bar, y, x + offset, offset, blockSizes[i]);
offset += blockSizes[i];
offset = CLAMP(offset, 0, w);
}
if (offset < w) {
- attrset(CRT_colors[BAR_SHADOW]);
- mvaddnstr(y, x + offset, bar + offset, w - offset);
+ RichString_setAttrn(&bar, CRT_colors[BAR_SHADOW], offset, w - 1);
+ RichString_printoffnVal(bar, y, x + offset, offset, w - offset);
}
+ RichString_end(bar);
+
move(y, x + w + 1);
attrset(CRT_colors[RESET_COLOR]);
}
@@ -260,8 +276,10 @@ static int GraphMeterMode_pixPerRow;
static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
- if (!this->drawData) this->drawData = xCalloc(1, sizeof(GraphData));
- GraphData* data = (GraphData*) this->drawData;
+ if (!this->drawData) {
+ this->drawData = xCalloc(1, sizeof(GraphData));
+ }
+ GraphData* data = this->drawData;
const int nValues = METER_BUFFER_LEN;
#ifdef HAVE_LIBNCURSESW
@@ -284,32 +302,32 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
struct timeval now;
gettimeofday(&now, NULL);
if (!timercmp(&now, &(data->time), <)) {
- struct timeval delay = { .tv_sec = (int)(CRT_delay/10), .tv_usec = (CRT_delay-((int)(CRT_delay/10)*10)) * 100000 };
+ int globalDelay = this->pl->settings->delay;
+ struct timeval delay = { .tv_sec = globalDelay / 10, .tv_usec = (globalDelay - ((globalDelay / 10) * 10)) * 100000 };
timeradd(&now, &delay, &(data->time));
for (int i = 0; i < nValues - 1; i++)
- data->values[i] = data->values[i+1];
+ data->values[i] = data->values[i + 1];
- char buffer[nValues];
- Meter_updateValues(this, buffer, nValues - 1);
+ char buffer[METER_BUFFER_LEN];
+ Meter_updateValues(this, buffer, sizeof(buffer));
double value = 0.0;
- int items = Meter_getItems(this);
- for (int i = 0; i < items; i++)
+ for (uint8_t i = 0; i < this->curItems; i++)
value += this->values[i];
value /= this->total;
data->values[nValues - 1] = value;
}
- int i = nValues - (w*2) + 2, k = 0;
+ int i = nValues - (w * 2) + 2, k = 0;
if (i < 0) {
- k = -i/2;
+ k = -i / 2;
i = 0;
}
- for (; i < nValues - 1; i+=2, k++) {
+ for (; i < nValues - 1; i += 2, k++) {
int pix = GraphMeterMode_pixPerRow * GRAPH_HEIGHT;
int v1 = CLAMP((int) lround(data->values[i] * pix), 1, pix);
- int v2 = CLAMP((int) lround(data->values[i+1] * pix), 1, pix);
+ int v2 = CLAMP((int) lround(data->values[i + 1] * pix), 1, pix);
int colorIdx = GRAPH_1;
for (int line = 0; line < GRAPH_HEIGHT; line++) {
@@ -317,7 +335,7 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
int line2 = CLAMP(v2 - (GraphMeterMode_pixPerRow * (GRAPH_HEIGHT - 1 - line)), 0, GraphMeterMode_pixPerRow);
attrset(CRT_colors[colorIdx]);
- mvaddstr(y+line, x+k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
+ mvaddstr(y + line, x + k, GraphMeterMode_dots[line1 * (GraphMeterMode_pixPerRow + 1) + line2]);
colorIdx = GRAPH_2;
}
}
@@ -327,17 +345,17 @@ static void GraphMeterMode_draw(Meter* this, int x, int y, int w) {
/* ---------- LEDMeterMode ---------- */
static const char* const LEDMeterMode_digitsAscii[] = {
- " __ "," "," __ "," __ "," "," __ "," __ "," __ "," __ "," __ ",
- "| |"," |"," __|"," __|","|__|","|__ ","|__ "," |","|__|","|__|",
- "|__|"," |","|__ "," __|"," |"," __|","|__|"," |","|__|"," __|"
+ " __ ", " ", " __ ", " __ ", " ", " __ ", " __ ", " __ ", " __ ", " __ ",
+ "| |", " |", " __|", " __|", "|__|", "|__ ", "|__ ", " |", "|__|", "|__|",
+ "|__|", " |", "|__ ", " __|", " |", " __|", "|__|", " |", "|__|", " __|"
};
#ifdef HAVE_LIBNCURSESW
static const char* const LEDMeterMode_digitsUtf8[] = {
- "┌──┐"," ┐ ","╶──┐","╶──┐","╷ ╷","┌──╴","┌──╴","╶──┐","┌──┐","┌──┐",
- "│ │"," │ ","┌──┘"," ──┤","└──┤","└──┐","├──┐"," │","├──┤","└──┤",
- "└──┘"," ╵ ","└──╴","╶──┘"," ╵","╶──┘","└──┘"," ╵","└──┘"," ──┘"
+ "┌──┐", " ┐ ", "╶──┐", "╶──┐", "╷ ╷", "┌──╴", "┌──╴", "╶──┐", "┌──┐", "┌──┐",
+ "│ │", " │ ", "┌──┘", " ──┤", "└──┤", "└──┐", "├──┐", " │", "├──┤", "└──┤",
+ "└──┘", " ╵ ", "└──╴", "╶──┘", " ╵", "╶──┘", "└──┘", " ╵", "└──┘", " ──┘"
};
#endif
@@ -360,24 +378,24 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
LEDMeterMode_digits = LEDMeterMode_digitsAscii;
char buffer[METER_BUFFER_LEN];
- Meter_updateValues(this, buffer, METER_BUFFER_LEN - 1);
+ Meter_updateValues(this, buffer, sizeof(buffer));
RichString_begin(out);
Meter_displayBuffer(this, buffer, &out);
int yText =
#ifdef HAVE_LIBNCURSESW
- CRT_utf8 ? y+1 :
+ CRT_utf8 ? y + 1 :
#endif
- y+2;
+ y + 2;
attrset(CRT_colors[LED_COLOR]);
mvaddstr(yText, x, this->caption);
int xx = x + strlen(this->caption);
int len = RichString_sizeVal(out);
for (int i = 0; i < len; i++) {
- char c = RichString_getCharVal(out, i);
+ int c = RichString_getCharVal(out, i);
if (c >= '0' && c <= '9') {
- LEDMeterMode_drawDigit(xx, y, c-48);
+ LEDMeterMode_drawDigit(xx, y, c - 48);
xx += 4;
} else {
mvaddch(yText, xx, c);
@@ -412,7 +430,7 @@ static MeterMode LEDMeterMode = {
.draw = LEDMeterMode_draw,
};
-MeterMode* Meter_modes[] = {
+const MeterMode* const Meter_modes[] = {
NULL,
&BarMeterMode,
&TextMeterMode,
@@ -423,23 +441,21 @@ MeterMode* Meter_modes[] = {
/* Blank meter */
-static void BlankMeter_updateValues(Meter* this, char* buffer, int size) {
- (void) this; (void) buffer; (void) size;
+static void BlankMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) {
if (size > 0) {
*buffer = 0;
}
}
-static void BlankMeter_display(Object* cast, RichString* out) {
- (void) cast;
+static void BlankMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
RichString_prune(out);
}
-int BlankMeter_attributes[] = {
+static const int BlankMeter_attributes[] = {
DEFAULT_COLOR
};
-MeterClass BlankMeter_class = {
+const MeterClass BlankMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
diff --git a/Meter.h b/Meter.h
index c4d0039..cb05405 100644
--- a/Meter.h
+++ b/Meter.h
@@ -3,25 +3,57 @@
/*
htop - Meter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "ListItem.h"
+#include "config.h" // IWYU pragma: keep
+
+#include <stdbool.h>
+#include <stdint.h>
#include <sys/time.h>
+#include "ListItem.h"
+#include "Object.h"
+#include "ProcessList.h"
+
+
#define METER_BUFFER_LEN 256
+#define METER_BUFFER_CHECK(buffer, size, written) \
+ do { \
+ if ((written) < 0 || (size_t)(written) >= (size)) { \
+ return; \
+ } \
+ (buffer) += (written); \
+ (size) -= (size_t)(written); \
+ } while (0)
+
+#define METER_BUFFER_APPEND_CHR(buffer, size, c) \
+ do { \
+ if ((size) < 2) { \
+ return; \
+ } \
+ *(buffer)++ = c; \
+ *(buffer) = '\0'; \
+ (size)--; \
+ if ((size) == 0) { \
+ return; \
+ } \
+ } while (0)
+
+
+struct Meter_;
typedef struct Meter_ Meter;
typedef void(*Meter_Init)(Meter*);
typedef void(*Meter_Done)(Meter*);
typedef void(*Meter_UpdateMode)(Meter*, int);
-typedef void(*Meter_UpdateValues)(Meter*, char*, int);
+typedef void(*Meter_UpdateValues)(Meter*, char*, size_t);
typedef void(*Meter_Draw)(Meter*, int, int, int);
typedef struct MeterClass_ {
- ObjectClass super;
+ const ObjectClass super;
const Meter_Init init;
const Meter_Done done;
const Meter_UpdateMode updateMode;
@@ -29,16 +61,15 @@ typedef struct MeterClass_ {
const Meter_UpdateValues updateValues;
const int defaultMode;
const double total;
- const int* attributes;
- const char* name;
- const char* uiName;
- const char* caption;
- const char* description;
- const char maxItems;
- char curItems;
+ const int* const attributes;
+ const char* const name; /* internal name of the meter, must not contain any space */
+ const char* const uiName; /* display name in header setup menu */
+ const char* const caption; /* prefix in the actual header */
+ const char* const description; /* optional meter description in header setup menu */
+ const uint8_t maxItems;
} MeterClass;
-#define As_Meter(this_) ((MeterClass*)((this_)->super.klass))
+#define As_Meter(this_) ((const MeterClass*)((this_)->super.klass))
#define Meter_initFn(this_) As_Meter(this_)->init
#define Meter_init(this_) As_Meter(this_)->init((Meter*)(this_))
#define Meter_done(this_) As_Meter(this_)->done((Meter*)(this_))
@@ -49,12 +80,15 @@ typedef struct MeterClass_ {
#define Meter_updateValues(this_, buf_, sz_) \
As_Meter(this_)->updateValues((Meter*)(this_), buf_, sz_)
#define Meter_defaultMode(this_) As_Meter(this_)->defaultMode
-#define Meter_getItems(this_) As_Meter(this_)->curItems
-#define Meter_setItems(this_, n_) As_Meter(this_)->curItems = (n_)
#define Meter_attributes(this_) As_Meter(this_)->attributes
#define Meter_name(this_) As_Meter(this_)->name
#define Meter_uiName(this_) As_Meter(this_)->uiName
+typedef struct GraphData_ {
+ struct timeval time;
+ double values[METER_BUFFER_LEN];
+} GraphData;
+
struct Meter_ {
Object super;
Meter_Draw draw;
@@ -62,11 +96,13 @@ struct Meter_ {
char* caption;
int mode;
int param;
- void* drawData;
+ GraphData* drawData;
int h;
- struct ProcessList_* pl;
+ const ProcessList* pl;
+ uint8_t curItems;
double* values;
double total;
+ void* meterData;
};
typedef struct MeterMode_ {
@@ -84,16 +120,11 @@ typedef enum {
LAST_METERMODE
} MeterModeId;
-typedef struct GraphData_ {
- struct timeval time;
- double values[METER_BUFFER_LEN];
-} GraphData;
-
-extern MeterClass Meter_class;
+extern const MeterClass Meter_class;
-Meter* Meter_new(struct ProcessList_* pl, int param, MeterClass* type);
+Meter* Meter_new(const ProcessList* pl, int param, const MeterClass* type);
-int Meter_humanUnit(char* buffer, unsigned long int value, int size);
+int Meter_humanUnit(char* buffer, unsigned long int value, size_t size);
void Meter_delete(Object* cast);
@@ -103,10 +134,8 @@ void Meter_setMode(Meter* this, int modeIndex);
ListItem* Meter_toListItem(Meter* this, bool moving);
-extern MeterMode* Meter_modes[];
-
-extern int BlankMeter_attributes[];
+extern const MeterMode* const Meter_modes[];
-extern MeterClass BlankMeter_class;
+extern const MeterClass BlankMeter_class;
#endif
diff --git a/MetersPanel.c b/MetersPanel.c
index 417834a..7e47ad8 100644
--- a/MetersPanel.c
+++ b/MetersPanel.c
@@ -1,15 +1,21 @@
/*
htop - MetersPanel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "MetersPanel.h"
#include <stdlib.h>
-#include <assert.h>
+
#include "CRT.h"
+#include "FunctionBar.h"
+#include "Header.h"
+#include "ListItem.h"
+#include "Meter.h"
+#include "Object.h"
+#include "ProvideCurses.h"
// Note: In code the meters are known to have bar/text/graph "Modes", but in UI
@@ -27,6 +33,13 @@ static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<-
static int MetersMovingEvents[] = {' ', 13, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, ERR, KEY_DC, KEY_F(10)};
static FunctionBar* Meters_movingBar = NULL;
+void MetersPanel_cleanup() {
+ if (Meters_movingBar) {
+ FunctionBar_delete(Meters_movingBar);
+ Meters_movingBar = NULL;
+ }
+}
+
static void MetersPanel_delete(Object* object) {
Panel* super = (Panel*) object;
MetersPanel* this = (MetersPanel*) object;
@@ -48,7 +61,7 @@ void MetersPanel_setMoving(MetersPanel* this, bool moving) {
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOLLOW]);
super->currentBar = Meters_movingBar;
}
- FunctionBar_draw(this->super.currentBar, NULL);
+ FunctionBar_draw(this->super.currentBar);
}
static inline bool moveToNeighbor(MetersPanel* this, MetersPanel* neighbor, int selected) {
@@ -170,7 +183,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
}
}
if (result == HANDLED || sideMove) {
- Header* header = (Header*) this->scr->header;
+ Header* header = this->scr->header;
this->settings->changed = true;
Header_calculateHeight(header);
Header_draw(header);
@@ -179,7 +192,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) {
return result;
}
-PanelClass MetersPanel_class = {
+const PanelClass MetersPanel_class = {
.super = {
.extends = Class(Panel),
.delete = MetersPanel_delete
diff --git a/MetersPanel.h b/MetersPanel.h
index c02ce1c..cf4de60 100644
--- a/MetersPanel.h
+++ b/MetersPanel.h
@@ -3,14 +3,19 @@
/*
htop - MetersPanel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+
#include "Panel.h"
-#include "Settings.h"
#include "ScreenManager.h"
+#include "Settings.h"
+#include "Vector.h"
+
+struct MetersPanel_;
typedef struct MetersPanel_ MetersPanel;
struct MetersPanel_ {
@@ -24,9 +29,11 @@ struct MetersPanel_ {
bool moving;
};
+void MetersPanel_cleanup(void);
+
void MetersPanel_setMoving(MetersPanel* this, bool moving);
-extern PanelClass MetersPanel_class;
+extern const PanelClass MetersPanel_class;
MetersPanel* MetersPanel_new(Settings* settings, const char* header, Vector* meters, ScreenManager* scr);
diff --git a/NetworkIOMeter.c b/NetworkIOMeter.c
new file mode 100644
index 0000000..90ec990
--- /dev/null
+++ b/NetworkIOMeter.c
@@ -0,0 +1,125 @@
+#include "NetworkIOMeter.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <sys/time.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "Object.h"
+#include "Platform.h"
+#include "RichString.h"
+#include "XUtils.h"
+
+
+static const int NetworkIOMeter_attributes[] = {
+ METER_VALUE_IOREAD,
+ METER_VALUE_IOWRITE,
+};
+
+static bool hasData = false;
+
+static unsigned long int cached_rxb_diff = 0;
+static unsigned long int cached_rxp_diff = 0;
+static unsigned long int cached_txb_diff = 0;
+static unsigned long int cached_txp_diff = 0;
+
+static void NetworkIOMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) {
+ static unsigned long long int cached_last_update = 0;
+
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ unsigned long long int timeInMilliSeconds = (unsigned long long int)tv.tv_sec * 1000 + (unsigned long long int)tv.tv_usec / 1000;
+ unsigned long long int passedTimeInMs = timeInMilliSeconds - cached_last_update;
+
+ /* update only every 500ms */
+ if (passedTimeInMs > 500) {
+ static unsigned long int cached_rxb_total = 0;
+ static unsigned long int cached_rxp_total = 0;
+ static unsigned long int cached_txb_total = 0;
+ static unsigned long int cached_txp_total = 0;
+
+ cached_last_update = timeInMilliSeconds;
+
+ unsigned long int bytesReceived, packetsReceived, bytesTransmitted, packetsTransmitted;
+
+ hasData = Platform_getNetworkIO(&bytesReceived, &packetsReceived, &bytesTransmitted, &packetsTransmitted);
+ if (!hasData) {
+ xSnprintf(buffer, len, "no data");
+ return;
+ }
+
+ if (bytesReceived > cached_rxb_total) {
+ cached_rxb_diff = (bytesReceived - cached_rxb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
+ cached_rxb_diff = 1000.0 * cached_rxb_diff / passedTimeInMs; /* convert to per second */
+ } else {
+ cached_rxb_diff = 0;
+ }
+ cached_rxb_total = bytesReceived;
+
+ if (packetsReceived > cached_rxp_total) {
+ cached_rxp_diff = packetsReceived - cached_rxp_total;
+ } else {
+ cached_rxp_diff = 0;
+ }
+ cached_rxp_total = packetsReceived;
+
+ if (bytesTransmitted > cached_txb_total) {
+ cached_txb_diff = (bytesTransmitted - cached_txb_total) / 1024; /* Meter_humanUnit() expects unit in kilo */
+ cached_txb_diff = 1000.0 * cached_txb_diff / passedTimeInMs; /* convert to per second */
+ } else {
+ cached_txb_diff = 0;
+ }
+ cached_txb_total = bytesTransmitted;
+
+ if (packetsTransmitted > cached_txp_total) {
+ cached_txp_diff = packetsTransmitted - cached_txp_total;
+ } else {
+ cached_txp_diff = 0;
+ }
+ cached_txp_total = packetsTransmitted;
+ }
+
+ char bufferBytesReceived[12], bufferBytesTransmitted[12];
+ Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived));
+ Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted));
+ xSnprintf(buffer, len, "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted);
+}
+
+static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
+ if (!hasData) {
+ RichString_write(out, CRT_colors[METER_VALUE_ERROR], "no data");
+ return;
+ }
+
+ char buffer[64];
+
+ RichString_write(out, CRT_colors[METER_TEXT], "rx: ");
+ Meter_humanUnit(buffer, cached_rxb_diff, sizeof(buffer));
+ RichString_append(out, CRT_colors[METER_VALUE_IOREAD], buffer);
+ RichString_append(out, CRT_colors[METER_VALUE_IOREAD], "iB/s");
+
+ RichString_append(out, CRT_colors[METER_TEXT], " tx: ");
+ Meter_humanUnit(buffer, cached_txb_diff, sizeof(buffer));
+ RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], buffer);
+ RichString_append(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s");
+
+ xSnprintf(buffer, sizeof(buffer), " (%lu/%lu packets) ", cached_rxp_diff, cached_txp_diff);
+ RichString_append(out, CRT_colors[METER_TEXT], buffer);
+}
+
+const MeterClass NetworkIOMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = NetworkIOMeter_display
+ },
+ .updateValues = NetworkIOMeter_updateValues,
+ .defaultMode = TEXT_METERMODE,
+ .maxItems = 0,
+ .total = 100.0,
+ .attributes = NetworkIOMeter_attributes,
+ .name = "NetworkIO",
+ .uiName = "Network IO",
+ .caption = "Network: "
+};
diff --git a/NetworkIOMeter.h b/NetworkIOMeter.h
new file mode 100644
index 0000000..311b5e6
--- /dev/null
+++ b/NetworkIOMeter.h
@@ -0,0 +1,8 @@
+#ifndef HEADER_NetworkIOMeter
+#define HEADER_NetworkIOMeter
+
+#include "Meter.h"
+
+extern const MeterClass NetworkIOMeter_class;
+
+#endif /* HEADER_NetworkIOMeter */
diff --git a/Object.c b/Object.c
index 4d8045d..0a29d01 100644
--- a/Object.c
+++ b/Object.c
@@ -2,28 +2,32 @@
htop - Object.c
(C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Object.h"
-ObjectClass Object_class = {
+#include <stddef.h>
+
+
+const ObjectClass Object_class = {
.extends = NULL
};
-#ifdef DEBUG
+#ifndef NDEBUG
-bool Object_isA(Object* o, const ObjectClass* klass) {
+bool Object_isA(const Object* o, const ObjectClass* klass) {
if (!o)
return false;
- const ObjectClass* type = o->klass;
- while (type) {
- if (type == klass)
+
+ for (const ObjectClass* type = o->klass; type; type = type->extends) {
+ if (type == klass) {
return true;
- type = type->extends;
+ }
}
+
return false;
}
-#endif
+#endif /* NDEBUG */
diff --git a/Object.h b/Object.h
index 50a5f12..2c3ba9f 100644
--- a/Object.h
+++ b/Object.h
@@ -4,41 +4,50 @@
htop - Object.h
(C) 2004-2012 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
+#include <assert.h>
+
#include "RichString.h"
-#include "XAlloc.h"
-#include "Macros.h"
+#include "XUtils.h" // IWYU pragma: keep
+
+#ifndef NDEBUG
+#include <stdbool.h>
+#endif
+
+struct Object_;
typedef struct Object_ Object;
-typedef void(*Object_Display)(Object*, RichString*);
+typedef void(*Object_Display)(const Object*, RichString*);
typedef long(*Object_Compare)(const void*, const void*);
typedef void(*Object_Delete)(Object*);
-#define Object_getClass(obj_) ((Object*)(obj_))->klass
-#define Object_setClass(obj_, class_) Object_getClass(obj_) = (ObjectClass*) class_
+#define Object_getClass(obj_) ((const Object*)(obj_))->klass
+#define Object_setClass(obj_, class_) (((Object*)(obj_))->klass = (const ObjectClass*) (class_))
-#define Object_delete(obj_) Object_getClass(obj_)->delete((Object*)(obj_))
+#define Object_delete(obj_) (assert(Object_getClass(obj_)->delete), Object_getClass(obj_)->delete((Object*)(obj_)))
#define Object_displayFn(obj_) Object_getClass(obj_)->display
-#define Object_display(obj_, str_) Object_getClass(obj_)->display((Object*)(obj_), str_)
-#define Object_compare(obj_, other_) Object_getClass(obj_)->compare((const void*)(obj_), other_)
+#define Object_display(obj_, str_) (assert(Object_getClass(obj_)->display), Object_getClass(obj_)->display((const Object*)(obj_), str_))
+#define Object_compare(obj_, other_) (assert(Object_getClass(obj_)->compare), Object_getClass(obj_)->compare((const void*)(obj_), other_))
-#define Class(class_) ((ObjectClass*)(&(class_ ## _class)))
+#define Class(class_) ((const ObjectClass*)(&(class_ ## _class)))
-#define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_));
+#define AllocThis(class_) (class_*) xMalloc(sizeof(class_)); Object_setClass(this, Class(class_))
typedef struct ObjectClass_ {
- const void* extends;
+ const void* const extends;
const Object_Display display;
const Object_Delete delete;
const Object_Compare compare;
} ObjectClass;
struct Object_ {
- ObjectClass* klass;
+ const ObjectClass* klass;
};
typedef union {
@@ -46,12 +55,12 @@ typedef union {
void* v;
} Arg;
-extern ObjectClass Object_class;
+extern const ObjectClass Object_class;
-#ifdef DEBUG
+#ifndef NDEBUG
-bool Object_isA(Object* o, const ObjectClass* klass);
+bool Object_isA(const Object* o, const ObjectClass* klass);
-#endif
+#endif /* NDEBUG */
#endif
diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c
index 3e45bf8..b1137c7 100644
--- a/OpenFilesScreen.c
+++ b/OpenFilesScreen.c
@@ -1,93 +1,143 @@
/*
htop - OpenFilesScreen.c
(C) 2005-2006 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "OpenFilesScreen.h"
+#include "config.h" // IWYU pragma: keep
-#include "CRT.h"
-#include "ProcessList.h"
-#include "IncSet.h"
-#include "StringUtils.h"
-#include "FunctionBar.h"
+#include "OpenFilesScreen.h"
-#include <string.h>
+#include <fcntl.h>
#include <stdio.h>
-#include <unistd.h>
-#include <stdbool.h>
-#include <unistd.h>
#include <stdlib.h>
-#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include "Macros.h"
+#include "Panel.h"
+#include "ProvideCurses.h"
+#include "Vector.h"
+#include "XUtils.h"
-InfoScreenClass OpenFilesScreen_class = {
- .super = {
- .extends = Class(Object),
- .delete = OpenFilesScreen_delete
- },
- .scan = OpenFilesScreen_scan,
- .draw = OpenFilesScreen_draw
-};
-OpenFilesScreen* OpenFilesScreen_new(Process* process) {
+typedef struct OpenFiles_Data_ {
+ char* data[7];
+} OpenFiles_Data;
+
+typedef struct OpenFiles_ProcessData_ {
+ OpenFiles_Data data;
+ int error;
+ struct OpenFiles_FileData_* files;
+} OpenFiles_ProcessData;
+
+typedef struct OpenFiles_FileData_ {
+ OpenFiles_Data data;
+ struct OpenFiles_FileData_* next;
+} OpenFiles_FileData;
+
+static size_t getIndexForType(char type) {
+ switch (type) {
+ case 'f':
+ return 0;
+ case 'a':
+ return 1;
+ case 'D':
+ return 2;
+ case 'i':
+ return 3;
+ case 'n':
+ return 4;
+ case 's':
+ return 5;
+ case 't':
+ return 6;
+ }
+
+ /* should never reach here */
+ abort();
+}
+
+static const char* getDataForType(const OpenFiles_Data* data, char type) {
+ size_t index = getIndexForType(type);
+ return data->data[index] ? data->data[index] : "";
+}
+
+OpenFilesScreen* OpenFilesScreen_new(const Process* process) {
OpenFilesScreen* this = xMalloc(sizeof(OpenFilesScreen));
Object_setClass(this, Class(OpenFilesScreen));
- if (Process_isThread(process))
+ if (Process_isThread(process)) {
this->pid = process->tgid;
- else
+ } else {
this->pid = process->pid;
- return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " FD TYPE DEVICE SIZE NODE NAME");
+ }
+ return (OpenFilesScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 3, " FD TYPE MODE DEVICE SIZE NODE NAME");
}
void OpenFilesScreen_delete(Object* this) {
free(InfoScreen_done((InfoScreen*)this));
}
-void OpenFilesScreen_draw(InfoScreen* this) {
- InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, this->process->comm);
+static void OpenFilesScreen_draw(InfoScreen* this) {
+ InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, Process_getCommand(this->process));
}
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
- char buffer[1025];
- xSnprintf(buffer, 1024, "%d", pid);
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
- OpenFiles_FileData* fdata = NULL;
- OpenFiles_Data* item = &(pdata->data);
- int fdpair[2];
+
+ int fdpair[2] = {0, 0};
if (pipe(fdpair) == -1) {
pdata->error = 1;
return pdata;
}
+
pid_t child = fork();
if (child == -1) {
+ close(fdpair[1]);
+ close(fdpair[0]);
pdata->error = 1;
return pdata;
}
+
if (child == 0) {
close(fdpair[0]);
dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[1]);
int fdnull = open("/dev/null", O_WRONLY);
- if (fdnull < 0)
+ if (fdnull < 0) {
exit(1);
+ }
+
dup2(fdnull, STDERR_FILENO);
close(fdnull);
+ char buffer[32] = {0};
+ xSnprintf(buffer, sizeof(buffer), "%d", pid);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
exit(127);
}
close(fdpair[1]);
+
+ OpenFiles_Data* item = &(pdata->data);
+ OpenFiles_FileData* fdata = NULL;
+
FILE* fd = fdopen(fdpair[0], "r");
+ if (!fd) {
+ pdata->error = 1;
+ return pdata;
+ }
for (;;) {
char* line = String_readLine(fd);
if (!line) {
break;
}
+
unsigned char cmd = line[0];
- if (cmd == 'f') {
+ switch (cmd) {
+ case 'f': /* file descriptor */
+ {
OpenFiles_FileData* nextFile = xCalloc(1, sizeof(OpenFiles_FileData));
if (fdata == NULL) {
pdata->files = nextFile;
@@ -96,30 +146,60 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
}
fdata = nextFile;
item = &(fdata->data);
+ } /* FALLTHRU */
+ case 'a': /* file access mode */
+ case 'D': /* file's major/minor device number */
+ case 'i': /* file's inode number */
+ case 'n': /* file name, comment, Internet address */
+ case 's': /* file's size */
+ case 't': /* file's type */
+ {
+ size_t index = getIndexForType(cmd);
+ free(item->data[index]);
+ item->data[index] = xStrdup(line + 1);
+ break;
+ }
+ case 'c': /* process command name */
+ case 'd': /* file's device character code */
+ case 'g': /* process group ID */
+ case 'G': /* file flags */
+ case 'k': /* link count */
+ case 'l': /* file's lock status */
+ case 'L': /* process login name */
+ case 'o': /* file's offset */
+ case 'p': /* process ID */
+ case 'P': /* protocol name */
+ case 'R': /* parent process ID */
+ case 'T': /* TCP/TPI information, identified by prefixes */
+ case 'u': /* process user ID */
+ /* ignore */
+ break;
}
- item->data[cmd] = xStrdup(line + 1);
free(line);
}
fclose(fd);
+
int wstatus;
if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1;
return pdata;
}
- if (!WIFEXITED(wstatus))
+
+ if (!WIFEXITED(wstatus)) {
pdata->error = 1;
- else
+ } else {
pdata->error = WEXITSTATUS(wstatus);
+ }
+
return pdata;
}
-static inline void OpenFiles_Data_clear(OpenFiles_Data* data) {
- for (int i = 0; i < 255; i++)
- if (data->data[i])
- free(data->data[i]);
+static void OpenFiles_Data_clear(OpenFiles_Data* data) {
+ for (size_t i = 0; i < ARRAYSIZE(data->data); i++)
+ free(data->data[i]);
}
-void OpenFilesScreen_scan(InfoScreen* this) {
+static void OpenFilesScreen_scan(InfoScreen* this) {
Panel* panel = this->display;
int idx = Panel_getSelectedIndex(panel);
Panel_prune(panel);
@@ -131,19 +211,20 @@ void OpenFilesScreen_scan(InfoScreen* this) {
} else {
OpenFiles_FileData* fdata = pdata->files;
while (fdata) {
- char** data = fdata->data.data;
- int lenN = data['n'] ? strlen(data['n']) : 0;
- int sizeEntry = 5 + 7 + 10 + 10 + 10 + lenN + 5 /*spaces*/ + 1 /*null*/;
+ OpenFiles_Data* data = &fdata->data;
+ size_t lenN = strlen(getDataForType(data, 'n'));
+ size_t sizeEntry = 5 + 7 + 4 + 10 + 10 + 10 + lenN + 7 /*spaces*/ + 1 /*null*/;
char entry[sizeEntry];
- xSnprintf(entry, sizeEntry, "%5.5s %7.7s %10.10s %10.10s %10.10s %s",
- data['f'] ? data['f'] : "",
- data['t'] ? data['t'] : "",
- data['D'] ? data['D'] : "",
- data['s'] ? data['s'] : "",
- data['i'] ? data['i'] : "",
- data['n'] ? data['n'] : "");
+ xSnprintf(entry, sizeof(entry), "%5.5s %-7.7s %-4.4s %-10.10s %10.10s %10.10s %s",
+ getDataForType(data, 'f'),
+ getDataForType(data, 't'),
+ getDataForType(data, 'a'),
+ getDataForType(data, 'D'),
+ getDataForType(data, 's'),
+ getDataForType(data, 'i'),
+ getDataForType(data, 'n'));
InfoScreen_addLine(this, entry);
- OpenFiles_Data_clear(&fdata->data);
+ OpenFiles_Data_clear(data);
OpenFiles_FileData* old = fdata;
fdata = fdata->next;
free(old);
@@ -155,3 +236,12 @@ void OpenFilesScreen_scan(InfoScreen* this) {
Vector_insertionSort(panel->items);
Panel_setSelected(panel, idx);
}
+
+const InfoScreenClass OpenFilesScreen_class = {
+ .super = {
+ .extends = Class(Object),
+ .delete = OpenFilesScreen_delete
+ },
+ .scan = OpenFilesScreen_scan,
+ .draw = OpenFilesScreen_draw
+};
diff --git a/OpenFilesScreen.h b/OpenFilesScreen.h
index 2c83cf1..0fbafe0 100644
--- a/OpenFilesScreen.h
+++ b/OpenFilesScreen.h
@@ -3,40 +3,25 @@
/*
htop - OpenFilesScreen.h
(C) 2005-2006 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "InfoScreen.h"
-
-typedef struct OpenFiles_Data_ {
- char* data[256];
-} OpenFiles_Data;
-
-typedef struct OpenFiles_ProcessData_ {
- OpenFiles_Data data;
- int error;
- struct OpenFiles_FileData_* files;
-} OpenFiles_ProcessData;
+#include <sys/types.h>
-typedef struct OpenFiles_FileData_ {
- OpenFiles_Data data;
- struct OpenFiles_FileData_* next;
-} OpenFiles_FileData;
+#include "InfoScreen.h"
+#include "Object.h"
+#include "Process.h"
typedef struct OpenFilesScreen_ {
InfoScreen super;
pid_t pid;
} OpenFilesScreen;
-extern InfoScreenClass OpenFilesScreen_class;
+extern const InfoScreenClass OpenFilesScreen_class;
-OpenFilesScreen* OpenFilesScreen_new(Process* process);
+OpenFilesScreen* OpenFilesScreen_new(const Process* process);
void OpenFilesScreen_delete(Object* this);
-void OpenFilesScreen_draw(InfoScreen* this);
-
-void OpenFilesScreen_scan(InfoScreen* this);
-
#endif
diff --git a/OptionItem.c b/OptionItem.c
new file mode 100644
index 0000000..00001fc
--- /dev/null
+++ b/OptionItem.c
@@ -0,0 +1,191 @@
+/*
+htop - OptionItem.c
+(C) 2004-2011 Hisham H. Muhammad
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "OptionItem.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "RichString.h"
+#include "XUtils.h"
+
+
+static void OptionItem_delete(Object* cast) {
+ OptionItem* this = (OptionItem*)cast;
+ assert (this != NULL);
+
+ free(this->text);
+ free(this);
+}
+
+static void CheckItem_display(const Object* cast, RichString* out) {
+ const CheckItem* this = (const CheckItem*)cast;
+ assert (this != NULL);
+
+ RichString_write(out, CRT_colors[CHECK_BOX], "[");
+ if (CheckItem_get(this)) {
+ RichString_append(out, CRT_colors[CHECK_MARK], "x");
+ } else {
+ RichString_append(out, CRT_colors[CHECK_MARK], " ");
+ }
+ RichString_append(out, CRT_colors[CHECK_BOX], "] ");
+ RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
+}
+
+static void NumberItem_display(const Object* cast, RichString* out) {
+ const NumberItem* this = (const NumberItem*)cast;
+ assert (this != NULL);
+
+ char buffer[12];
+ RichString_write(out, CRT_colors[CHECK_BOX], "[");
+ int written;
+ if (this->scale < 0) {
+ written = xSnprintf(buffer, sizeof(buffer), "%.*f", -this->scale, pow(10, this->scale) * NumberItem_get(this));
+ } else if (this->scale > 0) {
+ written = xSnprintf(buffer, sizeof(buffer), "%d", (int) (pow(10, this->scale) * NumberItem_get(this)));
+ } else {
+ written = xSnprintf(buffer, sizeof(buffer), "%d", NumberItem_get(this));
+ }
+ RichString_append(out, CRT_colors[CHECK_MARK], buffer);
+ RichString_append(out, CRT_colors[CHECK_BOX], "]");
+ for (int i = written; i < 5; i++) {
+ RichString_append(out, CRT_colors[CHECK_BOX], " ");
+ }
+ RichString_append(out, CRT_colors[CHECK_TEXT], this->super.text);
+}
+
+const OptionItemClass OptionItem_class = {
+ .super = {
+ .extends = Class(Object),
+ .delete = OptionItem_delete
+ }
+};
+
+const OptionItemClass CheckItem_class = {
+ .super = {
+ .extends = Class(OptionItem),
+ .delete = OptionItem_delete,
+ .display = CheckItem_display
+ },
+ .kind = OPTION_ITEM_CHECK
+};
+
+const OptionItemClass NumberItem_class = {
+ .super = {
+ .extends = Class(OptionItem),
+ .delete = OptionItem_delete,
+ .display = NumberItem_display
+ },
+ .kind = OPTION_ITEM_NUMBER
+};
+
+CheckItem* CheckItem_newByRef(const char* text, bool* ref) {
+ CheckItem* this = AllocThis(CheckItem);
+ this->super.text = xStrdup(text);
+ this->value = false;
+ this->ref = ref;
+ return this;
+}
+
+CheckItem* CheckItem_newByVal(const char* text, bool value) {
+ CheckItem* this = AllocThis(CheckItem);
+ this->super.text = xStrdup(text);
+ this->value = value;
+ this->ref = NULL;
+ return this;
+}
+
+bool CheckItem_get(const CheckItem* this) {
+ if (this->ref) {
+ return *(this->ref);
+ } else {
+ return this->value;
+ }
+}
+
+void CheckItem_set(CheckItem* this, bool value) {
+ if (this->ref) {
+ *(this->ref) = value;
+ } else {
+ this->value = value;
+ }
+}
+
+void CheckItem_toggle(CheckItem* this) {
+ if (this->ref) {
+ *(this->ref) = !*(this->ref);
+ } else {
+ this->value = !this->value;
+ }
+}
+
+NumberItem* NumberItem_newByRef(const char* text, int* ref, int scale, int min, int max) {
+ assert(min <= max);
+
+ NumberItem* this = AllocThis(NumberItem);
+ this->super.text = xStrdup(text);
+ this->value = 0;
+ this->ref = ref;
+ this->scale = scale;
+ this->min = min;
+ this->max = max;
+ return this;
+}
+
+NumberItem* NumberItem_newByVal(const char* text, int value, int scale, int min, int max) {
+ assert(min <= max);
+
+ NumberItem* this = AllocThis(NumberItem);
+ this->super.text = xStrdup(text);
+ this->value = CLAMP(value, min, max);
+ this->ref = NULL;
+ this->scale = scale;
+ this->min = min;
+ this->max = max;
+ return this;
+}
+
+int NumberItem_get(const NumberItem* this) {
+ if (this->ref) {
+ return *(this->ref);
+ } else {
+ return this->value;
+ }
+}
+
+void NumberItem_decrease(NumberItem* this) {
+ if (this->ref) {
+ *(this->ref) = CLAMP(*(this->ref) - 1, this->min, this->max);
+ } else {
+ this->value = CLAMP(this->value - 1, this->min, this->max);
+ }
+}
+
+void NumberItem_increase(NumberItem* this) {
+ if (this->ref) {
+ *(this->ref) = CLAMP(*(this->ref) + 1, this->min, this->max);
+ } else {
+ this->value = CLAMP(this->value + 1, this->min, this->max);
+ }
+}
+
+void NumberItem_toggle(NumberItem* this) {
+ if (this->ref) {
+ if (*(this->ref) >= this->max)
+ *(this->ref) = this->min;
+ else
+ *(this->ref) += 1;
+ } else {
+ if (this->value >= this->max)
+ this->value = this->min;
+ else
+ this->value += 1;
+ }
+}
diff --git a/OptionItem.h b/OptionItem.h
new file mode 100644
index 0000000..8dd802d
--- /dev/null
+++ b/OptionItem.h
@@ -0,0 +1,70 @@
+#ifndef HEADER_OptionItem
+#define HEADER_OptionItem
+/*
+htop - OptionItem.h
+(C) 2004-2011 Hisham H. Muhammad
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include <stdbool.h>
+
+#include "Object.h"
+
+
+enum OptionItemType {
+ OPTION_ITEM_CHECK,
+ OPTION_ITEM_NUMBER,
+};
+
+typedef struct OptionItemClass_ {
+ const ObjectClass super;
+
+ enum OptionItemType kind;
+} OptionItemClass;
+
+#define As_OptionItem(this_) ((const OptionItemClass*)((this_)->super.klass))
+#define OptionItem_kind(this_) As_OptionItem(this_)->kind
+
+typedef struct OptionItem_ {
+ Object super;
+
+ char* text;
+} OptionItem;
+
+typedef struct CheckItem_ {
+ OptionItem super;
+
+ bool* ref;
+ bool value;
+} CheckItem;
+
+typedef struct NumberItem_ {
+ OptionItem super;
+
+ char* text;
+ int* ref;
+ int value;
+ int scale;
+ int min;
+ int max;
+} NumberItem;
+
+extern const OptionItemClass OptionItem_class;
+extern const OptionItemClass CheckItem_class;
+extern const OptionItemClass NumberItem_class;
+
+CheckItem* CheckItem_newByRef(const char* text, bool* ref);
+CheckItem* CheckItem_newByVal(const char* text, bool value);
+bool CheckItem_get(const CheckItem* this);
+void CheckItem_set(CheckItem* this, bool value);
+void CheckItem_toggle(CheckItem* this);
+
+NumberItem* NumberItem_newByRef(const char* text, int* ref, int scale, int min, int max);
+NumberItem* NumberItem_newByVal(const char* text, int value, int scale, int min, int max);
+int NumberItem_get(const NumberItem* this);
+void NumberItem_decrease(NumberItem* this);
+void NumberItem_increase(NumberItem* this);
+void NumberItem_toggle(NumberItem* this);
+
+#endif
diff --git a/Panel.c b/Panel.c
index b77483d..21dfbe2 100644
--- a/Panel.c
+++ b/Panel.c
@@ -1,25 +1,28 @@
/*
htop - Panel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
-#include "CRT.h"
-#include "RichString.h"
-#include "ListItem.h"
-#include "StringUtils.h"
-
-#include <math.h>
+#include <assert.h>
+#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
-#include <ctype.h>
#include <string.h>
-#include <assert.h>
+#include <strings.h>
-PanelClass Panel_class = {
+#include "CRT.h"
+#include "ListItem.h"
+#include "Macros.h"
+#include "ProvideCurses.h"
+#include "RichString.h"
+#include "XUtils.h"
+
+
+const PanelClass Panel_class = {
.super = {
.extends = Class(Object),
.delete = Panel_delete
@@ -27,7 +30,7 @@ PanelClass Panel_class = {
.eventHandler = Panel_selectByTyping,
};
-Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar) {
+Panel* Panel_new(int x, int y, int w, int h, bool owner, const ObjectClass* type, FunctionBar* fuBar) {
Panel* this;
this = xMalloc(sizeof(Panel));
Object_setClass(this, Class(Panel));
@@ -41,7 +44,7 @@ void Panel_delete(Object* cast) {
free(this);
}
-void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar) {
+void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar) {
this->x = x;
this->y = y;
this->w = w;
@@ -94,8 +97,10 @@ void Panel_move(Panel* this, int x, int y) {
void Panel_resize(Panel* this, int w, int h) {
assert (this != NULL);
- if (RichString_sizeVal(this->header) > 0)
+ if (RichString_sizeVal(this->header) > 0) {
h--;
+ }
+
this->w = w;
this->h = h;
this->needsRedraw = true;
@@ -142,33 +147,38 @@ Object* Panel_remove(Panel* this, int i) {
this->needsRedraw = true;
Object* removed = Vector_remove(this->items, i);
- if (this->selected > 0 && this->selected >= Vector_size(this->items))
+ if (this->selected > 0 && this->selected >= Vector_size(this->items)) {
this->selected--;
+ }
+
return removed;
}
Object* Panel_getSelected(Panel* this) {
assert (this != NULL);
- if (Vector_size(this->items) > 0)
+ if (Vector_size(this->items) > 0) {
return Vector_get(this->items, this->selected);
- else
+ } else {
return NULL;
+ }
}
void Panel_moveSelectedUp(Panel* this) {
assert (this != NULL);
Vector_moveUp(this->items, this->selected);
- if (this->selected > 0)
+ if (this->selected > 0) {
this->selected--;
+ }
}
void Panel_moveSelectedDown(Panel* this) {
assert (this != NULL);
Vector_moveDown(this->items, this->selected);
- if (this->selected + 1 < Vector_size(this->items))
+ if (this->selected + 1 < Vector_size(this->items)) {
this->selected++;
+ }
}
int Panel_getSelectedIndex(Panel* this) {
@@ -190,15 +200,16 @@ void Panel_setSelected(Panel* this, int selected) {
if (selected >= size) {
selected = size - 1;
}
- if (selected < 0)
+ if (selected < 0) {
selected = 0;
+ }
this->selected = selected;
if (Panel_eventHandlerFn(this)) {
Panel_eventHandler(this, EVENT_SET_SELECTED);
}
}
-void Panel_splice(Panel *this, Vector* from) {
+void Panel_splice(Panel* this, Vector* from) {
assert (this != NULL);
assert (from != NULL);
@@ -206,7 +217,7 @@ void Panel_splice(Panel *this, Vector* from) {
this->needsRedraw = true;
}
-void Panel_draw(Panel* this, bool focus) {
+void Panel_draw(Panel* this, bool focus, bool highlightSelected) {
assert (this != NULL);
int size = Vector_size(this->items);
@@ -251,28 +262,29 @@ void Panel_draw(Panel* this, bool focus) {
int upTo = MINIMUM(first + h, size);
int selectionColor = focus
- ? this->selectionColor
- : CRT_colors[PANEL_SELECTION_UNFOCUS];
+ ? this->selectionColor
+ : CRT_colors[PANEL_SELECTION_UNFOCUS];
if (this->needsRedraw) {
int line = 0;
- for(int i = first; line < h && i < upTo; i++) {
+ for (int i = first; line < h && i < upTo; i++) {
Object* itemObj = Vector_get(this->items, i);
- assert(itemObj); if(!itemObj) continue;
RichString_begin(item);
Object_display(itemObj, &item);
int itemLen = RichString_sizeVal(item);
int amt = MINIMUM(itemLen - scrollH, this->w);
- bool selected = (i == this->selected);
- if (selected) {
- attrset(selectionColor);
- RichString_setAttr(&item, selectionColor);
+ if (highlightSelected && i == this->selected) {
+ item.highlightAttr = selectionColor;
+ }
+ if (item.highlightAttr) {
+ attrset(item.highlightAttr);
+ RichString_setAttr(&item, item.highlightAttr);
this->selectedLen = itemLen;
}
mvhline(y + line, x, ' ', this->w);
if (amt > 0)
RichString_printoffnVal(item, y + line, x, scrollH, amt);
- if (selected)
+ if (item.highlightAttr)
attrset(CRT_colors[RESET_COLOR]);
RichString_end(item);
line++;
@@ -285,7 +297,6 @@ void Panel_draw(Panel* this, bool focus) {
} else {
Object* oldObj = Vector_get(this->items, this->oldSelected);
- assert(oldObj);
RichString_begin(old);
Object_display(oldObj, &old);
int oldLen = RichString_sizeVal(old);
@@ -294,15 +305,15 @@ void Panel_draw(Panel* this, bool focus) {
Object_display(newObj, &new);
int newLen = RichString_sizeVal(new);
this->selectedLen = newLen;
- mvhline(y+ this->oldSelected - first, x+0, ' ', this->w);
+ mvhline(y + this->oldSelected - first, x + 0, ' ', this->w);
if (scrollH < oldLen)
- RichString_printoffnVal(old, y+this->oldSelected - first, x,
+ RichString_printoffnVal(old, y + this->oldSelected - first, x,
scrollH, MINIMUM(oldLen - scrollH, this->w));
attrset(selectionColor);
- mvhline(y+this->selected - first, x+0, ' ', this->w);
+ mvhline(y + this->selected - first, x + 0, ' ', this->w);
RichString_setAttr(&new, selectionColor);
if (scrollH < newLen)
- RichString_printoffnVal(new, y+this->selected - first, x,
+ RichString_printoffnVal(new, y + this->selected - first, x,
scrollH, MINIMUM(newLen - scrollH, this->w));
attrset(CRT_colors[RESET_COLOR]);
RichString_end(new);
@@ -316,25 +327,27 @@ bool Panel_onKey(Panel* this, int key) {
assert (this != NULL);
int size = Vector_size(this->items);
+
+ #define CLAMP_INDEX(var, delta, min, max) \
+ CLAMP((var) + (delta), (min), MAXIMUM(0, (max)))
+
switch (key) {
case KEY_DOWN:
case KEY_CTRL('N'):
- this->selected++;
- break;
- case KEY_UP:
- case KEY_CTRL('P'):
- this->selected--;
- break;
#ifdef KEY_C_DOWN
case KEY_C_DOWN:
+ #endif
this->selected++;
break;
- #endif
+
+ case KEY_UP:
+ case KEY_CTRL('P'):
#ifdef KEY_C_UP
case KEY_C_UP:
+ #endif
this->selected--;
break;
- #endif
+
case KEY_LEFT:
case KEY_CTRL('B'):
if (this->scrollH > 0) {
@@ -342,43 +355,45 @@ bool Panel_onKey(Panel* this, int key) {
this->needsRedraw = true;
}
break;
+
case KEY_RIGHT:
case KEY_CTRL('F'):
this->scrollH += CRT_scrollHAmount;
this->needsRedraw = true;
break;
+
case KEY_PPAGE:
this->selected -= (this->h - 1);
- this->scrollV = MAXIMUM(0, this->scrollV - this->h + 1);
+ this->scrollV = CLAMP_INDEX(this->scrollV, -(this->h - 1), 0, size - this->h);
this->needsRedraw = true;
break;
+
case KEY_NPAGE:
this->selected += (this->h - 1);
- this->scrollV = MAXIMUM(0, MINIMUM(Vector_size(this->items) - this->h,
- this->scrollV + this->h - 1));
+ this->scrollV = CLAMP_INDEX(this->scrollV, +(this->h - 1), 0, size - this->h);
this->needsRedraw = true;
break;
+
case KEY_WHEELUP:
this->selected -= CRT_scrollWheelVAmount;
- this->scrollV -= CRT_scrollWheelVAmount;
+ this->scrollV = CLAMP_INDEX(this->scrollV, -CRT_scrollWheelVAmount, 0, size - this->h);
this->needsRedraw = true;
break;
+
case KEY_WHEELDOWN:
- {
this->selected += CRT_scrollWheelVAmount;
- this->scrollV += CRT_scrollWheelVAmount;
- if (this->scrollV > Vector_size(this->items) - this->h) {
- this->scrollV = Vector_size(this->items) - this->h;
- }
+ this->scrollV = CLAMP_INDEX(this->scrollV, +CRT_scrollWheelVAmount, 0, size - this->h);
this->needsRedraw = true;
break;
- }
+
case KEY_HOME:
this->selected = 0;
break;
+
case KEY_END:
this->selected = size - 1;
break;
+
case KEY_CTRL('A'):
case '^':
this->scrollH = 0;
@@ -393,6 +408,8 @@ bool Panel_onKey(Panel* this, int key) {
return false;
}
+ #undef CLAMP_INDEX
+
// ensure selection within bounds
if (this->selected < 0 || size == 0) {
this->selected = 0;
@@ -401,43 +418,60 @@ bool Panel_onKey(Panel* this, int key) {
this->selected = size - 1;
this->needsRedraw = true;
}
+
return true;
}
HandlerResult Panel_selectByTyping(Panel* this, int ch) {
int size = Panel_size(this);
+
if (!this->eventHandlerState)
this->eventHandlerState = xCalloc(100, sizeof(char));
char* buffer = this->eventHandlerState;
- if (ch > 0 && ch < 255 && isalnum(ch)) {
+ if (0 < ch && ch < 255 && isgraph((unsigned char)ch)) {
int len = strlen(buffer);
+ if (!len) {
+ if ('/' == ch) {
+ ch = '\001';
+ } else if ('q' == ch) {
+ return BREAK_LOOP;
+ }
+ } else if (1 == len && '\001' == buffer[0]) {
+ len--;
+ }
+
if (len < 99) {
buffer[len] = ch;
buffer[len+1] = '\0';
}
+
for (int try = 0; try < 2; try++) {
len = strlen(buffer);
for (int i = 0; i < size; i++) {
- char* cur = ((ListItem*) Panel_get(this, i))->value;
+ const char* cur = ((ListItem*) Panel_get(this, i))->value;
while (*cur == ' ') cur++;
if (strncasecmp(cur, buffer, len) == 0) {
Panel_setSelected(this, i);
return HANDLED;
}
}
+
// if current word did not match,
// retry considering the character the start of a new word.
buffer[0] = ch;
buffer[1] = '\0';
}
+
return HANDLED;
} else if (ch != ERR) {
buffer[0] = '\0';
}
+
if (ch == 13) {
return BREAK_LOOP;
}
+
return IGNORED;
}
diff --git a/Panel.h b/Panel.h
index 480e2c0..86c134e 100644
--- a/Panel.h
+++ b/Panel.h
@@ -3,14 +3,19 @@
/*
htop - Panel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+
+#include "FunctionBar.h"
#include "Object.h"
+#include "RichString.h"
#include "Vector.h"
-#include "FunctionBar.h"
+
+struct Panel_;
typedef struct Panel_ Panel;
typedef enum HandlerResult_ {
@@ -22,11 +27,11 @@ typedef enum HandlerResult_ {
SYNTH_KEY = 0x20,
} HandlerResult;
-#define EVENT_SET_SELECTED -1
+#define EVENT_SET_SELECTED (-1)
-#define EVENT_HEADER_CLICK(x_) (-10000 + x_)
-#define EVENT_IS_HEADER_CLICK(ev_) (ev_ >= -10000 && ev_ <= -9000)
-#define EVENT_HEADER_CLICK_GET_X(ev_) (ev_ + 10000)
+#define EVENT_HEADER_CLICK(x_) (-10000 + (x_))
+#define EVENT_IS_HEADER_CLICK(ev_) ((ev_) >= -10000 && (ev_) <= -9000)
+#define EVENT_HEADER_CLICK_GET_X(ev_) ((ev_) + 10000)
typedef HandlerResult(*Panel_EventHandler)(Panel*, int);
@@ -35,14 +40,13 @@ typedef struct PanelClass_ {
const Panel_EventHandler eventHandler;
} PanelClass;
-#define As_Panel(this_) ((PanelClass*)((this_)->super.klass))
+#define As_Panel(this_) ((const PanelClass*)((this_)->super.klass))
#define Panel_eventHandlerFn(this_) As_Panel(this_)->eventHandler
#define Panel_eventHandler(this_, ev_) As_Panel(this_)->eventHandler((Panel*)(this_), ev_)
struct Panel_ {
Object super;
int x, y, w, h;
- WINDOW* window;
Vector* items;
int selected;
int oldSelected;
@@ -57,17 +61,17 @@ struct Panel_ {
int selectionColor;
};
-#define Panel_setDefaultBar(this_) do{ (this_)->currentBar = (this_)->defaultBar; }while(0)
+#define Panel_setDefaultBar(this_) do { (this_)->currentBar = (this_)->defaultBar; } while (0)
#define KEY_CTRL(l) ((l)-'A'+1)
-extern PanelClass Panel_class;
+extern const PanelClass Panel_class;
-Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar);
+Panel* Panel_new(int x, int y, int w, int h, bool owner, const ObjectClass* type, FunctionBar* fuBar);
void Panel_delete(Object* cast);
-void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool owner, FunctionBar* fuBar);
+void Panel_init(Panel* this, int x, int y, int w, int h, const ObjectClass* type, bool owner, FunctionBar* fuBar);
void Panel_done(Panel* this);
@@ -105,9 +109,9 @@ int Panel_size(Panel* this);
void Panel_setSelected(Panel* this, int selected);
-void Panel_draw(Panel* this, bool focus);
+void Panel_draw(Panel* this, bool focus, bool highlightSelected);
-void Panel_splice(Panel *this, Vector* from);
+void Panel_splice(Panel* this, Vector* from);
bool Panel_onKey(Panel* this, int key);
diff --git a/Process.c b/Process.c
index 7a04c0f..8245f86 100644
--- a/Process.c
+++ b/Process.c
@@ -2,41 +2,41 @@
htop - Process.c
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "Process.h"
-#include "Settings.h"
-#include "config.h"
+#include <assert.h>
+#include <limits.h>
+#include <math.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/resource.h>
#include "CRT.h"
-#include "StringUtils.h"
-#include "RichString.h"
+#include "Macros.h"
#include "Platform.h"
+#include "ProcessList.h"
+#include "RichString.h"
+#include "Settings.h"
+#include "XUtils.h"
-#include <stdio.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <string.h>
-#include <stdbool.h>
-#include <pwd.h>
-#include <time.h>
-#include <assert.h>
-#include <math.h>
-#ifdef MAJOR_IN_MKDEV
+#if defined(MAJOR_IN_MKDEV)
#include <sys/mkdev.h>
#elif defined(MAJOR_IN_SYSMACROS)
#include <sys/sysmacros.h>
#endif
-static int Process_getuid = -1;
+
+static uid_t Process_getuid = (uid_t)-1;
char Process_pidFormat[20] = "%7d ";
@@ -44,7 +44,9 @@ static char Process_titleBuffer[20][20];
void Process_setupColumnWidths() {
int maxPid = Platform_getMaxPid();
- if (maxPid == -1) return;
+ if (maxPid == -1)
+ return;
+
int digits = ceil(log10(maxPid));
assert(digits < 20);
for (int i = 0; Process_pidColumns[i].label; i++) {
@@ -55,84 +57,104 @@ void Process_setupColumnWidths() {
xSnprintf(Process_pidFormat, sizeof(Process_pidFormat), "%%%dd ", digits);
}
-void Process_humanNumber(RichString* str, unsigned long number, bool coloring) {
- char buffer[11];
+void Process_humanNumber(RichString* str, unsigned long long number, bool coloring) {
+ char buffer[10];
int len;
int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
+ int processGigabytesColor = CRT_colors[PROCESS_GIGABYTES];
int processColor = CRT_colors[PROCESS];
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
+ processGigabytesColor = CRT_colors[PROCESS];
}
- if(number >= (10 * ONE_DECIMAL_M)) {
- #ifdef __LP64__
- if(number >= (100 * ONE_DECIMAL_G)) {
- len = snprintf(buffer, 10, "%4luT ", number / ONE_G);
- RichString_appendn(str, largeNumberColor, buffer, len);
- return;
- } else if (number >= (1000 * ONE_DECIMAL_M)) {
- len = snprintf(buffer, 10, "%4.1lfT ", (double)number / ONE_G);
- RichString_appendn(str, largeNumberColor, buffer, len);
- return;
- }
- #endif
- if(number >= (100 * ONE_DECIMAL_M)) {
- len = snprintf(buffer, 10, "%4luG ", number / ONE_M);
- RichString_appendn(str, largeNumberColor, buffer, len);
- return;
- }
- len = snprintf(buffer, 10, "%4.1lfG ", (double)number / ONE_M);
- RichString_appendn(str, largeNumberColor, buffer, len);
- return;
- } else if (number >= 100000) {
- len = snprintf(buffer, 10, "%4luM ", number / ONE_K);
- RichString_appendn(str, processMegabytesColor, buffer, len);
- return;
- } else if (number >= 1000) {
- len = snprintf(buffer, 10, "%2lu", number/1000);
+ if (number < 1000) {
+ //Plain number, no markings
+ len = xSnprintf(buffer, sizeof(buffer), "%5llu ", number);
+ RichString_appendn(str, processColor, buffer, len);
+ } else if (number < 100000) {
+ //2 digit MB, 3 digit KB
+ len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/1000);
RichString_appendn(str, processMegabytesColor, buffer, len);
number %= 1000;
- len = snprintf(buffer, 10, "%03lu ", number);
+ len = xSnprintf(buffer, sizeof(buffer), "%03llu ", number);
RichString_appendn(str, processColor, buffer, len);
- return;
+ } else if (number < 1000 * ONE_K) {
+ //3 digit MB
+ number /= ONE_K;
+ len = xSnprintf(buffer, sizeof(buffer), "%4lluM ", number);
+ RichString_appendn(str, processMegabytesColor, buffer, len);
+ } else if (number < 10000 * ONE_K) {
+ //1 digit GB, 3 digit MB
+ number /= ONE_K;
+ len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
+ RichString_appendn(str, processGigabytesColor, buffer, len);
+ number %= 1000;
+ len = xSnprintf(buffer, sizeof(buffer), "%03lluM ", number);
+ RichString_appendn(str, processMegabytesColor, buffer, len);
+ } else if (number < 100000 * ONE_K) {
+ //2 digit GB, 1 digit MB
+ number /= 100 * ONE_K;
+ len = xSnprintf(buffer, sizeof(buffer), "%2llu", number/10);
+ RichString_appendn(str, processGigabytesColor, buffer, len);
+ number %= 10;
+ len = xSnprintf(buffer, sizeof(buffer), ".%1llu", number);
+ RichString_appendn(str, processMegabytesColor, buffer, len);
+ RichString_append(str, processGigabytesColor, "G ");
+ } else if (number < 1000 * ONE_M) {
+ //3 digit GB
+ number /= ONE_M;
+ len = xSnprintf(buffer, sizeof(buffer), "%4lluG ", number);
+ RichString_appendn(str, processGigabytesColor, buffer, len);
+ } else if (number < 10000ULL * ONE_M) {
+ //1 digit TB, 3 digit GB
+ number /= ONE_M;
+ len = xSnprintf(buffer, sizeof(buffer), "%1llu", number/1000);
+ RichString_appendn(str, largeNumberColor, buffer, len);
+ number %= 1000;
+ len = xSnprintf(buffer, sizeof(buffer), "%03lluG ", number);
+ RichString_appendn(str, processGigabytesColor, buffer, len);
+ } else {
+ //2 digit TB and above
+ len = xSnprintf(buffer, sizeof(buffer), "%4.1lfT ", (double)number/ONE_G);
+ RichString_appendn(str, largeNumberColor, buffer, len);
}
- len = snprintf(buffer, 10, "%5lu ", number);
- RichString_appendn(str, processColor, buffer, len);
}
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring) {
- char buffer[14];
+ char buffer[13];
int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processColor = CRT_colors[PROCESS];
int processShadowColor = CRT_colors[PROCESS_SHADOW];
+
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
processShadowColor = CRT_colors[PROCESS];
}
- if ((long long) number == -1LL) {
- int len = snprintf(buffer, 13, " no perm ");
+ if (number == ULLONG_MAX) {
+ int len = xSnprintf(buffer, sizeof(buffer), " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (number >= 100000LL * ONE_DECIMAL_T) {
- xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_G);
+ xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_G);
RichString_appendn(str, largeNumberColor, buffer, 12);
} else if (number >= 100LL * ONE_DECIMAL_T) {
- xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_M);
+ xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_M);
RichString_appendn(str, largeNumberColor, buffer, 8);
RichString_appendn(str, processMegabytesColor, buffer+8, 4);
} else if (number >= 10LL * ONE_DECIMAL_G) {
- xSnprintf(buffer, 13, "%11llu ", number / ONE_DECIMAL_K);
+ xSnprintf(buffer, sizeof(buffer), "%11llu ", number / ONE_DECIMAL_K);
RichString_appendn(str, largeNumberColor, buffer, 5);
RichString_appendn(str, processMegabytesColor, buffer+5, 3);
RichString_appendn(str, processColor, buffer+8, 4);
} else {
- xSnprintf(buffer, 13, "%11llu ", number);
+ xSnprintf(buffer, sizeof(buffer), "%11llu ", number);
RichString_appendn(str, largeNumberColor, buffer, 2);
RichString_appendn(str, processMegabytesColor, buffer+2, 3);
RichString_appendn(str, processColor, buffer+5, 3);
@@ -147,25 +169,31 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths) {
int minutes = (totalSeconds / 60) % 60;
int seconds = totalSeconds % 60;
int hundredths = totalHundredths - (totalSeconds * 100);
- char buffer[11];
+ char buffer[10];
if (hours >= 100) {
- xSnprintf(buffer, 10, "%7lluh ", hours);
+ xSnprintf(buffer, sizeof(buffer), "%7lluh ", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
} else {
if (hours) {
- xSnprintf(buffer, 10, "%2lluh", hours);
+ xSnprintf(buffer, sizeof(buffer), "%2lluh", hours);
RichString_append(str, CRT_colors[LARGE_NUMBER], buffer);
- xSnprintf(buffer, 10, "%02d:%02d ", minutes, seconds);
+ xSnprintf(buffer, sizeof(buffer), "%02d:%02d ", minutes, seconds);
} else {
- xSnprintf(buffer, 10, "%2d:%02d.%02d ", minutes, seconds, hundredths);
+ xSnprintf(buffer, sizeof(buffer), "%2d:%02d.%02d ", minutes, seconds, hundredths);
}
RichString_append(str, CRT_colors[DEFAULT_COLOR], buffer);
}
}
-static inline void Process_writeCommand(Process* this, int attr, int baseattr, RichString* str) {
+void Process_fillStarttimeBuffer(Process* this) {
+ struct tm date;
+ (void) localtime_r(&this->starttime_ctime, &date);
+ strftime(this->starttime_show, sizeof(this->starttime_show) - 1, (this->starttime_ctime > (time(NULL) - 86400)) ? "%R " : "%b%d ", &date);
+}
+
+static inline void Process_writeCommand(const Process* this, int attr, int baseattr, RichString* str) {
int start = RichString_size(str), finish = 0;
- char* comm = this->comm;
+ const char* comm = this->comm;
if (this->settings->highlightBaseName || !this->settings->showProgramPath) {
int i, basename = 0;
@@ -178,10 +206,11 @@ static inline void Process_writeCommand(Process* this, int attr, int baseattr, R
}
}
if (!finish) {
- if (this->settings->showProgramPath)
+ if (this->settings->showProgramPath) {
start += basename;
- else
+ } else {
comm += basename;
+ }
finish = this->basenameOffset - basename;
}
finish += start - 1;
@@ -189,20 +218,23 @@ static inline void Process_writeCommand(Process* this, int attr, int baseattr, R
RichString_append(str, attr, comm);
- if (this->settings->highlightBaseName)
+ if (this->settings->highlightBaseName) {
RichString_setAttrn(str, baseattr, start, finish);
+ }
}
-void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring) {
+void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring) {
int largeNumberColor = CRT_colors[LARGE_NUMBER];
int processMegabytesColor = CRT_colors[PROCESS_MEGABYTES];
int processColor = CRT_colors[PROCESS];
+
if (!coloring) {
largeNumberColor = CRT_colors[PROCESS];
processMegabytesColor = CRT_colors[PROCESS];
}
- if (rate == -1) {
- int len = snprintf(buffer, n, " no perm ");
+
+ if (isnan(rate)) {
+ int len = xSnprintf(buffer, n, " N/A ");
RichString_appendn(str, CRT_colors[PROCESS_SHADOW], buffer, len);
} else if (rate < ONE_K) {
int len = snprintf(buffer, n, "%7.2f B/s ", rate);
@@ -222,21 +254,26 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
}
}
-void Process_writeField(Process* this, RichString* str, ProcessField field) {
+void Process_writeField(const Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int baseattr = CRT_colors[PROCESS_BASENAME];
- int n = sizeof(buffer) - 1;
+ size_t n = sizeof(buffer) - 1;
bool coloring = this->settings->highlightMegabytes;
switch (field) {
- case PERCENT_CPU: {
- if (this->percent_cpu > 999.9) {
- xSnprintf(buffer, n, "%4u ", (unsigned int)this->percent_cpu);
- } else if (this->percent_cpu > 99.9) {
- xSnprintf(buffer, n, "%3u. ", (unsigned int)this->percent_cpu);
+ case PERCENT_CPU:
+ case PERCENT_NORM_CPU: {
+ float cpuPercentage = this->percent_cpu;
+ if (field == PERCENT_NORM_CPU) {
+ cpuPercentage /= this->processList->cpuCount;
+ }
+ if (cpuPercentage > 999.9) {
+ xSnprintf(buffer, n, "%4u ", (unsigned int)cpuPercentage);
+ } else if (cpuPercentage > 99.9) {
+ xSnprintf(buffer, n, "%3u. ", (unsigned int)cpuPercentage);
} else {
- xSnprintf(buffer, n, "%4.1f ", this->percent_cpu);
+ xSnprintf(buffer, n, "%4.1f ", cpuPercentage);
}
break;
}
@@ -262,16 +299,20 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool lastItem = (this->indent < 0);
int indent = (this->indent < 0 ? -this->indent : this->indent);
- for (int i = 0; i < 32; i++)
- if (indent & (1U << i))
+ for (int i = 0; i < 32; i++) {
+ if (indent & (1U << i)) {
maxIndent = i+1;
- for (int i = 0; i < maxIndent - 1; i++) {
+ }
+ }
+
+ for (int i = 0; i < maxIndent - 1; i++) {
int written, ret;
- if (indent & (1 << i))
+ if (indent & (1 << i)) {
ret = snprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]);
- else
+ } else {
ret = snprintf(buf, n, " ");
- if (ret < 0 || ret >= n) {
+ }
+ if (ret < 0 || (size_t)ret >= n) {
written = n;
} else {
written = ret;
@@ -288,8 +329,8 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
}
case MAJFLT: Process_colorNumber(str, this->majflt, coloring); return;
case MINFLT: Process_colorNumber(str, this->minflt, coloring); return;
- case M_RESIDENT: Process_humanNumber(str, this->m_resident * PAGE_SIZE_KB, coloring); return;
- case M_SIZE: Process_humanNumber(str, this->m_size * PAGE_SIZE_KB, coloring); return;
+ case M_RESIDENT: Process_humanNumber(str, this->m_resident * CRT_pageSizeKB, coloring); return;
+ case M_VIRT: Process_humanNumber(str, this->m_virt * CRT_pageSizeKB, coloring); return;
case NICE: {
xSnprintf(buffer, n, "%3ld ", this->nice);
attr = this->nice < 0 ? CRT_colors[PROCESS_HIGH_PRIORITY]
@@ -329,7 +370,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
case TPGID: xSnprintf(buffer, n, Process_pidFormat, this->tpgid); break;
case TTY_NR: xSnprintf(buffer, n, "%3u:%3u ", major(this->tty_nr), minor(this->tty_nr)); break;
case USER: {
- if (Process_getuid != (int) this->st_uid)
+ if (Process_getuid != this->st_uid)
attr = CRT_colors[PROCESS_SHADOW];
if (this->user) {
xSnprintf(buffer, n, "%-9s ", this->user);
@@ -348,16 +389,29 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
RichString_append(str, attr, buffer);
}
-void Process_display(Object* cast, RichString* out) {
- Process* this = (Process*) cast;
- ProcessField* fields = this->settings->fields;
+void Process_display(const Object* cast, RichString* out) {
+ const Process* this = (const Process*) cast;
+ const ProcessField* fields = this->settings->fields;
RichString_prune(out);
for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]);
- if (this->settings->shadowOtherUsers && (int)this->st_uid != Process_getuid)
+
+ if (this->settings->shadowOtherUsers && this->st_uid != Process_getuid) {
RichString_setAttr(out, CRT_colors[PROCESS_SHADOW]);
- if (this->tag == true)
+ }
+
+ if (this->tag == true) {
RichString_setAttr(out, CRT_colors[PROCESS_TAG]);
+ }
+
+ if (this->settings->highlightChanges) {
+ if (Process_isTomb(this)) {
+ out->highlightAttr = CRT_colors[PROCESS_TOMB];
+ } else if (Process_isNew(this)) {
+ out->highlightAttr = CRT_colors[PROCESS_NEW];
+ }
+ }
+
assert(out->chlen > 0);
}
@@ -366,7 +420,11 @@ void Process_done(Process* this) {
free(this->comm);
}
-ProcessClass Process_class = {
+static const char* Process_getCommandStr(const Process* p) {
+ return p->comm ? p->comm : "";
+}
+
+const ProcessClass Process_class = {
.super = {
.extends = Class(Object),
.display = Process_display,
@@ -374,22 +432,38 @@ ProcessClass Process_class = {
.compare = Process_compare
},
.writeField = Process_writeField,
+ .getCommandStr = Process_getCommandStr,
};
-void Process_init(Process* this, struct Settings_* settings) {
+void Process_init(Process* this, const struct Settings_* settings) {
this->settings = settings;
this->tag = false;
this->showChildren = true;
this->show = true;
this->updated = false;
this->basenameOffset = -1;
- if (Process_getuid == -1) Process_getuid = getuid();
+
+ if (Process_getuid == (uid_t)-1) {
+ Process_getuid = getuid();
+ }
}
void Process_toggleTag(Process* this) {
this->tag = this->tag == true ? false : true;
}
+bool Process_isNew(const Process* this) {
+ assert(this->processList);
+ if (this->processList->scanTs >= this->seenTs) {
+ return this->processList->scanTs - this->seenTs <= this->processList->settings->highlightDelaySecs;
+ }
+ return false;
+}
+
+bool Process_isTomb(const Process* this) {
+ return this->tombTs > 0;
+}
+
bool Process_setPriority(Process* this, int priority) {
CRT_dropPrivileges();
int old_prio = getpriority(PRIO_PROCESS, this->pid);
@@ -413,73 +487,74 @@ bool Process_sendSignal(Process* this, Arg sgn) {
}
long Process_pidCompare(const void* v1, const void* v2) {
- Process* p1 = (Process*)v1;
- Process* p2 = (Process*)v2;
+ const Process* p1 = (const Process*)v1;
+ const Process* p2 = (const Process*)v2;
return (p1->pid - p2->pid);
}
long Process_compare(const void* v1, const void* v2) {
- Process *p1, *p2;
- Settings *settings = ((Process*)v1)->settings;
+ const Process *p1, *p2;
+ const Settings *settings = ((const Process*)v1)->settings;
+ int r;
+
if (settings->direction == 1) {
- p1 = (Process*)v1;
- p2 = (Process*)v2;
+ p1 = (const Process*)v1;
+ p2 = (const Process*)v2;
} else {
- p2 = (Process*)v1;
- p1 = (Process*)v2;
+ p2 = (const Process*)v1;
+ p1 = (const Process*)v2;
}
+
switch (settings->sortKey) {
case PERCENT_CPU:
- return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
+ case PERCENT_NORM_CPU:
+ return SPACESHIP_NUMBER(p2->percent_cpu, p1->percent_cpu);
case PERCENT_MEM:
- return (p2->m_resident - p1->m_resident);
+ return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident);
case COMM:
- return strcmp(p1->comm, p2->comm);
+ return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2));
case MAJFLT:
- return (p2->majflt - p1->majflt);
+ return SPACESHIP_NUMBER(p2->majflt, p1->majflt);
case MINFLT:
- return (p2->minflt - p1->minflt);
+ return SPACESHIP_NUMBER(p2->minflt, p1->minflt);
case M_RESIDENT:
- return (p2->m_resident - p1->m_resident);
- case M_SIZE:
- return (p2->m_size - p1->m_size);
+ return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident);
+ case M_VIRT:
+ return SPACESHIP_NUMBER(p2->m_virt, p1->m_virt);
case NICE:
- return (p1->nice - p2->nice);
+ return SPACESHIP_NUMBER(p1->nice, p2->nice);
case NLWP:
- return (p1->nlwp - p2->nlwp);
+ return SPACESHIP_NUMBER(p1->nlwp, p2->nlwp);
case PGRP:
- return (p1->pgrp - p2->pgrp);
+ return SPACESHIP_NUMBER(p1->pgrp, p2->pgrp);
case PID:
- return (p1->pid - p2->pid);
+ return SPACESHIP_NUMBER(p1->pid, p2->pid);
case PPID:
- return (p1->ppid - p2->ppid);
+ return SPACESHIP_NUMBER(p1->ppid, p2->ppid);
case PRIORITY:
- return (p1->priority - p2->priority);
+ return SPACESHIP_NUMBER(p1->priority, p2->priority);
case PROCESSOR:
- return (p1->processor - p2->processor);
+ return SPACESHIP_NUMBER(p1->processor, p2->processor);
case SESSION:
- return (p1->session - p2->session);
- case STARTTIME: {
- if (p1->starttime_ctime == p2->starttime_ctime)
- return (p1->pid - p2->pid);
- else
- return (p1->starttime_ctime - p2->starttime_ctime);
- }
+ return SPACESHIP_NUMBER(p1->session, p2->session);
+ case STARTTIME:
+ r = SPACESHIP_NUMBER(p1->starttime_ctime, p2->starttime_ctime);
+ return r != 0 ? r : SPACESHIP_NUMBER(p1->pid, p2->pid);
case STATE:
- return (Process_sortState(p1->state) - Process_sortState(p2->state));
+ return SPACESHIP_NUMBER(Process_sortState(p1->state), Process_sortState(p2->state));
case ST_UID:
- return (p1->st_uid - p2->st_uid);
+ return SPACESHIP_NUMBER(p1->st_uid, p2->st_uid);
case TIME:
- return ((p2->time) - (p1->time));
+ return SPACESHIP_NUMBER(p2->time, p1->time);
case TGID:
- return (p1->tgid - p2->tgid);
+ return SPACESHIP_NUMBER(p1->tgid, p2->tgid);
case TPGID:
- return (p1->tpgid - p2->tpgid);
+ return SPACESHIP_NUMBER(p1->tpgid, p2->tpgid);
case TTY_NR:
- return (p1->tty_nr - p2->tty_nr);
+ return SPACESHIP_NUMBER(p1->tty_nr, p2->tty_nr);
case USER:
- return strcmp(p1->user ? p1->user : "", p2->user ? p2->user : "");
+ return SPACESHIP_NULLSTR(p1->user, p2->user);
default:
- return (p1->pid - p2->pid);
+ return SPACESHIP_NUMBER(p1->pid, p2->pid);
}
}
diff --git a/Process.h b/Process.h
index ebf5cdf..48e31b1 100644
--- a/Process.h
+++ b/Process.h
@@ -4,27 +4,24 @@
htop - Process.h
(C) 2004-2015 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "Object.h"
+#include "RichString.h"
+
#ifdef __ANDROID__
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif
-// On Linux, this works only with glibc 2.1+. On earlier versions
-// the behavior is similar to have a hardcoded page size.
-#ifndef PAGE_SIZE
-#define PAGE_SIZE ( sysconf(_SC_PAGESIZE) )
-#endif
-#define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K )
-
-#include "Object.h"
-
-#include <sys/types.h>
-
#define PROCESS_FLAG_IO 0x0001
+#define DEFAULT_HIGHLIGHT_SECS 5
typedef enum ProcessFields {
NULL_PROCESSFIELD = 0,
@@ -42,7 +39,7 @@ typedef enum ProcessFields {
NICE = 19,
STARTTIME = 21,
PROCESSOR = 38,
- M_SIZE = 39,
+ M_VIRT = 39,
M_RESIDENT = 40,
ST_UID = 46,
PERCENT_CPU = 47,
@@ -51,6 +48,7 @@ typedef enum ProcessFields {
TIME = 50,
NLWP = 51,
TGID = 52,
+ PERCENT_NORM_CPU = 53,
} ProcessField;
typedef struct ProcessPidColumn_ {
@@ -58,16 +56,19 @@ typedef struct ProcessPidColumn_ {
const char* label;
} ProcessPidColumn;
+struct Settings_;
+
typedef struct Process_ {
Object super;
- struct Settings_* settings;
+ const struct ProcessList_* processList;
+ const struct Settings_* settings;
unsigned long long int time;
pid_t pid;
pid_t ppid;
pid_t tgid;
- char* comm;
+ char* comm; /* use Process_getCommand() for Command actually displayed */
int commLen;
int indent;
@@ -78,6 +79,7 @@ typedef struct Process_ {
bool tag;
bool showChildren;
bool show;
+ bool wasShown;
unsigned int pgrp;
unsigned int session;
unsigned int tty_nr;
@@ -88,7 +90,7 @@ typedef struct Process_ {
float percent_cpu;
float percent_mem;
- char* user;
+ const char* user;
long int priority;
long int nice;
@@ -96,101 +98,100 @@ typedef struct Process_ {
char starttime_show[8];
time_t starttime_ctime;
- long m_size;
+ long m_virt;
long m_resident;
int exit_signal;
+ time_t seenTs;
+ time_t tombTs;
+
unsigned long int minflt;
unsigned long int majflt;
- #ifdef DEBUG
- long int itrealvalue;
- unsigned long int vsize;
- long int rss;
- unsigned long int rlim;
- unsigned long int startcode;
- unsigned long int endcode;
- unsigned long int startstack;
- unsigned long int kstkesp;
- unsigned long int kstkeip;
- unsigned long int signal;
- unsigned long int blocked;
- unsigned long int sigignore;
- unsigned long int sigcatch;
- unsigned long int wchan;
- unsigned long int nswap;
- unsigned long int cnswap;
- #endif
+ unsigned int tree_left;
+ unsigned int tree_right;
+ unsigned int tree_depth;
+ unsigned int tree_index;
} Process;
typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
- int flags;
+ uint32_t flags;
} ProcessFieldData;
// Implemented in platform-specific code:
-void Process_writeField(Process* this, RichString* str, ProcessField field);
+void Process_writeField(const Process* this, RichString* str, ProcessField field);
long Process_compare(const void* v1, const void* v2);
void Process_delete(Object* cast);
-bool Process_isThread(Process* this);
+bool Process_isThread(const Process* this);
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
extern char Process_pidFormat[20];
-typedef Process*(*Process_New)(struct Settings_*);
-typedef void (*Process_WriteField)(Process*, RichString*, ProcessField);
+typedef Process*(*Process_New)(const struct Settings_*);
+typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
+typedef const char* (*Process_GetCommandStr)(const Process*);
typedef struct ProcessClass_ {
const ObjectClass super;
const Process_WriteField writeField;
+ const Process_GetCommandStr getCommandStr;
} ProcessClass;
-#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
+#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
+
+#define Process_getCommand(this_) (As_Process(this_)->getCommandStr ? As_Process(this_)->getCommandStr((const Process*)(this_)) : ((const Process*)(this_))->comm)
-#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
+static inline pid_t Process_getParentPid(const Process* this) {
+ return this->tgid == this->pid ? this->ppid : this->tgid;
+}
-#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
+static inline bool Process_isChildOf(const Process* this, pid_t pid) {
+ return pid == Process_getParentPid(this);
+}
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
-#define ONE_K 1024L
+#define ONE_K 1024UL
#define ONE_M (ONE_K * ONE_K)
#define ONE_G (ONE_M * ONE_K)
-#define ONE_T ((long long)ONE_G * ONE_K)
+#define ONE_T (1ULL * ONE_G * ONE_K)
-#define ONE_DECIMAL_K 1000L
+#define ONE_DECIMAL_K 1000UL
#define ONE_DECIMAL_M (ONE_DECIMAL_K * ONE_DECIMAL_K)
#define ONE_DECIMAL_G (ONE_DECIMAL_M * ONE_DECIMAL_K)
-#define ONE_DECIMAL_T ((long long)ONE_DECIMAL_G * ONE_DECIMAL_K)
-
-extern char Process_pidFormat[20];
+#define ONE_DECIMAL_T (1ULL * ONE_DECIMAL_G * ONE_DECIMAL_K)
-void Process_setupColumnWidths();
+void Process_setupColumnWidths(void);
-void Process_humanNumber(RichString* str, unsigned long number, bool coloring);
+void Process_humanNumber(RichString* str, unsigned long long number, bool coloring);
void Process_colorNumber(RichString* str, unsigned long long number, bool coloring);
void Process_printTime(RichString* str, unsigned long long totalHundredths);
-void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
+void Process_fillStarttimeBuffer(Process* this);
-void Process_writeField(Process* this, RichString* str, ProcessField field);
+void Process_outputRate(RichString* str, char* buffer, size_t n, double rate, int coloring);
-void Process_display(Object* cast, RichString* out);
+void Process_display(const Object* cast, RichString* out);
void Process_done(Process* this);
-extern ProcessClass Process_class;
+extern const ProcessClass Process_class;
-void Process_init(Process* this, struct Settings_* settings);
+void Process_init(Process* this, const struct Settings_* settings);
void Process_toggleTag(Process* this);
+bool Process_isNew(const Process* this);
+
+bool Process_isTomb(const Process* this);
+
bool Process_setPriority(Process* this, int priority);
bool Process_changePriorityBy(Process* this, Arg delta);
@@ -199,6 +200,4 @@ bool Process_sendSignal(Process* this, Arg sgn);
long Process_pidCompare(const void* v1, const void* v2);
-long Process_compare(const void* v1, const void* v2);
-
#endif
diff --git a/ProcessList.c b/ProcessList.c
index aa529d5..2d27339 100644
--- a/ProcessList.c
+++ b/ProcessList.c
@@ -1,33 +1,42 @@
/*
htop - ProcessList.c
(C) 2004,2005 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "ProcessList.h"
-#include "Platform.h"
-
-#include "CRT.h"
-#include "StringUtils.h"
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
+
+#include "CRT.h"
+#include "Hashtable.h"
+#include "Macros.h"
+#include "Vector.h"
+#include "XUtils.h"
-ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
+ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
this->processes = Vector_new(klass, true, DEFAULT_SIZE);
- this->processTable = Hashtable_new(140, false);
+ this->processes2 = Vector_new(klass, true, DEFAULT_SIZE); // tree-view auxiliary buffer
+
+ this->processTable = Hashtable_new(200, false);
+ this->displayTreeSet = Hashtable_new(200, false);
+ this->draftingTreeSet = Hashtable_new(200, false);
+
this->usersTable = usersTable;
this->pidMatchList = pidMatchList;
- this->userId = userId;
- // tree-view auxiliary buffer
- this->processes2 = Vector_new(klass, true, DEFAULT_SIZE);
+ this->userId = userId;
// set later by platform-specific code
this->cpuCount = 0;
+ this->scanTs = 0;
+
#ifdef HAVE_LIBHWLOC
this->topologyOk = false;
if (hwloc_topology_init(&this->topology) == 0) {
@@ -57,9 +66,13 @@ void ProcessList_done(ProcessList* this) {
hwloc_topology_destroy(this->topology);
}
#endif
+
+ Hashtable_delete(this->draftingTreeSet);
+ Hashtable_delete(this->displayTreeSet);
Hashtable_delete(this->processTable);
- Vector_delete(this->processes);
+
Vector_delete(this->processes2);
+ Vector_delete(this->processes);
}
void ProcessList_setPanel(ProcessList* this, Panel* panel) {
@@ -68,20 +81,31 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header);
- ProcessField* fields = this->settings->fields;
+
+ const ProcessField* fields = this->settings->fields;
+
for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title;
- if (!field) field = "- ";
- if (!this->settings->treeView && this->settings->sortKey == fields[i])
- RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field);
- else
- RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
+ if (!field) {
+ field = "- ";
+ }
+
+ int color = (this->settings->sortKey == fields[i]) ?
+ CRT_colors[PANEL_SELECTION_FOCUS] : CRT_colors[PANEL_HEADER_FOCUS];
+ RichString_append(header, color, field);
+ if (COMM == fields[i] && this->settings->showMergedCommand) {
+ RichString_append(header, color, "(merged)");
+ }
}
}
void ProcessList_add(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) == -1);
assert(Hashtable_get(this->processTable, p->pid) == NULL);
+ p->processList = this;
+
+ // highlighting processes found in first scan by first scan marked "far in the past"
+ p->seenTs = this->scanTs;
Vector_add(this->processes, p);
Hashtable_put(this->processTable, p->pid, p);
@@ -94,132 +118,339 @@ void ProcessList_add(ProcessList* this, Process* p) {
void ProcessList_remove(ProcessList* this, Process* p) {
assert(Vector_indexOf(this->processes, p, Process_pidCompare) != -1);
assert(Hashtable_get(this->processTable, p->pid) != NULL);
+
Process* pp = Hashtable_remove(this->processTable, p->pid);
assert(pp == p); (void)pp;
+
unsigned int pid = p->pid;
int idx = Vector_indexOf(this->processes, p, Process_pidCompare);
assert(idx != -1);
- if (idx >= 0) Vector_remove(this->processes, idx);
+
+ if (idx >= 0) {
+ Vector_remove(this->processes, idx);
+ }
+
assert(Hashtable_get(this->processTable, pid) == NULL); (void)pid;
assert(Hashtable_count(this->processTable) == Vector_count(this->processes));
}
Process* ProcessList_get(ProcessList* this, int idx) {
- return (Process*) (Vector_get(this->processes, idx));
+ return (Process*)Vector_get(this->processes, idx);
}
int ProcessList_size(ProcessList* this) {
- return (Vector_size(this->processes));
+ return Vector_size(this->processes);
+}
+
+// ProcessList_updateTreeSetLayer sorts this->displayTreeSet,
+// relying only on itself.
+//
+// Algorithm
+//
+// The algorithm is based on `depth-first search`,
+// even though `breadth-first search` approach may be more efficient on first glance,
+// after comparision it may be not, as it's not safe to go deeper without first updating the tree structure.
+// If it would be safe that approach would likely bring an advantage in performance.
+//
+// Each call of the function looks for a 'layer'. A 'layer' is a list of processes with the same depth.
+// First it sorts a list. Then it runs the function recursively for each element of the sorted list.
+// After that it updates the settings of processes.
+//
+// It relies on `leftBound` and `rightBound` as an optimization to cut the list size at the time it builds a 'layer'.
+//
+// It uses a temporary Hashtable `draftingTreeSet` because it's not safe to traverse a tree
+// and at the same time make changes in it.
+//
+static void ProcessList_updateTreeSetLayer(ProcessList* this, unsigned int leftBound, unsigned int rightBound, unsigned int deep, unsigned int left, unsigned int right, unsigned int* index, unsigned int* treeIndex, int indent) {
+
+ // It's guaranteed that layer_size is enough space
+ // but most likely it needs less. Specifically on first iteration.
+ int layerSize = (right - left) / 2;
+
+ // check if we reach `children` of `leaves`
+ if (layerSize == 0)
+ return;
+
+ Vector* layer = Vector_new(this->processes->type, false, layerSize);
+
+ // Find all processes on the same layer (process with the same `deep` value
+ // and included in a range from `leftBound` to `rightBound`).
+ //
+ // This loop also keeps track of left_bound and right_bound of these processes
+ // in order not to lose this information once the list is sorted.
+ //
+ // The variables left_bound and right_bound are different from what the values lhs and rhs represent.
+ // While left_bound and right_bound define a range of processes to look at, the values given by lhs and rhs are indices into an array
+ //
+ // In the below example note how filtering a range of indices i is different from filtering for processes in the bounds left_bound < x < right_bound …
+ //
+ // The nested tree set is sorted by left value, which is guaranteed upon entry/exit of this function.
+ //
+ // i | l | r
+ // 1 | 1 | 9
+ // 2 | 2 | 8
+ // 3 | 4 | 5
+ // 4 | 6 | 7
+ for (unsigned int i = leftBound; i < rightBound; i++) {
+ Process* proc = (Process*)Hashtable_get(this->displayTreeSet, i);
+ assert(proc);
+ if (proc && proc->tree_depth == deep && proc->tree_left > left && proc->tree_right < right) {
+ if (Vector_size(layer) > 0) {
+ Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
+
+ // Make a 'right_bound' of previous_process in a layer the current process's index.
+ //
+ // Use 'tree_depth' as a temporal variable.
+ // It's safe to do as later 'tree_depth' will be renovated.
+ previous_process->tree_depth = proc->tree_index;
+ }
+
+ Vector_add(layer, proc);
+ }
+ }
+
+ // The loop above changes just up to process-1.
+ // So the last process of the layer isn't updated by the above code.
+ //
+ // Thus, if present, set the `rightBound` to the last process on the layer
+ if (Vector_size(layer) > 0) {
+ Process* previous_process = (Process*)Vector_get(layer, Vector_size(layer) - 1);
+ previous_process->tree_depth = rightBound;
+ }
+
+ Vector_quickSort(layer);
+
+ int size = Vector_size(layer);
+ for (int i = 0; i < size; i++) {
+ Process* proc = (Process*)Vector_get(layer, i);
+
+ unsigned int idx = (*index)++;
+ int newLeft = (*treeIndex)++;
+
+ int level = deep == 0 ? 0 : (int)deep - 1;
+ int currentIndent = indent == -1 ? 0 : indent | (1 << level);
+ int nextIndent = indent == -1 ? 0 : ((i < size - 1) ? currentIndent : indent);
+
+ unsigned int newLeftBound = proc->tree_index;
+ unsigned int newRightBound = proc->tree_depth;
+ ProcessList_updateTreeSetLayer(this, newLeftBound, newRightBound, deep + 1, proc->tree_left, proc->tree_right, index, treeIndex, nextIndent);
+
+ int newRight = (*treeIndex)++;
+
+ proc->tree_left = newLeft;
+ proc->tree_right = newRight;
+ proc->tree_index = idx;
+ proc->tree_depth = deep;
+
+ if (indent == -1) {
+ proc->indent = 0;
+ } else if (i == size - 1) {
+ proc->indent = -currentIndent;
+ } else {
+ proc->indent = currentIndent;
+ }
+
+ Hashtable_put(this->draftingTreeSet, proc->tree_index, proc);
+
+ // It's not strictly necessary to do this, but doing so anyways
+ // allows for checking the correctness of the inner workings.
+ Hashtable_remove(this->displayTreeSet, newLeftBound);
+ }
+
+ Vector_delete(layer);
+}
+
+static void ProcessList_updateTreeSet(ProcessList* this) {
+ unsigned int index = 0;
+ unsigned int tree_index = 1;
+
+ const int vsize = Vector_size(this->processes);
+
+ assert(Hashtable_count(this->draftingTreeSet) == 0);
+ assert((int)Hashtable_count(this->displayTreeSet) == vsize);
+
+ ProcessList_updateTreeSetLayer(this, 0, vsize, 0, 0, vsize * 2 + 1, &index, &tree_index, -1);
+
+ Hashtable* tmp = this->draftingTreeSet;
+ this->draftingTreeSet = this->displayTreeSet;
+ this->displayTreeSet = tmp;
+
+ assert(Hashtable_count(this->draftingTreeSet) == 0);
+ assert((int)Hashtable_count(this->displayTreeSet) == vsize);
}
-static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show) {
+static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, int direction, bool show, int* node_counter, int* node_index) {
Vector* children = Vector_new(Class(Process), false, DEFAULT_SIZE);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
- Process* process = (Process*) (Vector_get(this->processes, i));
+ Process* process = (Process*)Vector_get(this->processes, i);
if (process->show && Process_isChildOf(process, pid)) {
- process = (Process*) (Vector_take(this->processes, i));
+ process = (Process*)Vector_take(this->processes, i);
Vector_add(children, process);
}
}
+
int size = Vector_size(children);
for (int i = 0; i < size; i++) {
- Process* process = (Process*) (Vector_get(children, i));
- if (!show)
+ int index = (*node_index)++;
+ Process* process = (Process*)Vector_get(children, i);
+
+ int lft = (*node_counter)++;
+
+ if (!show) {
process->show = false;
- int s = this->processes2->items;
- if (direction == 1)
+ }
+
+ int s = Vector_size(this->processes2);
+ if (direction == 1) {
Vector_add(this->processes2, process);
- else
+ } else {
Vector_insert(this->processes2, 0, process);
- assert(this->processes2->items == s+1); (void)s;
+ }
+
+ assert(Vector_size(this->processes2) == s + 1); (void)s;
+
int nextIndent = indent | (1 << level);
- ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false);
- if (i == size - 1)
+ ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < size - 1) ? nextIndent : indent, direction, show ? process->showChildren : false, node_counter, node_index);
+ if (i == size - 1) {
process->indent = -nextIndent;
- else
+ } else {
process->indent = nextIndent;
+ }
+
+ int rht = (*node_counter)++;
+
+ process->tree_left = lft;
+ process->tree_right = rht;
+ process->tree_depth = level + 1;
+ process->tree_index = index;
+ Hashtable_put(this->displayTreeSet, index, process);
}
Vector_delete(children);
}
-void ProcessList_sort(ProcessList* this) {
- if (!this->settings->treeView) {
- Vector_insertionSort(this->processes);
- } else {
- // Save settings
- int direction = this->settings->direction;
- int sortKey = this->settings->sortKey;
- // Sort by PID
- this->settings->sortKey = PID;
- this->settings->direction = 1;
- Vector_quickSort(this->processes);
- // Restore settings
- this->settings->sortKey = sortKey;
- this->settings->direction = direction;
- int vsize = Vector_size(this->processes);
- // Find all processes whose parent is not visible
- int size;
- while ((size = Vector_size(this->processes))) {
- int i;
- for (i = 0; i < size; i++) {
- Process* process = (Process*)(Vector_get(this->processes, i));
- // Immediately consume not shown processes
- if (!process->show) {
- process = (Process*)(Vector_take(this->processes, i));
- process->indent = 0;
- Vector_add(this->processes2, process);
- ProcessList_buildTree(this, process->pid, 0, 0, direction, false);
- break;
- }
- pid_t ppid = Process_getParentPid(process);
- // Bisect the process vector to find parent
- int l = 0, r = size;
- // If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
- // on Mac OS X 10.11.6) cancel bisecting and regard this process as
- // root.
- if (process->pid == ppid)
- r = 0;
- while (l < r) {
- int c = (l + r) / 2;
- pid_t pid = ((Process*)(Vector_get(this->processes, c)))->pid;
- if (ppid == pid) {
- break;
- } else if (ppid < pid) {
- r = c;
- } else {
- l = c + 1;
- }
- }
- // If parent not found, then construct the tree with this root
- if (l >= r) {
- process = (Process*)(Vector_take(this->processes, i));
- process->indent = 0;
- Vector_add(this->processes2, process);
- ProcessList_buildTree(this, process->pid, 0, 0, direction, process->showChildren);
+static long ProcessList_treeProcessCompare(const void* v1, const void* v2) {
+ const Process *p1 = (const Process*)v1;
+ const Process *p2 = (const Process*)v2;
+
+ return SPACESHIP_NUMBER(p1->tree_left, p2->tree_left);
+}
+
+static long ProcessList_treeProcessCompareByPID(const void* v1, const void* v2) {
+ const Process *p1 = (const Process*)v1;
+ const Process *p2 = (const Process*)v2;
+
+ return SPACESHIP_NUMBER(p1->pid, p2->pid);
+}
+
+// Builds a sorted tree from scratch, without relying on previously gathered information
+static void ProcessList_buildTree(ProcessList* this) {
+ int node_counter = 1;
+ int node_index = 0;
+ int direction = this->settings->direction;
+
+ // Sort by PID
+ Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompareByPID);
+ int vsize = Vector_size(this->processes);
+
+ // Find all processes whose parent is not visible
+ int size;
+ while ((size = Vector_size(this->processes))) {
+ int i;
+ for (i = 0; i < size; i++) {
+ Process* process = (Process*)Vector_get(this->processes, i);
+
+ // Immediately consume processes hidden from view
+ if (!process->show) {
+ process = (Process*)Vector_take(this->processes, i);
+ process->indent = 0;
+ process->tree_depth = 0;
+ process->tree_left = node_counter++;
+ process->tree_index = node_index++;
+ Vector_add(this->processes2, process);
+ ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, false, &node_counter, &node_index);
+ process->tree_right = node_counter++;
+ Hashtable_put(this->displayTreeSet, process->tree_index, process);
+ break;
+ }
+
+ pid_t ppid = Process_getParentPid(process);
+
+ // Bisect the process vector to find parent
+ int l = 0;
+ int r = size;
+
+ // If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
+ // on Mac OS X 10.11.6) cancel bisecting and regard this process as
+ // root.
+ if (process->pid == ppid)
+ r = 0;
+
+ // On Linux both the init process (pid 1) and the root UMH kernel thread (pid 2)
+ // use a ppid of 0. As that PID can't exist, we can skip searching for it.
+ if (!ppid)
+ r = 0;
+
+ while (l < r) {
+ int c = (l + r) / 2;
+ pid_t pid = ((Process*)Vector_get(this->processes, c))->pid;
+ if (ppid == pid) {
break;
+ } else if (ppid < pid) {
+ r = c;
+ } else {
+ l = c + 1;
}
}
- // There should be no loop in the process tree
- assert(i < size);
+
+ // If parent not found, then construct the tree with this node as root
+ if (l >= r) {
+ process = (Process*)Vector_take(this->processes, i);
+ process->indent = 0;
+ process->tree_depth = 0;
+ process->tree_left = node_counter++;
+ process->tree_index = node_index++;
+ Vector_add(this->processes2, process);
+ Hashtable_put(this->displayTreeSet, process->tree_index, process);
+ ProcessList_buildTreeBranch(this, process->pid, 0, 0, direction, process->showChildren, &node_counter, &node_index);
+ process->tree_right = node_counter++;
+ break;
+ }
}
- assert(Vector_size(this->processes2) == vsize); (void)vsize;
- assert(Vector_size(this->processes) == 0);
- // Swap listings around
- Vector* t = this->processes;
- this->processes = this->processes2;
- this->processes2 = t;
+
+ // There should be no loop in the process tree
+ assert(i < size);
}
+
+ // Swap listings around
+ Vector* t = this->processes;
+ this->processes = this->processes2;
+ this->processes2 = t;
+
+ // Check consistency of the built structures
+ assert(Vector_size(this->processes) == vsize); (void)vsize;
+ assert(Vector_size(this->processes2) == 0);
}
+void ProcessList_sort(ProcessList* this) {
+ if (this->settings->treeView) {
+ ProcessList_updateTreeSet(this);
+ Vector_quickSortCustomCompare(this->processes, ProcessList_treeProcessCompare);
+ } else {
+ Vector_insertionSort(this->processes);
+ }
+}
-ProcessField ProcessList_keyAt(ProcessList* this, int at) {
+ProcessField ProcessList_keyAt(const ProcessList* this, int at) {
int x = 0;
- ProcessField* fields = this->settings->fields;
+ const ProcessField* fields = this->settings->fields;
ProcessField field;
for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title;
- if (!title) title = "- ";
+ if (!title) {
+ title = "- ";
+ }
+
int len = strlen(title);
if (at >= x && at <= x + len) {
return field;
@@ -253,7 +484,7 @@ void ProcessList_rebuildPanel(ProcessList* this) {
if ( (!p->show)
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
- || (incFilter && !(String_contains_i(p->comm, incFilter)))
+ || (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
hidden = true;
@@ -282,12 +513,20 @@ Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting,
return proc;
}
-void ProcessList_scan(ProcessList* this) {
+void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate) {
+ struct timespec now;
+
+ // in pause mode only gather global data for meters (CPU/memory/...)
+ if (pauseProcessUpdate) {
+ ProcessList_goThroughEntries(this, true);
+ return;
+ }
// mark all process as "dirty"
for (int i = 0; i < Vector_size(this->processes); i++) {
Process* p = (Process*) Vector_get(this->processes, i);
p->updated = false;
+ p->wasShown = p->show;
p->show = true;
}
@@ -296,13 +535,46 @@ void ProcessList_scan(ProcessList* this) {
this->kernelThreads = 0;
this->runningTasks = 0;
- ProcessList_goThroughEntries(this);
+
+ // set scanTs
+ static bool firstScanDone = false;
+ if (!firstScanDone) {
+ this->scanTs = 0;
+ firstScanDone = true;
+ } else if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) {
+ this->scanTs = now.tv_sec;
+ }
+
+ ProcessList_goThroughEntries(this, false);
for (int i = Vector_size(this->processes) - 1; i >= 0; i--) {
Process* p = (Process*) Vector_get(this->processes, i);
- if (p->updated == false)
- ProcessList_remove(this, p);
- else
+ if (p->tombTs > 0) {
+ // remove tombed process
+ if (this->scanTs >= p->tombTs) {
+ ProcessList_remove(this, p);
+ }
+ } else if (p->updated == false) {
+ // process no longer exists
+ if (this->settings->highlightChanges && p->wasShown) {
+ // mark tombed
+ p->tombTs = this->scanTs + this->settings->highlightDelaySecs;
+ } else {
+ // immediately remove
+ ProcessList_remove(this, p);
+ }
+ } else {
p->updated = false;
+ }
+ }
+
+ if (this->settings->treeView) {
+ // Clear out the hashtable to avoid any left-over processes from previous build
+ //
+ // The sorting algorithm relies on the fact that
+ // len(this->displayTreeSet) == len(this->processes)
+ Hashtable_clear(this->displayTreeSet);
+
+ ProcessList_buildTree(this);
}
}
diff --git a/ProcessList.h b/ProcessList.h
index 7b572d8..b7ae400 100644
--- a/ProcessList.h
+++ b/ProcessList.h
@@ -3,21 +3,29 @@
/*
htop - ProcessList.h
(C) 2004,2005 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Vector.h"
+#include "config.h" // IWYU pragma: keep
+
+#include <stdbool.h>
+#include <sys/types.h>
+
#include "Hashtable.h"
-#include "UsersTable.h"
+#include "Object.h"
#include "Panel.h"
#include "Process.h"
+#include "RichString.h"
#include "Settings.h"
+#include "UsersTable.h"
+#include "Vector.h"
#ifdef HAVE_LIBHWLOC
#include <hwloc.h>
#endif
+
#ifndef MAX_NAME
#define MAX_NAME 128
#endif
@@ -27,13 +35,16 @@ in the source distribution for its full text.
#endif
typedef struct ProcessList_ {
- Settings* settings;
+ const Settings* settings;
Vector* processes;
Vector* processes2;
Hashtable* processTable;
UsersTable* usersTable;
+ Hashtable* displayTreeSet;
+ Hashtable* draftingTreeSet;
+
Panel* panel;
int following;
uid_t userId;
@@ -52,8 +63,6 @@ typedef struct ProcessList_ {
unsigned long long int totalMem;
unsigned long long int usedMem;
- unsigned long long int freeMem;
- unsigned long long int sharedMem;
unsigned long long int buffersMem;
unsigned long long int cachedMem;
unsigned long long int totalSwap;
@@ -62,14 +71,15 @@ typedef struct ProcessList_ {
int cpuCount;
+ time_t scanTs;
} ProcessList;
-ProcessList* ProcessList_new(UsersTable* ut, Hashtable* pidMatchList, uid_t userId);
+ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
-void ProcessList_goThroughEntries(ProcessList* pl);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
-ProcessList* ProcessList_init(ProcessList* this, ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
+ProcessList* ProcessList_init(ProcessList* this, const ObjectClass* klass, UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
void ProcessList_done(ProcessList* this);
@@ -87,7 +97,7 @@ int ProcessList_size(ProcessList* this);
void ProcessList_sort(ProcessList* this);
-ProcessField ProcessList_keyAt(ProcessList* this, int at);
+ProcessField ProcessList_keyAt(const ProcessList* this, int at);
void ProcessList_expandTree(ProcessList* this);
@@ -95,6 +105,6 @@ void ProcessList_rebuildPanel(ProcessList* this);
Process* ProcessList_getProcess(ProcessList* this, pid_t pid, bool* preExisting, Process_New constructor);
-void ProcessList_scan(ProcessList* this);
+void ProcessList_scan(ProcessList* this, bool pauseProcessUpdate);
#endif
diff --git a/ProcessLocksScreen.c b/ProcessLocksScreen.c
new file mode 100644
index 0000000..3e7762a
--- /dev/null
+++ b/ProcessLocksScreen.c
@@ -0,0 +1,105 @@
+/*
+htop - ProcessLocksScreen.c
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include "ProcessLocksScreen.h"
+
+#include <inttypes.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "Panel.h"
+#include "Platform.h"
+#include "ProvideCurses.h"
+#include "Vector.h"
+#include "XUtils.h"
+
+
+ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) {
+ ProcessLocksScreen* this = xMalloc(sizeof(ProcessLocksScreen));
+ Object_setClass(this, Class(ProcessLocksScreen));
+ if (Process_isThread(process))
+ this->pid = process->tgid;
+ else
+ this->pid = process->pid;
+ return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES-3, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME");
+}
+
+void ProcessLocksScreen_delete(Object* this) {
+ free(InfoScreen_done((InfoScreen*)this));
+}
+
+static void ProcessLocksScreen_draw(InfoScreen* this) {
+ InfoScreen_drawTitled(this, "Snapshot of file locks of process %d - %s", ((ProcessLocksScreen*)this)->pid, Process_getCommand(this->process));
+}
+
+static inline void FileLocks_Data_clear(FileLocks_Data* data) {
+ free(data->locktype);
+ free(data->exclusive);
+ free(data->readwrite);
+ free(data->filename);
+}
+
+static void ProcessLocksScreen_scan(InfoScreen* this) {
+ Panel* panel = this->display;
+ int idx = Panel_getSelectedIndex(panel);
+ Panel_prune(panel);
+ FileLocks_ProcessData* pdata = Platform_getProcessLocks(((ProcessLocksScreen*)this)->pid);
+ if (!pdata) {
+ InfoScreen_addLine(this, "This feature is not supported on your platform.");
+ } else if (pdata->error) {
+ InfoScreen_addLine(this, "Could not determine file locks.");
+ } else {
+ FileLocks_LockData* ldata = pdata->locks;
+ if (!ldata) {
+ InfoScreen_addLine(this, "No locks have been found for the selected process.");
+ }
+ while (ldata) {
+ FileLocks_Data* data = &ldata->data;
+
+ char entry[512];
+ if (ULLONG_MAX == data->end) {
+ xSnprintf(entry, sizeof(entry), "%10d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20s %s",
+ data->id,
+ data->locktype, data->exclusive, data->readwrite,
+ data->dev[0], data->dev[1], data->inode,
+ data->start, "<END OF FILE>",
+ data->filename ? data->filename : "<N/A>"
+ );
+ } else {
+ xSnprintf(entry, sizeof(entry), "%10d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20"PRIu64" %s",
+ data->id,
+ data->locktype, data->exclusive, data->readwrite,
+ data->dev[0], data->dev[1], data->inode,
+ data->start, data->end,
+ data->filename ? data->filename : "<N/A>"
+ );
+ }
+
+ InfoScreen_addLine(this, entry);
+ FileLocks_Data_clear(&ldata->data);
+
+ FileLocks_LockData* old = ldata;
+ ldata = ldata->next;
+ free(old);
+ }
+ }
+ free(pdata);
+ Vector_insertionSort(this->lines);
+ Vector_insertionSort(panel->items);
+ Panel_setSelected(panel, idx);
+}
+
+const InfoScreenClass ProcessLocksScreen_class = {
+ .super = {
+ .extends = Class(Object),
+ .delete = ProcessLocksScreen_delete
+ },
+ .scan = ProcessLocksScreen_scan,
+ .draw = ProcessLocksScreen_draw
+};
diff --git a/ProcessLocksScreen.h b/ProcessLocksScreen.h
new file mode 100644
index 0000000..c848228
--- /dev/null
+++ b/ProcessLocksScreen.h
@@ -0,0 +1,52 @@
+#ifndef HEADER_ProcessLocksScreen
+#define HEADER_ProcessLocksScreen
+/*
+htop - ProcessLocksScreen.h
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "InfoScreen.h"
+#include "Object.h"
+#include "Process.h"
+
+
+typedef struct ProcessLocksScreen_ {
+ InfoScreen super;
+ pid_t pid;
+} ProcessLocksScreen;
+
+typedef struct FileLocks_Data_ {
+ char* locktype;
+ char* exclusive;
+ char* readwrite;
+ char* filename;
+ int id;
+ unsigned int dev[2];
+ uint64_t inode;
+ uint64_t start;
+ uint64_t end;
+} FileLocks_Data;
+
+typedef struct FileLocks_LockData_ {
+ FileLocks_Data data;
+ struct FileLocks_LockData_* next;
+} FileLocks_LockData;
+
+typedef struct FileLocks_ProcessData_ {
+ bool error;
+ struct FileLocks_LockData_* locks;
+} FileLocks_ProcessData;
+
+extern const InfoScreenClass ProcessLocksScreen_class;
+
+ProcessLocksScreen* ProcessLocksScreen_new(const Process* process);
+
+void ProcessLocksScreen_delete(Object* this);
+
+#endif
diff --git a/ProvideCurses.h b/ProvideCurses.h
new file mode 100644
index 0000000..c35d696
--- /dev/null
+++ b/ProvideCurses.h
@@ -0,0 +1,34 @@
+#ifndef HEADER_ProvideCurses
+#define HEADER_ProvideCurses
+/*
+htop - RichString.h
+(C) 2004,2011 Hisham H. Muhammad
+Released under the GNU GPL, see the COPYING file
+in the source distribution for its full text.
+*/
+
+
+#include "config.h"
+
+// IWYU pragma: begin_exports
+
+#ifdef HAVE_NCURSESW_CURSES_H
+#include <ncursesw/curses.h>
+#elif defined(HAVE_NCURSES_NCURSES_H)
+#include <ncurses/ncurses.h>
+#elif defined(HAVE_NCURSES_CURSES_H)
+#include <ncurses/curses.h>
+#elif defined(HAVE_NCURSES_H)
+#include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+#include <curses.h>
+#endif
+
+#ifdef HAVE_LIBNCURSESW
+#include <wchar.h>
+#include <wctype.h>
+#endif
+
+// IWYU pragma: end_exports
+
+#endif // HEADER_ProvideCurses
diff --git a/README b/README
index 92bd285..880597d 100644
--- a/README
+++ b/README
@@ -7,37 +7,63 @@
[![Github Release](https://img.shields.io/github/release/htop-dev/htop.svg)](https://github.com/htop-dev/htop/releases/latest)
[![Download](https://api.bintray.com/packages/htop/source/htop/images/download.svg)](https://bintray.com/htop/source/htop/_latestVersion)
+![Screenshot of htop](docs/images/screenshot.png?raw=true)
+
## Introduction
`htop` is a cross-platform interactive process viewer.
-It requires `ncurses`.
-For more information and details on how to contribute to `htop`
-visit [htop.dev](https://htop.dev).
+`htop` allows scrolling the list of processes vertically and horizontally to see their full command lines and related information like memory and CPU consumption.
+
+The information displayed is configurable through a graphical setup and can be sorted and filtered interactively.
+
+Tasks related to processes (e.g. killing and renicing) can be done without entering their PIDs.
+
+Running `htop` requires `ncurses` libraries (typically named libncursesw*).
+
+For more information and details on how to contribute to `htop` visit [htop.dev](https://htop.dev).
## Build instructions
-This program is distributed as a standard autotools-based package.
-For detailed instructions see the `INSTALL` file, which
-is created after `./autogen.sh` is run.
+This program is distributed as a standard GNU autotools-based package.
+
+Compiling `htop` requires the header files for `ncurses` (libncursesw*-dev). Install these and other required packages for C development from your package manager.
-When compiling from a [release tarball](https://github.com/htop-dev/htop/releases/), run:
+Then, when compiling from a [release tarball](https://bintray.com/htop/source/htop), run:
~~~ shell
./configure && make
~~~
-For compiling sources downloaded from the Git repository, run:
+Alternatively, for compiling sources downloaded from the Git repository (`git clone` or downloads from [Github releases](https://github.com/htop-dev/htop/releases/)),
+install the header files for `ncurses` (libncursesw*-dev) and other required development packages from your distribution's package manager. Then run:
~~~ shell
./autogen.sh && ./configure && make
~~~
-By default `make install` will install into `/usr/local`, for changing
-the path use `./configure --prefix=/some/path`.
+By default `make install` will install into `/usr/local`, for changing the path use `./configure --prefix=/some/path`.
+
+See the manual page (`man htop`) or the on-line help ('F1' or 'h' inside `htop`) for a list of supported key commands.
+
+## Support
+
+If you have trouble running `htop` please consult your Operating System / Linux distribution documentation for getting support and filing bugs.
+
+## Bugs, development feedback
+
+We have a [development mailing list](https://htop.dev/mailinglist.html). Feel free to subscribe for release announcements or asking questions on the development of htop.
+
+You can also join our IRC channel #htop on freenode and talk to the developers there.
+
+If you have found an issue with the source of htop, please check whether this has already been reported in our [Github issue tracker](https://github.com/htop-dev/htop/issues).
+If not, please file a new issue describing the problem you have found, the location in the source code you are referring to and a possible fix.
+
+## History
+
+`htop` was invented, developed and maintained by Hisham Muhammad from 2004 to 2019. His [legacy repository](https://github.com/hishamhm/htop/) has been archived to preserve the history.
-See the manual page (`man htop`) or the on-line help ('F1' or 'h'
-inside `htop`) for a list of supported key commands.
+In 2020 a [team](https://github.com/orgs/htop-dev/people) took over the development amicably and continues to maintain `htop` collaboratively.
## License
diff --git a/RichString.c b/RichString.c
index b97e34e..790c15a 100644
--- a/RichString.c
+++ b/RichString.c
@@ -1,17 +1,19 @@
/*
htop - RichString.c
(C) 2004,2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "RichString.h"
-#include "XAlloc.h"
-#include "Macros.h"
#include <stdlib.h>
#include <string.h>
+#include "Macros.h"
+#include "XUtils.h"
+
+
#define charBytes(n) (sizeof(CharType) * (n))
static void RichString_extendLen(RichString* this, int len) {
@@ -34,15 +36,23 @@ static void RichString_extendLen(RichString* this, int len) {
this->chlen = len;
}
-#define RichString_setLen(this, len) do{ if(len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) { RichString_setChar(this,len,0); this->chlen=len; } else RichString_extendLen(this,len); }while(0)
+static void RichString_setLen(RichString* this, int len) {
+ if (len < RICHSTRING_MAXLEN && this->chlen < RICHSTRING_MAXLEN) {
+ RichString_setChar(this, len, 0);
+ this->chlen = len;
+ } else {
+ RichString_extendLen(this, len);
+ }
+}
#ifdef HAVE_LIBNCURSESW
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
- wchar_t data[len+1];
+ wchar_t data[len + 1];
len = mbstowcs(data, data_c, len);
if (len < 0)
return;
+
int newLen = from + len;
RichString_setLen(this, newLen);
for (int i = from, j = 0; i < newLen; i++, j++) {
@@ -70,13 +80,14 @@ int RichString_findChar(RichString* this, char c, int start) {
return -1;
}
-#else
+#else /* HAVE_LIBNCURSESW */
static inline void RichString_writeFrom(RichString* this, int attrs, const char* data_c, int from, int len) {
int newLen = from + len;
RichString_setLen(this, newLen);
- for (int i = from, j = 0; i < newLen; i++, j++)
- this->chptr[i] = (data_c[j] >= 32 ? data_c[j] : '?') | attrs;
+ for (int i = from, j = 0; i < newLen; i++, j++) {
+ this->chptr[i] = (((unsigned char)data_c[j]) >= 32 ? ((unsigned char)data_c[j]) : '?') | attrs;
+ }
this->chptr[newLen] = 0;
}
@@ -99,7 +110,7 @@ int RichString_findChar(RichString* this, char c, int start) {
return -1;
}
-#endif
+#endif /* HAVE_LIBNCURSESW */
void RichString_prune(RichString* this) {
if (this->chlen > RICHSTRING_MAXLEN)
@@ -108,6 +119,15 @@ void RichString_prune(RichString* this) {
this->chptr = this->chstr;
}
+void RichString_appendChr(RichString* this, char c, int count) {
+ int from = this->chlen;
+ int newLen = from + count;
+ RichString_setLen(this, newLen);
+ for (int i = from; i < newLen; i++) {
+ RichString_setChar(this, i, c);
+ }
+}
+
void RichString_setAttr(RichString* this, int attrs) {
RichString_setAttrn(this, attrs, 0, this->chlen - 1);
}
diff --git a/RichString.h b/RichString.h
index e6fb4c3..262befc 100644
--- a/RichString.h
+++ b/RichString.h
@@ -3,49 +3,33 @@
/*
htop - RichString.h
(C) 2004,2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-
#include "config.h"
-#include <ctype.h>
-
-#include <assert.h>
-#ifdef HAVE_NCURSESW_CURSES_H
-#include <ncursesw/curses.h>
-#elif HAVE_NCURSES_NCURSES_H
-#include <ncurses/ncurses.h>
-#elif HAVE_NCURSES_CURSES_H
-#include <ncurses/curses.h>
-#elif HAVE_NCURSES_H
-#include <ncurses.h>
-#elif HAVE_CURSES_H
-#include <curses.h>
-#endif
-#ifdef HAVE_LIBNCURSESW
-#include <wctype.h>
-#endif
+#include "ProvideCurses.h"
+
#define RichString_size(this) ((this)->chlen)
#define RichString_sizeVal(this) ((this).chlen)
-#define RichString_begin(this) RichString (this); memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
-#define RichString_beginAllocated(this) memset(&this, 0, sizeof(RichString)); (this).chptr = (this).chstr;
-#define RichString_end(this) RichString_prune(&(this));
+#define RichString_begin(this) RichString (this); RichString_beginAllocated(this)
+#define RichString_beginAllocated(this) do { memset(&(this), 0, sizeof(RichString)); (this).chptr = (this).chstr; } while(0)
+#define RichString_end(this) RichString_prune(&(this))
#ifdef HAVE_LIBNCURSESW
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr)
-#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + off, n)
+#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + (off), n)
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255)
-#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while(0)
+#define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = (CharType) { .chars = { ch, 0 } }; } while (0)
#define CharType cchar_t
#else
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
-#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + off, n)
+#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + (off), n)
#define RichString_getCharVal(this, i) ((this).chptr[i])
-#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = ch; } while(0)
+#define RichString_setChar(this, at, ch) do { (this)->chptr[(at)] = ch; } while (0)
#define CharType chtype
#endif
@@ -54,27 +38,20 @@ in the source distribution for its full text.
typedef struct RichString_ {
int chlen;
CharType* chptr;
- CharType chstr[RICHSTRING_MAXLEN+1];
+ CharType chstr[RICHSTRING_MAXLEN + 1];
+ int highlightAttr;
} RichString;
-#ifdef HAVE_LIBNCURSESW
-
-void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
-
-int RichString_findChar(RichString* this, char c, int start);
-
-#else
-
void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
int RichString_findChar(RichString* this, char c, int start);
-#endif
-
void RichString_prune(RichString* this);
void RichString_setAttr(RichString* this, int attrs);
+void RichString_appendChr(RichString* this, char c, int count);
+
void RichString_append(RichString* this, int attrs, const char* data);
void RichString_appendn(RichString* this, int attrs, const char* data, int len);
diff --git a/ScreenManager.c b/ScreenManager.c
index 92e792b..ac93721 100644
--- a/ScreenManager.c
+++ b/ScreenManager.c
@@ -1,34 +1,37 @@
/*
htop - ScreenManager.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "ScreenManager.h"
-#include "ProcessList.h"
-
-#include "Object.h"
-#include "CRT.h"
#include <assert.h>
-#include <time.h>
-#include <stdlib.h>
#include <stdbool.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include "CRT.h"
+#include "FunctionBar.h"
+#include "Object.h"
+#include "ProcessList.h"
+#include "ProvideCurses.h"
+#include "XUtils.h"
-ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner) {
+
+ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner) {
ScreenManager* this;
this = xMalloc(sizeof(ScreenManager));
- this->x1 = x1;
- this->y1 = y1;
- this->x2 = x2;
- this->y2 = y2;
- this->orientation = orientation;
+ this->x1 = 0;
+ this->y1 = header->height;
+ this->x2 = 0;
+ this->y2 = -1;
this->panels = Vector_new(Class(Panel), owner, DEFAULT_SIZE);
this->panelCount = 0;
this->header = header;
this->settings = settings;
+ this->state = state;
this->owner = owner;
this->allowFocusChange = true;
return this;
@@ -44,21 +47,18 @@ inline int ScreenManager_size(ScreenManager* this) {
}
void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
- if (this->orientation == HORIZONTAL) {
- int lastX = 0;
- if (this->panelCount > 0) {
- Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
- lastX = last->x + last->w + 1;
- }
- int height = LINES - this->y1 + this->y2;
- if (size > 0) {
- Panel_resize(item, size, height);
- } else {
- Panel_resize(item, COLS-this->x1+this->x2-lastX, height);
- }
- Panel_move(item, lastX, this->y1);
+ int lastX = 0;
+ if (this->panelCount > 0) {
+ Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
+ lastX = last->x + last->w + 1;
+ }
+ int height = LINES - this->y1 + this->y2;
+ if (size > 0) {
+ Panel_resize(item, size, height);
+ } else {
+ Panel_resize(item, COLS - this->x1 + this->x2 - lastX, height);
}
- // TODO: VERTICAL
+ Panel_move(item, lastX, this->y1);
Vector_add(this->panels, item);
item->needsRedraw = true;
this->panelCount++;
@@ -77,33 +77,35 @@ void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2) {
this->x2 = x2;
this->y2 = y2;
int panels = this->panelCount;
- if (this->orientation == HORIZONTAL) {
- int lastX = 0;
- for (int i = 0; i < panels - 1; i++) {
- Panel* panel = (Panel*) Vector_get(this->panels, i);
- Panel_resize(panel, panel->w, LINES-y1+y2);
- Panel_move(panel, lastX, y1);
- lastX = panel->x + panel->w + 1;
- }
- Panel* panel = (Panel*) Vector_get(this->panels, panels-1);
- Panel_resize(panel, COLS-x1+x2-lastX, LINES-y1+y2);
+ int lastX = 0;
+ for (int i = 0; i < panels - 1; i++) {
+ Panel* panel = (Panel*) Vector_get(this->panels, i);
+ Panel_resize(panel, panel->w, LINES - y1 + y2);
Panel_move(panel, lastX, y1);
+ lastX = panel->x + panel->w + 1;
}
- // TODO: VERTICAL
+ Panel* panel = (Panel*) Vector_get(this->panels, panels - 1);
+ Panel_resize(panel, COLS - x1 + x2 - lastX, LINES - y1 + y2);
+ Panel_move(panel, lastX, y1);
}
-static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool *rescan, bool *timedOut) {
+static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut) {
ProcessList* pl = this->header->pl;
struct timeval tv;
gettimeofday(&tv, NULL);
double newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
+
*timedOut = (newTime - *oldTime > this->settings->delay);
- *rescan = *rescan || *timedOut;
- if (newTime < *oldTime) *rescan = true; // clock was adjusted?
+ *rescan |= *timedOut;
+
+ if (newTime < *oldTime) {
+ *rescan = true; // clock was adjusted?
+ }
+
if (*rescan) {
*oldTime = newTime;
- ProcessList_scan(pl);
+ ProcessList_scan(pl, this->state->pauseProcessUpdate);
if (*sortTimeout == 0 || this->settings->treeView) {
ProcessList_sort(pl);
*sortTimeout = 1;
@@ -121,15 +123,17 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
const int nPanels = this->panelCount;
for (int i = 0; i < nPanels; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
- Panel_draw(panel, i == focus);
- if (this->orientation == HORIZONTAL) {
- mvvline(panel->y, panel->x+panel->w, ' ', panel->h+1);
- }
+ Panel_draw(panel, i == focus, !((panel == this->state->panel) && this->state->hideProcessSelection));
+ mvvline(panel->y, panel->x + panel->w, ' ', panel->h + 1);
}
}
-static Panel* setCurrentPanel(Panel* panel) {
- FunctionBar_draw(panel->currentBar, NULL);
+static Panel* setCurrentPanel(const ScreenManager* this, Panel* panel) {
+ FunctionBar_draw(panel->currentBar);
+ if (panel == this->state->panel && this->state->pauseProcessUpdate) {
+ FunctionBar_append("PAUSED", CRT_colors[PAUSED]);
+ }
+
return panel;
}
@@ -137,7 +141,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false;
int focus = 0;
- Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
+ Panel* panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
double oldTime = 0.0;
@@ -175,15 +179,15 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
} else {
for (int i = 0; i < this->panelCount; i++) {
Panel* panel = (Panel*) Vector_get(this->panels, i);
- if (mevent.x >= panel->x && mevent.x <= panel->x+panel->w) {
+ if (mevent.x >= panel->x && mevent.x <= panel->x + panel->w) {
if (mevent.y == panel->y) {
ch = EVENT_HEADER_CLICK(mevent.x - panel->x);
break;
- } else if (mevent.y > panel->y && mevent.y <= panel->y+panel->h) {
+ } else if (mevent.y > panel->y && mevent.y <= panel->y + panel->h) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i;
- panelFocus = setCurrentPanel(panel);
+ panelFocus = setCurrentPanel(this, panel);
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) {
@@ -211,8 +215,9 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (closeTimeout == 100) {
break;
}
- } else
+ } else {
closeTimeout = 0;
+ }
redraw = false;
continue;
}
@@ -254,14 +259,21 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (this->panelCount < 2) {
goto defaultHandler;
}
- if (!this->allowFocusChange)
+
+ if (!this->allowFocusChange) {
break;
- tryLeft:
- if (focus > 0)
+ }
+
+tryLeft:
+ if (focus > 0) {
focus--;
- panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
- if (Panel_size(panelFocus) == 0 && focus > 0)
+ }
+
+ panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
+ if (Panel_size(panelFocus) == 0 && focus > 0) {
goto tryLeft;
+ }
+
break;
case KEY_RIGHT:
case KEY_CTRL('F'):
@@ -269,30 +281,39 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
if (this->panelCount < 2) {
goto defaultHandler;
}
- if (!this->allowFocusChange)
+ if (!this->allowFocusChange) {
break;
- tryRight:
- if (focus < this->panelCount - 1)
+ }
+
+tryRight:
+ if (focus < this->panelCount - 1) {
focus++;
- panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
- if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1)
+ }
+
+ panelFocus = setCurrentPanel(this, (Panel*) Vector_get(this->panels, focus));
+ if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1) {
goto tryRight;
+ }
+
break;
- case KEY_F(10):
- case 'q':
case 27:
+ case 'q':
+ case KEY_F(10):
quit = true;
continue;
default:
- defaultHandler:
+defaultHandler:
sortTimeout = resetSortTimeout;
Panel_onKey(panelFocus, ch);
break;
}
}
- if (lastFocus)
+ if (lastFocus) {
*lastFocus = panelFocus;
- if (lastKey)
+ }
+
+ if (lastKey) {
*lastKey = ch;
+ }
}
diff --git a/ScreenManager.h b/ScreenManager.h
index 3c19a05..7dda4fc 100644
--- a/ScreenManager.h
+++ b/ScreenManager.h
@@ -3,35 +3,34 @@
/*
htop - ScreenManager.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Vector.h"
+#include <stdbool.h>
+
+#include "Action.h"
#include "Header.h"
-#include "Settings.h"
#include "Panel.h"
+#include "Settings.h"
+#include "Vector.h"
-typedef enum Orientation_ {
- VERTICAL,
- HORIZONTAL
-} Orientation;
typedef struct ScreenManager_ {
int x1;
int y1;
int x2;
int y2;
- Orientation orientation;
Vector* panels;
int panelCount;
- const Header* header;
+ Header* header;
const Settings* settings;
+ const State* state;
bool owner;
bool allowFocusChange;
} ScreenManager;
-ScreenManager* ScreenManager_new(int x1, int y1, int x2, int y2, Orientation orientation, const Header* header, const Settings* settings, bool owner);
+ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner);
void ScreenManager_delete(ScreenManager* this);
diff --git a/Settings.c b/Settings.c
index d6ef53b..0b4d0ed 100644
--- a/Settings.c
+++ b/Settings.c
@@ -1,26 +1,28 @@
/*
htop - Settings.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Settings.h"
-#include "Platform.h"
-
-#include "StringUtils.h"
-#include "Vector.h"
-#include "CRT.h"
-#include <sys/stat.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <unistd.h>
+#include <sys/stat.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "Meter.h"
+#include "Platform.h"
+#include "XUtils.h"
+
void Settings_delete(Settings* this) {
free(this->filename);
free(this->fields);
- for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) {
+ for (unsigned int i = 0; i < ARRAYSIZE(this->columns); i++) {
String_freeArray(this->columns[i].names);
free(this->columns[i].modes);
}
@@ -29,16 +31,14 @@ void Settings_delete(Settings* this) {
static void Settings_readMeters(Settings* this, char* line, int column) {
char* trim = String_trim(line);
- int nIds;
- char** ids = String_split(trim, ' ', &nIds);
+ char** ids = String_split(trim, ' ', NULL);
free(trim);
this->columns[column].names = ids;
}
static void Settings_readMeterModes(Settings* this, char* line, int column) {
char* trim = String_trim(line);
- int nIds;
- char** ids = String_split(trim, ' ', &nIds);
+ char** ids = String_split(trim, ' ', NULL);
free(trim);
int len = 0;
for (int i = 0; ids[i]; i++) {
@@ -53,9 +53,9 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
this->columns[column].modes = modes;
}
-static void Settings_defaultMeters(Settings* this) {
+static void Settings_defaultMeters(Settings* this, int initialCpuCount) {
int sizes[] = { 3, 3 };
- if (this->cpuCount > 4) {
+ if (initialCpuCount > 4) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
@@ -64,12 +64,12 @@ static void Settings_defaultMeters(Settings* this) {
this->columns[i].len = sizes[i];
}
int r = 0;
- if (this->cpuCount > 8) {
+ if (initialCpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE;
- } else if (this->cpuCount > 4) {
+ } else if (initialCpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs");
@@ -90,17 +90,16 @@ static void Settings_defaultMeters(Settings* this) {
this->columns[1].modes[r++] = TEXT_METERMODE;
}
-static void readFields(ProcessField* fields, int* flags, const char* line) {
+static void readFields(ProcessField* fields, uint32_t* flags, const char* line) {
char* trim = String_trim(line);
- int nIds;
- char** ids = String_split(trim, ' ', &nIds);
+ char** ids = String_split(trim, ' ', NULL);
free(trim);
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
- if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
+ if (id > 0 && id < Platform_numberOfFields && Process_fields[id].name) {
fields[j] = id;
*flags |= Process_fields[id].flags;
j++;
@@ -110,13 +109,14 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
String_freeArray(ids);
}
-static bool Settings_read(Settings* this, const char* fileName) {
+static bool Settings_read(Settings* this, const char* fileName, int initialCpuCount) {
FILE* fd;
CRT_dropPrivileges();
fd = fopen(fileName, "r");
CRT_restorePrivileges();
if (!fd)
return false;
+
bool didReadMeters = false;
bool didReadFields = false;
for (;;) {
@@ -124,7 +124,7 @@ static bool Settings_read(Settings* this, const char* fileName) {
if (!line) {
break;
}
- int nOptions;
+ size_t nOptions;
char** option = String_split(line, '=', &nOptions);
free (line);
if (nOptions < 2) {
@@ -141,8 +141,6 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
- } else if (String_eq(option[0], "hide_threads")) {
- this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) {
this->hideKernelThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_userland_threads")) {
@@ -159,6 +157,16 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->highlightMegabytes = atoi(option[1]);
} else if (String_eq(option[0], "highlight_threads")) {
this->highlightThreads = atoi(option[1]);
+ } else if (String_eq(option[0], "highlight_changes")) {
+ this->highlightChanges = atoi(option[1]);
+ } else if (String_eq(option[0], "highlight_changes_delay_secs")) {
+ this->highlightDelaySecs = CLAMP(atoi(option[1]), 1, 24*60*60);
+ } else if (String_eq(option[0], "find_comm_in_cmdline")) {
+ this->findCommInCmdline = atoi(option[1]);
+ } else if (String_eq(option[0], "strip_exe_from_cmdline")) {
+ this->stripExeFromCmdline = atoi(option[1]);
+ } else if (String_eq(option[0], "show_merged_command")) {
+ this->showMergedCommand = atoi(option[1]);
} else if (String_eq(option[0], "header_margin")) {
this->headerMargin = atoi(option[1]);
} else if (String_eq(option[0], "expand_system_time")) {
@@ -166,22 +174,33 @@ static bool Settings_read(Settings* this, const char* fileName) {
this->detailedCPUTime = atoi(option[1]);
} else if (String_eq(option[0], "detailed_cpu_time")) {
this->detailedCPUTime = atoi(option[1]);
+ } else if (String_eq(option[0], "cpu_count_from_one")) {
+ this->countCPUsFromOne = atoi(option[1]);
} else if (String_eq(option[0], "cpu_count_from_zero")) {
- this->countCPUsFromZero = atoi(option[1]);
+ // old (inverted) naming also supported for backwards compatibility
+ this->countCPUsFromOne = !atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_usage")) {
this->showCPUUsage = atoi(option[1]);
} else if (String_eq(option[0], "show_cpu_frequency")) {
this->showCPUFrequency = atoi(option[1]);
+ #ifdef HAVE_SENSORS_SENSORS_H
+ } else if (String_eq(option[0], "show_cpu_temperature")) {
+ this->showCPUTemperature = atoi(option[1]);
+ } else if (String_eq(option[0], "degree_fahrenheit")) {
+ this->degreeFahrenheit = atoi(option[1]);
+ #endif
} else if (String_eq(option[0], "update_process_names")) {
this->updateProcessNames = atoi(option[1]);
} else if (String_eq(option[0], "account_guest_in_cpu_meter")) {
this->accountGuestInCPUMeter = atoi(option[1]);
} else if (String_eq(option[0], "delay")) {
- this->delay = atoi(option[1]);
+ this->delay = CLAMP(atoi(option[1]), 1, 255);
} else if (String_eq(option[0], "color_scheme")) {
this->colorScheme = atoi(option[1]);
- if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) this->colorScheme = 0;
- } else if (String_eq(option[0], "enable_mouse")) {
+ if (this->colorScheme < 0 || this->colorScheme >= LAST_COLORSCHEME) {
+ this->colorScheme = 0;
+ }
+ } else if (String_eq(option[0], "enable_mouse")) {
this->enableMouse = atoi(option[1]);
} else if (String_eq(option[0], "left_meters")) {
Settings_readMeters(this, option[1], 0);
@@ -204,7 +223,7 @@ static bool Settings_read(Settings* this, const char* fileName) {
}
fclose(fd);
if (!didReadMeters) {
- Settings_defaultMeters(this);
+ Settings_defaultMeters(this, initialCpuCount);
}
return didReadFields;
}
@@ -214,7 +233,7 @@ static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
const char* sep = "";
for (int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format.
- fprintf(fd, "%s%d", sep, (int) fields[i]-1);
+ fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
sep = " ";
}
fprintf(fd, "\n");
@@ -252,9 +271,8 @@ bool Settings_write(Settings* this) {
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
writeFields(fd, this->fields, "fields");
// This "-1" is for compatibility with the older enum format.
- fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1);
+ fprintf(fd, "sort_key=%d\n", (int) this->sortKey - 1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
- fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
fprintf(fd, "shadow_other_users=%d\n", (int) this->shadowOtherUsers);
@@ -263,12 +281,21 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
+ fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges);
+ fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs);
+ fprintf(fd, "find_comm_in_cmdline=%d\n", (int) this->findCommInCmdline);
+ fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline);
+ fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
- fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
+ fprintf(fd, "cpu_count_from_one=%d\n", (int) this->countCPUsFromOne);
fprintf(fd, "show_cpu_usage=%d\n", (int) this->showCPUUsage);
fprintf(fd, "show_cpu_frequency=%d\n", (int) this->showCPUFrequency);
+ #ifdef HAVE_SENSORS_SENSORS_H
+ fprintf(fd, "show_cpu_temperature=%d\n", (int) this->showCPUTemperature);
+ fprintf(fd, "degree_fahrenheit=%d\n", (int) this->degreeFahrenheit);
+ #endif
fprintf(fd, "update_process_names=%d\n", (int) this->updateProcessNames);
fprintf(fd, "account_guest_in_cpu_meter=%d\n", (int) this->accountGuestInCPUMeter);
fprintf(fd, "color_scheme=%d\n", (int) this->colorScheme);
@@ -285,12 +312,11 @@ bool Settings_write(Settings* this) {
return true;
}
-Settings* Settings_new(int cpuCount) {
+Settings* Settings_new(int initialCpuCount) {
Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
- this->hideThreads = false;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
@@ -299,17 +325,25 @@ Settings* Settings_new(int cpuCount) {
this->highlightBaseName = false;
this->highlightMegabytes = false;
this->detailedCPUTime = false;
- this->countCPUsFromZero = false;
+ this->countCPUsFromOne = false;
this->showCPUUsage = true;
this->showCPUFrequency = false;
+ #ifdef HAVE_SENSORS_SENSORS_H
+ this->showCPUTemperature = false;
+ this->degreeFahrenheit = false;
+ #endif
this->updateProcessNames = false;
- this->cpuCount = cpuCount;
this->showProgramPath = true;
this->highlightThreads = true;
+ this->highlightChanges = false;
+ this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS;
+ this->findCommInCmdline = true;
+ this->stripExeFromCmdline = true;
+ this->showMergedCommand = false;
#ifdef HAVE_LIBHWLOC
this->topologyAffinity = false;
#endif
- this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
+ this->fields = xCalloc(Platform_numberOfFields + 1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
@@ -325,7 +359,9 @@ Settings* Settings_new(int cpuCount) {
this->filename = xStrdup(rcfile);
} else {
const char* home = getenv("HOME");
- if (!home) home = "";
+ if (!home)
+ home = "";
+
const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
char* configDir = NULL;
char* htopDir = NULL;
@@ -358,37 +394,42 @@ Settings* Settings_new(int cpuCount) {
this->delay = DEFAULT_DELAY;
bool ok = false;
if (legacyDotfile) {
- ok = Settings_read(this, legacyDotfile);
+ ok = Settings_read(this, legacyDotfile, initialCpuCount);
if (ok) {
// Transition to new location and delete old configuration file
- if (Settings_write(this))
+ if (Settings_write(this)) {
unlink(legacyDotfile);
+ }
}
free(legacyDotfile);
}
if (!ok) {
- ok = Settings_read(this, this->filename);
+ ok = Settings_read(this, this->filename, initialCpuCount);
}
if (!ok) {
this->changed = true;
// TODO: how to get SYSCONFDIR correctly through Autoconf?
char* systemSettings = String_cat(SYSCONFDIR, "/htoprc");
- ok = Settings_read(this, systemSettings);
+ ok = Settings_read(this, systemSettings, initialCpuCount);
free(systemSettings);
}
if (!ok) {
- Settings_defaultMeters(this);
+ Settings_defaultMeters(this, initialCpuCount);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
+ this->findCommInCmdline = true;
+ this->stripExeFromCmdline = true;
+ this->showMergedCommand = false;
this->headerMargin = true;
}
return this;
}
void Settings_invertSortOrder(Settings* this) {
- if (this->direction == 1)
+ if (this->direction == 1) {
this->direction = -1;
- else
+ } else {
this->direction = 1;
+ }
}
diff --git a/Settings.h b/Settings.h
index e1518ec..752970a 100644
--- a/Settings.h
+++ b/Settings.h
@@ -3,14 +3,19 @@
/*
htop - Settings.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#define DEFAULT_DELAY 15
+#include "config.h" // IWYU pragma: keep
-#include "Process.h"
#include <stdbool.h>
+#include <stdint.h>
+
+#include "Process.h"
+
+
+#define DEFAULT_DELAY 15
typedef struct {
int len;
@@ -23,21 +28,23 @@ typedef struct Settings_ {
MeterColumnSettings columns[2];
ProcessField* fields;
- int flags;
+ uint32_t flags;
int colorScheme;
int delay;
- int cpuCount;
int direction;
ProcessField sortKey;
- bool countCPUsFromZero;
+ bool countCPUsFromOne;
bool detailedCPUTime;
bool showCPUUsage;
bool showCPUFrequency;
+ #ifdef HAVE_SENSORS_SENSORS_H
+ bool showCPUTemperature;
+ bool degreeFahrenheit;
+ #endif
bool treeView;
bool showProgramPath;
- bool hideThreads;
bool shadowOtherUsers;
bool showThreadNames;
bool hideKernelThreads;
@@ -45,6 +52,11 @@ typedef struct Settings_ {
bool highlightBaseName;
bool highlightMegabytes;
bool highlightThreads;
+ bool highlightChanges;
+ int highlightDelaySecs;
+ bool findCommInCmdline;
+ bool stripExeFromCmdline;
+ bool showMergedCommand;
bool updateProcessNames;
bool accountGuestInCPUMeter;
bool headerMargin;
@@ -56,13 +68,13 @@ typedef struct Settings_ {
bool changed;
} Settings;
-#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromZero ? (cpu) : (cpu)+1)
+#define Settings_cpuId(settings, cpu) ((settings)->countCPUsFromOne ? (cpu)+1 : (cpu))
void Settings_delete(Settings* this);
bool Settings_write(Settings* this);
-Settings* Settings_new(int cpuCount);
+Settings* Settings_new(int initialCpuCount);
void Settings_invertSortOrder(Settings* this);
diff --git a/SignalsPanel.c b/SignalsPanel.c
index 8036142..436fc57 100644
--- a/SignalsPanel.c
+++ b/SignalsPanel.c
@@ -1,22 +1,21 @@
/*
htop - SignalsPanel.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Panel.h"
#include "SignalsPanel.h"
-#include "Platform.h"
-
-#include "ListItem.h"
-#include "RichString.h"
-#include <stdlib.h>
-#include <assert.h>
#include <signal.h>
+#include <stdbool.h>
-#include <ctype.h>
+#include "FunctionBar.h"
+#include "ListItem.h"
+#include "Object.h"
+#include "Panel.h"
+#include "Platform.h"
+#include "XUtils.h"
Panel* SignalsPanel_new() {
@@ -36,7 +35,7 @@ Panel* SignalsPanel_new() {
static char buf[16];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN;
- xSnprintf(buf, 16, "%2d SIGRTMIN%-+3d", sig, n);
+ xSnprintf(buf, sizeof(buf), "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) {
buf[11] = '\0';
}
diff --git a/SignalsPanel.h b/SignalsPanel.h
index 3d910ce..0a90b08 100644
--- a/SignalsPanel.h
+++ b/SignalsPanel.h
@@ -3,15 +3,17 @@
/*
htop - SignalsPanel.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "Panel.h"
+
typedef struct SignalItem_ {
const char* name;
int number;
} SignalItem;
-Panel* SignalsPanel_new();
+Panel* SignalsPanel_new(void);
#endif
diff --git a/StringUtils.c b/StringUtils.c
deleted file mode 100644
index e2386e3..0000000
--- a/StringUtils.c
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
-htop - StringUtils.c
-(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "StringUtils.h"
-#include "XAlloc.h"
-
-#include "config.h"
-
-#include <string.h>
-#include <strings.h>
-#include <stdlib.h>
-
-char* String_cat(const char* s1, const char* s2) {
- int l1 = strlen(s1);
- int l2 = strlen(s2);
- char* out = xMalloc(l1 + l2 + 1);
- memcpy(out, s1, l1);
- memcpy(out+l1, s2, l2+1);
- out[l1 + l2] = '\0';
- return out;
-}
-
-char* String_trim(const char* in) {
- while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
- in++;
- }
- int len = strlen(in);
- while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) {
- len--;
- }
- char* out = xMalloc(len+1);
- strncpy(out, in, len);
- out[len] = '\0';
- return out;
-}
-
-inline int String_eq(const char* s1, const char* s2) {
- if (s1 == NULL || s2 == NULL) {
- if (s1 == NULL && s2 == NULL)
- return 1;
- else
- return 0;
- }
- return (strcmp(s1, s2) == 0);
-}
-
-char** String_split(const char* s, char sep, int* n) {
- *n = 0;
- const int rate = 10;
- char** out = xCalloc(rate, sizeof(char*));
- int ctr = 0;
- int blocks = rate;
- char* where;
- while ((where = strchr(s, sep)) != NULL) {
- int size = where - s;
- char* token = xMalloc(size + 1);
- strncpy(token, s, size);
- token[size] = '\0';
- out[ctr] = token;
- ctr++;
- if (ctr == blocks) {
- blocks += rate;
- out = (char**) xRealloc(out, sizeof(char*) * blocks);
- }
- s += size + 1;
- }
- if (s[0] != '\0') {
- out[ctr] = xStrdup(s);
- ctr++;
- }
- out = xRealloc(out, sizeof(char*) * (ctr + 1));
- out[ctr] = NULL;
- *n = ctr;
- return out;
-}
-
-void String_freeArray(char** s) {
- if (!s) {
- return;
- }
- for (int i = 0; s[i] != NULL; i++) {
- free(s[i]);
- }
- free(s);
-}
-
-char* String_getToken(const char* line, const unsigned short int numMatch) {
- const unsigned short int len = strlen(line);
- char inWord = 0;
- unsigned short int count = 0;
- char match[50];
-
- unsigned short int foundCount = 0;
-
- for (unsigned short int i = 0; i < len; i++) {
- char lastState = inWord;
- inWord = line[i] == ' ' ? 0:1;
-
- if (lastState == 0 && inWord == 1)
- count++;
-
- if(inWord == 1){
- if (count == numMatch && line[i] != ' ' && line[i] != '\0' && line[i] != '\n' && line[i] != (char)EOF) {
- match[foundCount] = line[i];
- foundCount++;
- }
- }
- }
-
- match[foundCount] = '\0';
- return((char*)xStrdup(match));
-}
-
-char* String_readLine(FILE* fd) {
- const int step = 1024;
- int bufSize = step;
- char* buffer = xMalloc(step + 1);
- char* at = buffer;
- for (;;) {
- char* ok = fgets(at, step + 1, fd);
- if (!ok) {
- free(buffer);
- return NULL;
- }
- char* newLine = strrchr(at, '\n');
- if (newLine) {
- *newLine = '\0';
- return buffer;
- } else {
- if (feof(fd)) {
- return buffer;
- }
- }
- bufSize += step;
- buffer = xRealloc(buffer, bufSize + 1);
- at = buffer + bufSize - step;
- }
-}
diff --git a/StringUtils.h b/StringUtils.h
deleted file mode 100644
index cc0abb0..0000000
--- a/StringUtils.h
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef HEADER_StringUtils
-#define HEADER_StringUtils
-/*
-htop - StringUtils.h
-(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include <stdio.h>
-
-#define String_startsWith(s, match) (strncmp((s),(match),strlen(match)) == 0)
-#define String_contains_i(s1, s2) (strcasestr(s1, s2) != NULL)
-
-/*
- * String_startsWith gives better performance if strlen(match) can be computed
- * at compile time (e.g. when they are immutable string literals). :)
- */
-
-char* String_cat(const char* s1, const char* s2);
-
-char* String_trim(const char* in);
-
-int String_eq(const char* s1, const char* s2);
-
-char** String_split(const char* s, char sep, int* n);
-
-void String_freeArray(char** s);
-
-char* String_getToken(const char* line, const unsigned short int numMatch);
-
-char* String_readLine(FILE* fd);
-
-#endif
diff --git a/SwapMeter.c b/SwapMeter.c
index fcf450e..8e39c75 100644
--- a/SwapMeter.c
+++ b/SwapMeter.c
@@ -1,42 +1,37 @@
/*
htop - SwapMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "SwapMeter.h"
#include "CRT.h"
+#include "Object.h"
#include "Platform.h"
+#include "RichString.h"
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <sys/param.h>
-#include <assert.h>
-
-int SwapMeter_attributes[] = {
+static const int SwapMeter_attributes[] = {
SWAP
};
-static void SwapMeter_updateValues(Meter* this, char* buffer, int size) {
+static void SwapMeter_updateValues(Meter* this, char* buffer, size_t size) {
int written;
Platform_setSwapValues(this);
written = Meter_humanUnit(buffer, this->values[0], size);
- buffer += written;
- if ((size -= written) > 0) {
- *buffer++ = '/';
- size--;
- Meter_humanUnit(buffer, this->total, size);
- }
+ METER_BUFFER_CHECK(buffer, size, written);
+
+ METER_BUFFER_APPEND_CHR(buffer, size, '/');
+
+ Meter_humanUnit(buffer, this->total, size);
}
-static void SwapMeter_display(Object* cast, RichString* out) {
+static void SwapMeter_display(const Object* cast, RichString* out) {
char buffer[50];
- Meter* this = (Meter*)cast;
+ const Meter* this = (const Meter*)cast;
RichString_write(out, CRT_colors[METER_TEXT], ":");
Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
@@ -45,7 +40,7 @@ static void SwapMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[METER_VALUE], buffer);
}
-MeterClass SwapMeter_class = {
+const MeterClass SwapMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
diff --git a/SwapMeter.h b/SwapMeter.h
index 69a5855..623a036 100644
--- a/SwapMeter.h
+++ b/SwapMeter.h
@@ -3,14 +3,12 @@
/*
htop - SwapMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
-extern int SwapMeter_attributes[];
-
-extern MeterClass SwapMeter_class;
+extern const MeterClass SwapMeter_class;
#endif
diff --git a/TasksMeter.c b/TasksMeter.c
index 0b37ab2..7696988 100644
--- a/TasksMeter.c
+++ b/TasksMeter.c
@@ -1,22 +1,30 @@
/*
htop - TasksMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "TasksMeter.h"
-#include "Platform.h"
#include "CRT.h"
+#include "Macros.h"
+#include "Object.h"
+#include "ProcessList.h"
+#include "RichString.h"
+#include "Settings.h"
+#include "XUtils.h"
-int TasksMeter_attributes[] = {
- CPU_SYSTEM, PROCESS_THREAD, PROCESS, TASKS_RUNNING
+static const int TasksMeter_attributes[] = {
+ CPU_SYSTEM,
+ PROCESS_THREAD,
+ PROCESS,
+ TASKS_RUNNING
};
-static void TasksMeter_updateValues(Meter* this, char* buffer, int len) {
- ProcessList* pl = this->pl;
+static void TasksMeter_updateValues(Meter* this, char* buffer, size_t len) {
+ const ProcessList* pl = this->pl;
this->values[0] = pl->kernelThreads;
this->values[1] = pl->userlandThreads;
this->values[2] = pl->totalTasks - pl->kernelThreads - pl->userlandThreads;
@@ -24,15 +32,15 @@ static void TasksMeter_updateValues(Meter* this, char* buffer, int len) {
if (pl->totalTasks > this->total) {
this->total = pl->totalTasks;
}
- if (this->pl->settings->hideKernelThreads) {
+ if (pl->settings->hideKernelThreads) {
this->values[0] = 0;
}
xSnprintf(buffer, len, "%d/%d", (int) this->values[3], (int) this->total);
}
-static void TasksMeter_display(Object* cast, RichString* out) {
- Meter* this = (Meter*)cast;
- Settings* settings = this->pl->settings;
+static void TasksMeter_display(const Object* cast, RichString* out) {
+ const Meter* this = (const Meter*)cast;
+ const Settings* settings = this->pl->settings;
char buffer[20];
int processes = (int) this->values[2];
@@ -63,7 +71,7 @@ static void TasksMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[METER_TEXT], " running");
}
-MeterClass TasksMeter_class = {
+const MeterClass TasksMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
diff --git a/TasksMeter.h b/TasksMeter.h
index bdb62a7..cecb401 100644
--- a/TasksMeter.h
+++ b/TasksMeter.h
@@ -3,14 +3,12 @@
/*
htop - TasksMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
-extern int TasksMeter_attributes[];
-
-extern MeterClass TasksMeter_class;
+extern const MeterClass TasksMeter_class;
#endif
diff --git a/TraceScreen.c b/TraceScreen.c
index 0a3485e..47cf0ab 100644
--- a/TraceScreen.c
+++ b/TraceScreen.c
@@ -1,30 +1,32 @@
/*
htop - TraceScreen.c
(C) 2005-2006 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "TraceScreen.h"
+#include "config.h" // IWYU pragma: keep
-#include "CRT.h"
-#include "ProcessList.h"
-#include "ListItem.h"
-#include "IncSet.h"
-#include "StringUtils.h"
-#include "FunctionBar.h"
+#include "TraceScreen.h"
+#include <assert.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdbool.h>
#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
#include <stdlib.h>
-#include <stdbool.h>
+#include <string.h>
#include <unistd.h>
-#include <fcntl.h>
+#include <sys/select.h>
#include <sys/time.h>
-#include <sys/types.h>
#include <sys/wait.h>
-#include <signal.h>
+
+#include "CRT.h"
+#include "FunctionBar.h"
+#include "IncSet.h"
+#include "Panel.h"
+#include "ProvideCurses.h"
+#include "XUtils.h"
static const char* const TraceScreenFunctions[] = {"Search ", "Filter ", "AutoScroll ", "Stop Tracing ", "Done ", NULL};
@@ -33,7 +35,7 @@ static const char* const TraceScreenKeys[] = {"F3", "F4", "F8", "F9", "Esc"};
static int TraceScreenEvents[] = {KEY_F(3), KEY_F(4), KEY_F(8), KEY_F(9), 27};
-InfoScreenClass TraceScreen_class = {
+const InfoScreenClass TraceScreen_class = {
.super = {
.extends = Class(Object),
.delete = TraceScreen_delete
@@ -44,14 +46,13 @@ InfoScreenClass TraceScreen_class = {
};
TraceScreen* TraceScreen_new(Process* process) {
- TraceScreen* this = xMalloc(sizeof(TraceScreen));
+ // This initializes all TraceScreen variables to "false" so only default = true ones need to be set below
+ TraceScreen* this = xCalloc(1, sizeof(TraceScreen));
Object_setClass(this, Class(TraceScreen));
this->tracing = true;
- this->contLine = false;
- this->follow = false;
FunctionBar* fuBar = FunctionBar_new(TraceScreenFunctions, TraceScreenKeys, TraceScreenEvents);
CRT_disableDelay();
- return (TraceScreen*) InfoScreen_init(&this->super, process, fuBar, LINES-2, "");
+ return (TraceScreen*) InfoScreen_init(&this->super, process, fuBar, LINES - 2, "");
}
void TraceScreen_delete(Object* cast) {
@@ -59,63 +60,100 @@ void TraceScreen_delete(Object* cast) {
if (this->child > 0) {
kill(this->child, SIGTERM);
waitpid(this->child, NULL, 0);
+ }
+
+ if (this->strace) {
fclose(this->strace);
}
+
CRT_enableDelay();
- free(InfoScreen_done((InfoScreen*)cast));
+ free(InfoScreen_done((InfoScreen*)this));
}
void TraceScreen_draw(InfoScreen* this) {
attrset(CRT_colors[PANEL_HEADER_FOCUS]);
mvhline(0, 0, ' ', COLS);
- mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
+ mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc);
}
bool TraceScreen_forkTracer(TraceScreen* this) {
- char buffer[1001];
- int error = pipe(this->fdpair);
- if (error == -1) return false;
- this->child = fork();
- if (this->child == -1) return false;
- if (this->child == 0) {
+ int fdpair[2] = {0, 0};
+
+ if (pipe(fdpair) == -1)
+ return false;
+
+ if (fcntl(fdpair[0], F_SETFL, O_NONBLOCK) < 0)
+ goto err;
+
+ if (fcntl(fdpair[1], F_SETFL, O_NONBLOCK) < 0)
+ goto err;
+
+ pid_t child = fork();
+ if (child == -1)
+ goto err;
+
+ if (child == 0) {
+ close(fdpair[0]);
+
+ dup2(fdpair[1], STDOUT_FILENO);
+ dup2(fdpair[1], STDERR_FILENO);
+ close(fdpair[1]);
+
CRT_dropPrivileges();
- dup2(this->fdpair[1], STDERR_FILENO);
- int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK);
- if (ok != -1) {
- xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
- execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, NULL);
- }
+
+ char buffer[32] = {0};
+ xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
+ execlp("strace", "strace", "-T", "-tt", "-s", "512", "-p", buffer, NULL);
+
+ // Should never reach here, unless execlp fails ...
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";
- ssize_t written = write(this->fdpair[1], message, strlen(message));
+ ssize_t written = write(STDERR_FILENO, message, strlen(message));
(void) written;
- exit(1);
+
+ exit(127);
}
- int ok = fcntl(this->fdpair[0], F_SETFL, O_NONBLOCK);
- if (ok == -1) return false;
- this->strace = fdopen(this->fdpair[0], "r");
- this->fd_strace = fileno(this->strace);
+
+ FILE* fd = fdopen(fdpair[0], "r");
+ if (!fd)
+ goto err;
+
+ close(fdpair[1]);
+
+ this->child = child;
+ this->strace = fd;
return true;
+
+err:
+ close(fdpair[1]);
+ close(fdpair[0]);
+ return false;
}
void TraceScreen_updateTrace(InfoScreen* super) {
TraceScreen* this = (TraceScreen*) super;
- char buffer[1001];
+ char buffer[1025];
+
+ int fd_strace = fileno(this->strace);
+ assert(fd_strace != -1);
+
fd_set fds;
FD_ZERO(&fds);
// FD_SET(STDIN_FILENO, &fds);
- FD_SET(this->fd_strace, &fds);
- struct timeval tv;
- tv.tv_sec = 0; tv.tv_usec = 500;
- int ready = select(this->fd_strace+1, &fds, NULL, NULL, &tv);
- int nread = 0;
- if (ready > 0 && FD_ISSET(this->fd_strace, &fds))
- nread = fread(buffer, 1, 1000, this->strace);
+ FD_SET(fd_strace, &fds);
+
+ struct timeval tv = { .tv_sec = 0, .tv_usec = 500 };
+ int ready = select(fd_strace + 1, &fds, NULL, NULL, &tv);
+
+ size_t nread = 0;
+ if (ready > 0 && FD_ISSET(fd_strace, &fds))
+ nread = fread(buffer, 1, sizeof(buffer) - 1, this->strace);
+
if (nread && this->tracing) {
- char* line = buffer;
+ const char* line = buffer;
buffer[nread] = '\0';
- for (int i = 0; i < nread; i++) {
+ for (size_t i = 0; i < nread; i++) {
if (buffer[i] == '\n') {
buffer[i] = '\0';
if (this->contLine) {
@@ -124,16 +162,17 @@ void TraceScreen_updateTrace(InfoScreen* super) {
} else {
InfoScreen_addLine(&this->super, line);
}
- line = buffer+i+1;
+ line = buffer + i + 1;
}
}
- if (line < buffer+nread) {
+ if (line < buffer + nread) {
InfoScreen_addLine(&this->super, line);
buffer[nread] = '\0';
this->contLine = true;
}
- if (this->follow)
- Panel_setSelected(this->super.display, Panel_size(this->super.display)-1);
+ if (this->follow) {
+ Panel_setSelected(this->super.display, Panel_size(this->super.display) - 1);
+ }
}
}
diff --git a/TraceScreen.h b/TraceScreen.h
index 1e5218c..8192047 100644
--- a/TraceScreen.h
+++ b/TraceScreen.h
@@ -3,25 +3,30 @@
/*
htop - TraceScreen.h
(C) 2005-2006 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
+
#include "InfoScreen.h"
+#include "Object.h"
+#include "Process.h"
+
typedef struct TraceScreen_ {
InfoScreen super;
bool tracing;
- int fdpair[2];
- int child;
+ pid_t child;
FILE* strace;
- int fd_strace;
bool contLine;
bool follow;
} TraceScreen;
-extern InfoScreenClass TraceScreen_class;
+extern const InfoScreenClass TraceScreen_class;
TraceScreen* TraceScreen_new(Process* process);
diff --git a/UptimeMeter.c b/UptimeMeter.c
index da1c9fd..37740c2 100644
--- a/UptimeMeter.c
+++ b/UptimeMeter.c
@@ -1,29 +1,32 @@
/*
htop - UptimeMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "UptimeMeter.h"
-#include "Platform.h"
+
#include "CRT.h"
+#include "Object.h"
+#include "Platform.h"
+#include "XUtils.h"
-int UptimeMeter_attributes[] = {
+static const int UptimeMeter_attributes[] = {
UPTIME
};
-static void UptimeMeter_updateValues(Meter* this, char* buffer, int len) {
+static void UptimeMeter_updateValues(Meter* this, char* buffer, size_t len) {
int totalseconds = Platform_getUptime();
if (totalseconds == -1) {
xSnprintf(buffer, len, "(unknown)");
return;
}
int seconds = totalseconds % 60;
- int minutes = (totalseconds/60) % 60;
- int hours = (totalseconds/3600) % 24;
- int days = (totalseconds/86400);
+ int minutes = (totalseconds / 60) % 60;
+ int hours = (totalseconds / 3600) % 24;
+ int days = (totalseconds / 86400);
this->values[0] = days;
if (days > this->total) {
this->total = days;
@@ -41,7 +44,7 @@ static void UptimeMeter_updateValues(Meter* this, char* buffer, int len) {
xSnprintf(buffer, len, "%s%02d:%02d:%02d", daysbuf, hours, minutes, seconds);
}
-MeterClass UptimeMeter_class = {
+const MeterClass UptimeMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete
diff --git a/UptimeMeter.h b/UptimeMeter.h
index 5fa1e93..49300bb 100644
--- a/UptimeMeter.h
+++ b/UptimeMeter.h
@@ -3,14 +3,12 @@
/*
htop - UptimeMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
-extern int UptimeMeter_attributes[];
-
-extern MeterClass UptimeMeter_class;
+extern const MeterClass UptimeMeter_class;
#endif
diff --git a/UsersTable.c b/UsersTable.c
index 86ed75c..fdbfd68 100644
--- a/UsersTable.c
+++ b/UsersTable.c
@@ -1,28 +1,26 @@
/*
htop - UsersTable.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "UsersTable.h"
-#include "XAlloc.h"
+#include "config.h" // IWYU pragma: keep
-#include "config.h"
+#include "UsersTable.h"
-#include <stdio.h>
-#include <string.h>
-#include <strings.h>
#include <pwd.h>
-#include <sys/types.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <assert.h>
+
+#include "XUtils.h"
UsersTable* UsersTable_new() {
UsersTable* this;
this = xMalloc(sizeof(UsersTable));
- this->users = Hashtable_new(20, true);
+ this->users = Hashtable_new(10, true);
return this;
}
@@ -32,9 +30,9 @@ void UsersTable_delete(UsersTable* this) {
}
char* UsersTable_getRef(UsersTable* this, unsigned int uid) {
- char* name = (char*) (Hashtable_get(this->users, uid));
+ char* name = Hashtable_get(this->users, uid);
if (name == NULL) {
- struct passwd* userData = getpwuid(uid);
+ const struct passwd* userData = getpwuid(uid);
if (userData != NULL) {
name = xStrdup(userData->pw_name);
Hashtable_put(this->users, uid, name);
diff --git a/UsersTable.h b/UsersTable.h
index fb3eaed..0cac7dc 100644
--- a/UsersTable.h
+++ b/UsersTable.h
@@ -3,7 +3,7 @@
/*
htop - UsersTable.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -13,7 +13,7 @@ typedef struct UsersTable_ {
Hashtable* users;
} UsersTable;
-UsersTable* UsersTable_new();
+UsersTable* UsersTable_new(void);
void UsersTable_delete(UsersTable* this);
diff --git a/Vector.c b/Vector.c
index e51fd7b..40e6203 100644
--- a/Vector.c
+++ b/Vector.c
@@ -1,7 +1,7 @@
/*
htop - Vector.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -10,14 +10,18 @@ in the source distribution for its full text.
#include <assert.h>
#include <stdlib.h>
#include <string.h>
-#include <stdbool.h>
+#include "XUtils.h"
-Vector* Vector_new(ObjectClass* type, bool owner, int size) {
+
+Vector* Vector_new(const ObjectClass* type, bool owner, int size) {
Vector* this;
- if (size == DEFAULT_SIZE)
+ if (size == DEFAULT_SIZE) {
size = 10;
+ }
+
+ assert(size > 0);
this = xMalloc(sizeof(Vector));
this->growthRate = size;
this->array = (Object**) xCalloc(size, sizeof(Object*));
@@ -30,39 +34,56 @@ Vector* Vector_new(ObjectClass* type, bool owner, int size) {
void Vector_delete(Vector* this) {
if (this->owner) {
- for (int i = 0; i < this->items; i++)
- if (this->array[i])
+ for (int i = 0; i < this->items; i++) {
+ if (this->array[i]) {
Object_delete(this->array[i]);
+ }
+ }
}
free(this->array);
free(this);
}
-#ifdef DEBUG
+#ifndef NDEBUG
-static inline bool Vector_isConsistent(Vector* this) {
+static bool Vector_isConsistent(const Vector* this) {
assert(this->items <= this->arraySize);
+
if (this->owner) {
- for (int i = 0; i < this->items; i++)
- if (this->array[i] && !Object_isA(this->array[i], this->type))
+ for (int i = 0; i < this->items; i++) {
+ if (!this->array[i]) {
return false;
- return true;
- } else {
- return true;
+ }
+ }
}
+
+ return true;
}
-int Vector_count(Vector* this) {
- int items = 0;
+unsigned int Vector_count(const Vector* this) {
+ unsigned int items = 0;
for (int i = 0; i < this->items; i++) {
- if (this->array[i])
+ if (this->array[i]) {
items++;
+ }
}
- assert(items == this->items);
+ assert(items == (unsigned int)this->items);
return items;
}
-#endif
+Object* Vector_get(const Vector* this, int idx) {
+ assert(idx >= 0 && idx < this->items);
+ assert(this->array[idx]);
+ assert(Object_isA(this->array[idx], this->type));
+ return this->array[idx];
+}
+
+int Vector_size(const Vector* this) {
+ assert(Vector_isConsistent(this));
+ return this->items;
+}
+
+#endif /* NDEBUG */
void Vector_prune(Vector* this) {
assert(Vector_isConsistent(this));
@@ -76,14 +97,22 @@ void Vector_prune(Vector* this) {
this->items = 0;
}
-static int comparisons = 0;
+//static int comparisons = 0;
+
+static void swap(Object** array, int indexA, int indexB) {
+ assert(indexA >= 0);
+ assert(indexB >= 0);
+ Object* tmp = array[indexA];
+ array[indexA] = array[indexB];
+ array[indexB] = tmp;
+}
static int partition(Object** array, int left, int right, int pivotIndex, Object_Compare compare) {
- void* pivotValue = array[pivotIndex];
+ const Object* pivotValue = array[pivotIndex];
swap(array, pivotIndex, right);
int storeIndex = left;
for (int i = left; i < right; i++) {
- comparisons++;
+ //comparisons++;
if (compare(array[i], pivotValue) <= 0) {
swap(array, i, storeIndex);
storeIndex++;
@@ -96,7 +125,8 @@ static int partition(Object** array, int left, int right, int pivotIndex, Object
static void quickSort(Object** array, int left, int right, Object_Compare compare) {
if (left >= right)
return;
- int pivotIndex = (left+right) / 2;
+
+ int pivotIndex = (left + right) / 2;
int pivotNewIndex = partition(array, left, right, pivotIndex, compare);
quickSort(array, left, pivotNewIndex - 1, compare);
quickSort(array, pivotNewIndex + 1, right, compare);
@@ -126,24 +156,25 @@ static void combSort(Object** array, int left, int right, Object_Compare compare
*/
static void insertionSort(Object** array, int left, int right, Object_Compare compare) {
- for (int i = left+1; i <= right; i++) {
- void* t = array[i];
+ for (int i = left + 1; i <= right; i++) {
+ Object* t = array[i];
int j = i - 1;
while (j >= left) {
- comparisons++;
+ //comparisons++;
if (compare(array[j], t) <= 0)
break;
- array[j+1] = array[j];
+
+ array[j + 1] = array[j];
j--;
}
- array[j+1] = t;
+ array[j + 1] = t;
}
}
-void Vector_quickSort(Vector* this) {
- assert(this->type->compare);
+void Vector_quickSortCustomCompare(Vector* this, Object_Compare compare) {
+ assert(compare);
assert(Vector_isConsistent(this));
- quickSort(this->array, 0, this->items - 1, this->type->compare);
+ quickSort(this->array, 0, this->items - 1, compare);
assert(Vector_isConsistent(this));
}
@@ -179,8 +210,8 @@ void Vector_insert(Vector* this, int idx, void* data_) {
Vector_checkArraySize(this);
//assert(this->array[this->items] == NULL);
- for (int i = this->items; i > idx; i--) {
- this->array[i] = this->array[i-1];
+ if (idx < this->items) {
+ memmove(&this->array[idx + 1], &this->array[idx], (this->items - idx) * sizeof(this->array[0]));
}
this->array[idx] = data;
this->items++;
@@ -191,10 +222,11 @@ Object* Vector_take(Vector* this, int idx) {
assert(idx >= 0 && idx < this->items);
assert(Vector_isConsistent(this));
Object* removed = this->array[idx];
- //assert (removed != NULL);
+ assert(removed);
this->items--;
- for (int i = idx; i < this->items; i++)
- this->array[i] = this->array[i+1];
+ if (idx < this->items) {
+ memmove(&this->array[idx], &this->array[idx + 1], (this->items - idx) * sizeof(this->array[0]));
+ }
//this->array[this->items] = NULL;
assert(Vector_isConsistent(this));
return removed;
@@ -205,15 +237,18 @@ Object* Vector_remove(Vector* this, int idx) {
if (this->owner) {
Object_delete(removed);
return NULL;
- } else
+ } else {
return removed;
+ }
}
void Vector_moveUp(Vector* this, int idx) {
assert(idx >= 0 && idx < this->items);
assert(Vector_isConsistent(this));
+
if (idx == 0)
return;
+
Object* temp = this->array[idx];
this->array[idx] = this->array[idx - 1];
this->array[idx - 1] = temp;
@@ -222,8 +257,10 @@ void Vector_moveUp(Vector* this, int idx) {
void Vector_moveDown(Vector* this, int idx) {
assert(idx >= 0 && idx < this->items);
assert(Vector_isConsistent(this));
+
if (idx == this->items - 1)
return;
+
Object* temp = this->array[idx];
this->array[idx] = this->array[idx + 1];
this->array[idx + 1] = temp;
@@ -232,52 +269,23 @@ void Vector_moveDown(Vector* this, int idx) {
void Vector_set(Vector* this, int idx, void* data_) {
Object* data = data_;
assert(idx >= 0);
- assert(Object_isA((Object*)data, this->type));
+ assert(Object_isA(data, this->type));
assert(Vector_isConsistent(this));
Vector_checkArraySize(this);
if (idx >= this->items) {
- this->items = idx+1;
+ this->items = idx + 1;
} else {
if (this->owner) {
Object* removed = this->array[idx];
assert (removed != NULL);
- if (this->owner) {
- Object_delete(removed);
- }
+ Object_delete(removed);
}
}
this->array[idx] = data;
assert(Vector_isConsistent(this));
}
-#ifdef DEBUG
-
-inline Object* Vector_get(Vector* this, int idx) {
- assert(idx < this->items);
- assert(Vector_isConsistent(this));
- return this->array[idx];
-}
-
-#else
-
-// In this case, Vector_get is defined as a macro in vector.h
-
-#endif
-
-#ifdef DEBUG
-
-inline int Vector_size(Vector* this) {
- assert(Vector_isConsistent(this));
- return this->items;
-}
-
-#else
-
-// In this case, Vector_size is defined as a macro in vector.h
-
-#endif
-
/*
static void Vector_merge(Vector* this, Vector* v2) {
@@ -295,24 +303,25 @@ static void Vector_merge(Vector* this, Vector* v2) {
void Vector_add(Vector* this, void* data_) {
Object* data = data_;
- assert(Object_isA((Object*)data, this->type));
+ assert(Object_isA(data, this->type));
assert(Vector_isConsistent(this));
int i = this->items;
Vector_set(this, this->items, data);
- assert(this->items == i+1); (void)(i);
+ assert(this->items == i + 1); (void)(i);
assert(Vector_isConsistent(this));
}
-inline int Vector_indexOf(Vector* this, void* search_, Object_Compare compare) {
- Object* search = search_;
- assert(Object_isA((Object*)search, this->type));
+int Vector_indexOf(const Vector* this, const void* search_, Object_Compare compare) {
+ const Object* search = search_;
+ assert(Object_isA(search, this->type));
assert(compare);
assert(Vector_isConsistent(this));
for (int i = 0; i < this->items; i++) {
- Object* o = (Object*)this->array[i];
+ const Object* o = this->array[i];
assert(o);
- if (compare(search, o) == 0)
+ if (compare(search, o) == 0) {
return i;
+ }
}
return -1;
}
@@ -322,9 +331,10 @@ void Vector_splice(Vector* this, Vector* from) {
assert(Vector_isConsistent(from));
assert(!(this->owner && from->owner));
- int olditmes = this->items;
+ int olditems = this->items;
this->items += from->items;
Vector_checkArraySize(this);
- for (int j = 0; j < from->items; j++)
- this->array[olditmes + j] = from->array[j];
+ for (int j = 0; j < from->items; j++) {
+ this->array[olditems + j] = from->array[j];
+ }
}
diff --git a/Vector.h b/Vector.h
index 209e27c..ee51413 100644
--- a/Vector.h
+++ b/Vector.h
@@ -3,40 +3,38 @@
/*
htop - Vector.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Object.h"
-#define swap(a_,x_,y_) do{ void* tmp_ = a_[x_]; a_[x_] = a_[y_]; a_[y_] = tmp_; }while(0)
+#include <stdbool.h>
+
#ifndef DEFAULT_SIZE
-#define DEFAULT_SIZE -1
+#define DEFAULT_SIZE (-1)
#endif
typedef struct Vector_ {
- Object **array;
- ObjectClass* type;
+ Object** array;
+ const ObjectClass* type;
int arraySize;
int growthRate;
int items;
bool owner;
} Vector;
-Vector* Vector_new(ObjectClass* type, bool owner, int size);
+Vector* Vector_new(const ObjectClass* type, bool owner, int size);
void Vector_delete(Vector* this);
-#ifdef DEBUG
-
-int Vector_count(Vector* this);
-
-#endif
-
void Vector_prune(Vector* this);
-void Vector_quickSort(Vector* this);
+void Vector_quickSortCustomCompare(Vector* this, Object_Compare compare);
+static inline void Vector_quickSort(Vector* this) {
+ Vector_quickSortCustomCompare(this, this->type->compare);
+}
void Vector_insertionSort(Vector* this);
@@ -52,29 +50,27 @@ void Vector_moveDown(Vector* this, int idx);
void Vector_set(Vector* this, int idx, void* data_);
-#ifdef DEBUG
+#ifndef NDEBUG
-Object* Vector_get(Vector* this, int idx);
+Object* Vector_get(const Vector* this, int idx);
+int Vector_size(const Vector* this);
+unsigned int Vector_count(const Vector* this);
-#else
+#else /* NDEBUG */
-#define Vector_get(v_, idx_) ((v_)->array[idx_])
+static inline Object* Vector_get(Vector* this, int idx) {
+ return this->array[idx];
+}
-#endif
+static inline int Vector_size(const Vector* this) {
+ return this->items;
+}
-#ifdef DEBUG
-
-int Vector_size(Vector* this);
-
-#else
-
-#define Vector_size(v_) ((v_)->items)
-
-#endif
+#endif /* NDEBUG */
void Vector_add(Vector* this, void* data_);
-int Vector_indexOf(Vector* this, void* search_, Object_Compare compare);
+int Vector_indexOf(const Vector* this, const void* search_, Object_Compare compare);
void Vector_splice(Vector* this, Vector* from);
diff --git a/XAlloc.c b/XAlloc.c
deleted file mode 100644
index 38616df..0000000
--- a/XAlloc.c
+++ /dev/null
@@ -1,70 +0,0 @@
-
-#include "XAlloc.h"
-#include "RichString.h"
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#include <stdlib.h>
-#include <string.h>
-
-
-void fail() {
- curs_set(1);
- endwin();
- err(1, NULL);
-}
-
-void* xMalloc(size_t size) {
- void* data = malloc(size);
- if (!data && size > 0) {
- fail();
- }
- return data;
-}
-
-void* xCalloc(size_t nmemb, size_t size) {
- void* data = calloc(nmemb, size);
- if (!data && nmemb > 0 && size > 0) {
- fail();
- }
- return data;
-}
-
-void* xRealloc(void* ptr, size_t size) {
- void* data = realloc(ptr, size);
- if (!data && size > 0) {
- fail();
- }
- return data;
-}
-
-#undef xAsprintf
-
-#define xAsprintf(strp, fmt, ...) do { int _r=asprintf(strp, fmt, __VA_ARGS__); if (_r < 0) { fail(); } } while(0)
-
-#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
-
-#undef xStrdup
-#undef xStrdup_
-#ifdef NDEBUG
-# define xStrdup_ xStrdup
-#else
-# define xStrdup(str_) (assert(str_), xStrdup_(str_))
-#endif
-
-#ifndef __has_attribute // Clang's macro
-# define __has_attribute(x) 0
-#endif
-#if (__has_attribute(nonnull) || \
- ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
-char* xStrdup_(const char* str) __attribute__((nonnull));
-#endif // __has_attribute(nonnull) || GNU C 3.3 or later
-
-char* xStrdup_(const char* str) {
- char* data = strdup(str);
- if (!data) {
- fail();
- }
- return data;
-}
diff --git a/XAlloc.h b/XAlloc.h
deleted file mode 100644
index b933423..0000000
--- a/XAlloc.h
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef HEADER_XAlloc
-#define HEADER_XAlloc
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <err.h>
-#include <assert.h>
-#include <stdlib.h>
-
-void fail(void);
-
-void* xMalloc(size_t size);
-
-void* xCalloc(size_t nmemb, size_t size);
-
-void* xRealloc(void* ptr, size_t size);
-
-#undef xAsprintf
-
-#define xAsprintf(strp, fmt, ...) do { int _r=asprintf(strp, fmt, __VA_ARGS__); if (_r < 0) { fail(); } } while(0)
-
-#define xSnprintf(fmt, len, ...) do { int _l=len; int _n=snprintf(fmt, _l, __VA_ARGS__); if (!(_n > -1 && _n < _l)) { curs_set(1); endwin(); err(1, NULL); } } while(0)
-
-#undef xStrdup
-#undef xStrdup_
-#ifdef NDEBUG
-# define xStrdup_ xStrdup
-#else
-# define xStrdup(str_) (assert(str_), xStrdup_(str_))
-#endif
-
-#ifndef __has_attribute // Clang's macro
-# define __has_attribute(x) 0
-#endif
-#if (__has_attribute(nonnull) || \
- ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)))
-char* xStrdup_(const char* str) __attribute__((nonnull));
-#endif // __has_attribute(nonnull) || GNU C 3.3 or later
-
-char* xStrdup_(const char* str);
-
-#endif
diff --git a/XUtils.c b/XUtils.c
new file mode 100644
index 0000000..cd5edb9
--- /dev/null
+++ b/XUtils.c
@@ -0,0 +1,263 @@
+/*
+htop - StringUtils.c
+(C) 2004-2011 Hisham H. Muhammad
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include "XUtils.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "CRT.h"
+
+
+void fail() {
+ CRT_done();
+ abort();
+
+ _exit(1); // Should never reach here
+}
+
+void* xMalloc(size_t size) {
+ assert(size > 0);
+ void* data = malloc(size);
+ if (!data) {
+ fail();
+ }
+ return data;
+}
+
+void* xCalloc(size_t nmemb, size_t size) {
+ assert(nmemb > 0);
+ assert(size > 0);
+ void* data = calloc(nmemb, size);
+ if (!data) {
+ fail();
+ }
+ return data;
+}
+
+void* xRealloc(void* ptr, size_t size) {
+ assert(size > 0);
+ void* data = realloc(ptr, size); // deepcode ignore MemoryLeakOnRealloc: this goes to fail()
+ if (!data) {
+ free(ptr);
+ fail();
+ }
+ return data;
+}
+
+char* String_cat(const char* s1, const char* s2) {
+ const size_t l1 = strlen(s1);
+ const size_t l2 = strlen(s2);
+ char* out = xMalloc(l1 + l2 + 1);
+ memcpy(out, s1, l1);
+ memcpy(out + l1, s2, l2);
+ out[l1 + l2] = '\0';
+ return out;
+}
+
+char* String_trim(const char* in) {
+ while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
+ in++;
+ }
+
+ size_t len = strlen(in);
+ while (len > 0 && (in[len - 1] == ' ' || in[len - 1] == '\t' || in[len - 1] == '\n')) {
+ len--;
+ }
+
+ return xStrndup(in, len);
+}
+
+char** String_split(const char* s, char sep, size_t* n) {
+ const unsigned int rate = 10;
+ char** out = xCalloc(rate, sizeof(char*));
+ size_t ctr = 0;
+ unsigned int blocks = rate;
+ const char* where;
+ while ((where = strchr(s, sep)) != NULL) {
+ size_t size = (size_t)(where - s);
+ out[ctr] = xStrndup(s, size);
+ ctr++;
+ if (ctr == blocks) {
+ blocks += rate;
+ out = (char**) xRealloc(out, sizeof(char*) * blocks);
+ }
+ s += size + 1;
+ }
+ if (s[0] != '\0') {
+ out[ctr] = xStrdup(s);
+ ctr++;
+ }
+ out = xRealloc(out, sizeof(char*) * (ctr + 1));
+ out[ctr] = NULL;
+
+ if (n)
+ *n = ctr;
+
+ return out;
+}
+
+void String_freeArray(char** s) {
+ if (!s) {
+ return;
+ }
+ for (size_t i = 0; s[i] != NULL; i++) {
+ free(s[i]);
+ }
+ free(s);
+}
+
+char* String_getToken(const char* line, const unsigned short int numMatch) {
+ const size_t len = strlen(line);
+ char inWord = 0;
+ unsigned short int count = 0;
+ char match[50];
+
+ size_t foundCount = 0;
+
+ for (size_t i = 0; i < len; i++) {
+ char lastState = inWord;
+ inWord = line[i] == ' ' ? 0 : 1;
+
+ if (lastState == 0 && inWord == 1)
+ count++;
+
+ if (inWord == 1) {
+ if (count == numMatch && line[i] != ' ' && line[i] != '\0' && line[i] != '\n' && line[i] != (char)EOF) {
+ match[foundCount] = line[i];
+ foundCount++;
+ }
+ }
+ }
+
+ match[foundCount] = '\0';
+ return xStrdup(match);
+}
+
+char* String_readLine(FILE* fd) {
+ const unsigned int step = 1024;
+ unsigned int bufSize = step;
+ char* buffer = xMalloc(step + 1);
+ char* at = buffer;
+ for (;;) {
+ char* ok = fgets(at, step + 1, fd);
+ if (!ok) {
+ free(buffer);
+ return NULL;
+ }
+ char* newLine = strrchr(at, '\n');
+ if (newLine) {
+ *newLine = '\0';
+ return buffer;
+ } else {
+ if (feof(fd)) {
+ return buffer;
+ }
+ }
+ bufSize += step;
+ buffer = xRealloc(buffer, bufSize + 1);
+ at = buffer + bufSize - step;
+ }
+}
+
+int xAsprintf(char** strp, const char* fmt, ...) {
+ va_list vl;
+ va_start(vl, fmt);
+ int r = vasprintf(strp, fmt, vl);
+ va_end(vl);
+
+ if (r < 0 || !*strp) {
+ fail();
+ }
+
+ return r;
+}
+
+int xSnprintf(char* buf, size_t len, const char* fmt, ...) {
+ va_list vl;
+ va_start(vl, fmt);
+ int n = vsnprintf(buf, len, fmt, vl);
+ va_end(vl);
+
+ if (n < 0 || (size_t)n >= len) {
+ fail();
+ }
+
+ return n;
+}
+
+char* xStrdup(const char* str) {
+ char* data = strdup(str);
+ if (!data) {
+ fail();
+ }
+ return data;
+}
+
+char* xStrndup(const char* str, size_t len) {
+ char* data = strndup(str, len);
+ if (!data) {
+ fail();
+ }
+ return data;
+}
+
+static ssize_t readfd_internal(int fd, void* buffer, size_t count) {
+ if (!count) {
+ close(fd);
+ return -EINVAL;
+ }
+
+ ssize_t alreadyRead = 0;
+ count--; // reserve one for null-terminator
+
+ for (;;) {
+ ssize_t res = read(fd, buffer, count);
+ if (res == -1) {
+ if (errno == EINTR)
+ continue;
+
+ close(fd);
+ return -errno;
+ }
+
+ if (res > 0) {
+ buffer = ((char*)buffer) + res;
+ count -= (size_t)res;
+ alreadyRead += res;
+ }
+
+ if (count == 0 || res == 0) {
+ close(fd);
+ *((char*)buffer) = '\0';
+ return alreadyRead;
+ }
+ }
+}
+
+ssize_t xReadfile(const char* pathname, void* buffer, size_t count) {
+ int fd = open(pathname, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ return readfd_internal(fd, buffer, count);
+}
+
+ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count) {
+ int fd = Compat_openat(dirfd, pathname, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ return readfd_internal(fd, buffer, count);
+}
diff --git a/XUtils.h b/XUtils.h
new file mode 100644
index 0000000..19cfadb
--- /dev/null
+++ b/XUtils.h
@@ -0,0 +1,71 @@
+#ifndef HEADER_XUtils
+#define HEADER_XUtils
+/*
+htop - StringUtils.h
+(C) 2004-2011 Hisham H. Muhammad
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "config.h" // IWYU pragma: keep
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h> // IWYU pragma: keep
+#include <string.h> // IWYU pragma: keep
+#include <sys/types.h>
+
+#include "Compat.h"
+#include "Macros.h"
+
+
+void fail(void) ATTR_NORETURN;
+
+void* xMalloc(size_t size);
+
+void* xCalloc(size_t nmemb, size_t size);
+
+void* xRealloc(void* ptr, size_t size);
+
+/*
+ * String_startsWith gives better performance if strlen(match) can be computed
+ * at compile time (e.g. when they are immutable string literals). :)
+ */
+static inline bool String_startsWith(const char* s, const char* match) {
+ return strncmp(s, match, strlen(match)) == 0;
+}
+
+static inline bool String_contains_i(const char* s1, const char* s2) {
+ return strcasestr(s1, s2) != NULL;
+}
+
+static inline bool String_eq(const char* s1, const char* s2) {
+ return strcmp(s1, s2) == 0;
+}
+
+char* String_cat(const char* s1, const char* s2);
+
+char* String_trim(const char* in);
+
+char** String_split(const char* s, char sep, size_t* n);
+
+void String_freeArray(char** s);
+
+char* String_getToken(const char* line, unsigned short int numMatch);
+
+char* String_readLine(FILE* fd);
+
+ATTR_FORMAT(printf, 2, 3)
+int xAsprintf(char** strp, const char* fmt, ...);
+
+ATTR_FORMAT(printf, 3, 4)
+int xSnprintf(char* buf, size_t len, const char* fmt, ...);
+
+char* xStrdup(const char* str) ATTR_NONNULL;
+
+char* xStrndup(const char* str, size_t len) ATTR_NONNULL;
+
+ssize_t xReadfile(const char* pathname, void* buffer, size_t count);
+ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size_t count);
+
+#endif
diff --git a/autogen.sh b/autogen.sh
index c07e085..fb38e5e 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,3 +1,2 @@
#!/bin/sh
-mkdir -p m4
-autoreconf --install --force
+autoreconf --force --install --verbose -Wall
diff --git a/configure.ac b/configure.ac
index 30834f2..f624664 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,12 +2,11 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65)
-AC_INIT([htop],[3.0.2],[htop@groups.io])
+AC_INIT([htop],[3.0.3],[htop@groups.io])
AC_CONFIG_SRCDIR([htop.c])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_HEADERS([config.h])
-AC_CONFIG_MACRO_DIR([m4])
# Required by hwloc scripts
AC_CANONICAL_TARGET
@@ -27,24 +26,31 @@ AC_USE_SYSTEM_EXTENSIONS
case "$target_os" in
linux*|gnu*)
my_htop_platform=linux
+ AC_DEFINE([HTOP_LINUX], [], [Building for Linux])
;;
freebsd*|kfreebsd*)
my_htop_platform=freebsd
+ AC_DEFINE([HTOP_FREEBSD], [], [Building for FreeBSD])
;;
openbsd*)
my_htop_platform=openbsd
+ AC_DEFINE([HTOP_OPENBSD], [], [Building for OpenBSD])
;;
dragonfly*)
my_htop_platform=dragonflybsd
+ AC_DEFINE([HTOP_DRAGONFLYBSD], [], [Building for DragonFlyBSD])
;;
darwin*)
my_htop_platform=darwin
+ AC_DEFINE([HTOP_DARWIN], [], [Building for Darwin])
;;
solaris*)
my_htop_platform=solaris
+ AC_DEFINE([HTOP_SOLARIS], [], [Building for Solaris])
;;
*)
my_htop_platform=unsupported
+ AC_DEFINE([HTOP_UNSUPPORTED], [], [Building for an unsupported platform])
;;
esac
@@ -77,35 +83,38 @@ AC_HEADER_STDBOOL
AC_C_CONST
AC_TYPE_PID_T
AC_TYPE_UID_T
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
# Checks for library functions and compiler features.
# ----------------------------------------------------------------------
AC_FUNC_CLOSEDIR_VOID
-AC_TYPE_SIGNAL
AC_FUNC_STAT
-AC_CHECK_FUNCS([memmove strncasecmp strstr strdup])
+AC_CHECK_FUNCS([\
+ faccessat\
+ fstatat\
+ openat\
+ readlinkat\
+])
+
+AC_SEARCH_LIBS([dlopen], [dl dld])
save_cflags="${CFLAGS}"
CFLAGS="${CFLAGS} -std=c99"
-AC_MSG_CHECKING([whether gcc -std=c99 option works])
+AC_MSG_CHECKING([whether cc -std=c99 option works])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
[AC_INCLUDES_DEFAULT], [[char *a; a = strdup("foo"); int i = 0; i++; // C99]])],
[AC_MSG_RESULT([yes])],
- [AC_MSG_ERROR([htop is written in C99. A newer version of gcc is required.])])
+ [AC_MSG_ERROR([htop is written in C99. A newer compiler is required.])])
CFLAGS="$save_cflags"
-save_cflags="${CFLAGS}"
-CFLAGS="$CFLAGS -Wextra"
-AC_MSG_CHECKING([if compiler supports -Wextra])
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [])],[
- wextra_flag=-Wextra
- AC_MSG_RESULT([yes])
-],[
- wextra_flag=
- AC_MSG_RESULT([no])
-])
-CFLAGS="$save_cflags"
-AC_SUBST(wextra_flag)
+# Add -lexecinfo if needed
+AC_SEARCH_LIBS([backtrace], [execinfo])
+
+# Add -ldevstat if needed
+AC_SEARCH_LIBS([devstat_checkversion], [devstat])
# Checks for features and flags.
# ----------------------------------------------------------------------
@@ -118,23 +127,11 @@ AC_ARG_WITH(proc, [AS_HELP_STRING([--with-proc=DIR], [Location of a Linux-compat
fi,
AC_DEFINE(PROCDIR, "/proc", [Path of proc filesystem]))
-if test "x$cross_compiling" = xno; then
- if test "x$enable_proc" = xyes; then
- AC_CHECK_FILE($PROCDIR/stat,,AC_MSG_ERROR(Cannot find $PROCDIR/stat. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.))
- AC_CHECK_FILE($PROCDIR/meminfo,,AC_MSG_ERROR(Cannot find $PROCDIR/meminfo. Make sure you have a Linux-compatible /proc filesystem mounted. See the file README for help.))
- fi
-fi
-
AC_ARG_ENABLE(openvz, [AS_HELP_STRING([--enable-openvz], [enable OpenVZ support])], ,enable_openvz="no")
if test "x$enable_openvz" = xyes; then
AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.])
fi
-AC_ARG_ENABLE(cgroup, [AS_HELP_STRING([--enable-cgroup], [enable cgroups support])], ,enable_cgroup="no")
-if test "x$enable_cgroup" = xyes; then
- AC_DEFINE(HAVE_CGROUP, 1, [Define if cgroup support enabled.])
-fi
-
AC_ARG_ENABLE(vserver, [AS_HELP_STRING([--enable-vserver], [enable VServer support])], ,enable_vserver="no")
if test "x$enable_vserver" = xyes; then
AC_DEFINE(HAVE_VSERVER, 1, [Define if vserver support enabled.])
@@ -146,11 +143,6 @@ if test "x$enable_ancient_vserver" = xyes; then
AC_DEFINE(HAVE_ANCIENT_VSERVER, 1, [Define if ancient vserver support enabled.])
fi
-AC_ARG_ENABLE(taskstats, [AS_HELP_STRING([--enable-taskstats], [enable per-task IO Stats (taskstats kernel sup required)])], ,enable_taskstats="yes")
-if test "x$enable_taskstats" = xyes; then
- AC_DEFINE(HAVE_TASKSTATS, 1, [Define if taskstats support enabled.])
-fi
-
# HTOP_CHECK_SCRIPT(LIBNAME, FUNCTION, DEFINE, CONFIG_SCRIPT, ELSE_PART)
m4_define([HTOP_CHECK_SCRIPT],
[
@@ -191,6 +183,23 @@ m4_define([HTOP_CHECK_LIB],
], [$4])
])
+dnl https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+ _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+ AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
+
AC_ARG_ENABLE(unicode, [AS_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="yes")
if test "x$enable_unicode" = xyes; then
HTOP_CHECK_SCRIPT([ncursesw6], [addnwstr], [HAVE_LIBNCURSESW], "ncursesw6-config",
@@ -236,32 +245,43 @@ if test "$my_htop_platform" = "solaris"; then
AC_CHECK_LIB([malloc], [free], [], [missing_libraries="$missing_libraries libmalloc"])
fi
-AC_ARG_ENABLE(linux_affinity, [AS_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_linux_affinity="yes")
-if test "x$enable_linux_affinity" = xyes -a "x$cross_compiling" = xno; then
- AC_MSG_CHECKING([for usable sched_setaffinity])
- AC_RUN_IFELSE([
- AC_LANG_PROGRAM([[
- #include <sched.h>
- #include <errno.h>
- static cpu_set_t cpuset;
- ]], [[
- CPU_ZERO(&cpuset);
- sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
- if (errno == ENOSYS) return 1;
- ]])],
- [AC_MSG_RESULT([yes])],
- [enable_linux_affinity=no
- AC_MSG_RESULT([no])])
+AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity, disables Linux affinity])],, enable_hwloc="no")
+if test "x$enable_hwloc" = xyes
+then
+ AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"])
+ AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"])
+fi
+
+AC_ARG_ENABLE(linux_affinity, [AS_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, conflicts with hwloc])], ,enable_linux_affinity="check")
+if test "x$enable_linux_affinity" = xcheck; then
+ if test "x$enable_hwloc" = xyes; then
+ enable_linux_affinity=no
+ else
+ AC_MSG_CHECKING([for usable sched_setaffinity])
+ AC_RUN_IFELSE([
+ AC_LANG_PROGRAM([[
+ #include <sched.h>
+ #include <errno.h>
+ static cpu_set_t cpuset;
+ ]], [[
+ CPU_ZERO(&cpuset);
+ sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
+ if (errno == ENOSYS) return 1;
+ ]])],
+ [enable_linux_affinity=yes
+ AC_MSG_RESULT([yes])],
+ [enable_linux_affinity=no
+ AC_MSG_RESULT([no])],
+ [AC_MSG_RESULT([yes (assumed while cross compiling)])])
+ fi
fi
if test "x$enable_linux_affinity" = xyes; then
AC_DEFINE(HAVE_LINUX_AFFINITY, 1, [Define if Linux sched_setaffinity and sched_getaffinity are to be used.])
fi
-AC_ARG_ENABLE(hwloc, [AS_HELP_STRING([--enable-hwloc], [enable hwloc support for CPU affinity])],, enable_hwloc="no")
-if test "x$enable_hwloc" = xyes
+if test "x$enable_linux_affinity" = xyes -a "x$enable_hwloc" = xyes
then
- AC_CHECK_LIB([hwloc], [hwloc_get_proc_cpubind], [], [missing_libraries="$missing_libraries libhwloc"])
- AC_CHECK_HEADERS([hwloc.h],[:], [missing_headers="$missing_headers $ac_header"])
+ AC_MSG_ERROR([--enable-hwloc and --enable-linux-affinity are mutual exclusive. Specify at most one of them.])
fi
AC_ARG_ENABLE(setuid, [AS_HELP_STRING([--enable-setuid], [enable setuid support for platforms that need it])],, enable_setuid="no")
@@ -287,12 +307,41 @@ then
])
fi
+AC_ARG_WITH(sensors, [AS_HELP_STRING([--with-sensors], [Compile with libsensors support for reading temperature data. Only requires libsensors headers at compile time, at runtime libsensors is loaded via dlopen.])],, with_sensors="check")
+if test "x$with_sensors" = xyes; then
+ AC_CHECK_HEADERS([sensors/sensors.h], [], [missing_headers="$missing_headers $ac_header"])
+elif test "x$with_sensors" = xcheck; then
+ with_sensors=yes
+ AC_CHECK_HEADERS([sensors/sensors.h], [], [with_sensors=no])
+fi
+
+AM_CFLAGS="\
+ -Wall\
+ -Wcast-align\
+ -Wcast-qual\
+ -Wextra\
+ -Wfloat-equal\
+ -Wmissing-format-attribute\
+ -Wmissing-noreturn\
+ -Wmissing-prototypes\
+ -Wpointer-arith\
+ -Wshadow\
+ -Wstrict-prototypes\
+ -Wundef\
+ -Wunused\
+ -Wwrite-strings"
+
+AX_CHECK_COMPILE_FLAG([-Wnull-dereference], [AM_CFLAGS="$AM_CFLAGS -Wnull-dereference"], , [-Werror])
+
AC_ARG_ENABLE([werror], [AS_HELP_STRING([--enable-werror], [Treat warnings as errors (default: warnings are not errors)])], [enable_werror="$enableval"], [enable_werror=no])
AS_IF([test "x$enable_werror" = "xyes"], [AM_CFLAGS="$AM_CFLAGS -Werror"])
+
AC_SUBST([AM_CFLAGS])
-AC_CHECK_PROGS(PYTHON, [python python3 python2])
-AC_SUBST(PYTHON)
+AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [Enable asserts (default: asserts are disabled)])], [enable_debug="$enableval"], [enable_debug=no])
+AS_IF([test "x$enable_debug" = "xyes"], , [AM_CPPFLAGS="$AM_CPPFLAGS -DNDEBUG"])
+
+AC_SUBST([AM_CPPFLAGS])
# Bail out on errors.
# ----------------------------------------------------------------------
@@ -303,7 +352,7 @@ if test ! -z "$missing_headers"; then
AC_MSG_ERROR([missing headers: $missing_headers])
fi
-AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2018 Hisham Muhammad", [Copyright message.])
+AC_DEFINE_UNQUOTED(COPYRIGHT, "(C) 2004-2019 Hisham Muhammad. (C) 2020 htop dev team.", [Copyright message.])
# We're done, let's go!
# ----------------------------------------------------------------------
@@ -335,16 +384,16 @@ fi
AC_MSG_RESULT([
${PACKAGE_NAME} ${VERSION}
- platform: $my_htop_platform
- proc directory: $PROCDIR
- openvz: $enable_openvz
- cgroup: $enable_cgroup
- vserver: $enable_vserver
- ancient vserver: $enable_ancient_vserver
- taskstats: $enable_taskstats
- unicode: $enable_unicode
- linux affinity: $enable_linux_affinity
- hwlock: $enable_hwloc
- setuid: $enable_setuid
- linux delay accounting: $enable_delayacct
+ platform: $my_htop_platform
+ (Linux) proc directory: $PROCDIR
+ (Linux) openvz: $enable_openvz
+ (Linux) vserver: $enable_vserver
+ (Linux) ancient vserver: $enable_ancient_vserver
+ (Linux) affinity: $enable_linux_affinity
+ (Linux) delay accounting: $enable_delayacct
+ (Linux) sensors: $with_sensors
+ unicode: $enable_unicode
+ hwloc: $enable_hwloc
+ setuid: $enable_setuid
+ debug: $enable_debug
])
diff --git a/darwin/Battery.c b/darwin/Battery.c
deleted file mode 100644
index d52a595..0000000
--- a/darwin/Battery.c
+++ /dev/null
@@ -1,74 +0,0 @@
-
-#include "BatteryMeter.h"
-
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreFoundation/CFString.h>
-#include <IOKit/ps/IOPowerSources.h>
-#include <IOKit/ps/IOPSKeys.h>
-
-void Battery_getData(double* level, ACPresence* isOnAC) {
- CFTypeRef power_sources = IOPSCopyPowerSourcesInfo();
-
- *level = -1;
- *isOnAC = AC_ERROR;
-
- if(NULL == power_sources) {
- return;
- }
-
- if(power_sources != NULL) {
- CFArrayRef list = IOPSCopyPowerSourcesList(power_sources);
- CFDictionaryRef battery = NULL;
- int len;
-
- if(NULL == list) {
- CFRelease(power_sources);
-
- return;
- }
-
- len = CFArrayGetCount(list);
-
- /* Get the battery */
- for(int i = 0; i < len && battery == NULL; ++i) {
- CFDictionaryRef candidate = IOPSGetPowerSourceDescription(power_sources,
- CFArrayGetValueAtIndex(list, i)); /* GET rule */
- CFStringRef type;
-
- if(NULL != candidate) {
- type = (CFStringRef) CFDictionaryGetValue(candidate,
- CFSTR(kIOPSTransportTypeKey)); /* GET rule */
-
- if(kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPSInternalType), 0)) {
- CFRetain(candidate);
- battery = candidate;
- }
- }
- }
-
- if(NULL != battery) {
- /* Determine the AC state */
- CFStringRef power_state = CFDictionaryGetValue(battery, CFSTR(kIOPSPowerSourceStateKey));
-
- *isOnAC = (kCFCompareEqualTo == CFStringCompare(power_state, CFSTR(kIOPSACPowerValue), 0))
- ? AC_PRESENT
- : AC_ABSENT;
-
- /* Get the percentage remaining */
- double current;
- double max;
-
- CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSCurrentCapacityKey)),
- kCFNumberDoubleType, &current);
- CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSMaxCapacityKey)),
- kCFNumberDoubleType, &max);
-
- *level = (current * 100.0) / max;
-
- CFRelease(battery);
- }
-
- CFRelease(list);
- CFRelease(power_sources);
- }
-}
diff --git a/darwin/Battery.h b/darwin/Battery.h
deleted file mode 100644
index 21a1579..0000000
--- a/darwin/Battery.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef HEADER_Battery
-#define HEADER_Battery
-
-void Battery_getData(double* level, ACPresence* isOnAC);
-
-#endif
diff --git a/darwin/DarwinCRT.c b/darwin/DarwinCRT.c
deleted file mode 100644
index 2191f30..0000000
--- a/darwin/DarwinCRT.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-htop - DarwinCRT.c
-(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "config.h"
-#include "CRT.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <execinfo.h>
-
-void CRT_handleSIGSEGV(int sgn) {
- (void) sgn;
- CRT_done();
- #ifdef __APPLE__
- fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at https://htop.dev\n");
- #ifdef HAVE_EXECINFO_H
- size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
- fprintf(stderr, "\n Please include in your report the following backtrace: \n");
- backtrace_symbols_fd(backtraceArray, size, 2);
- fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
- fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
- fprintf(stderr, "\n\n otool -tvV `which htop` > ~/htop.otool");
- fprintf(stderr, "\n\nand then attach the file ~/htop.otool to your bug report.");
- fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
- #endif
- #else
- fprintf(stderr, "\nUnfortunately, you seem to be using an unsupported platform!");
- fprintf(stderr, "\nPlease contact your platform package maintainer!\n\n");
- #endif
- abort();
-}
diff --git a/darwin/DarwinCRT.h b/darwin/DarwinCRT.h
deleted file mode 100644
index 5e08422..0000000
--- a/darwin/DarwinCRT.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef HEADER_DarwinCRT
-#define HEADER_DarwinCRT
-/*
-htop - DarwinCRT.h
-(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void CRT_handleSIGSEGV(int sgn);
-
-#endif
diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c
index 430b2b4..f3c3425 100644
--- a/darwin/DarwinProcess.c
+++ b/darwin/DarwinProcess.c
@@ -1,22 +1,23 @@
/*
htop - DarwinProcess.c
(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Process.h"
#include "DarwinProcess.h"
-#include <stdlib.h>
#include <libproc.h>
-#include <string.h>
#include <stdio.h>
-
+#include <stdlib.h>
+#include <string.h>
#include <mach/mach.h>
+#include "CRT.h"
+#include "Process.h"
+
-ProcessClass DarwinProcess_class = {
+const ProcessClass DarwinProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
@@ -26,7 +27,7 @@ ProcessClass DarwinProcess_class = {
.writeField = Process_writeField,
};
-DarwinProcess* DarwinProcess_new(Settings* settings) {
+Process* DarwinProcess_new(const Settings* settings) {
DarwinProcess* this = xCalloc(1, sizeof(DarwinProcess));
Object_setClass(this, Class(DarwinProcess));
Process_init(&this->super, settings);
@@ -35,7 +36,7 @@ DarwinProcess* DarwinProcess_new(Settings* settings) {
this->stime = 0;
this->taskAccess = true;
- return this;
+ return &this->super;
}
void Process_delete(Object* cast) {
@@ -45,20 +46,12 @@ void Process_delete(Object* cast) {
free(this);
}
-bool Process_isThread(Process* this) {
+bool Process_isThread(const Process* this) {
(void) this;
return false;
}
-void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now) {
- struct tm date;
-
- proc->starttime_ctime = ep->p_starttime.tv_sec;
- (void) localtime_r(&proc->starttime_ctime, &date);
- strftime(proc->starttime_show, 7, ((proc->starttime_ctime > now - 86400) ? "%R " : "%b%d "), &date);
-}
-
-char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset) {
+static char* DarwinProcess_getCmdLine(const struct kinfo_proc* k, int* basenameOffset) {
/* This function is from the old Mac version of htop. Originally from ps? */
int mib[3], argmax, nargs, c = 0;
size_t size;
@@ -74,7 +67,7 @@ char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset) {
}
/* Allocate space for the arguments. */
- procargs = ( char * ) xMalloc( argmax );
+ procargs = (char*)xMalloc(argmax);
if ( procargs == NULL ) {
goto ERROR_A;
}
@@ -164,12 +157,12 @@ char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset) {
/* Convert previous '\0'. */
*np = ' ';
}
- /* Note location of current '\0'. */
- np = cp;
- if (*basenameOffset == 0) {
- *basenameOffset = cp - sp;
- }
- }
+ /* Note location of current '\0'. */
+ np = cp;
+ if (*basenameOffset == 0) {
+ *basenameOffset = cp - sp;
+ }
+ }
}
/*
@@ -201,8 +194,8 @@ ERROR_A:
return retval;
}
-void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists) {
- struct extern_proc *ep = &ps->kp_proc;
+void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists) {
+ const struct extern_proc* ep = &ps->kp_proc;
/* UNSET HERE :
*
@@ -211,14 +204,14 @@ void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t
* nlwp
* percent_cpu
* percent_mem
- * m_size
+ * m_virt
* m_resident
* minflt
* majflt
*/
/* First, the "immutable" parts */
- if(!exists) {
+ if (!exists) {
/* Set the PID/PGID/etc. */
proc->pid = ep->p_pid;
proc->ppid = ps->kp_eproc.e_ppid;
@@ -231,7 +224,9 @@ void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t
/* e_tdev == -1 for "no device" */
proc->tty_nr = ps->kp_eproc.e_tdev & 0xff; /* TODO tty_nr is unsigned */
- DarwinProcess_setStartTime(proc, ep, now);
+ proc->starttime_ctime = ep->p_starttime.tv_sec;
+ Process_fillStarttimeBuffer(proc);
+
proc->comm = DarwinProcess_getCmdLine(ps, &(proc->basenameOffset));
}
@@ -245,16 +240,16 @@ void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t
proc->updated = true;
}
-void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList *dpl) {
+void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl) {
struct proc_taskinfo pti;
- if(sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) {
- if(0 != proc->utime || 0 != proc->stime) {
+ if (sizeof(pti) == proc_pidinfo(proc->super.pid, PROC_PIDTASKINFO, 0, &pti, sizeof(pti))) {
+ if (0 != proc->utime || 0 != proc->stime) {
uint64_t diff = (pti.pti_total_system - proc->stime)
- + (pti.pti_total_user - proc->utime);
+ + (pti.pti_total_user - proc->utime);
proc->super.percent_cpu = (double)diff * (double)dpl->super.cpuCount
- / ((double)dpl->global_diff * 100000.0);
+ / ((double)dpl->global_diff * 100000.0);
// fprintf(stderr, "%f %llu %llu %llu %llu %llu\n", proc->super.percent_cpu,
// proc->stime, proc->utime, pti.pti_total_system, pti.pti_total_user, dpl->global_diff);
@@ -263,11 +258,11 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList
proc->super.time = (pti.pti_total_system + pti.pti_total_user) / 10000000;
proc->super.nlwp = pti.pti_threadnum;
- proc->super.m_size = pti.pti_virtual_size / 1024 / PAGE_SIZE_KB;
- proc->super.m_resident = pti.pti_resident_size / 1024 / PAGE_SIZE_KB;
+ proc->super.m_virt = pti.pti_virtual_size / CRT_pageSize;
+ proc->super.m_resident = pti.pti_resident_size / CRT_pageSize;
proc->super.majflt = pti.pti_faults;
proc->super.percent_mem = (double)pti.pti_resident_size * 100.0
- / (double)dpl->host_info.max_mem;
+ / (double)dpl->host_info.max_mem;
proc->stime = pti.pti_total_system;
proc->utime = pti.pti_total_user;
@@ -284,7 +279,7 @@ void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
*/
-void DarwinProcess_scanThreads(DarwinProcess *dp) {
+void DarwinProcess_scanThreads(DarwinProcess* dp) {
Process* proc = (Process*) dp;
kern_return_t ret;
diff --git a/darwin/DarwinProcess.h b/darwin/DarwinProcess.h
index cf76fa8..98897c9 100644
--- a/darwin/DarwinProcess.h
+++ b/darwin/DarwinProcess.h
@@ -3,14 +3,15 @@
/*
htop - DarwinProcess.h
(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Settings.h"
+#include <sys/sysctl.h>
+
#include "DarwinProcessList.h"
+#include "Settings.h"
-#include <sys/sysctl.h>
typedef struct DarwinProcess_ {
Process super;
@@ -20,27 +21,23 @@ typedef struct DarwinProcess_ {
bool taskAccess;
} DarwinProcess;
-extern ProcessClass DarwinProcess_class;
+extern const ProcessClass DarwinProcess_class;
-DarwinProcess* DarwinProcess_new(Settings* settings);
+Process* DarwinProcess_new(const Settings* settings);
void Process_delete(Object* cast);
-bool Process_isThread(Process* this);
-
-void DarwinProcess_setStartTime(Process *proc, struct extern_proc *ep, time_t now);
-
-char *DarwinProcess_getCmdLine(struct kinfo_proc* k, int* basenameOffset);
+bool Process_isThread(const Process* this);
-void DarwinProcess_setFromKInfoProc(Process *proc, struct kinfo_proc *ps, time_t now, bool exists);
+void DarwinProcess_setFromKInfoProc(Process* proc, const struct kinfo_proc* ps, bool exists);
-void DarwinProcess_setFromLibprocPidinfo(DarwinProcess *proc, DarwinProcessList *dpl);
+void DarwinProcess_setFromLibprocPidinfo(DarwinProcess* proc, DarwinProcessList* dpl);
/*
* Scan threads for process state information.
* Based on: http://stackoverflow.com/questions/6788274/ios-mac-cpu-usage-for-thread
* and https://github.com/max-horvath/htop-osx/blob/e86692e869e30b0bc7264b3675d2a4014866ef46/ProcessList.c
*/
-void DarwinProcess_scanThreads(DarwinProcess *dp);
+void DarwinProcess_scanThreads(DarwinProcess* dp);
#endif
diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c
index 39d99c1..ae1efb1 100644
--- a/darwin/DarwinProcessList.c
+++ b/darwin/DarwinProcessList.c
@@ -1,33 +1,36 @@
/*
htop - DarwinProcessList.c
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "ProcessList.h"
-#include "DarwinProcess.h"
#include "DarwinProcessList.h"
-#include "CRT.h"
-#include "zfs/ZfsArcStats.h"
-#include "zfs/openzfs_sysctl.h"
+#include <err.h>
+#include <errno.h>
+#include <libproc.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include <stdio.h>
-#include <libproc.h>
-#include <sys/mman.h>
#include <utmpx.h>
-#include <err.h>
+#include <sys/mman.h>
#include <sys/sysctl.h>
-#include <stdbool.h>
+
+#include "CRT.h"
+#include "DarwinProcess.h"
+#include "ProcessList.h"
+#include "zfs/openzfs_sysctl.h"
+#include "zfs/ZfsArcStats.h"
+
struct kern {
- short int version[3];
+ short int version[3];
};
-void GetKernelVersion(struct kern *k) {
+static void GetKernelVersion(struct kern* k) {
static short int version_[3] = {0};
if (!version_[0]) {
// just in case it fails someday
@@ -35,9 +38,11 @@ void GetKernelVersion(struct kern *k) {
char str[256] = {0};
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
- if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
- }
- memcpy(k->version, version_, sizeof(version_));
+ if (ret == 0) {
+ sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
+ }
+ }
+ memcpy(k->version, version_, sizeof(version_));
}
/* compare the given os version with the one installed returns:
@@ -45,77 +50,85 @@ void GetKernelVersion(struct kern *k) {
positive value if less than the installed version
negative value if more than the installed version
*/
-int CompareKernelVersion(short int major, short int minor, short int component) {
- struct kern k;
- GetKernelVersion(&k);
- if ( k.version[0] != major) return k.version[0] - major;
- if ( k.version[1] != minor) return k.version[1] - minor;
- if ( k.version[2] != component) return k.version[2] - component;
- return 0;
+static int CompareKernelVersion(short int major, short int minor, short int component) {
+ struct kern k;
+ GetKernelVersion(&k);
+
+ if (k.version[0] != major) {
+ return k.version[0] - major;
+ }
+ if (k.version[1] != minor) {
+ return k.version[1] - minor;
+ }
+ if (k.version[2] != component) {
+ return k.version[2] - component;
+ }
+
+ return 0;
}
-void ProcessList_getHostInfo(host_basic_info_data_t *p) {
+static void ProcessList_getHostInfo(host_basic_info_data_t* p) {
mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;
- if(0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
- CRT_fatalError("Unable to retrieve host info\n");
+ if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
+ CRT_fatalError("Unable to retrieve host info\n");
}
}
-void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) {
- if(NULL != p && NULL != *p) {
- if(0 != munmap(*p, vm_page_size)) {
- CRT_fatalError("Unable to free old CPU load information\n");
- }
- *p = NULL;
+static void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t* p) {
+ if (NULL != p && NULL != *p) {
+ if (0 != munmap(*p, vm_page_size)) {
+ CRT_fatalError("Unable to free old CPU load information\n");
+ }
+ *p = NULL;
}
}
-unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
+static unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t* p) {
mach_msg_type_number_t info_size = sizeof(processor_cpu_load_info_t);
unsigned cpu_count;
// TODO Improving the accuracy of the load counts woule help a lot.
- if(0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) {
- CRT_fatalError("Unable to retrieve CPU info\n");
+ if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t*)p, &info_size)) {
+ CRT_fatalError("Unable to retrieve CPU info\n");
}
return cpu_count;
}
-void ProcessList_getVMStats(vm_statistics_t p) {
- mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
+static void ProcessList_getVMStats(vm_statistics_t p) {
+ mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
- if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0)
- CRT_fatalError("Unable to retrieve VM statistics\n");
+ if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0) {
+ CRT_fatalError("Unable to retrieve VM statistics\n");
+ }
}
-struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) {
+static struct kinfo_proc* ProcessList_getKInfoProcs(size_t* count) {
int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
- struct kinfo_proc *processes = NULL;
+ struct kinfo_proc* processes = NULL;
- /* Note the two calls to sysctl(). One to get length and one to get the
- * data. This -does- mean that the second call could end up with a missing
- * process entry or two.
- */
- *count = 0;
- if (sysctl(mib, 4, NULL, count, NULL, 0) < 0)
- CRT_fatalError("Unable to get size of kproc_infos");
+ for (int retry = 3; retry > 0; retry--) {
+ size_t size = 0;
+ if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0 || size == 0) {
+ CRT_fatalError("Unable to get size of kproc_infos");
+ }
- processes = xMalloc(*count);
- if (processes == NULL)
- CRT_fatalError("Out of memory for kproc_infos");
+ processes = xRealloc(processes, size);
- if (sysctl(mib, 4, processes, count, NULL, 0) < 0)
- CRT_fatalError("Unable to get kinfo_procs");
+ if (sysctl(mib, 4, processes, &size, NULL, 0) == 0) {
+ *count = size / sizeof(struct kinfo_proc);
+ return processes;
+ }
- *count = *count / sizeof(struct kinfo_proc);
+ if (errno != ENOMEM)
+ break;
+ }
- return processes;
+ CRT_fatalError("Unable to get kinfo_procs");
}
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
- size_t len;
DarwinProcessList* this = xCalloc(1, sizeof(DarwinProcessList));
ProcessList_init(&this->super, Class(Process), usersTable, pidMatchList, userId);
@@ -145,67 +158,69 @@ void ProcessList_delete(ProcessList* this) {
free(this);
}
-void ProcessList_goThroughEntries(ProcessList* super) {
- DarwinProcessList *dpl = (DarwinProcessList *)super;
- bool preExisting = true;
- struct kinfo_proc *ps;
- size_t count;
- DarwinProcess *proc;
- struct timeval tv;
-
- gettimeofday(&tv, NULL); /* Start processing time */
-
- /* Update the global data (CPU times and VM stats) */
- ProcessList_freeCPULoadInfo(&dpl->prev_load);
- dpl->prev_load = dpl->curr_load;
- ProcessList_allocateCPULoadInfo(&dpl->curr_load);
- ProcessList_getVMStats(&dpl->vm_stats);
- openzfs_sysctl_updateArcStats(&dpl->zfs);
-
- /* Get the time difference */
- dpl->global_diff = 0;
- for(int i = 0; i < dpl->super.cpuCount; ++i) {
- for(size_t j = 0; j < CPU_STATE_MAX; ++j) {
- dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j];
- }
- }
-
- /* Clear the thread counts */
- super->kernelThreads = 0;
- super->userlandThreads = 0;
- super->totalTasks = 0;
- super->runningTasks = 0;
-
- /* We use kinfo_procs for initial data since :
- *
- * 1) They always succeed.
- * 2) The contain the basic information.
- *
- * We attempt to fill-in additional information with libproc.
- */
- ps = ProcessList_getKInfoProcs(&count);
-
- for(size_t i = 0; i < count; ++i) {
- proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new);
-
- DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
- DarwinProcess_setFromLibprocPidinfo(proc, dpl);
-
- // Disabled for High Sierra due to bug in macOS High Sierra
- bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);
-
- if (isScanThreadSupported){
- DarwinProcess_scanThreads(proc);
- }
-
- super->totalTasks += 1;
-
- if(!preExisting) {
- proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);
-
- ProcessList_add(super, &proc->super);
- }
- }
-
- free(ps);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
+ DarwinProcessList* dpl = (DarwinProcessList*)super;
+ bool preExisting = true;
+ struct kinfo_proc* ps;
+ size_t count;
+ DarwinProcess* proc;
+
+ /* Update the global data (CPU times and VM stats) */
+ ProcessList_freeCPULoadInfo(&dpl->prev_load);
+ dpl->prev_load = dpl->curr_load;
+ ProcessList_allocateCPULoadInfo(&dpl->curr_load);
+ ProcessList_getVMStats(&dpl->vm_stats);
+ openzfs_sysctl_updateArcStats(&dpl->zfs);
+
+ // in pause mode only gather global data for meters (CPU/memory/...)
+ if (pauseProcessUpdate) {
+ return;
+ }
+
+ /* Get the time difference */
+ dpl->global_diff = 0;
+ for (int i = 0; i < dpl->super.cpuCount; ++i) {
+ for (size_t j = 0; j < CPU_STATE_MAX; ++j) {
+ dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j];
+ }
+ }
+
+ /* Clear the thread counts */
+ super->kernelThreads = 0;
+ super->userlandThreads = 0;
+ super->totalTasks = 0;
+ super->runningTasks = 0;
+
+ /* We use kinfo_procs for initial data since :
+ *
+ * 1) They always succeed.
+ * 2) The contain the basic information.
+ *
+ * We attempt to fill-in additional information with libproc.
+ */
+ ps = ProcessList_getKInfoProcs(&count);
+
+ for (size_t i = 0; i < count; ++i) {
+ proc = (DarwinProcess*)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, DarwinProcess_new);
+
+ DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], preExisting);
+ DarwinProcess_setFromLibprocPidinfo(proc, dpl);
+
+ // Disabled for High Sierra due to bug in macOS High Sierra
+ bool isScanThreadSupported = ! ( CompareKernelVersion(17, 0, 0) >= 0 && CompareKernelVersion(17, 5, 0) < 0);
+
+ if (isScanThreadSupported) {
+ DarwinProcess_scanThreads(proc);
+ }
+
+ super->totalTasks += 1;
+
+ if (!preExisting) {
+ proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);
+
+ ProcessList_add(super, &proc->super);
+ }
+ }
+
+ free(ps);
}
diff --git a/darwin/DarwinProcessList.h b/darwin/DarwinProcessList.h
index c6b2966..1ae2f2b 100644
--- a/darwin/DarwinProcessList.h
+++ b/darwin/DarwinProcessList.h
@@ -3,25 +3,16 @@
/*
htop - DarwinProcessList.h
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-struct kern;
-
-void GetKernelVersion(struct kern *k);
-
-/* compare the given os version with the one installed returns:
-0 if equals the installed version
-positive value if less than the installed version
-negative value if more than the installed version
-*/
-int CompareKernelVersion(short int major, short int minor, short int component);
+#include <mach/mach_host.h>
+#include <sys/sysctl.h>
#include "ProcessList.h"
#include "zfs/ZfsArcStats.h"
-#include <mach/mach_host.h>
-#include <sys/sysctl.h>
+
typedef struct DarwinProcessList_ {
ProcessList super;
@@ -37,20 +28,10 @@ typedef struct DarwinProcessList_ {
ZfsArcStats zfs;
} DarwinProcessList;
-void ProcessList_getHostInfo(host_basic_info_data_t *p);
-
-void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p);
-
-unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p);
-
-void ProcessList_getVMStats(vm_statistics_t p);
-
-struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count);
-
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
void ProcessList_delete(ProcessList* this);
-void ProcessList_goThroughEntries(ProcessList* super);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif
diff --git a/darwin/Platform.c b/darwin/Platform.c
index a8ca45b..e24b67b 100644
--- a/darwin/Platform.c
+++ b/darwin/Platform.c
@@ -2,27 +2,36 @@
htop - darwin/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
+#include "Macros.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
+#include "DateMeter.h"
+#include "DateTimeMeter.h"
#include "HostnameMeter.h"
+#include "ProcessLocksScreen.h"
#include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
#include "DarwinProcessList.h"
+#include <math.h>
#include <stdlib.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CoreFoundation/CFString.h>
+#include <IOKit/ps/IOPowerSources.h>
+#include <IOKit/ps/IOPSKeys.h>
-ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
+ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
@@ -60,7 +69,7 @@ const SignalItem Platform_signals[] = {
{ .name = "31 SIGUSR2", .number = 31 },
};
-const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
+const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
@@ -79,7 +88,7 @@ ProcessFieldData Process_fields[] = {
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
- [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
+ [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
@@ -91,9 +100,11 @@ ProcessFieldData Process_fields[] = {
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
-MeterClass* Platform_meterTypes[] = {
+const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
+ &DateMeter_class,
+ &DateTimeMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
@@ -104,22 +115,37 @@ MeterClass* Platform_meterTypes[] = {
&UptimeMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
+ &AllCPUs4Meter_class,
+ &AllCPUs8Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
+ &LeftCPUs4Meter_class,
+ &RightCPUs4Meter_class,
+ &LeftCPUs8Meter_class,
+ &RightCPUs8Meter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
&BlankMeter_class,
NULL
};
+int Platform_numberOfFields = 100;
+
+void Platform_init(void) {
+ /* no platform-specific setup needed */
+}
+
+void Platform_done(void) {
+ /* no platform-specific cleanup needed */
+}
+
void Platform_setBindings(Htop_Action* keys) {
+ /* no platform-specific key bindings */
(void) keys;
}
-int Platform_numberOfFields = 100;
-
int Platform_getUptime() {
struct timeval bootTime, currTime;
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
@@ -137,7 +163,7 @@ int Platform_getUptime() {
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
double results[3];
- if(3 == getloadavg(results, 3)) {
+ if (3 == getloadavg(results, 3)) {
*one = results[0];
*five = results[1];
*fifteen = results[2];
@@ -164,8 +190,8 @@ ProcessPidColumn Process_pidColumns[] = {
};
static double Platform_setCPUAverageValues(Meter* mtr) {
- DarwinProcessList *dpl = (DarwinProcessList *)mtr->pl;
- int cpus = dpl->super.cpuCount;
+ const ProcessList* dpl = mtr->pl;
+ int cpus = dpl->cpuCount;
double sumNice = 0.0;
double sumNormal = 0.0;
double sumKernel = 0.0;
@@ -188,36 +214,37 @@ double Platform_setCPUValues(Meter* mtr, int cpu) {
return Platform_setCPUAverageValues(mtr);
}
- DarwinProcessList *dpl = (DarwinProcessList *)mtr->pl;
- processor_cpu_load_info_t prev = &dpl->prev_load[cpu-1];
- processor_cpu_load_info_t curr = &dpl->curr_load[cpu-1];
+ const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->pl;
+ const processor_cpu_load_info_t prev = &dpl->prev_load[cpu - 1];
+ const processor_cpu_load_info_t curr = &dpl->curr_load[cpu - 1];
double total = 0;
/* Take the sums */
- for(size_t i = 0; i < CPU_STATE_MAX; ++i) {
+ for (size_t i = 0; i < CPU_STATE_MAX; ++i) {
total += (double)curr->cpu_ticks[i] - (double)prev->cpu_ticks[i];
}
mtr->values[CPU_METER_NICE]
- = ((double)curr->cpu_ticks[CPU_STATE_NICE] - (double)prev->cpu_ticks[CPU_STATE_NICE])* 100.0 / total;
+ = ((double)curr->cpu_ticks[CPU_STATE_NICE] - (double)prev->cpu_ticks[CPU_STATE_NICE]) * 100.0 / total;
mtr->values[CPU_METER_NORMAL]
- = ((double)curr->cpu_ticks[CPU_STATE_USER] - (double)prev->cpu_ticks[CPU_STATE_USER])* 100.0 / total;
+ = ((double)curr->cpu_ticks[CPU_STATE_USER] - (double)prev->cpu_ticks[CPU_STATE_USER]) * 100.0 / total;
mtr->values[CPU_METER_KERNEL]
- = ((double)curr->cpu_ticks[CPU_STATE_SYSTEM] - (double)prev->cpu_ticks[CPU_STATE_SYSTEM])* 100.0 / total;
+ = ((double)curr->cpu_ticks[CPU_STATE_SYSTEM] - (double)prev->cpu_ticks[CPU_STATE_SYSTEM]) * 100.0 / total;
- Meter_setItems(mtr, 3);
+ mtr->curItems = 3;
/* Convert to percent and return */
total = mtr->values[CPU_METER_NICE] + mtr->values[CPU_METER_NORMAL] + mtr->values[CPU_METER_KERNEL];
- mtr->values[CPU_METER_FREQUENCY] = -1;
+ mtr->values[CPU_METER_FREQUENCY] = NAN;
+ mtr->values[CPU_METER_TEMPERATURE] = NAN;
return CLAMP(total, 0.0, 100.0);
}
void Platform_setMemoryValues(Meter* mtr) {
- DarwinProcessList *dpl = (DarwinProcessList *)mtr->pl;
- vm_statistics_t vm = &dpl->vm_stats;
+ const DarwinProcessList* dpl = (const DarwinProcessList*)mtr->pl;
+ const struct vm_statistics* vm = &dpl->vm_stats;
double page_K = (double)vm_page_size / (double)1024;
mtr->total = dpl->host_info.max_mem / 1024;
@@ -227,23 +254,23 @@ void Platform_setMemoryValues(Meter* mtr) {
}
void Platform_setSwapValues(Meter* mtr) {
- int mib[2] = {CTL_VM, VM_SWAPUSAGE};
- struct xsw_usage swapused;
- size_t swlen = sizeof(swapused);
- sysctl(mib, 2, &swapused, &swlen, NULL, 0);
+ int mib[2] = {CTL_VM, VM_SWAPUSAGE};
+ struct xsw_usage swapused;
+ size_t swlen = sizeof(swapused);
+ sysctl(mib, 2, &swapused, &swlen, NULL, 0);
- mtr->total = swapused.xsu_total / 1024;
- mtr->values[0] = swapused.xsu_used / 1024;
+ mtr->total = swapused.xsu_total / 1024;
+ mtr->values[0] = swapused.xsu_used / 1024;
}
void Platform_setZfsArcValues(Meter* this) {
- DarwinProcessList* dpl = (DarwinProcessList*) this->pl;
+ const DarwinProcessList* dpl = (const DarwinProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(dpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
- DarwinProcessList* dpl = (DarwinProcessList*) this->pl;
+ const DarwinProcessList* dpl = (const DarwinProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(dpl->zfs));
}
@@ -263,33 +290,33 @@ char* Platform_getProcessEnv(pid_t pid) {
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = pid;
- size_t bufsz = argmax;
+ bufsz = argmax;
if (sysctl(mib, 3, buf, &bufsz, 0, 0) == 0) {
if (bufsz > sizeof(int)) {
char *p = buf, *endp = buf + bufsz;
- int argc = *(int*)p;
+ int argc = *(int*)(void*)p;
p += sizeof(int);
// skip exe
- p = strchr(p, 0)+1;
+ p = strchr(p, 0) + 1;
// skip padding
- while(!*p && p < endp)
+ while (!*p && p < endp)
++p;
// skip argv
- for (; argc-- && p < endp; p = strrchr(p, 0)+1)
+ for (; argc-- && p < endp; p = strrchr(p, 0) + 1)
;
// skip padding
- while(!*p && p < endp)
+ while (!*p && p < endp)
++p;
size_t size = endp - p;
- env = xMalloc(size+2);
+ env = xMalloc(size + 2);
memcpy(env, p, size);
env[size] = 0;
- env[size+1] = 0;
+ env[size + 1] = 0;
}
}
free(buf);
@@ -298,3 +325,96 @@ char* Platform_getProcessEnv(pid_t pid) {
return env;
}
+
+char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
+ (void)pid;
+ (void)inode;
+ return NULL;
+}
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
+ (void)pid;
+ return NULL;
+}
+
+bool Platform_getDiskIO(DiskIOData* data) {
+ // TODO
+ (void)data;
+ return false;
+}
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted) {
+ // TODO
+ *bytesReceived = 0;
+ *packetsReceived = 0;
+ *bytesTransmitted = 0;
+ *packetsTransmitted = 0;
+ return false;
+}
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC) {
+ CFTypeRef power_sources = IOPSCopyPowerSourcesInfo();
+
+ *percent = NAN;
+ *isOnAC = AC_ERROR;
+
+ if (NULL == power_sources)
+ return;
+
+ CFArrayRef list = IOPSCopyPowerSourcesList(power_sources);
+ CFDictionaryRef battery = NULL;
+ int len;
+
+ if (NULL == list) {
+ CFRelease(power_sources);
+
+ return;
+ }
+
+ len = CFArrayGetCount(list);
+
+ /* Get the battery */
+ for (int i = 0; i < len && battery == NULL; ++i) {
+ CFDictionaryRef candidate = IOPSGetPowerSourceDescription(power_sources,
+ CFArrayGetValueAtIndex(list, i)); /* GET rule */
+ CFStringRef type;
+
+ if (NULL != candidate) {
+ type = (CFStringRef) CFDictionaryGetValue(candidate,
+ CFSTR(kIOPSTransportTypeKey)); /* GET rule */
+
+ if (kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPSInternalType), 0)) {
+ CFRetain(candidate);
+ battery = candidate;
+ }
+ }
+ }
+
+ if (NULL != battery) {
+ /* Determine the AC state */
+ CFStringRef power_state = CFDictionaryGetValue(battery, CFSTR(kIOPSPowerSourceStateKey));
+
+ *isOnAC = (kCFCompareEqualTo == CFStringCompare(power_state, CFSTR(kIOPSACPowerValue), 0))
+ ? AC_PRESENT
+ : AC_ABSENT;
+
+ /* Get the percentage remaining */
+ double current;
+ double max;
+
+ CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSCurrentCapacityKey)),
+ kCFNumberDoubleType, &current);
+ CFNumberGetValue(CFDictionaryGetValue(battery, CFSTR(kIOPSMaxCapacityKey)),
+ kCFNumberDoubleType, &max);
+
+ *percent = (current * 100.0) / max;
+
+ CFRelease(battery);
+ }
+
+ CFRelease(list);
+ CFRelease(power_sources);
+}
diff --git a/darwin/Platform.h b/darwin/Platform.h
index 7dd4ae6..e1f8355 100644
--- a/darwin/Platform.h
+++ b/darwin/Platform.h
@@ -4,35 +4,44 @@
htop - darwin/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+#include <sys/types.h>
+
#include "Action.h"
-#include "SignalsPanel.h"
-#include "CPUMeter.h"
#include "BatteryMeter.h"
+#include "CPUMeter.h"
#include "DarwinProcess.h"
+#include "DiskIOMeter.h"
+#include "ProcessLocksScreen.h"
+#include "SignalsPanel.h"
+
+extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[];
+extern int Platform_numberOfFields;
+
extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
-extern ProcessFieldData Process_fields[];
+extern const MeterClass* const Platform_meterTypes[];
-extern MeterClass* Platform_meterTypes[];
+void Platform_init(void);
-void Platform_setBindings(Htop_Action* keys);
+void Platform_done(void);
-extern int Platform_numberOfFields;
+void Platform_setBindings(Htop_Action* keys);
-int Platform_getUptime();
+int Platform_getUptime(void);
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
-int Platform_getMaxPid();
+int Platform_getMaxPid(void);
extern ProcessPidColumn Process_pidColumns[];
@@ -48,4 +57,17 @@ void Platform_setZfsCompressedArcValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
+char* Platform_getInodeFilename(pid_t pid, ino_t inode);
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
+
+bool Platform_getDiskIO(DiskIOData* data);
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted);
+
+void Platform_getBattery(double *percent, ACPresence *isOnAC);
+
#endif
diff --git a/docs/images/screenshot.png b/docs/images/screenshot.png
new file mode 100644
index 0000000..0ff3cfe
--- /dev/null
+++ b/docs/images/screenshot.png
Binary files differ
diff --git a/docs/styleguide.md b/docs/styleguide.md
new file mode 100644
index 0000000..a0856c4
--- /dev/null
+++ b/docs/styleguide.md
@@ -0,0 +1,224 @@
+htop coding style guide
+=======================
+
+Naming conventions
+------------------
+
+Names are important to convey what all those things inside the project are for.
+Filenames for source code traditionally used camel-case naming with the first letter written in uppercase.
+The file extension is always lowercase.
+
+The only exception here is `htop.c` which is the main entrance point into the code.
+
+Folders for e.g. platform-specific code or complex features spawning multiple files are written in lowercase, e.g. `linux`, `freebsd`, `zfs`.
+
+Inside files, the naming somewhat depends on the context.
+For functions names should include a camel-case prefix before the actual name, separated by an underscore.
+While this prefix usually coincides with the module name, this is not required, yet strongly advised.
+One important exception to this rule are the memory management and the string utility functions from `XUtils.h`.
+
+Variable names inside functions should be short and precise.
+Using `i` for some loop counter is totally fine, using `someCounterValueForThisSimpleLoop` is not.
+On the other hand, when you need to hold global storage try to keep this local to your module, i.e. declare such variables `static` within the C source file.
+Only if your variable really needs to be visible for the whole project (which is really rare) it deserves a declaration in the header, marked `extern`.
+
+File content structure
+----------------------
+
+The content within each file is usually structured according to the following loose template:
+
+* Copyright declaration
+* Inclusion of used headers
+* Necessary data structures and forward declarations
+* Static module-private function implementations
+* Externally visible function implementations
+* Externally visible constant structures (pseudo-OOP definitions)
+
+For header files header guards based on `#ifdef` should be used.
+These header guards are historically placed **before** the Copyright declaration.
+Stick to that for consistency please.
+
+Example:
+
+```c
+#ifndef HEADER_FILENAME
+#define HEADER_FILENAME
+/*
+htop - Filename.h
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+```
+
+Import and use of headers
+-------------------------
+
+Every file should import headers for all symbols it's using.
+Thus when using a symbol from a header, even if that symbol is already imported by something else you use, you should declare an import for that header.
+Doing so allows for easier restructuring of the code when things need to be moved around.
+If you are unsure if all necessary headers are included you might use IWYU to tell you.
+
+The list of includes should be the first thing in the file, after the copyright comment and be followed by two blank lines.
+The include list should be in the following order, with each group separated by one blank line:
+
+1. `include "config.h" // IWYU pragma: keep` if the global configuration
+ from automake&autoconfigure or any of the feature guards for C library headers
+ (like `__GNU_SOURCE`) are required, optional otherwise. Beware of the IWYU comment.
+2. Accompanying module header file (for C source files only, missing inside headers)
+3. List of used system headers (non-conditional includes)
+4. List of used program headers
+5. Conditionally included header files, system headers first
+
+The list of headers should be sorted with includes from subdirectories following after files inside their parent directory.
+Thus `unistd.h` sorts before `sys/time.h`.
+
+Symbol Exports
+--------------
+
+Exports of symbols should be used sparingly.
+Thus unless a function you write is intended to become public API of a module you should mark it as `static`.
+If a function should be public API an appropriate declaration for that function has to be placed in the accompanying header file.
+
+Please avoid function-like macros, in particular when exporting them in a header file.
+They have several downsides (re-evaluation of arguments, syntactic escapes, weak typing) for which usually a better alternative like an actual function exists.
+Furthermore when using function-like `define`s you may need to mark certain headers for IWYU so tracking of used symbols works.
+
+Memory Management
+-----------------
+
+When allocating memory make sure to free resources properly.
+For allocation this project uses a set of tiny wrappers around the common functions `malloc`, `calloc` and `realloc` named `xMalloc`, `xCalloc` and `xRealloc`.
+These functions check that memory allocation worked and error out on failure.
+
+Allocation functions assert the amount of memory requested is non-zero.
+Trying to allocate 0 bytes of memory is an error.
+Please use the explicit value `NULL` in this case and handle it in your code accordingly.
+
+Working with Strings
+--------------------
+
+It is strongly encouraged to use the functions starting with `String_` from `XUtils.h` for working with zero-terminated strings as these make the API easier to use and are intended to make the intent of your code easier to grasp.
+
+Thus instead of `!strcmp(foo, "foo")` it's preferred to use `String_eq(foo, "foo")` instead.
+While sometimes a bit more to type, this helps a lot with making the code easier to follow.
+
+Styling the code
+----------------
+
+Now for the style details that can mostly be automated: Indentation, spacing and bracing.
+While there is no definitive code style we use, a set of rules loosely enforced has evolved.
+
+Indentation in the code is done by three (3) spaces. No tabs are used. Ever.
+
+Before and after keywords should be a space, e.g. `if (condition)` and `do { … } while (condition);`.
+
+After opening and before closing braces a new line should be started.
+Content of such encoded blocks should be indented one level further than their enclosing block.
+
+If a line of source code becomes too long, or when structuring it into multiple parts for clarity, the continuation line should be indented one more level than the first line it continues:
+
+```c
+if (very_long_condition &&
+ another_very_complex_expression &&
+ something_else_to_check) {
+ // Code follows as normal ...
+} else {
+
+}
+```
+
+Braces around simple single code statements (return, break, continue, goto, trivial assignments) are usually left out.
+
+```c
+if (answer)
+ return 42;
+```
+
+If it helps readability (with several unrelated if statements in a row) or to avoid dangling-else situations braces can be added.
+
+Control flow statements and the instruction making up their body should not be put on a single line,
+i.e. after the condition of an if statement a new line should be inserted and the body indented accordingly.
+
+```c
+if (answer)
+ return 42;
+else if (again)
+ continue;
+else
+ break;
+```
+
+When the statements that form control flow constructs are complex (e.g. more than just a simple assignment or jump) or need explanatory comments you should use braces.
+If any block of such a statement uses braces then all blocks of that statement must have braces too.
+
+```c
+if ((fd = open(filename, O_RDONLY)) >= 0 &&
+ (amtRead = read(buffer, sizeof(buffer))) > 0) {
+ // Parse the information further ...
+ metric = handleBufferContent(buffer, amtRead);
+} else {
+ metric = -1;
+}
+
+if (fd >= 0)
+ close(fd);
+```
+
+While the existing code base isn't fully consistent with this code style yet it is strongly recommended that new code follows these rules.
+Adapting surrounding code near places you need to touch is encouraged.
+Try to separate such changes into a single, clean-up only commit to reduce noise while reviewing your changes.
+
+When writing your code consistency with the surrounding codebase is favoured.
+
+Don't shy away from leaving (single) blank lines to separate different groups of related statements.
+They can be a great asset to structure the flow of a method.
+
+```c
+ int stuff = 0;
+
+ // If asked for gives only half the answer ...
+ if (param)
+ stuff = 21;
+
+ // Compute the answer
+ stuff %= 2;
+ stuff *= 4;
+ stuff *= 5;
+ stuff += !!stuff;
+ stuff *= 2;
+
+ return stuff;
+```
+
+If you want to automate formatting your code, the following command gives you a good baseline of how it should look:
+
+```bash
+astyle -r -xb -s3 -p -xg -c -k1 -W1 \*.c \*.h
+```
+
+Working with System APIs
+------------------------
+
+Please try to be considerate when using modern platform features.
+While they usually provide quite a performance gain or make your life easier, it is beneficial if `htop` runs on rather ancient systems.
+Thus when you want to use such features you should try to have an alternative available that works as a fallback.
+
+An example for this are functions like `fstatat` on Linux that extend the kernel API on modern systems.
+But even though it has been around for over a decade you are asked to provide a POSIX alternative like emulating such calls by `fstat` if this is doable.
+If an alternative can not be provided you should gracefully downgrade. That could make a feature that requires this shiny API unavailable on systems that lack support for that API. Make this case visually clear to the user.
+
+In general, code written for the project should be able to compile on any C99-compliant compiler.
+
+Writing documentation
+---------------------
+
+The primary user documentation should be the man file which you can find in `htop.1.in`.
+
+Additional documentation, like this file, should be written in gh-style markdown.
+Make each sentence one line.
+Markdown will combined these in output formats.
+It does only insert a paragraph if you insert a blank line into the source file.
+This way git can better diff and present the changes when documentation is altered.
+
+Documentation files reside in the `docs/` directory and have a `.md` extension.
diff --git a/dragonflybsd/Battery.c b/dragonflybsd/Battery.c
deleted file mode 100644
index f17efb5..0000000
--- a/dragonflybsd/Battery.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-htop - dragonflybsd/Battery.c
-(C) 2015 Hisham H. Muhammad
-(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "BatteryMeter.h"
-#include <sys/sysctl.h>
-
-void Battery_getData(double* level, ACPresence* isOnAC) {
- int life;
- size_t life_len = sizeof(life);
- if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
- *level = -1;
- else
- *level = life;
-
- int acline;
- size_t acline_len = sizeof(acline);
- if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1)
- *isOnAC = AC_ERROR;
- else
- *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT;
-}
diff --git a/dragonflybsd/Battery.h b/dragonflybsd/Battery.h
deleted file mode 100644
index f764a2d..0000000
--- a/dragonflybsd/Battery.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef HEADER_Battery
-#define HEADER_Battery
-/*
-htop - dragonflybsd/Battery.h
-(C) 2015 Hisham H. Muhammad
-(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void Battery_getData(double* level, ACPresence* isOnAC);
-
-#endif
diff --git a/dragonflybsd/DragonFlyBSDCRT.c b/dragonflybsd/DragonFlyBSDCRT.c
deleted file mode 100644
index a092083..0000000
--- a/dragonflybsd/DragonFlyBSDCRT.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-htop - dragonflybsd/DragonFlyBSDCRT.c
-(C) 2014 Hisham H. Muhammad
-(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "config.h"
-#include "CRT.h"
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef HAVE_EXECINFO_H
-#include <execinfo.h>
-#endif
-
-void CRT_handleSIGSEGV(int sgn) {
- (void) sgn;
- CRT_done();
- fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at https://htop.dev\n");
- #ifdef HAVE_EXECINFO_H
- size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
- fprintf(stderr, "\n Please include in your report the following backtrace: \n");
- backtrace_symbols_fd(backtraceArray, size, 2);
- fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
- fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
- fprintf(stderr, "\n\n objdump -d `which htop` > ~/htop.objdump");
- fprintf(stderr, "\n\nand then attach the file ~/htop.objdump to your bug report.");
- fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
- #else
- fprintf(stderr, "\nPlease contact your DragonFlyBSD package maintainer!\n\n");
- #endif
- abort();
-}
diff --git a/dragonflybsd/DragonFlyBSDCRT.h b/dragonflybsd/DragonFlyBSDCRT.h
deleted file mode 100644
index 2bf85f7..0000000
--- a/dragonflybsd/DragonFlyBSDCRT.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef HEADER_DragonFlyBSDCRT
-#define HEADER_DragonFlyBSDCRT
-/*
-htop - dragonflybsd/DragonFlyBSDCRT.h
-(C) 2014 Hisham H. Muhammad
-(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void CRT_handleSIGSEGV(int sgn);
-
-#endif
diff --git a/dragonflybsd/DragonFlyBSDProcess.c b/dragonflybsd/DragonFlyBSDProcess.c
index 770143b..9311f03 100644
--- a/dragonflybsd/DragonFlyBSDProcess.c
+++ b/dragonflybsd/DragonFlyBSDProcess.c
@@ -2,7 +2,7 @@
htop - dragonflybsd/DragonFlyBSDProcess.c
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -18,14 +18,14 @@ in the source distribution for its full text.
#include <sys/syscall.h>
-ProcessClass DragonFlyBSDProcess_class = {
+const ProcessClass DragonFlyBSDProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = DragonFlyBSDProcess_compare
},
- .writeField = (Process_WriteField) DragonFlyBSDProcess_writeField,
+ .writeField = DragonFlyBSDProcess_writeField,
};
ProcessFieldData Process_fields[] = {
@@ -44,10 +44,11 @@ ProcessFieldData Process_fields[] = {
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
- [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
+ [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
+ [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
@@ -69,11 +70,11 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = 0, .label = NULL },
};
-DragonFlyBSDProcess* DragonFlyBSDProcess_new(Settings* settings) {
+Process* DragonFlyBSDProcess_new(const Settings* settings) {
DragonFlyBSDProcess* this = xCalloc(1, sizeof(DragonFlyBSDProcess));
Object_setClass(this, Class(DragonFlyBSDProcess));
Process_init(&this->super, settings);
- return this;
+ return &this->super;
}
void Process_delete(Object* cast) {
@@ -83,8 +84,8 @@ void Process_delete(Object* cast) {
free(this);
}
-void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField field) {
- DragonFlyBSDProcess* fp = (DragonFlyBSDProcess*) this;
+void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
+ const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
@@ -92,7 +93,7 @@ void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField
// add Platform-specific fields here
case PID: xSnprintf(buffer, n, Process_pidFormat, (fp->kernel ? -1 : this->pid)); break;
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
- case JAIL:{
+ case JAIL: {
xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') {
buffer[11] = ' ';
@@ -108,31 +109,34 @@ void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField
}
long DragonFlyBSDProcess_compare(const void* v1, const void* v2) {
- DragonFlyBSDProcess *p1, *p2;
- Settings *settings = ((Process*)v1)->settings;
+ const DragonFlyBSDProcess *p1, *p2;
+ const Settings *settings = ((const Process*)v1)->settings;
+
if (settings->direction == 1) {
- p1 = (DragonFlyBSDProcess*)v1;
- p2 = (DragonFlyBSDProcess*)v2;
+ p1 = (const DragonFlyBSDProcess*)v1;
+ p2 = (const DragonFlyBSDProcess*)v2;
} else {
- p2 = (DragonFlyBSDProcess*)v1;
- p1 = (DragonFlyBSDProcess*)v2;
+ p2 = (const DragonFlyBSDProcess*)v1;
+ p1 = (const DragonFlyBSDProcess*)v2;
}
+
switch ((int) settings->sortKey) {
// add Platform-specific fields here
case JID:
- return (p1->jid - p2->jid);
+ return SPACESHIP_NUMBER(p1->jid, p2->jid);
case JAIL:
- return strcmp(p1->jname ? p1->jname : "", p2->jname ? p2->jname : "");
+ return SPACESHIP_NULLSTR(p1->jname, p2->jname);
default:
return Process_compare(v1, v2);
}
}
-bool Process_isThread(Process* this) {
- DragonFlyBSDProcess* fp = (DragonFlyBSDProcess*) this;
+bool Process_isThread(const Process* this) {
+ const DragonFlyBSDProcess* fp = (const DragonFlyBSDProcess*) this;
- if (fp->kernel == 1 )
+ if (fp->kernel == 1 ) {
return 1;
- else
+ } else {
return (Process_isUserlandThread(this));
+ }
}
diff --git a/dragonflybsd/DragonFlyBSDProcess.h b/dragonflybsd/DragonFlyBSDProcess.h
index d5e5a6e..1befd94 100644
--- a/dragonflybsd/DragonFlyBSDProcess.h
+++ b/dragonflybsd/DragonFlyBSDProcess.h
@@ -4,7 +4,7 @@
htop - dragonflybsd/DragonFlyBSDProcess.h
(C) 2015 Hisham H. Muhammad
(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -27,20 +27,20 @@ typedef struct DragonFlyBSDProcess_ {
//#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#define Process_isUserlandThread(_process) (_process->nlwp > 1)
-extern ProcessClass DragonFlyBSDProcess_class;
+extern const ProcessClass DragonFlyBSDProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
-DragonFlyBSDProcess* DragonFlyBSDProcess_new(Settings* settings);
+Process* DragonFlyBSDProcess_new(const Settings* settings);
void Process_delete(Object* cast);
-void DragonFlyBSDProcess_writeField(Process* this, RichString* str, ProcessField field);
+void DragonFlyBSDProcess_writeField(const Process* this, RichString* str, ProcessField field);
long DragonFlyBSDProcess_compare(const void* v1, const void* v2);
-bool Process_isThread(Process* this);
+bool Process_isThread(const Process* this);
#endif
diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c
index cd5526a..940ec03 100644
--- a/dragonflybsd/DragonFlyBSDProcessList.c
+++ b/dragonflybsd/DragonFlyBSDProcessList.c
@@ -2,7 +2,7 @@
htop - DragonFlyBSDProcessList.c
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -21,8 +21,9 @@ in the source distribution for its full text.
#include <string.h>
#include <sys/param.h>
+#include "CRT.h"
+#include "Macros.h"
-#define _UNUSED_ __attribute__((unused))
static int MIB_hw_physmem[2];
static int MIB_vm_stats_vm_v_page_count[4];
@@ -55,8 +56,8 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
len = sizeof(pageSize);
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) {
- pageSize = PAGE_SIZE;
- pageSizeKb = PAGE_SIZE_KB;
+ pageSize = CRT_pageSize;
+ pageSizeKb = CRT_pageSizeKB;
} else {
pageSizeKb = pageSize / ONE_K;
}
@@ -100,10 +101,10 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
pl->cpuCount = MAXIMUM(cpus, 1);
if (cpus == 1 ) {
- dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData));
+ dfpl->cpus = xRealloc(dfpl->cpus, sizeof(CPUData));
} else {
- // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well)
- dfpl->cpus = xRealloc(dfpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
+ // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well)
+ dfpl->cpus = xRealloc(dfpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
}
len = sizeof(kernelFScale);
@@ -122,7 +123,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
void ProcessList_delete(ProcessList* this) {
const DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this;
- if (dfpl->kd) kvm_close(dfpl->kd);
+ if (dfpl->kd) {
+ kvm_close(dfpl->kd);
+ }
if (dfpl->jails) {
Hashtable_delete(dfpl->jails);
@@ -148,8 +151,8 @@ static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) {
size_t sizeof_cp_time_array;
- unsigned long *cp_time_n; // old clicks state
- unsigned long *cp_time_o; // current clicks state
+ unsigned long* cp_time_n; // old clicks state
+ unsigned long* cp_time_o; // current clicks state
unsigned long cp_time_d[CPUSTATES];
double cp_time_p[CPUSTATES];
@@ -160,12 +163,12 @@ static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) {
// get rest of CPUs
if (cpus > 1) {
- // on smp systems DragonFlyBSD kernel concats all CPU states into one long array in
- // kern.cp_times sysctl OID
- // we store averages in dfpl->cpus[0], and actual cores after that
- maxcpu = cpus + 1;
- sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES;
- sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_n, &sizeof_cp_time_array, NULL, 0);
+ // on smp systems DragonFlyBSD kernel concats all CPU states into one long array in
+ // kern.cp_times sysctl OID
+ // we store averages in dfpl->cpus[0], and actual cores after that
+ maxcpu = cpus + 1;
+ sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES;
+ sysctl(MIB_kern_cp_times, 2, dfpl->cp_times_n, &sizeof_cp_time_array, NULL, 0);
}
for (int i = 0; i < maxcpu; i++) {
@@ -175,14 +178,14 @@ static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) {
cp_time_o = dfpl->cp_time_o;
} else {
if (i == 0 ) {
- // average
- cp_time_n = dfpl->cp_time_n;
- cp_time_o = dfpl->cp_time_o;
+ // average
+ cp_time_n = dfpl->cp_time_n;
+ cp_time_o = dfpl->cp_time_o;
} else {
- // specific smp cores
- cp_times_offset = i - 1;
- cp_time_n = dfpl->cp_times_n + (cp_times_offset * CPUSTATES);
- cp_time_o = dfpl->cp_times_o + (cp_times_offset * CPUSTATES);
+ // specific smp cores
+ cp_times_offset = i - 1;
+ cp_time_n = dfpl->cp_times_n + (cp_times_offset * CPUSTATES);
+ cp_time_o = dfpl->cp_times_o + (cp_times_offset * CPUSTATES);
}
}
@@ -191,19 +194,21 @@ static inline void DragonFlyBSDProcessList_scanCPUTime(ProcessList* pl) {
unsigned long long total_n = 0;
unsigned long long total_d = 0;
for (int s = 0; s < CPUSTATES; s++) {
- cp_time_d[s] = cp_time_n[s] - cp_time_o[s];
- total_o += cp_time_o[s];
- total_n += cp_time_n[s];
+ cp_time_d[s] = cp_time_n[s] - cp_time_o[s];
+ total_o += cp_time_o[s];
+ total_n += cp_time_n[s];
}
// totals
total_d = total_n - total_o;
- if (total_d < 1 ) total_d = 1;
+ if (total_d < 1 ) {
+ total_d = 1;
+ }
// save current state as old and calc percentages
for (int s = 0; s < CPUSTATES; ++s) {
- cp_time_o[s] = cp_time_n[s];
- cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100;
+ cp_time_o[s] = cp_time_n[s];
+ cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100;
}
CPUData* cpuData = &(dfpl->cpus[i]);
@@ -248,14 +253,8 @@ static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) {
pl->cachedMem *= pageSizeKb;
pl->usedMem = dfpl->memActive + dfpl->memWire;
- //currently unused, same as with arc, custom meter perhaps
- //sysctl(MIB_vm_stats_vm_v_inactive_count, 4, &(dfpl->memInactive), &len, NULL, 0);
- //sysctl(MIB_vm_stats_vm_v_free_count, 4, &(dfpl->memFree), &len, NULL, 0);
- //pl->freeMem = dfpl->memInactive + dfpl->memFree;
- //pl->freeMem *= pageSizeKb;
-
struct kvm_swap swap[16];
- int nswap = kvm_getswapinfo(dfpl->kd, swap, sizeof(swap)/sizeof(swap[0]), 0);
+ int nswap = kvm_getswapinfo(dfpl->kd, swap, ARRAYSIZE(swap), 0);
pl->totalSwap = 0;
pl->usedSwap = 0;
for (int i = 0; i < nswap; i++) {
@@ -264,8 +263,6 @@ static inline void DragonFlyBSDProcessList_scanMemoryInfo(ProcessList* pl) {
}
pl->totalSwap *= pageSizeKb;
pl->usedSwap *= pageSizeKb;
-
- pl->sharedMem = 0; // currently unused
}
char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) {
@@ -295,9 +292,9 @@ char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kpro
static inline void DragonFlyBSDProcessList_scanJails(DragonFlyBSDProcessList* dfpl) {
size_t len;
- char *jls; /* Jail list */
- char *curpos;
- char *nextpos;
+ char* jls; /* Jail list */
+ char* curpos;
+ char* nextpos;
if (sysctlbyname("jail.list", NULL, &len, NULL, 0) == -1) {
fprintf(stderr, "initial sysctlbyname / jail.list failed\n");
@@ -328,30 +325,32 @@ retry:
curpos = jls;
while (curpos) {
int jailid;
- char *str_hostname;
+ char* str_hostname;
nextpos = strchr(curpos, '\n');
- if (nextpos)
+ if (nextpos) {
*nextpos++ = 0;
+ }
jailid = atoi(strtok(curpos, " "));
str_hostname = strtok(NULL, " ");
- char *jname = (char *) (Hashtable_get(dfpl->jails, jailid));
+ char* jname = (char*) (Hashtable_get(dfpl->jails, jailid));
if (jname == NULL) {
jname = xStrdup(str_hostname);
Hashtable_put(dfpl->jails, jailid, jname);
}
curpos = nextpos;
- }
- free(jls);
+ }
+
+ free(jls);
}
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid) {
char* hostname;
char* jname;
- if (jailid != 0 && dfpl->jails && (hostname = (char *)Hashtable_get(dfpl->jails, jailid))) {
+ if (jailid != 0 && dfpl->jails && (hostname = (char*)Hashtable_get(dfpl->jails, jailid))) {
jname = xStrdup(hostname);
} else {
jname = xStrdup("-");
@@ -359,16 +358,21 @@ char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int ja
return jname;
}
-void ProcessList_goThroughEntries(ProcessList* this) {
- DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) this;
- Settings* settings = this->settings;
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
+ DragonFlyBSDProcessList* dfpl = (DragonFlyBSDProcessList*) super;
+ const Settings* settings = super->settings;
bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads;
- DragonFlyBSDProcessList_scanMemoryInfo(this);
- DragonFlyBSDProcessList_scanCPUTime(this);
+ DragonFlyBSDProcessList_scanMemoryInfo(super);
+ DragonFlyBSDProcessList_scanCPUTime(super);
DragonFlyBSDProcessList_scanJails(dfpl);
+ // in pause mode only gather global data for meters (CPU/memory/...)
+ if (pauseProcessUpdate) {
+ return;
+ }
+
int count = 0;
// TODO Kernel Threads seem to be skipped, need to figure out the correct flag
@@ -377,10 +381,10 @@ void ProcessList_goThroughEntries(ProcessList* this) {
for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i];
bool preExisting = false;
- bool _UNUSED_ isIdleProcess = false;
+ bool ATTR_UNUSED isIdleProcess = false;
// note: dragonflybsd kernel processes all have the same pid, so we misuse the kernel thread address to give them a unique identifier
- Process* proc = ProcessList_getProcess(this, kproc->kp_ktaddr ? (pid_t)kproc->kp_ktaddr : kproc->kp_pid, &preExisting, (Process_New) DragonFlyBSDProcess_new);
+ Process* proc = ProcessList_getProcess(super, kproc->kp_ktaddr ? (pid_t)kproc->kp_ktaddr : kproc->kp_pid, &preExisting, DragonFlyBSDProcess_new);
DragonFlyBSDProcess* dfp = (DragonFlyBSDProcess*) proc;
proc->show = ! ((hideKernelThreads && Process_isKernelThread(dfp)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
@@ -405,24 +409,23 @@ void ProcessList_goThroughEntries(ProcessList* this) {
proc->st_uid = kproc->kp_uid; // user ID
proc->processor = kproc->kp_lwp.kl_origcpu;
proc->starttime_ctime = kproc->kp_start.tv_sec;
- proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
+ proc->user = UsersTable_getRef(super->usersTable, proc->st_uid);
- ProcessList_add((ProcessList*)this, proc);
+ ProcessList_add(super, proc);
proc->comm = DragonFlyBSDProcessList_readProcessName(dfpl->kd, kproc, &proc->basenameOffset);
dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid);
} else {
proc->processor = kproc->kp_lwp.kl_cpuid;
- if(dfp->jid != kproc->kp_jailid) { // process can enter jail anytime
+ if (dfp->jid != kproc->kp_jailid) { // process can enter jail anytime
dfp->jid = kproc->kp_jailid;
free(dfp->jname);
dfp->jname = DragonFlyBSDProcessList_readJailName(dfpl, kproc->kp_jailid);
}
- if (proc->ppid != kproc->kp_ppid) { // if there are reapers in the system, process can get reparented anytime
- proc->ppid = kproc->kp_ppid;
- }
- if(proc->st_uid != kproc->kp_uid) { // some processes change users (eg. to lower privs)
+ // if there are reapers in the system, process can get reparented anytime
+ proc->ppid = kproc->kp_ppid;
+ if (proc->st_uid != kproc->kp_uid) { // some processes change users (eg. to lower privs)
proc->st_uid = kproc->kp_uid;
- proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
+ proc->user = UsersTable_getRef(super->usersTable, proc->st_uid);
}
if (settings->updateProcessNames) {
free(proc->comm);
@@ -430,14 +433,13 @@ void ProcessList_goThroughEntries(ProcessList* this) {
}
}
- proc->m_size = kproc->kp_vm_map_size / 1024 / pageSizeKb;
+ proc->m_virt = kproc->kp_vm_map_size / pageSize;
proc->m_resident = kproc->kp_vm_rssize;
- proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
proc->nlwp = kproc->kp_nthreads; // number of lwp thread
proc->time = (kproc->kp_swtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale);
- proc->percent_mem = 100.0 * (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem);
+ proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem);
if (proc->percent_cpu > 0.1) {
// system idle process should own all CPU time left regardless of CPU count
@@ -472,18 +474,18 @@ void ProcessList_goThroughEntries(ProcessList* this) {
case SACTIVE:
switch (kproc->kp_lwp.kl_stat) {
case LSSLEEP:
- if (kproc->kp_lwp.kl_flags & LWP_SINTR) // interruptable wait short/long
+ if (kproc->kp_lwp.kl_flags & LWP_SINTR) // interruptible wait short/long
if (kproc->kp_lwp.kl_slptime >= MAXSLP) {
proc->state = 'I';
isIdleProcess = true;
} else {
proc->state = 'S';
}
- else if (kproc->kp_lwp.kl_tdflags & TDF_SINTR) // interruptable lwkt wait
+ else if (kproc->kp_lwp.kl_tdflags & TDF_SINTR) // interruptible lwkt wait
proc->state = 'S';
- else if (kproc->kp_paddr) // uninterruptable wait
+ else if (kproc->kp_paddr) // uninterruptible wait
proc->state = 'D';
- else // uninterruptable lwkt wait
+ else // uninterruptible lwkt wait
proc->state = 'B';
break;
case LSRUN:
@@ -519,12 +521,12 @@ void ProcessList_goThroughEntries(ProcessList* this) {
}
if (Process_isKernelThread(dfp)) {
- this->kernelThreads++;
+ super->kernelThreads++;
}
- this->totalTasks++;
+ super->totalTasks++;
if (proc->state == 'R')
- this->runningTasks++;
+ super->runningTasks++;
proc->updated = true;
}
}
diff --git a/dragonflybsd/DragonFlyBSDProcessList.h b/dragonflybsd/DragonFlyBSDProcessList.h
index 84ab1c5..682971e 100644
--- a/dragonflybsd/DragonFlyBSDProcessList.h
+++ b/dragonflybsd/DragonFlyBSDProcessList.h
@@ -4,7 +4,7 @@
htop - DragonFlyBSDProcessList.h
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -19,7 +19,7 @@ in the source distribution for its full text.
#include "Hashtable.h"
#include "DragonFlyBSDProcess.h"
-#define JAIL_ERRMSGLEN 1024
+#define JAIL_ERRMSGLEN 1024
extern char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ {
@@ -42,17 +42,15 @@ typedef struct DragonFlyBSDProcessList_ {
CPUData* cpus;
- unsigned long *cp_time_o;
- unsigned long *cp_time_n;
+ unsigned long* cp_time_o;
+ unsigned long* cp_time_n;
- unsigned long *cp_times_o;
- unsigned long *cp_times_n;
+ unsigned long* cp_times_o;
+ unsigned long* cp_times_n;
- Hashtable *jails;
+ Hashtable* jails;
} DragonFlyBSDProcessList;
-#define _UNUSED_ __attribute__((unused))
-
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId);
void ProcessList_delete(ProcessList* this);
@@ -61,6 +59,6 @@ char* DragonFlyBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kpro
char* DragonFlyBSDProcessList_readJailName(DragonFlyBSDProcessList* dfpl, int jailid);
-void ProcessList_goThroughEntries(ProcessList* this);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif
diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c
index 3b8e1a0..40a7348 100644
--- a/dragonflybsd/Platform.c
+++ b/dragonflybsd/Platform.c
@@ -2,11 +2,12 @@
htop - dragonflybsd/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
+#include "Macros.h"
#include "Meter.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
@@ -15,6 +16,8 @@ in the source distribution for its full text.
#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
#include "ClockMeter.h"
+#include "DateMeter.h"
+#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "DragonFlyBSDProcess.h"
#include "DragonFlyBSDProcessList.h"
@@ -28,7 +31,7 @@ in the source distribution for its full text.
#include <math.h>
-ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
+ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
@@ -69,15 +72,13 @@ const SignalItem Platform_signals[] = {
{ .name = "33 SIGLIBRT", .number = 33 },
};
-const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
+const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
-void Platform_setBindings(Htop_Action* keys) {
- (void) keys;
-}
-
-MeterClass* Platform_meterTypes[] = {
+const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
+ &DateMeter_class,
+ &DateTimeMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
@@ -88,14 +89,33 @@ MeterClass* Platform_meterTypes[] = {
&HostnameMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
+ &AllCPUs4Meter_class,
+ &AllCPUs8Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
+ &LeftCPUs4Meter_class,
+ &RightCPUs4Meter_class,
+ &LeftCPUs8Meter_class,
+ &RightCPUs8Meter_class,
&BlankMeter_class,
NULL
};
+void Platform_init(void) {
+ /* no platform-specific setup needed */
+}
+
+void Platform_done(void) {
+ /* no platform-specific cleanup needed */
+}
+
+void Platform_setBindings(Htop_Action* keys) {
+ /* no platform-specific key bindings */
+ (void) keys;
+}
+
int Platform_getUptime() {
struct timeval bootTime, currTime;
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
@@ -138,15 +158,15 @@ int Platform_getMaxPid() {
}
double Platform_setCPUValues(Meter* this, int cpu) {
- DragonFlyBSDProcessList* fpl = (DragonFlyBSDProcessList*) this->pl;
+ const DragonFlyBSDProcessList* fpl = (const DragonFlyBSDProcessList*) this->pl;
int cpus = this->pl->cpuCount;
- CPUData* cpuData;
+ const CPUData* cpuData;
if (cpus == 1) {
- // single CPU box has everything in fpl->cpus[0]
- cpuData = &(fpl->cpus[0]);
+ // single CPU box has everything in fpl->cpus[0]
+ cpuData = &(fpl->cpus[0]);
} else {
- cpuData = &(fpl->cpus[cpu]);
+ cpuData = &(fpl->cpus[cpu]);
}
double percent;
@@ -157,25 +177,25 @@ double Platform_setCPUValues(Meter* this, int cpu) {
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
- Meter_setItems(this, 4);
- percent = v[0]+v[1]+v[2]+v[3];
+ this->curItems = 4;
+ percent = v[0] + v[1] + v[2] + v[3];
} else {
v[2] = cpuData->systemAllPercent;
- Meter_setItems(this, 3);
- percent = v[0]+v[1]+v[2];
+ this->curItems = 3;
+ percent = v[0] + v[1] + v[2];
}
- percent = CLAMP(percent, 0.0, 100.0);
- if (isnan(percent)) percent = 0.0;
+ percent = isnan(percent) ? 0.0 : CLAMP(percent, 0.0, 100.0);
- v[CPU_METER_FREQUENCY] = -1;
+ v[CPU_METER_FREQUENCY] = NAN;
+ v[CPU_METER_TEMPERATURE] = NAN;
return percent;
}
void Platform_setMemoryValues(Meter* this) {
// TODO
- ProcessList* pl = (ProcessList*) this->pl;
+ const ProcessList* pl = this->pl;
this->total = pl->totalMem;
this->values[0] = pl->usedMem;
@@ -184,18 +204,58 @@ void Platform_setMemoryValues(Meter* this) {
}
void Platform_setSwapValues(Meter* this) {
- ProcessList* pl = (ProcessList*) this->pl;
+ const ProcessList* pl = this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
}
-void Platform_setTasksValues(Meter* this) {
+char* Platform_getProcessEnv(pid_t pid) {
+ // TODO
+ (void)pid; // prevent unused warning
+ return NULL;
+}
+
+char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
+ (void)pid;
+ (void)inode;
+ return NULL;
+}
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
+ (void)pid;
+ return NULL;
+}
+
+bool Platform_getDiskIO(DiskIOData* data) {
// TODO
- (void)this; // prevent unused warning
+ (void)data;
+ return false;
}
-char* Platform_getProcessEnv(pid_t pid) {
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted) {
// TODO
- (void)pid; // prevent unused warning
- return NULL;
+ *bytesReceived = 0;
+ *packetsReceived = 0;
+ *bytesTransmitted = 0;
+ *packetsTransmitted = 0;
+ return false;
+}
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC) {
+ int life;
+ size_t life_len = sizeof(life);
+ if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
+ *percent = NAN;
+ else
+ *percent = life;
+
+ int acline;
+ size_t acline_len = sizeof(acline);
+ if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1)
+ *isOnAC = AC_ERROR;
+ else
+ *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT;
}
diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h
index 83c14f5..5456539 100644
--- a/dragonflybsd/Platform.h
+++ b/dragonflybsd/Platform.h
@@ -4,12 +4,17 @@
htop - dragonflybsd/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2017 Diederik de Groot
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+#include <sys/types.h>
+
#include "Action.h"
#include "BatteryMeter.h"
+#include "DiskIOMeter.h"
+#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
extern ProcessFieldData Process_fields[];
@@ -22,15 +27,19 @@ extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
-void Platform_setBindings(Htop_Action* keys);
+extern const MeterClass* const Platform_meterTypes[];
+
+void Platform_init(void);
-extern MeterClass* Platform_meterTypes[];
+void Platform_done(void);
-int Platform_getUptime();
+void Platform_setBindings(Htop_Action* keys);
+
+int Platform_getUptime(void);
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
-int Platform_getMaxPid();
+int Platform_getMaxPid(void);
double Platform_setCPUValues(Meter* this, int cpu);
@@ -38,8 +47,19 @@ void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
-void Platform_setTasksValues(Meter* this);
-
char* Platform_getProcessEnv(pid_t pid);
+char* Platform_getInodeFilename(pid_t pid, ino_t inode);
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
+
+bool Platform_getDiskIO(DiskIOData* data);
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted);
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC);
+
#endif
diff --git a/freebsd/Battery.c b/freebsd/Battery.c
deleted file mode 100644
index b8c5e31..0000000
--- a/freebsd/Battery.c
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
-htop - freebsd/Battery.c
-(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "BatteryMeter.h"
-#include <sys/sysctl.h>
-
-void Battery_getData(double* level, ACPresence* isOnAC) {
- int life;
- size_t life_len = sizeof(life);
- if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
- *level = -1;
- else
- *level = life;
-
- int acline;
- size_t acline_len = sizeof(acline);
- if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1)
- *isOnAC = AC_ERROR;
- else
- *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT;
-}
diff --git a/freebsd/Battery.h b/freebsd/Battery.h
deleted file mode 100644
index 6987d78..0000000
--- a/freebsd/Battery.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef HEADER_Battery
-#define HEADER_Battery
-/*
-htop - freebsd/Battery.h
-(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void Battery_getData(double* level, ACPresence* isOnAC);
-
-#endif
diff --git a/freebsd/FreeBSDCRT.c b/freebsd/FreeBSDCRT.c
deleted file mode 100644
index 49cc5d0..0000000
--- a/freebsd/FreeBSDCRT.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-htop - UnsupportedCRT.c
-(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "config.h"
-#include "CRT.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-void CRT_handleSIGSEGV(int sgn) {
- (void) sgn;
- CRT_done();
- fprintf(stderr, "\n\nhtop " VERSION " aborting.\n");
- fprintf(stderr, "\nUnfortunately, you seem to be using an unsupported platform!");
- fprintf(stderr, "\nPlease contact your platform package maintainer!\n\n");
- abort();
-}
diff --git a/freebsd/FreeBSDCRT.h b/freebsd/FreeBSDCRT.h
deleted file mode 100644
index eb88fcc..0000000
--- a/freebsd/FreeBSDCRT.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef HEADER_FreeBSDCRT
-#define HEADER_FreeBSDCRT
-/*
-htop - UnsupportedCRT.h
-(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void CRT_handleSIGSEGV(int sgn);
-
-#endif
diff --git a/freebsd/FreeBSDProcess.c b/freebsd/FreeBSDProcess.c
index 33dc751..d6d67b7 100644
--- a/freebsd/FreeBSDProcess.c
+++ b/freebsd/FreeBSDProcess.c
@@ -1,31 +1,22 @@
/*
htop - FreeBSDProcess.c
(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Process.h"
-#include "ProcessList.h"
#include "FreeBSDProcess.h"
-#include "Platform.h"
-#include "CRT.h"
#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/syscall.h>
+#include "CRT.h"
+#include "Macros.h"
+#include "Process.h"
+#include "RichString.h"
+#include "XUtils.h"
-ProcessClass FreeBSDProcess_class = {
- .super = {
- .extends = Class(Process),
- .display = Process_display,
- .delete = Process_delete,
- .compare = FreeBSDProcess_compare
- },
- .writeField = (Process_WriteField) FreeBSDProcess_writeField,
-};
+
+const char* const nodevStr = "nodev";
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
@@ -35,7 +26,7 @@ ProcessFieldData Process_fields[] = {
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
- [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
+ [TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = PROCESS_FLAG_FREEBSD_TTY, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
@@ -44,10 +35,11 @@ ProcessFieldData Process_fields[] = {
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
- [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
+ [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
+ [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
@@ -69,11 +61,11 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = 0, .label = NULL },
};
-FreeBSDProcess* FreeBSDProcess_new(Settings* settings) {
+Process* FreeBSDProcess_new(const Settings* settings) {
FreeBSDProcess* this = xCalloc(1, sizeof(FreeBSDProcess));
Object_setClass(this, Class(FreeBSDProcess));
Process_init(&this->super, settings);
- return this;
+ return &this->super;
}
void Process_delete(Object* cast) {
@@ -83,15 +75,15 @@ void Process_delete(Object* cast) {
free(this);
}
-void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField field) {
- FreeBSDProcess* fp = (FreeBSDProcess*) this;
+static void FreeBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
+ const FreeBSDProcess* fp = (const FreeBSDProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int) field) {
// add FreeBSD-specific fields here
case JID: xSnprintf(buffer, n, Process_pidFormat, fp->jid); break;
- case JAIL:{
+ case JAIL: {
xSnprintf(buffer, n, "%-11s ", fp->jname);
if (buffer[11] != '\0') {
buffer[11] = ' ';
@@ -99,6 +91,16 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
}
break;
}
+ case TTY_NR:
+ if (fp->ttyPath) {
+ if (fp->ttyPath == nodevStr)
+ attr = CRT_colors[PROCESS_SHADOW];
+ xSnprintf(buffer, n, "%-8s", fp->ttyPath);
+ } else {
+ attr = CRT_colors[PROCESS_SHADOW];
+ xSnprintf(buffer, n, "? ");
+ }
+ break;
default:
Process_writeField(this, str, field);
return;
@@ -106,32 +108,47 @@ void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
RichString_append(str, attr, buffer);
}
-long FreeBSDProcess_compare(const void* v1, const void* v2) {
- FreeBSDProcess *p1, *p2;
- Settings *settings = ((Process*)v1)->settings;
+static long FreeBSDProcess_compare(const void* v1, const void* v2) {
+ const FreeBSDProcess *p1, *p2;
+ const Settings *settings = ((const Process*)v1)->settings;
+
if (settings->direction == 1) {
- p1 = (FreeBSDProcess*)v1;
- p2 = (FreeBSDProcess*)v2;
+ p1 = (const FreeBSDProcess*)v1;
+ p2 = (const FreeBSDProcess*)v2;
} else {
- p2 = (FreeBSDProcess*)v1;
- p1 = (FreeBSDProcess*)v2;
+ p2 = (const FreeBSDProcess*)v1;
+ p1 = (const FreeBSDProcess*)v2;
}
+
switch ((int) settings->sortKey) {
// add FreeBSD-specific fields here
case JID:
- return (p1->jid - p2->jid);
+ return SPACESHIP_NUMBER(p1->jid, p2->jid);
case JAIL:
- return strcmp(p1->jname ? p1->jname : "", p2->jname ? p2->jname : "");
+ return SPACESHIP_NULLSTR(p1->jname, p2->jname);
+ case TTY_NR:
+ return SPACESHIP_NULLSTR(p1->ttyPath, p2->ttyPath);
default:
return Process_compare(v1, v2);
}
}
-bool Process_isThread(Process* this) {
- FreeBSDProcess* fp = (FreeBSDProcess*) this;
+bool Process_isThread(const Process* this) {
+ const FreeBSDProcess* fp = (const FreeBSDProcess*) this;
- if (fp->kernel == 1 )
+ if (fp->kernel == 1 ) {
return 1;
- else
- return (Process_isUserlandThread(this));
+ } else {
+ return Process_isUserlandThread(this);
+ }
}
+
+const ProcessClass FreeBSDProcess_class = {
+ .super = {
+ .extends = Class(Process),
+ .display = Process_display,
+ .delete = Process_delete,
+ .compare = FreeBSDProcess_compare
+ },
+ .writeField = FreeBSDProcess_writeField,
+};
diff --git a/freebsd/FreeBSDProcess.h b/freebsd/FreeBSDProcess.h
index 897c532..8911976 100644
--- a/freebsd/FreeBSDProcess.h
+++ b/freebsd/FreeBSDProcess.h
@@ -3,10 +3,21 @@
/*
htop - FreeBSDProcess.h
(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+
+#include "Object.h"
+#include "Process.h"
+#include "Settings.h"
+
+
+#define PROCESS_FLAG_FREEBSD_TTY 0x0100
+
+extern const char* const nodevStr;
+
typedef enum FreeBSDProcessFields_ {
// Add platform-specific fields here, with ids >= 100
JID = 100,
@@ -19,26 +30,27 @@ typedef struct FreeBSDProcess_ {
int kernel;
int jid;
char* jname;
+ const char* ttyPath;
} FreeBSDProcess;
-#define Process_isKernelThread(_process) (_process->kernel == 1)
+static inline bool Process_isKernelThread(const Process* this) {
+ return ((const FreeBSDProcess*)this)->kernel == 1;
+}
-#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
+static inline bool Process_isUserlandThread(const Process* this) {
+ return this->pid != this->tgid;
+}
-extern ProcessClass FreeBSDProcess_class;
+extern const ProcessClass FreeBSDProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
-FreeBSDProcess* FreeBSDProcess_new(Settings* settings);
+Process* FreeBSDProcess_new(const Settings* settings);
void Process_delete(Object* cast);
-void FreeBSDProcess_writeField(Process* this, RichString* str, ProcessField field);
-
-long FreeBSDProcess_compare(const void* v1, const void* v2);
-
-bool Process_isThread(Process* this);
+bool Process_isThread(const Process* this);
#endif
diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c
index 6318d42..9aaab5d 100644
--- a/freebsd/FreeBSDProcessList.c
+++ b/freebsd/FreeBSDProcessList.c
@@ -1,26 +1,44 @@
/*
htop - FreeBSDProcessList.c
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "ProcessList.h"
#include "FreeBSDProcessList.h"
-#include "FreeBSDProcess.h"
-#include "zfs/ZfsArcStats.h"
-#include "zfs/openzfs_sysctl.h"
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <sys/user.h>
+#include <assert.h>
+#include <dirent.h>
#include <err.h>
-#include <fcntl.h>
#include <limits.h>
+#include <stdlib.h>
#include <string.h>
-#include <time.h>
+#include <sys/_iovec.h>
+#include <sys/dirent.h>
+#include <sys/errno.h>
+#include <sys/param.h> // needs to be included before <sys/jail.h> for MAXPATHLEN
+#include <sys/jail.h>
+#include <sys/priority.h>
+#include <sys/proc.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/user.h>
+
+#include "CRT.h"
+#include "Compat.h"
+#include "FreeBSDProcess.h"
+#include "Macros.h"
+#include "Object.h"
+#include "Process.h"
+#include "ProcessList.h"
+#include "Settings.h"
+#include "XUtils.h"
+#include "zfs/ZfsArcStats.h"
+#include "zfs/openzfs_sysctl.h"
+
char jail_errmsg[JAIL_ERRMSGLEN];
@@ -55,8 +73,8 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
len = sizeof(pageSize);
if (sysctlbyname("vm.stats.vm.v_page_size", &pageSize, &len, NULL, 0) == -1) {
- pageSize = PAGE_SIZE;
- pageSizeKb = PAGE_SIZE_KB;
+ pageSize = CRT_pageSize;
+ pageSizeKb = CRT_pageSize;
} else {
pageSizeKb = pageSize / ONE_K;
}
@@ -88,7 +106,9 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
if (smp) {
int err = sysctlbyname("kern.smp.cpus", &cpus, &len, NULL, 0);
- if (err) cpus = 1;
+ if (err) {
+ cpus = 1;
+ }
} else {
cpus = 1;
}
@@ -114,10 +134,10 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
pl->cpuCount = MAXIMUM(cpus, 1);
if (cpus == 1 ) {
- fpl->cpus = xRealloc(fpl->cpus, sizeof(CPUData));
+ fpl->cpus = xRealloc(fpl->cpus, sizeof(CPUData));
} else {
- // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well)
- fpl->cpus = xRealloc(fpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
+ // on smp we need CPUs + 1 to store averages too (as kernel kindly provides that as well)
+ fpl->cpus = xRealloc(fpl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
}
@@ -132,12 +152,19 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
errx(1, "kvm_open: %s", errbuf);
}
+ fpl->ttys = Hashtable_new(20, true);
+
return pl;
}
void ProcessList_delete(ProcessList* this) {
const FreeBSDProcessList* fpl = (FreeBSDProcessList*) this;
- if (fpl->kd) kvm_close(fpl->kd);
+
+ Hashtable_delete(fpl->ttys);
+
+ if (fpl->kd) {
+ kvm_close(fpl->kd);
+ }
free(fpl->cp_time_o);
free(fpl->cp_time_n);
@@ -160,8 +187,8 @@ static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) {
size_t sizeof_cp_time_array;
- unsigned long *cp_time_n; // old clicks state
- unsigned long *cp_time_o; // current clicks state
+ unsigned long* cp_time_n; // old clicks state
+ unsigned long* cp_time_o; // current clicks state
unsigned long cp_time_d[CPUSTATES];
double cp_time_p[CPUSTATES];
@@ -172,12 +199,12 @@ static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) {
// get rest of CPUs
if (cpus > 1) {
- // on smp systems FreeBSD kernel concats all CPU states into one long array in
- // kern.cp_times sysctl OID
- // we store averages in fpl->cpus[0], and actual cores after that
- maxcpu = cpus + 1;
- sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES;
- sysctl(MIB_kern_cp_times, 2, fpl->cp_times_n, &sizeof_cp_time_array, NULL, 0);
+ // on smp systems FreeBSD kernel concats all CPU states into one long array in
+ // kern.cp_times sysctl OID
+ // we store averages in fpl->cpus[0], and actual cores after that
+ maxcpu = cpus + 1;
+ sizeof_cp_time_array = cpus * sizeof(unsigned long) * CPUSTATES;
+ sysctl(MIB_kern_cp_times, 2, fpl->cp_times_n, &sizeof_cp_time_array, NULL, 0);
}
for (int i = 0; i < maxcpu; i++) {
@@ -187,14 +214,14 @@ static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) {
cp_time_o = fpl->cp_time_o;
} else {
if (i == 0 ) {
- // average
- cp_time_n = fpl->cp_time_n;
- cp_time_o = fpl->cp_time_o;
+ // average
+ cp_time_n = fpl->cp_time_n;
+ cp_time_o = fpl->cp_time_o;
} else {
- // specific smp cores
- cp_times_offset = i - 1;
- cp_time_n = fpl->cp_times_n + (cp_times_offset * CPUSTATES);
- cp_time_o = fpl->cp_times_o + (cp_times_offset * CPUSTATES);
+ // specific smp cores
+ cp_times_offset = i - 1;
+ cp_time_n = fpl->cp_times_n + (cp_times_offset * CPUSTATES);
+ cp_time_o = fpl->cp_times_o + (cp_times_offset * CPUSTATES);
}
}
@@ -203,19 +230,21 @@ static inline void FreeBSDProcessList_scanCPUTime(ProcessList* pl) {
unsigned long long total_n = 0;
unsigned long long total_d = 0;
for (int s = 0; s < CPUSTATES; s++) {
- cp_time_d[s] = cp_time_n[s] - cp_time_o[s];
- total_o += cp_time_o[s];
- total_n += cp_time_n[s];
+ cp_time_d[s] = cp_time_n[s] - cp_time_o[s];
+ total_o += cp_time_o[s];
+ total_n += cp_time_n[s];
}
// totals
total_d = total_n - total_o;
- if (total_d < 1 ) total_d = 1;
+ if (total_d < 1 ) {
+ total_d = 1;
+ }
// save current state as old and calc percentages
for (int s = 0; s < CPUSTATES; ++s) {
- cp_time_o[s] = cp_time_n[s];
- cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100;
+ cp_time_o[s] = cp_time_n[s];
+ cp_time_p[s] = ((double)cp_time_d[s]) / ((double)total_d) * 100;
}
CPUData* cpuData = &(fpl->cpus[i]);
@@ -247,7 +276,6 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) {
u_long totalMem;
u_int memActive, memWire, cachedMem;
long buffersMem;
- uint64_t memZfsArc;
size_t len;
//disabled for now, as it is always smaller than phycal amount of memory...
@@ -286,14 +314,8 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) {
pl->usedMem = fpl->memActive + fpl->memWire;
- //currently unused, same as with arc, custom meter perhaps
- //sysctl(MIB_vm_stats_vm_v_inactive_count, 4, &(fpl->memInactive), &len, NULL, 0);
- //sysctl(MIB_vm_stats_vm_v_free_count, 4, &(fpl->memFree), &len, NULL, 0);
- //pl->freeMem = fpl->memInactive + fpl->memFree;
- //pl->freeMem *= pageSizeKb;
-
struct kvm_swap swap[16];
- int nswap = kvm_getswapinfo(fpl->kd, swap, sizeof(swap)/sizeof(swap[0]), 0);
+ int nswap = kvm_getswapinfo(fpl->kd, swap, ARRAYSIZE(swap), 0);
pl->totalSwap = 0;
pl->usedSwap = 0;
for (int i = 0; i < nswap; i++) {
@@ -302,11 +324,73 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) {
}
pl->totalSwap *= pageSizeKb;
pl->usedSwap *= pageSizeKb;
+}
+
+static void FreeBSDProcessList_scanTTYs(ProcessList* pl) {
+ FreeBSDProcessList* fpl = (FreeBSDProcessList*) pl;
+
+ // scan /dev/tty*
+ {
+ DIR* dirPtr = opendir("/dev");
+ if (!dirPtr)
+ return;
- pl->sharedMem = 0; // currently unused
+ int dirFd = dirfd(dirPtr);
+ if (dirFd < 0)
+ goto err1;
+
+ const struct dirent* entry;
+ while ((entry = readdir(dirPtr))) {
+ if (!String_startsWith(entry->d_name, "tty"))
+ continue;
+
+ struct stat info;
+ if (Compat_fstatat(dirFd, "/dev", entry->d_name, &info, 0) < 0)
+ continue;
+
+ if (!S_ISCHR(info.st_mode))
+ continue;
+
+ if (!Hashtable_get(fpl->ttys, info.st_rdev))
+ Hashtable_put(fpl->ttys, info.st_rdev, xStrdup(entry->d_name));
+ }
+
+err1:
+ closedir(dirPtr);
+ }
+
+ // scan /dev/pts/*
+ {
+ DIR* dirPtr = opendir("/dev/pts");
+ if (!dirPtr)
+ return;
+
+ int dirFd = dirfd(dirPtr);
+ if (dirFd < 0)
+ goto err2;
+
+ const struct dirent* entry;
+ while ((entry = readdir(dirPtr))) {
+ struct stat info;
+ if (Compat_fstatat(dirFd, "/dev/pts", entry->d_name, &info, 0) < 0)
+ continue;
+
+ if (!S_ISCHR(info.st_mode))
+ continue;
+
+ if (!Hashtable_get(fpl->ttys, info.st_rdev)) {
+ char* path;
+ xAsprintf(&path, "pts/%s", entry->d_name);
+ Hashtable_put(fpl->ttys, info.st_rdev, path);
+ }
+ }
+
+err2:
+ closedir(dirPtr);
+ }
}
-char* FreeBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) {
+static char* FreeBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc* kproc, int* basenameEnd) {
char** argv = kvm_getargv(kd, kproc, 0);
if (!argv) {
return xStrdup(kproc->ki_comm);
@@ -331,109 +415,109 @@ char* FreeBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, in
return comm;
}
-char* FreeBSDProcessList_readJailName(struct kinfo_proc* kproc) {
- int jid;
- struct iovec jiov[6];
- char* jname;
+static char* FreeBSDProcessList_readJailName(const struct kinfo_proc* kproc) {
+ char* jname = NULL;
char jnamebuf[MAXHOSTNAMELEN];
- if (kproc->ki_jid != 0 ){
+ if (kproc->ki_jid != 0 ) {
+ struct iovec jiov[6];
+
memset(jnamebuf, 0, sizeof(jnamebuf));
- *(const void **)&jiov[0].iov_base = "jid";
+IGNORE_WCASTQUAL_BEGIN
+ *(const void**)&jiov[0].iov_base = "jid";
jiov[0].iov_len = sizeof("jid");
- jiov[1].iov_base = &kproc->ki_jid;
+ jiov[1].iov_base = (void*) &kproc->ki_jid;
jiov[1].iov_len = sizeof(kproc->ki_jid);
- *(const void **)&jiov[2].iov_base = "name";
+ *(const void**)&jiov[2].iov_base = "name";
jiov[2].iov_len = sizeof("name");
jiov[3].iov_base = jnamebuf;
jiov[3].iov_len = sizeof(jnamebuf);
- *(const void **)&jiov[4].iov_base = "errmsg";
+ *(const void**)&jiov[4].iov_base = "errmsg";
jiov[4].iov_len = sizeof("errmsg");
jiov[5].iov_base = jail_errmsg;
jiov[5].iov_len = JAIL_ERRMSGLEN;
+IGNORE_WCASTQUAL_END
jail_errmsg[0] = 0;
- jid = jail_get(jiov, 6, 0);
+
+ int jid = jail_get(jiov, 6, 0);
if (jid < 0) {
- if (!jail_errmsg[0])
+ if (!jail_errmsg[0]) {
xSnprintf(jail_errmsg, JAIL_ERRMSGLEN, "jail_get: %s", strerror(errno));
- return NULL;
+ }
} else if (jid == kproc->ki_jid) {
jname = xStrdup(jnamebuf);
- if (jname == NULL)
- strerror_r(errno, jail_errmsg, JAIL_ERRMSGLEN);
- return jname;
- } else {
- return NULL;
}
} else {
- jnamebuf[0]='-';
- jnamebuf[1]='\0';
- jname = xStrdup(jnamebuf);
+ jname = xStrdup("-");
}
+
return jname;
}
-void ProcessList_goThroughEntries(ProcessList* this) {
- FreeBSDProcessList* fpl = (FreeBSDProcessList*) this;
- Settings* settings = this->settings;
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
+ FreeBSDProcessList* fpl = (FreeBSDProcessList*) super;
+ const Settings* settings = super->settings;
bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads;
openzfs_sysctl_updateArcStats(&fpl->zfs);
- FreeBSDProcessList_scanMemoryInfo(this);
- FreeBSDProcessList_scanCPUTime(this);
+ FreeBSDProcessList_scanMemoryInfo(super);
+ FreeBSDProcessList_scanCPUTime(super);
+
+ // in pause mode only gather global data for meters (CPU/memory/...)
+ if (pauseProcessUpdate) {
+ return;
+ }
+
+ if (settings->flags & PROCESS_FLAG_FREEBSD_TTY) {
+ FreeBSDProcessList_scanTTYs(super);
+ }
- int cpus = this->cpuCount;
int count = 0;
struct kinfo_proc* kprocs = kvm_getprocs(fpl->kd, KERN_PROC_PROC, 0, &count);
- struct timeval tv;
- gettimeofday(&tv, NULL);
-
for (int i = 0; i < count; i++) {
struct kinfo_proc* kproc = &kprocs[i];
bool preExisting = false;
- bool isIdleProcess = false;
- struct tm date;
- Process* proc = ProcessList_getProcess(this, kproc->ki_pid, &preExisting, (Process_New) FreeBSDProcess_new);
+ // TODO: bool isIdleProcess = false;
+ Process* proc = ProcessList_getProcess(super, kproc->ki_pid, &preExisting, FreeBSDProcess_new);
FreeBSDProcess* fp = (FreeBSDProcess*) proc;
- proc->show = ! ((hideKernelThreads && Process_isKernelThread(fp)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
+ proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
if (!preExisting) {
fp->jid = kproc->ki_jid;
proc->pid = kproc->ki_pid;
- if ( ! ((kproc->ki_pid == 0) || (kproc->ki_pid == 1) ) && kproc->ki_flag & P_SYSTEM)
- fp->kernel = 1;
- else
- fp->kernel = 0;
+ if ( ! ((kproc->ki_pid == 0) || (kproc->ki_pid == 1) ) && kproc->ki_flag & P_SYSTEM) {
+ fp->kernel = 1;
+ } else {
+ fp->kernel = 0;
+ }
proc->ppid = kproc->ki_ppid;
proc->tpgid = kproc->ki_tpgid;
proc->tgid = kproc->ki_pid;
proc->session = kproc->ki_sid;
- proc->tty_nr = kproc->ki_tdev;
proc->pgrp = kproc->ki_pgid;
proc->st_uid = kproc->ki_uid;
proc->starttime_ctime = kproc->ki_start.tv_sec;
- proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
- ProcessList_add((ProcessList*)this, proc);
+ Process_fillStarttimeBuffer(proc);
+ proc->user = UsersTable_getRef(super->usersTable, proc->st_uid);
+ ProcessList_add(super, proc);
proc->comm = FreeBSDProcessList_readProcessName(fpl->kd, kproc, &proc->basenameOffset);
fp->jname = FreeBSDProcessList_readJailName(kproc);
} else {
- if(fp->jid != kproc->ki_jid) {
+ if (fp->jid != kproc->ki_jid) {
// process can enter jail anytime
fp->jid = kproc->ki_jid;
free(fp->jname);
fp->jname = FreeBSDProcessList_readJailName(kproc);
}
- if (proc->ppid != kproc->ki_ppid) {
- // if there are reapers in the system, process can get reparented anytime
- proc->ppid = kproc->ki_ppid;
- }
- if(proc->st_uid != kproc->ki_uid) {
+ // if there are reapers in the system, process can get reparented anytime
+ proc->ppid = kproc->ki_ppid;
+ if (proc->st_uid != kproc->ki_uid) {
// some processes change users (eg. to lower privs)
proc->st_uid = kproc->ki_uid;
- proc->user = UsersTable_getRef(this->usersTable, proc->st_uid);
+ proc->user = UsersTable_getRef(super->usersTable, proc->st_uid);
}
if (settings->updateProcessNames) {
free(proc->comm);
@@ -442,21 +526,23 @@ void ProcessList_goThroughEntries(ProcessList* this) {
}
// from FreeBSD source /src/usr.bin/top/machine.c
- proc->m_size = kproc->ki_size / 1024 / pageSizeKb;
+ proc->m_virt = kproc->ki_size / pageSize;
proc->m_resident = kproc->ki_rssize;
- proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem) * 100.0;
proc->nlwp = kproc->ki_numthreads;
proc->time = (kproc->ki_runtime + 5000) / 10000;
proc->percent_cpu = 100.0 * ((double)kproc->ki_pctcpu / (double)kernelFScale);
- proc->percent_mem = 100.0 * (proc->m_resident * PAGE_SIZE_KB) / (double)(this->totalMem);
-
- if (proc->percent_cpu > 0.1) {
- // system idle process should own all CPU time left regardless of CPU count
- if ( strcmp("idle", kproc->ki_comm) == 0 ) {
- isIdleProcess = true;
- }
- }
+ proc->percent_mem = 100.0 * (proc->m_resident * pageSizeKb) / (double)(super->totalMem);
+
+ /*
+ * TODO
+ * if (proc->percent_cpu > 0.1) {
+ * // system idle process should own all CPU time left regardless of CPU count
+ * if ( strcmp("idle", kproc->ki_comm) == 0 ) {
+ * isIdleProcess = true;
+ * }
+ * }
+ */
proc->priority = kproc->ki_pri.pri_level - PZERO;
@@ -481,16 +567,16 @@ void ProcessList_goThroughEntries(ProcessList* this) {
default: proc->state = '?';
}
- if (Process_isKernelThread(fp)) {
- this->kernelThreads++;
+ if (settings->flags & PROCESS_FLAG_FREEBSD_TTY) {
+ fp->ttyPath = (kproc->ki_tdev == NODEV) ? nodevStr : Hashtable_get(fpl->ttys, kproc->ki_tdev);
}
- (void) localtime_r((time_t*) &proc->starttime_ctime, &date);
- strftime(proc->starttime_show, 7, ((proc->starttime_ctime > tv.tv_sec - 86400) ? "%R " : "%b%d "), &date);
+ if (Process_isKernelThread(proc))
+ super->kernelThreads++;
- this->totalTasks++;
+ super->totalTasks++;
if (proc->state == 'R')
- this->runningTasks++;
+ super->runningTasks++;
proc->updated = true;
}
}
diff --git a/freebsd/FreeBSDProcessList.h b/freebsd/FreeBSDProcessList.h
index 281bf3e..f18275d 100644
--- a/freebsd/FreeBSDProcessList.h
+++ b/freebsd/FreeBSDProcessList.h
@@ -3,19 +3,21 @@
/*
htop - FreeBSDProcessList.h
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <kvm.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "Hashtable.h"
+#include "ProcessList.h"
+#include "UsersTable.h"
#include "zfs/ZfsArcStats.h"
-#include <kvm.h>
-#include <sys/param.h>
-#include <sys/jail.h>
-#include <sys/uio.h>
-#include <sys/resource.h>
-#define JAIL_ERRMSGLEN 1024
+#define JAIL_ERRMSGLEN 1024
extern char jail_errmsg[JAIL_ERRMSGLEN];
typedef struct CPUData_ {
@@ -40,11 +42,13 @@ typedef struct FreeBSDProcessList_ {
CPUData* cpus;
- unsigned long *cp_time_o;
- unsigned long *cp_time_n;
+ Hashtable* ttys;
- unsigned long *cp_times_o;
- unsigned long *cp_times_n;
+ unsigned long* cp_time_o;
+ unsigned long* cp_time_n;
+
+ unsigned long* cp_times_o;
+ unsigned long* cp_times_n;
} FreeBSDProcessList;
@@ -52,10 +56,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
void ProcessList_delete(ProcessList* this);
-char* FreeBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd);
-
-char* FreeBSDProcessList_readJailName(struct kinfo_proc* kproc);
-
-void ProcessList_goThroughEntries(ProcessList* this);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif
diff --git a/freebsd/Platform.c b/freebsd/Platform.c
index c51b37c..bc77cf4 100644
--- a/freebsd/Platform.c
+++ b/freebsd/Platform.c
@@ -1,35 +1,53 @@
/*
htop - freebsd/Platform.c
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
-#include "Meter.h"
+
+#include <devstat.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#include <net/if.h>
+#include <net/if_mib.h>
+#include <sys/_types.h>
+#include <sys/devicestat.h>
+#include <sys/param.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <vm/vm_param.h>
+
#include "CPUMeter.h"
+#include "ClockMeter.h"
+#include "DateMeter.h"
+#include "DateTimeMeter.h"
+#include "DiskIOMeter.h"
+#include "FreeBSDProcess.h"
+#include "FreeBSDProcessList.h"
+#include "HostnameMeter.h"
+#include "LoadAverageMeter.h"
+#include "Macros.h"
#include "MemoryMeter.h"
+#include "Meter.h"
+#include "NetworkIOMeter.h"
+#include "ProcessList.h"
+#include "Settings.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
-#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
-#include "ClockMeter.h"
-#include "HostnameMeter.h"
+#include "XUtils.h"
#include "zfs/ZfsArcMeter.h"
#include "zfs/ZfsCompressedArcMeter.h"
-#include "FreeBSDProcess.h"
-#include "FreeBSDProcessList.h"
-
-#include <sys/types.h>
-#include <sys/sysctl.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <vm/vm_param.h>
-#include <time.h>
-#include <math.h>
-ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
+ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
@@ -70,15 +88,13 @@ const SignalItem Platform_signals[] = {
{ .name = "33 SIGLIBRT", .number = 33 },
};
-const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
-
-void Platform_setBindings(Htop_Action* keys) {
- (void) keys;
-}
+const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
-MeterClass* Platform_meterTypes[] = {
+const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
+ &DateMeter_class,
+ &DateTimeMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
@@ -89,16 +105,37 @@ MeterClass* Platform_meterTypes[] = {
&HostnameMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
+ &AllCPUs4Meter_class,
+ &AllCPUs8Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
+ &LeftCPUs4Meter_class,
+ &RightCPUs4Meter_class,
+ &LeftCPUs8Meter_class,
+ &RightCPUs8Meter_class,
&BlankMeter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
+ &DiskIOMeter_class,
+ &NetworkIOMeter_class,
NULL
};
+void Platform_init(void) {
+ /* no platform-specific setup needed */
+}
+
+void Platform_done(void) {
+ /* no platform-specific cleanup needed */
+}
+
+void Platform_setBindings(Htop_Action* keys) {
+ /* no platform-specific key bindings */
+ (void) keys;
+}
+
int Platform_getUptime() {
struct timeval bootTime, currTime;
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
@@ -141,15 +178,15 @@ int Platform_getMaxPid() {
}
double Platform_setCPUValues(Meter* this, int cpu) {
- FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
+ const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl;
int cpus = this->pl->cpuCount;
- CPUData* cpuData;
+ const CPUData* cpuData;
if (cpus == 1) {
- // single CPU box has everything in fpl->cpus[0]
- cpuData = &(fpl->cpus[0]);
+ // single CPU box has everything in fpl->cpus[0]
+ cpuData = &(fpl->cpus[0]);
} else {
- cpuData = &(fpl->cpus[cpu]);
+ cpuData = &(fpl->cpus[cpu]);
}
double percent;
@@ -160,25 +197,25 @@ double Platform_setCPUValues(Meter* this, int cpu) {
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
- Meter_setItems(this, 4);
- percent = v[0]+v[1]+v[2]+v[3];
+ this->curItems = 4;
+ percent = v[0] + v[1] + v[2] + v[3];
} else {
v[2] = cpuData->systemAllPercent;
- Meter_setItems(this, 3);
- percent = v[0]+v[1]+v[2];
+ this->curItems = 3;
+ percent = v[0] + v[1] + v[2];
}
percent = CLAMP(percent, 0.0, 100.0);
- if (isnan(percent)) percent = 0.0;
- v[CPU_METER_FREQUENCY] = -1;
+ v[CPU_METER_FREQUENCY] = NAN;
+ v[CPU_METER_TEMPERATURE] = NAN;
return percent;
}
void Platform_setMemoryValues(Meter* this) {
// TODO
- ProcessList* pl = (ProcessList*) this->pl;
+ const ProcessList* pl = this->pl;
this->total = pl->totalMem;
this->values[0] = pl->usedMem;
@@ -187,28 +224,151 @@ void Platform_setMemoryValues(Meter* this) {
}
void Platform_setSwapValues(Meter* this) {
- ProcessList* pl = (ProcessList*) this->pl;
+ const ProcessList* pl = this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
}
void Platform_setZfsArcValues(Meter* this) {
- FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
+ const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(fpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
- FreeBSDProcessList* fpl = (FreeBSDProcessList*) this->pl;
+ const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(fpl->zfs));
}
-void Platform_setTasksValues(Meter* this) {
- // TODO
+char* Platform_getProcessEnv(pid_t pid) {
+ int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_ENV, pid };
+
+ size_t capacity = ARG_MAX;
+ char* env = xMalloc(capacity);
+
+ int err = sysctl(mib, 4, env, &capacity, NULL, 0);
+ if (err || capacity == 0) {
+ free(env);
+ return NULL;
+ }
+
+ if (env[capacity - 1] || env[capacity - 2]) {
+ env = xRealloc(env, capacity + 2);
+ env[capacity] = 0;
+ env[capacity + 1] = 0;
+ }
+
+ return env;
}
-char* Platform_getProcessEnv(pid_t pid) {
- // TODO
- return NULL;
+char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
+ (void)pid;
+ (void)inode;
+ return NULL;
+}
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
+ (void)pid;
+ return NULL;
+}
+
+bool Platform_getDiskIO(DiskIOData* data) {
+
+ if (devstat_checkversion(NULL) < 0)
+ return false;
+
+ struct devinfo info = { 0 };
+ struct statinfo current = { .dinfo = &info };
+
+ // get number of devices
+ if (devstat_getdevs(NULL, &current) < 0)
+ return false;
+
+ int count = current.dinfo->numdevs;
+
+ unsigned long int bytesReadSum = 0, bytesWriteSum = 0, timeSpendSum = 0;
+
+ // get data
+ for (int i = 0; i < count; i++) {
+ uint64_t bytes_read, bytes_write;
+ long double busy_time;
+
+ devstat_compute_statistics(&current.dinfo->devices[i],
+ NULL,
+ 1.0,
+ DSM_TOTAL_BYTES_READ, &bytes_read,
+ DSM_TOTAL_BYTES_WRITE, &bytes_write,
+ DSM_TOTAL_BUSY_TIME, &busy_time,
+ DSM_NONE);
+
+ bytesReadSum += bytes_read;
+ bytesWriteSum += bytes_write;
+ timeSpendSum += 1000 * busy_time;
+ }
+
+ data->totalBytesRead = bytesReadSum;
+ data->totalBytesWritten = bytesWriteSum;
+ data->totalMsTimeSpend = timeSpendSum;
+ return true;
+}
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted) {
+ int r;
+
+ // get number of interfaces
+ int count;
+ size_t countLen = sizeof(count);
+ const int countMib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_SYSTEM, IFMIB_IFCOUNT };
+
+ r = sysctl(countMib, ARRAYSIZE(countMib), &count, &countLen, NULL, 0);
+ if (r < 0)
+ return false;
+
+
+ unsigned long int bytesReceivedSum = 0, packetsReceivedSum = 0, bytesTransmittedSum = 0, packetsTransmittedSum = 0;
+
+ for (int i = 1; i <= count; i++) {
+ struct ifmibdata ifmd;
+ size_t ifmdLen = sizeof(ifmd);
+
+ const int dataMib[] = { CTL_NET, PF_LINK, NETLINK_GENERIC, IFMIB_IFDATA, i, IFDATA_GENERAL };
+
+ r = sysctl(dataMib, ARRAYSIZE(dataMib), &ifmd, &ifmdLen, NULL, 0);
+ if (r < 0)
+ continue;
+
+ if (ifmd.ifmd_flags & IFF_LOOPBACK)
+ continue;
+
+ bytesReceivedSum += ifmd.ifmd_data.ifi_ibytes;
+ packetsReceivedSum += ifmd.ifmd_data.ifi_ipackets;
+ bytesTransmittedSum += ifmd.ifmd_data.ifi_obytes;
+ packetsTransmittedSum += ifmd.ifmd_data.ifi_opackets;
+ }
+
+ *bytesReceived = bytesReceivedSum;
+ *packetsReceived = packetsReceivedSum;
+ *bytesTransmitted = bytesTransmittedSum;
+ *packetsTransmitted = packetsTransmittedSum;
+ return true;
+}
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC) {
+ int life;
+ size_t life_len = sizeof(life);
+ if (sysctlbyname("hw.acpi.battery.life", &life, &life_len, NULL, 0) == -1)
+ *percent = NAN;
+ else
+ *percent = life;
+
+ int acline;
+ size_t acline_len = sizeof(acline);
+ if (sysctlbyname("hw.acpi.acline", &acline, &acline_len, NULL, 0) == -1)
+ *isOnAC = AC_ERROR;
+ else
+ *isOnAC = acline == 0 ? AC_ABSENT : AC_PRESENT;
}
diff --git a/freebsd/Platform.h b/freebsd/Platform.h
index 1a18055..5b3b019 100644
--- a/freebsd/Platform.h
+++ b/freebsd/Platform.h
@@ -3,14 +3,22 @@
/*
htop - freebsd/Platform.h
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+#include <sys/types.h>
+
#include "Action.h"
#include "BatteryMeter.h"
+#include "DiskIOMeter.h"
+#include "Meter.h"
+#include "Process.h"
+#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
+
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[];
@@ -21,15 +29,19 @@ extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
-void Platform_setBindings(Htop_Action* keys);
+extern const MeterClass* const Platform_meterTypes[];
-extern MeterClass* Platform_meterTypes[];
+void Platform_init(void);
+
+void Platform_done(void);
+
+void Platform_setBindings(Htop_Action* keys);
-int Platform_getUptime();
+int Platform_getUptime(void);
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
-int Platform_getMaxPid();
+int Platform_getMaxPid(void);
double Platform_setCPUValues(Meter* this, int cpu);
@@ -41,8 +53,19 @@ void Platform_setZfsArcValues(Meter* this);
void Platform_setZfsCompressedArcValues(Meter* this);
-void Platform_setTasksValues(Meter* this);
-
char* Platform_getProcessEnv(pid_t pid);
+char* Platform_getInodeFilename(pid_t pid, ino_t inode);
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
+
+bool Platform_getDiskIO(DiskIOData* data);
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted);
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC);
+
#endif
diff --git a/htop.1.in b/htop.1.in
index c5b131b..0205a99 100644
--- a/htop.1.in
+++ b/htop.1.in
@@ -4,7 +4,7 @@ htop \- interactive process viewer
.SH "SYNOPSIS"
.LP
.B htop
-.RB [ \-dChpustv ]
+.RB [ \-dCFhpustvH ]
.SH "DESCRIPTION"
.LP
.B htop
@@ -36,6 +36,9 @@ Start
.B htop
in monochrome mode
.TP
+\fB\-F \-\-filter=FILTER
+Filter processes by command
+.TP
\fB\-h \-\-help
Display a help message and exit
.TP
@@ -51,11 +54,17 @@ Show only the processes of a given user
\fB\-U \-\-no-unicode\fR
Do not use unicode but ASCII characters for graph meters
.TP
-\fB\-v \-\-version
+\fB\-M \-\-no-mouse\fR
+Disable support of mouse control
+.TP
+\fB\-V \-\-version
Output version information and exit
.TP
\fB\-t \-\-tree
Show processes in tree view
+.TP
+\fB\-H \-\-highlight-changes=DELAY\fR
+Highlight new and old processes
.SH "INTERACTIVE COMMANDS"
.LP
The following commands are supported while in
@@ -113,6 +122,13 @@ update of system calls issued by the process.
Display open files for a process: if lsof(1) is installed, pressing this key
will display the list of file descriptors opened by the process.
.TP
+.B w
+Display the command line of the selected process in a separate screen, wrapped
+onto multiple lines as needed.
+.TP
+.B x
+Display the active file locks of the selected process in a separate screen.
+.TP
.B F1, h, ?
Go to the help screen
.TP
@@ -125,6 +141,10 @@ select which columns are displayed, in which order.
Incrementally search the command lines of all the displayed processes. The
currently selected (highlighted) command will update as you type. While in
search mode, pressing F3 will cycle through matching occurrences.
+
+Alternatively the search can be started by simply typing the command
+you are looking for, although for the first character normal key
+bindings take precedence.
.TP
.B F4, \\\\
Incremental process filtering: type in part of a process command line and
@@ -199,6 +219,12 @@ userspace processes in the process list. (This is a toggle key.)
.B p
Show full paths to running programs, where applicable. (This is a toggle key.)
.TP
+.B Z
+Pause/resume process updates.
+.TP
+.B m
+Merge exe, comm and cmdline, where applicable. (This is a toggle key.)
+.TP
.B Ctrl-L
Refresh: redraw screen and recalculate values.
.TP
@@ -219,7 +245,18 @@ main screen, it is shown below in parenthesis.
.LP
.TP 5
.B Command
-The full command line of the process (i.e. program name and arguments).
+The full command line of the process (i.e. program name and arguments). If the
+option 'Merge exe, comm and cmdline in Command' (toggled by the 'm' key) is set,
+and if readable, the executable path (/proc/[pid]/exe) and the command name
+(/proc/[pid]/comm) are also shown merged with the command line.
+.TP
+.B Comm
+The command name of the process obtained from /proc/[pid]/comm, if readable.
+.TP
+.B Exe
+The abbreviated basename of the executable of the process, obtained from
+/proc/[pid]/exe, if readable. htop is able to read this file on linux for ALL
+the processes only if it has the capability CAP_SYS_PTRACE or root privileges.
.TP
.B PID
The process ID.
@@ -293,7 +330,7 @@ The time the process was started.
.B PROCESSOR (CPU)
The ID of the CPU the process last executed on.
.TP
-.B M_SIZE (VIRT)
+.B M_VIRT (VIRT)
The size of the virtual memory of the process.
.TP
.B M_RESIDENT (RES)
@@ -317,6 +354,17 @@ The library size of the process.
.B M_DT (DIRTY)
The size of the dirty pages of the process.
.TP
+.B M_SWAP (SWAP)
+The size of the process's swapped pages.
+.TP
+.B M_PSS (PSS)
+The proportional set size, same as M_RESIDENT but each page is divided by the
+number of processes sharing it.
+.TP
+.B M_M_PSSWP (PSSWP)
+The proportional swap share of this mapping, unlike M_SWAP this does not take
+into account swapped out page of underlying shmem objects.
+.TP
.B ST_UID (UID)
The user ID of the process owner.
.TP
@@ -386,6 +434,9 @@ Which cgroup the process is in.
.B OOM
OOM killer score.
.TP
+.B CTXT
+Incremental sum of voluntary and nonvoluntary context switches.
+.TP
.B IO_PRIORITY (IO)
The I/O scheduling class followed by the priority if the class supports it:
\fBR\fR for Realtime
@@ -401,6 +452,12 @@ The percentage of time spent waiting for the completion of synchronous block I/O
.B PERCENT_SWAP_DELAY (SWAPD%)
The percentage of time spent swapping in pages. Requires CAP_NET_ADMIN.
.TP
+.B COMM
+The command name for the process. Requires Linux kernel 2.6.33 or newer.
+.TP
+.B EXE
+The executable file of the process as reported by the kernel. Requires CAP_SYS_PTRACE and PTRACE_MODE_READ_FSCRED.
+.TP
.B All other flags
Currently unsupported (always displays '-').
.SH "CONFIG FILE"
diff --git a/htop.c b/htop.c
index 2cf2602..23da081 100644
--- a/htop.c
+++ b/htop.c
@@ -1,63 +1,71 @@
/*
htop - htop.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "config.h"
-
-#include "FunctionBar.h"
-#include "Hashtable.h"
-#include "ColumnsPanel.h"
-#include "CRT.h"
-#include "MainPanel.h"
-#include "ProcessList.h"
-#include "ScreenManager.h"
-#include "Settings.h"
-#include "UsersTable.h"
-#include "Platform.h"
+#include "config.h" // IWYU pragma: keep
+#include <assert.h>
#include <getopt.h>
#include <locale.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
-//#link m
+#include "Action.h"
+#include "CRT.h"
+#include "Hashtable.h"
+#include "Header.h"
+#include "IncSet.h"
+#include "MainPanel.h"
+#include "MetersPanel.h"
+#include "Panel.h"
+#include "Platform.h"
+#include "Process.h"
+#include "ProcessList.h"
+#include "ProvideCurses.h"
+#include "ScreenManager.h"
+#include "Settings.h"
+#include "UsersTable.h"
+#include "XUtils.h"
-static void printVersionFlag() {
- fputs("htop " VERSION "\n", stdout);
- exit(0);
+static void printVersionFlag(void) {
+ fputs(PACKAGE " " VERSION "\n", stdout);
}
-static void printHelpFlag() {
- fputs("htop " VERSION "\n"
- "Released under the GNU GPL.\n\n"
- "-C --no-color Use a monochrome color scheme\n"
- "-m --no-mouse Disable the mouse\n"
- "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
- "-h --help Print this help screen\n"
- "-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
- "-t --tree Show the tree view by default\n"
- "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
- "-U --no-unicode Do not use unicode but plain ASCII\n"
- "-p --pid=PID,[,PID,PID...] Show only the given PIDs\n"
- "-v --version Print version info\n"
+static void printHelpFlag(void) {
+ fputs(PACKAGE " " VERSION "\n"
+ COPYRIGHT "\n"
+ "Released under the GNU GPLv2.\n\n"
+ "-C --no-color Use a monochrome color scheme\n"
+ "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
+ "-F --filter=FILTER Show only the commands matching the given filter\n"
+ "-h --help Print this help screen\n"
+ "-H --highlight-changes[=DELAY] Highlight new and old processes\n"
+ "-M --no-mouse Disable the mouse\n"
+ "-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
+ "-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
+ "-t --tree Show the tree view by default\n"
+ "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
+ "-U --no-unicode Do not use unicode but plain ASCII\n"
+ "-V --version Print version info\n"
"\n"
"Long options may be passed with a single dash.\n\n"
- "Press F1 inside htop for online help.\n"
- "See 'man htop' for more information.\n",
+ "Press F1 inside " PACKAGE " for online help.\n"
+ "See 'man " PACKAGE "' for more information.\n",
stdout);
- exit(0);
}
// ----------------------------------------
typedef struct CommandLineSettings_ {
Hashtable* pidMatchList;
+ char* commFilter;
uid_t userId;
int sortKey;
int delay;
@@ -65,58 +73,74 @@ typedef struct CommandLineSettings_ {
bool enableMouse;
bool treeView;
bool allowUnicode;
+ bool highlightChanges;
+ int highlightDelaySecs;
} CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) {
CommandLineSettings flags = {
.pidMatchList = NULL,
- .userId = -1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2))
+ .commFilter = NULL,
+ .userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2))
.sortKey = 0,
.delay = -1,
.useColors = true,
.enableMouse = true,
.treeView = false,
.allowUnicode = true,
+ .highlightChanges = false,
+ .highlightDelaySecs = -1,
};
static struct option long_opts[] =
{
{"help", no_argument, 0, 'h'},
- {"version", no_argument, 0, 'v'},
+ {"version", no_argument, 0, 'V'},
{"delay", required_argument, 0, 'd'},
{"sort-key", required_argument, 0, 's'},
{"user", optional_argument, 0, 'u'},
{"no-color", no_argument, 0, 'C'},
{"no-colour", no_argument, 0, 'C'},
- {"no-mouse", no_argument, 0, 'm'},
+ {"no-mouse", no_argument, 0, 'M'},
{"no-unicode", no_argument, 0, 'U'},
{"tree", no_argument, 0, 't'},
{"pid", required_argument, 0, 'p'},
+ {"filter", required_argument, 0, 'F'},
+ {"highlight-changes", optional_argument, 0, 'H'},
{0,0,0,0}
};
int opt, opti=0;
/* Parse arguments */
- while ((opt = getopt_long(argc, argv, "hvmCs:td:u::Up:", long_opts, &opti))) {
+ while ((opt = getopt_long(argc, argv, "hVMCs:td:u::Up:F:H::", long_opts, &opti))) {
if (opt == EOF) break;
switch (opt) {
case 'h':
printHelpFlag();
- break;
- case 'v':
+ exit(0);
+ case 'V':
printVersionFlag();
- break;
+ exit(0);
case 's':
- if (strcmp(optarg, "help") == 0) {
+ assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
+ if (String_eq(optarg, "help")) {
for (int j = 1; j < Platform_numberOfFields; j++) {
const char* name = Process_fields[j].name;
if (name) printf ("%s\n", name);
}
exit(0);
}
- flags.sortKey = ColumnsPanel_fieldNameToIndex(optarg);
- if (flags.sortKey == -1) {
+ flags.sortKey = 0;
+ for (int j = 1; j < Platform_numberOfFields; j++) {
+ if (Process_fields[j].name == NULL)
+ continue;
+ if (String_eq(optarg, Process_fields[j].name)) {
+ flags.sortKey = j;
+ break;
+ }
+ }
+ if (flags.sortKey == 0) {
fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
exit(1);
}
@@ -131,25 +155,25 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
}
break;
case 'u':
- if (!optarg && optind < argc && argv[optind] != NULL &&
+ {
+ const char *username = optarg;
+ if (!username && optind < argc && argv[optind] != NULL &&
(argv[optind][0] != '\0' && argv[optind][0] != '-')) {
- optarg = argv[optind++];
+ username = argv[optind++];
}
- if (!optarg) {
- optarg = getenv("USER");
+ if (!username) {
flags.userId = geteuid();
- }
-
- if (!Action_setUserOnly(optarg, &(flags.userId))) {
- fprintf(stderr, "Error: invalid user \"%s\".\n", optarg);
+ } else if (!Action_setUserOnly(username, &(flags.userId))) {
+ fprintf(stderr, "Error: invalid user \"%s\".\n", username);
exit(1);
}
break;
+ }
case 'C':
flags.useColors = false;
break;
- case 'm':
+ case 'M':
flags.enableMouse = false;
break;
case 'U':
@@ -159,6 +183,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
flags.treeView = true;
break;
case 'p': {
+ assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
char* argCopy = xStrdup(optarg);
char* saveptr;
char* pid = strtok_r(argCopy, ",", &saveptr);
@@ -169,6 +194,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
while(pid) {
unsigned int num_pid = atoi(pid);
+ // deepcode ignore CastIntegerToAddress: we just want a non-NUll pointer here
Hashtable_put(flags.pidMatchList, num_pid, (void *) 1);
pid = strtok_r(NULL, ",", &saveptr);
}
@@ -176,6 +202,30 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
break;
}
+ case 'F': {
+ assert(optarg);
+ flags.commFilter = xStrdup(optarg);
+
+ break;
+ }
+ case 'H': {
+ const char *delay = optarg;
+ if (!delay && optind < argc && argv[optind] != NULL &&
+ (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
+ delay = argv[optind++];
+ }
+ if (delay) {
+ if (sscanf(delay, "%16d", &(flags.highlightDelaySecs)) == 1) {
+ if (flags.highlightDelaySecs < 1)
+ flags.highlightDelaySecs = 1;
+ } else {
+ fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay);
+ exit(1);
+ }
+ }
+ flags.highlightChanges = true;
+ break;
+ }
default:
exit(1);
}
@@ -193,24 +243,35 @@ static void millisleep(unsigned long millisec) {
}
}
+static void setCommFilter(State* state, char** commFilter) {
+ MainPanel* panel = (MainPanel*)state->panel;
+ ProcessList* pl = state->pl;
+ IncSet* inc = panel->inc;
+ size_t maxlen = sizeof(inc->modes[INC_FILTER].buffer) - 1;
+ char* buffer = inc->modes[INC_FILTER].buffer;
+
+ strncpy(buffer, *commFilter, maxlen);
+ buffer[maxlen] = 0;
+ inc->modes[INC_FILTER].index = strlen(buffer);
+ inc->filtering = true;
+ pl->incFilter = IncSet_filter(inc);
+
+ free(*commFilter);
+ *commFilter = NULL;
+}
+
int main(int argc, char** argv) {
- char *lc_ctype = getenv("LC_CTYPE");
- if(lc_ctype != NULL)
- setlocale(LC_CTYPE, lc_ctype);
- else if ((lc_ctype = getenv("LC_ALL")))
+ /* initialize locale */
+ const char* lc_ctype;
+ if ((lc_ctype = getenv("LC_CTYPE")) || (lc_ctype = getenv("LC_ALL")))
setlocale(LC_CTYPE, lc_ctype);
else
setlocale(LC_CTYPE, "");
- CommandLineSettings flags = parseArguments(argc, argv); // may exit()
+ CommandLineSettings flags = parseArguments(argc, argv);
-#ifdef HTOP_LINUX
- if (access(PROCDIR, R_OK) != 0) {
- fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
- exit(1);
- }
-#endif
+ Platform_init();
Process_setupColumnWidths();
@@ -232,19 +293,23 @@ int main(int argc, char** argv) {
settings->enableMouse = false;
if (flags.treeView)
settings->treeView = true;
+ if (flags.highlightChanges)
+ settings->highlightChanges = true;
+ if (flags.highlightDelaySecs != -1)
+ settings->highlightDelaySecs = flags.highlightDelaySecs;
+ if (flags.sortKey > 0) {
+ settings->sortKey = flags.sortKey;
+ settings->treeView = false;
+ settings->direction = 1;
+ }
- CRT_init(settings->delay, settings->colorScheme, flags.allowUnicode);
+ CRT_init(&(settings->delay), settings->colorScheme, flags.allowUnicode);
MainPanel* panel = MainPanel_new();
ProcessList_setPanel(pl, (Panel*) panel);
MainPanel_updateTreeFunctions(panel, settings->treeView);
- if (flags.sortKey > 0) {
- settings->sortKey = flags.sortKey;
- settings->treeView = false;
- settings->direction = 1;
- }
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));
State state = {
@@ -253,15 +318,20 @@ int main(int argc, char** argv) {
.pl = pl,
.panel = (Panel*) panel,
.header = header,
+ .pauseProcessUpdate = false,
+ .hideProcessSelection = false,
};
+
MainPanel_setState(panel, &state);
+ if (flags.commFilter)
+ setCommFilter(&state, &(flags.commFilter));
- ScreenManager* scr = ScreenManager_new(0, header->height, 0, -1, HORIZONTAL, header, settings, true);
+ ScreenManager* scr = ScreenManager_new(header, settings, &state, true);
ScreenManager_add(scr, (Panel*) panel, -1);
- ProcessList_scan(pl);
+ ProcessList_scan(pl, false);
millisleep(75);
- ProcessList_scan(pl);
+ ProcessList_scan(pl, false);
ScreenManager_run(scr, NULL, NULL);
@@ -270,6 +340,8 @@ int main(int argc, char** argv) {
attroff(CRT_colors[RESET_COLOR]);
refresh();
+ Platform_done();
+
CRT_done();
if (settings->changed)
Settings_write(settings);
@@ -277,12 +349,13 @@ int main(int argc, char** argv) {
ProcessList_delete(pl);
ScreenManager_delete(scr);
+ MetersPanel_cleanup();
UsersTable_delete(ut);
Settings_delete(settings);
- if(flags.pidMatchList) {
+ if (flags.pidMatchList)
Hashtable_delete(flags.pidMatchList);
- }
+
return 0;
}
diff --git a/htop.png b/htop.png
index b040135..90020ad 100644
--- a/htop.png
+++ b/htop.png
Binary files differ
diff --git a/htop.svg b/htop.svg
new file mode 100644
index 0000000..80da71b
--- /dev/null
+++ b/htop.svg
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="30.398mm" height="30.399mm" version="1.1" viewBox="0 0 30.398 30.399" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <linearGradient id="aj" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(41.404 221.45)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
+ <linearGradient id="c">
+ <stop stop-color="#363d36" offset="0"/>
+ <stop stop-color="#565d56" offset=".2"/>
+ <stop stop-color="#6c766c" offset=".8"/>
+ <stop stop-color="#8c968c" offset="1"/>
+ </linearGradient>
+ <filter id="f" x="-.063963" y="-.025049" width="1.1279" height="1.0501" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.072789412"/>
+ </filter>
+ <linearGradient id="ai" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(41.508 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <linearGradient id="a">
+ <stop stop-color="#8bf18b" offset="0"/>
+ <stop stop-color="#5bc15b" offset=".2"/>
+ <stop stop-color="#47a347" offset=".8"/>
+ <stop stop-color="#177317" offset="1"/>
+ </linearGradient>
+ <filter id="l" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.065909587"/>
+ </filter>
+ <linearGradient id="d" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(24.234,-9.2667)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
+ <filter id="e" x="-.063963" y="-.025049" width="1.1279" height="1.0501" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.072789412"/>
+ </filter>
+ <linearGradient id="ah" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(44.242 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <filter id="k" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.065909587"/>
+ </filter>
+ <linearGradient id="ag" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(46.976 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <filter id="j" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.065909587"/>
+ </filter>
+ <linearGradient id="af" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(49.709 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <filter id="am" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.065909587"/>
+ </filter>
+ <linearGradient id="ae" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(52.443 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <filter id="al" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.065909587"/>
+ </filter>
+ <linearGradient id="ad" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(55.177 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <filter id="ak" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.065909587"/>
+ </filter>
+ <linearGradient id="ac" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(57.91 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <filter id="i" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.065909587"/>
+ </filter>
+ <linearGradient id="ab" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(60.644 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
+ <linearGradient id="b">
+ <stop stop-color="#fe7f7f" offset="0"/>
+ <stop stop-color="#ce4f4f" offset=".2"/>
+ <stop stop-color="#b23030" offset=".8"/>
+ <stop stop-color="#920000" offset="1"/>
+ </linearGradient>
+ <filter id="h" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.065909587"/>
+ </filter>
+ <linearGradient id="aa" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(63.376 221.43)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
+ <filter id="g" x="-.08928" y="-.022545" width="1.1786" height="1.0451" color-interpolation-filters="sRGB">
+ <feGaussianBlur stdDeviation="0.065909587"/>
+ </filter>
+ <linearGradient id="z" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(41.404 210.02)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
+ <linearGradient id="y" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(41.508 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <linearGradient id="x" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(44.242 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <linearGradient id="w" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(46.976 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <linearGradient id="v" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(57.91 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <linearGradient id="u" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(63.376 210)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
+ <linearGradient id="t" x1="42.304" x2="42.245" y1="57.522" y2="50.607" gradientTransform="translate(41.404 232.87)" gradientUnits="userSpaceOnUse" xlink:href="#c"/>
+ <linearGradient id="s" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(41.508 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <linearGradient id="r" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(44.242 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <linearGradient id="q" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(46.976 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ <linearGradient id="p" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(60.644 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
+ <linearGradient id="o" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(63.376 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
+ <linearGradient id="n" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(57.91 232.85)" gradientUnits="userSpaceOnUse" xlink:href="#b"/>
+ <linearGradient id="m" x1="44.761" x2="44.761" y1="50.493" y2="57.517" gradientTransform="translate(60.644 210)" gradientUnits="userSpaceOnUse" xlink:href="#a"/>
+ </defs>
+ <g transform="translate(.08011 -.58599)">
+ <g transform="matrix(.98769 0 0 .99741 -80.905 -258.93)" fill-rule="evenodd">
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m82.188 271.97 0.0251 6.963 2.7061-8e-3v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#aj)" filter="url(#f)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m85.401 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ai)" filter="url(#l)"/>
+ <path transform="matrix(-.99856 0 0 1.2772 177.16 218.32)" d="m65.017 41.255 0.02508 6.963 2.7061-0.0084v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#d)" filter="url(#e)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m88.135 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ah)" filter="url(#k)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m90.868 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ag)" filter="url(#j)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m93.602 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#af)" filter="url(#am)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m96.335 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ae)" filter="url(#al)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m99.069 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ad)" filter="url(#ak)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m101.8 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ac)" filter="url(#i)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m104.54 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#ab)" filter="url(#h)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -76.353)" d="m107.27 271.95v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#aa)" filter="url(#g)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m82.188 260.54 0.0251 6.963 2.7061-8e-3v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#z)" filter="url(#f)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m85.401 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#y)" filter="url(#l)"/>
+ <path transform="matrix(-.99856 0 0 1.2772 177.16 207.94)" d="m65.017 41.255 0.02508 6.963 2.7061-0.0084v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#d)" filter="url(#e)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m88.135 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#x)" filter="url(#k)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m90.868 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#w)" filter="url(#j)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m101.8 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#v)" filter="url(#i)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m107.27 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#u)" filter="url(#g)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m82.188 283.39 0.0251 6.963 2.7061-8e-3v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#t)" filter="url(#f)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m85.401 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#s)" filter="url(#l)"/>
+ <path transform="matrix(-.99856 0 0 1.2772 177.16 228.65)" d="m65.017 41.255 0.02508 6.963 2.7061-0.0084v-1.2194h-1.0357v-4.602h1.019v-1.1442z" fill="url(#d)" filter="url(#e)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m88.135 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#r)" filter="url(#k)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m90.868 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#q)" filter="url(#j)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m104.54 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#p)" filter="url(#h)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m107.27 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#o)" filter="url(#g)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -80.616)" d="m101.8 283.37v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#n)" filter="url(#h)"/>
+ <path transform="matrix(.99856 0 0 1.2772 .14037 -72.134)" d="m104.54 260.52v7.0132h1.7718v-7.0162h-1.7718z" fill="url(#m)" filter="url(#i)"/>
+ </g>
+ </g>
+</svg>
diff --git a/iwyu/htop.imp b/iwyu/htop.imp
new file mode 100644
index 0000000..7fe432c
--- /dev/null
+++ b/iwyu/htop.imp
@@ -0,0 +1,18 @@
+[
+ { include: ["<curses.h>", "private", "\"ProvideCurses.h\"", "public"] },
+ { include: ["<ncurses.h>", "private", "\"ProvideCurses.h\"", "public"] },
+ { include: ["<ncurses/curses.h>", "private", "\"ProvideCurses.h\"", "public"] },
+ { include: ["<ncurses/ncurses.h>", "private", "\"ProvideCurses.h\"", "public"] },
+
+ { include: ["<bits/types/struct_tm.h>", "private", "<time.h>", "public"] },
+
+ { include: ["<bits/getopt_core.h>", "private", "<unistd.h>", "public"] },
+
+ { include: ["<sys/signal.h>", "private", "<signal.h>", "public"] },
+
+ { include: ["<sys/_stdarg.h>", "private", "<stdarg.h>", "public"] },
+
+ { include: ["<sys/limits.h>", "private", "<limits.h>", "public"] },
+
+ { include: ["<x86/_inttypes.h>", "private", "<inttypes.h>", "public"] },
+]
diff --git a/iwyu/run_iwyu.sh b/iwyu/run_iwyu.sh
new file mode 100755
index 0000000..6139ebe
--- /dev/null
+++ b/iwyu/run_iwyu.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+SCRIPT=$(readlink -f "$0")
+SCRIPTDIR=$(dirname "$SCRIPT")
+SOURCEDIR="$SCRIPTDIR/.."
+
+PKG_NL3=$(pkg-config --cflags libnl-3.0)
+
+IWYU=${IWYU:-iwyu}
+
+cd "$SOURCEDIR" || exit
+
+make clean
+make -k -s CC="$IWYU" CFLAGS="-Xiwyu --no_comments -Xiwyu --no_fwd_decl -Xiwyu --mapping_file='$SCRIPTDIR/htop.imp' $PKG_NL3"
diff --git a/linux/Battery.c b/linux/Battery.c
deleted file mode 100644
index a8784da..0000000
--- a/linux/Battery.c
+++ /dev/null
@@ -1,322 +0,0 @@
-/*
-htop - linux/Battery.c
-(C) 2004-2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-
-Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
-*/
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#include <dirent.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <time.h>
-#include "BatteryMeter.h"
-#include "StringUtils.h"
-
-#define SYS_POWERSUPPLY_DIR "/sys/class/power_supply"
-
-// ----------------------------------------
-// READ FROM /proc
-// ----------------------------------------
-
-// This implementation reading from from /proc/acpi is really inefficient,
-// but I think this is on the way out so I did not rewrite it.
-// The /sys implementation below does things the right way.
-
-static unsigned long int parseBatInfo(const char *fileName, const unsigned short int lineNum, const unsigned short int wordNum) {
- const char batteryPath[] = PROCDIR "/acpi/battery/";
- DIR* batteryDir = opendir(batteryPath);
- if (!batteryDir)
- return 0;
-
- #define MAX_BATTERIES 64
- char* batteries[MAX_BATTERIES];
- unsigned int nBatteries = 0;
- memset(batteries, 0, MAX_BATTERIES * sizeof(char*));
-
- while (nBatteries < MAX_BATTERIES) {
- struct dirent* dirEntry = readdir(batteryDir);
- if (!dirEntry)
- break;
- char* entryName = dirEntry->d_name;
- if (strncmp(entryName, "BAT", 3))
- continue;
- batteries[nBatteries] = xStrdup(entryName);
- nBatteries++;
- }
- closedir(batteryDir);
-
- unsigned long int total = 0;
- for (unsigned int i = 0; i < nBatteries; i++) {
- char infoPath[30];
- xSnprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
-
- FILE* file = fopen(infoPath, "r");
- if (!file) {
- break;
- }
-
- char* line = NULL;
- for (unsigned short int j = 0; j < lineNum; j++) {
- free(line);
- line = String_readLine(file);
- if (!line) break;
- }
-
- fclose(file);
-
- if (!line) break;
-
- char *foundNumStr = String_getToken(line, wordNum);
- const unsigned long int foundNum = atoi(foundNumStr);
- free(foundNumStr);
- free(line);
-
- total += foundNum;
- }
-
- for (unsigned int i = 0; i < nBatteries; i++) {
- free(batteries[i]);
- }
-
- return total;
-}
-
-static ACPresence procAcpiCheck() {
- ACPresence isOn = AC_ERROR;
- const char *power_supplyPath = PROCDIR "/acpi/ac_adapter";
- DIR *dir = opendir(power_supplyPath);
- if (!dir) {
- return AC_ERROR;
- }
-
- for (;;) {
- struct dirent* dirEntry = readdir((DIR *) dir);
- if (!dirEntry)
- break;
-
- char* entryName = (char *) dirEntry->d_name;
-
- if (entryName[0] != 'A')
- continue;
-
- char statePath[256];
- xSnprintf((char *) statePath, sizeof statePath, "%s/%s/state", power_supplyPath, entryName);
- FILE* file = fopen(statePath, "r");
- if (!file) {
- isOn = AC_ERROR;
- continue;
- }
- char* line = String_readLine(file);
- fclose(file);
- if (!line) continue;
-
- const char *isOnline = String_getToken(line, 2);
- free(line);
-
- if (strcmp(isOnline, "on-line") == 0) {
- isOn = AC_PRESENT;
- } else {
- isOn = AC_ABSENT;
- }
- free((char *) isOnline);
- if (isOn == AC_PRESENT) {
- break;
- }
- }
-
- if (dir)
- closedir(dir);
- return isOn;
-}
-
-static double Battery_getProcBatData() {
- const unsigned long int totalFull = parseBatInfo("info", 3, 4);
- if (totalFull == 0)
- return 0;
-
- const unsigned long int totalRemain = parseBatInfo("state", 5, 3);
- if (totalRemain == 0)
- return 0;
-
- return totalRemain * 100.0 / (double) totalFull;
-}
-
-static void Battery_getProcData(double* level, ACPresence* isOnAC) {
- *level = Battery_getProcBatData();
- *isOnAC = procAcpiCheck();
-}
-
-// ----------------------------------------
-// READ FROM /sys
-// ----------------------------------------
-
-static inline ssize_t xread(int fd, void *buf, size_t count) {
- // Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested.
- size_t alreadyRead = 0;
- for(;;) {
- ssize_t res = read(fd, buf, count);
- if (res == -1 && errno == EINTR) continue;
- if (res > 0) {
- buf = ((char*)buf)+res;
- count -= res;
- alreadyRead += res;
- }
- if (res == -1) return -1;
- if (count == 0 || res == 0) return alreadyRead;
- }
-}
-
-static void Battery_getSysData(double* level, ACPresence* isOnAC) {
-
- *level = 0;
- *isOnAC = AC_ERROR;
-
- DIR *dir = opendir(SYS_POWERSUPPLY_DIR);
- if (!dir)
- return;
-
- unsigned long int totalFull = 0;
- unsigned long int totalRemain = 0;
-
- for (;;) {
- struct dirent* dirEntry = readdir((DIR *) dir);
- if (!dirEntry)
- break;
- char* entryName = (char *) dirEntry->d_name;
- const char filePath[256];
-
- xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName);
- int fd1 = open(filePath, O_RDONLY);
- if (fd1 == -1)
- continue;
-
- char type[8];
- ssize_t typelen = xread(fd1, type, 7);
- close(fd1);
- if (typelen < 1)
- continue;
-
- if (type[0] == 'B' && type[1] == 'a' && type[2] == 't') {
- xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
- int fd2 = open(filePath, O_RDONLY);
- if (fd2 == -1) {
- closedir(dir);
- return;
- }
- char buffer[1024];
- ssize_t buflen = xread(fd2, buffer, 1023);
- close(fd2);
- if (buflen < 1) {
- closedir(dir);
- return;
- }
- buffer[buflen] = '\0';
- char *buf = buffer;
- char *line = NULL;
- bool full = false;
- bool now = false;
- while ((line = strsep(&buf, "\n")) != NULL) {
- #define match(str,prefix) \
- (String_startsWith(str,prefix) ? (str) + strlen(prefix) : NULL)
- const char* ps = match(line, "POWER_SUPPLY_");
- if (!ps) {
- continue;
- }
- const char* energy = match(ps, "ENERGY_");
- if (!energy) {
- energy = match(ps, "CHARGE_");
- }
- if (!energy) {
- continue;
- }
- const char* value = (!full) ? match(energy, "FULL=") : NULL;
- if (value) {
- totalFull += atoi(value);
- full = true;
- if (now) break;
- continue;
- }
- value = (!now) ? match(energy, "NOW=") : NULL;
- if (value) {
- totalRemain += atoi(value);
- now = true;
- if (full) break;
- continue;
- }
- }
- #undef match
- } else if (entryName[0] == 'A') {
- if (*isOnAC != AC_ERROR) {
- continue;
- }
-
- xSnprintf((char *) filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
- int fd3 = open(filePath, O_RDONLY);
- if (fd3 == -1) {
- closedir(dir);
- return;
- }
- char buffer[2] = "";
- for(;;) {
- ssize_t res = read(fd3, buffer, 1);
- if (res == -1 && errno == EINTR) continue;
- break;
- }
- close(fd3);
- if (buffer[0] == '0') {
- *isOnAC = AC_ABSENT;
- } else if (buffer[0] == '1') {
- *isOnAC = AC_PRESENT;
- }
- }
- }
- closedir(dir);
- *level = totalFull > 0 ? ((double) totalRemain * 100) / (double) totalFull : 0;
-}
-
-static enum { BAT_PROC, BAT_SYS, BAT_ERR } Battery_method = BAT_PROC;
-
-static time_t Battery_cacheTime = 0;
-static double Battery_cacheLevel = 0;
-static ACPresence Battery_cacheIsOnAC = 0;
-
-void Battery_getData(double* level, ACPresence* isOnAC) {
- time_t now = time(NULL);
- // update battery reading is slow. Update it each 10 seconds only.
- if (now < Battery_cacheTime + 10) {
- *level = Battery_cacheLevel;
- *isOnAC = Battery_cacheIsOnAC;
- return;
- }
-
- if (Battery_method == BAT_PROC) {
- Battery_getProcData(level, isOnAC);
- if (*level == 0) {
- Battery_method = BAT_SYS;
- }
- }
- if (Battery_method == BAT_SYS) {
- Battery_getSysData(level, isOnAC);
- if (*level == 0) {
- Battery_method = BAT_ERR;
- }
- }
- if (Battery_method == BAT_ERR) {
- *level = -1;
- *isOnAC = AC_ERROR;
- }
- if (*level > 100.0) {
- *level = 100.0;
- }
- Battery_cacheLevel = *level;
- Battery_cacheIsOnAC = *isOnAC;
- Battery_cacheTime = now;
-}
diff --git a/linux/Battery.h b/linux/Battery.h
deleted file mode 100644
index 8ca0d7f..0000000
--- a/linux/Battery.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef HEADER_Battery
-#define HEADER_Battery
-/*
-htop - linux/Battery.h
-(C) 2004-2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-
-Linux battery readings written by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
-*/
-
-void Battery_getData(double* level, ACPresence* isOnAC);
-
-#endif
diff --git a/linux/IOPriority.h b/linux/IOPriority.h
index 7c4f3b6..551a7d5 100644
--- a/linux/IOPriority.h
+++ b/linux/IOPriority.h
@@ -3,7 +3,7 @@
/*
htop - IOPriority.h
(C) 2004-2012 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
Based on ionice,
@@ -28,7 +28,7 @@ enum {
typedef int IOPriority;
-#define IOPriority_tuple(class_, data_) (((class_) << IOPRIO_CLASS_SHIFT) | data_)
+#define IOPriority_tuple(class_, data_) (((class_) << IOPRIO_CLASS_SHIFT) | (data_))
#define IOPriority_error 0xffffffff
diff --git a/linux/IOPriorityPanel.c b/linux/IOPriorityPanel.c
index ebb77bf..c5a4a4c 100644
--- a/linux/IOPriorityPanel.c
+++ b/linux/IOPriorityPanel.c
@@ -1,20 +1,33 @@
/*
htop - IOPriorityPanel.c
(C) 2004-2012 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "IOPriorityPanel.h"
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "FunctionBar.h"
+#include "ListItem.h"
+#include "Object.h"
+#include "XUtils.h"
+
Panel* IOPriorityPanel_new(IOPriority currPrio) {
Panel* this = Panel_new(1, 1, 1, 1, true, Class(ListItem), FunctionBar_newEnterEsc("Set ", "Cancel "));
Panel_setHeader(this, "IO Priority:");
Panel_add(this, (Object*) ListItem_new("None (based on nice)", IOPriority_None));
- if (currPrio == IOPriority_None) Panel_setSelected(this, 0);
- static const struct { int klass; const char* name; } classes[] = {
+ if (currPrio == IOPriority_None) {
+ Panel_setSelected(this, 0);
+ }
+ static const struct {
+ int klass;
+ const char* name;
+ } classes[] = {
{ .klass = IOPRIO_CLASS_RT, .name = "Realtime" },
{ .klass = IOPRIO_CLASS_BE, .name = "Best-effort" },
{ .klass = 0, .name = NULL }
@@ -22,17 +35,22 @@ Panel* IOPriorityPanel_new(IOPriority currPrio) {
for (int c = 0; classes[c].name; c++) {
for (int i = 0; i < 8; i++) {
char name[50];
- xSnprintf(name, sizeof(name)-1, "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : ""));
+ xSnprintf(name, sizeof(name), "%s %d %s", classes[c].name, i, i == 0 ? "(High)" : (i == 7 ? "(Low)" : ""));
IOPriority ioprio = IOPriority_tuple(classes[c].klass, i);
Panel_add(this, (Object*) ListItem_new(name, ioprio));
- if (currPrio == ioprio) Panel_setSelected(this, Panel_size(this) - 1);
+ if (currPrio == ioprio) {
+ Panel_setSelected(this, Panel_size(this) - 1);
+ }
}
}
Panel_add(this, (Object*) ListItem_new("Idle", IOPriority_Idle));
- if (currPrio == IOPriority_Idle) Panel_setSelected(this, Panel_size(this) - 1);
+ if (currPrio == IOPriority_Idle) {
+ Panel_setSelected(this, Panel_size(this) - 1);
+ }
return this;
}
IOPriority IOPriorityPanel_getIOPriority(Panel* this) {
- return (IOPriority) ( ((ListItem*) Panel_getSelected(this))->key );
+ const ListItem* selected = (ListItem*) Panel_getSelected(this);
+ return selected ? selected->key : IOPriority_None;
}
diff --git a/linux/IOPriorityPanel.h b/linux/IOPriorityPanel.h
index 04c1d43..2ac4b31 100644
--- a/linux/IOPriorityPanel.h
+++ b/linux/IOPriorityPanel.h
@@ -3,13 +3,12 @@
/*
htop - IOPriorityPanel.h
(C) 2004-2012 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "IOPriority.h"
-#include "ListItem.h"
Panel* IOPriorityPanel_new(IOPriority currPrio);
diff --git a/linux/LibSensors.c b/linux/LibSensors.c
new file mode 100644
index 0000000..a30e21b
--- /dev/null
+++ b/linux/LibSensors.c
@@ -0,0 +1,104 @@
+#include "LibSensors.h"
+
+#ifdef HAVE_SENSORS_SENSORS_H
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <sensors/sensors.h>
+
+#include "XUtils.h"
+
+
+static int (*sym_sensors_init)(FILE*);
+static void (*sym_sensors_cleanup)(void);
+static const sensors_chip_name* (*sym_sensors_get_detected_chips)(const sensors_chip_name*, int*);
+static int (*sym_sensors_snprintf_chip_name)(char*, size_t, const sensors_chip_name*);
+static const sensors_feature* (*sym_sensors_get_features)(const sensors_chip_name*, int*);
+static const sensors_subfeature* (*sym_sensors_get_subfeature)(const sensors_chip_name*, const sensors_feature*, sensors_subfeature_type);
+static int (*sym_sensors_get_value)(const sensors_chip_name*, int, double*);
+
+static void* dlopenHandle = NULL;
+
+int LibSensors_init(FILE* input) {
+ if (!dlopenHandle) {
+ dlopenHandle = dlopen("libsensors.so", RTLD_LAZY);
+ if (!dlopenHandle)
+ goto dlfailure;
+
+ /* Clear any errors */
+ dlerror();
+
+ #define resolve(symbolname) do { \
+ *(void **)(&sym_##symbolname) = dlsym(dlopenHandle, #symbolname); \
+ if (!sym_##symbolname || dlerror() != NULL) \
+ goto dlfailure; \
+ } while(0)
+
+ resolve(sensors_init);
+ resolve(sensors_cleanup);
+ resolve(sensors_get_detected_chips);
+ resolve(sensors_snprintf_chip_name);
+ resolve(sensors_get_features);
+ resolve(sensors_get_subfeature);
+ resolve(sensors_get_value);
+
+ #undef resolve
+ }
+
+ return sym_sensors_init(input);
+
+dlfailure:
+ if (dlopenHandle) {
+ dlclose(dlopenHandle);
+ dlopenHandle = NULL;
+ }
+ return -1;
+}
+
+void LibSensors_cleanup(void) {
+ if (dlopenHandle) {
+ sym_sensors_cleanup();
+
+ dlclose(dlopenHandle);
+ dlopenHandle = NULL;
+ }
+}
+
+int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount) {
+ if (!dlopenHandle)
+ return -ENOTSUP;
+
+ int tempCount = 0;
+
+ int n = 0;
+ for (const sensors_chip_name *chip = sym_sensors_get_detected_chips(NULL, &n); chip; chip = sym_sensors_get_detected_chips(NULL, &n)) {
+ char buffer[32];
+ sym_sensors_snprintf_chip_name(buffer, sizeof(buffer), chip);
+ if (!String_startsWith(buffer, "coretemp") && !String_startsWith(buffer, "cpu_thermal"))
+ continue;
+
+ int m = 0;
+ for (const sensors_feature *feature = sym_sensors_get_features(chip, &m); feature; feature = sym_sensors_get_features(chip, &m)) {
+ if (feature->type != SENSORS_FEATURE_TEMP)
+ continue;
+
+ if (feature->number > cpuCount)
+ continue;
+
+ const sensors_subfeature *sub_feature = sym_sensors_get_subfeature(chip, feature, SENSORS_SUBFEATURE_TEMP_INPUT);
+ if (sub_feature) {
+ double temp;
+ int r = sym_sensors_get_value(chip, sub_feature->number, &temp);
+ if (r != 0)
+ continue;
+
+ cpus[feature->number].temperature = temp;
+ tempCount++;
+ }
+ }
+ }
+
+ return tempCount;
+}
+
+#endif /* HAVE_SENSORS_SENSORS_H */
diff --git a/linux/LibSensors.h b/linux/LibSensors.h
new file mode 100644
index 0000000..ed9be7b
--- /dev/null
+++ b/linux/LibSensors.h
@@ -0,0 +1,16 @@
+#ifndef HEADER_LibSensors
+#define HEADER_LibSensors
+
+#include "config.h" // IWYU pragma: keep
+
+#include <stdio.h>
+
+#include "LinuxProcessList.h"
+
+
+int LibSensors_init(FILE* input);
+void LibSensors_cleanup(void);
+
+int LibSensors_getCPUTemperatures(CPUData* cpus, int cpuCount);
+
+#endif /* HEADER_LibSensors */
diff --git a/linux/LinuxCRT.c b/linux/LinuxCRT.c
deleted file mode 100644
index c65b782..0000000
--- a/linux/LinuxCRT.c
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
-htop - LinuxCRT.c
-(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "config.h"
-#include "CRT.h"
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef HAVE_EXECINFO_H
-#include <execinfo.h>
-#endif
-
-void CRT_handleSIGSEGV(int sgn) {
- (void) sgn;
- CRT_done();
- #ifdef __linux
- fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at https://htop.dev\n");
- #ifdef HAVE_EXECINFO_H
- size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
- fprintf(stderr, "\n Please include in your report the following backtrace: \n");
- backtrace_symbols_fd(backtraceArray, size, 2);
- fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
- fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
- fprintf(stderr, "\n\n objdump -d `which htop` > ~/htop.objdump");
- fprintf(stderr, "\n\nand then attach the file ~/htop.objdump to your bug report.");
- fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
- #endif
- #else
- fprintf(stderr, "\nUnfortunately, you seem to be using an unsupported platform!");
- fprintf(stderr, "\nPlease contact your platform package maintainer!\n\n");
- #endif
- abort();
-}
diff --git a/linux/LinuxCRT.h b/linux/LinuxCRT.h
deleted file mode 100644
index c379a8f..0000000
--- a/linux/LinuxCRT.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef HEADER_LinuxCRT
-#define HEADER_LinuxCRT
-/*
-htop - LinuxCRT.h
-(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void CRT_handleSIGSEGV(int sgn);
-
-#endif
diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c
index 377aa5b..8298000 100644
--- a/linux/LinuxProcess.c
+++ b/linux/LinuxProcess.c
@@ -2,25 +2,33 @@
htop - LinuxProcess.c
(C) 2014 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Process.h"
-#include "ProcessList.h"
#include "LinuxProcess.h"
-#include "Platform.h"
-#include "CRT.h"
+#include <math.h>
+#include <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
#include <string.h>
-#include <sys/syscall.h>
-#include <time.h>
+#include <syscall.h>
+#include <unistd.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "Process.h"
+#include "ProvideCurses.h"
+#include "RichString.h"
+#include "XUtils.h"
+
/* semi-global */
long long btime;
+/* Used to identify kernel threads in Comm and Exe columns */
+static const char *const kthreadID = "KTHREAD";
+
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
@@ -61,28 +69,28 @@ ProcessFieldData Process_fields[] = {
[CNSWAP] = { .name = "CNSWAP", .title = NULL, .description = NULL, .flags = 0, },
[EXIT_SIGNAL] = { .name = "EXIT_SIGNAL", .title = NULL, .description = NULL, .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
- [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
+ [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[M_SHARE] = { .name = "M_SHARE", .title = " SHR ", .description = "Size of the process's shared pages", .flags = 0, },
[M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, },
[M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, },
- [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process", .flags = 0, },
- [M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process", .flags = 0, },
+ [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, },
+ [M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process (unused since Linux 2.6; always 0)", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
+ [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
#ifdef HAVE_OPENVZ
- [CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
- [VPID] = { .name = "VPID", .title = " VPID ", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
+ [CTID] = { .name = "CTID", .title = " CTID ", .description = "OpenVZ container ID (a.k.a. virtual environment ID)", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
+ [VPID] = { .name = "VPID", .title = " VPID ", .description = "OpenVZ process ID", .flags = PROCESS_FLAG_LINUX_OPENVZ, },
#endif
#ifdef HAVE_VSERVER
[VXID] = { .name = "VXID", .title = " VXID ", .description = "VServer process ID", .flags = PROCESS_FLAG_LINUX_VSERVER, },
#endif
-#ifdef HAVE_TASKSTATS
[RCHAR] = { .name = "RCHAR", .title = " RD_CHAR ", .description = "Number of bytes the process has read", .flags = PROCESS_FLAG_IO, },
[WCHAR] = { .name = "WCHAR", .title = " WR_CHAR ", .description = "Number of bytes the process has written", .flags = PROCESS_FLAG_IO, },
[SYSCR] = { .name = "SYSCR", .title = " RD_SYSC ", .description = "Number of read(2) syscalls for the process", .flags = PROCESS_FLAG_IO, },
@@ -93,10 +101,7 @@ ProcessFieldData Process_fields[] = {
[IO_READ_RATE] = { .name = "IO_READ_RATE", .title = " DISK READ ", .description = "The I/O rate of read(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, },
[IO_WRITE_RATE] = { .name = "IO_WRITE_RATE", .title = " DISK WRITE ", .description = "The I/O rate of write(2) in bytes per second for the process", .flags = PROCESS_FLAG_IO, },
[IO_RATE] = { .name = "IO_RATE", .title = " DISK R/W ", .description = "Total I/O rate in bytes per second", .flags = PROCESS_FLAG_IO, },
-#endif
-#ifdef HAVE_CGROUP
[CGROUP] = { .name = "CGROUP", .title = " CGROUP ", .description = "Which cgroup the process is in", .flags = PROCESS_FLAG_LINUX_CGROUP, },
-#endif
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, },
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT
@@ -107,6 +112,11 @@ ProcessFieldData Process_fields[] = {
[M_PSS] = { .name = "M_PSS", .title = " PSS ", .description = "proportional set size, same as M_RESIDENT but each page is divided by the number of processes sharing it.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
[M_SWAP] = { .name = "M_SWAP", .title = " SWAP ", .description = "Size of the process's swapped pages", .flags = PROCESS_FLAG_LINUX_SMAPS, },
[M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, Unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
+ [CTXT] = { .name = "CTXT", .title = " CTXT ", .description = "Context switches (incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches)", .flags = PROCESS_FLAG_LINUX_CTXT, },
+ [SECATTR] = { .name = "SECATTR", .title = " Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, },
+ [PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, },
+ [PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
+ [CWD] = { .name ="CWD", .title = "CWD ", .description = "The current working directory of the process", .flags = PROCESS_FLAG_LINUX_CWD, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
@@ -123,30 +133,37 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = 0, .label = NULL },
};
-ProcessClass LinuxProcess_class = {
- .super = {
- .extends = Class(Process),
- .display = Process_display,
- .delete = Process_delete,
- .compare = LinuxProcess_compare
- },
- .writeField = (Process_WriteField) LinuxProcess_writeField,
-};
+/* This function returns the string displayed in Command column, so that sorting
+ * happens on what is displayed - whether comm, full path, basename, etc.. So
+ * this follows LinuxProcess_writeField(COMM) and LinuxProcess_writeCommand */
+static const char* LinuxProcess_getCommandStr(const Process *this) {
+ const LinuxProcess *lp = (const LinuxProcess *)this;
+ if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !lp->mergedCommand.str) {
+ return this->comm;
+ }
+ return lp->mergedCommand.str;
+}
-LinuxProcess* LinuxProcess_new(Settings* settings) {
+Process* LinuxProcess_new(const Settings* settings) {
LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess));
Object_setClass(this, Class(LinuxProcess));
Process_init(&this->super, settings);
- return this;
+ return &this->super;
}
void Process_delete(Object* cast) {
LinuxProcess* this = (LinuxProcess*) cast;
Process_done((Process*)cast);
-#ifdef HAVE_CGROUP
free(this->cgroup);
+#ifdef HAVE_OPENVZ
+ free(this->ctid);
#endif
+ free(this->cwd);
+ free(this->secattr);
free(this->ttyDevice);
+ free(this->procExe);
+ free(this->procComm);
+ free(this->mergedCommand.str);
free(this);
}
@@ -158,7 +175,13 @@ effort class. The priority within the best effort class will be
dynamically derived from the cpu nice level of the process:
io_priority = (cpu_nice + 20) / 5. -- From ionice(1) man page
*/
-#define LinuxProcess_effectiveIOPriority(p_) (IOPriority_class(p_->ioPriority) == IOPRIO_CLASS_NONE ? IOPriority_tuple(IOPRIO_CLASS_BE, (p_->super.nice + 20) / 5) : p_->ioPriority)
+static int LinuxProcess_effectiveIOPriority(const LinuxProcess* this) {
+ if (IOPriority_class(this->ioPriority) == IOPRIO_CLASS_NONE) {
+ return IOPriority_tuple(IOPRIO_CLASS_BE, (this->super.nice + 20) / 5);
+ }
+
+ return this->ioPriority;
+}
IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) {
IOPriority ioprio = 0;
@@ -170,30 +193,432 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this) {
return ioprio;
}
-bool LinuxProcess_setIOPriority(LinuxProcess* this, Arg ioprio) {
+bool LinuxProcess_setIOPriority(Process* this, Arg ioprio) {
// Other OSes masquerading as Linux (NetBSD?) don't have this syscall
#ifdef SYS_ioprio_set
- syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->super.pid, ioprio.i);
+ syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, this->pid, ioprio.i);
#endif
- return (LinuxProcess_updateIOPriority(this) == ioprio.i);
+ return (LinuxProcess_updateIOPriority((LinuxProcess*)this) == ioprio.i);
}
#ifdef HAVE_DELAYACCT
-void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
- if (delay_percent == -1LL) {
- xSnprintf(buffer, n, " N/A ");
- } else {
- xSnprintf(buffer, n, "%4.1f ", delay_percent);
- }
+static void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
+ if (isnan(delay_percent)) {
+ xSnprintf(buffer, n, " N/A ");
+ } else {
+ xSnprintf(buffer, n, "%4.1f ", delay_percent);
+ }
}
#endif
-void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) {
- LinuxProcess* lp = (LinuxProcess*) this;
+/*
+TASK_COMM_LEN is defined to be 16 for /proc/[pid]/comm in man proc(5), but it is
+not available in an userspace header - so define it. Note: when colorizing a
+basename with the comm prefix, the entire basename (not just the comm prefix) is
+colorized for better readability, and it is implicit that only upto
+(TASK_COMM_LEN - 1) could be comm
+*/
+#define TASK_COMM_LEN 16
+
+static bool findCommInCmdline(const char *comm, const char *cmdline, int cmdlineBasenameOffset, int *pCommStart, int *pCommEnd) {
+ /* Try to find procComm in tokenized cmdline - this might in rare cases
+ * mis-identify a string or fail, if comm or cmdline had been unsuitably
+ * modified by the process */
+ const char *token;
+ const char *tokenBase;
+ size_t tokenLen;
+ const size_t commLen = strlen(comm);
+
+ for (token = cmdline + cmdlineBasenameOffset; *token; ) {
+ for (tokenBase = token; *token && *token != '\n'; ++token) {
+ if (*token == '/') {
+ tokenBase = token + 1;
+ }
+ }
+ tokenLen = token - tokenBase;
+
+ if ((tokenLen == commLen || (tokenLen > commLen && commLen == (TASK_COMM_LEN - 1))) &&
+ strncmp(tokenBase, comm, commLen) == 0) {
+ *pCommStart = tokenBase - cmdline;
+ *pCommEnd = token - cmdline;
+ return true;
+ }
+ if (*token) {
+ do {
+ ++token;
+ } while ('\n' == *token);
+ }
+ }
+ return false;
+}
+
+static int matchCmdlinePrefixWithExeSuffix(const char *cmdline, int cmdlineBaseOffset, const char *exe, int exeBaseOffset, int exeBaseLen) {
+ int matchLen; /* matching length to be returned */
+ char delim; /* delimiter following basename */
+
+ /* cmdline prefix is an absolute path: it must match whole exe. */
+ if (cmdline[0] == '/') {
+ matchLen = exeBaseLen + exeBaseOffset;
+ if (strncmp(cmdline, exe, matchLen) == 0) {
+ delim = cmdline[matchLen];
+ if (delim == 0 || delim == '\n' || delim == ' ') {
+ return matchLen;
+ }
+ }
+ return 0;
+ }
+
+ /* cmdline prefix is a relative path: We need to first match the basename at
+ * cmdlineBaseOffset and then reverse match the cmdline prefix with the exe
+ * suffix. But there is a catch: Some processes modify their cmdline in ways
+ * that make htop's identification of the basename in cmdline unreliable.
+ * For e.g. /usr/libexec/gdm-session-worker modifies its cmdline to
+ * "gdm-session-worker [pam/gdm-autologin]" and htop ends up with
+ * procCmdlineBasenameOffset at "gdm-autologin]". This issue could arise with
+ * chrome as well as it stores in cmdline its concatenated argument vector,
+ * without NUL delimiter between the arguments (which may contain a '/')
+ *
+ * So if needed, we adjust cmdlineBaseOffset to the previous (if any)
+ * component of the cmdline relative path, and retry the procedure. */
+ bool delimFound; /* if valid basename delimiter found */
+ do {
+ /* match basename */
+ matchLen = exeBaseLen + cmdlineBaseOffset;
+ if (cmdlineBaseOffset < exeBaseOffset &&
+ strncmp(cmdline + cmdlineBaseOffset, exe + exeBaseOffset, exeBaseLen) == 0) {
+ delim = cmdline[matchLen];
+ if (delim == 0 || delim == '\n' || delim == ' ') {
+ int i, j;
+ /* reverse match the cmdline prefix and exe suffix */
+ for (i = cmdlineBaseOffset - 1, j = exeBaseOffset - 1;
+ i >= 0 && cmdline[i] == exe[j]; --i, --j)
+ ;
+ /* full match, with exe suffix being a valid relative path */
+ if (i < 0 && exe[j] == '/') {
+ return matchLen;
+ }
+ }
+ }
+ /* Try to find the previous potential cmdlineBaseOffset - it would be
+ * preceded by '/' or nothing, and delimited by ' ' or '\n' */
+ for (delimFound = false, cmdlineBaseOffset -= 2; cmdlineBaseOffset > 0; --cmdlineBaseOffset) {
+ if (delimFound) {
+ if (cmdline[cmdlineBaseOffset - 1] == '/') {
+ break;
+ }
+ } else if (cmdline[cmdlineBaseOffset] == ' ' || cmdline[cmdlineBaseOffset] == '\n') {
+ delimFound = true;
+ }
+ }
+ } while (delimFound);
+
+ return 0;
+}
+
+/* stpcpy, but also converts newlines to spaces */
+static inline char *stpcpyWithNewlineConversion(char *dstStr, const char *srcStr) {
+ for (; *srcStr; ++srcStr) {
+ *dstStr++ = (*srcStr == '\n') ? ' ' : *srcStr;
+ }
+ *dstStr = 0;
+ return dstStr;
+}
+
+/*
+This function makes the merged Command string. It also stores the offsets of the
+basename, comm w.r.t the merged Command string - these offsets will be used by
+LinuxProcess_writeCommand() for coloring. The merged Command string is also
+returned by LinuxProcess_getCommandStr() for searching, sorting and filtering.
+*/
+void LinuxProcess_makeCommandStr(Process* this) {
+ LinuxProcess *lp = (LinuxProcess *)this;
+ LinuxProcessMergedCommand *mc = &lp->mergedCommand;
+
+ bool showMergedCommand = this->settings->showMergedCommand;
+ bool showProgramPath = this->settings->showProgramPath;
+ bool searchCommInCmdline = this->settings->findCommInCmdline;
+ bool stripExeFromCmdline = this->settings->stripExeFromCmdline;
+
+ /* lp->mergedCommand.str needs updating only if its state or contents changed.
+ * Its content is based on the fields cmdline, comm, and exe. */
+ if (
+ mc->prevMergeSet == showMergedCommand &&
+ mc->prevPathSet == showProgramPath &&
+ mc->prevCommSet == searchCommInCmdline &&
+ mc->prevCmdlineSet == stripExeFromCmdline &&
+ !mc->cmdlineChanged &&
+ !mc->commChanged &&
+ !mc->exeChanged
+ ) {
+ return;
+ }
+
+ /* The field separtor "│" has been chosen such that it will not match any
+ * valid string used for searching or filtering */
+ const char *SEPARATOR = CRT_treeStr[TREE_STR_VERT];
+ const int SEPARATOR_LEN = strlen(SEPARATOR);
+
+ /* Check for any changed fields since we last built this string */
+ if (mc->cmdlineChanged || mc->commChanged || mc->exeChanged) {
+ free(mc->str);
+ /* Accommodate the column text, two field separators and terminating NUL */
+ mc->str = xCalloc(1, mc->maxLen + 2*SEPARATOR_LEN + 1);
+ }
+
+ /* Preserve the settings used in this run */
+ mc->prevMergeSet = showMergedCommand;
+ mc->prevPathSet = showProgramPath;
+ mc->prevCommSet = searchCommInCmdline;
+ mc->prevCmdlineSet = stripExeFromCmdline;
+
+ /* Mark everything as unchanged */
+ mc->cmdlineChanged = false;
+ mc->commChanged = false;
+ mc->exeChanged = false;
+
+ /* Clear any separators */
+ mc->sep1 = 0;
+ mc->sep2 = 0;
+
+ /* Clear any highlighting locations */
+ mc->baseStart = 0;
+ mc->baseEnd = 0;
+ mc->commStart = 0;
+ mc->commEnd = 0;
+
+ const char *cmdline = this->comm;
+ const char *procExe = lp->procExe;
+ const char *procComm = lp->procComm;
+
+ char *strStart = mc->str;
+ char *str = strStart;
+
+ int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset;
+
+ if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
+ if (showMergedCommand && !procExe && procComm && strlen(procComm)) { /* Prefix column with comm */
+ if (strncmp(cmdline + cmdlineBasenameOffset, procComm, MINIMUM(TASK_COMM_LEN - 1, strlen(procComm))) != 0) {
+ mc->commStart = 0;
+ mc->commEnd = strlen(procComm);
+
+ str = stpcpy(str, procComm);
+
+ mc->sep1 = str - strStart;
+ str = stpcpy(str, SEPARATOR);
+ }
+ }
+
+ if (showProgramPath) {
+ (void) stpcpyWithNewlineConversion(str, cmdline);
+ mc->baseStart = cmdlineBasenameOffset;
+ mc->baseEnd = lp->procCmdlineBasenameEnd;
+ } else {
+ (void) stpcpyWithNewlineConversion(str, cmdline + cmdlineBasenameOffset);
+ mc->baseStart = 0;
+ mc->baseEnd = lp->procCmdlineBasenameEnd - cmdlineBasenameOffset;
+ }
+
+ if (mc->sep1) {
+ mc->baseStart += str - strStart - SEPARATOR_LEN + 1;
+ mc->baseEnd += str - strStart - SEPARATOR_LEN + 1;
+ }
+
+ return;
+ }
+
+ int exeLen = lp->procExeLen;
+ int exeBasenameOffset = lp->procExeBasenameOffset;
+ int exeBasenameLen = exeLen - exeBasenameOffset;
+
+ /* Start with copying exe */
+ if (showProgramPath) {
+ str = stpcpy(str, procExe);
+ mc->baseStart = exeBasenameOffset;
+ mc->baseEnd = exeLen;
+ } else {
+ str = stpcpy(str, procExe + exeBasenameOffset);
+ mc->baseStart = 0;
+ mc->baseEnd = exeBasenameLen;
+ }
+
+ mc->sep1 = 0;
+ mc->sep2 = 0;
+
+ int commStart = 0;
+ int commEnd = 0;
+ bool commInCmdline = false;
+
+ /* Try to match procComm with procExe's basename: This is reliable (predictable) */
+ if (strncmp(procExe + exeBasenameOffset, procComm, TASK_COMM_LEN - 1) == 0) {
+ commStart = mc->baseStart;
+ commEnd = mc->baseEnd;
+ } else if (searchCommInCmdline) {
+ /* commStart/commEnd will be adjusted later along with cmdline */
+ commInCmdline = findCommInCmdline(procComm, cmdline, cmdlineBasenameOffset, &commStart, &commEnd);
+ }
+
+ int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameOffset, procExe, exeBasenameOffset, exeBasenameLen);
+
+ /* Note: commStart, commEnd are offsets into RichString. But the multibyte
+ * separator (with size SEPARATOR_LEN) has size 1 in RichString. The offset
+ * adjustments below reflect this. */
+ if (commEnd) {
+ mc->unmatchedExe = !matchLen;
+
+ if (matchLen) {
+ /* strip the matched exe prefix */
+ cmdline += matchLen;
+
+ if (commInCmdline) {
+ commStart += str - strStart - matchLen;
+ commEnd += str - strStart - matchLen;
+ }
+ } else {
+ /* cmdline will be a separate field */
+ mc->sep1 = str - strStart;
+ str = stpcpy(str, SEPARATOR);
+
+ if (commInCmdline) {
+ commStart += str - strStart - SEPARATOR_LEN + 1;
+ commEnd += str - strStart - SEPARATOR_LEN + 1;
+ }
+ }
+
+ mc->separateComm = false; /* procComm merged */
+ } else {
+ mc->sep1 = str - strStart;
+ str = stpcpy(str, SEPARATOR);
+
+ commStart = str - strStart - SEPARATOR_LEN + 1;
+ str = stpcpy(str, procComm);
+ commEnd = str - strStart - SEPARATOR_LEN + 1; /* or commStart + strlen(procComm) */
+
+ mc->unmatchedExe = !matchLen;
+
+ if (matchLen) {
+ if (stripExeFromCmdline) {
+ cmdline += matchLen;
+ }
+ }
+
+ if (*cmdline) {
+ mc->sep2 = str - strStart - SEPARATOR_LEN + 1;
+ str = stpcpy(str, SEPARATOR);
+ }
+
+ mc->separateComm = true; /* procComm a separate field */
+ }
+
+ /* Display cmdline if it hasn't been consumed by procExe */
+ if (*cmdline) {
+ (void) stpcpyWithNewlineConversion(str, cmdline);
+ }
+
+ mc->commStart = commStart;
+ mc->commEnd = commEnd;
+}
+
+static void LinuxProcess_writeCommand(const Process* this, int attr, int baseAttr, RichString* str) {
+ const LinuxProcess *lp = (const LinuxProcess *)this;
+ const LinuxProcessMergedCommand *mc = &lp->mergedCommand;
+
+ int strStart = RichString_size(str);
+
+ int baseStart = strStart + lp->mergedCommand.baseStart;
+ int baseEnd = strStart + lp->mergedCommand.baseEnd;
+ int commStart = strStart + lp->mergedCommand.commStart;
+ int commEnd = strStart + lp->mergedCommand.commEnd;
+
+ int commAttr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
+
+ bool highlightBaseName = this->settings->highlightBaseName;
+
+ if(lp->procExeDeleted)
+ baseAttr = CRT_colors[FAILED_READ];
+
+ RichString_append(str, attr, lp->mergedCommand.str);
+
+ if (lp->mergedCommand.commEnd) {
+ if (!lp->mergedCommand.separateComm && commStart == baseStart && highlightBaseName) {
+ /* If it was matched with procExe's basename, make it bold if needed */
+ if (commEnd > baseEnd) {
+ RichString_setAttrn(str, A_BOLD | baseAttr, baseStart, baseEnd - 1);
+ RichString_setAttrn(str, A_BOLD | commAttr, baseEnd, commEnd - 1);
+ } else if (commEnd < baseEnd) {
+ RichString_setAttrn(str, A_BOLD | commAttr, commStart, commEnd - 1);
+ RichString_setAttrn(str, A_BOLD | baseAttr, commEnd, baseEnd - 1);
+ } else {
+ // Actually should be highlighted commAttr, but marked baseAttr to reduce visual noise
+ RichString_setAttrn(str, A_BOLD | baseAttr, commStart, commEnd - 1);
+ }
+
+ baseStart = baseEnd;
+ } else {
+ RichString_setAttrn(str, commAttr, commStart, commEnd - 1);
+ }
+ }
+
+ if (baseStart < baseEnd && highlightBaseName) {
+ RichString_setAttrn(str, baseAttr, baseStart, baseEnd - 1);
+ }
+
+ if (mc->sep1)
+ RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep1, strStart + mc->sep1);
+ if (mc->sep2)
+ RichString_setAttrn(str, CRT_colors[FAILED_READ], strStart + mc->sep2, strStart + mc->sep2);
+}
+
+static void LinuxProcess_writeCommandField(const Process *this, RichString *str, char *buffer, int n, int attr) {
+ /* This code is from Process_writeField for COMM, but we invoke
+ * LinuxProcess_writeCommand to display
+ * /proc/pid/exe (or its basename)│/proc/pid/comm│/proc/pid/cmdline */
+ int baseattr = CRT_colors[PROCESS_BASENAME];
+ if (this->settings->highlightThreads && Process_isThread(this)) {
+ attr = CRT_colors[PROCESS_THREAD];
+ baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
+ }
+ if (!this->settings->treeView || this->indent == 0) {
+ LinuxProcess_writeCommand(this, attr, baseattr, str);
+ } else {
+ char* buf = buffer;
+ int maxIndent = 0;
+ bool lastItem = (this->indent < 0);
+ int indent = (this->indent < 0 ? -this->indent : this->indent);
+ int vertLen = strlen(CRT_treeStr[TREE_STR_VERT]);
+
+ for (int i = 0; i < 32; i++) {
+ if (indent & (1U << i)) {
+ maxIndent = i+1;
+ }
+ }
+ for (int i = 0; i < maxIndent - 1; i++) {
+ if (indent & (1 << i)) {
+ if (buf - buffer + (vertLen + 3) > n) {
+ break;
+ }
+ buf = stpcpy(buf, CRT_treeStr[TREE_STR_VERT]);
+ buf = stpcpy(buf, " ");
+ } else {
+ if (buf - buffer + 4 > n) {
+ break;
+ }
+ buf = stpcpy(buf, " ");
+ }
+ }
+ n -= (buf - buffer);
+ const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
+ xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
+ RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
+ LinuxProcess_writeCommand(this, attr, baseattr, str);
+ }
+}
+
+static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) {
+ const LinuxProcess* lp = (const LinuxProcess*) this;
bool coloring = this->settings->highlightMegabytes;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
- int n = sizeof(buffer) - 1;
+ size_t n = sizeof(buffer) - 1;
switch ((int)field) {
case TTY_NR: {
if (lp->ttyDevice) {
@@ -206,11 +631,19 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
}
case CMINFLT: Process_colorNumber(str, lp->cminflt, coloring); return;
case CMAJFLT: Process_colorNumber(str, lp->cmajflt, coloring); return;
- case M_DRS: Process_humanNumber(str, lp->m_drs * PAGE_SIZE_KB, coloring); return;
- case M_DT: Process_humanNumber(str, lp->m_dt * PAGE_SIZE_KB, coloring); return;
- case M_LRS: Process_humanNumber(str, lp->m_lrs * PAGE_SIZE_KB, coloring); return;
- case M_TRS: Process_humanNumber(str, lp->m_trs * PAGE_SIZE_KB, coloring); return;
- case M_SHARE: Process_humanNumber(str, lp->m_share * PAGE_SIZE_KB, coloring); return;
+ case M_DRS: Process_humanNumber(str, lp->m_drs * CRT_pageSizeKB, coloring); return;
+ case M_DT: Process_humanNumber(str, lp->m_dt * CRT_pageSizeKB, coloring); return;
+ case M_LRS:
+ if (lp->m_lrs) {
+ Process_humanNumber(str, lp->m_lrs * CRT_pageSizeKB, coloring);
+ return;
+ }
+
+ attr = CRT_colors[PROCESS_SHADOW];
+ xSnprintf(buffer, n, " N/A ");
+ break;
+ case M_TRS: Process_humanNumber(str, lp->m_trs * CRT_pageSizeKB, coloring); return;
+ case M_SHARE: Process_humanNumber(str, lp->m_share * CRT_pageSizeKB, coloring); return;
case M_PSS: Process_humanNumber(str, lp->m_pss, coloring); return;
case M_SWAP: Process_humanNumber(str, lp->m_swap, coloring); return;
case M_PSSWP: Process_humanNumber(str, lp->m_psswp, coloring); return;
@@ -218,14 +651,6 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
case STIME: Process_printTime(str, lp->stime); return;
case CUTIME: Process_printTime(str, lp->cutime); return;
case CSTIME: Process_printTime(str, lp->cstime); return;
- case STARTTIME: {
- struct tm date;
- time_t starttimewall = btime + (lp->starttime / sysconf(_SC_CLK_TCK));
- (void) localtime_r(&starttimewall, &date);
- strftime(buffer, n, ((starttimewall > time(NULL) - 86400) ? "%R " : "%b%d "), &date);
- break;
- }
- #ifdef HAVE_TASKSTATS
case RCHAR: Process_colorNumber(str, lp->io_rchar, coloring); return;
case WCHAR: Process_colorNumber(str, lp->io_wchar, coloring); return;
case SYSCR: Process_colorNumber(str, lp->io_syscr, coloring); return;
@@ -236,22 +661,25 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
case IO_READ_RATE: Process_outputRate(str, buffer, n, lp->io_rate_read_bps, coloring); return;
case IO_WRITE_RATE: Process_outputRate(str, buffer, n, lp->io_rate_write_bps, coloring); return;
case IO_RATE: {
- double totalRate = (lp->io_rate_read_bps != -1)
- ? (lp->io_rate_read_bps + lp->io_rate_write_bps)
- : -1;
+ double totalRate = NAN;
+ if (!isnan(lp->io_rate_read_bps) && !isnan(lp->io_rate_write_bps))
+ totalRate = lp->io_rate_read_bps + lp->io_rate_write_bps;
+ else if (!isnan(lp->io_rate_read_bps))
+ totalRate = lp->io_rate_read_bps;
+ else if (!isnan(lp->io_rate_write_bps))
+ totalRate = lp->io_rate_write_bps;
+ else
+ totalRate = NAN;
Process_outputRate(str, buffer, n, totalRate, coloring); return;
}
- #endif
#ifdef HAVE_OPENVZ
- case CTID: xSnprintf(buffer, n, "%7u ", lp->ctid); break;
+ case CTID: xSnprintf(buffer, n, "%-8s ", lp->ctid ? lp->ctid : ""); break;
case VPID: xSnprintf(buffer, n, Process_pidFormat, lp->vpid); break;
#endif
#ifdef HAVE_VSERVER
case VXID: xSnprintf(buffer, n, "%5u ", lp->vxid); break;
#endif
- #ifdef HAVE_CGROUP
- case CGROUP: xSnprintf(buffer, n, "%-10s ", lp->cgroup); break;
- #endif
+ case CGROUP: xSnprintf(buffer, n, "%-10s ", lp->cgroup ? lp->cgroup : ""); break;
case OOM: xSnprintf(buffer, n, "%4u ", lp->oom); break;
case IO_PRIORITY: {
int klass = IOPriority_class(lp->ioPriority);
@@ -276,96 +704,175 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
case PERCENT_IO_DELAY: LinuxProcess_printDelay(lp->blkio_delay_percent, buffer, n); break;
case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); break;
#endif
+ case CTXT:
+ if (lp->ctxt_diff > 1000) {
+ attr |= A_BOLD;
+ }
+ xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff);
+ break;
+ case SECATTR: snprintf(buffer, n, "%-30s ", lp->secattr ? lp->secattr : "?"); break;
+ case COMM: {
+ if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !lp->mergedCommand.str) {
+ Process_writeField(this, str, field);
+ } else {
+ LinuxProcess_writeCommandField(this, str, buffer, n, attr);
+ }
+ return;
+ }
+ case PROC_COMM: {
+ if (lp->procComm) {
+ attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
+ /* 15 being (TASK_COMM_LEN - 1) */
+ xSnprintf(buffer, n, "%-15.15s ", lp->procComm);
+ } else {
+ attr = CRT_colors[PROCESS_SHADOW];
+ xSnprintf(buffer, n, "%-15.15s ", Process_isKernelThread(lp) ? kthreadID : "N/A");
+ }
+ break;
+ }
+ case PROC_EXE: {
+ if (lp->procExe) {
+ attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME];
+ if (lp->procExeDeleted)
+ attr = CRT_colors[FAILED_READ];
+ xSnprintf(buffer, n, "%-15.15s ", lp->procExe + lp->procExeBasenameOffset);
+ } else {
+ attr = CRT_colors[PROCESS_SHADOW];
+ xSnprintf(buffer, n, "%-15.15s ", Process_isKernelThread(lp) ? kthreadID : "N/A");
+ }
+ break;
+ }
+ case CWD:
+ if (!lp->cwd) {
+ xSnprintf(buffer, n, "%-25s ", "N/A");
+ attr = CRT_colors[PROCESS_SHADOW];
+ } else if (String_startsWith(lp->cwd, "/proc/") && strstr(lp->cwd, " (deleted)") != NULL) {
+ xSnprintf(buffer, n, "%-25s ", "main thread terminated");
+ attr = CRT_colors[PROCESS_SHADOW];
+ } else {
+ xSnprintf(buffer, n, "%-25.25s ", lp->cwd);
+ }
+ break;
default:
- Process_writeField((Process*)this, str, field);
+ Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
}
-long LinuxProcess_compare(const void* v1, const void* v2) {
- LinuxProcess *p1, *p2;
- Settings *settings = ((Process*)v1)->settings;
+static long LinuxProcess_compare(const void* v1, const void* v2) {
+ const LinuxProcess *p1, *p2;
+ const Settings *settings = ((const Process*)v1)->settings;
+
if (settings->direction == 1) {
- p1 = (LinuxProcess*)v1;
- p2 = (LinuxProcess*)v2;
+ p1 = (const LinuxProcess*)v1;
+ p2 = (const LinuxProcess*)v2;
} else {
- p2 = (LinuxProcess*)v1;
- p1 = (LinuxProcess*)v2;
+ p2 = (const LinuxProcess*)v1;
+ p1 = (const LinuxProcess*)v2;
}
- long long diff;
+
switch ((int)settings->sortKey) {
case M_DRS:
- return (p2->m_drs - p1->m_drs);
+ return SPACESHIP_NUMBER(p2->m_drs, p1->m_drs);
case M_DT:
- return (p2->m_dt - p1->m_dt);
+ return SPACESHIP_NUMBER(p2->m_dt, p1->m_dt);
case M_LRS:
- return (p2->m_lrs - p1->m_lrs);
+ return SPACESHIP_NUMBER(p2->m_lrs, p1->m_lrs);
case M_TRS:
- return (p2->m_trs - p1->m_trs);
+ return SPACESHIP_NUMBER(p2->m_trs, p1->m_trs);
case M_SHARE:
- return (p2->m_share - p1->m_share);
+ return SPACESHIP_NUMBER(p2->m_share, p1->m_share);
case M_PSS:
- return (p2->m_pss - p1->m_pss);
+ return SPACESHIP_NUMBER(p2->m_pss, p1->m_pss);
case M_SWAP:
- return (p2->m_swap - p1->m_swap);
+ return SPACESHIP_NUMBER(p2->m_swap, p1->m_swap);
case M_PSSWP:
- return (p2->m_psswp - p1->m_psswp);
- case UTIME: diff = p2->utime - p1->utime; goto test_diff;
- case CUTIME: diff = p2->cutime - p1->cutime; goto test_diff;
- case STIME: diff = p2->stime - p1->stime; goto test_diff;
- case CSTIME: diff = p2->cstime - p1->cstime; goto test_diff;
- case STARTTIME: {
- if (p1->starttime == p2->starttime)
- return (p1->super.pid - p2->super.pid);
- else
- return (p1->starttime - p2->starttime);
- }
- #ifdef HAVE_TASKSTATS
- case RCHAR: diff = p2->io_rchar - p1->io_rchar; goto test_diff;
- case WCHAR: diff = p2->io_wchar - p1->io_wchar; goto test_diff;
- case SYSCR: diff = p2->io_syscr - p1->io_syscr; goto test_diff;
- case SYSCW: diff = p2->io_syscw - p1->io_syscw; goto test_diff;
- case RBYTES: diff = p2->io_read_bytes - p1->io_read_bytes; goto test_diff;
- case WBYTES: diff = p2->io_write_bytes - p1->io_write_bytes; goto test_diff;
- case CNCLWB: diff = p2->io_cancelled_write_bytes - p1->io_cancelled_write_bytes; goto test_diff;
- case IO_READ_RATE: diff = p2->io_rate_read_bps - p1->io_rate_read_bps; goto test_diff;
- case IO_WRITE_RATE: diff = p2->io_rate_write_bps - p1->io_rate_write_bps; goto test_diff;
- case IO_RATE: diff = (p2->io_rate_read_bps + p2->io_rate_write_bps) - (p1->io_rate_read_bps + p1->io_rate_write_bps); goto test_diff;
- #endif
+ return SPACESHIP_NUMBER(p2->m_psswp, p1->m_psswp);
+ case UTIME:
+ return SPACESHIP_NUMBER(p2->utime, p1->utime);
+ case CUTIME:
+ return SPACESHIP_NUMBER(p2->cutime, p1->cutime);
+ case STIME:
+ return SPACESHIP_NUMBER(p2->stime, p1->stime);
+ case CSTIME:
+ return SPACESHIP_NUMBER(p2->cstime, p1->cstime);
+ case RCHAR:
+ return SPACESHIP_NUMBER(p2->io_rchar, p1->io_rchar);
+ case WCHAR:
+ return SPACESHIP_NUMBER(p2->io_wchar, p1->io_wchar);
+ case SYSCR:
+ return SPACESHIP_NUMBER(p2->io_syscr, p1->io_syscr);
+ case SYSCW:
+ return SPACESHIP_NUMBER(p2->io_syscw, p1->io_syscw);
+ case RBYTES:
+ return SPACESHIP_NUMBER(p2->io_read_bytes, p1->io_read_bytes);
+ case WBYTES:
+ return SPACESHIP_NUMBER(p2->io_write_bytes, p1->io_write_bytes);
+ case CNCLWB:
+ return SPACESHIP_NUMBER(p2->io_cancelled_write_bytes, p1->io_cancelled_write_bytes);
+ case IO_READ_RATE:
+ return SPACESHIP_NUMBER(p2->io_rate_read_bps, p1->io_rate_read_bps);
+ case IO_WRITE_RATE:
+ return SPACESHIP_NUMBER(p2->io_rate_write_bps, p1->io_rate_write_bps);
+ case IO_RATE:
+ return SPACESHIP_NUMBER(p2->io_rate_read_bps + p2->io_rate_write_bps, p1->io_rate_read_bps + p1->io_rate_write_bps);
#ifdef HAVE_OPENVZ
case CTID:
- return (p2->ctid - p1->ctid);
+ return SPACESHIP_NULLSTR(p1->ctid, p2->ctid);
case VPID:
- return (p2->vpid - p1->vpid);
+ return SPACESHIP_NUMBER(p2->vpid, p1->vpid);
#endif
#ifdef HAVE_VSERVER
case VXID:
- return (p2->vxid - p1->vxid);
+ return SPACESHIP_NUMBER(p2->vxid, p1->vxid);
#endif
- #ifdef HAVE_CGROUP
case CGROUP:
- return strcmp(p1->cgroup ? p1->cgroup : "", p2->cgroup ? p2->cgroup : "");
- #endif
+ return SPACESHIP_NULLSTR(p1->cgroup, p2->cgroup);
case OOM:
- return ((int)p2->oom - (int)p1->oom);
+ return SPACESHIP_NUMBER(p2->oom, p1->oom);
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY:
- return (p2->cpu_delay_percent > p1->cpu_delay_percent ? 1 : -1);
+ return SPACESHIP_NUMBER(p2->cpu_delay_percent, p1->cpu_delay_percent);
case PERCENT_IO_DELAY:
- return (p2->blkio_delay_percent > p1->blkio_delay_percent ? 1 : -1);
+ return SPACESHIP_NUMBER(p2->blkio_delay_percent, p1->blkio_delay_percent);
case PERCENT_SWAP_DELAY:
- return (p2->swapin_delay_percent > p1->swapin_delay_percent ? 1 : -1);
+ return SPACESHIP_NUMBER(p2->swapin_delay_percent, p1->swapin_delay_percent);
#endif
case IO_PRIORITY:
- return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2);
+ return SPACESHIP_NUMBER(LinuxProcess_effectiveIOPriority(p1), LinuxProcess_effectiveIOPriority(p2));
+ case CTXT:
+ return SPACESHIP_NUMBER(p2->ctxt_diff, p1->ctxt_diff);
+ case SECATTR:
+ return SPACESHIP_NULLSTR(p1->secattr, p2->secattr);
+ case PROC_COMM: {
+ const char *comm1 = p1->procComm ? p1->procComm : (Process_isKernelThread(p1) ? kthreadID : "");
+ const char *comm2 = p2->procComm ? p2->procComm : (Process_isKernelThread(p2) ? kthreadID : "");
+ return strcmp(comm1, comm2);
+ }
+ case PROC_EXE: {
+ const char *exe1 = p1->procExe ? (p1->procExe + p1->procExeBasenameOffset) : (Process_isKernelThread(p1) ? kthreadID : "");
+ const char *exe2 = p2->procExe ? (p2->procExe + p2->procExeBasenameOffset) : (Process_isKernelThread(p2) ? kthreadID : "");
+ return strcmp(exe1, exe2);
+ }
+ case CWD:
+ return SPACESHIP_NULLSTR(p1->cwd, p2->cwd);
default:
return Process_compare(v1, v2);
}
- test_diff:
- return (diff > 0) ? 1 : (diff < 0 ? -1 : 0);
}
-bool Process_isThread(Process* this) {
+bool Process_isThread(const Process* this) {
return (Process_isUserlandThread(this) || Process_isKernelThread(this));
}
+
+const ProcessClass LinuxProcess_class = {
+ .super = {
+ .extends = Class(Process),
+ .display = Process_display,
+ .delete = Process_delete,
+ .compare = LinuxProcess_compare
+ },
+ .writeField = LinuxProcess_writeField,
+ .getCommandStr = LinuxProcess_getCommandStr
+};
diff --git a/linux/LinuxProcess.h b/linux/LinuxProcess.h
index 021cae7..ad396fb 100644
--- a/linux/LinuxProcess.h
+++ b/linux/LinuxProcess.h
@@ -4,16 +4,30 @@
htop - LinuxProcess.h
(C) 2014 Hisham H. Muhammad
(C) 2020 Red Hat, Inc. All Rights Reserved.
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
-#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
-#define PROCESS_FLAG_LINUX_VSERVER 0x0400
-#define PROCESS_FLAG_LINUX_CGROUP 0x0800
-#define PROCESS_FLAG_LINUX_OOM 0x1000
-#define PROCESS_FLAG_LINUX_SMAPS 0x2000
+#include "config.h" // IWYU pragma: keep
+
+#include <stdbool.h>
+
+#include "IOPriority.h"
+#include "Object.h"
+#include "Process.h"
+#include "Settings.h"
+
+
+#define PROCESS_FLAG_LINUX_IOPRIO 0x00000100
+#define PROCESS_FLAG_LINUX_OPENVZ 0x00000200
+#define PROCESS_FLAG_LINUX_VSERVER 0x00000400
+#define PROCESS_FLAG_LINUX_CGROUP 0x00000800
+#define PROCESS_FLAG_LINUX_OOM 0x00001000
+#define PROCESS_FLAG_LINUX_SMAPS 0x00002000
+#define PROCESS_FLAG_LINUX_CTXT 0x00004000
+#define PROCESS_FLAG_LINUX_SECATTR 0x00008000
+#define PROCESS_FLAG_LINUX_LRS_FIX 0x00010000
+#define PROCESS_FLAG_LINUX_CWD 0x00020000
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@@ -55,7 +69,6 @@ typedef enum LinuxProcessFields {
#ifdef HAVE_VSERVER
VXID = 102,
#endif
- #ifdef HAVE_TASKSTATS
RCHAR = 103,
WCHAR = 104,
SYSCR = 105,
@@ -66,10 +79,7 @@ typedef enum LinuxProcessFields {
IO_READ_RATE = 110,
IO_WRITE_RATE = 111,
IO_RATE = 112,
- #endif
- #ifdef HAVE_CGROUP
CGROUP = 113,
- #endif
OOM = 114,
IO_PRIORITY = 115,
#ifdef HAVE_DELAYACCT
@@ -80,13 +90,48 @@ typedef enum LinuxProcessFields {
M_PSS = 119,
M_SWAP = 120,
M_PSSWP = 121,
- LAST_PROCESSFIELD = 122,
+ CTXT = 122,
+ SECATTR = 123,
+ PROC_COMM = 124,
+ PROC_EXE = 125,
+ CWD = 126,
+ LAST_PROCESSFIELD = 127,
} LinuxProcessField;
-#include "IOPriority.h"
+/* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It
+ * contains the merged Command string, and the information needed by
+ * LinuxProcess_writeCommand to color the string. str will be NULL for kernel
+ * threads and zombies */
+typedef struct LinuxProcessMergedCommand_ {
+ char *str; /* merged Command string */
+ int maxLen; /* maximum expected length of Command string */
+ int baseStart; /* basename's start offset */
+ int baseEnd; /* basename's end offset */
+ int commStart; /* comm's start offset */
+ int commEnd; /* comm's end offset */
+ int sep1; /* first field separator, used if non-zero */
+ int sep2; /* second field separator, used if non-zero */
+ bool separateComm; /* whether comm is a separate field */
+ bool unmatchedExe; /* whether exe matched with cmdline */
+ bool cmdlineChanged; /* whether cmdline changed */
+ bool exeChanged; /* whether exe changed */
+ bool commChanged; /* whether comm changed */
+ bool prevMergeSet; /* whether showMergedCommand was set */
+ bool prevPathSet; /* whether showProgramPath was set */
+ bool prevCommSet; /* whether findCommInCmdline was set */
+ bool prevCmdlineSet; /* whether findCommInCmdline was set */
+} LinuxProcessMergedCommand;
typedef struct LinuxProcess_ {
Process super;
+ char *procComm;
+ char *procExe;
+ int procExeLen;
+ int procExeBasenameOffset;
+ bool procExeDeleted;
+ int procCmdlineBasenameOffset;
+ int procCmdlineBasenameEnd;
+ LinuxProcessMergedCommand mergedCommand;
bool isKernelThread;
IOPriority ioPriority;
unsigned long int cminflt;
@@ -103,8 +148,6 @@ typedef struct LinuxProcess_ {
long m_drs;
long m_lrs;
long m_dt;
- unsigned long long starttime;
- #ifdef HAVE_TASKSTATS
unsigned long long io_rchar;
unsigned long long io_wchar;
unsigned long long io_syscr;
@@ -116,17 +159,14 @@ typedef struct LinuxProcess_ {
unsigned long long io_rate_write_time;
double io_rate_read_bps;
double io_rate_write_bps;
- #endif
#ifdef HAVE_OPENVZ
- unsigned int ctid;
- unsigned int vpid;
+ char* ctid;
+ pid_t vpid;
#endif
#ifdef HAVE_VSERVER
unsigned int vxid;
#endif
- #ifdef HAVE_CGROUP
char* cgroup;
- #endif
unsigned int oom;
char* ttyDevice;
#ifdef HAVE_DELAYACCT
@@ -138,11 +178,18 @@ typedef struct LinuxProcess_ {
float blkio_delay_percent;
float swapin_delay_percent;
#endif
+ unsigned long ctxt_total;
+ unsigned long ctxt_diff;
+ char* secattr;
+ unsigned long long int last_mlrs_calctime;
+ char* cwd;
} LinuxProcess;
-#define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
+#define Process_isKernelThread(_process) (((const LinuxProcess*)(_process))->isKernelThread)
-#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
+static inline bool Process_isUserlandThread(const Process* this) {
+ return this->pid != this->tgid;
+}
extern long long btime;
@@ -150,34 +197,20 @@ extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
-extern ProcessClass LinuxProcess_class;
+extern const ProcessClass LinuxProcess_class;
-LinuxProcess* LinuxProcess_new(Settings* settings);
+Process* LinuxProcess_new(const Settings* settings);
void Process_delete(Object* cast);
-/*
-[1] Note that before kernel 2.6.26 a process that has not asked for
-an io priority formally uses "none" as scheduling class, but the
-io scheduler will treat such processes as if it were in the best
-effort class. The priority within the best effort class will be
-dynamically derived from the cpu nice level of the process:
-extern io_priority;
-*/
-#define LinuxProcess_effectiveIOPriority(p_) (IOPriority_class(p_->ioPriority) == IOPRIO_CLASS_NONE ? IOPriority_tuple(IOPRIO_CLASS_BE, (p_->super.nice + 20) / 5) : p_->ioPriority)
-
IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
-bool LinuxProcess_setIOPriority(LinuxProcess* this, Arg ioprio);
-
-#ifdef HAVE_DELAYACCT
-void LinuxProcess_printDelay(float delay_percent, char* buffer, int n);
-#endif
-
-void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field);
+bool LinuxProcess_setIOPriority(Process* this, Arg ioprio);
-long LinuxProcess_compare(const void* v1, const void* v2);
+/* This function constructs the string that is displayed by
+ * LinuxProcess_writeCommand and also returned by LinuxProcess_getCommandStr */
+void LinuxProcess_makeCommandStr(Process *this);
-bool Process_isThread(Process* this);
+bool Process_isThread(const Process* this);
#endif
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c
index 8dae13c..b9ba247 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessList.c
@@ -1,95 +1,98 @@
/*
htop - LinuxProcessList.c
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h" // IWYU pragma: keep
+
#include "LinuxProcessList.h"
-#include "LinuxProcess.h"
-#include "CRT.h"
-#include "StringUtils.h"
-#include <errno.h>
-#include <sys/time.h>
-#include <sys/utsname.h>
-#include <sys/stat.h>
-#include <unistd.h>
+
+#include <assert.h>
#include <dirent.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdarg.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
#include <math.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <time.h>
-#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/types.h>
-#include <fcntl.h>
-#ifdef MAJOR_IN_MKDEV
-#include <sys/mkdev.h>
-#elif defined(MAJOR_IN_SYSMACROS)
-#include <sys/sysmacros.h>
-#endif
#ifdef HAVE_DELAYACCT
+#include <linux/netlink.h>
+#include <linux/taskstats.h>
#include <netlink/attr.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
#include <netlink/netlink.h>
+#include <netlink/socket.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
-#include <netlink/socket.h>
-#include <netlink/msg.h>
-#include <linux/taskstats.h>
#endif
-static ssize_t xread(int fd, void *buf, size_t count) {
- // Read some bytes. Retry on EINTR and when we don't get as many bytes as we requested.
- size_t alreadyRead = 0;
- for(;;) {
- ssize_t res = read(fd, buf, count);
- if (res == -1 && errno == EINTR) continue;
- if (res > 0) {
- buf = ((char*)buf)+res;
- count -= res;
- alreadyRead += res;
- }
- if (res == -1) return -1;
- if (count == 0 || res == 0) return alreadyRead;
- }
+#include "Compat.h"
+#include "CRT.h"
+#include "LinuxProcess.h"
+#include "Macros.h"
+#include "Object.h"
+#include "Process.h"
+#include "Settings.h"
+#include "XUtils.h"
+
+#ifdef MAJOR_IN_MKDEV
+#include <sys/mkdev.h>
+#elif defined(MAJOR_IN_SYSMACROS)
+#include <sys/sysmacros.h>
+#endif
+
+#ifdef HAVE_SENSORS_SENSORS_H
+#include "LibSensors.h"
+#endif
+
+
+static FILE* fopenat(openat_arg_t openatArg, const char* pathname, const char* mode) {
+ assert(String_eq(mode, "r")); /* only currently supported mode */
+
+ int fd = Compat_openat(openatArg, pathname, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ FILE* stream = fdopen(fd, mode);
+ if (!stream)
+ close(fd);
+
+ return stream;
}
static int sortTtyDrivers(const void* va, const void* vb) {
- TtyDriver* a = (TtyDriver*) va;
- TtyDriver* b = (TtyDriver*) vb;
- return (a->major == b->major) ? (a->minorFrom - b->minorFrom) : (a->major - b->major);
+ const TtyDriver* a = (const TtyDriver*) va;
+ const TtyDriver* b = (const TtyDriver*) vb;
+
+ int r = SPACESHIP_NUMBER(a->major, b->major);
+ if (r)
+ return r;
+
+ return SPACESHIP_NUMBER(a->minorFrom, b->minorFrom);
}
static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
TtyDriver* ttyDrivers;
- int fd = open(PROCTTYDRIVERSFILE, O_RDONLY);
- if (fd == -1)
- return;
- char* buf = NULL;
- int bufSize = MAX_READ;
- int bufLen = 0;
- for(;;) {
- buf = realloc(buf, bufSize);
- int size = xread(fd, buf + bufLen, MAX_READ);
- if (size <= 0) {
- buf[bufLen] = '\0';
- close(fd);
- break;
- }
- bufLen += size;
- bufSize += MAX_READ;
- }
- if (bufLen == 0) {
- free(buf);
+
+ char buf[16384];
+ ssize_t r = xReadfile(PROCTTYDRIVERSFILE, buf, sizeof(buf));
+ if (r < 0)
return;
- }
+
int numDrivers = 0;
int allocd = 10;
- ttyDrivers = malloc(sizeof(TtyDriver) * allocd);
+ ttyDrivers = xMalloc(sizeof(TtyDriver) * allocd);
char* at = buf;
while (*at != '\0') {
at = strchr(at, ' '); // skip first token
@@ -97,7 +100,7 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
char* token = at; // mark beginning of path
at = strchr(at, ' '); // find end of path
*at = '\0'; at++; // clear and skip
- ttyDrivers[numDrivers].path = strdup(token); // save
+ ttyDrivers[numDrivers].path = xStrdup(token); // save
while (*at == ' ') at++; // skip spaces
token = at; // mark beginning of major
at = strchr(at, ' '); // find end of major
@@ -123,12 +126,11 @@ static void LinuxProcessList_initTtyDrivers(LinuxProcessList* this) {
numDrivers++;
if (numDrivers == allocd) {
allocd += 10;
- ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * allocd);
+ ttyDrivers = xRealloc(ttyDrivers, sizeof(TtyDriver) * allocd);
}
}
- free(buf);
numDrivers++;
- ttyDrivers = realloc(ttyDrivers, sizeof(TtyDriver) * numDrivers);
+ ttyDrivers = xRealloc(ttyDrivers, sizeof(TtyDriver) * numDrivers);
ttyDrivers[numDrivers - 1].path = NULL;
qsort(ttyDrivers, numDrivers - 1, sizeof(TtyDriver), sortTtyDrivers);
this->ttyDrivers = ttyDrivers;
@@ -149,6 +151,46 @@ static void LinuxProcessList_initNetlinkSocket(LinuxProcessList* this) {
#endif
+static int LinuxProcessList_computeCPUcount(void) {
+ FILE* file = fopen(PROCSTATFILE, "r");
+ if (file == NULL) {
+ CRT_fatalError("Cannot open " PROCSTATFILE);
+ }
+
+ int cpus = 0;
+ char buffer[PROC_LINE_LENGTH + 1];
+ while (fgets(buffer, sizeof(buffer), file)) {
+ if (String_startsWith(buffer, "cpu")) {
+ cpus++;
+ }
+ }
+
+ fclose(file);
+
+ /* subtract raw cpu entry */
+ if (cpus > 0) {
+ cpus--;
+ }
+
+ return cpus;
+}
+
+static void LinuxProcessList_updateCPUcount(LinuxProcessList* this) {
+ ProcessList* pl = &(this->super);
+ int cpus = LinuxProcessList_computeCPUcount();
+ if (cpus == 0 || cpus == pl->cpuCount)
+ return;
+
+ pl->cpuCount = cpus;
+ free(this->cpus);
+ this->cpus = xCalloc(cpus + 1, sizeof(CPUData));
+
+ for (int i = 0; i <= cpus; i++) {
+ this->cpus[i].totalTime = 1;
+ this->cpus[i].totalPeriod = 1;
+ }
+}
+
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
LinuxProcessList* this = xCalloc(1, sizeof(LinuxProcessList));
ProcessList* pl = &(this->super);
@@ -162,41 +204,47 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
// Check for /proc/*/smaps_rollup availability (improves smaps parsing speed, Linux 4.14+)
FILE* file = fopen(PROCDIR "/self/smaps_rollup", "r");
- if(file != NULL) {
+ if (file != NULL) {
this->haveSmapsRollup = true;
fclose(file);
} else {
this->haveSmapsRollup = false;
}
- // Update CPU count:
- file = fopen(PROCSTATFILE, "r");
- if (file == NULL) {
- CRT_fatalError("Cannot open " PROCSTATFILE);
- }
- int cpus = 0;
- do {
- char buffer[PROC_LINE_LENGTH + 1];
- if (fgets(buffer, PROC_LINE_LENGTH + 1, file) == NULL) {
- CRT_fatalError("No btime in " PROCSTATFILE);
- } else if (String_startsWith(buffer, "cpu")) {
- cpus++;
- } else if (String_startsWith(buffer, "btime ")) {
- if (sscanf(buffer, "btime %lld\n", &btime) != 1)
- CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
- break;
+ // Read btime
+ {
+ FILE* statfile = fopen(PROCSTATFILE, "r");
+ if (statfile == NULL) {
+ CRT_fatalError("Cannot open " PROCSTATFILE);
}
- } while(true);
- fclose(file);
+ while (true) {
+ char buffer[PROC_LINE_LENGTH + 1];
+ if (fgets(buffer, sizeof(buffer), statfile) == NULL) {
+ CRT_fatalError("No btime in " PROCSTATFILE);
+ } else if (String_startsWith(buffer, "btime ")) {
+ if (sscanf(buffer, "btime %lld\n", &btime) != 1) {
+ CRT_fatalError("Failed to parse btime from " PROCSTATFILE);
+ }
+ break;
+ }
+ }
- pl->cpuCount = MAXIMUM(cpus - 1, 1);
- this->cpus = xCalloc(cpus, sizeof(CPUData));
+ fclose(statfile);
+ }
- for (int i = 0; i < cpus; i++) {
- this->cpus[i].totalTime = 1;
- this->cpus[i].totalPeriod = 1;
+ // Initialize CPU count
+ {
+ int cpus = LinuxProcessList_computeCPUcount();
+ pl->cpuCount = MAXIMUM(cpus, 1);
+ this->cpus = xCalloc(cpus + 1, sizeof(CPUData));
+
+ for (int i = 0; i <= cpus; i++) {
+ this->cpus[i].totalTime = 1;
+ this->cpus[i].totalPeriod = 1;
+ }
}
+
return pl;
}
@@ -205,7 +253,7 @@ void ProcessList_delete(ProcessList* pl) {
ProcessList_done(pl);
free(this->cpus);
if (this->ttyDrivers) {
- for(int i = 0; this->ttyDrivers[i].path; i++) {
+ for (int i = 0; this->ttyDrivers[i].path; i++) {
free(this->ttyDrivers[i].path);
}
free(this->ttyDrivers);
@@ -219,38 +267,41 @@ void ProcessList_delete(ProcessList* pl) {
free(this);
}
-static double jiffy = 0.0;
-
static inline unsigned long long LinuxProcess_adjustTime(unsigned long long t) {
- if(jiffy == 0.0) jiffy = sysconf(_SC_CLK_TCK);
- double jiffytime = 1.0 / jiffy;
- return (unsigned long long) t * jiffytime * 100;
+ static long jiffy = -1;
+ if (jiffy == -1) {
+ errno = 0;
+ jiffy = sysconf(_SC_CLK_TCK);
+ if (errno || -1 == jiffy) {
+ jiffy = -1;
+ return t; // Assume 100Hz clock
+ }
+ }
+ return t * 100 / jiffy;
}
-static bool LinuxProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command, int* commLen) {
+static bool LinuxProcessList_readStatFile(Process* process, openat_arg_t procFd, char* command, int* commLen) {
LinuxProcess* lp = (LinuxProcess*) process;
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
- int fd = open(filename, O_RDONLY);
- if (fd == -1)
- return false;
-
- static char buf[MAX_READ+1];
+ const int commLenIn = *commLen;
+ *commLen = 0;
- int size = xread(fd, buf, MAX_READ);
- close(fd);
- if (size <= 0) return false;
- buf[size] = '\0';
+ char buf[MAX_READ + 1];
+ ssize_t r = xReadfileat(procFd, "stat", buf, sizeof(buf));
+ if (r < 0)
+ return false;
assert(process->pid == atoi(buf));
- char *location = strchr(buf, ' ');
- if (!location) return false;
+ char* location = strchr(buf, ' ');
+ if (!location)
+ return false;
location += 2;
- char *end = strrchr(location, ')');
- if (!end) return false;
+ char* end = strrchr(location, ')');
+ if (!end)
+ return false;
- int commsize = end - location;
+ int commsize = MINIMUM(end - location, commLenIn - 1);
+ // deepcode ignore BufferOverflow: commsize is bounded by the allocated length passed in by commLen, saved into commLenIn
memcpy(command, location, commsize);
command[commsize] = '\0';
*commLen = commsize;
@@ -292,10 +343,16 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname,
location += 1;
process->nlwp = strtol(location, &location, 10);
location += 1;
- location = strchr(location, ' ')+1;
- lp->starttime = strtoll(location, &location, 10);
+ location = strchr(location, ' ') + 1;
+ if (process->starttime_ctime == 0) {
+ process->starttime_ctime = btime + LinuxProcess_adjustTime(strtoll(location, &location, 10)) / 100;
+ } else {
+ location = strchr(location, ' ') + 1;
+ }
location += 1;
- for (int i=0; i<15; i++) location = strchr(location, ' ')+1;
+ for (int i = 0; i < 15; i++) {
+ location = strchr(location, ' ') + 1;
+ }
process->exit_signal = strtol(location, &location, 10);
location += 1;
assert(location != NULL);
@@ -307,30 +364,25 @@ static bool LinuxProcessList_readStatFile(Process *process, const char* dirname,
}
-static bool LinuxProcessList_statProcessDir(Process* process, const char* dirname, char* name) {
- char filename[MAX_NAME+1];
- filename[MAX_NAME] = '\0';
-
- xSnprintf(filename, MAX_NAME, "%s/%s", dirname, name);
+static bool LinuxProcessList_statProcessDir(Process* process, openat_arg_t procFd) {
struct stat sstat;
- int statok = stat(filename, &sstat);
+#ifdef HAVE_OPENAT
+ int statok = fstat(procFd, &sstat);
+#else
+ int statok = stat(procFd, &sstat);
+#endif
if (statok == -1)
return false;
process->st_uid = sstat.st_uid;
return true;
}
-#ifdef HAVE_TASKSTATS
-
-static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirname, char* name, unsigned long long now) {
- char filename[MAX_NAME+1];
- filename[MAX_NAME] = '\0';
-
- xSnprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
- int fd = open(filename, O_RDONLY);
- if (fd == -1) {
- process->io_rate_read_bps = -1;
- process->io_rate_write_bps = -1;
+static void LinuxProcessList_readIoFile(LinuxProcess* process, openat_arg_t procFd, unsigned long long now) {
+ char buffer[1024];
+ ssize_t r = xReadfileat(procFd, "io", buffer, sizeof(buffer));
+ if (r < 0) {
+ process->io_rate_read_bps = NAN;
+ process->io_rate_write_bps = NAN;
process->io_rchar = -1LL;
process->io_wchar = -1LL;
process->io_syscr = -1LL;
@@ -343,171 +395,361 @@ static void LinuxProcessList_readIoFile(LinuxProcess* process, const char* dirna
return;
}
- char buffer[1024];
- ssize_t buflen = xread(fd, buffer, 1023);
- close(fd);
- if (buflen < 1) return;
- buffer[buflen] = '\0';
unsigned long long last_read = process->io_read_bytes;
unsigned long long last_write = process->io_write_bytes;
- char *buf = buffer;
- char *line = NULL;
+ char* buf = buffer;
+ char* line = NULL;
while ((line = strsep(&buf, "\n")) != NULL) {
switch (line[0]) {
case 'r':
- if (line[1] == 'c' && strncmp(line+2, "har: ", 5) == 0)
- process->io_rchar = strtoull(line+7, NULL, 10);
- else if (strncmp(line+1, "ead_bytes: ", 11) == 0) {
- process->io_read_bytes = strtoull(line+12, NULL, 10);
+ if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
+ process->io_rchar = strtoull(line + 7, NULL, 10);
+ } else if (String_startsWith(line + 1, "ead_bytes: ")) {
+ process->io_read_bytes = strtoull(line + 12, NULL, 10);
process->io_rate_read_bps =
- ((double)(process->io_read_bytes - last_read))/(((double)(now - process->io_rate_read_time))/1000);
+ ((double)(process->io_read_bytes - last_read)) / (((double)(now - process->io_rate_read_time)) / 1000);
process->io_rate_read_time = now;
}
break;
case 'w':
- if (line[1] == 'c' && strncmp(line+2, "har: ", 5) == 0)
- process->io_wchar = strtoull(line+7, NULL, 10);
- else if (strncmp(line+1, "rite_bytes: ", 12) == 0) {
- process->io_write_bytes = strtoull(line+13, NULL, 10);
+ if (line[1] == 'c' && String_startsWith(line + 2, "har: ")) {
+ process->io_wchar = strtoull(line + 7, NULL, 10);
+ } else if (String_startsWith(line + 1, "rite_bytes: ")) {
+ process->io_write_bytes = strtoull(line + 13, NULL, 10);
process->io_rate_write_bps =
- ((double)(process->io_write_bytes - last_write))/(((double)(now - process->io_rate_write_time))/1000);
+ ((double)(process->io_write_bytes - last_write)) / (((double)(now - process->io_rate_write_time)) / 1000);
process->io_rate_write_time = now;
}
break;
case 's':
- if (line[4] == 'r' && strncmp(line+1, "yscr: ", 6) == 0) {
- process->io_syscr = strtoull(line+7, NULL, 10);
- } else if (strncmp(line+1, "yscw: ", 6) == 0) {
- process->io_syscw = strtoull(line+7, NULL, 10);
+ if (line[4] == 'r' && String_startsWith(line + 1, "yscr: ")) {
+ process->io_syscr = strtoull(line + 7, NULL, 10);
+ } else if (String_startsWith(line + 1, "yscw: ")) {
+ process->io_syscw = strtoull(line + 7, NULL, 10);
}
break;
case 'c':
- if (strncmp(line+1, "ancelled_write_bytes: ", 22) == 0) {
- process->io_cancelled_write_bytes = strtoull(line+23, NULL, 10);
- }
+ if (String_startsWith(line + 1, "ancelled_write_bytes: ")) {
+ process->io_cancelled_write_bytes = strtoull(line + 23, NULL, 10);
+ }
}
}
}
-#endif
+typedef struct LibraryData_ {
+ uint64_t size;
+ bool exec;
+} LibraryData;
+
+static inline uint64_t fast_strtoull_dec(char **str, int maxlen) {
+ register uint64_t result = 0;
+
+ if (!maxlen)
+ --maxlen;
+
+ while (maxlen-- && **str >= '0' && **str <= '9') {
+ result *= 10;
+ result += **str - '0';
+ (*str)++;
+ }
+
+ return result;
+}
+
+static inline uint64_t fast_strtoull_hex(char **str, int maxlen) {
+ register uint64_t result = 0;
+ register int nibble, letter;
+ const long valid_mask = 0x03FF007E;
+
+ if (!maxlen)
+ --maxlen;
+
+ while (maxlen--) {
+ nibble = (unsigned char)**str;
+ if (!(valid_mask & (1 << (nibble & 0x1F))))
+ break;
+ if ((nibble < '0') || (nibble & ~0x20) > 'F')
+ break;
+ letter = (nibble & 0x40) ? 'A' - '9' - 1 : 0;
+ nibble &=~0x20; // to upper
+ nibble ^= 0x10; // switch letters and digits
+ nibble -= letter;
+ nibble &= 0x0f;
+ result <<= 4;
+ result += (uint64_t)nibble;
+ (*str)++;
+ }
+
+ return result;
+}
+
+static void LinuxProcessList_calcLibSize_helper(ATTR_UNUSED hkey_t key, void* value, void* data) {
+ if (!data)
+ return;
+
+ if (!value)
+ return;
+
+ LibraryData* v = (LibraryData *)value;
+ uint64_t* d = (uint64_t *)data;
+ if (!v->exec)
+ return;
+
+ *d += v->size;
+}
+
+static uint64_t LinuxProcessList_calcLibSize(openat_arg_t procFd) {
+ FILE* mapsfile = fopenat(procFd, "maps", "r");
+ if (!mapsfile)
+ return 0;
+ Hashtable* ht = Hashtable_new(64, true);
+ char buffer[1024];
+ while (fgets(buffer, sizeof(buffer), mapsfile)) {
+ uint64_t map_start;
+ uint64_t map_end;
+ char map_perm[5];
+ unsigned int map_devmaj;
+ unsigned int map_devmin;
+ uint64_t map_inode;
+
+ // Short circuit test: Look for a slash
+ if (!strchr(buffer, '/'))
+ continue;
+
+ // Parse format: "%Lx-%Lx %4s %x %2x:%2x %Ld"
+ char *readptr = buffer;
+
+ map_start = fast_strtoull_hex(&readptr, 16);
+ if ('-' != *readptr++)
+ continue;
+
+ map_end = fast_strtoull_hex(&readptr, 16);
+ if (' ' != *readptr++)
+ continue;
+
+ memcpy(map_perm, readptr, 4);
+ map_perm[4] = '\0';
+ readptr += 4;
+ if (' ' != *readptr++)
+ continue;
+
+ while(*readptr > ' ')
+ readptr++; // Skip parsing this hex value
+ if (' ' != *readptr++)
+ continue;
+
+ map_devmaj = fast_strtoull_hex(&readptr, 4);
+ if (':' != *readptr++)
+ continue;
+
+ map_devmin = fast_strtoull_hex(&readptr, 4);
+ if (' ' != *readptr++)
+ continue;
-static bool LinuxProcessList_readStatmFile(LinuxProcess* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
- int fd = open(filename, O_RDONLY);
- if (fd == -1)
+ //Minor shortcut: Once we know there's no file for this region, we skip
+ if (!map_devmaj && !map_devmin)
+ continue;
+
+ map_inode = fast_strtoull_dec(&readptr, 20);
+ if (!map_inode)
+ continue;
+
+ LibraryData* libdata = Hashtable_get(ht, map_inode);
+ if (!libdata) {
+ libdata = xCalloc(1, sizeof(LibraryData));
+ Hashtable_put(ht, map_inode, libdata);
+ }
+
+ libdata->size += map_end - map_start;
+ libdata->exec |= 'x' == map_perm[2];
+ }
+
+ fclose(mapsfile);
+
+ uint64_t total_size = 0;
+ Hashtable_foreach(ht, LinuxProcessList_calcLibSize_helper, &total_size);
+
+ Hashtable_delete(ht);
+
+ return total_size / CRT_pageSize;
+}
+
+static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t procFd, bool performLookup, unsigned long long now) {
+ FILE* statmfile = fopenat(procFd, "statm", "r");
+ if (!statmfile)
return false;
- char buf[PROC_LINE_LENGTH + 1];
- ssize_t rres = xread(fd, buf, PROC_LINE_LENGTH);
- close(fd);
- if (rres < 1) return false;
-
- char *p = buf;
- errno = 0;
- process->super.m_size = strtol(p, &p, 10); if (*p == ' ') p++;
- process->super.m_resident = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_share = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_trs = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_lrs = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_drs = strtol(p, &p, 10); if (*p == ' ') p++;
- process->m_dt = strtol(p, &p, 10);
- return (errno == 0);
+
+ long tmp_m_lrs = 0;
+ int r = fscanf(statmfile, "%ld %ld %ld %ld %ld %ld %ld",
+ &process->super.m_virt,
+ &process->super.m_resident,
+ &process->m_share,
+ &process->m_trs,
+ &tmp_m_lrs,
+ &process->m_drs,
+ &process->m_dt);
+ fclose(statmfile);
+
+ if (r == 7) {
+ if (tmp_m_lrs) {
+ process->m_lrs = tmp_m_lrs;
+ } else if (performLookup) {
+ // Check if we really should recalculate the M_LRS value for this process
+ uint64_t passedTimeInMs = now - process->last_mlrs_calctime;
+
+ uint64_t recheck = ((uint64_t)rand()) % 2048;
+
+ if(passedTimeInMs > 2000 || passedTimeInMs > recheck) {
+ process->last_mlrs_calctime = now;
+ process->m_lrs = LinuxProcessList_calcLibSize(procFd);
+ }
+ } else {
+ // Keep previous value
+ }
+ }
+
+ return r == 7;
}
-static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, const char* dirname, const char* name, bool haveSmapsRollup) {
+static bool LinuxProcessList_readSmapsFile(LinuxProcess* process, openat_arg_t procFd, bool haveSmapsRollup) {
//http://elixir.free-electrons.com/linux/v4.10/source/fs/proc/task_mmu.c#L719
//kernel will return data in chunks of size PAGE_SIZE or less.
-
- char buffer[PAGE_SIZE];// 4k
- char *start,*end;
- ssize_t nread=0;
- int tmp=0;
- if(haveSmapsRollup) {// only available in Linux 4.14+
- snprintf(buffer, PAGE_SIZE-1, "%s/%s/smaps_rollup", dirname, name);
- } else {
- snprintf(buffer, PAGE_SIZE-1, "%s/%s/smaps", dirname, name);
- }
- int fd = open(buffer, O_RDONLY);
- if (fd == -1)
+ FILE* f = fopenat(procFd, haveSmapsRollup ? "smaps_rollup" : "smaps", "r");
+ if (!f)
return false;
process->m_pss = 0;
process->m_swap = 0;
process->m_psswp = 0;
- while ( ( nread = read(fd,buffer, sizeof(buffer)) ) > 0 ){
- start = (char *)&buffer;
- end = start + nread;
- do{//parse 4k block
-
- if( (tmp = (end - start)) > 0 &&
- (start = memmem(start,tmp,"\nPss:",5)) != NULL )
- {
- process->m_pss += strtol(start+5, &start, 10);
- start += 3;//now we must be at the end of line "Pss: 0 kB"
- }else
- break; //read next 4k block
-
- if( (tmp = (end - start)) > 0 &&
- (start = memmem(start,tmp,"\nSwap:",6)) != NULL )
- {
- process->m_swap += strtol(start+6, &start, 10);
- start += 3;
- }else
- break;
-
- if( (tmp = (end - start)) > 0 &&
- (start = memmem(start,tmp,"\nSwapPss:",9)) != NULL )
- {
- process->m_psswp += strtol(start+9, &start, 10);
- start += 3;
- }else
- break;
-
- }while(1);
- }//while read
- close(fd);
+ char buffer[256];
+ while (fgets(buffer, sizeof(buffer), f)) {
+ if (!strchr(buffer, '\n')) {
+ // Partial line, skip to end of this line
+ while (fgets(buffer, sizeof(buffer), f)) {
+ if (strchr(buffer, '\n')) {
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (String_startsWith(buffer, "Pss:")) {
+ process->m_pss += strtol(buffer + 4, NULL, 10);
+ } else if (String_startsWith(buffer, "Swap:")) {
+ process->m_swap += strtol(buffer + 5, NULL, 10);
+ } else if (String_startsWith(buffer, "SwapPss:")) {
+ process->m_psswp += strtol(buffer + 8, NULL, 10);
+ }
+ }
+
+ fclose(f);
return true;
}
#ifdef HAVE_OPENVZ
-static void LinuxProcessList_readOpenVZData(LinuxProcess* process, const char* dirname, const char* name) {
- if ( (access("/proc/vz", R_OK) != 0)) {
+static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t procFd) {
+ if ( (access(PROCDIR "/vz", R_OK) != 0)) {
+ free(process->ctid);
+ process->ctid = NULL;
process->vpid = process->super.pid;
- process->ctid = 0;
return;
}
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
- FILE* file = fopen(filename, "r");
- if (!file)
+
+ FILE* file = fopenat(procFd, "status", "r");
+ if (!file) {
+ free(process->ctid);
+ process->ctid = NULL;
+ process->vpid = process->super.pid;
return;
- (void)! fscanf(file,
- "%*32u %*32s %*1c %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %*32u %*32u %*32u %*32u %*32u "
- "%*32u %*32u %32u %32u",
- &process->vpid, &process->ctid);
+ }
+
+ bool foundEnvID = false;
+ bool foundVPid = false;
+ char linebuf[256];
+ while (fgets(linebuf, sizeof(linebuf), file) != NULL) {
+ if (strchr(linebuf, '\n') == NULL) {
+ // Partial line, skip to end of this line
+ while (fgets(linebuf, sizeof(linebuf), file) != NULL) {
+ if (strchr(linebuf, '\n') != NULL) {
+ break;
+ }
+ }
+ continue;
+ }
+
+ char* name_value_sep = strchr(linebuf, ':');
+ if (name_value_sep == NULL) {
+ continue;
+ }
+
+ int field;
+ if (0 == strncasecmp(linebuf, "envID", name_value_sep - linebuf)) {
+ field = 1;
+ } else if (0 == strncasecmp(linebuf, "VPid", name_value_sep - linebuf)) {
+ field = 2;
+ } else {
+ continue;
+ }
+
+ do {
+ name_value_sep++;
+ } while (*name_value_sep != '\0' && *name_value_sep <= 32);
+
+ char* value_end = name_value_sep;
+
+ while(*value_end > 32) {
+ value_end++;
+ }
+
+ if (name_value_sep == value_end) {
+ continue;
+ }
+
+ *value_end = '\0';
+
+ switch(field) {
+ case 1:
+ foundEnvID = true;
+ if (!String_eq(name_value_sep, process->ctid ? process->ctid : "")) {
+ free(process->ctid);
+ process->ctid = xStrdup(name_value_sep);
+ }
+ break;
+ case 2:
+ foundVPid = true;
+ process->vpid = strtoul(name_value_sep, NULL, 0);
+ break;
+ default:
+ //Sanity Check: Should never reach here, or the implementation is missing something!
+ assert(false && "OpenVZ handling: Unimplemented case for field handling reached.");
+ }
+ }
+
fclose(file);
- return;
+
+ if (!foundEnvID) {
+ free(process->ctid);
+ process->ctid = NULL;
+ }
+
+ if (!foundVPid) {
+ process->vpid = process->super.pid;
+ }
}
#endif
-#ifdef HAVE_CGROUP
-
-static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
- FILE* file = fopen(filename, "r");
+static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "cgroup", "r");
if (!file) {
- process->cgroup = xStrdup("");
+ if (process->cgroup) {
+ free(process->cgroup);
+ process->cgroup = NULL;
+ }
return;
}
char output[PROC_LINE_LENGTH + 1];
@@ -516,10 +758,14 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* d
int left = PROC_LINE_LENGTH;
while (!feof(file) && left > 0) {
char buffer[PROC_LINE_LENGTH + 1];
- char *ok = fgets(buffer, PROC_LINE_LENGTH, file);
- if (!ok) break;
+ char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
+ if (!ok)
+ break;
+
char* group = strchr(buffer, ':');
- if (!group) break;
+ if (!group)
+ break;
+
if (at != output) {
*at = ';';
at++;
@@ -533,16 +779,13 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, const char* d
process->cgroup = xStrdup(output);
}
-#endif
-
#ifdef HAVE_VSERVER
-static void LinuxProcessList_readVServerData(LinuxProcess* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
- FILE* file = fopen(filename, "r");
+static void LinuxProcessList_readVServerData(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "status", "r");
if (!file)
return;
+
char buffer[PROC_LINE_LENGTH + 1];
process->vxid = 0;
while (fgets(buffer, PROC_LINE_LENGTH, file)) {
@@ -568,13 +811,11 @@ static void LinuxProcessList_readVServerData(LinuxProcess* process, const char*
#endif
-static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/oom_score", dirname, name);
- FILE* file = fopen(filename, "r");
- if (!file) {
+static void LinuxProcessList_readOomData(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "oom_score", "r");
+ if (!file)
return;
- }
+
char buffer[PROC_LINE_LENGTH + 1];
if (fgets(buffer, PROC_LINE_LENGTH, file)) {
unsigned int oom;
@@ -586,15 +827,94 @@ static void LinuxProcessList_readOomData(LinuxProcess* process, const char* dirn
fclose(file);
}
+static void LinuxProcessList_readCtxtData(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "status", "r");
+ if (!file)
+ return;
+
+ char buffer[PROC_LINE_LENGTH + 1];
+ unsigned long ctxt = 0;
+ while (fgets(buffer, PROC_LINE_LENGTH, file)) {
+ if (String_startsWith(buffer, "voluntary_ctxt_switches:")) {
+ unsigned long vctxt;
+ int ok = sscanf(buffer, "voluntary_ctxt_switches:\t%lu", &vctxt);
+ if (ok >= 1) {
+ ctxt += vctxt;
+ }
+ } else if (String_startsWith(buffer, "nonvoluntary_ctxt_switches:")) {
+ unsigned long nvctxt;
+ int ok = sscanf(buffer, "nonvoluntary_ctxt_switches:\t%lu", &nvctxt);
+ if (ok >= 1) {
+ ctxt += nvctxt;
+ }
+ }
+ }
+ fclose(file);
+ process->ctxt_diff = (ctxt > process->ctxt_total) ? (ctxt - process->ctxt_total) : 0;
+ process->ctxt_total = ctxt;
+}
+
+static void LinuxProcessList_readSecattrData(LinuxProcess* process, openat_arg_t procFd) {
+ FILE* file = fopenat(procFd, "attr/current", "r");
+ if (!file) {
+ free(process->secattr);
+ process->secattr = NULL;
+ return;
+ }
+
+ char buffer[PROC_LINE_LENGTH + 1];
+ char* res = fgets(buffer, sizeof(buffer), file);
+ fclose(file);
+ if (!res) {
+ free(process->secattr);
+ process->secattr = NULL;
+ return;
+ }
+ char* newline = strchr(buffer, '\n');
+ if (newline) {
+ *newline = '\0';
+ }
+ if (process->secattr && String_eq(process->secattr, buffer)) {
+ return;
+ }
+ free(process->secattr);
+ process->secattr = xStrdup(buffer);
+}
+
+static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) {
+ char pathBuffer[PATH_MAX + 1] = {0};
+
+#if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT)
+ ssize_t r = readlinkat(procFd, "cwd", pathBuffer, sizeof(pathBuffer) - 1);
+#else
+ char filename[MAX_NAME + 1];
+ xSnprintf(filename, sizeof(filename), "%s/cwd", procFd);
+ ssize_t r = readlink(filename, pathBuffer, sizeof(pathBuffer) - 1);
+#endif
+
+ if (r < 0) {
+ free(process->cwd);
+ process->cwd = NULL;
+ return;
+ }
+
+ pathBuffer[r] = '\0';
+
+ if (process->cwd && String_eq(process->cwd, pathBuffer))
+ return;
+
+ free(process->cwd);
+ process->cwd = xStrdup(pathBuffer);
+}
+
#ifdef HAVE_DELAYACCT
-static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) {
- struct nlmsghdr *nlhdr;
- struct nlattr *nlattrs[TASKSTATS_TYPE_MAX + 1];
- struct nlattr *nlattr;
- struct taskstats *stats;
+static int handleNetlinkMsg(struct nl_msg* nlmsg, void* linuxProcess) {
+ struct nlmsghdr* nlhdr;
+ struct nlattr* nlattrs[TASKSTATS_TYPE_MAX + 1];
+ struct nlattr* nlattr;
+ struct taskstats stats;
int rem;
- unsigned long long int timeDelta;
LinuxProcess* lp = (LinuxProcess*) linuxProcess;
nlhdr = nlmsg_hdr(nlmsg);
@@ -604,26 +924,28 @@ static int handleNetlinkMsg(struct nl_msg *nlmsg, void *linuxProcess) {
}
if ((nlattr = nlattrs[TASKSTATS_TYPE_AGGR_PID]) || (nlattr = nlattrs[TASKSTATS_TYPE_NULL])) {
- stats = nla_data(nla_next(nla_data(nlattr), &rem));
- assert(lp->super.pid == stats->ac_pid);
- timeDelta = (stats->ac_etime*1000 - lp->delay_read_time);
- #define BOUNDS(x) isnan(x) ? 0.0 : (x > 100) ? 100.0 : x;
- #define DELTAPERC(x,y) BOUNDS((float) (x - y) / timeDelta * 100);
- lp->cpu_delay_percent = DELTAPERC(stats->cpu_delay_total, lp->cpu_delay_total);
- lp->blkio_delay_percent = DELTAPERC(stats->blkio_delay_total, lp->blkio_delay_total);
- lp->swapin_delay_percent = DELTAPERC(stats->swapin_delay_total, lp->swapin_delay_total);
+ memcpy(&stats, nla_data(nla_next(nla_data(nlattr), &rem)), sizeof(stats));
+ assert(lp->super.pid == (pid_t)stats.ac_pid);
+
+ unsigned long long int timeDelta = stats.ac_etime * 1000 - lp->delay_read_time;
+ #define BOUNDS(x) (isnan(x) ? 0.0 : ((x) > 100) ? 100.0 : (x))
+ #define DELTAPERC(x,y) BOUNDS((float) ((x) - (y)) / timeDelta * 100)
+ lp->cpu_delay_percent = DELTAPERC(stats.cpu_delay_total, lp->cpu_delay_total);
+ lp->blkio_delay_percent = DELTAPERC(stats.blkio_delay_total, lp->blkio_delay_total);
+ lp->swapin_delay_percent = DELTAPERC(stats.swapin_delay_total, lp->swapin_delay_total);
#undef DELTAPERC
#undef BOUNDS
- lp->swapin_delay_total = stats->swapin_delay_total;
- lp->blkio_delay_total = stats->blkio_delay_total;
- lp->cpu_delay_total = stats->cpu_delay_total;
- lp->delay_read_time = stats->ac_etime*1000;
+
+ lp->swapin_delay_total = stats.swapin_delay_total;
+ lp->blkio_delay_total = stats.blkio_delay_total;
+ lp->cpu_delay_total = stats.cpu_delay_total;
+ lp->delay_read_time = stats.ac_etime * 1000;
}
return NL_OK;
}
static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProcess* process) {
- struct nl_msg *msg;
+ struct nl_msg* msg;
if (nl_socket_modify_cb(this->netlink_socket, NL_CB_VALID, NL_CB_CUSTOM, handleNetlinkMsg, process) < 0) {
return;
@@ -642,9 +964,9 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
}
if (nl_send_sync(this->netlink_socket, msg) < 0) {
- process->swapin_delay_percent = -1LL;
- process->blkio_delay_percent = -1LL;
- process->cpu_delay_percent = -1LL;
+ process->swapin_delay_percent = NAN;
+ process->blkio_delay_percent = NAN;
+ process->cpu_delay_percent = NAN;
return;
}
@@ -665,18 +987,12 @@ static void setCommand(Process* process, const char* command, int len) {
process->commLen = len;
}
-static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
- char filename[MAX_NAME+1];
- xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
- int fd = open(filename, O_RDONLY);
- if (fd == -1)
+static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t procFd) {
+ char command[4096 + 1]; // max cmdline length on Linux
+ ssize_t amtRead = xReadfileat(procFd, "cmdline", command, sizeof(command));
+ if (amtRead < 0)
return false;
- char command[4096+1]; // max cmdline length on Linux
- int amtRead = xread(fd, command, sizeof(command) - 1);
- close(fd);
- int tokenEnd = 0;
- int lastChar = 0;
if (amtRead == 0) {
if (process->state == 'Z') {
process->basenameOffset = 0;
@@ -684,25 +1000,183 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
((LinuxProcess*)process)->isKernelThread = true;
}
return true;
- } else if (amtRead < 0) {
- return false;
}
+
+ int tokenEnd = 0;
+ int tokenStart = 0;
+ int lastChar = 0;
+ bool argSepNUL = false;
+ bool argSepSpace = false;
+
for (int i = 0; i < amtRead; i++) {
- if (command[i] == '\0' || command[i] == '\n') {
+ /* newline used as delimiter - when forming the mergedCommand, newline is
+ * converted to space by LinuxProcess_makeCommandStr */
+ if (command[i] == '\0') {
+ command[i] = '\n';
+ } else {
+ /* Record some information for the argument parsing heuristic below. */
+ if (tokenEnd)
+ argSepNUL = true;
+ if (command[i] <= ' ')
+ argSepSpace = true;
+ }
+
+ if (command[i] == '\n') {
if (tokenEnd == 0) {
tokenEnd = i;
}
- command[i] = ' ';
} else {
+ /* htop considers the next character after the last / that is before
+ * basenameOffset, as the start of the basename in cmdline - see
+ * Process_writeCommand */
+ if (!tokenEnd && command[i] == '/') {
+ tokenStart = i + 1;
+ }
lastChar = i;
}
}
+
+ command[lastChar + 1] = '\0';
+
+ if (!argSepNUL && argSepSpace) {
+ /* Argument parsing heuristic.
+ *
+ * This heuristic is used for processes that rewrite their command line.
+ * Normally the command line is split by using NUL bytes between each argument.
+ * But some programs like chrome flatten this using spaces.
+ *
+ * This heuristic tries its best to undo this loss of information.
+ * To achieve this, we treat every character <= 32 as argument separators
+ * (i.e. all of ASCII control sequences and space).
+ * We then search for the basename of the cmdline in the first argument we found that way.
+ * As path names may contain we try to cross-validate if the path we got that way exists.
+ */
+
+ tokenStart = tokenEnd = 0;
+
+ // From initial scan we know there's at least one space.
+ // Check if that's part of a filename for an existing file.
+ if (Compat_faccessat(AT_FDCWD, command, F_OK, AT_SYMLINK_NOFOLLOW) != 0) {
+ // If we reach here the path does not exist.
+ // Thus begin searching for the part of it that actually is.
+
+ int tokenArg0Start = 0;
+
+ for (int i = 0; i <= lastChar; i++) {
+ /* Any ASCII control or space used as delimiter */
+ char tmpCommandChar = command[i];
+
+ if (command[i] <= ' ') {
+ if (!tokenEnd) {
+ command[i] = '\0';
+
+ bool found = Compat_faccessat(AT_FDCWD, command, F_OK, AT_SYMLINK_NOFOLLOW) == 0;
+
+ // Restore if this wasn't it
+ command[i] = found ? '\n' : tmpCommandChar;
+
+ if (found)
+ tokenEnd = i;
+ if (!tokenArg0Start)
+ tokenArg0Start = tokenStart;
+ } else {
+ // Split on every further separator, regardless of path correctness
+ command[i] = '\n';
+ }
+ } else if (!tokenEnd) {
+ if (command[i] == '/' || (command[i] == '\\' && (!tokenStart || command[tokenStart - 1] == '\\'))) {
+ tokenStart = i + 1;
+ } else if (command[i] == ':' && (command[i + 1] != '/' && command[i + 1] != '\\')) {
+ tokenEnd = i;
+ }
+ }
+ }
+
+ if (!tokenEnd) {
+ tokenStart = tokenArg0Start;
+
+ // No token delimiter found, forcibly split
+ for (int i = 0; i <= lastChar; i++) {
+ if (command[i] <= ' ') {
+ command[i] = '\n';
+ if (!tokenEnd) {
+ tokenEnd = i;
+ }
+ }
+ }
+ }
+ }
+ }
+
if (tokenEnd == 0) {
- tokenEnd = amtRead;
+ tokenEnd = lastChar + 1;
+ }
+
+ LinuxProcess *lp = (LinuxProcess *)process;
+ lp->mergedCommand.maxLen = lastChar + 1; /* accommodate cmdline */
+ if (!process->comm || !String_eq(command, process->comm)) {
+ process->basenameOffset = tokenEnd;
+ setCommand(process, command, lastChar + 1);
+ lp->procCmdlineBasenameOffset = tokenStart;
+ lp->procCmdlineBasenameEnd = tokenEnd;
+ lp->mergedCommand.cmdlineChanged = true;
+ }
+
+ /* /proc/[pid]/comm could change, so should be updated */
+ if ((amtRead = xReadfileat(procFd, "comm", command, sizeof(command))) > 0) {
+ command[amtRead - 1] = '\0';
+ lp->mergedCommand.maxLen += amtRead - 1; /* accommodate comm */
+ if (!lp->procComm || !String_eq(command, lp->procComm)) {
+ free(lp->procComm);
+ lp->procComm = xStrdup(command);
+ lp->mergedCommand.commChanged = true;
+ }
+ } else if (lp->procComm) {
+ free(lp->procComm);
+ lp->procComm = NULL;
+ lp->mergedCommand.commChanged = true;
+ }
+
+ char filename[MAX_NAME + 1];
+
+ /* execve could change /proc/[pid]/exe, so procExe should be updated */
+#if defined(HAVE_READLINKAT) && defined(HAVE_OPENAT)
+ amtRead = readlinkat(procFd, "exe", filename, sizeof(filename) - 1);
+#else
+ char path[4096];
+ xSnprintf(path, sizeof(path), "%s/exe", procFd);
+ amtRead = readlink(path, filename, sizeof(filename) - 1);
+#endif
+ if (amtRead > 0) {
+ filename[amtRead] = 0;
+ lp->mergedCommand.maxLen += amtRead; /* accommodate exe */
+ if (!lp->procExe || !String_eq(filename, lp->procExe)) {
+ free(lp->procExe);
+ lp->procExe = xStrdup(filename);
+ lp->procExeLen = amtRead;
+ /* exe is guaranteed to contain at least one /, but validate anyway */
+ while (amtRead && filename[--amtRead] != '/')
+ ;
+ lp->procExeBasenameOffset = amtRead + 1;
+ lp->mergedCommand.exeChanged = true;
+
+ const char* deletedMarker = " (deleted)";
+ if (strlen(lp->procExe) > strlen(deletedMarker)) {
+ lp->procExeDeleted = String_eq(lp->procExe + strlen(lp->procExe) - strlen(deletedMarker), deletedMarker);
+
+ if (lp->procExeDeleted && strlen(lp->procExe) - strlen(deletedMarker) == 1 && lp->procExe[0] == '/') {
+ lp->procExeBasenameOffset = 0;
+ }
+ }
+ }
+ } else if (lp->procExe) {
+ free(lp->procExe);
+ lp->procExe = NULL;
+ lp->procExeLen = 0;
+ lp->procExeBasenameOffset = 0;
+ lp->procExeDeleted = false;
+ lp->mergedCommand.exeChanged = true;
}
- command[lastChar + 1] = '\0';
- process->basenameOffset = tokenEnd;
- setCommand(process, command, lastChar + 1);
return true;
}
@@ -729,47 +1203,71 @@ static char* LinuxProcessList_updateTtyDevice(TtyDriver* ttyDrivers, unsigned in
unsigned int idx = min - ttyDrivers[i].minorFrom;
struct stat sstat;
char* fullPath;
- for(;;) {
+ for (;;) {
xAsprintf(&fullPath, "%s/%d", ttyDrivers[i].path, idx);
int err = stat(fullPath, &sstat);
- if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
+ if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) {
+ return fullPath;
+ }
free(fullPath);
+
xAsprintf(&fullPath, "%s%d", ttyDrivers[i].path, idx);
err = stat(fullPath, &sstat);
- if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) return fullPath;
+ if (err == 0 && major(sstat.st_rdev) == maj && minor(sstat.st_rdev) == min) {
+ return fullPath;
+ }
free(fullPath);
- if (idx == min) break;
+
+ if (idx == min) {
+ break;
+ }
+
idx = min;
}
int err = stat(ttyDrivers[i].path, &sstat);
- if (err == 0 && tty_nr == sstat.st_rdev) return strdup(ttyDrivers[i].path);
+ if (err == 0 && tty_nr == sstat.st_rdev) {
+ return xStrdup(ttyDrivers[i].path);
+ }
}
char* out;
xAsprintf(&out, "/dev/%u:%u", maj, min);
return out;
}
-static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char* dirname, Process* parent, double period, struct timeval tv) {
+static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_t parentFd, const char* dirname, const Process* parent, double period, unsigned long long now) {
ProcessList* pl = (ProcessList*) this;
- DIR* dir;
- struct dirent* entry;
- Settings* settings = pl->settings;
+ const struct dirent* entry;
+ const Settings* settings = pl->settings;
- #ifdef HAVE_TASKSTATS
- unsigned long long now = tv.tv_sec*1000LL+tv.tv_usec/1000LL;
- #endif
+#ifdef HAVE_OPENAT
+ int dirFd = openat(parentFd, dirname, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
+ if (dirFd < 0)
+ return false;
+ DIR* dir = fdopendir(dirFd);
+#else
+ char dirFd[4096];
+ xSnprintf(dirFd, sizeof(dirFd), "%s/%s", parentFd, dirname);
+ DIR* dir = opendir(dirFd);
+#endif
+ if (!dir) {
+ Compat_openatArgClose(dirFd);
+ return false;
+ }
- dir = opendir(dirname);
- if (!dir) return false;
int cpus = pl->cpuCount;
bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads;
while ((entry = readdir(dir)) != NULL) {
- char* name = entry->d_name;
+ const char* name = entry->d_name;
+
+ // Ignore all non-directories
+ if (entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN) {
+ continue;
+ }
// The RedHat kernel hides threads with a dot.
// I believe this is non-standard.
- if ((!settings->hideThreads) && name[0] == '.') {
+ if (name[0] == '.') {
name++;
}
@@ -781,107 +1279,160 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
// filename is a number: process directory
int pid = atoi(name);
- if (parent && pid == parent->pid)
+ if (pid <= 0)
continue;
- if (pid <= 0)
+ if (parent && pid == parent->pid)
continue;
- bool preExisting = false;
- Process* proc = ProcessList_getProcess(pl, pid, &preExisting, (Process_New) LinuxProcess_new);
+ bool preExisting;
+ Process* proc = ProcessList_getProcess(pl, pid, &preExisting, LinuxProcess_new);
+ LinuxProcess* lp = (LinuxProcess*) proc;
+
proc->tgid = parent ? parent->pid : pid;
- LinuxProcess* lp = (LinuxProcess*) proc;
+#ifdef HAVE_OPENAT
+ int procFd = openat(dirFd, entry->d_name, O_PATH | O_DIRECTORY | O_NOFOLLOW);
+ if (procFd < 0)
+ goto errorReadingProcess;
+#else
+ char procFd[4096];
+ xSnprintf(procFd, sizeof(procFd), "%s/%s", dirFd, entry->d_name);
+#endif
- char subdirname[MAX_NAME+1];
- xSnprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
- LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
+ LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period, now);
+
+ /*
+ * These conditions will not trigger on first occurrence, cause we need to
+ * add the process to the ProcessList and do all one time scans
+ * (e.g. parsing the cmdline to detect a kernel thread)
+ * But it will short-circuit subsequent scans.
+ */
+ if (preExisting && hideKernelThreads && Process_isKernelThread(proc)) {
+ proc->updated = true;
+ proc->show = false;
+ pl->kernelThreads++;
+ pl->totalTasks++;
+ Compat_openatArgClose(procFd);
+ continue;
+ }
+ if (preExisting && hideUserlandThreads && Process_isUserlandThread(proc)) {
+ proc->updated = true;
+ proc->show = false;
+ pl->userlandThreads++;
+ pl->totalTasks++;
+ Compat_openatArgClose(procFd);
+ continue;
+ }
- #ifdef HAVE_TASKSTATS
if (settings->flags & PROCESS_FLAG_IO)
- LinuxProcessList_readIoFile(lp, dirname, name, now);
- #endif
+ LinuxProcessList_readIoFile(lp, procFd, now);
- if (! LinuxProcessList_readStatmFile(lp, dirname, name))
+ if (!LinuxProcessList_readStatmFile(lp, procFd, !!(settings->flags & PROCESS_FLAG_LINUX_LRS_FIX), now))
goto errorReadingProcess;
- if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)){
- if (!parent){
+ if ((settings->flags & PROCESS_FLAG_LINUX_SMAPS) && !Process_isKernelThread(proc)) {
+ if (!parent) {
// Read smaps file of each process only every second pass to improve performance
static int smaps_flag = 0;
- if ((pid & 1) == smaps_flag){
- LinuxProcessList_readSmapsFile(lp, dirname, name, this->haveSmapsRollup);
+ if ((pid & 1) == smaps_flag) {
+ LinuxProcessList_readSmapsFile(lp, procFd, this->haveSmapsRollup);
}
if (pid == 1) {
smaps_flag = !smaps_flag;
}
- } else {
- lp->m_pss = ((LinuxProcess*)parent)->m_pss;
- }
+ } else {
+ lp->m_pss = ((const LinuxProcess*)parent)->m_pss;
+ }
}
- proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
-
- char command[MAX_NAME+1];
+ char command[MAX_NAME + 1];
unsigned long long int lasttimes = (lp->utime + lp->stime);
- int commLen = 0;
+ int commLen = sizeof(command);
unsigned int tty_nr = proc->tty_nr;
- if (! LinuxProcessList_readStatFile(proc, dirname, name, command, &commLen))
+ if (! LinuxProcessList_readStatFile(proc, procFd, command, &commLen))
goto errorReadingProcess;
+
if (tty_nr != proc->tty_nr && this->ttyDrivers) {
free(lp->ttyDevice);
lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
}
- if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
+
+ if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO) {
LinuxProcess_updateIOPriority(lp);
- float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
- proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0);
- if (isnan(proc->percent_cpu)) proc->percent_cpu = 0.0;
- proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(pl->totalMem) * 100.0;
+ }
+
+ /* period might be 0 after system sleep */
+ float percent_cpu = (period < 1e-6) ? 0.0f : ((lp->utime + lp->stime - lasttimes) / period * 100.0);
+ proc->percent_cpu = CLAMP(percent_cpu, 0.0f, cpus * 100.0f);
+ proc->percent_mem = (proc->m_resident * CRT_pageSizeKB) / (double)(pl->totalMem) * 100.0;
- if(!preExisting) {
+ if (!preExisting) {
- if (! LinuxProcessList_statProcessDir(proc, dirname, name))
+ if (! LinuxProcessList_statProcessDir(proc, procFd))
goto errorReadingProcess;
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
#ifdef HAVE_OPENVZ
if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
- LinuxProcessList_readOpenVZData(lp, dirname, name);
+ LinuxProcessList_readOpenVZData(lp, procFd);
}
#endif
#ifdef HAVE_VSERVER
if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) {
- LinuxProcessList_readVServerData(lp, dirname, name);
+ LinuxProcessList_readVServerData(lp, procFd);
}
#endif
- if (! LinuxProcessList_readCmdlineFile(proc, dirname, name)) {
+ if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
goto errorReadingProcess;
}
+ Process_fillStarttimeBuffer(proc);
+
ProcessList_add(pl, proc);
} else {
if (settings->updateProcessNames && proc->state != 'Z') {
- if (! LinuxProcessList_readCmdlineFile(proc, dirname, name)) {
+ if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
goto errorReadingProcess;
}
}
}
+ /* (Re)Generate the Command string, but only if the process is:
+ * - not a kernel thread, and
+ * - not a zombie or it became zombie under htop's watch, and
+ * - not a user thread or if showThreadNames is not set */
+ if (!Process_isKernelThread(proc) &&
+ (proc->state != 'Z' || lp->mergedCommand.str) &&
+ (!Process_isUserlandThread(proc) || !settings->showThreadNames)) {
+ LinuxProcess_makeCommandStr(proc);
+ }
#ifdef HAVE_DELAYACCT
LinuxProcessList_readDelayAcctData(this, lp);
#endif
- #ifdef HAVE_CGROUP
- if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
- LinuxProcessList_readCGroupFile(lp, dirname, name);
- #endif
+ if (settings->flags & PROCESS_FLAG_LINUX_CGROUP) {
+ LinuxProcessList_readCGroupFile(lp, procFd);
+ }
+
+ if (settings->flags & PROCESS_FLAG_LINUX_OOM) {
+ LinuxProcessList_readOomData(lp, procFd);
+ }
- if (settings->flags & PROCESS_FLAG_LINUX_OOM)
- LinuxProcessList_readOomData(lp, dirname, name);
+ if (settings->flags & PROCESS_FLAG_LINUX_CTXT) {
+ LinuxProcessList_readCtxtData(lp, procFd);
+ }
+
+ if (settings->flags & PROCESS_FLAG_LINUX_SECATTR) {
+ LinuxProcessList_readSecattrData(lp, procFd);
+ }
+
+ if (settings->flags & PROCESS_FLAG_LINUX_CWD) {
+ LinuxProcessList_readCwd(lp, procFd);
+ }
if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1;
@@ -891,8 +1442,9 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
proc->basenameOffset = -1;
setCommand(proc, command, commLen);
} else if (settings->showThreadNames) {
- if (! LinuxProcessList_readCmdlineFile(proc, dirname, name))
+ if (! LinuxProcessList_readCmdlineFile(proc, procFd)) {
goto errorReadingProcess;
+ }
}
if (Process_isKernelThread(proc)) {
pl->kernelThreads++;
@@ -901,14 +1453,25 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
}
}
+ /* Set at the end when we know if a new entry is a thread */
+ proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
+
pl->totalTasks++;
if (proc->state == 'R')
pl->runningTasks++;
proc->updated = true;
+ Compat_openatArgClose(procFd);
continue;
// Exception handler.
- errorReadingProcess: {
+
+errorReadingProcess:
+ {
+#ifdef HAVE_OPENAT
+ if (procFd >= 0)
+ close(procFd);
+#endif
+
if (preExisting) {
ProcessList_remove(pl, proc);
} else {
@@ -921,6 +1484,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
}
static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
+ unsigned long long int freeMem = 0;
unsigned long long int swapFree = 0;
unsigned long long int shmem = 0;
unsigned long long int sreclaimable = 0;
@@ -932,12 +1496,16 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
char buffer[128];
while (fgets(buffer, 128, file)) {
- #define tryRead(label, variable) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %32llu kB", variable)) { break; } } while(0)
+ #define tryRead(label, variable) \
+ if (String_startsWith(buffer, label)) { \
+ sscanf(buffer + strlen(label), " %32llu kB", variable); \
+ break; \
+ }
+
switch (buffer[0]) {
case 'M':
tryRead("MemTotal:", &this->totalMem);
- tryRead("MemFree:", &this->freeMem);
- tryRead("MemShared:", &this->sharedMem);
+ tryRead("MemFree:", &freeMem);
break;
case 'B':
tryRead("Buffers:", &this->buffersMem);
@@ -963,12 +1531,60 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
#undef tryRead
}
- this->usedMem = this->totalMem - this->freeMem;
+ this->usedMem = this->totalMem - freeMem;
this->cachedMem = this->cachedMem + sreclaimable - shmem;
this->usedSwap = this->totalSwap - swapFree;
fclose(file);
}
+static inline void LinuxProcessList_scanZramInfo(LinuxProcessList* this) {
+ unsigned long long int totalZram = 0;
+ unsigned long long int usedZramComp = 0;
+ unsigned long long int usedZramOrig = 0;
+
+ char mm_stat[34];
+ char disksize[34];
+
+ unsigned int i = 0;
+ for (;;) {
+ xSnprintf(mm_stat, sizeof(mm_stat), "/sys/block/zram%u/mm_stat", i);
+ xSnprintf(disksize, sizeof(disksize), "/sys/block/zram%u/disksize", i);
+ i++;
+ FILE* disksize_file = fopen(disksize, "r");
+ FILE* mm_stat_file = fopen(mm_stat, "r");
+ if (disksize_file == NULL || mm_stat_file == NULL) {
+ if (disksize_file) {
+ fclose(disksize_file);
+ }
+ if (mm_stat_file) {
+ fclose(mm_stat_file);
+ }
+ break;
+ }
+ unsigned long long int size = 0;
+ unsigned long long int orig_data_size = 0;
+ unsigned long long int compr_data_size = 0;
+
+ if (!fscanf(disksize_file, "%llu\n", &size) ||
+ !fscanf(mm_stat_file, " %llu %llu", &orig_data_size, &compr_data_size)) {
+ fclose(disksize_file);
+ fclose(mm_stat_file);
+ break;
+ }
+
+ totalZram += size;
+ usedZramComp += compr_data_size;
+ usedZramOrig += orig_data_size;
+
+ fclose(disksize_file);
+ fclose(mm_stat_file);
+ }
+
+ this->zram.totalZram = totalZram / 1024;
+ this->zram.usedZramComp = usedZramComp / 1024;
+ this->zram.usedZramOrig = usedZramOrig / 1024;
+}
+
static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
unsigned long long int dbufSize = 0;
unsigned long long int dnodeSize = 0;
@@ -981,8 +1597,17 @@ static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
}
char buffer[128];
while (fgets(buffer, 128, file)) {
- #define tryRead(label, variable) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %*2u %32llu", variable)) { break; } } while(0)
- #define tryReadFlag(label, variable, flag) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %*2u %32llu", variable)) { flag = 1; break; } else { flag = 0; } } while(0)
+ #define tryRead(label, variable) \
+ if (String_startsWith(buffer, label)) { \
+ sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
+ break; \
+ }
+ #define tryReadFlag(label, variable, flag) \
+ if (String_startsWith(buffer, label)) { \
+ (flag) = sscanf(buffer + strlen(label), " %*2u %32llu", variable); \
+ break; \
+ }
+
switch (buffer[0]) {
case 'c':
tryRead("c_max", &lpl->zfs.max);
@@ -1048,10 +1673,13 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
// 5, 7, 8 or 9 of these fields will be set.
// The rest will remain at zero.
char* ok = fgets(buffer, PROC_LINE_LENGTH, file);
- if (!ok) buffer[0] = '\0';
- if (i == 0)
+ if (!ok) {
+ buffer[0] = '\0';
+ }
+
+ if (i == 0) {
(void) sscanf(buffer, "cpu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
- else {
+ } else {
int cpuid;
(void) sscanf(buffer, "cpu%4d %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice);
assert(cpuid == i - 1);
@@ -1069,7 +1697,7 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
// Since we do a subtraction (usertime - guest) and cputime64_to_clock_t()
// used in /proc/stat rounds down numbers, it can lead to a case where the
// integer overflow.
- #define WRAP_SUBTRACT(a,b) (a > b) ? a - b : 0
+ #define WRAP_SUBTRACT(a,b) (((a) > (b)) ? (a) - (b) : 0)
cpuData->userPeriod = WRAP_SUBTRACT(usertime, cpuData->userTime);
cpuData->nicePeriod = WRAP_SUBTRACT(nicetime, cpuData->niceTime);
cpuData->systemPeriod = WRAP_SUBTRACT(systemtime, cpuData->systemTime);
@@ -1095,88 +1723,178 @@ static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
cpuData->stealTime = steal;
cpuData->guestTime = virtalltime;
cpuData->totalTime = totaltime;
-
}
+
double period = (double)this->cpus[0].totalPeriod / cpus;
fclose(file);
return period;
}
-static inline double LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
- ProcessList* pl = (ProcessList*) this;
- Settings* settings = pl->settings;
-
+static int scanCPUFreqencyFromSysCPUFreq(LinuxProcessList* this) {
int cpus = this->super.cpuCount;
- assert(cpus > 0);
+ int numCPUsWithFrequency = 0;
+ unsigned long totalFrequency = 0;
+
+ for (int i = 0; i < cpus; ++i) {
+ char pathBuffer[64];
+ xSnprintf(pathBuffer, sizeof(pathBuffer), "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", i);
+
+ FILE* file = fopen(pathBuffer, "r");
+ if (!file)
+ return -errno;
+
+ unsigned long frequency;
+ if (fscanf(file, "%lu", &frequency) == 1) {
+ /* convert kHz to MHz */
+ frequency = frequency / 1000;
+ this->cpus[i + 1].frequency = frequency;
+ numCPUsWithFrequency++;
+ totalFrequency += frequency;
+ }
- for (int i = 0; i <= cpus; i++) {
- CPUData* cpuData = &(this->cpus[i]);
- cpuData->frequency = -1;
+ fclose(file);
}
+ if (numCPUsWithFrequency > 0)
+ this->cpus[0].frequency = (double)totalFrequency / numCPUsWithFrequency;
+
+ return 0;
+}
+
+static void scanCPUFreqencyFromCPUinfo(LinuxProcessList* this) {
+ FILE* file = fopen(PROCCPUINFOFILE, "r");
+ if (file == NULL)
+ return;
+
+ int cpus = this->super.cpuCount;
int numCPUsWithFrequency = 0;
double totalFrequency = 0;
+ int cpuid = -1;
- if (settings->showCPUFrequency) {
- FILE* file = fopen(PROCCPUINFOFILE, "r");
- if (file == NULL) {
- CRT_fatalError("Cannot open " PROCCPUINFOFILE);
- }
-
- int cpuid = -1;
+ while (!feof(file)) {
double frequency;
- while (!feof(file)) {
- char buffer[PROC_LINE_LENGTH];
- char *ok = fgets(buffer, PROC_LINE_LENGTH, file);
- if (!ok) break;
-
- if (
- (sscanf(buffer, "processor : %d", &cpuid) == 1) ||
- (sscanf(buffer, "processor: %d", &cpuid) == 1)
- ) {
- if (cpuid < 0 || cpuid > (cpus - 1)) {
- char tmpbuffer[64];
- xSnprintf(tmpbuffer, sizeof(tmpbuffer), PROCCPUINFOFILE " contains out-of-range CPU number %d", cpuid);
- CRT_fatalError(tmpbuffer);
- }
- } else if (
- (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
- (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1)
- ) {
- if (cpuid < 0 || cpuid > (cpus - 1)) {
- CRT_fatalError(PROCCPUINFOFILE " is malformed: cpu MHz line without corresponding processor line");
- }
+ char buffer[PROC_LINE_LENGTH];
+
+ if (fgets(buffer, PROC_LINE_LENGTH, file) == NULL)
+ break;
+
+ if (
+ (sscanf(buffer, "processor : %d", &cpuid) == 1) ||
+ (sscanf(buffer, "processor: %d", &cpuid) == 1)
+ ) {
+ continue;
+ } else if (
+ (sscanf(buffer, "cpu MHz : %lf", &frequency) == 1) ||
+ (sscanf(buffer, "cpu MHz: %lf", &frequency) == 1)
+ ) {
+ if (cpuid < 0 || cpuid > (cpus - 1)) {
+ continue;
+ }
- int cpu = cpuid + 1;
- CPUData* cpuData = &(this->cpus[cpu]);
+ CPUData* cpuData = &(this->cpus[cpuid + 1]);
+ /* do not override sysfs data */
+ if (isnan(cpuData->frequency)) {
cpuData->frequency = frequency;
- numCPUsWithFrequency++;
- totalFrequency += frequency;
- } else if (buffer[0] == '\n') {
- cpuid = -1;
}
+ numCPUsWithFrequency++;
+ totalFrequency += frequency;
+ } else if (buffer[0] == '\n') {
+ cpuid = -1;
}
- fclose(file);
+ }
+ fclose(file);
+
+ if (numCPUsWithFrequency > 0) {
+ this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency;
+ }
+}
+
+static void LinuxProcessList_scanCPUFrequency(LinuxProcessList* this) {
+ int cpus = this->super.cpuCount;
+ assert(cpus > 0);
- if (numCPUsWithFrequency > 0) {
- this->cpus[0].frequency = totalFrequency / numCPUsWithFrequency;
+ for (int i = 0; i <= cpus; i++) {
+ this->cpus[i].frequency = NAN;
+ }
+
+ if (scanCPUFreqencyFromSysCPUFreq(this) == 0) {
+ return;
+ }
+
+ scanCPUFreqencyFromCPUinfo(this);
+}
+
+#ifdef HAVE_SENSORS_SENSORS_H
+static void LinuxProcessList_scanCPUTemperature(LinuxProcessList* this) {
+ const int cpuCount = this->super.cpuCount;
+
+ for (int i = 0; i <= cpuCount; i++) {
+ this->cpus[i].temperature = NAN;
+ }
+
+ int r = LibSensors_getCPUTemperatures(this->cpus, cpuCount);
+
+ /* No temperature - nothing to do */
+ if (r <= 0)
+ return;
+
+ /* Only package temperature - copy to all cpus */
+ if (r == 1 && !isnan(this->cpus[0].temperature)) {
+ double packageTemp = this->cpus[0].temperature;
+ for (int i = 1; i <= cpuCount; i++) {
+ this->cpus[i].temperature = packageTemp;
}
+
+ return;
}
- double period = (double)this->cpus[0].totalPeriod / cpus;
- return period;
+ /* Half the temperatures, probably HT/SMT - copy to second half */
+ if (r >= 2 && (r - 1) == (cpuCount / 2)) {
+ for (int i = cpuCount / 2 + 1; i <= cpuCount; i++) {
+ this->cpus[i].temperature = this->cpus[i/2].temperature;
+ }
+
+ return;
+ }
}
+#endif
-void ProcessList_goThroughEntries(ProcessList* super) {
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
LinuxProcessList* this = (LinuxProcessList*) super;
+ const Settings* settings = super->settings;
LinuxProcessList_scanMemoryInfo(super);
LinuxProcessList_scanZfsArcstats(this);
+ LinuxProcessList_updateCPUcount(this);
+ LinuxProcessList_scanZramInfo(this);
+
double period = LinuxProcessList_scanCPUTime(this);
- LinuxProcessList_scanCPUFrequency(this);
+ if (settings->showCPUFrequency) {
+ LinuxProcessList_scanCPUFrequency(this);
+ }
+
+ #ifdef HAVE_SENSORS_SENSORS_H
+ if (settings->showCPUTemperature)
+ LinuxProcessList_scanCPUTemperature(this);
+ #endif
+
+ // in pause mode only gather global data for meters (CPU/memory/...)
+ if (pauseProcessUpdate) {
+ return;
+ }
struct timeval tv;
gettimeofday(&tv, NULL);
- LinuxProcessList_recurseProcTree(this, PROCDIR, NULL, period, tv);
+ unsigned long long now = tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
+
+ /* PROCDIR is an absolute path */
+ assert(PROCDIR[0] == '/');
+#ifdef HAVE_OPENAT
+ openat_arg_t rootFd = AT_FDCWD;
+#else
+ openat_arg_t rootFd = "";
+#endif
+
+ LinuxProcessList_recurseProcTree(this, rootFd, PROCDIR, NULL, period, now);
}
diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h
index c484e75..09b84af 100644
--- a/linux/LinuxProcessList.h
+++ b/linux/LinuxProcessList.h
@@ -3,14 +3,21 @@
/*
htop - LinuxProcessList.h
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h"
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "Hashtable.h"
#include "ProcessList.h"
+#include "UsersTable.h"
+#include "ZramStats.h"
#include "zfs/ZfsArcStats.h"
-extern long long btime;
typedef struct CPUData_ {
unsigned long long int totalTime;
@@ -40,6 +47,10 @@ typedef struct CPUData_ {
unsigned long long int guestPeriod;
double frequency;
+
+ #ifdef HAVE_SENSORS_SENSORS_H
+ double temperature;
+ #endif
} CPUData;
typedef struct TtyDriver_ {
@@ -57,11 +68,12 @@ typedef struct LinuxProcessList_ {
bool haveSmapsRollup;
#ifdef HAVE_DELAYACCT
- struct nl_sock *netlink_socket;
+ struct nl_sock* netlink_socket;
int netlink_family;
#endif
ZfsArcStats zfs;
+ ZramStats zram;
} LinuxProcessList;
#ifndef PROCDIR
@@ -96,6 +108,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
void ProcessList_delete(ProcessList* pl);
-void ProcessList_goThroughEntries(ProcessList* super);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif
diff --git a/linux/Platform.c b/linux/Platform.c
index e82d8f8..590fc7a 100644
--- a/linux/Platform.c
+++ b/linux/Platform.c
@@ -1,41 +1,71 @@
/*
htop - linux/Platform.c
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include "config.h"
+
#include "Platform.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "BatteryMeter.h"
+#include "ClockMeter.h"
+#include "Compat.h"
+#include "CPUMeter.h"
+#include "DateMeter.h"
+#include "DateTimeMeter.h"
+#include "DiskIOMeter.h"
+#include "HostnameMeter.h"
#include "IOPriority.h"
#include "IOPriorityPanel.h"
#include "LinuxProcess.h"
#include "LinuxProcessList.h"
-#include "Battery.h"
-
+#include "LoadAverageMeter.h"
+#include "Macros.h"
+#include "MainPanel.h"
#include "Meter.h"
-#include "CPUMeter.h"
#include "MemoryMeter.h"
+#include "NetworkIOMeter.h"
+#include "Object.h"
+#include "Panel.h"
+#include "PressureStallMeter.h"
+#include "ProcessList.h"
+#include "ProvideCurses.h"
+#include "SELinuxMeter.h"
+#include "Settings.h"
#include "SwapMeter.h"
+#include "SystemdMeter.h"
#include "TasksMeter.h"
-#include "LoadAverageMeter.h"
#include "UptimeMeter.h"
-#include "PressureStallMeter.h"
-#include "ClockMeter.h"
-#include "HostnameMeter.h"
+#include "XUtils.h"
+#include "ZramMeter.h"
+#include "ZramStats.h"
+
#include "zfs/ZfsArcMeter.h"
+#include "zfs/ZfsArcStats.h"
#include "zfs/ZfsCompressedArcMeter.h"
-#include "LinuxProcess.h"
-
-#include <math.h>
-#include <assert.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
+#ifdef HAVE_SENSORS_SENSORS_H
+#include "LibSensors.h"
+#endif
-ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, (int)M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
-//static ProcessField defaultIoFields[] = { PID, IO_PRIORITY, USER, IO_READ_RATE, IO_WRITE_RATE, IO_RATE, COMM, 0 };
+ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, (int)M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
@@ -76,21 +106,46 @@ const SignalItem Platform_signals[] = {
{ .name = "31 SIGSYS", .number = 31 },
};
-const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
+const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
+
+static enum { BAT_PROC, BAT_SYS, BAT_ERR } Platform_Battery_method = BAT_PROC;
+static time_t Platform_Battery_cacheTime;
+static double Platform_Battery_cachePercent = NAN;
+static ACPresence Platform_Battery_cacheIsOnAC;
+
+void Platform_init(void) {
+ if (access(PROCDIR, R_OK) != 0) {
+ fprintf(stderr, "Error: could not read procfs (compiled to look in %s).\n", PROCDIR);
+ exit(1);
+ }
+
+#ifdef HAVE_SENSORS_SENSORS_H
+ LibSensors_init(NULL);
+#endif
+}
+
+void Platform_done(void) {
+#ifdef HAVE_SENSORS_SENSORS_H
+ LibSensors_cleanup();
+#endif
+}
static Htop_Reaction Platform_actionSetIOPriority(State* st) {
Panel* panel = st->panel;
LinuxProcess* p = (LinuxProcess*) Panel_getSelected(panel);
- if (!p) return HTOP_OK;
+ if (!p)
+ return HTOP_OK;
+
IOPriority ioprio1 = p->ioPriority;
Panel* ioprioPanel = IOPriorityPanel_new(ioprio1);
void* set = Action_pickFromVector(st, ioprioPanel, 21, true);
if (set) {
IOPriority ioprio2 = IOPriorityPanel_getIOPriority(ioprioPanel);
- bool ok = MainPanel_foreachProcess((MainPanel*)panel, (MainPanel_ForeachProcessFn) LinuxProcess_setIOPriority, (Arg){ .i = ioprio2 }, NULL);
- if (!ok)
+ bool ok = MainPanel_foreachProcess((MainPanel*)panel, LinuxProcess_setIOPriority, (Arg) { .i = ioprio2 }, NULL);
+ if (!ok) {
beep();
+ }
}
Panel_delete((Object*)ioprioPanel);
return HTOP_REFRESH | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
@@ -100,9 +155,11 @@ void Platform_setBindings(Htop_Action* keys) {
keys['i'] = Platform_actionSetIOPriority;
}
-MeterClass* Platform_meterTypes[] = {
+const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
+ &DateMeter_class,
+ &DateTimeMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
@@ -114,12 +171,15 @@ MeterClass* Platform_meterTypes[] = {
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
&AllCPUs4Meter_class,
+ &AllCPUs8Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&LeftCPUs4Meter_class,
&RightCPUs4Meter_class,
+ &LeftCPUs8Meter_class,
+ &RightCPUs8Meter_class,
&BlankMeter_class,
&PressureStallCPUSomeMeter_class,
&PressureStallIOSomeMeter_class,
@@ -128,6 +188,11 @@ MeterClass* Platform_meterTypes[] = {
&PressureStallMemoryFullMeter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
+ &ZramMeter_class,
+ &DiskIOMeter_class,
+ &NetworkIOMeter_class,
+ &SELinuxMeter_class,
+ &SystemdMeter_class,
NULL
};
@@ -137,15 +202,20 @@ int Platform_getUptime() {
if (fd) {
int n = fscanf(fd, "%64lf", &uptime);
fclose(fd);
- if (n <= 0) return 0;
+ if (n <= 0) {
+ return 0;
+ }
}
- return (int) floor(uptime);
+ return floor(uptime);
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
int activeProcs, totalProcs, lastProc;
- *one = 0; *five = 0; *fifteen = 0;
- FILE *fd = fopen(PROCDIR "/loadavg", "r");
+ *one = 0;
+ *five = 0;
+ *fifteen = 0;
+
+ FILE* fd = fopen(PROCDIR "/loadavg", "r");
if (fd) {
int total = fscanf(fd, "%32lf %32lf %32lf %32d/%32d %32d", one, five, fifteen,
&activeProcs, &totalProcs, &lastProc);
@@ -157,7 +227,9 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
int Platform_getMaxPid() {
FILE* file = fopen(PROCDIR "/sys/kernel/pid_max", "r");
- if (!file) return -1;
+ if (!file)
+ return -1;
+
int maxPid = 4194303;
int match = fscanf(file, "%32d", &maxPid);
(void) match;
@@ -166,8 +238,8 @@ int Platform_getMaxPid() {
}
double Platform_setCPUValues(Meter* this, int cpu) {
- LinuxProcessList* pl = (LinuxProcessList*) this->pl;
- CPUData* cpuData = &(pl->cpus[cpu]);
+ const LinuxProcessList* pl = (const LinuxProcessList*) this->pl;
+ const CPUData* cpuData = &(pl->cpus[cpu]);
double total = (double) ( cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod);
double percent;
double* v = this->values;
@@ -180,28 +252,38 @@ double Platform_setCPUValues(Meter* this, int cpu) {
v[CPU_METER_STEAL] = cpuData->stealPeriod / total * 100.0;
v[CPU_METER_GUEST] = cpuData->guestPeriod / total * 100.0;
v[CPU_METER_IOWAIT] = cpuData->ioWaitPeriod / total * 100.0;
- Meter_setItems(this, 8);
+ this->curItems = 8;
if (this->pl->settings->accountGuestInCPUMeter) {
- percent = v[0]+v[1]+v[2]+v[3]+v[4]+v[5]+v[6];
+ percent = v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6];
} else {
- percent = v[0]+v[1]+v[2]+v[3]+v[4];
+ percent = v[0] + v[1] + v[2] + v[3] + v[4];
}
} else {
v[2] = cpuData->systemAllPeriod / total * 100.0;
v[3] = (cpuData->stealPeriod + cpuData->guestPeriod) / total * 100.0;
- Meter_setItems(this, 4);
- percent = v[0]+v[1]+v[2]+v[3];
+ this->curItems = 4;
+ percent = v[0] + v[1] + v[2] + v[3];
}
percent = CLAMP(percent, 0.0, 100.0);
- if (isnan(percent)) percent = 0.0;
+ if (isnan(percent)) {
+ percent = 0.0;
+ }
v[CPU_METER_FREQUENCY] = cpuData->frequency;
+#ifdef HAVE_SENSORS_SENSORS_H
+ v[CPU_METER_TEMPERATURE] = cpuData->temperature;
+#else
+ v[CPU_METER_TEMPERATURE] = NAN;
+#endif
+
return percent;
}
void Platform_setMemoryValues(Meter* this) {
- ProcessList* pl = (ProcessList*) this->pl;
+ const ProcessList* pl = this->pl;
+ const LinuxProcessList* lpl = (const LinuxProcessList*) pl;
+
long int usedMem = pl->usedMem;
long int buffersMem = pl->buffersMem;
long int cachedMem = pl->cachedMem;
@@ -210,55 +292,193 @@ void Platform_setMemoryValues(Meter* this) {
this->values[0] = usedMem;
this->values[1] = buffersMem;
this->values[2] = cachedMem;
+
+ if (lpl->zfs.enabled != 0) {
+ this->values[0] -= lpl->zfs.size;
+ this->values[2] += lpl->zfs.size;
+ }
}
void Platform_setSwapValues(Meter* this) {
- ProcessList* pl = (ProcessList*) this->pl;
+ const ProcessList* pl = this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
}
+void Platform_setZramValues(Meter* this) {
+ const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
+ this->total = lpl->zram.totalZram;
+ this->values[0] = lpl->zram.usedZramComp;
+ this->values[1] = lpl->zram.usedZramOrig;
+}
+
void Platform_setZfsArcValues(Meter* this) {
- LinuxProcessList* lpl = (LinuxProcessList*) this->pl;
+ const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(lpl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
- LinuxProcessList* lpl = (LinuxProcessList*) this->pl;
+ const LinuxProcessList* lpl = (const LinuxProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(lpl->zfs));
}
+
char* Platform_getProcessEnv(pid_t pid) {
- char procname[32+1];
- xSnprintf(procname, 32, "/proc/%d/environ", pid);
+ char procname[128];
+ xSnprintf(procname, sizeof(procname), PROCDIR "/%d/environ", pid);
FILE* fd = fopen(procname, "r");
- char *env = NULL;
- if (fd) {
- size_t capacity = 4096, size = 0, bytes;
- env = xMalloc(capacity);
- while ((bytes = fread(env+size, 1, capacity-size, fd)) > 0) {
- size += bytes;
- capacity *= 2;
- env = xRealloc(env, capacity);
- }
- fclose(fd);
- if (size < 2 || env[size-1] || env[size-2]) {
- if (size + 2 < capacity) {
- env = xRealloc(env, capacity+2);
- }
- env[size] = 0;
- env[size+1] = 0;
- }
+ if (!fd)
+ return NULL;
+
+ char* env = NULL;
+
+ size_t capacity = 0;
+ size_t size = 0;
+ ssize_t bytes = 0;
+
+ do {
+ size += bytes;
+ capacity += 4096;
+ env = xRealloc(env, capacity);
+ } while ((bytes = fread(env + size, 1, capacity - size, fd)) > 0);
+
+ fclose(fd);
+
+ if (bytes < 0) {
+ free(env);
+ return NULL;
}
+
+ size += bytes;
+
+ env = xRealloc(env, size + 2);
+
+ env[size] = '\0';
+ env[size + 1] = '\0';
+
return env;
}
-void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred) {
+/*
+ * Return the absolute path of a file given its pid&inode number
+ *
+ * Based on implementation of lslocks from util-linux:
+ * https://sources.debian.org/src/util-linux/2.36-3/misc-utils/lslocks.c/#L162
+ */
+char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
+ struct stat sb;
+ struct dirent *de;
+ DIR *dirp;
+ ssize_t len;
+ int fd;
+
+ char path[PATH_MAX];
+ char sym[PATH_MAX];
+ char* ret = NULL;
+
+ memset(path, 0, sizeof(path));
+ memset(sym, 0, sizeof(sym));
+
+ xSnprintf(path, sizeof(path), "%s/%d/fd/", PROCDIR, pid);
+ if (strlen(path) >= (sizeof(path) - 2))
+ return NULL;
+
+ if (!(dirp = opendir(path)))
+ return NULL;
+
+ if ((fd = dirfd(dirp)) < 0 )
+ goto out;
+
+ while ((de = readdir(dirp))) {
+ if (String_eq(de->d_name, ".") || String_eq(de->d_name, ".."))
+ continue;
+
+ /* care only for numerical descriptors */
+ if (!strtoull(de->d_name, (char **) NULL, 10))
+ continue;
+
+ if (!Compat_fstatat(fd, path, de->d_name, &sb, 0) && inode != sb.st_ino)
+ continue;
+
+ if ((len = Compat_readlinkat(fd, path, de->d_name, sym, sizeof(sym) - 1)) < 1)
+ goto out;
+
+ sym[len] = '\0';
+
+ ret = xStrdup(sym);
+ break;
+ }
+
+out:
+ closedir(dirp);
+ return ret;
+}
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
+ FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData));
+
+ FILE* f = fopen(PROCDIR "/locks", "r");
+ if (!f) {
+ pdata->error = true;
+ return pdata;
+ }
+
+ char buffer[1024];
+ FileLocks_LockData** data_ref = &pdata->locks;
+ while(fgets(buffer, sizeof(buffer), f)) {
+ if (!strchr(buffer, '\n'))
+ continue;
+
+ int lock_id;
+ char lock_type[16];
+ char lock_excl[16];
+ char lock_rw[16];
+ pid_t lock_pid;
+ unsigned int lock_dev[2];
+ uint64_t lock_inode;
+ char lock_start[25];
+ char lock_end[25];
+
+ if (10 != sscanf(buffer, "%d: %15s %15s %15s %d %x:%x:%"PRIu64" %24s %24s",
+ &lock_id, lock_type, lock_excl, lock_rw, &lock_pid,
+ &lock_dev[0], &lock_dev[1], &lock_inode,
+ lock_start, lock_end))
+ continue;
+
+ if (pid != lock_pid)
+ continue;
+
+ FileLocks_LockData* ldata = xCalloc(1, sizeof(FileLocks_LockData));
+ FileLocks_Data* data = &ldata->data;
+ data->id = lock_id;
+ data->locktype = xStrdup(lock_type);
+ data->exclusive = xStrdup(lock_excl);
+ data->readwrite = xStrdup(lock_rw);
+ data->filename = Platform_getInodeFilename(lock_pid, lock_inode);
+ data->dev[0] = lock_dev[0];
+ data->dev[1] = lock_dev[1];
+ data->inode = lock_inode;
+ data->start = strtoull(lock_start, NULL, 10);
+ if (!String_eq(lock_end, "EOF")) {
+ data->end = strtoull(lock_end, NULL, 10);
+ } else {
+ data->end = ULLONG_MAX;
+ }
+
+ *data_ref = ldata;
+ data_ref = &ldata->next;
+ }
+
+ fclose(f);
+ return pdata;
+}
+
+void Platform_getPressureStall(const char* file, bool some, double* ten, double* sixty, double* threehundred) {
*ten = *sixty = *threehundred = 0;
- char procname[128+1];
- xSnprintf(procname, 128, PROCDIR "/pressure/%s", file);
- FILE *fd = fopen(procname, "r");
+ char procname[128];
+ xSnprintf(procname, sizeof(procname), PROCDIR "/pressure/%s", file);
+ FILE* fd = fopen(procname, "r");
if (!fd) {
*ten = *sixty = *threehundred = NAN;
return;
@@ -271,3 +491,366 @@ void Platform_getPressureStall(const char *file, bool some, double* ten, double*
assert(total == 3);
fclose(fd);
}
+
+bool Platform_getDiskIO(DiskIOData* data) {
+ FILE* fd = fopen(PROCDIR "/diskstats", "r");
+ if (!fd)
+ return false;
+
+ unsigned long int read_sum = 0, write_sum = 0, timeSpend_sum = 0;
+ char lineBuffer[256];
+ while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
+ char diskname[32];
+ unsigned long int read_tmp, write_tmp, timeSpend_tmp;
+ if (sscanf(lineBuffer, "%*d %*d %31s %*u %*u %lu %*u %*u %*u %lu %*u %*u %lu", diskname, &read_tmp, &write_tmp, &timeSpend_tmp) == 4) {
+ if (String_startsWith(diskname, "dm-"))
+ continue;
+
+ /* only count root disks, e.g. do not count IO from sda and sda1 twice */
+ if ((diskname[0] == 's' || diskname[0] == 'h')
+ && diskname[1] == 'd'
+ && isalpha((unsigned char)diskname[2])
+ && isdigit((unsigned char)diskname[3]))
+ continue;
+
+ /* only count root disks, e.g. do not count IO from mmcblk0 and mmcblk0p1 twice */
+ if (diskname[0] == 'm'
+ && diskname[1] == 'm'
+ && diskname[2] == 'c'
+ && diskname[3] == 'b'
+ && diskname[4] == 'l'
+ && diskname[5] == 'k'
+ && isdigit((unsigned char)diskname[6])
+ && diskname[7] == 'p')
+ continue;
+
+ read_sum += read_tmp;
+ write_sum += write_tmp;
+ timeSpend_sum += timeSpend_tmp;
+ }
+ }
+ fclose(fd);
+ /* multiply with sector size */
+ data->totalBytesRead = 512 * read_sum;
+ data->totalBytesWritten = 512 * write_sum;
+ data->totalMsTimeSpend = timeSpend_sum;
+ return true;
+}
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted) {
+ FILE* fd = fopen(PROCDIR "/net/dev", "r");
+ if (!fd)
+ return false;
+
+ unsigned long int bytesReceivedSum = 0, packetsReceivedSum = 0, bytesTransmittedSum = 0, packetsTransmittedSum = 0;
+ char lineBuffer[512];
+ while (fgets(lineBuffer, sizeof(lineBuffer), fd)) {
+ char interfaceName[32];
+ unsigned long int bytesReceivedParsed, packetsReceivedParsed, bytesTransmittedParsed, packetsTransmittedParsed;
+ if (sscanf(lineBuffer, "%31s %lu %lu %*u %*u %*u %*u %*u %*u %lu %lu",
+ interfaceName,
+ &bytesReceivedParsed,
+ &packetsReceivedParsed,
+ &bytesTransmittedParsed,
+ &packetsTransmittedParsed) != 5)
+ continue;
+
+ if (String_eq(interfaceName, "lo:"))
+ continue;
+
+ bytesReceivedSum += bytesReceivedParsed;
+ packetsReceivedSum += packetsReceivedParsed;
+ bytesTransmittedSum += bytesTransmittedParsed;
+ packetsTransmittedSum += packetsTransmittedParsed;
+ }
+
+ fclose(fd);
+
+ *bytesReceived = bytesReceivedSum;
+ *packetsReceived = packetsReceivedSum;
+ *bytesTransmitted = bytesTransmittedSum;
+ *packetsTransmitted = packetsTransmittedSum;
+ return true;
+}
+
+// Linux battery reading by Ian P. Hands (iphands@gmail.com, ihands@redhat.com).
+
+#define MAX_BATTERIES 64
+#define PROC_BATTERY_DIR PROCDIR "/acpi/battery"
+#define PROC_POWERSUPPLY_DIR PROCDIR "/acpi/ac_adapter"
+#define SYS_POWERSUPPLY_DIR "/sys/class/power_supply"
+
+// ----------------------------------------
+// READ FROM /proc
+// ----------------------------------------
+
+static unsigned long int parseBatInfo(const char* fileName, const unsigned short int lineNum, const unsigned short int wordNum) {
+ const char batteryPath[] = PROC_BATTERY_DIR;
+ DIR* batteryDir = opendir(batteryPath);
+ if (!batteryDir)
+ return 0;
+
+ char* batteries[MAX_BATTERIES];
+ unsigned int nBatteries = 0;
+ memset(batteries, 0, MAX_BATTERIES * sizeof(char*));
+
+ while (nBatteries < MAX_BATTERIES) {
+ struct dirent* dirEntry = readdir(batteryDir);
+ if (!dirEntry)
+ break;
+
+ char* entryName = dirEntry->d_name;
+ if (!String_startsWith(entryName, "BAT"))
+ continue;
+
+ batteries[nBatteries] = xStrdup(entryName);
+ nBatteries++;
+ }
+ closedir(batteryDir);
+
+ unsigned long int total = 0;
+ for (unsigned int i = 0; i < nBatteries; i++) {
+ char infoPath[30];
+ xSnprintf(infoPath, sizeof infoPath, "%s%s/%s", batteryPath, batteries[i], fileName);
+
+ FILE* file = fopen(infoPath, "r");
+ if (!file)
+ break;
+
+ char* line = NULL;
+ for (unsigned short int j = 0; j < lineNum; j++) {
+ free(line);
+ line = String_readLine(file);
+ if (!line)
+ break;
+ }
+
+ fclose(file);
+
+ if (!line)
+ break;
+
+ char* foundNumStr = String_getToken(line, wordNum);
+ const unsigned long int foundNum = atoi(foundNumStr);
+ free(foundNumStr);
+ free(line);
+
+ total += foundNum;
+ }
+
+ for (unsigned int i = 0; i < nBatteries; i++)
+ free(batteries[i]);
+
+ return total;
+}
+
+static ACPresence procAcpiCheck(void) {
+ ACPresence isOn = AC_ERROR;
+ const char* power_supplyPath = PROC_POWERSUPPLY_DIR;
+ DIR* dir = opendir(power_supplyPath);
+ if (!dir)
+ return AC_ERROR;
+
+ for (;;) {
+ struct dirent* dirEntry = readdir(dir);
+ if (!dirEntry)
+ break;
+
+ const char* entryName = dirEntry->d_name;
+
+ if (entryName[0] != 'A')
+ continue;
+
+ char statePath[256];
+ xSnprintf(statePath, sizeof(statePath), "%s/%s/state", power_supplyPath, entryName);
+ FILE* file = fopen(statePath, "r");
+ if (!file) {
+ isOn = AC_ERROR;
+ continue;
+ }
+ char* line = String_readLine(file);
+
+ fclose(file);
+
+ if (!line)
+ continue;
+
+ char* isOnline = String_getToken(line, 2);
+ free(line);
+
+ if (String_eq(isOnline, "on-line"))
+ isOn = AC_PRESENT;
+ else
+ isOn = AC_ABSENT;
+ free(isOnline);
+ if (isOn == AC_PRESENT)
+ break;
+ }
+
+ if (dir)
+ closedir(dir);
+
+ return isOn;
+}
+
+static double Platform_Battery_getProcBatInfo(void) {
+ const unsigned long int totalFull = parseBatInfo("info", 3, 4);
+ if (totalFull == 0)
+ return NAN;
+
+ const unsigned long int totalRemain = parseBatInfo("state", 5, 3);
+ if (totalRemain == 0)
+ return NAN;
+
+ return totalRemain * 100.0 / (double) totalFull;
+}
+
+static void Platform_Battery_getProcData(double* percent, ACPresence* isOnAC) {
+ *isOnAC = procAcpiCheck();
+ *percent = AC_ERROR != *isOnAC ? Platform_Battery_getProcBatInfo() : NAN;
+}
+
+// ----------------------------------------
+// READ FROM /sys
+// ----------------------------------------
+
+static void Platform_Battery_getSysData(double* percent, ACPresence* isOnAC) {
+
+ *percent = NAN;
+ *isOnAC = AC_ERROR;
+
+ DIR* dir = opendir(SYS_POWERSUPPLY_DIR);
+ if (!dir)
+ return;
+
+ unsigned long int totalFull = 0;
+ unsigned long int totalRemain = 0;
+
+ for (;;) {
+ struct dirent* dirEntry = readdir(dir);
+ if (!dirEntry)
+ break;
+
+ const char* entryName = dirEntry->d_name;
+ char filePath[256];
+
+ xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/type", entryName);
+
+ char type[8];
+ ssize_t r = xReadfile(filePath, type, sizeof(type));
+ if (r < 3)
+ continue;
+
+ if (type[0] == 'B' && type[1] == 'a' && type[2] == 't') {
+ xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/uevent", entryName);
+
+ char buffer[1024];
+ r = xReadfile(filePath, buffer, sizeof(buffer));
+ if (r < 0) {
+ closedir(dir);
+ return;
+ }
+
+ char* buf = buffer;
+ char* line = NULL;
+ bool full = false;
+ bool now = false;
+ int fullSize = 0;
+ double capacityLevel = NAN;
+
+ #define match(str,prefix) \
+ (String_startsWith(str,prefix) ? (str) + strlen(prefix) : NULL)
+
+ while ((line = strsep(&buf, "\n")) != NULL) {
+ const char* ps = match(line, "POWER_SUPPLY_");
+ if (!ps)
+ continue;
+ const char* capacity = match(ps, "CAPACITY=");
+ if (capacity)
+ capacityLevel = atoi(capacity) / 100.0;
+ const char* energy = match(ps, "ENERGY_");
+ if (!energy)
+ energy = match(ps, "CHARGE_");
+ if (!energy)
+ continue;
+ const char* value = (!full) ? match(energy, "FULL=") : NULL;
+ if (value) {
+ fullSize = atoi(value);
+ totalFull += fullSize;
+ full = true;
+ if (now)
+ break;
+ continue;
+ }
+ value = (!now) ? match(energy, "NOW=") : NULL;
+ if (value) {
+ totalRemain += atoi(value);
+ now = true;
+ if (full)
+ break;
+ continue;
+ }
+ }
+
+ #undef match
+
+ if (!now && full && !isnan(capacityLevel))
+ totalRemain += (capacityLevel * fullSize);
+
+ } else if (entryName[0] == 'A') {
+ if (*isOnAC != AC_ERROR)
+ continue;
+
+ xSnprintf(filePath, sizeof filePath, SYS_POWERSUPPLY_DIR "/%s/online", entryName);
+
+ char buffer[2];
+
+ r = xReadfile(filePath, buffer, sizeof(buffer));
+ if (r < 1) {
+ closedir(dir);
+ return;
+ }
+
+ if (buffer[0] == '0')
+ *isOnAC = AC_ABSENT;
+ else if (buffer[0] == '1')
+ *isOnAC = AC_PRESENT;
+ }
+ }
+ closedir(dir);
+
+ *percent = totalFull > 0 ? ((double) totalRemain * 100.0) / (double) totalFull : NAN;
+}
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC) {
+ time_t now = time(NULL);
+ // update battery reading is slow. Update it each 10 seconds only.
+ if (now < Platform_Battery_cacheTime + 10) {
+ *percent = Platform_Battery_cachePercent;
+ *isOnAC = Platform_Battery_cacheIsOnAC;
+ return;
+ }
+
+ if (Platform_Battery_method == BAT_PROC) {
+ Platform_Battery_getProcData(percent, isOnAC);
+ if (isnan(*percent))
+ Platform_Battery_method = BAT_SYS;
+ }
+ if (Platform_Battery_method == BAT_SYS) {
+ Platform_Battery_getSysData(percent, isOnAC);
+ if (isnan(*percent))
+ Platform_Battery_method = BAT_ERR;
+ }
+ if (Platform_Battery_method == BAT_ERR) {
+ *percent = NAN;
+ *isOnAC = AC_ERROR;
+ } else {
+ *percent = CLAMP(*percent, 0.0, 100.0);
+ }
+ Platform_Battery_cachePercent = *percent;
+ Platform_Battery_cacheIsOnAC = *isOnAC;
+ Platform_Battery_cacheTime = now;
+}
diff --git a/linux/Platform.h b/linux/Platform.h
index 9f0ee7f..280b997 100644
--- a/linux/Platform.h
+++ b/linux/Platform.h
@@ -3,14 +3,19 @@
/*
htop - linux/Platform.h
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+#include <sys/types.h>
+
#include "Action.h"
-#include "MainPanel.h"
#include "BatteryMeter.h"
-#include "LinuxProcess.h"
+#include "DiskIOMeter.h"
+#include "Meter.h"
+#include "Process.h"
+#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
extern ProcessField Platform_defaultFields[];
@@ -21,15 +26,19 @@ extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
-void Platform_setBindings(Htop_Action* keys);
+extern const MeterClass* const Platform_meterTypes[];
+
+void Platform_init(void);
-extern MeterClass* Platform_meterTypes[];
+void Platform_done(void);
-int Platform_getUptime();
+void Platform_setBindings(Htop_Action* keys);
+
+int Platform_getUptime(void);
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
-int Platform_getMaxPid();
+int Platform_getMaxPid(void);
double Platform_setCPUValues(Meter* this, int cpu);
@@ -37,11 +46,27 @@ void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
+void Platform_setZramValues(Meter* this);
+
void Platform_setZfsArcValues(Meter* this);
void Platform_setZfsCompressedArcValues(Meter* this);
+
char* Platform_getProcessEnv(pid_t pid);
+char* Platform_getInodeFilename(pid_t pid, ino_t inode);
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
+
void Platform_getPressureStall(const char *file, bool some, double* ten, double* sixty, double* threehundred);
+bool Platform_getDiskIO(DiskIOData* data);
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted);
+
+void Platform_getBattery(double *percent, ACPresence *isOnAC);
+
#endif
diff --git a/linux/PressureStallMeter.c b/linux/PressureStallMeter.c
index 56055bf..745068c 100644
--- a/linux/PressureStallMeter.c
+++ b/linux/PressureStallMeter.c
@@ -2,47 +2,52 @@
htop - PressureStallMeter.c
(C) 2004-2011 Hisham H. Muhammad
(C) 2019 Ran Benita
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "PressureStallMeter.h"
-#include "Platform.h"
-#include "CRT.h"
+#include <stdbool.h>
#include <string.h>
-/*{
+#include "CRT.h"
#include "Meter.h"
-}*/
+#include "Object.h"
+#include "Platform.h"
+#include "RichString.h"
+#include "XUtils.h"
+
-static int PressureStallMeter_attributes[] = {
- PRESSURE_STALL_TEN, PRESSURE_STALL_SIXTY, PRESSURE_STALL_THREEHUNDRED
+static const int PressureStallMeter_attributes[] = {
+ PRESSURE_STALL_TEN,
+ PRESSURE_STALL_SIXTY,
+ PRESSURE_STALL_THREEHUNDRED
};
-static void PressureStallMeter_updateValues(Meter* this, char* buffer, int len) {
- const char *file;
- if (strstr(Meter_name(this), "CPU")) {
- file = "cpu";
- } else if (strstr(Meter_name(this), "IO")) {
- file = "io";
- } else {
- file = "memory";
- }
+static void PressureStallMeter_updateValues(Meter* this, char* buffer, size_t len) {
+ const char* file;
+ if (strstr(Meter_name(this), "CPU")) {
+ file = "cpu";
+ } else if (strstr(Meter_name(this), "IO")) {
+ file = "io";
+ } else {
+ file = "memory";
+ }
- bool some;
- if (strstr(Meter_name(this), "Some")) {
- some = true;
- } else {
- some = false;
- }
+ bool some;
+ if (strstr(Meter_name(this), "Some")) {
+ some = true;
+ } else {
+ some = false;
+ }
- Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]);
- xSnprintf(buffer, len, "xxxx %.2lf%% %.2lf%% %.2lf%%", this->values[0], this->values[1], this->values[2]);
+ Platform_getPressureStall(file, some, &this->values[0], &this->values[1], &this->values[2]);
+ xSnprintf(buffer, len, "%s %s %.2lf%% %.2lf%% %.2lf%%", some ? "some" : "full", file, this->values[0], this->values[1], this->values[2]);
}
-static void PressureStallMeter_display(Object* cast, RichString* out) {
- Meter* this = (Meter*)cast;
+static void PressureStallMeter_display(const Object* cast, RichString* out) {
+ const Meter* this = (const Meter*)cast;
char buffer[20];
xSnprintf(buffer, sizeof(buffer), "%.2lf%% ", this->values[0]);
RichString_write(out, CRT_colors[PRESSURE_STALL_TEN], buffer);
@@ -52,7 +57,7 @@ static void PressureStallMeter_display(Object* cast, RichString* out) {
RichString_append(out, CRT_colors[PRESSURE_STALL_THREEHUNDRED], buffer);
}
-MeterClass PressureStallCPUSomeMeter_class = {
+const MeterClass PressureStallCPUSomeMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -64,11 +69,12 @@ MeterClass PressureStallCPUSomeMeter_class = {
.total = 100.0,
.attributes = PressureStallMeter_attributes,
.name = "PressureStallCPUSome",
- .uiName = "Pressure Stall Information, some CPU",
- .caption = "Some CPU pressure: "
+ .uiName = "PSI some CPU",
+ .caption = "PSI some CPU: ",
+ .description = "Pressure Stall Information, some cpu"
};
-MeterClass PressureStallIOSomeMeter_class = {
+const MeterClass PressureStallIOSomeMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -80,11 +86,12 @@ MeterClass PressureStallIOSomeMeter_class = {
.total = 100.0,
.attributes = PressureStallMeter_attributes,
.name = "PressureStallIOSome",
- .uiName = "Pressure Stall Information, some IO",
- .caption = "Some IO pressure: "
+ .uiName = "PSI some IO",
+ .caption = "PSI some IO: ",
+ .description = "Pressure Stall Information, some io"
};
-MeterClass PressureStallIOFullMeter_class = {
+const MeterClass PressureStallIOFullMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -96,11 +103,12 @@ MeterClass PressureStallIOFullMeter_class = {
.total = 100.0,
.attributes = PressureStallMeter_attributes,
.name = "PressureStallIOFull",
- .uiName = "Pressure Stall Information, full IO",
- .caption = "Full IO pressure: "
+ .uiName = "PSI full IO",
+ .caption = "PSI full IO: ",
+ .description = "Pressure Stall Information, full io"
};
-MeterClass PressureStallMemorySomeMeter_class = {
+const MeterClass PressureStallMemorySomeMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -112,11 +120,12 @@ MeterClass PressureStallMemorySomeMeter_class = {
.total = 100.0,
.attributes = PressureStallMeter_attributes,
.name = "PressureStallMemorySome",
- .uiName = "Pressure Stall Information, some memory",
- .caption = "Some Mem pressure: "
+ .uiName = "PSI some memory",
+ .caption = "PSI some memory: ",
+ .description = "Pressure Stall Information, some memory"
};
-MeterClass PressureStallMemoryFullMeter_class = {
+const MeterClass PressureStallMemoryFullMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
@@ -128,6 +137,7 @@ MeterClass PressureStallMemoryFullMeter_class = {
.total = 100.0,
.attributes = PressureStallMeter_attributes,
.name = "PressureStallMemoryFull",
- .uiName = "Pressure Stall Information, full memory",
- .caption = "Full Mem pressure: "
+ .uiName = "PSI full memory",
+ .caption = "PSI full memory: ",
+ .description = "Pressure Stall Information, full memory"
};
diff --git a/linux/PressureStallMeter.h b/linux/PressureStallMeter.h
index 22b8b97..1a0ad58 100644
--- a/linux/PressureStallMeter.h
+++ b/linux/PressureStallMeter.h
@@ -6,20 +6,20 @@
htop - PressureStallMeter.h
(C) 2004-2011 Hisham H. Muhammad
(C) 2019 Ran Benita
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Meter.h"
-extern MeterClass PressureStallCPUSomeMeter_class;
+extern const MeterClass PressureStallCPUSomeMeter_class;
-extern MeterClass PressureStallIOSomeMeter_class;
+extern const MeterClass PressureStallIOSomeMeter_class;
-extern MeterClass PressureStallIOFullMeter_class;
+extern const MeterClass PressureStallIOFullMeter_class;
-extern MeterClass PressureStallMemorySomeMeter_class;
+extern const MeterClass PressureStallMemorySomeMeter_class;
-extern MeterClass PressureStallMemoryFullMeter_class;
+extern const MeterClass PressureStallMemoryFullMeter_class;
#endif
diff --git a/linux/SELinuxMeter.c b/linux/SELinuxMeter.c
new file mode 100644
index 0000000..64a3f2a
--- /dev/null
+++ b/linux/SELinuxMeter.c
@@ -0,0 +1,94 @@
+/*
+htop - SELinuxMeter.c
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "SELinuxMeter.h"
+
+#include "CRT.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <linux/magic.h>
+#include <sys/statfs.h>
+#include <sys/statvfs.h>
+
+#include "Macros.h"
+#include "Object.h"
+#include "XUtils.h"
+
+
+static const int SELinuxMeter_attributes[] = {
+ METER_TEXT,
+};
+
+static bool enabled = false;
+static bool enforcing = false;
+
+static bool hasSELinuxMount(void) {
+ struct statfs sfbuf;
+ int r = statfs("/sys/fs/selinux", &sfbuf);
+ if (r != 0) {
+ return false;
+ }
+
+ if (sfbuf.f_type != SELINUX_MAGIC) {
+ return false;
+ }
+
+ struct statvfs vfsbuf;
+ r = statvfs("/sys/fs/selinux", &vfsbuf);
+ if (r != 0 || (vfsbuf.f_flag & ST_RDONLY)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool isSelinuxEnabled(void) {
+ return hasSELinuxMount() && (0 == access("/etc/selinux/config", F_OK));
+}
+
+static bool isSelinuxEnforcing(void) {
+ if (!enabled) {
+ return false;
+ }
+
+ char buf[20];
+ ssize_t r = xReadfile("/sys/fs/selinux/enforce", buf, sizeof(buf));
+ if (r < 0)
+ return false;
+
+ int enforce = 0;
+ if (sscanf(buf, "%d", &enforce) != 1) {
+ return false;
+ }
+
+ return !!enforce;
+}
+
+static void SELinuxMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t len) {
+ enabled = isSelinuxEnabled();
+ enforcing = isSelinuxEnforcing();
+
+ xSnprintf(buffer, len, "%s%s", enabled ? "enabled" : "disabled", enabled ? (enforcing ? "; mode: enforcing" : "; mode: permissive") : "");
+}
+
+const MeterClass SELinuxMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ },
+ .updateValues = SELinuxMeter_updateValues,
+ .defaultMode = TEXT_METERMODE,
+ .maxItems = 0,
+ .total = 100.0,
+ .attributes = SELinuxMeter_attributes,
+ .name = "SELinux",
+ .uiName = "SELinux",
+ .description = "SELinux state overview",
+ .caption = "SELinux: "
+};
diff --git a/linux/SELinuxMeter.h b/linux/SELinuxMeter.h
new file mode 100644
index 0000000..d79ad01
--- /dev/null
+++ b/linux/SELinuxMeter.h
@@ -0,0 +1,14 @@
+#ifndef HEADER_SELinuxMeter
+#define HEADER_SELinuxMeter
+/*
+htop - SELinuxMeter.h
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "Meter.h"
+
+extern const MeterClass SELinuxMeter_class;
+
+#endif /* HEADER_SELinuxMeter */
diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c
new file mode 100644
index 0000000..4350d26
--- /dev/null
+++ b/linux/SystemdMeter.c
@@ -0,0 +1,335 @@
+/*
+htop - SystemdMeter.c
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "SystemdMeter.h"
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "Object.h"
+#include "RichString.h"
+#include "XUtils.h"
+
+
+#define INVALID_VALUE ((unsigned int)-1)
+
+typedef void sd_bus;
+typedef void sd_bus_error;
+static int (*sym_sd_bus_open_system)(sd_bus**);
+static int (*sym_sd_bus_get_property_string)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char**);
+static int (*sym_sd_bus_get_property_trivial)(sd_bus*, const char*, const char*, const char*, const char*, sd_bus_error*, char, void*);
+static sd_bus* (*sym_sd_bus_unref)(sd_bus*);
+
+static char* systemState = NULL;
+static unsigned int nFailedUnits = INVALID_VALUE;
+static unsigned int nInstalledJobs = INVALID_VALUE;
+static unsigned int nNames = INVALID_VALUE;
+static unsigned int nJobs = INVALID_VALUE;
+static void* dlopenHandle = NULL;
+static sd_bus* bus = NULL;
+
+static void SystemdMeter_done(ATTR_UNUSED Meter* this) {
+ free(systemState);
+ systemState = NULL;
+
+ if (bus && dlopenHandle) {
+ sym_sd_bus_unref(bus);
+ }
+ bus = NULL;
+
+ if (dlopenHandle) {
+ dlclose(dlopenHandle);
+ dlopenHandle = NULL;
+ }
+}
+
+static int updateViaLib(void) {
+ if (!dlopenHandle) {
+ dlopenHandle = dlopen("libsystemd.so.0", RTLD_LAZY);
+ if (!dlopenHandle)
+ goto dlfailure;
+
+ /* Clear any errors */
+ dlerror();
+
+ #define resolve(symbolname) do { \
+ *(void **)(&sym_##symbolname) = dlsym(dlopenHandle, #symbolname); \
+ if (!sym_##symbolname || dlerror() != NULL) \
+ goto dlfailure; \
+ } while(0)
+
+ resolve(sd_bus_open_system);
+ resolve(sd_bus_get_property_string);
+ resolve(sd_bus_get_property_trivial);
+ resolve(sd_bus_unref);
+
+ #undef resolve
+ }
+
+ int r;
+
+ /* Connect to the system bus */
+ if (!bus) {
+ r = sym_sd_bus_open_system(&bus);
+ if (r < 0)
+ goto busfailure;
+ }
+
+ static const char* const busServiceName = "org.freedesktop.systemd1";
+ static const char* const busObjectPath = "/org/freedesktop/systemd1";
+ static const char* const busInterfaceName = "org.freedesktop.systemd1.Manager";
+
+ r = sym_sd_bus_get_property_string(bus,
+ busServiceName, /* service to contact */
+ busObjectPath, /* object path */
+ busInterfaceName, /* interface name */
+ "SystemState", /* property name */
+ NULL, /* object to return error in */
+ &systemState);
+ if (r < 0)
+ goto busfailure;
+
+ r = sym_sd_bus_get_property_trivial(bus,
+ busServiceName, /* service to contact */
+ busObjectPath, /* object path */
+ busInterfaceName, /* interface name */
+ "NFailedUnits", /* property name */
+ NULL, /* object to return error in */
+ 'u', /* property type */
+ &nFailedUnits);
+ if (r < 0)
+ goto busfailure;
+
+ r = sym_sd_bus_get_property_trivial(bus,
+ busServiceName, /* service to contact */
+ busObjectPath, /* object path */
+ busInterfaceName, /* interface name */
+ "NInstalledJobs", /* property name */
+ NULL, /* object to return error in */
+ 'u', /* property type */
+ &nInstalledJobs);
+ if (r < 0)
+ goto busfailure;
+
+ r = sym_sd_bus_get_property_trivial(bus,
+ busServiceName, /* service to contact */
+ busObjectPath, /* object path */
+ busInterfaceName, /* interface name */
+ "NNames", /* property name */
+ NULL, /* object to return error in */
+ 'u', /* property type */
+ &nNames);
+ if (r < 0)
+ goto busfailure;
+
+ r = sym_sd_bus_get_property_trivial(bus,
+ busServiceName, /* service to contact */
+ busObjectPath, /* object path */
+ busInterfaceName, /* interface name */
+ "NJobs", /* property name */
+ NULL, /* object to return error in */
+ 'u', /* property type */
+ &nJobs);
+ if (r < 0)
+ goto busfailure;
+
+ /* success */
+ return 0;
+
+busfailure:
+ sym_sd_bus_unref(bus);
+ bus = NULL;
+ return -2;
+
+dlfailure:
+ if (dlopenHandle) {
+ dlclose(dlopenHandle);
+ dlopenHandle = NULL;
+ }
+ return -1;
+}
+
+static void updateViaExec(void) {
+ int fdpair[2];
+ if (pipe(fdpair) < 0)
+ return;
+
+ pid_t child = fork();
+ if (child < 0) {
+ close(fdpair[1]);
+ close(fdpair[0]);
+ return;
+ }
+
+ if (child == 0) {
+ close(fdpair[0]);
+ dup2(fdpair[1], STDOUT_FILENO);
+ close(fdpair[1]);
+ int fdnull = open("/dev/null", O_WRONLY);
+ if (fdnull < 0)
+ exit(1);
+ dup2(fdnull, STDERR_FILENO);
+ close(fdnull);
+ execl("/bin/systemctl",
+ "/bin/systemctl",
+ "show",
+ "--property=SystemState",
+ "--property=NFailedUnits",
+ "--property=NNames",
+ "--property=NJobs",
+ "--property=NInstalledJobs",
+ NULL);
+ exit(127);
+ }
+ close(fdpair[1]);
+
+ int wstatus;
+ if (waitpid(child, &wstatus, 0) < 0 || !WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) {
+ close(fdpair[0]);
+ return;
+ }
+
+ FILE* commandOutput = fdopen(fdpair[0], "r");
+ if (!commandOutput) {
+ close(fdpair[0]);
+ return;
+ }
+
+ char lineBuffer[128];
+ while (fgets(lineBuffer, sizeof(lineBuffer), commandOutput)) {
+ if (String_startsWith(lineBuffer, "SystemState=")) {
+ char* newline = strchr(lineBuffer + strlen("SystemState="), '\n');
+ if (newline) {
+ *newline = '\0';
+ }
+ free(systemState);
+ systemState = xStrdup(lineBuffer + strlen("SystemState="));
+ } else if (String_startsWith(lineBuffer, "NFailedUnits=")) {
+ nFailedUnits = strtoul(lineBuffer + strlen("NFailedUnits="), NULL, 10);
+ } else if (String_startsWith(lineBuffer, "NNames=")) {
+ nNames = strtoul(lineBuffer + strlen("NNames="), NULL, 10);
+ } else if (String_startsWith(lineBuffer, "NJobs=")) {
+ nJobs = strtoul(lineBuffer + strlen("NJobs="), NULL, 10);
+ } else if (String_startsWith(lineBuffer, "NInstalledJobs=")) {
+ nInstalledJobs = strtoul(lineBuffer + strlen("NInstalledJobs="), NULL, 10);
+ }
+ }
+
+ fclose(commandOutput);
+}
+
+static void SystemdMeter_updateValues(ATTR_UNUSED Meter* this, char* buffer, size_t size) {
+ free(systemState);
+ systemState = NULL;
+ nFailedUnits = nInstalledJobs = nNames = nJobs = INVALID_VALUE;
+
+ if (updateViaLib() < 0)
+ updateViaExec();
+
+ xSnprintf(buffer, size, "%s", systemState ? systemState : "???");
+}
+
+static int zeroDigitColor(unsigned int value) {
+ switch (value) {
+ case 0:
+ return CRT_colors[METER_VALUE];
+ case INVALID_VALUE:
+ return CRT_colors[METER_VALUE_ERROR];
+ default:
+ return CRT_colors[METER_VALUE_NOTICE];
+ }
+}
+
+static int valueDigitColor(unsigned int value) {
+ switch (value) {
+ case 0:
+ return CRT_colors[METER_VALUE_NOTICE];
+ case INVALID_VALUE:
+ return CRT_colors[METER_VALUE_ERROR];
+ default:
+ return CRT_colors[METER_VALUE];
+ }
+}
+
+
+static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) {
+ char buffer[16];
+
+ int color = (systemState && 0 == strcmp(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR;
+ RichString_write(out, CRT_colors[color], systemState ? systemState : "???");
+
+ RichString_append(out, CRT_colors[METER_TEXT], " (");
+
+ if (nFailedUnits == INVALID_VALUE) {
+ buffer[0] = '?';
+ buffer[1] = '\0';
+ } else {
+ xSnprintf(buffer, sizeof(buffer), "%u", nFailedUnits);
+ }
+ RichString_append(out, zeroDigitColor(nFailedUnits), buffer);
+
+ RichString_append(out, CRT_colors[METER_TEXT], "/");
+
+ if (nNames == INVALID_VALUE) {
+ buffer[0] = '?';
+ buffer[1] = '\0';
+ } else {
+ xSnprintf(buffer, sizeof(buffer), "%u", nNames);
+ }
+ RichString_append(out, valueDigitColor(nNames), buffer);
+
+ RichString_append(out, CRT_colors[METER_TEXT], " failed) (");
+
+ if (nJobs == INVALID_VALUE) {
+ buffer[0] = '?';
+ buffer[1] = '\0';
+ } else {
+ xSnprintf(buffer, sizeof(buffer), "%u", nJobs);
+ }
+ RichString_append(out, zeroDigitColor(nJobs), buffer);
+
+ RichString_append(out, CRT_colors[METER_TEXT], "/");
+
+ if (nInstalledJobs == INVALID_VALUE) {
+ buffer[0] = '?';
+ buffer[1] = '\0';
+ } else {
+ xSnprintf(buffer, sizeof(buffer), "%u", nInstalledJobs);
+ }
+ RichString_append(out, valueDigitColor(nInstalledJobs), buffer);
+
+ RichString_append(out, CRT_colors[METER_TEXT], " jobs)");
+}
+
+static const int SystemdMeter_attributes[] = {
+ METER_VALUE
+};
+
+const MeterClass SystemdMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = SystemdMeter_display
+ },
+ .updateValues = SystemdMeter_updateValues,
+ .done = SystemdMeter_done,
+ .defaultMode = TEXT_METERMODE,
+ .maxItems = 0,
+ .total = 100.0,
+ .attributes = SystemdMeter_attributes,
+ .name = "Systemd",
+ .uiName = "Systemd state",
+ .description = "Systemd system state and unit overview",
+ .caption = "Systemd: ",
+};
diff --git a/linux/SystemdMeter.h b/linux/SystemdMeter.h
new file mode 100644
index 0000000..0f226d6
--- /dev/null
+++ b/linux/SystemdMeter.h
@@ -0,0 +1,15 @@
+#ifndef HEADER_SystemdMeter
+#define HEADER_SystemdMeter
+
+/*
+htop - SystemdMeter.h
+(C) 2020 htop dev team
+Released under the GNU GPLv2, see the COPYING file
+in the source distribution for its full text.
+*/
+
+#include "Meter.h"
+
+extern const MeterClass SystemdMeter_class;
+
+#endif /* HEADER_SystemdMeter */
diff --git a/linux/ZramMeter.c b/linux/ZramMeter.c
new file mode 100644
index 0000000..e6b6937
--- /dev/null
+++ b/linux/ZramMeter.c
@@ -0,0 +1,67 @@
+#include "ZramMeter.h"
+
+#include "CRT.h"
+#include "Meter.h"
+#include "Object.h"
+#include "Platform.h"
+#include "RichString.h"
+
+
+static const int ZramMeter_attributes[] = {
+ ZRAM
+};
+
+static void ZramMeter_updateValues(Meter* this, char* buffer, size_t size) {
+ int written;
+
+ Platform_setZramValues(this);
+
+ /* on print bar for compressed data size, not uncompressed */
+ this->curItems = 1;
+
+ written = Meter_humanUnit(buffer, this->values[0], size);
+ METER_BUFFER_CHECK(buffer, size, written);
+
+ METER_BUFFER_APPEND_CHR(buffer, size, '(');
+
+ written = Meter_humanUnit(buffer, this->values[1], size);
+ METER_BUFFER_CHECK(buffer, size, written);
+
+ METER_BUFFER_APPEND_CHR(buffer, size, ')');
+
+ METER_BUFFER_APPEND_CHR(buffer, size, '/');
+
+ Meter_humanUnit(buffer, this->total, size);
+}
+
+static void ZramMeter_display(const Object* cast, RichString* out) {
+ char buffer[50];
+ const Meter* this = (const Meter*)cast;
+ RichString_write(out, CRT_colors[METER_TEXT], ":");
+ Meter_humanUnit(buffer, this->total, sizeof(buffer));
+
+ RichString_append(out, CRT_colors[METER_VALUE], buffer);
+ Meter_humanUnit(buffer, this->values[0], sizeof(buffer));
+ RichString_append(out, CRT_colors[METER_TEXT], " used:");
+ RichString_append(out, CRT_colors[METER_VALUE], buffer);
+
+ Meter_humanUnit(buffer, this->values[1], sizeof(buffer));
+ RichString_append(out, CRT_colors[METER_TEXT], " uncompressed:");
+ RichString_append(out, CRT_colors[METER_VALUE], buffer);
+}
+
+const MeterClass ZramMeter_class = {
+ .super = {
+ .extends = Class(Meter),
+ .delete = Meter_delete,
+ .display = ZramMeter_display,
+ },
+ .updateValues = ZramMeter_updateValues,
+ .defaultMode = BAR_METERMODE,
+ .maxItems = 2,
+ .total = 100.0,
+ .attributes = ZramMeter_attributes,
+ .name = "Zram",
+ .uiName = "Zram",
+ .caption = "zrm"
+};
diff --git a/linux/ZramMeter.h b/linux/ZramMeter.h
new file mode 100644
index 0000000..7cf7861
--- /dev/null
+++ b/linux/ZramMeter.h
@@ -0,0 +1,8 @@
+#ifndef HEADER_ZramMeter
+#define HEADER_ZramMeter
+
+#include "Meter.h"
+
+extern const MeterClass ZramMeter_class;
+
+#endif
diff --git a/linux/ZramStats.h b/linux/ZramStats.h
new file mode 100644
index 0000000..2305cfd
--- /dev/null
+++ b/linux/ZramStats.h
@@ -0,0 +1,10 @@
+#ifndef HEADER_ZramStats
+#define HEADER_ZramStats
+
+typedef struct ZramStats_ {
+ unsigned long long int totalZram;
+ unsigned long long int usedZramComp;
+ unsigned long long int usedZramOrig;
+} ZramStats;
+
+#endif
diff --git a/openbsd/Battery.c b/openbsd/Battery.c
deleted file mode 100644
index c215e41..0000000
--- a/openbsd/Battery.c
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
-htop - openbsd/Battery.c
-(C) 2015 Hisham H. Muhammad
-(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "BatteryMeter.h"
-#include <sys/sysctl.h>
-#include <sys/sensors.h>
-#include <errno.h>
-#include <string.h>
-
-static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) {
- for (int devn = 0;; devn++) {
- mib[2] = devn;
- if (sysctl(mib, 3, snsrdev, sdlen, NULL, 0) == -1) {
- if (errno == ENXIO)
- continue;
- if (errno == ENOENT)
- return false;
- }
- if (strcmp(name, snsrdev->xname) == 0) {
- return true;
- }
- }
-}
-
-void Battery_getData(double* level, ACPresence* isOnAC) {
- static int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0};
- struct sensor s;
- size_t slen = sizeof(struct sensor);
- struct sensordev snsrdev;
- size_t sdlen = sizeof(struct sensordev);
-
- bool found = findDevice("acpibat0", mib, &snsrdev, &sdlen);
-
- *level = -1;
- if (found) {
- /* last full capacity */
- mib[3] = 7;
- mib[4] = 0;
- double last_full_capacity = 0;
- if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
- last_full_capacity = s.value;
- }
- if (last_full_capacity > 0) {
- /* remaining capacity */
- mib[3] = 7;
- mib[4] = 3;
- if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
- double charge = s.value;
- *level = 100*(charge / last_full_capacity);
- if (charge >= last_full_capacity) {
- *level = 100;
- }
- }
- }
- }
-
- found = findDevice("acpiac0", mib, &snsrdev, &sdlen);
-
- *isOnAC = AC_ERROR;
- if (found) {
- mib[3] = 9;
- mib[4] = 0;
- if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
- *isOnAC = s.value;
- }
- }
-}
diff --git a/openbsd/Battery.h b/openbsd/Battery.h
deleted file mode 100644
index e858b15..0000000
--- a/openbsd/Battery.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef HEADER_Battery
-#define HEADER_Battery
-/*
-htop - openbsd/Battery.h
-(C) 2015 Hisham H. Muhammad
-(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void Battery_getData(double* level, ACPresence* isOnAC);
-
-#endif
diff --git a/openbsd/OpenBSDCRT.c b/openbsd/OpenBSDCRT.c
deleted file mode 100644
index 233d30c..0000000
--- a/openbsd/OpenBSDCRT.c
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
-htop - UnsupportedCRT.c
-(C) 2014 Hisham H. Muhammad
-(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "config.h"
-#include "CRT.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-void CRT_handleSIGSEGV(int sgn) {
- (void) sgn;
- CRT_done();
- fprintf(stderr, "\n\nhtop " VERSION " aborting.\n");
- fprintf(stderr, "\nUnfortunately, you seem to be using an unsupported platform!");
- fprintf(stderr, "\nPlease contact your platform package maintainer!\n\n");
- abort();
-}
diff --git a/openbsd/OpenBSDCRT.h b/openbsd/OpenBSDCRT.h
deleted file mode 100644
index 85860a9..0000000
--- a/openbsd/OpenBSDCRT.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef HEADER_OpenBSDCRT
-#define HEADER_OpenBSDCRT
-/*
-htop - UnsupportedCRT.h
-(C) 2014 Hisham H. Muhammad
-(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void CRT_handleSIGSEGV(int sgn);
-
-#endif
diff --git a/openbsd/OpenBSDProcess.c b/openbsd/OpenBSDProcess.c
index 1a80784..df5002a 100644
--- a/openbsd/OpenBSDProcess.c
+++ b/openbsd/OpenBSDProcess.c
@@ -2,157 +2,177 @@
htop - OpenBSDProcess.c
(C) 2015 Hisham H. Muhammad
(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Process.h"
-#include "ProcessList.h"
#include "OpenBSDProcess.h"
-#include "Platform.h"
-#include "CRT.h"
#include <stdlib.h>
-#include <unistd.h>
-#include <sys/syscall.h>
+#include "CRT.h"
+#include "Process.h"
+#include "RichString.h"
+#include "XUtils.h"
-ProcessClass OpenBSDProcess_class = {
- .super = {
- .extends = Class(Process),
- .display = Process_display,
- .delete = Process_delete,
- .compare = OpenBSDProcess_compare
- },
- .writeField = (Process_WriteField) OpenBSDProcess_writeField,
-};
ProcessFieldData Process_fields[] = {
[0] = {
.name = "",
.title = NULL,
.description = NULL,
- .flags = 0, },
+ .flags = 0,
+ },
[PID] = {
.name = "PID",
.title = " PID ",
.description = "Process/thread ID",
- .flags = 0, },
+ .flags = 0,
+ },
[COMM] = {
.name = "Command",
.title = "Command ",
.description = "Command line",
- .flags = 0, },
+ .flags = 0,
+ },
[STATE] = {
.name = "STATE",
.title = "S ",
.description = "Process state (S sleeping, R running, D disk, Z zombie, T traced, W paging)",
- .flags = 0, },
+ .flags = 0,
+ },
[PPID] = {
.name = "PPID",
.title = " PPID ",
.description = "Parent process ID",
- .flags = 0, },
+ .flags = 0,
+ },
[PGRP] = {
.name = "PGRP",
.title = " PGRP ",
.description = "Process group ID",
- .flags = 0, },
+ .flags = 0,
+ },
[SESSION] = {
.name = "SESSION",
.title = " SESN ",
.description = "Process's session ID",
- .flags = 0, },
+ .flags = 0,
+ },
[TTY_NR] = {
.name = "TTY_NR",
.title = " TTY ",
.description = "Controlling terminal",
- .flags = 0, },
+ .flags = 0,
+ },
[TPGID] = {
.name = "TPGID",
.title = " TPGID ",
.description = "Process ID of the fg process group of the controlling terminal",
- .flags = 0, },
+ .flags = 0,
+ },
[MINFLT] = {
.name = "MINFLT",
.title = " MINFLT ",
.description = "Number of minor faults which have not required loading a memory page from disk",
- .flags = 0, },
+ .flags = 0,
+ },
[MAJFLT] = {
.name = "MAJFLT",
.title = " MAJFLT ",
.description = "Number of major faults which have required loading a memory page from disk",
- .flags = 0, },
+ .flags = 0,
+ },
[PRIORITY] = {
.name = "PRIORITY",
.title = "PRI ",
.description = "Kernel's internal priority for the process",
- .flags = 0, },
+ .flags = 0,
+ },
[NICE] = {
.name = "NICE",
.title = " NI ",
.description = "Nice value (the higher the value, the more it lets other processes take priority)",
- .flags = 0, },
+ .flags = 0,
+ },
[STARTTIME] = {
.name = "STARTTIME",
.title = "START ",
.description = "Time the process was started",
- .flags = 0, },
+ .flags = 0,
+ },
[PROCESSOR] = {
.name = "PROCESSOR",
.title = "CPU ",
.description = "Id of the CPU the process last executed on",
- .flags = 0, },
- [M_SIZE] = {
- .name = "M_SIZE",
+ .flags = 0,
+ },
+ [M_VIRT] = {
+ .name = "M_VIRT",
.title = " VIRT ",
.description = "Total program size in virtual memory",
- .flags = 0, },
+ .flags = 0,
+ },
[M_RESIDENT] = {
.name = "M_RESIDENT",
.title = " RES ",
.description = "Resident set size, size of the text and data sections, plus stack usage",
- .flags = 0, },
+ .flags = 0,
+ },
[ST_UID] = {
.name = "ST_UID",
.title = " UID ",
.description = "User ID of the process owner",
- .flags = 0, },
+ .flags = 0,
+ },
[PERCENT_CPU] = {
.name = "PERCENT_CPU",
.title = "CPU% ",
.description = "Percentage of the CPU time the process used in the last sampling",
- .flags = 0, },
+ .flags = 0,
+ },
+ [PERCENT_NORM_CPU] = {
+ .name = "PERCENT_NORM_CPU",
+ .title = "NCPU%",
+ .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)",
+ .flags = 0,
+ },
[PERCENT_MEM] = {
.name = "PERCENT_MEM",
.title = "MEM% ",
.description = "Percentage of the memory the process is using, based on resident memory size",
- .flags = 0, },
+ .flags = 0,
+ },
[USER] = {
.name = "USER",
.title = "USER ",
.description = "Username of the process owner (or user ID if name cannot be determined)",
- .flags = 0, },
+ .flags = 0,
+ },
[TIME] = {
.name = "TIME",
.title = " TIME+ ",
.description = "Total time the process has spent in user and system time",
- .flags = 0, },
+ .flags = 0,
+ },
[NLWP] = {
.name = "NLWP",
.title = "NLWP ",
.description = "Number of threads in the process",
- .flags = 0, },
+ .flags = 0,
+ },
[TGID] = {
.name = "TGID",
.title = " TGID ",
.description = "Thread group ID (i.e. process ID)",
- .flags = 0, },
+ .flags = 0,
+ },
[LAST_PROCESSFIELD] = {
.name = "*** report bug! ***",
.title = NULL,
.description = NULL,
- .flags = 0, },
+ .flags = 0,
+ },
};
ProcessPidColumn Process_pidColumns[] = {
@@ -165,11 +185,11 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = 0, .label = NULL },
};
-OpenBSDProcess* OpenBSDProcess_new(Settings* settings) {
+Process* OpenBSDProcess_new(const Settings* settings) {
OpenBSDProcess* this = xCalloc(sizeof(OpenBSDProcess), 1);
Object_setClass(this, Class(OpenBSDProcess));
Process_init(&this->super, settings);
- return this;
+ return &this->super;
}
void Process_delete(Object* cast) {
@@ -178,8 +198,8 @@ void Process_delete(Object* cast) {
free(this);
}
-void OpenBSDProcess_writeField(Process* this, RichString* str, ProcessField field) {
- //OpenBSDProcess* fp = (OpenBSDProcess*) this;
+static void OpenBSDProcess_writeField(const Process* this, RichString* str, ProcessField field) {
+ //const OpenBSDProcess* op = (const OpenBSDProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
//int n = sizeof(buffer) - 1;
@@ -192,16 +212,21 @@ void OpenBSDProcess_writeField(Process* this, RichString* str, ProcessField fiel
RichString_append(str, attr, buffer);
}
-long OpenBSDProcess_compare(const void* v1, const void* v2) {
- OpenBSDProcess *p1, *p2;
- Settings *settings = ((Process*)v1)->settings;
+static long OpenBSDProcess_compare(const void* v1, const void* v2) {
+ const OpenBSDProcess *p1, *p2;
+ const Settings *settings = ((const Process*)v1)->settings;
+
if (settings->direction == 1) {
- p1 = (OpenBSDProcess*)v1;
- p2 = (OpenBSDProcess*)v2;
+ p1 = (const OpenBSDProcess*)v1;
+ p2 = (const OpenBSDProcess*)v2;
} else {
- p2 = (OpenBSDProcess*)v1;
- p1 = (OpenBSDProcess*)v2;
+ p2 = (const OpenBSDProcess*)v1;
+ p1 = (const OpenBSDProcess*)v2;
}
+
+ // remove if actually used
+ (void)p1; (void)p2;
+
switch (settings->sortKey) {
// add OpenBSD-specific fields here
default:
@@ -209,6 +234,16 @@ long OpenBSDProcess_compare(const void* v1, const void* v2) {
}
}
-bool Process_isThread(Process* this) {
- return (Process_isKernelThread(this));
+const ProcessClass OpenBSDProcess_class = {
+ .super = {
+ .extends = Class(Process),
+ .display = Process_display,
+ .delete = Process_delete,
+ .compare = OpenBSDProcess_compare
+ },
+ .writeField = OpenBSDProcess_writeField,
+};
+
+bool Process_isThread(const Process* this) {
+ return Process_isKernelThread(this) || Process_isUserlandThread(this);
}
diff --git a/openbsd/OpenBSDProcess.h b/openbsd/OpenBSDProcess.h
index fede6f8..2d01513 100644
--- a/openbsd/OpenBSDProcess.h
+++ b/openbsd/OpenBSDProcess.h
@@ -4,10 +4,17 @@
htop - OpenBSDProcess.h
(C) 2015 Hisham H. Muhammad
(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+
+#include "Object.h"
+#include "Process.h"
+#include "Settings.h"
+
+
typedef enum OpenBSDProcessFields_ {
// Add platform-specific fields here, with ids >= 100
LAST_PROCESSFIELD = 100,
@@ -21,20 +28,16 @@ typedef struct OpenBSDProcess_ {
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
-extern ProcessClass OpenBSDProcess_class;
+extern const ProcessClass OpenBSDProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
-OpenBSDProcess* OpenBSDProcess_new(Settings* settings);
+Process* OpenBSDProcess_new(const Settings* settings);
void Process_delete(Object* cast);
-void OpenBSDProcess_writeField(Process* this, RichString* str, ProcessField field);
-
-long OpenBSDProcess_compare(const void* v1, const void* v2);
-
-bool Process_isThread(Process* this);
+bool Process_isThread(const Process* this);
#endif
diff --git a/openbsd/OpenBSDProcessList.c b/openbsd/OpenBSDProcessList.c
index c5d79f5..5412030 100644
--- a/openbsd/OpenBSDProcessList.c
+++ b/openbsd/OpenBSDProcessList.c
@@ -2,49 +2,53 @@
htop - OpenBSDProcessList.c
(C) 2014 Hisham H. Muhammad
(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "CRT.h"
-#include "ProcessList.h"
#include "OpenBSDProcessList.h"
-#include "OpenBSDProcess.h"
#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/proc.h>
-#include <sys/resource.h>
#include <sys/sched.h>
+#include <sys/swap.h>
#include <sys/sysctl.h>
#include <sys/types.h>
-#include <sys/user.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
+#include <uvm/uvmexp.h>
+
+#include "CRT.h"
+#include "Macros.h"
+#include "Object.h"
+#include "OpenBSDProcess.h"
+#include "Process.h"
+#include "ProcessList.h"
+#include "Settings.h"
+#include "XUtils.h"
+
static long fscale;
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
- int mib[] = { CTL_HW, HW_NCPU };
- int fmib[] = { CTL_KERN, KERN_FSCALE };
- int i, e;
- OpenBSDProcessList* opl;
- ProcessList* pl;
+ const int mib[] = { CTL_HW, HW_NCPU };
+ const int fmib[] = { CTL_KERN, KERN_FSCALE };
+ int r;
size_t size;
char errbuf[_POSIX2_LINE_MAX];
- opl = xCalloc(1, sizeof(OpenBSDProcessList));
- pl = (ProcessList*) opl;
- size = sizeof(pl->cpuCount);
+ OpenBSDProcessList* opl = xCalloc(1, sizeof(OpenBSDProcessList));
+ ProcessList* pl = (ProcessList*) opl;
ProcessList_init(pl, Class(OpenBSDProcess), usersTable, pidMatchList, userId);
- e = sysctl(mib, 2, &pl->cpuCount, &size, NULL, 0);
- if (e == -1 || pl->cpuCount < 1) {
+ size = sizeof(pl->cpuCount);
+ r = sysctl(mib, 2, &pl->cpuCount, &size, NULL, 0);
+ if (r < 0 || pl->cpuCount < 1) {
pl->cpuCount = 1;
}
opl->cpus = xCalloc(pl->cpuCount + 1, sizeof(CPUData));
@@ -54,8 +58,8 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
err(1, "fscale sysctl call failed");
}
- for (i = 0; i <= pl->cpuCount; i++) {
- CPUData *d = opl->cpus + i;
+ for (int i = 0; i <= pl->cpuCount; i++) {
+ CPUData* d = opl->cpus + i;
d->totalTime = 1;
d->totalPeriod = 1;
}
@@ -69,7 +73,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
}
void ProcessList_delete(ProcessList* this) {
- const OpenBSDProcessList* opl = (OpenBSDProcessList*) this;
+ OpenBSDProcessList* opl = (OpenBSDProcessList*) this;
if (opl->kd) {
kvm_close(opl->kd);
@@ -81,8 +85,8 @@ void ProcessList_delete(ProcessList* this) {
free(this);
}
-static inline void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
- static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
+static void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
+ const int uvmexp_mib[] = { CTL_VM, VM_UVMEXP };
struct uvmexp uvmexp;
size_t size_uvmexp = sizeof(uvmexp);
@@ -90,10 +94,11 @@ static inline void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
err(1, "uvmexp sysctl call failed");
}
- pl->totalMem = uvmexp.npages * PAGE_SIZE_KB;
+ pl->totalMem = uvmexp.npages * CRT_pageSizeKB;
+ pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * CRT_pageSizeKB;
// Taken from OpenBSD systat/iostat.c, top/machine.c and uvm_sysctl(9)
- static int bcache_mib[] = {CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT};
+ const int bcache_mib[] = { CTL_VFS, VFS_GENERIC, VFS_BCACHESTAT };
struct bcachestats bcstats;
size_t size_bcstats = sizeof(bcstats);
@@ -101,56 +106,56 @@ static inline void OpenBSDProcessList_scanMemoryInfo(ProcessList* pl) {
err(1, "cannot get vfs.bcachestat");
}
- pl->cachedMem = bcstats.numbufpages * PAGE_SIZE_KB;
- pl->freeMem = uvmexp.free * PAGE_SIZE_KB;
- pl->usedMem = (uvmexp.npages - uvmexp.free - uvmexp.paging) * PAGE_SIZE_KB;
+ pl->cachedMem = bcstats.numbufpages * CRT_pageSizeKB;
/*
- const OpenBSDProcessList* opl = (OpenBSDProcessList*) pl;
-
- size_t len = sizeof(pl->totalMem);
- sysctl(MIB_hw_physmem, 2, &(pl->totalMem), &len, NULL, 0);
- pl->totalMem /= 1024;
- sysctl(MIB_vm_stats_vm_v_wire_count, 4, &(pl->usedMem), &len, NULL, 0);
- pl->usedMem *= PAGE_SIZE_KB;
- pl->freeMem = pl->totalMem - pl->usedMem;
- sysctl(MIB_vm_stats_vm_v_cache_count, 4, &(pl->cachedMem), &len, NULL, 0);
- pl->cachedMem *= PAGE_SIZE_KB;
-
- struct kvm_swap swap[16];
- int nswap = kvm_getswapinfo(opl->kd, swap, sizeof(swap)/sizeof(swap[0]), 0);
- pl->totalSwap = 0;
- pl->usedSwap = 0;
- for (int i = 0; i < nswap; i++) {
- pl->totalSwap += swap[i].ksw_total;
- pl->usedSwap += swap[i].ksw_used;
- }
- pl->totalSwap *= PAGE_SIZE_KB;
- pl->usedSwap *= PAGE_SIZE_KB;
+ * Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
+ * All rights reserved.
+ *
+ * Taken almost directly from OpenBSD's top(1)
+ *
+ * Originally released under a BSD-3 license
+ * Modified through htop developers applying GPL-2
+ */
+ int nswap = swapctl(SWAP_NSWAP, 0, 0);
+ if (nswap > 0) {
+ struct swapent swdev[nswap];
+ int rnswap = swapctl(SWAP_STATS, swdev, nswap);
+
+ /* Total things up */
+ unsigned long long int total = 0, used = 0;
+ for (int i = 0; i < rnswap; i++) {
+ if (swdev[i].se_flags & SWF_ENABLE) {
+ used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
+ total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
+ }
+ }
- pl->sharedMem = 0; // currently unused
- pl->buffersMem = 0; // not exposed to userspace
- */
+ pl->totalSwap = total;
+ pl->usedSwap = used;
+ } else {
+ pl->totalSwap = pl->usedSwap = 0;
+ }
}
-char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd) {
- char *s, **arg;
- size_t len = 0, n;
- int i;
-
+static char* OpenBSDProcessList_readProcessName(kvm_t* kd, const struct kinfo_proc* kproc, int* basenameEnd) {
/*
* Like OpenBSD's top(1), we try to fall back to the command name
* (argv[0]) if we fail to construct the full command.
*/
- arg = kvm_getargv(kd, kproc, 500);
+ char** arg = kvm_getargv(kd, kproc, 500);
if (arg == NULL || *arg == NULL) {
*basenameEnd = strlen(kproc->p_comm);
return xStrdup(kproc->p_comm);
}
- for (i = 0; arg[i] != NULL; i++) {
+
+ size_t len = 0;
+ for (int i = 0; arg[i] != NULL; i++) {
len += strlen(arg[i]) + 1; /* room for arg and trailing space or NUL */
}
+
/* don't use xMalloc here - we want to handle huge argv's gracefully */
+ char* s;
if ((s = malloc(len)) == NULL) {
*basenameEnd = strlen(kproc->p_comm);
return xStrdup(kproc->p_comm);
@@ -158,11 +163,10 @@ char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, in
*s = '\0';
- for (i = 0; arg[i] != NULL; i++) {
- n = strlcat(s, arg[i], len);
+ for (int i = 0; arg[i] != NULL; i++) {
+ size_t n = strlcat(s, arg[i], len);
if (i == 0) {
- /* TODO: rename all basenameEnd to basenameLen, make size_t */
- *basenameEnd = MINIMUM(n, len-1);
+ *basenameEnd = MINIMUM(n, len - 1);
}
/* the trailing space should get truncated anyway */
strlcat(s, " ", len);
@@ -174,43 +178,29 @@ char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, in
/*
* Taken from OpenBSD's ps(1).
*/
-static double getpcpu(const struct kinfo_proc *kp) {
+static double getpcpu(const struct kinfo_proc* kp) {
if (fscale == 0)
- return (0.0);
+ return 0.0;
-#define fxtofl(fixpt) ((double)(fixpt) / fscale)
-
- return (100.0 * fxtofl(kp->p_pctcpu));
+ return 100.0 * (double)kp->p_pctcpu / fscale;
}
-static inline void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
- Settings* settings = this->super.settings;
+static void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
+ const Settings* settings = this->super.settings;
bool hideKernelThreads = settings->hideKernelThreads;
bool hideUserlandThreads = settings->hideUserlandThreads;
- struct kinfo_proc* kproc;
- bool preExisting;
- Process* proc;
- OpenBSDProcess* fp;
- struct tm date;
- struct timeval tv;
int count = 0;
- int i;
-
- // use KERN_PROC_KTHREAD to also include kernel threads
- struct kinfo_proc* kprocs = kvm_getprocs(this->kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &count);
- //struct kinfo_proc* kprocs = getprocs(KERN_PROC_ALL, 0, &count);
- gettimeofday(&tv, NULL);
+ const struct kinfo_proc* kprocs = kvm_getprocs(this->kd, KERN_PROC_KTHREAD, 0, sizeof(struct kinfo_proc), &count);
- for (i = 0; i < count; i++) {
- kproc = &kprocs[i];
+ for (int i = 0; i < count; i++) {
+ const struct kinfo_proc* kproc = &kprocs[i];
- preExisting = false;
- proc = ProcessList_getProcess(&this->super, kproc->p_pid, &preExisting, (Process_New) OpenBSDProcess_new);
- fp = (OpenBSDProcess*) proc;
+ bool preExisting = false;
+ Process* proc = ProcessList_getProcess(&this->super, kproc->p_pid, &preExisting, OpenBSDProcess_new);
+ //OpenBSDProcess* fp = (OpenBSDProcess*) proc;
- proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc))
- || (hideUserlandThreads && Process_isUserlandThread(proc)));
+ proc->show = ! ((hideKernelThreads && Process_isKernelThread(proc)) || (hideUserlandThreads && Process_isUserlandThread(proc)));
if (!preExisting) {
proc->ppid = kproc->p_ppid;
@@ -221,11 +211,10 @@ static inline void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
proc->pgrp = kproc->p__pgid;
proc->st_uid = kproc->p_uid;
proc->starttime_ctime = kproc->p_ustart_sec;
+ Process_fillStarttimeBuffer(proc);
proc->user = UsersTable_getRef(this->super.usersTable, proc->st_uid);
ProcessList_add(&this->super, proc);
proc->comm = OpenBSDProcessList_readProcessName(this->kd, kproc, &proc->basenameOffset);
- (void) localtime_r((time_t*) &kproc->p_ustart_sec, &date);
- strftime(proc->starttime_show, 7, ((proc->starttime_ctime > tv.tv_sec - 86400) ? "%R " : "%b%d "), &date);
} else {
if (settings->updateProcessNames) {
free(proc->comm);
@@ -233,15 +222,13 @@ static inline void OpenBSDProcessList_scanProcs(OpenBSDProcessList* this) {
}
}
- proc->m_size = kproc->p_vm_dsize;
+ proc->m_virt = kproc->p_vm_dsize;
proc->m_resident = kproc->p_vm_rssize;
- proc->percent_mem = (proc->m_resident * PAGE_SIZE_KB) / (double)(this->super.totalMem) * 100.0;
- proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->super.cpuCount*100.0);
+ proc->percent_mem = (proc->m_resident * CRT_pageSizeKB) / (double)(this->super.totalMem) * 100.0;
+ proc->percent_cpu = CLAMP(getpcpu(kproc), 0.0, this->super.cpuCount * 100.0);
//proc->nlwp = kproc->p_numthreads;
- //proc->time = kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 10);
proc->nice = kproc->p_nice - 20;
- proc->time = kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 1000000);
- proc->time *= 100;
+ proc->time = 100 * (kproc->p_rtime_sec + ((kproc->p_rtime_usec + 500000) / 1000000));
proc->priority = kproc->p_priority - PZERO;
switch (kproc->p_stat) {
@@ -273,10 +260,9 @@ static unsigned long long saturatingSub(unsigned long long a, unsigned long long
}
static void getKernelCPUTimes(int cpuId, u_int64_t* times) {
- int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId };
- size_t length = sizeof(u_int64_t) * CPUSTATES;
- if (sysctl(mib, 3, times, &length, NULL, 0) == -1 ||
- length != sizeof(u_int64_t) * CPUSTATES) {
+ const int mib[] = { CTL_KERN, KERN_CPTIME2, cpuId };
+ size_t length = sizeof(*times) * CPUSTATES;
+ if (sysctl(mib, 3, times, &length, NULL, 0) == -1 || length != sizeof(*times) * CPUSTATES) {
CRT_fatalError("sysctl kern.cp_time2 failed");
}
}
@@ -344,10 +330,16 @@ static void OpenBSDProcessList_scanCPUTime(OpenBSDProcessList* this) {
kernelCPUTimesToHtop(avg, this->cpus);
}
-void ProcessList_goThroughEntries(ProcessList* this) {
- OpenBSDProcessList* opl = (OpenBSDProcessList*) this;
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
+ OpenBSDProcessList* opl = (OpenBSDProcessList*) super;
- OpenBSDProcessList_scanMemoryInfo(this);
- OpenBSDProcessList_scanProcs(opl);
+ OpenBSDProcessList_scanMemoryInfo(super);
OpenBSDProcessList_scanCPUTime(opl);
+
+ // in pause mode only gather global data for meters (CPU/memory/...)
+ if (pauseProcessUpdate) {
+ return;
+ }
+
+ OpenBSDProcessList_scanProcs(opl);
}
diff --git a/openbsd/OpenBSDProcessList.h b/openbsd/OpenBSDProcessList.h
index 0d9defb..a6195a5 100644
--- a/openbsd/OpenBSDProcessList.h
+++ b/openbsd/OpenBSDProcessList.h
@@ -4,11 +4,18 @@
htop - OpenBSDProcessList.h
(C) 2014 Hisham H. Muhammad
(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include <kvm.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "Hashtable.h"
+#include "ProcessList.h"
+#include "UsersTable.h"
+
typedef struct CPUData_ {
unsigned long long int totalTime;
@@ -43,8 +50,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
void ProcessList_delete(ProcessList* this);
-char *OpenBSDProcessList_readProcessName(kvm_t* kd, struct kinfo_proc* kproc, int* basenameEnd);
-
-void ProcessList_goThroughEntries(ProcessList* this);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif
diff --git a/openbsd/Platform.c b/openbsd/Platform.c
index cb16207..dae8072 100644
--- a/openbsd/Platform.c
+++ b/openbsd/Platform.c
@@ -2,43 +2,47 @@
htop - openbsd/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
-#include "Meter.h"
+
+#include <errno.h>
+#include <kvm.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/resource.h>
+#include <sys/sensors.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <uvm/uvmexp.h>
+
#include "CPUMeter.h"
-#include "MemoryMeter.h"
-#include "SwapMeter.h"
-#include "TasksMeter.h"
-#include "LoadAverageMeter.h"
-#include "UptimeMeter.h"
#include "ClockMeter.h"
+#include "DateMeter.h"
+#include "DateTimeMeter.h"
#include "HostnameMeter.h"
-#include "SignalsPanel.h"
+#include "LoadAverageMeter.h"
+#include "Macros.h"
+#include "MemoryMeter.h"
+#include "Meter.h"
#include "OpenBSDProcess.h"
#include "OpenBSDProcessList.h"
-
-#include <sys/param.h>
-#include <sys/sysctl.h>
-#include <sys/swap.h>
-
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <time.h>
-#include <fcntl.h>
-#include <kvm.h>
-#include <limits.h>
-#include <math.h>
+#include "ProcessList.h"
+#include "Settings.h"
+#include "SignalsPanel.h"
+#include "SwapMeter.h"
+#include "TasksMeter.h"
+#include "UptimeMeter.h"
+#include "XUtils.h"
-ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
+ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
@@ -82,15 +86,13 @@ const SignalItem Platform_signals[] = {
{ .name = "32 SIGTHR", .number = 32 },
};
-const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
+const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
-void Platform_setBindings(Htop_Action* keys) {
- (void) keys;
-}
-
-MeterClass* Platform_meterTypes[] = {
+const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
+ &DateMeter_class,
+ &DateTimeMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
@@ -101,18 +103,36 @@ MeterClass* Platform_meterTypes[] = {
&HostnameMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
+ &AllCPUs4Meter_class,
+ &AllCPUs8Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
+ &LeftCPUs4Meter_class,
+ &RightCPUs4Meter_class,
+ &LeftCPUs8Meter_class,
+ &RightCPUs8Meter_class,
&BlankMeter_class,
NULL
};
-// preserved from FreeBSD port
+void Platform_init(void) {
+ /* no platform-specific setup needed */
+}
+
+void Platform_done(void) {
+ /* no platform-specific cleanup needed */
+}
+
+void Platform_setBindings(Htop_Action* keys) {
+ /* no platform-specific key bindings */
+ (void) keys;
+}
+
int Platform_getUptime() {
struct timeval bootTime, currTime;
- int mib[2] = { CTL_KERN, KERN_BOOTTIME };
+ const int mib[2] = { CTL_KERN, KERN_BOOTTIME };
size_t size = sizeof(bootTime);
int err = sysctl(mib, 2, &bootTime, &size, NULL, 0);
@@ -126,7 +146,7 @@ int Platform_getUptime() {
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
struct loadavg loadAverage;
- int mib[2] = { CTL_VM, VM_LOADAVG };
+ const int mib[2] = { CTL_VM, VM_LOADAVG };
size_t size = sizeof(loadAverage);
int err = sysctl(mib, 2, &loadAverage, &size, NULL, 0);
@@ -142,16 +162,16 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
}
int Platform_getMaxPid() {
- // this is hard-coded in sys/sys/proc.h - no sysctl exists
+ // this is hard-coded in sys/proc.h - no sysctl exists
return 99999;
}
double Platform_setCPUValues(Meter* this, int cpu) {
- const OpenBSDProcessList* pl = (OpenBSDProcessList*) this->pl;
+ const OpenBSDProcessList* pl = (const OpenBSDProcessList*) this->pl;
const CPUData* cpuData = &(pl->cpus[cpu]);
double total = cpuData->totalPeriod == 0 ? 1 : cpuData->totalPeriod;
double totalPercent;
- double *v = this->values;
+ double* v = this->values;
v[CPU_METER_NICE] = cpuData->nicePeriod / total * 100.0;
v[CPU_METER_NORMAL] = cpuData->userPeriod / total * 100.0;
@@ -162,23 +182,25 @@ double Platform_setCPUValues(Meter* this, int cpu) {
v[CPU_METER_STEAL] = 0.0;
v[CPU_METER_GUEST] = 0.0;
v[CPU_METER_IOWAIT] = 0.0;
- v[CPU_METER_FREQUENCY] = -1;
- Meter_setItems(this, 8);
- totalPercent = v[0]+v[1]+v[2]+v[3];
+ v[CPU_METER_FREQUENCY] = NAN;
+ this->curItems = 8;
+ totalPercent = v[0] + v[1] + v[2] + v[3];
} else {
v[2] = cpuData->sysAllPeriod / total * 100.0;
v[3] = 0.0; // No steal nor guest on OpenBSD
- totalPercent = v[0]+v[1]+v[2];
- Meter_setItems(this, 4);
+ totalPercent = v[0] + v[1] + v[2];
+ this->curItems = 4;
}
totalPercent = CLAMP(totalPercent, 0.0, 100.0);
- if (isnan(totalPercent)) totalPercent = 0.0;
+
+ v[CPU_METER_TEMPERATURE] = NAN;
+
return totalPercent;
}
void Platform_setMemoryValues(Meter* this) {
- ProcessList* pl = (ProcessList*) this->pl;
+ const ProcessList* pl = this->pl;
long int usedMem = pl->usedMem;
long int buffersMem = pl->buffersMem;
long int cachedMem = pl->cachedMem;
@@ -189,65 +211,27 @@ void Platform_setMemoryValues(Meter* this) {
this->values[2] = cachedMem;
}
-/*
- * Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
- * All rights reserved.
- *
- * Taken almost directly from OpenBSD's top(1)
- */
void Platform_setSwapValues(Meter* this) {
- ProcessList* pl = (ProcessList*) this->pl;
- struct swapent *swdev;
- unsigned long long int total, used;
- int nswap, rnswap, i;
- nswap = swapctl(SWAP_NSWAP, 0, 0);
- if (nswap == 0) {
- return;
- }
-
- swdev = xCalloc(nswap, sizeof(*swdev));
-
- rnswap = swapctl(SWAP_STATS, swdev, nswap);
- if (rnswap == -1) {
- free(swdev);
- return;
- }
-
- // if rnswap != nswap, then what?
-
- /* Total things up */
- total = used = 0;
- for (i = 0; i < nswap; i++) {
- if (swdev[i].se_flags & SWF_ENABLE) {
- used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
- total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
- }
- }
-
- this->total = pl->totalSwap = total;
- this->values[0] = pl->usedSwap = used;
-
- free(swdev);
-}
-
-void Platform_setTasksValues(Meter* this) {
- // TODO
+ const ProcessList* pl = this->pl;
+ this->total = pl->totalSwap;
+ this->values[0] = pl->usedSwap;
}
char* Platform_getProcessEnv(pid_t pid) {
char errbuf[_POSIX2_LINE_MAX];
- char *env;
- char **ptr;
+ char* env;
+ char** ptr;
int count;
- kvm_t *kt;
- struct kinfo_proc *kproc;
+ kvm_t* kt;
+ struct kinfo_proc* kproc;
size_t capacity = 4096, size = 0;
- if ((kt = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL)
+ if ((kt = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL) {
return NULL;
+ }
if ((kproc = kvm_getprocs(kt, KERN_PROC_PID, pid,
- sizeof(struct kinfo_proc), &count)) == NULL) {\
+ sizeof(struct kinfo_proc), &count)) == NULL) {
(void) kvm_close(kt);
return NULL;
}
@@ -258,7 +242,7 @@ char* Platform_getProcessEnv(pid_t pid) {
}
env = xMalloc(capacity);
- for (char **p = ptr; *p; p++) {
+ for (char** p = ptr; *p; p++) {
size_t len = strlen(*p) + 1;
if (size + len > capacity) {
@@ -271,12 +255,98 @@ char* Platform_getProcessEnv(pid_t pid) {
}
if (size < 2 || env[size - 1] || env[size - 2]) {
- if (size + 2 < capacity)
- env = xRealloc(env, capacity + 2);
- env[size] = 0;
- env[size+1] = 0;
+ if (size + 2 < capacity)
+ env = xRealloc(env, capacity + 2);
+ env[size] = 0;
+ env[size + 1] = 0;
}
(void) kvm_close(kt);
return env;
}
+
+char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
+ (void)pid;
+ (void)inode;
+ return NULL;
+}
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
+ (void)pid;
+ return NULL;
+}
+
+bool Platform_getDiskIO(DiskIOData* data) {
+ // TODO
+ (void)data;
+ return false;
+}
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted) {
+ // TODO
+ *bytesReceived = 0;
+ *packetsReceived = 0;
+ *bytesTransmitted = 0;
+ *packetsTransmitted = 0;
+ return false;
+}
+
+static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) {
+ for (int devn = 0;; devn++) {
+ mib[2] = devn;
+ if (sysctl(mib, 3, snsrdev, sdlen, NULL, 0) == -1) {
+ if (errno == ENXIO)
+ continue;
+ if (errno == ENOENT)
+ return false;
+ }
+ if (strcmp(name, snsrdev->xname) == 0) {
+ return true;
+ }
+ }
+}
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC) {
+ int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0};
+ struct sensor s;
+ size_t slen = sizeof(struct sensor);
+ struct sensordev snsrdev;
+ size_t sdlen = sizeof(struct sensordev);
+
+ bool found = findDevice("acpibat0", mib, &snsrdev, &sdlen);
+
+ *percent = NAN;
+ if (found) {
+ /* last full capacity */
+ mib[3] = 7;
+ mib[4] = 0;
+ double last_full_capacity = 0;
+ if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1)
+ last_full_capacity = s.value;
+ if (last_full_capacity > 0) {
+ /* remaining capacity */
+ mib[3] = 7;
+ mib[4] = 3;
+ if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
+ double charge = s.value;
+ *percent = 100 * (charge / last_full_capacity);
+ if (charge >= last_full_capacity) {
+ *percent = 100;
+ }
+ }
+ }
+ }
+
+ found = findDevice("acpiac0", mib, &snsrdev, &sdlen);
+
+ *isOnAC = AC_ERROR;
+ if (found) {
+ mib[3] = 9;
+ mib[4] = 0;
+ if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1)
+ *isOnAC = s.value;
+ }
+}
diff --git a/openbsd/Platform.h b/openbsd/Platform.h
index f51d6bc..0e2d435 100644
--- a/openbsd/Platform.h
+++ b/openbsd/Platform.h
@@ -4,14 +4,22 @@
htop - openbsd/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2015 Michael McConville
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <stdbool.h>
+#include <sys/types.h>
+
#include "Action.h"
#include "BatteryMeter.h"
+#include "DiskIOMeter.h"
+#include "Meter.h"
+#include "Process.h"
+#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
+
extern ProcessFieldData Process_fields[];
extern ProcessField Platform_defaultFields[];
@@ -23,15 +31,19 @@ extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
-void Platform_setBindings(Htop_Action* keys);
+extern const MeterClass* const Platform_meterTypes[];
-extern MeterClass* Platform_meterTypes[];
+void Platform_init(void);
+
+void Platform_done(void);
+
+void Platform_setBindings(Htop_Action* keys);
-int Platform_getUptime();
+int Platform_getUptime(void);
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
-int Platform_getMaxPid();
+int Platform_getMaxPid(void);
double Platform_setCPUValues(Meter* this, int cpu);
@@ -39,8 +51,19 @@ void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
-void Platform_setTasksValues(Meter* this);
-
char* Platform_getProcessEnv(pid_t pid);
+char* Platform_getInodeFilename(pid_t pid, ino_t inode);
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
+
+bool Platform_getDiskIO(DiskIOData* data);
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted);
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC);
+
#endif
diff --git a/scripts/htop_suppressions.valgrind b/scripts/htop_suppressions.valgrind
new file mode 100644
index 0000000..a7afb0f
--- /dev/null
+++ b/scripts/htop_suppressions.valgrind
@@ -0,0 +1,63 @@
+{
+ <ncurses internal memory allocated at startup>
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:CRT_init
+ fun:main
+}
+
+{
+ <ncurses internal memory allocated at startup>
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:CRT_init
+}
+
+{
+ <ncurses internal memory>
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:wgetch
+ fun:ScreenManager_run
+ fun:Action_runSetup
+ fun:actionSetup
+ fun:MainPanel_eventHandler
+ fun:ScreenManager_run
+ fun:main
+}
+
+{
+ <ncurses internal memory>
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:wgetch
+ fun:ScreenManager_run
+ fun:main
+}
+
+{
+ <ncurses internal memory>
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ ...
+ fun:wrefresh
+ fun:main
+}
+
+{
+ <ncurses internal memory>
+ Memcheck:Leak
+ match-leak-kinds: reachable
+ fun:realloc
+ fun:_nc_doalloc
+ fun:_nc_tparm_analyze
+ fun:tparm
+ ...
+ fun:doupdate_sp
+ fun:wrefresh
+ obj:*
+}
diff --git a/scripts/run_valgrind.sh b/scripts/run_valgrind.sh
new file mode 100755
index 0000000..c7c17ff
--- /dev/null
+++ b/scripts/run_valgrind.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+SCRIPT=$(readlink -f "$0")
+SCRIPTDIR=$(dirname "$SCRIPT")
+
+valgrind --leak-check=full --show-reachable=yes --show-leak-kinds=all --track-fds=yes --errors-for-leak-kinds=all --suppressions="${SCRIPTDIR}/htop_suppressions.valgrind" "${SCRIPTDIR}/../htop"
diff --git a/solaris/Battery.c b/solaris/Battery.c
deleted file mode 100644
index 080cf54..0000000
--- a/solaris/Battery.c
+++ /dev/null
@@ -1,7 +0,0 @@
-
-#include "BatteryMeter.h"
-
-void Battery_getData(double* level, ACPresence* isOnAC) {
- *level = -1;
- *isOnAC = AC_ERROR;
-}
diff --git a/solaris/Battery.h b/solaris/Battery.h
deleted file mode 100644
index 21a1579..0000000
--- a/solaris/Battery.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef HEADER_Battery
-#define HEADER_Battery
-
-void Battery_getData(double* level, ACPresence* isOnAC);
-
-#endif
diff --git a/solaris/Platform.c b/solaris/Platform.c
index 436c9ce..014eaf5 100644
--- a/solaris/Platform.c
+++ b/solaris/Platform.c
@@ -3,11 +3,12 @@ htop - solaris/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
(C) 2017,2018 Guy M. Broome
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
+#include "Macros.h"
#include "Meter.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
@@ -15,6 +16,8 @@ in the source distribution for its full text.
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
+#include "DateMeter.h"
+#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "UptimeMeter.h"
#include "zfs/ZfsArcMeter.h"
@@ -81,13 +84,15 @@ const SignalItem Platform_signals[] = {
{ .name = "41 SIGINFO", .number = 41 },
};
-const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
+const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
-ProcessField Platform_defaultFields[] = { PID, LWPID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
+ProcessField Platform_defaultFields[] = { PID, LWPID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
-MeterClass* Platform_meterTypes[] = {
+const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
+ &DateMeter_class,
+ &DateTimeMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
@@ -98,28 +103,43 @@ MeterClass* Platform_meterTypes[] = {
&UptimeMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
+ &AllCPUs4Meter_class,
+ &AllCPUs8Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
+ &LeftCPUs4Meter_class,
+ &RightCPUs4Meter_class,
+ &LeftCPUs8Meter_class,
+ &RightCPUs8Meter_class,
&ZfsArcMeter_class,
&ZfsCompressedArcMeter_class,
&BlankMeter_class,
NULL
};
-void Platform_setBindings(Htop_Action* keys) {
- (void) keys;
-}
-
int Platform_numberOfFields = LAST_PROCESSFIELD;
extern char Process_pidFormat[20];
+void Platform_init(void) {
+ /* no platform-specific setup needed */
+}
+
+void Platform_done(void) {
+ /* no platform-specific cleanup needed */
+}
+
+void Platform_setBindings(Htop_Action* keys) {
+ /* no platform-specific key bindings */
+ (void) keys;
+}
+
int Platform_getUptime() {
int boot_time = 0;
int curr_time = time(NULL);
- struct utmpx * ent;
+ struct utmpx* ent;
while (( ent = getutxent() )) {
if ( !strcmp("system boot", ent->ut_line )) {
@@ -129,7 +149,7 @@ int Platform_getUptime() {
endutxent();
- return (curr_time-boot_time);
+ return (curr_time - boot_time);
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
@@ -140,31 +160,35 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
}
int Platform_getMaxPid() {
- kstat_ctl_t *kc = NULL;
- kstat_t *kshandle = NULL;
- kvar_t *ksvar = NULL;
int vproc = 32778; // Reasonable Solaris default
- kc = kstat_open();
- if (kc != NULL) { kshandle = kstat_lookup(kc,"unix",0,"var"); }
- if (kshandle != NULL) { kstat_read(kc,kshandle,NULL); }
- ksvar = kshandle->ks_data;
- if (ksvar->v_proc > 0 ) {
- vproc = ksvar->v_proc;
+
+ kstat_ctl_t* kc = kstat_open();
+ if (kc != NULL) {
+ kstat_t* kshandle = kstat_lookup(kc, "unix", 0, "var");
+ if (kshandle != NULL) {
+ kstat_read(kc, kshandle, NULL);
+
+ kvar_t* ksvar = kshandle->ks_data;
+ if (ksvar && ksvar->v_proc > 0) {
+ vproc = ksvar->v_proc;
+ }
+ }
+ kstat_close(kc);
}
- if (kc != NULL) { kstat_close(kc); }
+
return vproc;
}
double Platform_setCPUValues(Meter* this, int cpu) {
- SolarisProcessList* spl = (SolarisProcessList*) this->pl;
+ const SolarisProcessList* spl = (const SolarisProcessList*) this->pl;
int cpus = this->pl->cpuCount;
- CPUData* cpuData = NULL;
+ const CPUData* cpuData = NULL;
if (cpus == 1) {
- // single CPU box has everything in spl->cpus[0]
- cpuData = &(spl->cpus[0]);
+ // single CPU box has everything in spl->cpus[0]
+ cpuData = &(spl->cpus[0]);
} else {
- cpuData = &(spl->cpus[cpu]);
+ cpuData = &(spl->cpus[cpu]);
}
double percent;
@@ -175,24 +199,24 @@ double Platform_setCPUValues(Meter* this, int cpu) {
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
- Meter_setItems(this, 4);
- percent = v[0]+v[1]+v[2]+v[3];
+ this->curItems = 4;
+ percent = v[0] + v[1] + v[2] + v[3];
} else {
v[2] = cpuData->systemAllPercent;
- Meter_setItems(this, 3);
- percent = v[0]+v[1]+v[2];
+ this->curItems = 3;
+ percent = v[0] + v[1] + v[2];
}
- percent = CLAMP(percent, 0.0, 100.0);
- if (isnan(percent)) percent = 0.0;
+ percent = isnan(percent) ? 0.0 : CLAMP(percent, 0.0, 100.0);
- v[CPU_METER_FREQUENCY] = -1;
+ v[CPU_METER_FREQUENCY] = NAN;
+ v[CPU_METER_TEMPERATURE] = NAN;
return percent;
}
void Platform_setMemoryValues(Meter* this) {
- ProcessList* pl = (ProcessList*) this->pl;
+ const ProcessList* pl = this->pl;
this->total = pl->totalMem;
this->values[0] = pl->usedMem;
this->values[1] = pl->buffersMem;
@@ -200,32 +224,34 @@ void Platform_setMemoryValues(Meter* this) {
}
void Platform_setSwapValues(Meter* this) {
- ProcessList* pl = (ProcessList*) this->pl;
+ const ProcessList* pl = this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
}
void Platform_setZfsArcValues(Meter* this) {
- SolarisProcessList* spl = (SolarisProcessList*) this->pl;
+ const SolarisProcessList* spl = (const SolarisProcessList*) this->pl;
ZfsArcMeter_readStats(this, &(spl->zfs));
}
void Platform_setZfsCompressedArcValues(Meter* this) {
- SolarisProcessList* spl = (SolarisProcessList*) this->pl;
+ const SolarisProcessList* spl = (const SolarisProcessList*) this->pl;
ZfsCompressedArcMeter_readStats(this, &(spl->zfs));
}
-static int Platform_buildenv(void *accum, struct ps_prochandle *Phandle, uintptr_t addr, const char *str) {
- envAccum *accump = accum;
+static int Platform_buildenv(void* accum, struct ps_prochandle* Phandle, uintptr_t addr, const char* str) {
+ envAccum* accump = accum;
(void) Phandle;
(void) addr;
size_t thissz = strlen(str);
- if ((thissz + 2) > (accump->capacity - accump->size))
+ if ((thissz + 2) > (accump->capacity - accump->size)) {
accump->env = xRealloc(accump->env, accump->capacity *= 2);
- if ((thissz + 2) > (accump->capacity - accump->size))
+ }
+ if ((thissz + 2) > (accump->capacity - accump->size)) {
return 1;
+ }
strlcpy( accump->env + accump->size, str, (accump->capacity - accump->size));
strncpy( accump->env + accump->size + thissz + 1, "\n", 1);
accump->size = accump->size + thissz + 1;
@@ -236,19 +262,54 @@ char* Platform_getProcessEnv(pid_t pid) {
envAccum envBuilder;
pid_t realpid = pid / 1024;
int graberr;
- struct ps_prochandle *Phandle;
+ struct ps_prochandle* Phandle;
- if ((Phandle = Pgrab(realpid,PGRAB_RDONLY,&graberr)) == NULL)
+ if ((Phandle = Pgrab(realpid, PGRAB_RDONLY, &graberr)) == NULL) {
return "Unable to read process environment.";
+ }
envBuilder.capacity = 4096;
envBuilder.size = 0;
envBuilder.env = xMalloc(envBuilder.capacity);
- (void) Penv_iter(Phandle,Platform_buildenv,&envBuilder);
+ (void) Penv_iter(Phandle, Platform_buildenv, &envBuilder);
Prelease(Phandle, 0);
strncpy( envBuilder.env + envBuilder.size, "\0", 1);
return envBuilder.env;
}
+
+char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
+ (void)pid;
+ (void)inode;
+ return NULL;
+}
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
+ (void)pid;
+ return NULL;
+}
+
+bool Platform_getDiskIO(DiskIOData* data) {
+ // TODO
+ (void)data;
+ return false;
+}
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted) {
+ // TODO
+ *bytesReceived = 0;
+ *packetsReceived = 0;
+ *bytesTransmitted = 0;
+ *packetsTransmitted = 0;
+ return false;
+}
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC) {
+ *percent = NAN;
+ *isOnAC = AC_ERROR;
+}
diff --git a/solaris/Platform.h b/solaris/Platform.h
index dd8614d..c338115 100644
--- a/solaris/Platform.h
+++ b/solaris/Platform.h
@@ -5,17 +5,23 @@ htop - solaris/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
(C) 2017,2018 Guy M. Broome
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-#include "Action.h"
-#include "BatteryMeter.h"
-#include "SignalsPanel.h"
+#include <libproc.h>
#include <signal.h>
+#include <stdbool.h>
#include <sys/mkdev.h>
#include <sys/proc.h>
-#include <libproc.h>
+#include <sys/types.h>
+
+#include "Action.h"
+#include "BatteryMeter.h"
+#include "DiskIOMeter.h"
+#include "ProcessLocksScreen.h"
+#include "SignalsPanel.h"
+
#define kill(pid, signal) kill(pid / 1024, signal)
@@ -26,7 +32,7 @@ typedef struct envAccum_ {
size_t capacity;
size_t size;
size_t bytes;
- char *env;
+ char* env;
} envAccum;
extern double plat_loadavg[3];
@@ -37,19 +43,23 @@ extern const unsigned int Platform_numberOfSignals;
extern ProcessField Platform_defaultFields[];
-extern MeterClass* Platform_meterTypes[];
-
-void Platform_setBindings(Htop_Action* keys);
+extern const MeterClass* const Platform_meterTypes[];
extern int Platform_numberOfFields;
extern char Process_pidFormat[20];
-int Platform_getUptime();
+void Platform_init(void);
+
+void Platform_done(void);
+
+void Platform_setBindings(Htop_Action* keys);
+
+int Platform_getUptime(void);
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
-int Platform_getMaxPid();
+int Platform_getMaxPid(void);
double Platform_setCPUValues(Meter* this, int cpu);
@@ -63,4 +73,17 @@ void Platform_setZfsCompressedArcValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
+char* Platform_getInodeFilename(pid_t pid, ino_t inode);
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
+
+bool Platform_getDiskIO(DiskIOData* data);
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted);
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC);
+
#endif
diff --git a/solaris/SolarisCRT.c b/solaris/SolarisCRT.c
deleted file mode 100644
index 13e82d7..0000000
--- a/solaris/SolarisCRT.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
-htop - SolarisCRT.c
-(C) 2014 Hisham H. Muhammad
-(C) 2018 Guy M. Broome
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "config.h"
-#include "CRT.h"
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef HAVE_EXECINFO_H
-#include <execinfo.h>
-#endif
-
-void CRT_handleSIGSEGV(int sgn) {
- (void) sgn;
- CRT_done();
- fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at https://htop.dev\n");
- #ifdef HAVE_EXECINFO_H
- size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
- fprintf(stderr, "\n Please include in your report the following backtrace: \n");
- backtrace_symbols_fd(backtraceArray, size, 2);
- fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
- fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
- fprintf(stderr, "\n\n objdump -d `which htop` > ~/htop.objdump");
- fprintf(stderr, "\n\nand then attach the file ~/htop.objdump to your bug report.");
- fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
- #endif
- abort();
-}
diff --git a/solaris/SolarisCRT.h b/solaris/SolarisCRT.h
deleted file mode 100644
index 4e37b7f..0000000
--- a/solaris/SolarisCRT.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef HEADER_SolarisCRT
-#define HEADER_SolarisCRT
-/*
-htop - SolarisCRT.h
-(C) 2014 Hisham H. Muhammad
-(C) 2018 Guy M. Broome
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void CRT_handleSIGSEGV(int sgn);
-
-#endif
diff --git a/solaris/SolarisProcess.c b/solaris/SolarisProcess.c
index ab0bcab..e0a3db2 100644
--- a/solaris/SolarisProcess.c
+++ b/solaris/SolarisProcess.c
@@ -2,7 +2,7 @@
htop - SolarisProcess.c
(C) 2015 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -18,14 +18,14 @@ in the source distribution for its full text.
#include <sys/syscall.h>
-ProcessClass SolarisProcess_class = {
+const ProcessClass SolarisProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = SolarisProcess_compare
},
- .writeField = (Process_WriteField) SolarisProcess_writeField,
+ .writeField = SolarisProcess_writeField,
};
ProcessFieldData Process_fields[] = {
@@ -44,10 +44,11 @@ ProcessFieldData Process_fields[] = {
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
- [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
+ [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
+ [PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
@@ -79,11 +80,11 @@ ProcessPidColumn Process_pidColumns[] = {
{ .id = 0, .label = NULL },
};
-SolarisProcess* SolarisProcess_new(Settings* settings) {
+Process* SolarisProcess_new(const Settings* settings) {
SolarisProcess* this = xCalloc(1, sizeof(SolarisProcess));
Object_setClass(this, Class(SolarisProcess));
Process_init(&this->super, settings);
- return this;
+ return &this->super;
}
void Process_delete(Object* cast) {
@@ -93,8 +94,8 @@ void Process_delete(Object* cast) {
free(sp);
}
-void SolarisProcess_writeField(Process* this, RichString* str, ProcessField field) {
- SolarisProcess* sp = (SolarisProcess*) this;
+void SolarisProcess_writeField(const Process* this, RichString* str, ProcessField field) {
+ const SolarisProcess* sp = (const SolarisProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
@@ -105,14 +106,7 @@ void SolarisProcess_writeField(Process* this, RichString* str, ProcessField fiel
case TASKID: xSnprintf(buffer, n, Process_pidFormat, sp->taskid); break;
case POOLID: xSnprintf(buffer, n, Process_pidFormat, sp->poolid); break;
case CONTID: xSnprintf(buffer, n, Process_pidFormat, sp->contid); break;
- case ZONE:{
- xSnprintf(buffer, n, "%-*s ", ZONENAME_MAX/4, sp->zname); break;
- if (buffer[ZONENAME_MAX/4] != '\0') {
- buffer[ZONENAME_MAX/4] = ' ';
- buffer[(ZONENAME_MAX/4)+1] = '\0';
- }
- break;
- }
+ case ZONE: xSnprintf(buffer, n, "%-*s ", ZONENAME_MAX/4, sp->zname); break;
case PID: xSnprintf(buffer, n, Process_pidFormat, sp->realpid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, sp->realppid); break;
case LWPID: xSnprintf(buffer, n, Process_pidFormat, sp->lwpid); break;
@@ -124,41 +118,43 @@ void SolarisProcess_writeField(Process* this, RichString* str, ProcessField fiel
}
long SolarisProcess_compare(const void* v1, const void* v2) {
- SolarisProcess *p1, *p2;
- Settings* settings = ((Process*)v1)->settings;
+ const SolarisProcess *p1, *p2;
+ const Settings* settings = ((const Process*)v1)->settings;
+
if (settings->direction == 1) {
- p1 = (SolarisProcess*)v1;
- p2 = (SolarisProcess*)v2;
+ p1 = (const SolarisProcess*)v1;
+ p2 = (const SolarisProcess*)v2;
} else {
- p2 = (SolarisProcess*)v1;
- p1 = (SolarisProcess*)v2;
+ p2 = (const SolarisProcess*)v1;
+ p1 = (const SolarisProcess*)v2;
}
+
switch ((int) settings->sortKey) {
case ZONEID:
- return (p1->zoneid - p2->zoneid);
+ return SPACESHIP_NUMBER(p1->zoneid, p2->zoneid);
case PROJID:
- return (p1->projid - p2->projid);
+ return SPACESHIP_NUMBER(p1->projid, p2->projid);
case TASKID:
- return (p1->taskid - p2->taskid);
+ return SPACESHIP_NUMBER(p1->taskid, p2->taskid);
case POOLID:
- return (p1->poolid - p2->poolid);
+ return SPACESHIP_NUMBER(p1->poolid, p2->poolid);
case CONTID:
- return (p1->contid - p2->contid);
+ return SPACESHIP_NUMBER(p1->contid, p2->contid);
case ZONE:
return strcmp(p1->zname ? p1->zname : "global", p2->zname ? p2->zname : "global");
case PID:
- return (p1->realpid - p2->realpid);
+ return SPACESHIP_NUMBER(p1->realpid, p2->realpid);
case PPID:
- return (p1->realppid - p2->realppid);
+ return SPACESHIP_NUMBER(p1->realppid, p2->realppid);
case LWPID:
- return (p1->lwpid - p2->lwpid);
+ return SPACESHIP_NUMBER(p1->lwpid, p2->lwpid);
default:
return Process_compare(v1, v2);
}
}
-bool Process_isThread(Process* this) {
- SolarisProcess* fp = (SolarisProcess*) this;
+bool Process_isThread(const Process* this) {
+ const SolarisProcess* fp = (const SolarisProcess*) this;
if (fp->kernel == 1 ) {
return 1;
diff --git a/solaris/SolarisProcess.h b/solaris/SolarisProcess.h
index d36dea3..4756634 100644
--- a/solaris/SolarisProcess.h
+++ b/solaris/SolarisProcess.h
@@ -4,7 +4,7 @@
htop - SolarisProcess.h
(C) 2015 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -44,20 +44,20 @@ typedef struct SolarisProcess_ {
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
-extern ProcessClass SolarisProcess_class;
+extern const ProcessClass SolarisProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
-SolarisProcess* SolarisProcess_new(Settings* settings);
+Process* SolarisProcess_new(const Settings* settings);
void Process_delete(Object* cast);
-void SolarisProcess_writeField(Process* this, RichString* str, ProcessField field);
+void SolarisProcess_writeField(const Process* this, RichString* str, ProcessField field);
long SolarisProcess_compare(const void* v1, const void* v2);
-bool Process_isThread(Process* this);
+bool Process_isThread(const Process* this);
#endif
diff --git a/solaris/SolarisProcessList.c b/solaris/SolarisProcessList.c
index 4e4d282..4249fa6 100644
--- a/solaris/SolarisProcessList.c
+++ b/solaris/SolarisProcessList.c
@@ -2,7 +2,7 @@
htop - SolarisProcessList.c
(C) 2014 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -23,19 +23,24 @@ in the source distribution for its full text.
#include <math.h>
#include <time.h>
+#include "CRT.h"
+
+
#define MAXCMDLINE 255
char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) {
- char* zname;
- if ( sproc->zoneid == 0 ) {
- zname = xStrdup(GZONE);
- } else if ( kd == NULL ) {
- zname = xStrdup(UZONE);
- } else {
- kstat_t* ks = kstat_lookup( kd, "zones", sproc->zoneid, NULL );
- zname = xStrdup(ks == NULL ? UZONE : ks->ks_name);
- }
- return zname;
+ char* zname;
+
+ if ( sproc->zoneid == 0 ) {
+ zname = xStrdup(GZONE);
+ } else if ( kd == NULL ) {
+ zname = xStrdup(UZONE);
+ } else {
+ kstat_t* ks = kstat_lookup( kd, "zones", sproc->zoneid, NULL );
+ zname = xStrdup(ks == NULL ? UZONE : ks->ks_name);
+ }
+
+ return zname;
}
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, uid_t userId) {
@@ -59,46 +64,51 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) {
const SolarisProcessList* spl = (SolarisProcessList*) pl;
int cpus = pl->cpuCount;
- kstat_t *cpuinfo = NULL;
+ kstat_t* cpuinfo = NULL;
int kchain = 0;
- kstat_named_t *idletime = NULL;
- kstat_named_t *intrtime = NULL;
- kstat_named_t *krnltime = NULL;
- kstat_named_t *usertime = NULL;
+ kstat_named_t* idletime = NULL;
+ kstat_named_t* intrtime = NULL;
+ kstat_named_t* krnltime = NULL;
+ kstat_named_t* usertime = NULL;
double idlebuf = 0;
double intrbuf = 0;
double krnlbuf = 0;
double userbuf = 0;
- uint64_t totaltime = 0;
int arrskip = 0;
assert(cpus > 0);
if (cpus > 1) {
- // Store values for the stats loop one extra element up in the array
- // to leave room for the average to be calculated afterwards
- arrskip++;
+ // Store values for the stats loop one extra element up in the array
+ // to leave room for the average to be calculated afterwards
+ arrskip++;
}
// Calculate per-CPU statistics first
for (int i = 0; i < cpus; i++) {
- if (spl->kd != NULL) { cpuinfo = kstat_lookup(spl->kd,"cpu",i,"sys"); }
- if (cpuinfo != NULL) { kchain = kstat_read(spl->kd,cpuinfo,NULL); }
+ if (spl->kd != NULL) {
+ cpuinfo = kstat_lookup(spl->kd, "cpu", i, "sys");
+ }
+ if (cpuinfo != NULL) {
+ kchain = kstat_read(spl->kd, cpuinfo, NULL);
+ }
if (kchain != -1 ) {
- idletime = kstat_data_lookup(cpuinfo,"cpu_nsec_idle");
- intrtime = kstat_data_lookup(cpuinfo,"cpu_nsec_intr");
- krnltime = kstat_data_lookup(cpuinfo,"cpu_nsec_kernel");
- usertime = kstat_data_lookup(cpuinfo,"cpu_nsec_user");
+ idletime = kstat_data_lookup(cpuinfo, "cpu_nsec_idle");
+ intrtime = kstat_data_lookup(cpuinfo, "cpu_nsec_intr");
+ krnltime = kstat_data_lookup(cpuinfo, "cpu_nsec_kernel");
+ usertime = kstat_data_lookup(cpuinfo, "cpu_nsec_user");
}
assert( (idletime != NULL) && (intrtime != NULL)
&& (krnltime != NULL) && (usertime != NULL) );
- CPUData* cpuData = &(spl->cpus[i+arrskip]);
- totaltime = (idletime->value.ui64 - cpuData->lidle)
- + (intrtime->value.ui64 - cpuData->lintr)
- + (krnltime->value.ui64 - cpuData->lkrnl)
- + (usertime->value.ui64 - cpuData->luser);
+ CPUData* cpuData = &(spl->cpus[i + arrskip]);
+
+ uint64_t totaltime = (idletime->value.ui64 - cpuData->lidle)
+ + (intrtime->value.ui64 - cpuData->lintr)
+ + (krnltime->value.ui64 - cpuData->lkrnl)
+ + (usertime->value.ui64 - cpuData->luser);
+
// Calculate percentages of deltas since last reading
cpuData->userPercent = ((usertime->value.ui64 - cpuData->luser) / (double)totaltime) * 100.0;
cpuData->nicePercent = (double)0.0; // Not implemented on Solaris
@@ -133,10 +143,10 @@ static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) {
static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) {
SolarisProcessList* spl = (SolarisProcessList*) pl;
- kstat_t *meminfo = NULL;
+ static kstat_t *meminfo = NULL;
int ksrphyserr = -1;
kstat_named_t *totalmem_pgs = NULL;
- kstat_named_t *lockedmem_pgs = NULL;
+ kstat_named_t *freemem_pgs = NULL;
kstat_named_t *pages = NULL;
struct swaptable *sl = NULL;
struct swapent *swapdev = NULL;
@@ -147,32 +157,45 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) {
char *spathbase = NULL;
// Part 1 - physical memory
- if (spl->kd != NULL) { meminfo = kstat_lookup(spl->kd,"unix",0,"system_pages"); }
- if (meminfo != NULL) { ksrphyserr = kstat_read(spl->kd,meminfo,NULL); }
+ if (spl->kd != NULL && meminfo == NULL) {
+ // Look up the kstat chain just one, it never changes
+ meminfo = kstat_lookup(spl->kd, "unix", 0, "system_pages");
+ }
+ if (meminfo != NULL) {
+ ksrphyserr = kstat_read(spl->kd, meminfo, NULL);
+ }
if (ksrphyserr != -1) {
- totalmem_pgs = kstat_data_lookup( meminfo, "physmem" );
- lockedmem_pgs = kstat_data_lookup( meminfo, "pageslocked" );
- pages = kstat_data_lookup( meminfo, "pagestotal" );
+ totalmem_pgs = kstat_data_lookup(meminfo, "physmem");
+ freemem_pgs = kstat_data_lookup(meminfo, "freemem");
+ pages = kstat_data_lookup(meminfo, "pagestotal");
- pl->totalMem = totalmem_pgs->value.ui64 * PAGE_SIZE_KB;
- pl->usedMem = lockedmem_pgs->value.ui64 * PAGE_SIZE_KB;
+ pl->totalMem = totalmem_pgs->value.ui64 * CRT_pageSizeKB;
+ if (pl->totalMem > freemem_pgs->value.ui64 * CRT_pageSizeKB) {
+ pl->usedMem = pl->totalMem - freemem_pgs->value.ui64 * CRT_pageSizeKB;
+ } else {
+ pl->usedMem = 0; // This can happen in non-global zone (in theory)
+ }
// Not sure how to implement this on Solaris - suggestions welcome!
pl->cachedMem = 0;
// Not really "buffers" but the best Solaris analogue that I can find to
// "memory in use but not by programs or the kernel itself"
- pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * PAGE_SIZE_KB;
- } else {
+ pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * CRT_pageSizeKB;
+ } else {
// Fall back to basic sysconf if kstat isn't working
- pl->totalMem = sysconf(_SC_PHYS_PAGES) * PAGE_SIZE;
+ pl->totalMem = sysconf(_SC_PHYS_PAGES) * CRT_pageSize;
pl->buffersMem = 0;
pl->cachedMem = 0;
- pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * PAGE_SIZE);
+ pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * CRT_pageSize);
}
// Part 2 - swap
nswap = swapctl(SC_GETNSWP, NULL);
- if (nswap > 0) { sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); }
- if (sl != NULL) { spathbase = xMalloc( nswap * MAXPATHLEN ); }
+ if (nswap > 0) {
+ sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int));
+ }
+ if (sl != NULL) {
+ spathbase = xMalloc( nswap * MAXPATHLEN );
+ }
if (spathbase != NULL) {
spath = spathbase;
swapdev = sl->swt_ent;
@@ -192,8 +215,8 @@ static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) {
}
free(spathbase);
free(sl);
- pl->totalSwap = totalswap * PAGE_SIZE_KB;
- pl->usedSwap = pl->totalSwap - (totalfree * PAGE_SIZE_KB);
+ pl->totalSwap = totalswap * CRT_pageSizeKB;
+ pl->usedSwap = pl->totalSwap - (totalfree * CRT_pageSizeKB);
}
static inline void SolarisProcessList_scanZfsArcstats(ProcessList* pl) {
@@ -202,8 +225,12 @@ static inline void SolarisProcessList_scanZfsArcstats(ProcessList* pl) {
int ksrphyserr = -1;
kstat_named_t *cur_kstat = NULL;
- if (spl->kd != NULL) { arcstats = kstat_lookup(spl->kd,"zfs",0,"arcstats"); }
- if (arcstats != NULL) { ksrphyserr = kstat_read(spl->kd,arcstats,NULL); }
+ if (spl->kd != NULL) {
+ arcstats = kstat_lookup(spl->kd, "zfs", 0, "arcstats");
+ }
+ if (arcstats != NULL) {
+ ksrphyserr = kstat_read(spl->kd, arcstats, NULL);
+ }
if (ksrphyserr != -1) {
cur_kstat = kstat_data_lookup( arcstats, "size" );
spl->zfs.size = cur_kstat->value.ui64 / 1024;
@@ -213,19 +240,19 @@ static inline void SolarisProcessList_scanZfsArcstats(ProcessList* pl) {
spl->zfs.max = cur_kstat->value.ui64 / 1024;
cur_kstat = kstat_data_lookup( arcstats, "mfu_size" );
- spl->zfs.MFU = cur_kstat->value.ui64 / 1024;
+ spl->zfs.MFU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;
cur_kstat = kstat_data_lookup( arcstats, "mru_size" );
- spl->zfs.MRU = cur_kstat->value.ui64 / 1024;
+ spl->zfs.MRU = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;
cur_kstat = kstat_data_lookup( arcstats, "anon_size" );
- spl->zfs.anon = cur_kstat->value.ui64 / 1024;
+ spl->zfs.anon = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;
cur_kstat = kstat_data_lookup( arcstats, "hdr_size" );
- spl->zfs.header = cur_kstat->value.ui64 / 1024;
+ spl->zfs.header = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;
cur_kstat = kstat_data_lookup( arcstats, "other_size" );
- spl->zfs.other = cur_kstat->value.ui64 / 1024;
+ spl->zfs.other = cur_kstat != NULL ? cur_kstat->value.ui64 / 1024 : 0;
if ((cur_kstat = kstat_data_lookup( arcstats, "compressed_size" )) != NULL) {
spl->zfs.compressed = cur_kstat->value.ui64 / 1024;
@@ -243,7 +270,9 @@ void ProcessList_delete(ProcessList* pl) {
SolarisProcessList* spl = (SolarisProcessList*) pl;
ProcessList_done(pl);
free(spl->cpus);
- if (spl->kd) kstat_close(spl->kd);
+ if (spl->kd) {
+ kstat_close(spl->kd);
+ }
free(spl);
}
@@ -253,18 +282,19 @@ void ProcessList_delete(ProcessList* pl) {
* system for more info.
*/
-int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr) {
- struct timeval tv;
- struct tm date;
+int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, void* listptr) {
bool preExisting;
pid_t getpid;
// Setup process list
- ProcessList *pl = (ProcessList*) listptr;
- SolarisProcessList *spl = (SolarisProcessList*) listptr;
+ ProcessList* pl = (ProcessList*) listptr;
+ SolarisProcessList* spl = (SolarisProcessList*) listptr;
id_t lwpid_real = _lwpsinfo->pr_lwpid;
- if (lwpid_real > 1023) return 0;
+ if (lwpid_real > 1023) {
+ return 0;
+ }
+
pid_t lwpid = (_psinfo->pr_pid * 1024) + lwpid_real;
bool onMasterLWP = (_lwpsinfo->pr_lwpid == _psinfo->pr_lwp.pr_lwpid);
if (onMasterLWP) {
@@ -272,10 +302,8 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *
} else {
getpid = lwpid;
}
- Process *proc = ProcessList_getProcess(pl, getpid, &preExisting, (Process_New) SolarisProcess_new);
- SolarisProcess *sproc = (SolarisProcess*) proc;
-
- gettimeofday(&tv, NULL);
+ Process* proc = ProcessList_getProcess(pl, getpid, &preExisting, SolarisProcess_new);
+ SolarisProcess* sproc = (SolarisProcess*) proc;
// Common code pass 1
proc->show = false;
@@ -290,22 +318,22 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *
// NOTE: This 'percentage' is a 16-bit BINARY FRACTIONS where 1.0 = 0x8000
// Source: https://docs.oracle.com/cd/E19253-01/816-5174/proc-4/index.html
// (accessed on 18 November 2017)
- proc->percent_mem = ((uint16_t)_psinfo->pr_pctmem/(double)32768)*(double)100.0;
+ proc->percent_mem = ((uint16_t)_psinfo->pr_pctmem / (double)32768) * (double)100.0;
proc->st_uid = _psinfo->pr_euid;
proc->pgrp = _psinfo->pr_pgid;
proc->nlwp = _psinfo->pr_nlwp;
proc->tty_nr = _psinfo->pr_ttydev;
- proc->m_resident = _psinfo->pr_rssize/PAGE_SIZE_KB;
- proc->m_size = _psinfo->pr_size/PAGE_SIZE_KB;
+ proc->m_resident = _psinfo->pr_rssize / CRT_pageSizeKB;
+ proc->m_virt = _psinfo->pr_size / CRT_pageSizeKB;
if (!preExisting) {
sproc->realpid = _psinfo->pr_pid;
sproc->lwpid = lwpid_real;
sproc->zoneid = _psinfo->pr_zoneid;
- sproc->zname = SolarisProcessList_readZoneName(spl->kd,sproc);
+ sproc->zname = SolarisProcessList_readZoneName(spl->kd, sproc);
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
proc->comm = xStrdup(_psinfo->pr_fname);
- proc->commLen = strnlen(_psinfo->pr_fname,PRFNSZ);
+ proc->commLen = strnlen(_psinfo->pr_fname, PRFNSZ);
}
// End common code pass 1
@@ -315,9 +343,9 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *
proc->tgid = (_psinfo->pr_ppid * 1024);
sproc->realppid = _psinfo->pr_ppid;
// See note above (in common section) about this BINARY FRACTION
- proc->percent_cpu = ((uint16_t)_psinfo->pr_pctcpu/(double)32768)*(double)100.0;
+ proc->percent_cpu = ((uint16_t)_psinfo->pr_pctcpu / (double)32768) * (double)100.0;
proc->time = _psinfo->pr_time.tv_sec;
- if(!preExisting) { // Tasks done only for NEW processes
+ if (!preExisting) { // Tasks done only for NEW processes
sproc->is_lwp = false;
proc->starttime_ctime = _psinfo->pr_start.tv_sec;
}
@@ -325,20 +353,24 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *
// Update proc and thread counts based on settings
if (sproc->kernel && !pl->settings->hideKernelThreads) {
pl->kernelThreads += proc->nlwp;
- pl->totalTasks += proc->nlwp+1;
- if (proc->state == 'O') pl->runningTasks++;
+ pl->totalTasks += proc->nlwp + 1;
+ if (proc->state == 'O') {
+ pl->runningTasks++;
+ }
} else if (!sproc->kernel) {
- if (proc->state == 'O') pl->runningTasks++;
+ if (proc->state == 'O') {
+ pl->runningTasks++;
+ }
if (pl->settings->hideUserlandThreads) {
pl->totalTasks++;
} else {
pl->userlandThreads += proc->nlwp;
- pl->totalTasks += proc->nlwp+1;
+ pl->totalTasks += proc->nlwp + 1;
}
}
proc->show = !(pl->settings->hideKernelThreads && sproc->kernel);
} else { // We are not in the master LWP, so jump to the LWP handling code
- proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu/(double)32768)*(double)100.0;
+ proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu / (double)32768) * (double)100.0;
proc->time = _lwpsinfo->pr_time.tv_sec;
if (!preExisting) { // Tasks done only for NEW LWPs
sproc->is_lwp = true;
@@ -350,8 +382,12 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *
}
// Top-level process only gets this for the representative LWP
- if (sproc->kernel && !pl->settings->hideKernelThreads) proc->show = true;
- if (!sproc->kernel && !pl->settings->hideUserlandThreads) proc->show = true;
+ if (sproc->kernel && !pl->settings->hideKernelThreads) {
+ proc->show = true;
+ }
+ if (!sproc->kernel && !pl->settings->hideUserlandThreads) {
+ proc->show = true;
+ }
} // Top-level LWP or subordinate LWP
// Common code pass 2
@@ -362,8 +398,7 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *
} else {
sproc->kernel = false;
}
- (void) localtime_r((time_t*) &proc->starttime_ctime, &date);
- strftime(proc->starttime_show, 7, ((proc->starttime_ctime > tv.tv_sec - 86400) ? "%R " : "%b%d "), &date);
+ Process_fillStarttimeBuffer(proc);
ProcessList_add(pl, proc);
}
proc->updated = true;
@@ -373,10 +408,16 @@ int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *
return 0;
}
-void ProcessList_goThroughEntries(ProcessList* this) {
- SolarisProcessList_scanCPUTime(this);
- SolarisProcessList_scanMemoryInfo(this);
- SolarisProcessList_scanZfsArcstats(this);
- this->kernelThreads = 1;
- proc_walk(&SolarisProcessList_walkproc, this, PR_WALK_LWP);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
+ SolarisProcessList_scanCPUTime(super);
+ SolarisProcessList_scanMemoryInfo(super);
+ SolarisProcessList_scanZfsArcstats(super);
+
+ // in pause mode only gather global data for meters (CPU/memory/...)
+ if (pauseProcessUpdate) {
+ return;
+ }
+
+ super->kernelThreads = 1;
+ proc_walk(&SolarisProcessList_walkproc, super, PR_WALK_LWP);
}
diff --git a/solaris/SolarisProcessList.h b/solaris/SolarisProcessList.h
index 9da39b4..f800d9d 100644
--- a/solaris/SolarisProcessList.h
+++ b/solaris/SolarisProcessList.h
@@ -4,14 +4,14 @@
htop - SolarisProcessList.h
(C) 2014 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#define MAXCMDLINE 255
-#define GZONE "global "
-#define UZONE "unknown "
+#define GZONE "global "
+#define UZONE "unknown "
#include "zfs/ZfsArcStats.h"
@@ -58,8 +58,8 @@ void ProcessList_delete(ProcessList* pl);
* system for more info.
*/
-int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr);
+int SolarisProcessList_walkproc(psinfo_t* _psinfo, lwpsinfo_t* _lwpsinfo, void* listptr);
-void ProcessList_goThroughEntries(ProcessList* this);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif
diff --git a/unsupported/Battery.c b/unsupported/Battery.c
deleted file mode 100644
index 080cf54..0000000
--- a/unsupported/Battery.c
+++ /dev/null
@@ -1,7 +0,0 @@
-
-#include "BatteryMeter.h"
-
-void Battery_getData(double* level, ACPresence* isOnAC) {
- *level = -1;
- *isOnAC = AC_ERROR;
-}
diff --git a/unsupported/Battery.h b/unsupported/Battery.h
deleted file mode 100644
index 21a1579..0000000
--- a/unsupported/Battery.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef HEADER_Battery
-#define HEADER_Battery
-
-void Battery_getData(double* level, ACPresence* isOnAC);
-
-#endif
diff --git a/unsupported/Platform.c b/unsupported/Platform.c
index 420dfb7..54fde50 100644
--- a/unsupported/Platform.c
+++ b/unsupported/Platform.c
@@ -2,17 +2,22 @@
htop - unsupported/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
+#include <math.h>
+
#include "Platform.h"
+#include "Macros.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
+#include "DateMeter.h"
+#include "DateTimeMeter.h"
#include "HostnameMeter.h"
#include "UptimeMeter.h"
@@ -21,9 +26,9 @@ const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
};
-const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
+const unsigned int Platform_numberOfSignals = ARRAYSIZE(Platform_signals);
-ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
+ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_VIRT, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
@@ -42,7 +47,7 @@ ProcessFieldData Process_fields[] = {
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
- [M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
+ [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
@@ -54,9 +59,11 @@ ProcessFieldData Process_fields[] = {
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
-MeterClass* Platform_meterTypes[] = {
+const MeterClass* const Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
+ &DateMeter_class,
+ &DateTimeMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
@@ -67,26 +74,39 @@ MeterClass* Platform_meterTypes[] = {
&UptimeMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
+ &AllCPUs4Meter_class,
+ &AllCPUs8Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
+ &LeftCPUs4Meter_class,
+ &RightCPUs4Meter_class,
+ &LeftCPUs8Meter_class,
+ &RightCPUs8Meter_class,
&BlankMeter_class,
NULL
};
-void Platform_setBindings(Htop_Action* keys) {
- (void) keys;
-}
-
int Platform_numberOfFields = 100;
-extern char Process_pidFormat[20];
-
ProcessPidColumn Process_pidColumns[] = {
{ .id = 0, .label = NULL },
};
+void Platform_init(void) {
+ /* no platform-specific setup needed */
+}
+
+void Platform_done(void) {
+ /* no platform-specific cleanup needed */
+}
+
+void Platform_setBindings(Htop_Action* keys) {
+ /* no platform-specific key bindings */
+ (void) keys;
+}
+
int Platform_getUptime() {
return 0;
}
@@ -105,7 +125,8 @@ double Platform_setCPUValues(Meter* this, int cpu) {
(void) cpu;
double* v = this->values;
- v[CPU_METER_FREQUENCY] = -1;
+ v[CPU_METER_FREQUENCY] = NAN;
+ v[CPU_METER_TEMPERATURE] = NAN;
return 0.0;
}
@@ -118,7 +139,7 @@ void Platform_setSwapValues(Meter* this) {
(void) this;
}
-bool Process_isThread(Process* this) {
+bool Process_isThread(const Process* this) {
(void) this;
return false;
}
@@ -127,3 +148,35 @@ char* Platform_getProcessEnv(pid_t pid) {
(void) pid;
return NULL;
}
+
+char* Platform_getInodeFilename(pid_t pid, ino_t inode) {
+ (void)pid;
+ (void)inode;
+ return NULL;
+}
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) {
+ (void)pid;
+ return NULL;
+}
+
+bool Platform_getDiskIO(DiskIOData* data) {
+ (void)data;
+ return false;
+}
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted) {
+ *bytesReceived = 0;
+ *packetsReceived = 0;
+ *bytesTransmitted = 0;
+ *packetsTransmitted = 0;
+ return false;
+}
+
+void Platform_getBattery(double* percent, ACPresence* isOnAC) {
+ *percent = NAN;
+ *isOnAC = AC_ERROR;
+}
diff --git a/unsupported/Platform.h b/unsupported/Platform.h
index 2a3d768..d3f5d72 100644
--- a/unsupported/Platform.h
+++ b/unsupported/Platform.h
@@ -4,12 +4,14 @@
htop - unsupported/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "Action.h"
#include "BatteryMeter.h"
+#include "DiskIOMeter.h"
+#include "ProcessLocksScreen.h"
#include "SignalsPanel.h"
#include "UnsupportedProcess.h"
@@ -21,9 +23,7 @@ extern ProcessField Platform_defaultFields[];
extern ProcessFieldData Process_fields[];
-extern MeterClass* Platform_meterTypes[];
-
-void Platform_setBindings(Htop_Action* keys);
+extern const MeterClass* const Platform_meterTypes[];
extern int Platform_numberOfFields;
@@ -31,11 +31,17 @@ extern char Process_pidFormat[20];
extern ProcessPidColumn Process_pidColumns[];
-int Platform_getUptime();
+void Platform_init(void);
+
+void Platform_done(void);
+
+void Platform_setBindings(Htop_Action* keys);
+
+int Platform_getUptime(void);
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
-int Platform_getMaxPid();
+int Platform_getMaxPid(void);
double Platform_setCPUValues(Meter* this, int cpu);
@@ -43,8 +49,21 @@ void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
-bool Process_isThread(Process* this);
+bool Process_isThread(const Process* this);
char* Platform_getProcessEnv(pid_t pid);
+char* Platform_getInodeFilename(pid_t pid, ino_t inode);
+
+FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid);
+
+bool Platform_getDiskIO(DiskIOData* data);
+
+bool Platform_getNetworkIO(unsigned long int* bytesReceived,
+ unsigned long int* packetsReceived,
+ unsigned long int* bytesTransmitted,
+ unsigned long int* packetsTransmitted);
+
+void Platform_getBattery(double *percent, ACPresence *isOnAC);
+
#endif
diff --git a/unsupported/UnsupportedCRT.c b/unsupported/UnsupportedCRT.c
deleted file mode 100644
index 49cc5d0..0000000
--- a/unsupported/UnsupportedCRT.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-htop - UnsupportedCRT.c
-(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-#include "config.h"
-#include "CRT.h"
-#include <stdio.h>
-#include <stdlib.h>
-
-void CRT_handleSIGSEGV(int sgn) {
- (void) sgn;
- CRT_done();
- fprintf(stderr, "\n\nhtop " VERSION " aborting.\n");
- fprintf(stderr, "\nUnfortunately, you seem to be using an unsupported platform!");
- fprintf(stderr, "\nPlease contact your platform package maintainer!\n\n");
- abort();
-}
diff --git a/unsupported/UnsupportedCRT.h b/unsupported/UnsupportedCRT.h
deleted file mode 100644
index 24d63e5..0000000
--- a/unsupported/UnsupportedCRT.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef HEADER_UnsupportedCRT
-#define HEADER_UnsupportedCRT
-/*
-htop - UnsupportedCRT.h
-(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
-in the source distribution for its full text.
-*/
-
-void CRT_handleSIGSEGV(int sgn);
-
-#endif
diff --git a/unsupported/UnsupportedProcess.c b/unsupported/UnsupportedProcess.c
index 63e26ad..0827c60 100644
--- a/unsupported/UnsupportedProcess.c
+++ b/unsupported/UnsupportedProcess.c
@@ -1,7 +1,7 @@
/*
htop - UnsupportedProcess.c
(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
diff --git a/unsupported/UnsupportedProcess.h b/unsupported/UnsupportedProcess.h
index dbe9bf5..11335cd 100644
--- a/unsupported/UnsupportedProcess.h
+++ b/unsupported/UnsupportedProcess.h
@@ -3,7 +3,7 @@
/*
htop - UnsupportedProcess.h
(C) 2015 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
diff --git a/unsupported/UnsupportedProcessList.c b/unsupported/UnsupportedProcessList.c
index fc8a54d..098eb48 100644
--- a/unsupported/UnsupportedProcessList.c
+++ b/unsupported/UnsupportedProcessList.c
@@ -1,7 +1,7 @@
/*
htop - UnsupportedProcessList.c
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -24,44 +24,50 @@ void ProcessList_delete(ProcessList* this) {
free(this);
}
-void ProcessList_goThroughEntries(ProcessList* super) {
- bool preExisting = true;
- Process *proc;
-
- proc = ProcessList_getProcess(super, 1, &preExisting, UnsupportedProcess_new);
-
- /* Empty values */
- proc->time = proc->time + 10;
- proc->pid = 1;
- proc->ppid = 1;
- proc->tgid = 0;
- proc->comm = "<unsupported architecture>";
- proc->basenameOffset = 0;
- proc->updated = true;
-
- proc->state = 'R';
- proc->show = true; /* Reflected in proc->settings-> "hideXXX" really */
- proc->pgrp = 0;
- proc->session = 0;
- proc->tty_nr = 0;
- proc->tpgid = 0;
- proc->st_uid = 0;
- proc->flags = 0;
- proc->processor = 0;
-
- proc->percent_cpu = 2.5;
- proc->percent_mem = 2.5;
- proc->user = "nobody";
-
- proc->priority = 0;
- proc->nice = 0;
- proc->nlwp = 1;
- strncpy(proc->starttime_show, "Jun 01 ", sizeof(proc->starttime_show));
- proc->starttime_ctime = 1433116800; // Jun 01, 2015
-
- proc->m_size = 100;
- proc->m_resident = 100;
-
- proc->minflt = 20;
- proc->majflt = 20;
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) {
+
+ // in pause mode only gather global data for meters (CPU/memory/...)
+ if (pauseProcessUpdate) {
+ return;
+ }
+
+ bool preExisting = true;
+ Process* proc;
+
+ proc = ProcessList_getProcess(super, 1, &preExisting, UnsupportedProcess_new);
+
+ /* Empty values */
+ proc->time = proc->time + 10;
+ proc->pid = 1;
+ proc->ppid = 1;
+ proc->tgid = 0;
+ proc->comm = "<unsupported architecture>";
+ proc->basenameOffset = 0;
+ proc->updated = true;
+
+ proc->state = 'R';
+ proc->show = true; /* Reflected in proc->settings-> "hideXXX" really */
+ proc->pgrp = 0;
+ proc->session = 0;
+ proc->tty_nr = 0;
+ proc->tpgid = 0;
+ proc->st_uid = 0;
+ proc->flags = 0;
+ proc->processor = 0;
+
+ proc->percent_cpu = 2.5;
+ proc->percent_mem = 2.5;
+ proc->user = "nobody";
+
+ proc->priority = 0;
+ proc->nice = 0;
+ proc->nlwp = 1;
+ proc->starttime_ctime = 1433116800; // Jun 01, 2015
+ Process_fillStarttimeBuffer(proc);
+
+ proc->m_virt = 100;
+ proc->m_resident = 100;
+
+ proc->minflt = 20;
+ proc->majflt = 20;
}
diff --git a/unsupported/UnsupportedProcessList.h b/unsupported/UnsupportedProcessList.h
index c4b51ac..1c53771 100644
--- a/unsupported/UnsupportedProcessList.h
+++ b/unsupported/UnsupportedProcessList.h
@@ -3,7 +3,7 @@
/*
htop - UnsupportedProcessList.h
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -11,6 +11,6 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidMatchList, ui
void ProcessList_delete(ProcessList* this);
-void ProcessList_goThroughEntries(ProcessList* super);
+void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate);
#endif
diff --git a/zfs/ZfsArcMeter.c b/zfs/ZfsArcMeter.c
index 8bd0f5d..e844d77 100644
--- a/zfs/ZfsArcMeter.c
+++ b/zfs/ZfsArcMeter.c
@@ -1,7 +1,7 @@
/*
htop - ZfsArcMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -9,25 +9,16 @@ in the source distribution for its full text.
#include "ZfsArcStats.h"
#include "CRT.h"
+#include "Object.h"
#include "Platform.h"
+#include "RichString.h"
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <sys/param.h>
-#include <assert.h>
-/*{
-#include "ZfsArcStats.h"
-
-#include "Meter.h"
-}*/
-
-int ZfsArcMeter_attributes[] = {
+static const int ZfsArcMeter_attributes[] = {
ZFS_MFU, ZFS_MRU, ZFS_ANON, ZFS_HEADER, ZFS_OTHER
};
-void ZfsArcMeter_readStats(Meter* this, ZfsArcStats* stats) {
+void ZfsArcMeter_readStats(Meter* this, const ZfsArcStats* stats) {
this->total = stats->max;
this->values[0] = stats->MFU;
this->values[1] = stats->MRU;
@@ -38,28 +29,27 @@ void ZfsArcMeter_readStats(Meter* this, ZfsArcStats* stats) {
// "Hide" the last value so it can
// only be accessed by index and is not
// displayed by the Bar or Graph style
- Meter_setItems(this, 5);
+ this->curItems = 5;
this->values[5] = stats->size;
}
-static void ZfsArcMeter_updateValues(Meter* this, char* buffer, int size) {
+static void ZfsArcMeter_updateValues(Meter* this, char* buffer, size_t size) {
int written;
Platform_setZfsArcValues(this);
written = Meter_humanUnit(buffer, this->values[5], size);
- buffer += written;
- if ((size -= written) > 0) {
- *buffer++ = '/';
- size--;
- Meter_humanUnit(buffer, this->total, size);
- }
+ METER_BUFFER_CHECK(buffer, size, written);
+
+ METER_BUFFER_APPEND_CHR(buffer, size, '/');
+
+ Meter_humanUnit(buffer, this->total, size);
}
-static void ZfsArcMeter_display(Object* cast, RichString* out) {
- char buffer[50];
- Meter* this = (Meter*)cast;
+static void ZfsArcMeter_display(const Object* cast, RichString* out) {
+ const Meter* this = (const Meter*)cast;
if (this->values[5] > 0) {
+ char buffer[50];
Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
Meter_humanUnit(buffer, this->values[5], 50);
@@ -86,7 +76,7 @@ static void ZfsArcMeter_display(Object* cast, RichString* out) {
}
}
-MeterClass ZfsArcMeter_class = {
+const MeterClass ZfsArcMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
diff --git a/zfs/ZfsArcMeter.h b/zfs/ZfsArcMeter.h
index e6b59d5..52bf784 100644
--- a/zfs/ZfsArcMeter.h
+++ b/zfs/ZfsArcMeter.h
@@ -3,7 +3,7 @@
/*
htop - ZfsArcMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -11,10 +11,8 @@ in the source distribution for its full text.
#include "Meter.h"
-extern int ZfsArcMeter_attributes[];
+void ZfsArcMeter_readStats(Meter* this, const ZfsArcStats* stats);
-void ZfsArcMeter_readStats(Meter* this, ZfsArcStats* stats);
-
-extern MeterClass ZfsArcMeter_class;
+extern const MeterClass ZfsArcMeter_class;
#endif
diff --git a/zfs/ZfsArcStats.c b/zfs/ZfsArcStats.c
index bfed07d..bead846 100644
--- a/zfs/ZfsArcStats.c
+++ b/zfs/ZfsArcStats.c
@@ -1,24 +1,10 @@
/*
htop - ZfsArcStats.c
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
-/*{
-typedef struct ZfsArcStats_ {
- int enabled;
- int isCompressed;
- unsigned long long int max;
- unsigned long long int size;
- unsigned long long int MFU;
- unsigned long long int MRU;
- unsigned long long int anon;
- unsigned long long int header;
- unsigned long long int other;
- unsigned long long int compressed;
- unsigned long long int uncompressed;
-} ZfsArcStats;
-}*/
+#include "Macros.h"
-static int make_iso_compilers_happy __attribute__((unused));
+static int make_iso_compilers_happy ATTR_UNUSED;
diff --git a/zfs/ZfsArcStats.h b/zfs/ZfsArcStats.h
index 087f3fc..d891dc2 100644
--- a/zfs/ZfsArcStats.h
+++ b/zfs/ZfsArcStats.h
@@ -3,7 +3,7 @@
/*
htop - ZfsArcStats.h
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
diff --git a/zfs/ZfsCompressedArcMeter.c b/zfs/ZfsCompressedArcMeter.c
index ac3944d..8766f80 100644
--- a/zfs/ZfsCompressedArcMeter.c
+++ b/zfs/ZfsCompressedArcMeter.c
@@ -1,33 +1,27 @@
/*
htop - ZfsCompressedArcMeter.c
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "ZfsCompressedArcMeter.h"
-#include "ZfsArcStats.h"
#include "CRT.h"
+#include "Meter.h"
+#include "Object.h"
#include "Platform.h"
+#include "RichString.h"
+#include "XUtils.h"
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <sys/param.h>
-#include <assert.h>
-
-/*{
-#include "ZfsArcStats.h"
+#include "zfs/ZfsArcStats.h"
-#include "Meter.h"
-}*/
-int ZfsCompressedArcMeter_attributes[] = {
+static const int ZfsCompressedArcMeter_attributes[] = {
ZFS_COMPRESSED
};
-void ZfsCompressedArcMeter_readStats(Meter* this, ZfsArcStats* stats) {
+void ZfsCompressedArcMeter_readStats(Meter* this, const ZfsArcStats* stats) {
if ( stats->isCompressed ) {
this->total = stats->uncompressed;
this->values[0] = stats->compressed;
@@ -38,21 +32,21 @@ void ZfsCompressedArcMeter_readStats(Meter* this, ZfsArcStats* stats) {
}
}
-static void ZfsCompressedArcMeter_printRatioString(Meter* this, char* buffer, int size) {
- xSnprintf(buffer, size, "%.2f:1", this->total/this->values[0]);
+static void ZfsCompressedArcMeter_printRatioString(const Meter* this, char* buffer, size_t size) {
+ xSnprintf(buffer, size, "%.2f:1", this->total / this->values[0]);
}
-static void ZfsCompressedArcMeter_updateValues(Meter* this, char* buffer, int size) {
+static void ZfsCompressedArcMeter_updateValues(Meter* this, char* buffer, size_t size) {
Platform_setZfsCompressedArcValues(this);
ZfsCompressedArcMeter_printRatioString(this, buffer, size);
}
-static void ZfsCompressedArcMeter_display(Object* cast, RichString* out) {
- char buffer[50];
- Meter* this = (Meter*)cast;
+static void ZfsCompressedArcMeter_display(const Object* cast, RichString* out) {
+ const Meter* this = (const Meter*)cast;
if (this->values[0] > 0) {
+ char buffer[50];
Meter_humanUnit(buffer, this->total, 50);
RichString_append(out, CRT_colors[METER_VALUE], buffer);
RichString_append(out, CRT_colors[METER_TEXT], " Uncompressed, ");
@@ -68,7 +62,7 @@ static void ZfsCompressedArcMeter_display(Object* cast, RichString* out) {
}
}
-MeterClass ZfsCompressedArcMeter_class = {
+const MeterClass ZfsCompressedArcMeter_class = {
.super = {
.extends = Class(Meter),
.delete = Meter_delete,
diff --git a/zfs/ZfsCompressedArcMeter.h b/zfs/ZfsCompressedArcMeter.h
index 8e64493..025a9dd 100644
--- a/zfs/ZfsCompressedArcMeter.h
+++ b/zfs/ZfsCompressedArcMeter.h
@@ -3,7 +3,7 @@
/*
htop - ZfsCompressedArcMeter.h
(C) 2004-2011 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
@@ -11,10 +11,8 @@ in the source distribution for its full text.
#include "Meter.h"
-extern int ZfsCompressedArcMeter_attributes[];
+void ZfsCompressedArcMeter_readStats(Meter* this, const ZfsArcStats* stats);
-void ZfsCompressedArcMeter_readStats(Meter* this, ZfsArcStats* stats);
-
-extern MeterClass ZfsCompressedArcMeter_class;
+extern const MeterClass ZfsCompressedArcMeter_class;
#endif
diff --git a/zfs/openzfs_sysctl.c b/zfs/openzfs_sysctl.c
index c1ab223..fd00d61 100644
--- a/zfs/openzfs_sysctl.c
+++ b/zfs/openzfs_sysctl.c
@@ -1,17 +1,18 @@
/*
htop - zfs/openzfs_sysctl.c
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "zfs/openzfs_sysctl.h"
-#include "zfs/ZfsArcStats.h"
-#include <unistd.h>
#include <stdlib.h>
-#include <sys/types.h>
-#include <sys/sysctl.h>
+#include <sys/types.h> // IWYU pragma: keep
+#include <sys/sysctl.h> // needs <sys/types.h> for u_int with gcc
+
+#include "zfs/ZfsArcStats.h"
+
static int MIB_kstat_zfs_misc_arcstats_size[5];
static int MIB_kstat_zfs_misc_arcstats_c_max[5];
@@ -23,76 +24,74 @@ static int MIB_kstat_zfs_misc_arcstats_other_size[5];
static int MIB_kstat_zfs_misc_arcstats_compressed_size[5];
static int MIB_kstat_zfs_misc_arcstats_uncompressed_size[5];
-/*{
-#include "zfs/ZfsArcStats.h"
-}*/
-
-void openzfs_sysctl_init(ZfsArcStats *stats) {
+void openzfs_sysctl_init(ZfsArcStats* stats) {
size_t len;
unsigned long long int arcSize;
len = sizeof(arcSize);
- if (sysctlbyname("kstat.zfs.misc.arcstats.size", &arcSize, &len,
- NULL, 0) == 0 && arcSize != 0) {
- stats->enabled = 1;
- len = 5; sysctlnametomib("kstat.zfs.misc.arcstats.size", MIB_kstat_zfs_misc_arcstats_size, &len);
-
- sysctlnametomib("kstat.zfs.misc.arcstats.c_max", MIB_kstat_zfs_misc_arcstats_c_max, &len);
- sysctlnametomib("kstat.zfs.misc.arcstats.mfu_size", MIB_kstat_zfs_misc_arcstats_mfu_size, &len);
- sysctlnametomib("kstat.zfs.misc.arcstats.mru_size", MIB_kstat_zfs_misc_arcstats_mru_size, &len);
- sysctlnametomib("kstat.zfs.misc.arcstats.anon_size", MIB_kstat_zfs_misc_arcstats_anon_size, &len);
- sysctlnametomib("kstat.zfs.misc.arcstats.hdr_size", MIB_kstat_zfs_misc_arcstats_hdr_size, &len);
- sysctlnametomib("kstat.zfs.misc.arcstats.other_size", MIB_kstat_zfs_misc_arcstats_other_size, &len);
- if (sysctlnametomib("kstat.zfs.misc.arcstats.compressed_size", MIB_kstat_zfs_misc_arcstats_compressed_size, &len) == 0) {
- stats->isCompressed = 1;
- sysctlnametomib("kstat.zfs.misc.arcstats.uncompressed_size", MIB_kstat_zfs_misc_arcstats_uncompressed_size, &len);
- } else {
- stats->isCompressed = 0;
- }
+ if (sysctlbyname("kstat.zfs.misc.arcstats.size", &arcSize, &len, NULL, 0) == 0 && arcSize != 0) {
+ stats->enabled = 1;
+
+ len = 5;
+ sysctlnametomib("kstat.zfs.misc.arcstats.size", MIB_kstat_zfs_misc_arcstats_size, &len);
+
+ sysctlnametomib("kstat.zfs.misc.arcstats.c_max", MIB_kstat_zfs_misc_arcstats_c_max, &len);
+ sysctlnametomib("kstat.zfs.misc.arcstats.mfu_size", MIB_kstat_zfs_misc_arcstats_mfu_size, &len);
+ sysctlnametomib("kstat.zfs.misc.arcstats.mru_size", MIB_kstat_zfs_misc_arcstats_mru_size, &len);
+ sysctlnametomib("kstat.zfs.misc.arcstats.anon_size", MIB_kstat_zfs_misc_arcstats_anon_size, &len);
+ sysctlnametomib("kstat.zfs.misc.arcstats.hdr_size", MIB_kstat_zfs_misc_arcstats_hdr_size, &len);
+ sysctlnametomib("kstat.zfs.misc.arcstats.other_size", MIB_kstat_zfs_misc_arcstats_other_size, &len);
+
+ if (sysctlnametomib("kstat.zfs.misc.arcstats.compressed_size", MIB_kstat_zfs_misc_arcstats_compressed_size, &len) == 0) {
+ stats->isCompressed = 1;
+ sysctlnametomib("kstat.zfs.misc.arcstats.uncompressed_size", MIB_kstat_zfs_misc_arcstats_uncompressed_size, &len);
+ } else {
+ stats->isCompressed = 0;
+ }
} else {
stats->enabled = 0;
}
}
-void openzfs_sysctl_updateArcStats(ZfsArcStats *stats) {
+void openzfs_sysctl_updateArcStats(ZfsArcStats* stats) {
size_t len;
if (stats->enabled) {
len = sizeof(stats->size);
- sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(stats->size), &len , NULL, 0);
+ sysctl(MIB_kstat_zfs_misc_arcstats_size, 5, &(stats->size), &len, NULL, 0);
stats->size /= 1024;
len = sizeof(stats->max);
- sysctl(MIB_kstat_zfs_misc_arcstats_c_max, 5, &(stats->max), &len , NULL, 0);
+ sysctl(MIB_kstat_zfs_misc_arcstats_c_max, 5, &(stats->max), &len, NULL, 0);
stats->max /= 1024;
len = sizeof(stats->MFU);
- sysctl(MIB_kstat_zfs_misc_arcstats_mfu_size, 5, &(stats->MFU), &len , NULL, 0);
+ sysctl(MIB_kstat_zfs_misc_arcstats_mfu_size, 5, &(stats->MFU), &len, NULL, 0);
stats->MFU /= 1024;
len = sizeof(stats->MRU);
- sysctl(MIB_kstat_zfs_misc_arcstats_mru_size, 5, &(stats->MRU), &len , NULL, 0);
+ sysctl(MIB_kstat_zfs_misc_arcstats_mru_size, 5, &(stats->MRU), &len, NULL, 0);
stats->MRU /= 1024;
len = sizeof(stats->anon);
- sysctl(MIB_kstat_zfs_misc_arcstats_anon_size, 5, &(stats->anon), &len , NULL, 0);
+ sysctl(MIB_kstat_zfs_misc_arcstats_anon_size, 5, &(stats->anon), &len, NULL, 0);
stats->anon /= 1024;
len = sizeof(stats->header);
- sysctl(MIB_kstat_zfs_misc_arcstats_hdr_size, 5, &(stats->header), &len , NULL, 0);
+ sysctl(MIB_kstat_zfs_misc_arcstats_hdr_size, 5, &(stats->header), &len, NULL, 0);
stats->header /= 1024;
len = sizeof(stats->other);
- sysctl(MIB_kstat_zfs_misc_arcstats_other_size, 5, &(stats->other), &len , NULL, 0);
+ sysctl(MIB_kstat_zfs_misc_arcstats_other_size, 5, &(stats->other), &len, NULL, 0);
stats->other /= 1024;
if (stats->isCompressed) {
len = sizeof(stats->compressed);
- sysctl(MIB_kstat_zfs_misc_arcstats_compressed_size, 5, &(stats->compressed), &len , NULL, 0);
+ sysctl(MIB_kstat_zfs_misc_arcstats_compressed_size, 5, &(stats->compressed), &len, NULL, 0);
stats->compressed /= 1024;
len = sizeof(stats->uncompressed);
- sysctl(MIB_kstat_zfs_misc_arcstats_uncompressed_size, 5, &(stats->uncompressed), &len , NULL, 0);
+ sysctl(MIB_kstat_zfs_misc_arcstats_uncompressed_size, 5, &(stats->uncompressed), &len, NULL, 0);
stats->uncompressed /= 1024;
}
}
diff --git a/zfs/openzfs_sysctl.h b/zfs/openzfs_sysctl.h
index adb4b24..b49128e 100644
--- a/zfs/openzfs_sysctl.h
+++ b/zfs/openzfs_sysctl.h
@@ -3,14 +3,14 @@
/*
htop - zfs/openzfs_sysctl.h
(C) 2014 Hisham H. Muhammad
-Released under the GNU GPL, see the COPYING file
+Released under the GNU GPLv2, see the COPYING file
in the source distribution for its full text.
*/
#include "zfs/ZfsArcStats.h"
-void openzfs_sysctl_init(ZfsArcStats *stats);
+void openzfs_sysctl_init(ZfsArcStats* stats);
-void openzfs_sysctl_updateArcStats(ZfsArcStats *stats);
+void openzfs_sysctl_updateArcStats(ZfsArcStats* stats);
#endif

© 2014-2024 Faster IT GmbH | imprint | privacy policy