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