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