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