netbsd support yay
This commit is contained in:
parent
4e5628f88d
commit
f5c138bc86
2 changed files with 139 additions and 77 deletions
|
@ -90,6 +90,7 @@ base_sources = files('src/core/system_data.cpp', 'src/os/shared.cpp', 'src/confi
|
|||
platform_sources = {
|
||||
'linux' : ['src/os/linux.cpp', 'src/os/linux/pkg_count.cpp'],
|
||||
'freebsd' : ['src/os/bsd.cpp'],
|
||||
'netbsd' : ['src/os/bsd.cpp'],
|
||||
'dragonfly' : ['src/os/bsd.cpp'],
|
||||
'darwin' : ['src/os/macos.cpp', 'src/os/macos/bridge.mm'],
|
||||
'windows' : ['src/os/windows.cpp'],
|
||||
|
|
215
src/os/bsd.cpp
215
src/os/bsd.cpp
|
@ -1,19 +1,22 @@
|
|||
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__NetBSD__)
|
||||
|
||||
// clang-format off
|
||||
#include <dbus/dbus-protocol.h> // DBUS_TYPE_*
|
||||
#include <dbus/dbus-shared.h> // DBUS_BUS_SESSION
|
||||
#include <fstream> // ifstream
|
||||
#include <kenv.h> // kenv
|
||||
#include <sys/socket.h> // ucred, getsockopt, SOL_SOCKET, SO_PEERCRED
|
||||
#include <sys/statvfs.h> // statvfs
|
||||
#include <sys/sysctl.h> // sysctlbyname
|
||||
#include <sys/types.h>
|
||||
#include <sys/ucred.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/utsname.h> // utsname, uname
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
#include <kenv.h> // kenv
|
||||
#include <sys/ucred.h> // xucred
|
||||
#endif
|
||||
|
||||
#include "src/util/defs.hpp"
|
||||
#include "src/util/error.hpp"
|
||||
#include "src/util/helpers.hpp"
|
||||
|
@ -30,6 +33,32 @@ using namespace util::types;
|
|||
using util::error::DracError, util::error::DracErrorCode;
|
||||
|
||||
namespace {
|
||||
fn GetPathByPid(pid_t pid) -> Result<String, DracError> {
|
||||
Array<char, PATH_MAX> exePathBuf;
|
||||
usize size = exePathBuf.size();
|
||||
int mib[4];
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC_ARGS; // Use KERN_PROC_ARGS which includes path
|
||||
mib[2] = pid;
|
||||
mib[3] = KERN_PROC_PATHNAME; // The specific subcommand
|
||||
|
||||
if (sysctl(mib, 4, exePathBuf.data(), &size, nullptr, 0) == -1) {
|
||||
// Fallback for older systems or if KERN_PROC_PATHNAME is restricted/unavailable
|
||||
// Note: KERN_PROC_ARGS might give args too, need parsing.
|
||||
// For simplicity, we'll just report the error for now.
|
||||
return Err(DracError::withErrno(std::format("sysctl KERN_PROC_PATHNAME failed for pid {}", pid)));
|
||||
}
|
||||
|
||||
if (size == 0 || exePathBuf[0] == '\0') {
|
||||
return Err(DracError(DracErrorCode::NotFound, std::format("sysctl KERN_PROC_PATHNAME returned empty path for pid {}", pid)));
|
||||
}
|
||||
|
||||
exePathBuf[std::min(size, exePathBuf.size() - 1)] = '\0';
|
||||
|
||||
return String(exePathBuf.data());
|
||||
}
|
||||
|
||||
fn GetX11WindowManager() -> Result<String, DracError> {
|
||||
using namespace xcb;
|
||||
|
||||
|
@ -119,64 +148,64 @@ namespace {
|
|||
if (fileDescriptor < 0)
|
||||
return Err(DracError(DracErrorCode::ApiUnavailable, "Failed to get Wayland file descriptor"));
|
||||
|
||||
xucred cred;
|
||||
pid_t peer_pid = -1; // Initialize PID
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
struct xucred cred; // Use xucred on FreeBSD/DragonFly
|
||||
socklen_t len = sizeof(cred);
|
||||
if (getsockopt(fileDescriptor, SOL_SOCKET, LOCAL_PEERCRED, &cred, &len) == -1) {
|
||||
return Err(DracError::withErrno("Failed to get socket credentials (LOCAL_PEERCRED)"));
|
||||
}
|
||||
if (len != sizeof(cred) || cred.cr_version != XUCRED_VERSION) {
|
||||
return Err(DracError(DracErrorCode::PlatformSpecific, "Invalid xucred structure received"));
|
||||
}
|
||||
peer_pid = cred.cr_pid; // <<< Get PID from xucred
|
||||
|
||||
if (getsockopt(fileDescriptor, SOL_SOCKET, LOCAL_PEERCRED, &cred, &len) == -1)
|
||||
return Err(DracError::withErrno("Failed to get socket credentials (SO_PEERCRED)"));
|
||||
|
||||
Array<char, 128> exeLinkPathBuf;
|
||||
|
||||
const isize count = exeLinkPathBuf.size() - 1;
|
||||
|
||||
std::format_to_n_result<char*> result = std::format_to_n(exeLinkPathBuf.data(), count, "/proc/{}/exe", cred.cr_uid);
|
||||
|
||||
if (result.size >= count)
|
||||
return Err(DracError(DracErrorCode::InternalError, "Failed to format /proc path (PID too large?)"));
|
||||
|
||||
*result.out = '\0';
|
||||
|
||||
const char* exeLinkPath = exeLinkPathBuf.data();
|
||||
|
||||
Array<char, PATH_MAX> exeRealPathBuf; // NOLINT(misc-include-cleaner) - PATH_MAX is in <climits>
|
||||
|
||||
const isize bytesRead = readlink(exeLinkPath, exeRealPathBuf.data(), exeRealPathBuf.size() - 1);
|
||||
|
||||
if (bytesRead == -1)
|
||||
return Err(DracError::withErrno(std::format("Failed to read link '{}'", exeLinkPath)));
|
||||
|
||||
exeRealPathBuf.at(bytesRead) = '\0';
|
||||
|
||||
StringView compositorNameView;
|
||||
|
||||
const StringView pathView(exeRealPathBuf.data(), bytesRead);
|
||||
|
||||
StringView filenameView;
|
||||
|
||||
if (const usize lastCharPos = pathView.find_last_not_of('/'); lastCharPos != StringView::npos) {
|
||||
const StringView relevantPart = pathView.substr(0, lastCharPos + 1);
|
||||
|
||||
if (const usize separatorPos = relevantPart.find_last_of('/'); separatorPos == StringView::npos)
|
||||
filenameView = relevantPart;
|
||||
else
|
||||
filenameView = relevantPart.substr(separatorPos + 1);
|
||||
#elif defined(__NetBSD__)
|
||||
uid_t euid;
|
||||
gid_t egid;
|
||||
if (getpeereid(fileDescriptor, &euid, &egid) == -1) {
|
||||
return Err(DracError::withErrno("getpeereid failed on Wayland socket"));
|
||||
}
|
||||
|
||||
if (!filenameView.empty())
|
||||
compositorNameView = filenameView;
|
||||
return "Wayland Compositor (Unknown Path)";
|
||||
#endif
|
||||
|
||||
if (peer_pid <= 0) {
|
||||
return Err(DracError(DracErrorCode::PlatformSpecific, "Failed to obtain a valid peer PID"));
|
||||
}
|
||||
|
||||
// --- Use the helper function to get the path ---
|
||||
Result<String, DracError> exePathResult = GetPathByPid(peer_pid);
|
||||
if (!exePathResult) {
|
||||
// Forward the error from GetPathByPid
|
||||
return Err(std::move(exePathResult).error());
|
||||
}
|
||||
|
||||
const String& exeRealPath = *exePathResult;
|
||||
|
||||
// --- Extract filename from path (OS-independent) ---
|
||||
StringView compositorNameView;
|
||||
if (const usize lastSlash = exeRealPath.rfind('/'); lastSlash != String::npos) {
|
||||
compositorNameView = StringView(exeRealPath).substr(lastSlash + 1);
|
||||
} else {
|
||||
compositorNameView = exeRealPath; // Path is just the filename
|
||||
}
|
||||
|
||||
if (compositorNameView.empty() || compositorNameView == "." || compositorNameView == "/")
|
||||
return Err(DracError(DracErrorCode::NotFound, "Failed to get compositor name from path"));
|
||||
return Err(DracError(DracErrorCode::NotFound, "Failed to get compositor name from path"));
|
||||
|
||||
|
||||
// --- Heuristic cleanup (OS-independent) ---
|
||||
if (constexpr StringView wrappedSuffix = "-wrapped"; compositorNameView.length() > 1 + wrappedSuffix.length() &&
|
||||
compositorNameView[0] == '.' && compositorNameView.ends_with(wrappedSuffix)) {
|
||||
const StringView cleanedView =
|
||||
compositorNameView.substr(1, compositorNameView.length() - 1 - wrappedSuffix.length());
|
||||
const StringView cleanedView =
|
||||
compositorNameView.substr(1, compositorNameView.length() - 1 - wrappedSuffix.length());
|
||||
|
||||
if (cleanedView.empty())
|
||||
return Err(DracError(DracErrorCode::NotFound, "Compositor name invalid after heuristic"));
|
||||
if (cleanedView.empty())
|
||||
return Err(DracError(DracErrorCode::NotFound, "Compositor name invalid after heuristic"));
|
||||
|
||||
return String(cleanedView);
|
||||
return String(cleanedView);
|
||||
}
|
||||
|
||||
return String(compositorNameView);
|
||||
|
@ -191,37 +220,44 @@ namespace os {
|
|||
|
||||
std::ifstream file(path);
|
||||
|
||||
if (!file)
|
||||
return Err(DracError(DracErrorCode::NotFound, std::format("Failed to open {}", path)));
|
||||
if (file) {
|
||||
String line;
|
||||
constexpr StringView prefix = "NAME=";
|
||||
|
||||
String line;
|
||||
constexpr StringView prefix = "NAME=";
|
||||
while (std::getline(file, line)) {
|
||||
if (StringView(line).starts_with(prefix)) {
|
||||
String value = line.substr(prefix.size());
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
if (StringView(line).starts_with(prefix)) {
|
||||
String value = line.substr(prefix.size());
|
||||
if ((value.length() >= 2 && value.front() == '"' && value.back() == '"') ||
|
||||
(value.length() >= 2 && value.front() == '\'' && value.back() == '\''))
|
||||
value = value.substr(1, value.length() - 2);
|
||||
|
||||
if ((value.length() >= 2 && value.front() == '"' && value.back() == '"') ||
|
||||
(value.length() >= 2 && value.front() == '\'' && value.back() == '\''))
|
||||
value = value.substr(1, value.length() - 2);
|
||||
|
||||
if (value.empty())
|
||||
return Err(
|
||||
DracError(DracErrorCode::ParseError, std::format("PRETTY_NAME value is empty or only quotes in {}", path))
|
||||
);
|
||||
|
||||
return value;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Err(DracError(DracErrorCode::NotFound, std::format("PRETTY_NAME line not found in {}", path)));
|
||||
utsname uts;
|
||||
if (uname(&uts) == -1)
|
||||
return Err(DracError::withErrno(std::format("Failed to open {} and uname() call also failed", path)));
|
||||
|
||||
String osName = uts.sysname;
|
||||
|
||||
if (osName.empty())
|
||||
return Err(DracError(DracErrorCode::ParseError,"uname() returned empty sysname or release"));
|
||||
|
||||
return osName;
|
||||
}
|
||||
|
||||
fn GetMemInfo() -> Result<u64, DracError> {
|
||||
u64 mem = 0;
|
||||
usize size = sizeof(mem);
|
||||
|
||||
#ifdef __NetBSD__
|
||||
sysctlbyname("hw.physmem64", &mem, &size, nullptr, 0);
|
||||
#else
|
||||
sysctlbyname("hw.physmem", &mem, &size, nullptr, 0);
|
||||
#endif
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
@ -407,18 +443,43 @@ namespace os {
|
|||
}
|
||||
|
||||
fn GetHost() -> Result<String, DracError> {
|
||||
auto getKenv = [](const char* name) -> Result<String, DracError> {
|
||||
Array<char, 256> buffer {};
|
||||
int result = kenv(KENV_GET, name, buffer.data(), sizeof(buffer));
|
||||
Array<char, 256> buffer {};
|
||||
usize size = buffer.size();
|
||||
|
||||
if (result == -1)
|
||||
return Err(DracError(DracErrorCode::NotFound, std::format("Environment variable '{}' not found", name)));
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
// Use kenv on FreeBSD / DragonFly
|
||||
int result = kenv(KENV_GET, "smbios.system.product", buffer.data(), buffer.size() -1); // Ensure space for null
|
||||
if (result == -1) {
|
||||
// Fallback: Maybe try hw.model?
|
||||
if (sysctlbyname("hw.model", buffer.data(), &size, nullptr, 0) == -1) {
|
||||
return Err(DracError::withErrno("kenv smbios.system.product failed and sysctl hw.model also failed"));
|
||||
}
|
||||
buffer[std::min(size, buffer.size() - 1)] = '\0'; // Null terminate sysctl result
|
||||
return String(buffer.data());
|
||||
}
|
||||
// kenv should null-terminate, but let's be safe if result > 0 (bytes written)
|
||||
if (result > 0) buffer[result] = '\0';
|
||||
else buffer[0] = '\0'; // Ensure null if result was 0 (empty string?)
|
||||
|
||||
return std::string(buffer.data(), buffer.size());
|
||||
};
|
||||
#elif defined(__NetBSD__)
|
||||
// Use sysctl on NetBSD
|
||||
constexpr const char* netbsd_mib = "machdep.dmi.system-product";
|
||||
if (sysctlbyname(netbsd_mib, buffer.data(), &size, nullptr, 0) == -1) {
|
||||
// Fallback: Try hw.model on NetBSD too
|
||||
size = buffer.size(); // reset size for next attempt
|
||||
if (sysctlbyname("hw.model", buffer.data(), &size, nullptr, 0) == -1) {
|
||||
return Err(DracError::withErrno(std::format("sysctlbyname failed for both {} and hw.model", netbsd_mib)));
|
||||
}
|
||||
}
|
||||
// Ensure null termination for sysctl results
|
||||
buffer[std::min(size, buffer.size() - 1)] = '\0';
|
||||
#endif
|
||||
// Ensure the buffer isn't empty before returning
|
||||
if(buffer[0] == '\0') {
|
||||
return Err(DracError(DracErrorCode::NotFound, "Failed to get host product information (empty result)"));
|
||||
}
|
||||
|
||||
return getKenv("smbios.system.product");
|
||||
}
|
||||
return String(buffer.data());}
|
||||
|
||||
fn GetKernelVersion() -> Result<String, DracError> {
|
||||
utsname uts;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue