258
|
1 #include "animone/util/osx.h"
|
|
2
|
|
3 #include <memory>
|
|
4 #include <string>
|
|
5
|
|
6 #include <libproc.h>
|
|
7 #include <sys/sysctl.h>
|
|
8
|
|
9 namespace animone::internal::osx::util {
|
|
10
|
|
11 typedef CFTypeRef (*LSASNCreateWithPidSpec)(CFAllocatorRef, pid_t);
|
|
12 typedef CFDictionaryRef (*LSCopyApplicationInformationSpec)(int, CFTypeRef, CFArrayRef);
|
|
13
|
|
14 /* retrieved dynamically from launchservices */
|
|
15 static LSCopyApplicationInformationSpec LSCopyApplicationInformation = nullptr;
|
|
16 static LSASNCreateWithPidSpec LSASNCreateWithPid = nullptr;
|
|
17
|
|
18 static CFStringRef kLSDisplayNameKey = nullptr;
|
|
19 static CFStringRef kLSPIDKey = nullptr;
|
|
20
|
|
21 /* retrieved from LaunchServicesSPI.h in WebKit */
|
|
22 static constexpr int kLSDefaultSessionID = -2;
|
|
23
|
|
24 static const CFStringRef kLaunchServicesBundleID = CFSTR("com.apple.LaunchServices");
|
|
25
|
|
26 static bool GetLaunchServicesPrivateSymbols() {
|
|
27 CFBundleRef launch_services_bundle = CFBundleGetBundleWithIdentifier(kLaunchServicesBundleID);
|
|
28 if (!launch_services_bundle)
|
|
29 return false;
|
|
30
|
|
31 LSCopyApplicationInformation = reinterpret_cast<LSCopyApplicationInformationSpec>(
|
|
32 CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSCopyApplicationInformation")));
|
|
33 if (!LSCopyApplicationInformation)
|
|
34 return false;
|
|
35
|
|
36 LSASNCreateWithPid = reinterpret_cast<LSASNCreateWithPidSpec>(
|
|
37 CFBundleGetFunctionPointerForName(launch_services_bundle, CFSTR("_LSASNCreateWithPid")));
|
|
38 if (!LSASNCreateWithPid)
|
|
39 return false;
|
|
40
|
|
41 CFStringRef* ptr_kLSDisplayNameKey = reinterpret_cast<CFStringRef*>(
|
|
42 CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSDisplayNameKey")));
|
|
43 if (!ptr_kLSDisplayNameKey)
|
|
44 return false;
|
|
45 kLSDisplayNameKey = *ptr_kLSDisplayNameKey;
|
|
46
|
|
47 CFStringRef* ptr_kLSPIDKey =
|
|
48 reinterpret_cast<CFStringRef*>(CFBundleGetDataPointerForName(launch_services_bundle, CFSTR("_kLSPIDKey")));
|
|
49 if (!ptr_kLSPIDKey)
|
|
50 return false;
|
|
51 kLSPIDKey = *ptr_kLSPIDKey;
|
|
52
|
|
53 return true;
|
|
54 }
|
|
55
|
|
56 static bool LaunchServicesGetProcessName(pid_t pid, std::string& result) {
|
|
57 if (!LSCopyApplicationInformation || !LSASNCreateWithPid || !kLSDisplayNameKey || !kLSPIDKey)
|
|
58 if (!GetLaunchServicesPrivateSymbols())
|
|
59 return false;
|
|
60
|
|
61 /* what the hell is an `asn`? */
|
|
62 CFPtr<CFTypeRef> asn = LSASNCreateWithPid(kCFAllocatorDefault, pid);
|
|
63 if (!asn)
|
|
64 return false;
|
|
65
|
|
66 CFPtr<CFArrayRef> request_array = CFArrayCreate(NULL, (const void**)kLSDisplayNameKey, 1, NULL);
|
|
67 if (!request_array)
|
|
68 return false;
|
|
69
|
|
70 CFPtr<CFDictionaryRef> dictionary =
|
|
71 LSCopyApplicationInformation(kLSDefaultSessionID, asn.get(), request_array.get());
|
|
72 if (!dictionary)
|
|
73 return false;
|
|
74
|
|
75 {
|
|
76 /* this doesn't need to be free'd */
|
|
77 CFStringRef rstr;
|
|
78
|
|
79 if (!CFDictionaryGetValueIfPresent(dictionary, kLSDisplayNameKey, (CFTypeRef*)&rstr) || !rstr)
|
|
80 return false;
|
|
81
|
|
82 if (!StringFromCFString(rstr, result))
|
|
83 return false;
|
|
84 }
|
|
85
|
|
86 result.resize(result.find('\0'));
|
|
87
|
|
88 return true;
|
|
89 }
|
|
90
|
|
91 bool StringFromCFString(CFStringRef string, std::string& result) {
|
|
92 if (!string)
|
|
93 return false;
|
|
94
|
|
95 result.resize(CFStringGetMaximumSizeForEncoding(CFStringGetLength(string), kCFStringEncodingUTF8) + 1);
|
|
96 if (!CFStringGetCString(string, &result.front(), result.length(), kCFStringEncodingUTF8))
|
|
97 return false;
|
|
98
|
|
99 return true;
|
|
100 }
|
|
101
|
|
102 static bool GetProcessArgs(pid_t pid, std::string& args) {
|
|
103 /* sysctl shouldn't touch these, so we define them as const */
|
|
104 int mib[3] = {CTL_KERN, KERN_PROCARGS2, static_cast<int>(pid)};
|
|
105 const size_t mib_size = sizeof(mib) / sizeof(*mib);
|
|
106
|
|
107 /* Get the initial size of the array
|
|
108 *
|
|
109 * NOTE: it IS possible for this value to change inbetween calls to sysctl().
|
|
110 * Unfortunately, I couldn't care less about handling this. :)
|
|
111 *
|
|
112 * is that really true, though? these should be constant values. but are
|
|
113 * argc and argv *really* constant?
|
|
114 */
|
|
115 size_t size;
|
|
116 {
|
|
117 int ret = sysctl((int*)mib, mib_size, nullptr, &size, nullptr, 0);
|
|
118 if (ret)
|
|
119 return false;
|
|
120 }
|
|
121
|
|
122 /* Reserve the space for it in args */
|
|
123 args.resize(size);
|
|
124
|
|
125 /* Get the contents of argc and argv */
|
|
126 {
|
|
127 int ret = sysctl((int*)mib, mib_size, &args.front(), &size, NULL, 0);
|
|
128 if (ret)
|
|
129 return false;
|
|
130 }
|
|
131
|
|
132 /* Is the size big enough to hold at least argc? */
|
|
133 if (size < sizeof(int))
|
|
134 return false;
|
|
135
|
|
136 args.resize(size);
|
|
137 return true;
|
|
138 }
|
|
139
|
|
140 static bool GetProcessNameFromArgs(pid_t pid, std::string& result) {
|
|
141 if (!GetProcessArgs(pid, result))
|
|
142 return false;
|
|
143
|
|
144 /* Get argc using memcpy */
|
|
145 int argc = 0;
|
|
146 memcpy(&argc, &result.front(), sizeof(argc));
|
|
147
|
|
148 /* Do we even have argv[0]? */
|
|
149 if (argc < 1)
|
|
150 return false;
|
|
151
|
|
152 /* Find the first null character */
|
|
153 size_t null_pos = result.find('\0', sizeof(argc));
|
|
154 if (null_pos == std::string::npos)
|
|
155 return false;
|
|
156
|
|
157 /* Find the last slash */
|
|
158 size_t last_slash = result.rfind('/', null_pos);
|
|
159 if (last_slash == std::string::npos)
|
|
160 return false;
|
|
161
|
|
162 /* Return our result */
|
|
163 result = result.substr(last_slash + 1, null_pos - last_slash - 1);
|
|
164 return true;
|
|
165 }
|
|
166
|
|
167 static bool GetProcessNameFromKernel(pid_t pid, std::string& result) {
|
|
168 result.resize(2 * MAXCOMLEN);
|
|
169
|
|
170 int size = proc_name(pid, &result.front(), result.length());
|
|
171 if (!size)
|
|
172 return false;
|
|
173
|
|
174 result.resize(size);
|
|
175 return true;
|
|
176 }
|
|
177
|
|
178 bool GetProcessName(pid_t pid, std::string& result) {
|
|
179 if (LaunchServicesGetProcessName(pid, result))
|
|
180 return true;
|
|
181
|
|
182 /* Try parsing the arguments, this prevents the process name being
|
|
183 * cut off to 2*MAXCOMLEN (32 chars) */
|
|
184 if (GetProcessNameFromArgs(pid, result))
|
|
185 return true;
|
|
186
|
|
187 /* Then attempt getting it from the kernel, which results in the
|
|
188 * process name being cut to 32 chars (worse, 16 chars if p_name is
|
|
189 * unavailable) */
|
|
190 if (GetProcessNameFromKernel(pid, result))
|
|
191 return true;
|
|
192
|
|
193 return false;
|
|
194 }
|
|
195
|
|
196 } // namespace animone::internal::osx::util
|