|  | //===-- 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 the 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 the 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((char *)bytes, size); | 
|  | return 0; // Success | 
|  | } else { | 
|  | DNBLogError("empty application property list."); | 
|  | result = -2; | 
|  | } | 
|  | } else { | 
|  | DNBLogError("serializing task list."); | 
|  | result = -3; | 
|  | } | 
|  |  | 
|  | return result; | 
|  | } |