summaryrefslogtreecommitdiff
path: root/tools/debugserver/source/RNBServices.cpp
blob: 89c4c27da1dd69ec341e816dbaf17f3cf116a788 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
//===-- RNBServices.cpp -----------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  Created by Christopher Friesen on 3/21/08.
//
//===----------------------------------------------------------------------===//

#include "RNBServices.h"

#include "CFString.h"
#include "DNBLog.h"
#include "MacOSX/CFUtils.h"
#include <CoreFoundation/CoreFoundation.h>
#include <libproc.h>
#include <sys/sysctl.h>
#include <unistd.h>
#include <vector>

// For now only SpringBoard has a notion of "Applications" that it can list for
// us.
// So we have to use the SpringBoard API's here.
#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)
#include <SpringBoardServices/SpringBoardServices.h>
#endif

// From DNB.cpp
size_t GetAllInfos(std::vector<struct kinfo_proc> &proc_infos);

int GetProcesses(CFMutableArrayRef plistMutableArray, bool all_users) {
  if (plistMutableArray == NULL)
    return -1;

  // Running as root, get all processes
  std::vector<struct kinfo_proc> proc_infos;
  const size_t num_proc_infos = GetAllInfos(proc_infos);
  if (num_proc_infos > 0) {
    const pid_t our_pid = getpid();
    const uid_t our_uid = getuid();
    uint32_t i;
    CFAllocatorRef alloc = kCFAllocatorDefault;

    for (i = 0; i < num_proc_infos; i++) {
      struct kinfo_proc &proc_info = proc_infos[i];

      bool kinfo_user_matches;
      // Special case, if lldb is being run as root we can attach to anything.
      if (all_users)
        kinfo_user_matches = true;
      else
        kinfo_user_matches = proc_info.kp_eproc.e_pcred.p_ruid == our_uid;

      const pid_t pid = proc_info.kp_proc.p_pid;
      // Skip zombie processes and processes with unset status
      if (!kinfo_user_matches || // User is acceptable
          pid == our_pid ||      // Skip this process
          pid == 0 ||            // Skip kernel (kernel pid is zero)
          proc_info.kp_proc.p_stat ==
              SZOMB || // Zombies are bad, they like brains...
          proc_info.kp_proc.p_flag & P_TRACED || // Being debugged?
          proc_info.kp_proc.p_flag & P_WEXIT ||  // Working on exiting?
          proc_info.kp_proc.p_flag &
              P_TRANSLATED) // Skip translated ppc (Rosetta)
        continue;

      // Create a new mutable dictionary for each application
      CFReleaser<CFMutableDictionaryRef> appInfoDict(
          ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
                                      &kCFTypeDictionaryValueCallBacks));

      // Get the process id for the app (if there is one)
      const int32_t pid_int32 = pid;
      CFReleaser<CFNumberRef> pidCFNumber(
          ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid_int32));
      ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
                             pidCFNumber.get());

      // Set a boolean to indicate if this is the front most
      ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
                             kCFBooleanFalse);

      const char *pid_basename = proc_info.kp_proc.p_comm;
      char proc_path_buf[PATH_MAX];

      int return_val = proc_pidpath(pid, proc_path_buf, PATH_MAX);
      if (return_val > 0) {
        // Okay, now search backwards from that to see if there is a
        // slash in the name.  Note, even though we got all the args we don't
        // care
        // because the list data is just a bunch of concatenated null terminated
        // strings
        // so strrchr will start from the end of argv0.

        pid_basename = strrchr(proc_path_buf, '/');
        if (pid_basename) {
          // Skip the '/'
          ++pid_basename;
        } else {
          // We didn't find a directory delimiter in the process argv[0], just
          // use what was in there
          pid_basename = proc_path_buf;
        }
        CFString cf_pid_path(proc_path_buf);
        if (cf_pid_path.get())
          ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
                                 cf_pid_path.get());
      }

      if (pid_basename && pid_basename[0]) {
        CFString pid_name(pid_basename);
        ::CFDictionarySetValue(appInfoDict.get(),
                               DTSERVICES_APP_DISPLAY_NAME_KEY, pid_name.get());
      }

      // Append the application info to the plist array
      ::CFArrayAppendValue(plistMutableArray, appInfoDict.get());
    }
  }
  return 0;
}
int ListApplications(std::string &plist, bool opt_runningApps,
                     bool opt_debuggable) {
  int result = -1;

  CFAllocatorRef alloc = kCFAllocatorDefault;

  // Create a mutable array that we can populate. Specify zero so it can be of
  // any size.
  CFReleaser<CFMutableArrayRef> plistMutableArray(
      ::CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks));

  const uid_t our_uid = getuid();

#if defined(WITH_SPRINGBOARD) || defined(WITH_BKS)

  if (our_uid == 0) {
    bool all_users = true;
    result = GetProcesses(plistMutableArray.get(), all_users);
  } else {
    CFReleaser<CFStringRef> sbsFrontAppID(
        ::SBSCopyFrontmostApplicationDisplayIdentifier());
    CFReleaser<CFArrayRef> sbsAppIDs(::SBSCopyApplicationDisplayIdentifiers(
        opt_runningApps, opt_debuggable));

    // Need to check the return value from SBSCopyApplicationDisplayIdentifiers.
    CFIndex count = sbsAppIDs.get() ? ::CFArrayGetCount(sbsAppIDs.get()) : 0;
    CFIndex i = 0;
    for (i = 0; i < count; i++) {
      CFStringRef displayIdentifier =
          (CFStringRef)::CFArrayGetValueAtIndex(sbsAppIDs.get(), i);

      // Create a new mutable dictionary for each application
      CFReleaser<CFMutableDictionaryRef> appInfoDict(
          ::CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks,
                                      &kCFTypeDictionaryValueCallBacks));

      // Get the process id for the app (if there is one)
      pid_t pid = INVALID_NUB_PROCESS;
      if (::SBSProcessIDForDisplayIdentifier((CFStringRef)displayIdentifier,
                                             &pid) == true) {
        CFReleaser<CFNumberRef> pidCFNumber(
            ::CFNumberCreate(alloc, kCFNumberSInt32Type, &pid));
        ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PID_KEY,
                               pidCFNumber.get());
      }

      // Set a boolean to indicate if this is the front most
      if (sbsFrontAppID.get() && displayIdentifier &&
          (::CFStringCompare(sbsFrontAppID.get(), displayIdentifier, 0) ==
           kCFCompareEqualTo))
        ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
                               kCFBooleanTrue);
      else
        ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_FRONTMOST_KEY,
                               kCFBooleanFalse);

      CFReleaser<CFStringRef> executablePath(
          ::SBSCopyExecutablePathForDisplayIdentifier(displayIdentifier));
      if (executablePath.get() != NULL) {
        ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_PATH_KEY,
                               executablePath.get());
      }

      CFReleaser<CFStringRef> iconImagePath(
          ::SBSCopyIconImagePathForDisplayIdentifier(displayIdentifier));
      if (iconImagePath.get() != NULL) {
        ::CFDictionarySetValue(appInfoDict.get(), DTSERVICES_APP_ICON_PATH_KEY,
                               iconImagePath.get());
      }

      CFReleaser<CFStringRef> localizedDisplayName(
          ::SBSCopyLocalizedApplicationNameForDisplayIdentifier(
              displayIdentifier));
      if (localizedDisplayName.get() != NULL) {
        ::CFDictionarySetValue(appInfoDict.get(),
                               DTSERVICES_APP_DISPLAY_NAME_KEY,
                               localizedDisplayName.get());
      }

      // Append the application info to the plist array
      ::CFArrayAppendValue(plistMutableArray.get(), appInfoDict.get());
    }
  }
#else // #if defined (WITH_SPRINGBOARD) || defined (WITH_BKS)
  // When root, show all processes
  bool all_users = (our_uid == 0);
  GetProcesses(plistMutableArray.get(), all_users);
#endif

  CFReleaser<CFDataRef> plistData(
      ::CFPropertyListCreateXMLData(alloc, plistMutableArray.get()));

  // write plist to service port
  if (plistData.get() != NULL) {
    CFIndex size = ::CFDataGetLength(plistData.get());
    const UInt8 *bytes = ::CFDataGetBytePtr(plistData.get());
    if (bytes != NULL && size > 0) {
      plist.assign((const char *)bytes, size);
      return 0; // Success
    } else {
      DNBLogError("empty application property list.");
      result = -2;
    }
  } else {
    DNBLogError("serializing task list.");
    result = -3;
  }

  return result;
}