Compare commits
5 Commits
2025.0519.
...
2025.0521.
Author | SHA1 | Date | |
---|---|---|---|
b3a57f13dc | |||
270d6ef792 | |||
9063edb45f | |||
fc6b310b89 | |||
7a710b525f |
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.10)
|
|||||||
project(dropshell VERSION 1.0.0 LANGUAGES CXX)
|
project(dropshell VERSION 1.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
set(CMAKE_C_STANDARD 23)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
# Set default build type to Release if not specified
|
# Set default build type to Release if not specified
|
||||||
|
@ -50,7 +50,7 @@ function install_bb64() {
|
|||||||
_die "Curl is not installed. Curl is required for agent installation."
|
_die "Curl is not installed. Curl is required for agent installation."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
curl -fsSL "https://gitea.jde.nz/public/bb64/releases/download/latest/install.sh" | bash -s -- "$AGENT_PATH" "$(id -u $USER):$(id -g $USER)"
|
curl -fsSL "https://gitea.jde.nz/public/bb64/releases/download/latest/install.sh" | bash -s -- "$AGENT_LOCAL_PATH" "$(id -u $USER):$(id -g $USER)"
|
||||||
|
|
||||||
# test result code from curl
|
# test result code from curl
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
@ -58,7 +58,7 @@ function install_bb64() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# test if bb64 is installed
|
# test if bb64 is installed
|
||||||
"$AGENT_PATH/bb64" -v
|
"$AGENT_LOCAL_PATH/bb64" -v
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
_die "bb64 did not install correctly."
|
_die "bb64 did not install correctly."
|
||||||
fi
|
fi
|
||||||
@ -71,11 +71,11 @@ function install_bb64() {
|
|||||||
#-------------------------------------------------------------------------
|
#-------------------------------------------------------------------------
|
||||||
|
|
||||||
set -a
|
set -a
|
||||||
AGENT_PATH="$SCRIPT_DIR"
|
AGENT_LOCAL_PATH="$SCRIPT_DIR"
|
||||||
set +a
|
set +a
|
||||||
|
|
||||||
_check_required_env_vars "AGENT_PATH"
|
_check_required_env_vars "AGENT_LOCAL_PATH"
|
||||||
echo "Installing host agent into $AGENT_PATH"
|
echo "Installing host agent into $AGENT_LOCAL_PATH"
|
||||||
|
|
||||||
_check_docker_installed || _die "Docker is required."
|
_check_docker_installed || _die "Docker is required."
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ namespace dropshell
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create backups directory locally if it doesn't exist
|
// Create backups directory locally if it doesn't exist
|
||||||
std::string local_backups_dir = gConfig().get_local_backup_path();
|
std::string local_backups_dir = localpath::backups();
|
||||||
if (local_backups_dir.empty())
|
if (local_backups_dir.empty())
|
||||||
{
|
{
|
||||||
error << "Error: Local backups directory not found" << std::endl;
|
error << "Error: Local backups directory not found" << std::endl;
|
||||||
|
63
source/src/commands/create-server.cpp
Normal file
63
source/src/commands/create-server.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "command_registry.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "utils/utils.hpp"
|
||||||
|
#include "utils/directories.hpp"
|
||||||
|
#include "shared_commands.hpp"
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include "utils/assert.hpp"
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
void create_server_autocomplete(const CommandContext& ctx);
|
||||||
|
int create_server_handler(const CommandContext& ctx);
|
||||||
|
|
||||||
|
static std::vector<std::string> create_server_name_list={"create-server"};
|
||||||
|
|
||||||
|
// Static registration
|
||||||
|
struct CreateServerCommandRegister {
|
||||||
|
CreateServerCommandRegister() {
|
||||||
|
CommandRegistry::instance().register_command({
|
||||||
|
create_server_name_list,
|
||||||
|
create_server_handler,
|
||||||
|
create_server_autocomplete,
|
||||||
|
false, // hidden
|
||||||
|
true, // requires_config
|
||||||
|
true, // requires_install
|
||||||
|
1, // min_args (after command)
|
||||||
|
1, // max_args (after command)
|
||||||
|
"create-server [SERVER]",
|
||||||
|
"Create a new server entry on this host.",
|
||||||
|
// heredoc
|
||||||
|
R"(
|
||||||
|
Create a new server entry on this host.
|
||||||
|
Note you will need to use ds install SERVER to prepare the service for use.
|
||||||
|
|
||||||
|
create-server SERVER
|
||||||
|
)"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} create_server_command_register;
|
||||||
|
|
||||||
|
|
||||||
|
void create_server_autocomplete(const CommandContext& ctx) {
|
||||||
|
return; // can't autocomplete as it's a new server!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int create_server_handler(const CommandContext& ctx) {
|
||||||
|
// create a new server entry on this host
|
||||||
|
if (ctx.args.size() == 0) {
|
||||||
|
error << "No server name provided" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bool ok = create_server(ctx.args[0]);
|
||||||
|
return ok ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dropshell
|
@ -94,7 +94,7 @@ namespace dropshell
|
|||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(readme_file, line))
|
while (std::getline(readme_file, line))
|
||||||
{
|
{
|
||||||
info << substitute_provided_key_value_pairs(line, all_env_vars) << std::endl;
|
rawout << substitute_provided_key_value_pairs(line, all_env_vars) << std::endl;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
63
source/src/commands/create-template.cpp
Normal file
63
source/src/commands/create-template.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "command_registry.hpp"
|
||||||
|
#include "config.hpp"
|
||||||
|
#include "utils/utils.hpp"
|
||||||
|
#include "utils/directories.hpp"
|
||||||
|
#include "shared_commands.hpp"
|
||||||
|
#include "version.hpp"
|
||||||
|
#include "utils/assert.hpp"
|
||||||
|
#include "templates.hpp"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace dropshell {
|
||||||
|
|
||||||
|
void create_template_autocomplete(const CommandContext& ctx);
|
||||||
|
int create_template_handler(const CommandContext& ctx);
|
||||||
|
|
||||||
|
static std::vector<std::string> create_template_name_list={"create-template"};
|
||||||
|
|
||||||
|
// Static registration
|
||||||
|
struct CreateTemplateCommandRegister {
|
||||||
|
CreateTemplateCommandRegister() {
|
||||||
|
CommandRegistry::instance().register_command({
|
||||||
|
create_template_name_list,
|
||||||
|
create_template_handler,
|
||||||
|
create_template_autocomplete,
|
||||||
|
false, // hidden
|
||||||
|
true, // requires_config
|
||||||
|
true, // requires_install
|
||||||
|
1, // min_args (after command)
|
||||||
|
1, // max_args (after command)
|
||||||
|
"create-template TEMPLATE",
|
||||||
|
"Create a new template.",
|
||||||
|
// heredoc
|
||||||
|
R"(
|
||||||
|
Create a new template.
|
||||||
|
|
||||||
|
create-template TEMPLATE
|
||||||
|
)"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} create_template_command_register;
|
||||||
|
|
||||||
|
|
||||||
|
void create_template_autocomplete(const CommandContext& ctx) {
|
||||||
|
return; // can't autocomplete as it's a new server!
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int create_template_handler(const CommandContext& ctx) {
|
||||||
|
// create a new server entry on this host
|
||||||
|
if (ctx.args.size() == 0) {
|
||||||
|
error << "No template name provided" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bool ok = gTemplateManager().create_template(ctx.args[0]);
|
||||||
|
return ok ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dropshell
|
@ -14,7 +14,7 @@ namespace dropshell
|
|||||||
{
|
{
|
||||||
|
|
||||||
int nuke_handler(const CommandContext &ctx);
|
int nuke_handler(const CommandContext &ctx);
|
||||||
static std::vector<std::string> nuke_name_list = {"nuke"};
|
static std::vector<std::string> nuke_name_list = {"destroy","nuke"};
|
||||||
|
|
||||||
// Static registration
|
// Static registration
|
||||||
struct NukeCommandRegister
|
struct NukeCommandRegister
|
||||||
@ -29,15 +29,15 @@ namespace dropshell
|
|||||||
true, // requires_install
|
true, // requires_install
|
||||||
2, // min_args (after command)
|
2, // min_args (after command)
|
||||||
2, // max_args (after command)
|
2, // max_args (after command)
|
||||||
"nuke SERVER SERVICE|all",
|
"destroy SERVER SERVICE|all",
|
||||||
"Nuke a service on a server. Destroys everything, both local and remote!",
|
"Destroy a service on a server. Erases everything, both local and remote!",
|
||||||
// heredoc
|
// heredoc
|
||||||
R"(
|
R"(
|
||||||
Nuke a service.
|
Nuke a service.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
nuke SERVER SERVICE nuke the given service on the given server.
|
destroy SERVER SERVICE destroy the given service on the given server.
|
||||||
nuke SERVER all nuke all services on the given server.
|
destroy SERVER all destroy all services on the given server.
|
||||||
|
|
||||||
Note: This command is destructive and will destroy all data and all configuration,
|
Note: This command is destructive and will destroy all data and all configuration,
|
||||||
both on the dropshell host and on the remote server.
|
both on the dropshell host and on the remote server.
|
@ -45,7 +45,7 @@ struct HelpCommandRegister {
|
|||||||
|
|
||||||
|
|
||||||
void help_autocomplete(const CommandContext& ctx) {
|
void help_autocomplete(const CommandContext& ctx) {
|
||||||
if (ctx.args.size() == 1) {
|
if (ctx.args.size() == 0) {
|
||||||
// list all commands
|
// list all commands
|
||||||
for (const auto& cmd : CommandRegistry::instance().list_primary_commands(false)) {
|
for (const auto& cmd : CommandRegistry::instance().list_primary_commands(false)) {
|
||||||
rawout << cmd << std::endl;
|
rawout << cmd << std::endl;
|
||||||
@ -55,6 +55,11 @@ void help_autocomplete(const CommandContext& ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void show_command(const std::string& cmd) {
|
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);
|
const auto& cmd_info = CommandRegistry::instance().find_command(cmd);
|
||||||
if (!cmd_info)
|
if (!cmd_info)
|
||||||
{
|
{
|
||||||
@ -62,9 +67,22 @@ void show_command(const std::string& cmd) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
info << " ";
|
if (cmd_info->help_usage.length() < width-secondcol)
|
||||||
info << left_align(cmd_info->help_usage, 32);
|
{
|
||||||
info << cmd_info->help_description << std::endl;
|
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;
|
extern const std::string VERSION;
|
||||||
@ -106,7 +124,7 @@ int help_handler(const CommandContext& ctx) {
|
|||||||
if (ctx.args.size() > 0)
|
if (ctx.args.size() > 0)
|
||||||
return show_command_help(ctx.args[0]);
|
return show_command_help(ctx.args[0]);
|
||||||
|
|
||||||
info << std::endl;
|
std::cout << std::endl;
|
||||||
maketitle("DropShell version " + VERSION);
|
maketitle("DropShell version " + VERSION);
|
||||||
info << std::endl;
|
info << std::endl;
|
||||||
info << "A tool for managing remote servers, by " << AUTHOR << std::endl;
|
info << "A tool for managing remote servers, by " << AUTHOR << std::endl;
|
||||||
@ -120,16 +138,18 @@ int help_handler(const CommandContext& ctx) {
|
|||||||
{
|
{
|
||||||
// show more!
|
// show more!
|
||||||
show_command("list");
|
show_command("list");
|
||||||
std::cout << std::endl;
|
info << std::endl;
|
||||||
show_command("install");
|
show_command("install");
|
||||||
show_command("uninstall");
|
show_command("uninstall");
|
||||||
show_command("nuke");
|
show_command("nuke");
|
||||||
std::cout << std::endl;
|
info << std::endl;
|
||||||
show_command("start");
|
show_command("start");
|
||||||
show_command("stop");
|
show_command("stop");
|
||||||
std::cout << std::endl;
|
info << std::endl;
|
||||||
show_command("ssh");
|
show_command("ssh");
|
||||||
std::cout << std::endl;
|
info << std::endl;
|
||||||
|
show_command("create-server");
|
||||||
|
show_command("create-service");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ namespace dropshell
|
|||||||
0, // min_args (after command)
|
0, // min_args (after command)
|
||||||
2, // max_args (after command)
|
2, // max_args (after command)
|
||||||
"install [SERVER] [SERVICE|all]",
|
"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
|
// heredoc
|
||||||
R"(
|
R"(
|
||||||
Install components on a server. This is safe to re-run (non-destructive) and used to update
|
Install components on a server. This is safe to re-run (non-destructive) and used to update
|
||||||
@ -238,30 +238,22 @@ namespace dropshell
|
|||||||
{
|
{
|
||||||
maketitle("Installing dropshell agent on this computer...");
|
maketitle("Installing dropshell agent on this computer...");
|
||||||
|
|
||||||
std::vector<std::filesystem::path> paths = {
|
// clear out old cruft.
|
||||||
gConfig().get_local_template_cache_path(),
|
std::filesystem::remove_all(localpath::agent_local());
|
||||||
gConfig().get_local_backup_path(),
|
std::filesystem::remove_all(localpath::agent_remote());
|
||||||
gConfig().get_local_tempfiles_path(),
|
|
||||||
localpath::agent()};
|
|
||||||
for (auto &p : gConfig().get_local_server_definition_paths())
|
|
||||||
paths.push_back(p);
|
|
||||||
|
|
||||||
for (auto &p : paths)
|
// recreate the directories.
|
||||||
if (!std::filesystem::exists(p))
|
localpath::create_directories();
|
||||||
{
|
|
||||||
info << "Creating directory: " << p << std::endl;
|
|
||||||
std::filesystem::create_directories(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the agent-local directory.
|
// populate the agent-local directory.
|
||||||
recreate_agent_local::recreate_tree(localpath::agent());
|
recreate_agent_local::recreate_tree(localpath::agent_local());
|
||||||
|
|
||||||
// run the local agent installer.
|
// run the local agent installer.
|
||||||
execute_local_command(localpath::agent(), "agent-install.sh",{}, nullptr, cMode::Defaults | cMode::NoBB64);
|
execute_local_command(localpath::agent_local(), "agent-install.sh",{}, nullptr, cMode::Defaults | cMode::NoBB64);
|
||||||
|
|
||||||
// create the agent-remote directory.
|
// populate the agent-remote directory.
|
||||||
info << "Creating local files to copy to remote agents..." << std::endl;
|
info << "Creating local files to copy to remote agents..." << std::endl;
|
||||||
recreate_agent_remote::recreate_tree(localpath::files_for_remote_agent());
|
recreate_agent_remote::recreate_tree(localpath::agent_remote());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -288,7 +280,7 @@ namespace dropshell
|
|||||||
// now create the agent.
|
// now create the agent.
|
||||||
// copy across from the local agent files.
|
// copy across from the local agent files.
|
||||||
info << "Copying local agent files to remote server... " << std::flush;
|
info << "Copying local agent files to remote server... " << std::flush;
|
||||||
shared_commands::rsync_tree_to_remote(localpath::files_for_remote_agent(), agent_path, server_env, false);
|
shared_commands::rsync_tree_to_remote(localpath::agent_remote(), agent_path, server_env, false);
|
||||||
info << "done." << std::endl;
|
info << "done." << std::endl;
|
||||||
|
|
||||||
// run the agent installer. Can't use BB64 yet, as we're installing it on the remote server.
|
// run the agent installer. Can't use BB64 yet, as we're installing it on the remote server.
|
||||||
|
@ -57,7 +57,7 @@ namespace dropshell
|
|||||||
|
|
||||||
std::vector<shared_commands::cBackupFileName> get_backup_files(const std::string &server, const std::string &match_service = "", const std::string &match_template_name = "")
|
std::vector<shared_commands::cBackupFileName> get_backup_files(const std::string &server, const std::string &match_service = "", const std::string &match_template_name = "")
|
||||||
{
|
{
|
||||||
std::string local_backups_dir = gConfig().get_local_backup_path();
|
std::string local_backups_dir = localpath::backups();
|
||||||
if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir))
|
if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir))
|
||||||
{
|
{
|
||||||
error << "Error: Local backups directory not found: " << local_backups_dir << std::endl;
|
error << "Error: Local backups directory not found: " << local_backups_dir << std::endl;
|
||||||
@ -137,7 +137,7 @@ namespace dropshell
|
|||||||
debug << " Server: " << server << std::endl;
|
debug << " Server: " << server << std::endl;
|
||||||
debug << " Service: " << service << std::endl;
|
debug << " Service: " << service << std::endl;
|
||||||
|
|
||||||
std::string local_backups_dir = gConfig().get_local_backup_path();
|
std::string local_backups_dir = localpath::backups();
|
||||||
if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir))
|
if (local_backups_dir.empty() || !std::filesystem::exists(local_backups_dir))
|
||||||
{
|
{
|
||||||
error << "Error: Local backups directory not found: " << local_backups_dir << std::endl;
|
error << "Error: Local backups directory not found: " << local_backups_dir << std::endl;
|
||||||
|
@ -46,6 +46,15 @@ bool config::load_config() { // load json config file.
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _append(std::vector<std::string> & a, const std::vector<std::string> & b) {
|
||||||
|
if (b.empty())
|
||||||
|
return;
|
||||||
|
if (a.empty())
|
||||||
|
a = b;
|
||||||
|
else
|
||||||
|
a.insert(std::end(a), std::begin(b), std::end(b));
|
||||||
|
}
|
||||||
|
|
||||||
bool config::save_config(bool create_aux_directories)
|
bool config::save_config(bool create_aux_directories)
|
||||||
{
|
{
|
||||||
std::string config_path = localfile::dropshell_json();
|
std::string config_path = localfile::dropshell_json();
|
||||||
@ -61,37 +70,27 @@ bool config::save_config(bool create_aux_directories)
|
|||||||
if (!mIsConfigSet)
|
if (!mIsConfigSet)
|
||||||
{
|
{
|
||||||
std::string homedir = localpath::current_user_home();
|
std::string homedir = localpath::current_user_home();
|
||||||
std::string dropshell_base = homedir + "/.dropshell";
|
std::string dropshell_base = homedir + "/.local/dropshell_files";
|
||||||
mConfig["tempfiles"] = dropshell_base + "/tmp";
|
|
||||||
mConfig["backups"] = dropshell_base + "/backups";
|
|
||||||
|
|
||||||
mConfig["template_cache"] = dropshell_base + "/template_cache";
|
|
||||||
mConfig["template_registry_URLs"] = {
|
|
||||||
"https://templates.dropshell.app"
|
|
||||||
};
|
|
||||||
mConfig["template_local_paths"] = {
|
|
||||||
dropshell_base + "/local_templates"
|
|
||||||
};
|
|
||||||
|
|
||||||
mConfig["server_definition_paths"] = {
|
mConfig["server_definition_paths"] = {
|
||||||
dropshell_base + "/servers"
|
dropshell_base + "/servers"
|
||||||
};
|
};
|
||||||
mConfig["template_upload_registry_url"] = "https://templates.dropshell.app";
|
mConfig["template_local_paths"] = {
|
||||||
mConfig["template_upload_registry_token"] = "SECRETTOKEN";
|
dropshell_base + "/local_templates"
|
||||||
|
};
|
||||||
|
mConfig["template_registry_URLs"] = {
|
||||||
|
"https://templates.dropshell.app"
|
||||||
|
};
|
||||||
|
mConfig["template_upload_token"] = "SECRETTOKEN";
|
||||||
}
|
}
|
||||||
|
|
||||||
config_file << mConfig.dump(4);
|
config_file << mConfig.dump(4);
|
||||||
config_file.close();
|
config_file.close();
|
||||||
|
|
||||||
if (create_aux_directories) {
|
if (create_aux_directories) {
|
||||||
std::vector<std::filesystem::path> paths = {
|
std::vector<std::string> paths;
|
||||||
get_local_template_cache_path(),
|
_append(paths, get_local_template_paths());
|
||||||
get_local_backup_path(),
|
_append(paths, get_local_server_definition_paths());
|
||||||
get_local_tempfiles_path()
|
|
||||||
};
|
|
||||||
for (auto & p : get_local_server_definition_paths())
|
|
||||||
paths.push_back(p);
|
|
||||||
|
|
||||||
for (auto & p : paths)
|
for (auto & p : paths)
|
||||||
if (!std::filesystem::exists(p))
|
if (!std::filesystem::exists(p))
|
||||||
{
|
{
|
||||||
@ -100,6 +99,11 @@ bool config::save_config(bool create_aux_directories)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug << "Config paths: " << std::endl;
|
||||||
|
for (auto [key,value] : mConfig.items()) {
|
||||||
|
debug << " " << key << ": " << value << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,31 +114,20 @@ bool config::is_config_set() const
|
|||||||
|
|
||||||
bool config::is_agent_installed()
|
bool config::is_agent_installed()
|
||||||
{
|
{
|
||||||
return std::filesystem::exists(localpath::agent() + "/bb64");
|
return std::filesystem::exists(localfile::bb64());
|
||||||
}
|
|
||||||
|
|
||||||
std::string config::get_local_tempfiles_path() {
|
|
||||||
return mConfig["tempfiles"];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string config::get_local_backup_path() {
|
|
||||||
return mConfig["backups"];
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string config::get_local_template_cache_path() {
|
|
||||||
return mConfig["template_cache"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> config::get_template_registry_urls() {
|
std::vector<std::string> config::get_template_registry_urls() {
|
||||||
nlohmann::json template_registry_urls = mConfig["template_registry_URLs"];
|
nlohmann::json template_registry_urls = mConfig["template_registry_URLs"];
|
||||||
std::vector<std::string> urls;
|
std::vector<std::string> urls;
|
||||||
for (auto &url : template_registry_urls) {
|
for (auto &url : template_registry_urls) {
|
||||||
urls.push_back(url);
|
if (url.is_string() && !url.empty())
|
||||||
|
urls.push_back(url);
|
||||||
}
|
}
|
||||||
return urls;
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> config::get_template_local_paths()
|
std::vector<std::string> config::get_local_template_paths()
|
||||||
{
|
{
|
||||||
nlohmann::json template_local_paths = mConfig["template_local_paths"];
|
nlohmann::json template_local_paths = mConfig["template_local_paths"];
|
||||||
std::vector<std::string> paths;
|
std::vector<std::string> paths;
|
||||||
@ -147,23 +140,40 @@ std::vector<std::string> config::get_template_local_paths()
|
|||||||
|
|
||||||
std::vector<std::string> config::get_local_server_definition_paths() {
|
std::vector<std::string> config::get_local_server_definition_paths() {
|
||||||
nlohmann::json server_definition_paths = mConfig["server_definition_paths"];
|
nlohmann::json server_definition_paths = mConfig["server_definition_paths"];
|
||||||
|
|
||||||
std::vector<std::string> paths;
|
std::vector<std::string> paths;
|
||||||
for (auto &path : server_definition_paths) {
|
for (auto &path : server_definition_paths) {
|
||||||
if (path.is_string() && !path.empty())
|
if (path.is_string() && !path.empty())
|
||||||
paths.push_back(path);
|
paths.push_back(path);
|
||||||
else
|
|
||||||
warning << "Invalid server definition path: " << path << std::endl;
|
|
||||||
}
|
}
|
||||||
return paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string config::get_template_upload_registry_url() {
|
std::string config::get_server_create_path()
|
||||||
return mConfig["template_upload_registry_url"];
|
{
|
||||||
|
std::vector<std::string> paths = get_local_server_definition_paths();
|
||||||
|
if (paths.empty())
|
||||||
|
return "";
|
||||||
|
return paths[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string config::get_template_upload_registry_token() {
|
std::string config::get_template_create_path()
|
||||||
return mConfig["template_upload_registry_token"];
|
{
|
||||||
|
std::vector<std::string> paths = get_local_template_paths();
|
||||||
|
if (paths.empty())
|
||||||
|
return "";
|
||||||
|
return paths[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string config::get_template_upload_url()
|
||||||
|
{
|
||||||
|
std::vector<std::string> urls = get_template_registry_urls();
|
||||||
|
if (urls.empty())
|
||||||
|
return "";
|
||||||
|
return urls[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string config::get_template_upload_token() {
|
||||||
|
return mConfig["template_upload_token"];
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dropshell
|
} // namespace dropshell
|
@ -17,15 +17,14 @@ class config {
|
|||||||
bool is_config_set() const;
|
bool is_config_set() const;
|
||||||
static bool is_agent_installed();
|
static bool is_agent_installed();
|
||||||
|
|
||||||
std::string get_local_tempfiles_path();
|
|
||||||
std::string get_local_backup_path();
|
|
||||||
std::string get_local_template_cache_path();
|
|
||||||
std::vector<std::string> get_template_registry_urls();
|
std::vector<std::string> get_template_registry_urls();
|
||||||
std::vector<std::string> get_template_local_paths();
|
std::vector<std::string> get_local_template_paths();
|
||||||
std::vector<std::string> get_local_server_definition_paths();
|
std::vector<std::string> get_local_server_definition_paths();
|
||||||
|
|
||||||
std::string get_template_upload_registry_url();
|
std::string get_server_create_path();
|
||||||
std::string get_template_upload_registry_token();
|
std::string get_template_create_path();
|
||||||
|
std::string get_template_upload_url();
|
||||||
|
std::string get_template_upload_token();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nlohmann::json mConfig;
|
nlohmann::json mConfig;
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "templates.hpp"
|
#include "templates.hpp"
|
||||||
#include "contrib/transwarp.hpp"
|
#include "contrib/transwarp.hpp"
|
||||||
|
#include "utils/output.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@ -76,16 +77,16 @@ bool create_server(const std::string &server_name)
|
|||||||
// 1. check if server name already exists
|
// 1. check if server name already exists
|
||||||
std::string server_existing_dir = localpath::server(server_name);
|
std::string server_existing_dir = localpath::server(server_name);
|
||||||
if (!server_existing_dir.empty()) {
|
if (!server_existing_dir.empty()) {
|
||||||
std::cerr << "Error: Server name already exists: " << server_name << std::endl;
|
error << "Error: Server name already exists: " << server_name << std::endl;
|
||||||
std::cerr << "Current server path: " << server_existing_dir << std::endl;
|
info << "Current server path: " << server_existing_dir << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. create a new directory in the user config directory
|
// 2. create a new directory in the user config directory
|
||||||
auto lsdp = gConfig().get_local_server_definition_paths();
|
auto lsdp = gConfig().get_local_server_definition_paths();
|
||||||
if (lsdp.empty() || lsdp[0].empty()) {
|
if (lsdp.empty() || lsdp[0].empty()) {
|
||||||
std::cerr << "Error: Local server definition path not found" << std::endl;
|
error << "Error: Local server definition path not found" << std::endl;
|
||||||
std::cerr << "Run 'dropshell edit' to configure DropShell" << std::endl;
|
info << "Run 'dropshell edit' to configure DropShell" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
std::string server_dir = lsdp[0] + "/" + server_name;
|
std::string server_dir = lsdp[0] + "/" + server_name;
|
||||||
@ -93,20 +94,20 @@ bool create_server(const std::string &server_name)
|
|||||||
|
|
||||||
// 3. create a template server.env file in the server directory
|
// 3. create a template server.env file in the server directory
|
||||||
std::string user = getenv("USER");
|
std::string user = getenv("USER");
|
||||||
std::string server_env_path = server_dir + "/server.env";
|
std::string server_env_path = server_dir + "/server.json";
|
||||||
std::ofstream server_env_file(server_env_path);
|
std::ofstream server_env_file(server_env_path);
|
||||||
server_env_file << "SSH_HOST=" << server_name << std::endl;
|
server_env_file << "{" << std::endl;
|
||||||
server_env_file << "SSH_USER=" << user << std::endl;
|
server_env_file << " \"SSH_HOST\": \"" << server_name << "\"," << std::endl;
|
||||||
server_env_file << "SSH_PORT=" << 22 << std::endl;
|
server_env_file << " \"SSH_USER\": \"" << user << "\"," << std::endl;
|
||||||
server_env_file << std::endl;
|
server_env_file << " \"SSH_PORT\": " << 22 << "," << std::endl;
|
||||||
server_env_file << "DROPSHELL_DIR=/home/"+user+"/.dropshell" << std::endl;
|
server_env_file << " \"DROPSHELL_DIR\": \"" << "/home/"+user+"/.dropshell\"" << std::endl;
|
||||||
|
server_env_file << "}" << std::endl;
|
||||||
server_env_file.close();
|
server_env_file.close();
|
||||||
|
|
||||||
std::cout << "Server created successfully: " << server_name << std::endl;
|
std::cout << "Server created successfully: " << server_name << std::endl;
|
||||||
std::cout << "Please complete the installation:" <<std::endl;
|
std::cout << "Please complete the installation:" <<std::endl;
|
||||||
std::cout << "1) edit the server configuration: dropshell edit " << server_name << std::endl;
|
std::cout << "1) edit the server configuration: dropshell edit " << server_name << std::endl;
|
||||||
std::cout << "2) test ssh is working: dropshell ssh " << server_name << std::endl;
|
std::cout << "2) install the server: dropshell install " << server_name << std::endl;
|
||||||
std::cout << "3) install the server: dropshell install " << server_name << std::endl;
|
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -137,7 +137,7 @@ std::set<std::string> list_backups(const std::string &server_name, const std::st
|
|||||||
return backups;
|
return backups;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string backups_dir = gConfig().get_local_backup_path();
|
std::string backups_dir = localpath::backups();
|
||||||
if (backups_dir.empty())
|
if (backups_dir.empty())
|
||||||
return backups;
|
return backups;
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto local_template_paths = gConfig().get_template_local_paths();
|
auto local_template_paths = gConfig().get_local_template_paths();
|
||||||
if (local_template_paths.empty()) {
|
if (local_template_paths.empty()) {
|
||||||
std::cerr << "Error: No local template paths found" << std::endl;
|
std::cerr << "Error: No local template paths found" << std::endl;
|
||||||
std::cerr << "Run 'dropshell edit' to add one to the DropShell config" << std::endl;
|
std::cerr << "Run 'dropshell edit' to add one to the DropShell config" << std::endl;
|
||||||
@ -252,7 +252,7 @@
|
|||||||
ASSERT(mSources.empty(), "Template manager already loaded (sources are not empty).");
|
ASSERT(mSources.empty(), "Template manager already loaded (sources are not empty).");
|
||||||
ASSERT(gConfig().is_config_set(), "Config not set.");
|
ASSERT(gConfig().is_config_set(), "Config not set.");
|
||||||
ASSERT(!mLoaded, "Template manager already loaded.");
|
ASSERT(!mLoaded, "Template manager already loaded.");
|
||||||
auto local_template_paths = gConfig().get_template_local_paths();
|
auto local_template_paths = gConfig().get_local_template_paths();
|
||||||
if (local_template_paths.empty())
|
if (local_template_paths.empty())
|
||||||
return;
|
return;
|
||||||
for (const auto& path : local_template_paths)
|
for (const auto& path : local_template_paths)
|
||||||
|
@ -41,6 +41,16 @@ namespace localfile {
|
|||||||
return (servicepath.empty() ? "" : (fs::path(servicepath) / ".template_info.env").string());
|
return (servicepath.empty() ? "" : (fs::path(servicepath) / ".template_info.env").string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string template_example()
|
||||||
|
{
|
||||||
|
return localpath::agent_local() + "/template_example";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string bb64()
|
||||||
|
{
|
||||||
|
return localpath::agent_local() + "/bb64";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace localfile
|
} // namespace localfile
|
||||||
|
|
||||||
|
|
||||||
@ -62,16 +72,17 @@ namespace localpath {
|
|||||||
|
|
||||||
std::string remote_versions(const std::string &server_name, const std::string &service_name)
|
std::string remote_versions(const std::string &server_name, const std::string &service_name)
|
||||||
{
|
{
|
||||||
std::string template_cache_path = gConfig().get_local_template_cache_path();
|
std::string template_cache_path = localpath::template_cache();
|
||||||
return ((template_cache_path.empty() || service_name.empty()) ? "" :
|
return ((template_cache_path.empty() || service_name.empty()) ? "" :
|
||||||
(template_cache_path+"/remote_versions/"+service_name+".json"));
|
(template_cache_path+"/remote_versions/"+service_name+".json"));
|
||||||
}
|
}
|
||||||
std::string agent(){
|
std::string agent_local()
|
||||||
return current_user_home() + "/.local/dropshell_agent";
|
|
||||||
}
|
|
||||||
std::string files_for_remote_agent()
|
|
||||||
{
|
{
|
||||||
return agent() + "/files_for_remote_agent";
|
return current_user_home()+"/.local/dropshell_agent/agent-local";
|
||||||
|
}
|
||||||
|
std::string agent_remote()
|
||||||
|
{
|
||||||
|
return current_user_home() + "/.local/dropshell_agent/agent-remote";
|
||||||
}
|
}
|
||||||
std::string current_user_home()
|
std::string current_user_home()
|
||||||
{
|
{
|
||||||
@ -84,6 +95,50 @@ namespace localpath {
|
|||||||
warning << "Couldn't determine user directory" << std::endl;
|
warning << "Couldn't determine user directory" << std::endl;
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string dropshell_files()
|
||||||
|
{
|
||||||
|
return current_user_home() + "/.local/dropshell_files";
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string backups()
|
||||||
|
{
|
||||||
|
return dropshell_files() + "/backups";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string temp_files()
|
||||||
|
{
|
||||||
|
return dropshell_files() + "/temp_files";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string template_cache()
|
||||||
|
{
|
||||||
|
return dropshell_files() + "template_cache";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool create_directories()
|
||||||
|
{
|
||||||
|
std::vector<std::filesystem::path> paths = {
|
||||||
|
dropshell_files(),
|
||||||
|
agent_local(),
|
||||||
|
agent_remote(),
|
||||||
|
template_cache(),
|
||||||
|
backups(),
|
||||||
|
temp_files()
|
||||||
|
};
|
||||||
|
for (auto &p : gConfig().get_local_server_definition_paths())
|
||||||
|
paths.push_back(p);
|
||||||
|
|
||||||
|
for (auto &p : paths)
|
||||||
|
if (!p.empty() && !std::filesystem::exists(p))
|
||||||
|
{
|
||||||
|
info << "Creating directory: " << p << std::endl;
|
||||||
|
std::filesystem::create_directories(p);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace localpath
|
} // namespace localpath
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------
|
||||||
|
@ -14,9 +14,29 @@ namespace dropshell {
|
|||||||
// ~/.config/dropshell/dropshell.json
|
// ~/.config/dropshell/dropshell.json
|
||||||
|
|
||||||
// ~/.local/dropshell_agent
|
// ~/.local/dropshell_agent
|
||||||
// |-- bb64 (only used locally, as it's for the local machine's architecture!)
|
// |-- agent-local
|
||||||
// |-- files_for_remote_agent
|
// |-- agent-install.sh
|
||||||
// |-- (other agent files, including _allservicesstatus.sh)
|
// |-- bb64 (only used locally, as it's for the local machine's architecture!)
|
||||||
|
// |-- template_example
|
||||||
|
// |-- agent-remote
|
||||||
|
// |-- (remote agent files, including _allservicesstatus.sh)
|
||||||
|
|
||||||
|
// ~/.local/dropshell_files
|
||||||
|
// |-- backups
|
||||||
|
// |-- katie-_-squashkiwi-_-squashkiwi-test-_-2025-04-28_21-23-59.tgz
|
||||||
|
// |-- temp_files
|
||||||
|
// |-- template_cache
|
||||||
|
// |-- templates
|
||||||
|
// | |-- <template_name>.json
|
||||||
|
// | |-- <template_name>
|
||||||
|
// | |-- (...script files...)
|
||||||
|
// | |-- _default.env
|
||||||
|
// | |-- config
|
||||||
|
// | |-- service.env
|
||||||
|
// | |-- .template_info.env
|
||||||
|
// | |-- (...other service config files...)
|
||||||
|
// |-- remote_versions
|
||||||
|
// | |-- server_name-service_name.json
|
||||||
|
|
||||||
// server_definition_path
|
// server_definition_path
|
||||||
// |-- <server_name>
|
// |-- <server_name>
|
||||||
@ -27,23 +47,7 @@ namespace dropshell {
|
|||||||
// |-- .template_info.env
|
// |-- .template_info.env
|
||||||
// |-- (...other config files for specific server&service...)
|
// |-- (...other config files for specific server&service...)
|
||||||
|
|
||||||
// backup path
|
|
||||||
// |-- katie-_-squashkiwi-_-squashkiwi-test-_-2025-04-28_21-23-59.tgz
|
|
||||||
|
|
||||||
// temp files path
|
|
||||||
|
|
||||||
// template cache path
|
|
||||||
// |-- templates
|
|
||||||
// | |-- <template_name>.json
|
|
||||||
// | |-- <template_name>
|
|
||||||
// | |-- (...script files...)
|
|
||||||
// | |-- _default.env
|
|
||||||
// | |-- config
|
|
||||||
// | |-- service.env
|
|
||||||
// | |-- .template_info.env
|
|
||||||
// | |-- (...other service config files...)
|
|
||||||
// |-- remote_versions
|
|
||||||
// | |-- server_name-service_name.json
|
|
||||||
|
|
||||||
namespace localfile {
|
namespace localfile {
|
||||||
// ~/.config/dropshell/dropshell.json
|
// ~/.config/dropshell/dropshell.json
|
||||||
@ -51,6 +55,8 @@ namespace dropshell {
|
|||||||
std::string server_json(const std::string &server_name);
|
std::string server_json(const std::string &server_name);
|
||||||
std::string service_env(const std::string &server_name, const std::string &service_name);
|
std::string service_env(const std::string &server_name, const std::string &service_name);
|
||||||
std::string template_info_env(const std::string &server_name, const std::string &service_name);
|
std::string template_info_env(const std::string &server_name, const std::string &service_name);
|
||||||
|
std::string template_example();
|
||||||
|
std::string bb64();
|
||||||
} // namespace localfile
|
} // namespace localfile
|
||||||
|
|
||||||
namespace localpath {
|
namespace localpath {
|
||||||
@ -59,9 +65,16 @@ namespace dropshell {
|
|||||||
|
|
||||||
std::string remote_versions(const std::string &server_name, const std::string &service_name);
|
std::string remote_versions(const std::string &server_name, const std::string &service_name);
|
||||||
|
|
||||||
std::string agent();
|
std::string agent_local();
|
||||||
std::string files_for_remote_agent();
|
std::string agent_remote();
|
||||||
std::string current_user_home();
|
std::string current_user_home();
|
||||||
|
|
||||||
|
std::string dropshell_files();
|
||||||
|
std::string backups();
|
||||||
|
std::string temp_files();
|
||||||
|
std::string template_cache();
|
||||||
|
|
||||||
|
bool create_directories();
|
||||||
} // namespace local
|
} // namespace local
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ namespace dropshell
|
|||||||
{
|
{
|
||||||
if (command.get_command_to_run().empty())
|
if (command.get_command_to_run().empty())
|
||||||
return false;
|
return false;
|
||||||
std::string full_command = command.construct_cmd(localpath::agent()+"/bb64"); // Get the command string
|
std::string full_command = command.construct_cmd(localfile::bb64()); // Get the command string
|
||||||
|
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ namespace dropshell
|
|||||||
|
|
||||||
std::string full_cmd;
|
std::string full_cmd;
|
||||||
if (!hasFlag(mode, cMode::NoBB64))
|
if (!hasFlag(mode, cMode::NoBB64))
|
||||||
full_cmd = command.construct_cmd(localpath::agent()+"/bb64");
|
full_cmd = command.construct_cmd(localfile::bb64());
|
||||||
else
|
else
|
||||||
full_cmd = command.construct_cmd("");
|
full_cmd = command.construct_cmd("");
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace dropshell
|
|||||||
std::lock_guard<std::mutex> lock(output_mutex);
|
std::lock_guard<std::mutex> lock(output_mutex);
|
||||||
if (c == EOF)
|
if (c == EOF)
|
||||||
return !EOF;
|
return !EOF;
|
||||||
if (at_line_start_ && c != '\n')
|
if (at_line_start_) // && c != '\n')
|
||||||
{
|
{
|
||||||
dest_ << GREY << tag_ << RESET << ' ' << colour_;
|
dest_ << GREY << tag_ << RESET << ' ' << colour_;
|
||||||
at_line_start_ = false;
|
at_line_start_ = false;
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
namespace dropshell {
|
namespace dropshell {
|
||||||
|
|
||||||
@ -388,4 +390,46 @@ std::string substitute_provided_key_value_pairs(std::string str, const std::map<
|
|||||||
return result;
|
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
|
} // namespace dropshell
|
@ -55,4 +55,8 @@ std::string center_align(const std::string & str, int width);
|
|||||||
std::string replace_with_environment_variables_like_bash(std::string str);
|
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);
|
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
|
} // namespace dropshell
|
Reference in New Issue
Block a user