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