This commit is contained in:
Your Name
2025-04-21 11:19:05 +12:00
parent 8c85fe8819
commit 10d663971c
20 changed files with 839 additions and 280 deletions

124
src/config.cpp Normal file
View File

@ -0,0 +1,124 @@
#include "dropshell.hpp"
#include <iostream>
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
namespace fs = boost::filesystem;
namespace pt = boost::property_tree;
namespace dropshell {
// Default user directory
static std::string user_directory;
static bool config_loaded = false;
bool is_config_loaded() {
return config_loaded;
}
bool get_config_path(std::string& path) {
// Try ~/.config/dropshell/dropshell.conf
const char* home = std::getenv("HOME");
if (home) {
fs::path user_path = fs::path(home) / ".config" / "dropshell" / "dropshell.conf";
if (!fs::exists(user_path)) {
// create path
fs::create_directories(user_path.parent_path());
}
path = user_path.string();
return true;
}
std::cerr << "Warning: Couldn't determine user directory" << std::endl;
path = "";
return false;
}
bool get_user_directory(std::string& path) {
path = user_directory;
return !path.empty();
}
bool load_config() {
std::string config_path;
if (!get_config_path(config_path))
return true;
if (config_path.empty()) {
// No config file found, but this is not an error
return true;
}
try {
pt::ptree tree;
pt::read_ini(config_path, tree);
bool config_okay = true;
// Try to read user directory from config
try {
user_directory = tree.get<std::string>("user.directory");
} catch (const pt::ptree_error&) {
std::cerr << "Warning: User directory not set in config" << std::endl;
config_okay = false; // Not a critical error
}
// config loaded okay.
config_loaded = config_okay;
return true;
} catch (const std::exception& e) {
std::cerr << "Warning: Error reading config file: " << e.what() << std::endl;
return true; // Not a critical error
}
}
void init_user_directory(const std::string& path) {
// Convert to absolute path
fs::path abs_path = fs::absolute(path);
// Create directory if it doesn't exist
if (!fs::exists(abs_path)) {
throw std::runtime_error("The user directory does not exist: " + abs_path.string());
}
// Update config file
std::string config_path;
if (!get_config_path(config_path)) {
// No config file exists, create one in user's home directory
const char* home = std::getenv("HOME");
if (!home) {
throw std::runtime_error("HOME environment variable not set");
}
fs::path config_dir = fs::path(home) / ".config" / "dropshell";
if (!fs::exists(config_dir)) {
fs::create_directories(config_dir);
}
config_path = (config_dir / "dropshell.conf").string();
}
try {
pt::ptree tree;
// Read existing config if it exists
if (fs::exists(config_path)) {
pt::read_ini(config_path, tree);
}
// Update user directory
tree.put("user.directory", abs_path.string());
// Write back to config file
pt::write_ini(config_path, tree);
// Update in-memory value
user_directory = abs_path.string();
std::cout << "User directory initialized to: " << abs_path.string() << std::endl;
} catch (const std::exception& e) {
throw std::runtime_error("Failed to update config: " + std::string(e.what()));
}
}
} // namespace dropshell

View File

@ -33,7 +33,6 @@ _dropshell_completions() {
servers+=("$server_name")
fi
done
COMPREPLY=( $(compgen -W "${servers[@]}" -- ${cur}) )
return 0
;;

35
src/dropshell.hpp Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <string>
#include <vector>
#include <boost/program_options.hpp>
namespace dropshell {
// Version information
const std::string VERSION = "1.0.0";
const std::string RELEASE_DATE = "2025-04-21";
const std::string AUTHOR = "j842";
const std::string LICENSE = "MIT";
// Server information structure
struct ServerInfo {
std::string name;
std::string address;
};
// Command handlers
void print_help(const boost::program_options::options_description& desc);
void print_version();
void check_status();
void list_servers();
void init_user_directory(const std::string& path);
// Utility functions
std::vector<ServerInfo> get_configured_servers();
bool get_config_path(std::string& path);
bool load_config();
bool is_config_loaded();
bool get_user_directory(std::string& path);
} // namespace dropshell

View File

@ -1,125 +0,0 @@
#!/bin/bash
# Script name: dropshell.sh
# Description: A basic shell script for common operations
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Source version information
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
source "$SCRIPT_DIR/version.sh"
# Source configuration
if [ -f "/etc/dropshell.conf" ]; then
source "/etc/dropshell.conf"
else
echo -e "${RED}Error: Configuration file not found${NC}"
echo "Please run the installation script first"
exit 1
fi
# Check if USER_DEFINITIONS is set
if [ -z "$USER_DEFINITIONS" ]; then
echo -e "${RED}Error: USER_DEFINITIONS not set${NC}"
echo "Please run the installation script first"
exit 1
fi
# Function to print usage
print_usage() {
echo "Usage: $0 <command> [options]"
echo
echo "Commands:"
echo " help - Show this help message"
echo " version - Show version information"
echo " status - Check system status"
echo " servers - List configured servers"
echo
echo "Options:"
echo " -v, --verbose Enable verbose output"
}
# Function to print version
print_version() {
get_version_info
}
# Function to check status
check_status() {
echo -e "${GREEN}System Status:${NC}"
echo "Date: $(date)"
echo "Uptime: $(uptime)"
echo "Memory Usage:"
free -h
echo
echo "Disk Usage:"
df -h /
}
# Function to list servers from _server.env files
list_servers() {
local servers_dir="/opt/dropshell/user/servers"
local max_name_len=0
local max_addr_len=0
local servers=()
# First pass: collect data and find max lengths
for server_dir in "$servers_dir"/*/; do
if [ -f "${server_dir}_server.env" ]; then
local server_name=$(basename "$server_dir")
local ssh_address=$(grep '^SSH_ADDRESS=' "${server_dir}_server.env" | cut -d'=' -f2-)
# Update max lengths
[ ${#server_name} -gt $max_name_len ] && max_name_len=${#server_name}
[ ${#ssh_address} -gt $max_addr_len ] && max_addr_len=${#ssh_address}
servers+=("$server_name|$ssh_address")
fi
done
# Add padding for headers
max_name_len=$((max_name_len > 4 ? max_name_len : 4))
max_addr_len=$((max_addr_len > 7 ? max_addr_len : 7))
# Print header
printf "%-${max_name_len}s | %-${max_addr_len}s\n" "Name" "Address"
printf "%-${max_name_len}s-+-%-${max_addr_len}s\n" "$(printf '%*s' $max_name_len '' | tr ' ' '-')" "$(printf '%*s' $max_addr_len '' | tr ' ' '-')"
# Print server rows
for server in "${servers[@]}"; do
IFS='|' read -r name addr <<< "$server"
printf "%-${max_name_len}s | %-${max_addr_len}s\n" "$name" "$addr"
done
}
# Main script logic
case "$1" in
"help"|"-h"|"--help")
print_usage
;;
"version"|"-V"|"--version")
print_version
;;
"status")
check_status
;;
"servers")
list_servers
;;
"")
echo -e "${RED}Error: No command provided${NC}"
print_usage
exit 1
;;
*)
echo -e "${RED}Error: Unknown command '$1'${NC}"
print_usage
exit 1
;;
esac
exit 0

24
src/help.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "dropshell.hpp"
#include <iostream>
namespace dropshell {
void print_help(const boost::program_options::options_description& desc) {
std::cout << "Usage: dropshell [OPTIONS] COMMAND [ARGS]" << std::endl;
std::cout << std::endl;
std::cout << "A tool for managing server configurations" << std::endl;
std::cout << std::endl;
std::cout << "Commands:" << std::endl;
std::cout << " help Show this help message" << std::endl;
std::cout << " version Show version information" << std::endl;
std::cout << " init DIR Initialize the user directory for server configurations" << std::endl;
std::cout << std::endl;
std::cout << " status Check system status" << std::endl;
std::cout << " servers List configured servers" << std::endl;
std::cout << std::endl;
std::cout << "Examples:" << std::endl;
std::cout << " dropshell servers" << std::endl;
std::cout << " dropshell init /path/to/directory" << std::endl;
}
} // namespace dropshell

84
src/main.cpp Normal file
View File

@ -0,0 +1,84 @@
#include "dropshell.hpp"
#include <iostream>
#include <boost/program_options.hpp>
#include <boost/filesystem.hpp>
namespace po = boost::program_options;
namespace fs = boost::filesystem;
int main(int argc, char* argv[]) {
try {
// Define command line options
po::options_description desc("Usage: dropshell <command> [options]");
desc.add_options()
("help,h", "Show help message")
("version,V", "Show version information")
("command", po::value<std::string>(), "Command to execute")
("directory", po::value<std::string>(), "Directory path for init command")
("verbose,v", "Enable verbose output");
po::positional_options_description p;
p.add("command", 1);
p.add("directory", 1); // Add directory as a positional argument
po::variables_map vm;
po::store(po::command_line_parser(argc, argv)
.options(desc)
.positional(p)
.run(), vm);
po::notify(vm);
// Load configuration
if (!dropshell::load_config()) {
std::cerr << "Error: Failed to load configuration" << std::endl;
return 1;
}
// Handle commands
if (vm.count("help") || (vm.count("command") && vm["command"].as<std::string>() == "help")) {
dropshell::print_help(desc);
return 0;
}
if (vm.count("version") || (vm.count("command") && vm["command"].as<std::string>() == "version")) {
dropshell::print_version();
return 0;
}
if (vm.count("command")) {
std::string cmd = vm["command"].as<std::string>();
if (cmd == "status") {
dropshell::check_status();
return 0;
} else if (cmd == "servers") {
dropshell::list_servers();
return 0;
} else if (cmd == "init") {
if (!vm.count("directory")) {
std::cerr << "Error: init command requires a directory argument" << std::endl;
return 1;
}
try {
dropshell::init_user_directory(vm["directory"].as<std::string>());
return 0;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
} else {
std::cerr << "Error: Unknown command '" << cmd << "'" << std::endl;
dropshell::print_help(desc);
return 1;
}
}
// No command provided
std::cerr << "Error: No command provided" << std::endl;
dropshell::print_help(desc);
return 1;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}

89
src/servers.cpp Normal file
View File

@ -0,0 +1,89 @@
#include "dropshell.hpp"
#include <iostream>
#include <fstream>
#include <iomanip>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
namespace fs = boost::filesystem;
namespace dropshell {
std::vector<ServerInfo> get_configured_servers() {
std::vector<ServerInfo> servers;
std::string user_dir;
if (!is_config_loaded()) {
std::cerr << "Error: Config not loaded" << std::endl;
return servers;
}
if (!get_user_directory(user_dir)) {
std::cerr << "Error: User directory not set" << std::endl;
return servers;
}
fs::path servers_dir = fs::path(user_dir) / "servers";
if (!fs::exists(servers_dir)) {
return servers;
}
for (const auto& entry : fs::directory_iterator(servers_dir)) {
if (fs::is_directory(entry)) {
fs::path env_file = entry.path() / "_server.env";
if (fs::exists(env_file)) {
std::ifstream file(env_file.string());
std::string line;
std::string address;
while (std::getline(file, line)) {
if (boost::starts_with(line, "SSH_ADDRESS=")) {
address = line.substr(12);
break;
}
}
if (!address.empty()) {
servers.push_back({
entry.path().filename().string(),
address
});
}
}
}
}
return servers;
}
void list_servers() {
auto servers = get_configured_servers();
if (servers.empty()) {
std::cout << "No servers configured." << std::endl;
return;
}
// Find maximum lengths for formatting
size_t max_name_len = 4; // "Name" is 4 chars
size_t max_addr_len = 7; // "Address" is 7 chars
for (const auto& server : servers) {
max_name_len = std::max(max_name_len, server.name.length());
max_addr_len = std::max(max_addr_len, server.address.length());
}
// Print header
std::cout << std::left << std::setw(max_name_len) << "Name" << " | "
<< std::setw(max_addr_len) << "Address" << std::endl;
// Print separator
std::cout << std::string(max_name_len, '-') << "-+-"
<< std::string(max_addr_len, '-') << std::endl;
// Print server rows
for (const auto& server : servers) {
std::cout << std::left << std::setw(max_name_len) << server.name << " | "
<< std::setw(max_addr_len) << server.address << std::endl;
}
}
} // namespace dropshell

58
src/status.cpp Normal file
View File

@ -0,0 +1,58 @@
#include "dropshell.hpp"
#include <iostream>
#include <fstream>
#include <chrono>
#include <ctime>
#include <sys/statvfs.h>
#include <sys/sysinfo.h>
namespace dropshell {
void check_status() {
// Get current time
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
std::cout << "System Status:" << std::endl;
std::cout << "Date: " << std::ctime(&time);
// Get uptime
struct sysinfo si;
if (sysinfo(&si) == 0) {
int days = si.uptime / 86400;
int hours = (si.uptime % 86400) / 3600;
int minutes = (si.uptime % 3600) / 60;
std::cout << "Uptime: " << days << " days, " << hours << " hours, " << minutes << " minutes" << std::endl;
}
// Get memory usage
std::ifstream meminfo("/proc/meminfo");
if (meminfo.is_open()) {
std::string line;
long total_mem = 0, free_mem = 0;
while (std::getline(meminfo, line)) {
if (line.find("MemTotal:") == 0) {
total_mem = std::stol(line.substr(9));
} else if (line.find("MemAvailable:") == 0) {
free_mem = std::stol(line.substr(13));
}
}
std::cout << "Memory Usage:" << std::endl;
std::cout << "Total: " << total_mem / 1024 << " MB" << std::endl;
std::cout << "Available: " << free_mem / 1024 << " MB" << std::endl;
}
// Get disk usage
struct statvfs vfs;
if (statvfs("/", &vfs) == 0) {
uint64_t total = vfs.f_blocks * vfs.f_frsize;
uint64_t free = vfs.f_bfree * vfs.f_frsize;
uint64_t used = total - free;
std::cout << "Disk Usage:" << std::endl;
std::cout << "Total: " << total / (1024*1024*1024) << " GB" << std::endl;
std::cout << "Used: " << used / (1024*1024*1024) << " GB" << std::endl;
std::cout << "Free: " << free / (1024*1024*1024) << " GB" << std::endl;
}
}
} // namespace dropshell

13
src/version.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "dropshell.hpp"
#include <iostream>
namespace dropshell {
void print_version() {
std::cout << "dropshell version " << VERSION << std::endl;
std::cout << "Release date: " << RELEASE_DATE << std::endl;
std::cout << "Author: " << AUTHOR << std::endl;
std::cout << "License: " << LICENSE << std::endl;
}
} // namespace dropshell

View File

@ -1,15 +0,0 @@
#!/bin/bash
# Version information
DROPSHELL_VERSION="1.0.0"
DROPSHELL_RELEASE_DATE="2025-04-21"
DROPSHELL_AUTHOR="j842"
DROPSHELL_LICENSE="MIT"
# Function to get full version information
get_version_info() {
echo "dropshell version $DROPSHELL_VERSION"
echo "Release date: $DROPSHELL_RELEASE_DATE"
echo "Author: $DROPSHELL_AUTHOR"
echo "License: $DROPSHELL_LICENSE"
}