1 //===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // Created by Christopher Friesen on 3/21/08.
11 //===----------------------------------------------------------------------===//
13 #include "RNBServices.h"
18 #include "MacOSX/CFUtils.h"
19 #include <CoreFoundation/CoreFoundation.h>
21 #include <sys/sysctl.h>
25 // For now only SpringBoard has a notion of "Applications" that it can list for
27 // So we have to use the SpringBoard API's here.
28 #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
29 #include <SpringBoardServices/SpringBoardServices.h>
32 int GetProcesses(CFMutableArrayRef plistMutableArray
, bool all_users
) {
33 if (plistMutableArray
== NULL
)
36 // Running as root, get all processes
37 std::vector
<struct kinfo_proc
> proc_infos
;
38 const size_t num_proc_infos
= DNBGetAllInfos(proc_infos
);
39 if (num_proc_infos
> 0) {
40 const pid_t our_pid
= getpid();
41 const uid_t our_uid
= getuid();
43 CFAllocatorRef alloc
= kCFAllocatorDefault
;
45 for (i
= 0; i
< num_proc_infos
; i
++) {
46 struct kinfo_proc
&proc_info
= proc_infos
[i
];
48 bool kinfo_user_matches
;
49 // Special case, if lldb is being run as root we can attach to anything.
51 kinfo_user_matches
= true;
53 kinfo_user_matches
= proc_info
.kp_eproc
.e_pcred
.p_ruid
== our_uid
;
55 const pid_t pid
= proc_info
.kp_proc
.p_pid
;
56 // Skip zombie processes and processes with unset status
57 if (!kinfo_user_matches
|| // User is acceptable
58 pid
== our_pid
|| // Skip this process
59 pid
== 0 || // Skip kernel (kernel pid is zero)
60 proc_info
.kp_proc
.p_stat
==
61 SZOMB
|| // Zombies are bad, they like brains...
62 proc_info
.kp_proc
.p_flag
& P_TRACED
|| // Being debugged?
63 proc_info
.kp_proc
.p_flag
& P_WEXIT
// Working on exiting?
67 // Create a new mutable dictionary for each application
68 CFReleaser
<CFMutableDictionaryRef
> appInfoDict(
69 ::CFDictionaryCreateMutable(alloc
, 0, &kCFTypeDictionaryKeyCallBacks
,
70 &kCFTypeDictionaryValueCallBacks
));
72 // Get the process id for the app (if there is one)
73 const int32_t pid_int32
= pid
;
74 CFReleaser
<CFNumberRef
> pidCFNumber(
75 ::CFNumberCreate(alloc
, kCFNumberSInt32Type
, &pid_int32
));
76 ::CFDictionarySetValue(appInfoDict
.get(), DTSERVICES_APP_PID_KEY
,
79 // Set a boolean to indicate if this is the front most
80 ::CFDictionarySetValue(appInfoDict
.get(), DTSERVICES_APP_FRONTMOST_KEY
,
83 const char *pid_basename
= proc_info
.kp_proc
.p_comm
;
84 char proc_path_buf
[PATH_MAX
];
86 int return_val
= proc_pidpath(pid
, proc_path_buf
, PATH_MAX
);
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
91 // because the list data is just a bunch of concatenated null terminated
93 // so strrchr will start from the end of argv0.
95 pid_basename
= strrchr(proc_path_buf
, '/');
100 // We didn't find a directory delimiter in the process argv[0], just
101 // use what was in there
102 pid_basename
= proc_path_buf
;
104 CFString
cf_pid_path(proc_path_buf
);
105 if (cf_pid_path
.get())
106 ::CFDictionarySetValue(appInfoDict
.get(), DTSERVICES_APP_PATH_KEY
,
110 if (pid_basename
&& pid_basename
[0]) {
111 CFString
pid_name(pid_basename
);
112 ::CFDictionarySetValue(appInfoDict
.get(),
113 DTSERVICES_APP_DISPLAY_NAME_KEY
, pid_name
.get());
116 // Append the application info to the plist array
117 ::CFArrayAppendValue(plistMutableArray
, appInfoDict
.get());
122 int ListApplications(std::string
&plist
, bool opt_runningApps
,
123 bool opt_debuggable
) {
126 CFAllocatorRef alloc
= kCFAllocatorDefault
;
128 // Create a mutable array that we can populate. Specify zero so it can be of
130 CFReleaser
<CFMutableArrayRef
> plistMutableArray(
131 ::CFArrayCreateMutable(alloc
, 0, &kCFTypeArrayCallBacks
));
133 const uid_t our_uid
= getuid();
135 #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
138 bool all_users
= true;
139 result
= GetProcesses(plistMutableArray
.get(), all_users
);
141 CFReleaser
<CFStringRef
> sbsFrontAppID(
142 ::SBSCopyFrontmostApplicationDisplayIdentifier());
143 CFReleaser
<CFArrayRef
> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers(
144 opt_runningApps
, opt_debuggable
));
146 // Need to check the return value from SBSCopyApplicationDisplayIdentifiers.
147 CFIndex count
= sbsAppIDs
.get() ? ::CFArrayGetCount(sbsAppIDs
.get()) : 0;
149 for (i
= 0; i
< count
; i
++) {
150 CFStringRef displayIdentifier
=
151 (CFStringRef
)::CFArrayGetValueAtIndex(sbsAppIDs
.get(), i
);
153 // Create a new mutable dictionary for each application
154 CFReleaser
<CFMutableDictionaryRef
> appInfoDict(
155 ::CFDictionaryCreateMutable(alloc
, 0, &kCFTypeDictionaryKeyCallBacks
,
156 &kCFTypeDictionaryValueCallBacks
));
158 // Get the process id for the app (if there is one)
159 pid_t pid
= INVALID_NUB_PROCESS
;
160 if (::SBSProcessIDForDisplayIdentifier((CFStringRef
)displayIdentifier
,
162 CFReleaser
<CFNumberRef
> pidCFNumber(
163 ::CFNumberCreate(alloc
, kCFNumberSInt32Type
, &pid
));
164 ::CFDictionarySetValue(appInfoDict
.get(), DTSERVICES_APP_PID_KEY
,
168 // Set a boolean to indicate if this is the front most
169 if (sbsFrontAppID
.get() && displayIdentifier
&&
170 (::CFStringCompare(sbsFrontAppID
.get(), displayIdentifier
, 0) ==
172 ::CFDictionarySetValue(appInfoDict
.get(), DTSERVICES_APP_FRONTMOST_KEY
,
175 ::CFDictionarySetValue(appInfoDict
.get(), DTSERVICES_APP_FRONTMOST_KEY
,
178 CFReleaser
<CFStringRef
> executablePath(
179 ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier
));
180 if (executablePath
.get() != NULL
) {
181 ::CFDictionarySetValue(appInfoDict
.get(), DTSERVICES_APP_PATH_KEY
,
182 executablePath
.get());
185 CFReleaser
<CFStringRef
> iconImagePath(
186 ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier
));
187 if (iconImagePath
.get() != NULL
) {
188 ::CFDictionarySetValue(appInfoDict
.get(), DTSERVICES_APP_ICON_PATH_KEY
,
189 iconImagePath
.get());
192 CFReleaser
<CFStringRef
> localizedDisplayName(
193 ::SBSCopyLocalizedApplicationNameForDisplayIdentifier(
195 if (localizedDisplayName
.get() != NULL
) {
196 ::CFDictionarySetValue(appInfoDict
.get(),
197 DTSERVICES_APP_DISPLAY_NAME_KEY
,
198 localizedDisplayName
.get());
201 // Append the application info to the plist array
202 ::CFArrayAppendValue(plistMutableArray
.get(), appInfoDict
.get());
205 #else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
206 // When root, show all processes
207 bool all_users
= (our_uid
== 0);
208 GetProcesses(plistMutableArray
.get(), all_users
);
211 CFReleaser
<CFDataRef
> plistData(
212 ::CFPropertyListCreateXMLData(alloc
, plistMutableArray
.get()));
214 // write plist to service port
215 if (plistData
.get() != NULL
) {
216 CFIndex size
= ::CFDataGetLength(plistData
.get());
217 const UInt8
*bytes
= ::CFDataGetBytePtr(plistData
.get());
218 if (bytes
!= NULL
&& size
> 0) {
219 plist
.assign((const char *)bytes
, size
);
222 DNBLogError("empty application property list.");
226 DNBLogError("serializing task list.");