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