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