Compare commits

...

10 Commits

Author SHA1 Message Date
fc6b310b89 dropshell release 2025.0521.1908
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-21 19:08:48 +12:00
7a710b525f dropshell release 2025.0521.1906
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-21 19:06:50 +12:00
1b16741288 .
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-19 23:09:43 +12:00
d8236a58df dropshell release 2025.0519.1727
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-19 17:28:01 +12:00
625de98890 Fix generated files in build, so they don't have to exist when the build starts.
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-19 11:24:42 +12:00
7c1b51a93c Don't include autogen in commits
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-19 11:19:47 +12:00
1be7af571f Remove autogen files. 2025-05-19 11:18:36 +12:00
1439ec2f79 Add dehydrate to build prereq's.
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-19 11:16:57 +12:00
6c3c35bf89 dropshell release 2025.0519.0021
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-19 00:21:34 +12:00
548ffea6f9 dropshell release 2025.0518.2308
Some checks failed
Dropshell Test / Build_and_Test (push) Has been cancelled
2025-05-18 23:08:44 +12:00
25 changed files with 530 additions and 640 deletions

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ out/
bin/
lib/
output/
autogen/
# Compiled Object files
*.o

View File

@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.10)
project(dropshell VERSION 1.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_C_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Set default build type to Release if not specified
@ -48,6 +49,19 @@ add_custom_target(run_createagent ALL
add_executable(dropshell ${SOURCES})
add_dependencies(dropshell run_createagent)
# Mark the generated files as GENERATED so CMake knows they'll be created during build
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/src/autogen/_agent-remote.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/autogen/_agent-local.cpp
PROPERTIES GENERATED TRUE
)
# Explicitly add the generated agent files, as they might not be in the source directory when globbed at the start.
target_sources(dropshell PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src/autogen/_agent-remote.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/autogen/_agent-local.cpp
)
# Set include directories
# build dir goes first so that we can use the generated version.hpp
target_include_directories(dropshell PRIVATE

View File

@ -0,0 +1,83 @@
#!/bin/bash
SCRIPT_DIR=$(dirname "$0")
echo "Installing dropshell host agent on this computer..."
# Prints an error message in red and exits with status code 1.
_die() {
echo -e "Error: $1"
exit 1
}
# Checks if listed environment variables are set; calls _die() if any are missing.
_check_required_env_vars() {
local required_vars=("$@")
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
_die "Required environment variable $var is not set"
fi
done
}
# Checks if Docker is installed, running, and user has permission. Returns 1 on failure.
_check_docker_installed() {
if ! command -v docker &> /dev/null; then
echo "Docker is not installed"
return 1
fi
# check if docker daemon is running
if ! docker info &> /dev/null; then
echo "Docker daemon is not running"
return 1
fi
# check if user has permission to run docker
if ! docker run --rm hello-world &> /dev/null; then
echo "User does not have permission to run docker"
return 1
fi
return 0
}
function install_bb64() {
# check curl installed
if ! command -v curl &> /dev/null; then
_die "Curl is not installed. Curl is required for agent installation."
fi
curl -fsSL "https://gitea.jde.nz/public/bb64/releases/download/latest/install.sh" | bash -s -- "$AGENT_PATH" "$(id -u $USER):$(id -g $USER)"
# test result code from curl
if [ $? -ne 0 ]; then
_die "Failed to install bb64. Curl returned non-zero exit code."
fi
# test if bb64 is installed
"$AGENT_PATH/bb64" -v
if [ $? -ne 0 ]; then
_die "bb64 did not install correctly."
fi
echo "bb64 installed successfully."
return 0;
}
#-------------------------------------------------------------------------
set -a
AGENT_PATH="$SCRIPT_DIR"
set +a
_check_required_env_vars "AGENT_PATH"
echo "Installing host agent into $AGENT_PATH"
_check_docker_installed || _die "Docker is required."
install_bb64

View File

@ -0,0 +1,87 @@
#!/bin/bash
# This script is used to install the dropshell agent on a remote server.
SCRIPT_DIR=$(dirname "$0")
set -a
AGENT_PATH="$SCRIPT_DIR"
set +a
if [ -f "$SCRIPT_DIR/common.sh" ]; then
source "$SCRIPT_DIR/common.sh"
else
echo "Error: common.sh not found in $SCRIPT_DIR"
exit 1
fi
_check_required_env_vars "AGENT_PATH"
function install_bb64() {
curl -fsSL "https://gitea.jde.nz/public/bb64/releases/download/latest/install.sh" | bash -s -- "$AGENT_PATH" "$(id -u $USER):$(id -g $USER)"
# test result code from curl
if [ $? -ne 0 ]; then
_die "Failed to install bb64. Curl returned non-zero exit code."
fi
# test if bb64 is installed
VER=$("$AGENT_PATH/bb64" -v)
if [ $? -ne 0 ]; then
_die "bb64 did not install correctly."
fi
echo "bb64 v$VER installed."
return 0;
}
#-------------------------------------------------------------------------
# Check pre-requisites
# Check if curl is installed
if ! command -v curl &> /dev/null; then
_die "Curl is not installed. Curl is required for agent installation."
fi
# check docker installation
if ! command -v docker &> /dev/null; then
echo "Docker is not installed. Docker is required for agent installation."
exit 1
fi
#-------------------------------------------------------------------------
echo "Installing dropshell agent..."
install_bb64
#-------------------------------------------------------------------------
# confirm we're in a good state.
required_files=(
"$AGENT_PATH/bb64"
"$AGENT_PATH/_allservicesstatus.sh"
"$AGENT_PATH/common.sh"
"$AGENT_PATH/datacommands.sh"
)
# check if all files exist
for file in "${required_files[@]}"; do
if [ ! -f "$file" ]; then
_die "$file does not exist. Please check the installation."
fi
done
#-------------------------------------------------------------------------
echo "Completed dropshell agent installation."
#-------------------------------------------------------------------------
exit 0

View File

@ -12,6 +12,10 @@ _autocommandrun_volume() {
case "$command" in
create)
if docker volume ls | grep -q ${volume_name}; then
echo "Volume ${volume_name} already exists - leaving unchanged"
return
fi
echo "Creating volume ${volume_name}"
docker volume create ${volume_name}
;;
@ -39,6 +43,10 @@ _autocommandrun_path() {
case "$command" in
create)
if [ -d "${path}" ]; then
echo "Path ${path} already exists - unchanged"
return
fi
echo "Creating path ${path}"
mkdir -p ${path}
;;
@ -47,7 +55,7 @@ _autocommandrun_path() {
local path_parent=$(dirname ${path})
local path_child=$(basename ${path})
if [ -d "${path_parent}/${path_child}" ]; then
docker run --rm -v ${path_parent}:/volume debian bash -c "rm -rf /volume/${path_child}" || echo "Failed to nuke path ${path}"
docker run --rm -v ${path_parent}:/volume debian bash -c "rm -rfv /volume/${path_child}" || echo "Failed to nuke path ${path}"
else
echo "Path ${path} does not exist - nothing to nuke"
fi
@ -61,8 +69,14 @@ _autocommandrun_path() {
fi
;;
restore)
if [ ! -f "${backup_folder}/backup.tgz" ]; then
echo "Backup file ${backup_folder}/backup.tgz does not exist - nothing to restore"
else
echo "Clearing existing data in path ${path}"
docker run --rm -v ${path}:/path debian bash -c "rm -rfv /path/{*,.*}"
echo "Restoring path ${path} from backup file ${backup_folder}/backup.tgz"
tar -xzvf ${backup_folder}/backup.tgz -C ${path} --strip-components=1
fi
;;
esac
}
@ -74,6 +88,12 @@ _autocommandrun_file() {
case "$command" in
create)
filepath_parent=$(dirname ${filepath})
filepath_child=$(basename ${filepath})
if [ ! -d "${filepath_parent}" ]; then
echo "Parent directory ${filepath_parent} of ${filepath_child} does not exist - creating"
mkdir -p ${filepath_parent}
fi
;;
nuke)
rm -f ${filepath}
@ -91,7 +111,8 @@ _autocommandrun_file() {
restore)
echo "Restoring file ${filepath}"
local file_name=$(basename ${filepath})
cp ${backup_folder}/${file_name} ${filepath}
rm -f ${filepath} || die "Unable to remove existing file ${filepath}, restore failed."
cp ${backup_folder}/${file_name} ${filepath} || die "Unable to copy file ${backup_folder}/${file_name} to ${filepath}, restore failed."
;;
esac
}

View File

@ -1,7 +0,0 @@
#!/bin/bash
echo "Running remote agent self-test..."
echo "Completed remote agent self-test."
exit 0

View File

@ -142,8 +142,12 @@ if [ -n "$SUDO_USER" ] && [ "$SUDO_USER" != "root" ]; then
fi
fi
# check if dehydrate command is installed
if ! command -v dehydrate &> /dev/null; then
curl -fsSL https://gitea.jde.nz/public/dehydrate/releases/download/latest/install.sh | bash
fi
dehydrate -u
print_status "All dependencies installed successfully!"

View File

@ -14,6 +14,7 @@ if ! command -v dehydrate &> /dev/null; then
curl -fsSL https://gitea.jde.nz/public/dehydrate/releases/download/latest/install.sh | bash
fi
SCRIPT_DIR=$(dirname "$0")
mkdir -p "${SCRIPT_DIR}/src/autogen"
dehydrate "${SCRIPT_DIR}/agent" "${SCRIPT_DIR}/src/autogen"
dehydrate "${SCRIPT_DIR}/agent-remote" "${SCRIPT_DIR}/src/autogen"
dehydrate "${SCRIPT_DIR}/agent-local" "${SCRIPT_DIR}/src/autogen"

View File

@ -1,443 +0,0 @@
#include <fstream>
#include <filesystem>
#include <string>
#include <iostream>
#include <cstring>
/*
THIS FILE IS AUTO-GENERATED BY DEHYDRATE.
DO NOT EDIT THIS FILE.
*/
#include "_agent.hpp"
namespace recreate_agent {
// Tiny dependency-free FNV-1a 64-bit hash
static uint64_t fnv1a_64(const void* data, size_t len) {
const uint8_t* p = static_cast<const uint8_t*>(data);
uint64_t h = 0xcbf29ce484222325ULL;
for (size_t i = 0; i < len; ++i)
h = (h ^ p[i]) * 0x100000001b3ULL;
return h;
}
// Base64 decoding function - no dependencies
static void base64_decode(const char* encoded_data, size_t encoded_len, unsigned char* output, size_t* output_len) {
const char* base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t out_pos = 0;
int val = 0, valb = -8;
for (size_t i = 0; i < encoded_len; i++) {
char c = encoded_data[i];
if (c == '=') break;
// Find position in base64_chars
const char* pos = strchr(base64_chars, c);
if (pos == nullptr) continue; // Skip invalid characters
val = (val << 6) + static_cast<int>(pos - base64_chars);
valb += 6;
if (valb >= 0) {
output[out_pos++] = static_cast<unsigned char>((val >> valb) & 0xFF);
valb -= 8;
}
}
*output_len = out_pos;
}
// Utility function to recreate a file with proper permissions
static bool _recreate_file_(const std::filesystem::path& outpath, uint64_t file_hash, std::filesystem::perms file_perms, const unsigned char* filedata, size_t filedata_len) {
namespace fs = std::filesystem;
bool needs_write = false;
// Check if file exists and has correct hash
if (fs::exists(outpath)) {
// Check content hash
std::ifstream in(outpath, std::ios::binary);
std::ostringstream oss;
oss << in.rdbuf();
std::string data = oss.str();
uint64_t existing_hash = fnv1a_64(data.data(), data.size());
needs_write = existing_hash != file_hash;
} else {
needs_write = true; // File doesn't exist, need to create it
}
bool needs_permission_update = true;
if (!needs_write) { // we always update permissions if the file is written or changed. Othewise we check.
fs::perms current_perms = fs::status(outpath).permissions();
needs_permission_update = current_perms != file_perms;
}
if (needs_write) {
fs::create_directories(outpath.parent_path());
std::ofstream out(outpath, std::ios::binary);
out.write(reinterpret_cast<const char*>(filedata), filedata_len);
out.close();
// Set the file permissions
fs::permissions(outpath, file_perms);
if (!fs::exists(outpath)) {
std::cout << "[dehydrate] " << outpath.filename() << ": created\n";
} else {
std::cout << "[dehydrate] " << outpath.filename() << ": updated (hash changed)\n";
}
return true;
}
if (needs_permission_update) {
// Update only permissions
fs::permissions(outpath, file_perms);
std::cout << "[dehydrate] " << outpath.filename() << ": updated (permissions changed)\n";
return true;
}
return false;
}
bool recreate_tree(std::string destination_folder) {
namespace fs = std::filesystem;
bool any_written = false;
{
// File: selftest.sh
fs::path outpath = fs::path(destination_folder) / "selftest.sh";
static const char filedata_base64[] = "IyEvYmluL2Jhc2gKCmVjaG8gIlJ1bm5pbmcgcmVtb3RlIGFnZW50IHNlbGYtdGVzdC4uLiIKCmVj"\
"aG8gIkNvbXBsZXRlZCByZW1vdGUgYWdlbnQgc2VsZi10ZXN0LiIKCmV4aXQgMAo=";
// Decode Base64 data
size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
unsigned char* decoded_data = new unsigned char[decoded_size];
size_t actual_size;
base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);
bool file_written = _recreate_file_(outpath, 11594895391899191874ULL, std::filesystem::perms(493), decoded_data, actual_size);
delete[] decoded_data;
any_written = any_written || file_written;
}
{
// File: datacommands.sh
fs::path outpath = fs::path(destination_folder) / "datacommands.sh";
static const char filedata_base64[] = "IyEvYmluL2Jhc2gKCiMgVGhpcyBzY3JpcHQgY29udGFpbnMgdGhlIGNvbW1vbiBjb2RlIGZvciB0"\
"aGUgYXV0b2NvbW1hbmRzLgoKTVlJRD0kKGlkIC11KQpNWUdSUD0kKGlkIC1nKQoKX2F1dG9jb21t"\
"YW5kcnVuX3ZvbHVtZSgpIHsKICAgIGxvY2FsIGNvbW1hbmQ9IiQxIgogICAgbG9jYWwgdm9sdW1l"\
"X25hbWU9IiQyIgogICAgbG9jYWwgYmFja3VwX2ZvbGRlcj0iJDMiCgogICAgY2FzZSAiJGNvbW1h"\
"bmQiIGluCiAgICAgICAgY3JlYXRlKQogICAgICAgICAgICBlY2hvICJDcmVhdGluZyB2b2x1bWUg"\
"JHt2b2x1bWVfbmFtZX0iCiAgICAgICAgICAgIGRvY2tlciB2b2x1bWUgY3JlYXRlICR7dm9sdW1l"\
"X25hbWV9CiAgICAgICAgICAgIDs7CiAgICAgICAgbnVrZSkKICAgICAgICAgICAgZWNobyAiTnVr"\
"aW5nIHZvbHVtZSAke3ZvbHVtZV9uYW1lfSIKICAgICAgICAgICAgZG9ja2VyIHZvbHVtZSBybSAk"\
"e3ZvbHVtZV9uYW1lfQogICAgICAgICAgICA7OwogICAgICAgIGJhY2t1cCkKICAgICAgICAgICAg"\
"ZWNobyAiQmFja2luZyB1cCB2b2x1bWUgJHt2b2x1bWVfbmFtZX0iCiAgICAgICAgICAgIGRvY2tl"\
"ciBydW4gLS1ybSAtdiAke3ZvbHVtZV9uYW1lfTovdm9sdW1lIC12ICR7YmFja3VwX2ZvbGRlcn06"\
"L2JhY2t1cCBkZWJpYW4gYmFzaCAtYyAidGFyIC1jenZmIC9iYWNrdXAvYmFja3VwLnRneiAtQyAv"\
"dm9sdW1lIC4gJiYgY2hvd24gLVIgJE1ZSUQ6JE1ZR1JQIC9iYWNrdXAiCiAgICAgICAgICAgIDs7"\
"CiAgICAgICAgcmVzdG9yZSkKICAgICAgICAgICAgZWNobyAiUmVzdG9yaW5nIHZvbHVtZSAke3Zv"\
"bHVtZV9uYW1lfSIKICAgICAgICAgICAgZG9ja2VyIHZvbHVtZSBybSAke3ZvbHVtZV9uYW1lfQog"\
"ICAgICAgICAgICBkb2NrZXIgdm9sdW1lIGNyZWF0ZSAke3ZvbHVtZV9uYW1lfQogICAgICAgICAg"\
"ICBkb2NrZXIgcnVuIC0tcm0gLXYgJHt2b2x1bWVfbmFtZX06L3ZvbHVtZSAtdiAke2JhY2t1cF9m"\
"b2xkZXJ9Oi9iYWNrdXAgZGViaWFuIGJhc2ggLWMgInRhciAteHp2ZiAvYmFja3VwL2JhY2t1cC50"\
"Z3ogLUMgL3ZvbHVtZSAtLXN0cmlwLWNvbXBvbmVudHM9MSIKICAgICAgICAgICAgOzsKICAgIGVz"\
"YWMKfSAgIAoKX2F1dG9jb21tYW5kcnVuX3BhdGgoKSB7CiAgICBsb2NhbCBjb21tYW5kPSIkMSIK"\
"ICAgIGxvY2FsIHBhdGg9IiQyIgogICAgbG9jYWwgYmFja3VwX2ZvbGRlcj0iJDMiCgogICAgY2Fz"\
"ZSAiJGNvbW1hbmQiIGluCiAgICAgICAgY3JlYXRlKQogICAgICAgICAgICBlY2hvICJDcmVhdGlu"\
"ZyBwYXRoICR7cGF0aH0iCiAgICAgICAgICAgIG1rZGlyIC1wICR7cGF0aH0KICAgICAgICAgICAg"\
"OzsKICAgICAgICBudWtlKQogICAgICAgICAgICBlY2hvICJOdWtpbmcgcGF0aCAke3BhdGh9Igog"\
"ICAgICAgICAgICBsb2NhbCBwYXRoX3BhcmVudD0kKGRpcm5hbWUgJHtwYXRofSkKICAgICAgICAg"\
"ICAgbG9jYWwgcGF0aF9jaGlsZD0kKGJhc2VuYW1lICR7cGF0aH0pCiAgICAgICAgICAgIGlmIFsg"\
"LWQgIiR7cGF0aF9wYXJlbnR9LyR7cGF0aF9jaGlsZH0iIF07IHRoZW4KICAgICAgICAgICAgICAg"\
"IGRvY2tlciBydW4gLS1ybSAtdiAke3BhdGhfcGFyZW50fTovdm9sdW1lIGRlYmlhbiBiYXNoIC1j"\
"ICJybSAtcmYgL3ZvbHVtZS8ke3BhdGhfY2hpbGR9IiB8fCBlY2hvICJGYWlsZWQgdG8gbnVrZSBw"\
"YXRoICR7cGF0aH0iCiAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgICAgIGVjaG8gIlBhdGgg"\
"JHtwYXRofSBkb2VzIG5vdCBleGlzdCAtIG5vdGhpbmcgdG8gbnVrZSIKICAgICAgICAgICAgZmkK"\
"ICAgICAgICAgICAgOzsKICAgICAgICBiYWNrdXApCiAgICAgICAgICAgIGVjaG8gIkJhY2tpbmcg"\
"dXAgcGF0aCAke3BhdGh9IgogICAgICAgICAgICBpZiBbIC1kICIke3BhdGh9IiBdOyB0aGVuCiAg"\
"ICAgICAgICAgICAgICBkb2NrZXIgcnVuIC0tcm0gLXYgJHtwYXRofTovcGF0aCAtdiAke2JhY2t1"\
"cF9mb2xkZXJ9Oi9iYWNrdXAgZGViaWFuIGJhc2ggLWMgInRhciAtY3p2ZiAvYmFja3VwL2JhY2t1"\
"cC50Z3ogLUMgL3BhdGggLiAmJiBjaG93biAtUiAkTVlJRDokTVlHUlAgL2JhY2t1cCIKICAgICAg"\
"ICAgICAgZWxzZQogICAgICAgICAgICAgICAgZWNobyAiUGF0aCAke3BhdGh9IGRvZXMgbm90IGV4"\
"aXN0IC0gbm90aGluZyB0byBiYWNrdXAiCiAgICAgICAgICAgIGZpCiAgICAgICAgICAgIDs7CiAg"\
"ICAgICAgcmVzdG9yZSkKICAgICAgICAgICAgZWNobyAiUmVzdG9yaW5nIHBhdGggJHtwYXRofSBm"\
"cm9tIGJhY2t1cCBmaWxlICR7YmFja3VwX2ZvbGRlcn0vYmFja3VwLnRneiIKICAgICAgICAgICAg"\
"dGFyIC14enZmICR7YmFja3VwX2ZvbGRlcn0vYmFja3VwLnRneiAtQyAke3BhdGh9IC0tc3RyaXAt"\
"Y29tcG9uZW50cz0xCiAgICAgICAgICAgIDs7CiAgICBlc2FjCn0KCl9hdXRvY29tbWFuZHJ1bl9m"\
"aWxlKCkgewogICAgbG9jYWwgY29tbWFuZD0iJDEiCiAgICBsb2NhbCBmaWxlcGF0aD0iJDIiCiAg"\
"ICBsb2NhbCBiYWNrdXBfZm9sZGVyPSIkMyIKCiAgICBjYXNlICIkY29tbWFuZCIgaW4KICAgICAg"\
"ICBjcmVhdGUpCiAgICAgICAgICAgIDs7CiAgICAgICAgbnVrZSkKICAgICAgICAgICAgcm0gLWYg"\
"JHtmaWxlcGF0aH0KICAgICAgICAgICAgOzsKICAgICAgICBiYWNrdXApCiAgICAgICAgICAgIGVj"\
"aG8gIkJhY2tpbmcgdXAgZmlsZSAke2ZpbGVwYXRofSIKICAgICAgICAgICAgbG9jYWwgZmlsZV9w"\
"YXJlbnQ9JChkaXJuYW1lICR7ZmlsZXBhdGh9KQogICAgICAgICAgICBsb2NhbCBmaWxlX25hbWU9"\
"JChiYXNlbmFtZSAke2ZpbGVwYXRofSkKICAgICAgICAgICAgaWYgWyAtZiAiJHtmaWxlX3BhcmVu"\
"dH0vJHtmaWxlX25hbWV9IiBdOyB0aGVuCiAgICAgICAgICAgICAgICBkb2NrZXIgcnVuIC0tcm0g"\
"LXYgJHtmaWxlX3BhcmVudH06L3ZvbHVtZSAtdiAke2JhY2t1cF9mb2xkZXJ9Oi9iYWNrdXAgZGVi"\
"aWFuIGJhc2ggLWMgImNwIC92b2x1bWUvJHtmaWxlX25hbWV9IC9iYWNrdXAvJHtmaWxlX25hbWV9"\
"ICYmIGNob3duIC1SICRNWUlEOiRNWUdSUCAvYmFja3VwIgogICAgICAgICAgICBlbHNlCiAgICAg"\
"ICAgICAgICAgICBlY2hvICJGaWxlICR7ZmlsZXBhdGh9IGRvZXMgbm90IGV4aXN0IC0gbm90aGlu"\
"ZyB0byBiYWNrdXAiCiAgICAgICAgICAgIGZpCiAgICAgICAgICAgIDs7CiAgICAgICAgcmVzdG9y"\
"ZSkKICAgICAgICAgICAgZWNobyAiUmVzdG9yaW5nIGZpbGUgJHtmaWxlcGF0aH0iCiAgICAgICAg"\
"ICAgIGxvY2FsIGZpbGVfbmFtZT0kKGJhc2VuYW1lICR7ZmlsZXBhdGh9KQogICAgICAgICAgICBj"\
"cCAke2JhY2t1cF9mb2xkZXJ9LyR7ZmlsZV9uYW1lfSAke2ZpbGVwYXRofQogICAgICAgICAgICA7"\
"OwogICAgZXNhYwp9CgpfYXV0b2NvbW1hbmRwYXJzZSgpIHsKICAgICMgZmlyc3QgYXJndW1lbnQg"\
"aXMgdGhlIGNvbW1hbmQKICAgICMgaWYgdGhlIGNvbW1hbmQgaXMgYmFja3VwIG9yIHJlc3RvcmUs"\
"IHRoZW4gdGhlIGxhc3QgdHdvIGFyZ3VtZW50cyBhcmUgdGhlIGJhY2t1cCBmaWxlIGFuZCB0aGUg"\
"dGVtcG9yYXJ5IHBhdGgKICAgICMgYWxsIG90aGVyIGFyZ3VtZW50cyBhcmUgb2YgZm9ybToKICAg"\
"ICMga2V5PXZhbHVlCiAgICAjIHdoZXJlIGtleSBjYW4gYmUgb25lIG9mIHZvbHVtZSwgcGF0aCBv"\
"ciBmaWxlLgogICAgIyB2YWx1ZSBpcyB0aGUgcGF0aCBvciB2b2x1bWUgbmFtZS4KCiAgICAjIHdl"\
"IGl0ZXJhdGUgb3ZlciB0aGUga2V5PXZhbHVlIGFyZ3VtZW50cywgYW5kIGZvciBlYWNoIHdlIGNh"\
"bGw6CiAgICAjICAgIGF1dG9ydW4gPGNvbW1hbmQ+IDxiYWNrdXBmaWxlPiA8a2V5PiA8dmFsdWU+"\
"CgogICAgbG9jYWwgY29tbWFuZD0iJDEiCiAgICBzaGlmdAoKICAgIGxvY2FsIGJhY2t1cF90ZW1w"\
"X3BhdGg9IiQxIgogICAgc2hpZnQKCiAgICBlY2hvICJhdXRvY29tbWFuZHBhcnNlOiBjb21tYW5k"\
"PSRjb21tYW5kIGJhY2t1cF90ZW1wX3BhdGg9JGJhY2t1cF90ZW1wX3BhdGgiCgogICAgIyBFeHRy"\
"YWN0IHRoZSBiYWNrdXAgZmlsZSBhbmQgdGVtcCBwYXRoIChsYXN0IHR3byBhcmd1bWVudHMpCiAg"\
"ICBsb2NhbCBhcmdzPSgiJEAiKQogICAgbG9jYWwgYXJnX2NvdW50PSR7I2FyZ3NbQF19CiAgICAK"\
"ICAgICMgUHJvY2VzcyBhbGwga2V5PXZhbHVlIHBhaXJzCiAgICBmb3IgKChpPTA7IGk8JGFyZ19j"\
"b3VudDsgaSsrKSk7IGRvCiAgICAgICAgbG9jYWwgcGFpcj0iJHthcmdzWyRpXX0iCiAgICAgICAg"\
"CiAgICAgICAgIyBTa2lwIGlmIG5vdCBpbiBrZXk9dmFsdWUgZm9ybWF0CiAgICAgICAgaWYgW1sg"\
"IiRwYWlyIiAhPSAqIj0iKiBdXTsgdGhlbgogICAgICAgICAgICBjb250aW51ZQogICAgICAgIGZp"\
"CiAgICAgICAgCiAgICAgICAgbG9jYWwga2V5PSIke3BhaXIlJT0qfSIKICAgICAgICBsb2NhbCB2"\
"YWx1ZT0iJHtwYWlyIyo9fSIKCiAgICAgICAgIyBjcmVhdGUgYmFja3VwIGZvbGRlciB1bmlxdWUg"\
"dG8ga2V5L3ZhbHVlLgogICAgICAgIGxvY2FsIGJmb2xkZXI9JChlY2hvICIke2tleX1fJHt2YWx1"\
"ZX0iIHwgdHIgLWNkICdbOmFsbnVtOl1fLScpCiAgICAgICAgbG9jYWwgdGFyZ2V0cGF0aD0iJHti"\
"YWNrdXBfdGVtcF9wYXRofS8ke2Jmb2xkZXJ9IgogICAgICAgIG1rZGlyIC1wICR7dGFyZ2V0cGF0"\
"aH0KCiAgICAgICAgIyBLZXkgbXVzdCBiZSBvbmUgb2Ygdm9sdW1lLCBwYXRoIG9yIGZpbGUKICAg"\
"ICAgICBjYXNlICIka2V5IiBpbgogICAgICAgICAgICB2b2x1bWUpCiAgICAgICAgICAgICAgICBf"\
"YXV0b2NvbW1hbmRydW5fdm9sdW1lICIkY29tbWFuZCIgIiR2YWx1ZSIgIiR0YXJnZXRwYXRoIgog"\
"ICAgICAgICAgICAgICAgOzsKICAgICAgICAgICAgcGF0aCkKICAgICAgICAgICAgICAgIF9hdXRv"\
"Y29tbWFuZHJ1bl9wYXRoICIkY29tbWFuZCIgIiR2YWx1ZSIgIiR0YXJnZXRwYXRoIgogICAgICAg"\
"ICAgICAgICAgOzsKICAgICAgICAgICAgZmlsZSkKICAgICAgICAgICAgICAgIF9hdXRvY29tbWFu"\
"ZHJ1bl9maWxlICIkY29tbWFuZCIgIiR2YWx1ZSIgIiR0YXJnZXRwYXRoIgogICAgICAgICAgICAg"\
"ICAgOzsKICAgICAgICAgICAgKikKICAgICAgICAgICAgICAgIF9kaWUgIlVua25vd24ga2V5ICRr"\
"ZXkgcGFzc2VkIHRvIGF1dG8ke2NvbW1hbmR9LiBXZSBvbmx5IHN1cHBvcnQgdm9sdW1lLCBwYXRo"\
"IGFuZCBmaWxlLiIKICAgICAgICAgICAgICAgIDs7CiAgICAgICAgZXNhYwogICAgZG9uZQp9CgoK"\
"ZGF0YWNyZWF0ZSgpIHsKICAgIF9hdXRvY29tbWFuZHBhcnNlIGNyZWF0ZSBub25lICIkQCIKfQoK"\
"CmRhdGFudWtlKCkgewogICAgX2F1dG9jb21tYW5kcGFyc2UgbnVrZSBub25lICIkQCIKfQoKZGF0"\
"YWJhY2t1cCgpIHsKICAgIF9jaGVja19yZXF1aXJlZF9lbnZfdmFycyAiQkFDS1VQX0ZJTEUiICJU"\
"RU1QX0RJUiIKICAgIEJBQ0tVUF9URU1QX1BBVEg9IiRURU1QX0RJUi9iYWNrdXAiCgoKICAgIG1r"\
"ZGlyIC1wICIkQkFDS1VQX1RFTVBfUEFUSCIKICAgIGVjaG8gIl9hdXRvY29tbWFuZHBhcnNlIFti"\
"YWNrdXBdIFskQkFDS1VQX1RFTVBfUEFUSF0gWyRAXSIKICAgIF9hdXRvY29tbWFuZHBhcnNlIGJh"\
"Y2t1cCAiJEJBQ0tVUF9URU1QX1BBVEgiICIkQCIKCiAgICB0YXIgemN2ZiAiJEJBQ0tVUF9GSUxF"\
"IiAtQyAiJEJBQ0tVUF9URU1QX1BBVEgiIC4KfQoKZGF0YXJlc3RvcmUoKSB7CiAgICBfY2hlY2tf"\
"cmVxdWlyZWRfZW52X3ZhcnMgIkJBQ0tVUF9GSUxFIiAiVEVNUF9ESVIiCiAgICBCQUNLVVBfVEVN"\
"UF9QQVRIPSIkVEVNUF9ESVIvcmVzdG9yZSIKCiAgICBlY2hvICJfYXV0b2NvbW1hbmRwYXJzZSBb"\
"cmVzdG9yZV0gWyRCQUNLVVBfVEVNUF9QQVRIXSBbJEBdIgoKICAgIG1rZGlyIC1wICIkQkFDS1VQ"\
"X1RFTVBfUEFUSCIKICAgIHRhciB6eHZmICIkQkFDS1VQX0ZJTEUiIC1DICIkQkFDS1VQX1RFTVBf"\
"UEFUSCIgLS1zdHJpcC1jb21wb25lbnRzPTEKCiAgICBfYXV0b2NvbW1hbmRwYXJzZSByZXN0b3Jl"\
"ICIkQkFDS1VQX1RFTVBfUEFUSCIgIiRAIgp9Cg==";
// Decode Base64 data
size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
unsigned char* decoded_data = new unsigned char[decoded_size];
size_t actual_size;
base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);
bool file_written = _recreate_file_(outpath, 10759194710819511084ULL, std::filesystem::perms(493), decoded_data, actual_size);
delete[] decoded_data;
any_written = any_written || file_written;
}
{
// File: _allservicesstatus.sh
fs::path outpath = fs::path(destination_folder) / "_allservicesstatus.sh";
static const char filedata_base64[] = "IyEvYmluL2Jhc2gKCiMgVGhpcyBzY3JpcHQgY2hlY2tzIEFMTCBzZXJ2aWNlcyBvbiB0aGUgc2Vy"\
"dmVyIGFuZCByZXR1cm5zIGEgc3RhdHVzIGZvciBlYWNoLgoKIyBSZXR1cm4gZm9ybWF0IGlzIHNp"\
"bXBsZSBFTlYgd2l0aCB0aGUgZm9sbG93aW5nIGZvcm1hdDoKIyBTRVJWSUNFX05BTUVfSEVBTFRI"\
"PWhlYWx0aHl8dW5oZWFsdGh5fHVua25vd24KIyBTRVJWSUNFX05BTUVfUE9SVFM9cG9ydDEscG9y"\
"dDIscG9ydDMKCiMgR2V0IGFsbCBzZXJ2aWNlcyBvbiB0aGUgc2VydmVyClNDUklQVF9ESVI9IiQo"\
"ZGlybmFtZSAiJDAiKSIKCiAgICAjIC8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t"\
"LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t"\
"LS0tLS0tCiAgICAjIC8vIHJlbW90ZSBwYXRocwogICAgIyAvLyBEUk9QU0hFTExfRElSCiAgICAj"\
"IC8vICAgfC0tIGJhY2t1cHMKICAgICMgLy8gICB8LS0gdGVtcF9maWxlcwogICAgIyAvLyAgIHwt"\
"LSBhZ2VudAogICAgIyAvLyAgIHwgICB8LS0gYmI2NAogICAgIyAvLyAgIHwgICB8LS0gKG90aGVy"\
"IGFnZW50IGZpbGVzLCBpbmNsdWRpbmcgX2FsbHNlcnZpY2Vzc3RhdHVzLnNoKQogICAgIyAvLyAg"\
"IHwtLSBzZXJ2aWNlcwogICAgIyAvLyAgICAgICB8LS0gc2VydmljZSBuYW1lCiAgICAjIC8vICAg"\
"ICAgICAgICB8LS0gY29uZmlnCiAgICAjIC8vICAgICAgICAgICAgICAgfC0tIHNlcnZpY2UuZW52"\
"IChhY3R1YWwgc2VydmljZSBjb25maWcpCiAgICAjIC8vICAgICAgICAgICAgICAgfC0tIC50ZW1w"\
"bGF0ZV9pbmZvLmVudgogICAgIyAvLyAgICAgICAgICAgfC0tIHRlbXBsYXRlCiAgICAjIC8vICAg"\
"ICAgICAgICAgICAgfC0tIF9kZWZhdWx0LmVudgogICAgIyAvLyAgICAgICAgICAgICAgIHwtLSAo"\
"c2NyaXB0IGZpbGVzKQogICAgIyAvLyAgICAgICAgICAgICAgIHwtLSBjb25maWcKICAgICMgLy8g"\
"ICAgICAgICAgICAgICAgICAgfC0tIHNlcnZpY2UuZW52IChkZWZhdWx0IHNlcnZpY2UgY29uZmln"\
"KQogICAgIyAvLyAgICAgICAgICAgICAgICAgICB8LS0gLnRlbXBsYXRlX2luZm8uZW52CiAgICAj"\
"IC8vICAgICAgICAgICAgICAgICAgIHwtLSAob3RoZXIgY29uZmlnIGZpbGVzIGZvciBzcGVjaWZp"\
"YyBzZXJ2ZXImc2VydmljZSkKCiMgR2V0IGFsbCBzZXJ2aWNlcyBvbiB0aGUgc2VydmVyClNFUlZJ"\
"Q0VTX1BBVEg9JChyZWFscGF0aCAiJHtTQ1JJUFRfRElSfS8uLi9zZXJ2aWNlcy8iKQoKQ1VSUkVO"\
"VF9PVVRQVVQ9IiIKQ1VSUkVOVF9FWElUX0NPREU9MAoKbG9hZF9kb3RlbnYoKXsKICAgIGxvY2Fs"\
"IGZpbGVfcGF0aD0kMQogICAgaWYgWyAtZiAiJHtmaWxlX3BhdGh9IiBdOyB0aGVuCiAgICAgICAg"\
"c291cmNlICIke2ZpbGVfcGF0aH0iCiAgICBmaQp9CgpfY2hlY2tfcmVxdWlyZWRfZW52X3ZhcnNf"\
"YWxsc2VydmljZXNzdGF0dXMoKSB7CiAgICBsb2NhbCByZXF1aXJlZF92YXJzPSgiJEAiKQogICAg"\
"Zm9yIHZhciBpbiAiJHtyZXF1aXJlZF92YXJzW0BdfSI7IGRvCiAgICAgICAgaWYgWyAteiAiJHsh"\
"dmFyfSIgXTsgdGhlbgogICAgICAgICAgICBfZGllICJSZXF1aXJlZCBlbnZpcm9ubWVudCB2YXJp"\
"YWJsZSAkdmFyIGlzIG5vdCBzZXQiCiAgICAgICAgZmkKICAgIGRvbmUKfQoKZnVuY3Rpb24gcnVu"\
"X2NvbW1hbmQoKSB7CiAgICBsb2NhbCBzZXJ2aWNlX3BhdGg9JDEKICAgIGxvY2FsIGNvbW1hbmQ9"\
"JDIKICAgIGxvY2FsIGNhcHR1cmVfb3V0cHV0PSR7MzotZmFsc2V9ICAjIGRlZmF1bHQgdG8gZmFs"\
"c2UgaWYgbm90IHNwZWNpZmllZAoKICAgICMgY2hlY2sgaWYgdGhlIGNvbW1hbmQgaXMgYSBmaWxl"\
"CiAgICBpZiBbICEgLWYgIiR7c2VydmljZV9wYXRofS90ZW1wbGF0ZS8ke2NvbW1hbmR9LnNoIiBd"\
"OyB0aGVuCiAgICAgICAgcmV0dXJuOwogICAgZmkKCiAgICAjIHJ1biB0aGUgY29tbWFuZCBpbiBh"\
"IHN1YnNoZWxsIHRvIHByZXZlbnQgZW52aXJvbm1lbnQgY2hhbmdlcwogICAgQ1VSUkVOVF9PVVRQ"\
"VVQ9JCgKICAgICAgICBzZXQgLWEKCiAgICAgICAgbG9hZF9kb3RlbnYgIiR7c2VydmljZV9wYXRo"\
"fS90ZW1wbGF0ZS9fZGVmYXVsdC5lbnYiCiAgICAgICAgbG9hZF9kb3RlbnYgIiR7c2VydmljZV9w"\
"YXRofS9jb25maWcvc2VydmljZS5lbnYiCiAgICAgICAgbG9hZF9kb3RlbnYgIiR7c2VydmljZV9w"\
"YXRofS9jb25maWcvLnRlbXBsYXRlX2luZm8uZW52IgoKICAgICAgICAjIHVwZGF0ZSB0aGUgbWFp"\
"biB2YXJpYWJsZXMuCiAgICAgICAgQ09ORklHX1BBVEg9IiR7c2VydmljZV9wYXRofS9jb25maWci"\
"CiAgICAgICAgU0VSVklDRT0iJHtTRVJWSUNFX05BTUV9IgogICAgICAgIERPQ0tFUl9DTElfSElO"\
"VFM9ZmFsc2UKCiAgICAgICAgc2V0ICthCgogICAgICAgIF9jaGVja19yZXF1aXJlZF9lbnZfdmFy"\
"c19hbGxzZXJ2aWNlc3N0YXR1cyAiQ09ORklHX1BBVEgiICJTRVJWRVIiICJTRVJWSUNFIiAiQUdF"\
"TlRfUEFUSCIgIkhPU1RfTkFNRSIgIlRFTVBMQVRFIgoKICAgICAgICBpZiBbICIkY2FwdHVyZV9v"\
"dXRwdXQiID0gInRydWUiIF07IHRoZW4KICAgICAgICAgICAgIyBDYXB0dXJlIGFuZCByZXR1cm4g"\
"b3V0cHV0CiAgICAgICAgICAgIGJhc2ggIiR7c2VydmljZV9wYXRofS90ZW1wbGF0ZS8ke2NvbW1h"\
"bmR9LnNoIiAyPiYxCiAgICAgICAgZWxzZQogICAgICAgICAgICAjIFJ1biBzaWxlbnRseSBhbmQg"\
"cmV0dXJuIGV4aXQgY29kZQogICAgICAgICAgICBiYXNoICIke3NlcnZpY2VfcGF0aH0vdGVtcGxh"\
"dGUvJHtjb21tYW5kfS5zaCIgPiAvZGV2L251bGwgMj4mMQogICAgICAgIGZpCiAgICApCiAgICBD"\
"VVJSRU5UX0VYSVRfQ09ERT0kPwp9CgpmdW5jdGlvbiBjb21tYW5kX2V4aXN0cygpIHsKICAgIGxv"\
"Y2FsIHNlcnZpY2VfcGF0aD0kMQogICAgbG9jYWwgY29tbWFuZD0kMgogICAgaWYgWyAhIC1mICIk"\
"e3NlcnZpY2VfcGF0aH0vdGVtcGxhdGUvJHtjb21tYW5kfS5zaCIgXTsgdGhlbgogICAgICAgIHJl"\
"dHVybiAxCiAgICBmaQogICAgcmV0dXJuIDAKfQoKCmlmIFsgISAtZCAiJHtTRVJWSUNFU19QQVRI"\
"fSIgXTsgdGhlbgogICAgZWNobyAiU2VydmljZXMgcGF0aCBkb2VzIG5vdCBleGlzdDogJHtTRVJW"\
"SUNFU19QQVRIfSIKICAgIGV4aXQgMApmaQoKIyBHZXQgYWxsIHNlcnZpY2UgbmFtZXMKU0VSVklD"\
"RV9OQU1FUz0kKGxzICIke1NFUlZJQ0VTX1BBVEh9IikKCiMgSXRlcmF0ZSBvdmVyIGFsbCBzZXJ2"\
"aWNlIG5hbWVzCmZvciBTRVJWSUNFX05BTUUgaW4gJHtTRVJWSUNFX05BTUVTfTsgZG8KCiAgICBT"\
"RVJWSUNFX1BBVEg9JChyZWFscGF0aCAiJHtTRVJWSUNFU19QQVRIfS8ke1NFUlZJQ0VfTkFNRX0i"\
"KQoKICAgICMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQogICAgIyBHZXQgdGhlIHNl"\
"cnZpY2UgaGVhbHRoCiAgICBpZiAhIGNvbW1hbmRfZXhpc3RzICIke1NFUlZJQ0VfUEFUSH0iICJz"\
"dGF0dXMiOyB0aGVuCiAgICAgICAgU0VSVklDRV9IRUFMVEg9InVua25vd24iCiAgICBlbHNlCiAg"\
"ICAgICAgcnVuX2NvbW1hbmQgIiR7U0VSVklDRV9QQVRIfSIgInN0YXR1cyIgImZhbHNlIgogICAg"\
"ICAgIGlmIFsgIiR7Q1VSUkVOVF9FWElUX0NPREV9IiAtZXEgMCBdOyB0aGVuCiAgICAgICAgICAg"\
"IFNFUlZJQ0VfSEVBTFRIPSJoZWFsdGh5IgogICAgICAgIGVsc2UKICAgICAgICAgICAgU0VSVklD"\
"RV9IRUFMVEg9InVuaGVhbHRoeSIKICAgICAgICBmaQogICAgZmkKCiAgICAjLS0tLS0tLS0tLS0t"\
"LS0tLS0tLS0tLS0tLS0tLS0tLS0KICAgICMgR2V0IHRoZSBzZXJ2aWNlIHBvcnRzCiAgICBpZiAh"\
"IGNvbW1hbmRfZXhpc3RzICIke1NFUlZJQ0VfUEFUSH0iICJwb3J0cyI7IHRoZW4KICAgICAgICBT"\
"RVJWSUNFX1BPUlRTPSIiCiAgICBlbHNlCiAgICAgICAgcnVuX2NvbW1hbmQgIiR7U0VSVklDRV9Q"\
"QVRIfSIgInBvcnRzIiAidHJ1ZSIKICAgICAgICBTRVJWSUNFX1BPUlRTPSIke0NVUlJFTlRfT1VU"\
"UFVUfSIKICAgIGZpCgogICAgIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiAgICAj"\
"IHJldHVybiB0aGUgaGVhbHRoIGFuZCBwb3J0cwogICAgZWNobyAiJHtTRVJWSUNFX05BTUV9X0hF"\
"QUxUSD0ke1NFUlZJQ0VfSEVBTFRIfSIKICAgIGVjaG8gIiR7U0VSVklDRV9OQU1FfV9QT1JUUz0k"\
"e1NFUlZJQ0VfUE9SVFN9Igpkb25lCg==";
// Decode Base64 data
size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
unsigned char* decoded_data = new unsigned char[decoded_size];
size_t actual_size;
base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);
bool file_written = _recreate_file_(outpath, 4669115953916396805ULL, std::filesystem::perms(493), decoded_data, actual_size);
delete[] decoded_data;
any_written = any_written || file_written;
}
{
// File: common.sh
fs::path outpath = fs::path(destination_folder) / "common.sh";
static const char filedata_base64[] = "IyBDT01NT04gRlVOQ1RJT05TCiMgSkRFCiMgMjAyNS0wNS0wMwoKIyBUaGlzIGZpbGUgaXMgYXZh"\
"aWxhYmxlIFRPICoqKkFMTCoqKiB0ZW1wbGF0ZXMsIGFzICR7QUdFTlRfUEFUSH0vX2NvbW1vbi5z"\
"aAoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t"\
"LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoj"\
"IHN1bW1hcnkgb2YgZnVuY3Rpb25zOgojICAgX2RpZSAibWVzc2FnZSIgICAgICAgICAgICAgICAg"\
"ICAgICAgICAgOiBQcmludHMgYW4gZXJyb3IgbWVzc2FnZSBpbiByZWQgYW5kIGV4aXRzIHdpdGgg"\
"c3RhdHVzIGNvZGUgMS4KIyAgIF9ncmV5X3N0YXJ0ICAgICAgICAgICAgICAgICAgICAgICAgICAg"\
"IDogU3dpdGNoZXMgdGVybWluYWwgb3V0cHV0IGNvbG9yIHRvIGdyZXkuCiMgICBfZ3JleV9lbmQg"\
"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICA6IFJlc2V0cyB0ZXJtaW5hbCBvdXRwdXQgY29s"\
"b3IgZnJvbSBncmV5LgojICAgX2NyZWF0ZV9hbmRfc3RhcnRfY29udGFpbmVyICI8cnVuX2NtZD4i"\
"IDxjb250YWluZXJfbmFtZT4gOiBDcmVhdGVzL3N0YXJ0cyBhIGNvbnRhaW5lciwgdmVyaWZ5aW5n"\
"IGl0IHJ1bnMuCiMgICBfY3JlYXRlX2ZvbGRlciA8Zm9sZGVyX3BhdGg+ICAgICAgICAgICA6IENy"\
"ZWF0ZXMgYSBkaXJlY3RvcnkgaWYgaXQgZG9lc24ndCBleGlzdCAoY2htb2QgNzc3KS4KIyAgIF9j"\
"aGVja19kb2NrZXJfaW5zdGFsbGVkICAgICAgICAgICAgICAgOiBDaGVja3MgaWYgRG9ja2VyIGlz"\
"IGluc3RhbGxlZCwgcnVubmluZywgYW5kIHVzZXIgaGFzIHBlcm1pc3Npb24uIFJldHVybnMgMSBv"\
"biBmYWlsdXJlLgojICAgX2lzX2NvbnRhaW5lcl9leGlzdHMgPGNvbnRhaW5lcl9uYW1lPiA6IENo"\
"ZWNrcyBpZiBhIGNvbnRhaW5lciAoYW55IHN0YXRlKSBleGlzdHMuIFJldHVybnMgMSBpZiBub3Qg"\
"Zm91bmQuCiMgICBfaXNfY29udGFpbmVyX3J1bm5pbmcgPGNvbnRhaW5lcl9uYW1lPjogQ2hlY2tz"\
"IGlmIGEgY29udGFpbmVyIGlzIGN1cnJlbnRseSBydW5uaW5nLiBSZXR1cm5zIDEgaWYgbm90IHJ1"\
"bm5pbmcuCiMgICBfZ2V0X2NvbnRhaW5lcl9pZCA8Y29udGFpbmVyX25hbWU+ICAgIDogUHJpbnRz"\
"IHRoZSBJRCBvZiB0aGUgbmFtZWQgY29udGFpbmVyLgojICAgX2dldF9jb250YWluZXJfc3RhdHVz"\
"IDxjb250YWluZXJfbmFtZT46IFByaW50cyB0aGUgc3RhdHVzIHN0cmluZyBvZiB0aGUgbmFtZWQg"\
"Y29udGFpbmVyLgojICAgX3N0YXJ0X2NvbnRhaW5lciA8Y29udGFpbmVyX25hbWU+ICAgICA6IFN0"\
"YXJ0cyBhbiBleGlzdGluZywgc3RvcHBlZCBjb250YWluZXIuCiMgICBfc3RvcF9jb250YWluZXIg"\
"PGNvbnRhaW5lcl9uYW1lPiAgICAgIDogU3RvcHMgYSBydW5uaW5nIGNvbnRhaW5lci4KIyAgIF9y"\
"ZW1vdmVfY29udGFpbmVyIDxjb250YWluZXJfbmFtZT4gICAgOiBTdG9wcyAoaWYgbmVlZGVkKSBh"\
"bmQgcmVtb3ZlcyBhIGNvbnRhaW5lci4KIyAgIF9nZXRfY29udGFpbmVyX2xvZ3MgPGNvbnRhaW5l"\
"cl9uYW1lPiAgOiBQcmludHMgdGhlIGxvZ3MgZm9yIGEgY29udGFpbmVyLgojICAgX2NoZWNrX3Jl"\
"cXVpcmVkX2Vudl92YXJzICJWQVIxIiAuLi4gICAgOiBDaGVja3MgaWYgbGlzdGVkIGVudmlyb25t"\
"ZW50IHZhcmlhYmxlcyBhcmUgc2V0OyBjYWxscyBfZGllKCkgaWYgYW55IGFyZSBtaXNzaW5nLgoj"\
"ICAgX3Jvb3RfcmVtb3ZlX3RyZWUgPHBhdGg+ICAgICAgICAgICAgICA6IFJlbW92ZXMgYSBwYXRo"\
"IHVzaW5nIGEgcm9vdCBEb2NrZXIgY29udGFpbmVyIChmb3IgcGVybWlzc2lvbnMpLgoKIyAtLS0t"\
"LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0t"\
"LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIFByaW50cyBh"\
"biBlcnJvciBtZXNzYWdlIGluIHJlZCBhbmQgZXhpdHMgd2l0aCBzdGF0dXMgY29kZSAxLgpfZGll"\
"KCkgewogICAgZWNobyAtZSAiRXJyb3I6ICQxIgogICAgZXhpdCAxCn0KCiMgQ3JlYXRlcy9zdGFy"\
"dHMgYSBjb250YWluZXIsIHZlcmlmeWluZyBpdCBydW5zLgpfY3JlYXRlX2FuZF9zdGFydF9jb250"\
"YWluZXIoKSB7CiAgICBpZiBbIC16ICIkMSIgXSB8fCBbIC16ICIkMiIgXTsgdGhlbgogICAgICAg"\
"IF9kaWUgIlRlbXBsYXRlIGVycm9yOiBjcmVhdGVfYW5kX3N0YXJ0X2NvbnRhaW5lciA8cnVuX2Nt"\
"ZD4gPGNvbnRhaW5lcl9uYW1lPiIKICAgIGZpCgogICAgbG9jYWwgcnVuX2NtZD0iJDEiCiAgICBs"\
"b2NhbCBjb250YWluZXJfbmFtZT0iJDIiCgogICAgaWYgX2lzX2NvbnRhaW5lcl9leGlzdHMgJGNv"\
"bnRhaW5lcl9uYW1lOyB0aGVuCiAgICAgICAgX2lzX2NvbnRhaW5lcl9ydW5uaW5nICRjb250YWlu"\
"ZXJfbmFtZSAmJiByZXR1cm4gMAogICAgICAgIF9zdGFydF9jb250YWluZXIgJGNvbnRhaW5lcl9u"\
"YW1lCiAgICBlbHNlCiAgICAgICAgJHJ1bl9jbWQKICAgIGZpCgogICAgaWYgISBfaXNfY29udGFp"\
"bmVyX3J1bm5pbmcgJGNvbnRhaW5lcl9uYW1lOyB0aGVuCiAgICAgICAgX2RpZSAiQ29udGFpbmVy"\
"ICR7Y29udGFpbmVyX25hbWV9IGZhaWxlZCB0byBzdGFydCIKICAgIGZpCgogICAgSUQ9JChfZ2V0"\
"X2NvbnRhaW5lcl9pZCAkY29udGFpbmVyX25hbWUpCiAgICBlY2hvICJDb250YWluZXIgJHtjb250"\
"YWluZXJfbmFtZX0gaXMgcnVubmluZyB3aXRoIElEICR7SUR9Igp9CgojIENyZWF0ZXMgYSBkaXJl"\
"Y3RvcnkgaWYgaXQgZG9lc24ndCBleGlzdCAoY2htb2QgNzc3KS4KX2NyZWF0ZV9mb2xkZXIoKSB7"\
"CiAgICBsb2NhbCBmb2xkZXI9IiQxIgogICAgaWYgWyAtZCAiJGZvbGRlciIgXTsgdGhlbgogICAg"\
"ICAgIHJldHVybiAwCiAgICBmaQogICAgaWYgISBta2RpciAtcCAiJGZvbGRlciI7IHRoZW4KICAg"\
"ICAgICBfZGllICJGYWlsZWQgdG8gY3JlYXRlIGZvbGRlcjogJGZvbGRlciIKICAgIGZpCiAgICBj"\
"aG1vZCA3NzcgIiRmb2xkZXIiCiAgICBlY2hvICJGb2xkZXIgY3JlYXRlZDogJGZvbGRlciIKfQoK"\
"IyBDaGVja3MgaWYgRG9ja2VyIGlzIGluc3RhbGxlZCwgcnVubmluZywgYW5kIHVzZXIgaGFzIHBl"\
"cm1pc3Npb24uIFJldHVybnMgMSBvbiBmYWlsdXJlLgpfY2hlY2tfZG9ja2VyX2luc3RhbGxlZCgp"\
"IHsKICAgIGlmICEgY29tbWFuZCAtdiBkb2NrZXIgJj4gL2Rldi9udWxsOyB0aGVuCiAgICAgICAg"\
"ZWNobyAiRG9ja2VyIGlzIG5vdCBpbnN0YWxsZWQiCiAgICAgICAgcmV0dXJuIDEKICAgIGZpCgog"\
"ICAgIyBjaGVjayBpZiBkb2NrZXIgZGFlbW9uIGlzIHJ1bm5pbmcKICAgIGlmICEgZG9ja2VyIGlu"\
"Zm8gJj4gL2Rldi9udWxsOyB0aGVuCiAgICAgICAgZWNobyAiRG9ja2VyIGRhZW1vbiBpcyBub3Qg"\
"cnVubmluZyIKICAgICAgICByZXR1cm4gMQogICAgZmkKCiAgICAjIGNoZWNrIGlmIHVzZXIgaGFz"\
"IHBlcm1pc3Npb24gdG8gcnVuIGRvY2tlcgogICAgaWYgISBkb2NrZXIgcnVuIC0tcm0gaGVsbG8t"\
"d29ybGQgJj4gL2Rldi9udWxsOyB0aGVuCiAgICAgICAgZWNobyAiVXNlciBkb2VzIG5vdCBoYXZl"\
"IHBlcm1pc3Npb24gdG8gcnVuIGRvY2tlciIKICAgICAgICByZXR1cm4gMQogICAgZmkKCiAgICBy"\
"ZXR1cm4gMAp9CgojIENoZWNrcyBpZiBhIGNvbnRhaW5lciAoYW55IHN0YXRlKSBleGlzdHMuIFJl"\
"dHVybnMgMSBpZiBub3QgZm91bmQuCl9pc19jb250YWluZXJfZXhpc3RzKCkgewogICAgaWYgISBk"\
"b2NrZXIgcHMgLWEgLS1mb3JtYXQgInt7Lk5hbWVzfX0iIHwgZ3JlcCAtcSAiXiQxJCI7IHRoZW4K"\
"ICAgICAgICByZXR1cm4gMQogICAgZmkKICAgIHJldHVybiAwCn0KCiMgQ2hlY2tzIGlmIGEgY29u"\
"dGFpbmVyIGlzIGN1cnJlbnRseSBydW5uaW5nLiBSZXR1cm5zIDEgaWYgbm90IHJ1bm5pbmcuCl9p"\
"c19jb250YWluZXJfcnVubmluZygpIHsKICAgIGlmICEgZG9ja2VyIHBzIC0tZm9ybWF0ICJ7ey5O"\
"YW1lc319IiB8IGdyZXAgLXEgIl4kMSQiOyB0aGVuCiAgICAgICAgcmV0dXJuIDEKICAgIGZpCiAg"\
"ICByZXR1cm4gMAp9CgojIFByaW50cyB0aGUgSUQgb2YgdGhlIG5hbWVkIGNvbnRhaW5lci4KX2dl"\
"dF9jb250YWluZXJfaWQoKSB7CiAgICBkb2NrZXIgcHMgLS1mb3JtYXQgInt7LklEfX0iIC0tZmls"\
"dGVyICJuYW1lPSQxIgp9CgojIFByaW50cyB0aGUgc3RhdHVzIHN0cmluZyBvZiB0aGUgbmFtZWQg"\
"Y29udGFpbmVyLgpfZ2V0X2NvbnRhaW5lcl9zdGF0dXMoKSB7CiAgICBkb2NrZXIgcHMgLS1mb3Jt"\
"YXQgInt7LlN0YXR1c319IiAtLWZpbHRlciAibmFtZT0kMSIKfQoKIyBTdGFydHMgYW4gZXhpc3Rp"\
"bmcsIHN0b3BwZWQgY29udGFpbmVyLgpfc3RhcnRfY29udGFpbmVyKCkgewogICAgX2lzX2NvbnRh"\
"aW5lcl9leGlzdHMgJDEgfHwgcmV0dXJuIDEKICAgIF9pc19jb250YWluZXJfcnVubmluZyAkMSAm"\
"JiByZXR1cm4gMAogICAgZG9ja2VyIHN0YXJ0ICQxCn0KCiMgU3RvcHMgYSBydW5uaW5nIGNvbnRh"\
"aW5lci4KX3N0b3BfY29udGFpbmVyKCkgewogICAgX2lzX2NvbnRhaW5lcl9ydW5uaW5nICQxIHx8"\
"IHJldHVybiAwOwogICAgZG9ja2VyIHN0b3AgJDEKfSAgIAoKIyBTdG9wcyAoaWYgbmVlZGVkKSBh"\
"bmQgcmVtb3ZlcyBhIGNvbnRhaW5lci4KX3JlbW92ZV9jb250YWluZXIoKSB7CiAgICBfc3RvcF9j"\
"b250YWluZXIgJDEKICAgIF9pc19jb250YWluZXJfZXhpc3RzICQxIHx8IHJldHVybiAwOwogICAg"\
"ZG9ja2VyIHJtICQxCn0KCiMgUHJpbnRzIHRoZSBsb2dzIGZvciBhIGNvbnRhaW5lci4KX2dldF9j"\
"b250YWluZXJfbG9ncygpIHsKICAgIGlmICEgX2lzX2NvbnRhaW5lcl9leGlzdHMgJDE7IHRoZW4K"\
"ICAgICAgICBlY2hvICJDb250YWluZXIgJDEgZG9lcyBub3QgZXhpc3QiCiAgICAgICAgcmV0dXJu"\
"IDEKICAgIGZpCgogICAgZG9ja2VyIGxvZ3MgJDEKfQoKIyBDaGVja3MgaWYgbGlzdGVkIGVudmly"\
"b25tZW50IHZhcmlhYmxlcyBhcmUgc2V0OyBjYWxscyBfZGllKCkgaWYgYW55IGFyZSBtaXNzaW5n"\
"LgpfY2hlY2tfcmVxdWlyZWRfZW52X3ZhcnMoKSB7CiAgICBsb2NhbCByZXF1aXJlZF92YXJzPSgi"\
"JEAiKQogICAgZm9yIHZhciBpbiAiJHtyZXF1aXJlZF92YXJzW0BdfSI7IGRvCiAgICAgICAgaWYg"\
"WyAteiAiJHshdmFyfSIgXTsgdGhlbgogICAgICAgICAgICBfZGllICJSZXF1aXJlZCBlbnZpcm9u"\
"bWVudCB2YXJpYWJsZSAkdmFyIGlzIG5vdCBzZXQiCiAgICAgICAgZmkKICAgIGRvbmUKfQoKIyBS"\
"ZW1vdmVzIGEgcGF0aCB1c2luZyBhIHJvb3QgRG9ja2VyIGNvbnRhaW5lciAoZm9yIHBlcm1pc3Np"\
"b25zKS4KX3Jvb3RfcmVtb3ZlX3RyZWUoKSB7CiAgICBsb2NhbCB0b19yZW1vdmU9IiQxIgogICAg"\
"cGFyZW50PSQoZGlybmFtZSAiJHRvX3JlbW92ZSIpCiAgICBhYnNfcGFyZW50PSQocmVhbHBhdGgg"\
"IiRwYXJlbnQiKQogICAgY2hpbGQ9JChiYXNlbmFtZSAiJHRvX3JlbW92ZSIpCiAgICBkb2NrZXIg"\
"cnVuIC0tcm0gLXYgIiRhYnNfcGFyZW50IjovZGF0YSBhbHBpbmUgcm0gLXJmICIvZGF0YS8kY2hp"\
"bGQiCn0KCgojIExvYWQgYXV0b2NvbW1hbmRzCnNvdXJjZSAiJHtBR0VOVF9QQVRIfS9kYXRhY29t"\
"bWFuZHMuc2gi";
// Decode Base64 data
size_t decoded_size = (strlen(filedata_base64) * 3) / 4;
unsigned char* decoded_data = new unsigned char[decoded_size];
size_t actual_size;
base64_decode(filedata_base64, strlen(filedata_base64), decoded_data, &actual_size);
bool file_written = _recreate_file_(outpath, 6967493376886731479ULL, std::filesystem::perms(493), decoded_data, actual_size);
delete[] decoded_data;
any_written = any_written || file_written;
}
return any_written;
}
}

View File

@ -1,15 +0,0 @@
#pragma once
/*
THIS FILE IS AUTO-GENERATED BY DEHYDRATE.
DO NOT EDIT THIS FILE.
*/
#include <string>
namespace recreate_agent {
bool recreate_tree(std::string destination_folder);
}

View File

@ -1,11 +0,0 @@
// version.hpp (dummy for linter/IntelliSense)
#pragma once
#include <string>
namespace dropshell {
extern const std::string VERSION;
extern const std::string RELEASE_DATE;
extern const std::string AUTHOR;
extern const std::string LICENSE;
}

View File

@ -7,6 +7,8 @@
#include "utils/utils.hpp"
#include "services.hpp"
#include <fstream>
namespace dropshell
{
@ -65,6 +67,39 @@ namespace dropshell
namespace shared_commands
{
bool print_readme(const template_info &tinfo, std::string server, std::string service)
{
std::vector<std::string> variants_to_try = {"README.txt", "readme.txt", "ReadMe.txt", "README", "readme", "README.md", "readme.md"};
std::filesystem::path readme_path = tinfo.local_template_path();
for (const auto &variant : variants_to_try)
{
if (std::filesystem::exists(readme_path / variant))
{
readme_path = readme_path / variant;
break;
}
}
if (!std::filesystem::exists(readme_path))
return false;
std::map<std::string, std::string> all_env_vars;
get_all_service_env_vars(server, service, all_env_vars);
all_env_vars["LOCAL_CONFIG_PATH"] = localpath::service(server, service);
all_env_vars["LOCAL_TEMPLATE_PATH"] = tinfo.local_template_path().string();
info << std::endl;
std::ifstream readme_file(readme_path);
std::string line;
while (std::getline(readme_file, line))
{
rawout << substitute_provided_key_value_pairs(line, all_env_vars) << std::endl;
}
return true;
}
bool create_service(const std::string &server_name, const std::string &template_name, const std::string &service_name)
{
if (server_name.empty() || template_name.empty() || service_name.empty())
@ -112,10 +147,14 @@ namespace dropshell
recursive_copy(tinfo.local_template_path() / "config", service_dir);
info << "Service " << service_name << " created successfully" << std::endl;
if (!print_readme(tinfo, server_name, service_name))
{
info << std::endl;
info << "To complete the installation, please:" << std::endl;
info << "1. edit the service config file: dropshell edit " << server_name << " " << service_name << std::endl;
info << "2. install the remote service: dropshell install " << server_name << " " << service_name << std::endl;
}
return true;
}

View File

@ -55,6 +55,11 @@ void help_autocomplete(const CommandContext& ctx) {
}
void show_command(const std::string& cmd) {
// get console width
int width = get_console_width() - 6; // 5 for [INF] + 1 for space
int firstcol = 34;
int secondcol = width - firstcol - 3;
const auto& cmd_info = CommandRegistry::instance().find_command(cmd);
if (!cmd_info)
{
@ -62,9 +67,22 @@ void show_command(const std::string& cmd) {
return;
}
info << " ";
info << left_align(cmd_info->help_usage, 32);
info << cmd_info->help_description << std::endl;
if (cmd_info->help_usage.length() < width-secondcol)
{
std::string remaining_description = cmd_info->help_description;
info << " " << left_align(cmd_info->help_usage, firstcol) << get_line_wrap(remaining_description, secondcol);
while (!remaining_description.empty())
info << " " << left_align(" ",firstcol) << get_line_wrap(remaining_description, secondcol-1);
}
else
{
info << " " << cmd_info->help_usage << std::endl;
std::string remaining_description = cmd_info->help_description;
info << " " << left_align(" ",firstcol) << get_line_wrap(remaining_description, secondcol);
while (!remaining_description.empty())
info << " " << left_align(" ",firstcol) << get_line_wrap(remaining_description, secondcol-1);
}
}
extern const std::string VERSION;
@ -106,7 +124,7 @@ int help_handler(const CommandContext& ctx) {
if (ctx.args.size() > 0)
return show_command_help(ctx.args[0]);
info << std::endl;
std::cout << std::endl;
maketitle("DropShell version " + VERSION);
info << std::endl;
info << "A tool for managing remote servers, by " << AUTHOR << std::endl;
@ -120,16 +138,17 @@ int help_handler(const CommandContext& ctx) {
{
// show more!
show_command("list");
std::cout << std::endl;
info << std::endl;
show_command("install");
show_command("uninstall");
show_command("nuke");
std::cout << std::endl;
info << std::endl;
show_command("start");
show_command("stop");
std::cout << std::endl;
info << std::endl;
show_command("ssh");
std::cout << std::endl;
info << std::endl;
show_command("create-service");
}
return 0;
}

View File

@ -5,16 +5,19 @@
#include "templates.hpp"
#include "shared_commands.hpp"
#include "utils/hash.hpp"
#include "autogen/_agent.hpp"
#include "autogen/_agent-local.hpp"
#include "autogen/_agent-remote.hpp"
#include "services.hpp"
#include "utils/output.hpp"
#include <fstream>
#include <unistd.h>
#include <cstring>
#include <iostream>
#include <sstream>
#include <filesystem>
#include "utils/assert.hpp"
#include "servers.hpp"
namespace dropshell
{
@ -37,14 +40,14 @@ namespace dropshell
0, // min_args (after command)
2, // max_args (after command)
"install [SERVER] [SERVICE|all]",
"Install/reinstall host, remote servers, or service(s). Safe/non-destructive way to update.",
"Install/reinstall host and remote servers, or service(s). Safe way to update.",
// heredoc
R"(
Install components on a server. This is safe to re-run (non-destructive) and used to update
servers and their services.
install (re)install dropshell components on this computer.
install SERVER (re)install dropshell agent on the given server.
install (re)install dropshell components on this computer, and on all servers.
install SERVER (re)install dropshell agent on the particular given server.
install SERVER [SERVICE|all] (re)install the given service (or all services) on the given server.
Note you need to create the service first with:
@ -53,6 +56,7 @@ namespace dropshell
}
} install_command_register;
namespace shared_commands
{
@ -130,6 +134,8 @@ namespace dropshell
// print health tick
info << "Health: " << shared_commands::healthtick(server, service) << std::endl;
return true;
}
@ -159,6 +165,8 @@ namespace dropshell
int update_dropshell()
{
maketitle("Updating dropshell on this computer...");
// determine path to this executable
std::filesystem::path dropshell_path = std::filesystem::canonical("/proc/self/exe");
std::filesystem::path parent_path = dropshell_path.parent_path();
@ -202,8 +210,8 @@ namespace dropshell
if (currentver >= newver)
{
std::cout << "Current dropshell version: " << currentver << ", published version: " << newver << std::endl;
std::cout << "No update needed." << std::endl;
info << "Current dropshell version: " << currentver << ", published version: " << newver << std::endl;
info << "Release version is not newer, no update needed." << std::endl;
return 0;
}
@ -214,20 +222,22 @@ namespace dropshell
rval = system(bash_script_2.c_str());
if (rval != 0)
{
std::cerr << "Failed to install new version of dropshell." << std::endl;
error << "Failed to install new version of dropshell." << std::endl;
return -1;
}
std::cout << "Successfully updated " << dropshell_path << " to the latest " << arch << " version." << std::endl;
info << "Successfully updated " << dropshell_path << " to the latest " << arch << " version." << std::endl;
// execute the new version
execlp("bash", "bash", "-c", (parent_path / "dropshell").c_str(), "install", (char *)nullptr);
std::cerr << "Failed to execute new version of dropshell." << std::endl;
error << "Failed to execute new version of dropshell." << std::endl;
return -1;
}
int install_local_agent()
{
maketitle("Installing dropshell agent on this computer...");
std::vector<std::filesystem::path> paths = {
gConfig().get_local_template_cache_path(),
gConfig().get_local_backup_path(),
@ -239,46 +249,20 @@ namespace dropshell
for (auto &p : paths)
if (!std::filesystem::exists(p))
{
std::cout << "Creating directory: " << p << std::endl;
info << "Creating directory: " << p << std::endl;
std::filesystem::create_directories(p);
}
// download bb64 for the host architecture.
if (!std::filesystem::exists(localpath::agent() + "bb64"))
{
std::string cmd = "cd " + localpath::agent() + " && curl -fsSL -o bb64 https://gitea.jde.nz/public/bb64/releases/download/latest/bb64.amd64 && chmod a+x bb64";
int ret = system(cmd.c_str());
if (EXITSTATUSCHECK(ret))
std::cout << "Downloaded local bb64 to " << localpath::agent() << std::endl;
else
std::cerr << "Failed to download local bb64 to " << localpath::agent() << std::endl;
}
else
{
std::cout << "Updating local bb64..." << std::endl;
system((localpath::agent() + "bb64 -u").c_str()); // update.
}
// create the agent-local directory.
recreate_agent_local::recreate_tree(localpath::agent());
std::cout << "Creating local files to copy to remote agents..." << std::endl;
recreate_agent::recreate_tree(localpath::files_for_remote_agent());
// run the local agent installer.
execute_local_command(localpath::agent(), "agent-install.sh",{}, nullptr, cMode::Defaults | cMode::NoBB64);
return 0;
}
// create the agent-remote directory.
info << "Creating local files to copy to remote agents..." << std::endl;
recreate_agent_remote::recreate_tree(localpath::files_for_remote_agent());
int install_host()
{
// update dropshell.
// install the local dropshell agent.
int rval = update_dropshell();
if (rval != 0)
return rval;
rval = install_local_agent();
if (rval != 0)
return rval;
std::cout << "Installation complete." << std::endl;
return 0;
}
@ -307,23 +291,9 @@ namespace dropshell
shared_commands::rsync_tree_to_remote(localpath::files_for_remote_agent(), agent_path, server_env, false);
info << "done." << std::endl;
// add in bb64. We can't use execute_remote_command() here, as that relies on bb64 which we're installing!
info << "Installing bb64 on " << server << "..." << std::endl
<< std::flush;
// run the agent installer. Can't use BB64 yet, as we're installing it on the remote server.
std::string remote_cmd =
"ssh -p " + server_env.get_SSH_INFO().port + " " + server_env.get_SSH_INFO().user + "@" + server_env.get_SSH_INFO().host +
" 'mkdir -p " + quote(agent_path) + " && curl -fsSL \"https://gitea.jde.nz/public/bb64/releases/download/latest/install.sh\" | bash -s -- " +
quote(agent_path) + " " + quote("$(id -u " + server_env.get_SSH_USER() + "):$(id -g " + server_env.get_SSH_USER() + ")") + "'";
// std::cout << "Executing: " << remote_cmd << std::endl;
if (!execute_local_command("", remote_cmd, {}, nullptr, cMode::Silent))
error << "Failed to download bb64 to " << agent_path << " on remote server." << std::endl;
else
debug << "Downloaded bb64 to " << agent_path << " on remote server." << std::endl;
// run the self-test.
bool okay = execute_ssh_command(server_env.get_SSH_INFO(), sCommand(agent_path, "./selftest.sh", {}), cMode::Defaults, nullptr);
bool okay = execute_ssh_command(server_env.get_SSH_INFO(), sCommand(agent_path, "agent-install.sh",{}), cMode::Defaults | cMode::NoBB64, nullptr);
if (!okay)
{
error << "ERROR: Failed to install remote agent on " << server << std::endl;
@ -334,6 +304,35 @@ namespace dropshell
return 0;
}
// ------------------------------------------------------------------------------------------------
// install_host
// ------------------------------------------------------------------------------------------------
int install_host()
{
// update dropshell.
// install the local dropshell agent.
int rval = update_dropshell();
if (rval != 0)
return rval;
rval = install_local_agent();
if (rval != 0)
return rval;
// install the dropshell agent on all servers.
std::vector<ServerInfo> servers = get_configured_servers();
for (const auto &server : servers)
{
rval = install_server(server.name);
if (rval != 0)
return rval;
}
std::cout << "Installation complete." << std::endl;
return 0;
}
// ------------------------------------------------------------------------------------------------
// install command implementation
// ------------------------------------------------------------------------------------------------

View File

@ -127,6 +127,16 @@ namespace dropshell
ASSERT(backup_details.has_value() && backup_details->is_valid(), "Invalid backup file.");
debug << "Backup details: " << std::endl;
debug << " Backup filename: " << backup_details->get_filename() << std::endl;
debug << " Backup template: " << backup_details->get_template_name() << std::endl;
debug << " Backup taken from server: " << backup_details->get_server() << std::endl;
debug << " Backup taken from service: " << backup_details->get_service() << std::endl;
debug << " " << std::endl;
debug << "Restoring to:" << std::endl;
debug << " Server: " << server << std::endl;
debug << " Service: " << service << std::endl;
std::string local_backups_dir = gConfig().get_local_backup_path();
if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir))
{
@ -143,14 +153,11 @@ namespace dropshell
if (backup_details->get_template_name() != service_info.template_name)
{
error << "Error: Backup template does not match service template. Can't restore." << std::endl;
debug << "Backup template: " << backup_details->get_template_name() << std::endl;
debug << "Service template: " << service_info.template_name << std::endl;
debug << "Backup file: " << local_backup_file_path << std::endl;
info << "Backup template: " << backup_details->get_template_name() << std::endl;
info << "Service template: " << service_info.template_name << std::endl;
return 1;
}
info << "Restoring " << backup_details->get_datetime() << " backup of " << backup_details->get_service() << " taken from " << backup_details->get_server() << ", onto " << server << "/" << service << std::endl;
info << std::endl;
warning << "*** ALL DATA FOR " << server << "/" << service << " WILL BE OVERWRITTEN! ***" << std::endl;
// run the restore script
@ -160,9 +167,9 @@ namespace dropshell
info << "1) Backing up old service... " << std::endl;
if (!shared_commands::backupdata_service(server, service))
{
error << "Error: Backup failed, restore aborted." << std::endl;
error << "You can try using dropshell install " << server << " " << service << " to install the service afresh." << std::endl;
error << "Otherwise, stop the service, create and initialise a new one, then restore to that." << std::endl;
error << "Backup failed, restore aborted." << std::endl;
info << "You can try using dropshell install " << server << " " << service << " to install the service afresh." << std::endl;
info << "Otherwise, stop the service, create and initialise a new one, then restore to that." << std::endl;
return 1;
}
info << "Backup complete." << std::endl;
@ -191,6 +198,8 @@ namespace dropshell
std::string remote_backups_dir = remotepath::backups(server);
std::string remote_backup_file_path = remote_backups_dir + "/" + backup_details->get_filename();
debug << "Copying backup file from local to server: " << local_backup_file_path << " -> " << remote_backup_file_path << std::endl;
// Copy backup file from local to server
if (!shared_commands::scp_file_to_remote(server_env, local_backup_file_path, remote_backup_file_path, false))
{
@ -199,6 +208,11 @@ namespace dropshell
}
shared_commands::cRemoteTempFolder remote_temp_folder(server_env);
debug << "Running restore script on server: " << server << std::endl;
debug << " BACKUP_FILE: " << remote_backup_file_path << std::endl;
debug << " TEMP_DIR: " << remote_temp_folder.path() << std::endl;
server_env.run_remote_template_command(service, "restore", {}, false, {{"BACKUP_FILE", remote_backup_file_path}, {"TEMP_DIR", remote_temp_folder.path()}});
} // dtor of remote_temp_folder will clean up the temp folder on the server

View File

@ -121,7 +121,7 @@ namespace dropshell
std::string output;
if (!execute_ssh_command(env.get_SSH_INFO(), sCommand(remotepath::agent(server_name), "./_allservicesstatus.sh", {{"HOST_NAME", server_name}, {"SERVER", server_name}, {"AGENT_PATH", remotepath::agent(server_name)}}),
cMode::CaptureOutput | cMode::Silent,
cMode::Silent,
&output))
return status;

View File

@ -218,8 +218,7 @@ bool server_env_manager::run_remote_template_command_and_capture_output(const st
for (const auto& [key, value] : extra_env_vars)
scommand->add_env_var(key, value);
cMode mode = cMode::CaptureOutput;
return execute_ssh_command(get_SSH_INFO(), scommand.value(), mode, &output);
return execute_ssh_command(get_SSH_INFO(), scommand.value(), cMode::Defaults, &output);
}

View File

@ -19,11 +19,11 @@ class template_info {
template_info() : mIsSet(false) {}
template_info(const std::string& template_name, const std::string& location_id, const std::filesystem::path& local_template_path);
virtual ~template_info() {}
bool is_set() { return mIsSet; }
std::string name() { return mTemplateName; }
std::string locationID() { return mLocationID; }
std::filesystem::path local_template_path() { return mTemplateLocalPath; }
bool template_valid() { return mTemplateValid; }
bool is_set() const { return mIsSet; }
std::string name() const { return mTemplateName; }
std::string locationID() const { return mLocationID; }
std::filesystem::path local_template_path() const { return mTemplateLocalPath; }
bool template_valid() const { return mTemplateValid; }
private:
std::string mTemplateName;
std::string mLocationID;

View File

@ -22,7 +22,6 @@ namespace dropshell
return (ret != -1 && WIFEXITED(ret) && (WEXITSTATUS(ret) == 0)); // ret is -1 if the command failed to execute.
}
// ----------------------------------------------------------------------------------------------------------
// execute_local_command_interactive
// ----------------------------------------------------------------------------------------------------------
@ -30,7 +29,7 @@ namespace dropshell
{
if (command.get_command_to_run().empty())
return false;
std::string full_command = command.construct_cmd(localpath::agent()); // Get the command string
std::string full_command = command.construct_cmd(localpath::agent()+"/bb64"); // Get the command string
pid_t pid = fork();
@ -85,15 +84,19 @@ namespace dropshell
currentColour_ = startColour_;
}
colourstream(currentColour_) << chunk;
newline_ = (chunk[chunk.size()-1] == '\n');
newline_ = (chunk[chunk.size() - 1] == '\n');
}
void print(const std::string& buffer) {
void print(const std::string &buffer)
{
size_t start = 0;
while (start < buffer.size()) {
while (start < buffer.size())
{
size_t newline_pos = buffer.find('\n', start);
if (newline_pos == std::string::npos) {
if (start < buffer.size()) {
if (newline_pos == std::string::npos)
{
if (start < buffer.size())
{
print_chunk(buffer.substr(start));
}
break;
@ -109,26 +112,30 @@ namespace dropshell
sColour currentColour_;
};
bool execute_local_command(std::string directory_to_run_in, std::string command_to_run, const std::map<std::string, std::string> &env_vars, std::string *output, cMode mode)
{
sCommand command(directory_to_run_in, command_to_run, env_vars);
if (hasFlag(mode, cMode::Interactive))
{
ASSERT(!hasFlag(mode, cMode::CaptureOutput), "Interactive mode and capture output mode cannot be used together");
ASSERT(output == nullptr, "Interactive mode and an output string cannot be used together");
return execute_local_command_interactive(command);
}
if (command.get_command_to_run().empty())
return false;
bool silent = hasFlag(mode, cMode::Silent);
std::string full_cmd = command.construct_cmd(localpath::agent()) + (hasFlag(mode, cMode::CaptureOutput) ? " 2>&1" : ""); // capture both stdout and stderr
std::string full_cmd;
if (!hasFlag(mode, cMode::NoBB64))
full_cmd = command.construct_cmd(localpath::agent()+"/bb64");
else
full_cmd = command.construct_cmd("");
if (output != nullptr)
full_cmd += " 2>&1"; // capture both stdout and stderr
FILE *pipe = popen(full_cmd.c_str(), "r");
if (!pipe)
@ -157,17 +164,18 @@ namespace dropshell
if (remote_command.get_command_to_run().empty())
return false;
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 remote_agent_path = remotepath::agent(ssh_info.server_ID);
std::string remote_bb64_path;
if (!hasFlag(mode, cMode::NoBB64))
remote_bb64_path = remotepath::agent(ssh_info.server_ID) + "/bb64";
bool rval = execute_local_command(
"", // directory to run in
ssh_cmd.str() + " " + remote_command.construct_cmd(remote_agent_path), // local command to run
"", // local directory to run in
ssh_cmd.str() + " " + remote_command.construct_cmd(remote_bb64_path), // local command to run
{}, // environment variables
output, // output string
mode // mode
@ -175,12 +183,8 @@ namespace dropshell
if (!rval && !hasFlag(mode, cMode::Silent))
{
std::cerr << std::endl
<< std::endl;
std::cerr << "Error: Failed to execute ssh command:" << std::endl;
std::cerr << "\033[90m" << ssh_cmd.str() + " " + remote_command.construct_cmd(remote_agent_path) << "\033[0m" << std::endl;
std::cerr << std::endl
<< std::endl;
error << "Error: Failed to execute ssh command:" << std::endl;
debug << ssh_cmd.str() + " " + remote_command.construct_cmd(remote_bb64_path) << std::endl;
}
return rval;
}
@ -188,19 +192,19 @@ namespace dropshell
// ----------------------------------------------------------------------------------------------------------
// makesafecmd
// ----------------------------------------------------------------------------------------------------------
std::string sCommand::makesafecmd(std::string agent_path, const std::string &command) const
std::string sCommand::makesafecmd(std::string bb64path, const std::string &command) const
{
if (command.empty())
return "";
std::string encoded = base64_encode(dequote(trim(command)));
std::string commandstr = agent_path + "/bb64 " + encoded;
std::string commandstr = bb64path + " " + encoded;
return commandstr;
}
// ----------------------------------------------------------------------------------------------------------
// construct_cmd
// ----------------------------------------------------------------------------------------------------------
std::string sCommand::construct_cmd(std::string agent_path) const
std::string sCommand::construct_cmd(std::string bb64path) const
{
if (mCmd.empty())
return "";
@ -208,6 +212,8 @@ namespace dropshell
// need to construct to change directory and set environment variables
std::string cmdstr;
if (!bb64path.empty())
{
if (!mDir.empty())
cmdstr += "cd " + quote(mDir) + " && ";
@ -217,8 +223,16 @@ namespace dropshell
cmdstr += mCmd;
if (!agent_path.empty())
cmdstr = makesafecmd(agent_path, cmdstr);
cmdstr = makesafecmd(bb64path, cmdstr);
}
else
{ // raw! bootstrapping only.
ASSERT(mVars.empty(), "Bootstrapping command must not have environment variables");
if (!mDir.empty())
cmdstr += mDir + "/" + mCmd;
else
cmdstr += mCmd;
}
return cmdstr;
}

View File

@ -13,7 +13,7 @@ enum class cMode {
Defaults = 0,
Interactive = 1,
Silent = 2,
CaptureOutput = 4
NoBB64 = 4
};
inline cMode operator&(cMode lhs, cMode rhs) {return static_cast<cMode>(static_cast<int>(lhs) & static_cast<int>(rhs));}
@ -52,10 +52,10 @@ class sCommand {
bool empty() const { return mCmd.empty(); }
std::string construct_cmd(std::string agent_path) const;
std::string construct_cmd(std::string bb64path) const;
private:
std::string makesafecmd(std::string agent_path, const std::string& command) const;
std::string makesafecmd(std::string bb64path, const std::string& command) const;
private:
std::string mDir;

View File

@ -42,7 +42,7 @@ namespace dropshell
std::lock_guard<std::mutex> lock(output_mutex);
if (c == EOF)
return !EOF;
if (at_line_start_ && c != '\n')
if (at_line_start_) // && c != '\n')
{
dest_ << GREY << tag_ << RESET << ' ' << colour_;
at_line_start_ = false;

View File

@ -7,6 +7,8 @@
#include <filesystem>
#include <regex>
#include <random>
#include <sys/ioctl.h>
#include <unistd.h>
namespace dropshell {
@ -286,29 +288,6 @@ std::vector<std::string> split(const std::string& str, const std::string& delimi
return tokens;
}
std::string replace_with_environment_variables_like_bash(std::string str) {
// Combined regex pattern for both ${var} and $var formats
std::regex var_pattern("\\$(?:\\{([^}]+)\\}|([a-zA-Z0-9_]+))");
std::string result = str;
std::smatch match;
while (std::regex_search(result, match, var_pattern)) {
// match[1] will contain capture from ${var} format
// match[2] will contain capture from $var format
std::string var_name = match[1].matched ? match[1].str() : match[2].str();
// Get value from system environment variables
const char* env_value = std::getenv(var_name.c_str());
std::string value = env_value ? env_value : "";
result = result.replace(match.position(), match.length(), value);
}
// dequote the result
return result;
}
std::string random_alphanumeric_string(int length)
{
static std::mt19937 generator(std::random_device{}());
@ -366,5 +345,91 @@ std::string center_align(const std::string & str, int width) {
}
std::string replace_with_environment_variables_like_bash(std::string str) {
// Combined regex pattern for both ${var} and $var formats
std::regex var_pattern("\\$(?:\\{([^}]+)\\}|([a-zA-Z0-9_]+))");
std::string result = str;
std::smatch match;
while (std::regex_search(result, match, var_pattern)) {
// match[1] will contain capture from ${var} format
// match[2] will contain capture from $var format
std::string var_name = match[1].matched ? match[1].str() : match[2].str();
// Get value from system environment variables
const char* env_value = std::getenv(var_name.c_str());
std::string value = env_value ? env_value : "";
result = result.replace(match.position(), match.length(), value);
}
// dequote the result
return result;
}
std::string substitute_provided_key_value_pairs(std::string str, const std::map<std::string, std::string> &env_vars)
{
// Combined regex pattern for both ${var} and $var formats
std::regex var_pattern("\\$(?:\\{([^}]+)\\}|([a-zA-Z0-9_]+))");
std::string result = str;
std::smatch match;
while (std::regex_search(result, match, var_pattern)) {
// match[1] will contain capture from ${var} format
// match[2] will contain capture from $var format
std::string var_name = match[1].matched ? match[1].str() : match[2].str();
// Get value from environment variables map
auto it = env_vars.find(var_name);
std::string value = (it != env_vars.end()) ? it->second : "";
result = result.replace(match.position(), match.length(), value);
}
return result;
}
int get_console_width()
{
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) {
return w.ws_col;
}
// Fallback to a reasonable default if we can't get the width
return 80;
}
std::string remove_return(std::string str)
{
str.erase(std::remove(str.begin(), str.end(), '\n'), str.end());
return str;
}
std::string get_line_wrap(std::string &src, int maxchars)
{
if (src.empty())
return "";
if (src.length() <= maxchars)
{
std::string out = src;
src.erase();
return remove_return(out) + '\n';
}
// find last whitespace up to but not more than maxchars
size_t grab_to=maxchars;
size_t lastreturn = src.rfind('\n', maxchars);
size_t lastspace = src.rfind(' ', maxchars);
if (lastreturn != std::string::npos)
grab_to = lastreturn;
else if (lastspace != std::string::npos)
grab_to = lastspace;
std::string out = src.substr(0, grab_to);
src = src.substr(grab_to + 1);
return remove_return(out) + '\n';
}
} // namespace dropshell

View File

@ -2,6 +2,7 @@
#include <string>
#include <vector>
#include <map>
#include "output.hpp"
@ -41,8 +42,6 @@ void ensure_directories_exist(std::vector<std::string> directories);
std::vector<int> search(const std::string &pat, const std::string &txt);
int count_substring(const std::string &substring, const std::string &text);
std::string replace_with_environment_variables_like_bash(std::string str);
std::string random_alphanumeric_string(int length);
int die(const std::string & msg);
@ -53,4 +52,11 @@ std::string left_align(const std::string & str, int width);
std::string right_align(const std::string & str, int width);
std::string center_align(const std::string & str, int width);
std::string replace_with_environment_variables_like_bash(std::string str);
std::string substitute_provided_key_value_pairs(std::string str, const std::map<std::string, std::string> & env_vars);
int get_console_width();
std::string get_line_wrap(std::string & src, int maxchars);
} // namespace dropshell