bool IsSBProcess (nub_process_t pid) { #ifdef WITH_SPRINGBOARD bool opt_runningApps = true; bool opt_debuggable = false; CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (opt_runningApps, opt_debuggable)); if (sbsAppIDs.get() != NULL) { CFIndex count = ::CFArrayGetCount (sbsAppIDs.get()); CFIndex i = 0; for (i = 0; i < count; i++) { CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i); // Get the process id for the app (if there is one) pid_t sbs_pid = INVALID_NUB_PROCESS; if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &sbs_pid) == TRUE) { if (sbs_pid == pid) return true; } } } #endif return false; }
bool IsSBProcess (nub_process_t pid) { #ifdef WITH_SPRINGBOARD CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid)); return appIdsForPID.get() != NULL; #else return false; #endif }
void * MachTask::ExceptionThread (void *arg) { if (arg == NULL) return NULL; MachTask *mach_task = (MachTask*) arg; MachProcess *mach_proc = mach_task->Process(); DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s ( arg = %p ) starting thread...", __FUNCTION__, arg); // We keep a count of the number of consecutive exceptions received so // we know to grab all exceptions without a timeout. We do this to get a // bunch of related exceptions on our exception port so we can process // then together. When we have multiple threads, we can get an exception // per thread and they will come in consecutively. The main loop in this // thread can stop periodically if needed to service things related to this // process. // flag set in the options, so we will wait forever for an exception on // our exception port. After we get one exception, we then will use the // MACH_RCV_TIMEOUT option with a zero timeout to grab all other current // exceptions for our process. After we have received the last pending // exception, we will get a timeout which enables us to then notify // our main thread that we have an exception bundle avaiable. We then wait // for the main thread to tell this exception thread to start trying to get // exceptions messages again and we start again with a mach_msg read with // infinite timeout. uint32_t num_exceptions_received = 0; DNBError err; task_t task = mach_task->TaskPort(); mach_msg_timeout_t periodic_timeout = 0; #if defined (__arm__) mach_msg_timeout_t watchdog_elapsed = 0; mach_msg_timeout_t watchdog_timeout = 60 * 1000; pid_t pid = mach_proc->ProcessID(); CFReleaser<SBSWatchdogAssertionRef> watchdog; if (mach_proc->ProcessUsingSpringBoard()) { // Request a renewal for every 60 seconds if we attached using SpringBoard watchdog.reset(::SBSWatchdogAssertionCreateForPID(NULL, pid, 60)); DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionCreateForPID (NULL, %4.4x, 60 ) => %p", pid, watchdog.get()); if (watchdog.get()) { ::SBSWatchdogAssertionRenew (watchdog.get()); CFTimeInterval watchdogRenewalInterval = ::SBSWatchdogAssertionGetRenewalInterval (watchdog.get()); DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionGetRenewalInterval ( %p ) => %g seconds", watchdog.get(), watchdogRenewalInterval); if (watchdogRenewalInterval > 0.0) { watchdog_timeout = (mach_msg_timeout_t)watchdogRenewalInterval * 1000; if (watchdog_timeout > 3000) watchdog_timeout -= 1000; // Give us a second to renew our timeout else if (watchdog_timeout > 1000) watchdog_timeout -= 250; // Give us a quarter of a second to renew our timeout } } if (periodic_timeout == 0 || periodic_timeout > watchdog_timeout) periodic_timeout = watchdog_timeout; } #endif // #if defined (__arm__) while (mach_task->ExceptionPortIsValid()) { ::pthread_testcancel (); MachException::Message exception_message; if (num_exceptions_received > 0) { // No timeout, just receive as many exceptions as we can since we already have one and we want // to get all currently available exceptions for this task err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, 0); } else if (periodic_timeout > 0) { // We need to stop periodically in this loop, so try and get a mach message with a valid timeout (ms) err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT | MACH_RCV_TIMEOUT, periodic_timeout); } else { // We don't need to parse all current exceptions or stop periodically, // just wait for an exception forever. err = exception_message.Receive(mach_task->ExceptionPort(), MACH_RCV_MSG | MACH_RCV_INTERRUPT, 0); } if (err.Error() == MACH_RCV_INTERRUPTED) { // If we have no task port we should exit this thread if (!mach_task->ExceptionPortIsValid()) { DNBLogThreadedIf(LOG_EXCEPTIONS, "thread cancelled..."); break; } // Make sure our task is still valid if (MachTask::IsValid(task)) { // Task is still ok DNBLogThreadedIf(LOG_EXCEPTIONS, "interrupted, but task still valid, continuing..."); continue; } else { DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited..."); mach_proc->SetState(eStateExited); // Our task has died, exit the thread. break; } } else if (err.Error() == MACH_RCV_TIMED_OUT) { if (num_exceptions_received > 0) { // We were receiving all current exceptions with a timeout of zero // it is time to go back to our normal looping mode num_exceptions_received = 0; // Notify our main thread we have a complete exception message // bundle available. mach_proc->ExceptionMessageBundleComplete(); // in case we use a timeout value when getting exceptions... // Make sure our task is still valid if (MachTask::IsValid(task)) { // Task is still ok DNBLogThreadedIf(LOG_EXCEPTIONS, "got a timeout, continuing..."); continue; } else { DNBLogThreadedIf(LOG_EXCEPTIONS, "task has exited..."); mach_proc->SetState(eStateExited); // Our task has died, exit the thread. break; } continue; } #if defined (__arm__) if (watchdog.get()) { watchdog_elapsed += periodic_timeout; if (watchdog_elapsed >= watchdog_timeout) { DNBLogThreadedIf(LOG_TASK, "SBSWatchdogAssertionRenew ( %p )", watchdog.get()); ::SBSWatchdogAssertionRenew (watchdog.get()); watchdog_elapsed = 0; } } #endif } else if (err.Error() != KERN_SUCCESS) { DNBLogThreadedIf(LOG_EXCEPTIONS, "got some other error, do something about it??? nah, continuing for now..."); // TODO: notify of error? } else { if (exception_message.CatchExceptionRaise()) { ++num_exceptions_received; mach_proc->ExceptionMessageReceived(exception_message); } } } #if defined (__arm__) if (watchdog.get()) { // TODO: change SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel when we // all are up and running on systems that support it. The SBS framework has a #define // that will forward SBSWatchdogAssertionRelease to SBSWatchdogAssertionCancel for now // so it should still build either way. DNBLogThreadedIf(LOG_TASK, "::SBSWatchdogAssertionRelease(%p)", watchdog.get()); ::SBSWatchdogAssertionRelease (watchdog.get()); } #endif // #if defined (__arm__) DNBLogThreadedIf(LOG_EXCEPTIONS, "MachTask::%s (%p): thread exiting...", __FUNCTION__, arg); return NULL; }
int ListApplications(std::string& plist, bool opt_runningApps, bool opt_debuggable) { #ifdef WITH_SPRINGBOARD 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)); 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()); } 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; #else // TODO: list all current processes DNBLogError("SBS doesn't support getting application list."); return -1; #endif }
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; }