1 //===-- source/Host/openbsd/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 <sys/types.h>
12 
13 #include <sys/signal.h>
14 #include <sys/exec.h>
15 #include <sys/proc.h>
16 #include <sys/ptrace.h>
17 #include <sys/sysctl.h>
18 #include <sys/user.h>
19 
20 #include <stdio.h>
21 
22 // C++ Includes
23 // Other libraries and framework includes
24 // Project includes
25 #include "lldb/Core/Module.h"
26 #include "lldb/Core/StreamFile.h"
27 #include "lldb/Host/Host.h"
28 #include "lldb/Host/HostInfo.h"
29 #include "lldb/Target/Platform.h"
30 #include "lldb/Target/Process.h"
31 #include "lldb/Utility/DataExtractor.h"
32 #include "lldb/Utility/Endian.h"
33 #include "lldb/Utility/Log.h"
34 #include "lldb/Utility/Status.h"
35 #include "lldb/Utility/StreamString.h"
36 
37 #include "lldb/Utility/CleanUp.h"
38 #include "lldb/Utility/DataBufferHeap.h"
39 #include "lldb/Utility/DataExtractor.h"
40 #include "lldb/Utility/NameMatches.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 *v;
53   char **var = environ;
54   for (; var != NULL && *var != NULL; ++var) {
55     v = strchr(*var, (int)'-');
56     if (v == NULL)
57       continue;
58     env.AppendString(v);
59   }
60   return env.GetSize();
61 }
62 
63 static bool
64 GetOpenBSDProcessArgs(const ProcessInstanceInfoMatch *match_info_ptr,
65                       ProcessInstanceInfo &process_info) {
66   if (process_info.ProcessIDIsValid()) {
67     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS,
68                   (int)process_info.GetProcessID()};
69 
70     char arg_data[8192];
71     size_t arg_data_size = sizeof(arg_data);
72     if (::sysctl(mib, 4, arg_data, &arg_data_size, NULL, 0) == 0) {
73       DataExtractor data(arg_data, arg_data_size, endian::InlHostByteOrder(),
74                          sizeof(void *));
75       lldb::offset_t offset = 0;
76       const char *cstr;
77 
78       cstr = data.GetCStr(&offset);
79       if (cstr) {
80         process_info.GetExecutableFile().SetFile(cstr, false);
81 
82         if (!(match_info_ptr == NULL ||
83               NameMatches(
84                   process_info.GetExecutableFile().GetFilename().GetCString(),
85                   match_info_ptr->GetNameMatchType(),
86                   match_info_ptr->GetProcessInfo().GetName())))
87           return false;
88 
89         Args &proc_args = process_info.GetArguments();
90         while (1) {
91           const uint8_t *p = data.PeekData(offset, 1);
92           while ((p != NULL) && (*p == '\0') && offset < arg_data_size) {
93             ++offset;
94             p = data.PeekData(offset, 1);
95           }
96           if (p == NULL || offset >= arg_data_size)
97             return true;
98 
99           cstr = data.GetCStr(&offset);
100           if (cstr)
101             proc_args.AppendArgument(llvm::StringRef(cstr));
102           else
103             return true;
104         }
105       }
106     }
107   }
108   return false;
109 }
110 
111 static bool GetOpenBSDProcessCPUType(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 GetOpenBSDProcessUserAndGroup(ProcessInstanceInfo &process_info) {
122   struct kinfo_proc proc_kinfo;
123   size_t proc_kinfo_size;
124 
125   if (process_info.ProcessIDIsValid()) {
126     int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID,
127                   (int)process_info.GetProcessID()};
128     proc_kinfo_size = sizeof(struct kinfo_proc);
129 
130     if (::sysctl(mib, 4, &proc_kinfo, &proc_kinfo_size, NULL, 0) == 0) {
131       if (proc_kinfo_size > 0) {
132         process_info.SetParentProcessID(proc_kinfo.p_ppid);
133         process_info.SetUserID(proc_kinfo.p_ruid);
134         process_info.SetGroupID(proc_kinfo.p_rgid);
135         process_info.SetEffectiveUserID(proc_kinfo.p_uid);
136 	process_info.SetEffectiveGroupID(proc_kinfo.p_gid);
137         return true;
138       }
139     }
140   }
141   process_info.SetParentProcessID(LLDB_INVALID_PROCESS_ID);
142   process_info.SetUserID(UINT32_MAX);
143   process_info.SetGroupID(UINT32_MAX);
144   process_info.SetEffectiveUserID(UINT32_MAX);
145   process_info.SetEffectiveGroupID(UINT32_MAX);
146   return false;
147 }
148 
149 uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
150                              ProcessInstanceInfoList &process_infos) {
151   std::vector<struct kinfo_proc> kinfos;
152 
153   int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
154 
155   size_t pid_data_size = 0;
156   if (::sysctl(mib, 3, NULL, &pid_data_size, NULL, 0) != 0)
157     return 0;
158 
159   // Add a few extra in case a few more show up
160   const size_t estimated_pid_count =
161       (pid_data_size / sizeof(struct kinfo_proc)) + 10;
162 
163   kinfos.resize(estimated_pid_count);
164   pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
165 
166   if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, NULL, 0) != 0)
167     return 0;
168 
169   const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
170 
171   bool all_users = match_info.GetMatchAllUsers();
172   const ::pid_t our_pid = getpid();
173   const uid_t our_uid = getuid();
174   for (size_t i = 0; i < actual_pid_count; i++) {
175     const struct kinfo_proc &kinfo = kinfos[i];
176     const bool kinfo_user_matches = (all_users || (kinfo.p_ruid == our_uid) ||
177                                      // Special case, if lldb is being run as
178                                      // root we can attach to anything.
179                                      (our_uid == 0));
180 
181     if (kinfo_user_matches == false || // Make sure the user is acceptable
182         kinfo.p_pid == our_pid ||     // Skip this process
183         kinfo.p_pid == 0 ||           // Skip kernel (kernel pid is zero)
184         kinfo.p_stat == SZOMB ||      // Zombies are bad, they like brains...
185         kinfo.p_psflags & PS_TRACED || // Being debugged?
186         kinfo.p_flag & P_WEXIT)       // Working on exiting
187       continue;
188 
189     ProcessInstanceInfo process_info;
190     process_info.SetProcessID(kinfo.p_pid);
191     process_info.SetParentProcessID(kinfo.p_ppid);
192     process_info.SetUserID(kinfo.p_ruid);
193     process_info.SetGroupID(kinfo.p_rgid);
194     process_info.SetEffectiveUserID(kinfo.p_svuid);
195     process_info.SetEffectiveGroupID(kinfo.p_svgid);
196 
197     // Make sure our info matches before we go fetch the name and cpu type
198     if (match_info.Matches(process_info) &&
199         GetOpenBSDProcessArgs(&match_info, process_info)) {
200       GetOpenBSDProcessCPUType(process_info);
201       if (match_info.Matches(process_info))
202         process_infos.Append(process_info);
203     }
204   }
205 
206   return process_infos.GetSize();
207 }
208 
209 bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
210   process_info.SetProcessID(pid);
211 
212   if (GetOpenBSDProcessArgs(NULL, process_info)) {
213     // should use libprocstat instead of going right into sysctl?
214     GetOpenBSDProcessCPUType(process_info);
215     GetOpenBSDProcessUserAndGroup(process_info);
216     return true;
217   }
218 
219   process_info.Clear();
220   return false;
221 }
222 
223 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
224   return Status("unimplemented");
225 }
226