1//===-- ProcessInfo.cpp -----------------------------------------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Utility/ProcessInfo.h"
10
11#include "lldb/Utility/ArchSpec.h"
12#include "lldb/Utility/Stream.h"
13#include "lldb/Utility/StreamString.h"
14#include "lldb/Utility/UserIDResolver.h"
15#include "llvm/ADT/SmallString.h"
16
17#include <climits>
18
19using namespace lldb;
20using namespace lldb_private;
21
22ProcessInfo::ProcessInfo()
23    : m_executable(), m_arguments(), m_environment(), m_uid(UINT32_MAX),
24      m_gid(UINT32_MAX), m_arch(), m_pid(LLDB_INVALID_PROCESS_ID) {}
25
26ProcessInfo::ProcessInfo(const char *name, const ArchSpec &arch,
27                         lldb::pid_t pid)
28    : m_executable(name), m_arguments(), m_environment(), m_uid(UINT32_MAX),
29      m_gid(UINT32_MAX), m_arch(arch), m_pid(pid) {}
30
31void ProcessInfo::Clear() {
32  m_executable.Clear();
33  m_arguments.Clear();
34  m_environment.clear();
35  m_uid = UINT32_MAX;
36  m_gid = UINT32_MAX;
37  m_arch.Clear();
38  m_pid = LLDB_INVALID_PROCESS_ID;
39}
40
41const char *ProcessInfo::GetName() const {
42  return m_executable.GetFilename().GetCString();
43}
44
45llvm::StringRef ProcessInfo::GetNameAsStringRef() const {
46  return m_executable.GetFilename().GetStringRef();
47}
48
49void ProcessInfo::Dump(Stream &s, Platform *platform) const {
50  s << "Executable: " << GetName() << "\n";
51  s << "Triple: ";
52  m_arch.DumpTriple(s.AsRawOstream());
53  s << "\n";
54
55  s << "Arguments:\n";
56  m_arguments.Dump(s);
57
58  s.Format("Environment:\n{0}", m_environment);
59}
60
61void ProcessInfo::SetExecutableFile(const FileSpec &exe_file,
62                                    bool add_exe_file_as_first_arg) {
63  if (exe_file) {
64    m_executable = exe_file;
65    if (add_exe_file_as_first_arg) {
66      llvm::SmallString<128> filename;
67      exe_file.GetPath(filename);
68      if (!filename.empty())
69        m_arguments.InsertArgumentAtIndex(0, filename);
70    }
71  } else {
72    m_executable.Clear();
73  }
74}
75
76llvm::StringRef ProcessInfo::GetArg0() const { return m_arg0; }
77
78void ProcessInfo::SetArg0(llvm::StringRef arg) { m_arg0 = arg; }
79
80void ProcessInfo::SetArguments(char const **argv,
81                               bool first_arg_is_executable) {
82  m_arguments.SetArguments(argv);
83
84  // Is the first argument the executable?
85  if (first_arg_is_executable) {
86    const char *first_arg = m_arguments.GetArgumentAtIndex(0);
87    if (first_arg) {
88      // Yes the first argument is an executable, set it as the executable in
89      // the launch options. Don't resolve the file path as the path could be a
90      // remote platform path
91      m_executable.SetFile(first_arg, FileSpec::Style::native);
92    }
93  }
94}
95
96void ProcessInfo::SetArguments(const Args &args, bool first_arg_is_executable) {
97  // Copy all arguments
98  m_arguments = args;
99
100  // Is the first argument the executable?
101  if (first_arg_is_executable) {
102    const char *first_arg = m_arguments.GetArgumentAtIndex(0);
103    if (first_arg) {
104      // Yes the first argument is an executable, set it as the executable in
105      // the launch options. Don't resolve the file path as the path could be a
106      // remote platform path
107      m_executable.SetFile(first_arg, FileSpec::Style::native);
108    }
109  }
110}
111
112void ProcessInstanceInfo::Dump(Stream &s, UserIDResolver &resolver) const {
113  if (m_pid != LLDB_INVALID_PROCESS_ID)
114    s.Printf("    pid = %" PRIu64 "\n", m_pid);
115
116  if (m_parent_pid != LLDB_INVALID_PROCESS_ID)
117    s.Printf(" parent = %" PRIu64 "\n", m_parent_pid);
118
119  if (m_executable) {
120    s.Printf("   name = %s\n", m_executable.GetFilename().GetCString());
121    s.PutCString("   file = ");
122    m_executable.Dump(s.AsRawOstream());
123    s.EOL();
124  }
125  const uint32_t argc = m_arguments.GetArgumentCount();
126  if (argc > 0) {
127    for (uint32_t i = 0; i < argc; i++) {
128      const char *arg = m_arguments.GetArgumentAtIndex(i);
129      if (i < 10)
130        s.Printf(" arg[%u] = %s\n", i, arg);
131      else
132        s.Printf("arg[%u] = %s\n", i, arg);
133    }
134  }
135
136  s.Format("{0}", m_environment);
137
138  if (m_arch.IsValid()) {
139    s.Printf("   arch = ");
140    m_arch.DumpTriple(s.AsRawOstream());
141    s.EOL();
142  }
143
144  if (UserIDIsValid()) {
145    s.Format("    uid = {0,-5} ({1})\n", GetUserID(),
146             resolver.GetUserName(GetUserID()).getValueOr(""));
147  }
148  if (GroupIDIsValid()) {
149    s.Format("    gid = {0,-5} ({1})\n", GetGroupID(),
150             resolver.GetGroupName(GetGroupID()).getValueOr(""));
151  }
152  if (EffectiveUserIDIsValid()) {
153    s.Format("   euid = {0,-5} ({1})\n", GetEffectiveUserID(),
154             resolver.GetUserName(GetEffectiveUserID()).getValueOr(""));
155  }
156  if (EffectiveGroupIDIsValid()) {
157    s.Format("   egid = {0,-5} ({1})\n", GetEffectiveGroupID(),
158             resolver.GetGroupName(GetEffectiveGroupID()).getValueOr(""));
159  }
160}
161
162void ProcessInstanceInfo::DumpTableHeader(Stream &s, bool show_args,
163                                          bool verbose) {
164  const char *label;
165  if (show_args || verbose)
166    label = "ARGUMENTS";
167  else
168    label = "NAME";
169
170  if (verbose) {
171    s.Printf("PID    PARENT USER       GROUP      EFF USER   EFF GROUP  TRIPLE "
172             "                        %s\n",
173             label);
174    s.PutCString(
175        "====== ====== ========== ========== ========== ========== "
176        "============================== ============================\n");
177  } else {
178    s.Printf("PID    PARENT USER       TRIPLE                         %s\n",
179             label);
180    s.PutCString("====== ====== ========== ============================== "
181                 "============================\n");
182  }
183}
184
185void ProcessInstanceInfo::DumpAsTableRow(Stream &s, UserIDResolver &resolver,
186                                         bool show_args, bool verbose) const {
187  if (m_pid != LLDB_INVALID_PROCESS_ID) {
188    s.Printf("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, m_parent_pid);
189
190    StreamString arch_strm;
191    if (m_arch.IsValid())
192      m_arch.DumpTriple(arch_strm.AsRawOstream());
193
194    auto print = [&](bool (ProcessInstanceInfo::*isValid)() const,
195                     uint32_t (ProcessInstanceInfo::*getID)() const,
196                     llvm::Optional<llvm::StringRef> (UserIDResolver::*getName)(
197                         UserIDResolver::id_t id)) {
198      const char *format = "{0,-10} ";
199      if (!(this->*isValid)()) {
200        s.Format(format, "");
201        return;
202      }
203      uint32_t id = (this->*getID)();
204      if (auto name = (resolver.*getName)(id))
205        s.Format(format, *name);
206      else
207        s.Format(format, id);
208    };
209    if (verbose) {
210      print(&ProcessInstanceInfo::UserIDIsValid,
211            &ProcessInstanceInfo::GetUserID, &UserIDResolver::GetUserName);
212      print(&ProcessInstanceInfo::GroupIDIsValid,
213            &ProcessInstanceInfo::GetGroupID, &UserIDResolver::GetGroupName);
214      print(&ProcessInstanceInfo::EffectiveUserIDIsValid,
215            &ProcessInstanceInfo::GetEffectiveUserID,
216            &UserIDResolver::GetUserName);
217      print(&ProcessInstanceInfo::EffectiveGroupIDIsValid,
218            &ProcessInstanceInfo::GetEffectiveGroupID,
219            &UserIDResolver::GetGroupName);
220
221      s.Printf("%-30s ", arch_strm.GetData());
222    } else {
223      print(&ProcessInstanceInfo::EffectiveUserIDIsValid,
224            &ProcessInstanceInfo::GetEffectiveUserID,
225            &UserIDResolver::GetUserName);
226      s.Printf("%-30s ", arch_strm.GetData());
227    }
228
229    if (verbose || show_args) {
230      s.PutCString(m_arg0);
231      const uint32_t argc = m_arguments.GetArgumentCount();
232      for (uint32_t i = 0; i < argc; i++) {
233        s.PutChar(' ');
234        s.PutCString(m_arguments.GetArgumentAtIndex(i));
235      }
236    } else {
237      s.PutCString(GetName());
238    }
239
240    s.EOL();
241  }
242}
243
244bool ProcessInstanceInfoMatch::ArchitectureMatches(
245    const ArchSpec &arch_spec) const {
246  return !m_match_info.GetArchitecture().IsValid() ||
247         m_match_info.GetArchitecture().IsCompatibleMatch(arch_spec);
248}
249
250bool ProcessInstanceInfoMatch::NameMatches(const char *process_name) const {
251  if (m_name_match_type == NameMatch::Ignore)
252    return true;
253  const char *match_name = m_match_info.GetName();
254  if (!match_name)
255    return true;
256
257  return lldb_private::NameMatches(process_name, m_name_match_type, match_name);
258}
259
260bool ProcessInstanceInfoMatch::ProcessIDsMatch(
261    const ProcessInstanceInfo &proc_info) const {
262  if (m_match_info.ProcessIDIsValid() &&
263      m_match_info.GetProcessID() != proc_info.GetProcessID())
264    return false;
265
266  if (m_match_info.ParentProcessIDIsValid() &&
267      m_match_info.GetParentProcessID() != proc_info.GetParentProcessID())
268    return false;
269  return true;
270}
271
272bool ProcessInstanceInfoMatch::UserIDsMatch(
273    const ProcessInstanceInfo &proc_info) const {
274  if (m_match_info.UserIDIsValid() &&
275      m_match_info.GetUserID() != proc_info.GetUserID())
276    return false;
277
278  if (m_match_info.GroupIDIsValid() &&
279      m_match_info.GetGroupID() != proc_info.GetGroupID())
280    return false;
281
282  if (m_match_info.EffectiveUserIDIsValid() &&
283      m_match_info.GetEffectiveUserID() != proc_info.GetEffectiveUserID())
284    return false;
285
286  if (m_match_info.EffectiveGroupIDIsValid() &&
287      m_match_info.GetEffectiveGroupID() != proc_info.GetEffectiveGroupID())
288    return false;
289  return true;
290}
291bool ProcessInstanceInfoMatch::Matches(
292    const ProcessInstanceInfo &proc_info) const {
293  return ArchitectureMatches(proc_info.GetArchitecture()) &&
294         ProcessIDsMatch(proc_info) && UserIDsMatch(proc_info) &&
295         NameMatches(proc_info.GetName());
296}
297
298bool ProcessInstanceInfoMatch::MatchAllProcesses() const {
299  if (m_name_match_type != NameMatch::Ignore)
300    return false;
301
302  if (m_match_info.ProcessIDIsValid())
303    return false;
304
305  if (m_match_info.ParentProcessIDIsValid())
306    return false;
307
308  if (m_match_info.UserIDIsValid())
309    return false;
310
311  if (m_match_info.GroupIDIsValid())
312    return false;
313
314  if (m_match_info.EffectiveUserIDIsValid())
315    return false;
316
317  if (m_match_info.EffectiveGroupIDIsValid())
318    return false;
319
320  if (m_match_info.GetArchitecture().IsValid())
321    return false;
322
323  if (m_match_all_users)
324    return false;
325
326  return true;
327}
328
329void ProcessInstanceInfoMatch::Clear() {
330  m_match_info.Clear();
331  m_name_match_type = NameMatch::Ignore;
332  m_match_all_users = false;
333}
334