summaryrefslogtreecommitdiffstats
path: root/src/detection/host
diff options
context:
space:
mode:
authorsumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
committersumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
commit76424950e373d3b04ac3dd13019151bfba3e8423 (patch)
tree4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/detection/host
Add the files
Diffstat (limited to 'src/detection/host')
-rw-r--r--src/detection/host/host.h20
-rw-r--r--src/detection/host/host_android.c27
-rw-r--r--src/detection/host/host_apple.c68
-rw-r--r--src/detection/host/host_bsd.c26
-rw-r--r--src/detection/host/host_linux.c117
-rw-r--r--src/detection/host/host_mac.c444
-rw-r--r--src/detection/host/host_nbsd.c25
-rw-r--r--src/detection/host/host_nosupport.c5
-rw-r--r--src/detection/host/host_obsd.c22
-rw-r--r--src/detection/host/host_windows.c74
10 files changed, 828 insertions, 0 deletions
diff --git a/src/detection/host/host.h b/src/detection/host/host.h
new file mode 100644
index 0000000..a6fe3fb
--- /dev/null
+++ b/src/detection/host/host.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "fastfetch.h"
+#include "modules/host/option.h"
+
+typedef struct FFHostResult {
+ FFstrbuf family;
+ FFstrbuf name;
+ FFstrbuf version;
+ FFstrbuf sku;
+ FFstrbuf serial;
+ FFstrbuf uuid;
+ FFstrbuf vendor;
+} FFHostResult;
+
+const char* ffHostGetMacProductNameWithHwModel(const FFstrbuf* hwModel);
+#if __x86_64__
+bool ffHostDetectMac(FFHostResult* host);
+#endif
+const char* ffDetectHost(FFHostResult* host);
diff --git a/src/detection/host/host_android.c b/src/detection/host/host_android.c
new file mode 100644
index 0000000..1157a92
--- /dev/null
+++ b/src/detection/host/host_android.c
@@ -0,0 +1,27 @@
+#include "host.h"
+#include "common/settings.h"
+
+#include <ctype.h>
+
+const char* ffDetectHost(FFHostResult* host) {
+ // http://newandroidbook.com/ddb/
+ ffSettingsGetAndroidProperty("ro.product.device", &host->family);
+
+ ffSettingsGetAndroidProperty("ro.product.marketname", &host->name) || ffSettingsGetAndroidProperty("ro.vendor.product.display", &host->name) || ffSettingsGetAndroidProperty("ro.vivo.market.name", &host->name) || ffSettingsGetAndroidProperty("ro.product.oppo_model", &host->name) || ffSettingsGetAndroidProperty("ro.oppo.market.name", &host->name) || ffSettingsGetAndroidProperty("ro.vendor.oplus.market.enname", &host->name) || ffSettingsGetAndroidProperty("ro.config.devicename", &host->name) || ffSettingsGetAndroidProperty("ro.config.marketing_name", &host->name) || ffSettingsGetAndroidProperty("ro.product.vendor.model", &host->name) || ffSettingsGetAndroidProperty("ro.product.brand", &host->name);
+
+ if (ffSettingsGetAndroidProperty("ro.product.model", &host->version)) {
+ if (ffStrbufStartsWithIgnCase(&host->version, &host->name)) {
+ ffStrbufSubstrAfter(&host->version, host->name.length);
+ ffStrbufTrimLeft(&host->version, ' ');
+ }
+ }
+
+ ffSettingsGetAndroidProperty("ro.product.manufacturer", &host->vendor);
+
+ if (host->vendor.length && !ffStrbufStartsWithIgnCase(&host->name, &host->vendor)) {
+ ffStrbufPrependS(&host->name, " ");
+ ffStrbufPrepend(&host->name, &host->vendor);
+ }
+
+ return NULL;
+}
diff --git a/src/detection/host/host_apple.c b/src/detection/host/host_apple.c
new file mode 100644
index 0000000..a177bf4
--- /dev/null
+++ b/src/detection/host/host_apple.c
@@ -0,0 +1,68 @@
+#include "host.h"
+#include "common/sysctl.h"
+#include "common/apple/cf_helpers.h"
+
+#include <IOKit/IOKitLib.h>
+
+const char* getProductNameWithIokit(FFstrbuf* result) {
+ FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t registryEntry = IORegistryEntryFromPath(MACH_PORT_NULL, "IODeviceTree:/product");
+ if (!registryEntry) {
+ return "IOServiceGetMatchingService() failed";
+ }
+
+ FF_CFTYPE_AUTO_RELEASE CFStringRef productName = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("product-name"), kCFAllocatorDefault, kNilOptions);
+ if (!productName) {
+ return "IORegistryEntryCreateCFProperty() failed";
+ }
+
+ return ffCfStrGetString(productName, result);
+}
+
+const char* getOthersByIokit(FFHostResult* host) {
+ FF_IOOBJECT_AUTO_RELEASE io_registry_entry_t registryEntry = IOServiceGetMatchingService(MACH_PORT_NULL, IOServiceMatching("IOPlatformExpertDevice"));
+ if (!registryEntry) {
+ return "IOServiceGetMatchingService() failed";
+ }
+
+ FF_CFTYPE_AUTO_RELEASE CFStringRef serialNumber = IORegistryEntryCreateCFProperty(registryEntry, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, kNilOptions);
+ if (serialNumber) {
+ ffCfStrGetString(serialNumber, &host->serial);
+ }
+
+ FF_CFTYPE_AUTO_RELEASE CFStringRef uuid = IORegistryEntryCreateCFProperty(registryEntry, CFSTR(kIOPlatformUUIDKey), kCFAllocatorDefault, kNilOptions);
+ if (uuid) {
+ ffCfStrGetString(uuid, &host->uuid);
+ }
+
+ FF_CFTYPE_AUTO_RELEASE CFStringRef manufacturer = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("manufacturer"), kCFAllocatorDefault, kNilOptions);
+ if (manufacturer) {
+ ffCfStrGetString(manufacturer, &host->vendor);
+ }
+
+ FF_CFTYPE_AUTO_RELEASE CFStringRef version = IORegistryEntryCreateCFProperty(registryEntry, CFSTR("version"), kCFAllocatorDefault, kNilOptions);
+ if (version) {
+ ffCfStrGetString(version, &host->version);
+ }
+
+ return NULL;
+}
+
+const char* ffDetectHost(FFHostResult* host) {
+ const char* error = ffSysctlGetString("hw.product", &host->family);
+ if (error) {
+ error = ffSysctlGetString("hw.model", &host->family);
+ }
+ if (error) {
+ return error;
+ }
+
+ ffStrbufSetStatic(&host->name, ffHostGetMacProductNameWithHwModel(&host->family));
+ if (host->name.length == 0) {
+ getProductNameWithIokit(&host->name);
+ }
+ if (host->name.length == 0) {
+ ffStrbufSet(&host->name, &host->family);
+ }
+ getOthersByIokit(host);
+ return NULL;
+}
diff --git a/src/detection/host/host_bsd.c b/src/detection/host/host_bsd.c
new file mode 100644
index 0000000..3eb547d
--- /dev/null
+++ b/src/detection/host/host_bsd.c
@@ -0,0 +1,26 @@
+#include "host.h"
+#include "common/settings.h"
+#include "common/smbios.h"
+
+const char* ffDetectHost(FFHostResult* host) {
+ ffSettingsGetFreeBSDKenv("smbios.system.product", &host->name);
+ ffCleanUpSmbiosValue(&host->name);
+ ffSettingsGetFreeBSDKenv("smbios.system.family", &host->family);
+ ffCleanUpSmbiosValue(&host->family);
+ ffSettingsGetFreeBSDKenv("smbios.system.version", &host->version);
+ ffCleanUpSmbiosValue(&host->version);
+ ffSettingsGetFreeBSDKenv("smbios.system.sku", &host->sku);
+ ffCleanUpSmbiosValue(&host->sku);
+ ffSettingsGetFreeBSDKenv("smbios.system.serial", &host->serial);
+ ffCleanUpSmbiosValue(&host->serial);
+ ffSettingsGetFreeBSDKenv("smbios.system.uuid", &host->uuid);
+ ffCleanUpSmbiosValue(&host->uuid);
+ ffSettingsGetFreeBSDKenv("smbios.system.maker", &host->vendor);
+ ffCleanUpSmbiosValue(&host->vendor);
+
+#ifdef __x86_64__
+ ffHostDetectMac(host);
+#endif
+
+ return NULL;
+}
diff --git a/src/detection/host/host_linux.c b/src/detection/host/host_linux.c
new file mode 100644
index 0000000..81418a8
--- /dev/null
+++ b/src/detection/host/host_linux.c
@@ -0,0 +1,117 @@
+#include "host.h"
+#include "common/io.h"
+#include "common/processing.h"
+#include "common/smbios.h"
+
+#include <stdlib.h>
+
+static bool getHostProductName(FFstrbuf* name) {
+ if (ffReadFileBuffer("/sys/firmware/devicetree/base/model", name) ||
+ ffReadFileBuffer("/sys/firmware/devicetree/base/banner-name", name)) {
+ ffStrbufTrimRight(name, '\0');
+ return true;
+ }
+
+ if (ffReadFileBuffer("/tmp/sysinfo/model", name)) {
+ ffStrbufTrimRightSpace(name);
+ ffStrbufTrimRight(name, '\0');
+ if (ffIsSmbiosValueSet(name)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool getHostSerialNumber(FFstrbuf* serial) {
+ if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/serial", serial) ||
+ ffReadFileBuffer("/sys/firmware/devicetree/base/serial-number", serial)) {
+ ffStrbufTrimRight(serial, '\0');
+ return true;
+ }
+ return false;
+}
+
+static bool getHostProductFamily(FFstrbuf* family) {
+ if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/family", family) ||
+ ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/product", family)) {
+ ffStrbufTrimRight(family, '\0');
+ return true;
+ }
+ return false;
+}
+
+static bool getHostVendor(FFstrbuf* vendor) {
+ if (ffReadFileBuffer("/sys/firmware/devicetree/base/smbios/smbios/system/manufacturer", vendor)) {
+ ffStrbufTrimRight(vendor, '\0');
+ return true;
+ }
+ return false;
+}
+
+const char* ffDetectHost(FFHostResult* host) {
+ // This is a hack for Asahi Linux, whose product_family is empty
+ bool productName = ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_name", "/sys/class/dmi/id/product_name", &host->name);
+ bool productFamily = ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_family", "/sys/class/dmi/id/product_family", &host->family);
+ if (productName || productFamily) {
+ ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_version", "/sys/class/dmi/id/product_version", &host->version);
+ ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_sku", "/sys/class/dmi/id/product_sku", &host->sku);
+ ffGetSmbiosValue("/sys/devices/virtual/dmi/id/product_serial", "/sys/class/dmi/id/product_serial", &host->serial);
+ ffGetSmbiosValue("/sys/devices/virtual/dmi/id/sys_vendor", "/sys/class/dmi/id/sys_vendor", &host->vendor);
+
+#if __x86_64__
+ ffHostDetectMac(host);
+#endif
+
+ // KVM/Qemu virtual machine
+ if (ffStrbufStartsWithS(&host->name, "Standard PC")) {
+ ffStrbufPrependS(&host->name, "KVM/QEMU ");
+ }
+#if __aarch64__
+ else if (host->family.length == 0 && ffStrbufEqualS(&host->vendor, "Apple Inc.") && ffStrbufStartsWithS(&host->name, "Mac")) {
+ // Hack for Asahi Linux
+ ffStrbufDestroy(&host->family);
+ ffStrbufInitMove(&host->family, &host->name);
+ getHostProductName(&host->name);
+ getHostSerialNumber(&host->serial);
+ }
+#endif
+ } else {
+ getHostProductFamily(&host->family);
+ getHostProductName(&host->name);
+ getHostSerialNumber(&host->serial);
+ getHostVendor(&host->vendor);
+ }
+
+ if (host->family.length == 0 && host->name.length == 0) {
+ const char* wslDistroName = getenv("WSL_DISTRO_NAME");
+ // On WSL, the real host can't be detected. Instead use WSL as host.
+ if (wslDistroName != NULL || getenv("WSL_DISTRO") != NULL || getenv("WSL_INTEROP") != NULL) {
+ ffStrbufSetStatic(&host->name, "Windows Subsystem for Linux");
+ if (wslDistroName) {
+ ffStrbufAppendF(&host->name, " - %s", wslDistroName);
+ }
+ ffStrbufSetStatic(&host->family, "WSL");
+ ffStrbufSetStatic(&host->vendor, "Microsoft Corporation");
+
+ if (instance.config.general.detectVersion) {
+ ffProcessAppendStdOut(&host->version, (char* const[]) {
+ "wslinfo",
+ "--wsl-version",
+ "-n",
+ NULL,
+ }); // supported in 2.2.3 and later
+ }
+ } else if (ffStrbufStartsWithS(&instance.state.platform.sysinfo.version, "FreeBSD ")) {
+ ffStrbufSetStatic(&host->name, "Linux Binary Compatibility on FreeBSD");
+ ffStrbufSetStatic(&host->family, "FreeBSD");
+ ffStrbufSetStatic(&host->vendor, "FreeBSD Foundation");
+ if (instance.config.general.detectVersion) {
+ ffStrbufSetS(&host->version, instance.state.platform.sysinfo.version.chars + strlen("FreeBSD "));
+ ffStrbufSubstrBeforeFirstC(&host->version, ' ');
+ }
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/detection/host/host_mac.c b/src/detection/host/host_mac.c
new file mode 100644
index 0000000..d680332
--- /dev/null
+++ b/src/detection/host/host_mac.c
@@ -0,0 +1,444 @@
+#include "host.h"
+#include "common/strutil.h"
+
+const char* ffHostGetMacProductNameWithHwModel(const FFstrbuf* hwModel) {
+ // Macbook Pro: https://support.apple.com/en-us/HT201300
+ // Macbook Air: https://support.apple.com/en-us/HT201862
+ // Mac mini: https://support.apple.com/en-us/HT201894
+ // iMac: https://support.apple.com/en-us/HT201634
+ // Mac Pro: https://support.apple.com/en-us/HT202888
+ // Mac Studio: https://support.apple.com/en-us/HT213073
+
+ if (ffStrbufStartsWithS(hwModel, "MacBookPro")) {
+ const char* version = hwModel->chars + strlen("MacBookPro");
+ if (ffStrEquals(version, "18,3") ||
+ ffStrEquals(version, "18,4")) {
+ return "MacBook Pro (14-inch, 2021)";
+ }
+ if (ffStrEquals(version, "18,1") ||
+ ffStrEquals(version, "18,2")) {
+ return "MacBook Pro (16-inch, 2021)";
+ }
+ if (ffStrEquals(version, "17,1")) {
+ return "MacBook Pro (13-inch, M1, 2020)";
+ }
+ if (ffStrEquals(version, "16,3")) {
+ return "MacBook Pro (13-inch, 2020, Two Thunderbolt 3 ports)";
+ }
+ if (ffStrEquals(version, "16,2")) {
+ return "MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)";
+ }
+ if (ffStrEquals(version, "16,4") ||
+ ffStrEquals(version, "16,1")) {
+ return "MacBook Pro (16-inch, 2019)";
+ }
+ if (ffStrEquals(version, "15,4")) {
+ return "MacBook Pro (13-inch, 2019, Two Thunderbolt 3 ports)";
+ }
+ if (ffStrEquals(version, "15,3")) {
+ return "MacBook Pro (15-inch, 2019)";
+ }
+ if (ffStrEquals(version, "15,2")) {
+ return "MacBook Pro (13-inch, 2018/2019, Four Thunderbolt 3 ports)";
+ }
+ if (ffStrEquals(version, "15,1")) {
+ return "MacBook Pro (15-inch, 2018/2019)";
+ }
+ if (ffStrEquals(version, "14,3")) {
+ return "MacBook Pro (15-inch, 2017)";
+ }
+ if (ffStrEquals(version, "14,2")) {
+ return "MacBook Pro (13-inch, 2017, Four Thunderbolt 3 ports)";
+ }
+ if (ffStrEquals(version, "14,1")) {
+ return "MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)";
+ }
+ if (ffStrEquals(version, "13,3")) {
+ return "MacBook Pro (15-inch, 2016)";
+ }
+ if (ffStrEquals(version, "13,2")) {
+ return "MacBook Pro (13-inch, 2016, Four Thunderbolt 3 ports)";
+ }
+ if (ffStrEquals(version, "13,1")) {
+ return "MacBook Pro (13-inch, 2016, Two Thunderbolt 3 ports)";
+ }
+ if (ffStrEquals(version, "12,1")) {
+ return "MacBook Pro (Retina, 13-inch, Early 2015)";
+ }
+ if (ffStrEquals(version, "11,4") ||
+ ffStrEquals(version, "11,5")) {
+ return "MacBook Pro (Retina, 15-inch, Mid 2015)";
+ }
+ if (ffStrEquals(version, "11,2") ||
+ ffStrEquals(version, "11,3")) {
+ return "MacBook Pro (Retina, 15-inch, Late 2013/Mid 2014)";
+ }
+ if (ffStrEquals(version, "11,1")) {
+ return "MacBook Pro (Retina, 13-inch, Late 2013/Mid 2014)";
+ }
+ if (ffStrEquals(version, "10,2")) {
+ return "MacBook Pro (Retina, 13-inch, Late 2012/Early 2013)";
+ }
+ if (ffStrEquals(version, "10,1")) {
+ return "MacBook Pro (Retina, 15-inch, Mid 2012/Early 2013)";
+ }
+ if (ffStrEquals(version, "9,2")) {
+ return "MacBook Pro (13-inch, Mid 2012)";
+ }
+ if (ffStrEquals(version, "9,1")) {
+ return "MacBook Pro (15-inch, Mid 2012)";
+ }
+ if (ffStrEquals(version, "8,3")) {
+ return "MacBook Pro (17-inch, 2011)";
+ }
+ if (ffStrEquals(version, "8,2")) {
+ return "MacBook Pro (15-inch, 2011)";
+ }
+ if (ffStrEquals(version, "8,1")) {
+ return "MacBook Pro (13-inch, 2011)";
+ }
+ if (ffStrEquals(version, "7,1")) {
+ return "MacBook Pro (13-inch, Mid 2010)";
+ }
+ if (ffStrEquals(version, "6,2")) {
+ return "MacBook Pro (15-inch, Mid 2010)";
+ }
+ if (ffStrEquals(version, "6,1")) {
+ return "MacBook Pro (17-inch, Mid 2010)";
+ }
+ if (ffStrEquals(version, "5,5")) {
+ return "MacBook Pro (13-inch, Mid 2009)";
+ }
+ if (ffStrEquals(version, "5,3")) {
+ return "MacBook Pro (15-inch, Mid 2009)";
+ }
+ if (ffStrEquals(version, "5,2")) {
+ return "MacBook Pro (17-inch, Mid/Early 2009)";
+ }
+ if (ffStrEquals(version, "5,1")) {
+ return "MacBook Pro (15-inch, Late 2008)";
+ }
+ if (ffStrEquals(version, "4,1")) {
+ return "MacBook Pro (17/15-inch, Early 2008)";
+ }
+ } else if (ffStrbufStartsWithS(hwModel, "MacBookAir")) {
+ const char* version = hwModel->chars + strlen("MacBookAir");
+ if (ffStrEquals(version, "10,1")) {
+ return "MacBook Air (M1, 2020)";
+ }
+ if (ffStrEquals(version, "9,1")) {
+ return "MacBook Air (Retina, 13-inch, 2020)";
+ }
+ if (ffStrEquals(version, "8,2")) {
+ return "MacBook Air (Retina, 13-inch, 2019)";
+ }
+ if (ffStrEquals(version, "8,1")) {
+ return "MacBook Air (Retina, 13-inch, 2018)";
+ }
+ if (ffStrEquals(version, "7,2")) {
+ return "MacBook Air (13-inch, Early 2015/2017)";
+ }
+ if (ffStrEquals(version, "7,1")) {
+ return "MacBook Air (11-inch, Early 2015)";
+ }
+ if (ffStrEquals(version, "6,2")) {
+ return "MacBook Air (13-inch, Mid 2013/Early 2014)";
+ }
+ if (ffStrEquals(version, "6,1")) {
+ return "MacBook Air (11-inch, Mid 2013/Early 2014)";
+ }
+ if (ffStrEquals(version, "5,2")) {
+ return "MacBook Air (13-inch, Mid 2012)";
+ }
+ if (ffStrEquals(version, "5,1")) {
+ return "MacBook Air (11-inch, Mid 2012)";
+ }
+ if (ffStrEquals(version, "4,2")) {
+ return "MacBook Air (13-inch, Mid 2011)";
+ }
+ if (ffStrEquals(version, "4,1")) {
+ return "MacBook Air (11-inch, Mid 2011)";
+ }
+ if (ffStrEquals(version, "3,2")) {
+ return "MacBook Air (13-inch, Late 2010)";
+ }
+ if (ffStrEquals(version, "3,1")) {
+ return "MacBook Air (11-inch, Late 2010)";
+ }
+ if (ffStrEquals(version, "2,1")) {
+ return "MacBook Air (Mid 2009)";
+ }
+ } else if (ffStrbufStartsWithS(hwModel, "Macmini")) {
+ const char* version = hwModel->chars + strlen("Macmini");
+ if (ffStrEquals(version, "9,1")) {
+ return "Mac mini (M1, 2020)";
+ }
+ if (ffStrEquals(version, "8,1")) {
+ return "Mac mini (2018)";
+ }
+ if (ffStrEquals(version, "7,1")) {
+ return "Mac mini (Mid 2014)";
+ }
+ if (ffStrEquals(version, "6,1") ||
+ ffStrEquals(version, "6,2")) {
+ return "Mac mini (Late 2012)";
+ }
+ if (ffStrEquals(version, "5,1") ||
+ ffStrEquals(version, "5,2")) {
+ return "Mac mini (Mid 2011)";
+ }
+ if (ffStrEquals(version, "4,1")) {
+ return "Mac mini (Mid 2010)";
+ }
+ if (ffStrEquals(version, "3,1")) {
+ return "Mac mini (Early/Late 2009)";
+ }
+ } else if (ffStrbufStartsWithS(hwModel, "MacBook")) {
+ const char* version = hwModel->chars + strlen("MacBook");
+ if (ffStrEquals(version, "10,1")) {
+ return "MacBook (Retina, 12-inch, 2017)";
+ }
+ if (ffStrEquals(version, "9,1")) {
+ return "MacBook (Retina, 12-inch, Early 2016)";
+ }
+ if (ffStrEquals(version, "8,1")) {
+ return "MacBook (Retina, 12-inch, Early 2015)";
+ }
+ if (ffStrEquals(version, "7,1")) {
+ return "MacBook (13-inch, Mid 2010)";
+ }
+ if (ffStrEquals(version, "6,1")) {
+ return "MacBook (13-inch, Late 2009)";
+ }
+ if (ffStrEquals(version, "5,2")) {
+ return "MacBook (13-inch, Early/Mid 2009)";
+ }
+ } else if (ffStrbufStartsWithS(hwModel, "MacPro")) {
+ const char* version = hwModel->chars + strlen("MacPro");
+ if (ffStrEquals(version, "7,1")) {
+ return "Mac Pro (2019)";
+ }
+ if (ffStrEquals(version, "6,1")) {
+ return "Mac Pro (Late 2013)";
+ }
+ if (ffStrEquals(version, "5,1")) {
+ return "Mac Pro (Mid 2010 - Mid 2012)";
+ }
+ if (ffStrEquals(version, "4,1")) {
+ return "Mac Pro (Early 2009)";
+ }
+ } else if (ffStrbufStartsWithS(hwModel, "Mac")) {
+ const char* version = hwModel->chars + strlen("Mac");
+ if (ffStrEquals(version, "17,9")) {
+ return "MacBook Pro (14-inch, M5 Pro, 2026)";
+ }
+ if (ffStrEquals(version, "17,8")) {
+ return "MacBook Pro (16-inch, M5 Pro, 2026)";
+ }
+ if (ffStrEquals(version, "17,7")) {
+ return "MacBook Pro (14-inch, M5 Max, 2026)";
+ }
+ if (ffStrEquals(version, "17,6")) {
+ return "MacBook Pro (16-inch, M5 Max, 2026)";
+ }
+ if (ffStrEquals(version, "17,5")) {
+ return "MacBook Neo (13-inch, A18 Pro, 2026)";
+ }
+ if (ffStrEquals(version, "17,4")) {
+ return "MacBook Air (15-inch, M5, 2026)";
+ }
+ if (ffStrEquals(version, "17,3")) {
+ return "MacBook Air (13-inch, M5, 2026)";
+ }
+ if (ffStrEquals(version, "17,2")) {
+ return "MacBook Pro (14-inch, M5, 2025)";
+ }
+ if (ffStrEquals(version, "16,13")) {
+ return "MacBook Air (15-inch, M4, 2025)";
+ }
+ if (ffStrEquals(version, "16,12")) {
+ return "MacBook Air (13-inch, M4, 2025)";
+ }
+ if (ffStrEquals(version, "16,11") ||
+ ffStrEquals(version, "16,10")) {
+ return "Mac Mini (2024)";
+ }
+ if (ffStrEquals(version, "16,9")) {
+ return "Mac Studio (M4 Max, 2025)";
+ }
+ if (ffStrEquals(version, "16,3")) {
+ return "iMac (24-inch, 2024, Four Thunderbolt / USB 4 ports)";
+ }
+ if (ffStrEquals(version, "16,2")) {
+ return "iMac (24-inch, 2024, Two Thunderbolt / USB 4 ports)";
+ }
+ if (ffStrEquals(version, "16,1")) {
+ return "MacBook Pro (14-inch, 2024, Three Thunderbolt 4 ports)";
+ }
+ if (ffStrEquals(version, "16,6") ||
+ ffStrEquals(version, "16,8")) {
+ return "MacBook Pro (14-inch, 2024, Three Thunderbolt 5 ports)";
+ }
+ if (ffStrEquals(version, "16,7") ||
+ ffStrEquals(version, "16,5")) {
+ return "MacBook Pro (16-inch, 2024, Three Thunderbolt 5 ports)";
+ }
+ if (ffStrEquals(version, "15,14")) {
+ return "Mac Studio (M3 Ultra, 2025)";
+ }
+ if (ffStrEquals(version, "15,13")) {
+ return "MacBook Air (15-inch, M3, 2024)";
+ }
+ if (ffStrEquals(version, "15,12")) {
+ return "MacBook Air (13-inch, M3, 2024)";
+ }
+ if (ffStrEquals(version, "15,3")) {
+ return "MacBook Pro (14-inch, Nov 2023, Two Thunderbolt / USB 4 ports)";
+ }
+ if (ffStrEquals(version, "15,4")) {
+ return "iMac (24-inch, 2023, Two Thunderbolt / USB 4 ports)";
+ }
+ if (ffStrEquals(version, "15,5")) {
+ return "iMac (24-inch, 2023, Two Thunderbolt / USB 4 ports, Two USB 3 ports)";
+ }
+ if (ffStrEquals(version, "15,6") ||
+ ffStrEquals(version, "15,8") ||
+ ffStrEquals(version, "15,10")) {
+ return "MacBook Pro (14-inch, Nov 2023, Three Thunderbolt 4 ports)";
+ }
+ if (ffStrEquals(version, "15,7") ||
+ ffStrEquals(version, "15,9") ||
+ ffStrEquals(version, "15,11")) {
+ return "MacBook Pro (16-inch, Nov 2023, Three Thunderbolt 4 ports)";
+ }
+ if (ffStrEquals(version, "14,15")) {
+ return "MacBook Air (15-inch, M2, 2023)";
+ }
+ if (ffStrEquals(version, "14,14")) {
+ return "Mac Studio (M2 Ultra, 2023, Two Thunderbolt 4 front ports)";
+ }
+ if (ffStrEquals(version, "14,13")) {
+ return "Mac Studio (M2 Max, 2023, Two USB-C front ports)";
+ }
+ if (ffStrEquals(version, "14,8")) {
+ return "Mac Pro (2023)";
+ }
+ if (ffStrEquals(version, "14,6") ||
+ ffStrEquals(version, "14,10")) {
+ return "MacBook Pro (16-inch, 2023)";
+ }
+ if (ffStrEquals(version, "14,5") ||
+ ffStrEquals(version, "14,9")) {
+ return "MacBook Pro (14-inch, 2023)";
+ }
+ if (ffStrEquals(version, "14,3")) {
+ return "Mac mini (M2, 2023, Two Thunderbolt 4 ports)";
+ }
+ if (ffStrEquals(version, "14,12")) {
+ return "Mac mini (M2 Pro, 2023, Four Thunderbolt 4 ports)";
+ }
+ if (ffStrEquals(version, "14,7")) {
+ return "MacBook Pro (13-inch, M2, 2022)";
+ }
+ if (ffStrEquals(version, "14,2")) {
+ return "MacBook Air (M2, 2022)";
+ }
+ if (ffStrEquals(version, "13,1")) {
+ return "Mac Studio (M1 Max, 2022, Two USB-C front ports)";
+ }
+ if (ffStrEquals(version, "13,2")) {
+ return "Mac Studio (M1 Ultra, 2022, Two Thunderbolt 4 front ports)";
+ }
+ } else if (ffStrbufStartsWithS(hwModel, "iMac")) {
+ const char* version = hwModel->chars + strlen("iMac");
+ if (ffStrEquals(version, "21,1")) {
+ return "iMac (24-inch, M1, 2021, Two Thunderbolt / USB 4 ports, Two USB 3 ports)";
+ }
+ if (ffStrEquals(version, "21,2")) {
+ return "iMac (24-inch, M1, 2021, Two Thunderbolt / USB 4 ports)";
+ }
+ if (ffStrEquals(version, "20,1") ||
+ ffStrEquals(version, "20,2")) {
+ return "iMac (Retina 5K, 27-inch, 2020)";
+ }
+ if (ffStrEquals(version, "19,1")) {
+ return "iMac (Retina 5K, 27-inch, 2019)";
+ }
+ if (ffStrEquals(version, "19,2")) {
+ return "iMac (Retina 4K, 21.5-inch, 2019)";
+ }
+ if (ffStrEquals(version, "Pro1,1")) {
+ return "iMac Pro (2017)";
+ }
+ if (ffStrEquals(version, "18,3")) {
+ return "iMac (Retina 5K, 27-inch, 2017)";
+ }
+ if (ffStrEquals(version, "18,2")) {
+ return "iMac (Retina 4K, 21.5-inch, 2017)";
+ }
+ if (ffStrEquals(version, "18,1")) {
+ return "iMac (21.5-inch, 2017)";
+ }
+ if (ffStrEquals(version, "17,1")) {
+ return "iMac (Retina 5K, 27-inch, Late 2015)";
+ }
+ if (ffStrEquals(version, "16,2")) {
+ return "iMac (Retina 4K, 21.5-inch, Late 2015)";
+ }
+ if (ffStrEquals(version, "16,1")) {
+ return "iMac (21.5-inch, Late 2015)";
+ }
+ if (ffStrEquals(version, "15,1")) {
+ return "iMac (Retina 5K, 27-inch, Late 2014 - Mid 2015)";
+ }
+ if (ffStrEquals(version, "14,4")) {
+ return "iMac (21.5-inch, Mid 2014)";
+ }
+ if (ffStrEquals(version, "14,2")) {
+ return "iMac (27-inch, Late 2013)";
+ }
+ if (ffStrEquals(version, "14,1")) {
+ return "iMac (21.5-inch, Late 2013)";
+ }
+ if (ffStrEquals(version, "13,2")) {
+ return "iMac (27-inch, Late 2012)";
+ }
+ if (ffStrEquals(version, "13,1")) {
+ return "iMac (21.5-inch, Late 2012)";
+ }
+ if (ffStrEquals(version, "12,2")) {
+ return "iMac (27-inch, Mid 2011)";
+ }
+ if (ffStrEquals(version, "12,1")) {
+ return "iMac (21.5-inch, Mid 2011)";
+ }
+ if (ffStrEquals(version, "11,3")) {
+ return "iMac (27-inch, Mid 2010)";
+ }
+ if (ffStrEquals(version, "11,2")) {
+ return "iMac (21.5-inch, Mid 2010)";
+ }
+ if (ffStrEquals(version, "10,1")) {
+ return "iMac (27/21.5-inch, Late 2009)";
+ }
+ if (ffStrEquals(version, "9,1")) {
+ return "iMac (24/20-inch, Early 2009)";
+ }
+ }
+ return NULL;
+}
+
+#ifdef __x86_64__
+bool ffHostDetectMac(FFHostResult* host) {
+ if (ffStrbufStartsWithS(&host->family, "Mac") && ffStrbufEqualS(&host->vendor, "Apple Inc.")) {
+ const char* productName = ffHostGetMacProductNameWithHwModel(&host->name);
+ if (productName) {
+ ffStrbufDestroy(&host->family);
+ ffStrbufInitMove(&host->family, &host->name);
+ ffStrbufSetStatic(&host->name, productName);
+ return true;
+ }
+ }
+ return false;
+}
+#endif
diff --git a/src/detection/host/host_nbsd.c b/src/detection/host/host_nbsd.c
new file mode 100644
index 0000000..a5a29d2
--- /dev/null
+++ b/src/detection/host/host_nbsd.c
@@ -0,0 +1,25 @@
+#include "host.h"
+#include "common/sysctl.h"
+#include "common/smbios.h"
+
+const char* ffDetectHost(FFHostResult* host) {
+ const char* error = NULL;
+ if ((error = ffSysctlGetString("machdep.dmi.system-product", &host->name))) {
+ return error;
+ }
+ ffCleanUpSmbiosValue(&host->name);
+ if (ffSysctlGetString("machdep.dmi.system-vendor", &host->vendor) == NULL) {
+ ffCleanUpSmbiosValue(&host->vendor);
+ }
+ if (ffSysctlGetString("machdep.dmi.system-version", &host->version) == NULL) {
+ ffCleanUpSmbiosValue(&host->version);
+ }
+ if (ffSysctlGetString("machdep.dmi.system-serial", &host->serial) == NULL) {
+ ffCleanUpSmbiosValue(&host->serial);
+ }
+ if (ffSysctlGetString("machdep.dmi.system-uuid", &host->uuid) == NULL) {
+ ffCleanUpSmbiosValue(&host->uuid);
+ }
+
+ return NULL;
+}
diff --git a/src/detection/host/host_nosupport.c b/src/detection/host/host_nosupport.c
new file mode 100644
index 0000000..61a7254
--- /dev/null
+++ b/src/detection/host/host_nosupport.c
@@ -0,0 +1,5 @@
+#include "host.h"
+
+const char* ffDetectHost(FF_A_UNUSED FFHostResult* host) {
+ return "Not supported on this platform";
+}
diff --git a/src/detection/host/host_obsd.c b/src/detection/host/host_obsd.c
new file mode 100644
index 0000000..cb16ecf
--- /dev/null
+++ b/src/detection/host/host_obsd.c
@@ -0,0 +1,22 @@
+#include "host.h"
+#include "common/sysctl.h"
+#include "common/smbios.h"
+
+const char* ffDetectHost(FFHostResult* host) {
+ const char* error = NULL;
+ if ((error = ffSysctlGetString(CTL_HW, HW_PRODUCT, &host->name))) {
+ return error;
+ }
+ ffCleanUpSmbiosValue(&host->name);
+ if (ffSysctlGetString(CTL_HW, HW_VENDOR, &host->vendor) == NULL) {
+ ffCleanUpSmbiosValue(&host->vendor);
+ }
+ if (ffSysctlGetString(CTL_HW, HW_VERSION, &host->version) == NULL) {
+ ffCleanUpSmbiosValue(&host->version);
+ }
+ if (ffSysctlGetString(CTL_HW, HW_SERIALNO, &host->serial) == NULL) {
+ ffCleanUpSmbiosValue(&host->serial);
+ }
+
+ return NULL;
+}
diff --git a/src/detection/host/host_windows.c b/src/detection/host/host_windows.c
new file mode 100644
index 0000000..9f25829
--- /dev/null
+++ b/src/detection/host/host_windows.c
@@ -0,0 +1,74 @@
+#include "host.h"
+#include "common/smbios.h"
+
+typedef struct FFSmbiosSystemInfo {
+ FFSmbiosHeader Header;
+
+ uint8_t Manufacturer; // string
+ uint8_t ProductName; // string
+ uint8_t Version; // string
+ uint8_t SerialNumber; // string
+
+ // 2.1+
+ struct {
+ uint32_t TimeLow;
+ uint16_t TimeMid;
+ uint16_t TimeHighAndVersion;
+ uint8_t ClockSeqHiAndReserved;
+ uint8_t ClockSeqLow;
+ uint8_t Node[6];
+ } FF_A_PACKED UUID; // varies
+ uint8_t WakeUpType; // enum
+
+ // 2.4+
+ uint8_t SKUNumber; // string
+ uint8_t Family; // string
+} FF_A_PACKED FFSmbiosSystemInfo;
+
+static_assert(offsetof(FFSmbiosSystemInfo, Family) == 0x1A,
+ "FFSmbiosSystemInfo: Wrong struct alignment");
+
+const char* ffDetectHost(FFHostResult* host) {
+ const FFSmbiosHeaderTable* smbiosTable = ffGetSmbiosHeaderTable();
+ if (!smbiosTable) {
+ return "Failed to get SMBIOS data";
+ }
+
+ const FFSmbiosSystemInfo* data = (const FFSmbiosSystemInfo*) (*smbiosTable)[FF_SMBIOS_TYPE_SYSTEM_INFO];
+ if (!data) {
+ return "System information is not found in SMBIOS data";
+ }
+
+ const char* strings = (const char*) data + data->Header.Length;
+
+ ffStrbufSetStatic(&host->vendor, ffSmbiosLocateString(strings, data->Manufacturer));
+ ffCleanUpSmbiosValue(&host->vendor);
+ ffStrbufSetStatic(&host->name, ffSmbiosLocateString(strings, data->ProductName));
+ ffCleanUpSmbiosValue(&host->name);
+ ffStrbufSetStatic(&host->version, ffSmbiosLocateString(strings, data->Version));
+ ffCleanUpSmbiosValue(&host->version);
+ ffStrbufSetStatic(&host->serial, ffSmbiosLocateString(strings, data->SerialNumber));
+ ffCleanUpSmbiosValue(&host->serial);
+
+ static_assert(offsetof(FFSmbiosSystemInfo, UUID) == 0x08, "FFSmbiosSystemInfo.UUID offset is wrong");
+ if (data->Header.Length > offsetof(FFSmbiosSystemInfo, UUID)) {
+ ffStrbufSetF(&host->uuid, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", data->UUID.TimeLow, data->UUID.TimeMid, data->UUID.TimeHighAndVersion, data->UUID.ClockSeqHiAndReserved, data->UUID.ClockSeqLow, data->UUID.Node[0], data->UUID.Node[1], data->UUID.Node[2], data->UUID.Node[3], data->UUID.Node[4], data->UUID.Node[5]);
+ }
+
+ static_assert(offsetof(FFSmbiosSystemInfo, SKUNumber) == 0x19, "FFSmbiosSystemInfo.SKUNumber offset is wrong");
+ if (data->Header.Length > offsetof(FFSmbiosSystemInfo, SKUNumber)) {
+ ffStrbufSetStatic(&host->sku, ffSmbiosLocateString(strings, data->SKUNumber));
+ ffCleanUpSmbiosValue(&host->sku);
+ }
+
+ if (data->Header.Length > offsetof(FFSmbiosSystemInfo, Family)) {
+ ffStrbufSetStatic(&host->family, ffSmbiosLocateString(strings, data->Family));
+ ffCleanUpSmbiosValue(&host->family);
+ }
+
+#if __x86_64__
+ ffHostDetectMac(host);
+#endif
+
+ return NULL;
+}