//===-- PlatformPOSIX.cpp ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "PlatformPOSIX.h" // C Includes // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Log.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------ /// Default Constructor //------------------------------------------------------------------ PlatformPOSIX::PlatformPOSIX (bool is_host) : Platform(is_host), // This is the local host platform m_remote_platform_sp () { } //------------------------------------------------------------------ /// Destructor. /// /// The destructor is virtual since this class is designed to be /// inherited from by the plug-in instance. //------------------------------------------------------------------ PlatformPOSIX::~PlatformPOSIX() { } lldb_private::OptionGroupOptions* PlatformPOSIX::GetConnectionOptions (lldb_private::CommandInterpreter& interpreter) { if (m_options.get() == NULL) { m_options.reset(new OptionGroupOptions(interpreter)); m_options->Append(new OptionGroupPlatformRSync()); m_options->Append(new OptionGroupPlatformSSH()); m_options->Append(new OptionGroupPlatformCaching()); } return m_options.get(); } lldb_private::Error PlatformPOSIX::RunShellCommand (const char *command, // Shouldn't be NULL const char *working_dir, // Pass NULL to use the current working directory int *status_ptr, // Pass NULL if you don't want the process exit status int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit std::string *command_output, // Pass NULL if you don't want the command output uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish { if (IsHost()) return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); else { if (m_remote_platform_sp) return m_remote_platform_sp->RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec); else return Error("unable to run a remote command without a platform"); } } uint32_t PlatformPOSIX::MakeDirectory (const std::string &path, mode_t mode) { if (IsHost()) { return Host::MakeDirectory (path.c_str(), mode); } if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->MakeDirectory(path, mode); return Platform::MakeDirectory(path,mode); } lldb::user_id_t PlatformPOSIX::OpenFile (const FileSpec& file_spec, uint32_t flags, mode_t mode, Error &error) { if (IsHost()) { return Host::OpenFile(file_spec, flags, mode, error); } if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error); return Platform::OpenFile(file_spec, flags, mode, error); } bool PlatformPOSIX::CloseFile (lldb::user_id_t fd, Error &error) { if (IsHost()) { return Host::CloseFile(fd, error); } if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->CloseFile(fd, error); return Platform::CloseFile(fd, error); } uint64_t PlatformPOSIX::ReadFile (lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Error &error) { if (IsHost()) { return Host::ReadFile(fd, offset, dst, dst_len, error); } if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error); return Platform::ReadFile(fd, offset, dst, dst_len, error); } uint64_t PlatformPOSIX::WriteFile (lldb::user_id_t fd, uint64_t offset, const void* src, uint64_t src_len, Error &error) { if (IsHost()) { return Host::WriteFile(fd, offset, src, src_len, error); } if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error); return Platform::WriteFile(fd, offset, src, src_len, error); } static uint32_t chown_file(Platform *platform, const char* path, uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) { if (!platform || !path || *path == 0) return UINT32_MAX; if (uid == UINT32_MAX && gid == UINT32_MAX) return 0; // pretend I did chown correctly - actually I just didn't care StreamString command; command.PutCString("chown "); if (uid != UINT32_MAX) command.Printf("%d",uid); if (gid != UINT32_MAX) command.Printf(":%d",gid); command.Printf("%s",path); int status; platform->RunShellCommand(command.GetData(), NULL, &status, NULL, NULL, 10); return status; } lldb_private::Error PlatformPOSIX::PutFile (const lldb_private::FileSpec& source, const lldb_private::FileSpec& destination, uint32_t uid, uint32_t gid) { if (IsHost()) { if (FileSpec::Equal(source, destination, true)) return Error(); // cp src dst // chown uid:gid dst std::string src_path (source.GetPath()); if (src_path.empty()) return Error("unable to get file path for source"); std::string dst_path (destination.GetPath()); if (dst_path.empty()) return Error("unable to get file path for destination"); StreamString command; command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str()); int status; RunShellCommand(command.GetData(), NULL, &status, NULL, NULL, 10); if (status != 0) return Error("unable to perform copy"); if (uid == UINT32_MAX && gid == UINT32_MAX) return Error(); if (chown_file(this,dst_path.c_str(),uid,gid) != 0) return Error("unable to perform chown"); return Error(); } else if (IsRemote() && m_remote_platform_sp) { if (GetSupportsRSync()) { std::string src_path (source.GetPath()); if (src_path.empty()) return Error("unable to get file path for source"); std::string dst_path (destination.GetPath()); if (dst_path.empty()) return Error("unable to get file path for destination"); StreamString command; if (GetIgnoresRemoteHostname()) { if (!GetRSyncPrefix()) command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(), dst_path.c_str()); else command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(), GetRSyncPrefix(), dst_path.c_str()); } else command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(), GetHostname(), dst_path.c_str()); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("[PutFile] Running command: %s\n", command.GetData()); int retcode; Host::RunShellCommand(command.GetData(), NULL, &retcode, NULL, NULL, 60); if (retcode == 0) { // Don't chown a local file for a remote system // if (chown_file(this,dst_path.c_str(),uid,gid) != 0) // return Error("unable to perform chown"); return Error(); } // if we are still here rsync has failed - let's try the slow way before giving up } // open // read, write, read, write, ... // close // chown uid:gid dst Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("[PutFile] Using block by block transfer....\n"); File source_file(source, File::eOpenOptionRead, File::ePermissionsUserRW); if (!source_file.IsValid()) return Error("unable to open source file"); Error error; lldb::user_id_t dest_file = OpenFile (destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate, File::ePermissionsUserRWX | File::ePermissionsGroupRX | File::ePermissionsWorldRX, error); if (log) log->Printf ("dest_file = %" PRIu64 "\n", dest_file); if (error.Fail()) return error; if (dest_file == UINT64_MAX) return Error("unable to open target file"); lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); uint64_t offset = 0; while (error.Success()) { size_t bytes_read = buffer_sp->GetByteSize(); error = source_file.Read(buffer_sp->GetBytes(), bytes_read); if (bytes_read) { WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error); offset += bytes_read; } else break; } CloseFile(dest_file, error); if (uid == UINT32_MAX && gid == UINT32_MAX) return error; // This is remopve, don't chown a local file... // std::string dst_path (destination.GetPath()); // if (chown_file(this,dst_path.c_str(),uid,gid) != 0) // return Error("unable to perform chown"); return error; } return Platform::PutFile(source,destination,uid,gid); } lldb::user_id_t PlatformPOSIX::GetFileSize (const FileSpec& file_spec) { if (IsHost()) { return Host::GetFileSize(file_spec); } if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->GetFileSize(file_spec); return Platform::GetFileSize(file_spec); } bool PlatformPOSIX::GetFileExists (const FileSpec& file_spec) { if (IsHost()) { return file_spec.Exists(); } if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->GetFileExists(file_spec); return Platform::GetFileExists(file_spec); } uint32_t PlatformPOSIX::GetFilePermissions (const lldb_private::FileSpec &file_spec, lldb_private::Error &error) { if (IsHost()) { return File::GetPermissions(file_spec.GetPath().c_str(), error); } if (IsRemote() && m_remote_platform_sp) return m_remote_platform_sp->GetFilePermissions(file_spec, error); return Platform::GetFilePermissions(file_spec, error); } lldb_private::Error PlatformPOSIX::GetFile (const lldb_private::FileSpec& source /* remote file path */, const lldb_private::FileSpec& destination /* local file path */) { // Check the args, first. std::string src_path (source.GetPath()); if (src_path.empty()) return Error("unable to get file path for source"); std::string dst_path (destination.GetPath()); if (dst_path.empty()) return Error("unable to get file path for destination"); if (IsHost()) { if (FileSpec::Equal(source, destination, true)) return Error("local scenario->source and destination are the same file path: no operation performed"); // cp src dst StreamString cp_command; cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str()); int status; RunShellCommand(cp_command.GetData(), NULL, &status, NULL, NULL, 10); if (status != 0) return Error("unable to perform copy"); return Error(); } else if (IsRemote() && m_remote_platform_sp) { if (GetSupportsRSync()) { StreamString command; if (GetIgnoresRemoteHostname()) { if (!GetRSyncPrefix()) command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(), dst_path.c_str()); else command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(), src_path.c_str(), dst_path.c_str()); } else command.Printf("rsync %s %s:%s %s", GetRSyncOpts(), m_remote_platform_sp->GetHostname(), src_path.c_str(), dst_path.c_str()); Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("[GetFile] Running command: %s\n", command.GetData()); int retcode; Host::RunShellCommand(command.GetData(), NULL, &retcode, NULL, NULL, 60); if (retcode == 0) return Error(); // If we are here, rsync has failed - let's try the slow way before giving up } // open src and dst // read/write, read/write, read/write, ... // close src // close dst Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); if (log) log->Printf("[GetFile] Using block by block transfer....\n"); Error error; user_id_t fd_src = OpenFile (source, File::eOpenOptionRead, File::ePermissionsDefault, error); if (fd_src == UINT64_MAX) return Error("unable to open source file"); uint32_t permissions = GetFilePermissions(source, error); if (permissions == 0) permissions = File::ePermissionsDefault; user_id_t fd_dst = Host::OpenFile(destination, File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate, permissions, error); if (fd_dst == UINT64_MAX) { if (error.Success()) error.SetErrorString("unable to open destination file"); } if (error.Success()) { lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); uint64_t offset = 0; error.Clear(); while (error.Success()) { const uint64_t n_read = ReadFile (fd_src, offset, buffer_sp->GetBytes(), buffer_sp->GetByteSize(), error); if (error.Fail()) break; if (n_read == 0) break; if (Host::WriteFile(fd_dst, offset, buffer_sp->GetBytes(), n_read, error) != n_read) { if (!error.Fail()) error.SetErrorString("unable to write to destination file"); break; } offset += n_read; } } // Ignore the close error of src. if (fd_src != UINT64_MAX) CloseFile(fd_src, error); // And close the dst file descriptot. if (fd_dst != UINT64_MAX && !Host::CloseFile(fd_dst, error)) { if (!error.Fail()) error.SetErrorString("unable to close destination file"); } return error; } return Platform::GetFile(source,destination); } std::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() { StreamString stream; if (GetSupportsRSync()) { stream.PutCString("rsync"); if ( (GetRSyncOpts() && *GetRSyncOpts()) || (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) { stream.Printf(", options: "); if (GetRSyncOpts() && *GetRSyncOpts()) stream.Printf("'%s' ",GetRSyncOpts()); stream.Printf(", prefix: "); if (GetRSyncPrefix() && *GetRSyncPrefix()) stream.Printf("'%s' ",GetRSyncPrefix()); if (GetIgnoresRemoteHostname()) stream.Printf("ignore remote-hostname "); } } if (GetSupportsSSH()) { stream.PutCString("ssh"); if (GetSSHOpts() && *GetSSHOpts()) stream.Printf(", options: '%s' ",GetSSHOpts()); } if (GetLocalCacheDirectory() && *GetLocalCacheDirectory()) stream.Printf("cache dir: %s",GetLocalCacheDirectory()); if (stream.GetSize()) return stream.GetData(); else return ""; } bool PlatformPOSIX::CalculateMD5 (const FileSpec& file_spec, uint64_t &low, uint64_t &high) { if (IsHost()) return Platform::CalculateMD5 (file_spec, low, high); if (m_remote_platform_sp) return m_remote_platform_sp->CalculateMD5(file_spec, low, high); return false; }