//===-- RNBServices.cpp -----------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Created by Christopher Friesen on 3/21/08. // //===----------------------------------------------------------------------===// #include "RNBServices.h" #include "CFString.h" #include "DNBLog.h" #include "MacOSX/CFUtils.h" #include #include #include #include #include // 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 #endif // From DNB.cpp size_t GetAllInfos(std::vector &proc_infos); int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) { if (plistMutableArray == NULL) return -1; // Running as root, get all processes std::vector 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 || // 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 appInfoDict( ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); // Get the process id for the app (if there is one) const int32_t pid_int32 = pid; CFReleaser 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 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 sbsFrontAppID( ::SBSCopyFrontmostApplicationDisplayIdentifier()); CFReleaser 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 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 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 executablePath( ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier)); if (executablePath.get() != NULL) { ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY, executablePath.get()); } CFReleaser iconImagePath( ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier)); if (iconImagePath.get() != NULL) { ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY, iconImagePath.get()); } CFReleaser 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 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; }