changeset 169:e44b7c428d7c

dep/animia: add libkvm method (UNTESTED)
author Paper <mrpapersonic@gmail.com>
date Sun, 19 Nov 2023 17:30:38 -0500
parents 79a2a24453fa
children c8375765f0fc 8f6f8dd2eb23
files dep/animia/CMakeLists.txt dep/animia/src/fd/kvm.cc dep/animia/src/fd/win32.cc dep/animia/src/fd/xnu.cc dep/animia/src/util/osx.cc
diffstat 5 files changed, 192 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/dep/animia/CMakeLists.txt	Sun Nov 19 05:36:41 2023 -0500
+++ b/dep/animia/CMakeLists.txt	Sun Nov 19 17:30:38 2023 -0500
@@ -54,16 +54,15 @@
 		src/util/win32.cc
 	)
 else() # NOT WIN32 AND NOT APPLE
+	find_library(LIBUTIL_LIBRARY util)
+	find_library(LIBKVM_LIBRARY kvm)
+
 	if(LINUX)
 		list(APPEND DEFINES LINUX)
 		list(APPEND SRC_FILES
 			src/fd/proc.cc
 		)
-	endif() # LINUX
-
-	# FreeBSD
-	find_library(LIBUTIL_LIBRARY util)
-	if(LIBUTIL_LIBRARY)
+	elseif(LIBUTIL_LIBRARY) # FreeBSD's libutil
 		get_filename_component(LIBUTIL_DIR ${LIBUTIL_LIBRARY} DIRECTORY)
 
 		include(CheckLibraryExists)
@@ -74,7 +73,18 @@
 			list(APPEND DEFINES LIBUTIL)
 			list(APPEND SRC_FILES src/fd/libutil.cc)
 		endif() # LIBUTIL_GOOD
-	endif() # LIBUTIL_LIBRARY
+	elseif(LIBKVM_LIBRARY) # BSD libkvm
+		get_filename_component(LIBKVM_DIR ${LIBKVM_LIBRARY} DIRECTORY)
+
+		include(CheckLibraryExists)
+		check_library_exists(kvm kvm_getprocs ${LIBKVM_DIR} LIBKVM_GOOD)
+
+		if(LIBKVM_GOOD)
+			list(APPEND LIBRARIES ${LIBKVM_LIBRARY})
+			list(APPEND DEFINES LIBKVM)
+			list(APPEND SRC_FILES src/fd/libkvm.cc)
+		endif() # LIBUTIL_GOOD
+	endif() # LINUX
 
 	# X11
 	find_package(X11 COMPONENTS X11)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/fd/kvm.cc	Sun Nov 19 17:30:38 2023 -0500
@@ -0,0 +1,134 @@
+/* kvm.cc: provides support for libkvm in multiple BSDs
+**
+** this is really the only way to get a thing that works on
+** OpenBSD AND NetBSD.
+**
+** Much of this file is taken from the fstat source code in
+** NetBSD.
+*/
+
+#include <kvm.h>
+
+namespace animia::internal::kvm {
+
+static bool GetFilename(kvm_t* kvm, struct vnode *vp, std::string& name) {
+	struct vnode vn;
+	if (!kvm_read(kvm, vp, &vn, sizeof(*vn)))
+		return 0;
+
+	struct filestat fst;
+	const char* type = vfilestat(vn, &fst);
+	if (type == dead)
+		return false;
+
+	for (DEVS* d = devs; d != NULL; d = d->next) {
+		if (d->fsid == fst->fsid && d->ino == fst->fileid) {
+			name = d->name;
+			break;
+		}
+	}
+
+	return true;
+}
+
+static bool GetFilePath(kvm_t* kvm, fdfile_t* fp, std::string& path) {
+	struct file file;
+	fdfile_t fdfile;
+
+	if (!kvm_read(kvm, fp, &fdfile, sizeof(fdfile)))
+		return false;
+
+	if (!fdfile.ff_file)
+		return false;
+
+	if (!kvm_read(fdfile.ff_file, &file, sizeof(file)))
+		return false;
+
+	if (file.f_type != DTYPE_VNODE)
+		return false;
+
+	return GetFilename(kvm, file.f_data, path);
+}
+
+static bool OpenFiles(kvm_t* kvm, struct kinfo_proc* p, open_file_proc_t open_file_proc) {
+	if (p->proc->p_fd == 0 || p->proc->p_cwdi == 0)
+		return false;
+
+	struct filedesc filed;
+	if (!kvm_read(kvm, p->proc->p_fd, &filed, sizeof(filed)))
+		return false;
+
+	if (filed.fd_lastfile == -1)
+		return false;
+
+	struct cwdinfo cwdi;
+	if (!kvm_read(kvm, p->proc->p_cwdi, &cwdi, sizeof(cwdi)))
+		return false;
+
+	struct fdtab dt;
+	if (!kvm_read(kvm, filed.fd_dt, &dt, sizeof(dt)))
+		return false;
+
+	/* check for corrupted files? */
+	if ((unsigned)filed.fd_lastfile >= dt.dt_nfiles || filed.fd_freefile > filed.fd_lastfile + 1)
+		return false;
+
+	/* open files */
+	std::unique_ptr<fdfile_t*[]> ofiles = nullptr;
+	{
+		ofiles.reset(malloc((filed.fd_lastfile + 1) * sizeof(fdfile_t*)));
+		if (!ofiles.get())
+			return false;
+	}
+
+	if (!kvm_read(kvm, &filed.fd_dt->dt_ff, ofiles.get(), filed.fd_lastfile + 1 * (sizeof(fdfile_t*))))
+		return false;
+
+	for (int i = 0; i <= filed.fd_lastfile; i++) {
+		if (!ofiles[i])
+			continue;
+		std::string name;
+		GetFilePath(kvm, ofiles[i], name);
+		if (!open_file_proc(p->proc->p_pid, name))
+			return false;
+	}
+
+	return true;
+}
+
+bool EnumerateOpenProcesses(process_proc_t process_proc) {
+	char errbuf[_POSIX2_LINE_MAX];
+	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
+	if (!kernel)
+		return false;
+
+	int entries = 0;
+	struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc), &nentries);
+	if (!kinfo)
+		return false;
+
+	for (int i = 0; i < entries; i++)
+		if (!process_proc({kinfo[i].p_pid, kinfo[i].p_comm}))
+			return false;
+
+	return true;
+}
+
+bool EnumerateOpenFiles(std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	kvm_t* kernel = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf);
+	if (!kernel)
+		return false;
+
+	for (const auto& pid : pids) {
+		int cnt;
+		struct kinfo_proc* kinfo = kvm_getprocs(kernel, KERN_PROC_PID, pid, &cnt);
+		if (!kinfo)
+			return false;
+
+		for (int i = 0; i < cnt; i++) {
+			OpenFiles(kernel, kinfo, open_file_proc);
+		}
+	}
+}
+
+}
--- a/dep/animia/src/fd/win32.cc	Sun Nov 19 05:36:41 2023 -0500
+++ b/dep/animia/src/fd/win32.cc	Sun Nov 19 17:30:38 2023 -0500
@@ -1,8 +1,3 @@
-/*
-** win32.cpp
-**  - provides support for Windows clients
-**
-*/
 #include "animia/fd/win32.h"
 #include "animia/util/win32.h"
 #include "animia.h"
@@ -24,12 +19,18 @@
 #include <winternl.h>
 
 /* This file is noticably more complex than Unix and Linux, and that's because
-   there is no "simple" way to get the paths of a file. In fact, this thing requires
-   you to use *internal functions* that can't even be linked to, hence why we have to
-   use GetProcAddress and such. What a mess. */
+ * there is no "simple" way to get the paths of a file. In fact, this thing requires
+ * you to use *internal functions* that can't even be linked to, hence why we have to
+ * use GetProcAddress and such. What a mess.
+ *
+ * Speaking of which, because this file uses internal functions of the OS, it is not
+ * guaranteed to work far into the future. However, it has worked since NT 6.0 (Vista)
+ * at least, so it's unlikely to be changed much ever.
+*/
 
 /* SystemExtendedHandleInformation is only available in NT 5.1+ (XP and higher) and provides information for
-   32-bit PIDs, unlike SystemHandleInformation */
+ * 32-bit PIDs, unlike SystemHandleInformation
+*/
 constexpr SYSTEM_INFORMATION_CLASS SystemExtendedHandleInformation = static_cast<SYSTEM_INFORMATION_CLASS>(0x40);
 
 /* more constants not in winternl.h */
--- a/dep/animia/src/fd/xnu.cc	Sun Nov 19 05:36:41 2023 -0500
+++ b/dep/animia/src/fd/xnu.cc	Sun Nov 19 17:30:38 2023 -0500
@@ -1,11 +1,5 @@
-/*
-** fd/xnu.cc
-**  - provides support for XNU (part of Darwin)
-*/
 #include "animia/fd/xnu.h"
-#ifdef HAVE_COREFOUNDATION
-#	include "animia/util/osx.h"
-#endif
+#include "animia/util/osx.h"
 #include "animia.h"
 
 #include <cassert>
--- a/dep/animia/src/util/osx.cc	Sun Nov 19 05:36:41 2023 -0500
+++ b/dep/animia/src/util/osx.cc	Sun Nov 19 17:30:38 2023 -0500
@@ -1,5 +1,8 @@
 #include "animia/util/osx.h"
 
+#include <string>
+#include <memory>
+
 #ifdef HAVE_COREFOUNDATION
 #include <CoreFoundation/CoreFoundation.h>
 #endif
@@ -7,12 +10,20 @@
 namespace animia::internal::osx::util {
 
 #ifdef HAVE_COREFOUNDATION
-/* All of these LaunchServices things use *internal functions* that are subject
-** to change. Granted, it's not very likely that these will change very much
-** because I'm fairly sure Apple uses them lots in their own internal code.
+/* I don't want to have to call CFRelease */
+template<typename T>
+struct CFDeleter {
+	using pointer = T;
+	void operator()(pointer p) { CFRelease(p); }
+}
+
+template<typename T>
+typedef CFReference = std::unique_ptr<T, CFDeleter>;
+
+/* all of these LaunchServices things use *internal functions* that are subject
+ * to change. Granted, it's not very likely that these will change very much
+ * because I'm fairly sure Apple uses them lots in their own internal code.
 */
-
-/* from RDProcess */
 typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t);
 typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef);
 
@@ -21,6 +32,7 @@
 
 /* retrieved from LaunchServicesSPI.h in WebKit */
 static constexpr int kLSDefaultSessionID = -2;
+
 static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices");
 
 /* retrieved dynamically */
@@ -56,33 +68,26 @@
 		if (!GetLaunchServicesPrivateSymbols())
 			return false;
 
-	CFTypeRef asn = LSASNCreateWithPid(kCFAllocatorDefault, pid);
-
-	CFArrayRef request_array = CFArrayCreate(NULL, (const void **)kLSDisplayNameKey, 1, NULL);
+	CFReference<CFTypeRef> asn = LSASNCreateWithPid(kCFAllocatorDefault, pid);
 
-	CFDictionaryRef dictionary = LSCopyApplicationInformation(kLaunchServicesMagicConstant, asn, request_array);
+	CFReference<CFArrayRef> request_array = CFArrayCreate(NULL, (const void **)kLSDisplayNameKey, 1, NULL);
 
-	CFRelease(request_array);
-	if (!dictionary)
+	CFReference<CFDictionaryRef> dictionary = LSCopyApplicationInformation(kLaunchServicesMagicConstant, asn, request_array.get());
+	if (!dictionary.get())
 		return false;
 
-	CFStringRef str;
-	if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&str) || !str) {
-		CFRelease(dictionary);
-		return false;
-	}
-	CFRetain(str);
-
-	CFRelease(dictionary);
-
-	result.reserve(CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1);
-
-	if (!CFStringGetCString(str, &result.front(), result.length(), result.length())) {
-		CFRelease(str);
-		return false;
+	CFReference<CFStringRef> str;
+	{
+		CFStringRef rstr;
+		if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&rstr) || !rstr)
+			return false;
+		str.reset(rstr);
 	}
 
-	CFRelease(str);
+	result.reserve(CFStringGetMaximumSizeForEncoding(CFStringGetLength(str.get()), kCFStringEncodingUTF8) + 1);
+
+	if (!CFStringGetCString(str.get(), &result.front(), result.length(), result.length()))
+		return false;
 
 	return true;
 }