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