RNBServices.cpp revision d77371fe8083d697edc4f9ed7d039ed7b47b88a6
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
30size_t GetAllInfos (std::vector<struct kinfo_proc>& proc_infos);
31
32int
33GetPrcoesses (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}
121int
122ListApplications(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
228bool
229IsSBProcess (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