232 lines · cpp
1//===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Created by Christopher Friesen on 3/21/08.10//11//===----------------------------------------------------------------------===//12 13#include "RNBServices.h"14 15#include "DNB.h"16#include "CFString.h"17#include "DNBLog.h"18#include "MacOSX/CFUtils.h"19#include <CoreFoundation/CoreFoundation.h>20#include <libproc.h>21#include <sys/sysctl.h>22#include <unistd.h>23#include <vector>24 25// For now only SpringBoard has a notion of "Applications" that it can list for26// us.27// So we have to use the SpringBoard API's here.28#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)29#include <SpringBoardServices/SpringBoardServices.h>30#endif31 32int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) {33 if (plistMutableArray == NULL)34 return -1;35 36 // Running as root, get all processes37 std::vector<struct kinfo_proc> proc_infos;38 const size_t num_proc_infos = DNBGetAllInfos(proc_infos);39 if (num_proc_infos > 0) {40 const pid_t our_pid = getpid();41 const uid_t our_uid = getuid();42 uint32_t i;43 CFAllocatorRef alloc = kCFAllocatorDefault;44 45 for (i = 0; i < num_proc_infos; i++) {46 struct kinfo_proc &proc_info = proc_infos[i];47 48 bool kinfo_user_matches;49 // Special case, if lldb is being run as root we can attach to anything.50 if (all_users)51 kinfo_user_matches = true;52 else53 kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid;54 55 const pid_t pid = proc_info.kp_proc.p_pid;56 // Skip zombie processes and processes with unset status57 if (!kinfo_user_matches || // User is acceptable58 pid == our_pid || // Skip this process59 pid == 0 || // Skip kernel (kernel pid is zero)60 proc_info.kp_proc.p_stat ==61 SZOMB || // Zombies are bad, they like brains...62 proc_info.kp_proc.p_flag & P_TRACED || // Being debugged?63 proc_info.kp_proc.p_flag & P_WEXIT // Working on exiting?64 )65 continue;66 67 // Create a new mutable dictionary for each application68 CFReleaser<CFMutableDictionaryRef> appInfoDict(69 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,70 &kCFTypeDictionaryValueCallBacks));71 72 // Get the process id for the app (if there is one)73 const int32_t pid_int32 = pid;74 CFReleaser<CFNumberRef> pidCFNumber(75 ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32));76 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,77 pidCFNumber.get());78 79 // Set a boolean to indicate if this is the front most80 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,81 kCFBooleanFalse);82 83 const char *pid_basename = proc_info.kp_proc.p_comm;84 char proc_path_buf[PATH_MAX];85 86 int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX);87 if (return_val > 0) {88 // Okay, now search backwards from that to see if there is a89 // slash in the name. Note, even though we got all the args we don't90 // care91 // because the list data is just a bunch of concatenated null terminated92 // strings93 // so strrchr will start from the end of argv0.94 95 pid_basename = strrchr(proc_path_buf, '/');96 if (pid_basename) {97 // Skip the '/'98 ++pid_basename;99 } else {100 // We didn't find a directory delimiter in the process argv[0], just101 // use what was in there102 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,107 cf_pid_path.get());108 }109 110 if (pid_basename && pid_basename[0]) {111 CFString pid_name(pid_basename);112 ::CFDictionarySetValue(appInfoDict.get(),113 DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get());114 }115 116 // Append the application info to the plist array117 ::CFArrayAppendValue(plistMutableArray, appInfoDict.get());118 }119 }120 return 0;121}122int ListApplications(std::string &plist, bool opt_runningApps,123 bool opt_debuggable) {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 of129 // any size.130 CFReleaser<CFMutableArrayRef> plistMutableArray(131 ::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 bool all_users = true;139 result = GetProcesses(plistMutableArray.get(), all_users);140 } else {141 CFReleaser<CFStringRef> sbsFrontAppID(142 ::SBSCopyFrontmostApplicationDisplayIdentifier());143 CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers(144 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 CFStringRef displayIdentifier =151 (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i);152 153 // Create a new mutable dictionary for each application154 CFReleaser<CFMutableDictionaryRef> appInfoDict(155 ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,156 &kCFTypeDictionaryValueCallBacks));157 158 // Get the process id for the app (if there is one)159 pid_t pid = INVALID_NUB_PROCESS;160 if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier,161 &pid) == true) {162 CFReleaser<CFNumberRef> pidCFNumber(163 ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid));164 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,165 pidCFNumber.get());166 }167 168 // Set a boolean to indicate if this is the front most169 if (sbsFrontAppID.get() && displayIdentifier &&170 (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) ==171 kCFCompareEqualTo))172 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,173 kCFBooleanTrue);174 else175 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,176 kCFBooleanFalse);177 178 CFReleaser<CFStringRef> executablePath(179 ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier));180 if (executablePath.get() != NULL) {181 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,182 executablePath.get());183 }184 185 CFReleaser<CFStringRef> iconImagePath(186 ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier));187 if (iconImagePath.get() != NULL) {188 ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY,189 iconImagePath.get());190 }191 192 CFReleaser<CFStringRef> localizedDisplayName(193 ::SBSCopyLocalizedApplicationNameForDisplayIdentifier(194 displayIdentifier));195 if (localizedDisplayName.get() != NULL) {196 ::CFDictionarySetValue(appInfoDict.get(),197 DTSERVICES_APP_DISPLAY_NAME_KEY,198 localizedDisplayName.get());199 }200 201 // Append the application info to the plist array202 ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get());203 }204 }205#else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)206 // When root, show all processes207 bool all_users = (our_uid == 0);208 GetProcesses(plistMutableArray.get(), all_users);209#endif210 211 CFReleaser<CFDataRef> plistData(::CFPropertyListCreateData(212 alloc, plistMutableArray.get(), kCFPropertyListXMLFormat_v1_0, 0, NULL));213 214 // write plist to service port215 if (plistData.get() != NULL) {216 CFIndex size = ::CFDataGetLength(plistData.get());217 const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get());218 if (bytes != NULL && size > 0) {219 plist.assign((const char *)bytes, size);220 return 0; // Success221 } else {222 DNBLogError("empty application property list.");223 result = -2;224 }225 } else {226 DNBLogError("serializing task list.");227 result = -3;228 }229 230 return result;231}232