From 7fe08bddd04a7ae69f4236670b0e1a3af1d3b841 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 28 Aug 2026 17:40:57 +0800 Subject: [PATCH] Processes: adds option `countKprocs` --- CMakeLists.txt | 3 +- doc/json_schema.json | 7 ++- src/detection/processes/processes.h | 3 +- src/detection/processes/processes_apple.c | 44 ++++++++++++----- src/detection/processes/processes_bsd.c | 32 +++++++++---- src/detection/processes/processes_dbsd.c | 38 +++++++++++++++ src/detection/processes/processes_gnu.c | 27 ++++++----- src/detection/processes/processes_haiku.c | 2 +- src/detection/processes/processes_linux.c | 53 +++++++++++++-------- src/detection/processes/processes_nbsd.c | 13 +++-- src/detection/processes/processes_obsd.c | 38 +++++++++------ src/detection/processes/processes_sunos.c | 28 ++++++----- src/detection/processes/processes_windows.c | 13 +++-- src/modules/processes/option.h | 2 + src/modules/processes/processes.c | 14 ++++-- 15 files changed, 216 insertions(+), 101 deletions(-) create mode 100644 src/detection/processes/processes_dbsd.c diff --git a/CMakeLists.txt b/CMakeLists.txt index a07298098..e18136630 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -813,7 +813,6 @@ elseif(FreeBSD) src/detection/os/os_linux.c src/detection/packages/packages_bsd.c src/detection/poweradapter/poweradapter_nosupport.c - src/detection/processes/processes_bsd.c src/detection/gtk_qt/qt.c src/detection/sound/sound_bsd.c src/detection/swap/swap_bsd.c @@ -833,12 +832,14 @@ elseif(FreeBSD) if(DragonFly) list(APPEND LIBFASTFETCH_SRC src/detection/bluetooth/bluetooth_nosupport.c + src/detection/processes/processes_dbsd.c src/detection/top/top_dbsd.c src/detection/wifi/wifi_nosupport.c ) else() list(APPEND LIBFASTFETCH_SRC src/detection/bluetooth/bluetooth_bsd.c + src/detection/processes/processes_bsd.c src/detection/top/top_bsd.c src/detection/wifi/wifi_bsd.c ) diff --git a/doc/json_schema.json b/doc/json_schema.json index e948d9285..2768b13df 100644 --- a/doc/json_schema.json +++ b/doc/json_schema.json @@ -3984,7 +3984,12 @@ "properties": { "type": { "const": "processes", - "description": "Print the number of running processes" + "description": "Print the number of running processes and threads" + }, + "countKprocs": { + "description": "Whether to take kernel processes and threads into account", + "type": "boolean", + "default": false }, "key": { "$ref": "#/$defs/key" diff --git a/src/detection/processes/processes.h b/src/detection/processes/processes.h index f9dc380cb..fdc7ceedb 100644 --- a/src/detection/processes/processes.h +++ b/src/detection/processes/processes.h @@ -1,6 +1,7 @@ #pragma once #include "fastfetch.h" +#include "modules/processes/option.h" typedef struct FFProcessesResult { @@ -8,4 +9,4 @@ typedef struct FFProcessesResult uint32_t threads; } FFProcessesResult; -const char* ffDetectProcesses(FFProcessesResult* result); +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result); diff --git a/src/detection/processes/processes_apple.c b/src/detection/processes/processes_apple.c index 6866c64fe..8ed1472b2 100644 --- a/src/detection/processes/processes_apple.c +++ b/src/detection/processes/processes_apple.c @@ -1,21 +1,41 @@ #include "processes.h" #include "common/mallocHelper.h" +#include #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; +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + size_t length; + + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, nullptr}) failed"; + } + + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + FF_AUTO_FREE struct kinfo_proc* processes = malloc(length); + + if (sysctl(request, ARRAY_SIZE(request), processes, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL, processes}) failed"; + } + + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &processes[i]; + if (!options->countKprocs && (proc->kp_proc.p_flag & P_SYSTEM)) { + continue; + } + result->processes++; + + pid_t pid = proc->kp_proc.p_pid; + struct proc_taskinfo taskInfo; + if (proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) != sizeof(taskInfo)) { + continue; + } + result->threads += 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 8410e2901..32ee53736 100644 --- a/src/detection/processes/processes_bsd.c +++ b/src/detection/processes/processes_bsd.c @@ -1,23 +1,35 @@ #include "processes.h" +#include "common/mallocHelper.h" #include #include #include -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) { +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC }; + size_t length; + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_PROC}) failed"; } - 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"; + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + FF_AUTO_FREE struct kinfo_proc* procs = malloc(length); + if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_PROC}) failed"; + } + + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &procs[i]; + + if (!options->countKprocs && (proc->ki_flag & P_KPROC)) { + continue; + } + + result->processes++; + result->threads += (uint32_t) proc->ki_numthreads; } - 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_dbsd.c b/src/detection/processes/processes_dbsd.c new file mode 100644 index 000000000..c0fa246de --- /dev/null +++ b/src/detection/processes/processes_dbsd.c @@ -0,0 +1,38 @@ +#include "processes.h" +#include "common/mallocHelper.h" + +#include +#include +#include +#include + +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { + int request[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL | (options->countKprocs ? KERN_PROC_FLAG_LWKT : 0) }; + size_t length; + if (sysctl(request, ARRAY_SIZE(request), nullptr, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}) failed"; + } + + // The process table may change between the two sysctl calls; retry with a larger buffer. + length += length / 8 + sizeof(struct kinfo_proc); + FF_AUTO_FREE struct kinfo_proc* procs = malloc(length); + if (sysctl(request, ARRAY_SIZE(request), procs, &length, nullptr, 0) != 0) { + return "sysctl({CTL_KERN, KERN_PROC, KERN_PROC_ALL}) failed"; + } + + uint32_t count = (uint32_t) (length / sizeof(struct kinfo_proc)); + for (uint32_t i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &procs[i]; + + if (proc->kp_pid == -1) { + // Kernel thread, only returned when countKprocs is set via KERN_PROC_FLAG_LWKT + result->threads++; + continue; + } + + result->processes++; + result->threads += (uint32_t) proc->kp_nthreads; + } + + return nullptr; +} diff --git a/src/detection/processes/processes_gnu.c b/src/detection/processes/processes_gnu.c index 73d287d5d..ba4351f5e 100644 --- a/src/detection/processes/processes_gnu.c +++ b/src/detection/processes/processes_gnu.c @@ -3,7 +3,7 @@ #include #include -const char* ffDetectProcesses(FFProcessesResult* result) { +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { struct ps_context* context = nullptr; if (ps_context_create(getproc(), &context) != 0) { return "ps_context_create(getproc()) failed"; @@ -16,26 +16,27 @@ const char* ffDetectProcesses(FFProcessesResult* result) { goto done; } - struct proc_stat **stats = nullptr; - unsigned int numProcs = 0; - if (proc_stat_list_add_all(list, &stats, &numProcs) != 0) { + if (proc_stat_list_add_all(list, nullptr, nullptr) != 0) { error = "proc_stat_list_add_all() failed"; goto done; } - 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]); + proc_stat_list_set_flags(list, PSTAT_NUM_THREADS); + + for (uint32_t i = 0; i < list->num_procs; i++) { + struct proc_stat* stat = list->proc_stats[i]; + if (!options->countKprocs && proc_stat_pid(stat) == 2) { // Kernel Task + continue; + } + + if (proc_stat_has(stat, PSTAT_NUM_THREADS)) { + result->threads += proc_stat_num_threads(stat); + } } - result->processes = (uint32_t) numProcs; - result->threads = nthread; + result->processes = (uint32_t) list->num_procs; 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 8b0c1cd4b..484a32119 100644 --- a/src/detection/processes/processes_haiku.c +++ b/src/detection/processes/processes_haiku.c @@ -2,7 +2,7 @@ #include -const char* ffDetectProcesses(FFProcessesResult* result) { +const char* ffDetectProcesses(const FFProcessesOptions*, FFProcessesResult* result) { system_info info; if (get_system_info(&info) != B_OK) { return "Error getting system info"; diff --git a/src/detection/processes/processes_linux.c b/src/detection/processes/processes_linux.c index 05048d55a..68f819481 100644 --- a/src/detection/processes/processes_linux.c +++ b/src/detection/processes/processes_linux.c @@ -4,26 +4,23 @@ #include "common/memrchr.h" #include "common/strutil.h" -const char* ffDetectProcesses(FFProcessesResult* result) { +#define PF_KTHREAD 0x00200000 // defined in kernel include/linux/sched.h + +const char* ffDetectProcesses(const FFProcessesOptions* options, 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 ((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]; + char statBuffer[512]; ssize_t statLength = ffReadFileDataRelative(procfd, statPath, sizeof(statBuffer) - 1, statBuffer); if (statLength < 0) { continue; @@ -34,27 +31,43 @@ const char* ffDetectProcesses(FFProcessesResult* result) { if (!cursor) { continue; } - ++cursor; + cursor += 2; // skip ") " + if (__builtin_expect(cursor >= statBuffer + statLength, false)) { + goto skip_process; + } - for (uint32_t field = 2 /*comm*/; field < 20 /*num_threads*/; ++field) { - while (*cursor != ' ' && __builtin_expect(*cursor != '\0', true)) { - ++cursor; + char* p; + for (uint32_t field = 3; field <= 20 /*num_threads*/; ++field) { + switch (field) { + case 9: // flags + if ((strtoul(cursor, &p, 10) & PF_KTHREAD) && !options->countKprocs) { + goto skip_process; + } + cursor = p; + break; + + case 20: // num_threads + result->threads += (uint32_t) strtoul(cursor, &p, 10); + cursor = p; + break; + + default: + while (*cursor != ' ' && __builtin_expect(*cursor != '\0', true)) { + ++cursor; + } + break; } + ++cursor; - if (__builtin_expect(*cursor == '\0', false)) { - break; + if (__builtin_expect(cursor >= statBuffer + statLength, false)) { + goto skip_process; } } - if (__builtin_expect(*cursor == '\0', false)) { - continue; - } - threads += (uint32_t) strtoul(cursor, nullptr, 10); + ++result->processes; } + skip_process:; } - 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 8e89ab3a1..cb91db096 100644 --- a/src/detection/processes/processes_nbsd.c +++ b/src/detection/processes/processes_nbsd.c @@ -3,7 +3,7 @@ #include -const char* ffDetectProcesses(FFProcessesResult* result) { +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { int request[] = { CTL_KERN, KERN_PROC2, KERN_PROC_ALL, -1, sizeof(struct kinfo_proc2), INT_MAX }; size_t length = 0; @@ -17,18 +17,21 @@ const char* ffDetectProcesses(FFProcessesResult* result) { } 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) { + const struct kinfo_proc2* proc = &procs[i]; + if (!options->countKprocs && (proc->p_flag & P_SYSTEM)) { + continue; + } + ++result->processes; + 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 += (uint32_t) (lwpLength / sizeof(struct kinfo_lwp)); } } - result->threads = threads; return nullptr; } diff --git a/src/detection/processes/processes_obsd.c b/src/detection/processes/processes_obsd.c index cb960ce57..d0de8170d 100644 --- a/src/detection/processes/processes_obsd.c +++ b/src/detection/processes/processes_obsd.c @@ -1,26 +1,36 @@ #include "processes.h" +#include "common/mallocHelper.h" + #include #include +#include -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"; +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { + kvm_t* kd = kvm_open(nullptr, nullptr, nullptr, KVM_NO_FILES, nullptr); + if (!kd) { + return "kvm_open() 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"; + int count = 0; + // KERN_PROC_ALL returns all user-level processes, excluding kernel processes and threads + const struct kinfo_proc* procs = kvm_getprocs(kd, + (options->countKprocs ? KERN_PROC_KTHREAD : KERN_PROC_ALL) | KERN_PROC_SHOW_THREADS, + 0, sizeof(struct kinfo_proc), &count); + if (!procs) { + kvm_close(kd); + return "kvm_getprocs() failed"; } - result->threads = (uint32_t) (threadLength / sizeof(struct kinfo_proc)); + for (int i = 0; i < count; ++i) { + const struct kinfo_proc* proc = &procs[i]; + ++result->threads; + if (proc->p_tid == -1) { + ++result->processes; + } + } + + kvm_close(kd); return nullptr; } diff --git a/src/detection/processes/processes_sunos.c b/src/detection/processes/processes_sunos.c index ce0da325c..a815390bb 100644 --- a/src/detection/processes/processes_sunos.c +++ b/src/detection/processes/processes_sunos.c @@ -5,34 +5,36 @@ #include -const char* ffDetectProcesses(FFProcessesResult* result) { +const char* ffDetectProcesses(const FFProcessesOptions* options, 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"); + char path[32]; + char* pidEnd = ffStrCopy(path, entry->d_name, sizeof(path)); + if (!options->countKprocs) { + pstatus_t status; + strcpy(pidEnd, "/status"); + if (ffReadFileDataRelative(procfd, path, sizeof(status), &status) != (ssize_t) sizeof(status) || + status.pr_flags & PR_ISSYS) { + continue; // The process may have exited, no permission or is a kernel process + } + } + strcpy(pidEnd, "/psinfo"); psinfo_t info; - if (ffReadFileDataRelative(procfd, psinfoPath, sizeof(info), &info) == (ssize_t) sizeof(info)) { - threads += info.pr_nlwp; + if (ffReadFileDataRelative(procfd, path, sizeof(info), &info) == (ssize_t) sizeof(info)) { + ++result->processes; + result->threads += (uint32_t) 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 cb617099e..7ccd13149 100644 --- a/src/detection/processes/processes_windows.c +++ b/src/detection/processes/processes_windows.c @@ -4,7 +4,7 @@ #include #include -const char* ffDetectProcesses(FFProcessesResult* result) { +const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) { FF_AUTO_FREE SYSTEM_PROCESS_INFORMATION* pstart = nullptr; // Multiple attempts in case processes change while @@ -25,17 +25,16 @@ const char* ffDetectProcesses(FFProcessesResult* 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 (!options->countKprocs && ptr->CreateTime.QuadPart == 0) { + continue; + } + ++result->processes; + result->threads += ptr->NumberOfThreads; if (ptr->NextEntryOffset == 0) { break; } } - result->processes = processes; - result->threads = threads; return nullptr; } diff --git a/src/modules/processes/option.h b/src/modules/processes/option.h index 2ee25634d..9ef218135 100644 --- a/src/modules/processes/option.h +++ b/src/modules/processes/option.h @@ -4,6 +4,8 @@ typedef struct FFProcessesOptions { FFModuleArgs moduleArgs; + + bool countKprocs; } FFProcessesOptions; static_assert(sizeof(FFProcessesOptions) <= FF_OPTION_MAX_SIZE, "FFProcessesOptions size exceeds maximum allowed size"); diff --git a/src/modules/processes/processes.c b/src/modules/processes/processes.c index 9bb68239c..e0838a925 100644 --- a/src/modules/processes/processes.c +++ b/src/modules/processes/processes.c @@ -6,7 +6,7 @@ bool ffPrintProcesses(FFProcessesOptions* options) { FFProcessesResult result = {}; - const char* error = ffDetectProcesses(&result); + const char* error = ffDetectProcesses(options, &result); if (error) { ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error); @@ -32,17 +32,23 @@ void ffParseProcessesJsonObject(FFProcessesOptions* options, yyjson_val* module) continue; } + if (unsafe_yyjson_equals_str(key, "countKprocs")) { + options->countKprocs = yyjson_get_bool(val); + continue; + } + ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Processes), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } void ffGenerateProcessesJsonConfig(FFProcessesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs); + yyjson_mut_obj_add_bool(doc, module, "countKprocs", options->countKprocs); } bool ffGenerateProcessesJsonResult([[maybe_unused]] FFProcessesOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { FFProcessesResult result = {}; - const char* error = ffDetectProcesses(&result); + const char* error = ffDetectProcesses(options, &result); if (error) { yyjson_mut_obj_add_str(doc, module, "error", error); @@ -57,6 +63,8 @@ bool ffGenerateProcessesJsonResult([[maybe_unused]] FFProcessesOptions* options, void ffInitProcessesOptions(FFProcessesOptions* options) { ffOptionInitModuleArg(&options->moduleArgs, ""); + + options->countKprocs = false; } void ffDestroyProcessesOptions(FFProcessesOptions* options) { @@ -65,7 +73,7 @@ void ffDestroyProcessesOptions(FFProcessesOptions* options) { FFModuleBaseInfo ffProcessesModuleInfo = { .name = "Processes", - .description = "Print number of running processes", + .description = "Print number of running processes and threads", .displayName = { .en = "Processes", .ar = "العمليات",