From 5d7a22794d60950f16821ef108ba737976b7e282 Mon Sep 17 00:00:00 2001
From: Your Name <j@842.be>
Date: Fri, 25 Apr 2025 22:00:41 +1200
Subject: [PATCH] Server ssh works!!

---
 src/main.cpp           | 27 +++++++++++++++---------
 src/servers.hpp        | 24 ++++++++++-----------
 src/service_runner.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++
 src/service_runner.hpp |  5 ++++-
 src/services.cpp       |  1 +
 src/services.hpp       |  2 ++
 6 files changed, 84 insertions(+), 23 deletions(-)

diff --git a/src/main.cpp b/src/main.cpp
index ea7bd2c..90f42e3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -20,14 +20,10 @@ void print_help() {
     std::cout << std::endl;
     std::cout << "A tool for managing server configurations" << std::endl;
     std::cout << std::endl;
+    std::cout << "dropshell ..." << std::endl;
     std::cout << "  help           Show this help message" << std::endl;
-    std::cout << "  version        Show version information" << std::endl;
-    std::cout << "  init DIR       Add a local dropshell config directory (you can add several)" << std::endl;
-    std::cout << std::endl;
-    std::cout << std::endl;
-    std::cout << "Server commands:" << std::endl;
-    std::cout << "  servers        Summary of all configured servers" << std::endl;
-    std::cout << "  servers NAME   Show details for specific server" << std::endl;
+    std::cout << "  init DIR       Add DIR as a local configuration directory for dropshell (can add several)" << std::endl;
+    std::cout << "  server NAME    Show details for specific server" << std::endl;
     std::cout << "  templates      List all available templates" << std::endl;
     std::cout << std::endl;
     std::cout << std::endl;
@@ -35,8 +31,9 @@ void print_help() {
     std::cout << "  install SERVER [SERVICE]     Install/reinstall/update service(s). Non-destructive." << std::endl;
     std::cout << "  COMMAND SERVER [SERVICE]     Run a command on service(s)." << std::endl;
     std::cout << std::endl;
-    std::cout << "Standard commands: install, backup, uninstall, start, stop" << std::endl;
+    std::cout << "Standard commands: install, uninstall, backup, restore, start, stop" << std::endl;
     std::cout << std::endl;
+    std::cout << "  ssh     SERVER [SERVICE]     Launch an interactive shell on a server or service" << std::endl;
     std::cout << std::endl;
     std::cout << "Creation commands: (apply to the first local config directory)"<<std::endl;
     std::cout << "  create-template TEMPLATE" << std::endl;
@@ -132,7 +129,7 @@ int main(int argc, char* argv[]) {
             });
             if (cfg->is_config_set()) 
                 commands.merge(std::set<std::string>{
-                    "servers","templates","create-service","create-template","create-server"
+                    "server","templates","create-service","create-template","create-server","ssh"
                 });
             
             for (const auto& command : commands) {
@@ -183,7 +180,7 @@ int main(int argc, char* argv[]) {
             return 0;
         }
 
-        if (cmd == "servers") {
+        if (cmd == "server") {
             if (argc > 2) {
                 // Show details for specific server
                 dropshell::show_server_details(argv[2]);
@@ -226,6 +223,16 @@ int main(int argc, char* argv[]) {
             return 0;
         }
 
+        if (cmd == "ssh" && argc < 4) {
+            if (argc < 3) 
+            {
+                std::cerr << "Error: ssh requires a server name and optionally service name" << std::endl;
+                return 1;
+            }
+            dropshell::interactive_ssh(argv[2], "");
+            return 0;
+        }
+
         // handle running a command. 
         for (const auto& command : commands) {
             if (cmd == command) {
diff --git a/src/servers.hpp b/src/servers.hpp
index 9f02ff4..4e7eda3 100644
--- a/src/servers.hpp
+++ b/src/servers.hpp
@@ -8,20 +8,20 @@
 
 namespace dropshell {
 
-// Server information structure
-struct ServerInfo {
-    std::string name;
-    std::string ssh_host;
-    std::string ssh_user;
-    std::string ssh_port;
-};
+    // Server information structure
+    struct ServerInfo {
+        std::string name;
+        std::string ssh_host;
+        std::string ssh_user;
+        std::string ssh_port;
+    };
 
-std::vector<ServerInfo> get_configured_servers();
-void list_servers();
-void show_server_details(const std::string& server_name);
-
-void create_server(const std::string& server_name);
+    std::vector<ServerInfo> get_configured_servers();
+    void list_servers();
+    void show_server_details(const std::string& server_name);
 
+    void create_server(const std::string& server_name);
+   
 } // namespace dropshell
 
 #endif // SERVERS_HPP
diff --git a/src/service_runner.cpp b/src/service_runner.cpp
index 901dfa2..9880c00 100644
--- a/src/service_runner.cpp
+++ b/src/service_runner.cpp
@@ -270,6 +270,10 @@ bool service_runner::run_command(const std::string& command) {
         return uninstall();
     if (command == "backup") 
         return backup();
+    if (command == "ssh") {
+        interactive_ssh_service();
+        return true;
+    }
 
     // Run the generic command
     std::string run_cmd = construct_standard_command_run_cmd(command);
@@ -480,4 +484,48 @@ std::string service_runner::healthmark()
     return HealthStatus2String(status);
 }
 
+void interactive_ssh(const std::string & server_name, const std::string & command) {
+    std::string serverpath = get_local_server_path(server_name);
+    if (serverpath.empty()) {
+        std::cerr << "Error: Server not found: " << server_name << std::endl;
+        return;
+    }
+
+    server_env env(server_name);
+    if (!env.is_valid()) {
+        std::cerr << "Error: Invalid server environment file: " << server_name << std::endl;
+        return;
+    }
+
+    std::string ssh_address = env.get_SSH_HOST();
+    std::string ssh_user = env.get_SSH_USER();
+    std::string ssh_port = env.get_SSH_PORT();
+
+    std::string login = ssh_user + "@" + ssh_address;
+
+    // Execute ssh with server_name and command
+    if (command.empty())
+        execlp("ssh", "ssh", login.c_str(), "-p", ssh_port.c_str(), nullptr);
+    else
+        execlp("ssh", "ssh", login.c_str(), "-p", ssh_port.c_str(), command.c_str(), nullptr);
+    
+    // If exec returns, it means there was an error
+    perror("ssh execution failed");
+    exit(EXIT_FAILURE);
+}
+
+void service_runner::interactive_ssh_service()
+{
+    std::set<std::string> used_commands = get_used_commands(m_server_name, m_service_info.service_name);
+    if (used_commands.find("ssh") == used_commands.end()) {
+        std::cerr << "Error: "<< m_service_info.service_name <<" does not support ssh" << std::endl;
+        return;
+    }
+
+    std::string command = construct_standard_command_run_cmd("ssh");
+    interactive_ssh(m_server_name, command);
+}
+
+
+
 } // namespace dropshell 
\ No newline at end of file
diff --git a/src/service_runner.hpp b/src/service_runner.hpp
index 7c0b9d9..7f6e8ca 100644
--- a/src/service_runner.hpp
+++ b/src/service_runner.hpp
@@ -80,7 +80,9 @@ class service_runner {
         // 4. copy it to the local user_dir/backups folder
         bool backup();
 
-
+        // launch an interactive ssh session on a server or service
+        // replaces the current dropshell process with the ssh process
+        void interactive_ssh_service();
 
     private:
         std::string m_server_name;
@@ -106,6 +108,7 @@ class service_runner {
         static bool execute_local_command_and_capture_output(const std::string& command, std::string & output);
 };
 
+        void interactive_ssh(const std::string & server_name, const std::string & command);
 
 } // namespace dropshell
 
diff --git a/src/services.cpp b/src/services.cpp
index a98d659..63cdb06 100644
--- a/src/services.cpp
+++ b/src/services.cpp
@@ -4,6 +4,7 @@
 #include "templates.hpp"
 #include "config.hpp"
 #include "utils/utils.hpp"
+#include "server_env.hpp"
 #include <boost/filesystem.hpp>
 #include <iostream>
 
diff --git a/src/services.hpp b/src/services.hpp
index 1c50d85..befede7 100644
--- a/src/services.hpp
+++ b/src/services.hpp
@@ -20,6 +20,8 @@ namespace dropshell {
 
     bool create_service(const std::string& server_name, const std::string& service_name);
 
+    void interactive_ssh(const std::string & server_name, const std::string & service_name, const std::string & command);
+
 } // namespace dropshell