From 51b370a10576faedf0acccd88a5352999d0eac70 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 28 Aug 2026 15:21:52 +0800 Subject: [PATCH] Processes: adds total thread count detection --- CMakeLists.txt | 4 +- src/detection/processes/processes.h | 8 ++- src/detection/processes/processes_apple.c | 21 ++++++++ src/detection/processes/processes_bsd.c | 28 +++++----- src/detection/processes/processes_gnu.c | 19 +++++-- src/detection/processes/processes_haiku.c | 5 +- src/detection/processes/processes_linux.c | 51 +++++++++++++++---- src/detection/processes/processes_nbsd.c | 27 ++++++++-- src/detection/processes/processes_nosupport.c | 2 +- src/detection/processes/processes_obsd.c | 25 ++++++--- src/detection/processes/processes_sunos.c | 38 ++++++++++++++ src/detection/processes/processes_windows.c | 15 ++++-- src/modules/processes/processes.c | 13 ++--- 13 files changed, 202 insertions(+), 54 deletions(-) create mode 100644 src/detection/processes/processes_apple.c create mode 100644 src/detection/processes/processes_sunos.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ba505650..a07298098 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1065,7 +1065,7 @@ elseif(APPLE) src/detection/packages/packages_apple.c src/detection/packages/packages_nix.c src/detection/poweradapter/poweradapter_apple.c - src/detection/processes/processes_bsd.c + src/detection/processes/processes_apple.c src/detection/sound/sound_apple.c src/detection/swap/swap_apple.c src/detection/terminalfont/terminalfont_apple.m @@ -1240,7 +1240,7 @@ elseif(SunOS) src/detection/os/os_sunos.c src/detection/packages/packages_sunos.c src/detection/poweradapter/poweradapter_nosupport.c - src/detection/processes/processes_linux.c + src/detection/processes/processes_sunos.c src/detection/gtk_qt/qt.c src/detection/sound/sound_sunos.c src/detection/swap/swap_sunos.c diff --git a/src/detection/processes/processes.h b/src/detection/processes/processes.h index 69cf46381..f9dc380cb 100644 --- a/src/detection/processes/processes.h +++ b/src/detection/processes/processes.h @@ -2,4 +2,10 @@ #include "fastfetch.h" -const char* ffDetectProcesses(uint32_t* result); +typedef struct FFProcessesResult +{ + uint32_t processes; + uint32_t threads; +} FFProcessesResult; + +const char* ffDetectProcesses(FFProcessesResult* result); diff --git a/src/detection/processes/processes_apple.c b/src/detection/processes/processes_apple.c new file mode 100644 index 000000000..6866c64fe --- /dev/null +++ b/src/detection/processes/processes_apple.c @@ -0,0 +1,21 @@ +#include "processes.h" +#include "common/mallocHelper.h" + +#import + +const char* ffDetectProcesses(FFProcessesResult* result) { + FF_AUTO_FREE pid_t* pids = malloc(sizeof(pid_t) * 0x1000); + int count = proc_listallpids(pids, 0x1000); + int trdCount = 0; + for (int index = 0; index < count; ++index) { + struct proc_taskinfo taskInfo; + if (proc_pidinfo(pids[index], PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) != sizeof(taskInfo)) { + continue; + } + trdCount += taskInfo.pti_threadnum; + } + + result->processes = (uint32_t) count; + result->threads = (uint32_t) trdCount; + return nullptr; +} diff --git a/src/detection/processes/processes_bsd.c b/src/detection/processes/processes_bsd.c index 4d16bc3af..8410e2901 100644 --- a/src/detection/processes/processes_bsd.c +++ b/src/detection/processes/processes_bsd.c @@ -1,23 +1,23 @@ #include "processes.h" #include -#ifdef __FreeBSD__ - #include - #include -#endif +#include +#include -#ifndef KERN_PROC_PROC - #define KERN_PROC_PROC KERN_PROC_ALL // Apple -#endif - -const char* ffDetectProcesses(uint32_t* result) { - int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC }; - size_t length; - - if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { +const char* ffDetectProcesses(FFProcessesResult* result) { + int procRequest[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC }; + size_t procLength; + if (sysctl(procRequest, ARRAY_SIZE(procRequest), nullptr, &procLength, nullptr, 0) != 0) { return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_PROC}) failed"; } - *result = (uint32_t) (length / sizeof(struct kinfo_proc)); + int threadRequest[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + size_t threadLength; + if (sysctl(threadRequest, ARRAY_SIZE(threadRequest), nullptr, &threadLength, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}) failed"; + } + + result->processes = (uint32_t) (procLength / sizeof(struct kinfo_proc)); + result->threads = (uint32_t) (threadLength / sizeof(struct kinfo_proc)); return nullptr; } diff --git a/src/detection/processes/processes_gnu.c b/src/detection/processes/processes_gnu.c index 7dc739d2b..73d287d5d 100644 --- a/src/detection/processes/processes_gnu.c +++ b/src/detection/processes/processes_gnu.c @@ -3,7 +3,7 @@ #include #include -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses(FFProcessesResult* result) { struct ps_context* context = nullptr; if (ps_context_create(getproc(), &context) != 0) { return "ps_context_create(getproc()) failed"; @@ -16,15 +16,26 @@ const char* ffDetectProcesses(uint32_t* result) { goto done; } - // No flags are needed; we only count the processes. - if (proc_stat_list_add_all(list, nullptr, nullptr) != 0) { + struct proc_stat **stats = nullptr; + unsigned int numProcs = 0; + if (proc_stat_list_add_all(list, &stats, &numProcs) != 0) { error = "proc_stat_list_add_all() failed"; goto done; } - *result = (uint32_t) list->num_procs; + uint32_t nthread = 0; + for (unsigned int i = 0; i < numProcs; i++) { + proc_stat_get_basic_info(stats[i]); + nthread += proc_stat_thread_count(stats[i]); + } + + result->processes = (uint32_t) numProcs; + result->threads = nthread; done: + if (stats) { + proc_stat_list_free_stats(stats, numProcs); + } if (list) { proc_stat_list_free(list); } diff --git a/src/detection/processes/processes_haiku.c b/src/detection/processes/processes_haiku.c index 0d960ab39..8b0c1cd4b 100644 --- a/src/detection/processes/processes_haiku.c +++ b/src/detection/processes/processes_haiku.c @@ -2,13 +2,14 @@ #include -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses(FFProcessesResult* result) { system_info info; if (get_system_info(&info) != B_OK) { return "Error getting system info"; } - *result = info.used_teams; + result->processes = info.used_teams; + result->threads = info.used_threads; return nullptr; } diff --git a/src/detection/processes/processes_linux.c b/src/detection/processes/processes_linux.c index 78790c2eb..05048d55a 100644 --- a/src/detection/processes/processes_linux.c +++ b/src/detection/processes/processes_linux.c @@ -1,27 +1,60 @@ #include "processes.h" #include "common/io.h" +#include "common/memrchr.h" #include "common/strutil.h" -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses(FFProcessesResult* result) { FF_AUTO_CLOSE_DIR DIR* dir = opendir("/proc"); if (dir == nullptr) { return "opendir(\"/proc\") failed"; } - uint32_t num = 0; + uint32_t processes = 0; + uint32_t threads = 0; + + int procfd = dirfd(dir); struct dirent* entry; while ((entry = readdir(dir)) != nullptr) { - if ( -#ifdef _DIRENT_HAVE_D_TYPE - (entry->d_type == DT_DIR || entry->d_type == DT_UNKNOWN) && -#endif - ffCharIsDigit(entry->d_name[0])) - ++num; + if ((entry->d_type == DT_DIR || entry->d_type == DT_UNKNOWN) && ffCharIsDigit(entry->d_name[0])) { + ++processes; + + char statPath[32]; + strcpy(ffStrCopy(statPath, entry->d_name, sizeof(statPath)), "/stat"); + + char statBuffer[256]; + ssize_t statLength = ffReadFileDataRelative(procfd, statPath, sizeof(statBuffer) - 1, statBuffer); + if (statLength < 0) { + continue; + } + statBuffer[statLength] = '\0'; + + const char* cursor = (const char*) memrchr(statBuffer, ')', (size_t) statLength); + if (!cursor) { + continue; + } + ++cursor; + + for (uint32_t field = 2 /*comm*/; field < 20 /*num_threads*/; ++field) { + while (*cursor != ' ' && __builtin_expect(*cursor != '\0', true)) { + ++cursor; + } + ++cursor; + if (__builtin_expect(*cursor == '\0', false)) { + break; + } + } + if (__builtin_expect(*cursor == '\0', false)) { + continue; + } + + threads += (uint32_t) strtoul(cursor, nullptr, 10); + } } - *result = num; + result->processes = processes; + result->threads = threads; return nullptr; } diff --git a/src/detection/processes/processes_nbsd.c b/src/detection/processes/processes_nbsd.c index c3e7d047f..8e89ab3a1 100644 --- a/src/detection/processes/processes_nbsd.c +++ b/src/detection/processes/processes_nbsd.c @@ -1,15 +1,34 @@ #include "processes.h" +#include "common/mallocHelper.h" #include -const char* ffDetectProcesses(uint32_t* result) { - int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, -1, sizeof(struct kinfo_proc2), 0 }; +const char* ffDetectProcesses(FFProcessesResult* result) { + int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, -1, sizeof(struct kinfo_proc2), INT_MAX }; size_t length = 0; if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { - return "sysctl({CTL_KERN, KERN_PROC2, KERN_PROC_ALL}) failed"; + return "sysctl({CTL_KERN, KERN_PROC2, KERN_PROC_ALL, nullptr}) failed"; } - *result = (uint32_t) (length / sizeof(struct kinfo_proc2)); + FF_AUTO_FREE struct kinfo_proc2* procs = malloc(length); + if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC2, KERN_PROC_ALL, procs}) failed"; + } + + uint32_t procCount = (uint32_t) (length / sizeof(struct kinfo_proc2)); + result->processes = procCount; + + uint32_t threads = 0; + for (uint32_t i = 0; i < procCount; ++i) { + int lwpRequest[] = { CTL_KERN, KERN_LWP, procs[i].p_pid, sizeof(struct kinfo_lwp), 0 }; + size_t lwpLength = 0; + + if (sysctl(lwpRequest, ARRAY_SIZE(lwpRequest), nullptr, &lwpLength, nullptr, 0) == 0) { + threads += (uint32_t) (lwpLength / sizeof(struct kinfo_lwp)); + } + } + + result->threads = threads; return nullptr; } diff --git a/src/detection/processes/processes_nosupport.c b/src/detection/processes/processes_nosupport.c index 7da208ec4..e7218a5c2 100644 --- a/src/detection/processes/processes_nosupport.c +++ b/src/detection/processes/processes_nosupport.c @@ -1,5 +1,5 @@ #include "processes.h" -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses([[maybe_unused]] FFProcessesResult* result) { return "Not supported on this platform"; } diff --git a/src/detection/processes/processes_obsd.c b/src/detection/processes/processes_obsd.c index 5a5ba31f4..cb960ce57 100644 --- a/src/detection/processes/processes_obsd.c +++ b/src/detection/processes/processes_obsd.c @@ -2,14 +2,25 @@ #include #include -#include -const char* ffDetectProcesses(uint32_t* result) { - kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr); - const void* ret = kvm_getprocs(kd, KERN_PROC_ALL, 0, 1, result); - kvm_close(kd); - if (!ret) { - return "kvm_getprocs() failed"; +const char* ffDetectProcesses(FFProcessesResult* result) { + int procRequest[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), 0 }; + size_t procLength = 0; + + if (sysctl(procRequest, ARRAY_SIZE(procRequest), nullptr, &procLength, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}) failed"; } + + result->processes = (uint32_t) (procLength / sizeof(struct kinfo_proc)); + + int threadRequest[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL | KERN_PROC_SHOW_THREADS, 0, sizeof(struct kinfo_proc), 0 }; + size_t threadLength = 0; + + if (sysctl(threadRequest, ARRAY_SIZE(threadRequest), nullptr, &threadLength, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL | KERN_PROC_SHOW_THREADS}) failed"; + } + + result->threads = (uint32_t) (threadLength / sizeof(struct kinfo_proc)); + return nullptr; } diff --git a/src/detection/processes/processes_sunos.c b/src/detection/processes/processes_sunos.c new file mode 100644 index 000000000..ce0da325c --- /dev/null +++ b/src/detection/processes/processes_sunos.c @@ -0,0 +1,38 @@ +#include "processes.h" + +#include "common/io.h" +#include "common/strutil.h" + +#include + +const char* ffDetectProcesses(FFProcessesResult* result) { + FF_AUTO_CLOSE_DIR DIR* dir = opendir("/proc"); + if (dir == nullptr) { + return "opendir(\"/proc\") failed"; + } + + uint32_t processes = 0; + uint32_t threads = 0; + + int procfd = dirfd(dir); + + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + if (ffCharIsDigit(entry->d_name[0])) { + ++processes; + + char psinfoPath[32]; + strcpy(ffStrCopy(psinfoPath, entry->d_name, sizeof(psinfoPath)), "/psinfo"); + + psinfo_t info; + if (ffReadFileDataRelative(procfd, psinfoPath, sizeof(info), &info) == (ssize_t) sizeof(info)) { + threads += info.pr_nlwp; + } + } + } + + result->processes = processes; + result->threads = threads; + + return nullptr; +} diff --git a/src/detection/processes/processes_windows.c b/src/detection/processes/processes_windows.c index 78ed840b4..cb617099e 100644 --- a/src/detection/processes/processes_windows.c +++ b/src/detection/processes/processes_windows.c @@ -4,7 +4,7 @@ #include #include -const char* ffDetectProcesses(uint32_t* result) { +const char* ffDetectProcesses(FFProcessesResult* result) { FF_AUTO_FREE SYSTEM_PROCESS_INFORMATION* pstart = nullptr; // Multiple attempts in case processes change while @@ -25,10 +25,17 @@ const char* ffDetectProcesses(uint32_t* result) { } } - *result = 1; // Init with 1 because we test for ptr->NextEntryOffset - for (SYSTEM_PROCESS_INFORMATION* ptr = pstart; ptr->NextEntryOffset; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) { - ++*result; + uint32_t processes = 0; + uint32_t threads = 0; + for (SYSTEM_PROCESS_INFORMATION* ptr = pstart; ; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) { + ++processes; + threads += ptr->NumberOfThreads; + if (ptr->NextEntryOffset == 0) { + break; + } } + result->processes = processes; + result->threads = threads; return nullptr; } diff --git a/src/modules/processes/processes.c b/src/modules/processes/processes.c index 428120456..9bb68239c 100644 --- a/src/modules/processes/processes.c +++ b/src/modules/processes/processes.c @@ -5,8 +5,8 @@ #include "modules/processes/processes.h" bool ffPrintProcesses(FFProcessesOptions* options) { - uint32_t numProcesses = 0; - const char* error = ffDetectProcesses(&numProcesses); + FFProcessesResult result = {}; + const char* error = ffDetectProcesses(&result); if (error) { ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); @@ -16,9 +16,9 @@ bool ffPrintProcesses(FFProcessesOptions* options) { if (options->moduleArgs.outputFormat.length == 0) { ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); - printf("%u\n", numProcesses); + printf("%u (%u threads)\n", result.processes, result.threads); } else { - FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(numProcesses, "result") })); + FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) { FF_ARG(result.processes, "result") })); } return true; @@ -41,7 +41,7 @@ void ffGenerateProcessesJsonConfig(FFProcessesOptions* options, yyjson_mut_doc* } bool ffGenerateProcessesJsonResult([[maybe_unused]] FFProcessesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { - uint32_t result; + FFProcessesResult result = {}; const char* error = ffDetectProcesses(&result); if (error) { @@ -49,7 +49,8 @@ bool ffGenerateProcessesJsonResult([[maybe_unused]] FFProcessesOptions* options, return false; } - yyjson_mut_obj_add_uint(doc, module, "result", result); + yyjson_mut_obj_add_uint(doc, module, "processes", result.processes); + yyjson_mut_obj_add_uint(doc, module, "threads", result.threads); return true; }