| //===-- RNBServices.cpp -----------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Created by Christopher Friesen on 3/21/08. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "RNBServices.h" |
| |
| #include "CFString.h" |
| #include "DNBLog.h" |
| #include "MacOSX/CFUtils.h" |
| #include <CoreFoundation/CoreFoundation.h> |
| #include <libproc.h> |
| #include <sys/sysctl.h> |
| #include <unistd.h> |
| #include <vector> |
| |
| // For now only SpringBoard has a notion of "Applications" that it can list for |
| // us. |
| // So we have to use the SpringBoard API's here. |
| #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) |
| #include <SpringBoardServices/SpringBoardServices.h> |
| #endif |
| |
| // From DNB.cpp |
| size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos); |
| |
| int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) { |
| if (plistMutableArray == NULL) |
| return -1; |
| |
| // Running as root, get all processes |
| std::vector<struct kinfo_proc> proc_infos; |
| const size_t num_proc_infos = GetAllInfos(proc_infos); |
| if (num_proc_infos > 0) { |
| const pid_t our_pid = getpid(); |
| const uid_t our_uid = getuid(); |
| uint32_t i; |
| CFAllocatorRef alloc = kCFAllocatorDefault; |
| |
| for (i = 0; i < num_proc_infos; i++) { |
| struct kinfo_proc &proc_info = proc_infos[i]; |
| |
| bool kinfo_user_matches; |
| // Special case, if lldb is being run as root we can attach to anything. |
| if (all_users) |
| kinfo_user_matches = true; |
| else |
| kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid; |
| |
| const pid_t pid = proc_info.kp_proc.p_pid; |
| // Skip zombie processes and processes with unset status |
| if (kinfo_user_matches == false || // User is acceptable |
| pid == our_pid || // Skip this process |
| pid == 0 || // Skip kernel (kernel pid is zero) |
| proc_info.kp_proc.p_stat == |
| SZOMB || // Zombies are bad, they like brains... |
| proc_info.kp_proc.p_flag & P_TRACED || // Being debugged? |
| proc_info.kp_proc.p_flag & P_WEXIT || // Working on exiting? |
| proc_info.kp_proc.p_flag & |
| P_TRANSLATED) // Skip translated ppc (Rosetta) |
| continue; |
| |
| // Create a new mutable dictionary for each application |
| CFReleaser<CFMutableDictionaryRef> appInfoDict( |
| ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, |
| &kCFTypeDictionaryValueCallBacks)); |
| |
| // Get the process id for the app (if there is one) |
| const int32_t pid_int32 = pid; |
| CFReleaser<CFNumberRef> pidCFNumber( |
| ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32)); |
| ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY, |
| pidCFNumber.get()); |
| |
| // Set a boolean to indicate if this is the front most |
| ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, |
| kCFBooleanFalse); |
| |
| const char *pid_basename = proc_info.kp_proc.p_comm; |
| char proc_path_buf[PATH_MAX]; |
| |
| int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX); |
| if (return_val > 0) { |
| // Okay, now search backwards from that to see if there is a |
| // slash in the name. Note, even though we got all the args we don't |
| // care |
| // because the list data is just a bunch of concatenated null terminated |
| // strings |
| // so strrchr will start from the end of argv0. |
| |
| pid_basename = strrchr(proc_path_buf, '/'); |
| if (pid_basename) { |
| // Skip the '/' |
| ++pid_basename; |
| } else { |
| // We didn't find a directory delimiter in the process argv[0], just |
| // use what was in there |
| pid_basename = proc_path_buf; |
| } |
| CFString cf_pid_path(proc_path_buf); |
| if (cf_pid_path.get()) |
| ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY, |
| cf_pid_path.get()); |
| } |
| |
| if (pid_basename && pid_basename[0]) { |
| CFString pid_name(pid_basename); |
| ::CFDictionarySetValue(appInfoDict.get(), |
| DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get()); |
| } |
| |
| // Append the application info to the plist array |
| ::CFArrayAppendValue(plistMutableArray, appInfoDict.get()); |
| } |
| } |
| return 0; |
| } |
| int ListApplications(std::string &plist, bool opt_runningApps, |
| bool opt_debuggable) { |
| int result = -1; |
| |
| CFAllocatorRef alloc = kCFAllocatorDefault; |
| |
| // Create a mutable array that we can populate. Specify zero so it can be of |
| // any size. |
| CFReleaser<CFMutableArrayRef> plistMutableArray( |
| ::CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks)); |
| |
| const uid_t our_uid = getuid(); |
| |
| #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS) |
| |
| if (our_uid == 0) { |
| bool all_users = true; |
| result = GetProcesses(plistMutableArray.get(), all_users); |
| } else { |
| CFReleaser<CFStringRef> sbsFrontAppID( |
| ::SBSCopyFrontmostApplicationDisplayIdentifier()); |
| CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers( |
| opt_runningApps, opt_debuggable)); |
| |
| // Need to check the return value from SBSCopyApplicationDisplayIdentifiers. |
| CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount(sbsAppIDs.get()) : 0; |
| CFIndex i = 0; |
| for (i = 0; i < count; i++) { |
| CFStringRef displayIdentifier = |
| (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i); |
| |
| // Create a new mutable dictionary for each application |
| CFReleaser<CFMutableDictionaryRef> appInfoDict( |
| ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, |
| &kCFTypeDictionaryValueCallBacks)); |
| |
| // Get the process id for the app (if there is one) |
| pid_t pid = INVALID_NUB_PROCESS; |
| if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier, |
| &pid) == true) { |
| CFReleaser<CFNumberRef> pidCFNumber( |
| ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid)); |
| ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY, |
| pidCFNumber.get()); |
| } |
| |
| // Set a boolean to indicate if this is the front most |
| if (sbsFrontAppID.get() && displayIdentifier && |
| (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) == |
| kCFCompareEqualTo)) |
| ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, |
| kCFBooleanTrue); |
| else |
| ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, |
| kCFBooleanFalse); |
| |
| CFReleaser<CFStringRef> executablePath( |
| ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier)); |
| if (executablePath.get() != NULL) { |
| ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY, |
| executablePath.get()); |
| } |
| |
| CFReleaser<CFStringRef> iconImagePath( |
| ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier)); |
| if (iconImagePath.get() != NULL) { |
| ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY, |
| iconImagePath.get()); |
| } |
| |
| CFReleaser<CFStringRef> localizedDisplayName( |
| ::SBSCopyLocalizedApplicationNameForDisplayIdentifier( |
| displayIdentifier)); |
| if (localizedDisplayName.get() != NULL) { |
| ::CFDictionarySetValue(appInfoDict.get(), |
| DTSERVICES_APP_DISPLAY_NAME_KEY, |
| localizedDisplayName.get()); |
| } |
| |
| // Append the application info to the plist array |
| ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get()); |
| } |
| } |
| #else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS) |
| // When root, show all processes |
| bool all_users = (our_uid == 0); |
| GetProcesses(plistMutableArray.get(), all_users); |
| #endif |
| |
| CFReleaser<CFDataRef> plistData( |
| ::CFPropertyListCreateXMLData(alloc, plistMutableArray.get())); |
| |
| // write plist to service port |
| if (plistData.get() != NULL) { |
| CFIndex size = ::CFDataGetLength(plistData.get()); |
| const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get()); |
| if (bytes != NULL && size > 0) { |
| plist.assign((const char *)bytes, size); |
| return 0; // Success |
| } else { |
| DNBLogError("empty application property list."); |
| result = -2; |
| } |
| } else { |
| DNBLogError("serializing task list."); |
| result = -3; |
| } |
| |
| return result; |
| } |