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