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 36extern "C" { 37extern char **environ; 38} 39 40using namespace lldb; 41using namespace lldb_private; 42 43namespace lldb_private { 44class ProcessLaunchInfo; 45} 46 47Environment Host::GetEnvironment() { return Environment(environ); } 48 49static 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 102static 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 112static 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 148error: 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 157uint32_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 231bool 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 244Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) { 245 return Status("unimplemented"); 246} 247