comparison dep/animia/src/util/osx.cc @ 163:44c5e6dd9488

dep/animia/osx: move GetProcessName to util/osx so we can use it in quartz
author Paper <mrpapersonic@gmail.com>
date Sat, 18 Nov 2023 00:47:40 -0500
parents 61b76c7b656a
children 8937fb7f2d66
comparison
equal deleted inserted replaced
162:61b76c7b656a 163:44c5e6dd9488
1 /* A wrapper around multiple LaunchServices things */
2
3 #include "animia/util/osx.h" 1 #include "animia/util/osx.h"
4 2
3 #ifdef HAVE_COREFOUNDATION
5 #include <CoreFoundation/CoreFoundation.h> 4 #include <CoreFoundation/CoreFoundation.h>
6 5 #endif
7 #include <iostream>
8 6
9 namespace animia::internal::osx::util { 7 namespace animia::internal::osx::util {
10 8
11 #define RDSymbolNameStr(symbol) (CFSTR("_"#symbol)) 9 #ifdef HAVE_COREFOUNDATION
10 /* All of these LaunchServices things use *internal functions* that are subject
11 ** to change. Granted, it's not very likely that these will change very much
12 ** because I'm fairly sure Apple uses them lots in their own internal code.
13 */
12 14
13 static constexpr int kLaunchServicesMagicConstant = -2; // or -1, dunno the difference 15 /* from RDProcess */
14
15 typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t); 16 typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t);
16 typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef); 17 typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef);
17 18
18 static LSCopyApplicationInformationSpec LSCopyApplicationInformation = nullptr; 19 static LSCopyApplicationInformationSpec LSCopyApplicationInformation = nullptr;
19 static LSASNCreateWithPidSpec LSASNCreateWithPid = nullptr; 20 static LSASNCreateWithPidSpec LSASNCreateWithPid = nullptr;
20 21
21 static CFStringRef (kLSDisplayNameKey) = nullptr; 22 /* retrieved from LaunchServicesSPI.h in WebKit */
22 static CFStringRef (kLSPIDKey) = nullptr; 23 static constexpr int kLSDefaultSessionID = -2;
24 static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices");
23 25
24 static CFStringRef (kLaunchServicesBundleID) = CFSTR("com.apple.LaunchServices"); 26 /* retrieved dynamically */
27 static CFStringRef kLSDisplayNameKey = nullptr;
28 static CFStringRef kLSPIDKey = nullptr;
25 29
26 static bool FindLaunchServicesPrivateSymbols() { 30 static bool GetLaunchServicesPrivateSymbols() {
27 CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(kLaunchServicesBundleID); 31 CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(kLaunchServicesBundleID);
28 if (!launch_services_bundle) 32 if (!launch_services_bundle)
29 return false; 33 return false;
30 34
31 LSCopyApplicationInformation = (LSCopyApplicationInformationSpec)CFBundleGetFunctionPointerForName(launch_services_bundle, RDSymbolNameStr(LSCopyApplicationInformation)); 35 LSCopyApplicationInformation = (LSCopyApplicationInformationSpec)CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSCopyApplicationInformation"));
32 if (!LSCopyApplicationInformation) 36 if (!LSCopyApplicationInformation)
33 return false; 37 return false;
34 38
35 LSASNCreateWithPid = (LSASNCreateWithPidSpec)CFBundleGetFunctionPointerForName(launch_services_bundle, RDSymbolNameStr(LSASNCreateWithPid)); 39 LSASNCreateWithPid = (LSASNCreateWithPidSpec)CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSASNCreateWithPid"));
36 if (!LSASNCreateWithPid) 40 if (!LSASNCreateWithPid)
37 return false; 41 return false;
38 42
39 kLSDisplayNameKey = *(CFStringRef*)CFBundleGetDataPointerForName(launch_services_bundle, RDSymbolNameStr(kLSDisplayNameKey)); 43 kLSDisplayNameKey = *(CFStringRef*)CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSDisplayNameKey"));
40 if (!kLSDisplayNameKey) 44 if (!kLSDisplayNameKey)
41 return false; 45 return false;
42 46
43 kLSPIDKey = *(CFStringRef*)CFBundleGetDataPointerForName(launch_services_bundle, RDSymbolNameStr(kLSPIDKey)); 47 kLSPIDKey = *(CFStringRef*)CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSPIDKey"));
44 if (!kLSPIDKey) 48 if (!kLSPIDKey)
45 return false; 49 return false;
46 50
47 return true; 51 return true;
48 } 52 }
49 53
50 bool LaunchServicesGetProcessName(pid_t pid, std::string& result) { 54 static bool LaunchServicesGetProcessName(pid_t pid, std::string& result) {
51 if (!LSCopyApplicationInformation || !LSASNCreateWithPid) 55 if (!LSCopyApplicationInformation || !LSASNCreateWithPid)
52 if (!FindLaunchServicesPrivateSymbols()) 56 if (!GetLaunchServicesPrivateSymbols())
53 return false; 57 return false;
54 58
55 CFTypeRef asn = LSASNCreateWithPid(kCFAllocatorDefault, pid); 59 CFTypeRef asn = LSASNCreateWithPid(kCFAllocatorDefault, pid);
56 60
57 CFArrayRef request_array = CFArrayCreate(NULL, (const void **)kLSDisplayNameKey, 1, NULL); 61 CFArrayRef request_array = CFArrayCreate(NULL, (const void **)kLSDisplayNameKey, 1, NULL);
80 84
81 CFRelease(str); 85 CFRelease(str);
82 86
83 return true; 87 return true;
84 } 88 }
89 #endif // HAVE_COREFOUNDATION
90
91 static bool GetProcessArgs(pid_t pid, std::string& args) {
92 /* sysctl shouldn't touch these, so we define them as const */
93 const int mib[3] = {CTL_KERN, KERN_PROCARGS2, static_cast<int>(pid)};
94 const size_t mib_size = sizeof(mib)/sizeof(*mib);
95
96 /* Get the initial size of the array */
97 size_t size;
98 {
99 int ret = sysctl((int*)mib, mib_size, nullptr, &size, nullptr, 0);
100 if (ret)
101 return false;
102 }
103
104 /* Reserve the space for it in args */
105 args.resize(size);
106
107 /* Get the contents of argc and argv */
108 {
109 int ret = sysctl((int*)mib, mib_size, &args.front(), &size, NULL, 0);
110 if (ret)
111 return false;
112 }
113
114 /* Is the size big enough to hold at least argc? */
115 if (size < sizeof(int))
116 return false;
117
118 args.resize(size);
119 }
120
121 static bool GetProcessNameFromArgs(pid_t pid, std::string& result) {
122 if (!GetProcessArgs(pid, result))
123 return false;
124
125 /* Get argc using memcpy */
126 int argc;
127 memcpy(&argc, &args.front(), sizeof(argc));
128
129 /* Do we even have argv[0]? */
130 if (argc < 1)
131 return false;
132
133 /* Find the first null character */
134 size_t null_pos = args.find('\0', sizeof(argc));
135 if (null_pos == std::string::npos)
136 return false;
137
138 /* Find the last slash */
139 size_t last_slash = args.rfind('/', null_pos);
140 if (last_slash == std::string::npos)
141 return false;
142
143 /* Return our result */
144 result = args.substr(last_slash + 1, null_pos - last_slash - 1);
145 return true;
146 }
147
148 static bool GetProcessNameFromKernel(pid_t pid, std::string& result) {
149 result.reserve(2*MAXCOMLEN);
150 if (!proc_name(pid, &result.front(), result.length()))
151 return false;
152
153 result.shrink_to_fit();
154 return true;
155 }
156
157 static bool GetProcessName(pid_t pid, std::string& result) {
158 #ifdef HAVE_COREFOUNDATION
159 if (LaunchServicesGetProcessName(pid, result))
160 return true;
161 #endif // HAVE_COREFOUNDATION
162
163 /* Try parsing the arguments, this prevents the process name being
164 cut off to 2*MAXCOMLEN (32 chars) */
165 if (GetProcessNameFromArgs(pid, result))
166 return true;
167
168 /* Then attempt getting it from the kernel, which results in the
169 process name being cut to 32 chars (worse, 16 chars if p_name is
170 unavailable) */
171 if (GetProcessNameFromKernel(pid, result))
172 return true;
173
174 return false;
175 }
85 176
86 } 177 }