diff --git a/CMakeLists.txt b/CMakeLists.txt index 17c7d2f..0407167 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,9 @@ configure_file( # Set CMAKE_MODULE_PATH to include our custom find modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) +# Find required libraries +find_package(LibSSH REQUIRED) + # Auto-detect source files file(GLOB_RECURSE SOURCES "src/*.cpp") file(GLOB_RECURSE HEADERS "src/*.hpp") @@ -45,6 +48,7 @@ target_include_directories(dropshell PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/utils ${CMAKE_CURRENT_SOURCE_DIR}/src/contrib $ + ${LIBSSH_INCLUDE_DIRS} ) include(FetchContent) @@ -54,7 +58,7 @@ FetchContent_Declare( GIT_TAG v2.1.5 # ) FetchContent_MakeAvailable(libassert) -target_link_libraries(dropshell libassert::assert) +target_link_libraries(dropshell PRIVATE libassert::assert) # On windows copy libassert.dll to the same directory as the executable for your_target if(WIN32) @@ -68,6 +72,7 @@ endif() # Link libraries target_link_libraries(dropshell PRIVATE + ${LIBSSH_LIBRARIES} ) # Install targets diff --git a/cmake/FindLibSSH.cmake b/cmake/FindLibSSH.cmake new file mode 100644 index 0000000..6cd2e4d --- /dev/null +++ b/cmake/FindLibSSH.cmake @@ -0,0 +1,35 @@ +# - Try to find LibSSH +# Once done this will define +# LIBSSH_FOUND - System has LibSSH +# LIBSSH_INCLUDE_DIRS - The LibSSH include directories +# LIBSSH_LIBRARIES - The libraries needed to use LibSSH +# LIBSSH_DEFINITIONS - Compiler switches required for using LibSSH + +find_package(PkgConfig QUIET) +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_LIBSSH QUIET libssh) + set(LIBSSH_DEFINITIONS ${PC_LIBSSH_CFLAGS_OTHER}) +endif() + +find_path(LIBSSH_INCLUDE_DIR + NAMES libssh/libssh.h + HINTS ${PC_LIBSSH_INCLUDEDIR} ${PC_LIBSSH_INCLUDE_DIRS} + PATH_SUFFIXES libssh +) + +find_library(LIBSSH_LIBRARY + NAMES ssh libssh + HINTS ${PC_LIBSSH_LIBDIR} ${PC_LIBSSH_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +# Handle the QUIETLY and REQUIRED arguments and set LIBSSH_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(LibSSH DEFAULT_MSG + LIBSSH_LIBRARY LIBSSH_INCLUDE_DIR +) + +mark_as_advanced(LIBSSH_INCLUDE_DIR LIBSSH_LIBRARY) + +set(LIBSSH_LIBRARIES ${LIBSSH_LIBRARY}) +set(LIBSSH_INCLUDE_DIRS ${LIBSSH_INCLUDE_DIR}) \ No newline at end of file diff --git a/src/service_runner.cpp b/src/service_runner.cpp index d5e6a62..626681d 100644 --- a/src/service_runner.cpp +++ b/src/service_runner.cpp @@ -66,6 +66,9 @@ bool service_runner::install(bool silent) { return false; } + // make sure all shell files are executable + make_shell_files_executable(tinfo.local_template_path().string()); + // Copy template files { if (!rsync_copy(tinfo.local_template_path().string()+"/", remotepath::service_template(mServer, mService)+"/", silent)) { @@ -387,7 +390,7 @@ bool service_runner::interactive_ssh(const std::string & server_name, const std: std::cerr << "Error: Invalid server environment file: " << server_name << std::endl; return false; } - return 0==runner::execute_cmd("bash",{}, "", {}, false, true, env.get_SSH_INFO()); + return 0==runner::execute_cmd("", {}, "", {}, false, true, env.get_SSH_INFO()); } void service_runner::edit_server(const std::string &server_name) diff --git a/src/utils/runner.cpp b/src/utils/runner.cpp index 0e55072..87f049f 100644 --- a/src/utils/runner.cpp +++ b/src/utils/runner.cpp @@ -118,36 +118,56 @@ std::string ssh_build_command_only(const std::string& command, const std::vector } int ssh_interactive_shell_session(ssh_session session, ssh_channel channel, const std::string& remote_cmd_str, const std::string& command, std::string* output) { + // Request a PTY with xterm-256color type for color support + // First try using default terminal settings - should work with libssh 0.9.0+ int rc = ssh_channel_request_pty(channel); if (rc != SSH_OK) { if (output) *output = std::string("Failed to request pty: ") + ssh_get_error(session); return -1; } + + // Try to explicitly tell the server we want colors through additional means + // 1. Set TERM environment variable + rc = ssh_channel_request_env(channel, "TERM", "xterm-256color"); + // Ignore errors - this is optional + + // 2. Request a shell rc = ssh_channel_request_shell(channel); if (rc != SSH_OK) { if (output) *output = std::string("Failed to request shell: ") + ssh_get_error(session); return -1; } + struct termios orig_termios, raw_termios; tcgetattr(STDIN_FILENO, &orig_termios); raw_termios = orig_termios; cfmakeraw(&raw_termios); tcsetattr(STDIN_FILENO, TCSANOW, &raw_termios); + if (!command.empty()) { ssh_channel_write(channel, remote_cmd_str.c_str(), remote_cmd_str.size()); ssh_channel_write(channel, "\n", 1); + } else { + // Initialize bash with color support if no specific command + std::string init_cmd = "export TERM=xterm-256color && if [ -f ~/.bashrc ]; then source ~/.bashrc; fi"; + ssh_channel_write(channel, init_cmd.c_str(), init_cmd.size()); + ssh_channel_write(channel, "\n", 1); } + int maxfd = STDIN_FILENO > STDOUT_FILENO ? STDIN_FILENO : STDOUT_FILENO; maxfd = maxfd > ssh_get_fd(session) ? maxfd : ssh_get_fd(session); char buffer[4096]; bool done = false; + while (!done) { fd_set fds_read; FD_ZERO(&fds_read); FD_SET(STDIN_FILENO, &fds_read); FD_SET(ssh_get_fd(session), &fds_read); + int ret = select(maxfd + 1, &fds_read, nullptr, nullptr, nullptr); if (ret < 0) break; + if (FD_ISSET(STDIN_FILENO, &fds_read)) { ssize_t n = read(STDIN_FILENO, buffer, sizeof(buffer)); if (n > 0) { @@ -157,6 +177,7 @@ int ssh_interactive_shell_session(ssh_session session, ssh_channel channel, cons done = true; } } + if (FD_ISSET(ssh_get_fd(session), &fds_read)) { int n = ssh_channel_read(channel, buffer, sizeof(buffer), 0); if (n > 0) { @@ -165,10 +186,12 @@ int ssh_interactive_shell_session(ssh_session session, ssh_channel channel, cons done = true; } } + if (ssh_channel_is_closed(channel) || ssh_channel_is_eof(channel)) { done = true; } } + tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); return 0; } diff --git a/src/utils/runner.hpp b/src/utils/runner.hpp index 2f6a697..ce908bb 100644 --- a/src/utils/runner.hpp +++ b/src/utils/runner.hpp @@ -31,7 +31,7 @@ int execute_cmd( const std::map& env, const bool silent, const bool interactive, - const copySSHPtr sshinfo = nullptr, + const copySSHPtr& sshinfo = nullptr, std::string* output = nullptr ); diff --git a/src/utils/utils.cpp b/src/utils/utils.cpp index a84d905..926b105 100644 --- a/src/utils/utils.cpp +++ b/src/utils/utils.cpp @@ -312,6 +312,32 @@ std::string random_alphanumeric_string(int length) return random_string; } +void make_shell_files_executable(const std::string &dir_path) +{ // recursively make all shell files in the directory executable + + const auto desired_perms = std::filesystem::perms::owner_read | std::filesystem::perms::owner_exec | + std::filesystem::perms::group_read | std::filesystem::perms::group_exec | + std::filesystem::perms::others_read | std::filesystem::perms::others_exec; + + for (const auto& entry : std::filesystem::directory_iterator(dir_path)) { + if (entry.path().extension() == ".sh") { + // check if permissions are already set + auto currentperms = std::filesystem::status(entry.path()).permissions(); + if ((currentperms & desired_perms) == desired_perms) { + continue; + } + + // set permissions + std::cout << "Setting executable permissions for " << entry.path() << std::endl; + + std::filesystem::permissions(entry.path(), desired_perms, std::filesystem::perm_options::add); + } + else if (std::filesystem::is_directory(entry.path())) { + make_shell_files_executable(entry.path()); + } + } +} + std::string requote(std::string str) { return quote(trim(dequote(trim(str)))); } diff --git a/src/utils/utils.hpp b/src/utils/utils.hpp index 58febfb..597737e 100644 --- a/src/utils/utils.hpp +++ b/src/utils/utils.hpp @@ -41,4 +41,6 @@ std::string replace_with_environment_variables_like_bash(std::string str); std::string random_alphanumeric_string(int length); +void make_shell_files_executable(const std::string& dir_path); + } // namespace dropshell \ No newline at end of file diff --git a/templates/dropshell-agent/_nuke_other.sh b/templates/dropshell-agent/_nuke_other.sh old mode 100644 new mode 100755 diff --git a/templates/dropshell-agent/install.sh b/templates/dropshell-agent/install.sh old mode 100644 new mode 100755 diff --git a/templates/dropshell-agent/shared/_allservicesstatus.sh b/templates/dropshell-agent/shared/_allservicesstatus.sh old mode 100644 new mode 100755 diff --git a/templates/dropshell-agent/shared/_autocommands.sh b/templates/dropshell-agent/shared/_autocommands.sh old mode 100644 new mode 100755 diff --git a/templates/dropshell-agent/shared/_common.sh b/templates/dropshell-agent/shared/_common.sh old mode 100644 new mode 100755 diff --git a/templates/dropshell-agent/uninstall.sh b/templates/dropshell-agent/uninstall.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/backup.sh b/templates/squashkiwi/backup.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/install.sh b/templates/squashkiwi/install.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/logs.sh b/templates/squashkiwi/logs.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/nuke.sh b/templates/squashkiwi/nuke.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/ports.sh b/templates/squashkiwi/ports.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/restore.sh b/templates/squashkiwi/restore.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/ssh.sh b/templates/squashkiwi/ssh.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/start.sh b/templates/squashkiwi/start.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/status.sh b/templates/squashkiwi/status.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/stop.sh b/templates/squashkiwi/stop.sh old mode 100644 new mode 100755 diff --git a/templates/squashkiwi/uninstall.sh b/templates/squashkiwi/uninstall.sh old mode 100644 new mode 100755