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