From f288666edc9180a2e81e6655951878124f321df6 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Sun, 5 Feb 2023 04:25:56 +0100 Subject: New upstream version 3.2.2 --- .github/dependabot.yml | 9 ++ .github/workflows/build_release.yml | 2 +- .github/workflows/ci.yml | 48 +++++----- .github/workflows/codeql-analysis.yml | 49 ++++++++++ .gitignore | 3 + Action.c | 38 ++++++-- Action.h | 1 + AffinityPanel.c | 2 +- AvailableColumnsPanel.c | 2 +- AvailableMetersPanel.c | 2 +- AvailableMetersPanel.h | 2 +- CPUMeter.c | 18 +++- CRT.c | 81 ++++++++-------- CRT.h | 4 + ChangeLog | 40 ++++++++ ColumnsPanel.c | 2 +- CommandLine.c | 56 +++++++---- Compat.c | 37 ++++++++ Compat.h | 5 + DiskIOMeter.c | 10 +- DisplayOptionsPanel.c | 2 + Header.c | 11 ++- MainPanel.c | 18 ++-- MemoryMeter.c | 20 ++-- MemoryMeter.h | 8 ++ MetersPanel.c | 7 +- NetworkIOMeter.c | 15 +-- OpenFilesScreen.c | 2 +- Panel.c | 6 +- Process.c | 90 ++++++++++++++---- Process.h | 6 +- ProcessList.c | 4 +- ProcessLocksScreen.c | 15 +-- ProcessLocksScreen.h | 4 +- README | 3 +- RichString.c | 6 +- ScreenManager.c | 28 ++++-- ScreenManager.h | 4 +- ScreensPanel.c | 36 ++++--- ScreensPanel.h | 2 +- Settings.c | 33 +++++-- Settings.h | 2 + SwapMeter.c | 6 +- SwapMeter.h | 5 + TraceScreen.c | 6 +- UsersTable.c | 2 +- XUtils.c | 31 +++++- XUtils.h | 2 + configure.ac | 10 +- darwin/DarwinProcess.c | 2 +- darwin/DarwinProcessList.c | 3 +- darwin/Platform.c | 22 ++--- darwin/Platform.h | 18 ++-- darwin/PlatformHelpers.c | 9 +- darwin/PlatformHelpers.h | 2 +- docs/images/screenshot.png | Bin 80937 -> 46142 bytes dragonflybsd/DragonFlyBSDProcessList.c | 4 +- dragonflybsd/Platform.c | 24 ++--- dragonflybsd/Platform.h | 18 ++-- freebsd/FreeBSDProcessList.c | 12 +++ freebsd/Platform.c | 30 +++--- freebsd/Platform.h | 18 ++-- htop.1.in | 7 +- htop.png | Bin 3537 -> 2616 bytes linux/CGroupUtils.c | 10 +- linux/CGroupUtils.h | 2 +- linux/LinuxProcess.c | 4 +- linux/LinuxProcessList.c | 95 ++++++++++++++----- linux/Platform.c | 167 ++++++++++++++------------------- linux/Platform.h | 18 ++-- linux/SystemdMeter.c | 6 +- netbsd/Platform.c | 26 ++--- netbsd/Platform.h | 18 ++-- openbsd/Platform.c | 24 ++--- openbsd/Platform.h | 18 ++-- pcp-htop.5.in | 2 +- pcp/PCPDynamicColumn.c | 7 ++ pcp/PCPDynamicMeter.c | 10 +- pcp/PCPProcessList.c | 2 +- pcp/Platform.c | 84 ++++++++++------- pcp/Platform.h | 2 - solaris/Platform.c | 24 ++--- solaris/Platform.h | 18 ++-- unsupported/Platform.c | 10 +- unsupported/Platform.h | 18 ++-- 85 files changed, 992 insertions(+), 537 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5516fd6 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +--- + +version: 2 + +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml index 8c87915..655853c 100644 --- a/.github/workflows/build_release.yml +++ b/.github/workflows/build_release.yml @@ -11,7 +11,7 @@ jobs: name: build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 with: submodules: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index af8ed88..2e75ec0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: build-ubuntu-latest-minimal-gcc: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Dependencies run: sudo apt-get install --no-install-recommends libncursesw5-dev - name: Bootstrap @@ -31,16 +31,16 @@ jobs: build-ubuntu-latest-minimal-clang: runs-on: ubuntu-latest env: - CC: clang-12 + CC: clang-15 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - 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/focal/ llvm-toolchain-focal-12 main' -y + sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main' -y sudo apt-get update -q - name: Install Dependencies - run: sudo apt-get install --no-install-recommends clang-12 libncursesw5-dev + run: sudo apt-get install --no-install-recommends clang-15 libncursesw5-dev - name: Bootstrap run: ./autogen.sh - name: Configure @@ -57,7 +57,7 @@ jobs: CFLAGS: -O3 -g -flto LDFLAGS: -O3 -g -flto -Wl,--as-needed steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Dependencies run: sudo apt-get install --no-install-recommends libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev - name: Bootstrap @@ -72,16 +72,16 @@ jobs: build-ubuntu-latest-full-featured-clang: runs-on: ubuntu-latest env: - CC: clang-12 + CC: clang-15 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - 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/focal/ llvm-toolchain-focal-12 main' -y + sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main' -y sudo apt-get update -q - name: Install Dependencies - run: sudo apt-get install --no-install-recommends clang-12 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev + run: sudo apt-get install --no-install-recommends clang-15 libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev - name: Bootstrap run: ./autogen.sh - name: Configure @@ -98,7 +98,7 @@ jobs: CFLAGS: -O3 -g -flto LDFLAGS: -O3 -g -flto steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Dependencies run: sudo apt-get install --no-install-recommends libncursesw5-dev libtinfo-dev libgpm-dev libsensors4-dev libcap-dev - name: Bootstrap @@ -111,14 +111,10 @@ jobs: run: make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-static --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --disable-hwloc --disable-delayacct --enable-sensors --enable-capabilities' build-ubuntu-latest-pcp: - # Turns out 'ubuntu-latest' can be older than 20.04, we want PCP v5+ - runs-on: ubuntu-20.04 - env: - # Until Ubuntu catches up with pcp-5.2.3+: - # pcp/Platform.c:309:45: warning: passing argument 2 of ‘pmLookupName’ from incompatible pointer type [-Wincompatible-pointer-types] - CFLAGS: -Wno-error=incompatible-pointer-types + # we want PCP v5.2.3+ + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Dependencies run: sudo apt-get install --no-install-recommends libpcp3-dev libncursesw5-dev libtinfo-dev libgpm-dev - name: Bootstrap @@ -131,29 +127,29 @@ jobs: build-ubuntu-latest-clang-analyzer: runs-on: ubuntu-latest env: - CC: clang-12 + CC: clang-15 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - 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/focal/ llvm-toolchain-focal-12 main' -y + sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-15 main' -y sudo apt-get update -q - name: Install Dependencies - run: sudo apt-get install --no-install-recommends clang-12 clang-tools-12 libncursesw5-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev + run: sudo apt-get install --no-install-recommends clang-15 clang-tools-15 libncursesw5-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev - name: Bootstrap run: ./autogen.sh - name: Configure - run: scan-build-12 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-delayacct --enable-sensors --enable-capabilities + run: scan-build-15 -analyze-headers --status-bugs ./configure --enable-debug --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-delayacct --enable-sensors --enable-capabilities - name: Build - run: scan-build-12 -analyze-headers --status-bugs make -j"$(nproc)" + run: scan-build-15 -analyze-headers --status-bugs make -j"$(nproc)" build-macos-latest-clang: runs-on: macOS-latest env: CC: clang steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Dependencies run: brew install automake - name: Bootstrap @@ -168,6 +164,6 @@ jobs: whitespace_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: check-whitespaces run: git diff-tree --check $(git hash-object -t tree /dev/null) HEAD diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..17356b6 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,49 @@ +name: "CodeQL" + +on: + push: + branches: [main] + pull_request: + branches: [main] + schedule: + - cron: '0 1 * * 0' + +permissions: + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + security-events: write + + env: + # Enable format attributes in ncurses headers + # Enable fortified memory/string handling + CPPFLAGS: -DGCC_PRINTF -DGCC_SCANF -D_FORTIFY_SOURCE=2 + + steps: + - name: Checkout Repository + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: cpp + + - name: Install Dependencies + run: sudo apt-get install --no-install-recommends libncursesw5-dev libhwloc-dev libnl-3-dev libnl-genl-3-dev libsensors4-dev libcap-dev + + - name: Bootstrap + run: ./autogen.sh + + - name: Configure + run: ./configure --enable-werror --enable-openvz --enable-vserver --enable-ancient-vserver --enable-unicode --enable-hwloc --enable-delayacct --enable-sensors --enable-capabilities + + - name: Build + run: make + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.gitignore b/.gitignore index 8262449..50f3bd1 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,6 @@ callgrind.out.* # IDE workspace configurations /.idea/ /.vscode/ + +# Language Servers +/.cache/clangd/ diff --git a/Action.c b/Action.c index b765248..81432f5 100644 --- a/Action.c +++ b/Action.c @@ -213,22 +213,37 @@ static Htop_Reaction actionSortByTime(State* st) { static Htop_Reaction actionToggleKernelThreads(State* st) { st->settings->hideKernelThreads = !st->settings->hideKernelThreads; + st->settings->lastUpdate++; + return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleUserlandThreads(State* st) { st->settings->hideUserlandThreads = !st->settings->hideUserlandThreads; + st->settings->lastUpdate++; + + return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; +} + +static Htop_Reaction actionToggleRunningInContainer(State* st) { + st->settings->hideRunningInContainer = !st->settings->hideRunningInContainer; + st->settings->lastUpdate++; + return HTOP_RECALCULATE | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleProgramPath(State* st) { st->settings->showProgramPath = !st->settings->showProgramPath; - return HTOP_REFRESH | HTOP_SAVE_SETTINGS; + st->settings->lastUpdate++; + + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; } static Htop_Reaction actionToggleMergedCommand(State* st) { st->settings->showMergedCommand = !st->settings->showMergedCommand; - return HTOP_REFRESH | HTOP_SAVE_SETTINGS; + st->settings->lastUpdate++; + + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionToggleTreeView(State* st) { @@ -240,6 +255,11 @@ static Htop_Reaction actionToggleTreeView(State* st) { return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR; } +static Htop_Reaction actionToggleHideMeters(State* st) { + st->hideMeters = !st->hideMeters; + return HTOP_RESIZE | HTOP_KEEP_FOLLOWING; +} + static Htop_Reaction actionExpandOrCollapseAllBranches(State* st) { ScreenSettings* ss = st->settings->ss; if (!ss->treeView) { @@ -285,10 +305,13 @@ static Htop_Reaction actionLowerPriority(State* st) { static Htop_Reaction actionInvertSortOrder(State* st) { ScreenSettings_invertSortOrder(st->settings->ss); st->pl->needsSort = true; - return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING; + return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_UPDATE_PANELHDR; } static Htop_Reaction actionExpandOrCollapse(State* st) { + if (!st->settings->ss->treeView) + return HTOP_OK; + bool changed = expandCollapse((Panel*)st->mainPanel); return changed ? HTOP_RECALCULATE : HTOP_OK; } @@ -312,7 +335,7 @@ static Htop_Reaction actionNextScreen(State* st) { settings->ssIndex = 0; } settings->ss = settings->screens[settings->ssIndex]; - return HTOP_REFRESH; + return HTOP_UPDATE_PANELHDR | HTOP_REFRESH; } static Htop_Reaction actionPrevScreen(State* st) { @@ -323,7 +346,7 @@ static Htop_Reaction actionPrevScreen(State* st) { settings->ssIndex--; } settings->ss = settings->screens[settings->ssIndex]; - return HTOP_REFRESH; + return HTOP_UPDATE_PANELHDR | HTOP_REFRESH; } Htop_Reaction Action_setScreenTab(Settings* settings, int x) { @@ -337,7 +360,7 @@ Htop_Reaction Action_setScreenTab(Settings* settings, int x) { if (x <= s + len + 1) { settings->ssIndex = i; settings->ss = settings->screens[i]; - return HTOP_REFRESH; + return HTOP_UPDATE_PANELHDR | HTOP_REFRESH; } s += len + 3; } @@ -506,6 +529,7 @@ static const struct { bool roInactive; const char* info; } helpLeft[] = { + { .key = " #: ", .roInactive = false, .info = "hide/show header meters" }, { .key = " Tab: ", .roInactive = false, .info = "switch to next screen tab" }, { .key = " Arrows: ", .roInactive = false, .info = "scroll process list" }, { .key = " Digits: ", .roInactive = false, .info = "incremental PID search" }, @@ -732,6 +756,7 @@ static Htop_Reaction actionShowCommandScreen(State* st) { void Action_setBindings(Htop_Action* keys) { keys[' '] = actionTag; + keys['#'] = actionToggleHideMeters; keys['*'] = actionExpandOrCollapseAllBranches; keys['+'] = actionExpandOrCollapse; keys[','] = actionSetSortColumn; @@ -749,6 +774,7 @@ void Action_setBindings(Htop_Action* keys) { keys['K'] = actionToggleKernelThreads; keys['M'] = actionSortByMemory; keys['N'] = actionSortByPID; + keys['O'] = actionToggleRunningInContainer; keys['P'] = actionSortByCPU; keys['S'] = actionSetup; keys['T'] = actionSortByTime; diff --git a/Action.h b/Action.h index 06af188..09b68bd 100644 --- a/Action.h +++ b/Action.h @@ -43,6 +43,7 @@ typedef struct State_ { Header* header; bool pauseProcessUpdate; bool hideProcessSelection; + bool hideMeters; } State; static inline bool State_hideFunctionBar(const State* st) { diff --git a/AffinityPanel.c b/AffinityPanel.c index 9875a5c..b724397 100644 --- a/AffinityPanel.c +++ b/AffinityPanel.c @@ -201,7 +201,7 @@ static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) { MaskItem* selected = (MaskItem*) Panel_getSelected(super); bool keepSelected = true; - switch(ch) { + switch (ch) { case KEY_MOUSE: case KEY_RECLICK: case ' ': diff --git a/AvailableColumnsPanel.c b/AvailableColumnsPanel.c index 7720bef..b8c09c7 100644 --- a/AvailableColumnsPanel.c +++ b/AvailableColumnsPanel.c @@ -45,7 +45,7 @@ static HandlerResult AvailableColumnsPanel_eventHandler(Panel* super, int ch) { AvailableColumnsPanel* this = (AvailableColumnsPanel*) super; HandlerResult result = IGNORED; - switch(ch) { + switch (ch) { case 13: case KEY_ENTER: case KEY_F(5): diff --git a/AvailableMetersPanel.c b/AvailableMetersPanel.c index 368f494..c7ab89b 100644 --- a/AvailableMetersPanel.c +++ b/AvailableMetersPanel.c @@ -54,7 +54,7 @@ static HandlerResult AvailableMetersPanel_eventHandler(Panel* super, int ch) { HandlerResult result = IGNORED; bool update = false; - switch(ch) { + switch (ch) { case KEY_F(5): case 'l': case 'L': diff --git a/AvailableMetersPanel.h b/AvailableMetersPanel.h index f5f7a2d..1c0555a 100644 --- a/AvailableMetersPanel.h +++ b/AvailableMetersPanel.h @@ -29,6 +29,6 @@ typedef struct AvailableMetersPanel_ { extern const PanelClass AvailableMetersPanel_class; -AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel **meterPanels, ScreenManager* scr, const ProcessList* pl); +AvailableMetersPanel* AvailableMetersPanel_new(Settings* settings, Header* header, size_t columns, MetersPanel** meterPanels, ScreenManager* scr, const ProcessList* pl); #endif diff --git a/CPUMeter.c b/CPUMeter.c index 9974db9..ba00595 100644 --- a/CPUMeter.c +++ b/CPUMeter.c @@ -167,6 +167,18 @@ static void CPUMeter_display(const Object* cast, RichString* out) { } } + if (this->pl->settings->showCPUFrequency) { + char cpuFrequencyBuffer[10]; + double cpuFrequency = this->values[CPU_METER_FREQUENCY]; + if (isnan(cpuFrequency)) { + len = xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "N/A "); + } else { + len = xSnprintf(cpuFrequencyBuffer, sizeof(cpuFrequencyBuffer), "%4uMHz ", (unsigned)cpuFrequency); + } + RichString_appendAscii(out, CRT_colors[METER_TEXT], "freq: "); + RichString_appendnWide(out, CRT_colors[METER_VALUE], cpuFrequencyBuffer, len); + } + #ifdef BUILD_WITH_CPU_TEMP if (this->pl->settings->showCPUTemperature) { char cpuTemperatureBuffer[10]; @@ -187,7 +199,7 @@ static void CPUMeter_display(const Object* cast, RichString* out) { static void AllCPUsMeter_getRange(const Meter* this, int* start, int* count) { const CPUMeterData* data = this->meterData; unsigned int cpus = data->cpus; - switch(Meter_name(this)[0]) { + switch (Meter_name(this)[0]) { default: case 'A': // All *start = 0; @@ -195,10 +207,10 @@ static void AllCPUsMeter_getRange(const Meter* this, int* start, int* count) { break; case 'L': // First Half *start = 0; - *count = (cpus+1) / 2; + *count = (cpus + 1) / 2; break; case 'R': // Second Half - *start = (cpus+1) / 2; + *start = (cpus + 1) / 2; *count = cpus / 2; break; } diff --git a/CRT.c b/CRT.c index 868f237..64c259f 100644 --- a/CRT.c +++ b/CRT.c @@ -134,7 +134,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [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_IOWRITE] = A_BOLD | ColorPair(Blue, Black), [METER_VALUE_NOTICE] = A_BOLD | ColorPair(White, Black), [METER_VALUE_OK] = ColorPair(Green, Black), [METER_VALUE_WARN] = A_BOLD | ColorPair(Yellow, Black), @@ -156,7 +156,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [PROCESS_THREAD] = ColorPair(Green, Black), [PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black), [PROCESS_COMM] = ColorPair(Magenta, Black), - [PROCESS_THREAD_COMM] = ColorPair(Blue, Black), + [PROCESS_THREAD_COMM] = A_BOLD | ColorPair(Blue, Black), [BAR_BORDER] = A_BOLD, [BAR_SHADOW] = A_BOLD | ColorPairGrayBlack, [SWAP] = ColorPair(Red, Black), @@ -164,14 +164,14 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [GRAPH_1] = A_BOLD | ColorPair(Cyan, Black), [GRAPH_2] = ColorPair(Cyan, Black), [MEMORY_USED] = ColorPair(Green, Black), - [MEMORY_BUFFERS] = ColorPair(Blue, Black), + [MEMORY_BUFFERS] = A_BOLD | ColorPair(Blue, Black), [MEMORY_BUFFERS_TEXT] = A_BOLD | ColorPair(Blue, Black), [MEMORY_CACHE] = ColorPair(Yellow, Black), [MEMORY_SHARED] = ColorPair(Magenta, Black), [HUGEPAGE_1] = ColorPair(Green, Black), [HUGEPAGE_2] = ColorPair(Yellow, Black), [HUGEPAGE_3] = ColorPair(Red, Black), - [HUGEPAGE_4] = ColorPair(Blue, Black), + [HUGEPAGE_4] = A_BOLD | ColorPair(Blue, Black), [LOAD_AVERAGE_FIFTEEN] = ColorPair(Cyan, Black), [LOAD_AVERAGE_FIVE] = A_BOLD | ColorPair(Cyan, Black), [LOAD_AVERAGE_ONE] = A_BOLD | ColorPair(White, Black), @@ -185,7 +185,7 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CHECK_MARK] = A_BOLD, [CHECK_TEXT] = A_NORMAL, [HOSTNAME] = A_BOLD, - [CPU_NICE] = ColorPair(Blue, Black), + [CPU_NICE] = A_BOLD | ColorPair(Blue, Black), [CPU_NICE_TEXT] = A_BOLD | ColorPair(Blue, Black), [CPU_NORMAL] = ColorPair(Green, Black), [CPU_SYSTEM] = ColorPair(Red, Black), @@ -202,19 +202,19 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [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_MFU] = A_BOLD | 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_COMPRESSED] = A_BOLD | ColorPair(Blue, Black), [ZFS_RATIO] = ColorPair(Magenta, Black), [ZRAM] = ColorPair(Yellow, Black), [DYNAMIC_GRAY] = ColorPairGrayBlack, [DYNAMIC_DARKGRAY] = A_BOLD | ColorPairGrayBlack, [DYNAMIC_RED] = ColorPair(Red, Black), [DYNAMIC_GREEN] = ColorPair(Green, Black), - [DYNAMIC_BLUE] = ColorPair(Blue, Black), + [DYNAMIC_BLUE] = A_BOLD | ColorPair(Blue, Black), [DYNAMIC_CYAN] = ColorPair(Cyan, Black), [DYNAMIC_MAGENTA] = ColorPair(Magenta, Black), [DYNAMIC_YELLOW] = ColorPair(Yellow, Black), @@ -408,11 +408,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Blue, White), [CPU_STEAL] = ColorPair(Cyan, White), [CPU_GUEST] = ColorPair(Cyan, White), - [PANEL_EDIT] = ColorPair(White,Blue), - [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Black,White), - [SCREENS_OTH_TEXT] = A_BOLD | ColorPair(Black,White), - [SCREENS_CUR_BORDER] = ColorPair(Green,Green), - [SCREENS_CUR_TEXT] = ColorPair(Black,Green), + [PANEL_EDIT] = ColorPair(White, Blue), + [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Black, White), + [SCREENS_OTH_TEXT] = A_BOLD | ColorPair(Black, White), + [SCREENS_CUR_BORDER] = ColorPair(Green, Green), + [SCREENS_CUR_TEXT] = ColorPair(Black, Green), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, White), [PRESSURE_STALL_SIXTY] = ColorPair(Black, White), [PRESSURE_STALL_TEN] = ColorPair(Black, White), @@ -515,11 +515,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Blue, Black), [CPU_STEAL] = ColorPair(Black, Black), [CPU_GUEST] = ColorPair(Black, Black), - [PANEL_EDIT] = ColorPair(White,Blue), - [SCREENS_OTH_BORDER] = ColorPair(Blue,Black), - [SCREENS_OTH_TEXT] = ColorPair(Blue,Black), - [SCREENS_CUR_BORDER] = ColorPair(Green,Green), - [SCREENS_CUR_TEXT] = ColorPair(Black,Green), + [PANEL_EDIT] = ColorPair(White, Blue), + [SCREENS_OTH_BORDER] = ColorPair(Blue, Black), + [SCREENS_OTH_TEXT] = ColorPair(Blue, Black), + [SCREENS_CUR_BORDER] = ColorPair(Green, Green), + [SCREENS_CUR_TEXT] = ColorPair(Black, Green), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Black, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Black, Black), [PRESSURE_STALL_TEN] = ColorPair(Black, Black), @@ -622,11 +622,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Black, Blue), [CPU_STEAL] = ColorPair(White, Blue), [CPU_GUEST] = ColorPair(White, Blue), - [PANEL_EDIT] = ColorPair(White,Blue), - [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Yellow,Blue), - [SCREENS_OTH_TEXT] = ColorPair(Cyan,Blue), - [SCREENS_CUR_BORDER] = ColorPair(Cyan,Cyan), - [SCREENS_CUR_TEXT] = ColorPair(Black,Cyan), + [PANEL_EDIT] = ColorPair(White, Blue), + [SCREENS_OTH_BORDER] = A_BOLD | ColorPair(Yellow, Blue), + [SCREENS_OTH_TEXT] = ColorPair(Cyan, Blue), + [SCREENS_CUR_BORDER] = ColorPair(Cyan, Cyan), + [SCREENS_CUR_TEXT] = ColorPair(Black, Cyan), [PRESSURE_STALL_THREEHUNDRED] = A_BOLD | ColorPair(Black, Blue), [PRESSURE_STALL_SIXTY] = A_NORMAL | ColorPair(White, Blue), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(White, Blue), @@ -727,11 +727,11 @@ static int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = { [CPU_SOFTIRQ] = ColorPair(Blue, Black), [CPU_STEAL] = ColorPair(Cyan, Black), [CPU_GUEST] = ColorPair(Cyan, Black), - [PANEL_EDIT] = ColorPair(White,Cyan), - [SCREENS_OTH_BORDER] = ColorPair(White,Black), - [SCREENS_OTH_TEXT] = ColorPair(Cyan,Black), - [SCREENS_CUR_BORDER] = A_BOLD | ColorPair(White,Black), - [SCREENS_CUR_TEXT] = A_BOLD | ColorPair(Green,Black), + [PANEL_EDIT] = ColorPair(White, Cyan), + [SCREENS_OTH_BORDER] = ColorPair(White, Black), + [SCREENS_OTH_TEXT] = ColorPair(Cyan, Black), + [SCREENS_CUR_BORDER] = A_BOLD | ColorPair(White, Black), + [SCREENS_CUR_TEXT] = A_BOLD | ColorPair(Green, Black), [PRESSURE_STALL_THREEHUNDRED] = ColorPair(Green, Black), [PRESSURE_STALL_SIXTY] = ColorPair(Green, Black), [PRESSURE_STALL_TEN] = A_BOLD | ColorPair(Green, Black), @@ -834,7 +834,7 @@ static void dumpStderr(void) { fprintf(stderr, ">>>>>>>>>> stderr output >>>>>>>>>>\n"); header = true; } - (void)! write(STDERR_FILENO, buffer, res); + full_write(STDERR_FILENO, buffer, res); } } @@ -900,8 +900,8 @@ void CRT_resetSignalHandlers(void) { signal(SIGQUIT, SIG_DFL); } -void CRT_setMouse(bool enabled) { #ifdef HAVE_GETMOUSE +void CRT_setMouse(bool enabled) { if (enabled) { #if NCURSES_MOUSE_VERSION > 1 mousemask(BUTTON1_RELEASED | BUTTON4_PRESSED | BUTTON5_PRESSED, NULL); @@ -911,13 +911,12 @@ void CRT_setMouse(bool enabled) { } else { mousemask(0, NULL); } -#endif } +#endif void CRT_init(const Settings* settings, bool allowUnicode) { - redirectStderr(); - initscr(); + redirectStderr(); noecho(); CRT_crashSettings = settings; CRT_delay = &(settings->delay); @@ -1012,10 +1011,12 @@ IGNORE_WCASTQUAL_END CRT_degreeSign = initDegreeSign(); } -void CRT_done() { - attron(CRT_colors[RESET_COLOR]); +void CRT_done(void) { + int resetColor = CRT_colors ? CRT_colors[RESET_COLOR] : CRT_colorSchemes[COLORSCHEME_DEFAULT][RESET_COLOR]; + + attron(resetColor); mvhline(LINES - 1, 0, ' ', COLS); - attroff(CRT_colors[RESET_COLOR]); + attroff(resetColor); refresh(); curs_set(1); @@ -1031,7 +1032,7 @@ void CRT_fatalError(const char* note) { exit(2); } -int CRT_readKey() { +int CRT_readKey(void) { nocbreak(); cbreak(); nodelay(stdscr, FALSE); @@ -1040,13 +1041,13 @@ int CRT_readKey() { return ret; } -void CRT_disableDelay() { +void CRT_disableDelay(void) { nocbreak(); cbreak(); nodelay(stdscr, TRUE); } -void CRT_enableDelay() { +void CRT_enableDelay(void) { halfdelay(*CRT_delay); } @@ -1095,7 +1096,7 @@ static void print_backtrace(void) { unw_get_proc_name(&cursor, symbolName, sizeof(symbolName), &offset); unw_proc_info_t pip; - pip.unwind_info = NULL; + pip.unwind_info = 0; const char* fname = "?"; const void* ptr = 0; diff --git a/CRT.h b/CRT.h index 297e896..c06d3ae 100644 --- a/CRT.h +++ b/CRT.h @@ -185,7 +185,11 @@ extern int CRT_scrollWheelVAmount; extern ColorScheme CRT_colorScheme; +#ifdef HAVE_GETMOUSE void CRT_setMouse(bool enabled); +#else +#define CRT_setMouse(enabled) +#endif void CRT_init(const Settings* settings, bool allowUnicode); diff --git a/ChangeLog b/ChangeLog index 8f5266c..5717a52 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,43 @@ +What's new in version 3.2.2 + +* CPUMeter now can show frequency in text mode +* Add option to render distribution path prefixes shadowed +* DiskIOMeter converts to bytes per second (not per interval) +* DiskIOMeter uses complete units, including missing "iB/s" +* DiskIOMeter indicates read and write in meter mode +* NetworkIOMeter converts to packets per second, shows packet rate +* Allow continued process following when changing display settings +* Update the panel header when changing to another tab +* Drop margin around the header if there are no meters +* Use Unicode replacement character for non-printable characters +* Default color preset uses bold blue for better visibility +* Update the Panel header on sort order inversions ('I') +* Toggle the header meters with pound key +* Fix ScreenPanel to handle quitting the panel while renaming +* Add fallback for HOME environment variable using passwd database +* Replace meaningless ID column with FD column in lock screen +* Use device format in the lock screen matching the files screen +* On Linux, improvements to file-descriptor lock detection +* On Linux, further distinguish systemd states in the SystemdMeter +* On Linux, improvements to cgroup and container identification +* On Linux, support openat(2) without readlinkat(2) platforms +* On Darwin, fix current process buffer handling for busy systems +* On DragonFly BSD, fix incorrect processor time of processes +* On FreeBSD, fix an issue with the memory graph not showing correctly +* On FreeBSD, add support for displaying shared memory usage +* On PCP, use pmLookupDescs(3) if available for efficiency +* On PCP, normalize generic columns values for consistent display +* On PCP, changes preparing for configurable, dynamic screens +* Handle invalid process columns from the configuration file +* Avoid undefined behaviour with deeply nested processes +* Fix crash when removing the currently active screen +* Prevent possible crash on a very early error path +* Include automake for Debian/Ubuntu +* Restore non-mouse support +* Reject unsupported command line arguments +* Document idle process state +* Clarify M_TRS/M_DRS columns + What's new in version 3.2.1 * Fix setting to show all branches collapsed by default diff --git a/ColumnsPanel.c b/ColumnsPanel.c index 2482693..d53fff2 100644 --- a/ColumnsPanel.c +++ b/ColumnsPanel.c @@ -38,7 +38,7 @@ static HandlerResult ColumnsPanel_eventHandler(Panel* super, int ch) { HandlerResult result = IGNORED; int size = Panel_size(super); - switch(ch) { + switch (ch) { case 0x0a: case 0x0d: case KEY_ENTER: diff --git a/CommandLine.c b/CommandLine.c index d21f482..682e054 100644 --- a/CommandLine.c +++ b/CommandLine.c @@ -66,7 +66,6 @@ static void printHelpFlag(const char* name) { "-V --version Print version info\n"); Platform_longOptionsUsage(name); printf("\n" - "Long options may be passed with a single dash.\n\n" "Press F1 inside %s for online help.\n" "See 'man %s' for more information.\n", name, name); } @@ -80,7 +79,9 @@ typedef struct CommandLineSettings_ { int sortKey; int delay; bool useColors; +#ifdef HAVE_GETMOUSE bool enableMouse; +#endif bool treeView; bool allowUnicode; bool highlightChanges; @@ -97,7 +98,9 @@ static CommandLineStatus parseArguments(const char* program, int argc, char** ar .sortKey = 0, .delay = -1, .useColors = true, +#ifdef HAVE_GETMOUSE .enableMouse = true, +#endif .treeView = false, .allowUnicode = true, .highlightChanges = false, @@ -143,7 +146,8 @@ static CommandLineStatus parseArguments(const char* program, int argc, char** ar for (int j = 1; j < LAST_PROCESSFIELD; j++) { const char* name = Process_fields[j].name; const char* description = Process_fields[j].description; - if (name) printf("%19s %s\n", name, description); + if (name) + printf("%19s %s\n", name, description); } return STATUS_OK_EXIT; } @@ -163,8 +167,10 @@ static CommandLineStatus parseArguments(const char* program, int argc, char** ar break; case 'd': if (sscanf(optarg, "%16d", &(flags->delay)) == 1) { - if (flags->delay < 1) flags->delay = 1; - if (flags->delay > 100) flags->delay = 100; + if (flags->delay < 1) + flags->delay = 1; + if (flags->delay > 100) + flags->delay = 100; } else { fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg); return STATUS_ERROR_EXIT; @@ -172,7 +178,7 @@ static CommandLineStatus parseArguments(const char* program, int argc, char** ar break; case 'u': { - const char *username = optarg; + const char* username = optarg; if (!username && optind < argc && argv[optind] != NULL && (argv[optind][0] != '\0' && argv[optind][0] != '-')) { username = argv[optind++]; @@ -181,7 +187,7 @@ static CommandLineStatus parseArguments(const char* program, int argc, char** ar if (!username) { flags->userId = geteuid(); } else if (!Action_setUserOnly(username, &(flags->userId))) { - for (const char *itr = username; *itr; ++itr) + for (const char* itr = username; *itr; ++itr) if (!isdigit((unsigned char)*itr)) { fprintf(stderr, "Error: invalid user \"%s\".\n", username); return STATUS_ERROR_EXIT; @@ -214,11 +220,11 @@ static CommandLineStatus parseArguments(const char* program, int argc, char** ar flags->pidMatchList = Hashtable_new(8, false); } - 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); + 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); } free(argCopy); @@ -230,19 +236,19 @@ static CommandLineStatus parseArguments(const char* program, int argc, char** ar break; } case 'H': { - const char *delay = optarg; + const char* delay = optarg; if (!delay && optind < argc && argv[optind] != NULL && (argv[optind][0] != '\0' && argv[optind][0] != '-')) { - delay = argv[optind++]; + 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); - return STATUS_ERROR_EXIT; - } + 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); + return STATUS_ERROR_EXIT; + } } flags->highlightChanges = true; break; @@ -259,6 +265,15 @@ static CommandLineStatus parseArguments(const char* program, int argc, char** ar } } } + + if (optind < argc) { + fprintf(stderr, "Error: unsupported non-option ARGV-elements:"); + while (optind < argc) + fprintf(stderr, " %s", argv[optind++]); + fprintf(stderr, "\n"); + return STATUS_ERROR_EXIT; + } + return STATUS_OK; } @@ -359,6 +374,7 @@ int CommandLine_run(const char* name, int argc, char** argv) { .header = header, .pauseProcessUpdate = false, .hideProcessSelection = false, + .hideMeters = false, }; MainPanel_setState(panel, &state); diff --git a/Compat.c b/Compat.c index 8c88138..d0ad2cc 100644 --- a/Compat.c +++ b/Compat.c @@ -11,6 +11,7 @@ in the source distribution for its full text. #include #include // IWYU pragma: keep +#include #include #include #include // IWYU pragma: keep @@ -18,6 +19,12 @@ in the source distribution for its full text. #include "XUtils.h" // IWYU pragma: keep +/* GNU/Hurd does not have PATH_MAX in limits.h */ +#ifndef PATH_MAX +# define PATH_MAX 4096 +#endif + + int Compat_faccessat(int dirfd, const char* pathname, int mode, @@ -117,3 +124,33 @@ ssize_t Compat_readlinkat(int dirfd, #endif } + +ssize_t Compat_readlink(openat_arg_t dirfd, + const char* pathname, + char* buf, + size_t bufsize) { + +#ifdef HAVE_OPENAT + + char fdPath[32]; + xSnprintf(fdPath, sizeof(fdPath), "/proc/self/fd/%d", dirfd); + + char dirPath[PATH_MAX + 1]; + ssize_t r = readlink(fdPath, dirPath, sizeof(dirPath) - 1); + if (r < 0) + return r; + + dirPath[r] = '\0'; + + char linkPath[PATH_MAX + 1]; + xSnprintf(linkPath, sizeof(linkPath), "%s/%s", dirPath, pathname); + +#else + + char linkPath[PATH_MAX + 1]; + xSnprintf(linkPath, sizeof(linkPath), "%s/%s", dirfd, pathname); + +#endif /* HAVE_OPENAT */ + + return readlink(linkPath, buf, bufsize); +} diff --git a/Compat.h b/Compat.h index 2b5e205..1c4794e 100644 --- a/Compat.h +++ b/Compat.h @@ -56,4 +56,9 @@ ssize_t Compat_readlinkat(int dirfd, char* buf, size_t bufsize); +ssize_t Compat_readlink(openat_arg_t dirfd, + const char* pathname, + char* buf, + size_t bufsize); + #endif /* HEADER_Compat */ diff --git a/DiskIOMeter.c b/DiskIOMeter.c index 12c87de..adab8f7 100644 --- a/DiskIOMeter.c +++ b/DiskIOMeter.c @@ -64,7 +64,8 @@ static void DiskIOMeter_updateValues(Meter* this) { if (data.totalBytesRead > cached_read_total) { diff = data.totalBytesRead - cached_read_total; - diff /= 1024; /* Meter_humanUnit() expects unit in kilo */ + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + diff /= ONE_K; /* convert to KiB/s */ cached_read_diff = (uint32_t)diff; } else { cached_read_diff = 0; @@ -73,7 +74,8 @@ static void DiskIOMeter_updateValues(Meter* this) { if (data.totalBytesWritten > cached_write_total) { diff = data.totalBytesWritten - cached_write_total; - diff /= 1024; /* Meter_humanUnit() expects unit in kilo */ + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + diff /= ONE_K; /* convert to KiB/s */ cached_write_diff = (uint32_t)diff; } else { cached_write_diff = 0; @@ -104,7 +106,7 @@ static void DiskIOMeter_updateValues(Meter* this) { char bufferRead[12], bufferWrite[12]; Meter_humanUnit(bufferRead, cached_read_diff, sizeof(bufferRead)); Meter_humanUnit(bufferWrite, cached_write_diff, sizeof(bufferWrite)); - snprintf(this->txtBuffer, sizeof(this->txtBuffer), "%sB %sB %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); + snprintf(this->txtBuffer, sizeof(this->txtBuffer), "r:%siB/s w:%siB/s %.1f%%", bufferRead, bufferWrite, cached_utilisation_diff); } static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { @@ -132,10 +134,12 @@ static void DiskIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) RichString_appendAscii(out, CRT_colors[METER_TEXT], " read: "); Meter_humanUnit(buffer, cached_read_diff, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], buffer); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOREAD], "iB/s"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " write: "); Meter_humanUnit(buffer, cached_write_diff, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer); + RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); } const MeterClass DiskIOMeter_class = { diff --git a/DisplayOptionsPanel.c b/DisplayOptionsPanel.c index fc23cb6..f9fa9b1 100644 --- a/DisplayOptionsPanel.c +++ b/DisplayOptionsPanel.c @@ -116,11 +116,13 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager* 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("Hide processes running in containers", &(settings->hideRunningInContainer))); 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("Highlight out-dated/removed programs (red) / libraries (yellow)", &(settings->highlightDeletedExe))); + Panel_add(super, (Object*) CheckItem_newByRef("Shadow distribution path prefixes", &(settings->shadowDistPathPrefix))); 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))); diff --git a/Header.c b/Header.c index 8dff89b..1953c02 100644 --- a/Header.c +++ b/Header.c @@ -286,10 +286,19 @@ int Header_calculateHeight(Header* this) { } maxHeight = MAXIMUM(maxHeight, height); } + + if (maxHeight == pad) { + maxHeight = 0; + this->pad = 0; + } else { + this->pad = pad; + } + if (this->settings->screenTabs) { maxHeight++; } + this->height = maxHeight; - this->pad = pad; + return maxHeight; } diff --git a/MainPanel.c b/MainPanel.c index 44915df..89b4e7d 100644 --- a/MainPanel.c +++ b/MainPanel.c @@ -122,28 +122,28 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) { } } - if (reaction & HTOP_REDRAW_BAR) { + if ((reaction & HTOP_REDRAW_BAR) == HTOP_REDRAW_BAR) { MainPanel_updateLabels(this, settings->ss->treeView, this->state->pl->incFilter); } - if (reaction & HTOP_RESIZE) { + if ((reaction & HTOP_RESIZE) == HTOP_RESIZE) { result |= RESIZE; } - if (reaction & HTOP_UPDATE_PANELHDR) { + if ((reaction & HTOP_UPDATE_PANELHDR) == HTOP_UPDATE_PANELHDR) { result |= REDRAW; } - if (reaction & HTOP_REFRESH) { + if ((reaction & HTOP_REFRESH) == HTOP_REFRESH) { result |= REFRESH; } - if (reaction & HTOP_RECALCULATE) { + if ((reaction & HTOP_RECALCULATE) == HTOP_RECALCULATE) { result |= RESCAN; } - if (reaction & HTOP_SAVE_SETTINGS) { + if ((reaction & HTOP_SAVE_SETTINGS) == HTOP_SAVE_SETTINGS) { this->state->settings->changed = true; } - if (reaction & HTOP_QUIT) { + if ((reaction & HTOP_QUIT) == HTOP_QUIT) { return BREAK_LOOP; } - if (!(reaction & HTOP_KEEP_FOLLOWING)) { + if ((reaction & HTOP_KEEP_FOLLOWING) != HTOP_KEEP_FOLLOWING) { this->state->pl->following = -1; Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS); } @@ -210,7 +210,7 @@ const PanelClass MainPanel_class = { .printHeader = MainPanel_printHeader }; -MainPanel* MainPanel_new() { +MainPanel* MainPanel_new(void) { MainPanel* this = AllocThis(MainPanel); Panel_init((Panel*) this, 1, 1, 1, 1, Class(Process), false, FunctionBar_new(Settings_isReadonly() ? MainFunctions_ro : MainFunctions, NULL, NULL)); this->keys = xCalloc(KEY_MAX, sizeof(Htop_Action)); diff --git a/MemoryMeter.c b/MemoryMeter.c index 89e242c..ac01dfe 100644 --- a/MemoryMeter.c +++ b/MemoryMeter.c @@ -29,14 +29,14 @@ static void MemoryMeter_updateValues(Meter* this) { int written; /* shared and available memory are not supported on all platforms */ - this->values[2] = NAN; - this->values[4] = NAN; + this->values[MEMORY_METER_SHARED] = NAN; + this->values[MEMORY_METER_AVAILABLE] = NAN; Platform_setMemoryValues(this); /* Do not print available memory in bar mode */ this->curItems = 4; - written = Meter_humanUnit(buffer, this->values[0], size); + written = Meter_humanUnit(buffer, this->values[MEMORY_METER_USED], size); METER_BUFFER_CHECK(buffer, size, written); METER_BUFFER_APPEND_CHR(buffer, size, '/'); @@ -52,28 +52,28 @@ static void MemoryMeter_display(const Object* cast, RichString* out) { Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); - Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[MEMORY_METER_USED], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[MEMORY_USED], buffer); - Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[MEMORY_METER_BUFFERS], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " buffers:"); RichString_appendAscii(out, CRT_colors[MEMORY_BUFFERS_TEXT], buffer); /* shared memory is not supported on all platforms */ - if (!isnan(this->values[2])) { - Meter_humanUnit(buffer, this->values[2], sizeof(buffer)); + if (!isnan(this->values[MEMORY_METER_SHARED])) { + Meter_humanUnit(buffer, this->values[MEMORY_METER_SHARED], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " shared:"); RichString_appendAscii(out, CRT_colors[MEMORY_SHARED], buffer); } - Meter_humanUnit(buffer, this->values[3], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[MEMORY_METER_CACHE], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[MEMORY_CACHE], buffer); /* available memory is not supported on all platforms */ - if (!isnan(this->values[4])) { - Meter_humanUnit(buffer, this->values[4], sizeof(buffer)); + if (!isnan(this->values[MEMORY_METER_AVAILABLE])) { + Meter_humanUnit(buffer, this->values[MEMORY_METER_AVAILABLE], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " available:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); } diff --git a/MemoryMeter.h b/MemoryMeter.h index d23021e..cc2a160 100644 --- a/MemoryMeter.h +++ b/MemoryMeter.h @@ -9,6 +9,14 @@ in the source distribution for its full text. #include "Meter.h" +typedef enum { + MEMORY_METER_USED = 0, + MEMORY_METER_BUFFERS = 1, + MEMORY_METER_SHARED = 2, + MEMORY_METER_CACHE = 3, + MEMORY_METER_AVAILABLE = 4, + MEMORY_METER_ITEMCOUNT = 5, // number of entries in this enum +} MemoryMeterValues; extern const MeterClass MemoryMeter_class; diff --git a/MetersPanel.c b/MetersPanel.c index 97da630..580e41b 100644 --- a/MetersPanel.c +++ b/MetersPanel.c @@ -33,7 +33,7 @@ static const char* const MetersMovingKeys[] = {"Space", "Enter", "Up", "Dn", "<- static const 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() { +void MetersPanel_cleanup(void) { if (Meters_movingBar) { FunctionBar_delete(Meters_movingBar); Meters_movingBar = NULL; @@ -91,7 +91,7 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { HandlerResult result = IGNORED; bool sideMove = false; - switch(ch) { + switch (ch) { case 0x0a: case 0x0d: case KEY_ENTER: @@ -110,7 +110,8 @@ static HandlerResult MetersPanel_eventHandler(Panel* super, int ch) { break; Meter* meter = (Meter*) Vector_get(this->meters, selected); int mode = meter->mode + 1; - if (mode == LAST_METERMODE) mode = 1; + if (mode == LAST_METERMODE) + mode = 1; Meter_setMode(meter, mode); Panel_set(super, selected, (Object*) Meter_toListItem(meter, this->moving)); result = HANDLED; diff --git a/NetworkIOMeter.c b/NetworkIOMeter.c index dd91b75..d41eafa 100644 --- a/NetworkIOMeter.c +++ b/NetworkIOMeter.c @@ -59,8 +59,8 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.bytesReceived > cached_rxb_total) { diff = data.bytesReceived - cached_rxb_total; - diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */ - diff = (1000 * diff) / passedTimeInMs; /* convert to per second */ + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + diff /= ONE_K; /* convert to KiB/s */ cached_rxb_diff = (uint32_t)diff; } else { cached_rxb_diff = 0; @@ -69,6 +69,7 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.packetsReceived > cached_rxp_total) { diff = data.packetsReceived - cached_rxp_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ cached_rxp_diff = (uint32_t)diff; } else { cached_rxp_diff = 0; @@ -77,8 +78,8 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.bytesTransmitted > cached_txb_total) { diff = data.bytesTransmitted - cached_txb_total; - diff /= ONE_K; /* Meter_humanUnit() expects unit in kilo */ - diff = (1000 * diff) / passedTimeInMs; /* convert to per second */ + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ + diff /= ONE_K; /* convert to KiB/s */ cached_txb_diff = (uint32_t)diff; } else { cached_txb_diff = 0; @@ -87,6 +88,7 @@ static void NetworkIOMeter_updateValues(Meter* this) { if (data.packetsTransmitted > cached_txp_total) { diff = data.packetsTransmitted - cached_txp_total; + diff = (1000 * diff) / passedTimeInMs; /* convert to B/s */ cached_txp_diff = (uint32_t)diff; } else { cached_txp_diff = 0; @@ -112,7 +114,8 @@ static void NetworkIOMeter_updateValues(Meter* this) { char bufferBytesReceived[12], bufferBytesTransmitted[12]; Meter_humanUnit(bufferBytesReceived, cached_rxb_diff, sizeof(bufferBytesReceived)); Meter_humanUnit(bufferBytesTransmitted, cached_txb_diff, sizeof(bufferBytesTransmitted)); - xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "rx:%siB/s tx:%siB/s", bufferBytesReceived, bufferBytesTransmitted); + xSnprintf(this->txtBuffer, sizeof(this->txtBuffer), "rx:%siB/s tx:%siB/s %d/%dpkts/s", + bufferBytesReceived, bufferBytesTransmitted, cached_rxp_diff, cached_txp_diff); } static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { @@ -143,7 +146,7 @@ static void NetworkIOMeter_display(ATTR_UNUSED const Object* cast, RichString* o RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], buffer); RichString_appendAscii(out, CRT_colors[METER_VALUE_IOWRITE], "iB/s"); - len = xSnprintf(buffer, sizeof(buffer), " (%u/%u packets) ", cached_rxp_diff, cached_txp_diff); + len = xSnprintf(buffer, sizeof(buffer), " (%u/%u pkts/s) ", cached_rxp_diff, cached_txp_diff); RichString_appendnAscii(out, CRT_colors[METER_TEXT], buffer, len); } diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c index 787e17b..4256575 100644 --- a/OpenFilesScreen.c +++ b/OpenFilesScreen.c @@ -226,7 +226,7 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) { struct stat st; if (stat(filename, &st) == 0) { char fileSizeBuf[21]; /* 20 (long long) + 1 (NULL) */ - xSnprintf(fileSizeBuf, sizeof(fileSizeBuf), "%"PRIu64, st.st_size); /* st.st_size is long long on macOS, long on linux */ + xSnprintf(fileSizeBuf, sizeof(fileSizeBuf), "%"PRIu64, (uint64_t)st.st_size); /* st.st_size is long long on macOS, long on linux */ free_and_xStrdup(&item->data[fileSizeIndex], fileSizeBuf); } } diff --git a/Panel.c b/Panel.c index 4ea03f6..d1bc6a7 100644 --- a/Panel.c +++ b/Panel.c @@ -443,6 +443,9 @@ bool Panel_onKey(Panel* this, int key) { HandlerResult Panel_selectByTyping(Panel* this, int ch) { int size = Panel_size(this); + if (ch == '#') + return IGNORED; + if (!this->eventHandlerState) this->eventHandlerState = xCalloc(100, sizeof(char)); char* buffer = this->eventHandlerState; @@ -468,7 +471,8 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) { len = strlen(buffer); for (int i = 0; i < size; i++) { const char* cur = ((ListItem*) Panel_get(this, i))->value; - while (*cur == ' ') cur++; + while (*cur == ' ') + cur++; if (strncasecmp(cur, buffer, len) == 0) { Panel_setSelected(this, i); return HANDLED; diff --git a/Process.c b/Process.c index 2a4c809..fcaa3d5 100644 --- a/Process.c +++ b/Process.c @@ -44,7 +44,7 @@ static uid_t Process_getuid = (uid_t)-1; int Process_pidDigits = PROCESS_MIN_PID_DIGITS; int Process_uidDigits = PROCESS_MIN_UID_DIGITS; -void Process_setupColumnWidths() { +void Process_setupColumnWidths(void) { int maxPid = Platform_getMaxPid(); if (maxPid == -1) return; @@ -413,6 +413,7 @@ void Process_makeCommandStr(Process* this) { bool searchCommInCmdline = settings->findCommInCmdline; bool stripExeFromCmdline = settings->stripExeFromCmdline; bool showThreadNames = settings->showThreadNames; + bool shadowDistPathPrefix = settings->shadowDistPathPrefix; uint64_t settingsStamp = settings->lastUpdate; @@ -472,6 +473,56 @@ void Process_makeCommandStr(Process* this) { str = stpcpy(str, SEPARATOR); \ } while (0) + #define CHECK_AND_MARK(str_, prefix_) \ + if (String_startsWith(str_, prefix_)) { \ + WRITE_HIGHLIGHT(0, strlen(prefix_), CRT_colors[PROCESS_SHADOW], CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR); \ + break; \ + } else (void)0 + + #define CHECK_AND_MARK_DIST_PATH_PREFIXES(str_) \ + do { \ + if ((str_)[0] != '/') { \ + break; \ + } \ + switch ((str_)[1]) { \ + case 'b': \ + CHECK_AND_MARK(str_, "/bin/"); \ + break; \ + case 'l': \ + CHECK_AND_MARK(str_, "/lib/"); \ + CHECK_AND_MARK(str_, "/lib32/"); \ + CHECK_AND_MARK(str_, "/lib64/"); \ + CHECK_AND_MARK(str_, "/libx32/"); \ + break; \ + case 's': \ + CHECK_AND_MARK(str_, "/sbin/"); \ + break; \ + case 'u': \ + if (String_startsWith(str_, "/usr/")) { \ + switch ((str_)[5]) { \ + case 'b': \ + CHECK_AND_MARK(str_, "/usr/bin/"); \ + break; \ + case 'l': \ + CHECK_AND_MARK(str_, "/usr/libexec/"); \ + CHECK_AND_MARK(str_, "/usr/lib/"); \ + CHECK_AND_MARK(str_, "/usr/lib32/"); \ + CHECK_AND_MARK(str_, "/usr/lib64/"); \ + CHECK_AND_MARK(str_, "/usr/libx32/"); \ + \ + CHECK_AND_MARK(str_, "/usr/local/bin/"); \ + CHECK_AND_MARK(str_, "/usr/local/lib/"); \ + CHECK_AND_MARK(str_, "/usr/local/sbin/"); \ + break; \ + case 's': \ + CHECK_AND_MARK(str_, "/usr/sbin/"); \ + break; \ + } \ + } \ + break; \ + } \ + } while (0) + const int baseAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_BASENAME] : CRT_colors[PROCESS_BASENAME]; const int commAttr = Process_isThread(this) ? CRT_colors[PROCESS_THREAD_COMM] : CRT_colors[PROCESS_COMM]; const int delExeAttr = CRT_colors[FAILED_READ]; @@ -503,13 +554,16 @@ void Process_makeCommandStr(Process* this) { WRITE_HIGHLIGHT(0, strlen(procComm), commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); str = stpcpy(str, procComm); - if(!showMergedCommand) + if (!showMergedCommand) return; WRITE_SEPARATOR; } } + if (shadowDistPathPrefix && showProgramPath) + CHECK_AND_MARK_DIST_PATH_PREFIXES(cmdline); + if (cmdlineBasenameEnd > cmdlineBasenameStart) WRITE_HIGHLIGHT(showProgramPath ? cmdlineBasenameStart : 0, cmdlineBasenameEnd - cmdlineBasenameStart, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); @@ -537,6 +591,8 @@ void Process_makeCommandStr(Process* this) { /* Start with copying exe */ if (showProgramPath) { + if (shadowDistPathPrefix) + CHECK_AND_MARK_DIST_PATH_PREFIXES(procExe); if (haveCommInExe) WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); WRITE_HIGHLIGHT(exeBasenameOffset, exeBasenameLen, baseAttr, CMDLINE_HIGHLIGHT_FLAG_BASENAME); @@ -594,6 +650,9 @@ void Process_makeCommandStr(Process* this) { WRITE_SEPARATOR; } + if (shadowDistPathPrefix) + CHECK_AND_MARK_DIST_PATH_PREFIXES(cmdline); + if (!haveCommInExe && haveCommInCmdline && !haveCommField && (!Process_isUserlandThread(this) || showThreadNames)) WRITE_HIGHLIGHT(commStart, commEnd - commStart, commAttr, CMDLINE_HIGHLIGHT_FLAG_COMM); @@ -601,6 +660,8 @@ void Process_makeCommandStr(Process* this) { if (*cmdline) (void)stpcpyWithNewlineConversion(str, cmdline); + #undef CHECK_AND_MARK_DIST_PATH_PREFIXES + #undef CHECK_AND_MARK #undef WRITE_SEPARATOR #undef WRITE_HIGHLIGHT } @@ -609,6 +670,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin (void)baseAttr; const ProcessMergedCommand* mc = &this->mergedCommand; + const char* mergedCommand = mc->str; int strStart = RichString_size(str); @@ -616,7 +678,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin const bool highlightSeparator = true; const bool highlightDeleted = this->settings->highlightDeletedExe; - if (!this->mergedCommand.str) { + if (!mergedCommand) { int len = 0; const char* cmdline = this->cmdline; @@ -649,7 +711,7 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin return; } - RichString_appendWide(str, attr, this->mergedCommand.str); + RichString_appendWide(str, attr, mergedCommand); for (size_t i = 0, hlCount = CLAMP(mc->highlightCount, 0, ARRAYSIZE(mc->highlights)); i < hlCount; i++) { const ProcessCmdlineHighlight* hl = &mc->highlights[i]; @@ -669,6 +731,10 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin if (!highlightDeleted) continue; + if (hl->flags & CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR) + if (!highlightDeleted) + continue; + RichString_setAttrn(str, hl->attr, strStart + hl->offset, hl->length); } } @@ -782,19 +848,11 @@ void Process_writeField(const Process* this, RichString* str, ProcessField field } char* buf = buffer; - int maxIndent = 0; - 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)) { - maxIndent = i + 1; - } - } + const bool lastItem = (this->indent < 0); - for (int i = 0; i < maxIndent - 1; i++) { + for (uint32_t indent = (this->indent < 0 ? -this->indent : this->indent); indent > 1; indent >>= 1) { int written, ret; - if (indent & (1 << i)) { + if (indent & 1U) { ret = xSnprintf(buf, n, "%s ", CRT_treeStr[TREE_STR_VERT]); } else { ret = xSnprintf(buf, n, " "); @@ -1248,7 +1306,7 @@ void Process_updateExe(Process* this, const char* exe) { uint8_t Process_fieldWidths[LAST_PROCESSFIELD] = { 0 }; -void Process_resetFieldWidths() { +void Process_resetFieldWidths(void) { for (size_t i = 0; i < LAST_PROCESSFIELD; i++) { if (!Process_fields[i].autoWidth) continue; diff --git a/Process.h b/Process.h index a1ca50f..eb79470 100644 --- a/Process.h +++ b/Process.h @@ -134,6 +134,9 @@ typedef struct Process_ { /* This is a userland thread / LWP */ bool isUserlandThread; + /* This process is running inside a container */ + bool isRunningInContainer; + /* Controlling terminal identifier of the process */ unsigned long int tty_nr; @@ -242,7 +245,7 @@ typedef struct Process_ { /* * Internal state for tree-mode. */ - int indent; + int32_t indent; unsigned int tree_depth; /* Has no known parent process */ @@ -328,6 +331,7 @@ static inline bool Process_isThread(const Process* this) { #define CMDLINE_HIGHLIGHT_FLAG_BASENAME 0x00000002 #define CMDLINE_HIGHLIGHT_FLAG_COMM 0x00000004 #define CMDLINE_HIGHLIGHT_FLAG_DELETED 0x00000008 +#define CMDLINE_HIGHLIGHT_FLAG_PREFIXDIR 0x00000010 #define ONE_K 1024UL #define ONE_M (ONE_K * ONE_K) diff --git a/ProcessList.c b/ProcessList.c index bbaddd8..d115678 100644 --- a/ProcessList.c +++ b/ProcessList.c @@ -199,7 +199,7 @@ static void ProcessList_removeIndex(ProcessList* this, const Process* p, int idx assert(Vector_countEquals(this->processes, Hashtable_count(this->processTable))); } -static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, int indent, bool show) { +static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, unsigned int level, int32_t indent, bool show) { // On OpenBSD the kernel thread 'swapper' has pid 0. // Do not treat it as root of any tree. if (pid == 0) @@ -239,7 +239,7 @@ static void ProcessList_buildTreeBranch(ProcessList* this, pid_t pid, int level, Vector_add(this->displayList, process); - int nextIndent = indent | (1 << level); + int32_t nextIndent = indent | ((int32_t)1 << MINIMUM(level, sizeof(process->indent) * 8 - 2)); ProcessList_buildTreeBranch(this, process->pid, level + 1, (i < lastShown) ? nextIndent : indent, process->show && process->showChildren); if (i == lastShown) { process->indent = -nextIndent; diff --git a/ProcessLocksScreen.c b/ProcessLocksScreen.c index b842d2b..57c9ce7 100644 --- a/ProcessLocksScreen.c +++ b/ProcessLocksScreen.c @@ -27,7 +27,8 @@ ProcessLocksScreen* ProcessLocksScreen_new(const Process* process) { this->pid = process->tgid; else this->pid = process->pid; - return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " ID TYPE EXCLUSION READ/WRITE DEVICE:INODE START END FILENAME"); + + return (ProcessLocksScreen*) InfoScreen_init(&this->super, process, NULL, LINES - 2, " FD TYPE EXCLUSION READ/WRITE DEVICE NODE START END FILENAME"); } void ProcessLocksScreen_delete(Object* this) { @@ -64,18 +65,18 @@ static void ProcessLocksScreen_scan(InfoScreen* this) { 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, + xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %#6"PRIx64" %10"PRIu64" %19"PRIu64" %19s %s", + data->fd, data->locktype, data->exclusive, data->readwrite, - data->dev[0], data->dev[1], data->inode, + (uint64_t) data->dev, data->inode, data->start, "", data->filename ? data->filename : "" ); } else { - xSnprintf(entry, sizeof(entry), "%10d %-10s %-10s %-10s %02x:%02x:%020"PRIu64" %20"PRIu64" %20"PRIu64" %s", - data->id, + xSnprintf(entry, sizeof(entry), "%5d %-10s %-10s %-10s %#6"PRIx64" %10"PRIu64" %19"PRIu64" %19"PRIu64" %s", + data->fd, data->locktype, data->exclusive, data->readwrite, - data->dev[0], data->dev[1], data->inode, + (uint64_t) data->dev, data->inode, data->start, data->end, data->filename ? data->filename : "" ); diff --git a/ProcessLocksScreen.h b/ProcessLocksScreen.h index cf34de4..417df7b 100644 --- a/ProcessLocksScreen.h +++ b/ProcessLocksScreen.h @@ -26,8 +26,8 @@ typedef struct FileLocks_Data_ { char* exclusive; char* readwrite; char* filename; - int id; - unsigned int dev[2]; + int fd; + dev_t dev; uint64_t inode; uint64_t start; uint64_t end; diff --git a/README b/README index 55372b4..7ace7cd 100644 --- a/README +++ b/README @@ -34,6 +34,7 @@ List of build-time dependencies: * standard GNU autotools-based C toolchain - C99 compliant compiler - `autoconf` + - `automake` - `autotools` * `ncurses` @@ -54,7 +55,7 @@ Install these and other required packages for C development from your package ma **Debian/Ubuntu** ~~~ shell -sudo apt install libncursesw5-dev autotools-dev autoconf build-essential +sudo apt install libncursesw5-dev autotools-dev autoconf automake build-essential ~~~ **Fedora/RHEL** diff --git a/RichString.c b/RichString.c index 3dac083..daa0c91 100644 --- a/RichString.c +++ b/RichString.c @@ -61,7 +61,7 @@ static inline int RichString_writeFromWide(RichString* this, int attrs, const ch int newLen = from + len; RichString_setLen(this, newLen); for (int i = from, j = 0; i < newLen; i++, j++) { - this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : '?') } }; + this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (iswprint(data[j]) ? data[j] : L'\xFFFD') } }; } return len; @@ -79,7 +79,7 @@ int RichString_appendnWideColumns(RichString* this, int attrs, const char* data_ int columnsWritten = 0; int pos = from; for (int j = 0; j < len; j++) { - wchar_t c = iswprint(data[j]) ? data[j] : '?'; + wchar_t c = iswprint(data[j]) ? data[j] : L'\xFFFD'; int cwidth = wcwidth(c); if (cwidth > *columns) break; @@ -101,7 +101,7 @@ static inline int RichString_writeFromAscii(RichString* this, int attrs, const c int newLen = from + len; RichString_setLen(this, newLen); for (int i = from, j = 0; i < newLen; i++, j++) { - this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : '?') } }; + this->chptr[i] = (CharType) { .attr = attrs & 0xffffff, .chars = { (isprint(data[j]) ? data[j] : L'\xFFFD') } }; } return len; diff --git a/ScreenManager.c b/ScreenManager.c index e4b04bd..55cacd2 100644 --- a/ScreenManager.c +++ b/ScreenManager.c @@ -24,7 +24,7 @@ in the source distribution for its full text. #include "XUtils.h" -ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner) { +ScreenManager* ScreenManager_new(Header* header, const Settings* settings, State* state, bool owner) { ScreenManager* this; this = xMalloc(sizeof(ScreenManager)); this->x1 = 0; @@ -53,18 +53,28 @@ void ScreenManager_add(ScreenManager* this, Panel* item, int size) { ScreenManager_insert(this, item, size, Vector_size(this->panels)); } +static int header_height(const ScreenManager* this) { + if (this->state->hideMeters) + return 0; + + if (this->header) + return this->header->height; + + return 0; +} + void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) { int lastX = 0; if (idx > 0) { const Panel* last = (const Panel*) Vector_get(this->panels, idx - 1); lastX = last->x + last->w + 1; } - int height = LINES - this->y1 - (this->header ? this->header->height : 0) + this->y2; + int height = LINES - this->y1 - header_height(this) + this->y2; if (size <= 0) { size = COLS - this->x1 + this->x2 - lastX; } Panel_resize(item, size, height); - Panel_move(item, lastX, this->y1 + (this->header ? this->header->height : 0)); + Panel_move(item, lastX, this->y1 + header_height(this)); if (idx < this->panelCount) { for (int i = idx + 1; i <= this->panelCount; i++) { Panel* p = (Panel*) Vector_get(this->panels, i); @@ -91,7 +101,7 @@ Panel* ScreenManager_remove(ScreenManager* this, int idx) { } void ScreenManager_resize(ScreenManager* this) { - int y1_header = this->y1 + (this->header ? this->header->height : 0); + int y1_header = this->y1 + header_height(this); int panels = this->panelCount; int lastX = 0; for (int i = 0; i < panels - 1; i++) { @@ -105,7 +115,7 @@ void ScreenManager_resize(ScreenManager* this) { Panel_move(panel, lastX, y1_header); } -static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool *force_redraw) { +static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTimeout, bool* redraw, bool* rescan, bool* timedOut, bool* force_redraw) { ProcessList* pl = this->header->pl; Platform_gettime_realtime(&pl->realtime, &pl->realtimeMs); @@ -137,7 +147,8 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi } if (*redraw) { ProcessList_rebuildPanel(pl); - Header_draw(this->header); + if (!this->state->hideMeters) + Header_draw(this->header); } *rescan = false; } @@ -375,6 +386,11 @@ tryRight: goto tryRight; } + break; + case '#': + this->state->hideMeters = !this->state->hideMeters; + ScreenManager_resize(this); + force_redraw = true; break; case 27: case 'q': diff --git a/ScreenManager.h b/ScreenManager.h index 978b524..d08a941 100644 --- a/ScreenManager.h +++ b/ScreenManager.h @@ -26,11 +26,11 @@ typedef struct ScreenManager_ { int panelCount; Header* header; const Settings* settings; - const State* state; + State* state; bool allowFocusChange; } ScreenManager; -ScreenManager* ScreenManager_new(Header* header, const Settings* settings, const State* state, bool owner); +ScreenManager* ScreenManager_new(Header* header, const Settings* settings, State* state, bool owner); void ScreenManager_delete(ScreenManager* this); diff --git a/ScreensPanel.c b/ScreensPanel.c index 785c387..cb664ac 100644 --- a/ScreensPanel.c +++ b/ScreensPanel.c @@ -55,6 +55,10 @@ static void ScreensPanel_delete(Object* object) { item->ss = NULL; } + /* during renaming the ListItem's value points to our static buffer */ + if (this->renamingItem) + this->renamingItem->value = this->saved; + Panel_done(super); free(this); } @@ -70,7 +74,7 @@ static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) { Panel_setCursorToSelection(super); } } else { - switch(ch) { + switch (ch) { case 127: case KEY_BACKSPACE: { @@ -89,9 +93,10 @@ static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) { ListItem* item = (ListItem*) Panel_getSelected(super); if (!item) break; + assert(item == this->renamingItem); free(this->saved); item->value = xStrdup(this->buffer); - this->renaming = false; + this->renamingItem = NULL; super->cursorOn = false; Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS); ScreensPanel_update(super); @@ -102,8 +107,9 @@ static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) { ListItem* item = (ListItem*) Panel_getSelected(super); if (!item) break; + assert(item == this->renamingItem); item->value = this->saved; - this->renaming = false; + this->renamingItem = NULL; super->cursorOn = false; Panel_setSelectionColor(super, PANEL_SELECTION_FOCUS); break; @@ -119,7 +125,7 @@ static void startRenaming(Panel* super) { ListItem* item = (ListItem*) Panel_getSelected(super); if (item == NULL) return; - this->renaming = true; + this->renamingItem = item; super->cursorOn = true; char* name = item->value; this->saved = name; @@ -132,7 +138,7 @@ static void startRenaming(Panel* super) { Panel_setCursorToSelection(super); } -static void rebuildSettingsArray(Panel* super) { +static void rebuildSettingsArray(Panel* super, int selected) { ScreensPanel* const this = (ScreensPanel*) super; int n = Panel_size(super); @@ -144,13 +150,20 @@ static void rebuildSettingsArray(Panel* super) { this->settings->screens[i] = item->ss; } this->settings->nScreens = n; + /* ensure selection is in valid range */ + if (selected > n - 1) + selected = n - 1; + else if (selected < 0) + selected = 0; + this->settings->ssIndex = selected; + this->settings->ss = this->settings->screens[selected]; } static void addNewScreen(Panel* super) { ScreensPanel* const this = (ScreensPanel*) super; const char* name = "New"; - ScreenSettings* ss = Settings_newScreen(this->settings, &(const ScreenDefaults){ .name = name, .columns = "PID Command", .sortKey = "PID" }); + ScreenSettings* ss = Settings_newScreen(this->settings, &(const ScreenDefaults) { .name = name, .columns = "PID Command", .sortKey = "PID" }); ScreenListItem* item = ScreenListItem_new(name, ss); int idx = Panel_getSelectedIndex(super); Panel_insert(super, idx + 1, (Object*) item); @@ -164,7 +177,7 @@ static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) { ScreenListItem* oldFocus = (ScreenListItem*) Panel_getSelected(super); bool shouldRebuildArray = false; HandlerResult result = IGNORED; - switch(ch) { + switch (ch) { case '\n': case '\r': case KEY_ENTER: @@ -242,9 +255,8 @@ static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) { case KEY_F(9): //case KEY_DC: { - if (Panel_size(super) > 1) { + if (Panel_size(super) > 1) Panel_remove(super, selected); - } shouldRebuildArray = true; result = HANDLED; break; @@ -264,7 +276,7 @@ static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) { result = HANDLED; } if (shouldRebuildArray) - rebuildSettingsArray(super); + rebuildSettingsArray(super, selected); if (result == HANDLED) ScreensPanel_update(super); return result; @@ -273,7 +285,7 @@ static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) { static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) { ScreensPanel* const this = (ScreensPanel*) super; - if (this->renaming) { + if (this->renamingItem) { return ScreensPanel_eventHandlerRenaming(super, ch); } else { return ScreensPanel_eventHandlerNormal(super, ch); @@ -298,7 +310,7 @@ ScreensPanel* ScreensPanel_new(Settings* settings) { this->settings = settings; this->columns = ColumnsPanel_new(settings->screens[0], columns, &(settings->changed)); this->moving = false; - this->renaming = false; + this->renamingItem = NULL; super->cursorOn = false; this->cursor = 0; Panel_setHeader(super, "Screens"); diff --git a/ScreensPanel.h b/ScreensPanel.h index 1f82395..88d85b5 100644 --- a/ScreensPanel.h +++ b/ScreensPanel.h @@ -31,7 +31,7 @@ typedef struct ScreensPanel_ { char* saved; int cursor; bool moving; - bool renaming; + ListItem *renamingItem; } ScreensPanel; typedef struct ScreenListItem_ { diff --git a/Settings.c b/Settings.c index 7d6fca4..8543b9e 100644 --- a/Settings.c +++ b/Settings.c @@ -10,6 +10,7 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include @@ -261,10 +262,9 @@ static void ScreenSettings_readFields(ScreenSettings* ss, Hashtable* columns, co } int id = toFieldIndex(columns, ids[i]); if (id >= 0) - ss->fields[j] = id; + ss->fields[j++] = id; if (id > 0 && id < LAST_PROCESSFIELD) ss->flags |= Process_fields[id].flags; - j++; } String_freeArray(ids); } @@ -381,6 +381,8 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini this->hideKernelThreads = atoi(option[1]); } else if (String_eq(option[0], "hide_userland_threads")) { this->hideUserlandThreads = atoi(option[1]); + } else if (String_eq(option[0], "hide_running_in_container")) { + this->hideRunningInContainer = atoi(option[1]); } else if (String_eq(option[0], "shadow_other_users")) { this->shadowOtherUsers = atoi(option[1]); } else if (String_eq(option[0], "show_thread_names")) { @@ -391,6 +393,8 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini this->highlightBaseName = atoi(option[1]); } else if (String_eq(option[0], "highlight_deleted_exe")) { this->highlightDeletedExe = atoi(option[1]); + } else if (String_eq(option[0], "shadow_distribution_path_prefix")) { + this->shadowDistPathPrefix = atoi(option[1]); } else if (String_eq(option[0], "highlight_megabytes")) { this->highlightMegabytes = atoi(option[1]); } else if (String_eq(option[0], "highlight_threads")) { @@ -475,13 +479,17 @@ static bool Settings_read(Settings* this, const char* fileName, unsigned int ini this->topologyAffinity = !!atoi(option[1]); #endif } else if (strncmp(option[0], "screen:", 7) == 0) { - screen = Settings_newScreen(this, &(const ScreenDefaults){ .name = option[0] + 7, .columns = option[1] }); + screen = Settings_newScreen(this, &(const ScreenDefaults) { .name = option[0] + 7, .columns = option[1] }); } else if (String_eq(option[0], ".sort_key")) { - if (screen) - screen->sortKey = toFieldIndex(this->dynamicColumns, option[1]); + if (screen) { + int key = toFieldIndex(this->dynamicColumns, option[1]); + screen->sortKey = key > 0 ? key : PID; + } } else if (String_eq(option[0], ".tree_sort_key")) { - if (screen) - screen->treeSortKey = toFieldIndex(this->dynamicColumns, option[1]); + if (screen) { + int key = toFieldIndex(this->dynamicColumns, option[1]); + screen->treeSortKey = key > 0 ? key : PID; + } } else if (String_eq(option[0], ".sort_direction")) { if (screen) screen->direction = atoi(option[1]); @@ -575,11 +583,13 @@ int Settings_write(const Settings* this, bool onCrash) { fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, this->dynamicColumns, false, separator); printSettingInteger("hide_kernel_threads", this->hideKernelThreads); printSettingInteger("hide_userland_threads", this->hideUserlandThreads); + printSettingInteger("hide_running_in_container", this->hideRunningInContainer); printSettingInteger("shadow_other_users", this->shadowOtherUsers); printSettingInteger("show_thread_names", this->showThreadNames); printSettingInteger("show_program_path", this->showProgramPath); printSettingInteger("highlight_base_name", this->highlightBaseName); printSettingInteger("highlight_deleted_exe", this->highlightDeletedExe); + printSettingInteger("shadow_distribution_path_prefix", this->shadowDistPathPrefix); printSettingInteger("highlight_megabytes", this->highlightMegabytes); printSettingInteger("highlight_threads", this->highlightThreads); printSettingInteger("highlight_changes", this->highlightChanges); @@ -668,8 +678,10 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) this->showThreadNames = false; this->hideKernelThreads = true; this->hideUserlandThreads = false; + this->hideRunningInContainer = false; this->highlightBaseName = false; this->highlightDeletedExe = true; + this->shadowDistPathPrefix = false; this->highlightMegabytes = true; this->detailedCPUTime = false; this->countCPUsFromOne = false; @@ -702,9 +714,10 @@ Settings* Settings_new(unsigned int initialCpuCount, Hashtable* dynamicColumns) this->filename = xStrdup(rcfile); } else { const char* home = getenv("HOME"); - if (!home) - home = ""; - + if (!home) { + const struct passwd* pw = getpwuid(getuid()); + home = pw ? pw->pw_dir : ""; + } const char* xdgConfigHome = getenv("XDG_CONFIG_HOME"); char* configDir = NULL; char* htopDir = NULL; diff --git a/Settings.h b/Settings.h index facd3f2..baf05da 100644 --- a/Settings.h +++ b/Settings.h @@ -73,9 +73,11 @@ typedef struct Settings_ { bool shadowOtherUsers; bool showThreadNames; bool hideKernelThreads; + bool hideRunningInContainer; bool hideUserlandThreads; bool highlightBaseName; bool highlightDeletedExe; + bool shadowDistPathPrefix; bool highlightMegabytes; bool highlightThreads; bool highlightChanges; diff --git a/SwapMeter.c b/SwapMeter.c index 081967f..c0f4820 100644 --- a/SwapMeter.c +++ b/SwapMeter.c @@ -45,12 +45,12 @@ static void SwapMeter_display(const Object* cast, RichString* out) { RichString_writeAscii(out, CRT_colors[METER_TEXT], ":"); Meter_humanUnit(buffer, this->total, sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); - Meter_humanUnit(buffer, this->values[0], sizeof(buffer)); + Meter_humanUnit(buffer, this->values[SWAP_METER_USED], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " used:"); RichString_appendAscii(out, CRT_colors[METER_VALUE], buffer); - if (!isnan(this->values[1])) { - Meter_humanUnit(buffer, this->values[1], sizeof(buffer)); + if (!isnan(this->values[SWAP_METER_CACHE])) { + Meter_humanUnit(buffer, this->values[SWAP_METER_CACHE], sizeof(buffer)); RichString_appendAscii(out, CRT_colors[METER_TEXT], " cache:"); RichString_appendAscii(out, CRT_colors[SWAP_CACHE], buffer); } diff --git a/SwapMeter.h b/SwapMeter.h index 485485a..b71e83f 100644 --- a/SwapMeter.h +++ b/SwapMeter.h @@ -9,6 +9,11 @@ in the source distribution for its full text. #include "Meter.h" +typedef enum { + SWAP_METER_USED = 0, + SWAP_METER_CACHE = 1, + SWAP_METER_ITEMCOUNT = 2, // number of entries in this enum +} SwapMeterValues; extern const MeterClass SwapMeter_class; diff --git a/TraceScreen.c b/TraceScreen.c index 90400b4..2aa0781 100644 --- a/TraceScreen.c +++ b/TraceScreen.c @@ -164,17 +164,17 @@ static void TraceScreen_updateTrace(InfoScreen* super) { static bool TraceScreen_onKey(InfoScreen* super, int ch) { TraceScreen* this = (TraceScreen*) super; - switch(ch) { + switch (ch) { case 'f': case KEY_F(8): this->follow = !(this->follow); if (this->follow) - Panel_setSelected(super->display, Panel_size(super->display)-1); + Panel_setSelected(super->display, Panel_size(super->display) - 1); return true; case 't': case KEY_F(9): this->tracing = !this->tracing; - FunctionBar_setLabel(super->display->defaultBar, KEY_F(9), this->tracing?"Stop Tracing ":"Resume Tracing "); + FunctionBar_setLabel(super->display->defaultBar, KEY_F(9), this->tracing ? "Stop Tracing " : "Resume Tracing "); InfoScreen_draw(this); return true; } diff --git a/UsersTable.c b/UsersTable.c index 8c4a0ed..6586a4b 100644 --- a/UsersTable.c +++ b/UsersTable.c @@ -17,7 +17,7 @@ in the source distribution for its full text. #include "XUtils.h" -UsersTable* UsersTable_new() { +UsersTable* UsersTable_new(void) { UsersTable* this; this = xMalloc(sizeof(UsersTable)); this->users = Hashtable_new(10, true); diff --git a/XUtils.c b/XUtils.c index 2012b6c..6a021f3 100644 --- a/XUtils.c +++ b/XUtils.c @@ -21,7 +21,7 @@ in the source distribution for its full text. #include "CRT.h" -void fail() { +void fail(void) { CRT_done(); abort(); @@ -104,9 +104,9 @@ inline bool String_contains_i(const char* s1, const char* s2, bool multi) { String_freeArray(needles); return true; } - } - String_freeArray(needles); - return false; + } + String_freeArray(needles); + return false; } else { return strcasestr(s1, s2) != NULL; } @@ -313,3 +313,26 @@ ssize_t xReadfileat(openat_arg_t dirfd, const char* pathname, void* buffer, size return readfd_internal(fd, buffer, count); } + +ssize_t full_write(int fd, const void* buf, size_t count) { + ssize_t written = 0; + + while (count > 0) { + ssize_t r = write(fd, buf, count); + if (r < 0) { + if (errno == EINTR) + continue; + + return r; + } + + if (r == 0) + break; + + written += r; + buf = (const unsigned char*)buf + r; + count -= (size_t)r; + } + + return written; +} diff --git a/XUtils.h b/XUtils.h index 2522a71..e1c50be 100644 --- a/XUtils.h +++ b/XUtils.h @@ -73,4 +73,6 @@ char* xStrndup(const char* str, size_t len) ATTR_NONNULL ATTR_MALLOC; 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); +ssize_t full_write(int fd, const void* buf, size_t count); + #endif diff --git a/configure.ac b/configure.ac index 7a52ab5..bbb042a 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ # ---------------------------------------------------------------------- AC_PREREQ([2.69]) -AC_INIT([htop], [3.2.1], [htop@groups.io], [], [https://htop.dev/]) +AC_INIT([htop], [3.2.2], [htop@groups.io], [], [https://htop.dev/]) AC_CONFIG_SRCDIR([htop.c]) AC_CONFIG_AUX_DIR([build-aux]) @@ -212,7 +212,7 @@ AC_RUN_IFELSE([ # Checks for generic library functions. # ---------------------------------------------------------------------- -AC_CHECK_LIB([m], [ceil], [], [AC_MSG_ERROR([can not find required function ceil()])]) +AC_SEARCH_LIBS([ceil], [m], [], [AC_MSG_ERROR([can not find required function ceil()])]) if test "$my_htop_platform" = dragonflybsd; then AC_SEARCH_LIBS([kvm_open], [kvm], [], [AC_MSG_ERROR([can not find required function kvm_open()])]) @@ -266,6 +266,10 @@ if test "$my_htop_platform" = darwin; then AC_CHECK_FUNCS([mach_timebase_info]) fi +if test "$my_htop_platform" = pcp; then + AC_CHECK_FUNCS([pmLookupDescs]) +fi + if test "$my_htop_platform" = linux && test "x$enable_static" = xyes; then AC_CHECK_LIB([systemd], [sd_bus_open_system]) fi @@ -723,7 +727,7 @@ AC_SUBST([AM_CPPFLAGS]) # We're done, let's go! # ---------------------------------------------------------------------- -AC_DEFINE_UNQUOTED([COPYRIGHT], ["(C) 2004-2019 Hisham Muhammad. (C) 2020-2022 htop dev team."], [Copyright message.]) +AC_DEFINE_UNQUOTED([COPYRIGHT], ["(C) 2004-2019 Hisham Muhammad. (C) 2020-2023 htop dev team."], [Copyright message.]) AM_CONDITIONAL([HTOP_LINUX], [test "$my_htop_platform" = linux]) AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd]) diff --git a/darwin/DarwinProcess.c b/darwin/DarwinProcess.c index d0204dd..6027c25 100644 --- a/darwin/DarwinProcess.c +++ b/darwin/DarwinProcess.c @@ -282,7 +282,7 @@ static char* DarwinProcess_getDevname(dev_t dev) { return NULL; } char buf[sizeof("/dev/") + MAXNAMLEN]; - char *name = devname_r(dev, S_IFCHR, buf, MAXNAMLEN); + char* name = devname_r(dev, S_IFCHR, buf, MAXNAMLEN); if (name) { return xStrdup(name); } diff --git a/darwin/DarwinProcessList.c b/darwin/DarwinProcessList.c index bd7821b..dae588b 100644 --- a/darwin/DarwinProcessList.c +++ b/darwin/DarwinProcessList.c @@ -68,12 +68,13 @@ 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; - for (int retry = 3; retry > 0; retry--) { + for (unsigned int retry = 0; retry < 4; 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"); } + size += 16 * retry * retry * sizeof(struct kinfo_proc); processes = xRealloc(processes, size); if (sysctl(mib, 4, processes, &size, NULL, 0) == 0) { diff --git a/darwin/Platform.c b/darwin/Platform.c index 4b34d88..332752b 100644 --- a/darwin/Platform.c +++ b/darwin/Platform.c @@ -172,7 +172,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -200,7 +200,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { } } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { /* http://opensource.apple.com/source/xnu/xnu-2782.1.97/bsd/sys/proc_internal.hh */ return 99999; } @@ -264,11 +264,11 @@ void Platform_setMemoryValues(Meter* mtr) { double page_K = (double)vm_page_size / (double)1024; mtr->total = dpl->host_info.max_mem / 1024; - mtr->values[0] = (double)(vm->active_count + vm->wire_count) * page_K; - mtr->values[1] = (double)vm->purgeable_count * page_K; - // mtr->values[2] = "shared memory, like tmpfs and shm" - mtr->values[3] = (double)vm->inactive_count * page_K; - // mtr->values[4] = "available memory" + mtr->values[MEMORY_METER_USED] = (double)(vm->active_count + vm->wire_count) * page_K; + mtr->values[MEMORY_METER_BUFFERS] = (double)vm->purgeable_count * page_K; + // mtr->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + mtr->values[MEMORY_METER_CACHE] = (double)vm->inactive_count * page_K; + // mtr->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* mtr) { @@ -278,7 +278,7 @@ void Platform_setSwapValues(Meter* mtr) { sysctl(mib, 2, &swapused, &swlen, NULL, 0); mtr->total = swapused.xsu_total / 1024; - mtr->values[0] = swapused.xsu_used / 1024; + mtr->values[SWAP_METER_USED] = swapused.xsu_used / 1024; } void Platform_setZfsArcValues(Meter* this) { @@ -344,12 +344,6 @@ 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; diff --git a/darwin/Platform.h b/darwin/Platform.h index 4f8e7c9..6636207 100644 --- a/darwin/Platform.h +++ b/darwin/Platform.h @@ -68,8 +68,6 @@ 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); @@ -100,7 +98,9 @@ static inline void Platform_gettime_realtime(struct timeval* tv, uint64_t* msec) void Platform_gettime_monotonic(uint64_t* msec); -static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } @@ -110,12 +110,18 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } -static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } -static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } +static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { + return NULL; +} -static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} #endif diff --git a/darwin/PlatformHelpers.c b/darwin/PlatformHelpers.c index bde9068..97f0741 100644 --- a/darwin/PlatformHelpers.c +++ b/darwin/PlatformHelpers.c @@ -58,7 +58,7 @@ bool Platform_KernelVersionIsBetween(KernelVersion lowerBound, KernelVersion upp && Platform_CompareKernelVersion(upperBound) < 0; } -void Platform_getCPUBrandString(char *cpuBrandString, size_t cpuBrandStringSize) { +void Platform_getCPUBrandString(char* cpuBrandString, size_t cpuBrandStringSize) { if (sysctlbyname("machdep.cpu.brand_string", cpuBrandString, &cpuBrandStringSize, NULL, 0) == -1) { fprintf(stderr, "WARN: Unable to determine the CPU brand string.\n" @@ -69,12 +69,13 @@ void Platform_getCPUBrandString(char *cpuBrandString, size_t cpuBrandStringSize) } // Adapted from https://developer.apple.com/documentation/apple-silicon/about-the-rosetta-translation-environment -bool Platform_isRunningTranslated() { +bool Platform_isRunningTranslated(void) { int ret = 0; size_t size = sizeof(ret); errno = 0; if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) { - if (errno == ENOENT) return false; + if (errno == ENOENT) + return false; fprintf(stderr, "WARN: Could not determine if this process was running in a translation environment like Rosetta 2.\n" @@ -86,7 +87,7 @@ bool Platform_isRunningTranslated() { return ret; } -double Platform_calculateNanosecondsPerMachTick() { +double Platform_calculateNanosecondsPerMachTick(void) { // Check if we can determine the timebase used on this system. // If the API is unavailable assume we get our timebase in nanoseconds. #ifndef HAVE_MACH_TIMEBASE_INFO diff --git a/darwin/PlatformHelpers.h b/darwin/PlatformHelpers.h index 07f3fe2..45aea1a 100644 --- a/darwin/PlatformHelpers.h +++ b/darwin/PlatformHelpers.h @@ -31,7 +31,7 @@ bool Platform_KernelVersionIsBetween(KernelVersion lowerBound, KernelVersion upp double Platform_calculateNanosecondsPerMachTick(void); -void Platform_getCPUBrandString(char *cpuBrandString, size_t cpuBrandStringSize); +void Platform_getCPUBrandString(char* cpuBrandString, size_t cpuBrandStringSize); bool Platform_isRunningTranslated(void); diff --git a/docs/images/screenshot.png b/docs/images/screenshot.png index 0ff3cfe..85cb9dd 100644 Binary files a/docs/images/screenshot.png and b/docs/images/screenshot.png differ diff --git a/dragonflybsd/DragonFlyBSDProcessList.c b/dragonflybsd/DragonFlyBSDProcessList.c index 0d0e1a4..f46d6ce 100644 --- a/dragonflybsd/DragonFlyBSDProcessList.c +++ b/dragonflybsd/DragonFlyBSDProcessList.c @@ -509,7 +509,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { proc->m_virt = kproc->kp_vm_map_size / ONE_K; proc->m_resident = kproc->kp_vm_rssize * pageSizeKb; proc->nlwp = kproc->kp_nthreads; // number of lwp thread - proc->time = (kproc->kp_swtime + 5000) / 10000; + proc->time = (kproc->kp_lwp.kl_uticks + kproc->kp_lwp.kl_sticks + kproc->kp_lwp.kl_iticks) / 10000; proc->percent_cpu = 100.0 * ((double)kproc->kp_lwp.kl_pctcpu / (double)kernelFScale); proc->percent_mem = 100.0 * proc->m_resident / (double)(super->totalMem); @@ -527,7 +527,7 @@ void ProcessList_goThroughEntries(ProcessList* super, bool pauseProcessUpdate) { else proc->priority = -kproc->kp_lwp.kl_tdprio; - switch(kproc->kp_lwp.kl_rtprio.type) { + switch (kproc->kp_lwp.kl_rtprio.type) { case RTP_PRIO_REALTIME: proc->nice = PRIO_MIN - 1 - RTP_PRIO_MAX + kproc->kp_lwp.kl_rtprio.prio; break; diff --git a/dragonflybsd/Platform.c b/dragonflybsd/Platform.c index ea5f4fa..8a684d8 100644 --- a/dragonflybsd/Platform.c +++ b/dragonflybsd/Platform.c @@ -126,7 +126,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -157,7 +157,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { int maxPid; size_t size = sizeof(maxPid); int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0); @@ -208,18 +208,18 @@ void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = pl->cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } char* Platform_getProcessEnv(pid_t pid) { @@ -228,12 +228,6 @@ char* Platform_getProcessEnv(pid_t 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; diff --git a/dragonflybsd/Platform.h b/dragonflybsd/Platform.h index ec140ad..cf69356 100644 --- a/dragonflybsd/Platform.h +++ b/dragonflybsd/Platform.h @@ -59,8 +59,6 @@ void Platform_setSwapValues(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); @@ -93,7 +91,9 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } -static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } @@ -103,12 +103,18 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } -static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } -static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } +static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { + return NULL; +} -static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} #endif diff --git a/freebsd/FreeBSDProcessList.c b/freebsd/FreeBSDProcessList.c index f58f338..2c7e20b 100644 --- a/freebsd/FreeBSDProcessList.c +++ b/freebsd/FreeBSDProcessList.c @@ -25,6 +25,7 @@ in the source distribution for its full text. #include #include #include +#include #include "CRT.h" #include "Compat.h" @@ -49,6 +50,7 @@ static int MIB_vm_stats_vm_v_active_count[4]; static int MIB_vm_stats_vm_v_cache_count[4]; static int MIB_vm_stats_vm_v_inactive_count[4]; static int MIB_vm_stats_vm_v_free_count[4]; +static int MIB_vm_vmtotal[2]; static int MIB_vfs_bufspace[2]; @@ -82,6 +84,7 @@ ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* dynamicMeters, H len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", MIB_vm_stats_vm_v_cache_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_inactive_count", MIB_vm_stats_vm_v_inactive_count, &len); len = 4; sysctlnametomib("vm.stats.vm.v_free_count", MIB_vm_stats_vm_v_free_count, &len); + len = 2; sysctlnametomib("vm.vmtotal", MIB_vm_vmtotal, &len); len = 2; sysctlnametomib("vfs.bufspace", MIB_vfs_bufspace, &len); @@ -330,6 +333,7 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { u_int memActive, memWire, cachedMem; long buffersMem; size_t len; + struct vmtotal vmtotal; //disabled for now, as it is always smaller than phycal amount of memory... //...to avoid "where is my memory?" questions @@ -360,6 +364,10 @@ static inline void FreeBSDProcessList_scanMemoryInfo(ProcessList* pl) { cachedMem *= pageSizeKb; pl->cachedMem = cachedMem; + len = sizeof(vmtotal); + sysctl(MIB_vm_vmtotal, 2, &(vmtotal), &len, NULL, 0); + pl->sharedMem = vmtotal.t_vmshr * pageSizeKb; + if (fpl->zfs.enabled) { fpl->memWire -= fpl->zfs.size; pl->cachedMem += fpl->zfs.size; @@ -397,6 +405,7 @@ static void FreeBSDProcessList_updateExe(const struct kinfo_proc* kproc, Process } static void FreeBSDProcessList_updateCwd(const struct kinfo_proc* kproc, Process* proc) { +#ifdef KERN_PROC_CWD const int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_CWD, kproc->ki_pid }; char buffer[2048]; size_t size = sizeof(buffer); @@ -414,6 +423,9 @@ static void FreeBSDProcessList_updateCwd(const struct kinfo_proc* kproc, Process } free_and_xStrdup(&proc->procCwd, buffer); +#else + proc->procCwd = NULL; +#endif } static void FreeBSDProcessList_updateProcessName(kvm_t* kd, const struct kinfo_proc* kproc, Process* proc) { diff --git a/freebsd/Platform.c b/freebsd/Platform.c index 1a731b7..646163a 100644 --- a/freebsd/Platform.c +++ b/freebsd/Platform.c @@ -148,7 +148,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; const int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -179,7 +179,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { int maxPid; size_t size = sizeof(maxPid); int err = sysctlbyname("kern.pid_max", &maxPid, &size, NULL, 0); @@ -230,28 +230,28 @@ void Platform_setMemoryValues(Meter* this) { const FreeBSDProcessList* fpl = (const FreeBSDProcessList*) pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = pl->cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + this->values[MEMORY_METER_SHARED] = pl->sharedMem; + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" if (fpl->zfs.enabled) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; if (fpl->zfs.size > fpl->zfs.min) shrinkableSize = fpl->zfs.size - fpl->zfs.min; - this->values[0] -= shrinkableSize; - this->values[3] += shrinkableSize; - // this->values[4] += shrinkableSize; + this->values[MEMORY_METER_USED] -= shrinkableSize; + this->values[MEMORY_METER_CACHE] += shrinkableSize; + // this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; } } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } void Platform_setZfsArcValues(Meter* this) { @@ -287,12 +287,6 @@ 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; diff --git a/freebsd/Platform.h b/freebsd/Platform.h index c0292d9..51269c0 100644 --- a/freebsd/Platform.h +++ b/freebsd/Platform.h @@ -59,8 +59,6 @@ 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); @@ -93,7 +91,9 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } -static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } @@ -103,12 +103,18 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } -static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} -static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } +static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { + return NULL; +} static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } -static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} #endif diff --git a/htop.1.in b/htop.1.in index c81c819..eefc37f 100644 --- a/htop.1.in +++ b/htop.1.in @@ -1,4 +1,4 @@ -.TH "HTOP" "1" "2022" "@PACKAGE_STRING@" "User Commands" +.TH "HTOP" "1" "2023" "@PACKAGE_STRING@" "User Commands" .SH "NAME" htop, pcp-htop \- interactive process viewer .SH "SYNOPSIS" @@ -327,7 +327,8 @@ The process ID. .TP .B STATE (S) The state of the process: - \fBS\fR for sleeping (idle) + \fBS\fR for sleeping + \fBI\fR for idle (longer inactivity than sleeping on platforms that distinguish) \fBR\fR for running \fBD\fR for disk sleep (uninterruptible) \fBZ\fR for zombie (waiting for parent to read its exit status) @@ -668,7 +669,7 @@ communities, and forms part of the Performance Co-Pilot suite of tools. .SH "COPYRIGHT" Copyright \(co 2004-2019 Hisham Muhammad. .br -Copyright \(co 2020-2022 htop dev team. +Copyright \(co 2020-2023 htop dev team. .LP License GPLv2+: GNU General Public License version 2 or, at your option, any later version. .LP diff --git a/htop.png b/htop.png index 90020ad..7809d22 100644 Binary files a/htop.png and b/htop.png differ diff --git a/linux/CGroupUtils.c b/linux/CGroupUtils.c index 22cce91..c11c460 100644 --- a/linux/CGroupUtils.c +++ b/linux/CGroupUtils.c @@ -11,7 +11,7 @@ in the source distribution for its full text. typedef struct StrBuf_state { - char *buf; + char* buf; size_t size; size_t pos; } StrBuf_state; @@ -60,7 +60,7 @@ static bool Label_checkSuffix(const char* labelStart, size_t labelLen, const cha return labelLen > strlen(expected) && String_startsWith(labelStart + labelLen - strlen(expected), expected); } -static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrBuf_putc_t w) { +static bool CGroup_filterName_internal(const char* cgroup, StrBuf_state* s, StrBuf_putc_t w) { const char* str_slice_suffix = ".slice"; const char* str_system_slice = "system.slice"; const char* str_user_slice = "user.slice"; @@ -237,7 +237,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB if (String_startsWith(cgroup, "user@")) { cgroup = nextSlash; - while(*cgroup == '/') + while (*cgroup == '/') cgroup++; continue; @@ -275,7 +275,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB cgroup += strlen(str_nspawn_payload_label); continue; - } else if(Label_checkPrefix(labelStart, scopeNameLen, str_snap_scope_prefix)) { + } else if (Label_checkPrefix(labelStart, scopeNameLen, str_snap_scope_prefix)) { const char* nextDot = strchrnul(labelStart + strlen(str_snap_scope_prefix), '.'); if (!StrBuf_putsz(s, w, "!snap:")) @@ -316,7 +316,7 @@ static bool CGroup_filterName_internal(const char *cgroup, StrBuf_state* s, StrB return true; } -char* CGroup_filterName(const char *cgroup) { +char* CGroup_filterName(const char* cgroup) { StrBuf_state s = { .buf = NULL, .size = 0, diff --git a/linux/CGroupUtils.h b/linux/CGroupUtils.h index db2df7f..ff13437 100644 --- a/linux/CGroupUtils.h +++ b/linux/CGroupUtils.h @@ -11,6 +11,6 @@ in the source distribution for its full text. #include -char* CGroup_filterName(const char *cgroup); +char* CGroup_filterName(const char* cgroup); #endif /* HEADER_CGroupUtils */ diff --git a/linux/LinuxProcess.c b/linux/LinuxProcess.c index 92be326..381b7cf 100644 --- a/linux/LinuxProcess.c +++ b/linux/LinuxProcess.c @@ -53,8 +53,8 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = { [M_VIRT] = { .name = "M_VIRT", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, .defaultSortDesc = true, }, [M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, }, [M_SHARE] = { .name = "M_SHARE", .title = " SHR ", .description = "Size of the process's shared pages", .flags = 0, .defaultSortDesc = true, }, - [M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the text segment of the process", .flags = 0, .defaultSortDesc = true, }, - [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the data segment plus stack usage of the process", .flags = 0, .defaultSortDesc = true, }, + [M_TRS] = { .name = "M_TRS", .title = " CODE ", .description = "Size of the .text segment of the process (CODE)", .flags = 0, .defaultSortDesc = true, }, + [M_DRS] = { .name = "M_DRS", .title = " DATA ", .description = "Size of the .data segment plus stack usage of the process (DATA)", .flags = 0, .defaultSortDesc = true, }, [M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, .defaultSortDesc = true, }, [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, .defaultSortDesc = true, .autoWidth = true, }, diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c index 45b045c..eca9459 100644 --- a/linux/LinuxProcessList.c +++ b/linux/LinuxProcessList.c @@ -10,6 +10,7 @@ in the source distribution for its full text. #include "linux/LinuxProcessList.h" #include +#include #include #include #include @@ -181,7 +182,7 @@ static unsigned int scanAvailableCPUsFromCPUinfo(LinuxProcessList* this) { if (String_startsWith(buffer, "processor")) availableCPUs++; - } + } fclose(file); @@ -219,7 +220,7 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { if (!String_startsWith(entry->d_name, "cpu")) continue; - char *endp; + char* endp; unsigned long int id = strtoul(entry->d_name + 3, &endp, 10); if (id == ULONG_MAX || endp == entry->d_name + 3 || *endp != '\0') continue; @@ -263,9 +264,9 @@ static void LinuxProcessList_updateCPUcount(ProcessList* super) { return; if (Running_containerized) { - /* LXC munges /proc/cpuinfo but not the /sys/devices/system/cpu/ files, - * so limit the visible CPUs to what the guest has been configured to see: */ - currExisting = active = scanAvailableCPUsFromCPUinfo(this); + /* LXC munges /proc/cpuinfo but not the /sys/devices/system/cpu/ files, + * so limit the visible CPUs to what the guest has been configured to see: */ + currExisting = active = scanAvailableCPUsFromCPUinfo(this); } #ifdef HAVE_SENSORS_SENSORS_H @@ -671,7 +672,7 @@ static void LinuxProcessList_readMaps(LinuxProcess* process, openat_arg_t procFd if (' ' != *readptr++) continue; - while(*readptr > ' ') + while (*readptr > ' ') readptr++; // Skip parsing this hex value if (' ' != *readptr++) continue; @@ -763,6 +764,43 @@ static bool LinuxProcessList_readStatmFile(LinuxProcess* process, openat_arg_t p return r == 7; } +static bool LinuxProcessList_checkPidNamespace(Process* process, openat_arg_t procFd) { + FILE* statusfile = fopenat(procFd, "status", "r"); + if (!statusfile) + return false; + + while (true) { + char buffer[PROC_LINE_LENGTH + 1]; + if (fgets(buffer, sizeof(buffer), statusfile) == NULL) + break; + + if (!String_startsWith(buffer, "NSpid:")) + continue; + + char* ptr = buffer; + int pid_ns_count = 0; + while (*ptr && *ptr != '\n' && !isdigit(*ptr)) + ++ptr; + + while (*ptr && *ptr != '\n') { + if (isdigit(*ptr)) + pid_ns_count++; + while (isdigit(*ptr)) + ++ptr; + while (*ptr && *ptr != '\n' && !isdigit(*ptr)) + ++ptr; + } + + if (pid_ns_count > 1) + process->isRunningInContainer = true; + + break; + } + + fclose(statusfile); + return true; +} + 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. @@ -851,7 +889,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t char* value_end = name_value_sep; - while(*value_end > 32) { + while (*value_end > 32) { value_end++; } @@ -861,7 +899,7 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t *value_end = '\0'; - switch(field) { + switch (field) { case 1: foundEnvID = true; if (!String_eq(name_value_sep, process->ctid ? process->ctid : "")) @@ -891,6 +929,13 @@ static void LinuxProcessList_readOpenVZData(LinuxProcess* process, openat_arg_t #endif +static bool isContainerOrVMSlice(char* cgroup) { + if (String_startsWith(cgroup, "/user") || String_startsWith(cgroup, "/system")) + return false; + + return true; +} + static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t procFd) { FILE* file = fopenat(procFd, "cgroup", "r"); if (!file) { @@ -941,7 +986,7 @@ static void LinuxProcessList_readCGroupFile(LinuxProcess* process, openat_arg_t free_and_xStrdup(&process->cgroup, output); if (!changed) { - if(process->cgroup_short) { + if (process->cgroup_short) { Process_updateFieldWidth(CCGROUP, strlen(process->cgroup_short)); } else { //CCGROUP is alias to normal CGROUP if shortening fails @@ -1090,9 +1135,7 @@ static void LinuxProcessList_readCwd(LinuxProcess* process, openat_arg_t procFd) #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); + ssize_t r = Compat_readlink(procFd, "cwd", pathBuffer, sizeof(pathBuffer) - 1); #endif if (r < 0) { @@ -1329,9 +1372,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, openat_arg_t proc #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); + amtRead = Compat_readlink(procFd, "exe", filename, sizeof(filename) - 1); #endif if (amtRead > 0) { filename[amtRead] = 0; @@ -1423,7 +1464,7 @@ static bool isOlderThan(const ProcessList* pl, const Process* proc, unsigned int /* Starttime might not yet be parsed */ if (proc->starttime_ctime <= 0) - return false; + return false; uint64_t realtime = pl->realtimeMs / 1000; @@ -1457,6 +1498,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ const unsigned int activeCPUs = pl->activeCPUs; const bool hideKernelThreads = settings->hideKernelThreads; const bool hideUserlandThreads = settings->hideUserlandThreads; + const bool hideRunningInContainer = settings->hideRunningInContainer; while ((entry = readdir(dir)) != NULL) { const char* name = entry->d_name; @@ -1508,6 +1550,15 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ LinuxProcessList_recurseProcTree(this, procFd, "task", proc, period); + if (ss->flags & PROCESS_FLAG_LINUX_CGROUP || hideRunningInContainer) { + LinuxProcessList_readCGroupFile(lp, procFd); + if (hideRunningInContainer && lp->cgroup && isContainerOrVMSlice(lp->cgroup)) { + if (!LinuxProcessList_checkPidNamespace(proc, procFd)) { + goto errorReadingProcess; + } + } + } + /* * These conditions will not trigger on first occurrence, cause we need to * add the process to the ProcessList and do all one time scans @@ -1530,6 +1581,12 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ Compat_openatArgClose(procFd); continue; } + if (preExisting && hideRunningInContainer && proc->isRunningInContainer) { + proc->updated = true; + proc->show = false; + Compat_openatArgClose(procFd); + continue; + } if (ss->flags & PROCESS_FLAG_IO) LinuxProcessList_readIoFile(lp, procFd, pl->realtimeMs); @@ -1580,7 +1637,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ char statCommand[MAX_NAME + 1]; unsigned long long int lasttimes = (lp->utime + lp->stime); unsigned long int tty_nr = proc->tty_nr; - if (! LinuxProcessList_readStatFile(proc, procFd, statCommand, sizeof(statCommand))) + if (!LinuxProcessList_readStatFile(proc, procFd, statCommand, sizeof(statCommand))) goto errorReadingProcess; if (lp->flags & PF_KTHREAD) { @@ -1644,10 +1701,6 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, openat_arg_ } #endif - if (ss->flags & PROCESS_FLAG_LINUX_CGROUP) { - LinuxProcessList_readCGroupFile(lp, procFd); - } - if (ss->flags & PROCESS_FLAG_LINUX_OOM) { LinuxProcessList_readOomData(lp, procFd); } diff --git a/linux/Platform.c b/linux/Platform.c index 38b66e8..64f25c4 100644 --- a/linux/Platform.c +++ b/linux/Platform.c @@ -12,6 +12,7 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include @@ -21,6 +22,7 @@ in the source distribution for its full text. #include #include #include +#include #include "BatteryMeter.h" #include "ClockMeter.h" @@ -250,7 +252,7 @@ const MeterClass* const Platform_meterTypes[] = { NULL }; -int Platform_getUptime() { +int Platform_getUptime(void) { double uptime = 0; FILE* fd = fopen(PROCDIR "/uptime", "r"); if (fd) { @@ -285,7 +287,7 @@ err: *fifteen = NAN; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { FILE* file = fopen(PROCDIR "/sys/kernel/pid_max", "r"); if (!file) return -1; @@ -351,20 +353,20 @@ void Platform_setMemoryValues(Meter* this) { const LinuxProcessList* lpl = (const LinuxProcessList*) pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - this->values[2] = pl->sharedMem; - this->values[3] = pl->cachedMem; - this->values[4] = pl->availableMem; + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + this->values[MEMORY_METER_SHARED] = pl->sharedMem; + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + this->values[MEMORY_METER_AVAILABLE] = pl->availableMem; if (lpl->zfs.enabled != 0 && !Running_containerized) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; if (lpl->zfs.size > lpl->zfs.min) shrinkableSize = lpl->zfs.size - lpl->zfs.min; - this->values[0] -= shrinkableSize; - this->values[3] += shrinkableSize; - this->values[4] += shrinkableSize; + this->values[MEMORY_METER_USED] -= shrinkableSize; + this->values[MEMORY_METER_CACHE] += shrinkableSize; + this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; } } @@ -430,117 +432,90 @@ char* Platform_getProcessEnv(pid_t pid) { return env; } -/* - * 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; - const struct dirent* de; +FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { + FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData)); DIR* dirp; - ssize_t len; - int fd; + int dfd; 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); + xSnprintf(path, sizeof(path), PROCDIR "/%d/fdinfo/", pid); if (strlen(path) >= (sizeof(path) - 2)) - return NULL; + goto err; if (!(dirp = opendir(path))) - return NULL; + goto err; - if ((fd = dirfd(dirp)) < 0 ) - goto out; + if ((dfd = dirfd(dirp)) == -1) { + closedir(dirp); + goto err; + } - while ((de = readdir(dirp))) { + FileLocks_LockData** data_ref = &pdata->locks; + for (struct dirent* de; (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)) + errno = 0; + char *end = de->d_name; + int file = strtoull(de->d_name, &end, 10); + if (errno || *end) continue; - if (!Compat_fstatat(fd, path, de->d_name, &sb, 0) && inode != sb.st_ino) + int fd = openat(dfd, de->d_name, O_RDONLY | O_CLOEXEC); + if(fd == -1) continue; + FILE *f = fdopen(fd, "r"); + if(!f) { + close(fd); + 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; -} + for (char buffer[1024]; fgets(buffer, sizeof(buffer), f); ) { + if (!strchr(buffer, '\n')) + continue; -FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { - FileLocks_ProcessData* pdata = xCalloc(1, sizeof(FileLocks_ProcessData)); + if (strncmp(buffer, "lock:\t", strlen("lock:\t"))) + continue; - FILE* f = fopen(PROCDIR "/locks", "r"); - if (!f) { - pdata->error = true; - return pdata; - } + FileLocks_Data data = {.fd = file}; + int _; + unsigned int maj, min; + char lock_end[25], locktype[32], exclusive[32], readwrite[32]; + if (10 != sscanf(buffer + strlen("lock:\t"), "%d: %31s %31s %31s %d %x:%x:%"PRIu64" %"PRIu64" %24s", + &_, locktype, exclusive, readwrite, &_, + &maj, &min, &data.inode, + &data.start, lock_end)) + continue; - char buffer[1024]; - FileLocks_LockData** data_ref = &pdata->locks; - while(fgets(buffer, sizeof(buffer), f)) { - if (!strchr(buffer, '\n')) - continue; + data.locktype = xStrdup(locktype); + data.exclusive = xStrdup(exclusive); + data.readwrite = xStrdup(readwrite); + data.dev = makedev(maj, min); - 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 (String_eq(lock_end, "EOF")) + data.end = ULLONG_MAX; + else + data.end = strtoull(lock_end, NULL, 10); - if (pid != lock_pid) - continue; + xSnprintf(path, sizeof(path), PROCDIR "/%d/fd/%s", pid, de->d_name); + char link[PATH_MAX]; + ssize_t link_len; + if (strlen(path) < (sizeof(path) - 2) && (link_len = readlink(path, link, sizeof(link))) != -1) + data.filename = xStrndup(link, link_len); - 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 = xCalloc(1, sizeof(FileLocks_LockData)); + (*data_ref)->data = data; + data_ref = &(*data_ref)->next; } - *data_ref = ldata; - data_ref = &ldata->next; + fclose(f); } - fclose(f); + closedir(dirp); + return pdata; + +err: + pdata->error = true; return pdata; } diff --git a/linux/Platform.h b/linux/Platform.h index e6fa161..f6ac188 100644 --- a/linux/Platform.h +++ b/linux/Platform.h @@ -75,8 +75,6 @@ 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); @@ -114,7 +112,9 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } -static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } @@ -124,12 +124,18 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } -static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } -static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } +static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { + return NULL; +} -static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} #endif diff --git a/linux/SystemdMeter.c b/linux/SystemdMeter.c index 53ae2d2..cee3231 100644 --- a/linux/SystemdMeter.c +++ b/linux/SystemdMeter.c @@ -310,8 +310,12 @@ static int valueDigitColor(unsigned int value) { static void SystemdMeter_display(ATTR_UNUSED const Object* cast, RichString* out) { char buffer[16]; int len; + int color = METER_VALUE_ERROR; - int color = (systemState && String_eq(systemState, "running")) ? METER_VALUE_OK : METER_VALUE_ERROR; + if (systemState) { + color = String_eq(systemState, "running") ? METER_VALUE_OK : + String_eq(systemState, "degraded") ? METER_VALUE_ERROR : METER_VALUE_WARN; + } RichString_writeAscii(out, CRT_colors[color], systemState ? systemState : "N/A"); RichString_appendAscii(out, CRT_colors[METER_TEXT], " ("); diff --git a/netbsd/Platform.c b/netbsd/Platform.c index cf6079d..ad6050c 100644 --- a/netbsd/Platform.c +++ b/netbsd/Platform.c @@ -196,7 +196,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; const int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -227,7 +227,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { // https://nxr.netbsd.org/xref/src/sys/sys/ansi.h#__pid_t // pid is assigned as a 32bit Integer. return INT32_MAX; @@ -270,18 +270,18 @@ double Platform_setCPUValues(Meter* this, int cpu) { void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = pl->cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } char* Platform_getProcessEnv(pid_t pid) { @@ -338,12 +338,6 @@ end: 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; @@ -351,7 +345,7 @@ FileLocks_ProcessData* Platform_getProcessLocks(pid_t pid) { bool Platform_getDiskIO(DiskIOData* data) { const int mib[] = { CTL_HW, HW_IOSTATS, sizeof(struct io_sysctl) }; - struct io_sysctl *iostats = NULL; + struct io_sysctl* iostats = NULL; size_t size = 0; for (int retry = 3; retry > 0; retry--) { diff --git a/netbsd/Platform.h b/netbsd/Platform.h index 3ad51e2..0e53b45 100644 --- a/netbsd/Platform.h +++ b/netbsd/Platform.h @@ -65,8 +65,6 @@ void Platform_setSwapValues(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); @@ -97,7 +95,9 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } -static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } @@ -107,12 +107,18 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } -static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } -static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } +static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { + return NULL; +} -static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} #endif diff --git a/openbsd/Platform.c b/openbsd/Platform.c index b222bee..1ce5ba1 100644 --- a/openbsd/Platform.c +++ b/openbsd/Platform.c @@ -143,7 +143,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { struct timeval bootTime, currTime; const int mib[2] = { CTL_KERN, KERN_BOOTTIME }; size_t size = sizeof(bootTime); @@ -174,7 +174,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = (double) loadAverage.ldavg[2] / loadAverage.fscale; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { return 2 * THREAD_PID_OFFSET; } @@ -227,18 +227,18 @@ void Platform_setMemoryValues(Meter* this) { long int cachedMem = pl->cachedMem; usedMem -= buffersMem + cachedMem; this->total = pl->totalMem; - this->values[0] = usedMem; - this->values[1] = buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = usedMem; + this->values[MEMORY_METER_BUFFERS] = buffersMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_CACHE] = cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } char* Platform_getProcessEnv(pid_t pid) { @@ -296,12 +296,6 @@ end: 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; diff --git a/openbsd/Platform.h b/openbsd/Platform.h index e3d6116..27d792e 100644 --- a/openbsd/Platform.h +++ b/openbsd/Platform.h @@ -57,8 +57,6 @@ void Platform_setSwapValues(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); @@ -91,7 +89,9 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } -static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } @@ -101,12 +101,18 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } -static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } -static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } +static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { + return NULL; +} -static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} #endif diff --git a/pcp-htop.5.in b/pcp-htop.5.in index 0f5cb14..dbc0dfb 100644 --- a/pcp-htop.5.in +++ b/pcp-htop.5.in @@ -1,4 +1,4 @@ -.TH "PCP-HTOP" "5" "2022" "@PACKAGE_STRING@" "File Formats" +.TH "PCP-HTOP" "5" "2023" "@PACKAGE_STRING@" "File Formats" .SH "NAME" \f3pcp-htop\f1 \- pcp-htop configuration file .SH "DESCRIPTION" diff --git a/pcp/PCPDynamicColumn.c b/pcp/PCPDynamicColumn.c index aab2525..33c6d72 100644 --- a/pcp/PCPDynamicColumn.c +++ b/pcp/PCPDynamicColumn.c @@ -14,6 +14,7 @@ in the source distribution for its full text. #include #include #include +#include #include #include #include @@ -194,6 +195,12 @@ void PCPDynamicColumns_init(PCPDynamicColumns* columns) { const char* home = getenv("HOME"); char* path; + if (!xdgConfigHome && !home) { + const struct passwd* pw = getpwuid(getuid()); + if (pw) + home = pw->pw_dir; + } + columns->table = Hashtable_new(0, true); /* developer paths - PCP_HTOP_DIR=./pcp ./pcp-htop */ diff --git a/pcp/PCPDynamicMeter.c b/pcp/PCPDynamicMeter.c index 7c55e4b..e899988 100644 --- a/pcp/PCPDynamicMeter.c +++ b/pcp/PCPDynamicMeter.c @@ -12,12 +12,14 @@ in the source distribution for its full text. #include #include #include -#include +#include #include #include #include #include +#include + #include "Macros.h" #include "Platform.h" #include "RichString.h" @@ -251,6 +253,12 @@ void PCPDynamicMeters_init(PCPDynamicMeters* meters) { const char* home = getenv("HOME"); char* path; + if (!xdgConfigHome && !home) { + const struct passwd* pw = getpwuid(getuid()); + if (pw) + home = pw->pw_dir; + } + meters->table = Hashtable_new(0, true); /* developer paths - PCP_HTOP_DIR=./pcp ./pcp-htop */ diff --git a/pcp/PCPProcessList.c b/pcp/PCPProcessList.c index 045f7ae..f893689 100644 --- a/pcp/PCPProcessList.c +++ b/pcp/PCPProcessList.c @@ -571,7 +571,7 @@ static void PCPProcessList_updateAllCPUTime(PCPProcessList* this, PCPMetric metr { pmAtomValue* value = &this->cpu[cpumetric]; if (PCPMetric_values(metric, value, 1, PM_TYPE_U64) == NULL) - memset(&value, 0, sizeof(pmAtomValue)); + memset(value, 0, sizeof(pmAtomValue)); } static void PCPProcessList_updatePerCPUTime(PCPProcessList* this, PCPMetric metric, CPUMetric cpumetric) diff --git a/pcp/Platform.c b/pcp/Platform.c index 342bf43..994cef3 100644 --- a/pcp/Platform.c +++ b/pcp/Platform.c @@ -1,8 +1,8 @@ /* htop - linux/Platform.c (C) 2014 Hisham H. Muhammad -(C) 2020-2021 htop dev team -(C) 2020-2021 Red Hat, Inc. +(C) 2020-2022 htop dev team +(C) 2020-2022 Red Hat, Inc. Released under the GNU GPLv2+, see the COPYING file in the source distribution for its full text. */ @@ -248,6 +248,37 @@ static const char* Platform_metricNames[] = { [PCP_METRIC_COUNT] = NULL }; +#ifndef HAVE_PMLOOKUPDESCS +/* + * pmLookupDescs(3) exists in latest versions of libpcp (5.3.6+), + * but for older versions we provide an implementation here. This + * involves multiple round trips to pmcd though, which the latest + * libpcp version avoids by using a protocol extension. In time, + * perhaps in a few years, we could remove this back-compat code. + */ +int pmLookupDescs(int numpmid, pmID* pmids, pmDesc* descs) { + int count = 0; + + for (int i = 0; i < numpmid; i++) { + /* expect some metrics to be missing - e.g. PMDA not available */ + if (pmids[i] == PM_ID_NULL) + continue; + + int sts = pmLookupDesc(pmids[i], &descs[i]); + if (sts < 0) { + if (pmDebugOptions.appl0) + fprintf(stderr, "Error: cannot lookup metric %s(%s): %s\n", + pcp->names[i], pmIDStr(pcp->pmids[i]), pmErrStr(sts)); + pmids[i] = PM_ID_NULL; + continue; + } + + count++; + } + return count; +} +#endif + size_t Platform_addMetric(PCPMetric id, const char* name) { unsigned int i = (unsigned int)id; @@ -325,21 +356,14 @@ bool Platform_init(void) { return false; } - for (size_t i = 0; i < pcp->totalMetrics; i++) { - pcp->fetch[i] = PM_ID_NULL; /* default is to not sample */ - - /* expect some metrics to be missing - e.g. PMDA not available */ - if (pcp->pmids[i] == PM_ID_NULL) - continue; - - sts = pmLookupDesc(pcp->pmids[i], &pcp->descs[i]); - if (sts < 0) { - if (pmDebugOptions.appl0) - fprintf(stderr, "Error: cannot lookup metric %s(%s): %s\n", - pcp->names[i], pmIDStr(pcp->pmids[i]), pmErrStr(sts)); - pcp->pmids[i] = PM_ID_NULL; - continue; - } + sts = pmLookupDescs(pcp->totalMetrics, pcp->pmids, pcp->descs); + if (sts < 1) { + if (sts < 0) + fprintf(stderr, "Error: cannot lookup descriptors: %s\n", pmErrStr(sts)); + else /* ensure we have at least one valid metric to work with */ + fprintf(stderr, "Error: cannot find a single valid metric, exiting\n"); + Platform_done(); + return false; } /* set proc.control.perclient.threads to 1 for live contexts */ @@ -504,28 +528,28 @@ void Platform_setMemoryValues(Meter* this) { const PCPProcessList* ppl = (const PCPProcessList*) pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - this->values[2] = pl->sharedMem; - this->values[3] = pl->cachedMem; - this->values[4] = pl->availableMem; + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + this->values[MEMORY_METER_SHARED] = pl->sharedMem; + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + this->values[MEMORY_METER_AVAILABLE] = pl->availableMem; if (ppl->zfs.enabled != 0) { // ZFS does not shrink below the value of zfs_arc_min. unsigned long long int shrinkableSize = 0; if (ppl->zfs.size > ppl->zfs.min) shrinkableSize = ppl->zfs.size - ppl->zfs.min; - this->values[0] -= shrinkableSize; - this->values[3] += shrinkableSize; - this->values[4] += shrinkableSize; + this->values[MEMORY_METER_USED] -= shrinkableSize; + this->values[MEMORY_METER_CACHE] += shrinkableSize; + this->values[MEMORY_METER_AVAILABLE] += shrinkableSize; } } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = pl->cachedSwap; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = pl->cachedSwap; } void Platform_setZramValues(Meter* this) { @@ -645,12 +669,6 @@ char* Platform_getProcessEnv(pid_t pid) { return value.cp; } -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; diff --git a/pcp/Platform.h b/pcp/Platform.h index dc51c73..f06f226 100644 --- a/pcp/Platform.h +++ b/pcp/Platform.h @@ -98,8 +98,6 @@ 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); diff --git a/solaris/Platform.c b/solaris/Platform.c index 9c5acb5..96f3526 100644 --- a/solaris/Platform.c +++ b/solaris/Platform.c @@ -144,7 +144,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { int boot_time = 0; int curr_time = time(NULL); struct utmpx* ent; @@ -173,7 +173,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = plat_loadavg[LOADAVG_15MIN]; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { int vproc = 32778; // Reasonable Solaris default kstat_ctl_t* kc = kstat_open(); @@ -237,18 +237,18 @@ double Platform_setCPUValues(Meter* this, unsigned int cpu) { void Platform_setMemoryValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalMem; - this->values[0] = pl->usedMem; - this->values[1] = pl->buffersMem; - // this->values[2] = "shared memory, like tmpfs and shm" - this->values[3] = pl->cachedMem; - // this->values[4] = "available memory" + this->values[MEMORY_METER_USED] = pl->usedMem; + this->values[MEMORY_METER_BUFFERS] = pl->buffersMem; + // this->values[MEMORY_METER_SHARED] = "shared memory, like tmpfs and shm" + this->values[MEMORY_METER_CACHE] = pl->cachedMem; + // this->values[MEMORY_METER_AVAILABLE] = "available memory" } void Platform_setSwapValues(Meter* this) { const ProcessList* pl = this->pl; this->total = pl->totalSwap; - this->values[0] = pl->usedSwap; - this->values[1] = NAN; + this->values[SWAP_METER_USED] = pl->usedSwap; + this->values[SWAP_METER_CACHE] = NAN; } void Platform_setZfsArcValues(Meter* this) { @@ -308,12 +308,6 @@ char* Platform_getProcessEnv(pid_t pid) { return xRealloc(envBuilder.env, envBuilder.size + 1); } -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; diff --git a/solaris/Platform.h b/solaris/Platform.h index 1b3dc9f..14431e3 100644 --- a/solaris/Platform.h +++ b/solaris/Platform.h @@ -86,8 +86,6 @@ 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); @@ -132,7 +130,9 @@ IGNORE_WCASTQUAL_BEGIN IGNORE_WCASTQUAL_END } -static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } @@ -142,12 +142,18 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } -static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } -static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } +static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { + return NULL; +} -static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} #endif diff --git a/unsupported/Platform.c b/unsupported/Platform.c index 33d7c41..27bc560 100644 --- a/unsupported/Platform.c +++ b/unsupported/Platform.c @@ -90,7 +90,7 @@ void Platform_setBindings(Htop_Action* keys) { (void) keys; } -int Platform_getUptime() { +int Platform_getUptime(void) { return 0; } @@ -100,7 +100,7 @@ void Platform_getLoadAverage(double* one, double* five, double* fifteen) { *fifteen = 0; } -int Platform_getMaxPid() { +int Platform_getMaxPid(void) { return 1; } @@ -129,12 +129,6 @@ char* Platform_getProcessEnv(pid_t 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; diff --git a/unsupported/Platform.h b/unsupported/Platform.h index 5c874d4..f475dda 100644 --- a/unsupported/Platform.h +++ b/unsupported/Platform.h @@ -50,8 +50,6 @@ void Platform_setSwapValues(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); @@ -80,7 +78,9 @@ static inline void Platform_gettime_monotonic(uint64_t* msec) { Generic_gettime_monotonic(msec); } -static inline Hashtable* Platform_dynamicMeters(void) { return NULL; } +static inline Hashtable* Platform_dynamicMeters(void) { + return NULL; +} static inline void Platform_dynamicMetersDone(ATTR_UNUSED Hashtable* table) { } @@ -90,12 +90,18 @@ static inline void Platform_dynamicMeterUpdateValues(ATTR_UNUSED Meter* meter) { static inline void Platform_dynamicMeterDisplay(ATTR_UNUSED const Meter* meter, ATTR_UNUSED RichString* out) { } -static inline Hashtable* Platform_dynamicColumns(void) { return NULL; } +static inline Hashtable* Platform_dynamicColumns(void) { + return NULL; +} static inline void Platform_dynamicColumnsDone(ATTR_UNUSED Hashtable* table) { } -static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { return NULL; } +static inline const char* Platform_dynamicColumnInit(ATTR_UNUSED unsigned int key) { + return NULL; +} -static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { return false; } +static inline bool Platform_dynamicColumnWriteField(ATTR_UNUSED const Process* proc, ATTR_UNUSED RichString* str, ATTR_UNUSED unsigned int key) { + return false; +} #endif -- cgit v1.2.3