This commit is contained in:
parent
de337f51f3
commit
409f532409
@ -14,6 +14,14 @@ namespace runner {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
// Add working_dir to the forward declaration
|
||||||
|
ssh_session ssh_connect_and_auth(const sSSHInfo* sshinfo, const std::map<std::string, std::string>& env, std::string* error);
|
||||||
|
std::string ssh_build_remote_command(const std::string& command, const std::vector<std::string>& args, const std::string& working_dir, const std::map<std::string, std::string>& env);
|
||||||
|
std::string escape_shell_arg(const std::string& arg);
|
||||||
|
int ssh_interactive_shell_session(ssh_session session, ssh_channel channel, const std::string& remote_cmd_str, const std::string& command, std::string* output);
|
||||||
|
int ssh_exec_command(ssh_session session, ssh_channel channel, const std::string& remote_cmd_str, bool silent, std::string* output, const std::map<std::string, std::string>& env, const std::string& working_dir);
|
||||||
|
int local_execute_cmd(const std::string& command, const std::vector<std::string>& args, const std::string& working_dir, const std::map<std::string, std::string>& env, bool silent, bool interactive, std::string* output);
|
||||||
|
|
||||||
ssh_session ssh_connect_and_auth(const sSSHInfo* sshinfo, const std::map<std::string, std::string>& env, std::string* error) {
|
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();
|
ssh_session session = ssh_new();
|
||||||
if (!session) {
|
if (!session) {
|
||||||
@ -66,6 +74,30 @@ std::string ssh_build_remote_command(const std::string& command, const std::vect
|
|||||||
return remote_cmd.str();
|
return remote_cmd.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utility function to escape special shell characters
|
||||||
|
std::string escape_shell_arg(const std::string& arg) {
|
||||||
|
std::ostringstream escaped;
|
||||||
|
escaped << '"';
|
||||||
|
for (char c : arg) {
|
||||||
|
if (c == '"' || c == '\\' || c == '$' || c == '`') {
|
||||||
|
escaped << '\\';
|
||||||
|
}
|
||||||
|
escaped << c;
|
||||||
|
}
|
||||||
|
escaped << '"';
|
||||||
|
return escaped.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For non-interactive SSH, just build the command with args
|
||||||
|
std::string ssh_build_command_only(const std::string& command, const std::vector<std::string>& args) {
|
||||||
|
std::ostringstream remote_cmd;
|
||||||
|
remote_cmd << command;
|
||||||
|
for (const auto& arg : args) {
|
||||||
|
remote_cmd << " " << escape_shell_arg(arg);
|
||||||
|
}
|
||||||
|
return remote_cmd.str();
|
||||||
|
}
|
||||||
|
|
||||||
int ssh_interactive_shell_session(ssh_session session, ssh_channel channel, const std::string& remote_cmd_str, const std::string& command, std::string* output) {
|
int ssh_interactive_shell_session(ssh_session session, ssh_channel channel, const std::string& remote_cmd_str, const std::string& command, std::string* output) {
|
||||||
int rc = ssh_channel_request_pty(channel);
|
int rc = ssh_channel_request_pty(channel);
|
||||||
if (rc != SSH_OK) {
|
if (rc != SSH_OK) {
|
||||||
@ -122,36 +154,102 @@ int ssh_interactive_shell_session(ssh_session session, ssh_channel channel, cons
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ssh_exec_command(ssh_session session, ssh_channel channel, const std::string& remote_cmd_str, bool silent, std::string* output, const std::map<std::string, std::string>& env) {
|
int ssh_exec_command(ssh_session session, ssh_channel channel, const std::string& remote_cmd_str, bool silent, std::string* output, const std::map<std::string, std::string>& env, const std::string& working_dir) {
|
||||||
// Build command with env assignments as prefix using 'env'
|
// Build complete command with env, working_dir, and the command itself
|
||||||
std::ostringstream cmd_with_env;
|
std::ostringstream cmd_with_env;
|
||||||
|
|
||||||
|
// Create a simple, flat command that will work reliably
|
||||||
|
// Format: env VAR=value bash -c 'cd /path && command args'
|
||||||
|
|
||||||
|
// Start with env variables
|
||||||
|
if (!env.empty()) {
|
||||||
cmd_with_env << "env ";
|
cmd_with_env << "env ";
|
||||||
for (const auto& kv : env) {
|
for (const auto& kv : env) {
|
||||||
if (kv.first == "SSHPASS") continue;
|
if (kv.first == "SSHPASS") continue;
|
||||||
cmd_with_env << kv.first << "='" << kv.second << "' ";
|
cmd_with_env << kv.first << "='" << kv.second << "' ";
|
||||||
}
|
}
|
||||||
cmd_with_env << remote_cmd_str;
|
}
|
||||||
|
|
||||||
|
// Use a single bash -c with the entire command inside single quotes
|
||||||
|
cmd_with_env << "bash -c '";
|
||||||
|
|
||||||
|
// Add cd if working directory specified
|
||||||
|
if (!working_dir.empty()) {
|
||||||
|
cmd_with_env << "cd " << working_dir << " && ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the command, but replace any single quotes with '\''
|
||||||
|
std::string escaped_cmd = remote_cmd_str;
|
||||||
|
size_t pos = 0;
|
||||||
|
while ((pos = escaped_cmd.find('\'', pos)) != std::string::npos) {
|
||||||
|
escaped_cmd.replace(pos, 1, "'\\''");
|
||||||
|
pos += 4; // Length of "'\\''"
|
||||||
|
}
|
||||||
|
cmd_with_env << escaped_cmd;
|
||||||
|
|
||||||
|
// Close the single quote
|
||||||
|
cmd_with_env << "'";
|
||||||
|
|
||||||
std::string final_cmd = cmd_with_env.str();
|
std::string final_cmd = cmd_with_env.str();
|
||||||
|
|
||||||
|
// Debug: Show the command being executed
|
||||||
|
std::cerr << "SSH exec command: " << final_cmd << std::endl;
|
||||||
|
|
||||||
int rc = ssh_channel_request_exec(channel, final_cmd.c_str());
|
int rc = ssh_channel_request_exec(channel, final_cmd.c_str());
|
||||||
if (rc != SSH_OK) {
|
if (rc != SSH_OK) {
|
||||||
if (output) *output = std::string("Failed to exec remote command: ") + ssh_get_error(session);
|
std::string error = std::string("Failed to exec remote command: ") + ssh_get_error(session);
|
||||||
|
std::cerr << "SSH exec error: " << error << std::endl;
|
||||||
|
if (output) *output = error;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output) {
|
if (output) {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
int nbytes;
|
int nbytes;
|
||||||
|
|
||||||
|
// Read from stdout
|
||||||
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
|
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
|
||||||
|
std::cerr << "Read " << nbytes << " bytes from stdout" << std::endl;
|
||||||
oss.write(buffer, nbytes);
|
oss.write(buffer, nbytes);
|
||||||
}
|
}
|
||||||
|
if (nbytes < 0) {
|
||||||
|
std::cerr << "Error reading from stdout" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read from stderr
|
||||||
|
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 1)) > 0) {
|
||||||
|
std::cerr << "Read " << nbytes << " bytes from stderr" << std::endl;
|
||||||
|
oss.write(buffer, nbytes);
|
||||||
|
}
|
||||||
|
if (nbytes < 0) {
|
||||||
|
std::cerr << "Error reading from stderr" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
*output = oss.str();
|
*output = oss.str();
|
||||||
} else if (!silent) {
|
} else if (!silent) {
|
||||||
char buffer[4096];
|
char buffer[4096];
|
||||||
int nbytes;
|
int nbytes;
|
||||||
|
|
||||||
|
// Read from stdout
|
||||||
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
|
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
|
||||||
|
std::cerr << "Read " << nbytes << " bytes from stdout (writing to fd 1)" << std::endl;
|
||||||
write(1, buffer, nbytes);
|
write(1, buffer, nbytes);
|
||||||
}
|
}
|
||||||
|
if (nbytes < 0) {
|
||||||
|
std::cerr << "Error reading from stdout" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read from stderr
|
||||||
|
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 1)) > 0) {
|
||||||
|
std::cerr << "Read " << nbytes << " bytes from stderr (writing to fd 2)" << std::endl;
|
||||||
|
write(2, buffer, nbytes);
|
||||||
|
}
|
||||||
|
if (nbytes < 0) {
|
||||||
|
std::cerr << "Error reading from stderr" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,13 +362,17 @@ int execute_cmd(
|
|||||||
ssh_free(session);
|
ssh_free(session);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
std::string remote_cmd_str = ssh_build_remote_command(command, args, working_dir, {}); // Don't prefix env here
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
if (interactive) {
|
if (interactive) {
|
||||||
|
std::string remote_cmd_str = ssh_build_remote_command(command, args, working_dir, {});
|
||||||
ret = ssh_interactive_shell_session(session, channel, remote_cmd_str, command, output);
|
ret = ssh_interactive_shell_session(session, channel, remote_cmd_str, command, output);
|
||||||
} else {
|
} else {
|
||||||
ret = ssh_exec_command(session, channel, remote_cmd_str, silent, output, env);
|
// For non-interactive, handle working directory in ssh_exec_command
|
||||||
|
std::string remote_cmd_str = ssh_build_command_only(command, args);
|
||||||
|
ret = ssh_exec_command(session, channel, remote_cmd_str, silent, output, env, working_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
ssh_channel_send_eof(channel);
|
ssh_channel_send_eof(channel);
|
||||||
ssh_channel_close(channel);
|
ssh_channel_close(channel);
|
||||||
ssh_channel_free(channel);
|
ssh_channel_free(channel);
|
||||||
|
@ -93,8 +93,9 @@ void test_ssh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void test_env() {
|
void test_env() {
|
||||||
std::string command = "env";
|
std::string command = "bash";
|
||||||
std::vector<std::string> args = {};
|
// Simplify the command to avoid nested quoting issues
|
||||||
|
std::vector<std::string> args = {"-c", "env | grep WHATSUP && echo The value is: $WHATSUP"};
|
||||||
std::string working_dir = "/home";
|
std::string working_dir = "/home";
|
||||||
std::map<std::string, std::string> env = {{"WHATSUP", "Waaaaattttsssuuuppppp!"}};
|
std::map<std::string, std::string> env = {{"WHATSUP", "Waaaaattttsssuuuppppp!"}};
|
||||||
bool silent = false;
|
bool silent = false;
|
||||||
@ -104,7 +105,22 @@ void test_env() {
|
|||||||
ssh.user = "katie";
|
ssh.user = "katie";
|
||||||
ssh.port = "22";
|
ssh.port = "22";
|
||||||
|
|
||||||
runner::execute_cmd(command, args, working_dir, env, silent, interactive, &ssh);
|
std::cerr << "Running test_env with SSH to " << ssh.user << "@" << ssh.host << std::endl;
|
||||||
|
|
||||||
|
// Capture output explicitly
|
||||||
|
std::string output;
|
||||||
|
int ret = runner::execute_cmd(command, args, working_dir, env, silent, interactive, &ssh, &output);
|
||||||
|
|
||||||
|
std::cerr << "Command returned code: " << ret << std::endl;
|
||||||
|
std::cerr << "Output length: " << output.length() << " bytes" << std::endl;
|
||||||
|
|
||||||
|
// Print the actual output
|
||||||
|
std::cout << "=== BEGIN OUTPUT ===" << std::endl;
|
||||||
|
std::cout << output;
|
||||||
|
if (!output.empty() && output[output.length()-1] != '\n') {
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << "=== END OUTPUT ===" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user