changeset 338:f63dfa309380

dep/animone: separate *BSD into separate files they are wholly different operating systems with very different kernels on the inside
author Paper <paper@paper.us.eu.org>
date Wed, 19 Jun 2024 13:06:10 -0400
parents a7d4e5107531
children eac06513db86
files dep/animone/Makefile.am dep/animone/configure.ac dep/animone/include/animone/fd/bsd.h dep/animone/include/animone/fd/freebsd.h dep/animone/include/animone/fd/netbsd.h dep/animone/include/animone/fd/openbsd.h dep/animone/src/fd/bsd.cc dep/animone/src/fd/freebsd.cc dep/animone/src/fd/netbsd.cc dep/animone/src/fd/openbsd.cc
diffstat 10 files changed, 401 insertions(+), 270 deletions(-) [+]
line wrap: on
line diff
--- a/dep/animone/Makefile.am	Wed Jun 19 12:51:15 2024 -0400
+++ b/dep/animone/Makefile.am	Wed Jun 19 13:06:10 2024 -0400
@@ -10,7 +10,9 @@
 	include/animone/types.h
 
 noinst_HEADERS = \
-	include/animone/fd/bsd.h \
+	include/animone/fd/freebsd.h \
+	include/animone/fd/openbsd.h \
+	include/animone/fd/netbsd.h \
 	include/animone/fd/proc.h \
 	include/animone/fd/win32.h \
 	include/animone/fd/xnu.h \
@@ -38,22 +40,17 @@
 files_linux = src/fd/proc.cc
 endif
 
-# BSD stuff
-if BUILD_BSD
+if BUILD_FREEBSD
 
-files_bsd = src/fd/bsd.cc
+files_freebsd = src/fd/freebsd.cc
+libs_freebsd = -lutil
 
 endif
 
-if BUILD_LIBUTIL
-
-libs_libutil = -lutil
+if BUILD_OPENBSD
 
-endif
-
-if BUILD_LIBKVM
-
-libs_libkvm = -lkvm
+files_openbsd = src/fd/openbsd.cc
+libs_openbsd = -lkvm
 
 endif
 
@@ -76,9 +73,9 @@
 	$(files_win) \
 	$(files_osx) \
 	$(files_linux) \
-	$(files_bsd) \
-	$(files_x11) \
-	$(files_wayland)
+	$(files_freebsd) \
+	$(files_openbsd) \
+	$(files_x11)
 
 libanimone_la_CPPFLAGS = -I$(top_srcdir)/include $(DEFS)
 
--- a/dep/animone/configure.ac	Wed Jun 19 12:51:15 2024 -0400
+++ b/dep/animone/configure.ac	Wed Jun 19 13:06:10 2024 -0400
@@ -42,19 +42,20 @@
 		;;
 	*)
 		dnl BSDs
+		saved_LIBS="$LIBS"
 		AC_CHECK_LIB([util], [kinfo_getfile], [build_libutil=yes], [build_libutil=no])
 		AC_CHECK_LIB([kvm], [kvm_getfiles], [build_kvm=yes], [build_kvm=no])
+		LIBS="$saved_LIBS"
 
 		if test "x$build_kvm" = "xyes"; then
-			AC_DEFINE([USE_LIBKVM])
-			AC_DEFINE([BSD])
+			AC_DEFINE([USE_OPENBSD])
 		elif test "x$build_libutil" = "xyes"; then
-			AC_DEFINE([USE_LIBUTIL])
-			AC_DEFINE([BSD])
+			AC_DEFINE([USE_FREEBSD])
 		fi
 		;;
 esac
 
+dnl todo: configure flag for this
 if test "x$build_osx" != "xyes" && test "x$build_windows" != "xyes"; then
 	PKG_CHECK_MODULES(XCB, [xcb xcb-res], [build_x11=yes], [build_x11=no])
 	if test "x$build_x11" = "xyes"; then
@@ -67,8 +68,8 @@
 AM_CONDITIONAL([BUILD_WIN], [test "x$build_windows" = "xyes"])
 AM_CONDITIONAL([BUILD_OSX], [test "x$build_osx" = "xyes"])
 AM_CONDITIONAL([BUILD_LINUX], [test "x$build_linux" = "xyes"])
-AM_CONDITIONAL([BUILD_LIBUTIL], [test "x$build_libutil" = "xyes"])
-AM_CONDITIONAL([BUILD_LIBKVM], [test "x$build_kvm" = "xyes"])
+AM_CONDITIONAL([BUILD_FREEBSD], [test "x$build_libutil" = "xyes"])
+AM_CONDITIONAL([BUILD_OPENBSD], [test "x$build_kvm" = "xyes"])
 AM_CONDITIONAL([BUILD_BSD], [test "x$build_bsd" = "xyes"])
 
 AM_CONDITIONAL([BUILD_XCB], [test "x$build_x11" = "xyes"])
--- a/dep/animone/include/animone/fd/bsd.h	Wed Jun 19 12:51:15 2024 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-#ifndef ANIMONE_ANIMONE_FD_BSD_H_
-#define ANIMONE_ANIMONE_FD_BSD_H_
-
-#include <set>
-#include <string>
-
-#include "animone/fd.h"
-#include "animone/types.h"
-
-namespace animone::internal::bsd {
-
-bool GetProcessName(pid_t pid, std::string& name);
-bool EnumerateOpenProcesses(process_proc_t process_proc);
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
-
-} // namespace animone::internal::kvm
-
-#endif // ANIMONE_ANIMONE_FD_BSD_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/fd/freebsd.h	Wed Jun 19 13:06:10 2024 -0400
@@ -0,0 +1,18 @@
+#ifndef ANIMONE_ANIMONE_FD_FREEBSD_H_
+#define ANIMONE_ANIMONE_FD_FREEBSD_H_
+
+#include <set>
+#include <string>
+
+#include "animone/fd.h"
+#include "animone/types.h"
+
+namespace animone::internal::freebsd {
+
+bool GetProcessName(pid_t pid, std::string& name);
+bool EnumerateOpenProcesses(process_proc_t process_proc);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
+
+} // namespace animone::internal::kvm
+
+#endif // ANIMONE_ANIMONE_FD_FREEBSD_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/fd/netbsd.h	Wed Jun 19 13:06:10 2024 -0400
@@ -0,0 +1,18 @@
+#ifndef ANIMONE_ANIMONE_FD_NETBSD_H_
+#define ANIMONE_ANIMONE_FD_NETBSD_H_
+
+#include <set>
+#include <string>
+
+#include "animone/fd.h"
+#include "animone/types.h"
+
+namespace animone::internal::netbsd {
+
+bool GetProcessName(pid_t pid, std::string& name);
+bool EnumerateOpenProcesses(process_proc_t process_proc);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
+
+} // namespace animone::internal::netbsd
+
+#endif // ANIMONE_ANIMONE_FD_NETBSD_H_
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/include/animone/fd/openbsd.h	Wed Jun 19 13:06:10 2024 -0400
@@ -0,0 +1,18 @@
+#ifndef ANIMONE_ANIMONE_FD_OPENBSD_H_
+#define ANIMONE_ANIMONE_FD_OPENBSD_H_
+
+#include <set>
+#include <string>
+
+#include "animone/fd.h"
+#include "animone/types.h"
+
+namespace animone::internal::openbsd {
+
+bool GetProcessName(pid_t pid, std::string& name);
+bool EnumerateOpenProcesses(process_proc_t process_proc);
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc);
+
+} // namespace animone::internal::openbsd
+
+#endif // ANIMONE_ANIMONE_FD_OPENBSD_H_
--- a/dep/animone/src/fd/bsd.cc	Wed Jun 19 12:51:15 2024 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,231 +0,0 @@
-/*
- * fd/win32.cc: support for most BSDs
- *
- * should support (Free/Open/Net)BSD. possibly more,
- * but this code is untested outside of FreeBSD.
- */
-#include "animone/fd/bsd.h"
-#include "animone.h"
-#include "animone/fd.h"
-
-#include <sys/file.h>
-#include <sys/filedesc.h>
-#include <sys/param.h>
-#include <sys/queue.h>
-#include <sys/sysctl.h>
-#include <sys/types.h>
-#include <sys/user.h>
-#include <sys/vnode.h>
-
-#ifdef HAVE_KVM_GETFILES
-#	include <kvm.h>
-#	include <fts.h>
-#elif defined(LIBUTIL)
-#	include <libutil.h>
-#endif
-
-#include <string>
-
-namespace animone::internal::bsd {
-
-static std::string Basename(const std::string& name) {
-	size_t s = name.find_last_of('/');
-
-	if (s == std::string::npos)
-		return name;
-
-	return name.substr(s, name.size());
-}
-
-bool GetProcessName(pid_t pid, std::string& name) {
-#ifdef HAVE_KVM_GETFILES
-	char errbuf[_POSIX2_LINE_MAX];
-	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
-	if (!kernel)
-		return false;
-
-	int entries = 0;
-	struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_PID, pid, &entries);
-	if (!kinfo) {
-		kvm_close(kernel);
-		return false;
-	}
-
-	if (entries < 1) {
-		kvm_close(kernel);
-		return false;
-	}
-
-	name = Basename(kinfo[0].ki_paddr->p_comm);
-
-	return true;
-#else
-	/* use sysctl as a fallback */
-	static const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
-
-	struct kinfo_proc result;
-
-	size_t length = 1;
-	if (sysctl((int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, &result, &length, NULL, 0) == -1)
-		return false;
-
-	name = Basename(result.ki_comm);
-
-	return true;
-#endif
-}
-
-/* Most of the BSDs share the common kvm library,
- * so accessing this information can be trivial.
- */
-bool EnumerateOpenProcesses(process_proc_t process_proc) {
-#ifdef HAVE_KVM_GETFILES
-	char errbuf[_POSIX2_LINE_MAX];
-	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
-	if (!kernel)
-		return false;
-
-	int entries = 0;
-	struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_ALL, 0, &entries);
-	if (!kinfo) {
-		kvm_close(kernel);
-		return false;
-	}
-
-	for (int i = 0; i < entries; i++) {
-		if (!process_proc({.platform = ExecutablePlatform::Posix, .pid = kinfo[i].ki_paddr->p_pid, .comm = Basename(kinfo[i].ki_paddr->p_comm)})) {
-			kvm_close(kernel);
-			return false;
-		}
-	}
-
-	kvm_close(kernel);
-
-	return true;
-#else
-	/* use sysctl as a fallback */
-	static const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
-	size_t length = 0;
-
-	sysctl((int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, NULL, &length, NULL, 0);
-
-	std::unique_ptr<struct kinfo_proc[]> result;
-	result.reset(new struct kinfo_proc[length]);
-
-	if (!result.get())
-		return false;
-
-	/* actually get our results */
-	if (sysctl((const int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, result.get(), &length, NULL, 0) == ENOMEM)
-		return false;
-
-	if (length < sizeof(struct kinfo_proc))
-		return false;
-
-	for (int i = 0; i < length / sizeof(result[0]); i++)
-		if (!process_proc({.platform = ExecutablePlatform::Posix, .pid = result[i].ki_pid, .comm = result[i].ki_comm}))
-			return false;
-
-	return true;
-#endif
-}
-
-#ifdef HAVE_KVM_GETFILES
-static bool GetOpenFileName(const struct kinfo_file& file, std::string& name) {
-	/* OpenBSD doesn't provide a native API for this, so we have
-	 * to do it ourselves */
-	static constexpr std::string_view root = "/";
-
-	FTS* file_system = fts_open(root.data(), FTS_COMFOLLOW | FTS_NOCHDIR, nullptr);
-	if (!file_system)
-		return false;
-
-	/* Search through the filesystem for a file that matches our
-	 * kinfo_file structure */
-	FTSENT* parent = nullptr;
-	while ((parent = fts_read(file_system))) {
-		FTSENT* child = fts_children(file_system, 0);
-		while (child && child->fts_link) {
-			child = child->fts_link;
-			if (!S_ISREG(child->fts_statp->st_mode) || !S_ISLNK(child->fts_statp->st_mode))
-				continue;
-
-			if (child->fts_statp->st_dev != file->va_fsid)
-				continue;
-
-			if (child->fts_statp->st_ino != file->va_fileid)
-				continue;
-
-			name = std::string(child->fts_path) + child->fts_name;
-			fts_close(file_system);
-			return true;
-		}
-	}
-
-	fts_close(filesystem);
-	return false;
-}
-#endif /* HAVE_KVM_GETFILES */
-
-bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
-#ifdef HAVE_KVM_GETFILES
-	char errbuf[_POSIX2_LINE_MAX];
-	kvm_t* kernel = kvm_openfiles(nullptr, nullptr, nullptr, O_RDONLY, errbuf);
-	if (!kernel)
-		return false;
-
-	for (const auto& pid : pids) {
-		int cnt;
-		struct kinfo_file* kfile = kvm_getfiles(kernel, KERN_FILE_BYPID, pid, &cnt);
-		if (!kfile) {
-			kvm_close(kernel);
-			return false;
-		}
-
-		for (int i = 0; i < cnt; i++) {
-			uint32_t oflags = kfile[i].kf_flags & O_ACCMODE;
-			if (oflags == O_WRONLY || oflags == O_RDWR)
-				continue;
-
-			std::string name;
-			if (!GetOpenFileName(kfile[i], name))
-				continue;
-
-			if (!open_file_proc({pid, name})) {
-				kvm_close(kernel);
-				return false;
-			}
-		}
-	}
-
-	kvm_close(kernel);
-	return true;
-#elif defined(LIBUTIL)
-	for (const auto& pid : pids) {
-		int cnt;
-		std::unique_ptr<struct kinfo_file[]> files(kinfo_getfile(pid, &cnt));
-		if (!files)
-			return false;
-
-		for (int i = 0; i < cnt; i++) {
-			const struct kinfo_file& current = files[i];
-			if (current.kf_vnode_type != KF_VTYPE_VREG || current.kf_vnode_type != KF_VTYPE_VLNK)
-				continue;
-
-			const int oflags = current.kf_flags & O_ACCMODE;
-			if (oflags == O_WRONLY || oflags == O_RDWR)
-				continue;
-
-			if (!open_file_proc({pid, current.kf_path}))
-				return false;
-		}
-	}
-
-	return true;
-#else
-	/* NetBSD doesn't even provide a real API for this */
-	return false;
-#endif
-}
-
-} // namespace animone::internal::kvm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/fd/freebsd.cc	Wed Jun 19 13:06:10 2024 -0400
@@ -0,0 +1,98 @@
+/*
+ * fd/freebsd.cc: support for FreeBSD
+ *
+ * somewhat tested...
+ */
+#include "animone/fd/bsd.h"
+#include "animone.h"
+#include "animone/fd.h"
+
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/vnode.h>
+
+#include <libutil.h>
+
+#include <string>
+
+namespace animone::internal::freebsd {
+
+static std::string Basename(const std::string& name) {
+	size_t s = name.find_last_of('/');
+
+	if (s == std::string::npos)
+		return name;
+
+	return name.substr(s, name.size());
+}
+
+bool GetProcessName(pid_t pid, std::string& name) {
+	static const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+
+	struct kinfo_proc result;
+
+	size_t length = 1;
+	if (sysctl((int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, &result, &length, NULL, 0) == -1)
+		return false;
+
+	name = Basename(result.ki_comm);
+
+	return true;
+}
+
+bool EnumerateOpenProcesses(process_proc_t process_proc) {
+	static const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
+	size_t length = 0;
+
+	sysctl((int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, NULL, &length, NULL, 0);
+
+	std::unique_ptr<struct kinfo_proc[]> result;
+	result.reset(new struct kinfo_proc[length]);
+
+	if (!result.get())
+		return false;
+
+	/* actually get our results */
+	if (sysctl((const int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, result.get(), &length, NULL, 0) == ENOMEM)
+		return false;
+
+	if (length < sizeof(struct kinfo_proc))
+		return false;
+
+	for (int i = 0; i < length / sizeof(result[0]); i++)
+		if (!process_proc({.platform = ExecutablePlatform::Posix, .pid = result[i].ki_pid, .comm = result[i].ki_comm}))
+			return false;
+
+	return true;
+}
+
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	for (const auto& pid : pids) {
+		int cnt;
+		std::unique_ptr<struct kinfo_file[]> files(kinfo_getfile(pid, &cnt));
+		if (!files)
+			return false;
+
+		for (int i = 0; i < cnt; i++) {
+			const struct kinfo_file& current = files[i];
+			if (current.kf_vnode_type != KF_VTYPE_VREG || current.kf_vnode_type != KF_VTYPE_VLNK)
+				continue;
+
+			const int oflags = current.kf_flags & O_ACCMODE;
+			if (oflags == O_WRONLY || oflags == O_RDWR)
+				continue;
+
+			if (!open_file_proc({pid, current.kf_path}))
+				return false;
+		}
+	}
+
+	return true;
+}
+
+} // namespace animone::internal::freebsd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/fd/netbsd.cc	Wed Jun 19 13:06:10 2024 -0400
@@ -0,0 +1,77 @@
+/*
+ * fd/netbsd.cc: support for NetBSD
+ *
+ * as with OpenBSD, completely untested
+ */
+#include "animone/fd/bsd.h"
+#include "animone.h"
+#include "animone/fd.h"
+
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/vnode.h>
+
+#include <string>
+
+namespace animone::internal::netbsd {
+
+static std::string Basename(const std::string& name) {
+	size_t s = name.find_last_of('/');
+
+	if (s == std::string::npos)
+		return name;
+
+	return name.substr(s, name.size());
+}
+
+bool GetProcessName(pid_t pid, std::string& name) {
+	static const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+
+	struct kinfo_proc result;
+
+	size_t length = 1;
+	if (sysctl((int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, &result, &length, NULL, 0) == -1)
+		return false;
+
+	name = Basename(result.ki_comm);
+
+	return true;
+}
+
+bool EnumerateOpenProcesses(process_proc_t process_proc) {
+	static const int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
+	size_t length = 0;
+
+	sysctl((int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, NULL, &length, NULL, 0);
+
+	std::unique_ptr<struct kinfo_proc[]> result;
+	result.reset(new struct kinfo_proc[length]);
+
+	if (!result.get())
+		return false;
+
+	/* actually get our results */
+	if (sysctl((const int*)mib, (sizeof(mib) / sizeof(*mib)) - 1, result.get(), &length, NULL, 0) == ENOMEM)
+		return false;
+
+	if (length < sizeof(struct kinfo_proc))
+		return false;
+
+	for (int i = 0; i < length / sizeof(result[0]); i++)
+		if (!process_proc({.platform = ExecutablePlatform::Posix, .pid = result[i].ki_pid, .comm = result[i].ki_comm}))
+			return false;
+
+	return true;
+}
+
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	/* I don't think NetBSD actually provides a real API for this. */
+	return false;
+}
+
+} // namespace animone::internal::netbsd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/fd/openbsd.cc	Wed Jun 19 13:06:10 2024 -0400
@@ -0,0 +1,153 @@
+/*
+ * fd/openbsd.cc: support for OpenBSD
+ *
+ * COMPLETELY UNTESTED. may or may not work.
+ */
+#include "animone/fd/bsd.h"
+#include "animone.h"
+#include "animone/fd.h"
+
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <sys/vnode.h>
+
+#include <kvm.h>
+#include <fts.h>
+
+#include <string>
+
+namespace animone::internal::openbsd {
+
+/* XXX is this necessary */
+static std::string Basename(const std::string& name) {
+	size_t s = name.find_last_of('/');
+
+	if (s == std::string::npos)
+		return name;
+
+	return name.substr(s, name.size());
+}
+
+bool GetProcessName(pid_t pid, std::string& name) {
+	char errbuf[_POSIX2_LINE_MAX];
+	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
+	if (!kernel)
+		return false;
+
+	int entries = 0;
+	struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_PID, pid, &entries);
+	if (!kinfo) {
+		kvm_close(kernel);
+		return false;
+	}
+
+	if (entries < 1) {
+		kvm_close(kernel);
+		return false;
+	}
+
+	name = Basename(kinfo[0].ki_paddr->p_comm);
+
+	return true;
+}
+
+bool EnumerateOpenProcesses(process_proc_t process_proc) {
+	char errbuf[_POSIX2_LINE_MAX];
+	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
+	if (!kernel)
+		return false;
+
+	int entries = 0;
+	struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_ALL, 0, &entries);
+	if (!kinfo) {
+		kvm_close(kernel);
+		return false;
+	}
+
+	for (int i = 0; i < entries; i++) {
+		if (!process_proc({.platform = ExecutablePlatform::Posix, .pid = kinfo[i].ki_paddr->p_pid, .comm = Basename(kinfo[i].ki_paddr->p_comm)})) {
+			kvm_close(kernel);
+			return false;
+		}
+	}
+
+	kvm_close(kernel);
+
+	return true;
+}
+
+static bool GetOpenFileName(const struct kinfo_file& file, std::string& name) {
+	/* OpenBSD doesn't provide a native API for this, so we have
+	 * to do it ourselves */
+	static constexpr std::string_view root = "/";
+
+	FTS* file_system = fts_open(root.data(), FTS_COMFOLLOW | FTS_NOCHDIR, nullptr);
+	if (!file_system)
+		return false;
+
+	/* Search through the filesystem for a file that matches our
+	 * kinfo_file structure */
+	FTSENT* parent = nullptr;
+	while ((parent = fts_read(file_system))) {
+		FTSENT* child = fts_children(file_system, 0);
+		while (child && child->fts_link) {
+			child = child->fts_link;
+			if (!S_ISREG(child->fts_statp->st_mode) || !S_ISLNK(child->fts_statp->st_mode))
+				continue;
+
+			if (child->fts_statp->st_dev != file->va_fsid)
+				continue;
+
+			if (child->fts_statp->st_ino != file->va_fileid)
+				continue;
+
+			name = std::string(child->fts_path) + child->fts_name;
+			fts_close(file_system);
+			return true;
+		}
+	}
+
+	fts_close(filesystem);
+	return false;
+}
+
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	char errbuf[_POSIX2_LINE_MAX];
+	kvm_t* kernel = kvm_openfiles(nullptr, nullptr, nullptr, O_RDONLY, errbuf);
+	if (!kernel)
+		return false;
+
+	for (const auto& pid : pids) {
+		int cnt;
+		struct kinfo_file* kfile = kvm_getfiles(kernel, KERN_FILE_BYPID, pid, &cnt);
+		if (!kfile) {
+			kvm_close(kernel);
+			return false;
+		}
+
+		for (int i = 0; i < cnt; i++) {
+			uint32_t oflags = kfile[i].kf_flags & O_ACCMODE;
+			if (oflags == O_WRONLY || oflags == O_RDWR)
+				continue;
+
+			std::string name;
+			if (!GetOpenFileName(kfile[i], name))
+				continue;
+
+			if (!open_file_proc({pid, name})) {
+				kvm_close(kernel);
+				return false;
+			}
+		}
+	}
+
+	kvm_close(kernel);
+	return true;
+}
+
+} // namespace animone::internal::openbsd