diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 5193e5ee..6c77e2d2 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -18,12 +18,6 @@ #include #include -#include -#include -#include - -#include "sshconnectionmanager.h" - #include #include @@ -35,11 +29,6 @@ #include -#define SFTP_TRANSFER_CHUNK_SIZE 16384 -#define SSH_BUFFER_SIZE 256 - -using namespace QSsh; - ServerController::ServerController(std::shared_ptr settings, QObject *parent) : m_settings(settings) { @@ -49,48 +38,6 @@ ServerController::~ServerController() { } -ErrorCode ServerController::connectToHost(const ServerCredentials &credentials, ssh_session &session) { - - if (session == NULL) { - return ErrorCode::InternalError; - } - - int port = credentials.port; - int logVerbosity = SSH_LOG_NOLOG; - std::string hostIp = credentials.hostName.toStdString(); - std::string hostUsername = credentials.userName.toStdString() + "@" + hostIp; - - ssh_options_set(session, SSH_OPTIONS_HOST, hostIp.c_str()); - ssh_options_set(session, SSH_OPTIONS_PORT, &port); - ssh_options_set(session, SSH_OPTIONS_USER, hostUsername.c_str()); - ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &logVerbosity); - - int connection_result = ssh_connect(session); - - if (connection_result != SSH_OK) { - return ErrorCode::SshTimeoutError; - } - - std::string auth_username = credentials.userName.toStdString(); - - int auth_result = SSH_ERROR; - if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) { - ssh_key priv_key; - ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, nullptr, nullptr, &priv_key); - auth_result = ssh_userauth_publickey(session, auth_username.c_str(), priv_key); - } - else { - auth_result = ssh_userauth_password(session, auth_username.c_str(), credentials.password.toStdString().c_str()); - } - - if (auth_result != SSH_OK) { - ssh_disconnect(session); - return ErrorCode::SshAuthenticationError; - } - - return ErrorCode::NoError; -} - ErrorCode ServerController::runScript(const ServerCredentials &credentials, QString script, const std::function &cbReadStdOut, @@ -107,7 +54,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr script.replace("\r", ""); - qDebug() << "Run script " << script; + qDebug() << "Run script"; QString totalLine; const QStringList &lines = script.split("\n", Qt::SkipEmptyParts); @@ -132,8 +79,6 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr continue; } - lineToExec += "\n"; - qDebug().noquote() << "EXEC" << lineToExec; Debug::appendSshLog("Run command:" + lineToExec); @@ -288,40 +233,13 @@ ErrorCode ServerController::checkOpenVpnServer(DockerContainer container, const ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, QSsh::SftpOverwriteMode overwriteMode) { - - ssh_session ssh = ssh_new(); - - ErrorCode e = connectToHost(credentials, ssh); - if (e) { - ssh_free(ssh); - return e; + std::shared_ptr session = m_sshClient.getSession(); + if (!session) { + return ErrorCode::SshInternalError; } - - ssh_channel channel = ssh_channel_new(ssh); - - if (channel == NULL) { - ssh_disconnect(ssh); - ssh_free(ssh); - return ErrorCode::SshSftpError; - } - - sftp_session sftp_sess = sftp_new(ssh); - - if (sftp_sess == NULL) { - ssh_disconnect(ssh); - ssh_free(ssh); - return ErrorCode::SshSftpError; - } - - int init_result = sftp_init(sftp_sess); - - if (init_result != SSH_OK) { - - sftp_free(sftp_sess); - ssh_disconnect(ssh); - ssh_free(ssh); - - return ErrorCode::SshSftpError; + auto error = session->initSftp(credentials); + if (error != ErrorCode::NoError) { + return error; } QTemporaryFile localFile; @@ -331,146 +249,60 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential qDebug() << "remotePath" << remotePath; - e = copyFileToRemoteHost(ssh, sftp_sess, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc"); - - if (e) { - sftp_free(sftp_sess); - ssh_disconnect(ssh); - ssh_free(ssh); - return e; + error = session->sftpFileCopy(localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc"); + if (error != ErrorCode::NoError) { + return error; } - else { - - sftp_free(sftp_sess); - ssh_disconnect(ssh); - ssh_free(ssh); - return ErrorCode::NoError; - } -} - - -ErrorCode ServerController::copyFileToRemoteHost(ssh_session& ssh, sftp_session& sftp, std::string local_path, std::string remote_path, std::string file_desc) { - int access_type = O_WRONLY | O_CREAT | O_TRUNC; - sftp_file file; - char buffer[SFTP_TRANSFER_CHUNK_SIZE]; - int length {sizeof (buffer)}; - - file = sftp_open(sftp, remote_path.c_str(), access_type, 0);//S_IRWXU); - - if (file == NULL) { - return ErrorCode::SshSftpError; - } - - int local_file_size = std::filesystem::file_size(local_path); - int num_full_chunks = local_file_size / (SFTP_TRANSFER_CHUNK_SIZE); - - std::ifstream fin(local_path, std::ios::binary | std::ios::in); - - if (fin.is_open()) { - - for (int current_chunk_id = 0; current_chunk_id < num_full_chunks; current_chunk_id++) { - // Getting chunk from local file - fin.read(buffer, length); - - int nwritten = sftp_write(file, buffer, length); - - std::string chunk(buffer, length); - - qDebug() << "write -> " << QString(chunk.c_str()); - - if (nwritten != length) { - fin.close(); - sftp_close(file); - return ErrorCode::SshSftpError; - } - } - - int last_chapter_size = local_file_size % (SFTP_TRANSFER_CHUNK_SIZE); - - if (last_chapter_size != 0) { - char* last_chapter_buffer = new char[last_chapter_size]; - fin.read(last_chapter_buffer, last_chapter_size); - - QByteArray arr_tmp(last_chapter_buffer, last_chapter_size); - - QString std_test = QString::fromUtf8(arr_tmp); - - qDebug() << "test file " << std_test; - - int nwritten = sftp_write(file, last_chapter_buffer, last_chapter_size); - - if (nwritten != last_chapter_size) { - - fin.close(); - sftp_close(file); - delete[] last_chapter_buffer; - return ErrorCode::SshSftpError; - } - delete[] last_chapter_buffer; - } - - } else { - sftp_close(file); - return ErrorCode::SshSftpError; - } - - fin.close(); - - int close_result = sftp_close(file); - if (close_result != SSH_OK) { - return ErrorCode::SshSftpError; - } - return ErrorCode::NoError; } -ErrorCode ServerController::fromSshConnectionErrorCode(SshError error) -{ - switch (error) { - case(SshNoError): return ErrorCode::NoError; - case(QSsh::SshSocketError): return ErrorCode::SshSocketError; - case(QSsh::SshTimeoutError): return ErrorCode::SshTimeoutError; - case(QSsh::SshProtocolError): return ErrorCode::SshProtocolError; - case(QSsh::SshHostKeyError): return ErrorCode::SshHostKeyError; - case(QSsh::SshKeyFileError): return ErrorCode::SshKeyFileError; - case(QSsh::SshAuthenticationError): return ErrorCode::SshAuthenticationError; - case(QSsh::SshClosedByServerError): return ErrorCode::SshClosedByServerError; - case(QSsh::SshInternalError): return ErrorCode::SshInternalError; - default: return ErrorCode::SshInternalError; - } -} +//ErrorCode ServerController::fromSshConnectionErrorCode(SshError error) +//{ +// switch (error) { +// case(SshNoError): return ErrorCode::NoError; +// case(QSsh::SshSocketError): return ErrorCode::SshSocketError; +// case(QSsh::SshTimeoutError): return ErrorCode::SshTimeoutError; +// case(QSsh::SshProtocolError): return ErrorCode::SshProtocolError; +// case(QSsh::SshHostKeyError): return ErrorCode::SshHostKeyError; +// case(QSsh::SshKeyFileError): return ErrorCode::SshKeyFileError; +// case(QSsh::SshAuthenticationError): return ErrorCode::SshAuthenticationError; +// case(QSsh::SshClosedByServerError): return ErrorCode::SshClosedByServerError; +// case(QSsh::SshInternalError): return ErrorCode::SshInternalError; +// default: return ErrorCode::SshInternalError; +// } +//} -ErrorCode ServerController::fromSshProcessExitStatus(int exitStatus) -{ - qDebug() << exitStatus; - switch (SshRemoteProcess::ExitStatus(exitStatus)) { - case(SshRemoteProcess::ExitStatus::NormalExit): return ErrorCode::NoError; - case(SshRemoteProcess::ExitStatus::FailedToStart): return ErrorCode::FailedToStartRemoteProcessError; - case(SshRemoteProcess::ExitStatus::CrashExit): return ErrorCode::RemoteProcessCrashError; - default: return ErrorCode::SshInternalError; - } -} +//ErrorCode ServerController::fromSshProcessExitStatus(int exitStatus) +//{ +// qDebug() << exitStatus; +// switch (SshRemoteProcess::ExitStatus(exitStatus)) { +// case(SshRemoteProcess::ExitStatus::NormalExit): return ErrorCode::NoError; +// case(SshRemoteProcess::ExitStatus::FailedToStart): return ErrorCode::FailedToStartRemoteProcessError; +// case(SshRemoteProcess::ExitStatus::CrashExit): return ErrorCode::RemoteProcessCrashError; +// default: return ErrorCode::SshInternalError; +// } +//} -SshConnectionParameters ServerController::sshParams(const ServerCredentials &credentials) -{ - QSsh::SshConnectionParameters sshParams; - if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) { - sshParams.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePublicKey; - sshParams.privateKeyFile = credentials.password; - } - else { - sshParams.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePassword; - sshParams.setPassword(credentials.password); - } - sshParams.setHost(credentials.hostName); - sshParams.setUserName(credentials.userName); - sshParams.timeout = 10; - sshParams.setPort(credentials.port); - sshParams.hostKeyCheckingMode = QSsh::SshHostKeyCheckingMode::SshHostKeyCheckingNone; - sshParams.options = SshIgnoreDefaultProxy; +//SshConnectionParameters ServerController::sshParams(const ServerCredentials &credentials) +//{ +// QSsh::SshConnectionParameters sshParams; +// if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) { +// sshParams.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePublicKey; +// sshParams.privateKeyFile = credentials.password; +// } +// else { +// sshParams.authenticationType = QSsh::SshConnectionParameters::AuthenticationTypePassword; +// sshParams.setPassword(credentials.password); +// } +// sshParams.setHost(credentials.hostName); +// sshParams.setUserName(credentials.userName); +// sshParams.timeout = 10; +// sshParams.setPort(credentials.port); +// sshParams.hostKeyCheckingMode = QSsh::SshHostKeyCheckingMode::SshHostKeyCheckingNone; +// sshParams.options = SshIgnoreDefaultProxy; - return sshParams; -} +// return sshParams; +//} ErrorCode ServerController::removeAllContainers(const ServerCredentials &credentials) { @@ -830,8 +662,8 @@ QString ServerController::checkSshConnection(const ServerCredentials &credential void ServerController::disconnectFromHost(const ServerCredentials &credentials) { - SshConnection *client = acquireConnection(sshParams(credentials)); - if (client) client->disconnectFromHost(); +// SshConnection *client = acquireConnection(sshParams(credentials)); +// if (client) client->disconnectFromHost(); } ErrorCode ServerController::setupServerFirewall(const ServerCredentials &credentials) diff --git a/client/core/servercontroller.h b/client/core/servercontroller.h index d1926a5a..256e1a43 100644 --- a/client/core/servercontroller.h +++ b/client/core/servercontroller.h @@ -3,11 +3,6 @@ #include #include -#include "sshconnection.h" -#include "sshremoteprocess.h" - -#include -#include #include "debug.h" #include "defs.h" @@ -31,12 +26,12 @@ public: typedef QList> Vars; - ErrorCode fromSshConnectionErrorCode(QSsh::SshError error); +// ErrorCode fromSshConnectionErrorCode(QSsh::SshError error); // QSsh exitCode and exitStatus are different things - ErrorCode fromSshProcessExitStatus(int exitStatus); +// ErrorCode fromSshProcessExitStatus(int exitStatus); - QSsh::SshConnectionParameters sshParams(const ServerCredentials &credentials); +// QSsh::SshConnectionParameters sshParams(const ServerCredentials &credentials); void disconnectFromHost(const ServerCredentials &credentials); ErrorCode removeAllContainers(const ServerCredentials &credentials); @@ -79,10 +74,6 @@ public: QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr); private: - - ErrorCode copyFileToRemoteHost(ssh_session& ssh, sftp_session& sftp, std::string local_path, std::string remote_path, std::string file_desc); - ErrorCode connectToHost(const ServerCredentials &credentials, ssh_session& session); - ErrorCode installDockerWorker(const ServerCredentials &credentials, DockerContainer container); ErrorCode prepareHostWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); ErrorCode buildContainerWorker(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config = QJsonObject()); diff --git a/client/core/sshsession.cpp b/client/core/sshsession.cpp index a41e2441..0bd7c501 100644 --- a/client/core/sshsession.cpp +++ b/client/core/sshsession.cpp @@ -3,6 +3,9 @@ #include #include +#include +#include + SshSession::SshSession(QObject *parent) : QObject(parent) { @@ -16,6 +19,9 @@ SshSession::~SshSession() if (m_isChannelOpened) { ssh_channel_free(m_channel); } + if (m_isSftpInitialized) { + sftp_free(m_sftpSession); + } if (m_isSessionConnected) { ssh_disconnect(m_session); } @@ -104,11 +110,11 @@ ErrorCode SshSession::initChannel(const ServerCredentials &credentials) return ErrorCode::SshInternalError; } -// result = ssh_channel_request_shell(m_channel); -// if (result != SSH_OK) { -// qDebug() << ssh_get_error(m_session); -// return ErrorCode::SshInternalError; -// } + result = ssh_channel_request_shell(m_channel); + if (result != SSH_OK) { + qDebug() << ssh_get_error(m_session); + return ErrorCode::SshInternalError; + } return ErrorCode::NoError; } @@ -117,61 +123,48 @@ ErrorCode SshSession::writeToChannel(const QString &data, const std::function &cbReadStdOut, const std::function &cbReadStdErr) { + if (m_channel == NULL) { + qDebug() << "ssh channel not initialized"; + return ErrorCode::SshAuthenticationError; + } + QFutureWatcher watcher; connect(&watcher, &QFutureWatcher::finished, this, &SshSession::writeToChannelFinished); QFuture future = QtConcurrent::run([this, &data, &cbReadStdOut, &cbReadStdErr]() { - const int channelReadTimeoutMs = 10; const size_t bufferSize = 2048; int bytesRead = 0; - int attempts = 0; char buffer[bufferSize]; - std::string output1; - if (ssh_channel_is_open(m_channel) && !ssh_channel_is_eof(m_channel)) { - bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), 0, channelReadTimeoutMs); - while (bytesRead > 0) - { - output1.append(buffer, bytesRead); - bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), 0, channelReadTimeoutMs); - } - } - qDebug().noquote() << "stdOut: " << QString(output1.c_str()); - int bytesWritten = ssh_channel_write(m_channel, data.toUtf8(), (uint32_t)data.size()); + ssh_channel_write(m_channel, "\n", 1); if (bytesWritten == data.size()) { - std::string stdOut; - std::string stdErr; - auto readOutput = [&](bool isStdErr) { std::string output; if (ssh_channel_is_open(m_channel) && !ssh_channel_is_eof(m_channel)) { - bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, channelReadTimeoutMs); + bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, 50); while (bytesRead > 0) { - output.append(buffer, bytesRead); - bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, channelReadTimeoutMs); + output = std::string(buffer, bytesRead); + if (!output.empty()) { + qDebug().noquote() << (isStdErr ? "stdErr" : "stdOut") << QString(output.c_str()); + + if (cbReadStdOut && !isStdErr){ + cbReadStdOut(output.c_str()); + } + if (cbReadStdErr && isStdErr){ + cbReadStdErr(output.c_str()); + } + } + bytesRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), isStdErr, 500); } } return output; }; - stdOut = readOutput(false); - stdErr = readOutput(true); - - if (cbReadStdOut){ - cbReadStdOut(stdOut.c_str()); - } - if (cbReadStdErr){ - cbReadStdErr(stdErr.c_str()); - } - if (!stdOut.empty()) { - qDebug().noquote() << "stdOut: " << QString(stdOut.c_str()); - } - if (!stdErr.empty()) { - qDebug().noquote() << "stdErr: " << QString(stdOut.c_str()); - } + readOutput(false); + readOutput(true); } else { qDebug() << ssh_get_error(m_session); return ErrorCode::SshInternalError; @@ -188,3 +181,118 @@ ErrorCode SshSession::writeToChannel(const QString &data, return watcher.result(); } + +ErrorCode SshSession::initSftp(const ServerCredentials &credentials) +{ + m_session = ssh_new(); + + ErrorCode error = connectToHost(credentials); + if (error) { + return error; + } + + m_sftpSession = sftp_new(m_session); + + if (m_sftpSession == NULL) { + qDebug() << ssh_get_error(m_session); + return ErrorCode::SshSftpError; + } + + int result = sftp_init(m_sftpSession); + + if (result != SSH_OK) { + qDebug() << ssh_get_error(m_session); + return ErrorCode::SshSftpError; + } + + return ErrorCode::NoError; +} + +ErrorCode SshSession::sftpFileCopy(const std::string& localPath, const std::string& remotePath, const std::string& fileDesc) +{ + if (m_sftpSession == NULL) { + qDebug() << "ssh sftp session not initialized"; + return ErrorCode::SshSftpError; + } + + QFutureWatcher watcher; + connect(&watcher, &QFutureWatcher::finished, this, &SshSession::sftpFileCopyFinished); + + QFuture future = QtConcurrent::run([this, &localPath, &remotePath, &fileDesc]() { + int accessType = O_WRONLY | O_CREAT | O_TRUNC; + sftp_file file; + const size_t bufferSize = 16384; + char buffer[bufferSize]; + + file = sftp_open(m_sftpSession, remotePath.c_str(), accessType, 0);//S_IRWXU); + + if (file == NULL) { + qDebug() << ssh_get_error(m_session); + return ErrorCode::SshSftpError; + } + + int localFileSize = std::filesystem::file_size(localPath); + int chunksCount = localFileSize / (bufferSize); + + std::ifstream fin(localPath, std::ios::binary | std::ios::in); + + if (fin.is_open()) { + for (int currentChunkId = 0; currentChunkId < chunksCount; currentChunkId++) { + fin.read(buffer, bufferSize); + + int bytesWritten = sftp_write(file, buffer, bufferSize); + + std::string chunk(buffer, bufferSize); + qDebug() << "write -> " << QString(chunk.c_str()); + + if (bytesWritten != bufferSize) { + fin.close(); + sftp_close(file); + qDebug() << ssh_get_error(m_session); + return ErrorCode::SshSftpError; + } + } + + int lastChunkSize = localFileSize % (bufferSize); + + if (lastChunkSize != 0) { + fin.read(buffer, lastChunkSize); + + std::string chunk(buffer, lastChunkSize); + qDebug() << "write -> " << QString(chunk.c_str()); + + int bytesWritten = sftp_write(file, buffer, lastChunkSize); + + if (bytesWritten != lastChunkSize) { + fin.close(); + sftp_close(file); + qDebug() << ssh_get_error(m_session); + return ErrorCode::SshSftpError; + } + } + + } else { + sftp_close(file); + qDebug() << ssh_get_error(m_session); + return ErrorCode::SshSftpError; + } + + fin.close(); + + int result = sftp_close(file); + if (result != SSH_OK) { + qDebug() << ssh_get_error(m_session); + return ErrorCode::SshSftpError; + } + + return ErrorCode::NoError; + }); + watcher.setFuture(future); + + QEventLoop wait; + + QObject::connect(this, &SshSession::sftpFileCopyFinished, &wait, &QEventLoop::quit); + wait.exec(); + + return watcher.result(); +} diff --git a/client/core/sshsession.h b/client/core/sshsession.h index 5a399214..6d86ed83 100644 --- a/client/core/sshsession.h +++ b/client/core/sshsession.h @@ -18,20 +18,25 @@ public: ~SshSession(); ErrorCode initChannel(const ServerCredentials &credentials); + ErrorCode initSftp(const ServerCredentials &credentials); ErrorCode writeToChannel(const QString &data, const std::function &cbReadStdOut, const std::function &cbReadStdErr); + ErrorCode sftpFileCopy(const std::string& localPath, const std::string& remotePath, const std::string& fileDesc); private: ErrorCode connectToHost(const ServerCredentials &credentials); ssh_session m_session; ssh_channel m_channel; + sftp_session m_sftpSession; bool m_isChannelOpened = false; bool m_isSessionConnected = false; bool m_isNeedSendChannelEof = false; + bool m_isSftpInitialized = false; signals: void writeToChannelFinished(); + void sftpFileCopyFinished(); }; #endif // SSHSESSION_H