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