moved and formatted code for sftp to sshSession class

This commit is contained in:
vladimir.kuznetsov 2022-12-22 19:55:30 +03:00
parent 5075fe358e
commit 81cf108471
4 changed files with 211 additions and 275 deletions

View file

@ -18,12 +18,6 @@
#include <fstream>
#include <sys/stat.h>
#include <fcntl.h>
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include "sshconnectionmanager.h"
#include <chrono>
#include <thread>
@ -35,11 +29,6 @@
#include <configurators/vpn_configurator.h>
#define SFTP_TRANSFER_CHUNK_SIZE 16384
#define SSH_BUFFER_SIZE 256
using namespace QSsh;
ServerController::ServerController(std::shared_ptr<Settings> 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<void(const QString &)> &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<SshSession> 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)

View file

@ -3,11 +3,6 @@
#include <QJsonObject>
#include <QObject>
#include "sshconnection.h"
#include "sshremoteprocess.h"
#include <libssh/libssh.h>
#include <libssh/sftp.h>
#include "debug.h"
#include "defs.h"
@ -31,12 +26,12 @@ public:
typedef QList<QPair<QString, QString>> 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());

View file

@ -3,6 +3,9 @@
#include <QEventLoop>
#include <QtConcurrent>
#include <fstream>
#include <fcntl.h>
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<void(const QString &)> &cbReadStdOut,
const std::function<void(const QString &)> &cbReadStdErr)
{
if (m_channel == NULL) {
qDebug() << "ssh channel not initialized";
return ErrorCode::SshAuthenticationError;
}
QFutureWatcher<ErrorCode> watcher;
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &SshSession::writeToChannelFinished);
QFuture<ErrorCode> 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<ErrorCode> watcher;
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &SshSession::sftpFileCopyFinished);
QFuture<ErrorCode> 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();
}

View file

@ -18,20 +18,25 @@ public:
~SshSession();
ErrorCode initChannel(const ServerCredentials &credentials);
ErrorCode initSftp(const ServerCredentials &credentials);
ErrorCode writeToChannel(const QString &data,
const std::function<void(const QString &)> &cbReadStdOut,
const std::function<void(const QString &)> &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