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 = std::nullopt;
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 = std::nullopt;
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   m_opaque_sp = Platform::Create(platform_name);
296 }
297 
298 SBPlatform::SBPlatform(const SBPlatform &rhs) {
299   LLDB_INSTRUMENT_VA(this, rhs);
300 
301   m_opaque_sp = rhs.m_opaque_sp;
302 }
303 
304 SBPlatform &SBPlatform::operator=(const SBPlatform &rhs) {
305   LLDB_INSTRUMENT_VA(this, rhs);
306 
307   m_opaque_sp = rhs.m_opaque_sp;
308   return *this;
309 }
310 
311 SBPlatform::~SBPlatform() = default;
312 
313 SBPlatform SBPlatform::GetHostPlatform() {
314   LLDB_INSTRUMENT();
315 
316   SBPlatform host_platform;
317   host_platform.m_opaque_sp = Platform::GetHostPlatform();
318   return host_platform;
319 }
320 
321 bool SBPlatform::IsValid() const {
322   LLDB_INSTRUMENT_VA(this);
323   return this->operator bool();
324 }
325 SBPlatform::operator bool() const {
326   LLDB_INSTRUMENT_VA(this);
327 
328   return m_opaque_sp.get() != nullptr;
329 }
330 
331 void SBPlatform::Clear() {
332   LLDB_INSTRUMENT_VA(this);
333 
334   m_opaque_sp.reset();
335 }
336 
337 const char *SBPlatform::GetName() {
338   LLDB_INSTRUMENT_VA(this);
339 
340   PlatformSP platform_sp(GetSP());
341   if (platform_sp)
342     return ConstString(platform_sp->GetName()).AsCString();
343   return nullptr;
344 }
345 
346 lldb::PlatformSP SBPlatform::GetSP() const { return m_opaque_sp; }
347 
348 void SBPlatform::SetSP(const lldb::PlatformSP &platform_sp) {
349   m_opaque_sp = platform_sp;
350 }
351 
352 const char *SBPlatform::GetWorkingDirectory() {
353   LLDB_INSTRUMENT_VA(this);
354 
355   PlatformSP platform_sp(GetSP());
356   if (platform_sp)
357     return platform_sp->GetWorkingDirectory().GetPathAsConstString().AsCString();
358   return nullptr;
359 }
360 
361 bool SBPlatform::SetWorkingDirectory(const char *path) {
362   LLDB_INSTRUMENT_VA(this, path);
363 
364   PlatformSP platform_sp(GetSP());
365   if (platform_sp) {
366     if (path)
367       platform_sp->SetWorkingDirectory(FileSpec(path));
368     else
369       platform_sp->SetWorkingDirectory(FileSpec());
370     return true;
371   }
372   return false;
373 }
374 
375 SBError SBPlatform::ConnectRemote(SBPlatformConnectOptions &connect_options) {
376   LLDB_INSTRUMENT_VA(this, connect_options);
377 
378   SBError sb_error;
379   PlatformSP platform_sp(GetSP());
380   if (platform_sp && connect_options.GetURL()) {
381     Args args;
382     args.AppendArgument(connect_options.GetURL());
383     sb_error.ref() = platform_sp->ConnectRemote(args);
384   } else {
385     sb_error.SetErrorString("invalid platform");
386   }
387   return sb_error;
388 }
389 
390 void SBPlatform::DisconnectRemote() {
391   LLDB_INSTRUMENT_VA(this);
392 
393   PlatformSP platform_sp(GetSP());
394   if (platform_sp)
395     platform_sp->DisconnectRemote();
396 }
397 
398 bool SBPlatform::IsConnected() {
399   LLDB_INSTRUMENT_VA(this);
400 
401   PlatformSP platform_sp(GetSP());
402   if (platform_sp)
403     return platform_sp->IsConnected();
404   return false;
405 }
406 
407 const char *SBPlatform::GetTriple() {
408   LLDB_INSTRUMENT_VA(this);
409 
410   PlatformSP platform_sp(GetSP());
411   if (platform_sp) {
412     ArchSpec arch(platform_sp->GetSystemArchitecture());
413     if (arch.IsValid()) {
414       // Const-ify the string so we don't need to worry about the lifetime of
415       // the string
416       return ConstString(arch.GetTriple().getTriple().c_str()).GetCString();
417     }
418   }
419   return nullptr;
420 }
421 
422 const char *SBPlatform::GetOSBuild() {
423   LLDB_INSTRUMENT_VA(this);
424 
425   PlatformSP platform_sp(GetSP());
426   if (platform_sp) {
427     std::string s = platform_sp->GetOSBuildString().value_or("");
428     if (!s.empty()) {
429       // Const-ify the string so we don't need to worry about the lifetime of
430       // the string
431       return ConstString(s).GetCString();
432     }
433   }
434   return nullptr;
435 }
436 
437 const char *SBPlatform::GetOSDescription() {
438   LLDB_INSTRUMENT_VA(this);
439 
440   PlatformSP platform_sp(GetSP());
441   if (platform_sp) {
442     std::string s = platform_sp->GetOSKernelDescription().value_or("");
443     if (!s.empty()) {
444       // Const-ify the string so we don't need to worry about the lifetime of
445       // the string
446       return ConstString(s.c_str()).GetCString();
447     }
448   }
449   return nullptr;
450 }
451 
452 const char *SBPlatform::GetHostname() {
453   LLDB_INSTRUMENT_VA(this);
454 
455   PlatformSP platform_sp(GetSP());
456   if (platform_sp)
457     return platform_sp->GetHostname();
458   return nullptr;
459 }
460 
461 uint32_t SBPlatform::GetOSMajorVersion() {
462   LLDB_INSTRUMENT_VA(this);
463 
464   llvm::VersionTuple version;
465   if (PlatformSP platform_sp = GetSP())
466     version = platform_sp->GetOSVersion();
467   return version.empty() ? UINT32_MAX : version.getMajor();
468 }
469 
470 uint32_t SBPlatform::GetOSMinorVersion() {
471   LLDB_INSTRUMENT_VA(this);
472 
473   llvm::VersionTuple version;
474   if (PlatformSP platform_sp = GetSP())
475     version = platform_sp->GetOSVersion();
476   return version.getMinor().value_or(UINT32_MAX);
477 }
478 
479 uint32_t SBPlatform::GetOSUpdateVersion() {
480   LLDB_INSTRUMENT_VA(this);
481 
482   llvm::VersionTuple version;
483   if (PlatformSP platform_sp = GetSP())
484     version = platform_sp->GetOSVersion();
485   return version.getSubminor().value_or(UINT32_MAX);
486 }
487 
488 void SBPlatform::SetSDKRoot(const char *sysroot) {
489   LLDB_INSTRUMENT_VA(this, sysroot);
490   if (PlatformSP platform_sp = GetSP())
491     platform_sp->SetSDKRootDirectory(ConstString(sysroot));
492 }
493 
494 SBError SBPlatform::Get(SBFileSpec &src, SBFileSpec &dst) {
495   LLDB_INSTRUMENT_VA(this, src, dst);
496 
497   SBError sb_error;
498   PlatformSP platform_sp(GetSP());
499   if (platform_sp) {
500     sb_error.ref() = platform_sp->GetFile(src.ref(), dst.ref());
501   } else {
502     sb_error.SetErrorString("invalid platform");
503   }
504   return sb_error;
505 }
506 
507 SBError SBPlatform::Put(SBFileSpec &src, SBFileSpec &dst) {
508   LLDB_INSTRUMENT_VA(this, src, dst);
509   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
510     if (src.Exists()) {
511       uint32_t permissions = FileSystem::Instance().GetPermissions(src.ref());
512       if (permissions == 0) {
513         if (FileSystem::Instance().IsDirectory(src.ref()))
514           permissions = eFilePermissionsDirectoryDefault;
515         else
516           permissions = eFilePermissionsFileDefault;
517       }
518 
519       return platform_sp->PutFile(src.ref(), dst.ref(), permissions);
520     }
521 
522     Status error;
523     error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
524                                    src.ref().GetPath().c_str());
525     return error;
526   });
527 }
528 
529 SBError SBPlatform::Install(SBFileSpec &src, SBFileSpec &dst) {
530   LLDB_INSTRUMENT_VA(this, src, dst);
531   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
532     if (src.Exists())
533       return platform_sp->Install(src.ref(), dst.ref());
534 
535     Status error;
536     error.SetErrorStringWithFormat("'src' argument doesn't exist: '%s'",
537                                    src.ref().GetPath().c_str());
538     return error;
539   });
540 }
541 
542 SBError SBPlatform::Run(SBPlatformShellCommand &shell_command) {
543   LLDB_INSTRUMENT_VA(this, shell_command);
544   return ExecuteConnected(
545       [&](const lldb::PlatformSP &platform_sp) {
546         const char *command = shell_command.GetCommand();
547         if (!command)
548           return Status("invalid shell command (empty)");
549 
550         if (shell_command.GetWorkingDirectory() == nullptr) {
551           std::string platform_working_dir =
552               platform_sp->GetWorkingDirectory().GetPath();
553           if (!platform_working_dir.empty())
554             shell_command.SetWorkingDirectory(platform_working_dir.c_str());
555         }
556         return platform_sp->RunShellCommand(
557             shell_command.m_opaque_ptr->m_shell, command,
558             FileSpec(shell_command.GetWorkingDirectory()),
559             &shell_command.m_opaque_ptr->m_status,
560             &shell_command.m_opaque_ptr->m_signo,
561             &shell_command.m_opaque_ptr->m_output,
562             shell_command.m_opaque_ptr->m_timeout);
563       });
564 }
565 
566 SBError SBPlatform::Launch(SBLaunchInfo &launch_info) {
567   LLDB_INSTRUMENT_VA(this, launch_info);
568   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
569     ProcessLaunchInfo info = launch_info.ref();
570     Status error = platform_sp->LaunchProcess(info);
571     launch_info.set_ref(info);
572     return error;
573   });
574 }
575 
576 SBError SBPlatform::Kill(const lldb::pid_t pid) {
577   LLDB_INSTRUMENT_VA(this, pid);
578   return ExecuteConnected([&](const lldb::PlatformSP &platform_sp) {
579     return platform_sp->KillProcess(pid);
580   });
581 }
582 
583 SBError SBPlatform::ExecuteConnected(
584     const std::function<Status(const lldb::PlatformSP &)> &func) {
585   SBError sb_error;
586   const auto platform_sp(GetSP());
587   if (platform_sp) {
588     if (platform_sp->IsConnected())
589       sb_error.ref() = func(platform_sp);
590     else
591       sb_error.SetErrorString("not connected");
592   } else
593     sb_error.SetErrorString("invalid platform");
594 
595   return sb_error;
596 }
597 
598 SBError SBPlatform::MakeDirectory(const char *path, uint32_t file_permissions) {
599   LLDB_INSTRUMENT_VA(this, path, file_permissions);
600 
601   SBError sb_error;
602   PlatformSP platform_sp(GetSP());
603   if (platform_sp) {
604     sb_error.ref() =
605         platform_sp->MakeDirectory(FileSpec(path), file_permissions);
606   } else {
607     sb_error.SetErrorString("invalid platform");
608   }
609   return sb_error;
610 }
611 
612 uint32_t SBPlatform::GetFilePermissions(const char *path) {
613   LLDB_INSTRUMENT_VA(this, path);
614 
615   PlatformSP platform_sp(GetSP());
616   if (platform_sp) {
617     uint32_t file_permissions = 0;
618     platform_sp->GetFilePermissions(FileSpec(path), file_permissions);
619     return file_permissions;
620   }
621   return 0;
622 }
623 
624 SBError SBPlatform::SetFilePermissions(const char *path,
625                                        uint32_t file_permissions) {
626   LLDB_INSTRUMENT_VA(this, path, file_permissions);
627 
628   SBError sb_error;
629   PlatformSP platform_sp(GetSP());
630   if (platform_sp) {
631     sb_error.ref() =
632         platform_sp->SetFilePermissions(FileSpec(path), file_permissions);
633   } else {
634     sb_error.SetErrorString("invalid platform");
635   }
636   return sb_error;
637 }
638 
639 SBUnixSignals SBPlatform::GetUnixSignals() const {
640   LLDB_INSTRUMENT_VA(this);
641 
642   if (auto platform_sp = GetSP())
643     return SBUnixSignals{platform_sp};
644 
645   return SBUnixSignals();
646 }
647 
648 SBEnvironment SBPlatform::GetEnvironment() {
649   LLDB_INSTRUMENT_VA(this);
650   PlatformSP platform_sp(GetSP());
651 
652   if (platform_sp) {
653     return SBEnvironment(platform_sp->GetEnvironment());
654   }
655 
656   return SBEnvironment();
657 }
658