dropshell/runner/runner.cpp
Your Name 39e083898f
Some checks failed
Dropshell Test / Build_and_Test (push) Failing after 19s
GPT 4.1 trying
2025-05-10 14:06:30 +12:00

179 lines
5.4 KiB
C++

#include "runner.h"
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
namespace 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,
bool silent,
bool interactive,
sSSHInfo* sshinfo,
std::string* output
) {
if (sshinfo) {
// Build remote command string
std::ostringstream remote_cmd;
// Set environment variables
for (const auto& kv : env) {
remote_cmd << kv.first << "='" << kv.second << "' ";
}
// Change working directory
if (!working_dir.empty()) {
remote_cmd << "cd '" << working_dir << "' && ";
}
// Command and args
remote_cmd << command;
for (const auto& arg : args) {
remote_cmd << " '" << arg << "'";
}
std::string remote_cmd_str = remote_cmd.str();
// Build ssh command
std::vector<std::string> ssh_argv = {"ssh"};
if (!sshinfo->port.empty()) {
ssh_argv.push_back("-p");
ssh_argv.push_back(sshinfo->port);
}
std::string userhost = sshinfo->user.empty() ? sshinfo->host : (sshinfo->user + "@" + sshinfo->host);
ssh_argv.push_back(userhost);
ssh_argv.push_back(remote_cmd_str);
// Prepare for exec
std::vector<char*> argv;
for (auto& s : ssh_argv) argv.push_back(const_cast<char*>(s.c_str()));
argv.push_back(nullptr);
int pipefd[2];
bool use_pipe = output && !interactive;
if (use_pipe && pipe(pipefd) == -1) {
perror("pipe");
return -1;
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return -1;
}
if (pid == 0) {
// Child process
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();
}
execvp("ssh", argv.data());
perror("execvp ssh");
exit(-1);
} else {
// Parent process
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);
}
close(pipefd[0]);
*output = oss.str();
}
int status = 0;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
} else {
return -1;
}
}
} else {
int pipefd[2];
bool use_pipe = output && !interactive;
if (use_pipe && pipe(pipefd) == -1) {
perror("pipe");
return -1;
}
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return -1;
}
if (pid == 0) {
// Child process
if (!working_dir.empty()) {
if (chdir(working_dir.c_str()) != 0) {
perror("chdir");
exit(-1);
}
}
// Set environment variables
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) {
// Detach from terminal if not interactive
setsid();
}
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(nullptr);
execvp(command.c_str(), argv.data());
perror("execvp");
exit(-1);
} else {
// Parent process
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);
}
close(pipefd[0]);
*output = oss.str();
}
int status = 0;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
} else {
return -1;
}
}
}
}
} // namespace runner