diff dep/animia/src/fd/bsd.cc @ 138:28842a8d0c6b

dep/animia: huge refactor (again...) but this time, it actually compiles! and it WORKS! (on win32... not sure about other platforms...) configuring players is still not supported: at some point I'll prune something up...
author Paper <mrpapersonic@gmail.com>
date Sun, 12 Nov 2023 04:53:19 -0500
parents
children 478f3b366199
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animia/src/fd/bsd.cc	Sun Nov 12 04:53:19 2023 -0500
@@ -0,0 +1,116 @@
+/**
+ * bsd.cpp
+ *  - provides support for most* versions of BSD
+ *  - this also works for OS X :)
+ * more technical details: this is essentially a wrapper
+ * around the very C-like BSD system functions that are...
+ * kind of unnatural to use in modern C++.
+ **/
+#include <fcntl.h>
+#include <iostream>
+#include <string>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+#include <sys/user.h>
+#include <unordered_map>
+#include <vector>
+#ifdef __FreeBSD__
+#	include <libutil.h>
+#elif defined(__APPLE__)
+#	include <libproc.h>
+#endif
+
+namespace animia::internal::unix {
+
+/* 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 GetAllPids(std::set<pid_t>& pids) {
+	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 std::vector<int>();
+
+	/* 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);
+		throw std::bad_alloc();
+	}
+
+	/* add pids to our vector */
+	pids.reserve(length / sizeof(*result));
+	for (int i = 0; i < length / sizeof(*result); i++)
+		pids.push_back(result[i].kp_proc.p_pid);
+}
+
+bool GetProcessName(pid_t pid, std::string& result) {
+#ifdef __FreeBSD__
+	struct kinfo_proc* proc = kinfo_getproc(pid);
+	if (!proc)
+		return false;
+	result = proc->ki_comm;
+
+	/* FreeBSD manpage for kinfo_getproc():
+	   "The pointer was obtained by an internal call to malloc(3) and
+	   must be freed by the caller with a call to free(3)." */
+	free(proc);
+
+	return true;
+#elif defined(__APPLE__)
+	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;
+#endif
+}
+
+/* this only works on OS X :( */
+bool EnumerateOpenFiles(const std::set<pid_t>& pids, std::vector<std::tuple<pid_t, std::string>>& files) {
+	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;
+				*/
+
+				files.push_back({pid, vnodeInfo.pvip.vip_path});
+			}
+		}
+	}
+	return true;
+}
+
+} // namespace animia::internal::unix