15ffd83dbSDimitry Andric //===-- source/Host/freebsd/Host.cpp --------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include <sys/types.h>
100b57cec5SDimitry Andric
110b57cec5SDimitry Andric #include <sys/exec.h>
120b57cec5SDimitry Andric #include <sys/proc.h>
130b57cec5SDimitry Andric #include <sys/ptrace.h>
140b57cec5SDimitry Andric #include <sys/sysctl.h>
150b57cec5SDimitry Andric #include <sys/user.h>
160b57cec5SDimitry Andric
170b57cec5SDimitry Andric #include <machine/elf.h>
180b57cec5SDimitry Andric
19fe6060f1SDimitry Andric #include <cstdio>
200b57cec5SDimitry Andric #include <dlfcn.h>
210b57cec5SDimitry Andric #include <execinfo.h>
220b57cec5SDimitry Andric
230b57cec5SDimitry Andric #include "lldb/Host/Host.h"
240b57cec5SDimitry Andric #include "lldb/Host/HostInfo.h"
250b57cec5SDimitry Andric #include "lldb/Utility/DataBufferHeap.h"
260b57cec5SDimitry Andric #include "lldb/Utility/DataExtractor.h"
270b57cec5SDimitry Andric #include "lldb/Utility/Endian.h"
280b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
290b57cec5SDimitry Andric #include "lldb/Utility/NameMatches.h"
300b57cec5SDimitry Andric #include "lldb/Utility/ProcessInfo.h"
310b57cec5SDimitry Andric #include "lldb/Utility/Status.h"
320b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h"
330b57cec5SDimitry Andric
34*06c3fb27SDimitry Andric #include "llvm/TargetParser/Host.h"
350b57cec5SDimitry Andric
360b57cec5SDimitry Andric extern "C" {
370b57cec5SDimitry Andric extern char **environ;
380b57cec5SDimitry Andric }
390b57cec5SDimitry Andric
400b57cec5SDimitry Andric namespace lldb_private {
410b57cec5SDimitry Andric class ProcessLaunchInfo;
420b57cec5SDimitry Andric }
430b57cec5SDimitry Andric
440b57cec5SDimitry Andric using namespace lldb;
450b57cec5SDimitry Andric using namespace lldb_private;
460b57cec5SDimitry Andric
470b57cec5SDimitry Andric static bool
GetFreeBSDProcessArgs(const ProcessInstanceInfoMatch * match_info_ptr,ProcessInstanceInfo & process_info)480b57cec5SDimitry Andric GetFreeBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
490b57cec5SDimitry Andric ProcessInstanceInfo &process_info) {
500b57cec5SDimitry Andric if (!process_info.ProcessIDIsValid())
510b57cec5SDimitry Andric return false;
520b57cec5SDimitry Andric
530b57cec5SDimitry Andric int pid = process_info.GetProcessID();
540b57cec5SDimitry Andric
550b57cec5SDimitry Andric int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, pid};
560b57cec5SDimitry Andric
570b57cec5SDimitry Andric char arg_data[8192];
580b57cec5SDimitry Andric size_t arg_data_size = sizeof(arg_data);
590b57cec5SDimitry Andric if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) != 0)
600b57cec5SDimitry Andric return false;
610b57cec5SDimitry Andric
620b57cec5SDimitry Andric DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(),
630b57cec5SDimitry Andric sizeof(void *));
640b57cec5SDimitry Andric lldb::offset_t offset = 0;
650b57cec5SDimitry Andric const char *cstr;
660b57cec5SDimitry Andric
670b57cec5SDimitry Andric cstr = data.GetCStr(&offset);
680b57cec5SDimitry Andric if (!cstr)
690b57cec5SDimitry Andric return false;
700b57cec5SDimitry Andric
710b57cec5SDimitry Andric // Get pathname for pid. If that fails fall back to argv[0].
720b57cec5SDimitry Andric char pathname[MAXPATHLEN];
730b57cec5SDimitry Andric size_t pathname_len = sizeof(pathname);
740b57cec5SDimitry Andric mib[2] = KERN_PROC_PATHNAME;
750b57cec5SDimitry Andric if (::sysctl(mib, 4, pathname, &pathname_len, NULL, 0) == 0)
760b57cec5SDimitry Andric process_info.GetExecutableFile().SetFile(pathname, FileSpec::Style::native);
770b57cec5SDimitry Andric else
780b57cec5SDimitry Andric process_info.GetExecutableFile().SetFile(cstr, FileSpec::Style::native);
790b57cec5SDimitry Andric
800b57cec5SDimitry Andric if (!(match_info_ptr == NULL ||
810b57cec5SDimitry Andric NameMatches(process_info.GetExecutableFile().GetFilename().GetCString(),
820b57cec5SDimitry Andric match_info_ptr->GetNameMatchType(),
830b57cec5SDimitry Andric match_info_ptr->GetProcessInfo().GetName())))
840b57cec5SDimitry Andric return false;
850b57cec5SDimitry Andric
86e8d8bef9SDimitry Andric process_info.SetArg0(cstr);
870b57cec5SDimitry Andric Args &proc_args = process_info.GetArguments();
880b57cec5SDimitry Andric while (1) {
890b57cec5SDimitry Andric const uint8_t *p = data.PeekData(offset, 1);
900b57cec5SDimitry Andric while ((p != NULL) && (*p == '\0') && offset < arg_data_size) {
910b57cec5SDimitry Andric ++offset;
920b57cec5SDimitry Andric p = data.PeekData(offset, 1);
930b57cec5SDimitry Andric }
940b57cec5SDimitry Andric if (p == NULL || offset >= arg_data_size)
950b57cec5SDimitry Andric break;
960b57cec5SDimitry Andric
970b57cec5SDimitry Andric cstr = data.GetCStr(&offset);
980b57cec5SDimitry Andric if (!cstr)
990b57cec5SDimitry Andric break;
1000b57cec5SDimitry Andric
1010b57cec5SDimitry Andric proc_args.AppendArgument(llvm::StringRef(cstr));
1020b57cec5SDimitry Andric }
1030b57cec5SDimitry Andric
1040b57cec5SDimitry Andric return true;
1050b57cec5SDimitry Andric }
1060b57cec5SDimitry Andric
GetFreeBSDProcessCPUType(ProcessInstanceInfo & process_info)1070b57cec5SDimitry Andric static bool GetFreeBSDProcessCPUType(ProcessInstanceInfo &process_info) {
1080b57cec5SDimitry Andric if (process_info.ProcessIDIsValid()) {
1090b57cec5SDimitry Andric process_info.GetArchitecture() =
1100b57cec5SDimitry Andric HostInfo::GetArchitecture(HostInfo::eArchKindDefault);
1110b57cec5SDimitry Andric return true;
1120b57cec5SDimitry Andric }
1130b57cec5SDimitry Andric process_info.GetArchitecture().Clear();
1140b57cec5SDimitry Andric return false;
1150b57cec5SDimitry Andric }
1160b57cec5SDimitry Andric
GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo & process_info)1170b57cec5SDimitry Andric static bool GetFreeBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
1180b57cec5SDimitry Andric struct kinfo_proc proc_kinfo;
1190b57cec5SDimitry Andric size_t proc_kinfo_size;
1200b57cec5SDimitry Andric const int pid = process_info.GetProcessID();
1210b57cec5SDimitry Andric int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
1220b57cec5SDimitry Andric
1230b57cec5SDimitry Andric if (!process_info.ProcessIDIsValid())
1240b57cec5SDimitry Andric goto error;
1250b57cec5SDimitry Andric
1260b57cec5SDimitry Andric proc_kinfo_size = sizeof(struct kinfo_proc);
1270b57cec5SDimitry Andric
1280b57cec5SDimitry Andric if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) != 0)
1290b57cec5SDimitry Andric goto error;
1300b57cec5SDimitry Andric
1310b57cec5SDimitry Andric if (proc_kinfo_size == 0)
1320b57cec5SDimitry Andric goto error;
1330b57cec5SDimitry Andric
1340b57cec5SDimitry Andric process_info.SetParentProcessID(proc_kinfo.ki_ppid);
1350b57cec5SDimitry Andric process_info.SetUserID(proc_kinfo.ki_ruid);
1360b57cec5SDimitry Andric process_info.SetGroupID(proc_kinfo.ki_rgid);
1370b57cec5SDimitry Andric process_info.SetEffectiveUserID(proc_kinfo.ki_uid);
1380b57cec5SDimitry Andric if (proc_kinfo.ki_ngroups > 0)
1390b57cec5SDimitry Andric process_info.SetEffectiveGroupID(proc_kinfo.ki_groups[0]);
1400b57cec5SDimitry Andric else
1410b57cec5SDimitry Andric process_info.SetEffectiveGroupID(UINT32_MAX);
1420b57cec5SDimitry Andric return true;
1430b57cec5SDimitry Andric
1440b57cec5SDimitry Andric error:
1450b57cec5SDimitry Andric process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
1460b57cec5SDimitry Andric process_info.SetUserID(UINT32_MAX);
1470b57cec5SDimitry Andric process_info.SetGroupID(UINT32_MAX);
1480b57cec5SDimitry Andric process_info.SetEffectiveUserID(UINT32_MAX);
1490b57cec5SDimitry Andric process_info.SetEffectiveGroupID(UINT32_MAX);
1500b57cec5SDimitry Andric return false;
1510b57cec5SDimitry Andric }
1520b57cec5SDimitry Andric
FindProcessesImpl(const ProcessInstanceInfoMatch & match_info,ProcessInstanceInfoList & process_infos)1535ffd83dbSDimitry Andric uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
1540b57cec5SDimitry Andric ProcessInstanceInfoList &process_infos) {
1550b57cec5SDimitry Andric const ::pid_t our_pid = ::getpid();
1560b57cec5SDimitry Andric const ::uid_t our_uid = ::getuid();
1570b57cec5SDimitry Andric std::vector<struct kinfo_proc> kinfos;
1580b57cec5SDimitry Andric // Special case, if lldb is being run as root we can attach to anything.
1590b57cec5SDimitry Andric bool all_users = match_info.GetMatchAllUsers() || (our_uid == 0);
1600b57cec5SDimitry Andric
1610b57cec5SDimitry Andric int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
1620b57cec5SDimitry Andric
1630b57cec5SDimitry Andric size_t pid_data_size = 0;
1640b57cec5SDimitry Andric if (::sysctl(mib, 3, NULL, &pid_data_size, NULL, 0) != 0)
1650b57cec5SDimitry Andric return 0;
1660b57cec5SDimitry Andric
1670b57cec5SDimitry Andric // Add a few extra in case a few more show up
1680b57cec5SDimitry Andric const size_t estimated_pid_count =
1690b57cec5SDimitry Andric (pid_data_size / sizeof(struct kinfo_proc)) + 10;
1700b57cec5SDimitry Andric
1710b57cec5SDimitry Andric kinfos.resize(estimated_pid_count);
1720b57cec5SDimitry Andric pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
1730b57cec5SDimitry Andric
1740b57cec5SDimitry Andric if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, NULL, 0) != 0)
1750b57cec5SDimitry Andric return 0;
1760b57cec5SDimitry Andric
1770b57cec5SDimitry Andric const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
1780b57cec5SDimitry Andric
179e8d8bef9SDimitry Andric ProcessInstanceInfoMatch match_info_noname{match_info};
180e8d8bef9SDimitry Andric match_info_noname.SetNameMatchType(NameMatch::Ignore);
181e8d8bef9SDimitry Andric
1820b57cec5SDimitry Andric for (size_t i = 0; i < actual_pid_count; i++) {
1830b57cec5SDimitry Andric const struct kinfo_proc &kinfo = kinfos[i];
1840b57cec5SDimitry Andric
1850b57cec5SDimitry Andric /* Make sure the user is acceptable */
1860b57cec5SDimitry Andric if (!all_users && kinfo.ki_ruid != our_uid)
1870b57cec5SDimitry Andric continue;
1880b57cec5SDimitry Andric
1890b57cec5SDimitry Andric if (kinfo.ki_pid == our_pid || // Skip this process
1900b57cec5SDimitry Andric kinfo.ki_pid == 0 || // Skip kernel (kernel pid is 0)
1910b57cec5SDimitry Andric kinfo.ki_stat == SZOMB || // Zombies are bad
1920b57cec5SDimitry Andric kinfo.ki_flag & P_TRACED || // Being debugged?
1930b57cec5SDimitry Andric kinfo.ki_flag & P_WEXIT) // Working on exiting
1940b57cec5SDimitry Andric continue;
1950b57cec5SDimitry Andric
1960b57cec5SDimitry Andric // Every thread is a process in FreeBSD, but all the threads of a single
1970b57cec5SDimitry Andric // process have the same pid. Do not store the process info in the result
1980b57cec5SDimitry Andric // list if a process with given identifier is already registered there.
1990b57cec5SDimitry Andric bool already_registered = false;
2000b57cec5SDimitry Andric for (uint32_t pi = 0;
2010b57cec5SDimitry Andric !already_registered && (const int)kinfo.ki_numthreads > 1 &&
2025ffd83dbSDimitry Andric pi < (const uint32_t)process_infos.size();
2030b57cec5SDimitry Andric pi++)
2040b57cec5SDimitry Andric already_registered =
2055ffd83dbSDimitry Andric (process_infos[pi].GetProcessID() == (uint32_t)kinfo.ki_pid);
2060b57cec5SDimitry Andric
2070b57cec5SDimitry Andric if (already_registered)
2080b57cec5SDimitry Andric continue;
2090b57cec5SDimitry Andric
2100b57cec5SDimitry Andric ProcessInstanceInfo process_info;
2110b57cec5SDimitry Andric process_info.SetProcessID(kinfo.ki_pid);
2120b57cec5SDimitry Andric process_info.SetParentProcessID(kinfo.ki_ppid);
2130b57cec5SDimitry Andric process_info.SetUserID(kinfo.ki_ruid);
2140b57cec5SDimitry Andric process_info.SetGroupID(kinfo.ki_rgid);
2150b57cec5SDimitry Andric process_info.SetEffectiveUserID(kinfo.ki_svuid);
2160b57cec5SDimitry Andric process_info.SetEffectiveGroupID(kinfo.ki_svgid);
2170b57cec5SDimitry Andric
2180b57cec5SDimitry Andric // Make sure our info matches before we go fetch the name and cpu type
219e8d8bef9SDimitry Andric if (match_info_noname.Matches(process_info) &&
2200b57cec5SDimitry Andric GetFreeBSDProcessArgs(&match_info, process_info)) {
2210b57cec5SDimitry Andric GetFreeBSDProcessCPUType(process_info);
2220b57cec5SDimitry Andric if (match_info.Matches(process_info))
2235ffd83dbSDimitry Andric process_infos.push_back(process_info);
2240b57cec5SDimitry Andric }
2250b57cec5SDimitry Andric }
2260b57cec5SDimitry Andric
2275ffd83dbSDimitry Andric return process_infos.size();
2280b57cec5SDimitry Andric }
2290b57cec5SDimitry Andric
GetProcessInfo(lldb::pid_t pid,ProcessInstanceInfo & process_info)2300b57cec5SDimitry Andric bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
2310b57cec5SDimitry Andric process_info.SetProcessID(pid);
2320b57cec5SDimitry Andric
2330b57cec5SDimitry Andric if (GetFreeBSDProcessArgs(NULL, process_info)) {
2340b57cec5SDimitry Andric // should use libprocstat instead of going right into sysctl?
2350b57cec5SDimitry Andric GetFreeBSDProcessCPUType(process_info);
2360b57cec5SDimitry Andric GetFreeBSDProcessUserAndGroup(process_info);
2370b57cec5SDimitry Andric return true;
2380b57cec5SDimitry Andric }
2390b57cec5SDimitry Andric
2400b57cec5SDimitry Andric process_info.Clear();
2410b57cec5SDimitry Andric return false;
2420b57cec5SDimitry Andric }
2430b57cec5SDimitry Andric
GetEnvironment()2440b57cec5SDimitry Andric Environment Host::GetEnvironment() { return Environment(environ); }
2450b57cec5SDimitry Andric
ShellExpandArguments(ProcessLaunchInfo & launch_info)2460b57cec5SDimitry Andric Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
2470b57cec5SDimitry Andric return Status("unimplemented");
2480b57cec5SDimitry Andric }
249