1 //===-- 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 14 #include "RNBServices.h" 15 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 // From DNB.cpp 33 size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos); 34 35 int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) { 36 if (plistMutableArray == NULL) 37 return -1; 38 39 // 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(); 44 const uid_t our_uid = getuid(); 45 uint32_t i; 46 CFAllocatorRef alloc = kCFAllocatorDefault; 47 48 for (i = 0; i < num_proc_infos; i++) { 49 struct kinfo_proc &proc_info = proc_infos[i]; 50 51 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; 57 58 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; 70 71 // Create a new mutable dictionary for each application 72 CFReleaser<CFMutableDictionaryRef> appInfoDict( 73 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, 74 &kCFTypeDictionaryValueCallBacks)); 75 76 // 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()); 82 83 // Set the a boolean to indicate if this is the front most 84 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, 85 kCFBooleanFalse); 86 87 const char *pid_basename = proc_info.kp_proc.p_comm; 88 char proc_path_buf[PATH_MAX]; 89 90 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. 98 99 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; 107 } 108 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()); 122 } 123 } 124 return 0; 125 } 126 int 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 } 209 #else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) 210 // When root, show all processes 211 bool all_users = (our_uid == 0); 212 GetProcesses(plistMutableArray.get(), all_users); 213 #endif 214 215 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; 235 } 236