diff dep/animone/src/util/osx.cc @ 258:862d0d8619f6

*: HUUUGE changes animia has been renamed to animone, so instead of thinking of a health condition, you think of a beautiful flower :) I've also edited some of the code for animone, but I have no idea if it even works or not because I don't have a mac or windows machine lying around. whoops! ... anyway, all of the changes divergent from Anisthesia are now licensed under BSD. it's possible that I could even rewrite most of the code to where I don't even have to keep the MIT license, but that's thinking too far into the future I've been slacking off on implementing the anime seasons page, mostly out of laziness. I think I'd have to create another db file specifically for the seasons anyway, this code is being pushed *primarily* because the hard drive it's on is failing! yay :)
author Paper <paper@paper.us.eu.org>
date Mon, 01 Apr 2024 02:43:44 -0400
parents
children 382b50754fe4
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dep/animone/src/util/osx.cc	Mon Apr 01 02:43:44 2024 -0400
@@ -0,0 +1,196 @@
+#include "animone/util/osx.h"
+
+#include <memory>
+#include <string>
+
+#include <libproc.h>
+#include <sys/sysctl.h>
+
+namespace animone::internal::osx::util {
+
+typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t);
+typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef);
+
+/* retrieved dynamically from launchservices */
+static LSCopyApplicationInformationSpec LSCopyApplicationInformation = nullptr;
+static LSASNCreateWithPidSpec LSASNCreateWithPid = nullptr;
+
+static CFStringRef kLSDisplayNameKey = nullptr;
+static CFStringRef kLSPIDKey = nullptr;
+
+/* retrieved from LaunchServicesSPI.h in WebKit */
+static constexpr int kLSDefaultSessionID = -2;
+
+static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices");
+
+static bool GetLaunchServicesPrivateSymbols() {
+	CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(kLaunchServicesBundleID);
+	if (!launch_services_bundle)
+		return false;
+
+	LSCopyApplicationInformation = reinterpret_cast<LSCopyApplicationInformationSpec>(
+	    CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSCopyApplicationInformation")));
+	if (!LSCopyApplicationInformation)
+		return false;
+
+	LSASNCreateWithPid = reinterpret_cast<LSASNCreateWithPidSpec>(
+	    CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSASNCreateWithPid")));
+	if (!LSASNCreateWithPid)
+		return false;
+
+	CFStringRef* ptr_kLSDisplayNameKey = reinterpret_cast<CFStringRef*>(
+	    CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSDisplayNameKey")));
+	if (!ptr_kLSDisplayNameKey)
+		return false;
+	kLSDisplayNameKey = *ptr_kLSDisplayNameKey;
+
+	CFStringRef* ptr_kLSPIDKey =
+	    reinterpret_cast<CFStringRef*>(CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSPIDKey")));
+	if (!ptr_kLSPIDKey)
+		return false;
+	kLSPIDKey = *ptr_kLSPIDKey;
+
+	return true;
+}
+
+static bool LaunchServicesGetProcessName(pid_t pid, std::string& result) {
+	if (!LSCopyApplicationInformation || !LSASNCreateWithPid || !kLSDisplayNameKey || !kLSPIDKey)
+		if (!GetLaunchServicesPrivateSymbols())
+			return false;
+
+	/* what the hell is an `asn`? */
+	CFPtr<CFTypeRef> asn = LSASNCreateWithPid(kCFAllocatorDefault, pid);
+	if (!asn)
+		return false;
+
+	CFPtr<CFArrayRef> request_array = CFArrayCreate(NULL, (const void**)kLSDisplayNameKey, 1, NULL);
+	if (!request_array)
+		return false;
+
+	CFPtr<CFDictionaryRef> dictionary =
+	    LSCopyApplicationInformation(kLSDefaultSessionID, asn.get(), request_array.get());
+	if (!dictionary)
+		return false;
+
+	{
+		/* this doesn't need to be free'd */
+		CFStringRef rstr;
+
+		if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&rstr) || !rstr)
+			return false;
+
+		if (!StringFromCFString(rstr, result))
+			return false;
+	}
+
+	result.resize(result.find('\0'));
+
+	return true;
+}
+
+bool StringFromCFString(CFStringRef string, std::string& result) {
+	if (!string)
+		return false;
+
+	result.resize(CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1);
+	if (!CFStringGetCString(string, &result.front(), result.length(), kCFStringEncodingUTF8))
+		return false;
+
+	return true;
+}
+
+static bool GetProcessArgs(pid_t pid, std::string& args) {
+	/* sysctl shouldn't touch these, so we define them as 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
+	 *
+	 * NOTE: it IS possible for this value to change inbetween calls to sysctl().
+	 * Unfortunately, I couldn't care less about handling this. :)
+	 *
+	 * is that really true, though? these should be constant values. but are
+	 * argc and argv *really* constant?
+	 */
+	size_t size;
+	{
+		int ret = sysctl((int*)mib, mib_size, nullptr, &size, nullptr, 0);
+		if (ret)
+			return false;
+	}
+
+	/* Reserve the space for it in args */
+	args.resize(size);
+
+	/* Get the contents of argc and argv */
+	{
+		int 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);
+	return true;
+}
+
+static bool GetProcessNameFromArgs(pid_t pid, std::string& result) {
+	if (!GetProcessArgs(pid, result))
+		return false;
+
+	/* Get argc using memcpy */
+	int argc = 0;
+	memcpy(&argc, &result.front(), sizeof(argc));
+
+	/* Do we even have argv[0]? */
+	if (argc < 1)
+		return false;
+
+	/* Find the first null character */
+	size_t null_pos = result.find('\0', sizeof(argc));
+	if (null_pos == std::string::npos)
+		return false;
+
+	/* Find the last slash */
+	size_t last_slash = result.rfind('/', null_pos);
+	if (last_slash == std::string::npos)
+		return false;
+
+	/* Return our result */
+	result = result.substr(last_slash + 1, null_pos - last_slash - 1);
+	return true;
+}
+
+static bool GetProcessNameFromKernel(pid_t pid, std::string& result) {
+	result.resize(2 * MAXCOMLEN);
+
+	int size = proc_name(pid, &result.front(), result.length());
+	if (!size)
+		return false;
+
+	result.resize(size);
+	return true;
+}
+
+bool GetProcessName(pid_t pid, std::string& result) {
+	if (LaunchServicesGetProcessName(pid, result))
+		return true;
+
+	/* 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 (worse, 16 chars if p_name is
+	 * unavailable) */
+	if (GetProcessNameFromKernel(pid, result))
+		return true;
+
+	return false;
+}
+
+} // namespace animone::internal::osx::util