This commit is contained in:
parent
330bdf9941
commit
4d6702b099
@ -32,7 +32,8 @@ bool execute_local_command_interactive(const sCommand &command, bool silent)
|
||||
return false;
|
||||
} else if (pid == 0) {
|
||||
// Child process
|
||||
std::vector<const char *> commandvec = {"bash", "-c", full_command.c_str(),NULL};
|
||||
ASSERT(full_command.find("'") == std::string::npos, "Raw command must not contain single quotes");
|
||||
std::vector<const char *> commandvec = {"bash", "-c", halfquote(full_command).c_str(),NULL};
|
||||
|
||||
if (!silent) {
|
||||
std::cout << "Executing command: ";
|
||||
@ -121,28 +122,17 @@ bool execute_ssh_command(const sSSHInfo &ssh_info, const sCommand &command, cMod
|
||||
ssh_cmd << "ssh -p " << ssh_info.port << " " << (hasFlag(mode, cMode::Interactive) ? "-tt " : "")
|
||||
<< ssh_info.user << "@" << ssh_info.host;
|
||||
|
||||
std::string cmdstr;
|
||||
if (!is_raw(mode))
|
||||
cmdstr = quote("bash -c " + command.construct_cmd(cStyle::Safe));
|
||||
else
|
||||
{
|
||||
std::string raw_cmd = command.construct_cmd(cStyle::Raw);
|
||||
ASSERT(raw_cmd.find("'") == std::string::npos, "Raw command must not contain single quotes");
|
||||
cmdstr = "bash -c "+ halfquote(raw_cmd);
|
||||
}
|
||||
|
||||
std::string cmdstr = command.construct_cmd(is_raw(mode) ? cStyle::Raw : cStyle::Safe);
|
||||
ASSERT(cmdstr.find("'") == std::string::npos, "Raw command must not contain single quotes");
|
||||
cmdstr = "bash -c " + halfquote(cmdstr);
|
||||
sCommand ssh_command(ssh_cmd.str() + " " + cmdstr);
|
||||
|
||||
bool rval = execute_local_command(ssh_command, mode, output);
|
||||
|
||||
if (!rval) {
|
||||
std::cerr <<std::endl<<std::endl;
|
||||
std::cerr << "Error: Failed to execute ssh command: { [" << ssh_command.get_directory_to_run_in() << "], [";
|
||||
std::cerr << ssh_command.get_command_to_run() << "], [";
|
||||
for (const auto& env_var : ssh_command.get_env_vars()) {
|
||||
std::cerr << env_var.first << "=" << env_var.second << ", ";
|
||||
}
|
||||
std::cerr << "] }" << std::endl;
|
||||
std::cerr << "Error: Failed to execute ssh command:" << std::endl;
|
||||
std::cerr << "\033[90m" << ssh_command.get_command_to_run() << "\033[0m" << std::endl;
|
||||
std::cerr <<std::endl<<std::endl;
|
||||
}
|
||||
return rval;
|
||||
|
@ -1,363 +0,0 @@
|
||||
#include "runner.hpp"
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include <termios.h>
|
||||
#include <sys/select.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
namespace runner {
|
||||
|
||||
namespace {
|
||||
|
||||
// String trimming functions
|
||||
void ltrim(std::string& s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
}
|
||||
|
||||
void rtrim(std::string& s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), s.end());
|
||||
}
|
||||
|
||||
void trim(std::string& s) {
|
||||
ltrim(s);
|
||||
rtrim(s);
|
||||
}
|
||||
|
||||
// Safe version that handles nullptr
|
||||
void trim(std::string* s) {
|
||||
if (s) {
|
||||
trim(*s);
|
||||
}
|
||||
}
|
||||
|
||||
ssh_session ssh_connect_and_auth(const sSSHInfo* sshinfo, const std::map<std::string, std::string>& env, std::string* error) {
|
||||
ssh_session session = ssh_new();
|
||||
if (!session) {
|
||||
if (error) *error = "Failed to create SSH session.";
|
||||
return nullptr;
|
||||
}
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, sshinfo->host.c_str());
|
||||
if (!sshinfo->port.empty()) {
|
||||
int port = std::stoi(sshinfo->port);
|
||||
ssh_options_set(session, SSH_OPTIONS_PORT, &port);
|
||||
}
|
||||
if (!sshinfo->user.empty()) {
|
||||
ssh_options_set(session, SSH_OPTIONS_USER, sshinfo->user.c_str());
|
||||
}
|
||||
int rc = ssh_connect(session);
|
||||
if (rc != SSH_OK) {
|
||||
if (error) *error = std::string("SSH connection failed: ") + ssh_get_error(session);
|
||||
ssh_free(session);
|
||||
return nullptr;
|
||||
}
|
||||
rc = ssh_userauth_publickey_auto(session, nullptr, nullptr);
|
||||
if (rc != SSH_AUTH_SUCCESS) {
|
||||
auto it = env.find("SSHPASS");
|
||||
if (it != env.end()) {
|
||||
rc = ssh_userauth_password(session, nullptr, it->second.c_str());
|
||||
}
|
||||
}
|
||||
if (rc != SSH_AUTH_SUCCESS) {
|
||||
if (error) *error = std::string("SSH authentication failed: ") + ssh_get_error(session);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
return nullptr;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
} // namespace runner
|
||||
|
||||
// runner_local: non-interactive local command execution
|
||||
bool runner_local::execute() {
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
std::cerr << "Failed to fork" << std::endl;
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
if (pid == 0) {
|
||||
// Child process
|
||||
if (!mWorkingDir.empty()) {
|
||||
if (chdir(mWorkingDir.c_str()) != 0) {
|
||||
perror("chdir");
|
||||
exit(127);
|
||||
}
|
||||
}
|
||||
// Set environment variables
|
||||
for (const auto& [k, v] : mEnv) {
|
||||
setenv(k.c_str(), v.c_str(), 1);
|
||||
}
|
||||
// Build argv
|
||||
std::vector<char*> argv;
|
||||
argv.push_back(const_cast<char*>(mCommand.c_str()));
|
||||
for (auto& arg : mArgs) argv.push_back(const_cast<char*>(arg.c_str()));
|
||||
argv.push_back(nullptr);
|
||||
if (mSilent) {
|
||||
int fd = open("/dev/null", O_WRONLY);
|
||||
if (fd != -1) {
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Executing command: " << mCommand << std::endl;
|
||||
std::cout << "Args: ";
|
||||
for (auto& arg : mArgs) std::cout << "[" << arg << "] ";
|
||||
std::cout << std::endl;
|
||||
|
||||
execvp(mCommand.c_str(), argv.data());
|
||||
perror("execvp");
|
||||
exit(127);
|
||||
} else {
|
||||
int status = 0;
|
||||
waitpid(pid, &status, 0);
|
||||
if (WIFEXITED(status)) {
|
||||
mReturnCode = WEXITSTATUS(status);
|
||||
} else {
|
||||
mReturnCode = -1;
|
||||
}
|
||||
return mReturnCode == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// runner_local_interactive: interactive local command execution
|
||||
bool runner_local_interactive::execute() {
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
std::cerr << "Failed to fork" << std::endl;
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
if (pid == 0) {
|
||||
if (!mWorkingDir.empty()) {
|
||||
if (chdir(mWorkingDir.c_str()) != 0) {
|
||||
perror("chdir");
|
||||
exit(127);
|
||||
}
|
||||
}
|
||||
for (const auto& [k, v] : mEnv) {
|
||||
setenv(k.c_str(), v.c_str(), 1);
|
||||
}
|
||||
std::vector<char*> argv;
|
||||
argv.push_back(const_cast<char*>(mCommand.c_str()));
|
||||
for (auto& arg : mArgs) argv.push_back(const_cast<char*>(arg.c_str()));
|
||||
argv.push_back(nullptr);
|
||||
// Attach to terminal (no redirection)
|
||||
execvp(mCommand.c_str(), argv.data());
|
||||
perror("execvp");
|
||||
exit(127);
|
||||
} else {
|
||||
int status = 0;
|
||||
waitpid(pid, &status, 0);
|
||||
if (WIFEXITED(status)) {
|
||||
mReturnCode = WEXITSTATUS(status);
|
||||
} else {
|
||||
mReturnCode = -1;
|
||||
}
|
||||
return mReturnCode == 0;
|
||||
}
|
||||
}
|
||||
|
||||
// runner_ssh: non-interactive SSH command execution
|
||||
bool runner_ssh::execute() {
|
||||
std::string error;
|
||||
ssh_session session = ssh_connect_and_auth(&mSSHInfo, mEnv, &error);
|
||||
if (!session) {
|
||||
std::cerr << error << std::endl;
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
ssh_channel channel = ssh_channel_new(session);
|
||||
if (!channel) {
|
||||
std::cerr << "Failed to create SSH channel." << std::endl;
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
if (ssh_channel_open_session(channel) != SSH_OK) {
|
||||
std::cerr << "Failed to open SSH channel: " << ssh_get_error(session) << std::endl;
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
std::stringstream cmd;
|
||||
cmd << mCommand;
|
||||
for (const auto& arg : mArgs) {
|
||||
cmd << " '" << arg << "'";
|
||||
}
|
||||
int rc = ssh_channel_request_exec(channel, cmd.str().c_str());
|
||||
if (rc != SSH_OK) {
|
||||
std::cerr << "SSH exec failed: " << ssh_get_error(session) << std::endl;
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
char buffer[256];
|
||||
int nbytes;
|
||||
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
|
||||
if (!mSilent) std::cout.write(buffer, nbytes);
|
||||
}
|
||||
mReturnCode = ssh_channel_get_exit_status(channel);
|
||||
ssh_channel_send_eof(channel);
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
return mReturnCode == 0;
|
||||
}
|
||||
|
||||
// runner_ssh_interactive: interactive SSH command execution
|
||||
bool runner_ssh_interactive::execute() {
|
||||
std::string error;
|
||||
ssh_session session = ssh_connect_and_auth(&mSSHInfo, mEnv, &error);
|
||||
if (!session) {
|
||||
std::cerr << error << std::endl;
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
ssh_channel channel = ssh_channel_new(session);
|
||||
if (!channel) {
|
||||
std::cerr << "Failed to create SSH channel." << std::endl;
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
if (ssh_channel_open_session(channel) != SSH_OK) {
|
||||
std::cerr << "Failed to open SSH channel: " << ssh_get_error(session) << std::endl;
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
std::stringstream cmd;
|
||||
cmd << mCommand;
|
||||
for (const auto& arg : mArgs) {
|
||||
cmd << " '" << arg << "'";
|
||||
}
|
||||
int rc = ssh_channel_request_pty(channel);
|
||||
if (rc != SSH_OK) {
|
||||
std::cerr << "Failed to request PTY: " << ssh_get_error(session) << std::endl;
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
rc = ssh_channel_request_shell(channel);
|
||||
if (rc != SSH_OK) {
|
||||
std::cerr << "Failed to request shell: " << ssh_get_error(session) << std::endl;
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
// Forward input/output between user and SSH channel
|
||||
fd_set fds;
|
||||
char buffer[256];
|
||||
while (true) {
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0, &fds); // stdin
|
||||
int ssh_fd = ssh_get_fd(session);
|
||||
FD_SET(ssh_fd, &fds);
|
||||
int maxfd = std::max(0, ssh_fd) + 1;
|
||||
int ret = select(maxfd, &fds, nullptr, nullptr, nullptr);
|
||||
if (ret < 0) break;
|
||||
if (FD_ISSET(0, &fds)) {
|
||||
int n = read(0, buffer, sizeof(buffer));
|
||||
if (n > 0) ssh_channel_write(channel, buffer, n);
|
||||
}
|
||||
if (FD_ISSET(ssh_fd, &fds)) {
|
||||
int n = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
|
||||
if (n > 0) write(1, buffer, n);
|
||||
else if (n == 0) break;
|
||||
}
|
||||
if (ssh_channel_is_closed(channel)) break;
|
||||
}
|
||||
mReturnCode = ssh_channel_get_exit_status(channel);
|
||||
ssh_channel_send_eof(channel);
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
return mReturnCode == 0;
|
||||
}
|
||||
|
||||
// runner_ssh_capture: SSH command execution with output capture
|
||||
bool runner_ssh_capture::execute() {
|
||||
std::string error;
|
||||
ssh_session session = ssh_connect_and_auth(&mSSHInfo, mEnv, &error);
|
||||
if (!session) {
|
||||
std::cerr << error << std::endl;
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
ssh_channel channel = ssh_channel_new(session);
|
||||
if (!channel) {
|
||||
std::cerr << "Failed to create SSH channel." << std::endl;
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
if (ssh_channel_open_session(channel) != SSH_OK) {
|
||||
std::cerr << "Failed to open SSH channel: " << ssh_get_error(session) << std::endl;
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
std::stringstream cmd;
|
||||
cmd << mCommand;
|
||||
for (const auto& arg : mArgs) {
|
||||
cmd << " '" << arg << "'";
|
||||
}
|
||||
int rc = ssh_channel_request_exec(channel, cmd.str().c_str());
|
||||
if (rc != SSH_OK) {
|
||||
std::cerr << "SSH exec failed: " << ssh_get_error(session) << std::endl;
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
char buffer[256];
|
||||
int nbytes;
|
||||
mOutput.clear();
|
||||
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
|
||||
mOutput.append(buffer, nbytes);
|
||||
}
|
||||
mReturnCode = ssh_channel_get_exit_status(channel);
|
||||
ssh_channel_send_eof(channel);
|
||||
ssh_channel_close(channel);
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
return mReturnCode == 0;
|
||||
}
|
||||
} // namespace runner
|
@ -1,81 +0,0 @@
|
||||
#ifndef RUNNER_HPP
|
||||
#define RUNNER_HPP
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
namespace runner {
|
||||
|
||||
struct sSSHInfo {
|
||||
std::string host;
|
||||
std::string user;
|
||||
std::string port;
|
||||
};
|
||||
|
||||
class runner {
|
||||
public:
|
||||
runner(std::string command, std::vector<std::string> args={}, std::string working_dir="", std::map<std::string, std::string> env={}, bool silent=false) :
|
||||
mCommand(command),
|
||||
mArgs(args),
|
||||
mWorkingDir(working_dir),
|
||||
mEnv(env),
|
||||
mSilent(silent),
|
||||
mReturnCode(0) {}
|
||||
virtual ~runner() {}
|
||||
|
||||
virtual bool execute() = 0;
|
||||
|
||||
int return_code() const { return mReturnCode; }
|
||||
protected:
|
||||
std::string mCommand;
|
||||
std::vector<std::string> mArgs;
|
||||
std::string mWorkingDir;
|
||||
std::map<std::string, std::string> mEnv;
|
||||
bool mSilent;
|
||||
int mReturnCode;
|
||||
};
|
||||
|
||||
class runner_local : public runner {
|
||||
public:
|
||||
runner_local(std::string command, std::vector<std::string> args={}, std::string working_dir="", std::map<std::string, std::string> env={}, bool silent=false) :
|
||||
runner(command, args, working_dir, env, silent) {}
|
||||
bool execute() override;
|
||||
};
|
||||
|
||||
class runner_local_interactive : public runner {
|
||||
public:
|
||||
runner_local_interactive(std::string command, std::vector<std::string> args={}, std::string working_dir="", std::map<std::string, std::string> env={}) :
|
||||
runner(command, args, working_dir, env, false) {}
|
||||
bool execute() override;
|
||||
};
|
||||
|
||||
class runner_ssh : public runner {
|
||||
public:
|
||||
runner_ssh(const sSSHInfo sshinfo, std::string command, std::vector<std::string> args={}, std::string working_dir="", std::map<std::string, std::string> env={}, bool silent=false) :
|
||||
runner(command, args, working_dir, env, silent), mSSHInfo(sshinfo) {}
|
||||
bool execute() override;
|
||||
protected:
|
||||
const sSSHInfo mSSHInfo;
|
||||
};
|
||||
|
||||
class runner_ssh_interactive : public runner_ssh {
|
||||
public:
|
||||
runner_ssh_interactive(const sSSHInfo sshinfo, std::string command, std::vector<std::string> args={}, std::string working_dir="", std::map<std::string, std::string> env={}, bool silent=true) :
|
||||
runner_ssh(sshinfo, command, args, working_dir, env, silent) {}
|
||||
bool execute() override;
|
||||
};
|
||||
|
||||
class runner_ssh_capture : public runner_ssh {
|
||||
public:
|
||||
runner_ssh_capture(const sSSHInfo sshinfo, std::string output, std::string command, std::vector<std::string> args={}, std::string working_dir="", std::map<std::string, std::string> env={}) :
|
||||
runner_ssh(sshinfo, command, args, working_dir, env, true), mOutput(output) {}
|
||||
bool execute() override;
|
||||
protected:
|
||||
std::string & mOutput;
|
||||
};
|
||||
|
||||
} // namespace runner
|
||||
|
||||
#endif // RUNNER_HPP
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
source "${AGENT_PATH}/_common.sh"
|
||||
_check_required_env_vars
|
||||
_check_required_env_vars "CONFIG_PATH"
|
||||
|
||||
# PORT SCRIPT
|
||||
# The port script is OPTIONAL.
|
||||
|
Loading…
x
Reference in New Issue
Block a user