1 //===-- source/Host/linux/Host.cpp ------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include <dirent.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <sys/utsname.h>
17 #include <unistd.h>
18 
19 #include "llvm/Object/ELF.h"
20 #include "llvm/Support/ScopedPrinter.h"
21 
22 #include "lldb/Target/Process.h"
23 #include "lldb/Utility/Log.h"
24 #include "lldb/Utility/Status.h"
25 
26 #include "lldb/Host/FileSystem.h"
27 #include "lldb/Host/Host.h"
28 #include "lldb/Host/HostInfo.h"
29 #include "lldb/Host/linux/Support.h"
30 #include "lldb/Utility/DataExtractor.h"
31 
32 using namespace lldb;
33 using namespace lldb_private;
34 
35 namespace {
36 enum class ProcessState {
37   Unknown,
38   DiskSleep,
39   Paging,
40   Running,
41   Sleeping,
42   TracedOrStopped,
43   Zombie,
44 };
45 }
46 
47 static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
48                           ProcessState &State, ::pid_t &TracerPid) {
49   auto BufferOrError = getProcFile(Pid, "status");
50   if (!BufferOrError)
51     return false;
52 
53   llvm::StringRef Rest = BufferOrError.get()->getBuffer();
54   while(!Rest.empty()) {
55     llvm::StringRef Line;
56     std::tie(Line, Rest) = Rest.split('\n');
57 
58     if (Line.consume_front("Gid:")) {
59       // Real, effective, saved set, and file system GIDs. Read the first two.
60       Line = Line.ltrim();
61       uint32_t RGid, EGid;
62       Line.consumeInteger(10, RGid);
63       Line = Line.ltrim();
64       Line.consumeInteger(10, EGid);
65 
66       ProcessInfo.SetGroupID(RGid);
67       ProcessInfo.SetEffectiveGroupID(EGid);
68     } else if (Line.consume_front("Uid:")) {
69       // Real, effective, saved set, and file system UIDs. Read the first two.
70       Line = Line.ltrim();
71       uint32_t RUid, EUid;
72       Line.consumeInteger(10, RUid);
73       Line = Line.ltrim();
74       Line.consumeInteger(10, EUid);
75 
76       ProcessInfo.SetUserID(RUid);
77       ProcessInfo.SetEffectiveUserID(EUid);
78     } else if (Line.consume_front("PPid:")) {
79       ::pid_t PPid;
80       Line.ltrim().consumeInteger(10, PPid);
81       ProcessInfo.SetParentProcessID(PPid);
82     } else if (Line.consume_front("State:")) {
83       char S = Line.ltrim().front();
84       switch (S) {
85       case 'R':
86         State = ProcessState::Running;
87         break;
88       case 'S':
89         State = ProcessState::Sleeping;
90         break;
91       case 'D':
92         State = ProcessState::DiskSleep;
93         break;
94       case 'Z':
95         State = ProcessState::Zombie;
96         break;
97       case 'T':
98         State = ProcessState::TracedOrStopped;
99         break;
100       case 'W':
101         State = ProcessState::Paging;
102         break;
103       }
104     } else if (Line.consume_front("TracerPid:")) {
105       Line = Line.ltrim();
106       Line.consumeInteger(10, TracerPid);
107     }
108   }
109   return true;
110 }
111 
112 static bool IsDirNumeric(const char *dname) {
113   for (; *dname; dname++) {
114     if (!isdigit(*dname))
115       return false;
116   }
117   return true;
118 }
119 
120 static ArchSpec GetELFProcessCPUType(llvm::StringRef exe_path) {
121   Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);
122 
123   auto buffer_sp = FileSystem::Instance().CreateDataBuffer(exe_path, 0x20, 0);
124   if (!buffer_sp)
125     return ArchSpec();
126 
127   uint8_t exe_class =
128       llvm::object::getElfArchType(
129           {buffer_sp->GetChars(), size_t(buffer_sp->GetByteSize())})
130           .first;
131 
132   switch (exe_class) {
133   case llvm::ELF::ELFCLASS32:
134     return HostInfo::GetArchitecture(HostInfo::eArchKind32);
135   case llvm::ELF::ELFCLASS64:
136     return HostInfo::GetArchitecture(HostInfo::eArchKind64);
137   default:
138     LLDB_LOG(log, "Unknown elf class ({0}) in file {1}", exe_class, exe_path);
139     return ArchSpec();
140   }
141 }
142 
143 static bool GetProcessAndStatInfo(::pid_t pid,
144                                   ProcessInstanceInfo &process_info,
145                                   ProcessState &State, ::pid_t &tracerpid) {
146   tracerpid = 0;
147   process_info.Clear();
148 
149   Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
150 
151   // We can't use getProcFile here because proc/[pid]/exe is a symbolic link.
152   llvm::SmallString<64> ProcExe;
153   (llvm::Twine("/proc/") + llvm::Twine(pid) + "/exe").toVector(ProcExe);
154   std::string ExePath(PATH_MAX, '\0');
155 
156   ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX);
157   if (len <= 0) {
158     LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid,
159              Status(errno, eErrorTypePOSIX));
160     return false;
161   }
162   ExePath.resize(len);
163 
164   // If the binary has been deleted, the link name has " (deleted)" appended.
165   // Remove if there.
166   llvm::StringRef PathRef = ExePath;
167   PathRef.consume_back(" (deleted)");
168 
169   process_info.SetArchitecture(GetELFProcessCPUType(PathRef));
170 
171   // Get the process environment.
172   auto BufferOrError = getProcFile(pid, "environ");
173   if (!BufferOrError)
174     return false;
175   std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError);
176 
177   // Get the command line used to start the process.
178   BufferOrError = getProcFile(pid, "cmdline");
179   if (!BufferOrError)
180     return false;
181   std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError);
182 
183   // Get User and Group IDs and get tracer pid.
184   if (!GetStatusInfo(pid, process_info, State, tracerpid))
185     return false;
186 
187   process_info.SetProcessID(pid);
188   process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native);
189 
190   llvm::StringRef Rest = Environ->getBuffer();
191   while (!Rest.empty()) {
192     llvm::StringRef Var;
193     std::tie(Var, Rest) = Rest.split('\0');
194     process_info.GetEnvironment().insert(Var);
195   }
196 
197   llvm::StringRef Arg0;
198   std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0');
199   process_info.SetArg0(Arg0);
200   while (!Rest.empty()) {
201     llvm::StringRef Arg;
202     std::tie(Arg, Rest) = Rest.split('\0');
203     process_info.GetArguments().AppendArgument(Arg);
204   }
205 
206   return true;
207 }
208 
209 uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
210                              ProcessInstanceInfoList &process_infos) {
211   static const char procdir[] = "/proc/";
212 
213   DIR *dirproc = opendir(procdir);
214   if (dirproc) {
215     struct dirent *direntry = NULL;
216     const uid_t our_uid = getuid();
217     const lldb::pid_t our_pid = getpid();
218     bool all_users = match_info.GetMatchAllUsers();
219 
220     while ((direntry = readdir(dirproc)) != NULL) {
221       if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
222         continue;
223 
224       lldb::pid_t pid = atoi(direntry->d_name);
225 
226       // Skip this process.
227       if (pid == our_pid)
228         continue;
229 
230       ::pid_t tracerpid;
231       ProcessState State;
232       ProcessInstanceInfo process_info;
233 
234       if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid))
235         continue;
236 
237       // Skip if process is being debugged.
238       if (tracerpid != 0)
239         continue;
240 
241       if (State == ProcessState::Zombie)
242         continue;
243 
244       // Check for user match if we're not matching all users and not running
245       // as root.
246       if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid))
247         continue;
248 
249       if (match_info.Matches(process_info)) {
250         process_infos.Append(process_info);
251       }
252     }
253 
254     closedir(dirproc);
255   }
256 
257   return process_infos.GetSize();
258 }
259 
260 bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {
261   bool tids_changed = false;
262   static const char procdir[] = "/proc/";
263   static const char taskdir[] = "/task/";
264   std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir;
265   DIR *dirproc = opendir(process_task_dir.c_str());
266 
267   if (dirproc) {
268     struct dirent *direntry = NULL;
269     while ((direntry = readdir(dirproc)) != NULL) {
270       if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
271         continue;
272 
273       lldb::tid_t tid = atoi(direntry->d_name);
274       TidMap::iterator it = tids_to_attach.find(tid);
275       if (it == tids_to_attach.end()) {
276         tids_to_attach.insert(TidPair(tid, false));
277         tids_changed = true;
278       }
279     }
280     closedir(dirproc);
281   }
282 
283   return tids_changed;
284 }
285 
286 bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
287   ::pid_t tracerpid;
288   ProcessState State;
289   return GetProcessAndStatInfo(pid, process_info, State, tracerpid);
290 }
291 
292 Environment Host::GetEnvironment() { return Environment(environ); }
293 
294 Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
295   return Status("unimplemented");
296 }
297