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 ) 67 continue; 68 69 // Create a new mutable dictionary for each application 70 CFReleaser<CFMutableDictionaryRef> appInfoDict( 71 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, 72 &kCFTypeDictionaryValueCallBacks)); 73 74 // Get the process id for the app (if there is one) 75 const int32_t pid_int32 = pid; 76 CFReleaser<CFNumberRef> pidCFNumber( 77 ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32)); 78 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY, 79 pidCFNumber.get()); 80 81 // Set a boolean to indicate if this is the front most 82 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, 83 kCFBooleanFalse); 84 85 const char *pid_basename = proc_info.kp_proc.p_comm; 86 char proc_path_buf[PATH_MAX]; 87 88 int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX); 89 if (return_val > 0) { 90 // Okay, now search backwards from that to see if there is a 91 // slash in the name. Note, even though we got all the args we don't 92 // care 93 // because the list data is just a bunch of concatenated null terminated 94 // strings 95 // so strrchr will start from the end of argv0. 96 97 pid_basename = strrchr(proc_path_buf, '/'); 98 if (pid_basename) { 99 // Skip the '/' 100 ++pid_basename; 101 } else { 102 // We didn't find a directory delimiter in the process argv[0], just 103 // use what was in there 104 pid_basename = proc_path_buf; 105 } 106 CFString cf_pid_path(proc_path_buf); 107 if (cf_pid_path.get()) 108 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY, 109 cf_pid_path.get()); 110 } 111 112 if (pid_basename && pid_basename[0]) { 113 CFString pid_name(pid_basename); 114 ::CFDictionarySetValue(appInfoDict.get(), 115 DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get()); 116 } 117 118 // Append the application info to the plist array 119 ::CFArrayAppendValue(plistMutableArray, appInfoDict.get()); 120 } 121 } 122 return 0; 123 } 124 int ListApplications(std::string &plist, bool opt_runningApps, 125 bool opt_debuggable) { 126 int result = -1; 127 128 CFAllocatorRef alloc = kCFAllocatorDefault; 129 130 // Create a mutable array that we can populate. Specify zero so it can be of 131 // any size. 132 CFReleaser<CFMutableArrayRef> plistMutableArray( 133 ::CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks)); 134 135 const uid_t our_uid = getuid(); 136 137 #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) 138 139 if (our_uid == 0) { 140 bool all_users = true; 141 result = GetProcesses(plistMutableArray.get(), all_users); 142 } else { 143 CFReleaser<CFStringRef> sbsFrontAppID( 144 ::SBSCopyFrontmostApplicationDisplayIdentifier()); 145 CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers( 146 opt_runningApps, opt_debuggable)); 147 148 // Need to check the return value from SBSCopyApplicationDisplayIdentifiers. 149 CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount(sbsAppIDs.get()) : 0; 150 CFIndex i = 0; 151 for (i = 0; i < count; i++) { 152 CFStringRef displayIdentifier = 153 (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i); 154 155 // Create a new mutable dictionary for each application 156 CFReleaser<CFMutableDictionaryRef> appInfoDict( 157 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, 158 &kCFTypeDictionaryValueCallBacks)); 159 160 // Get the process id for the app (if there is one) 161 pid_t pid = INVALID_NUB_PROCESS; 162 if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier, 163 &pid) == true) { 164 CFReleaser<CFNumberRef> pidCFNumber( 165 ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid)); 166 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY, 167 pidCFNumber.get()); 168 } 169 170 // Set a boolean to indicate if this is the front most 171 if (sbsFrontAppID.get() && displayIdentifier && 172 (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) == 173 kCFCompareEqualTo)) 174 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, 175 kCFBooleanTrue); 176 else 177 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, 178 kCFBooleanFalse); 179 180 CFReleaser<CFStringRef> executablePath( 181 ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier)); 182 if (executablePath.get() != NULL) { 183 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY, 184 executablePath.get()); 185 } 186 187 CFReleaser<CFStringRef> iconImagePath( 188 ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier)); 189 if (iconImagePath.get() != NULL) { 190 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY, 191 iconImagePath.get()); 192 } 193 194 CFReleaser<CFStringRef> localizedDisplayName( 195 ::SBSCopyLocalizedApplicationNameForDisplayIdentifier( 196 displayIdentifier)); 197 if (localizedDisplayName.get() != NULL) { 198 ::CFDictionarySetValue(appInfoDict.get(), 199 DTSERVICES_APP_DISPLAY_NAME_KEY, 200 localizedDisplayName.get()); 201 } 202 203 // Append the application info to the plist array 204 ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get()); 205 } 206 } 207 #else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) 208 // When root, show all processes 209 bool all_users = (our_uid == 0); 210 GetProcesses(plistMutableArray.get(), all_users); 211 #endif 212 213 CFReleaser<CFDataRef> plistData( 214 ::CFPropertyListCreateXMLData(alloc, plistMutableArray.get())); 215 216 // write plist to service port 217 if (plistData.get() != NULL) { 218 CFIndex size = ::CFDataGetLength(plistData.get()); 219 const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get()); 220 if (bytes != NULL && size > 0) { 221 plist.assign((const char *)bytes, size); 222 return 0; // Success 223 } else { 224 DNBLogError("empty application property list."); 225 result = -2; 226 } 227 } else { 228 DNBLogError("serializing task list."); 229 result = -3; 230 } 231 232 return result; 233 } 234