Home | History | Annotate | Download | only in source
      1 //===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 //  Created by Christopher Friesen on 3/21/08.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #import "RNBServices.h"
     15 
     16 #import <CoreFoundation/CoreFoundation.h>
     17 #include <libproc.h>
     18 #import <unistd.h>
     19 #include <sys/sysctl.h>
     20 #include "CFString.h"
     21 #include <vector>
     22 #import "DNBLog.h"
     23 #include "MacOSX/CFUtils.h"
     24 
     25 #ifdef WITH_SPRINGBOARD
     26 #import <SpringBoardServices/SpringBoardServices.h>
     27 #endif
     28 
     29 // From DNB.cpp
     30 size_t GetAllInfos (std::vector<struct kinfo_proc>& proc_infos);
     31 
     32 int
     33 GetPrcoesses (CFMutableArrayRef plistMutableArray, bool all_users)
     34 {
     35     if (plistMutableArray == NULL)
     36         return -1;
     37 
     38     // Running as root, get all processes
     39     std::vector<struct kinfo_proc> proc_infos;
     40     const size_t num_proc_infos = GetAllInfos(proc_infos);
     41     if (num_proc_infos > 0)
     42     {
     43         const pid_t our_pid = getpid();
     44         const uid_t our_uid = getuid();
     45         uint32_t i;
     46         CFAllocatorRef alloc = kCFAllocatorDefault;
     47 
     48         for (i=0; i<num_proc_infos; i++)
     49         {
     50             struct kinfo_proc &proc_info = proc_infos[i];
     51 
     52             bool kinfo_user_matches;
     53             // Special case, if lldb is being run as root we can attach to anything.
     54             if (all_users)
     55                 kinfo_user_matches = true;
     56             else
     57                 kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid;
     58 
     59 
     60             const pid_t pid = proc_info.kp_proc.p_pid;
     61             // Skip zombie processes and processes with unset status
     62             if (kinfo_user_matches == false             || // User is acceptable
     63                 pid == our_pid                          || // Skip this process
     64                 pid == 0                                || // Skip kernel (kernel pid is zero)
     65                 proc_info.kp_proc.p_stat == SZOMB       || // Zombies are bad, they like brains...
     66                 proc_info.kp_proc.p_flag & P_TRACED     || // Being debugged?
     67                 proc_info.kp_proc.p_flag & P_WEXIT      || // Working on exiting?
     68                 proc_info.kp_proc.p_flag & P_TRANSLATED)   // Skip translated ppc (Rosetta)
     69                 continue;
     70 
     71             // Create a new mutable dictionary for each application
     72             CFReleaser<CFMutableDictionaryRef> appInfoDict (::CFDictionaryCreateMutable (alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
     73 
     74             // Get the process id for the app (if there is one)
     75             const int32_t pid_int32 = pid;
     76             CFReleaser<CFNumberRef> pidCFNumber (::CFNumberCreate (alloc,  kCFNumberSInt32Type, &pid_int32));
     77             ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PID_KEY, pidCFNumber.get());
     78 
     79             // Set the a boolean to indicate if this is the front most
     80             ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanFalse);
     81 
     82             const char *pid_basename = proc_info.kp_proc.p_comm;
     83             char proc_path_buf[PATH_MAX];
     84 
     85             int return_val = proc_pidpath (pid, proc_path_buf, PATH_MAX);
     86             if (return_val > 0)
     87             {
     88                 // Okay, now search backwards from that to see if there is a
     89                 // slash in the name.  Note, even though we got all the args we don't care
     90                 // because the list data is just a bunch of concatenated null terminated strings
     91                 // so strrchr will start from the end of argv0.
     92 
     93                 pid_basename = strrchr(proc_path_buf, '/');
     94                 if (pid_basename)
     95                 {
     96                     // Skip the '/'
     97                     ++pid_basename;
     98                 }
     99                 else
    100                 {
    101                     // We didn't find a directory delimiter in the process argv[0], just use what was in there
    102                     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, cf_pid_path.get());
    107             }
    108 
    109             if (pid_basename && pid_basename[0])
    110             {
    111                 CFString pid_name (pid_basename);
    112                 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get());
    113             }
    114 
    115             // Append the application info to the plist array
    116             ::CFArrayAppendValue (plistMutableArray, appInfoDict.get());
    117         }
    118     }
    119     return 0;
    120 }
    121 int
    122 ListApplications(std::string& plist, bool opt_runningApps, bool opt_debuggable)
    123 {
    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 of any size.
    129     CFReleaser<CFMutableArrayRef> plistMutableArray (::CFArrayCreateMutable (alloc, 0, &kCFTypeArrayCallBacks));
    130 
    131     const uid_t our_uid = getuid();
    132 
    133 #ifdef WITH_SPRINGBOARD
    134 
    135 
    136     if (our_uid == 0)
    137     {
    138         bool all_users = true;
    139         result = GetPrcoesses (plistMutableArray.get(), all_users);
    140     }
    141     else
    142     {
    143         CFReleaser<CFStringRef> sbsFrontAppID (::SBSCopyFrontmostApplicationDisplayIdentifier ());
    144         CFReleaser<CFArrayRef> sbsAppIDs (::SBSCopyApplicationDisplayIdentifiers (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         {
    151             CFStringRef displayIdentifier = (CFStringRef)::CFArrayGetValueAtIndex (sbsAppIDs.get(), i);
    152 
    153             // Create a new mutable dictionary for each application
    154             CFReleaser<CFMutableDictionaryRef> appInfoDict (::CFDictionaryCreateMutable (alloc, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
    155 
    156             // Get the process id for the app (if there is one)
    157             pid_t pid = INVALID_NUB_PROCESS;
    158             if (::SBSProcessIDForDisplayIdentifier ((CFStringRef)displayIdentifier, &pid) == true)
    159             {
    160                 CFReleaser<CFNumberRef> pidCFNumber (::CFNumberCreate (alloc,  kCFNumberSInt32Type, &pid));
    161                 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PID_KEY, pidCFNumber.get());
    162             }
    163 
    164             // Set the a boolean to indicate if this is the front most
    165             if (sbsFrontAppID.get() && displayIdentifier && (::CFStringCompare (sbsFrontAppID.get(), displayIdentifier, 0) == kCFCompareEqualTo))
    166                 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanTrue);
    167             else
    168                 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY, kCFBooleanFalse);
    169 
    170 
    171             CFReleaser<CFStringRef> executablePath (::SBSCopyExecutablePathForDisplayIdentifier (displayIdentifier));
    172             if (executablePath.get() != NULL)
    173             {
    174                 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_PATH_KEY, executablePath.get());
    175             }
    176 
    177             CFReleaser<CFStringRef> iconImagePath (::SBSCopyIconImagePathForDisplayIdentifier (displayIdentifier)) ;
    178             if (iconImagePath.get() != NULL)
    179             {
    180                 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY, iconImagePath.get());
    181             }
    182 
    183             CFReleaser<CFStringRef> localizedDisplayName (::SBSCopyLocalizedApplicationNameForDisplayIdentifier (displayIdentifier));
    184             if (localizedDisplayName.get() != NULL)
    185             {
    186                 ::CFDictionarySetValue (appInfoDict.get(), DTSERVICES_APP_DISPLAY_NAME_KEY, localizedDisplayName.get());
    187             }
    188 
    189             // Append the application info to the plist array
    190             ::CFArrayAppendValue (plistMutableArray.get(), appInfoDict.get());
    191         }
    192     }
    193 #else
    194     // When root, show all processes
    195     bool all_users = (our_uid == 0);
    196     result = GetPrcoesses (plistMutableArray.get(), all_users);
    197 #endif
    198 
    199     CFReleaser<CFDataRef> plistData (::CFPropertyListCreateXMLData (alloc, plistMutableArray.get()));
    200 
    201     // write plist to service port
    202     if (plistData.get() != NULL)
    203     {
    204         CFIndex size = ::CFDataGetLength (plistData.get());
    205         const UInt8 *bytes = ::CFDataGetBytePtr (plistData.get());
    206         if (bytes != NULL && size > 0)
    207         {
    208             plist.assign((char *)bytes, size);
    209             return 0;   // Success
    210         }
    211         else
    212         {
    213             DNBLogError("empty application property list.");
    214             result = -2;
    215         }
    216     }
    217     else
    218     {
    219         DNBLogError("serializing task list.");
    220         result = -3;
    221     }
    222 
    223     return result;
    224 
    225 }
    226 
    227 
    228 bool
    229 IsSBProcess (nub_process_t pid)
    230 {
    231 #ifdef WITH_SPRINGBOARD
    232     CFReleaser<CFArrayRef> appIdsForPID (::SBSCopyDisplayIdentifiersForProcessID(pid));
    233     return appIdsForPID.get() != NULL;
    234 #else
    235     return false;
    236 #endif
    237 }
    238 
    239