1 //===-- PlatformPOSIX.cpp ---------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "PlatformPOSIX.h"
14 // Other libraries and framework includes
17 #include "lldb/Core/DataBufferHeap.h"
18 #include "lldb/Core/Debugger.h"
19 #include "lldb/Core/Log.h"
20 #include "lldb/Core/Module.h"
21 #include "lldb/Core/StreamString.h"
22 #include "lldb/Host/File.h"
23 #include "lldb/Host/FileCache.h"
24 #include "lldb/Host/FileSpec.h"
25 #include "lldb/Host/FileSystem.h"
26 #include "lldb/Host/Host.h"
27 #include "lldb/Target/ProcessLaunchInfo.h"
30 using namespace lldb_private;
33 //------------------------------------------------------------------
34 /// Default Constructor
35 //------------------------------------------------------------------
36 PlatformPOSIX::PlatformPOSIX (bool is_host) :
37 Platform(is_host), // This is the local host platform
38 m_remote_platform_sp ()
42 //------------------------------------------------------------------
45 /// The destructor is virtual since this class is designed to be
46 /// inherited from by the plug-in instance.
47 //------------------------------------------------------------------
48 PlatformPOSIX::~PlatformPOSIX()
52 lldb_private::OptionGroupOptions*
53 PlatformPOSIX::GetConnectionOptions (lldb_private::CommandInterpreter& interpreter)
55 if (m_options.get() == NULL)
57 m_options.reset(new OptionGroupOptions(interpreter));
58 m_options->Append(new OptionGroupPlatformRSync());
59 m_options->Append(new OptionGroupPlatformSSH());
60 m_options->Append(new OptionGroupPlatformCaching());
62 return m_options.get();
66 PlatformPOSIX::IsConnected () const
70 else if (m_remote_platform_sp)
71 return m_remote_platform_sp->IsConnected();
76 PlatformPOSIX::RunShellCommand (const char *command, // Shouldn't be NULL
77 const char *working_dir, // Pass NULL to use the current working directory
78 int *status_ptr, // Pass NULL if you don't want the process exit status
79 int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit
80 std::string *command_output, // Pass NULL if you don't want the command output
81 uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish
84 return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec);
87 if (m_remote_platform_sp)
88 return m_remote_platform_sp->RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec);
90 return Error("unable to run a remote command without a platform");
95 PlatformPOSIX::MakeDirectory (const char *path, uint32_t file_permissions)
97 if (m_remote_platform_sp)
98 return m_remote_platform_sp->MakeDirectory(path, file_permissions);
100 return Platform::MakeDirectory(path ,file_permissions);
104 PlatformPOSIX::GetFilePermissions (const char *path, uint32_t &file_permissions)
106 if (m_remote_platform_sp)
107 return m_remote_platform_sp->GetFilePermissions(path, file_permissions);
109 return Platform::GetFilePermissions(path ,file_permissions);
113 PlatformPOSIX::SetFilePermissions (const char *path, uint32_t file_permissions)
115 if (m_remote_platform_sp)
116 return m_remote_platform_sp->SetFilePermissions(path, file_permissions);
118 return Platform::SetFilePermissions(path ,file_permissions);
122 PlatformPOSIX::OpenFile (const FileSpec& file_spec,
128 return FileCache::GetInstance().OpenFile(file_spec, flags, mode, error);
129 else if (m_remote_platform_sp)
130 return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error);
132 return Platform::OpenFile(file_spec, flags, mode, error);
136 PlatformPOSIX::CloseFile (lldb::user_id_t fd, Error &error)
139 return FileCache::GetInstance().CloseFile(fd, error);
140 else if (m_remote_platform_sp)
141 return m_remote_platform_sp->CloseFile(fd, error);
143 return Platform::CloseFile(fd, error);
147 PlatformPOSIX::ReadFile (lldb::user_id_t fd,
154 return FileCache::GetInstance().ReadFile(fd, offset, dst, dst_len, error);
155 else if (m_remote_platform_sp)
156 return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error);
158 return Platform::ReadFile(fd, offset, dst, dst_len, error);
162 PlatformPOSIX::WriteFile (lldb::user_id_t fd,
169 return FileCache::GetInstance().WriteFile(fd, offset, src, src_len, error);
170 else if (m_remote_platform_sp)
171 return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error);
173 return Platform::WriteFile(fd, offset, src, src_len, error);
177 chown_file(Platform *platform,
179 uint32_t uid = UINT32_MAX,
180 uint32_t gid = UINT32_MAX)
182 if (!platform || !path || *path == 0)
185 if (uid == UINT32_MAX && gid == UINT32_MAX)
186 return 0; // pretend I did chown correctly - actually I just didn't care
188 StreamString command;
189 command.PutCString("chown ");
190 if (uid != UINT32_MAX)
191 command.Printf("%d",uid);
192 if (gid != UINT32_MAX)
193 command.Printf(":%d",gid);
194 command.Printf("%s",path);
196 platform->RunShellCommand(command.GetData(),
206 PlatformPOSIX::PutFile (const lldb_private::FileSpec& source,
207 const lldb_private::FileSpec& destination,
211 Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
215 if (FileSpec::Equal(source, destination, true))
219 std::string src_path (source.GetPath());
220 if (src_path.empty())
221 return Error("unable to get file path for source");
222 std::string dst_path (destination.GetPath());
223 if (dst_path.empty())
224 return Error("unable to get file path for destination");
225 StreamString command;
226 command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
228 RunShellCommand(command.GetData(),
235 return Error("unable to perform copy");
236 if (uid == UINT32_MAX && gid == UINT32_MAX)
238 if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
239 return Error("unable to perform chown");
242 else if (m_remote_platform_sp)
244 if (GetSupportsRSync())
246 std::string src_path (source.GetPath());
247 if (src_path.empty())
248 return Error("unable to get file path for source");
249 std::string dst_path (destination.GetPath());
250 if (dst_path.empty())
251 return Error("unable to get file path for destination");
252 StreamString command;
253 if (GetIgnoresRemoteHostname())
255 if (!GetRSyncPrefix())
256 command.Printf("rsync %s %s %s",
261 command.Printf("rsync %s %s %s%s",
268 command.Printf("rsync %s %s %s:%s",
274 log->Printf("[PutFile] Running command: %s\n", command.GetData());
276 Host::RunShellCommand(command.GetData(),
284 // Don't chown a local file for a remote system
285 // if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
286 // return Error("unable to perform chown");
289 // if we are still here rsync has failed - let's try the slow way before giving up
293 log->Printf ("PlatformPOSIX::PutFile(src='%s', dst='%s', uid=%u, gid=%u)",
294 source.GetPath().c_str(),
295 destination.GetPath().c_str(),
297 gid); // REMOVE THIS PRINTF PRIOR TO CHECKIN
299 // read, write, read, write, ...
303 log->Printf("[PutFile] Using block by block transfer....\n");
305 uint32_t source_open_options = File::eOpenOptionRead;
306 if (source.GetFileType() == FileSpec::eFileTypeSymbolicLink)
307 source_open_options |= File::eOpenoptionDontFollowSymlinks;
309 File source_file(source, source_open_options, lldb::eFilePermissionsUserRW);
311 uint32_t permissions = source_file.GetPermissions(error);
312 if (permissions == 0)
313 permissions = lldb::eFilePermissionsFileDefault;
315 if (!source_file.IsValid())
316 return Error("unable to open source file");
317 lldb::user_id_t dest_file = OpenFile (destination,
318 File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate,
322 log->Printf ("dest_file = %" PRIu64 "\n", dest_file);
325 if (dest_file == UINT64_MAX)
326 return Error("unable to open target file");
327 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
329 while (error.Success())
331 size_t bytes_read = buffer_sp->GetByteSize();
332 error = source_file.Read(buffer_sp->GetBytes(), bytes_read);
335 const uint64_t bytes_written = WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error);
336 offset += bytes_written;
337 if (bytes_written != bytes_read)
339 // We didn't write the correct numbe of bytes, so adjust
340 // the file position in the source file we are reading from...
341 source_file.SeekFromStart(offset);
347 CloseFile(dest_file, error);
348 if (uid == UINT32_MAX && gid == UINT32_MAX)
350 // This is remopve, don't chown a local file...
351 // std::string dst_path (destination.GetPath());
352 // if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
353 // return Error("unable to perform chown");
359 if (FileSystem::CalculateMD5 (source, src_md5[0], src_md5[1]) && CalculateMD5 (destination, dst_md5[0], dst_md5[1]))
361 if (src_md5[0] != dst_md5[0] || src_md5[1] != dst_md5[1])
363 error.SetErrorString("md5 checksum of installed file doesn't match, installation failed");
368 return Platform::PutFile(source,destination,uid,gid);
372 PlatformPOSIX::GetFileSize (const FileSpec& file_spec)
375 return FileSystem::GetFileSize(file_spec);
376 else if (m_remote_platform_sp)
377 return m_remote_platform_sp->GetFileSize(file_spec);
379 return Platform::GetFileSize(file_spec);
383 PlatformPOSIX::CreateSymlink(const char *src, const char *dst)
386 return FileSystem::Symlink(src, dst);
387 else if (m_remote_platform_sp)
388 return m_remote_platform_sp->CreateSymlink(src, dst);
390 return Platform::CreateSymlink(src, dst);
394 PlatformPOSIX::GetFileExists (const FileSpec& file_spec)
397 return file_spec.Exists();
398 else if (m_remote_platform_sp)
399 return m_remote_platform_sp->GetFileExists(file_spec);
401 return Platform::GetFileExists(file_spec);
405 PlatformPOSIX::Unlink (const char *path)
408 return FileSystem::Unlink(path);
409 else if (m_remote_platform_sp)
410 return m_remote_platform_sp->Unlink(path);
412 return Platform::Unlink(path);
416 PlatformPOSIX::GetFile (const lldb_private::FileSpec& source /* remote file path */,
417 const lldb_private::FileSpec& destination /* local file path */)
419 Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
421 // Check the args, first.
422 std::string src_path (source.GetPath());
423 if (src_path.empty())
424 return Error("unable to get file path for source");
425 std::string dst_path (destination.GetPath());
426 if (dst_path.empty())
427 return Error("unable to get file path for destination");
430 if (FileSpec::Equal(source, destination, true))
431 return Error("local scenario->source and destination are the same file path: no operation performed");
433 StreamString cp_command;
434 cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
436 RunShellCommand(cp_command.GetData(),
443 return Error("unable to perform copy");
446 else if (m_remote_platform_sp)
448 if (GetSupportsRSync())
450 StreamString command;
451 if (GetIgnoresRemoteHostname())
453 if (!GetRSyncPrefix())
454 command.Printf("rsync %s %s %s",
459 command.Printf("rsync %s %s%s %s",
466 command.Printf("rsync %s %s:%s %s",
468 m_remote_platform_sp->GetHostname(),
472 log->Printf("[GetFile] Running command: %s\n", command.GetData());
474 Host::RunShellCommand(command.GetData(),
482 // If we are here, rsync has failed - let's try the slow way before giving up
485 // read/write, read/write, read/write, ...
489 log->Printf("[GetFile] Using block by block transfer....\n");
491 user_id_t fd_src = OpenFile (source,
492 File::eOpenOptionRead,
493 lldb::eFilePermissionsFileDefault,
496 if (fd_src == UINT64_MAX)
497 return Error("unable to open source file");
499 uint32_t permissions = 0;
500 error = GetFilePermissions(source.GetPath().c_str(), permissions);
502 if (permissions == 0)
503 permissions = lldb::eFilePermissionsFileDefault;
505 user_id_t fd_dst = FileCache::GetInstance().OpenFile(
506 destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate, permissions,
509 if (fd_dst == UINT64_MAX)
512 error.SetErrorString("unable to open destination file");
517 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
520 while (error.Success())
522 const uint64_t n_read = ReadFile (fd_src,
524 buffer_sp->GetBytes(),
525 buffer_sp->GetByteSize(),
531 if (FileCache::GetInstance().WriteFile(fd_dst, offset, buffer_sp->GetBytes(), n_read, error) != n_read)
534 error.SetErrorString("unable to write to destination file");
540 // Ignore the close error of src.
541 if (fd_src != UINT64_MAX)
542 CloseFile(fd_src, error);
543 // And close the dst file descriptot.
544 if (fd_dst != UINT64_MAX && !FileCache::GetInstance().CloseFile(fd_dst, error))
547 error.SetErrorString("unable to close destination file");
552 return Platform::GetFile(source,destination);
556 PlatformPOSIX::GetPlatformSpecificConnectionInformation()
559 if (GetSupportsRSync())
561 stream.PutCString("rsync");
562 if ( (GetRSyncOpts() && *GetRSyncOpts()) ||
563 (GetRSyncPrefix() && *GetRSyncPrefix()) ||
564 GetIgnoresRemoteHostname())
566 stream.Printf(", options: ");
567 if (GetRSyncOpts() && *GetRSyncOpts())
568 stream.Printf("'%s' ",GetRSyncOpts());
569 stream.Printf(", prefix: ");
570 if (GetRSyncPrefix() && *GetRSyncPrefix())
571 stream.Printf("'%s' ",GetRSyncPrefix());
572 if (GetIgnoresRemoteHostname())
573 stream.Printf("ignore remote-hostname ");
576 if (GetSupportsSSH())
578 stream.PutCString("ssh");
579 if (GetSSHOpts() && *GetSSHOpts())
580 stream.Printf(", options: '%s' ",GetSSHOpts());
582 if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
583 stream.Printf("cache dir: %s",GetLocalCacheDirectory());
584 if (stream.GetSize())
585 return stream.GetData();
591 PlatformPOSIX::CalculateMD5 (const FileSpec& file_spec,
596 return Platform::CalculateMD5 (file_spec, low, high);
597 if (m_remote_platform_sp)
598 return m_remote_platform_sp->CalculateMD5(file_spec, low, high);
602 lldb_private::ConstString
603 PlatformPOSIX::GetRemoteWorkingDirectory()
605 if (IsRemote() && m_remote_platform_sp)
606 return m_remote_platform_sp->GetRemoteWorkingDirectory();
608 return Platform::GetRemoteWorkingDirectory();
612 PlatformPOSIX::SetRemoteWorkingDirectory(const lldb_private::ConstString &path)
614 if (IsRemote() && m_remote_platform_sp)
615 return m_remote_platform_sp->SetRemoteWorkingDirectory(path);
617 return Platform::SetRemoteWorkingDirectory(path);
621 PlatformPOSIX::GetRemoteOSVersion ()
623 if (m_remote_platform_sp)
624 return m_remote_platform_sp->GetOSVersion (m_major_os_version,
626 m_update_os_version);
631 PlatformPOSIX::GetRemoteOSBuildString (std::string &s)
633 if (m_remote_platform_sp)
634 return m_remote_platform_sp->GetRemoteOSBuildString (s);
640 PlatformPOSIX::GetEnvironment (StringList &env)
644 if (m_remote_platform_sp)
645 return m_remote_platform_sp->GetEnvironment(env);
648 return Host::GetEnvironment(env);
652 PlatformPOSIX::GetRemoteOSKernelDescription (std::string &s)
654 if (m_remote_platform_sp)
655 return m_remote_platform_sp->GetRemoteOSKernelDescription (s);
660 // Remote Platform subclasses need to override this function
662 PlatformPOSIX::GetRemoteSystemArchitecture ()
664 if (m_remote_platform_sp)
665 return m_remote_platform_sp->GetRemoteSystemArchitecture ();
670 PlatformPOSIX::GetHostname ()
673 return Platform::GetHostname();
675 if (m_remote_platform_sp)
676 return m_remote_platform_sp->GetHostname ();
681 PlatformPOSIX::GetUserName (uint32_t uid)
683 // Check the cache in Platform in case we have already looked this uid up
684 const char *user_name = Platform::GetUserName(uid);
688 if (IsRemote() && m_remote_platform_sp)
689 return m_remote_platform_sp->GetUserName(uid);
694 PlatformPOSIX::GetGroupName (uint32_t gid)
696 const char *group_name = Platform::GetGroupName(gid);
700 if (IsRemote() && m_remote_platform_sp)
701 return m_remote_platform_sp->GetGroupName(gid);
706 PlatformPOSIX::ConnectRemote (Args& args)
711 error.SetErrorStringWithFormat ("can't connect to the host platform '%s', always connected", GetPluginName().GetCString());
715 if (!m_remote_platform_sp)
716 m_remote_platform_sp = Platform::Create (ConstString("remote-gdb-server"), error);
718 if (m_remote_platform_sp && error.Success())
719 error = m_remote_platform_sp->ConnectRemote (args);
721 error.SetErrorString ("failed to create a 'remote-gdb-server' platform");
724 m_remote_platform_sp.reset();
727 if (error.Success() && m_remote_platform_sp)
731 OptionGroupOptions* options = m_options.get();
732 OptionGroupPlatformRSync* m_rsync_options = (OptionGroupPlatformRSync*)options->GetGroupWithOption('r');
733 OptionGroupPlatformSSH* m_ssh_options = (OptionGroupPlatformSSH*)options->GetGroupWithOption('s');
734 OptionGroupPlatformCaching* m_cache_options = (OptionGroupPlatformCaching*)options->GetGroupWithOption('c');
736 if (m_rsync_options->m_rsync)
738 SetSupportsRSync(true);
739 SetRSyncOpts(m_rsync_options->m_rsync_opts.c_str());
740 SetRSyncPrefix(m_rsync_options->m_rsync_prefix.c_str());
741 SetIgnoresRemoteHostname(m_rsync_options->m_ignores_remote_hostname);
743 if (m_ssh_options->m_ssh)
745 SetSupportsSSH(true);
746 SetSSHOpts(m_ssh_options->m_ssh_opts.c_str());
748 SetLocalCacheDirectory(m_cache_options->m_cache_dir.c_str());
756 PlatformPOSIX::DisconnectRemote ()
762 error.SetErrorStringWithFormat ("can't disconnect from the host platform '%s', always connected", GetPluginName().GetCString());
766 if (m_remote_platform_sp)
767 error = m_remote_platform_sp->DisconnectRemote ();
769 error.SetErrorString ("the platform is not currently connected");
775 PlatformPOSIX::LaunchProcess (ProcessLaunchInfo &launch_info)
781 error = Platform::LaunchProcess (launch_info);
785 if (m_remote_platform_sp)
786 error = m_remote_platform_sp->LaunchProcess (launch_info);
788 error.SetErrorString ("the platform is not currently connected");
794 PlatformPOSIX::Attach (ProcessAttachInfo &attach_info,
799 lldb::ProcessSP process_sp;
800 Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
806 TargetSP new_target_sp;
808 error = debugger.GetTargetList().CreateTarget (debugger,
814 target = new_target_sp.get();
816 log->Printf ("PlatformPOSIX::%s created new target", __FUNCTION__);
822 log->Printf ("PlatformPOSIX::%s target already existed, setting target", __FUNCTION__);
825 if (target && error.Success())
827 debugger.GetTargetList().SetSelectedTarget(target);
830 ModuleSP exe_module_sp = target->GetExecutableModule ();
831 log->Printf ("PlatformPOSIX::%s set selected target to %p %s", __FUNCTION__,
833 exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str () : "<null>" );
837 process_sp = target->CreateProcess (attach_info.GetListenerForProcess(debugger), attach_info.GetProcessPluginName(), NULL);
841 // Set UnixSignals appropriately.
842 process_sp->SetUnixSignals (Host::GetUnixSignals ());
844 ListenerSP listener_sp (new Listener("lldb.PlatformPOSIX.attach.hijack"));
845 attach_info.SetHijackListener(listener_sp);
846 process_sp->HijackProcessEvents(listener_sp.get());
847 error = process_sp->Attach (attach_info);
853 if (m_remote_platform_sp)
854 process_sp = m_remote_platform_sp->Attach (attach_info, debugger, target, error);
856 error.SetErrorString ("the platform is not currently connected");
862 PlatformPOSIX::DebugProcess (ProcessLaunchInfo &launch_info,
864 Target *target, // Can be NULL, if NULL create a new target, else use existing one
867 ProcessSP process_sp;
871 // We are going to hand this process off to debugserver which will be in charge of setting the exit status.
872 // We still need to reap it from lldb but if we let the monitor thread also set the exit status, we set up a
873 // race between debugserver & us for who will find out about the debugged process's death.
874 launch_info.GetFlags().Set(eLaunchFlagDontSetExitStatus);
875 process_sp = Platform::DebugProcess (launch_info, debugger, target, error);
879 if (m_remote_platform_sp)
880 process_sp = m_remote_platform_sp->DebugProcess (launch_info, debugger, target, error);
882 error.SetErrorString ("the platform is not currently connected");
889 PlatformPOSIX::CalculateTrapHandlerSymbolNames ()
891 m_trap_handlers.push_back (ConstString ("_sigtramp"));