137 lines
5.1 KiB
C++
137 lines
5.1 KiB
C++
#include "command_registry.hpp"
|
|
#include "config.hpp"
|
|
#include "utils/utils.hpp"
|
|
#include "utils/directories.hpp"
|
|
#include "utils/execute.hpp"
|
|
#include "shared_commands.hpp"
|
|
#include "servers.hpp"
|
|
#include "services.hpp"
|
|
#include "templates.hpp"
|
|
#include "utils/output.hpp"
|
|
#include <libassert/assert.hpp>
|
|
|
|
namespace dropshell
|
|
{
|
|
|
|
int exec_handler(const CommandContext &ctx);
|
|
|
|
static std::vector<std::string> exec_name_list = {"exec"};
|
|
|
|
// Static registration
|
|
struct ExecCommandRegister
|
|
{
|
|
ExecCommandRegister()
|
|
{
|
|
CommandRegistry::instance().register_command({exec_name_list,
|
|
exec_handler,
|
|
shared_commands::std_autocomplete,
|
|
false, // hidden
|
|
true, // requires_config
|
|
true, // requires_install
|
|
3, // min_args (server, service, command)
|
|
-1, // max_args (unlimited for command with args)
|
|
"exec SERVER SERVICE COMMAND [ARGS...]",
|
|
"Execute a command on a server within a service environment.",
|
|
R"(
|
|
|
|
exec SERVER SERVICE COMMAND [ARGS...] Execute a command in the service's environment.
|
|
|
|
This command runs a command on the remote server with the service's environment
|
|
variables loaded (including those from service.env and the usual variables).
|
|
The command is executed in the service's template directory context.
|
|
)"});
|
|
}
|
|
} exec_command_register;
|
|
|
|
int exec_handler(const CommandContext &ctx)
|
|
{
|
|
if (ctx.args.size() < 3)
|
|
{
|
|
error << "Server name, service name, and command are required" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
std::string server = safearg(ctx.args, 0);
|
|
std::string service = safearg(ctx.args, 1);
|
|
std::string command = safearg(ctx.args, 2);
|
|
|
|
// Collect any additional arguments for the command
|
|
std::vector<std::string> command_args;
|
|
for (size_t i = 3; i < ctx.args.size(); ++i)
|
|
{
|
|
command_args.push_back(ctx.args[i]);
|
|
}
|
|
|
|
// Validate server
|
|
ServerConfig server_env(server);
|
|
if (!server_env.is_valid())
|
|
{
|
|
error << "Server " << server << " is not valid" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Validate service name
|
|
if (!legal_service_name(service))
|
|
{
|
|
error << "Service name contains illegal characters: " << service << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Get service info to validate it exists
|
|
LocalServiceInfo sinfo = get_service_info(server, service);
|
|
if (!SIvalid(sinfo))
|
|
{
|
|
error << "Service " << service << " is not valid on server " << server << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Get the user for this service
|
|
std::string user = server_env.get_user_for_service(service);
|
|
|
|
// Get all service environment variables
|
|
std::map<std::string, std::string> env_vars;
|
|
if (!get_all_service_env_vars(server, service, env_vars))
|
|
{
|
|
error << "Failed to get environment variables for service " << service << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Add HOST_NAME like other commands do
|
|
env_vars["HOST_NAME"] = server_env.get_SSH_HOST();
|
|
|
|
// Get the remote service template path for working directory
|
|
std::string remote_service_template_path = remotepath(server, user).service_template(service);
|
|
|
|
// Build the command string with arguments
|
|
std::string full_command = command;
|
|
for (const auto &arg : command_args)
|
|
{
|
|
full_command += " " + quote(dequote(trim(arg)));
|
|
}
|
|
|
|
// Create the command structure with environment variables
|
|
// Note: execute_ssh_command will automatically use bb64 to encode and execute this safely
|
|
sCommand scommand(remote_service_template_path, full_command, env_vars);
|
|
|
|
// Execute the command on the remote server
|
|
info << "Executing command on " << server << "/" << service << ": " << command;
|
|
if (!command_args.empty())
|
|
{
|
|
rawout << " with args:";
|
|
for (const auto &arg : command_args)
|
|
rawout << " " << arg;
|
|
}
|
|
rawout << std::endl;
|
|
|
|
bool success = execute_ssh_command(server_env.get_SSH_INFO(user), scommand, cMode::Interactive);
|
|
|
|
if (!success)
|
|
{
|
|
error << "Command execution failed" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
} // namespace dropshell
|