1 //===-- SBPlatform.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 "lldb/API/SBPlatform.h"
10 #include "lldb/API/SBEnvironment.h"
11 #include "lldb/API/SBError.h"
12 #include "lldb/API/SBFileSpec.h"
13 #include "lldb/API/SBLaunchInfo.h"
14 #include "lldb/API/SBPlatform.h"
15 #include "lldb/API/SBUnixSignals.h"
16 #include "lldb/Host/File.h"
17 #include "lldb/Target/Platform.h"
18 #include "lldb/Target/Target.h"
19 #include "lldb/Utility/ArchSpec.h"
20 #include "lldb/Utility/Args.h"
21 #include "lldb/Utility/Instrumentation.h"
22 #include "lldb/Utility/Status.h"
23 
24 #include "llvm/Support/FileSystem.h"
25 
26 #include <functional>
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 
31 // PlatformConnectOptions
32 struct PlatformConnectOptions {
33   PlatformConnectOptions(const char *url = nullptr) {
34     if (url && url[0])
35       m_url = url;
36   }
37 
38   ~PlatformConnectOptions() = default;
39 
40   std::string m_url;
41   std::string m_rsync_options;
42   std::string m_rsync_remote_path_prefix;
43   bool m_rsync_enabled = false;
44   bool m_rsync_omit_hostname_from_remote_path = false;
45   ConstString m_local_cache_directory;
46 };
47 
48 // PlatformShellCommand
49 struct PlatformShellCommand {
50   PlatformShellCommand(llvm::StringRef shell_interpreter,
51                        llvm::StringRef shell_command) {
52     if (!shell_interpreter.empty())
53       m_shell = shell_interpreter.str();
54 
55     if (!m_shell.empty() && !shell_command.empty())
56       m_command = shell_command.str();
57   }
58 
59   PlatformShellCommand(llvm::StringRef shell_command = llvm::StringRef()) {
60     if (!shell_command.empty())
61       m_command = shell_command.str();
62   }
63 
64   ~PlatformShellCommand() = default;
65 
66   std::string m_shell;
67   std::string m_command;
68   std::string m_working_dir;
69   std::string m_output;
70   int m_status = 0;
71   int m_signo = 0;
72   Timeout<std::ratio<1>> m_timeout = llvm::None;
73 };
74 // SBPlatformConnectOptions
75 SBPlatformConnectOptions::SBPlatformConnectOptions(const char *url)
76     : m_opaque_ptr(new PlatformConnectOptions(url)) {
77   LLDB_INSTRUMENT_VA(this, url);
78 }
79 
80 SBPlatformConnectOptions::SBPlatformConnectOptions(
81     const SBPlatformConnectOptions &rhs)
82     : m_opaque_ptr(new PlatformConnectOptions()) {
83   LLDB_INSTRUMENT_VA(this, rhs);
84 
85   *m_opaque_ptr = *rhs.m_opaque_ptr;
86 }
87 
88 SBPlatformConnectOptions::~SBPlatformConnectOptions() { delete m_opaque_ptr; }
89 
90 SBPlatformConnectOptions &
91 SBPlatformConnectOptions::operator=(const SBPlatformConnectOptions &rhs) {
92   LLDB_INSTRUMENT_VA(this, rhs);
93 
94   *m_opaque_ptr = *rhs.m_opaque_ptr;
95   return *this;
96 }
97 
98 const char *SBPlatformConnectOptions::GetURL() {
99   LLDB_INSTRUMENT_VA(this);
100 
101   if (m_opaque_ptr->m_url.empty())
102     return nullptr;
103   return m_opaque_ptr->m_url.c_str();
104 }
105 
106 void SBPlatformConnectOptions::SetURL(const char *url) {
107   LLDB_INSTRUMENT_VA(this, url);
108 
109   if (url && url[0])
110     m_opaque_ptr->m_url = url;
111   else
112     m_opaque_ptr->m_url.clear();
113 }
114 
115 bool SBPlatformConnectOptions::GetRsyncEnabled() {
116   LLDB_INSTRUMENT_VA(this);
117 
118   return m_opaque_ptr->m_rsync_enabled;
119 }
120 
121 void SBPlatformConnectOptions::EnableRsync(
122     const char *options, const char *remote_path_prefix,
123     bool omit_hostname_from_remote_path) {
124   LLDB_INSTRUMENT_VA(this, options, remote_path_prefix,
125                      omit_hostname_from_remote_path);
126 
127   m_opaque_ptr->m_rsync_enabled = true;
128   m_opaque_ptr->m_rsync_omit_hostname_from_remote_path =
129       omit_hostname_from_remote_path;
130   if (remote_path_prefix && remote_path_prefix[0])
131     m_opaque_ptr->m_rsync_remote_path_prefix = remote_path_prefix;
132   else
133     m_opaque_ptr->m_rsync_remote_path_prefix.clear();
134 
135   if (options && options[0])
136     m_opaque_ptr->m_rsync_options = options;
137   else
138     m_opaque_ptr->m_rsync_options.clear();
139 }
140 
141 void SBPlatformConnectOptions::DisableRsync() {
142   LLDB_INSTRUMENT_VA(this);
143 
144   m_opaque_ptr->m_rsync_enabled = false;
145 }
146 
147 const char *SBPlatformConnectOptions::GetLocalCacheDirectory() {
148   LLDB_INSTRUMENT_VA(this);
149 
150   return m_opaque_ptr->m_local_cache_directory.GetCString();
151 }
152 
153 void SBPlatformConnectOptions::SetLocalCacheDirectory(const char *path) {
154   LLDB_INSTRUMENT_VA(this, path);
155 
156   if (path && path[0])
157     m_opaque_ptr->m_local_cache_directory.SetCString(path);
158   else
159     m_opaque_ptr->m_local_cache_directory = ConstString();
160 }
161 
162 // SBPlatformShellCommand
163 SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_interpreter,
164                                                const char *shell_command)
165     : m_opaque_ptr(new PlatformShellCommand(shell_interpreter, shell_command)) {
166   LLDB_INSTRUMENT_VA(this, shell_interpreter, shell_command);
167 }
168 
169 SBPlatformShellCommand::SBPlatformShellCommand(const char *shell_command)
170     : m_opaque_ptr(new PlatformShellCommand(shell_command)) {
171   LLDB_INSTRUMENT_VA(this, shell_command);
172 }
173 
174 SBPlatformShellCommand::SBPlatformShellCommand(
175     const SBPlatformShellCommand &rhs)
176     : m_opaque_ptr(new PlatformShellCommand()) {
177   LLDB_INSTRUMENT_VA(this, rhs);
178 
179   *m_opaque_ptr = *rhs.m_opaque_ptr;
180 }
181 
182 SBPlatformShellCommand &
183 SBPlatformShellCommand::operator=(const SBPlatformShellCommand &rhs) {
184 
185   LLDB_INSTRUMENT_VA(this, rhs);
186 
187   *m_opaque_ptr = *rhs.m_opaque_ptr;
188   return *this;
189 }
190 
191 SBPlatformShellCommand::~SBPlatformShellCommand() { delete m_opaque_ptr; }
192 
193 void SBPlatformShellCommand::Clear() {
194   LLDB_INSTRUMENT_VA(this);
195 
196   m_opaque_ptr->m_output = std::string();
197   m_opaque_ptr->m_status = 0;
198   m_opaque_ptr->m_signo = 0;
199 }
200 
201 const char *SBPlatformShellCommand::GetShell() {
202   LLDB_INSTRUMENT_VA(this);
203 
204   if (m_opaque_ptr->m_shell.empty())
205     return nullptr;
206   return m_opaque_ptr->m_shell.c_str();
207 }
208 
209 void SBPlatformShellCommand::SetShell(const char *shell_interpreter) {
210   LLDB_INSTRUMENT_VA(this, shell_interpreter);
211 
212   if (shell_interpreter && shell_interpreter[0])
213     m_opaque_ptr->m_shell = shell_interpreter;
214   else
215     m_opaque_ptr->m_shell.clear();
216 }
217 
218 const char *SBPlatformShellCommand::GetCommand() {
219   LLDB_INSTRUMENT_VA(this);
220 
221   if (m_opaque_ptr->m_command.empty())
222     return nullptr;
223   return m_opaque_ptr->m_command.c_str();
224 }
225 
226 void SBPlatformShellCommand::SetCommand(const char *shell_command) {
227   LLDB_INSTRUMENT_VA(this, shell_command);
228 
229   if (shell_command && shell_command[0])
230     m_opaque_ptr->m_command = shell_command;
231   else
232     m_opaque_ptr->m_command.clear();
233 }
234 
235 const char *SBPlatformShellCommand::GetWorkingDirectory() {
236   LLDB_INSTRUMENT_VA(this);
237 
238   if (m_opaque_ptr->m_working_dir.empty())
239     return nullptr;
240   return m_opaque_ptr->m_working_dir.c_str();
241 }
242 
243 void SBPlatformShellCommand::SetWorkingDirectory(const char *path) {
244   LLDB_INSTRUMENT_VA(this, path);
245 
246   if (path && path[0])
247     m_opaque_ptr->m_working_dir = path;
248   else
249     m_opaque_ptr->m_working_dir.clear();
250 }
251 
252 uint32_t SBPlatformShellCommand::GetTimeoutSeconds() {
253   LLDB_INSTRUMENT_VA(this);
254 
255   if (m_opaque_ptr->m_timeout)
256     return m_opaque_ptr->m_timeout->count();
257   return UINT32_MAX;
258 }
259 
260 void SBPlatformShellCommand::SetTimeoutSeconds(uint32_t sec) {
261   LLDB_INSTRUMENT_VA(this, sec);
262 
263   if (sec == UINT32_MAX)
264     m_opaque_ptr->m_timeout = llvm::None;
265   else
266     m_opaque_ptr->m_timeout = std::chrono::seconds(sec);
267 }
268 
269 int SBPlatformShellCommand::GetSignal() {
270   LLDB_INSTRUMENT_VA(this);
271 
272   return m_opaque_ptr->m_signo;
273 }
274 
275 int SBPlatformShellCommand::GetStatus() {
276   LLDB_INSTRUMENT_VA(this);
277 
278   return m_opaque_ptr->m_status;
279 }
280 
281 const char *SBPlatformShellCommand::GetOutput() {
282   LLDB_INSTRUMENT_VA(this);
283 
284   if (m_opaque_ptr->m_output.empty())
285     return nullptr;
286   return m_opaque_ptr->m_output.c_str();
287 }
288 
289 // SBPlatform
290 SBPlatform::SBPlatform() { LLDB_INSTRUMENT_VA(this); }
291 
292 SBPlatform::SBPlatform(const char *platform_name) {
293   LLDB_INSTRUMENT_VA(this, platform_name);
294 
295   Status error;
296   if (platform_name && platform_name[0])
297     m_opaque_sp = Platform::Create(ConstString(platform_name), error);
298 }
299 
300 SBPlatform::SBPlatform(const SBPlatform &rhs) {
301   LLDB_INSTRUMENT_VA(this, rhs);
302 
303   m_opaque_sp = rhs.m_opaque_sp;
304 }
305 
306 SBPlatform &SBPlatform::operator=(const SBPlatform &rhs) {
307   LLDB_INSTRUMENT_VA(this, rhs);
308 
309   m_opaque_sp = rhs.m_opaque_sp;
310   return *this;
311 }
312 
313 SBPlatform::~SBPlatform() = default;
314 
315 SBPlatform SBPlatform::GetHostPlatform() {
316   LLDB_INSTRUMENT();
317 
318   SBPlatform host_platform;
319   host_platform.m_opaque_sp = Platform::GetHostPlatform();
320   return host_platform;
321 }
322 
323 bool SBPlatform::IsValid() const {
324   LLDB_INSTRUMENT_VA(this);
325   return this->operator bool();
326 }
327 SBPlatform::operator bool() const {
328   LLDB_INSTRUMENT_VA(this);
329 
330   return m_opaque_sp.get() != nullptr;
331 }
332 
333 void SBPlatform::Clear() {
334   LLDB_INSTRUMENT_VA(this);
335 
336   m_opaque_sp.reset();
337 }
338 
339 const char *SBPlatform::GetName() {
340   LLDB_INSTRUMENT_VA(this);
341 
342   PlatformSP platform_sp(GetSP());
343   if (platform_sp)
344     return ConstString(platform_sp->GetName()).AsCString();
345   return nullptr;
346 }
347 
348 lldb::PlatformSP SBPlatform::GetSP() const { return m_opaque_sp; }
349 
350 void SBPlatform::SetSP(const lldb::PlatformSP &platform_sp) {
351   m_opaque_sp = platform_sp;
352 }
353 
354 const char *SBPlatform::GetWorkingDirectory() {
355   LLDB_INSTRUMENT_VA(this);
356 
357   PlatformSP platform_sp(GetSP());
358   if (platform_sp)
359     return platform_sp->GetWorkingDirectory().GetCString();
360   return nullptr;
361 }
362 
363 bool SBPlatform::SetWorkingDirectory(const char *path) {
364   LLDB_INSTRUMENT_VA(this, path);
365 
366   PlatformSP platform_sp(GetSP());
367   if (platform_sp) {
368     if (path)
369       platform_sp->SetWorkingDirectory(FileSpec(path));
370     else
371       platform_sp->SetWorkingDirectory(FileSpec());
372     return true;
373   }
374   return false;
375 }
376 
377 SBError SBPlatform::ConnectRemote(SBPlatformConnectOptions &connect_options) {
378   LLDB_INSTRUMENT_VA(this, connect_options);
379 
380   SBError sb_error;
381   PlatformSP platform_sp(GetSP());
382   if (platform_sp && connect_options.GetURL()) {
383     Args args;
384     args.AppendArgument(connect_options.GetURL());
385     sb_error.ref() = platform_sp->ConnectRemote(args);
386   } else {
387     sb_error.SetErrorString("invalid platform");
388   }
389   return sb_error;
390 }
391 
392 void SBPlatform::DisconnectRemote() {
393   LLDB_INSTRUMENT_VA(this);
394 
395   PlatformSP platform_sp(GetSP());
396   if (platform_sp)
397     platform_sp->DisconnectRemote();
398 }
399 
400 bool SBPlatform::IsConnected() {
401   LLDB_INSTRUMENT_VA(this);
402 
403   PlatformSP platform_sp(GetSP());
404   if (platform_sp)
405     return platform_sp->IsConnected();
406   return false;
407 }
408 
409 const char *SBPlatform::GetTriple() {
410   LLDB_INSTRUMENT_VA(this);
411 
412   PlatformSP platform_sp(GetSP());
413   if (platform_sp) {
414     ArchSpec arch(platform_sp->GetSystemArchitecture());
415     if (arch.IsValid()) {
416       // Const-ify the string so we don't need to worry about the lifetime of
417       // the string
418       return ConstString(arch.GetTriple().getTriple().c_str()).GetCString();
419     }
420   }
421   return nullptr;
422 }
423 
424 const char *SBPlatform::GetOSBuild() {
425   LLDB_INSTRUMENT_VA(this);
426 
427   PlatformSP platform_sp(GetSP());
428   if (platform_sp) {
429     std::string s = platform_sp->GetOSBuildString().getValueOr("");
430     if (!s.empty()) {
431       // Const-ify the string so we don't need to worry about the lifetime of
432       // the string
433       return ConstString(s).GetCString();
434     }
435   }
436   return nullptr;
437 }
438 
439 const char *SBPlatform::GetOSDescription() {
440   LLDB_INSTRUMENT_VA(this);
441 
442   PlatformSP platform_sp(GetSP());
443   if (platform_sp) {
444     std::string s = platform_sp->GetOSKernelDescription().getValueOr("");
445     if (!s.empty()) {
446       // Const-ify the string so we don't need to worry about the lifetime of
447       // the string
448       return ConstString(s.c_str()).GetCString();
449     }
450   }
451   return nullptr;
452 }
453 
454 const char *SBPlatform::GetHostname() {
455   LLDB_INSTRUMENT_VA(this);
456 
457   PlatformSP platform_sp(GetSP());
458   if (platform_sp)
459     return platform_sp->GetHostname();
460   return nullptr;
461 }
462 
463 uint32_t SBPlatform::GetOSMajorVersion() {
464   LLDB_INSTRUMENT_VA(this);
465 
466   llvm::VersionTuple version;
467   if (PlatformSP platform_sp = GetSP())
468     version = platform_sp->GetOSVersion();
469   return version.empty() ? UINT32_MAX : version.getMajor();
470 }
471 
472 uint32_t SBPlatform::GetOSMinorVersion() {
473   LLDB_INSTRUMENT_VA(this);
474 
475   llvm::VersionTuple version;
476   if (PlatformSP platform_sp = GetSP())
477     version = platform_sp->GetOSVersion();
478   return version.getMinor().getValueOr(UINT32_MAX);
479 }
480 
481 uint32_t SBPlatform::GetOSUpdateVersion() {
482   LLDB_INSTRUMENT_VA(this);
483 
484   llvm::VersionTuple version;
485   if (PlatformSP platform_sp = GetSP())
486     version = platform_sp->GetOSVersion();
487   return version.getSubminor().getValueOr(UINT32_MAX);
488 }
489 
490 void SBPlatform::SetSDKRoot(const char *sysroot) {
491   LLDB_INSTRUMENT_VA(this, sysroot);
492   if (PlatformSP platform_sp = GetSP())
493     platform_sp->SetSDKRootDirectory(ConstString(sysroot));
494 }
495 
496 SBError SBPlatform::Get(SBFileSpec &src, SBFileSpec &dst) {
497   LLDB_INSTRUMENT_VA(this, src, dst);
498 
499   SBError sb_error;
500   PlatformSP platform_sp(GetSP());
501   if (platform_sp) {
502     sb_error.ref() = platform_sp->GetFile(src.ref(), dst.ref());
503   } else {
504     sb_error.SetErrorString("invalid platform");
505   }
506   return sb_error;
507 }
508 
509 SBError SBPlatform::Put(SBFileSpec &src, SBFileSpec &dst) {
510   LLDB_INSTRUMENT_VA(this, src, dst);
511   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
512     if (src.Exists()) {
513       uint32_t permissions = FileSystem::Instance().GetPermissions(src.ref());
514       if (permissions == 0) {
515         if (FileSystem::Instance().IsDirectory(src.ref()))
516           permissions = eFilePermissionsDirectoryDefault;
517         else
518           permissions = eFilePermissionsFileDefault;
519       }
520 
521       return platform_sp->PutFile(src.ref(), dst.ref(), permissions);
522     }
523 
524     Status error;
525     error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
526                                    src.ref().GetPath().c_str());
527     return error;
528   });
529 }
530 
531 SBError SBPlatform::Install(SBFileSpec &src, SBFileSpec &dst) {
532   LLDB_INSTRUMENT_VA(this, src, dst);
533   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
534     if (src.Exists())
535       return platform_sp->Install(src.ref(), dst.ref());
536 
537     Status error;
538     error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
539                                    src.ref().GetPath().c_str());
540     return error;
541   });
542 }
543 
544 SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) {
545   LLDB_INSTRUMENT_VA(this, shell_command);
546   return ExecuteConnected(
547       [&](const lldb::PlatformSP &platform_sp) {
548         const char *command = shell_command.GetCommand();
549         if (!command)
550           return Status("invalid shell command (empty)");
551 
552         const char *working_dir = shell_command.GetWorkingDirectory();
553         if (working_dir == nullptr) {
554           working_dir = platform_sp->GetWorkingDirectory().GetCString();
555           if (working_dir)
556             shell_command.SetWorkingDirectory(working_dir);
557         }
558         return platform_sp->RunShellCommand(
559             shell_command.m_opaque_ptr->m_shell, command, FileSpec(working_dir),
560             &shell_command.m_opaque_ptr->m_status,
561             &shell_command.m_opaque_ptr->m_signo,
562             &shell_command.m_opaque_ptr->m_output,
563             shell_command.m_opaque_ptr->m_timeout);
564       });
565 }
566 
567 SBError SBPlatform::Launch(SBLaunchInfo &launch_info) {
568   LLDB_INSTRUMENT_VA(this, launch_info);
569   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
570     ProcessLaunchInfo info = launch_info.ref();
571     Status error = platform_sp->LaunchProcess(info);
572     launch_info.set_ref(info);
573     return error;
574   });
575 }
576 
577 SBError SBPlatform::Kill(const lldb::pid_t pid) {
578   LLDB_INSTRUMENT_VA(this, pid);
579   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
580     return platform_sp->KillProcess(pid);
581   });
582 }
583 
584 SBError SBPlatform::ExecuteConnected(
585     const std::function<Status(const lldb::PlatformSP &)> &func) {
586   SBError sb_error;
587   const auto platform_sp(GetSP());
588   if (platform_sp) {
589     if (platform_sp->IsConnected())
590       sb_error.ref() = func(platform_sp);
591     else
592       sb_error.SetErrorString("not connected");
593   } else
594     sb_error.SetErrorString("invalid platform");
595 
596   return sb_error;
597 }
598 
599 SBError SBPlatform::MakeDirectory(const char *path, uint32_t file_permissions) {
600   LLDB_INSTRUMENT_VA(this, path, file_permissions);
601 
602   SBError sb_error;
603   PlatformSP platform_sp(GetSP());
604   if (platform_sp) {
605     sb_error.ref() =
606         platform_sp->MakeDirectory(FileSpec(path), file_permissions);
607   } else {
608     sb_error.SetErrorString("invalid platform");
609   }
610   return sb_error;
611 }
612 
613 uint32_t SBPlatform::GetFilePermissions(const char *path) {
614   LLDB_INSTRUMENT_VA(this, path);
615 
616   PlatformSP platform_sp(GetSP());
617   if (platform_sp) {
618     uint32_t file_permissions = 0;
619     platform_sp->GetFilePermissions(FileSpec(path), file_permissions);
620     return file_permissions;
621   }
622   return 0;
623 }
624 
625 SBError SBPlatform::SetFilePermissions(const char *path,
626                                        uint32_t file_permissions) {
627   LLDB_INSTRUMENT_VA(this, path, file_permissions);
628 
629   SBError sb_error;
630   PlatformSP platform_sp(GetSP());
631   if (platform_sp) {
632     sb_error.ref() =
633         platform_sp->SetFilePermissions(FileSpec(path), file_permissions);
634   } else {
635     sb_error.SetErrorString("invalid platform");
636   }
637   return sb_error;
638 }
639 
640 SBUnixSignals SBPlatform::GetUnixSignals() const {
641   LLDB_INSTRUMENT_VA(this);
642 
643   if (auto platform_sp = GetSP())
644     return SBUnixSignals{platform_sp};
645 
646   return SBUnixSignals();
647 }
648 
649 SBEnvironment SBPlatform::GetEnvironment() {
650   LLDB_INSTRUMENT_VA(this);
651   PlatformSP platform_sp(GetSP());
652 
653   if (platform_sp) {
654     return SBEnvironment(platform_sp->GetEnvironment());
655   }
656 
657   return SBEnvironment();
658 }
659