Integrate LuaJIT as an optional runtime for better performance, with a fallback to standard Lua 5.4. Update Dockerfile to install LuaJIT and create a wrapper script for execution. Enhance network module with socket fallback support and update README to reflect these changes and configuration options.
147 lines
4.2 KiB
Lua
147 lines
4.2 KiB
Lua
-- SSH utilities module for auto-boot-ollama-host
|
|
-- Provides SSH command execution functionality
|
|
|
|
local utils = require("utils")
|
|
local config = require("config")
|
|
|
|
local ssh_module = {}
|
|
|
|
-- Quote a string for safe single-quoted POSIX shell context
|
|
local function sq(s)
|
|
-- Replace ' with: '\'' (close, escape quote, reopen)
|
|
return "'" .. tostring(s):gsub("'", "'\\''") .. "'"
|
|
end
|
|
|
|
-- Helper function to log SSH commands with proper formatting
|
|
local function log_ssh_command(prefix, command, full_command)
|
|
if config.is_debug() then
|
|
utils.log(prefix .. full_command)
|
|
else
|
|
utils.log(prefix .. sq(command))
|
|
end
|
|
end
|
|
|
|
-- Execute a remote command over SSH
|
|
-- Signature: ssh(command, user, host, port, identity_file)
|
|
function ssh_module.execute(command, user, host, port, identity_file)
|
|
-- Basic validation and defaults
|
|
user = tostring(user or "")
|
|
host = tostring(host or "")
|
|
port = tonumber(port or 22) or 22
|
|
identity_file = tostring(identity_file or "")
|
|
|
|
|
|
-- Build base ssh command (run locally)
|
|
-- -oBatchMode to avoid interactive prompts
|
|
-- -oConnectTimeout for faster failure
|
|
-- -oStrictHostKeyChecking uses known_hosts; adjust if needed
|
|
local dest = (user ~= "" and (user .. "@" .. host) or host)
|
|
local pieces = {
|
|
"ssh",
|
|
"-p", tostring(port),
|
|
"-o", "BatchMode=yes",
|
|
"-o", "ConnectTimeout=30",
|
|
"-o", "ServerAliveInterval=5",
|
|
"-o", "ServerAliveCountMax=1",
|
|
"-o", "UserKnownHostsFile=/root/.ssh/known_hosts",
|
|
"-o", "StrictHostKeyChecking=yes",
|
|
}
|
|
|
|
if identity_file ~= "" then
|
|
table.insert(pieces, "-i")
|
|
table.insert(pieces, identity_file)
|
|
end
|
|
table.insert(pieces, dest)
|
|
|
|
-- Pass remote command as provided; caller is responsible for proper quoting
|
|
table.insert(pieces, "--")
|
|
-- Quote the remote command to prevent shell interpretation of && and ||
|
|
table.insert(pieces, sq(command))
|
|
|
|
-- Join with spaces for os.execute
|
|
local function join(args)
|
|
-- We only quote the remote command explicitly. Other args are simple tokens.
|
|
return table.concat(args, " ")
|
|
end
|
|
|
|
local full = join(pieces)
|
|
|
|
-- Log SSH command
|
|
log_ssh_command("SSH exec: ", command, full)
|
|
|
|
local ok, reason, code = os.execute(full)
|
|
if ok == true or ok == 0 then
|
|
utils.log("SSH command completed successfully")
|
|
return true
|
|
else
|
|
local msg = string.format("SSH failed: reason=%s code=%s", tostring(reason), tostring(code))
|
|
utils.log(msg)
|
|
return false, msg
|
|
end
|
|
end
|
|
|
|
-- Execute a remote command over SSH and return the output
|
|
-- Signature: ssh.execute_with_output(command, user, host, port, identity_file)
|
|
-- Returns: success, output, error_message
|
|
function ssh_module.execute_with_output(command, user, host, port, identity_file)
|
|
-- Basic validation and defaults
|
|
user = tostring(user or "")
|
|
host = tostring(host or "")
|
|
port = tonumber(port or 22) or 22
|
|
identity_file = tostring(identity_file or "")
|
|
|
|
-- Build base ssh command (run locally)
|
|
local dest = (user ~= "" and (user .. "@" .. host) or host)
|
|
local pieces = {
|
|
"ssh",
|
|
"-p", tostring(port),
|
|
"-o", "BatchMode=yes",
|
|
"-o", "ConnectTimeout=30",
|
|
"-o", "ServerAliveInterval=5",
|
|
"-o", "ServerAliveCountMax=1",
|
|
"-o", "UserKnownHostsFile=/root/.ssh/known_hosts",
|
|
"-o", "StrictHostKeyChecking=yes",
|
|
}
|
|
|
|
if identity_file ~= "" then
|
|
table.insert(pieces, "-i")
|
|
table.insert(pieces, identity_file)
|
|
end
|
|
table.insert(pieces, dest)
|
|
|
|
-- Pass remote command as provided
|
|
table.insert(pieces, "--")
|
|
-- Quote the remote command to prevent shell interpretation of && and ||
|
|
table.insert(pieces, sq(command))
|
|
|
|
-- Join with spaces for io.popen
|
|
local function join(args)
|
|
return table.concat(args, " ")
|
|
end
|
|
|
|
local full = join(pieces)
|
|
|
|
-- Log SSH command
|
|
log_ssh_command("SSH exec (with output): ", command, full)
|
|
|
|
-- Use io.popen to capture output
|
|
local fh = io.popen(full, "r")
|
|
if not fh then
|
|
return false, "", "Failed to open SSH command"
|
|
end
|
|
|
|
local output = fh:read("*a")
|
|
local success, reason, code = fh:close()
|
|
|
|
if success then
|
|
utils.log("SSH command completed successfully with output")
|
|
return true, output, nil
|
|
else
|
|
local msg = string.format("SSH failed: reason=%s code=%s", tostring(reason), tostring(code))
|
|
utils.log(msg)
|
|
return false, output, msg
|
|
end
|
|
end
|
|
|
|
return ssh_module
|