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

59
.gitignore vendored Normal file
View File

@ -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/

36
CMakeLists.txt Normal file
View File

@ -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
)

View File

@ -1,3 +1,83 @@
# dropshell
# Dropshell
Simple remote server management system, based on bash scripts, and works with anything.
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

80
build.sh Executable file
View File

@ -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!"

8
debian/changelog vendored Normal file
View File

@ -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 <j842@example.com> Sun, 21 Apr 2025 12:00:00 +0000

15
debian/control vendored Normal file
View File

@ -0,0 +1,15 @@
Source: dropshell
Section: utils
Priority: optional
Maintainer: j842 <j842@example.com>
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.

26
debian/copyright vendored Normal file
View File

@ -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.

14
debian/rules vendored Normal file
View File

@ -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

View File

@ -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 <USER_DEFINITIONS_PATH>"
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

92
install_prerequisites.sh Executable file
View File

@ -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"

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"
}