1 //===-- source/Host/netbsd/Host.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 <dlfcn.h>
10 #include <execinfo.h>
11 #include <stdio.h>
12 #include <sys/proc.h>
13 #include <sys/sysctl.h>
14 #include <sys/types.h>
15 
16 #include <limits.h>
17 
18 #include <elf.h>
19 #include <kvm.h>
20 #include <sys/exec.h>
21 #include <sys/ptrace.h>
22 
23 #include "lldb/Host/Host.h"
24 #include "lldb/Host/HostInfo.h"
25 #include "lldb/Utility/DataBufferHeap.h"
26 #include "lldb/Utility/DataExtractor.h"
27 #include "lldb/Utility/Endian.h"
28 #include "lldb/Utility/Log.h"
29 #include "lldb/Utility/NameMatches.h"
30 #include "lldb/Utility/ProcessInfo.h"
31 #include "lldb/Utility/Status.h"
32 #include "lldb/Utility/StreamString.h"
33 
34 #include "llvm/Support/Host.h"
35 
36 extern "C" {
37 extern char **environ;
38 }
39 
40 using namespace lldb;
41 using namespace lldb_private;
42 
43 namespace lldb_private {
44 class ProcessLaunchInfo;
45 }
46 
GetEnvironment()47 Environment Host::GetEnvironment() { return Environment(environ); }
48 
GetNetBSDProcessArgs(const ProcessInstanceInfoMatch * match_info_ptr,ProcessInstanceInfo & process_info)49 static bool GetNetBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
50                                  ProcessInstanceInfo &process_info) {
51   if (!process_info.ProcessIDIsValid())
52     return false;
53 
54   int pid = process_info.GetProcessID();
55 
56   int mib[4] = {CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV};
57 
58   char arg_data[8192];
59   size_t arg_data_size = sizeof(arg_data);
60   if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) != 0)
61     return false;
62 
63   DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(),
64                      sizeof(void *));
65   lldb::offset_t offset = 0;
66   const char *cstr;
67 
68   cstr = data.GetCStr(&offset);
69   if (!cstr)
70     return false;
71 
72   process_info.GetExecutableFile().SetFile(cstr,
73                                            FileSpec::Style::native);
74 
75   if (!(match_info_ptr == NULL ||
76         NameMatches(process_info.GetExecutableFile().GetFilename().GetCString(),
77                     match_info_ptr->GetNameMatchType(),
78                     match_info_ptr->GetProcessInfo().GetName())))
79     return false;
80 
81   process_info.SetArg0(cstr);
82   Args &proc_args = process_info.GetArguments();
83   while (1) {
84     const uint8_t *p = data.PeekData(offset, 1);
85     while ((p != NULL) && (*p == '\0') && offset < arg_data_size) {
86       ++offset;
87       p = data.PeekData(offset, 1);
88     }
89     if (p == NULL || offset >= arg_data_size)
90       break;
91 
92     cstr = data.GetCStr(&offset);
93     if (!cstr)
94       break;
95 
96     proc_args.AppendArgument(llvm::StringRef(cstr));
97   }
98 
99   return true;
100 }
101 
GetNetBSDProcessCPUType(ProcessInstanceInfo & process_info)102 static bool GetNetBSDProcessCPUType(ProcessInstanceInfo &process_info) {
103   if (process_info.ProcessIDIsValid()) {
104     process_info.GetArchitecture() =
105         HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
106     return true;
107   }
108   process_info.GetArchitecture().Clear();
109   return false;
110 }
111 
GetNetBSDProcessUserAndGroup(ProcessInstanceInfo & process_info)112 static bool GetNetBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
113   ::kvm_t *kdp;
114   char errbuf[_POSIX2_LINE_MAX]; /* XXX: error string unused */
115 
116   struct ::kinfo_proc2 *proc_kinfo;
117   const int pid = process_info.GetProcessID();
118   int nproc;
119 
120   if (!process_info.ProcessIDIsValid())
121     goto error;
122 
123   if ((kdp = ::kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL)
124     goto error;
125 
126   if ((proc_kinfo = ::kvm_getproc2(kdp, KERN_PROC_PID, pid,
127                                    sizeof(struct ::kinfo_proc2), &nproc)) ==
128       NULL) {
129     ::kvm_close(kdp);
130     goto error;
131   }
132 
133   if (nproc < 1) {
134     ::kvm_close(kdp); /* XXX: we don't check for error here */
135     goto error;
136   }
137 
138   process_info.SetParentProcessID(proc_kinfo->p_ppid);
139   process_info.SetUserID(proc_kinfo->p_ruid);
140   process_info.SetGroupID(proc_kinfo->p_rgid);
141   process_info.SetEffectiveUserID(proc_kinfo->p_uid);
142   process_info.SetEffectiveGroupID(proc_kinfo->p_gid);
143 
144   ::kvm_close(kdp); /* XXX: we don't check for error here */
145 
146   return true;
147 
148 error:
149   process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
150   process_info.SetUserID(UINT32_MAX);
151   process_info.SetGroupID(UINT32_MAX);
152   process_info.SetEffectiveUserID(UINT32_MAX);
153   process_info.SetEffectiveGroupID(UINT32_MAX);
154   return false;
155 }
156 
FindProcesses(const ProcessInstanceInfoMatch & match_info,ProcessInstanceInfoList & process_infos)157 uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
158                              ProcessInstanceInfoList &process_infos) {
159   const ::pid_t our_pid = ::getpid();
160   const ::uid_t our_uid = ::getuid();
161 
162   const bool all_users =
163       match_info.GetMatchAllUsers() ||
164       // Special case, if lldb is being run as root we can attach to anything
165       (our_uid == 0);
166 
167   kvm_t *kdp;
168   char errbuf[_POSIX2_LINE_MAX]; /* XXX: error string unused */
169   if ((kdp = ::kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, errbuf)) == NULL)
170     return 0;
171 
172   struct ::kinfo_proc2 *proc_kinfo;
173   int nproc;
174   if ((proc_kinfo = ::kvm_getproc2(kdp, KERN_PROC_ALL, 0,
175                                    sizeof(struct ::kinfo_proc2), &nproc)) ==
176       NULL) {
177     ::kvm_close(kdp);
178     return 0;
179   }
180 
181   for (int i = 0; i < nproc; i++) {
182     if (proc_kinfo[i].p_pid < 1)
183       continue; /* not valid */
184     /* Make sure the user is acceptable */
185     if (!all_users && proc_kinfo[i].p_ruid != our_uid)
186       continue;
187 
188     if (proc_kinfo[i].p_pid == our_pid ||  // Skip this process
189         proc_kinfo[i].p_pid == 0 ||        // Skip kernel (kernel pid is 0)
190         proc_kinfo[i].p_stat == LSZOMB ||  // Zombies are bad
191         proc_kinfo[i].p_flag & P_TRACED || // Being debugged?
192         proc_kinfo[i].p_flag & P_WEXIT)    // Working on exiting
193       continue;
194 
195     // Every thread is a process in NetBSD, but all the threads of a single
196     // process have the same pid. Do not store the process info in the result
197     // list if a process with given identifier is already registered there.
198     if (proc_kinfo[i].p_nlwps > 1) {
199       bool already_registered = false;
200       for (size_t pi = 0; pi < process_infos.GetSize(); pi++) {
201         if (process_infos.GetProcessIDAtIndex(pi) == proc_kinfo[i].p_pid) {
202           already_registered = true;
203           break;
204         }
205       }
206 
207       if (already_registered)
208         continue;
209     }
210     ProcessInstanceInfo process_info;
211     process_info.SetProcessID(proc_kinfo[i].p_pid);
212     process_info.SetParentProcessID(proc_kinfo[i].p_ppid);
213     process_info.SetUserID(proc_kinfo[i].p_ruid);
214     process_info.SetGroupID(proc_kinfo[i].p_rgid);
215     process_info.SetEffectiveUserID(proc_kinfo[i].p_uid);
216     process_info.SetEffectiveGroupID(proc_kinfo[i].p_gid);
217     // Make sure our info matches before we go fetch the name and cpu type
218     if (match_info.Matches(process_info) &&
219         GetNetBSDProcessArgs(&match_info, process_info)) {
220       GetNetBSDProcessCPUType(process_info);
221       if (match_info.Matches(process_info))
222         process_infos.Append(process_info);
223     }
224   }
225 
226   kvm_close(kdp); /* XXX: we don't check for error here */
227 
228   return process_infos.GetSize();
229 }
230 
GetProcessInfo(lldb::pid_t pid,ProcessInstanceInfo & process_info)231 bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
232   process_info.SetProcessID(pid);
233 
234   if (GetNetBSDProcessArgs(NULL, process_info)) {
235     GetNetBSDProcessCPUType(process_info);
236     GetNetBSDProcessUserAndGroup(process_info);
237     return true;
238   }
239 
240   process_info.Clear();
241   return false;
242 }
243 
ShellExpandArguments(ProcessLaunchInfo & launch_info)244 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
245   return Status("unimplemented");
246 }
247