diff dep/animia/src/fd/xnu.cc @ 160:900b5b530883

dep/animia: fd/xnu: use path args to get executable filename
author Paper <mrpapersonic@gmail.com>
date Fri, 17 Nov 2023 12:37:31 -0500
parents 18c8eb5d1edc
children 61b76c7b656a
line wrap: on
line diff
--- a/dep/animia/src/fd/xnu.cc	Thu Nov 16 16:51:34 2023 -0500
+++ b/dep/animia/src/fd/xnu.cc	Fri Nov 17 12:37:31 2023 -0500
@@ -9,6 +9,7 @@
 #include <string>
 #include <unordered_map>
 #include <vector>
+#include <memory>
 
 #include <fcntl.h>
 #include <libproc.h>
@@ -16,27 +17,99 @@
 #include <sys/types.h>
 #include <sys/user.h>
 
+#include <iostream>
+
 namespace animia::internal::xnu {
 
-static std::string GetProcessName(pid_t pid) {
-	char name[2*MAXCOMLEN];
-	proc_name(pid, name, sizeof(name));
+static bool GetProcessNameFromArgs(pid_t pid, std::string& result) {
+	/* sysctl shouldn't touch these, so we define them as const */
+	const int mib[3] = {CTL_KERN, KERN_PROCARGS2, static_cast<int>(pid)};
+	const size_t mib_size = sizeof(mib)/sizeof(*mib);
+
+	/* Get the initial size of the array */
+	size_t size;
+	int ret = sysctl((int*)mib, mib_size, nullptr, &size, nullptr, 0);
+	if (ret)
+		return false;
+
+	/* Reserve the space for it in a std::string */
+	std::string args;
+	args.resize(size);
+
+	/* Get the contents of argc and argv */
+	ret = sysctl((int*)mib, mib_size, &args.front(), &size, NULL, 0);
+	if (ret)
+		return false;
+
+	/* Is the size big enough to hold at least argc? */
+	if (size < sizeof(int))
+		return false;
+
+	args.resize(size);
+
+	/* Get argc using memcpy */
+	int argc;
+	memcpy(&argc, &args.front(), sizeof(argc));
+
+	/* Check for a condition that, realistically, would never happen,
+	   but better to be safe than sorry */
+	if (argc < 1)
+		return false;
 
-	return name;
+	/* Find the first null character */
+	size_t null_pos = args.find('\0', sizeof(argc));
+	if (null_pos == std::string::npos)
+		return false;
+
+	/* Find the last slash */
+	size_t last_slash = args.rfind('/', null_pos);
+	if (last_slash == std::string::npos)
+		return false;
+
+	/* Return our result */
+	result = args.substr(last_slash + 1, null_pos - last_slash - 1);
+	return true;
+}
+
+static bool GetProcessNameFromKernel(pid_t pid, std::string& result) {
+	result.reserve(2*MAXCOMLEN);
+	if (!proc_name(pid, &result.front(), result.length()))
+		return false;
+
+	result.shrink_to_fit();
+	return true;
+}
+
+static bool GetProcessName(pid_t pid, std::string& result) {
+	/* First try parsing the arguments, this prevents the process name being
+	   cut off to 2*MAXCOMLEN (32 chars) */
+	if (GetProcessNameFromArgs(pid, result))
+		return true;
+
+	/* Then attempt getting it from the kernel, which results in the process name
+	   being cut to 32 chars (16 chars if p_name is unavailable) */
+	if (GetProcessNameFromKernel(pid, result))
+		return true;
+
+	return false;
 }
 
 bool XnuFdTools::EnumerateOpenProcesses(process_proc_t process_proc) {
-	std::vector<pid_t> pids;
-	pids.reserve(1024);
+	size_t pids_size = 512;
+	std::unique_ptr<pid_t[]> pids;
 
-	for (int returned_size = pids.capacity(); pids.capacity() > returned_size; pids.reserve(pids.capacity() * 2)) {
-		returned_size = proc_listpids(PROC_ALL_PIDS, 0, pids.data(), pids.capacity() * sizeof(pid_t));
+	int returned_size = 0;
+	do {
+		pids.reset(new pid_t[pids_size]);
+		returned_size = proc_listpids(PROC_ALL_PIDS, 0, pids.get(), pids_size * sizeof(pid_t));
 		if (returned_size == -1)
 			return false;
-	}
+	} while ((pids_size * sizeof(size_t)) < returned_size);
 
-	for (const auto& pid : pids) {
-		if (!process_proc({pid, GetProcessName(pid)}))
+	for (int i = 0; i < pids_size; i++) {
+		std::string result;
+		GetProcessName(pids[i], result);
+		if (!process_proc({pids[i], result}))
 			return false;
 	}