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