master ohmu/fastfetch-openbsd / src / detection / cpu / cpu_sunos.c
ohmu  ·  2026-10-15
  1#include "cpu.h"
  2#include "common/debug.h"
  3#include "common/processing.h"
  4#include "common/strutil.h"
  5#include <kstat.h>
  6
  7static const char* detectCPUTempByKstat(const FFCPUOptions* options, kstat_ctl_t* kc, FFCPUResult* cpu) {
  8    const char* possibleModules[] = { "temperature", "cpu_temp", "acpi_thermal", nullptr };
  9
 10    if (options->tempSensor.length > 0) {
 11        possibleModules[0] = options->tempSensor.chars;
 12        possibleModules[1] = nullptr;
 13    }
 14
 15    for (int i = 0; possibleModules[i] != nullptr; i++) {
 16        kstat_t* ks = kstat_lookup(kc, possibleModules[i], -1, nullptr);
 17        if (ks && kstat_read(kc, ks, nullptr) >= 0) {
 18            kstat_named_t* kn = kstat_data_lookup(ks, "temperature");
 19            if (kn) {
 20                switch (kn->data_type) {
 21                    case KSTAT_DATA_INT32:
 22                        cpu->temperature = (float) kn->value.i32;
 23                        return nullptr;
 24                    case KSTAT_DATA_UINT32:
 25                        cpu->temperature = (float) kn->value.ui32;
 26                        return nullptr;
 27                    case KSTAT_DATA_FLOAT:
 28                        cpu->temperature = kn->value.f;
 29                        return nullptr;
 30                }
 31            }
 32        }
 33    }
 34
 35    FF_DEBUG("Failed to find CPU temperature using kstat");
 36    return "Failed to find CPU temperature using kstat";
 37}
 38
 39static const char* detectCPUTempByIpmiTool(FFCPUResult* cpu) {
 40    FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
 41    const char* error = ffProcessAppendStdOut(&buffer, (char* const[]) { "ipmitool", "-c", "sdr", "list", nullptr });
 42
 43    if (error) {
 44        return error;
 45    }
 46
 47    char* line = nullptr;
 48    size_t len = 0;
 49    while (ffStrbufGetline(&line, &len, &buffer)) {
 50        if (sscanf(line, "CPU%*d Temp,%lf,degrees C,ok", &cpu->temperature) == 1) {
 51            return nullptr;
 52        }
 53    }
 54
 55    FF_DEBUG("ipmitool sdr list failed to find CPU temperature");
 56    return "ipmitool sdr list failed to find CPU temperature";
 57}
 58
 59static inline void kstatFreeWrap(kstat_ctl_t** pkc) {
 60    assert(pkc);
 61    if (*pkc) {
 62        kstat_close(*pkc);
 63    }
 64}
 65
 66static inline uint16_t countTypeId(kstat_ctl_t* kc, const char* type) {
 67    uint64_t low = 0, high = 0;
 68    for (kstat_t* ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
 69        if (ffStrStartsWith(ksp->ks_module, "cpu_info")) {
 70            if (kstat_read(kc, ksp, nullptr) < 0) {
 71                continue;
 72            }
 73
 74            kstat_named_t* stat = kstat_data_lookup(ksp, type);
 75            if (!stat) {
 76                continue;
 77            }
 78
 79            uint32_t id = 0;
 80            switch (stat->data_type) {
 81#ifdef _INT64_TYPE
 82                case KSTAT_DATA_INT64:
 83                case KSTAT_DATA_UINT64:
 84                    id = (uint32_t) stat->value.ui64;
 85                    break;
 86#endif
 87                case KSTAT_DATA_INT32:
 88                case KSTAT_DATA_UINT32:
 89                    id = stat->value.ui32;
 90                    break;
 91                default:
 92                    continue;
 93            }
 94            if (__builtin_expect(id > 64, false)) {
 95                high |= 1ULL << (id - 64);
 96            } else {
 97                low |= 1ULL << id;
 98            }
 99        }
100    }
101    return (uint16_t) (__builtin_popcountll(low) + __builtin_popcountll(high));
102}
103
104const char* ffDetectCPUImpl(const FFCPUOptions* options, FFCPUResult* cpu) {
105    [[gnu::cleanup(kstatFreeWrap)]] kstat_ctl_t* kc = kstat_open();
106    if (!kc) {
107        FF_DEBUG("kstat_open() failed");
108        return "kstat_open() failed";
109    }
110
111    kstat_t* ks = kstat_lookup(kc, "cpu_info", -1, nullptr);
112    if (!ks) {
113        FF_DEBUG("kstat_lookup() failed");
114        return "kstat_lookup() failed";
115    }
116
117    if (kstat_read(kc, ks, nullptr) < 0) {
118        FF_DEBUG("kstat_read() failed");
119        return "kstat_read() failed";
120    }
121
122    {
123        kstat_named_t* kn = kstat_data_lookup(ks, "brand");
124        if (kn) {
125            ffStrbufSetNS(&cpu->name, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn));
126        }
127    }
128    {
129        kstat_named_t* kn = kstat_data_lookup(ks, "vendor_id");
130        if (kn) {
131            ffStrbufSetNS(&cpu->vendor, KSTAT_NAMED_STR_BUFLEN(kn) - 1, KSTAT_NAMED_STR_PTR(kn));
132        }
133    }
134    ffCPUDetectByCpuid(cpu);
135    {
136        kstat_named_t* kn = kstat_data_lookup(ks, "clock_MHz");
137        if (kn && kn->value.ui32 > cpu->frequencyBase) {
138            cpu->frequencyBase = kn->value.ui32;
139        }
140    }
141
142    ks = kstat_lookup(kc, "unix", -1, "system_misc");
143    if (ks && kstat_read(kc, ks, nullptr) >= 0) {
144        kstat_named_t* kn = kstat_data_lookup(ks, "ncpus");
145        if (kn) {
146            cpu->coresLogical = cpu->coresOnline = (uint16_t) kn->value.ui32;
147        }
148    }
149
150    cpu->packages = countTypeId(kc, "chip_id");
151    cpu->coresPhysical = countTypeId(kc, "core_id");
152
153    if (options->temp) {
154        if (detectCPUTempByKstat(options, kc, cpu) != nullptr) {
155            detectCPUTempByIpmiTool(cpu);
156        }
157    }
158
159    return nullptr;
160}