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 #import "RNBServices.h" 15 16 #import <CoreFoundation/CoreFoundation.h> 17 #include <libproc.h> 18 #import <unistd.h> 19 #include <sys/sysctl.h> 20 #include "CFString.h" 21 #include <vector> 22 #import "DNBLog.h" 23 #include "MacOSX/CFUtils.h" 24 25 #ifdef WITH_SPRINGBOARD 26 #import <SpringBoardServices/SpringBoardServices.h> 27 #endif 28 29 // From DNB.cpp 30 size_t GetAllInfos (std::vector<struct kinfo_proc>& proc_infos); 31 32 int 33 GetPrcoesses (CFMutableArrayRef plistMutableArray, bool all_users) 34 { 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 { 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 { 50 struct kinfo_proc &proc_info = proc_infos[i]; 51 52 bool kinfo_user_matches; 53 // Special case, if lldb is being run as root we can attach to anything. 54 if (all_users) 55 kinfo_user_matches = true; 56 else 57 kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid; 58 59 60 const pid_t pid = proc_info.kp_proc.p_pid; 61 // Skip zombie processes and processes with unset status 62 if (kinfo_user_matches == false || // User is acceptable 63 pid == our_pid || // Skip this process 64 pid == 0 || // Skip kernel (kernel pid is zero) 65 proc_info.kp_proc.p_stat == SZOMB || // Zombies are bad, they like brains... 66 proc_info.kp_proc.p_flag & P_TRACED || // Being debugged? 67 proc_info.kp_proc.p_flag & P_WEXIT || // Working on exiting? 68 proc_info.kp_proc.p_flag & P_TRANSLATED) // Skip translated ppc (Rosetta) 69 continue; 70 71 // Create a new mutable dictionary for each application 72 CFReleaser<CFMutableDictionaryRef> appInfoDict (::CFDictionaryCreateMutable (alloc, 0, &kCFTypeDictionaryKeyCallBacks, &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 (::CFNumberCreate (alloc, kCFNumberSInt32Type, &pid_int32)); 77 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PID_KEY, pidCFNumber.get()); 78 79 // Set the a boolean to indicate if this is the front most 80 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanFalse); 81 82 const char *pid_basename = proc_info.kp_proc.p_comm; 83 char proc_path_buf[PATH_MAX]; 84 85 int return_val = proc_pidpath (pid, proc_path_buf, PATH_MAX); 86 if (return_val > 0) 87 { 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 care 90 // because the list data is just a bunch of concatenated null terminated strings 91 // so strrchr will start from the end of argv0. 92 93 pid_basename = strrchr(proc_path_buf, '/'); 94 if (pid_basename) 95 { 96 // Skip the '/' 97 ++pid_basename; 98 } 99 else 100 { 101 // We didn't find a directory delimiter in the process argv[0], just 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, cf_pid_path.get()); 107 } 108 109 if (pid_basename && pid_basename[0]) 110 { 111 CFString pid_name (pid_basename); 112 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get()); 113 } 114 115 // Append the application info to the plist array 116 ::CFArrayAppendValue (plistMutableArray, appInfoDict.get()); 117 } 118 } 119 return 0; 120 } 121 int 122 ListApplications(std::string& plist, bool opt_runningApps, bool opt_debuggable) 123 { 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 any size. 129 CFReleaser<CFMutableArrayRef> plistMutableArray (::CFArrayCreateMutable (alloc, 0, &kCFTypeArrayCallBacks)); 130 131 const uid_t our_uid = getuid(); 132 133 #ifdef WITH_SPRINGBOARD 134 135 136 if (our_uid == 0) 137 { 138 bool all_users = true; 139 result = GetPrcoesses (plistMutableArray.get(), all_users); 140 } 141 else 142 { 143 CFReleaser<CFStringRef> sbsFrontAppID (::SBSCopyFrontmostApplicationDisplayIdentifier ()); 144 CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (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 { 151 CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i); 152 153 // Create a new mutable dictionary for each application 154 CFReleaser<CFMutableDictionaryRef> appInfoDict (::CFDictionaryCreateMutable (alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 155 156 // Get the process id for the app (if there is one) 157 pid_t pid = INVALID_NUB_PROCESS; 158 if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &pid) == true) 159 { 160 CFReleaser<CFNumberRef> pidCFNumber (::CFNumberCreate (alloc, kCFNumberSInt32Type, &pid)); 161 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PID_KEY, pidCFNumber.get()); 162 } 163 164 // Set the a boolean to indicate if this is the front most 165 if (sbsFrontAppID.get() && displayIdentifier && (::CFStringCompare (sbsFrontAppID.get(), displayIdentifier, 0) == kCFCompareEqualTo)) 166 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanTrue); 167 else 168 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanFalse); 169 170 171 CFReleaser<CFStringRef> executablePath (::SBSCopyExecutablePathForDisplayIdentifier (displayIdentifier)); 172 if (executablePath.get() != NULL) 173 { 174 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PATH_KEY, executablePath.get()); 175 } 176 177 CFReleaser<CFStringRef> iconImagePath (::SBSCopyIconImagePathForDisplayIdentifier (displayIdentifier)) ; 178 if (iconImagePath.get() != NULL) 179 { 180 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY, iconImagePath.get()); 181 } 182 183 CFReleaser<CFStringRef> localizedDisplayName (::SBSCopyLocalizedApplicationNameForDisplayIdentifier (displayIdentifier)); 184 if (localizedDisplayName.get() != NULL) 185 { 186 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_DISPLAY_NAME_KEY, localizedDisplayName.get()); 187 } 188 189 // Append the application info to the plist array 190 ::CFArrayAppendValue (plistMutableArray.get(), appInfoDict.get()); 191 } 192 } 193 #else 194 // When root, show all processes 195 bool all_users = (our_uid == 0); 196 result = GetPrcoesses (plistMutableArray.get(), all_users); 197 #endif 198 199 CFReleaser<CFDataRef> plistData (::CFPropertyListCreateXMLData (alloc, plistMutableArray.get())); 200 201 // write plist to service port 202 if (plistData.get() != NULL) 203 { 204 CFIndex size = ::CFDataGetLength (plistData.get()); 205 const UInt8 *bytes = ::CFDataGetBytePtr (plistData.get()); 206 if (bytes != NULL && size > 0) 207 { 208 plist.assign((char *)bytes, size); 209 return 0; // Success 210 } 211 else 212 { 213 DNBLogError("empty application property list."); 214 result = -2; 215 } 216 } 217 else 218 { 219 DNBLogError("serializing task list."); 220 result = -3; 221 } 222 223 return result; 224 225 } 226 227 228 bool 229 IsSBProcess (nub_process_t pid) 230 { 231 #ifdef WITH_SPRINGBOARD 232 CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid)); 233 return appIdsForPID.get() != NULL; 234 #else 235 return false; 236 #endif 237 } 238 239