Example #1
0
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;
}
Example #2
0
bool
IsSBProcess (nub_process_t pid)
{
#ifdef WITH_SPRINGBOARD
    CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid));
    return appIdsForPID.get() != NULL;
#else
    return false;
#endif
}
Example #3
0
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;
}
Example #4
0
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;
}