1 //===-- SBPlatform.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 "lldb/API/SBPlatform.h"
10 #include "lldb/API/SBError.h"
11 #include "lldb/API/SBFileSpec.h"
12 #include "lldb/API/SBLaunchInfo.h"
13 #include "lldb/API/SBUnixSignals.h"
14 #include "lldb/Host/File.h"
15 #include "lldb/Target/Platform.h"
16 #include "lldb/Target/Target.h"
17 #include "lldb/Utility/ArchSpec.h"
18 #include "lldb/Utility/Args.h"
19 #include "lldb/Utility/Status.h"
20 
21 #include "llvm/Support/FileSystem.h"
22 
23 #include <functional>
24 
25 using namespace lldb;
26 using namespace lldb_private;
27 
28 //----------------------------------------------------------------------
29 // PlatformConnectOptions
30 //----------------------------------------------------------------------
31 struct PlatformConnectOptions {
32   PlatformConnectOptions(const char *url = NULL)
33       : m_url(), m_rsync_options(), m_rsync_remote_path_prefix(),
34         m_rsync_enabled(false), m_rsync_omit_hostname_from_remote_path(false),
35         m_local_cache_directory() {
36     if (url && url[0])
37       m_url = url;
38   }
39 
40   ~PlatformConnectOptions() {}
41 
42   std::string m_url;
43   std::string m_rsync_options;
44   std::string m_rsync_remote_path_prefix;
45   bool m_rsync_enabled;
46   bool m_rsync_omit_hostname_from_remote_path;
47   ConstString m_local_cache_directory;
48 };
49 
50 //----------------------------------------------------------------------
51 // PlatformShellCommand
52 //----------------------------------------------------------------------
53 struct PlatformShellCommand {
54   PlatformShellCommand(const char *shell_command = NULL)
55       : m_command(), m_working_dir(), m_status(0), m_signo(0) {
56     if (shell_command && shell_command[0])
57       m_command = shell_command;
58   }
59 
60   ~PlatformShellCommand() {}
61 
62   std::string m_command;
63   std::string m_working_dir;
64   std::string m_output;
65   int m_status;
66   int m_signo;
67   Timeout<std::ratio<1>> m_timeout = llvm::None;
68 };
69 //----------------------------------------------------------------------
70 // SBPlatformConnectOptions
71 //----------------------------------------------------------------------
72 SBPlatformConnectOptions::SBPlatformConnectOptions(const char *url)
73     : m_opaque_ptr(new PlatformConnectOptions(url)) {}
74 
75 SBPlatformConnectOptions::SBPlatformConnectOptions(
76     const SBPlatformConnectOptions &rhs)
77     : m_opaque_ptr(new PlatformConnectOptions()) {
78   *m_opaque_ptr = *rhs.m_opaque_ptr;
79 }
80 
81 SBPlatformConnectOptions::~SBPlatformConnectOptions() { delete m_opaque_ptr; }
82 
83 void SBPlatformConnectOptions::operator=(const SBPlatformConnectOptions &rhs) {
84   *m_opaque_ptr = *rhs.m_opaque_ptr;
85 }
86 
87 const char *SBPlatformConnectOptions::GetURL() {
88   if (m_opaque_ptr->m_url.empty())
89     return NULL;
90   return m_opaque_ptr->m_url.c_str();
91 }
92 
93 void SBPlatformConnectOptions::SetURL(const char *url) {
94   if (url && url[0])
95     m_opaque_ptr->m_url = url;
96   else
97     m_opaque_ptr->m_url.clear();
98 }
99 
100 bool SBPlatformConnectOptions::GetRsyncEnabled() {
101   return m_opaque_ptr->m_rsync_enabled;
102 }
103 
104 void SBPlatformConnectOptions::EnableRsync(
105     const char *options, const char *remote_path_prefix,
106     bool omit_hostname_from_remote_path) {
107   m_opaque_ptr->m_rsync_enabled = true;
108   m_opaque_ptr->m_rsync_omit_hostname_from_remote_path =
109       omit_hostname_from_remote_path;
110   if (remote_path_prefix && remote_path_prefix[0])
111     m_opaque_ptr->m_rsync_remote_path_prefix = remote_path_prefix;
112   else
113     m_opaque_ptr->m_rsync_remote_path_prefix.clear();
114 
115   if (options && options[0])
116     m_opaque_ptr->m_rsync_options = options;
117   else
118     m_opaque_ptr->m_rsync_options.clear();
119 }
120 
121 void SBPlatformConnectOptions::DisableRsync() {
122   m_opaque_ptr->m_rsync_enabled = false;
123 }
124 
125 const char *SBPlatformConnectOptions::GetLocalCacheDirectory() {
126   return m_opaque_ptr->m_local_cache_directory.GetCString();
127 }
128 
129 void SBPlatformConnectOptions::SetLocalCacheDirectory(const char *path) {
130   if (path && path[0])
131     m_opaque_ptr->m_local_cache_directory.SetCString(path);
132   else
133     m_opaque_ptr->m_local_cache_directory = ConstString();
134 }
135 
136 //----------------------------------------------------------------------
137 // SBPlatformShellCommand
138 //----------------------------------------------------------------------
139 SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_command)
140     : m_opaque_ptr(new PlatformShellCommand(shell_command)) {}
141 
142 SBPlatformShellCommand::SBPlatformShellCommand(
143     const SBPlatformShellCommand &rhs)
144     : m_opaque_ptr(new PlatformShellCommand()) {
145   *m_opaque_ptr = *rhs.m_opaque_ptr;
146 }
147 
148 SBPlatformShellCommand::~SBPlatformShellCommand() { delete m_opaque_ptr; }
149 
150 void SBPlatformShellCommand::Clear() {
151   m_opaque_ptr->m_output = std::string();
152   m_opaque_ptr->m_status = 0;
153   m_opaque_ptr->m_signo = 0;
154 }
155 
156 const char *SBPlatformShellCommand::GetCommand() {
157   if (m_opaque_ptr->m_command.empty())
158     return NULL;
159   return m_opaque_ptr->m_command.c_str();
160 }
161 
162 void SBPlatformShellCommand::SetCommand(const char *shell_command) {
163   if (shell_command && shell_command[0])
164     m_opaque_ptr->m_command = shell_command;
165   else
166     m_opaque_ptr->m_command.clear();
167 }
168 
169 const char *SBPlatformShellCommand::GetWorkingDirectory() {
170   if (m_opaque_ptr->m_working_dir.empty())
171     return NULL;
172   return m_opaque_ptr->m_working_dir.c_str();
173 }
174 
175 void SBPlatformShellCommand::SetWorkingDirectory(const char *path) {
176   if (path && path[0])
177     m_opaque_ptr->m_working_dir = path;
178   else
179     m_opaque_ptr->m_working_dir.clear();
180 }
181 
182 uint32_t SBPlatformShellCommand::GetTimeoutSeconds() {
183   if (m_opaque_ptr->m_timeout)
184     return m_opaque_ptr->m_timeout->count();
185   return UINT32_MAX;
186 }
187 
188 void SBPlatformShellCommand::SetTimeoutSeconds(uint32_t sec) {
189   if (sec == UINT32_MAX)
190     m_opaque_ptr->m_timeout = llvm::None;
191   else
192     m_opaque_ptr->m_timeout = std::chrono::seconds(sec);
193 }
194 
195 int SBPlatformShellCommand::GetSignal() { return m_opaque_ptr->m_signo; }
196 
197 int SBPlatformShellCommand::GetStatus() { return m_opaque_ptr->m_status; }
198 
199 const char *SBPlatformShellCommand::GetOutput() {
200   if (m_opaque_ptr->m_output.empty())
201     return NULL;
202   return m_opaque_ptr->m_output.c_str();
203 }
204 
205 //----------------------------------------------------------------------
206 // SBPlatform
207 //----------------------------------------------------------------------
208 SBPlatform::SBPlatform() : m_opaque_sp() {}
209 
210 SBPlatform::SBPlatform(const char *platform_name) : m_opaque_sp() {
211   Status error;
212   if (platform_name && platform_name[0])
213     m_opaque_sp = Platform::Create(ConstString(platform_name), error);
214 }
215 
216 SBPlatform::~SBPlatform() {}
217 
218 bool SBPlatform::IsValid() const { return m_opaque_sp.get() != NULL; }
219 
220 void SBPlatform::Clear() { m_opaque_sp.reset(); }
221 
222 const char *SBPlatform::GetName() {
223   PlatformSP platform_sp(GetSP());
224   if (platform_sp)
225     return platform_sp->GetName().GetCString();
226   return NULL;
227 }
228 
229 lldb::PlatformSP SBPlatform::GetSP() const { return m_opaque_sp; }
230 
231 void SBPlatform::SetSP(const lldb::PlatformSP &platform_sp) {
232   m_opaque_sp = platform_sp;
233 }
234 
235 const char *SBPlatform::GetWorkingDirectory() {
236   PlatformSP platform_sp(GetSP());
237   if (platform_sp)
238     return platform_sp->GetWorkingDirectory().GetCString();
239   return NULL;
240 }
241 
242 bool SBPlatform::SetWorkingDirectory(const char *path) {
243   PlatformSP platform_sp(GetSP());
244   if (platform_sp) {
245     if (path)
246       platform_sp->SetWorkingDirectory(FileSpec(path));
247     else
248       platform_sp->SetWorkingDirectory(FileSpec());
249     return true;
250   }
251   return false;
252 }
253 
254 SBError SBPlatform::ConnectRemote(SBPlatformConnectOptions &connect_options) {
255   SBError sb_error;
256   PlatformSP platform_sp(GetSP());
257   if (platform_sp && connect_options.GetURL()) {
258     Args args;
259     args.AppendArgument(
260         llvm::StringRef::withNullAsEmpty(connect_options.GetURL()));
261     sb_error.ref() = platform_sp->ConnectRemote(args);
262   } else {
263     sb_error.SetErrorString("invalid platform");
264   }
265   return sb_error;
266 }
267 
268 void SBPlatform::DisconnectRemote() {
269   PlatformSP platform_sp(GetSP());
270   if (platform_sp)
271     platform_sp->DisconnectRemote();
272 }
273 
274 bool SBPlatform::IsConnected() {
275   PlatformSP platform_sp(GetSP());
276   if (platform_sp)
277     return platform_sp->IsConnected();
278   return false;
279 }
280 
281 const char *SBPlatform::GetTriple() {
282   PlatformSP platform_sp(GetSP());
283   if (platform_sp) {
284     ArchSpec arch(platform_sp->GetSystemArchitecture());
285     if (arch.IsValid()) {
286       // Const-ify the string so we don't need to worry about the lifetime of
287       // the string
288       return ConstString(arch.GetTriple().getTriple().c_str()).GetCString();
289     }
290   }
291   return NULL;
292 }
293 
294 const char *SBPlatform::GetOSBuild() {
295   PlatformSP platform_sp(GetSP());
296   if (platform_sp) {
297     std::string s;
298     if (platform_sp->GetOSBuildString(s)) {
299       if (!s.empty()) {
300         // Const-ify the string so we don't need to worry about the lifetime of
301         // the string
302         return ConstString(s.c_str()).GetCString();
303       }
304     }
305   }
306   return NULL;
307 }
308 
309 const char *SBPlatform::GetOSDescription() {
310   PlatformSP platform_sp(GetSP());
311   if (platform_sp) {
312     std::string s;
313     if (platform_sp->GetOSKernelDescription(s)) {
314       if (!s.empty()) {
315         // Const-ify the string so we don't need to worry about the lifetime of
316         // the string
317         return ConstString(s.c_str()).GetCString();
318       }
319     }
320   }
321   return NULL;
322 }
323 
324 const char *SBPlatform::GetHostname() {
325   PlatformSP platform_sp(GetSP());
326   if (platform_sp)
327     return platform_sp->GetHostname();
328   return NULL;
329 }
330 
331 uint32_t SBPlatform::GetOSMajorVersion() {
332   llvm::VersionTuple version;
333   if (PlatformSP platform_sp = GetSP())
334     version = platform_sp->GetOSVersion();
335   return version.empty() ? UINT32_MAX : version.getMajor();
336 }
337 
338 uint32_t SBPlatform::GetOSMinorVersion() {
339   llvm::VersionTuple version;
340   if (PlatformSP platform_sp = GetSP())
341     version = platform_sp->GetOSVersion();
342   return version.getMinor().getValueOr(UINT32_MAX);
343 }
344 
345 uint32_t SBPlatform::GetOSUpdateVersion() {
346   llvm::VersionTuple version;
347   if (PlatformSP platform_sp = GetSP())
348     version = platform_sp->GetOSVersion();
349   return version.getSubminor().getValueOr(UINT32_MAX);
350 }
351 
352 SBError SBPlatform::Get(SBFileSpec &src, SBFileSpec &dst) {
353   SBError sb_error;
354   PlatformSP platform_sp(GetSP());
355   if (platform_sp) {
356     sb_error.ref() = platform_sp->GetFile(src.ref(), dst.ref());
357   } else {
358     sb_error.SetErrorString("invalid platform");
359   }
360   return sb_error;
361 }
362 
363 SBError SBPlatform::Put(SBFileSpec &src, SBFileSpec &dst) {
364   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
365     if (src.Exists()) {
366       uint32_t permissions = FileSystem::Instance().GetPermissions(src.ref());
367       if (permissions == 0) {
368         if (FileSystem::Instance().IsDirectory(src.ref()))
369           permissions = eFilePermissionsDirectoryDefault;
370         else
371           permissions = eFilePermissionsFileDefault;
372       }
373 
374       return platform_sp->PutFile(src.ref(), dst.ref(), permissions);
375     }
376 
377     Status error;
378     error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
379                                    src.ref().GetPath().c_str());
380     return error;
381   });
382 }
383 
384 SBError SBPlatform::Install(SBFileSpec &src, SBFileSpec &dst) {
385   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
386     if (src.Exists())
387       return platform_sp->Install(src.ref(), dst.ref());
388 
389     Status error;
390     error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
391                                    src.ref().GetPath().c_str());
392     return error;
393   });
394 }
395 
396 SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) {
397   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
398     const char *command = shell_command.GetCommand();
399     if (!command)
400       return Status("invalid shell command (empty)");
401 
402     const char *working_dir = shell_command.GetWorkingDirectory();
403     if (working_dir == NULL) {
404       working_dir = platform_sp->GetWorkingDirectory().GetCString();
405       if (working_dir)
406         shell_command.SetWorkingDirectory(working_dir);
407     }
408     return platform_sp->RunShellCommand(command, FileSpec(working_dir),
409                                         &shell_command.m_opaque_ptr->m_status,
410                                         &shell_command.m_opaque_ptr->m_signo,
411                                         &shell_command.m_opaque_ptr->m_output,
412                                         shell_command.m_opaque_ptr->m_timeout);
413   });
414 }
415 
416 SBError SBPlatform::Launch(SBLaunchInfo &launch_info) {
417   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
418     ProcessLaunchInfo info = launch_info.ref();
419     Status error = platform_sp->LaunchProcess(info);
420     launch_info.set_ref(info);
421     return error;
422   });
423 }
424 
425 SBError SBPlatform::Kill(const lldb::pid_t pid) {
426   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
427     return platform_sp->KillProcess(pid);
428   });
429 }
430 
431 SBError SBPlatform::ExecuteConnected(
432     const std::function<Status(const lldb::PlatformSP &)> &func) {
433   SBError sb_error;
434   const auto platform_sp(GetSP());
435   if (platform_sp) {
436     if (platform_sp->IsConnected())
437       sb_error.ref() = func(platform_sp);
438     else
439       sb_error.SetErrorString("not connected");
440   } else
441     sb_error.SetErrorString("invalid platform");
442 
443   return sb_error;
444 }
445 
446 SBError SBPlatform::MakeDirectory(const char *path, uint32_t file_permissions) {
447   SBError sb_error;
448   PlatformSP platform_sp(GetSP());
449   if (platform_sp) {
450     sb_error.ref() =
451         platform_sp->MakeDirectory(FileSpec(path), file_permissions);
452   } else {
453     sb_error.SetErrorString("invalid platform");
454   }
455   return sb_error;
456 }
457 
458 uint32_t SBPlatform::GetFilePermissions(const char *path) {
459   PlatformSP platform_sp(GetSP());
460   if (platform_sp) {
461     uint32_t file_permissions = 0;
462     platform_sp->GetFilePermissions(FileSpec(path), file_permissions);
463     return file_permissions;
464   }
465   return 0;
466 }
467 
468 SBError SBPlatform::SetFilePermissions(const char *path,
469                                        uint32_t file_permissions) {
470   SBError sb_error;
471   PlatformSP platform_sp(GetSP());
472   if (platform_sp) {
473     sb_error.ref() =
474         platform_sp->SetFilePermissions(FileSpec(path), file_permissions);
475   } else {
476     sb_error.SetErrorString("invalid platform");
477   }
478   return sb_error;
479 }
480 
481 SBUnixSignals SBPlatform::GetUnixSignals() const {
482   if (auto platform_sp = GetSP())
483     return SBUnixSignals{platform_sp};
484 
485   return {};
486 }
487