Workign on stuff

This commit is contained in:
John 2025-04-27 11:27:02 +12:00
parent e3e8e5f7d3
commit 8c21260e49
8 changed files with 450 additions and 100 deletions

View File

@ -28,7 +28,6 @@ configure_file(
) )
# Find required packages # Find required packages
find_package(Curses REQUIRED)
find_package(TBB REQUIRED) find_package(TBB REQUIRED)
# Auto-detect source files # Auto-detect source files
@ -42,12 +41,10 @@ add_executable(dropshell ${SOURCES})
target_include_directories(dropshell PRIVATE target_include_directories(dropshell PRIVATE
src src
${CMAKE_CURRENT_BINARY_DIR}/src ${CMAKE_CURRENT_BINARY_DIR}/src
${CURSES_INCLUDE_DIRS}
) )
# Link libraries # Link libraries
target_link_libraries(dropshell PRIVATE target_link_libraries(dropshell PRIVATE
${CURSES_LIBRARIES}
TBB::tbb TBB::tbb
) )

282
src/contrib/base64.cpp Normal file
View File

@ -0,0 +1,282 @@
/*
base64.cpp and base64.h
base64 encoding and decoding with C++.
More information at
https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
Version: 2.rc.09 (release candidate)
Copyright (C) 2004-2017, 2020-2022 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include "contrib/base64.hpp"
#include <algorithm>
#include <stdexcept>
//
// Depending on the url parameter in base64_chars, one of
// two sets of base64 characters needs to be chosen.
// They differ in their last two characters.
//
static const char* base64_chars[2] = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"+/",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"-_"};
static unsigned int pos_of_char(const unsigned char chr) {
//
// Return the position of chr within base64_encode()
//
if (chr >= 'A' && chr <= 'Z') return chr - 'A';
else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
else if (chr == '+' || chr == '-') return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters (
else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_'
else
//
// 2020-10-23: Throw std::exception rather than const char*
//(Pablo Martin-Gomez, https://github.com/Bouska)
//
throw std::runtime_error("Input is not valid base64-encoded data.");
}
static std::string insert_linebreaks(std::string str, size_t distance) {
//
// Provided by https://github.com/JomaCorpFX, adapted by me.
//
if (!str.length()) {
return "";
}
size_t pos = distance;
while (pos < str.size()) {
str.insert(pos, "\n");
pos += distance + 1;
}
return str;
}
template <typename String, unsigned int line_length>
static std::string encode_with_line_breaks(String s) {
return insert_linebreaks(base64_encode(s, false), line_length);
}
template <typename String>
static std::string encode_pem(String s) {
return encode_with_line_breaks<String, 64>(s);
}
template <typename String>
static std::string encode_mime(String s) {
return encode_with_line_breaks<String, 76>(s);
}
template <typename String>
static std::string encode(String s, bool url) {
return base64_encode(reinterpret_cast<const unsigned char*>(s.data()), s.length(), url);
}
std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len, bool url) {
size_t len_encoded = (in_len +2) / 3 * 4;
unsigned char trailing_char = url ? '.' : '=';
//
// Choose set of base64 characters. They differ
// for the last two positions, depending on the url
// parameter.
// A bool (as is the parameter url) is guaranteed
// to evaluate to either 0 or 1 in C++ therefore,
// the correct character set is chosen by subscripting
// base64_chars with url.
//
const char* base64_chars_ = base64_chars[url];
std::string ret;
ret.reserve(len_encoded);
unsigned int pos = 0;
while (pos < in_len) {
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]);
if (pos+1 < in_len) {
ret.push_back(base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]);
if (pos+2 < in_len) {
ret.push_back(base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]);
ret.push_back(base64_chars_[ bytes_to_encode[pos + 2] & 0x3f]);
}
else {
ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]);
ret.push_back(trailing_char);
}
}
else {
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]);
ret.push_back(trailing_char);
ret.push_back(trailing_char);
}
pos += 3;
}
return ret;
}
template <typename String>
static std::string decode(String const& encoded_string, bool remove_linebreaks) {
//
// decode(…) is templated so that it can be used with String = const std::string&
// or std::string_view (requires at least C++17)
//
if (encoded_string.empty()) return std::string();
if (remove_linebreaks) {
std::string copy(encoded_string);
copy.erase(std::remove(copy.begin(), copy.end(), '\n'), copy.end());
return base64_decode(copy, false);
}
size_t length_of_string = encoded_string.length();
size_t pos = 0;
//
// The approximate length (bytes) of the decoded string might be one or
// two bytes smaller, depending on the amount of trailing equal signs
// in the encoded string. This approximation is needed to reserve
// enough space in the string to be returned.
//
size_t approx_length_of_decoded_string = length_of_string / 4 * 3;
std::string ret;
ret.reserve(approx_length_of_decoded_string);
while (pos < length_of_string) {
//
// Iterate over encoded input string in chunks. The size of all
// chunks except the last one is 4 bytes.
//
// The last chunk might be padded with equal signs or dots
// in order to make it 4 bytes in size as well, but this
// is not required as per RFC 2045.
//
// All chunks except the last one produce three output bytes.
//
// The last chunk produces at least one and up to three bytes.
//
size_t pos_of_char_1 = pos_of_char(encoded_string.at(pos+1) );
//
// Emit the first output byte that is produced in each chunk:
//
ret.push_back(static_cast<std::string::value_type>( ( (pos_of_char(encoded_string.at(pos+0)) ) << 2 ) + ( (pos_of_char_1 & 0x30 ) >> 4)));
if ( ( pos + 2 < length_of_string ) && // Check for data that is not padded with equal signs (which is allowed by RFC 2045)
encoded_string.at(pos+2) != '=' &&
encoded_string.at(pos+2) != '.' // accept URL-safe base 64 strings, too, so check for '.' also.
)
{
//
// Emit a chunk's second byte (which might not be produced in the last chunk).
//
unsigned int pos_of_char_2 = pos_of_char(encoded_string.at(pos+2) );
ret.push_back(static_cast<std::string::value_type>( (( pos_of_char_1 & 0x0f) << 4) + (( pos_of_char_2 & 0x3c) >> 2)));
if ( ( pos + 3 < length_of_string ) &&
encoded_string.at(pos+3) != '=' &&
encoded_string.at(pos+3) != '.'
)
{
//
// Emit a chunk's third byte (which might not be produced in the last chunk).
//
ret.push_back(static_cast<std::string::value_type>( ( (pos_of_char_2 & 0x03 ) << 6 ) + pos_of_char(encoded_string.at(pos+3)) ));
}
}
pos += 4;
}
return ret;
}
std::string base64_decode(std::string const& s, bool remove_linebreaks) {
return decode(s, remove_linebreaks);
}
std::string base64_encode(std::string const& s, bool url) {
return encode(s, url);
}
std::string base64_encode_pem (std::string const& s) {
return encode_pem(s);
}
std::string base64_encode_mime(std::string const& s) {
return encode_mime(s);
}
#if __cplusplus >= 201703L
//
// Interface with std::string_view rather than const std::string&
// Requires C++17
// Provided by Yannic Bonenberger (https://github.com/Yannic)
//
std::string base64_encode(std::string_view s, bool url) {
return encode(s, url);
}
std::string base64_encode_pem(std::string_view s) {
return encode_pem(s);
}
std::string base64_encode_mime(std::string_view s) {
return encode_mime(s);
}
std::string base64_decode(std::string_view s, bool remove_linebreaks) {
return decode(s, remove_linebreaks);
}
#endif // __cplusplus >= 201703L

35
src/contrib/base64.hpp Normal file
View File

@ -0,0 +1,35 @@
//
// base64 encoding and decoding with C++.
// Version: 2.rc.09 (release candidate)
//
#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
#include <string>
#if __cplusplus >= 201703L
#include <string_view>
#endif // __cplusplus >= 201703L
std::string base64_encode (std::string const& s, bool url = false);
std::string base64_encode_pem (std::string const& s);
std::string base64_encode_mime(std::string const& s);
std::string base64_decode(std::string const& s, bool remove_linebreaks = false);
std::string base64_encode(unsigned char const*, size_t len, bool url = false);
#if __cplusplus >= 201703L
//
// Interface with std::string_view rather than const std::string&
// Requires C++17
// Provided by Yannic Bonenberger (https://github.com/Yannic)
//
std::string base64_encode (std::string_view s, bool url = false);
std::string base64_encode_pem (std::string_view s);
std::string base64_encode_mime(std::string_view s);
std::string base64_decode(std::string_view s, bool remove_linebreaks = false);
#endif // __cplusplus >= 201703L
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */

View File

@ -203,7 +203,8 @@ int backup(const std::vector<std::string> & args, bool silent) {
std::string remote_backups_dir = get_remote_backups_path(server_name); std::string remote_backups_dir = get_remote_backups_path(server_name);
if (!silent) std::cout << "Remote backups directory on "<< server_name <<": " << remote_backups_dir << std::endl; if (!silent) std::cout << "Remote backups directory on "<< server_name <<": " << remote_backups_dir << std::endl;
std::string mkdir_cmd = "'mkdir -p " + quote(remote_backups_dir) + "'"; std::string mkdir_cmd = "'mkdir -p " + quote(remote_backups_dir) + "'";
if (!env.execute_ssh_command(mkdir_cmd, "Failed to create backups directory on server")) { if (!env.execute_ssh_command(mkdir_cmd)) {
std::cerr << "Failed to create backups directory on server" << std::endl;
return false; return false;
} }
@ -235,12 +236,8 @@ int backup(const std::vector<std::string> & args, bool silent) {
ASSERT(3 == count_substring(magic_string, local_backup_file_path)); ASSERT(3 == count_substring(magic_string, local_backup_file_path));
// Run backup script // Run backup script
std::string backup_cmd = "'cd " + quote(remote_service_template_path) + if (!env.run_remote_template_command(service_name, command, {remote_backup_file_path}, silent)) {
" && /bin/bash "+quote(remote_command_script_file)+" "+quote(remote_service_config_path)+" "+ std::cerr << "Backup script failed on remote server: " << remote_backup_file_path << std::endl;
quote(remote_backup_file_path)+"'"+ (silent ? " > /dev/null 2>&1" : "");
if (!env.execute_ssh_command(backup_cmd, "Backup script failed")) {
std::cerr << "Backup script failed: " << backup_cmd << std::endl;
return false; return false;
} }
@ -248,7 +245,8 @@ int backup(const std::vector<std::string> & args, bool silent) {
std::string scp_cmd = "scp -P " + env.get_SSH_PORT() + " " + std::string scp_cmd = "scp -P " + env.get_SSH_PORT() + " " +
env.get_SSH_USER() + "@" + env.get_SSH_HOST() + ":" + env.get_SSH_USER() + "@" + env.get_SSH_HOST() + ":" +
quote(remote_backup_file_path) + " " + quote(local_backup_file_path) + (silent ? " > /dev/null 2>&1" : ""); quote(remote_backup_file_path) + " " + quote(local_backup_file_path) + (silent ? " > /dev/null 2>&1" : "");
if (!env.execute_local_command(scp_cmd, "Failed to copy backup file from server")) { if (!env.execute_local_command(scp_cmd)) {
std::cerr << "Failed to copy backup file from server" << std::endl;
return false; return false;
} }

View File

@ -3,15 +3,13 @@
#include "utils/directories.hpp" #include "utils/directories.hpp"
#include "utils/utils.hpp" #include "utils/utils.hpp"
#include "services.hpp" #include "services.hpp"
#include "contrib/base64.hpp"
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <filesystem> #include <filesystem>
namespace dropshell {
bool server_env::is_valid() const { namespace dropshell {
return mValid;
}
server_env::server_env(const std::string& server_name) : mValid(false), mServer_name(server_name) { server_env::server_env(const std::string& server_name) : mValid(false), mServer_name(server_name) {
if (server_name.empty()) if (server_name.empty())
@ -62,27 +60,6 @@ std::string server_env::get_variable(const std::string& name) const {
return m_env_manager->get_variable_substituted(name); return m_env_manager->get_variable_substituted(name);
} }
const std::map<std::string, std::string> &server_env::get_variables() const
{
return variables;
}
std::string server_env::get_SSH_HOST() const {
return get_variable("SSH_HOST");
}
std::string server_env::get_SSH_USER() const {
return get_variable("SSH_USER");
}
std::string server_env::get_SSH_PORT() const {
return get_variable("SSH_PORT");
}
std::string server_env::get_DROPSHELL_DIR() const {
return get_variable("DROPSHELL_DIR");
}
// Helper method implementations // Helper method implementations
std::string server_env::construct_ssh_cmd() const { std::string server_env::construct_ssh_cmd() const {
std::stringstream ssh_cmd; std::stringstream ssh_cmd;
@ -91,34 +68,38 @@ std::string server_env::construct_ssh_cmd() const {
return ssh_cmd.str(); return ssh_cmd.str();
} }
std::string server_env::construct_standard_command_run_cmd(const std::string &service_name, const std::string &command, std::vector<std::string> args) const std::string server_env::construct_standard_command_run_cmd(const std::string &service_name, const std::string &command, std::vector<std::string> args, bool silent) const
{ {
std::string remote_service_template_path = get_remote_service_template_path(mServer_name,service_name); std::string remote_service_template_path = get_remote_service_template_path(mServer_name,service_name);
std::string remote_service_config_path = get_remote_service_config_path(mServer_name,service_name); std::string remote_service_config_path = get_remote_service_config_path(mServer_name,service_name);
std::string script_path = remote_service_template_path + "/" + command + ".sh"; std::string script_path = remote_service_template_path + "/" + command + ".sh";
std::string run_cmd = "'cd " + quote(remote_service_template_path) +
" && /bin/bash "+quote(script_path)+" "+quote(remote_service_config_path)+"'"; std::map<std::string, std::string> env_vars;
envmanager env_manager(get_local_service_env_path(mServer_name,service_name));
env_manager.load();
env_manager.get_all_variables(env_vars);
env_vars["CONFIG_PATH"] = remote_service_config_path;
std::string argstr = quote(remote_service_config_path);
for (const auto& arg : args) {
argstr += " " + quote(dequote(trim(arg)));
}
sCommand scommand(remote_service_template_path, "bash " + quote(script_path) + " " + argstr + (silent ? " > /dev/null 2>&1" : ""), env_vars);
std::string run_cmd = scommand.construct_safecmd();
return run_cmd; return run_cmd;
} }
bool server_env::check_remote_dir_exists(const std::string &dir_path) const bool server_env::check_remote_dir_exists(const std::string &dir_path) const
{ {
std::string check_dir_cmd = construct_ssh_cmd() + "'test -d " + quote(dir_path) + "'"; sCommand scommand("test -d " + quote(dir_path));
if (system(check_dir_cmd.c_str()) != 0) { return execute_ssh_command(scommand);
std::cerr << "Error: Directory not found on remote server:" << std::filesystem::path(dir_path).filename().string() << std::endl;
return false;
}
return true;
} }
bool server_env::check_remote_file_exists(const std::string& file_path) const { bool server_env::check_remote_file_exists(const std::string& file_path) const {
std::string check_cmd = construct_ssh_cmd() + "'test -f " + quote(file_path) + "'"; sCommand scommand("test -f " + quote(file_path));
if (system(check_cmd.c_str()) != 0) { return execute_ssh_command(scommand);
std::cerr << "Error: File not found on remote server: " << std::filesystem::path(file_path).filename().string() << std::endl;
return false;
}
return true;
} }
bool server_env::check_remote_items_exist(const std::vector<std::string> &file_paths) const bool server_env::check_remote_items_exist(const std::vector<std::string> &file_paths) const
@ -131,55 +112,74 @@ bool server_env::check_remote_items_exist(const std::vector<std::string> &file_p
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.
std::string check_cmd = construct_ssh_cmd() + "'for item in " + file_paths_str + "; do test -f $item; done'"; sCommand scommand("for item in " + file_paths_str + "; do test -f $item; done");
if (system(check_cmd.c_str()) != 0) {
bool okay = execute_ssh_command(scommand);
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;
} }
return true; return true;
} }
bool server_env::execute_ssh_command(const std::string& command, const std::string& error_msg) const { bool server_env::execute_ssh_command(const sCommand& command) const {
std::string full_cmd = construct_ssh_cmd() + command; std::string full_cmd = construct_ssh_cmd() + command.construct_safecmd();
return execute_local_command(full_cmd, error_msg); return execute_local_command(full_cmd);
} }
bool server_env::execute_ssh_command_and_capture_output(const std::string &command, std::string &output, const std::string &error_msg) const bool server_env::execute_ssh_command_and_capture_output(const sCommand& command, std::string &output) const
{ {
std::string full_cmd = construct_ssh_cmd() + command; std::string full_cmd = construct_ssh_cmd() + command.construct_safecmd();
return execute_local_command_and_capture_output(full_cmd, output); return execute_local_command_and_capture_output(full_cmd, output);
} }
bool server_env::run_remote_template_command(const std::string &service_name, const std::string &command, std::vector<std::string> args, const std::string &error_msg) const bool server_env::run_remote_template_command(const std::string &service_name, const std::string &command, std::vector<std::string> args, bool silent) const
{ {
std::string full_cmd = construct_standard_command_run_cmd(service_name, command, args); std::string full_cmd = construct_standard_command_run_cmd(service_name, command, args, silent);
return execute_ssh_command(full_cmd, error_msg); return execute_ssh_command(full_cmd);
} }
bool server_env::execute_local_command(const std::string& command, const std::string& error_msg) { bool server_env::execute_local_command(const sCommand& command) {
bool okay = (system(command.c_str()) == 0); bool okay = (system(command.construct_safecmd().c_str()) == 0);
if (!okay && !error_msg.empty())
std::cerr << "Error: " << error_msg << std::endl;
return okay; return okay;
} }
bool server_env::execute_local_command_and_capture_output(const std::string &command, std::string &output, const std::string& error_msg) bool server_env::execute_local_command_and_capture_output(const sCommand& command, std::string &output)
{ {
std::string full_cmd = command + " 2>&1"; std::string full_cmd = command.construct_safecmd() + " 2>&1";
FILE *pipe = popen(full_cmd.c_str(), "r"); FILE *pipe = popen(full_cmd.c_str(), "r");
if (!pipe) { if (!pipe) {
if (!error_msg.empty())
std::cerr << "Error: " << error_msg << std::endl;
return false; return false;
} }
char buffer[128]; char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
output += buffer; output += buffer;
} }
pclose(pipe); int status = pclose(pipe);
return true; return (status == 0);
} }
std::string sCommand::construct_safecmd() const
{
std::string to_encode;
for (const auto& env_var : mVars) {
to_encode += env_var.first + "=" + quote(dequote(trim(env_var.second))) + " ";
}
to_encode += mCmd;
std::string encoded = base64_encode(to_encode);
std::string commandstr = "echo " + encoded + " | base64 -d | bash";
if (!mDir.empty())
commandstr = "cd " + quote(mDir) + " && " + commandstr;
return commandstr;
}
// base64 <<< "FOO=BAR WHEE=YAY bash ./test.sh"
// echo YmFzaCAtYyAnRk9PPUJBUiBXSEVFPVlBWSBiYXNoIC4vdGVzdC5zaCcK | base64 -d | bash
} // namespace dropshell } // namespace dropshell

View File

@ -11,6 +11,29 @@
#include "utils/envmanager.hpp" #include "utils/envmanager.hpp"
namespace dropshell { namespace dropshell {
class sCommand {
public:
sCommand(std::string directory_to_run_in, 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) {}
sCommand(std::string command_to_run) :
mDir(""), mCmd(command_to_run), mVars({}) {}
std::string get_directory_to_run_in() const { return mDir; }
std::string get_command_to_run() const { return mCmd; }
const std::map<std::string, std::string>& get_env_vars() const { return mVars; }
void add_env_var(const std::string& key, const std::string& value) { mVars[key] = value; }
std::string construct_safecmd() const;
private:
std::string mDir;
std::string mCmd;
std::map<std::string, std::string> mVars;
};
// reads path / server.env and provides a class to access the variables. // reads path / server.env and provides a class to access the variables.
// each env file is required to have the following variables: // each env file is required to have the following variables:
// SSH_HOST // SSH_HOST
@ -22,34 +45,33 @@ class server_env {
public: public:
server_env(const std::string& server_name); server_env(const std::string& server_name);
std::string get_variable(const std::string& name) const; std::string get_variable(const std::string& name) const;
const std::map<std::string, std::string>& get_variables() const;
std::string get_SSH_HOST() const; // trivial getters.
std::string get_SSH_USER() const; const std::map<std::string, std::string>& get_variables() const { return variables; }
std::string get_SSH_PORT() const; std::string get_SSH_HOST() const { return get_variable("SSH_HOST"); }
std::string get_SSH_USER() const { return get_variable("SSH_USER"); }
std::string get_DROPSHELL_DIR() const; std::string get_SSH_PORT() const { return get_variable("SSH_PORT"); }
std::string get_DROPSHELL_DIR() const { return get_variable("DROPSHELL_DIR"); }
bool is_valid() const; bool is_valid() const { return mValid; }
// helper functions // helper functions
public: public:
bool check_remote_items_exist(const std::vector<std::string>& file_paths) const;
bool run_remote_template_command(const std::string& service_name, const std::string& command, std::vector<std::string> args, const std::string& error_msg="") const;
bool execute_ssh_command(const std::string& command, const std::string& error_msg="") const;
bool execute_ssh_command_and_capture_output(const std::string& command, std::string & output, const std::string& error_msg="") const;
static bool execute_local_command(const std::string& command, const std::string& error_msg="");
static bool execute_local_command_and_capture_output(const std::string& command, std::string & output, const std::string& error_msg="");
bool check_remote_dir_exists(const std::string &dir_path) const; bool check_remote_dir_exists(const std::string &dir_path) const;
bool check_remote_file_exists(const std::string& file_path) const; bool check_remote_file_exists(const std::string& file_path) const;
bool check_remote_items_exist(const std::vector<std::string>& file_paths) const;
bool run_remote_template_command(const std::string& service_name, const std::string& command, std::vector<std::string> args, bool silent=false) const;
public:
bool execute_ssh_command(const sCommand& command) const;
bool execute_ssh_command_and_capture_output(const sCommand& command, std::string & output) const;
static bool execute_local_command(const sCommand& command);
static bool execute_local_command_and_capture_output(const sCommand& command, std::string & output);
private: private:
std::string construct_ssh_cmd() const; std::string construct_ssh_cmd() const;
std::string construct_standard_command_run_cmd(const std::string& service_name, const std::string& command, std::vector<std::string> args) const; std::string construct_standard_command_run_cmd(const std::string& service_name, const std::string& command, std::vector<std::string> args, bool silent) const;
private: private:
std::string mServer_name; std::string mServer_name;

View File

@ -50,14 +50,20 @@ bool service_runner::install() {
return false; return false;
// Create service directory // Create service directory
std::string mkdir_cmd = "'mkdir -p " + quote(mRemote_service_path) + "'"; std::string mkdir_cmd = "mkdir -p " + quote(mRemote_service_path);
if (!m_server_env.execute_ssh_command(mkdir_cmd, "Failed to create service directory")) if (!m_server_env.execute_ssh_command(mkdir_cmd))
{
std::cerr << "Failed to create service directory" << 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'"; std::string check_rsync_cmd = "which rsync > /dev/null 2>&1";
if (!m_server_env.execute_ssh_command(check_rsync_cmd, "rsync is not installed on the remote host")) if (!m_server_env.execute_ssh_command(check_rsync_cmd))
{
std::cerr << "rsync is not installed on the remote host" << std::endl;
return false; return false;
}
// Copy template files // Copy template files
{ {
@ -67,7 +73,12 @@ bool service_runner::install() {
m_server_env.get_SSH_USER() + "@" + m_server_env.get_SSH_HOST() + ":" + m_server_env.get_SSH_USER() + "@" + m_server_env.get_SSH_HOST() + ":" +
quote(mRemote_service_template_path+"/"); quote(mRemote_service_template_path+"/");
//std::cout << std::endl << rsync_cmd << std::endl << std::endl; //std::cout << std::endl << rsync_cmd << std::endl << std::endl;
m_server_env.execute_local_command(rsync_cmd,"Failed to copy template files"); if (!m_server_env.execute_local_command(rsync_cmd))
{
std::cerr << "Failed to copy template files using rsync" << std::endl;
std::cerr << "Is rsync installed on the remote host?" << std::endl;
return false;
}
} }
// Copy service files (including service.env) // Copy service files (including service.env)
@ -82,7 +93,11 @@ bool service_runner::install() {
quote(local_service_path + "/") + " "+ quote(local_service_path + "/") + " "+
m_server_env.get_SSH_USER() + "@" + m_server_env.get_SSH_HOST() + ":" + m_server_env.get_SSH_USER() + "@" + m_server_env.get_SSH_HOST() + ":" +
quote(mRemote_service_config_path + "/"); quote(mRemote_service_config_path + "/");
m_server_env.execute_local_command(rsync_cmd,"Failed to copy service files"); if (!m_server_env.execute_local_command(rsync_cmd))
{
std::cerr << "Failed to copy service files using rsync" << std::endl;
return false;
}
} }
// Run install script // Run install script
@ -124,7 +139,8 @@ bool service_runner::uninstall() {
// 4. Remove the service directory from the server // 4. Remove the service directory from the server
std::string rm_cmd = "'rm -rf " + quote(mRemote_service_path) + "'"; std::string rm_cmd = "'rm -rf " + quote(mRemote_service_path) + "'";
if (!m_server_env.execute_ssh_command(rm_cmd, "Failed to remove service directory")) { if (!m_server_env.execute_ssh_command(rm_cmd)) {
std::cerr << "Failed to remove service directory" << std::endl;
return false; return false;
} }

View File

@ -33,7 +33,7 @@ bool get_templates(std::vector<template_info>& templates) {
auto it = std::find_if(templates.begin(), templates.end(), auto it = std::find_if(templates.begin(), templates.end(),
[&info](const template_info& t) { return t.template_name == info.template_name; }); [&info](const template_info& t) { return t.template_name == info.template_name; });
duplicate = (it!=templates.end()); duplicate = (it!=templates.end());
duplicate |= (info.template_name=="example"); // don't include the example template! //duplicate |= (info.template_name=="example"); // don't include the example template!
if (!duplicate) { if (!duplicate) {
templates.push_back(info); templates.push_back(info);