diff dep/animia/src/fd/xnu.cc @ 153:bd439dd6ffc5

*: make win stuff actually work, rename bsd.cc to xnu.cc It's been OS X only for ages, and these functions are different between most BSDs anyway
author Paper <mrpapersonic@gmail.com>
date Wed, 15 Nov 2023 13:28:18 -0500
parents
children d43d68408d3c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/fd/xnu.cc	Wed Nov 15 13:28:18 2023 -0500
@@ -0,0 +1,101 @@
+/*
+** fd/xnu.cpp
+**  - provides support for XNU (part of Darwin)
+*/
+#include "animia/fd/xnu.h"
+#include "animia.h"
+
+#include <unordered_map>
+#include <vector>
+#include <string>
+
+#include <fcntl.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <libproc.h>
+
+namespace animia::internal::xnu {
+
+static bool GetProcessName(pid_t pid, std::string& result) {
+	struct proc_bsdinfo proc;
+
+	int st = proc_pidinfo(pid, PROC_PIDTBSDINFO, 0, &proc, PROC_PIDTBSDINFO_SIZE);
+	if (st != PROC_PIDTBSDINFO_SIZE)
+		return false;
+
+	/* fixme: is this right? pbi_comm is an alternative, but it reduces the string size to
+	   16 chars. does pbi_name do the same, or is it different? */
+	result = proc.pbi_name;
+	return true;
+}
+
+/* this is a cleaned up version of a function from... Apple?
+   ...anyway, what it essentially does is gets the size and stuff from
+   sysctl() and reserves the space in a vector to store the PIDs */
+bool XnuFdTools::EnumerateOpenProcesses(process_proc_t process_proc) {
+	struct kinfo_proc* result = NULL;
+	size_t length = 0;
+	static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
+
+	/* get appropriate length from sysctl()
+	   note: the reason this isn't checked is actually because this will
+	   *always* return an error on OS X (or... maybe I'm doing it wrong :) ) */
+	sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0);
+
+	result = (struct kinfo_proc*)malloc(length);
+	if (result == NULL)
+		return false;
+
+	/* TODO: this might actually return ENOMEM if the amount of file handles changes between the
+	   original sysctl() call and this one, which is technically possible */
+	if (sysctl((int*)name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0) == ENOMEM) {
+		assert(result != NULL);
+		free(result);
+		return false;
+	}
+
+	for (int i = 0; i < length / sizeof(*result); i++) {
+		const pid_t pid = result[i].kp_proc.p_pid;
+		if (!process_proc({pid, GetProcessName(pid)}))
+			return false;
+	}
+}
+
+bool XnuFdTools::EnumerateOpenFiles(const std::set<pid_t>& pids, open_file_proc_t open_file_proc) {
+	if (!open_file_proc)
+		return false;
+
+	for (const auto& pid : pids) {
+		int bufsz = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0);
+		if (bufsz == -1)
+			return false;
+
+		struct proc_fdinfo* info = (struct proc_fdinfo*)malloc(bufsz);
+		if (!info)
+			return false;
+
+		proc_pidinfo(pid, PROC_PIDLISTFDS, 0, info, bufsz);
+
+		for (int i = 0; i < bufsz / sizeof(info[0]); i++) {
+			if (info[i].proc_fdtype == PROX_FDTYPE_VNODE) {
+				struct vnode_fdinfowithpath vnodeInfo;
+
+				int sz = proc_pidfdinfo(pid, info[i].proc_fd, PROC_PIDFDVNODEPATHINFO, &vnodeInfo, PROC_PIDFDVNODEPATHINFO_SIZE);
+				if (sz != PROC_PIDFDVNODEPATHINFO_SIZE)
+					return false;
+
+				/* this doesn't work!
+				if (vnodeInfo.pfi.fi_openflags & O_WRONLY || vnodeInfo.pfi.fi_openflags & O_RDWR)
+					continue;
+				*/
+
+				if (!open_file_proc({pid, vnodeInfo.pvip.vip_path}))
+					return false;
+			}
+		}
+	}
+	return true;
+}
+
+} // namespace animia::internal::unix