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