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