Workign on stuff
This commit is contained in:
parent
e3e8e5f7d3
commit
8c21260e49
@ -28,7 +28,6 @@ configure_file(
|
||||
)
|
||||
|
||||
# Find required packages
|
||||
find_package(Curses REQUIRED)
|
||||
find_package(TBB REQUIRED)
|
||||
|
||||
# Auto-detect source files
|
||||
@ -42,12 +41,10 @@ add_executable(dropshell ${SOURCES})
|
||||
target_include_directories(dropshell PRIVATE
|
||||
src
|
||||
${CMAKE_CURRENT_BINARY_DIR}/src
|
||||
${CURSES_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
# Link libraries
|
||||
target_link_libraries(dropshell PRIVATE
|
||||
${CURSES_LIBRARIES}
|
||||
TBB::tbb
|
||||
)
|
||||
|
||||
|
282
src/contrib/base64.cpp
Normal file
282
src/contrib/base64.cpp
Normal 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
35
src/contrib/base64.hpp
Normal 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 */
|
@ -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);
|
||||
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) + "'";
|
||||
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;
|
||||
}
|
||||
|
||||
@ -235,12 +236,8 @@ int backup(const std::vector<std::string> & args, bool silent) {
|
||||
ASSERT(3 == count_substring(magic_string, local_backup_file_path));
|
||||
|
||||
// Run backup script
|
||||
std::string backup_cmd = "'cd " + quote(remote_service_template_path) +
|
||||
" && /bin/bash "+quote(remote_command_script_file)+" "+quote(remote_service_config_path)+" "+
|
||||
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;
|
||||
if (!env.run_remote_template_command(service_name, command, {remote_backup_file_path}, silent)) {
|
||||
std::cerr << "Backup script failed on remote server: " << remote_backup_file_path << std::endl;
|
||||
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() + " " +
|
||||
env.get_SSH_USER() + "@" + env.get_SSH_HOST() + ":" +
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -3,15 +3,13 @@
|
||||
#include "utils/directories.hpp"
|
||||
#include "utils/utils.hpp"
|
||||
#include "services.hpp"
|
||||
#include "contrib/base64.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
namespace dropshell {
|
||||
|
||||
bool server_env::is_valid() const {
|
||||
return mValid;
|
||||
}
|
||||
namespace dropshell {
|
||||
|
||||
server_env::server_env(const std::string& server_name) : mValid(false), mServer_name(server_name) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
std::string server_env::construct_ssh_cmd() const {
|
||||
std::stringstream ssh_cmd;
|
||||
@ -91,34 +68,38 @@ std::string server_env::construct_ssh_cmd() const {
|
||||
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_config_path = get_remote_service_config_path(mServer_name,service_name);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) + "'";
|
||||
if (system(check_dir_cmd.c_str()) != 0) {
|
||||
std::cerr << "Error: Directory not found on remote server:" << std::filesystem::path(dir_path).filename().string() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
sCommand scommand("test -d " + quote(dir_path));
|
||||
return execute_ssh_command(scommand);
|
||||
}
|
||||
|
||||
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) + "'";
|
||||
if (system(check_cmd.c_str()) != 0) {
|
||||
std::cerr << "Error: File not found on remote server: " << std::filesystem::path(file_path).filename().string() << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
sCommand scommand("test -f " + quote(file_path));
|
||||
return execute_ssh_command(scommand);
|
||||
}
|
||||
|
||||
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() + " ";
|
||||
}
|
||||
// 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'";
|
||||
if (system(check_cmd.c_str()) != 0) {
|
||||
sCommand scommand("for item in " + file_paths_str + "; do test -f $item; done");
|
||||
|
||||
bool okay = execute_ssh_command(scommand);
|
||||
if (!okay) {
|
||||
std::cerr << "Error: Required items not found on remote server: " << file_names_str << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool server_env::execute_ssh_command(const std::string& command, const std::string& error_msg) const {
|
||||
std::string full_cmd = construct_ssh_cmd() + command;
|
||||
return execute_local_command(full_cmd, error_msg);
|
||||
bool server_env::execute_ssh_command(const sCommand& command) const {
|
||||
std::string full_cmd = construct_ssh_cmd() + command.construct_safecmd();
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
return execute_ssh_command(full_cmd, error_msg);
|
||||
std::string full_cmd = construct_standard_command_run_cmd(service_name, command, args, silent);
|
||||
return execute_ssh_command(full_cmd);
|
||||
}
|
||||
|
||||
bool server_env::execute_local_command(const std::string& command, const std::string& error_msg) {
|
||||
bool okay = (system(command.c_str()) == 0);
|
||||
|
||||
if (!okay && !error_msg.empty())
|
||||
std::cerr << "Error: " << error_msg << std::endl;
|
||||
bool server_env::execute_local_command(const sCommand& command) {
|
||||
bool okay = (system(command.construct_safecmd().c_str()) == 0);
|
||||
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");
|
||||
if (!pipe) {
|
||||
if (!error_msg.empty())
|
||||
std::cerr << "Error: " << error_msg << std::endl;
|
||||
return false;
|
||||
}
|
||||
char buffer[128];
|
||||
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||
output += buffer;
|
||||
}
|
||||
pclose(pipe);
|
||||
return true;
|
||||
int status = pclose(pipe);
|
||||
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
|
@ -11,6 +11,29 @@
|
||||
#include "utils/envmanager.hpp"
|
||||
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.
|
||||
// each env file is required to have the following variables:
|
||||
// SSH_HOST
|
||||
@ -22,34 +45,33 @@ class server_env {
|
||||
public:
|
||||
server_env(const std::string& server_name);
|
||||
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;
|
||||
std::string get_SSH_USER() const;
|
||||
std::string get_SSH_PORT() const;
|
||||
|
||||
std::string get_DROPSHELL_DIR() const;
|
||||
|
||||
bool is_valid() const;
|
||||
// trivial getters.
|
||||
const std::map<std::string, std::string>& get_variables() const { return variables; }
|
||||
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_SSH_PORT() const { return get_variable("SSH_PORT"); }
|
||||
std::string get_DROPSHELL_DIR() const { return get_variable("DROPSHELL_DIR"); }
|
||||
bool is_valid() const { return mValid; }
|
||||
|
||||
// helper functions
|
||||
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_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:
|
||||
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:
|
||||
std::string mServer_name;
|
||||
|
@ -50,14 +50,20 @@ bool service_runner::install() {
|
||||
return false;
|
||||
|
||||
// Create service directory
|
||||
std::string mkdir_cmd = "'mkdir -p " + quote(mRemote_service_path) + "'";
|
||||
if (!m_server_env.execute_ssh_command(mkdir_cmd, "Failed to create service directory"))
|
||||
std::string mkdir_cmd = "mkdir -p " + quote(mRemote_service_path);
|
||||
if (!m_server_env.execute_ssh_command(mkdir_cmd))
|
||||
{
|
||||
std::cerr << "Failed to create service directory" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if rsync is installed on remote host
|
||||
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"))
|
||||
std::string check_rsync_cmd = "which rsync > /dev/null 2>&1";
|
||||
if (!m_server_env.execute_ssh_command(check_rsync_cmd))
|
||||
{
|
||||
std::cerr << "rsync is not installed on the remote host" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy template files
|
||||
{
|
||||
@ -67,7 +73,12 @@ bool service_runner::install() {
|
||||
m_server_env.get_SSH_USER() + "@" + m_server_env.get_SSH_HOST() + ":" +
|
||||
quote(mRemote_service_template_path+"/");
|
||||
//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)
|
||||
@ -82,7 +93,11 @@ bool service_runner::install() {
|
||||
quote(local_service_path + "/") + " "+
|
||||
m_server_env.get_SSH_USER() + "@" + m_server_env.get_SSH_HOST() + ":" +
|
||||
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
|
||||
@ -124,7 +139,8 @@ bool service_runner::uninstall() {
|
||||
|
||||
// 4. Remove the service directory from the server
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,7 @@ bool get_templates(std::vector<template_info>& templates) {
|
||||
auto it = std::find_if(templates.begin(), templates.end(),
|
||||
[&info](const template_info& t) { return t.template_name == info.template_name; });
|
||||
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) {
|
||||
templates.push_back(info);
|
||||
|
Loading…
x
Reference in New Issue
Block a user