From 10d663971ce9e711793f5c0ef5300c3418078f68 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 21 Apr 2025 11:19:05 +1200 Subject: [PATCH] . --- .gitignore | 59 +++++++++++++++ CMakeLists.txt | 36 +++++++++ README.md | 84 ++++++++++++++++++++- build.sh | 80 ++++++++++++++++++++ debian/changelog | 8 ++ debian/control | 15 ++++ debian/copyright | 26 +++++++ debian/rules | 14 ++++ install.sh | 137 ---------------------------------- install_prerequisites.sh | 92 +++++++++++++++++++++++ src/config.cpp | 124 ++++++++++++++++++++++++++++++ src/dropshell-completion.bash | 1 - src/dropshell.hpp | 35 +++++++++ src/dropshell.sh | 125 ------------------------------- src/help.cpp | 24 ++++++ src/main.cpp | 84 +++++++++++++++++++++ src/servers.cpp | 89 ++++++++++++++++++++++ src/status.cpp | 58 ++++++++++++++ src/version.cpp | 13 ++++ src/version.sh | 15 ---- 20 files changed, 839 insertions(+), 280 deletions(-) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100755 build.sh create mode 100644 debian/changelog create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/rules delete mode 100755 install.sh create mode 100755 install_prerequisites.sh create mode 100644 src/config.cpp create mode 100644 src/dropshell.hpp delete mode 100755 src/dropshell.sh create mode 100644 src/help.cpp create mode 100644 src/main.cpp create mode 100644 src/servers.cpp create mode 100644 src/status.cpp create mode 100644 src/version.cpp delete mode 100644 src/version.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e6df21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,59 @@ +# Build directories +build/ +cmake-build-*/ +out/ +bin/ +lib/ + +# Compiled Object files +*.o +*.obj +*.a +*.lib + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Compiled Static libraries +*.lai +*.la +*.a + +# Executables +dropshell + +# CMake +CMakeCache.txt +CMakeFiles/ +cmake_install.cmake +Makefile +*.cmake + +# IDE specific files +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# System files +.DS_Store +Thumbs.db + +# Logs and databases +*.log +*.sqlite + +# Local configuration +*.local +*.conf.local + +# Dependencies +deps/ +vendor/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c955c1b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.10) +project(dropshell VERSION 1.0.0 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Find required packages +find_package(Boost REQUIRED COMPONENTS program_options filesystem system) + +# Add executable +add_executable(dropshell + src/main.cpp + src/version.cpp + src/status.cpp + src/servers.cpp + src/help.cpp + src/config.cpp +) + +# Link libraries +target_link_libraries(dropshell PRIVATE + Boost::program_options + Boost::filesystem + Boost::system +) + +# Install targets +install(TARGETS dropshell + RUNTIME DESTINATION bin +) + +# Install completion script +install(FILES src/dropshell-completion.bash + DESTINATION /etc/bash_completion.d + RENAME dropshell +) diff --git a/README.md b/README.md index 77d8d41..a07e66b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,83 @@ -# dropshell +# Dropshell -Simple remote server management system, based on bash scripts, and works with anything. \ No newline at end of file +A system management tool for server operations, written in C++. + +## Features + +- System status monitoring +- Server configuration management +- Bash completion support +- Command-line interface with help and version information + +## Building from Source + +### Prerequisites + +- CMake (>= 3.10) +- Boost C++ Libraries (program_options, filesystem, system) +- C++17 compatible compiler +- Debian packaging tools (for creating packages) + +### Building + +```bash +# Clone the repository +git clone https://github.com/j842/dropshell.git +cd dropshell + +# Create build directory +mkdir build +cd build + +# Configure and build +cmake .. +make +``` + +### Installing + +#### From Source + +```bash +# Install from source build +sudo make install +``` + +#### From Debian Package + +To create a Debian package: + +```bash +# Install build dependencies +sudo apt-get install devscripts debhelper cmake libboost-all-dev + +# Build the package +dpkg-buildpackage -us -uc + +# Install the package +sudo dpkg -i ../dropshell_1.0.0-1_*.deb +``` + +## Usage + +```bash +# Show help +dropshell help + +# Show version +dropshell version + +# Check system status +dropshell status + +# List configured servers +dropshell servers +``` + +## Configuration + +The configuration file is located at `/etc/dropshell.conf`. You can modify this file to change default settings. + +## License + +MIT License - see LICENSE file for details \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..92ee32c --- /dev/null +++ b/build.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# Exit on error +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print status messages +print_status() { + echo -e "${GREEN}[*] $1${NC}" +} + +print_error() { + echo -e "${RED}[!] $1${NC}" +} + +print_warning() { + echo -e "${YELLOW}[!] $1${NC}" +} + +# Check if build directory exists, if not create it +if [ ! -d "build" ]; then + print_status "Creating build directory..." + mkdir build +fi + +# Enter build directory +cd build + +# Check if CMake is installed +if ! command -v cmake &> /dev/null; then + print_error "CMake is not installed. Please install CMake first." + exit 1 +fi + +# Check if make is installed +if ! command -v make &> /dev/null; then + print_error "Make is not installed. Please install Make first." + exit 1 +fi + +# Configure with CMake +print_status "Configuring with CMake..." +cmake .. -DCMAKE_BUILD_TYPE=Release + +# Build the project +print_status "Building project..." +make -j$(nproc) + +# Check if build was successful +if [ $? -eq 0 ]; then + print_status "Build successful!" + print_status "Binary location: $(pwd)/dropshell" +else + print_error "Build failed!" + exit 1 +fi + +# Check if user wants to install +read -p "Do you want to install the program? (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + print_status "Installing dropshell..." + sudo make install + if [ $? -eq 0 ]; then + print_status "Installation successful!" + else + print_error "Installation failed!" + exit 1 + fi +fi + +# Return to original directory +cd .. + +print_status "Build process completed!" \ No newline at end of file diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..9a75e85 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,8 @@ +dropshell (1.0.0-1) unstable; urgency=medium + + * Initial release. + * Converted from bash script to C++ implementation + * Added proper system status monitoring + * Improved server management functionality + + -- j842 Sun, 21 Apr 2025 12:00:00 +0000 \ No newline at end of file diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..6b8f572 --- /dev/null +++ b/debian/control @@ -0,0 +1,15 @@ +Source: dropshell +Section: utils +Priority: optional +Maintainer: j842 +Build-Depends: debhelper (>= 10), cmake, libboost-all-dev +Standards-Version: 4.5.0 +Homepage: https://github.com/j842/dropshell + +Package: dropshell +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends}, libboost-program-options1.74.0, libboost-filesystem1.74.0, libboost-system1.74.0 +Description: A system management tool for server operations + dropshell is a command-line tool for managing and monitoring servers. + It provides functionality for checking system status, managing server + configurations, and performing common administrative tasks. \ No newline at end of file diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..73c95d3 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,26 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: dropshell +Source: https://github.com/j842/dropshell + +Files: * +Copyright: 2025 j842 +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. \ No newline at end of file diff --git a/debian/rules b/debian/rules new file mode 100644 index 0000000..e3fcd10 --- /dev/null +++ b/debian/rules @@ -0,0 +1,14 @@ +#!/usr/bin/make -f + +%: + dh $@ + +override_dh_auto_configure: + dh_auto_configure -- -DCMAKE_BUILD_TYPE=Release + +override_dh_auto_install: + dh_auto_install + # Install bash completion + install -D -m 644 src/dropshell-completion.bash debian/dropshell/etc/bash_completion.d/dropshell + # Install configuration + install -D -m 644 src/dropshell.conf debian/dropshell/etc/dropshell.conf \ No newline at end of file diff --git a/install.sh b/install.sh deleted file mode 100755 index 4e44771..0000000 --- a/install.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/bin/bash - -SCRIPT_DIR="$(dirname "$(readlink -f "$0")")" -source "$SCRIPT_DIR/src/version.sh" - -# Colors for output -GREEN='\033[0;32m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -# Function to create directory with proper ownership -mkdir_with_ownership() { - local target_dir="$1" - local parent_dir="$(dirname "$target_dir")" - - # Find the lowest existing parent directory - while [ ! -d "$parent_dir" ] && [ "$parent_dir" != "/" ]; do - parent_dir="$(dirname "$parent_dir")" - done - - # If we found an existing directory, get its ownership - if [ -d "$parent_dir" ]; then - local owner=$(stat -c %U "$parent_dir") - local group=$(stat -c %G "$parent_dir") - install -d -o "$owner" -g "$group" "$target_dir" - else - echo -e "${RED}Error: Could not find any existing parent directory${NC}" - return 1 - fi -} - -# Check if USER_DEFINITIONS is provided -if [ -z "$1" ]; then - echo -e "${RED}Error: USER_DEFINITIONS path is required${NC}" - echo "Usage: $0 " - echo "Example: $0 ~/.config/dropshell" - exit 1 -fi - -USER_DEFINITIONS="$1" - -# Script locations -SCRIPT_SOURCE="$SCRIPT_DIR/src/dropshell.sh" -SCRIPT_DEST="/usr/local/bin/dropshell" -COMPLETION_SOURCE="$SCRIPT_DIR/src/dropshell-completion.bash" -COMPLETION_DEST="/etc/bash_completion.d/dropshell" -EXAMPLE_DIR="$SCRIPT_DIR/.example" -DROPSHELL_OPT="/opt/dropshell" - -# Check if running with sudo/root -if [ "$EUID" -ne 0 ]; then - echo -e "${RED}Please run as root or with sudo${NC}" - exit 1 -fi - -# Check if source script exists -if [ ! -f "$SCRIPT_SOURCE" ]; then - echo -e "${RED}Error: $SCRIPT_SOURCE not found${NC}" - echo "Please ensure the script exists in the src directory" - exit 1 -fi - -# Check if completion script exists -if [ ! -f "$COMPLETION_SOURCE" ]; then - echo -e "${RED}Error: $COMPLETION_SOURCE not found${NC}" - echo "Please ensure the completion script exists in the src directory" - exit 1 -fi - -# Create user definitions directory if it doesn't exist -if [ ! -d "$USER_DEFINITIONS" ]; then - echo "Creating user definitions directory..." - mkdir_with_ownership "$USER_DEFINITIONS" || exit 1 -fi - -# Check if example directory exists and copy example files if needed -if [ ! -d "$EXAMPLE_DIR" ]; then - echo -e "${RED}Error: Example directory not found at $EXAMPLE_DIR${NC}" - echo "Please ensure the .example directory exists in the project root" - exit 1 -fi - -# Check and create servers directory -if [ ! -d "$USER_DEFINITIONS/servers" ]; then - echo "Copying example files..." - mkdir_with_ownership "$USER_DEFINITIONS/servers" || exit 1 - cp -ra "$EXAMPLE_DIR"/servers/* "$USER_DEFINITIONS/servers" -fi - -# Check and create usertemplates directory -if [ ! -d "$USER_DEFINITIONS/usertemplates" ]; then - echo "Copying example files..." - mkdir_with_ownership "$USER_DEFINITIONS/usertemplates" || exit 1 - cp -ra "$EXAMPLE_DIR"/usertemplates/* "$USER_DEFINITIONS/usertemplates" -fi - -# install user directory. -echo "Creating symbolic link for /opt/dropshell/user..." -mkdir -p "$DROPSHELL_OPT" || exit 1 -if [ -e "$DROPSHELL_OPT/user" ]; then - rm -rf "$DROPSHELL_OPT/user" -fi -ln -s "$USER_DEFINITIONS" "$DROPSHELL_OPT/user" - -# Create symbolic link -echo "Creating symbolic link for running from $SCRIPT_DEST..." -if [ -L "$SCRIPT_DEST" ]; then - rm "$SCRIPT_DEST" -fi -ln -s "$SCRIPT_SOURCE" "$SCRIPT_DEST" - -# Make sure the scripts are executable -chmod +x "$SCRIPT_SOURCE" -chmod +x "$COMPLETION_SOURCE" - -# Install bash completion -echo "Installing bash completion..." -if [ ! -d "/etc/bash_completion.d" ]; then - mkdir_with_ownership "/etc/bash_completion.d" || exit 1 -fi - -# Install completion script -cp "$COMPLETION_SOURCE" "$COMPLETION_DEST" -chmod 644 "$COMPLETION_DEST" - -# Create environment file -cat > "/etc/dropshell.conf" << EOF -# Dropshell configuration -USER_DEFINITIONS="$USER_DEFINITIONS" -EOF - -# Source the completion script -echo "source $COMPLETION_DEST" >> ~/.bashrc - -echo -e "${GREEN}Installation completed successfully!${NC}" - -exit 0 diff --git a/install_prerequisites.sh b/install_prerequisites.sh new file mode 100755 index 0000000..85f7097 --- /dev/null +++ b/install_prerequisites.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Function to print status messages +print_status() { + echo -e "${GREEN}[*] $1${NC}" +} + +print_error() { + echo -e "${RED}[!] $1${NC}" +} + +print_warning() { + echo -e "${YELLOW}[!] $1${NC}" +} + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + print_error "Please run this script as root (use sudo)" + exit 1 +fi + +# Detect distribution +if [ -f /etc/os-release ]; then + . /etc/os-release + OS=$NAME + VER=$VERSION_ID +else + print_error "Could not detect distribution" + exit 1 +fi + +print_status "Detected OS: $OS $VER" + +# Define packages based on distribution +case $OS in + "Ubuntu"|"Debian GNU/Linux") + # Common packages for both Ubuntu and Debian + PACKAGES="cmake make g++ libboost-all-dev devscripts debhelper" + ;; + *) + print_error "Unsupported distribution: $OS" + exit 1 + ;; +esac + +# Function to check if a package is installed +is_package_installed() { + dpkg -l "$1" 2>/dev/null | grep -q "^ii" +} + +# Install missing packages +print_status "Checking and installing required packages..." +for pkg in $PACKAGES; do + if ! is_package_installed "$pkg"; then + print_status "Installing $pkg..." + apt-get install -y "$pkg" + if [ $? -ne 0 ]; then + print_error "Failed to install $pkg" + exit 1 + fi + else + print_status "$pkg is already installed" + fi +done + +# Update package lists +print_status "Updating package lists..." +apt-get update + +# Verify all required tools are installed +print_status "Verifying installation..." +for tool in cmake make g++; do + if ! command -v "$tool" &> /dev/null; then + print_error "$tool is not installed properly" + exit 1 + fi +done + +# Check Boost installation +if [ ! -d "/usr/include/boost" ]; then + print_error "Boost headers not found" + exit 1 +fi + +print_status "All dependencies installed successfully!" +print_status "You can now run ./build.sh to build the project" \ No newline at end of file diff --git a/src/config.cpp b/src/config.cpp new file mode 100644 index 0000000..4d91a84 --- /dev/null +++ b/src/config.cpp @@ -0,0 +1,124 @@ +#include "dropshell.hpp" +#include +#include +#include +#include +#include + +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("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 \ No newline at end of file diff --git a/src/dropshell-completion.bash b/src/dropshell-completion.bash index 75299ec..58dd035 100755 --- a/src/dropshell-completion.bash +++ b/src/dropshell-completion.bash @@ -33,7 +33,6 @@ _dropshell_completions() { servers+=("$server_name") fi done - COMPREPLY=( $(compgen -W "${servers[@]}" -- ${cur}) ) return 0 ;; diff --git a/src/dropshell.hpp b/src/dropshell.hpp new file mode 100644 index 0000000..0971ea8 --- /dev/null +++ b/src/dropshell.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +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 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 \ No newline at end of file diff --git a/src/dropshell.sh b/src/dropshell.sh deleted file mode 100755 index 7826378..0000000 --- a/src/dropshell.sh +++ /dev/null @@ -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 [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 \ No newline at end of file diff --git a/src/help.cpp b/src/help.cpp new file mode 100644 index 0000000..ff8957d --- /dev/null +++ b/src/help.cpp @@ -0,0 +1,24 @@ +#include "dropshell.hpp" +#include + +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 \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..6a2422c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,84 @@ +#include "dropshell.hpp" +#include +#include +#include + +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 [options]"); + desc.add_options() + ("help,h", "Show help message") + ("version,V", "Show version information") + ("command", po::value(), "Command to execute") + ("directory", po::value(), "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() == "help")) { + dropshell::print_help(desc); + return 0; + } + + if (vm.count("version") || (vm.count("command") && vm["command"].as() == "version")) { + dropshell::print_version(); + return 0; + } + + if (vm.count("command")) { + std::string cmd = vm["command"].as(); + 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()); + 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; + } +} \ No newline at end of file diff --git a/src/servers.cpp b/src/servers.cpp new file mode 100644 index 0000000..daefe9c --- /dev/null +++ b/src/servers.cpp @@ -0,0 +1,89 @@ +#include "dropshell.hpp" +#include +#include +#include +#include +#include + +namespace fs = boost::filesystem; + +namespace dropshell { + +std::vector get_configured_servers() { + std::vector 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 \ No newline at end of file diff --git a/src/status.cpp b/src/status.cpp new file mode 100644 index 0000000..73d7b4e --- /dev/null +++ b/src/status.cpp @@ -0,0 +1,58 @@ +#include "dropshell.hpp" +#include +#include +#include +#include +#include +#include + +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 \ No newline at end of file diff --git a/src/version.cpp b/src/version.cpp new file mode 100644 index 0000000..309eae6 --- /dev/null +++ b/src/version.cpp @@ -0,0 +1,13 @@ +#include "dropshell.hpp" +#include + +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 \ No newline at end of file diff --git a/src/version.sh b/src/version.sh deleted file mode 100644 index 59f76bd..0000000 --- a/src/version.sh +++ /dev/null @@ -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" -} \ No newline at end of file