blob: d0b7e099d23830cb81fcb614524dcd17cc910d8a [file] [log] [blame]
Chris Lattner30fdc8d2010-06-08 16:52:24 +00001//===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// Created by Christopher Friesen on 3/21/08.
11//
12//===----------------------------------------------------------------------===//
13
Bruce Mitchenera026de02015-07-22 17:31:44 +000014#include "RNBServices.h"
Chris Lattner30fdc8d2010-06-08 16:52:24 +000015
Greg Clayton9eb4e032012-11-06 23:36:26 +000016#include "CFString.h"
Bruce Mitchenera026de02015-07-22 17:31:44 +000017#include "DNBLog.h"
Greg Clayton6f35f5c2010-09-09 06:32:46 +000018#include "MacOSX/CFUtils.h"
Kate Stoneb9c1b512016-09-06 20:57:50 +000019#include <CoreFoundation/CoreFoundation.h>
20#include <libproc.h>
21#include <sys/sysctl.h>
22#include <unistd.h>
23#include <vector>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000024
Kate Stoneb9c1b512016-09-06 20:57:50 +000025// For now only SpringBoard has a notion of "Applications" that it can list for
26// us.
Jason Molendaa3329782014-03-29 18:54:20 +000027// So we have to use the SpringBoard API's here.
Kate Stoneb9c1b512016-09-06 20:57:50 +000028#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
Bruce Mitchenera026de02015-07-22 17:31:44 +000029#include <SpringBoardServices/SpringBoardServices.h>
Chris Lattner30fdc8d2010-06-08 16:52:24 +000030#endif
31
Greg Clayton9eb4e032012-11-06 23:36:26 +000032// From DNB.cpp
Kate Stoneb9c1b512016-09-06 20:57:50 +000033size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos);
Greg Clayton9eb4e032012-11-06 23:36:26 +000034
Kate Stoneb9c1b512016-09-06 20:57:50 +000035int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) {
36 if (plistMutableArray == NULL)
37 return -1;
Greg Clayton9eb4e032012-11-06 23:36:26 +000038
Kate Stoneb9c1b512016-09-06 20:57:50 +000039 // Running as root, get all processes
40 std::vector<struct kinfo_proc> proc_infos;
41 const size_t num_proc_infos = GetAllInfos(proc_infos);
42 if (num_proc_infos > 0) {
43 const pid_t our_pid = getpid();
Greg Clayton9eb4e032012-11-06 23:36:26 +000044 const uid_t our_uid = getuid();
Kate Stoneb9c1b512016-09-06 20:57:50 +000045 uint32_t i;
46 CFAllocatorRef alloc = kCFAllocatorDefault;
Chris Lattner30fdc8d2010-06-08 16:52:24 +000047
Kate Stoneb9c1b512016-09-06 20:57:50 +000048 for (i = 0; i < num_proc_infos; i++) {
49 struct kinfo_proc &proc_info = proc_infos[i];
Chris Lattner30fdc8d2010-06-08 16:52:24 +000050
Kate Stoneb9c1b512016-09-06 20:57:50 +000051 bool kinfo_user_matches;
52 // Special case, if lldb is being run as root we can attach to anything.
53 if (all_users)
54 kinfo_user_matches = true;
55 else
56 kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid;
Greg Clayton9eb4e032012-11-06 23:36:26 +000057
Kate Stoneb9c1b512016-09-06 20:57:50 +000058 const pid_t pid = proc_info.kp_proc.p_pid;
59 // Skip zombie processes and processes with unset status
60 if (kinfo_user_matches == false || // User is acceptable
61 pid == our_pid || // Skip this process
62 pid == 0 || // Skip kernel (kernel pid is zero)
63 proc_info.kp_proc.p_stat ==
64 SZOMB || // Zombies are bad, they like brains...
65 proc_info.kp_proc.p_flag & P_TRACED || // Being debugged?
66 proc_info.kp_proc.p_flag & P_WEXIT || // Working on exiting?
67 proc_info.kp_proc.p_flag &
68 P_TRANSLATED) // Skip translated ppc (Rosetta)
69 continue;
Greg Clayton9eb4e032012-11-06 23:36:26 +000070
Kate Stoneb9c1b512016-09-06 20:57:50 +000071 // Create a new mutable dictionary for each application
72 CFReleaser<CFMutableDictionaryRef> appInfoDict(
73 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
74 &kCFTypeDictionaryValueCallBacks));
Greg Clayton9eb4e032012-11-06 23:36:26 +000075
Kate Stoneb9c1b512016-09-06 20:57:50 +000076 // Get the process id for the app (if there is one)
77 const int32_t pid_int32 = pid;
78 CFReleaser<CFNumberRef> pidCFNumber(
79 ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32));
80 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
81 pidCFNumber.get());
Greg Clayton9eb4e032012-11-06 23:36:26 +000082
Kate Stoneb9c1b512016-09-06 20:57:50 +000083 // Set the a boolean to indicate if this is the front most
84 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
85 kCFBooleanFalse);
Greg Clayton9eb4e032012-11-06 23:36:26 +000086
Kate Stoneb9c1b512016-09-06 20:57:50 +000087 const char *pid_basename = proc_info.kp_proc.p_comm;
88 char proc_path_buf[PATH_MAX];
Greg Clayton9eb4e032012-11-06 23:36:26 +000089
Kate Stoneb9c1b512016-09-06 20:57:50 +000090 int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX);
91 if (return_val > 0) {
92 // Okay, now search backwards from that to see if there is a
93 // slash in the name. Note, even though we got all the args we don't
94 // care
95 // because the list data is just a bunch of concatenated null terminated
96 // strings
97 // so strrchr will start from the end of argv0.
Greg Clayton9eb4e032012-11-06 23:36:26 +000098
Kate Stoneb9c1b512016-09-06 20:57:50 +000099 pid_basename = strrchr(proc_path_buf, '/');
100 if (pid_basename) {
101 // Skip the '/'
102 ++pid_basename;
103 } else {
104 // We didn't find a directory delimiter in the process argv[0], just
105 // use what was in there
106 pid_basename = proc_path_buf;
Greg Clayton9eb4e032012-11-06 23:36:26 +0000107 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000108 CFString cf_pid_path(proc_path_buf);
109 if (cf_pid_path.get())
110 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
111 cf_pid_path.get());
112 }
113
114 if (pid_basename && pid_basename[0]) {
115 CFString pid_name(pid_basename);
116 ::CFDictionarySetValue(appInfoDict.get(),
117 DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get());
118 }
119
120 // Append the application info to the plist array
121 ::CFArrayAppendValue(plistMutableArray, appInfoDict.get());
Greg Clayton9eb4e032012-11-06 23:36:26 +0000122 }
Kate Stoneb9c1b512016-09-06 20:57:50 +0000123 }
124 return 0;
125}
126int ListApplications(std::string &plist, bool opt_runningApps,
127 bool opt_debuggable) {
128 int result = -1;
129
130 CFAllocatorRef alloc = kCFAllocatorDefault;
131
132 // Create a mutable array that we can populate. Specify zero so it can be of
133 // any size.
134 CFReleaser<CFMutableArrayRef> plistMutableArray(
135 ::CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks));
136
137 const uid_t our_uid = getuid();
138
139#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
140
141 if (our_uid == 0) {
142 bool all_users = true;
143 result = GetProcesses(plistMutableArray.get(), all_users);
144 } else {
145 CFReleaser<CFStringRef> sbsFrontAppID(
146 ::SBSCopyFrontmostApplicationDisplayIdentifier());
147 CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers(
148 opt_runningApps, opt_debuggable));
149
150 // Need to check the return value from SBSCopyApplicationDisplayIdentifiers.
151 CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount(sbsAppIDs.get()) : 0;
152 CFIndex i = 0;
153 for (i = 0; i < count; i++) {
154 CFStringRef displayIdentifier =
155 (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i);
156
157 // Create a new mutable dictionary for each application
158 CFReleaser<CFMutableDictionaryRef> appInfoDict(
159 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
160 &kCFTypeDictionaryValueCallBacks));
161
162 // Get the process id for the app (if there is one)
163 pid_t pid = INVALID_NUB_PROCESS;
164 if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier,
165 &pid) == true) {
166 CFReleaser<CFNumberRef> pidCFNumber(
167 ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid));
168 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
169 pidCFNumber.get());
170 }
171
172 // Set the a boolean to indicate if this is the front most
173 if (sbsFrontAppID.get() && displayIdentifier &&
174 (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) ==
175 kCFCompareEqualTo))
176 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
177 kCFBooleanTrue);
178 else
179 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
180 kCFBooleanFalse);
181
182 CFReleaser<CFStringRef> executablePath(
183 ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier));
184 if (executablePath.get() != NULL) {
185 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
186 executablePath.get());
187 }
188
189 CFReleaser<CFStringRef> iconImagePath(
190 ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier));
191 if (iconImagePath.get() != NULL) {
192 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY,
193 iconImagePath.get());
194 }
195
196 CFReleaser<CFStringRef> localizedDisplayName(
197 ::SBSCopyLocalizedApplicationNameForDisplayIdentifier(
198 displayIdentifier));
199 if (localizedDisplayName.get() != NULL) {
200 ::CFDictionarySetValue(appInfoDict.get(),
201 DTSERVICES_APP_DISPLAY_NAME_KEY,
202 localizedDisplayName.get());
203 }
204
205 // Append the application info to the plist array
206 ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get());
207 }
208 }
Jason Molendaa3329782014-03-29 18:54:20 +0000209#else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
Kate Stoneb9c1b512016-09-06 20:57:50 +0000210 // When root, show all processes
211 bool all_users = (our_uid == 0);
212 GetProcesses(plistMutableArray.get(), all_users);
Greg Clayton9eb4e032012-11-06 23:36:26 +0000213#endif
Greg Clayton9eb4e032012-11-06 23:36:26 +0000214
Kate Stoneb9c1b512016-09-06 20:57:50 +0000215 CFReleaser<CFDataRef> plistData(
216 ::CFPropertyListCreateXMLData(alloc, plistMutableArray.get()));
217
218 // write plist to service port
219 if (plistData.get() != NULL) {
220 CFIndex size = ::CFDataGetLength(plistData.get());
221 const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get());
222 if (bytes != NULL && size > 0) {
223 plist.assign((char *)bytes, size);
224 return 0; // Success
225 } else {
226 DNBLogError("empty application property list.");
227 result = -2;
228 }
229 } else {
230 DNBLogError("serializing task list.");
231 result = -3;
232 }
233
234 return result;
Chris Lattner30fdc8d2010-06-08 16:52:24 +0000235}