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