xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/RNBServices.cpp (revision dda2819751e49c83612958492e38917049128b41)
1061da546Spatrick //===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick //  Created by Christopher Friesen on 3/21/08.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick 
13061da546Spatrick #include "RNBServices.h"
14061da546Spatrick 
15*dda28197Spatrick #include "DNB.h"
16061da546Spatrick #include "CFString.h"
17061da546Spatrick #include "DNBLog.h"
18061da546Spatrick #include "MacOSX/CFUtils.h"
19061da546Spatrick #include <CoreFoundation/CoreFoundation.h>
20061da546Spatrick #include <libproc.h>
21061da546Spatrick #include <sys/sysctl.h>
22061da546Spatrick #include <unistd.h>
23061da546Spatrick #include <vector>
24061da546Spatrick 
25061da546Spatrick // For now only SpringBoard has a notion of "Applications" that it can list for
26061da546Spatrick // us.
27061da546Spatrick // So we have to use the SpringBoard API's here.
28061da546Spatrick #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
29061da546Spatrick #include <SpringBoardServices/SpringBoardServices.h>
30061da546Spatrick #endif
31061da546Spatrick 
GetProcesses(CFMutableArrayRef plistMutableArray,bool all_users)32061da546Spatrick int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) {
33061da546Spatrick   if (plistMutableArray == NULL)
34061da546Spatrick     return -1;
35061da546Spatrick 
36061da546Spatrick   // Running as root, get all processes
37061da546Spatrick   std::vector<struct kinfo_proc> proc_infos;
38*dda28197Spatrick   const size_t num_proc_infos = DNBGetAllInfos(proc_infos);
39061da546Spatrick   if (num_proc_infos > 0) {
40061da546Spatrick     const pid_t our_pid = getpid();
41061da546Spatrick     const uid_t our_uid = getuid();
42061da546Spatrick     uint32_t i;
43061da546Spatrick     CFAllocatorRef alloc = kCFAllocatorDefault;
44061da546Spatrick 
45061da546Spatrick     for (i = 0; i < num_proc_infos; i++) {
46061da546Spatrick       struct kinfo_proc &proc_info = proc_infos[i];
47061da546Spatrick 
48061da546Spatrick       bool kinfo_user_matches;
49061da546Spatrick       // Special case, if lldb is being run as root we can attach to anything.
50061da546Spatrick       if (all_users)
51061da546Spatrick         kinfo_user_matches = true;
52061da546Spatrick       else
53061da546Spatrick         kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid;
54061da546Spatrick 
55061da546Spatrick       const pid_t pid = proc_info.kp_proc.p_pid;
56061da546Spatrick       // Skip zombie processes and processes with unset status
57061da546Spatrick       if (!kinfo_user_matches || // User is acceptable
58061da546Spatrick           pid == our_pid ||      // Skip this process
59061da546Spatrick           pid == 0 ||            // Skip kernel (kernel pid is zero)
60061da546Spatrick           proc_info.kp_proc.p_stat ==
61061da546Spatrick               SZOMB || // Zombies are bad, they like brains...
62061da546Spatrick           proc_info.kp_proc.p_flag & P_TRACED || // Being debugged?
63*dda28197Spatrick           proc_info.kp_proc.p_flag & P_WEXIT     // Working on exiting?
64*dda28197Spatrick       )
65061da546Spatrick         continue;
66061da546Spatrick 
67061da546Spatrick       // Create a new mutable dictionary for each application
68061da546Spatrick       CFReleaser<CFMutableDictionaryRef> appInfoDict(
69061da546Spatrick           ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
70061da546Spatrick                                       &kCFTypeDictionaryValueCallBacks));
71061da546Spatrick 
72061da546Spatrick       // Get the process id for the app (if there is one)
73061da546Spatrick       const int32_t pid_int32 = pid;
74061da546Spatrick       CFReleaser<CFNumberRef> pidCFNumber(
75061da546Spatrick           ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32));
76061da546Spatrick       ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
77061da546Spatrick                              pidCFNumber.get());
78061da546Spatrick 
79061da546Spatrick       // Set a boolean to indicate if this is the front most
80061da546Spatrick       ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
81061da546Spatrick                              kCFBooleanFalse);
82061da546Spatrick 
83061da546Spatrick       const char *pid_basename = proc_info.kp_proc.p_comm;
84061da546Spatrick       char proc_path_buf[PATH_MAX];
85061da546Spatrick 
86061da546Spatrick       int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX);
87061da546Spatrick       if (return_val > 0) {
88061da546Spatrick         // Okay, now search backwards from that to see if there is a
89061da546Spatrick         // slash in the name.  Note, even though we got all the args we don't
90061da546Spatrick         // care
91061da546Spatrick         // because the list data is just a bunch of concatenated null terminated
92061da546Spatrick         // strings
93061da546Spatrick         // so strrchr will start from the end of argv0.
94061da546Spatrick 
95061da546Spatrick         pid_basename = strrchr(proc_path_buf, '/');
96061da546Spatrick         if (pid_basename) {
97061da546Spatrick           // Skip the '/'
98061da546Spatrick           ++pid_basename;
99061da546Spatrick         } else {
100061da546Spatrick           // We didn't find a directory delimiter in the process argv[0], just
101061da546Spatrick           // use what was in there
102061da546Spatrick           pid_basename = proc_path_buf;
103061da546Spatrick         }
104061da546Spatrick         CFString cf_pid_path(proc_path_buf);
105061da546Spatrick         if (cf_pid_path.get())
106061da546Spatrick           ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
107061da546Spatrick                                  cf_pid_path.get());
108061da546Spatrick       }
109061da546Spatrick 
110061da546Spatrick       if (pid_basename && pid_basename[0]) {
111061da546Spatrick         CFString pid_name(pid_basename);
112061da546Spatrick         ::CFDictionarySetValue(appInfoDict.get(),
113061da546Spatrick                                DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get());
114061da546Spatrick       }
115061da546Spatrick 
116061da546Spatrick       // Append the application info to the plist array
117061da546Spatrick       ::CFArrayAppendValue(plistMutableArray, appInfoDict.get());
118061da546Spatrick     }
119061da546Spatrick   }
120061da546Spatrick   return 0;
121061da546Spatrick }
ListApplications(std::string & plist,bool opt_runningApps,bool opt_debuggable)122061da546Spatrick int ListApplications(std::string &plist, bool opt_runningApps,
123061da546Spatrick                      bool opt_debuggable) {
124061da546Spatrick   int result = -1;
125061da546Spatrick 
126061da546Spatrick   CFAllocatorRef alloc = kCFAllocatorDefault;
127061da546Spatrick 
128061da546Spatrick   // Create a mutable array that we can populate. Specify zero so it can be of
129061da546Spatrick   // any size.
130061da546Spatrick   CFReleaser<CFMutableArrayRef> plistMutableArray(
131061da546Spatrick       ::CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks));
132061da546Spatrick 
133061da546Spatrick   const uid_t our_uid = getuid();
134061da546Spatrick 
135061da546Spatrick #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
136061da546Spatrick 
137061da546Spatrick   if (our_uid == 0) {
138061da546Spatrick     bool all_users = true;
139061da546Spatrick     result = GetProcesses(plistMutableArray.get(), all_users);
140061da546Spatrick   } else {
141061da546Spatrick     CFReleaser<CFStringRef> sbsFrontAppID(
142061da546Spatrick         ::SBSCopyFrontmostApplicationDisplayIdentifier());
143061da546Spatrick     CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers(
144061da546Spatrick         opt_runningApps, opt_debuggable));
145061da546Spatrick 
146061da546Spatrick     // Need to check the return value from SBSCopyApplicationDisplayIdentifiers.
147061da546Spatrick     CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount(sbsAppIDs.get()) : 0;
148061da546Spatrick     CFIndex i = 0;
149061da546Spatrick     for (i = 0; i < count; i++) {
150061da546Spatrick       CFStringRef displayIdentifier =
151061da546Spatrick           (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i);
152061da546Spatrick 
153061da546Spatrick       // Create a new mutable dictionary for each application
154061da546Spatrick       CFReleaser<CFMutableDictionaryRef> appInfoDict(
155061da546Spatrick           ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
156061da546Spatrick                                       &kCFTypeDictionaryValueCallBacks));
157061da546Spatrick 
158061da546Spatrick       // Get the process id for the app (if there is one)
159061da546Spatrick       pid_t pid = INVALID_NUB_PROCESS;
160061da546Spatrick       if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier,
161061da546Spatrick                                              &pid) == true) {
162061da546Spatrick         CFReleaser<CFNumberRef> pidCFNumber(
163061da546Spatrick             ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid));
164061da546Spatrick         ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
165061da546Spatrick                                pidCFNumber.get());
166061da546Spatrick       }
167061da546Spatrick 
168061da546Spatrick       // Set a boolean to indicate if this is the front most
169061da546Spatrick       if (sbsFrontAppID.get() && displayIdentifier &&
170061da546Spatrick           (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) ==
171061da546Spatrick            kCFCompareEqualTo))
172061da546Spatrick         ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
173061da546Spatrick                                kCFBooleanTrue);
174061da546Spatrick       else
175061da546Spatrick         ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
176061da546Spatrick                                kCFBooleanFalse);
177061da546Spatrick 
178061da546Spatrick       CFReleaser<CFStringRef> executablePath(
179061da546Spatrick           ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier));
180061da546Spatrick       if (executablePath.get() != NULL) {
181061da546Spatrick         ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
182061da546Spatrick                                executablePath.get());
183061da546Spatrick       }
184061da546Spatrick 
185061da546Spatrick       CFReleaser<CFStringRef> iconImagePath(
186061da546Spatrick           ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier));
187061da546Spatrick       if (iconImagePath.get() != NULL) {
188061da546Spatrick         ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY,
189061da546Spatrick                                iconImagePath.get());
190061da546Spatrick       }
191061da546Spatrick 
192061da546Spatrick       CFReleaser<CFStringRef> localizedDisplayName(
193061da546Spatrick           ::SBSCopyLocalizedApplicationNameForDisplayIdentifier(
194061da546Spatrick               displayIdentifier));
195061da546Spatrick       if (localizedDisplayName.get() != NULL) {
196061da546Spatrick         ::CFDictionarySetValue(appInfoDict.get(),
197061da546Spatrick                                DTSERVICES_APP_DISPLAY_NAME_KEY,
198061da546Spatrick                                localizedDisplayName.get());
199061da546Spatrick       }
200061da546Spatrick 
201061da546Spatrick       // Append the application info to the plist array
202061da546Spatrick       ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get());
203061da546Spatrick     }
204061da546Spatrick   }
205061da546Spatrick #else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
206061da546Spatrick   // When root, show all processes
207061da546Spatrick   bool all_users = (our_uid == 0);
208061da546Spatrick   GetProcesses(plistMutableArray.get(), all_users);
209061da546Spatrick #endif
210061da546Spatrick 
211061da546Spatrick   CFReleaser<CFDataRef> plistData(
212061da546Spatrick       ::CFPropertyListCreateXMLData(alloc, plistMutableArray.get()));
213061da546Spatrick 
214061da546Spatrick   // write plist to service port
215061da546Spatrick   if (plistData.get() != NULL) {
216061da546Spatrick     CFIndex size = ::CFDataGetLength(plistData.get());
217061da546Spatrick     const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get());
218061da546Spatrick     if (bytes != NULL && size > 0) {
219061da546Spatrick       plist.assign((const char *)bytes, size);
220061da546Spatrick       return 0; // Success
221061da546Spatrick     } else {
222061da546Spatrick       DNBLogError("empty application property list.");
223061da546Spatrick       result = -2;
224061da546Spatrick     }
225061da546Spatrick   } else {
226061da546Spatrick     DNBLogError("serializing task list.");
227061da546Spatrick     result = -3;
228061da546Spatrick   }
229061da546Spatrick 
230061da546Spatrick   return result;
231061da546Spatrick }
232