130fdc8d8SChris Lattner //===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//
230fdc8d8SChris Lattner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
630fdc8d8SChris Lattner //
730fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
830fdc8d8SChris Lattner //
930fdc8d8SChris Lattner // Created by Christopher Friesen on 3/21/08.
1030fdc8d8SChris Lattner //
1130fdc8d8SChris Lattner //===----------------------------------------------------------------------===//
1230fdc8d8SChris Lattner
13a026de05SBruce Mitchener #include "RNBServices.h"
1430fdc8d8SChris Lattner
15*843a0f97SJason Molenda #include "DNB.h"
169eb4e038SGreg Clayton #include "CFString.h"
17a026de05SBruce Mitchener #include "DNBLog.h"
186f35f5cfSGreg Clayton #include "MacOSX/CFUtils.h"
19b9c1b51eSKate Stone #include <CoreFoundation/CoreFoundation.h>
20b9c1b51eSKate Stone #include <libproc.h>
21b9c1b51eSKate Stone #include <sys/sysctl.h>
22b9c1b51eSKate Stone #include <unistd.h>
23b9c1b51eSKate Stone #include <vector>
2430fdc8d8SChris Lattner
25b9c1b51eSKate Stone // For now only SpringBoard has a notion of "Applications" that it can list for
26b9c1b51eSKate Stone // us.
27a332978bSJason Molenda // So we have to use the SpringBoard API's here.
28a332978bSJason Molenda #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
29a026de05SBruce Mitchener #include <SpringBoardServices/SpringBoardServices.h>
3030fdc8d8SChris Lattner #endif
3130fdc8d8SChris Lattner
GetProcesses(CFMutableArrayRef plistMutableArray,bool all_users)32b9c1b51eSKate Stone int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) {
339eb4e038SGreg Clayton if (plistMutableArray == NULL)
349eb4e038SGreg Clayton return -1;
359eb4e038SGreg Clayton
369eb4e038SGreg Clayton // Running as root, get all processes
379eb4e038SGreg Clayton std::vector<struct kinfo_proc> proc_infos;
38*843a0f97SJason Molenda const size_t num_proc_infos = DNBGetAllInfos(proc_infos);
39b9c1b51eSKate Stone if (num_proc_infos > 0) {
409eb4e038SGreg Clayton const pid_t our_pid = getpid();
419eb4e038SGreg Clayton const uid_t our_uid = getuid();
429eb4e038SGreg Clayton uint32_t i;
439eb4e038SGreg Clayton CFAllocatorRef alloc = kCFAllocatorDefault;
449eb4e038SGreg Clayton
45b9c1b51eSKate Stone for (i = 0; i < num_proc_infos; i++) {
469eb4e038SGreg Clayton struct kinfo_proc &proc_info = proc_infos[i];
479eb4e038SGreg Clayton
489eb4e038SGreg Clayton bool kinfo_user_matches;
499eb4e038SGreg Clayton // Special case, if lldb is being run as root we can attach to anything.
509eb4e038SGreg Clayton if (all_users)
519eb4e038SGreg Clayton kinfo_user_matches = true;
529eb4e038SGreg Clayton else
539eb4e038SGreg Clayton kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid;
549eb4e038SGreg Clayton
559eb4e038SGreg Clayton const pid_t pid = proc_info.kp_proc.p_pid;
569eb4e038SGreg Clayton // Skip zombie processes and processes with unset status
57a6682a41SJonas Devlieghere if (!kinfo_user_matches || // User is acceptable
589eb4e038SGreg Clayton pid == our_pid || // Skip this process
599eb4e038SGreg Clayton pid == 0 || // Skip kernel (kernel pid is zero)
60b9c1b51eSKate Stone proc_info.kp_proc.p_stat ==
61b9c1b51eSKate Stone SZOMB || // Zombies are bad, they like brains...
629eb4e038SGreg Clayton proc_info.kp_proc.p_flag & P_TRACED || // Being debugged?
63441aebc5SVedant Kumar proc_info.kp_proc.p_flag & P_WEXIT // Working on exiting?
64441aebc5SVedant Kumar )
659eb4e038SGreg Clayton continue;
669eb4e038SGreg Clayton
679eb4e038SGreg Clayton // Create a new mutable dictionary for each application
68b9c1b51eSKate Stone CFReleaser<CFMutableDictionaryRef> appInfoDict(
69b9c1b51eSKate Stone ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
70b9c1b51eSKate Stone &kCFTypeDictionaryValueCallBacks));
719eb4e038SGreg Clayton
729eb4e038SGreg Clayton // Get the process id for the app (if there is one)
739eb4e038SGreg Clayton const int32_t pid_int32 = pid;
74b9c1b51eSKate Stone CFReleaser<CFNumberRef> pidCFNumber(
75b9c1b51eSKate Stone ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32));
76b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
77b9c1b51eSKate Stone pidCFNumber.get());
789eb4e038SGreg Clayton
794ebdee0aSBruce Mitchener // Set a boolean to indicate if this is the front most
80b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
81b9c1b51eSKate Stone kCFBooleanFalse);
829eb4e038SGreg Clayton
839eb4e038SGreg Clayton const char *pid_basename = proc_info.kp_proc.p_comm;
849eb4e038SGreg Clayton char proc_path_buf[PATH_MAX];
859eb4e038SGreg Clayton
869eb4e038SGreg Clayton int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX);
87b9c1b51eSKate Stone if (return_val > 0) {
889eb4e038SGreg Clayton // Okay, now search backwards from that to see if there is a
89b9c1b51eSKate Stone // slash in the name. Note, even though we got all the args we don't
90b9c1b51eSKate Stone // care
91b9c1b51eSKate Stone // because the list data is just a bunch of concatenated null terminated
92b9c1b51eSKate Stone // strings
939eb4e038SGreg Clayton // so strrchr will start from the end of argv0.
949eb4e038SGreg Clayton
959eb4e038SGreg Clayton pid_basename = strrchr(proc_path_buf, '/');
96b9c1b51eSKate Stone if (pid_basename) {
979eb4e038SGreg Clayton // Skip the '/'
989eb4e038SGreg Clayton ++pid_basename;
99b9c1b51eSKate Stone } else {
100b9c1b51eSKate Stone // We didn't find a directory delimiter in the process argv[0], just
101b9c1b51eSKate Stone // use what was in there
1029eb4e038SGreg Clayton pid_basename = proc_path_buf;
1039eb4e038SGreg Clayton }
1049eb4e038SGreg Clayton CFString cf_pid_path(proc_path_buf);
1059eb4e038SGreg Clayton if (cf_pid_path.get())
106b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
107b9c1b51eSKate Stone cf_pid_path.get());
1089eb4e038SGreg Clayton }
1099eb4e038SGreg Clayton
110b9c1b51eSKate Stone if (pid_basename && pid_basename[0]) {
1119eb4e038SGreg Clayton CFString pid_name(pid_basename);
112b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(),
113b9c1b51eSKate Stone DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get());
1149eb4e038SGreg Clayton }
1159eb4e038SGreg Clayton
1169eb4e038SGreg Clayton // Append the application info to the plist array
1179eb4e038SGreg Clayton ::CFArrayAppendValue(plistMutableArray, appInfoDict.get());
1189eb4e038SGreg Clayton }
1199eb4e038SGreg Clayton }
1209eb4e038SGreg Clayton return 0;
1219eb4e038SGreg Clayton }
ListApplications(std::string & plist,bool opt_runningApps,bool opt_debuggable)122b9c1b51eSKate Stone int ListApplications(std::string &plist, bool opt_runningApps,
123b9c1b51eSKate Stone bool opt_debuggable) {
12430fdc8d8SChris Lattner int result = -1;
12530fdc8d8SChris Lattner
12630fdc8d8SChris Lattner CFAllocatorRef alloc = kCFAllocatorDefault;
12730fdc8d8SChris Lattner
128b9c1b51eSKate Stone // Create a mutable array that we can populate. Specify zero so it can be of
129b9c1b51eSKate Stone // any size.
130b9c1b51eSKate Stone CFReleaser<CFMutableArrayRef> plistMutableArray(
131b9c1b51eSKate Stone ::CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks));
13230fdc8d8SChris Lattner
1339eb4e038SGreg Clayton const uid_t our_uid = getuid();
1349eb4e038SGreg Clayton
135a332978bSJason Molenda #if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
1369eb4e038SGreg Clayton
137b9c1b51eSKate Stone if (our_uid == 0) {
1389eb4e038SGreg Clayton bool all_users = true;
139a332978bSJason Molenda result = GetProcesses(plistMutableArray.get(), all_users);
140b9c1b51eSKate Stone } else {
141b9c1b51eSKate Stone CFReleaser<CFStringRef> sbsFrontAppID(
142b9c1b51eSKate Stone ::SBSCopyFrontmostApplicationDisplayIdentifier());
143b9c1b51eSKate Stone CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers(
144b9c1b51eSKate Stone opt_runningApps, opt_debuggable));
14530fdc8d8SChris Lattner
146bbc00939SJohnny Chen // Need to check the return value from SBSCopyApplicationDisplayIdentifiers.
147bbc00939SJohnny Chen CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount(sbsAppIDs.get()) : 0;
14830fdc8d8SChris Lattner CFIndex i = 0;
149b9c1b51eSKate Stone for (i = 0; i < count; i++) {
150b9c1b51eSKate Stone CFStringRef displayIdentifier =
151b9c1b51eSKate Stone (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i);
15230fdc8d8SChris Lattner
15330fdc8d8SChris Lattner // Create a new mutable dictionary for each application
154b9c1b51eSKate Stone CFReleaser<CFMutableDictionaryRef> appInfoDict(
155b9c1b51eSKate Stone ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
156b9c1b51eSKate Stone &kCFTypeDictionaryValueCallBacks));
15730fdc8d8SChris Lattner
15830fdc8d8SChris Lattner // Get the process id for the app (if there is one)
15930fdc8d8SChris Lattner pid_t pid = INVALID_NUB_PROCESS;
160b9c1b51eSKate Stone if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier,
161b9c1b51eSKate Stone &pid) == true) {
162b9c1b51eSKate Stone CFReleaser<CFNumberRef> pidCFNumber(
163b9c1b51eSKate Stone ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid));
164b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
165b9c1b51eSKate Stone pidCFNumber.get());
16630fdc8d8SChris Lattner }
16730fdc8d8SChris Lattner
1684ebdee0aSBruce Mitchener // Set a boolean to indicate if this is the front most
169b9c1b51eSKate Stone if (sbsFrontAppID.get() && displayIdentifier &&
170b9c1b51eSKate Stone (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) ==
171b9c1b51eSKate Stone kCFCompareEqualTo))
172b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
173b9c1b51eSKate Stone kCFBooleanTrue);
17430fdc8d8SChris Lattner else
175b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
176b9c1b51eSKate Stone kCFBooleanFalse);
17730fdc8d8SChris Lattner
178b9c1b51eSKate Stone CFReleaser<CFStringRef> executablePath(
179b9c1b51eSKate Stone ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier));
180b9c1b51eSKate Stone if (executablePath.get() != NULL) {
181b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
182b9c1b51eSKate Stone executablePath.get());
18330fdc8d8SChris Lattner }
18430fdc8d8SChris Lattner
185b9c1b51eSKate Stone CFReleaser<CFStringRef> iconImagePath(
186b9c1b51eSKate Stone ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier));
187b9c1b51eSKate Stone if (iconImagePath.get() != NULL) {
188b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY,
189b9c1b51eSKate Stone iconImagePath.get());
19030fdc8d8SChris Lattner }
19130fdc8d8SChris Lattner
192b9c1b51eSKate Stone CFReleaser<CFStringRef> localizedDisplayName(
193b9c1b51eSKate Stone ::SBSCopyLocalizedApplicationNameForDisplayIdentifier(
194b9c1b51eSKate Stone displayIdentifier));
195b9c1b51eSKate Stone if (localizedDisplayName.get() != NULL) {
196b9c1b51eSKate Stone ::CFDictionarySetValue(appInfoDict.get(),
197b9c1b51eSKate Stone DTSERVICES_APP_DISPLAY_NAME_KEY,
198b9c1b51eSKate Stone localizedDisplayName.get());
19930fdc8d8SChris Lattner }
20030fdc8d8SChris Lattner
20130fdc8d8SChris Lattner // Append the application info to the plist array
20230fdc8d8SChris Lattner ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get());
20330fdc8d8SChris Lattner }
2049eb4e038SGreg Clayton }
205a332978bSJason Molenda #else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
2069eb4e038SGreg Clayton // When root, show all processes
2079eb4e038SGreg Clayton bool all_users = (our_uid == 0);
208aeb9a06fSJason Molenda GetProcesses(plistMutableArray.get(), all_users);
2099eb4e038SGreg Clayton #endif
21030fdc8d8SChris Lattner
211b9c1b51eSKate Stone CFReleaser<CFDataRef> plistData(
212b9c1b51eSKate Stone ::CFPropertyListCreateXMLData(alloc, plistMutableArray.get()));
21330fdc8d8SChris Lattner
21430fdc8d8SChris Lattner // write plist to service port
215b9c1b51eSKate Stone if (plistData.get() != NULL) {
21630fdc8d8SChris Lattner CFIndex size = ::CFDataGetLength(plistData.get());
21730fdc8d8SChris Lattner const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get());
218b9c1b51eSKate Stone if (bytes != NULL && size > 0) {
21907d95614SVedant Kumar plist.assign((const char *)bytes, size);
22030fdc8d8SChris Lattner return 0; // Success
221b9c1b51eSKate Stone } else {
22230fdc8d8SChris Lattner DNBLogError("empty application property list.");
22330fdc8d8SChris Lattner result = -2;
22430fdc8d8SChris Lattner }
225b9c1b51eSKate Stone } else {
22630fdc8d8SChris Lattner DNBLogError("serializing task list.");
22730fdc8d8SChris Lattner result = -3;
22830fdc8d8SChris Lattner }
22930fdc8d8SChris Lattner
23030fdc8d8SChris Lattner return result;
23130fdc8d8SChris Lattner }
232