Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
efeab5a97f | ||
![]() |
a6cac3a426 | ||
![]() |
e85aa5c81b |
@ -108,7 +108,6 @@ void printversion() {
|
|||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
HAPPYEXIT("hash", hash_demo_raw(safearg(argc,argv,2)))
|
HAPPYEXIT("hash", hash_demo_raw(safearg(argc,argv,2)))
|
||||||
HAPPYEXIT("makesafecmd", std::cout<<makesafecmd(safearg(argc,argv,2))<<std::endl)
|
|
||||||
HAPPYEXIT("version", printversion())
|
HAPPYEXIT("version", printversion())
|
||||||
BOOLEXIT("test-template", gTemplateManager().test_template(safearg(argc,argv,2)))
|
BOOLEXIT("test-template", gTemplateManager().test_template(safearg(argc,argv,2)))
|
||||||
ASSERT(safearg(argc,argv,1) != "assert", "Hello! Here is an assert.");
|
ASSERT(safearg(argc,argv,1) != "assert", "Hello! Here is an assert.");
|
||||||
|
@ -121,7 +121,9 @@ sCommand server_env_manager::construct_standard_template_run_cmd(const std::stri
|
|||||||
argstr += " " + quote(dequote(trim(arg)));
|
argstr += " " + quote(dequote(trim(arg)));
|
||||||
}
|
}
|
||||||
|
|
||||||
sCommand scommand(remote_service_template_path, "bash " + quote(script_path) + argstr + (silent ? " > /dev/null 2>&1" : ""), env_vars);
|
std::vector<std::string> cmd = {script_path};
|
||||||
|
cmd.insert(cmd.end(), args.begin(), args.end());
|
||||||
|
sCommand scommand(remote_service_template_path, cmd, env_vars);
|
||||||
|
|
||||||
if (scommand.empty())
|
if (scommand.empty())
|
||||||
std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl;
|
std::cerr << "Error: Failed to construct command for " << service_name << " " << command << std::endl;
|
||||||
@ -132,13 +134,15 @@ sCommand server_env_manager::construct_standard_template_run_cmd(const std::stri
|
|||||||
|
|
||||||
bool server_env_manager::check_remote_dir_exists(const std::string &dir_path) const
|
bool server_env_manager::check_remote_dir_exists(const std::string &dir_path) const
|
||||||
{
|
{
|
||||||
sCommand scommand("test -d " + quote(dir_path));
|
std::vector<std::string> cmd = {"test", "-d", dir_path};
|
||||||
return execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent);
|
sCommand scommand(cmd);
|
||||||
|
return execute_command(get_SSH_INFO(), scommand, cMode::Silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_env_manager::check_remote_file_exists(const std::string& file_path) const {
|
bool server_env_manager::check_remote_file_exists(const std::string& file_path) const {
|
||||||
sCommand scommand("test -f " + quote(file_path));
|
std::vector<std::string> cmd = {"test", "-f", file_path};
|
||||||
return execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent);
|
sCommand scommand(cmd);
|
||||||
|
return execute_command(get_SSH_INFO(), scommand, cMode::Silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_env_manager::check_remote_items_exist(const std::vector<std::string> &file_paths) const
|
bool server_env_manager::check_remote_items_exist(const std::vector<std::string> &file_paths) const
|
||||||
@ -151,9 +155,11 @@ bool server_env_manager::check_remote_items_exist(const std::vector<std::string>
|
|||||||
file_names_str += std::filesystem::path(file_path).filename().string() + " ";
|
file_names_str += std::filesystem::path(file_path).filename().string() + " ";
|
||||||
}
|
}
|
||||||
// check if all items in the vector exist on the remote server, in a single command.
|
// check if all items in the vector exist on the remote server, in a single command.
|
||||||
sCommand scommand("for item in " + file_paths_str + "; do test -f $item; done");
|
sCommand scommand({
|
||||||
|
"bash", "-c", "for item in " + file_paths_str + "; do test -f $item; done"
|
||||||
|
});
|
||||||
|
|
||||||
bool okay = execute_ssh_command(get_SSH_INFO(), scommand, cMode::Silent);
|
bool okay = execute_command(get_SSH_INFO(), scommand, cMode::Silent);
|
||||||
if (!okay) {
|
if (!okay) {
|
||||||
std::cerr << "Error: Required items not found on remote server: " << file_names_str << std::endl;
|
std::cerr << "Error: Required items not found on remote server: " << file_names_str << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -171,8 +177,8 @@ bool server_env_manager::run_remote_template_command(const std::string &service_
|
|||||||
|
|
||||||
if (scommand.get_command_to_run().empty())
|
if (scommand.get_command_to_run().empty())
|
||||||
return false;
|
return false;
|
||||||
cMode mode = (command=="ssh") ? (cMode::Interactive | cMode::RawCommand) : cMode::Silent;
|
cMode mode = (command=="ssh" ? cMode::Defaults : cMode::Silent);
|
||||||
return execute_ssh_command(get_SSH_INFO(), scommand, mode);
|
return execute_command(get_SSH_INFO(), scommand, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool server_env_manager::run_remote_template_command_and_capture_output(const std::string &service_name, const std::string &command, std::vector<std::string> args, std::string &output, bool silent, std::map<std::string, std::string> extra_env_vars) const
|
bool server_env_manager::run_remote_template_command_and_capture_output(const std::string &service_name, const std::string &command, std::vector<std::string> args, std::string &output, bool silent, std::map<std::string, std::string> extra_env_vars) const
|
||||||
@ -185,8 +191,8 @@ bool server_env_manager::run_remote_template_command_and_capture_output(const st
|
|||||||
for (const auto& [key, value] : extra_env_vars)
|
for (const auto& [key, value] : extra_env_vars)
|
||||||
scommand.add_env_var(key, value);
|
scommand.add_env_var(key, value);
|
||||||
|
|
||||||
cMode mode = cMode::CaptureOutput | cMode::RawCommand;
|
cMode mode = cMode::CaptureOutput;
|
||||||
return execute_ssh_command(get_SSH_INFO(), scommand, mode, &output);
|
return execute_command(get_SSH_INFO(), scommand, mode, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -55,16 +54,14 @@ bool service_runner::install(bool silent) {
|
|||||||
|
|
||||||
// Create service directory
|
// Create service directory
|
||||||
std::string remote_service_path = remotepath::service(mServer, mService);
|
std::string remote_service_path = remotepath::service(mServer, mService);
|
||||||
std::string mkdir_cmd = "mkdir -p " + quote(remote_service_path);
|
if (!execute_command(mServerEnv.get_SSH_INFO(), sCommand({"mkdir", "-p", quote(remote_service_path)}), cMode::Silent))
|
||||||
if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(mkdir_cmd), cMode::Silent))
|
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to create service directory " << remote_service_path << std::endl;
|
std::cerr << "Failed to create service directory " << remote_service_path << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if rsync is installed on remote host
|
// Check if rsync is installed on remote host
|
||||||
std::string check_rsync_cmd = "which rsync > /dev/null 2>&1";
|
if (!execute_command(mServerEnv.get_SSH_INFO(), sCommand({"which", "rsync", ">", "/dev/null", "2>&1"}), cMode::Silent))
|
||||||
if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(check_rsync_cmd), cMode::Silent))
|
|
||||||
{
|
{
|
||||||
std::cerr << "rsync is not installed on the remote host" << std::endl;
|
std::cerr << "rsync is not installed on the remote host" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -73,15 +70,13 @@ bool service_runner::install(bool silent) {
|
|||||||
// Copy template files
|
// Copy template files
|
||||||
{
|
{
|
||||||
std::cout << "Copying: [LOCAL] " << tinfo.local_template_path() << std::endl << std::string(8,' ')<<"[REMOTE] " << remotepath::service_template(mServer, mService) << "/" << std::endl;
|
std::cout << "Copying: [LOCAL] " << tinfo.local_template_path() << std::endl << std::string(8,' ')<<"[REMOTE] " << remotepath::service_template(mServer, mService) << "/" << std::endl;
|
||||||
std::string rsync_cmd = "rsync --delete -zrpc -e 'ssh -p " + mServerEnv.get_SSH_PORT() + "' " +
|
std::vector<std::string> rsync_cmd = {"rsync", "--delete", "-zrpc", "-e", quote("ssh -p " + mServerEnv.get_SSH_PORT()),
|
||||||
quote(tinfo.local_template_path().string()+"/") + " "+
|
quote(tinfo.local_template_path().string()+"/"),
|
||||||
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" +
|
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remotepath::service_template(mServer, mService)+"/")
|
||||||
quote(remotepath::service_template(mServer, mService)+"/");
|
};
|
||||||
//std::cout << std::endl << rsync_cmd << std::endl << std::endl;
|
if (!execute_command(rsync_cmd, silent ? cMode::Silent : cMode::Defaults))
|
||||||
if (!execute_local_command(rsync_cmd, silent ? cMode::Silent : cMode::None))
|
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to copy template files using rsync" << std::endl;
|
std::cerr << "Failed to copy template files using rsync" << std::endl;
|
||||||
std::cerr << "Is rsync installed on the remote host?" << std::endl;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,11 +89,11 @@ bool service_runner::install(bool silent) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::cout << "Copying: [LOCAL] " << local_service_path << std::endl <<std::string(8,' ')<<"[REMOTE] " << remotepath::service_config(mServer,mService) << std::endl;
|
std::cout << "Copying: [LOCAL] " << local_service_path << std::endl <<std::string(8,' ')<<"[REMOTE] " << remotepath::service_config(mServer,mService) << std::endl;
|
||||||
std::string rsync_cmd = "rsync --delete -zrpc -e 'ssh -p " + mServerEnv.get_SSH_PORT() + "' " +
|
std::vector<std::string> rsync_cmd = {"rsync", "--delete", "-zrpc", "-e", quote("ssh -p " + mServerEnv.get_SSH_PORT()),
|
||||||
quote(local_service_path + "/") + " "+
|
quote(local_service_path + "/"),
|
||||||
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" +
|
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remotepath::service_config(mServer,mService) + "/")
|
||||||
quote(remotepath::service_config(mServer,mService) + "/");
|
};
|
||||||
if (!execute_local_command(rsync_cmd, silent ? cMode::Silent : cMode::None))
|
if (!execute_command(rsync_cmd, silent ? cMode::Silent : cMode::Defaults))
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to copy service files using rsync" << std::endl;
|
std::cerr << "Failed to copy service files using rsync" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -141,8 +136,8 @@ bool service_runner::uninstall(bool silent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Remove the service directory from the server
|
// 4. Remove the service directory from the server
|
||||||
std::string rm_cmd = "rm -rf " + quote(remotepath::service(mServer, mService));
|
std::vector<std::string> rm_cmd = {"rm", "-rf", remotepath::service(mServer, mService)};
|
||||||
if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent)) {
|
if (!execute_command(mServerEnv.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent)) {
|
||||||
std::cerr << "Failed to remove service directory" << std::endl;
|
std::cerr << "Failed to remove service directory" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -189,8 +184,8 @@ bool service_runner::fullnuke()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string rm_cmd = "rm -rf " + quote(local_service_path);
|
std::vector<std::string> rm_cmd = {"rm", "-rf", local_service_path};
|
||||||
if (!execute_local_command(rm_cmd, cMode::Silent)) {
|
if (!execute_command(sCommand(rm_cmd), cMode::Silent)) {
|
||||||
std::cerr << "Failed to remove service directory" << std::endl;
|
std::cerr << "Failed to remove service directory" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -407,13 +402,13 @@ bool service_runner::interactive_ssh(const std::string & server_name, const std:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sCommand scommand("bash");
|
sCommand scommand({"bash"});
|
||||||
server_env_manager env(server_name);
|
server_env_manager env(server_name);
|
||||||
if (!env.is_valid()) {
|
if (!env.is_valid()) {
|
||||||
std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
|
std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return execute_ssh_command(env.get_SSH_INFO(), scommand, cMode::Interactive | cMode::RawCommand);
|
return execute_command(env.get_SSH_INFO(), scommand, cMode::Defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
void service_runner::edit_server(const std::string &server_name)
|
void service_runner::edit_server(const std::string &server_name)
|
||||||
@ -445,14 +440,14 @@ bool service_runner::edit_file(const std::string &file_path)
|
|||||||
std::string parent_dir = get_parent(file_path);
|
std::string parent_dir = get_parent(file_path);
|
||||||
std::filesystem::create_directories(parent_dir);
|
std::filesystem::create_directories(parent_dir);
|
||||||
|
|
||||||
std::string editor_cmd;
|
std::vector<std::string> editor_cmd;
|
||||||
const char* editor_env = std::getenv("EDITOR");
|
const char* editor_env = std::getenv("EDITOR");
|
||||||
|
|
||||||
if (editor_env && std::strlen(editor_env) > 0) {
|
if (editor_env && std::strlen(editor_env) > 0) {
|
||||||
editor_cmd = std::string(editor_env) + " " + quote(file_path);
|
editor_cmd = {std::string(editor_env), file_path};
|
||||||
} else if (isatty(STDIN_FILENO)) {
|
} else if (isatty(STDIN_FILENO)) {
|
||||||
// Check if stdin is connected to a terminal if EDITOR is not set
|
// Check if stdin is connected to a terminal if EDITOR is not set
|
||||||
editor_cmd = "nano -w " + quote(file_path);
|
editor_cmd = {"nano", "-w", file_path};
|
||||||
} else {
|
} else {
|
||||||
std::cerr << "Error: Standard input is not a terminal and EDITOR environment variable is not set." << std::endl;
|
std::cerr << "Error: Standard input is not a terminal and EDITOR environment variable is not set." << std::endl;
|
||||||
std::cerr << "Try setting the EDITOR environment variable (e.g., export EDITOR=nano) or run in an interactive terminal." << std::endl;
|
std::cerr << "Try setting the EDITOR environment variable (e.g., export EDITOR=nano) or run in an interactive terminal." << std::endl;
|
||||||
@ -461,7 +456,7 @@ bool service_runner::edit_file(const std::string &file_path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Editing file: " << file_path << std::endl;
|
std::cout << "Editing file: " << file_path << std::endl;
|
||||||
return execute_local_command(editor_cmd, cMode::Interactive | cMode::RawCommand);
|
return execute_command(editor_cmd, cMode::Defaults);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool service_runner::interactive_ssh_service()
|
bool service_runner::interactive_ssh_service()
|
||||||
@ -567,8 +562,8 @@ bool service_runner::restore(std::string backup_file, bool silent)
|
|||||||
std::string remote_backup_file_path = remote_backups_dir + "/" + backup_file;
|
std::string remote_backup_file_path = remote_backups_dir + "/" + backup_file;
|
||||||
|
|
||||||
// Copy backup file from local to server
|
// Copy backup file from local to server
|
||||||
std::string scp_cmd = "scp -P " + mServerEnv.get_SSH_PORT() + " " + quote(local_backup_file_path) + " " + mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_backup_file_path) + (silent ? " > /dev/null 2>&1" : "");
|
std::vector<std::string> scp_cmd = {"scp", "-P", mServerEnv.get_SSH_PORT(), quote(local_backup_file_path), mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" + quote(remote_backup_file_path) + (silent ? " > /dev/null 2>&1" : "")};
|
||||||
if (!execute_local_command(scp_cmd, silent ? cMode::Silent : cMode::None)) {
|
if (!execute_command(sCommand(scp_cmd), silent ? cMode::Silent : cMode::Defaults)) {
|
||||||
std::cerr << "Failed to copy backup file from server" << std::endl;
|
std::cerr << "Failed to copy backup file from server" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -645,8 +640,7 @@ bool service_runner::backup(bool silent) {
|
|||||||
// Create backups directory on server if it doesn't exist
|
// Create backups directory on server if it doesn't exist
|
||||||
std::string remote_backups_dir = remotepath::backups(mServer);
|
std::string remote_backups_dir = remotepath::backups(mServer);
|
||||||
if (!silent) std::cout << "Remote backups directory on "<< mServer <<": " << remote_backups_dir << std::endl;
|
if (!silent) std::cout << "Remote backups directory on "<< mServer <<": " << remote_backups_dir << std::endl;
|
||||||
std::string mkdir_cmd = "mkdir -p " + quote(remote_backups_dir);
|
if (!execute_command(mServerEnv.get_SSH_INFO(), sCommand({"mkdir","-p",remote_backups_dir}), cMode::Silent)) {
|
||||||
if (!execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(mkdir_cmd), cMode::Silent)) {
|
|
||||||
std::cerr << "Failed to create backups directory on server" << std::endl;
|
std::cerr << "Failed to create backups directory on server" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -687,10 +681,11 @@ bool service_runner::backup(bool silent) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy backup file from server to local
|
// Copy backup file from server to local
|
||||||
std::string scp_cmd = "scp -P " + mServerEnv.get_SSH_PORT() + " " +
|
std::vector<std::string> scp_cmd = {"scp", "-P", mServerEnv.get_SSH_PORT(),
|
||||||
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" +
|
mServerEnv.get_SSH_USER() + "@" + mServerEnv.get_SSH_HOST() + ":" +
|
||||||
quote(remote_backup_file_path) + " " + quote(local_backup_file_path) + (silent ? " > /dev/null 2>&1" : "");
|
remote_backup_file_path,
|
||||||
if (!execute_local_command(scp_cmd, silent ? cMode::Silent : cMode::None)) {
|
local_backup_file_path};
|
||||||
|
if (!execute_command(sCommand(scp_cmd), silent ? cMode::Silent : cMode::Defaults)) {
|
||||||
std::cerr << "Failed to copy backup file from server" << std::endl;
|
std::cerr << "Failed to copy backup file from server" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -706,8 +701,8 @@ bool service_runner::backup(bool silent) {
|
|||||||
cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mServerEnv(server_env)
|
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);
|
std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10);
|
||||||
std::string mkdir_cmd = "mkdir -p " + quote(p);
|
std::vector<std::string> mkdir_cmd = {"mkdir", "-p", p};
|
||||||
if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand(mkdir_cmd), cMode::Silent))
|
if (!execute_command(server_env.get_SSH_INFO(), sCommand(mkdir_cmd), cMode::Silent))
|
||||||
std::cerr << "Failed to create temp directory on server" << std::endl;
|
std::cerr << "Failed to create temp directory on server" << std::endl;
|
||||||
else
|
else
|
||||||
mPath = p;
|
mPath = p;
|
||||||
@ -715,8 +710,8 @@ cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mSe
|
|||||||
|
|
||||||
cRemoteTempFolder::~cRemoteTempFolder()
|
cRemoteTempFolder::~cRemoteTempFolder()
|
||||||
{
|
{
|
||||||
std::string rm_cmd = "rm -rf " + quote(mPath);
|
std::vector<std::string> rm_cmd = {"rm", "-rf", mPath};
|
||||||
execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent);
|
execute_command(mServerEnv.get_SSH_INFO(), sCommand(rm_cmd), cMode::Silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cRemoteTempFolder::path() const
|
std::string cRemoteTempFolder::path() const
|
||||||
|
@ -7,22 +7,37 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <libassert/assert.hpp>
|
#include <libassert/assert.hpp>
|
||||||
|
#include <algorithm>
|
||||||
#include "execute.hpp"
|
#include "execute.hpp"
|
||||||
#include "contrib/base64.hpp"
|
#include "contrib/base64.hpp"
|
||||||
#include "utils/utils.hpp"
|
#include "utils/utils.hpp"
|
||||||
|
#include "utils/directories.hpp"
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
bool EXITSTATUSCHECK(int ret) {
|
bool EXITSTATUSCHECK(int ret) {
|
||||||
return (ret != -1 && WIFEXITED(ret) && (WEXITSTATUS(ret) == 0)); // ret is -1 if the command failed to execute.
|
return (ret != -1 && WIFEXITED(ret) && (WEXITSTATUS(ret) == 0)); // ret is -1 if the command failed to execute.
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
bool execute_local_command_interactive(const sCommand &command, bool silent)
|
bool __execute_command(std::vector<std::string> command, std::string * output)
|
||||||
{
|
{
|
||||||
if (command.get_command_to_run().empty())
|
// Save TTY state if possible
|
||||||
|
struct termios orig_termios;
|
||||||
|
int tty_fd = isatty(STDIN_FILENO) ? STDIN_FILENO : -1;
|
||||||
|
if (tty_fd != -1) {
|
||||||
|
tcgetattr(tty_fd, &orig_termios);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Executing command: ";
|
||||||
|
for (auto & x : command) std::cout << "[" << x << "] ";
|
||||||
|
std::cout << std::endl << std::flush;
|
||||||
|
|
||||||
|
int pipefd[2];
|
||||||
|
bool capture = (output != nullptr);
|
||||||
|
if (capture && pipe(pipefd) == -1) {
|
||||||
|
perror("pipe failed");
|
||||||
return false;
|
return false;
|
||||||
std::string full_command = command.construct_cmd(cStyle::Raw); // Get the command string
|
}
|
||||||
|
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
@ -32,151 +47,129 @@ bool execute_local_command_interactive(const sCommand &command, bool silent)
|
|||||||
return false;
|
return false;
|
||||||
} else if (pid == 0) {
|
} else if (pid == 0) {
|
||||||
// Child process
|
// Child process
|
||||||
std::vector<const char *> commandvec = {"bash", "-c", full_command.c_str(),NULL};
|
if (capture) {
|
||||||
|
close(pipefd[0]); // Close read end
|
||||||
if (!silent) {
|
dup2(pipefd[1], STDOUT_FILENO); // Redirect stdout to pipe
|
||||||
std::cout << "Executing command: ";
|
dup2(pipefd[1], STDERR_FILENO); // Redirect stderr to pipe
|
||||||
for (auto & x : commandvec) std::cout << x << " ";
|
close(pipefd[1]);
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<const char *> commandvec;
|
||||||
|
for (auto & x : command) {
|
||||||
|
commandvec.push_back(x.c_str());
|
||||||
|
}
|
||||||
|
commandvec.push_back(NULL);
|
||||||
|
|
||||||
|
// if (!silent) {
|
||||||
|
// std::cout << "Executing command: ";
|
||||||
|
// for (auto & x : commandvec) std::cout << x << " ";
|
||||||
|
// std::cout << std::endl;
|
||||||
|
// }
|
||||||
|
|
||||||
execvp(commandvec[0], const_cast<char* const*>(commandvec.data()));
|
execvp(commandvec[0], const_cast<char* const*>(commandvec.data()));
|
||||||
// If execvp returns, it means an error occurred
|
// If execvp returns, it means an error occurred
|
||||||
perror("execvp failed");
|
perror("execvp failed");
|
||||||
exit(EXIT_FAILURE); // Exit child process on error
|
exit(EXIT_FAILURE); // Exit child process on error
|
||||||
} else {
|
} else {
|
||||||
// Parent process
|
// Parent process
|
||||||
|
if (capture) {
|
||||||
|
close(pipefd[1]); // Close write end
|
||||||
|
char buffer[256];
|
||||||
|
ssize_t count;
|
||||||
|
while ((count = read(pipefd[0], buffer, sizeof(buffer))) > 0) {
|
||||||
|
output->append(buffer, count);
|
||||||
|
}
|
||||||
|
close(pipefd[0]);
|
||||||
|
}
|
||||||
int ret;
|
int ret;
|
||||||
// Wait for the child process to complete
|
// Wait for the child process to complete
|
||||||
waitpid(pid, &ret, 0);
|
waitpid(pid, &ret, 0);
|
||||||
|
|
||||||
|
// Restore TTY state if possible
|
||||||
|
if (tty_fd != -1) {
|
||||||
|
tcsetattr(tty_fd, TCSANOW, &orig_termios);
|
||||||
|
}
|
||||||
|
|
||||||
return EXITSTATUSCHECK(ret);
|
return EXITSTATUSCHECK(ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool execute_local_command_and_capture_output(const sCommand& command, std::string * output, cMode mode)
|
bool execute_command(const sSSHInfo * ssh_info, const sCommand command, cMode mode, std::string * output)
|
||||||
{
|
{
|
||||||
ASSERT(output != nullptr, "Output string must be provided");
|
|
||||||
ASSERT(is_raw(mode), "Capture output mode requires raw command mode");
|
|
||||||
ASSERT(!hasFlag(mode, cMode::Silent), "Silent mode is not allowed with capture output mode");
|
|
||||||
if (command.get_command_to_run().empty())
|
if (command.get_command_to_run().empty())
|
||||||
return false;
|
return false;
|
||||||
cStyle style = getStyle(mode);
|
|
||||||
std::string full_cmd = command.construct_cmd(style) + " 2>&1";
|
std::vector<std::string> commandvec;
|
||||||
FILE *pipe = popen(full_cmd.c_str(), "r");
|
|
||||||
if (!pipe) {
|
// Construct the shell command with proper environment variables and directory
|
||||||
return false;
|
std::string shell_command;
|
||||||
|
|
||||||
|
// Add working directory if provided
|
||||||
|
if (!command.get_directory_to_run_in().empty()) {
|
||||||
|
shell_command += "cd " + quote(command.get_directory_to_run_in()) + " && ";
|
||||||
}
|
}
|
||||||
char buffer[128];
|
|
||||||
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
// Add environment variables if provided
|
||||||
(*output) += buffer;
|
for (const auto& env_var : command.get_env_vars()) {
|
||||||
|
shell_command += env_var.first + "=" + quote(env_var.second) + " ";
|
||||||
}
|
}
|
||||||
int ret = pclose(pipe);
|
|
||||||
return EXITSTATUSCHECK(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Add the command arguments properly joined
|
||||||
|
const auto& args = command.get_command_to_run();
|
||||||
bool execute_local_command(const sCommand & command, cMode mode, std::string * output /* = nullptr */)
|
std::ostringstream cmd_stream;
|
||||||
{
|
for (size_t i = 0; i < args.size(); ++i) {
|
||||||
if (hasFlag(mode, cMode::Interactive)) {
|
if (i > 0) cmd_stream << " ";
|
||||||
ASSERT(! hasFlag(mode, cMode::CaptureOutput), "Interactive mode and capture output mode cannot be used together");
|
cmd_stream << args[i];
|
||||||
ASSERT(output == nullptr, "Interactive mode and an output string cannot be used together");
|
|
||||||
ASSERT(is_raw(mode), "Interactive mode requires raw command mode");
|
|
||||||
|
|
||||||
return execute_local_command_interactive(command, hasFlag(mode, cMode::Silent));
|
|
||||||
}
|
}
|
||||||
|
shell_command += cmd_stream.str();
|
||||||
|
|
||||||
|
if (ssh_info) {
|
||||||
|
// Use bash -c for SSH to ensure proper command execution
|
||||||
|
commandvec = {
|
||||||
|
"/usr/bin/ssh",
|
||||||
|
"-p", ssh_info->port,
|
||||||
|
(hasFlag(mode, cMode::CaptureOutput) ? "" : "-tt"),
|
||||||
|
ssh_info->user + "@" + ssh_info->host,
|
||||||
|
"bash", "-c", halfquote(shell_command)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Local execution
|
||||||
|
commandvec = {"bash", "-c", halfquote(shell_command)};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any empty strings
|
||||||
|
commandvec.erase(
|
||||||
|
std::remove_if(commandvec.begin(), commandvec.end(),
|
||||||
|
[](const std::string& s) { return s.empty(); }),
|
||||||
|
commandvec.end());
|
||||||
|
|
||||||
if (hasFlag(mode, cMode::CaptureOutput)) {
|
if (hasFlag(mode, cMode::CaptureOutput)) {
|
||||||
ASSERT(output != nullptr, "Capture output mode requires an output string to be provided");
|
ASSERT(output, "Capture output mode requires an output string to be provided");
|
||||||
ASSERT(is_raw(mode), "Capture output mode requires raw command mode");
|
return __execute_command(commandvec, output);
|
||||||
ASSERT(!hasFlag(mode, cMode::Silent), "Silent mode is not allowed with capture output mode");
|
} else {
|
||||||
|
return __execute_command(commandvec, nullptr);
|
||||||
return execute_local_command_and_capture_output(command, output, mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.get_command_to_run().empty())
|
|
||||||
return false;
|
|
||||||
cStyle style = getStyle(mode);
|
|
||||||
std::string full_cmd = command.construct_cmd(style) + " 2>&1" + (hasFlag(mode, cMode::Silent) ? " > /dev/null" : "");
|
|
||||||
int ret = system(full_cmd.c_str());
|
|
||||||
|
|
||||||
bool ok = EXITSTATUSCHECK(ret);
|
|
||||||
if (!ok) {
|
|
||||||
std::cerr << "Error: Failed to execute command: " << std::endl;
|
|
||||||
std::cerr << full_cmd << std::endl;
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool execute_ssh_command(const sSSHInfo &ssh_info, const sCommand &command, cMode mode, std::string *output)
|
bool execute_command(const sSSHInfo ssh_info, const sCommand command, const cMode mode)
|
||||||
{
|
{
|
||||||
if (command.get_command_to_run().empty())
|
return execute_command(&ssh_info, command, mode, nullptr);
|
||||||
return false;
|
|
||||||
|
|
||||||
ASSERT(!(hasFlag(mode, cMode::Interactive) && !is_raw(mode)), "Interactive mode requires raw command mode");
|
|
||||||
ASSERT(!(hasFlag(mode, cMode::CaptureOutput) && output == nullptr), "Capture output mode must be used with an output string");
|
|
||||||
|
|
||||||
std::stringstream ssh_cmd;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <<std::endl<<std::endl;
|
|
||||||
}
|
|
||||||
return rval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string makesafecmd(const std::string &command)
|
bool execute_command(const sSSHInfo ssh_info, const sCommand command, const cMode mode, std::string & output)
|
||||||
{
|
{
|
||||||
if (command.empty())
|
return execute_command(&ssh_info, command, mode, &output);
|
||||||
return "";
|
|
||||||
std::string encoded = base64_encode(dequote(trim(command)));
|
|
||||||
std::string commandstr = "echo " + encoded + " | base64 -d | bash";
|
|
||||||
return commandstr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string sCommand::construct_cmd(cStyle style) const
|
bool execute_command(const sCommand command, const cMode mode)
|
||||||
{
|
{
|
||||||
if (mCmd.empty())
|
return execute_command(nullptr, command, mode, nullptr);
|
||||||
return "";
|
|
||||||
|
|
||||||
std::string cdcmd;
|
|
||||||
if (!mDir.empty())
|
|
||||||
cdcmd = "cd " + quote(mDir) + " && ";
|
|
||||||
|
|
||||||
std::string cmdstr;
|
|
||||||
for (const auto& env_var : mVars) {
|
|
||||||
cmdstr += env_var.first + "=" + quote(dequote(trim(env_var.second))) + " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
cmdstr += mCmd;
|
|
||||||
|
|
||||||
if (is_safe(style))
|
|
||||||
cmdstr = makesafecmd(cmdstr);
|
|
||||||
|
|
||||||
return cdcmd + cmdstr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool execute_command(const sCommand command, const cMode mode, std::string & output)
|
||||||
|
{
|
||||||
|
return execute_command(nullptr, command, mode, &output);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
@ -10,15 +11,9 @@ class sCommand;
|
|||||||
|
|
||||||
// mode bitset
|
// mode bitset
|
||||||
enum class cMode {
|
enum class cMode {
|
||||||
None = 0,
|
Defaults = 0,
|
||||||
Interactive = 1,
|
Silent = 1,
|
||||||
Silent = 2,
|
CaptureOutput = 2
|
||||||
CaptureOutput = 4,
|
|
||||||
RawCommand = 8
|
|
||||||
};
|
|
||||||
enum class cStyle {
|
|
||||||
Safe = 0,
|
|
||||||
Raw = 1
|
|
||||||
};
|
};
|
||||||
|
|
||||||
inline cMode operator&(cMode lhs, cMode rhs) {return static_cast<cMode>(static_cast<int>(lhs) & static_cast<int>(rhs));}
|
inline cMode operator&(cMode lhs, cMode rhs) {return static_cast<cMode>(static_cast<int>(lhs) & static_cast<int>(rhs));}
|
||||||
@ -27,10 +22,6 @@ inline cMode operator-(cMode lhs, cMode rhs) {return static_cast<cMode>(static_c
|
|||||||
inline cMode operator|(cMode lhs, cMode rhs) {return static_cast<cMode>(static_cast<int>(lhs) | static_cast<int>(rhs));}
|
inline cMode operator|(cMode lhs, cMode rhs) {return static_cast<cMode>(static_cast<int>(lhs) | static_cast<int>(rhs));}
|
||||||
inline cMode operator|=(cMode & lhs, cMode rhs) {return lhs = lhs | rhs;}
|
inline cMode operator|=(cMode & lhs, cMode rhs) {return lhs = lhs | rhs;}
|
||||||
inline bool hasFlag(cMode mode, cMode flag) {return (mode & flag) == flag;}
|
inline bool hasFlag(cMode mode, cMode flag) {return (mode & flag) == flag;}
|
||||||
inline bool is_safe(cStyle style) { return style == cStyle::Safe; }
|
|
||||||
inline bool is_raw(cStyle style) { return style == cStyle::Raw; }
|
|
||||||
inline bool is_raw(cMode mode) { return hasFlag(mode, cMode::RawCommand); }
|
|
||||||
inline cStyle getStyle(cMode mode) { return is_raw(mode) ? cStyle::Raw : cStyle::Safe; }
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct sSSHInfo {
|
typedef struct sSSHInfo {
|
||||||
@ -39,11 +30,10 @@ typedef struct sSSHInfo {
|
|||||||
std::string port;
|
std::string port;
|
||||||
} sSSHInfo;
|
} sSSHInfo;
|
||||||
|
|
||||||
bool execute_local_command(const sCommand & command, cMode mode = cMode::None, std::string * output = nullptr);
|
bool execute_command(const sSSHInfo ssh_info, const sCommand command, const cMode mode);
|
||||||
bool execute_ssh_command(const sSSHInfo & ssh_info, const sCommand & command, cMode mode = cMode::None, std::string * output = nullptr);
|
bool execute_command(const sSSHInfo ssh_info, const sCommand command, const cMode mode, std::string & output);
|
||||||
|
bool execute_command(const sCommand command, const cMode mode);
|
||||||
std::string makesafecmd(const std::string& command);
|
bool execute_command(const sCommand command, const cMode mode, std::string & output);
|
||||||
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -52,27 +42,24 @@ std::string makesafecmd(const std::string& command);
|
|||||||
// class to hold a command to run on the remote server.
|
// class to hold a command to run on the remote server.
|
||||||
class sCommand {
|
class sCommand {
|
||||||
public:
|
public:
|
||||||
sCommand(std::string directory_to_run_in, std::string command_to_run, const std::map<std::string, std::string> & env_vars) :
|
sCommand(std::string directory_to_run_in, const std::vector<std::string> & command_to_run, const std::map<std::string, std::string> & env_vars) :
|
||||||
mDir(directory_to_run_in), mCmd(command_to_run), mVars(env_vars) {}
|
mDir(directory_to_run_in), mCmdArgs(command_to_run), mEnvVars(env_vars) {}
|
||||||
sCommand(std::string command_to_run) :
|
sCommand(const std::vector<std::string> & command_to_run) :
|
||||||
mDir(""), mCmd(command_to_run), mVars({}) {}
|
mDir(""), mCmdArgs(command_to_run), mEnvVars({}) {}
|
||||||
sCommand() : mDir(""), mCmd(""), mVars({}) {}
|
sCommand() : mDir(""), mCmdArgs({}), mEnvVars({}) {}
|
||||||
|
|
||||||
std::string get_directory_to_run_in() const { return mDir; }
|
std::string get_directory_to_run_in() const { return mDir; }
|
||||||
std::string get_command_to_run() const { return mCmd; }
|
const std::vector<std::string>& get_command_to_run() const { return mCmdArgs; }
|
||||||
const std::map<std::string, std::string>& get_env_vars() const { return mVars; }
|
const std::map<std::string, std::string>& get_env_vars() const { return mEnvVars; }
|
||||||
|
|
||||||
void add_env_var(const std::string& key, const std::string& value) { mVars[key] = value; }
|
void add_env_var(const std::string& key, const std::string& value) { mEnvVars[key] = value; }
|
||||||
|
|
||||||
|
bool empty() const { return mCmdArgs.empty(); }
|
||||||
std::string construct_cmd(cStyle style) const;
|
|
||||||
|
|
||||||
bool empty() const { return mCmd.empty(); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mDir;
|
std::string mDir;
|
||||||
std::string mCmd;
|
std::vector<std::string> mCmdArgs;
|
||||||
std::map<std::string, std::string> mVars;
|
std::map<std::string, std::string> mEnvVars;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
0
templates/dropshell-agent/_allservicesstatus.sh
Normal file → Executable file
0
templates/dropshell-agent/_allservicesstatus.sh
Normal file → Executable file
0
templates/dropshell-agent/_nuke_other.sh
Normal file → Executable file
0
templates/dropshell-agent/_nuke_other.sh
Normal file → Executable file
0
templates/dropshell-agent/install.sh
Normal file → Executable file
0
templates/dropshell-agent/install.sh
Normal file → Executable file
0
templates/dropshell-agent/shared/_autocommands.sh
Normal file → Executable file
0
templates/dropshell-agent/shared/_autocommands.sh
Normal file → Executable file
0
templates/dropshell-agent/shared/_common.sh
Normal file → Executable file
0
templates/dropshell-agent/shared/_common.sh
Normal file → Executable file
32
templates/dropshell-agent/shared/_run.sh
Executable file
32
templates/dropshell-agent/shared/_run.sh
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
directory_to_run_in=${1:-}
|
||||||
|
command_to_run_base64=${2:-}
|
||||||
|
|
||||||
|
if [ -z "$directory_to_run_in" ]; then
|
||||||
|
echo "Usage: $0 <directory_to_run_in> <command_to_run>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$command_to_run_base64" ]; then
|
||||||
|
echo "Usage: $0 <directory_to_run_in> <command_to_run>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
command_to_run=$(echo "$command_to_run_base64" | base64 -d)
|
||||||
|
|
||||||
|
if [ -z "$command_to_run" ]; then
|
||||||
|
echo "Usage: $0 <directory_to_run_in> <command_to_run>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ -n "$directory_to_run_in" ]; then
|
||||||
|
cd "$directory_to_run_in"
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval "$command_to_run"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
0
templates/dropshell-agent/uninstall.sh
Normal file → Executable file
0
templates/dropshell-agent/uninstall.sh
Normal file → Executable file
Loading…
x
Reference in New Issue
Block a user