Compare commits

...
6 Commits
Author SHA1 Message Date
Carter Li 6a82126aa9 Logo (Builtin): removes Turkmen Linux
I **LOVE** removing code

Ref: #2422
2026-11-10 18:40:37 +08:00
Carter Li 798e2ccc24 Shell (Windows): add pwsh-preview version detection 2026-11-09 12:27:18 +08:00
Carter Li 2978651c9d GPU (Linux): marks Redhat Virtio GPU as integrated 2026-11-09 09:55:32 +08:00
Carter Li ced33a8355 GPU (Linux): improves PCI GPU name resolution 2026-11-09 00:16:22 +08:00
Carter Li 6843b56f1f PhysicalMemory (Linux): reads unprivileged DMI physical memory slots from /run/udev/data/+dmi:id 2026-11-07 21:13:21 +08:00
Piotr Kubaj c018073384 Libc (FreeBSD): uses base objdump to detect libc version
The FBSD version was only detected when binutils' /usr/local/bin/objdump
was installed. Supported FreeBSD releases ship llvm-objdump as
/usr/bin/objdump in base, which gives the same result, so try it first
like MidnightBSD already does and keep /usr/local/bin/objdump as a
fallback.

DragonFly sets FreeBSD too, so exclude it from both branches. Until now
the FreeBSD branch caught DragonFly first and the DF version was never
detected.
2026-11-07 21:13:21 +08:00
10 changed files with 546 additions and 273 deletions

No files matched your search

+8
View File
@@ -1,3 +1,11 @@
# Unreleased
Changes:
* Improved PCI GPU name resolution by checking udev's hardware database and the NVIDIA driver's procfs before falling back to pci.ids. (GPU, Linux)
Bugfixes:
* Fixed libc version detection on FreeBSD without binutils installed, and on DragonFly BSD. (Libc, FreeBSD / DragonFly)
# 2.71.0
Features:
+10 -9
View File
@@ -620,6 +620,7 @@ if(LINUX)
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_linux.c
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/physicalmemory/physicalmemory_shared.c
src/detection/diskio/diskio_linux.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/common.c
@@ -798,7 +799,7 @@ elseif(FreeBSD)
src/detection/disk/disk_bsd.c
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_bsd.c
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/physicalmemory/physicalmemory_shared.c
src/detection/diskio/diskio_bsd.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/common.c
@@ -897,7 +898,7 @@ elseif(NetBSD)
src/detection/disk/disk_bsd.c
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_nbsd.c
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/physicalmemory/physicalmemory_shared.c
src/detection/diskio/diskio_nbsd.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/common.c
@@ -983,7 +984,7 @@ elseif(OpenBSD)
src/detection/disk/disk_bsd.c
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_obsd.c
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/physicalmemory/physicalmemory_shared.c
src/detection/diskio/diskio_obsd.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/common.c
@@ -1124,7 +1125,7 @@ elseif(APPLE)
src/detection/board/board_windows.c
src/detection/chassis/chassis_windows.c
src/detection/host/host_windows.c
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/physicalmemory/physicalmemory_shared.c
)
else()
list(APPEND LIBFASTFETCH_SRC
@@ -1188,7 +1189,7 @@ elseif(WIN32)
src/detection/media/media_windows.cpp
src/detection/memory/memory_windows.c
src/detection/mouse/mouse_windows.c
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/physicalmemory/physicalmemory_shared.c
src/detection/netio/netio_windows.c
src/detection/opengl/opengl_windows.c
src/detection/os/os_windows.c
@@ -1244,7 +1245,7 @@ elseif(SunOS)
src/detection/disk/disk_sunos.c
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_sunos.c
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/physicalmemory/physicalmemory_shared.c
src/detection/diskio/diskio_sunos.c
src/detection/displayserver/linux/displayserver_linux.c
src/detection/displayserver/linux/common.c
@@ -1329,7 +1330,7 @@ elseif(Haiku)
src/detection/disk/disk_haiku.cpp
src/detection/dns/dns_linux.c
src/detection/physicaldisk/physicaldisk_haiku.c
src/detection/physicalmemory/physicalmemory_linux.c
src/detection/physicalmemory/physicalmemory_shared.c
src/detection/diskio/diskio_nosupport.c
src/detection/displayserver/displayserver_haiku.cpp
src/detection/font/font_haiku.cpp
@@ -1610,14 +1611,14 @@ if(APPLE AND EXISTS "/usr/bin/otool")
target_compile_definitions(libfastfetch PUBLIC FF_LIBSYSTEM_VERSION="${CMAKE_MATCH_1}")
endif()
endif()
if(MidnightBSD AND EXISTS "/usr/bin/objdump")
if(FreeBSD AND NOT DragonFly AND EXISTS "/usr/bin/objdump")
execute_process(COMMAND /bin/sh -c "/usr/bin/objdump -T /lib/libc.so.* | grep 'FBSD_[0-9][0-9]*\\.[0-9][0-9]*' -o | sort -Vru | head -1"
OUTPUT_VARIABLE OBJDUMP_T_RESULT)
if("${OBJDUMP_T_RESULT}" MATCHES "FBSD_([0-9]+\\.[0-9]+)")
message(STATUS "Found FBSD ${CMAKE_MATCH_1}")
target_compile_definitions(libfastfetch PUBLIC FF_FBSD_VERSION="${CMAKE_MATCH_1}")
endif()
elseif(FreeBSD AND EXISTS "/usr/local/bin/objdump")
elseif(FreeBSD AND NOT DragonFly AND EXISTS "/usr/local/bin/objdump")
execute_process(COMMAND /bin/sh -c "/usr/local/bin/objdump -T /lib/libc.so.* | grep 'FBSD_[0-9][0-9]*\\.[0-9][0-9]*' -o | sort -Vru | head -1"
OUTPUT_VARIABLE OBJDUMP_T_RESULT)
if("${OBJDUMP_T_RESULT}" MATCHES "FBSD_([0-9]+\\.[0-9]+)")
+1 -1
View File
@@ -341,7 +341,7 @@ bool ffGPUDetectTypeByVendorAndName(FFGPUResult* gpu) {
} else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_INTEL) {
// 0000:00:02.0 is reserved for Intel integrated graphics
gpu->type = gpu->deviceId == ffGPUPciAddr2Id(0, 0, 2, 0) ? FF_GPU_TYPE_INTEGRATED : FF_GPU_TYPE_DISCRETE;
} else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_VMWARE || gpu->vendor.chars == FF_GPU_VENDOR_NAME_PARALLELS) {
} else if (gpu->vendor.chars == FF_GPU_VENDOR_NAME_VMWARE || gpu->vendor.chars == FF_GPU_VENDOR_NAME_PARALLELS || gpu->vendor.chars == FF_GPU_VENDOR_NAME_REDHAT) {
// Virtualized GPUs
gpu->type = FF_GPU_TYPE_INTEGRATED;
}
+64 -3
View File
@@ -1,12 +1,10 @@
#include "detection/gpu/gpu.h"
#include "common/debug.h"
#include "common/io.h"
#include "common/FFstrbuf.h"
#include "common/properties.h"
#include "common/strutil.h"
#include "modules/gpu/option.h"
#include <inttypes.h>
#include <stdint.h>
static bool detectDriverFromSysfs(FFstrbuf* result, FFstrbuf* pciDir, FFstrbuf* buffer, [[maybe_unused]] const char* drmKey) {
uint32_t pciDirLength = pciDir->length;
@@ -337,6 +335,64 @@ static inline uint16_t pcieWidthToLanes(const FFstrbuf* buffer) {
return FF_GPU_PCIE_SPEED_UNSET;
}
static bool parseModelName(const char* data, const char* key, bool stripVendorPrefix, FFstrbuf* name) {
ffStrbufClear(name);
if (!ffParsePropLines(data, key, name) || !name->length) {
return false;
}
ffStrbufDecodeHexEscapeSequences(name); // udev hex-escapes characters that are not allowed in property values
ffStrbufTrimSpace(name);
// hwdb names have the form `Codename [Marketing Name]`. Reduce them exactly like
// ffGPUParsePciIdsFile() does, so displaying the name does not change with the source.
if (name->length > 1 && name->chars[name->length - 1] == ']') {
const char* openingBracket = memrchr(name->chars, '[', name->length - 1);
if (openingBracket) {
ffStrbufSubstrAfter(name, (uint32_t) (openingBracket - name->chars));
ffStrbufSubstrBefore(name, name->length - 1);
}
}
// The driver repeats the vendor in its own model line. Dropping it keeps the pci.ids
// shape and lets ffGPUDetectTypeByVendorAndName() see the product family prefix.
if (stripVendorPrefix && ffStrbufStartsWithIgnCaseS(name, "NVIDIA ")) {
ffStrbufSubstrAfter(name, (uint32_t) strlen("NVIDIA ") - 1); // excludes the index itself
}
ffStrbufTrimSpace(name);
return name->length > 0;
}
static bool nameFromFile(const FFstrbuf* path, const char* key, bool stripVendorPrefix, FFstrbuf* buffer, FFstrbuf* name) {
if (!ffReadFileBuffer(path->chars, buffer) || !parseModelName(buffer->chars, key, stripVendorPrefix, name)) {
return false;
}
FF_DEBUG("GPU name resolved from %s: %s", path->chars, name->chars);
return true;
}
// The driver and udev already resolved the model name at probe / boot time. Only `Model:` is
// consumed: the sibling `power` file goes through the RM and can wake a GPU suspended in runtime D3.
static bool resolveNameFromFiles(const char* pciPath, bool isNvidia, FFstrbuf* buffer, FFstrbuf* name) {
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateA(128);
if (isNvidia) {
ffStrbufSetF(&path, "/proc/driver/nvidia/gpus/%s/information", pciPath);
if (nameFromFile(&path, "Model:", true, buffer, name)) {
return true;
}
}
ffStrbufSetF(&path, "/run/udev/data/+pci:%s", pciPath);
if (nameFromFile(&path, "E:ID_MODEL_FROM_DATABASE=", false, buffer, name)) {
return true;
}
ffStrbufClear(name);
return false;
}
static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf* buffer, FFstrbuf* deviceDir, const char* drmKey) {
const uint32_t drmDirPathLength = deviceDir->length;
uint32_t vendorId, deviceId, subVendorId, subDeviceId;
@@ -465,8 +521,13 @@ static const char* detectPci(const FFGPUOptions* options, FFlist* gpus, FFstrbuf
}
if (gpu->name.length == 0) {
// Stop at the first hit; unknown vendors are left to pci.ids, which also fills in `vendor`.
bool resolved = gpu->vendor.length > 0 &&
resolveNameFromFiles(pPciPath, gpu->vendor.chars == FF_GPU_VENDOR_NAME_NVIDIA, buffer, &gpu->name);
if (!resolved) {
ffGPUFillVendorAndName(subclassId, (uint16_t) vendorId, (uint16_t) deviceId, gpu);
}
}
ffGPUDetectTypeByVendorAndName(gpu);
@@ -1,244 +1,225 @@
#include "physicalmemory.h"
#include "common/debug.h"
#include "common/endian.h"
#include "common/io.h"
#include "common/smbios.h"
#include "common/strutil.h"
// 7.18
typedef struct [[gnu::packed]] FFSmbiosMemoryDevice {
FFSmbiosHeader Header;
// systemd >= 248 decodes SMBIOS Type 16 / 17 into udev properties of the `dmi:id` device
// (see systemd's src/udev/dmi_memory_id/dmi_memory_id.c). The database is world readable,
// while /sys/firmware/dmi/tables/DMI and /dev/mem are root-only, so try it first.
#define FF_UDEV_DMI_ID_DB "/run/udev/data/+dmi:id"
#define FF_UDEV_MEMORY_DEVICE_PREFIX "E:MEMORY_DEVICE_"
#define FF_UDEV_MAX_MEMORY_DEVICES 256
// 2.1+
uint16_t PhysicalMemoryArrayHandle; // varies
uint16_t MemoryErrorInformationHandle; // varies
uint16_t TotalWidth; // varies
uint16_t DataWidth; // varies
uint16_t Size; // varies
uint8_t FormFactor; // enum
uint8_t DeviceSet; // varies
uint8_t DeviceLocator; // string
uint8_t BankLocator; // string
uint8_t MemoryType; // enum
uint16_t TypeDetail; // bit field
static void ffUdevResultAdd(FFlist* result) {
*FF_LIST_ADD(FFPhysicalMemoryResult, *result) = (FFPhysicalMemoryResult){
// Doubles as parse-time state: absent PRESENT / SIZE means "present, unknown
// size", so it starts true and is only ever cleared by PRESENT=0 or SIZE=0.
.installed = true,
.type = ffStrbufCreate(),
.formFactor = ffStrbufCreate(),
.locator = ffStrbufCreate(),
.partNumber = ffStrbufCreate(),
.vendor = ffStrbufCreate(),
.serial = ffStrbufCreate(),
};
}
// 2.3+
uint16_t Speed; // varies
uint8_t Manufacturer; // string
uint8_t SerialNumber; // string
uint8_t AssetTag; // string
uint8_t PartNumber; // string
static void ffUdevResultDestroy(FFPhysicalMemoryResult* device) {
ffStrbufDestroy(&device->type);
ffStrbufDestroy(&device->formFactor);
ffStrbufDestroy(&device->locator);
ffStrbufDestroy(&device->partNumber);
ffStrbufDestroy(&device->vendor);
ffStrbufDestroy(&device->serial);
}
// 2.6+
uint8_t Attributes; // varies
// udev hex-escapes characters that are not allowed in property values (\xHH).
// Also drop the placeholder strings ("Unknown", "0x0000...", "<OUT OF SPEC>", ...).
static void ffUdevSetString(FFstrbuf* strbuf, const char* value) {
ffStrbufSetS(strbuf, value);
ffStrbufDecodeHexEscapeSequences(strbuf);
// Older systemd versions print an empty value, newer ones "<OUT OF SPEC>".
if (ffStrbufStartsWithS(strbuf, "<OUT OF SPEC>")) {
ffStrbufClear(strbuf);
}
ffCleanUpSmbiosValue(strbuf);
}
// 2.7+
uint32_t ExtendedSize; // varies
uint16_t ConfiguredMemorySpeed; // varies
// `locator` is the "bank/device" join, but the two properties arrive in no guaranteed
// order. Prepending the bank and appending the device yields the same result either way.
// systemd emits each property at most once per device.
static void ffUdevAppendLocator(FFPhysicalMemoryResult* device, const char* value, bool bank) {
FF_STRBUF_AUTO_DESTROY part = ffStrbufCreate();
ffUdevSetString(&part, value);
if (part.length == 0) {
return;
}
// 2.8+
uint16_t MinimumVoltage; // varies
uint16_t MaximumVoltage; // varies
uint16_t ConfiguredVoltage; // varies
if (device->locator.length > 0) {
if (bank) {
ffStrbufPrependC(&device->locator, '/');
} else {
ffStrbufAppendC(&device->locator, '/');
}
}
if (bank) {
ffStrbufPrepend(&device->locator, &part);
} else {
ffStrbufAppend(&device->locator, &part);
}
}
// 3.2+
uint8_t MemoryTechnology; // varies
uint16_t MemoryOperatingMode; // bit field
uint8_t FirmwareVersion; // string
uint16_t ModuleManufacturerID; // varies
uint16_t ModuleProductID; // varies
uint16_t MemorySubsystemControllerManufacturerID; // vaies
uint16_t MemorySubsystemControllerProductID; // varies
uint64_t NonVolatileSize; // varies
uint64_t VolatileSize; // varies
uint64_t CacheSize; // varies
uint64_t LogicalSize; // varies
// Splits a line like "E:MEMORY_DEVICE_0_SIZE=4294967296" in place. Returns the field
// name, or nullptr when the line is unrelated or has an out-of-range device index.
// The mere presence of such a line means the udev database describes the memory devices
// completely, whether or not its field is recognized or its value is a placeholder.
static const char* ffUdevParseHeader(char* line, uint32_t* index, const char** value) {
if (!ffStrStartsWith(line, FF_UDEV_MEMORY_DEVICE_PREFIX)) {
return nullptr;
}
// 3.3+
uint32_t ExtendedSpeed; // varies
uint32_t ExtendedConfiguredSpeed; // varies
char* eq = strchr(line, '=');
if (!eq) {
return nullptr;
}
// 3.7+
uint16_t Pmic0ManufacturerID; // varies
uint16_t Pmic0RevisionNumber; // varies
uint16_t RcdManufacturerID; // varies
uint16_t RcdRevisionNumber; // varies
} FFSmbiosMemoryDevice;
const char* p = line + sizeof(FF_UDEV_MEMORY_DEVICE_PREFIX) - 1;
char* end = nullptr;
unsigned long n = strtoul(p, &end, 10);
if (end == p || *end != '_' || n >= FF_UDEV_MAX_MEMORY_DEVICES) {
return nullptr;
}
static_assert(offsetof(FFSmbiosMemoryDevice, RcdRevisionNumber) == 0x62,
"FFSmbiosMemoryDevice: Wrong struct alignment");
*eq = '\0';
*index = (uint32_t) n;
*value = eq + 1;
return end + 1;
}
// The udev database is authoritative: as soon as it contains any MEMORY_DEVICE_*
// property, the set of memory devices it describes is considered complete and the
// caller must not fall back to SMBIOS -- even when every slot is empty, all values
// are placeholders, or showEmptySlots filters everything out of `result`.
static bool ffDetectPhysicalMemoryFromUdev(FFPhysicalMemoryOptions* options, FFlist* result) {
FF_STRBUF_AUTO_DESTROY db = ffStrbufCreate();
if (!ffReadFileBuffer(FF_UDEV_DMI_ID_DB, &db)) {
FF_DEBUG("Failed to read %s", FF_UDEV_DMI_ID_DB);
return false;
}
// The udev index addresses `result` directly: MEMORY_DEVICE_n_* maps to entry
// `base + n`. Entries are therefore naturally sorted and can be updated in place,
// without a staging list, a comparator or any result copying. FFstrbuf holds no
// pointer into itself, so FF_LIST_ADD may relocate existing entries freely.
// Sparse indices leave placeholder entries behind; they are removed below.
bool seen[FF_UDEV_MAX_MEMORY_DEVICES] = { false };
uint16_t totalWidths[FF_UDEV_MAX_MEMORY_DEVICES] = { 0 };
uint16_t dataWidths[FF_UDEV_MAX_MEMORY_DEVICES] = { 0 };
const uint32_t base = result->length;
bool found = false;
char* line = nullptr;
size_t len = 0;
while (ffStrbufGetline(&line, &len, &db)) {
// `len` must stay intact: the next ffStrbufGetline call advances the cursor by it.
if (len > 0 && line[len - 1] == '\r') {
line[len - 1] = '\0';
}
uint32_t index;
const char* value;
const char* field = ffUdevParseHeader(line, &index, &value);
if (!field) {
continue;
}
while (base + index >= result->length) {
ffUdevResultAdd(result);
}
found = true;
seen[index] = true;
FFPhysicalMemoryResult* device = FF_LIST_GET(FFPhysicalMemoryResult, *result, base + index);
if (ffStrEquals(field, "SIZE")) {
device->size = strtoull(value, nullptr, 10);
if (device->size == 0) {
device->installed = false;
}
} else if (ffStrEquals(field, "PRESENT")) {
if (strtoul(value, nullptr, 10) == 0) {
device->installed = false;
}
} else if (ffStrEquals(field, "TOTAL_WIDTH")) {
totalWidths[index] = (uint16_t) strtoul(value, nullptr, 10);
} else if (ffStrEquals(field, "DATA_WIDTH")) {
dataWidths[index] = (uint16_t) strtoul(value, nullptr, 10);
} else if (ffStrEquals(field, "SPEED_MTS")) {
device->maxSpeed = (uint32_t) strtoul(value, nullptr, 10);
} else if (ffStrEquals(field, "CONFIGURED_SPEED_MTS")) {
device->runningSpeed = (uint32_t) strtoul(value, nullptr, 10);
} else if (ffStrEquals(field, "FORM_FACTOR")) {
ffUdevSetString(&device->formFactor, value);
} else if (ffStrEquals(field, "BANK_LOCATOR")) {
ffUdevAppendLocator(device, value, true);
} else if (ffStrEquals(field, "LOCATOR")) {
ffUdevAppendLocator(device, value, false);
} else if (ffStrEquals(field, "TYPE")) {
ffUdevSetString(&device->type, value);
} else if (ffStrEquals(field, "MANUFACTURER")) {
ffUdevSetString(&device->vendor, value);
} else if (ffStrEquals(field, "SERIAL_NUMBER")) {
ffUdevSetString(&device->serial, value);
} else if (ffStrEquals(field, "PART_NUMBER")) {
ffUdevSetString(&device->partNumber, value);
}
}
if (!found) {
FF_DEBUG("No MEMORY_DEVICE_* properties in %s", FF_UDEV_DMI_ID_DB);
return false;
}
// Finalize and drop sparse / empty slots. `i` tracks the current list position
// while `index` tracks the original udev index, which survives the removals.
uint32_t i = base;
for (uint32_t index = 0; i < result->length; ++index) {
FFPhysicalMemoryResult* device = FF_LIST_GET(FFPhysicalMemoryResult, *result, i);
if (!seen[index] || (!device->installed && !options->showEmptySlots)) {
ffUdevResultDestroy(device);
FF_LIST_REMOVE_AT(FFPhysicalMemoryResult, *result, i);
continue;
}
device->ecc = totalWidths[index] > 0 && dataWidths[index] > 0 &&
totalWidths[index] > dataWidths[index];
if (!device->installed) {
device->maxSpeed = device->runningSpeed = 0;
ffStrbufSetStatic(&device->type, "Empty");
ffStrbufClear(&device->vendor);
ffStrbufClear(&device->serial);
ffStrbufClear(&device->partNumber);
} else {
if (device->type.length == 0) {
ffStrbufSetStatic(&device->type, "Unknown");
}
FFPhysicalMemoryUpdateVendorString(device);
}
++i;
}
return true;
}
const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* result) {
const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable();
if (!smbiosTable) {
FF_DEBUG("Failed to get SMBIOS data");
return "Failed to get SMBIOS data";
}
const FFSmbiosMemoryDevice* data = (const FFSmbiosMemoryDevice*) (*smbiosTable)[FF_SMBIOS_TYPE_MEMORY_DEVICE];
if (!data) {
FF_DEBUG("Memory device is not found in SMBIOS data");
return "Memory device is not found in SMBIOS data";
}
const FFSmbiosMemoryDevice* endOfTable = (const FFSmbiosMemoryDevice*) (*smbiosTable)[FF_SMBIOS_TYPE_END_OF_TABLE];
for (; data != endOfTable; data = (const FFSmbiosMemoryDevice*) ffSmbiosNextEntry(&data->Header)) {
if (data->Header.Type != FF_SMBIOS_TYPE_MEMORY_DEVICE) {
continue;
}
const char* strings = (const char*) data + data->Header.Length;
uint16_t size = FF_READ_LE(data->Size);
bool installed = size != 0;
if (!installed && !options->showEmptySlots) {
continue;
}
FFPhysicalMemoryResult* device = FF_LIST_ADD(FFPhysicalMemoryResult, *result);
ffStrbufInit(&device->type);
ffStrbufInit(&device->formFactor);
ffStrbufInit(&device->locator);
ffStrbufInit(&device->vendor);
ffStrbufInit(&device->serial);
ffStrbufInit(&device->partNumber);
device->size = 0;
device->maxSpeed = 0;
device->runningSpeed = 0;
device->installed = installed;
device->ecc = false;
uint16_t totalWidth = FF_READ_LE(data->TotalWidth);
uint16_t dataWidth = FF_READ_LE(data->DataWidth);
if (installed && totalWidth != 0xFFFF && dataWidth != 0xFFFF) {
device->ecc = totalWidth > dataWidth;
}
if (installed && size != 0xFFFF) {
if (size == 0x7FFF) {
device->size = (FF_READ_LE(data->ExtendedSize) & ~(1ULL << 31)) * 1024ULL * 1024ULL;
} else if (size & (1 << 15)) {
// in kB
device->size = (size & ~(1ULL << 15)) * 1024ULL;
} else {
// in MB
device->size = size * 1024ULL * 1024ULL;
}
}
// https://github.com/fastfetch-cli/fastfetch/issues/1051#issuecomment-2206687345
const char* lbank = ffSmbiosLocateString(strings, data->BankLocator);
const char* ldevice = ffSmbiosLocateString(strings, data->DeviceLocator);
if (lbank && ldevice) {
ffStrbufSetF(&device->locator, "%s/%s", lbank, ldevice);
} else if (lbank) {
ffStrbufSetS(&device->locator, lbank);
} else if (ldevice) {
ffStrbufSetS(&device->locator, ldevice);
}
const char* formFactorNames[] = {
nullptr, // 0x00 (Placeholder for indexing)
"Other", // 0x01
"Unknown", // 0x02
"SIMM", // 0x03
"SIP", // 0x04
"Chip", // 0x05
"DIP", // 0x06
"ZIP", // 0x07
"Proprietary Card", // 0x08
"DIMM", // 0x09
"TSOP", // 0x0A
"Row of chips", // 0x0B
"RIMM", // 0x0C
"SODIMM", // 0x0D
"SRIMM", // 0x0E
"FBDIMM", // 0x0F
"Die", // 0x10
"CAMM", // 0x11
"CUDIMM", // 0x12
"CSODIMM", // 0x13
};
if (data->FormFactor > 0 && data->FormFactor < ARRAY_SIZE(formFactorNames)) {
ffStrbufSetS(&device->formFactor, formFactorNames[data->FormFactor]);
} else {
ffStrbufSetF(&device->formFactor, "Unknown (%d)", (int) data->FormFactor);
}
const char* memoryTypeNames[] = {
nullptr, // 0x00 (Placeholder for indexing)
"Other", // 0x01
"Unknown", // 0x02
"DRAM", // 0x03
"EDRAM", // 0x04
"VRAM", // 0x05
"SRAM", // 0x06
"RAM", // 0x07
"ROM", // 0x08
"FLASH", // 0x09
"EEPROM", // 0x0A
"FEPROM", // 0x0B
"EPROM", // 0x0C
"CDRAM", // 0x0D
"3DRAM", // 0x0E
"SDRAM", // 0x0F
"SGRAM", // 0x10
"RDRAM", // 0x11
"DDR", // 0x12
"DDR2", // 0x13
"DDR2 FB-DIMM", // 0x14
"Reserved", // 0x15
"Reserved", // 0x16
"Reserved", // 0x17
"DDR3", // 0x18
"FBD2", // 0x19
"DDR4", // 0x1A
"LPDDR", // 0x1B
"LPDDR2", // 0x1C
"LPDDR3", // 0x1D
"LPDDR4", // 0x1E
"Logical non-volatile device", // 0x1F
"HBM", // 0x20
"HBM2", // 0x21
"DDR5", // 0x22
"LPDDR5", // 0x23
"HBM3", // 0x24
"MRDIMM", // 0x25
};
if (!installed) {
ffStrbufSetStatic(&device->type, "Empty");
} else if (data->MemoryType > 0 && data->MemoryType < ARRAY_SIZE(memoryTypeNames)) {
ffStrbufSetStatic(&device->type, memoryTypeNames[data->MemoryType]);
} else {
ffStrbufSetF(&device->type, "Unknown (%d)", (int) data->MemoryType);
}
if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, Speed)) // 2.3+
{
uint16_t speed = FF_READ_LE(data->Speed);
if (speed) {
device->maxSpeed = speed == 0xFFFF ? FF_READ_LE(data->ExtendedSpeed) : speed;
}
ffStrbufSetStatic(&device->vendor, ffSmbiosLocateString(strings, data->Manufacturer));
ffCleanUpSmbiosValue(&device->vendor);
FFPhysicalMemoryUpdateVendorString(device);
ffStrbufSetStatic(&device->serial, ffSmbiosLocateString(strings, data->SerialNumber));
ffCleanUpSmbiosValue(&device->serial);
ffStrbufSetStatic(&device->partNumber, ffSmbiosLocateString(strings, data->PartNumber));
ffCleanUpSmbiosValue(&device->partNumber);
}
if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, ConfiguredMemorySpeed)) // 2.7+
{
uint16_t configuredSpeed = FF_READ_LE(data->ConfiguredMemorySpeed);
if (configuredSpeed) {
device->runningSpeed = configuredSpeed == 0xFFFF
? FF_READ_LE(data->ExtendedConfiguredSpeed)
: configuredSpeed;
}
}
}
if (ffDetectPhysicalMemoryFromUdev(options, result)) {
FF_DEBUG("Physical memory detected from %s", FF_UDEV_DMI_ID_DB);
return nullptr;
}
FF_DEBUG("Falling back to reading SMBIOS directly");
// Implemented in physicalmemory_shared.c. On Linux it is used as a fallback when udev data
// is unavailable; on other SMBIOS capable platforms it backs ffDetectPhysicalMemory directly.
const char* ffDetectPhysicalMemoryFromSmbios(FFPhysicalMemoryOptions * options, FFlist * result);
return ffDetectPhysicalMemoryFromSmbios(options, result);
}
@@ -0,0 +1,252 @@
#include "physicalmemory.h"
#include "common/debug.h"
#include "common/endian.h"
#include "common/smbios.h"
// 7.18
typedef struct [[gnu::packed]] FFSmbiosMemoryDevice {
FFSmbiosHeader Header;
// 2.1+
uint16_t PhysicalMemoryArrayHandle; // varies
uint16_t MemoryErrorInformationHandle; // varies
uint16_t TotalWidth; // varies
uint16_t DataWidth; // varies
uint16_t Size; // varies
uint8_t FormFactor; // enum
uint8_t DeviceSet; // varies
uint8_t DeviceLocator; // string
uint8_t BankLocator; // string
uint8_t MemoryType; // enum
uint16_t TypeDetail; // bit field
// 2.3+
uint16_t Speed; // varies
uint8_t Manufacturer; // string
uint8_t SerialNumber; // string
uint8_t AssetTag; // string
uint8_t PartNumber; // string
// 2.6+
uint8_t Attributes; // varies
// 2.7+
uint32_t ExtendedSize; // varies
uint16_t ConfiguredMemorySpeed; // varies
// 2.8+
uint16_t MinimumVoltage; // varies
uint16_t MaximumVoltage; // varies
uint16_t ConfiguredVoltage; // varies
// 3.2+
uint8_t MemoryTechnology; // varies
uint16_t MemoryOperatingMode; // bit field
uint8_t FirmwareVersion; // string
uint16_t ModuleManufacturerID; // varies
uint16_t ModuleProductID; // varies
uint16_t MemorySubsystemControllerManufacturerID; // vaies
uint16_t MemorySubsystemControllerProductID; // varies
uint64_t NonVolatileSize; // varies
uint64_t VolatileSize; // varies
uint64_t CacheSize; // varies
uint64_t LogicalSize; // varies
// 3.3+
uint32_t ExtendedSpeed; // varies
uint32_t ExtendedConfiguredSpeed; // varies
// 3.7+
uint16_t Pmic0ManufacturerID; // varies
uint16_t Pmic0RevisionNumber; // varies
uint16_t RcdManufacturerID; // varies
uint16_t RcdRevisionNumber; // varies
} FFSmbiosMemoryDevice;
static_assert(offsetof(FFSmbiosMemoryDevice, RcdRevisionNumber) == 0x62,
"FFSmbiosMemoryDevice: Wrong struct alignment");
const char* ffDetectPhysicalMemoryFromSmbios(FFPhysicalMemoryOptions* options, FFlist* result) {
const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable();
if (!smbiosTable) {
FF_DEBUG("Failed to get SMBIOS data");
return "Failed to get SMBIOS data";
}
const FFSmbiosMemoryDevice* data = (const FFSmbiosMemoryDevice*) (*smbiosTable)[FF_SMBIOS_TYPE_MEMORY_DEVICE];
if (!data) {
FF_DEBUG("Memory device is not found in SMBIOS data");
return "Memory device is not found in SMBIOS data";
}
const FFSmbiosMemoryDevice* endOfTable = (const FFSmbiosMemoryDevice*) (*smbiosTable)[FF_SMBIOS_TYPE_END_OF_TABLE];
for (; data != endOfTable; data = (const FFSmbiosMemoryDevice*) ffSmbiosNextEntry(&data->Header)) {
if (data->Header.Type != FF_SMBIOS_TYPE_MEMORY_DEVICE) {
continue;
}
const char* strings = (const char*) data + data->Header.Length;
uint16_t size = FF_READ_LE(data->Size);
bool installed = size != 0;
if (!installed && !options->showEmptySlots) {
continue;
}
FFPhysicalMemoryResult* device = FF_LIST_ADD(FFPhysicalMemoryResult, *result);
*device = (FFPhysicalMemoryResult) {
.installed = installed,
.type = ffStrbufCreate(),
.formFactor = ffStrbufCreate(),
.locator = ffStrbufCreate(),
.partNumber = ffStrbufCreate(),
.vendor = ffStrbufCreate(),
.serial = ffStrbufCreate(),
};
uint16_t totalWidth = FF_READ_LE(data->TotalWidth);
uint16_t dataWidth = FF_READ_LE(data->DataWidth);
if (installed && totalWidth != 0xFFFF && dataWidth != 0xFFFF) {
device->ecc = totalWidth > dataWidth;
}
if (installed && size != 0xFFFF) {
if (size == 0x7FFF) {
device->size = (FF_READ_LE(data->ExtendedSize) & ~(1ULL << 31)) * 1024ULL * 1024ULL;
} else if (size & (1 << 15)) {
// in kB
device->size = (size & ~(1ULL << 15)) * 1024ULL;
} else {
// in MB
device->size = size * 1024ULL * 1024ULL;
}
}
// https://github.com/fastfetch-cli/fastfetch/issues/1051#issuecomment-2206687345
const char* lbank = ffSmbiosLocateString(strings, data->BankLocator);
const char* ldevice = ffSmbiosLocateString(strings, data->DeviceLocator);
if (lbank) {
ffStrbufSetS(&device->locator, lbank);
}
if (lbank && ldevice) {
ffStrbufAppendC(&device->locator, '/');
}
if (ldevice) {
ffStrbufAppendS(&device->locator, ldevice);
}
const char* formFactorNames[] = {
nullptr, // 0x00 (Placeholder for indexing)
"Other", // 0x01
"Unknown", // 0x02
"SIMM", // 0x03
"SIP", // 0x04
"Chip", // 0x05
"DIP", // 0x06
"ZIP", // 0x07
"Proprietary Card", // 0x08
"DIMM", // 0x09
"TSOP", // 0x0A
"Row of chips", // 0x0B
"RIMM", // 0x0C
"SODIMM", // 0x0D
"SRIMM", // 0x0E
"FBDIMM", // 0x0F
"Die", // 0x10
"CAMM", // 0x11
"CUDIMM", // 0x12
"CSODIMM", // 0x13
};
if (data->FormFactor > 0 && data->FormFactor < ARRAY_SIZE(formFactorNames)) {
ffStrbufSetS(&device->formFactor, formFactorNames[data->FormFactor]);
} else {
ffStrbufSetF(&device->formFactor, "Unknown (%d)", (int) data->FormFactor);
}
const char* memoryTypeNames[] = {
nullptr, // 0x00 (Placeholder for indexing)
"Other", // 0x01
"Unknown", // 0x02
"DRAM", // 0x03
"EDRAM", // 0x04
"VRAM", // 0x05
"SRAM", // 0x06
"RAM", // 0x07
"ROM", // 0x08
"FLASH", // 0x09
"EEPROM", // 0x0A
"FEPROM", // 0x0B
"EPROM", // 0x0C
"CDRAM", // 0x0D
"3DRAM", // 0x0E
"SDRAM", // 0x0F
"SGRAM", // 0x10
"RDRAM", // 0x11
"DDR", // 0x12
"DDR2", // 0x13
"DDR2 FB-DIMM", // 0x14
"Reserved", // 0x15
"Reserved", // 0x16
"Reserved", // 0x17
"DDR3", // 0x18
"FBD2", // 0x19
"DDR4", // 0x1A
"LPDDR", // 0x1B
"LPDDR2", // 0x1C
"LPDDR3", // 0x1D
"LPDDR4", // 0x1E
"Logical non-volatile device", // 0x1F
"HBM", // 0x20
"HBM2", // 0x21
"DDR5", // 0x22
"LPDDR5", // 0x23
"HBM3", // 0x24
"MRDIMM", // 0x25
};
if (!installed) {
ffStrbufSetStatic(&device->type, "Empty");
} else if (data->MemoryType > 0 && data->MemoryType < ARRAY_SIZE(memoryTypeNames)) {
ffStrbufSetStatic(&device->type, memoryTypeNames[data->MemoryType]);
} else {
ffStrbufSetF(&device->type, "Unknown (%d)", (int) data->MemoryType);
}
if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, Speed)) // 2.3+
{
uint16_t speed = FF_READ_LE(data->Speed);
if (speed) {
device->maxSpeed = speed == 0xFFFF ? FF_READ_LE(data->ExtendedSpeed) : speed;
}
ffStrbufSetStatic(&device->vendor, ffSmbiosLocateString(strings, data->Manufacturer));
ffCleanUpSmbiosValue(&device->vendor);
FFPhysicalMemoryUpdateVendorString(device);
ffStrbufSetStatic(&device->serial, ffSmbiosLocateString(strings, data->SerialNumber));
ffCleanUpSmbiosValue(&device->serial);
ffStrbufSetStatic(&device->partNumber, ffSmbiosLocateString(strings, data->PartNumber));
ffCleanUpSmbiosValue(&device->partNumber);
}
if (installed && data->Header.Length > offsetof(FFSmbiosMemoryDevice, ConfiguredMemorySpeed)) // 2.7+
{
uint16_t configuredSpeed = FF_READ_LE(data->ConfiguredMemorySpeed);
if (configuredSpeed) {
device->runningSpeed = configuredSpeed == 0xFFFF
? FF_READ_LE(data->ExtendedConfiguredSpeed)
: configuredSpeed;
}
}
}
return nullptr;
}
// On Linux ffDetectPhysicalMemory is provided by physicalmemory_linux.c, which prefers udev
// data and only falls back to the SMBIOS parser above. Every other platform uses it directly.
#ifndef __linux__
const char* ffDetectPhysicalMemory(FFPhysicalMemoryOptions* options, FFlist* result) {
return ffDetectPhysicalMemoryFromSmbios(options, result);
}
#endif
+1 -1
View File
@@ -414,7 +414,7 @@ bool fftsGetShellVersion(FFstrbuf* exe, const char* exeName, FFstrbuf* version)
if (ffStrEqualsIgnCase(exeName, "fish")) {
return getShellVersionFish(exe, version);
}
if (ffStrEqualsIgnCase(exeName, "pwsh")) {
if (ffStrEqualsIgnCase(exeName, "pwsh") || ffStrEqualsIgnCase(exeName, "pwsh-preview")) {
return getShellVersionPwsh(exe, version);
}
if (ffStrEqualsIgnCase(exeName, "csh") || ffStrEqualsIgnCase(exeName, "tcsh")) {
@@ -63,6 +63,8 @@ static uint32_t getShellInfo(FFShellResult* result, uint32_t pid) {
static void setShellInfoDetails(FFShellResult* result) {
if (ffStrbufIgnCaseEqualS(&result->prettyName, "pwsh")) {
ffStrbufSetS(&result->prettyName, "PowerShell");
} else if (ffStrbufIgnCaseEqualS(&result->prettyName, "pwsh-preview")) {
ffStrbufSetS(&result->prettyName, "PowerShell Preview");
} else if (ffStrbufIgnCaseEqualS(&result->prettyName, "powershell")) {
ffStrbufSetS(&result->prettyName, "Windows PowerShell");
} else if (ffStrbufIgnCaseEqualS(&result->prettyName, "powershell_ise")) {
-12
View File
@@ -133,18 +133,6 @@ static const FFlogo T[] = {
.colorTitle = FF_COLOR_FG_256 "32",
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_TURKISH
// Turkish
{
.names = { "Turkish", },
.lines = FASTFETCH_DATATEXT_LOGO_TURKISH,
.colors = {
FF_COLOR_FG_DEFAULT,
},
.colorKeys = FF_COLOR_FG_BLUE,
.colorTitle = FF_COLOR_FG_RED,
},
#endif
#ifdef FASTFETCH_DATATEXT_LOGO_TUXEDO_OS
// TuxedoOS
{
-20
View File
@@ -1,20 +0,0 @@
@@@@@@@@@
@@@@@@ @@
@@@@@
@@@@@
@@@@@@
@@@@@ @@@@@
@@@@@@ @@@@@@@@@@@@@
@@@@@@@ @@ @@@@@@ @
@@@@@@@@@@@ @@@@@
@@@@
@@@@
@@@@@@@@@@ @@@@@
@@@@@@@ @@ @@@@@@ @
@@@@@@ @@@@@@@@@@@@@
@@@@@ @@@@@@@
@@@@@@
@@@@@
@@@@@
@@@@@@ @@
@@@@@@@@@@