1061da546Spatrick //===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick // Created by Christopher Friesen on 3/21/08.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick
13061da546Spatrick #include "RNBServices.h"
14061da546Spatrick
15*dda28197Spatrick #include "DNB.h"
16061da546Spatrick #include "CFString.h"
17061da546Spatrick #include "DNBLog.h"
18061da546Spatrick #include "MacOSX/CFUtils.h"
19061da546Spatrick #include <CoreFoundation/CoreFoundation.h>
20061da546Spatrick #include <libproc.h>
21061da546Spatrick #include <sys/sysctl.h>
22061da546Spatrick #include <unistd.h>
23061da546Spatrick #include <vector>
24061da546Spatrick
25061da546Spatrick // For now only SpringBoard has a notion of "Applications" that it can list for
26061da546Spatrick // us.
27061da546Spatrick // So we have to use the SpringBoard API's here.
28061da546Spatrick #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
29061da546Spatrick #include <SpringBoardServices/SpringBoardServices.h>
30061da546Spatrick #endif
31061da546Spatrick
GetProcesses(CFMutableArrayRef plistMutableArray,bool all_users)32061da546Spatrick int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) {
33061da546Spatrick if (plistMutableArray == NULL)
34061da546Spatrick return -1;
35061da546Spatrick
36061da546Spatrick // Running as root, get all processes
37061da546Spatrick std::vector<struct kinfo_proc> proc_infos;
38*dda28197Spatrick const size_t num_proc_infos = DNBGetAllInfos(proc_infos);
39061da546Spatrick if (num_proc_infos > 0) {
40061da546Spatrick const pid_t our_pid = getpid();
41061da546Spatrick const uid_t our_uid = getuid();
42061da546Spatrick uint32_t i;
43061da546Spatrick CFAllocatorRef alloc = kCFAllocatorDefault;
44061da546Spatrick
45061da546Spatrick for (i = 0; i < num_proc_infos; i++) {
46061da546Spatrick struct kinfo_proc &proc_info = proc_infos[i];
47061da546Spatrick
48061da546Spatrick bool kinfo_user_matches;
49061da546Spatrick // Special case, if lldb is being run as root we can attach to anything.
50061da546Spatrick if (all_users)
51061da546Spatrick kinfo_user_matches = true;
52061da546Spatrick else
53061da546Spatrick kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid;
54061da546Spatrick
55061da546Spatrick const pid_t pid = proc_info.kp_proc.p_pid;
56061da546Spatrick // Skip zombie processes and processes with unset status
57061da546Spatrick if (!kinfo_user_matches || // User is acceptable
58061da546Spatrick pid == our_pid || // Skip this process
59061da546Spatrick pid == 0 || // Skip kernel (kernel pid is zero)
60061da546Spatrick proc_info.kp_proc.p_stat ==
61061da546Spatrick SZOMB || // Zombies are bad, they like brains...
62061da546Spatrick proc_info.kp_proc.p_flag & P_TRACED || // Being debugged?
63*dda28197Spatrick proc_info.kp_proc.p_flag & P_WEXIT // Working on exiting?
64*dda28197Spatrick )
65061da546Spatrick continue;
66061da546Spatrick
67061da546Spatrick // Create a new mutable dictionary for each application
68061da546Spatrick CFReleaser<CFMutableDictionaryRef> appInfoDict(
69061da546Spatrick ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
70061da546Spatrick &kCFTypeDictionaryValueCallBacks));
71061da546Spatrick
72061da546Spatrick // Get the process id for the app (if there is one)
73061da546Spatrick const int32_t pid_int32 = pid;
74061da546Spatrick CFReleaser<CFNumberRef> pidCFNumber(
75061da546Spatrick ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32));
76061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
77061da546Spatrick pidCFNumber.get());
78061da546Spatrick
79061da546Spatrick // Set a boolean to indicate if this is the front most
80061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
81061da546Spatrick kCFBooleanFalse);
82061da546Spatrick
83061da546Spatrick const char *pid_basename = proc_info.kp_proc.p_comm;
84061da546Spatrick char proc_path_buf[PATH_MAX];
85061da546Spatrick
86061da546Spatrick int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX);
87061da546Spatrick if (return_val > 0) {
88061da546Spatrick // Okay, now search backwards from that to see if there is a
89061da546Spatrick // slash in the name. Note, even though we got all the args we don't
90061da546Spatrick // care
91061da546Spatrick // because the list data is just a bunch of concatenated null terminated
92061da546Spatrick // strings
93061da546Spatrick // so strrchr will start from the end of argv0.
94061da546Spatrick
95061da546Spatrick pid_basename = strrchr(proc_path_buf, '/');
96061da546Spatrick if (pid_basename) {
97061da546Spatrick // Skip the '/'
98061da546Spatrick ++pid_basename;
99061da546Spatrick } else {
100061da546Spatrick // We didn't find a directory delimiter in the process argv[0], just
101061da546Spatrick // use what was in there
102061da546Spatrick pid_basename = proc_path_buf;
103061da546Spatrick }
104061da546Spatrick CFString cf_pid_path(proc_path_buf);
105061da546Spatrick if (cf_pid_path.get())
106061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
107061da546Spatrick cf_pid_path.get());
108061da546Spatrick }
109061da546Spatrick
110061da546Spatrick if (pid_basename && pid_basename[0]) {
111061da546Spatrick CFString pid_name(pid_basename);
112061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(),
113061da546Spatrick DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get());
114061da546Spatrick }
115061da546Spatrick
116061da546Spatrick // Append the application info to the plist array
117061da546Spatrick ::CFArrayAppendValue(plistMutableArray, appInfoDict.get());
118061da546Spatrick }
119061da546Spatrick }
120061da546Spatrick return 0;
121061da546Spatrick }
ListApplications(std::string & plist,bool opt_runningApps,bool opt_debuggable)122061da546Spatrick int ListApplications(std::string &plist, bool opt_runningApps,
123061da546Spatrick bool opt_debuggable) {
124061da546Spatrick int result = -1;
125061da546Spatrick
126061da546Spatrick CFAllocatorRef alloc = kCFAllocatorDefault;
127061da546Spatrick
128061da546Spatrick // Create a mutable array that we can populate. Specify zero so it can be of
129061da546Spatrick // any size.
130061da546Spatrick CFReleaser<CFMutableArrayRef> plistMutableArray(
131061da546Spatrick ::CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks));
132061da546Spatrick
133061da546Spatrick const uid_t our_uid = getuid();
134061da546Spatrick
135061da546Spatrick #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
136061da546Spatrick
137061da546Spatrick if (our_uid == 0) {
138061da546Spatrick bool all_users = true;
139061da546Spatrick result = GetProcesses(plistMutableArray.get(), all_users);
140061da546Spatrick } else {
141061da546Spatrick CFReleaser<CFStringRef> sbsFrontAppID(
142061da546Spatrick ::SBSCopyFrontmostApplicationDisplayIdentifier());
143061da546Spatrick CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers(
144061da546Spatrick opt_runningApps, opt_debuggable));
145061da546Spatrick
146061da546Spatrick // Need to check the return value from SBSCopyApplicationDisplayIdentifiers.
147061da546Spatrick CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount(sbsAppIDs.get()) : 0;
148061da546Spatrick CFIndex i = 0;
149061da546Spatrick for (i = 0; i < count; i++) {
150061da546Spatrick CFStringRef displayIdentifier =
151061da546Spatrick (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i);
152061da546Spatrick
153061da546Spatrick // Create a new mutable dictionary for each application
154061da546Spatrick CFReleaser<CFMutableDictionaryRef> appInfoDict(
155061da546Spatrick ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
156061da546Spatrick &kCFTypeDictionaryValueCallBacks));
157061da546Spatrick
158061da546Spatrick // Get the process id for the app (if there is one)
159061da546Spatrick pid_t pid = INVALID_NUB_PROCESS;
160061da546Spatrick if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier,
161061da546Spatrick &pid) == true) {
162061da546Spatrick CFReleaser<CFNumberRef> pidCFNumber(
163061da546Spatrick ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid));
164061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
165061da546Spatrick pidCFNumber.get());
166061da546Spatrick }
167061da546Spatrick
168061da546Spatrick // Set a boolean to indicate if this is the front most
169061da546Spatrick if (sbsFrontAppID.get() && displayIdentifier &&
170061da546Spatrick (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) ==
171061da546Spatrick kCFCompareEqualTo))
172061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
173061da546Spatrick kCFBooleanTrue);
174061da546Spatrick else
175061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
176061da546Spatrick kCFBooleanFalse);
177061da546Spatrick
178061da546Spatrick CFReleaser<CFStringRef> executablePath(
179061da546Spatrick ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier));
180061da546Spatrick if (executablePath.get() != NULL) {
181061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
182061da546Spatrick executablePath.get());
183061da546Spatrick }
184061da546Spatrick
185061da546Spatrick CFReleaser<CFStringRef> iconImagePath(
186061da546Spatrick ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier));
187061da546Spatrick if (iconImagePath.get() != NULL) {
188061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY,
189061da546Spatrick iconImagePath.get());
190061da546Spatrick }
191061da546Spatrick
192061da546Spatrick CFReleaser<CFStringRef> localizedDisplayName(
193061da546Spatrick ::SBSCopyLocalizedApplicationNameForDisplayIdentifier(
194061da546Spatrick displayIdentifier));
195061da546Spatrick if (localizedDisplayName.get() != NULL) {
196061da546Spatrick ::CFDictionarySetValue(appInfoDict.get(),
197061da546Spatrick DTSERVICES_APP_DISPLAY_NAME_KEY,
198061da546Spatrick localizedDisplayName.get());
199061da546Spatrick }
200061da546Spatrick
201061da546Spatrick // Append the application info to the plist array
202061da546Spatrick ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get());
203061da546Spatrick }
204061da546Spatrick }
205061da546Spatrick #else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
206061da546Spatrick // When root, show all processes
207061da546Spatrick bool all_users = (our_uid == 0);
208061da546Spatrick GetProcesses(plistMutableArray.get(), all_users);
209061da546Spatrick #endif
210061da546Spatrick
211061da546Spatrick CFReleaser<CFDataRef> plistData(
212061da546Spatrick ::CFPropertyListCreateXMLData(alloc, plistMutableArray.get()));
213061da546Spatrick
214061da546Spatrick // write plist to service port
215061da546Spatrick if (plistData.get() != NULL) {
216061da546Spatrick CFIndex size = ::CFDataGetLength(plistData.get());
217061da546Spatrick const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get());
218061da546Spatrick if (bytes != NULL && size > 0) {
219061da546Spatrick plist.assign((const char *)bytes, size);
220061da546Spatrick return 0; // Success
221061da546Spatrick } else {
222061da546Spatrick DNBLogError("empty application property list.");
223061da546Spatrick result = -2;
224061da546Spatrick }
225061da546Spatrick } else {
226061da546Spatrick DNBLogError("serializing task list.");
227061da546Spatrick result = -3;
228061da546Spatrick }
229061da546Spatrick
230061da546Spatrick return result;
231061da546Spatrick }
232