1 //===-- RNBServices.cpp -----------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Created by Christopher Friesen on 3/21/08. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "RNBServices.h" 14 15 #include "CFString.h" 16 #include "DNBLog.h" 17 #include "MacOSX/CFUtils.h" 18 #include <CoreFoundation/CoreFoundation.h> 19 #include <libproc.h> 20 #include <sys/sysctl.h> 21 #include <unistd.h> 22 #include <vector> 23 24 // For now only SpringBoard has a notion of "Applications" that it can list for 25 // us. 26 // So we have to use the SpringBoard API's here. 27 #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) 28 #include <SpringBoardServices/SpringBoardServices.h> 29 #endif 30 31 // From DNB.cpp 32 size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos); 33 34 int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) { 35 if (plistMutableArray == NULL) 36 return -1; 37 38 // Running as root, get all processes 39 std::vector<struct kinfo_proc> proc_infos; 40 const size_t num_proc_infos = GetAllInfos(proc_infos); 41 if (num_proc_infos > 0) { 42 const pid_t our_pid = getpid(); 43 const uid_t our_uid = getuid(); 44 uint32_t i; 45 CFAllocatorRef alloc = kCFAllocatorDefault; 46 47 for (i = 0; i < num_proc_infos; i++) { 48 struct kinfo_proc &proc_info = proc_infos[i]; 49 50 bool kinfo_user_matches; 51 // Special case, if lldb is being run as root we can attach to anything. 52 if (all_users) 53 kinfo_user_matches = true; 54 else 55 kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid; 56 57 const pid_t pid = proc_info.kp_proc.p_pid; 58 // Skip zombie processes and processes with unset status 59 if (!kinfo_user_matches || // User is acceptable 60 pid == our_pid || // Skip this process 61 pid == 0 || // Skip kernel (kernel pid is zero) 62 proc_info.kp_proc.p_stat == 63 SZOMB || // Zombies are bad, they like brains... 64 proc_info.kp_proc.p_flag & P_TRACED || // Being debugged? 65 proc_info.kp_proc.p_flag & P_WEXIT || // Working on exiting? 66 proc_info.kp_proc.p_flag & 67 P_TRANSLATED) // Skip translated ppc (Rosetta) 68 continue; 69 70 // Create a new mutable dictionary for each application 71 CFReleaser<CFMutableDictionaryRef> appInfoDict( 72 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, 73 &kCFTypeDictionaryValueCallBacks)); 74 75 // Get the process id for the app (if there is one) 76 const int32_t pid_int32 = pid; 77 CFReleaser<CFNumberRef> pidCFNumber( 78 ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32)); 79 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY, 80 pidCFNumber.get()); 81 82 // Set a boolean to indicate if this is the front most 83 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, 84 kCFBooleanFalse); 85 86 const char *pid_basename = proc_info.kp_proc.p_comm; 87 char proc_path_buf[PATH_MAX]; 88 89 int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX); 90 if (return_val > 0) { 91 // Okay, now search backwards from that to see if there is a 92 // slash in the name. Note, even though we got all the args we don't 93 // care 94 // because the list data is just a bunch of concatenated null terminated 95 // strings 96 // so strrchr will start from the end of argv0. 97 98 pid_basename = strrchr(proc_path_buf, '/'); 99 if (pid_basename) { 100 // Skip the '/' 101 ++pid_basename; 102 } else { 103 // We didn't find a directory delimiter in the process argv[0], just 104 // use what was in there 105 pid_basename = proc_path_buf; 106 } 107 CFString cf_pid_path(proc_path_buf); 108 if (cf_pid_path.get()) 109 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY, 110 cf_pid_path.get()); 111 } 112 113 if (pid_basename && pid_basename[0]) { 114 CFString pid_name(pid_basename); 115 ::CFDictionarySetValue(appInfoDict.get(), 116 DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get()); 117 } 118 119 // Append the application info to the plist array 120 ::CFArrayAppendValue(plistMutableArray, appInfoDict.get()); 121 } 122 } 123 return 0; 124 } 125 int ListApplications(std::string &plist, bool opt_runningApps, 126 bool opt_debuggable) { 127 int result = -1; 128 129 CFAllocatorRef alloc = kCFAllocatorDefault; 130 131 // Create a mutable array that we can populate. Specify zero so it can be of 132 // any size. 133 CFReleaser<CFMutableArrayRef> plistMutableArray( 134 ::CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks)); 135 136 const uid_t our_uid = getuid(); 137 138 #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) 139 140 if (our_uid == 0) { 141 bool all_users = true; 142 result = GetProcesses(plistMutableArray.get(), all_users); 143 } else { 144 CFReleaser<CFStringRef> sbsFrontAppID( 145 ::SBSCopyFrontmostApplicationDisplayIdentifier()); 146 CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers( 147 opt_runningApps, opt_debuggable)); 148 149 // Need to check the return value from SBSCopyApplicationDisplayIdentifiers. 150 CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount(sbsAppIDs.get()) : 0; 151 CFIndex i = 0; 152 for (i = 0; i < count; i++) { 153 CFStringRef displayIdentifier = 154 (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i); 155 156 // Create a new mutable dictionary for each application 157 CFReleaser<CFMutableDictionaryRef> appInfoDict( 158 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, 159 &kCFTypeDictionaryValueCallBacks)); 160 161 // Get the process id for the app (if there is one) 162 pid_t pid = INVALID_NUB_PROCESS; 163 if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier, 164 &pid) == true) { 165 CFReleaser<CFNumberRef> pidCFNumber( 166 ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid)); 167 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY, 168 pidCFNumber.get()); 169 } 170 171 // Set a boolean to indicate if this is the front most 172 if (sbsFrontAppID.get() && displayIdentifier && 173 (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) == 174 kCFCompareEqualTo)) 175 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, 176 kCFBooleanTrue); 177 else 178 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, 179 kCFBooleanFalse); 180 181 CFReleaser<CFStringRef> executablePath( 182 ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier)); 183 if (executablePath.get() != NULL) { 184 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY, 185 executablePath.get()); 186 } 187 188 CFReleaser<CFStringRef> iconImagePath( 189 ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier)); 190 if (iconImagePath.get() != NULL) { 191 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY, 192 iconImagePath.get()); 193 } 194 195 CFReleaser<CFStringRef> localizedDisplayName( 196 ::SBSCopyLocalizedApplicationNameForDisplayIdentifier( 197 displayIdentifier)); 198 if (localizedDisplayName.get() != NULL) { 199 ::CFDictionarySetValue(appInfoDict.get(), 200 DTSERVICES_APP_DISPLAY_NAME_KEY, 201 localizedDisplayName.get()); 202 } 203 204 // Append the application info to the plist array 205 ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get()); 206 } 207 } 208 #else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) 209 // When root, show all processes 210 bool all_users = (our_uid == 0); 211 GetProcesses(plistMutableArray.get(), all_users); 212 #endif 213 214 CFReleaser<CFDataRef> plistData( 215 ::CFPropertyListCreateXMLData(alloc, plistMutableArray.get())); 216 217 // write plist to service port 218 if (plistData.get() != NULL) { 219 CFIndex size = ::CFDataGetLength(plistData.get()); 220 const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get()); 221 if (bytes != NULL && size > 0) { 222 plist.assign((const char *)bytes, size); 223 return 0; // Success 224 } else { 225 DNBLogError("empty application property list."); 226 result = -2; 227 } 228 } else { 229 DNBLogError("serializing task list."); 230 result = -3; 231 } 232 233 return result; 234 } 235