This commit is contained in:
parent
f9dca5fea1
commit
dbcef96bc2
@ -134,11 +134,13 @@ std::string server_env_manager::get_variable(const std::string& name) const {
|
||||
|
||||
bool server_env_manager::check_remote_dir_exists(const std::string &dir_path) const
|
||||
{
|
||||
return 0==runner::execute_cmd("test",{"-d", quote(dir_path)}, {}, {}, true, false, get_SSH_INFO());
|
||||
runner::runner_ssh test_runner(get_SSH_INFO(),"test",{"-d", quote(dir_path)});
|
||||
return test_runner.execute();
|
||||
}
|
||||
|
||||
bool server_env_manager::check_remote_file_exists(const std::string& file_path) const {
|
||||
return 0==runner::execute_cmd("test",{"-f",quote(file_path)}, {}, {}, true, false, get_SSH_INFO());
|
||||
runner::runner_ssh test_runner(get_SSH_INFO(),"test",{"-f",quote(file_path)});
|
||||
return test_runner.execute();
|
||||
}
|
||||
|
||||
bool server_env_manager::check_remote_items_exist(const std::vector<std::string> &file_paths) const
|
||||
@ -149,8 +151,8 @@ bool server_env_manager::check_remote_items_exist(const std::vector<std::string>
|
||||
file_paths_str += quote(file_path) + " ";
|
||||
}
|
||||
// check if all items in the vector exist on the remote server, in a single command.
|
||||
|
||||
return 0==runner::execute_cmd("bash",{"-c","for item in " + file_paths_str + "; do test -f $item; done"}, {}, {}, true, false, get_SSH_INFO());
|
||||
runner::runner_ssh test_runner(get_SSH_INFO(),"bash",{"-c","for item in " + file_paths_str + "; do test -f $item; done"});
|
||||
return test_runner.execute();
|
||||
}
|
||||
|
||||
bool server_env_manager::run_remote_template_command(const std::string &service_name, const std::string &command, std::vector<std::string> args, bool silent, std::map<std::string, std::string> extra_env_vars, std::string * output) const
|
||||
@ -177,7 +179,13 @@ bool server_env_manager::run_remote_template_command(const std::string &service_
|
||||
ASSERT(!output || !silent); // if output is captured, silent must be false
|
||||
ASSERT(!interactive || !silent); // if command is ssh, silent must be false
|
||||
|
||||
return 0==runner::execute_cmd("bash",{"-c", quote(script_path) + argstr}, working_dir, env_vars, silent, interactive, get_SSH_INFO(), output);
|
||||
if (interactive) {
|
||||
runner::runner_ssh_interactive bash_runner(get_SSH_INFO(),"bash",{"-c", quote(script_path) + argstr}, working_dir, env_vars);
|
||||
return bash_runner.execute();
|
||||
} else {
|
||||
runner::runner_ssh bash_runner(get_SSH_INFO(),"bash",{"-c", quote(script_path) + argstr}, working_dir, env_vars, silent);
|
||||
return bash_runner.execute();
|
||||
}
|
||||
}
|
||||
|
||||
// base64 <<< "FOO=BAR WHEE=YAY bash ./test.sh"
|
||||
|
@ -52,18 +52,23 @@ bool service_runner::install(bool silent) {
|
||||
if (!tinfo.is_set())
|
||||
return false;
|
||||
|
||||
// Create service directory
|
||||
if (0!=runner::execute_cmd("mkdir",{"-p",remotepath::service(mServer, mService)},"",{},true,false,mServerEnv.get_SSH_INFO()))
|
||||
{
|
||||
std::cerr << "Failed to create service directory " << remotepath::service(mServer, mService) << std::endl;
|
||||
return false;
|
||||
|
||||
{ // Create service directory
|
||||
runner::runner_ssh mkdir_runner(mServerEnv.get_SSH_INFO(),"mkdir",{"-p",remotepath::service(mServer, mService)});
|
||||
if (!mkdir_runner.execute())
|
||||
{
|
||||
std::cerr << "Failed to create service directory " << remotepath::service(mServer, mService) << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if rsync is installed on remote host
|
||||
if (0!=runner::execute_cmd("which",{"rsync"},"",{},true,false,mServerEnv.get_SSH_INFO()))
|
||||
{
|
||||
std::cerr << "rsync is not installed on the remote host" << std::endl;
|
||||
return false;
|
||||
|
||||
{ // Check if rsync is installed on remote host
|
||||
runner::runner_ssh which_runner(mServerEnv.get_SSH_INFO(),"which",{"rsync"});
|
||||
if (!which_runner.execute())
|
||||
{
|
||||
std::cerr << "rsync is not installed on the remote host" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// make sure all shell files are executable
|
||||
@ -125,8 +130,11 @@ bool service_runner::uninstall(bool silent) {
|
||||
}
|
||||
|
||||
// 4. Remove the service directory from the server
|
||||
if (0!=runner::execute_cmd("rm -rf " + quote(remotepath::service(mServer, mService)), {}, "", {}, true, false, mServerEnv.get_SSH_INFO()))
|
||||
std::cerr << "Failed to remove remote service directory at " << remotepath::service(mServer, mService) << std::endl;
|
||||
{
|
||||
runner::runner_ssh rm_runner(mServerEnv.get_SSH_INFO(),"rm",{"-rf",remotepath::service(mServer, mService)});
|
||||
if (!rm_runner.execute())
|
||||
std::cerr << "Failed to remove remote service directory at " << remotepath::service(mServer, mService) << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Service " << mService << " successfully uninstalled from " << mServer << std::endl;
|
||||
return true;
|
||||
@ -170,8 +178,11 @@ bool service_runner::fullnuke()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0!=runner::execute_cmd("rm -rf " + quote(local_service_path), {}, "", {}, true, false, mServerEnv.get_SSH_INFO()))
|
||||
std::cerr << "Failed to remove local service directory at " << local_service_path << std::endl;
|
||||
{
|
||||
runner::runner_local rm_runner("rm",{"-rf",quote(local_service_path)});
|
||||
if (!rm_runner.execute())
|
||||
std::cerr << "Failed to remove local service directory at " << local_service_path << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Service " << mService << " successfully fully nuked from " << mServer << std::endl;
|
||||
return true;
|
||||
@ -265,7 +276,6 @@ std::map<std::string, ServiceStatus> service_runner::get_all_services_status(std
|
||||
{
|
||||
std::map<std::string, ServiceStatus> status;
|
||||
|
||||
std::string command = "_allservicesstatus";
|
||||
std::string service_name = "dropshell-agent";
|
||||
|
||||
|
||||
@ -275,12 +285,15 @@ std::map<std::string, ServiceStatus> service_runner::get_all_services_status(std
|
||||
return status;
|
||||
}
|
||||
|
||||
std::string cmd_path = remotepath::service_template(server_name,service_name) + "/shared/";
|
||||
std::string cmd_path = remotepath::service_template(server_name,service_name) + "/shared/_allservicesstatus.sh";
|
||||
std::string output;
|
||||
if (0!=runner::execute_cmd("bash",{cmd_path+command+".sh"}, cmd_path, {}, true, false, env.get_SSH_INFO(), &output))
|
||||
{
|
||||
std::cerr << "Error: Failed to run command script at " << cmd_path << std::endl;
|
||||
return status;
|
||||
runner::runner_ssh_capture bash_runner(env.get_SSH_INFO(),output,cmd_path);
|
||||
if (!bash_runner.execute())
|
||||
{
|
||||
std::cerr << "Error: Failed to run command script at " << cmd_path << std::endl;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream ss(output);
|
||||
@ -390,7 +403,8 @@ bool service_runner::interactive_ssh(const std::string & server_name, const std:
|
||||
std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
|
||||
return false;
|
||||
}
|
||||
return 0==runner::execute_cmd("", {}, "", {}, false, true, env.get_SSH_INFO());
|
||||
runner::runner_ssh_interactive ssh_runner(env.get_SSH_INFO(),command);
|
||||
return ssh_runner.execute();
|
||||
}
|
||||
|
||||
void service_runner::edit_server(const std::string &server_name)
|
||||
@ -438,7 +452,8 @@ bool service_runner::edit_file(const std::string &file_path)
|
||||
}
|
||||
|
||||
std::cout << "Editing file: " << file_path << std::endl;
|
||||
return 0==runner::execute_cmd(editor_cmd, {file_path}, "", {}, false, true, nullptr);
|
||||
runner::runner_local_interactive editor_runner(editor_cmd,{file_path});
|
||||
return editor_runner.execute();
|
||||
}
|
||||
|
||||
bool service_runner::interactive_ssh_service()
|
||||
@ -544,7 +559,8 @@ bool service_runner::restore(std::string backup_file, bool silent)
|
||||
std::string remote_backup_file_path = remote_backups_dir + "/" + backup_file;
|
||||
|
||||
// Copy backup file from local to server
|
||||
if (0!=runner::execute_cmd("scp", {quote(local_backup_file_path), mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_backup_file_path)}, "", {}, false, false, mServerEnv.get_SSH_INFO()))
|
||||
runner::runner_local scp_runner("scp",{quote(local_backup_file_path), mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_backup_file_path)});
|
||||
if (!scp_runner.execute())
|
||||
{
|
||||
std::cerr << "Failed to copy backup file from server" << std::endl;
|
||||
return false;
|
||||
@ -621,11 +637,14 @@ bool service_runner::backup(bool silent) {
|
||||
|
||||
// Create backups directory on server if it doesn't exist
|
||||
std::string remote_backups_dir = remotepath::backups(mServer);
|
||||
if (!silent) std::cout << "Remote backups directory on "<< mServer <<": " << remote_backups_dir << std::endl;
|
||||
if (0!=runner::execute_cmd("mkdir", {"-p",quote(remote_backups_dir)}, "", {}, true, false, mServerEnv.get_SSH_INFO()))
|
||||
if (!silent) std::cout << "Remote backups directory on "<< mServer <<": " << remote_backups_dir << std::endl;
|
||||
{
|
||||
std::cerr << "Failed to create backups directory on server" << std::endl;
|
||||
return false;
|
||||
runner::runner_ssh mkdir_runner(mServerEnv.get_SSH_INFO(),"mkdir",{"-p",quote(remote_backups_dir)});
|
||||
if (!mkdir_runner.execute())
|
||||
{
|
||||
std::cerr << "Failed to create backups directory on server" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create backups directory locally if it doesn't exist
|
||||
@ -664,7 +683,8 @@ bool service_runner::backup(bool silent) {
|
||||
}
|
||||
|
||||
// Copy backup file from server to local
|
||||
if (0!=runner::execute_cmd("scp", {quote(remote_backup_file_path), quote(local_backup_file_path)}, "", {}, silent, false, mServerEnv.get_SSH_INFO()))
|
||||
runner::runner_local scp_runner("scp",{"-P", mServerEnv.get_SSH_PORT(), mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_backup_file_path), quote(local_backup_file_path)});
|
||||
if (!scp_runner.execute())
|
||||
{
|
||||
std::cerr << "Failed to copy backup file from server" << std::endl;
|
||||
return false;
|
||||
@ -681,15 +701,19 @@ bool service_runner::backup(bool silent) {
|
||||
cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mServerEnv(server_env)
|
||||
{
|
||||
std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10);
|
||||
if (0!=runner::execute_cmd("mkdir", {"-p",quote(p)}, "", {}, true, false, server_env.get_SSH_INFO()))
|
||||
std::cerr << "Failed to create temp directory on server" << std::endl;
|
||||
else
|
||||
mPath = p;
|
||||
{
|
||||
runner::runner_ssh mkdir_runner(server_env.get_SSH_INFO(),"mkdir",{"-p",quote(p)});
|
||||
if (!mkdir_runner.execute())
|
||||
std::cerr << "Failed to create temp directory on server" << std::endl;
|
||||
else
|
||||
mPath = p;
|
||||
}
|
||||
}
|
||||
|
||||
cRemoteTempFolder::~cRemoteTempFolder()
|
||||
{
|
||||
if (0!=runner::execute_cmd("rm", {"-rf",quote(mPath)}, "", {}, true, false, mServerEnv.get_SSH_INFO()))
|
||||
runner::runner_ssh rm_runner(mServerEnv.get_SSH_INFO(),"rm",{"-rf",quote(mPath)});
|
||||
if (!rm_runner.execute())
|
||||
std::cerr << "Failed to remove temp directory on server" << std::endl;
|
||||
}
|
||||
|
||||
@ -748,11 +772,10 @@ std::string service_runner::get_latest_backup_file(const std::string& server, co
|
||||
bool service_runner::rsync_copy(const std::string& local_path, const std::string& remote_path, bool silent) {
|
||||
std::cout << "Copying: [LOCAL] " << local_path << std::endl << std::string(8,' ')<<"[REMOTE] " << remote_path << std::endl;
|
||||
|
||||
if (0 != runner::execute_cmd(
|
||||
"rsync",
|
||||
{"--delete","--mkpath","-zrpc","-e",quote("ssh -p " + mServerEnv.get_SSH_PORT()),local_path,
|
||||
mServerEnv.get_SSH_USER()+"@"+mServerEnv.get_SSH_HOST()+":"+remote_path},
|
||||
"", {}, true, false)) {
|
||||
runner::runner_local rsync_runner("rsync", {"--delete","--mkpath","-zrpc","-e",quote("ssh -p " + mServerEnv.get_SSH_PORT()),local_path,
|
||||
mServerEnv.get_SSH_USER()+"@"+mServerEnv.get_SSH_HOST()+":"+remote_path}, "", {}, true);
|
||||
|
||||
if (!rsync_runner.execute()) {
|
||||
std::cerr << "Failed to copy files using rsync" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
@ -77,364 +77,281 @@ ssh_session ssh_connect_and_auth(const sSSHInfo* sshinfo, const std::map<std::st
|
||||
return session;
|
||||
}
|
||||
|
||||
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::ostringstream remote_cmd;
|
||||
for (const auto& kv : env) {
|
||||
if (kv.first == "SSHPASS") continue;
|
||||
remote_cmd << kv.first << "='" << kv.second << "' ";
|
||||
}
|
||||
if (!working_dir.empty()) {
|
||||
remote_cmd << "cd '" << working_dir << "' && ";
|
||||
}
|
||||
remote_cmd << command;
|
||||
for (const auto& arg : args) {
|
||||
remote_cmd << " " << arg;
|
||||
}
|
||||
return remote_cmd.str();
|
||||
}
|
||||
} // namespace runner
|
||||
|
||||
// // 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 << " " << 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) {
|
||||
// Request a PTY with xterm-256color type for color support
|
||||
// First try using default terminal settings - should work with libssh 0.9.0+
|
||||
int rc = ssh_channel_request_pty(channel);
|
||||
if (rc != SSH_OK) {
|
||||
if (output) *output = std::string("Failed to request pty: ") + ssh_get_error(session);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Try to explicitly tell the server we want colors through additional means
|
||||
// 1. Set TERM environment variable
|
||||
rc = ssh_channel_request_env(channel, "TERM", "xterm-256color");
|
||||
// Ignore errors - this is optional
|
||||
|
||||
// 2. Request a shell
|
||||
rc = ssh_channel_request_shell(channel);
|
||||
if (rc != SSH_OK) {
|
||||
if (output) *output = std::string("Failed to request shell: ") + ssh_get_error(session);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct termios orig_termios, raw_termios;
|
||||
tcgetattr(STDIN_FILENO, &orig_termios);
|
||||
raw_termios = orig_termios;
|
||||
cfmakeraw(&raw_termios);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &raw_termios);
|
||||
|
||||
if (!command.empty()) {
|
||||
ssh_channel_write(channel, remote_cmd_str.c_str(), remote_cmd_str.size());
|
||||
ssh_channel_write(channel, "\n", 1);
|
||||
} else {
|
||||
// Initialize bash with color support if no specific command
|
||||
std::string init_cmd = "export TERM=xterm-256color && if [ -f ~/.bashrc ]; then source ~/.bashrc; fi";
|
||||
ssh_channel_write(channel, init_cmd.c_str(), init_cmd.size());
|
||||
ssh_channel_write(channel, "\n", 1);
|
||||
}
|
||||
|
||||
int maxfd = STDIN_FILENO > STDOUT_FILENO ? STDIN_FILENO : STDOUT_FILENO;
|
||||
maxfd = maxfd > ssh_get_fd(session) ? maxfd : ssh_get_fd(session);
|
||||
char buffer[4096];
|
||||
bool done = false;
|
||||
|
||||
while (!done) {
|
||||
fd_set fds_read;
|
||||
FD_ZERO(&fds_read);
|
||||
FD_SET(STDIN_FILENO, &fds_read);
|
||||
FD_SET(ssh_get_fd(session), &fds_read);
|
||||
|
||||
int ret = select(maxfd + 1, &fds_read, nullptr, nullptr, nullptr);
|
||||
if (ret < 0) break;
|
||||
|
||||
if (FD_ISSET(STDIN_FILENO, &fds_read)) {
|
||||
ssize_t n = read(STDIN_FILENO, buffer, sizeof(buffer));
|
||||
if (n > 0) {
|
||||
ssh_channel_write(channel, buffer, n);
|
||||
} else {
|
||||
ssh_channel_send_eof(channel);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(ssh_get_fd(session), &fds_read)) {
|
||||
int n = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
|
||||
if (n > 0) {
|
||||
write(STDOUT_FILENO, buffer, n);
|
||||
} else if (n == 0) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ssh_channel_is_closed(channel) || ssh_channel_is_eof(channel)) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
|
||||
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, const std::string& working_dir) {
|
||||
// Build complete command with env, working_dir, and the command itself
|
||||
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 ";
|
||||
for (const auto& kv : env) {
|
||||
if (kv.first == "SSHPASS") continue;
|
||||
cmd_with_env << kv.first << "=\"" << kv.second << "\" ";
|
||||
}
|
||||
}
|
||||
|
||||
// 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 << " && ";
|
||||
}
|
||||
cmd_with_env << remote_cmd_str;
|
||||
|
||||
// Close the single quote
|
||||
cmd_with_env << "'";
|
||||
|
||||
std::string final_cmd = cmd_with_env.str();
|
||||
|
||||
std::cout << "Final remote command: " << final_cmd << std::endl;
|
||||
|
||||
int rc = ssh_channel_request_exec(channel, final_cmd.c_str());
|
||||
if (rc != SSH_OK) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (output) {
|
||||
std::ostringstream oss;
|
||||
char buffer[4096];
|
||||
int nbytes;
|
||||
|
||||
// Read from stdout
|
||||
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
|
||||
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) {
|
||||
oss.write(buffer, nbytes);
|
||||
}
|
||||
if (nbytes < 0) {
|
||||
std::cerr << "Error reading from stderr" << std::endl;
|
||||
}
|
||||
|
||||
*output = oss.str();
|
||||
} else if (!silent) {
|
||||
char buffer[4096];
|
||||
int nbytes;
|
||||
|
||||
// Read from stdout
|
||||
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
|
||||
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) {
|
||||
write(2, buffer, nbytes);
|
||||
}
|
||||
if (nbytes < 0) {
|
||||
std::cerr << "Error reading from stderr" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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
|
||||
) {
|
||||
std::cerr << "Local command: " << command << std::endl;
|
||||
|
||||
int pipefd[2];
|
||||
bool use_pipe = output && !interactive;
|
||||
if (use_pipe && pipe(pipefd) == -1) {
|
||||
perror("pipe");
|
||||
std::cerr << "Pipe error: " << strerror(errno) << std::endl;
|
||||
return -1;
|
||||
}
|
||||
// runner_local: non-interactive local command execution
|
||||
bool runner_local::execute() {
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
perror("fork");
|
||||
std::cerr << "Fork error: " << strerror(errno) << std::endl;
|
||||
return -1;
|
||||
std::cerr << "Failed to fork" << std::endl;
|
||||
mReturnCode = -1;
|
||||
return false;
|
||||
}
|
||||
if (pid == 0) {
|
||||
if (!working_dir.empty()) {
|
||||
if (chdir(working_dir.c_str()) != 0) {
|
||||
// Child process
|
||||
if (!mWorkingDir.empty()) {
|
||||
if (chdir(mWorkingDir.c_str()) != 0) {
|
||||
perror("chdir");
|
||||
std::cerr << "Chdir error: " << strerror(errno) << std::endl;
|
||||
exit(-1);
|
||||
exit(127);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& kv : env) {
|
||||
setenv(kv.first.c_str(), kv.second.c_str(), 1);
|
||||
}
|
||||
|
||||
if (use_pipe) {
|
||||
close(pipefd[0]);
|
||||
dup2(pipefd[1], STDOUT_FILENO);
|
||||
dup2(pipefd[1], STDERR_FILENO);
|
||||
close(pipefd[1]);
|
||||
} else if (silent && !interactive) {
|
||||
int devnull = open("/dev/null", O_WRONLY);
|
||||
dup2(devnull, STDOUT_FILENO);
|
||||
dup2(devnull, STDERR_FILENO);
|
||||
close(devnull);
|
||||
}
|
||||
if (!interactive) {
|
||||
setsid();
|
||||
// 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*>(command.c_str()));
|
||||
for (const auto& arg : args) {
|
||||
argv.push_back(const_cast<char*>(arg.c_str()));
|
||||
}
|
||||
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);
|
||||
|
||||
std::cerr << "Local command: " << command << std::endl;
|
||||
for (const auto& arg : args) {
|
||||
std::cerr << "Local arg: " << arg << std::endl;
|
||||
}
|
||||
|
||||
execvp(command.c_str(), argv.data());
|
||||
perror("execvp");
|
||||
exit(-1);
|
||||
} else {
|
||||
if (use_pipe) {
|
||||
close(pipefd[1]);
|
||||
std::ostringstream oss;
|
||||
char buf[4096];
|
||||
ssize_t n;
|
||||
while ((n = read(pipefd[0], buf, sizeof(buf))) > 0) {
|
||||
oss.write(buf, n);
|
||||
if (mSilent) {
|
||||
int fd = open("/dev/null", O_WRONLY);
|
||||
if (fd != -1) {
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
}
|
||||
close(pipefd[0]);
|
||||
*output = oss.str();
|
||||
}
|
||||
execvp(mCommand.c_str(), argv.data());
|
||||
perror("execvp");
|
||||
exit(127);
|
||||
} else {
|
||||
int status = 0;
|
||||
waitpid(pid, &status, 0);
|
||||
std::cerr << "Waitpid status: " << status << std::endl;
|
||||
if (WIFEXITED(status)) {
|
||||
return WEXITSTATUS(status);
|
||||
mReturnCode = WEXITSTATUS(status);
|
||||
} else {
|
||||
return -1;
|
||||
mReturnCode = -1;
|
||||
}
|
||||
return mReturnCode == 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
int 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,
|
||||
const bool silent,
|
||||
const bool interactive,
|
||||
const copySSHPtr& sshinfo,
|
||||
std::string* output
|
||||
) {
|
||||
if (sshinfo.valid()) {
|
||||
std::string error;
|
||||
ssh_session session = ssh_connect_and_auth(&sshinfo, env, &error);
|
||||
if (!session) {
|
||||
if (output) *output = error;
|
||||
std::cerr << "SSH connection error: " << error << std::endl;
|
||||
return -1;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
ssh_channel channel = ssh_channel_new(session);
|
||||
if (!channel) {
|
||||
if (output) *output = "Failed to create SSH channel.";
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
std::cerr << "SSH channel error: " << output->c_str() << std::endl;
|
||||
return -1;
|
||||
for (const auto& [k, v] : mEnv) {
|
||||
setenv(k.c_str(), v.c_str(), 1);
|
||||
}
|
||||
int rc = ssh_channel_open_session(channel);
|
||||
if (rc != SSH_OK) {
|
||||
if (output) *output = std::string("Failed to open SSH channel: ") + ssh_get_error(session);
|
||||
ssh_channel_free(channel);
|
||||
ssh_disconnect(session);
|
||||
ssh_free(session);
|
||||
std::cerr << "SSH channel open error: " << output->c_str() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
std::string remote_cmd_str = ssh_build_remote_command(command, args, working_dir, {});
|
||||
if (interactive) {
|
||||
ret = ssh_interactive_shell_session(session, channel, remote_cmd_str, command, output);
|
||||
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 {
|
||||
// For non-interactive, handle working directory in ssh_exec_command
|
||||
ret = ssh_exec_command(session, channel, remote_cmd_str, silent, output, env, working_dir);
|
||||
mReturnCode = -1;
|
||||
}
|
||||
|
||||
ssh_channel_send_eof(channel);
|
||||
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);
|
||||
|
||||
if (output) trim(output);
|
||||
|
||||
std::cerr << "SSH command execution result: " << ret << std::endl;
|
||||
return ret;
|
||||
} else {
|
||||
int ret=local_execute_cmd(command, args, working_dir, env, silent, interactive, output);
|
||||
if (output) trim(output);
|
||||
|
||||
std::cerr << "Local command execution result: " << ret << std::endl;
|
||||
return ret;
|
||||
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
|
@ -14,26 +14,67 @@ struct sSSHInfo {
|
||||
std::string port;
|
||||
};
|
||||
|
||||
class copySSHPtr {
|
||||
class runner {
|
||||
public:
|
||||
copySSHPtr(const sSSHInfo* sshinfo) : mSSHInfo(sshinfo ? *sshinfo : sSSHInfo()) {}
|
||||
copySSHPtr(const sSSHInfo& sshinfo) : mSSHInfo(sshinfo) {}
|
||||
bool valid() const { return !mSSHInfo.host.empty(); }
|
||||
const sSSHInfo * operator&() const { return (valid() ? &mSSHInfo : nullptr); }
|
||||
private:
|
||||
sSSHInfo mSSHInfo;
|
||||
};
|
||||
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() {}
|
||||
|
||||
int 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,
|
||||
const bool silent,
|
||||
const bool interactive,
|
||||
const copySSHPtr& sshinfo = nullptr,
|
||||
std::string* output = nullptr
|
||||
);
|
||||
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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user