master ohmu/fastfetch-openbsd / src / detection / processes / processes_linux.c
ohmu  ·  2026-10-15
 1#include "processes.h"
 2
 3#include "common/debug.h"
 4#include "common/io.h"
 5#include "common/memrchr.h"
 6#include "common/strutil.h"
 7
 8#include <string.h>
 9
10#define PF_KTHREAD 0x00200000 // defined in kernel include/linux/sched.h
11
12const char* ffDetectProcesses(const FFProcessesOptions* options, FFProcessesResult* result) {
13    FF_AUTO_CLOSE_DIR DIR* dir = opendir("/proc");
14    if (dir == nullptr) {
15        FF_DEBUG("opendir(/proc) failed: %s", strerror(errno));
16        return "opendir(\"/proc\") failed";
17    }
18
19    int procfd = dirfd(dir);
20
21    struct dirent* entry;
22    while ((entry = readdir(dir)) != nullptr) {
23        if ((entry->d_type == DT_DIR || entry->d_type == DT_UNKNOWN) && ffCharIsDigit(entry->d_name[0])) {
24            char statPath[32];
25            strcpy(ffStrCopy(statPath, entry->d_name, sizeof(statPath)), "/stat");
26
27            char statBuffer[512];
28            ssize_t statLength = ffReadFileDataRelative(procfd, statPath, sizeof(statBuffer) - 1, statBuffer);
29            if (statLength < 0) {
30                continue;
31            }
32            statBuffer[statLength] = '\0';
33
34            const char* cursor = (const char*) memrchr(statBuffer, ')', (size_t) statLength);
35            if (!cursor) {
36                continue;
37            }
38            cursor += 2; // skip ") "
39            if (__builtin_expect(cursor >= statBuffer + statLength, false)) {
40                goto skip_process;
41            }
42
43            char* p;
44            for (uint32_t field = 3; field <= 20 /*num_threads*/; ++field) {
45                switch (field) {
46                    case 9: // flags
47                        if ((strtoul(cursor, &p, 10) & PF_KTHREAD) && !options->countKprocs) {
48                            goto skip_process;
49                        }
50                        cursor = p;
51                        break;
52
53                    case 20: // num_threads
54                        result->threads += (uint32_t) strtoul(cursor, &p, 10);
55                        cursor = p;
56                        break;
57
58                    default:
59                        while (*cursor != ' ' && __builtin_expect(*cursor != '\0', true)) {
60                            ++cursor;
61                        }
62                        break;
63                }
64
65                ++cursor;
66                if (__builtin_expect(cursor >= statBuffer + statLength, false)) {
67                    goto skip_process;
68                }
69            }
70
71            ++result->processes;
72        }
73        skip_process:;
74    }
75
76    return nullptr;
77}