This commit is contained in:
parent
068ef34709
commit
8827ea5a42
@ -1,90 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
project(runner VERSION 1.0)
|
project(runner_demo)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
# Include directories
|
find_package(OpenSSL REQUIRED)
|
||||||
include_directories(include)
|
|
||||||
|
|
||||||
# Find required packages
|
add_library(runner_lib runner.cpp)
|
||||||
find_package(nlohmann_json QUIET)
|
target_include_directories(runner_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
if(NOT nlohmann_json_FOUND)
|
|
||||||
include(FetchContent)
|
|
||||||
FetchContent_Declare(
|
|
||||||
nlohmann_json
|
|
||||||
GIT_REPOSITORY https://github.com/nlohmann/json.git
|
|
||||||
GIT_TAG v3.11.2
|
|
||||||
)
|
|
||||||
FetchContent_MakeAvailable(nlohmann_json)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Try to find libssh using different methods
|
add_executable(runner runner_demo.cpp)
|
||||||
find_package(libssh QUIET)
|
target_link_libraries(runner runner_lib OpenSSL::SSL OpenSSL::Crypto)
|
||||||
if(NOT libssh_FOUND)
|
|
||||||
find_package(PkgConfig QUIET)
|
|
||||||
if(PKG_CONFIG_FOUND)
|
|
||||||
pkg_check_modules(LIBSSH libssh QUIET)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT LIBSSH_FOUND)
|
|
||||||
# Try to find manually if pkg-config failed too
|
|
||||||
find_path(LIBSSH_INCLUDE_DIR
|
|
||||||
NAMES libssh/libssh.h
|
|
||||||
PATHS /usr/include /usr/local/include
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(LIBSSH_LIBRARY
|
|
||||||
NAMES ssh libssh
|
|
||||||
PATHS /usr/lib /usr/local/lib /usr/lib/x86_64-linux-gnu
|
|
||||||
)
|
|
||||||
|
|
||||||
if(LIBSSH_INCLUDE_DIR AND LIBSSH_LIBRARY)
|
|
||||||
set(LIBSSH_FOUND TRUE)
|
|
||||||
set(LIBSSH_LIBRARIES ${LIBSSH_LIBRARY})
|
|
||||||
set(LIBSSH_INCLUDE_DIRS ${LIBSSH_INCLUDE_DIR})
|
|
||||||
message(STATUS "Found libssh: ${LIBSSH_LIBRARY}")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "libssh not found. Please install libssh-dev package.\nOn Ubuntu/Debian: sudo apt install libssh-dev\nOn CentOS/RHEL: sudo yum install libssh-devel\nOn macOS: brew install libssh")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Print libssh information for debugging
|
|
||||||
message(STATUS "LIBSSH_FOUND: ${LIBSSH_FOUND}")
|
|
||||||
message(STATUS "LIBSSH_LIBRARIES: ${LIBSSH_LIBRARIES}")
|
|
||||||
message(STATUS "LIBSSH_INCLUDE_DIRS: ${LIBSSH_INCLUDE_DIRS}")
|
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
|
|
||||||
# Library target
|
|
||||||
add_library(dropshell_runner STATIC
|
|
||||||
src/runner.cpp
|
|
||||||
src/base64.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(dropshell_runner PUBLIC
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
|
||||||
${LIBSSH_INCLUDE_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Link with libssh
|
|
||||||
if(libssh_FOUND)
|
|
||||||
# libssh was found using the find_package method
|
|
||||||
target_link_libraries(dropshell_runner PUBLIC
|
|
||||||
nlohmann_json::nlohmann_json
|
|
||||||
ssh
|
|
||||||
Threads::Threads
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
# libssh was found using pkg-config or manual search
|
|
||||||
target_link_libraries(dropshell_runner PUBLIC
|
|
||||||
nlohmann_json::nlohmann_json
|
|
||||||
${LIBSSH_LIBRARIES}
|
|
||||||
Threads::Threads
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Executable target
|
|
||||||
add_executable(runner src/main.cpp)
|
|
||||||
target_link_libraries(runner PRIVATE dropshell_runner)
|
|
254
runner/README.md
254
runner/README.md
@ -7,207 +7,77 @@ use:
|
|||||||
|
|
||||||
BASE64COMMAND is Base64 encoded json. The json format is as described below. The exit code is that of the command run, or -1 if the command couldn't be run.
|
BASE64COMMAND is Base64 encoded json. The json format is as described below. The exit code is that of the command run, or -1 if the command couldn't be run.
|
||||||
|
|
||||||
The c++ library used, which is contained in this codebase, has two simple functions:
|
|
||||||
bool runner(nlohmann::json run_json); // no output capture
|
|
||||||
bool runner(nlohmann::json run_json, std::string & output); // with output capture.
|
|
||||||
|
|
||||||
## JSON Specification
|
|
||||||
|
|
||||||
```json
|
The c++ library used, which is contained in this codebase, has one simple function:
|
||||||
|
|
||||||
|
typedef struct sSSHInfo {
|
||||||
|
std::string host;
|
||||||
|
std::string user;
|
||||||
|
std::string port;
|
||||||
|
} sSSHInfo;
|
||||||
|
|
||||||
|
|
||||||
|
static int execute_cmd(
|
||||||
|
const std::string& command,
|
||||||
|
const std::vector<std::string>& args,
|
||||||
|
const std::string& working_dir,
|
||||||
|
const std::map<std::string, std::string>& env,
|
||||||
|
bool silent,
|
||||||
|
bool interactive,
|
||||||
|
sSSHInfo * sshinfo = nullptr,
|
||||||
|
std::string* output = nullptr,
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
If SSH information is provided, the command will be executed on the remote server. Otherwise on the local machine.
|
||||||
|
|
||||||
|
If output is provided, the output of the command is captured.
|
||||||
|
|
||||||
|
If interactive is true, then an interactive session is created - e.g. for running nano to remotely edit a file, or sshing into a docker container from the remote host.
|
||||||
|
|
||||||
|
If silent is true, then all output is suppressed.
|
||||||
|
|
||||||
|
Before the command is run, the current directory is changed to working_dir on the remote host (or local host if no ssh info provided).
|
||||||
|
|
||||||
|
|
||||||
|
## BASE64COMMAND JSON Format
|
||||||
|
|
||||||
|
The BASE64COMMAND argument should be a Base64-encoded JSON object with the following fields:
|
||||||
|
|
||||||
|
- `command` (string, required): The command to execute (e.g., "ls").
|
||||||
|
- `args` (array of strings, optional): Arguments to pass to the command (e.g., ["-l", "/tmp"]).
|
||||||
|
- `working_dir` (string, optional): Directory to change to before running the command.
|
||||||
|
- `env` (object, optional): Environment variables to set (e.g., {"FOO": "bar"}).
|
||||||
|
- `silent` (boolean, optional): If true, suppress all output. Default: false.
|
||||||
|
- `interactive` (boolean, optional): If true, run the command interactively. Default: false.
|
||||||
|
- `sshinfo` (object, optional): If present, run the command on a remote host (not implemented in demo):
|
||||||
|
- `host` (string): Hostname or IP.
|
||||||
|
- `user` (string): Username.
|
||||||
|
- `port` (string): SSH port.
|
||||||
|
|
||||||
|
### Example JSON
|
||||||
|
|
||||||
|
```
|
||||||
{
|
{
|
||||||
"ssh": { // Optional: SSH connection information
|
"command": "ls",
|
||||||
"host": "hostname", // Remote host to connect to
|
"args": ["-l", "/tmp"],
|
||||||
"port": 22, // Port number (default: 22)
|
"working_dir": "/",
|
||||||
"user": "username", // Username for SSH connection
|
"env": {"FOO": "bar"},
|
||||||
"key": "path/to/key" // Path to SSH key file or "auto" to use current user's key
|
"silent": false,
|
||||||
},
|
"interactive": false
|
||||||
"working_directory": "/path", // Optional: Directory to change to before executing the command
|
|
||||||
"env": { // Optional: Environment variables
|
|
||||||
"VAR1": "value1",
|
|
||||||
"VAR2": "value2"
|
|
||||||
},
|
|
||||||
"command": "command_name", // Required: Command to execute
|
|
||||||
"args": ["arg1", "arg2"], // Optional: Command arguments
|
|
||||||
"options": { // Optional: Execution options
|
|
||||||
"silent": false, // Suppress all terminal output (default: false)
|
|
||||||
"interactive": false // Hook up TTY for interactive sessions (default: false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If SSH information is provided, the command will be executed on the remote server.
|
To use, encode the JSON as a single line, then base64 encode it:
|
||||||
|
|
||||||
## Build Instructions
|
```
|
||||||
|
echo -n '{"command":"ls","args":["-l","/tmp"],"working_dir":"/","env":{"FOO":"bar"},"silent":false,"interactive":false}' | base64
|
||||||
### Prerequisites
|
|
||||||
|
|
||||||
- CMake 3.10 or newer
|
|
||||||
- C++17 compatible compiler
|
|
||||||
- libssh development libraries
|
|
||||||
- jq (for the helper scripts)
|
|
||||||
|
|
||||||
#### Installing Dependencies
|
|
||||||
|
|
||||||
##### Quick Installation (Ubuntu/Debian)
|
|
||||||
|
|
||||||
For Ubuntu/Debian systems, you can use the provided installation script:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ./install_deps.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This will install all required dependencies (cmake, g++, libssh-dev, jq).
|
Then run:
|
||||||
|
|
||||||
##### Manual Installation
|
```
|
||||||
|
./build/runner <BASE64COMMAND>
|
||||||
###### Ubuntu/Debian
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt-get install cmake g++ libssh-dev jq
|
|
||||||
```
|
```
|
||||||
|
|
||||||
###### CentOS/RHEL
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo yum install cmake gcc-c++ libssh-devel jq
|
|
||||||
```
|
|
||||||
|
|
||||||
###### macOS
|
|
||||||
|
|
||||||
```bash
|
|
||||||
brew install cmake libssh jq
|
|
||||||
```
|
|
||||||
|
|
||||||
###### Windows
|
|
||||||
|
|
||||||
Using vcpkg:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
vcpkg install libssh nlohmann-json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Building
|
|
||||||
|
|
||||||
To build the project, you can use the provided build script:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./build.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
Or manually:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mkdir -p build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
make
|
|
||||||
```
|
|
||||||
|
|
||||||
The executable will be created at `build/runner`.
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
A simple test script is included to verify the functionality:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./test.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
This will run basic tests for command execution, environment variables, silent mode, and return codes.
|
|
||||||
|
|
||||||
If you have SSH configured on your local machine and want to test the SSH functionality:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ENABLE_SSH_TEST=1 ./test.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
### Troubleshooting
|
|
||||||
|
|
||||||
If CMake cannot find libssh, you can:
|
|
||||||
|
|
||||||
1. Run the libssh finder script to locate your installation:
|
|
||||||
```bash
|
|
||||||
./examples/find_libssh.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Specify its location manually:
|
|
||||||
```bash
|
|
||||||
cmake -DCMAKE_PREFIX_PATH=/path/to/libssh/installation ..
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Or set the libssh_DIR environment variable:
|
|
||||||
```bash
|
|
||||||
export libssh_DIR=/path/to/libssh/installation
|
|
||||||
cmake ..
|
|
||||||
```
|
|
||||||
|
|
||||||
4. If the problem persists, specify the library and include paths directly:
|
|
||||||
```bash
|
|
||||||
cmake -DLIBSSH_LIBRARY=/path/to/libssh.so -DLIBSSH_INCLUDE_DIR=/path/to/include ..
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage Examples
|
|
||||||
|
|
||||||
### Running a local command
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create a JSON configuration for the 'ls -l' command
|
|
||||||
JSON='{"command":"ls","args":["-l"]}'
|
|
||||||
|
|
||||||
# Base64 encode the JSON
|
|
||||||
BASE64=$(echo -n "$JSON" | base64)
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
./build/runner $BASE64
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running a command with environment variables
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create a JSON configuration with environment variables
|
|
||||||
JSON='{"command":"echo","args":["$GREETING"],"env":{"GREETING":"Hello, World!"}}'
|
|
||||||
|
|
||||||
# Base64 encode the JSON
|
|
||||||
BASE64=$(echo -n "$JSON" | base64)
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
./build/runner $BASE64
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running a command on a remote server via SSH
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create a JSON configuration for a remote command
|
|
||||||
JSON='{"ssh":{"host":"example.com","user":"username","key":"auto"},"command":"hostname"}'
|
|
||||||
|
|
||||||
# Base64 encode the JSON
|
|
||||||
BASE64=$(echo -n "$JSON" | base64)
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
./build/runner $BASE64
|
|
||||||
```
|
|
||||||
|
|
||||||
### Running an interactive command
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Create a JSON configuration for an interactive command
|
|
||||||
JSON='{"command":"vim","options":{"interactive":true}}'
|
|
||||||
|
|
||||||
# Base64 encode the JSON
|
|
||||||
BASE64=$(echo -n "$JSON" | base64)
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
./build/runner $BASE64
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the helper script
|
|
||||||
|
|
||||||
The `run.sh` script simplifies testing by handling the JSON validation and Base64 encoding:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run with a JSON file
|
|
||||||
./run.sh examples/local_command.json
|
|
||||||
|
|
||||||
# Run with a JSON string
|
|
||||||
./run.sh '{"command":"echo","args":["Hello World"]}'
|
|
||||||
```
|
|
||||||
|
103
runner/build.sh
103
runner/build.sh
@ -1,78 +1,39 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
# Don't use set -e because we want to handle errors ourselves
|
# Check for cmake
|
||||||
# set -e
|
if ! command -v cmake &> /dev/null; then
|
||||||
|
echo "Error: cmake is not installed. Please install cmake." >&2
|
||||||
echo "Building Runner - Dropshell Command Execution Library"
|
|
||||||
echo "===================================================="
|
|
||||||
|
|
||||||
# Create build directory if it doesn't exist
|
|
||||||
mkdir -p build || {
|
|
||||||
echo "ERROR: Failed to create build directory"
|
|
||||||
exit 1
|
exit 1
|
||||||
}
|
fi
|
||||||
cd build
|
|
||||||
|
|
||||||
# Check if pkg-config is available and can find libssh
|
# Check for g++
|
||||||
if command -v pkg-config &> /dev/null && pkg-config --exists libssh; then
|
if ! command -v g++ &> /dev/null; then
|
||||||
# If libssh is found through pkg-config, use it
|
echo "Error: g++ is not installed. Please install g++." >&2
|
||||||
LIBSSH_PREFIX=$(pkg-config --variable=prefix libssh)
|
exit 1
|
||||||
echo "Found libssh through pkg-config at: $LIBSSH_PREFIX"
|
fi
|
||||||
CMAKE_ARGS="-DCMAKE_PREFIX_PATH=$LIBSSH_PREFIX"
|
|
||||||
|
# Check for OpenSSL
|
||||||
|
if ! pkg-config --exists openssl; then
|
||||||
|
echo "Error: OpenSSL development libraries not found. Please install libssl-dev." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for nlohmann_json
|
||||||
|
if ! pkg-config --exists nlohmann_json; then
|
||||||
|
echo "Warning: nlohmann_json not found via pkg-config. Make sure it is installed or available to CMake." >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
BUILD_DIR=build
|
||||||
|
mkdir -p "$BUILD_DIR"
|
||||||
|
cd "$BUILD_DIR"
|
||||||
|
|
||||||
|
cmake ..
|
||||||
|
make -j$(nproc)
|
||||||
|
|
||||||
|
if [ -f runner ]; then
|
||||||
|
echo "Build successful. Run ./build/runner BASE64COMMAND to test."
|
||||||
else
|
else
|
||||||
# Otherwise, let CMake try to find it
|
echo "Build failed. Check the output above for errors."
|
||||||
CMAKE_ARGS=""
|
|
||||||
|
|
||||||
# Check if libssh-dev/libssh-devel is installed
|
|
||||||
if [ ! -f "/usr/include/libssh/libssh.h" ] && [ ! -f "/usr/local/include/libssh/libssh.h" ]; then
|
|
||||||
echo "WARNING: libssh development headers not found in standard locations."
|
|
||||||
echo "You may need to install the libssh development package:"
|
|
||||||
echo " - Ubuntu/Debian: sudo apt install libssh-dev"
|
|
||||||
echo " - CentOS/RHEL: sudo yum install libssh-devel"
|
|
||||||
echo " - macOS: brew install libssh"
|
|
||||||
echo ""
|
|
||||||
echo "Continuing build anyway, but it may fail..."
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Offer to run the find_libssh.sh helper script
|
|
||||||
if [ -x "../examples/find_libssh.sh" ]; then
|
|
||||||
echo "Would you like to run the libssh finder helper script? (y/n)"
|
|
||||||
read -r answer
|
|
||||||
if [[ "$answer" =~ ^[Yy] ]]; then
|
|
||||||
cd ..
|
|
||||||
./examples/find_libssh.sh
|
|
||||||
cd build
|
|
||||||
echo ""
|
|
||||||
echo "Continuing with build..."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Configure and build with error handling
|
|
||||||
echo "Running cmake with args: $CMAKE_ARGS"
|
|
||||||
if ! cmake $CMAKE_ARGS ..; then
|
|
||||||
echo ""
|
|
||||||
echo "ERROR: CMake configuration failed."
|
|
||||||
echo "This is likely due to missing dependencies. Please check the error message above."
|
|
||||||
echo "For libssh issues, try:"
|
|
||||||
echo " 1. Install libssh development package: sudo apt install libssh-dev"
|
|
||||||
echo " 2. Run our helper script: ./examples/find_libssh.sh"
|
|
||||||
echo " 3. If you know where libssh is installed, specify it with:"
|
|
||||||
echo " cmake -DLIBSSH_LIBRARY=/path/to/libssh.so -DLIBSSH_INCLUDE_DIR=/path/to/include .."
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! make -j$(nproc); then
|
|
||||||
echo ""
|
|
||||||
echo "ERROR: Build failed."
|
|
||||||
echo "Please check the error messages above for details."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Build complete. Binary is at build/runner"
|
|
||||||
echo ""
|
|
||||||
echo "Examples:"
|
|
||||||
echo " ./run.sh examples/local_command.json # Run a local command"
|
|
||||||
echo " ./run.sh examples/env_vars.json # Run with environment variables"
|
|
@ -1 +0,0 @@
|
|||||||
{"command":"bash","args":["-c","echo Hello $NAME, the current directory is $PWD"],"env":{"NAME":"Runner","CUSTOM_VAR":"This is a custom environment variable"}}
|
|
@ -1,64 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Helper script to find libssh installation on your system
|
|
||||||
|
|
||||||
echo "Looking for libssh installation..."
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Check pkg-config path
|
|
||||||
if command -v pkg-config &> /dev/null; then
|
|
||||||
echo "Checking pkg-config for libssh:"
|
|
||||||
if pkg-config --exists libssh; then
|
|
||||||
echo " - Found libssh with pkg-config"
|
|
||||||
echo " - Version: $(pkg-config --modversion libssh)"
|
|
||||||
echo " - Include path: $(pkg-config --variable=includedir libssh)"
|
|
||||||
echo " - Library path: $(pkg-config --variable=libdir libssh)"
|
|
||||||
echo
|
|
||||||
echo "To use this in cmake:"
|
|
||||||
echo " cmake -DCMAKE_PREFIX_PATH=$(pkg-config --variable=prefix libssh) .."
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
echo " - libssh not found with pkg-config"
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "pkg-config not found on your system"
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check common include paths
|
|
||||||
for DIR in /usr/include /usr/local/include /opt/local/include; do
|
|
||||||
if [ -d "$DIR" ] && [ -f "$DIR/libssh/libssh.h" ]; then
|
|
||||||
echo "Found libssh headers at: $DIR/libssh/libssh.h"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Check common library paths
|
|
||||||
LIB_PATHS=()
|
|
||||||
for DIR in /usr/lib /usr/local/lib /usr/lib/x86_64-linux-gnu /usr/local/lib64 /opt/local/lib; do
|
|
||||||
if [ -d "$DIR" ]; then
|
|
||||||
LIBS=$(find "$DIR" -name "libssh.so*" -o -name "libssh.dylib" -o -name "libssh.a" 2>/dev/null)
|
|
||||||
if [ -n "$LIBS" ]; then
|
|
||||||
echo "Found libssh libraries in $DIR:"
|
|
||||||
echo "$LIBS" | sed 's/^/ - /'
|
|
||||||
LIB_DIR="$DIR"
|
|
||||||
LIB_PATHS+=("$DIR")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo
|
|
||||||
if [ ${#LIB_PATHS[@]} -gt 0 ]; then
|
|
||||||
echo "To build with the detected libssh installation, you can use:"
|
|
||||||
echo " cmake -DLIBSSH_LIBRARY=${LIB_PATHS[0]}/libssh.so -DLIBSSH_INCLUDE_DIR=/usr/include .."
|
|
||||||
echo
|
|
||||||
echo "Or set environment variables:"
|
|
||||||
echo " export LIBSSH_LIBRARY=${LIB_PATHS[0]}/libssh.so"
|
|
||||||
echo " export LIBSSH_INCLUDE_DIR=/usr/include"
|
|
||||||
echo " cmake .."
|
|
||||||
else
|
|
||||||
echo "Could not find libssh on your system."
|
|
||||||
echo "Please install it with your package manager:"
|
|
||||||
echo " - Ubuntu/Debian: sudo apt install libssh-dev"
|
|
||||||
echo " - CentOS/RHEL: sudo yum install libssh-devel"
|
|
||||||
echo " - macOS: brew install libssh"
|
|
||||||
fi
|
|
@ -1 +0,0 @@
|
|||||||
{"command":"bash","options":{"interactive":true}}
|
|
@ -1 +0,0 @@
|
|||||||
{"command":"ls","args":["-la"],"options":{"silent":false}}
|
|
@ -1 +0,0 @@
|
|||||||
{"ssh":{"host":"localhost","key":"auto"},"command":"echo","args":["Hello from SSH on localhost!"]}
|
|
@ -1 +0,0 @@
|
|||||||
{"command":"ls","args":["-la"],"options":{"silent":true}}
|
|
@ -1 +0,0 @@
|
|||||||
{"ssh":{"host":"localhost","port":22,"user":"USERNAME","key":"auto"},"command":"hostname","options":{"interactive":false}}
|
|
@ -1 +0,0 @@
|
|||||||
{"ssh":{"host":"localhost","user":"USERNAME","key":"auto"},"command":"pwd","working_directory":"/tmp"}
|
|
@ -1 +0,0 @@
|
|||||||
{"command":"pwd","working_directory":"/tmp"}
|
|
@ -1,2 +0,0 @@
|
|||||||
lkjdfslka
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
#ifndef BASE64_H
|
|
||||||
#define BASE64_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a Base64 encoded string to its original form
|
|
||||||
* @param encoded The Base64 encoded string
|
|
||||||
* @return The decoded string
|
|
||||||
*/
|
|
||||||
std::string base64_decode(const std::string& encoded);
|
|
||||||
|
|
||||||
#endif // BASE64_H
|
|
@ -1,104 +0,0 @@
|
|||||||
#ifndef DROPSHELL_RUNNER_H
|
|
||||||
#define DROPSHELL_RUNNER_H
|
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
namespace dropshell {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runner library for executing commands locally or remotely via SSH
|
|
||||||
*/
|
|
||||||
class Runner {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Execute a command according to the specification in the JSON
|
|
||||||
* @param run_json JSON specification for the command
|
|
||||||
* @return true if command executed successfully, false otherwise
|
|
||||||
*/
|
|
||||||
static bool run(const nlohmann::json& run_json);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a command and capture its output
|
|
||||||
* @param run_json JSON specification for the command
|
|
||||||
* @param output Reference to string where output will be stored
|
|
||||||
* @return true if command executed successfully, false otherwise
|
|
||||||
*/
|
|
||||||
static bool run(const nlohmann::json& run_json, std::string& output);
|
|
||||||
|
|
||||||
private:
|
|
||||||
/**
|
|
||||||
* Execute a command locally
|
|
||||||
* @param command Command to execute
|
|
||||||
* @param args Command arguments
|
|
||||||
* @param working_dir Working directory to change to before execution
|
|
||||||
* @param env Environment variables
|
|
||||||
* @param silent Whether to suppress output
|
|
||||||
* @param interactive Whether to enable interactive mode
|
|
||||||
* @param output Reference to string where output will be stored (if capturing)
|
|
||||||
* @param capture_output Whether to capture output
|
|
||||||
* @return exit code of the command, or -1 if execution failed
|
|
||||||
*/
|
|
||||||
static int execute_local(
|
|
||||||
const std::string& command,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::string& working_dir,
|
|
||||||
const std::map<std::string, std::string>& env,
|
|
||||||
bool silent,
|
|
||||||
bool interactive,
|
|
||||||
std::string* output = nullptr,
|
|
||||||
bool capture_output = false
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a command remotely via SSH
|
|
||||||
* @param ssh_config SSH configuration
|
|
||||||
* @param command Command to execute
|
|
||||||
* @param args Command arguments
|
|
||||||
* @param working_dir Working directory to change to on the remote server
|
|
||||||
* @param env Environment variables
|
|
||||||
* @param silent Whether to suppress output
|
|
||||||
* @param interactive Whether to enable interactive mode
|
|
||||||
* @param output Reference to string where output will be stored (if capturing)
|
|
||||||
* @param capture_output Whether to capture output
|
|
||||||
* @return exit code of the command, or -1 if execution failed
|
|
||||||
*/
|
|
||||||
static int execute_ssh(
|
|
||||||
const nlohmann::json& ssh_config,
|
|
||||||
const std::string& command,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::string& working_dir,
|
|
||||||
const std::map<std::string, std::string>& env,
|
|
||||||
bool silent,
|
|
||||||
bool interactive,
|
|
||||||
std::string* output = nullptr,
|
|
||||||
bool capture_output = false
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse command specification from JSON
|
|
||||||
* @param run_json JSON specification
|
|
||||||
* @param command Output parameter for command
|
|
||||||
* @param args Output parameter for command arguments
|
|
||||||
* @param working_dir Output parameter for working directory
|
|
||||||
* @param env Output parameter for environment variables
|
|
||||||
* @param silent Output parameter for silent option
|
|
||||||
* @param interactive Output parameter for interactive option
|
|
||||||
* @return true if parsing was successful, false otherwise
|
|
||||||
*/
|
|
||||||
static bool parse_json(
|
|
||||||
const nlohmann::json& run_json,
|
|
||||||
std::string& command,
|
|
||||||
std::vector<std::string>& args,
|
|
||||||
std::string& working_dir,
|
|
||||||
std::map<std::string, std::string>& env,
|
|
||||||
bool& silent,
|
|
||||||
bool& interactive
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace dropshell
|
|
||||||
|
|
||||||
#endif // DROPSHELL_RUNNER_H
|
|
@ -1,64 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Install dependencies script for Runner
|
|
||||||
# This script is for Ubuntu/Debian systems
|
|
||||||
|
|
||||||
echo "Installing dependencies for Runner - Dropshell Command Execution Library"
|
|
||||||
echo "======================================================================"
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Check if running as root or with sudo
|
|
||||||
if [ "$EUID" -ne 0 ]; then
|
|
||||||
echo "Please run this script with sudo:"
|
|
||||||
echo " sudo $0"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Updating package lists..."
|
|
||||||
apt-get update
|
|
||||||
|
|
||||||
echo "Installing required packages:"
|
|
||||||
echo " - build-essential (C++ compiler and build tools)"
|
|
||||||
echo " - cmake (build system)"
|
|
||||||
echo " - libssh-dev (SSH client library)"
|
|
||||||
echo " - jq (JSON parsing for helper scripts)"
|
|
||||||
echo
|
|
||||||
|
|
||||||
apt-get install -y build-essential cmake libssh-dev jq
|
|
||||||
|
|
||||||
# Check if installation was successful
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo
|
|
||||||
echo "Dependencies installed successfully!"
|
|
||||||
echo
|
|
||||||
echo "You can now build the project with:"
|
|
||||||
echo " ./build.sh"
|
|
||||||
echo
|
|
||||||
else
|
|
||||||
echo
|
|
||||||
echo "Error: Failed to install dependencies."
|
|
||||||
echo "Please check the error messages above."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Verify libssh installation
|
|
||||||
if [ -f "/usr/include/libssh/libssh.h" ]; then
|
|
||||||
echo "Verified: libssh development headers are installed."
|
|
||||||
|
|
||||||
# Find libssh shared library
|
|
||||||
LIB=$(find /usr/lib -name "libssh.so*" | head -1)
|
|
||||||
if [ -n "$LIB" ]; then
|
|
||||||
echo "Verified: libssh shared library is installed at $LIB"
|
|
||||||
else
|
|
||||||
echo "Warning: Could not find libssh shared library."
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "You're all set! Build the project with ./build.sh"
|
|
||||||
else
|
|
||||||
echo "Warning: Could not verify libssh installation."
|
|
||||||
echo "The package might have been installed in a non-standard location."
|
|
||||||
echo "You may need to manually specify the location when building:"
|
|
||||||
echo
|
|
||||||
echo " cmake -DLIBSSH_LIBRARY=/path/to/libssh.so -DLIBSSH_INCLUDE_DIR=/path/to/include .."
|
|
||||||
fi
|
|
20
runner/jt.sh
20
runner/jt.sh
@ -1,20 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
|
|
||||||
# JSON=$(cat <<'EOF'
|
|
||||||
# {"command":"nano","args":["-w","./hello.txt"]}
|
|
||||||
# EOF
|
|
||||||
# )
|
|
||||||
|
|
||||||
JSON=$(cat <<'EOF'
|
|
||||||
{"command":"nano","args":["-w","./hello.txt"],"options":{"interactive":true}}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
BASE64=$(echo -n "$JSON" | base64 -w0)
|
|
||||||
echo "JSON: $JSON"
|
|
||||||
echo "Running command..."
|
|
||||||
build/runner "$BASE64"
|
|
||||||
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
echo "Running minimal libssh test to diagnose linking issues"
|
|
||||||
echo "===================================================="
|
|
||||||
|
|
||||||
# Create test directory
|
|
||||||
TESTDIR="ssh_test_tmp"
|
|
||||||
mkdir -p $TESTDIR
|
|
||||||
cd $TESTDIR
|
|
||||||
|
|
||||||
# Create minimal C program that uses libssh
|
|
||||||
cat > test.c << 'EOF'
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <libssh/libssh.h>
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
ssh_session session = ssh_new();
|
|
||||||
if (session == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create SSH session\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Successfully created SSH session\n");
|
|
||||||
|
|
||||||
ssh_free(session);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "Created minimal test program. Attempting to compile..."
|
|
||||||
|
|
||||||
# Check pkg-config first
|
|
||||||
if command -v pkg-config &> /dev/null && pkg-config --exists libssh; then
|
|
||||||
CFLAGS=$(pkg-config --cflags libssh)
|
|
||||||
LIBS=$(pkg-config --libs libssh)
|
|
||||||
|
|
||||||
echo "Found libssh with pkg-config:"
|
|
||||||
echo " CFLAGS: $CFLAGS"
|
|
||||||
echo " LIBS: $LIBS"
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "Compiling with pkg-config flags..."
|
|
||||||
gcc -o test_pkg test.c $CFLAGS $LIBS
|
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "Compilation successful!"
|
|
||||||
echo "Running the test program:"
|
|
||||||
./test_pkg
|
|
||||||
else
|
|
||||||
echo "Compilation failed with pkg-config flags."
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Try with simple -lssh flag
|
|
||||||
echo "Compiling with simple -lssh flag..."
|
|
||||||
gcc -o test_simple test.c -lssh
|
|
||||||
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "Compilation successful with -lssh!"
|
|
||||||
echo "Running the test program:"
|
|
||||||
./test_simple
|
|
||||||
else
|
|
||||||
echo "Compilation failed with simple -lssh flag."
|
|
||||||
echo "This indicates your libssh installation might not be in the standard location."
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Try to find libssh manually
|
|
||||||
echo "Searching for libssh.so in common locations..."
|
|
||||||
LIBSSH_PATHS=$(find /usr/lib /usr/local/lib /usr/lib/x86_64-linux-gnu -name "libssh.so*" 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -n "$LIBSSH_PATHS" ]; then
|
|
||||||
echo "Found libssh in the following locations:"
|
|
||||||
echo "$LIBSSH_PATHS" | sed 's/^/ - /'
|
|
||||||
|
|
||||||
# Extract directory of first result
|
|
||||||
LIBSSH_DIR=$(dirname $(echo "$LIBSSH_PATHS" | head -1))
|
|
||||||
echo
|
|
||||||
echo "For CMake, you can try:"
|
|
||||||
echo " cmake -DLIBSSH_LIBRARY=$LIBSSH_DIR/libssh.so -DLIBSSH_INCLUDE_DIR=/usr/include .."
|
|
||||||
else
|
|
||||||
echo "Could not find libssh.so."
|
|
||||||
echo "Consider installing libssh-dev package:"
|
|
||||||
echo " sudo apt install libssh-dev"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
cd ..
|
|
||||||
echo
|
|
||||||
echo "Cleaning up..."
|
|
||||||
rm -rf $TESTDIR
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Test completed. Use this information to troubleshoot your libssh configuration."
|
|
25578
runner/nlohmann/json.hpp
Normal file
25578
runner/nlohmann/json.hpp
Normal file
File diff suppressed because it is too large
Load Diff
187
runner/nlohmann/json_fwd.hpp
Normal file
187
runner/nlohmann/json_fwd.hpp
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||||
|
#define INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
||||||
|
|
||||||
|
#include <cstdint> // int64_t, uint64_t
|
||||||
|
#include <map> // map
|
||||||
|
#include <memory> // allocator
|
||||||
|
#include <string> // string
|
||||||
|
#include <vector> // vector
|
||||||
|
|
||||||
|
// #include <nlohmann/detail/abi_macros.hpp>
|
||||||
|
// __ _____ _____ _____
|
||||||
|
// __| | __| | | | JSON for Modern C++
|
||||||
|
// | | |__ | | | | | | version 3.12.0
|
||||||
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
||||||
|
//
|
||||||
|
// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// This file contains all macro definitions affecting or depending on the ABI
|
||||||
|
|
||||||
|
#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
|
||||||
|
#if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
|
||||||
|
#if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0
|
||||||
|
#warning "Already included a different version of the library!"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
|
||||||
|
#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
|
||||||
|
#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
|
||||||
|
|
||||||
|
#ifndef JSON_DIAGNOSTICS
|
||||||
|
#define JSON_DIAGNOSTICS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_DIAGNOSTIC_POSITIONS
|
||||||
|
#define JSON_DIAGNOSTIC_POSITIONS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||||
|
#define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JSON_DIAGNOSTICS
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JSON_DIAGNOSTIC_POSITIONS
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Construct the namespace ABI tags component
|
||||||
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
|
||||||
|
#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
|
||||||
|
NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
|
||||||
|
|
||||||
|
#define NLOHMANN_JSON_ABI_TAGS \
|
||||||
|
NLOHMANN_JSON_ABI_TAGS_CONCAT( \
|
||||||
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
|
||||||
|
NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
|
||||||
|
NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)
|
||||||
|
|
||||||
|
// Construct the namespace version component
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
|
||||||
|
_v ## major ## _ ## minor ## _ ## patch
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
|
||||||
|
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
|
||||||
|
|
||||||
|
#if NLOHMANN_JSON_NAMESPACE_NO_VERSION
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE_VERSION
|
||||||
|
#else
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE_VERSION \
|
||||||
|
NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
|
||||||
|
NLOHMANN_JSON_VERSION_MINOR, \
|
||||||
|
NLOHMANN_JSON_VERSION_PATCH)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Combine namespace components
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
|
||||||
|
NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
|
||||||
|
|
||||||
|
#ifndef NLOHMANN_JSON_NAMESPACE
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE \
|
||||||
|
nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||||
|
NLOHMANN_JSON_ABI_TAGS, \
|
||||||
|
NLOHMANN_JSON_NAMESPACE_VERSION)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE_BEGIN \
|
||||||
|
namespace nlohmann \
|
||||||
|
{ \
|
||||||
|
inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
|
||||||
|
NLOHMANN_JSON_ABI_TAGS, \
|
||||||
|
NLOHMANN_JSON_NAMESPACE_VERSION) \
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
#define NLOHMANN_JSON_NAMESPACE_END \
|
||||||
|
} /* namespace (inline namespace) NOLINT(readability/namespace) */ \
|
||||||
|
} // namespace nlohmann
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief namespace for Niels Lohmann
|
||||||
|
@see https://github.com/nlohmann
|
||||||
|
@since version 1.0.0
|
||||||
|
*/
|
||||||
|
NLOHMANN_JSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief default JSONSerializer template argument
|
||||||
|
|
||||||
|
This serializer ignores the template arguments and uses ADL
|
||||||
|
([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
|
||||||
|
for serialization.
|
||||||
|
*/
|
||||||
|
template<typename T = void, typename SFINAE = void>
|
||||||
|
struct adl_serializer;
|
||||||
|
|
||||||
|
/// a class to store JSON values
|
||||||
|
/// @sa https://json.nlohmann.me/api/basic_json/
|
||||||
|
template<template<typename U, typename V, typename... Args> class ObjectType =
|
||||||
|
std::map,
|
||||||
|
template<typename U, typename... Args> class ArrayType = std::vector,
|
||||||
|
class StringType = std::string, class BooleanType = bool,
|
||||||
|
class NumberIntegerType = std::int64_t,
|
||||||
|
class NumberUnsignedType = std::uint64_t,
|
||||||
|
class NumberFloatType = double,
|
||||||
|
template<typename U> class AllocatorType = std::allocator,
|
||||||
|
template<typename T, typename SFINAE = void> class JSONSerializer =
|
||||||
|
adl_serializer,
|
||||||
|
class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
|
||||||
|
class CustomBaseClass = void>
|
||||||
|
class basic_json;
|
||||||
|
|
||||||
|
/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
|
||||||
|
/// @sa https://json.nlohmann.me/api/json_pointer/
|
||||||
|
template<typename RefStringType>
|
||||||
|
class json_pointer;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@brief default specialization
|
||||||
|
@sa https://json.nlohmann.me/api/json/
|
||||||
|
*/
|
||||||
|
using json = basic_json<>;
|
||||||
|
|
||||||
|
/// @brief a minimal map-like container that preserves insertion order
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_map/
|
||||||
|
template<class Key, class T, class IgnoredLess, class Allocator>
|
||||||
|
struct ordered_map;
|
||||||
|
|
||||||
|
/// @brief specialization that maintains the insertion order of object keys
|
||||||
|
/// @sa https://json.nlohmann.me/api/ordered_json/
|
||||||
|
using ordered_json = basic_json<nlohmann::ordered_map>;
|
||||||
|
|
||||||
|
NLOHMANN_JSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_
|
@ -1,83 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Check if jq is installed
|
|
||||||
if ! command -v jq &> /dev/null; then
|
|
||||||
echo "Error: jq is required but not installed."
|
|
||||||
echo "Install it with: sudo apt-get install jq"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Function to print usage
|
|
||||||
usage() {
|
|
||||||
echo "Usage: $0 <json_file>"
|
|
||||||
echo " $0 'json_string'"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if argument is provided
|
|
||||||
if [ $# -ne 1 ]; then
|
|
||||||
usage
|
|
||||||
fi
|
|
||||||
|
|
||||||
JSON=""
|
|
||||||
|
|
||||||
# Process and clean up JSON input to handle any whitespace or formatting issues
|
|
||||||
process_json() {
|
|
||||||
local input="$1"
|
|
||||||
# Use 'tr' to remove any trailing whitespace and control characters
|
|
||||||
# Then use jq to validate and normalize the JSON structure
|
|
||||||
echo "$input" | tr -d '\r' | sed 's/[[:space:]]*$//' | jq -c '.'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if the argument is a file or a JSON string
|
|
||||||
if [ -f "$1" ]; then
|
|
||||||
# Validate JSON file
|
|
||||||
if ! jq . "$1" > /dev/null 2>&1; then
|
|
||||||
echo "Error: Invalid JSON in file $1"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Read file content and process it
|
|
||||||
FILE_CONTENT=$(cat "$1")
|
|
||||||
JSON=$(process_json "$FILE_CONTENT")
|
|
||||||
else
|
|
||||||
# Check if it's a valid JSON string
|
|
||||||
if ! echo "$1" | jq . > /dev/null 2>&1; then
|
|
||||||
echo "Error: Invalid JSON string"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Process the JSON string
|
|
||||||
JSON=$(process_json "$1")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Double-check that we have valid JSON (defensive programming)
|
|
||||||
if ! echo "$JSON" | jq . > /dev/null 2>&1; then
|
|
||||||
echo "Error: Something went wrong processing the JSON"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Base64 encode the JSON, ensuring no trailing newlines
|
|
||||||
BASE64=$(echo -n "$JSON" | base64 | tr -d '\n')
|
|
||||||
|
|
||||||
# Ensure the binary exists
|
|
||||||
if [ ! -f "build/runner" ]; then
|
|
||||||
echo "Building the project first..."
|
|
||||||
./build.sh
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
echo "Running command with JSON:"
|
|
||||||
echo "$JSON" | jq .
|
|
||||||
echo
|
|
||||||
echo "Base64 encoded: $BASE64"
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Execute the runner with the processed base64 input
|
|
||||||
build/runner "$BASE64"
|
|
||||||
EXIT_CODE=$?
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "Command exited with code: $EXIT_CODE"
|
|
||||||
exit $EXIT_CODE
|
|
93
runner/runner.cpp
Normal file
93
runner/runner.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include "runner.h"
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
int execute_cmd(
|
||||||
|
const std::string& command,
|
||||||
|
const std::vector<std::string>& args,
|
||||||
|
const std::string& working_dir,
|
||||||
|
const std::map<std::string, std::string>& env,
|
||||||
|
bool silent,
|
||||||
|
bool interactive,
|
||||||
|
sSSHInfo* sshinfo,
|
||||||
|
std::string* output
|
||||||
|
) {
|
||||||
|
// SSH execution is not implemented in this demo
|
||||||
|
if (sshinfo) {
|
||||||
|
std::cerr << "Remote SSH execution is not implemented in this demo.\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pipefd[2];
|
||||||
|
if (output && pipe(pipefd) == -1) {
|
||||||
|
perror("pipe");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
perror("fork");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
// Child process
|
||||||
|
if (!working_dir.empty()) {
|
||||||
|
if (chdir(working_dir.c_str()) != 0) {
|
||||||
|
perror("chdir");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set environment variables
|
||||||
|
for (const auto& kv : env) {
|
||||||
|
setenv(kv.first.c_str(), kv.second.c_str(), 1);
|
||||||
|
}
|
||||||
|
if (output) {
|
||||||
|
close(pipefd[0]);
|
||||||
|
dup2(pipefd[1], STDOUT_FILENO);
|
||||||
|
dup2(pipefd[1], STDERR_FILENO);
|
||||||
|
close(pipefd[1]);
|
||||||
|
} else if (silent) {
|
||||||
|
int devnull = open("/dev/null", O_WRONLY);
|
||||||
|
dup2(devnull, STDOUT_FILENO);
|
||||||
|
dup2(devnull, STDERR_FILENO);
|
||||||
|
close(devnull);
|
||||||
|
}
|
||||||
|
if (!interactive) {
|
||||||
|
// Detach from terminal if not interactive
|
||||||
|
setsid();
|
||||||
|
}
|
||||||
|
std::vector<char*> argv;
|
||||||
|
argv.push_back(const_cast<char*>(command.c_str()));
|
||||||
|
for (const auto& arg : args) {
|
||||||
|
argv.push_back(const_cast<char*>(arg.c_str()));
|
||||||
|
}
|
||||||
|
argv.push_back(nullptr);
|
||||||
|
execvp(command.c_str(), argv.data());
|
||||||
|
perror("execvp");
|
||||||
|
exit(-1);
|
||||||
|
} else {
|
||||||
|
// Parent process
|
||||||
|
if (output) {
|
||||||
|
close(pipefd[1]);
|
||||||
|
std::ostringstream oss;
|
||||||
|
char buf[4096];
|
||||||
|
ssize_t n;
|
||||||
|
while ((n = read(pipefd[0], buf, sizeof(buf))) > 0) {
|
||||||
|
oss.write(buf, n);
|
||||||
|
}
|
||||||
|
close(pipefd[0]);
|
||||||
|
*output = oss.str();
|
||||||
|
}
|
||||||
|
int status = 0;
|
||||||
|
waitpid(pid, &status, 0);
|
||||||
|
if (WIFEXITED(status)) {
|
||||||
|
return WEXITSTATUS(status);
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
21
runner/runner.h
Normal file
21
runner/runner.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
struct sSSHInfo {
|
||||||
|
std::string host;
|
||||||
|
std::string user;
|
||||||
|
std::string port;
|
||||||
|
};
|
||||||
|
|
||||||
|
int execute_cmd(
|
||||||
|
const std::string& command,
|
||||||
|
const std::vector<std::string>& args,
|
||||||
|
const std::string& working_dir,
|
||||||
|
const std::map<std::string, std::string>& env,
|
||||||
|
bool silent,
|
||||||
|
bool interactive,
|
||||||
|
sSSHInfo* sshinfo = nullptr,
|
||||||
|
std::string* output = nullptr
|
||||||
|
);
|
63
runner/runner_demo.cpp
Normal file
63
runner/runner_demo.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include "runner.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
std::string base64_decode(const std::string& encoded) {
|
||||||
|
BIO* bio, *b64;
|
||||||
|
int decodeLen = (encoded.length() * 3) / 4;
|
||||||
|
std::string decoded(decodeLen, '\0');
|
||||||
|
bio = BIO_new_mem_buf(encoded.data(), encoded.length());
|
||||||
|
b64 = BIO_new(BIO_f_base64());
|
||||||
|
bio = BIO_push(b64, bio);
|
||||||
|
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
|
||||||
|
int len = BIO_read(bio, &decoded[0], encoded.length());
|
||||||
|
decoded.resize(len > 0 ? len : 0);
|
||||||
|
BIO_free_all(bio);
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cerr << "Usage: runner BASE64COMMAND\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::string decoded = base64_decode(argv[1]);
|
||||||
|
json j;
|
||||||
|
try {
|
||||||
|
j = json::parse(decoded);
|
||||||
|
} catch (...) {
|
||||||
|
std::cerr << "Invalid JSON in decoded command\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::string command = j.value("command", "");
|
||||||
|
std::vector<std::string> args = j.value("args", std::vector<std::string>{});
|
||||||
|
std::string working_dir = j.value("working_dir", "");
|
||||||
|
std::map<std::string, std::string> env = j.value("env", std::map<std::string, std::string>{});
|
||||||
|
bool silent = j.value("silent", false);
|
||||||
|
bool interactive = j.value("interactive", false);
|
||||||
|
sSSHInfo* sshinfo = nullptr;
|
||||||
|
sSSHInfo ssh;
|
||||||
|
if (j.contains("sshinfo")) {
|
||||||
|
ssh.host = j["sshinfo"].value("host", "");
|
||||||
|
ssh.user = j["sshinfo"].value("user", "");
|
||||||
|
ssh.port = j["sshinfo"].value("port", "");
|
||||||
|
sshinfo = &ssh;
|
||||||
|
}
|
||||||
|
std::string output;
|
||||||
|
int ret = execute_cmd(command, args, working_dir, env, silent, interactive, sshinfo, &output);
|
||||||
|
if (!silent && !output.empty()) {
|
||||||
|
std::cout << output;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
@ -1,62 +0,0 @@
|
|||||||
#include "base64.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
std::string base64_decode(const std::string& encoded) {
|
|
||||||
const std::string base64_chars =
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
"abcdefghijklmnopqrstuvwxyz"
|
|
||||||
"0123456789+/";
|
|
||||||
|
|
||||||
size_t in_len = encoded.size();
|
|
||||||
size_t i = 0;
|
|
||||||
size_t in_ = 0;
|
|
||||||
unsigned char char_array_4[4], char_array_3[3];
|
|
||||||
std::string decoded;
|
|
||||||
|
|
||||||
while (in_ < in_len && encoded[in_] != '=' &&
|
|
||||||
(std::isalnum(encoded[in_]) || encoded[in_] == '+' || encoded[in_] == '/')) {
|
|
||||||
char_array_4[i++] = encoded[in_++];
|
|
||||||
if (i == 4) {
|
|
||||||
// Translate values in char_array_4 from base64 alphabet to indices
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
char_array_4[i] = base64_chars.find(char_array_4[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode to original bytes
|
|
||||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
||||||
|
|
||||||
for (i = 0; i < 3; i++) {
|
|
||||||
decoded += char_array_3[i];
|
|
||||||
}
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle any remaining bytes
|
|
||||||
if (i) {
|
|
||||||
// Fill remaining positions with zeros
|
|
||||||
for (size_t j = i; j < 4; j++) {
|
|
||||||
char_array_4[j] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to indices
|
|
||||||
for (size_t j = 0; j < 4; j++) {
|
|
||||||
char_array_4[j] = base64_chars.find(char_array_4[j]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode remaining bytes
|
|
||||||
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
||||||
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
||||||
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
||||||
|
|
||||||
// Only add valid bytes based on how many characters we had
|
|
||||||
for (size_t j = 0; j < i - 1; j++) {
|
|
||||||
decoded += char_array_3[j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoded;
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
#include "runner.h"
|
|
||||||
#include "base64.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
void print_usage() {
|
|
||||||
std::cerr << "Usage: runner BASE64COMMAND" << std::endl;
|
|
||||||
std::cerr << " where BASE64COMMAND is a Base64 encoded JSON string" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
if (argc != 2) {
|
|
||||||
print_usage();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string base64_command = argv[1];
|
|
||||||
std::string json_string;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Decode Base64
|
|
||||||
json_string = base64_decode(base64_command);
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Error decoding Base64: " << e.what() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse JSON
|
|
||||||
nlohmann::json run_json;
|
|
||||||
try {
|
|
||||||
run_json = nlohmann::json::parse(json_string);
|
|
||||||
} catch (const nlohmann::json::parse_error& e) {
|
|
||||||
std::cerr << "Error parsing JSON: " << e.what() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute command
|
|
||||||
bool success = dropshell::Runner::run(run_json);
|
|
||||||
|
|
||||||
// Return the exit code
|
|
||||||
return success ? 0 : 1;
|
|
||||||
}
|
|
@ -1,513 +0,0 @@
|
|||||||
#include "runner.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <array>
|
|
||||||
#include <cstring>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <libssh/libssh.h>
|
|
||||||
#include <libssh/sftp.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
|
|
||||||
namespace dropshell {
|
|
||||||
|
|
||||||
bool Runner::run(const nlohmann::json& run_json) {
|
|
||||||
std::string command;
|
|
||||||
std::vector<std::string> args;
|
|
||||||
std::string working_dir;
|
|
||||||
std::map<std::string, std::string> env;
|
|
||||||
bool silent, interactive;
|
|
||||||
|
|
||||||
if (!parse_json(run_json, command, args, working_dir, env, silent, interactive)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int exit_code;
|
|
||||||
if (run_json.contains("ssh")) {
|
|
||||||
exit_code = execute_ssh(run_json["ssh"], command, args, working_dir, env, silent, interactive);
|
|
||||||
} else {
|
|
||||||
exit_code = execute_local(command, args, working_dir, env, silent, interactive);
|
|
||||||
}
|
|
||||||
|
|
||||||
return exit_code == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Runner::run(const nlohmann::json& run_json, std::string& output) {
|
|
||||||
std::string command;
|
|
||||||
std::vector<std::string> args;
|
|
||||||
std::string working_dir;
|
|
||||||
std::map<std::string, std::string> env;
|
|
||||||
bool silent, interactive;
|
|
||||||
|
|
||||||
if (!parse_json(run_json, command, args, working_dir, env, silent, interactive)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int exit_code;
|
|
||||||
if (run_json.contains("ssh")) {
|
|
||||||
exit_code = execute_ssh(run_json["ssh"], command, args, working_dir, env, silent, interactive, &output, true);
|
|
||||||
} else {
|
|
||||||
exit_code = execute_local(command, args, working_dir, env, silent, interactive, &output, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return exit_code == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Runner::parse_json(
|
|
||||||
const nlohmann::json& run_json,
|
|
||||||
std::string& command,
|
|
||||||
std::vector<std::string>& args,
|
|
||||||
std::string& working_dir,
|
|
||||||
std::map<std::string, std::string>& env,
|
|
||||||
bool& silent,
|
|
||||||
bool& interactive
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
// Command is required
|
|
||||||
if (!run_json.contains("command") || !run_json["command"].is_string()) {
|
|
||||||
std::cerr << "Error: 'command' field is required and must be a string" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
command = run_json["command"];
|
|
||||||
|
|
||||||
// Args are optional
|
|
||||||
args.clear();
|
|
||||||
if (run_json.contains("args")) {
|
|
||||||
if (!run_json["args"].is_array()) {
|
|
||||||
std::cerr << "Error: 'args' field must be an array" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (const auto& arg : run_json["args"]) {
|
|
||||||
if (!arg.is_string()) {
|
|
||||||
std::cerr << "Error: All arguments must be strings" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
args.push_back(arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Working directory is optional
|
|
||||||
working_dir = "";
|
|
||||||
if (run_json.contains("working_directory")) {
|
|
||||||
if (!run_json["working_directory"].is_string()) {
|
|
||||||
std::cerr << "Error: 'working_directory' field must be a string" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
working_dir = run_json["working_directory"];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Environment variables are optional
|
|
||||||
env.clear();
|
|
||||||
if (run_json.contains("env")) {
|
|
||||||
if (!run_json["env"].is_object()) {
|
|
||||||
std::cerr << "Error: 'env' field must be an object" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (auto it = run_json["env"].begin(); it != run_json["env"].end(); ++it) {
|
|
||||||
if (!it.value().is_string()) {
|
|
||||||
std::cerr << "Error: All environment variable values must be strings" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
env[it.key()] = it.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Options are optional
|
|
||||||
silent = false;
|
|
||||||
interactive = false;
|
|
||||||
if (run_json.contains("options")) {
|
|
||||||
if (!run_json["options"].is_object()) {
|
|
||||||
std::cerr << "Error: 'options' field must be an object" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (run_json["options"].contains("silent")) {
|
|
||||||
if (!run_json["options"]["silent"].is_boolean()) {
|
|
||||||
std::cerr << "Error: 'silent' option must be a boolean" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
silent = run_json["options"]["silent"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (run_json["options"].contains("interactive")) {
|
|
||||||
if (!run_json["options"]["interactive"].is_boolean()) {
|
|
||||||
std::cerr << "Error: 'interactive' option must be a boolean" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
interactive = run_json["options"]["interactive"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
std::cerr << "Error parsing JSON: " << e.what() << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Runner::execute_local(
|
|
||||||
const std::string& command,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::string& working_dir,
|
|
||||||
const std::map<std::string, std::string>& env,
|
|
||||||
bool silent,
|
|
||||||
bool interactive,
|
|
||||||
std::string* output,
|
|
||||||
bool capture_output
|
|
||||||
) {
|
|
||||||
int pipefd[2];
|
|
||||||
if (capture_output && pipe(pipefd) == -1) {
|
|
||||||
std::cerr << "Error creating pipe: " << strerror(errno) << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t pid = fork();
|
|
||||||
if (pid == -1) {
|
|
||||||
std::cerr << "Fork failed: " << strerror(errno) << std::endl;
|
|
||||||
if (capture_output) {
|
|
||||||
close(pipefd[0]);
|
|
||||||
close(pipefd[1]);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pid == 0) {
|
|
||||||
// Child process
|
|
||||||
|
|
||||||
// Set up output redirection if needed
|
|
||||||
if (capture_output) {
|
|
||||||
close(pipefd[0]); // Close read end
|
|
||||||
dup2(pipefd[1], STDOUT_FILENO);
|
|
||||||
dup2(pipefd[1], STDERR_FILENO);
|
|
||||||
close(pipefd[1]);
|
|
||||||
} else if (silent) {
|
|
||||||
int devnull = open("/dev/null", O_WRONLY);
|
|
||||||
if (devnull != -1) {
|
|
||||||
dup2(devnull, STDOUT_FILENO);
|
|
||||||
dup2(devnull, STDERR_FILENO);
|
|
||||||
close(devnull);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change to working directory if specified
|
|
||||||
if (!working_dir.empty()) {
|
|
||||||
if (chdir(working_dir.c_str()) != 0) {
|
|
||||||
std::cerr << "Error changing to directory " << working_dir << ": " << strerror(errno) << std::endl;
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set environment variables
|
|
||||||
for (const auto& [key, value] : env) {
|
|
||||||
setenv(key.c_str(), value.c_str(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare arguments
|
|
||||||
std::vector<char*> c_args;
|
|
||||||
c_args.push_back(const_cast<char*>(command.c_str()));
|
|
||||||
for (const auto& arg : args) {
|
|
||||||
c_args.push_back(const_cast<char*>(arg.c_str()));
|
|
||||||
}
|
|
||||||
c_args.push_back(nullptr);
|
|
||||||
|
|
||||||
// Execute command
|
|
||||||
execvp(command.c_str(), c_args.data());
|
|
||||||
|
|
||||||
// If exec fails
|
|
||||||
std::cerr << "exec failed: " << strerror(errno) << std::endl;
|
|
||||||
exit(1);
|
|
||||||
} else {
|
|
||||||
// Parent process
|
|
||||||
if (capture_output) {
|
|
||||||
close(pipefd[1]); // Close write end
|
|
||||||
|
|
||||||
std::array<char, 4096> buffer;
|
|
||||||
std::ostringstream oss;
|
|
||||||
|
|
||||||
ssize_t bytes_read;
|
|
||||||
while ((bytes_read = read(pipefd[0], buffer.data(), buffer.size() - 1)) > 0) {
|
|
||||||
buffer[bytes_read] = '\0';
|
|
||||||
oss << buffer.data();
|
|
||||||
|
|
||||||
if (!silent) {
|
|
||||||
std::cout << buffer.data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(pipefd[0]);
|
|
||||||
|
|
||||||
if (output) {
|
|
||||||
*output = oss.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int status;
|
|
||||||
waitpid(pid, &status, 0);
|
|
||||||
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
return WEXITSTATUS(status);
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string find_ssh_key_for_user() {
|
|
||||||
const char* home_dir = getenv("HOME");
|
|
||||||
if (!home_dir) {
|
|
||||||
struct passwd* pw = getpwuid(getuid());
|
|
||||||
if (pw) {
|
|
||||||
home_dir = pw->pw_dir;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!home_dir) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common SSH key locations
|
|
||||||
std::vector<std::string> key_paths = {
|
|
||||||
std::string(home_dir) + "/.ssh/id_rsa",
|
|
||||||
std::string(home_dir) + "/.ssh/id_ed25519",
|
|
||||||
std::string(home_dir) + "/.ssh/id_ecdsa",
|
|
||||||
std::string(home_dir) + "/.ssh/id_dsa"
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const auto& path : key_paths) {
|
|
||||||
if (access(path.c_str(), F_OK) == 0) {
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
int Runner::execute_ssh(
|
|
||||||
const nlohmann::json& ssh_config,
|
|
||||||
const std::string& command,
|
|
||||||
const std::vector<std::string>& args,
|
|
||||||
const std::string& working_dir,
|
|
||||||
const std::map<std::string, std::string>& env,
|
|
||||||
bool silent,
|
|
||||||
bool interactive,
|
|
||||||
std::string* output,
|
|
||||||
bool capture_output
|
|
||||||
) {
|
|
||||||
if (!ssh_config.contains("host") || !ssh_config["host"].is_string()) {
|
|
||||||
std::cerr << "Error: SSH configuration requires 'host' field" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
std::string host = ssh_config["host"];
|
|
||||||
|
|
||||||
int port = 22;
|
|
||||||
if (ssh_config.contains("port")) {
|
|
||||||
if (ssh_config["port"].is_number_integer()) {
|
|
||||||
port = ssh_config["port"];
|
|
||||||
} else {
|
|
||||||
std::cerr << "Error: SSH 'port' must be an integer" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string user = "";
|
|
||||||
if (ssh_config.contains("user") && ssh_config["user"].is_string()) {
|
|
||||||
user = ssh_config["user"];
|
|
||||||
} else {
|
|
||||||
// Get current username as default
|
|
||||||
char username[256];
|
|
||||||
if (getlogin_r(username, sizeof(username)) == 0) {
|
|
||||||
user = username;
|
|
||||||
} else {
|
|
||||||
struct passwd* pw = getpwuid(getuid());
|
|
||||||
if (pw) {
|
|
||||||
user = pw->pw_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string key_path = "";
|
|
||||||
if (ssh_config.contains("key") && ssh_config["key"].is_string()) {
|
|
||||||
std::string key = ssh_config["key"];
|
|
||||||
if (key == "auto") {
|
|
||||||
key_path = find_ssh_key_for_user();
|
|
||||||
if (key_path.empty()) {
|
|
||||||
std::cerr << "Error: Could not find SSH key automatically" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
key_path = key;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
key_path = find_ssh_key_for_user();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize SSH session
|
|
||||||
ssh_session session = ssh_new();
|
|
||||||
if (session == nullptr) {
|
|
||||||
std::cerr << "Error: Failed to create SSH session" << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set SSH options
|
|
||||||
ssh_options_set(session, SSH_OPTIONS_HOST, host.c_str());
|
|
||||||
ssh_options_set(session, SSH_OPTIONS_PORT, &port);
|
|
||||||
ssh_options_set(session, SSH_OPTIONS_USER, user.c_str());
|
|
||||||
|
|
||||||
// Set pseudo-terminal flag (equivalent to ssh -tt)
|
|
||||||
int flag = 1;
|
|
||||||
ssh_options_set(session, SSH_OPTIONS_STRICTHOSTKEYCHECK, &flag);
|
|
||||||
|
|
||||||
// Connect to server
|
|
||||||
int rc = ssh_connect(session);
|
|
||||||
if (rc != SSH_OK) {
|
|
||||||
std::cerr << "Error connecting to " << host << ": " << ssh_get_error(session) << std::endl;
|
|
||||||
ssh_free(session);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authenticate with key
|
|
||||||
if (!key_path.empty()) {
|
|
||||||
rc = ssh_userauth_publickey_auto(session, nullptr, key_path.empty() ? nullptr : key_path.c_str());
|
|
||||||
if (rc != SSH_AUTH_SUCCESS) {
|
|
||||||
std::cerr << "Error authenticating with key: " << ssh_get_error(session) << std::endl;
|
|
||||||
ssh_disconnect(session);
|
|
||||||
ssh_free(session);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Try default authentication methods
|
|
||||||
rc = ssh_userauth_publickey_auto(session, nullptr, nullptr);
|
|
||||||
if (rc != SSH_AUTH_SUCCESS) {
|
|
||||||
std::cerr << "Error authenticating: " << ssh_get_error(session) << std::endl;
|
|
||||||
ssh_disconnect(session);
|
|
||||||
ssh_free(session);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare command
|
|
||||||
std::ostringstream cmd_stream;
|
|
||||||
|
|
||||||
// Add environment variables
|
|
||||||
for (const auto& [key, value] : env) {
|
|
||||||
cmd_stream << "export " << key << "=\"" << value << "\"; ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add cd command if working_directory is specified
|
|
||||||
if (!working_dir.empty()) {
|
|
||||||
cmd_stream << "cd \"" << working_dir << "\" && ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add command and args
|
|
||||||
cmd_stream << command;
|
|
||||||
for (const auto& arg : args) {
|
|
||||||
cmd_stream << " " << arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string full_command = cmd_stream.str();
|
|
||||||
|
|
||||||
int exit_status = -1;
|
|
||||||
ssh_channel channel = ssh_channel_new(session);
|
|
||||||
if (channel == nullptr) {
|
|
||||||
std::cerr << "Error creating SSH channel: " << ssh_get_error(session) << std::endl;
|
|
||||||
ssh_disconnect(session);
|
|
||||||
ssh_free(session);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = ssh_channel_open_session(channel);
|
|
||||||
if (rc != SSH_OK) {
|
|
||||||
std::cerr << "Error opening SSH session: " << ssh_get_error(session) << std::endl;
|
|
||||||
ssh_channel_free(channel);
|
|
||||||
ssh_disconnect(session);
|
|
||||||
ssh_free(session);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interactive) {
|
|
||||||
// Request a pseudo-terminal for interactive commands with specific term type
|
|
||||||
const char* term_type = "xterm-256color";
|
|
||||||
|
|
||||||
// Force PTY allocation first with basic settings
|
|
||||||
rc = ssh_channel_request_pty(channel);
|
|
||||||
if (rc != SSH_OK) {
|
|
||||||
std::cerr << "Error requesting basic PTY: " << ssh_get_error(session) << std::endl;
|
|
||||||
ssh_channel_close(channel);
|
|
||||||
ssh_channel_free(channel);
|
|
||||||
ssh_disconnect(session);
|
|
||||||
ssh_free(session);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then set the size and term type
|
|
||||||
rc = ssh_channel_request_pty_size(channel, term_type, 80, 24);
|
|
||||||
if (rc != SSH_OK) {
|
|
||||||
std::cerr << "Warning: Could not set PTY size, but continuing anyway" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up terminal mode to be more raw (important for full-screen apps)
|
|
||||||
if (env.find("TERM") == env.end()) {
|
|
||||||
// Add TERM environment if not present
|
|
||||||
cmd_stream << "export TERM=\"" << term_type << "\"; ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the command
|
|
||||||
rc = ssh_channel_request_exec(channel, full_command.c_str());
|
|
||||||
if (rc != SSH_OK) {
|
|
||||||
std::cerr << "Error executing command: " << ssh_get_error(session) << std::endl;
|
|
||||||
ssh_channel_close(channel);
|
|
||||||
ssh_channel_free(channel);
|
|
||||||
ssh_disconnect(session);
|
|
||||||
ssh_free(session);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read command output
|
|
||||||
char buffer[4096];
|
|
||||||
int nbytes;
|
|
||||||
std::ostringstream oss;
|
|
||||||
|
|
||||||
// Read from stdout
|
|
||||||
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
|
|
||||||
if (capture_output) {
|
|
||||||
oss.write(buffer, nbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!silent) {
|
|
||||||
std::cout.write(buffer, nbytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read from stderr if needed
|
|
||||||
while ((nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 1)) > 0) {
|
|
||||||
if (capture_output) {
|
|
||||||
oss.write(buffer, nbytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!silent) {
|
|
||||||
std::cerr.write(buffer, nbytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (capture_output && output) {
|
|
||||||
*output = oss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get exit status
|
|
||||||
ssh_channel_send_eof(channel);
|
|
||||||
exit_status = ssh_channel_get_exit_status(channel);
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
ssh_channel_close(channel);
|
|
||||||
ssh_channel_free(channel);
|
|
||||||
ssh_disconnect(session);
|
|
||||||
ssh_free(session);
|
|
||||||
|
|
||||||
return exit_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dropshell
|
|
148
runner/test.sh
148
runner/test.sh
@ -1,148 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Simple test script to verify that the runner works properly
|
|
||||||
|
|
||||||
echo "Testing Runner - Dropshell Command Execution Library"
|
|
||||||
echo "=================================================="
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Ensure the binary is built
|
|
||||||
if [ ! -f "build/runner" ]; then
|
|
||||||
echo "Building the project first..."
|
|
||||||
./build.sh
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run a simple echo command
|
|
||||||
echo "Test 1: Basic command execution"
|
|
||||||
JSON=$(cat <<'EOF'
|
|
||||||
{"command":"echo","args":["Hello from runner test!"]}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
BASE64=$(echo -n "$JSON" | base64 -w0)
|
|
||||||
echo "JSON: $JSON"
|
|
||||||
echo "Running command..."
|
|
||||||
build/runner "$BASE64"
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "✅ Test 1 passed"
|
|
||||||
else
|
|
||||||
echo "❌ Test 1 failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test with environment variables
|
|
||||||
echo "Test 2: Environment variables"
|
|
||||||
JSON=$(cat <<'EOF'
|
|
||||||
{"command":"bash","args":["-c","echo Value of TEST_VAR: $TEST_VAR"],"env":{"TEST_VAR":"This is a test environment variable"}}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
BASE64=$(echo -n "$JSON" | base64 -w0)
|
|
||||||
echo "JSON: $JSON"
|
|
||||||
echo "Running command..."
|
|
||||||
build/runner "$BASE64"
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
echo "✅ Test 2 passed"
|
|
||||||
else
|
|
||||||
echo "❌ Test 2 failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test silent mode
|
|
||||||
echo "Test 3: Silent mode"
|
|
||||||
JSON=$(cat <<'EOF'
|
|
||||||
{"command":"echo","args":["This should not be displayed"],"options":{"silent":true}}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
BASE64=$(echo -n "$JSON" | base64 -w0)
|
|
||||||
echo "JSON: $JSON"
|
|
||||||
echo "Running command (no output expected)..."
|
|
||||||
OUTPUT=$(build/runner "$BASE64")
|
|
||||||
if [ $? -eq 0 ] && [ -z "$OUTPUT" ]; then
|
|
||||||
echo "✅ Test 3 passed"
|
|
||||||
else
|
|
||||||
echo "❌ Test 3 failed, unexpected output: '$OUTPUT'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test return code handling
|
|
||||||
echo "Test 4: Error return code"
|
|
||||||
JSON=$(cat <<'EOF'
|
|
||||||
{"command":"false"}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
BASE64=$(echo -n "$JSON" | base64 -w0)
|
|
||||||
echo "JSON: $JSON"
|
|
||||||
echo "Running command (should fail)..."
|
|
||||||
set +e # Temporarily disable exiting on error
|
|
||||||
build/runner "$BASE64"
|
|
||||||
RC=$?
|
|
||||||
set -e # Re-enable exiting on error
|
|
||||||
if [ $RC -ne 0 ]; then
|
|
||||||
echo "✅ Test 4 passed (command correctly reported failure, exit code: $RC)"
|
|
||||||
else
|
|
||||||
echo "❌ Test 4 failed (command should have returned non-zero)"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Test working directory feature
|
|
||||||
echo "Test 5: Working directory"
|
|
||||||
JSON=$(cat <<'EOF'
|
|
||||||
{"command":"pwd","working_directory":"/tmp"}
|
|
||||||
EOF
|
|
||||||
)
|
|
||||||
BASE64=$(echo -n "$JSON" | base64 -w0)
|
|
||||||
echo "JSON: $JSON"
|
|
||||||
echo "Running command (should show /tmp)..."
|
|
||||||
OUTPUT=$(build/runner "$BASE64")
|
|
||||||
if [ $? -eq 0 ] && [[ "$OUTPUT" == "/tmp"* ]]; then
|
|
||||||
echo "✅ Test 5 passed (command ran in the correct directory)"
|
|
||||||
else
|
|
||||||
echo "❌ Test 5 failed, unexpected output: '$OUTPUT'"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
|
|
||||||
# Optional SSH test (disabled by default)
|
|
||||||
echo "Test 6: SSH to localhost (make sure SSH server is running and you can connect to localhost)"
|
|
||||||
if [ -f "examples/local_ssh.json" ]; then
|
|
||||||
echo "Using examples/local_ssh.json for the test..."
|
|
||||||
./run.sh examples/local_ssh.json || {
|
|
||||||
echo "❌ Test 6 failed"
|
|
||||||
echo "Note: This test requires SSH server running on localhost and proper key-based authentication."
|
|
||||||
echo "Check your SSH configuration or disable this test."
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
echo "✅ Test 6 passed"
|
|
||||||
else
|
|
||||||
echo "❌ Test 6 failed: examples/local_ssh.json not found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "Test 7: SSH with working directory (make sure SSH server is running and you can connect to localhost)"
|
|
||||||
if [ -f "examples/ssh_working_dir.json" ]; then
|
|
||||||
echo "Using examples/ssh_working_dir.json for the test..."
|
|
||||||
OUTPUT=$(./run.sh examples/ssh_working_dir.json | grep "/tmp")
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "❌ Test 7 failed, command did not run"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$OUTPUT" == *"/tmp"* ]]; then
|
|
||||||
echo "✅ Test 7 passed (SSH command ran in the correct directory)"
|
|
||||||
else
|
|
||||||
echo "❌ Test 7 failed, directory was not changed correctly"
|
|
||||||
echo "Note: This test requires SSH server running on localhost and proper key-based authentication."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "❌ Test 7 failed: examples/ssh_working_dir.json not found"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo "All tests passed! 🎉"
|
|
Loading…
x
Reference in New Issue
Block a user