| //===-- 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"); |
| } |
| } |
| |
| Error |
| PlatformPOSIX::MakeDirectory (const char *path, uint32_t file_permissions) |
| { |
| if (m_remote_platform_sp) |
| return m_remote_platform_sp->MakeDirectory(path, file_permissions); |
| else |
| return Platform::MakeDirectory(path ,file_permissions); |
| } |
| |
| Error |
| PlatformPOSIX::GetFilePermissions (const char *path, uint32_t &file_permissions) |
| { |
| if (m_remote_platform_sp) |
| return m_remote_platform_sp->GetFilePermissions(path, file_permissions); |
| else |
| return Platform::GetFilePermissions(path ,file_permissions); |
| } |
| |
| Error |
| PlatformPOSIX::SetFilePermissions (const char *path, uint32_t file_permissions) |
| { |
| if (m_remote_platform_sp) |
| return m_remote_platform_sp->MakeDirectory(path, file_permissions); |
| else |
| return Platform::SetFilePermissions(path ,file_permissions); |
| } |
| |
| lldb::user_id_t |
| PlatformPOSIX::OpenFile (const FileSpec& file_spec, |
| uint32_t flags, |
| uint32_t mode, |
| Error &error) |
| { |
| if (IsHost()) |
| return Host::OpenFile(file_spec, flags, mode, error); |
| else if (m_remote_platform_sp) |
| return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error); |
| else |
| 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); |
| else if (m_remote_platform_sp) |
| return m_remote_platform_sp->CloseFile(fd, error); |
| else |
| 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); |
| else if (m_remote_platform_sp) |
| return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error); |
| else |
| 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); |
| else if (m_remote_platform_sp) |
| return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error); |
| else |
| 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) |
| { |
| Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); |
| |
| 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 (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()); |
| 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 |
| } |
| |
| if (log) |
| log->Printf ("PlatformPOSIX::PutFile(src='%s', dst='%s', uid=%u, gid=%u)", |
| source.GetPath().c_str(), |
| destination.GetPath().c_str(), |
| uid, |
| gid); // REMOVE THIS PRINTF PRIOR TO CHECKIN |
| // open |
| // read, write, read, write, ... |
| // close |
| // chown uid:gid dst |
| if (log) |
| log->Printf("[PutFile] Using block by block transfer....\n"); |
| |
| uint32_t source_open_options = File::eOpenOptionRead; |
| if (source.GetFileType() == FileSpec::eFileTypeSymbolicLink) |
| source_open_options |= File::eOpenoptionDontFollowSymlinks; |
| |
| File source_file(source, source_open_options, lldb::eFilePermissionsUserRW); |
| Error error; |
| uint32_t permissions = source_file.GetPermissions(error); |
| if (permissions == 0) |
| permissions = lldb::eFilePermissionsFileDefault; |
| |
| if (!source_file.IsValid()) |
| return Error("unable to open source file"); |
| lldb::user_id_t dest_file = OpenFile (destination, |
| File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate, |
| permissions, |
| 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); |
| else if (m_remote_platform_sp) |
| return m_remote_platform_sp->GetFileSize(file_spec); |
| else |
| return Platform::GetFileSize(file_spec); |
| } |
| |
| Error |
| PlatformPOSIX::CreateSymlink(const char *src, const char *dst) |
| { |
| if (IsHost()) |
| return Host::Symlink(src, dst); |
| else if (m_remote_platform_sp) |
| return m_remote_platform_sp->CreateSymlink(src, dst); |
| else |
| return Platform::CreateSymlink(src, dst); |
| } |
| |
| bool |
| PlatformPOSIX::GetFileExists (const FileSpec& file_spec) |
| { |
| if (IsHost()) |
| return file_spec.Exists(); |
| else if (m_remote_platform_sp) |
| return m_remote_platform_sp->GetFileExists(file_spec); |
| else |
| return Platform::GetFileExists(file_spec); |
| } |
| |
| Error |
| PlatformPOSIX::Unlink (const char *path) |
| { |
| if (IsHost()) |
| return Host::Unlink (path); |
| else if (m_remote_platform_sp) |
| return m_remote_platform_sp->Unlink(path); |
| else |
| return Platform::Unlink(path); |
| } |
| |
| lldb_private::Error |
| PlatformPOSIX::GetFile (const lldb_private::FileSpec& source /* remote file path */, |
| const lldb_private::FileSpec& destination /* local file path */) |
| { |
| Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); |
| |
| // 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 (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()); |
| 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 |
| if (log) |
| log->Printf("[GetFile] Using block by block transfer....\n"); |
| Error error; |
| user_id_t fd_src = OpenFile (source, |
| File::eOpenOptionRead, |
| lldb::eFilePermissionsFileDefault, |
| error); |
| |
| if (fd_src == UINT64_MAX) |
| return Error("unable to open source file"); |
| |
| uint32_t permissions = 0; |
| error = GetFilePermissions(source.GetPath().c_str(), permissions); |
| |
| if (permissions == 0) |
| permissions = lldb::eFilePermissionsFileDefault; |
| |
| 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; |
| } |
| |
| lldb_private::ConstString |
| PlatformPOSIX::GetRemoteWorkingDirectory() |
| { |
| if (IsRemote() && m_remote_platform_sp) |
| return m_remote_platform_sp->GetRemoteWorkingDirectory(); |
| else |
| return Platform::GetRemoteWorkingDirectory(); |
| } |
| |
| bool |
| PlatformPOSIX::SetRemoteWorkingDirectory(const lldb_private::ConstString &path) |
| { |
| if (IsRemote() && m_remote_platform_sp) |
| return m_remote_platform_sp->SetRemoteWorkingDirectory(path); |
| else |
| return Platform::SetRemoteWorkingDirectory(path); |
| } |
| |
| void |
| PlatformPOSIX::CalculateTrapHandlerSymbolNames () |
| { |
| m_trap_handlers.push_back (ConstString ("_sigtramp")); |
| } |