espilon-source/espilon_bot/components/core/command.c
Eun0us 6d45770d98 epsilon: merge command system into core + add 5 new modules
Move command registry from components/command/ into components/core/.
New modules: mod_canbus, mod_honeypot, mod_fallback, mod_redteam, mod_ota.
Replace mod_proxy with tun_core (multiplexed SOCKS5 tunnel).
Kconfig extended with per-module settings and async worker config.
2026-02-28 20:07:59 +01:00

228 lines
6.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "utils.h"
#include "esp_log.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
static const char *TAG = "COMMAND";
static const command_t *registry[MAX_COMMANDS];
static size_t registry_count = 0;
/* Max longueur lue/copied par arg (sécurité si non \0) */
#ifndef COMMAND_MAX_ARG_LEN
#define COMMAND_MAX_ARG_LEN 128
#endif
/* Max args temporaires quon accepte ici (doit couvrir tes commandes) */
#ifndef COMMAND_MAX_ARGS
#define COMMAND_MAX_ARGS 16
#endif
/* =========================================================
* Register command
* ========================================================= */
void command_register(const command_t *cmd)
{
if (!cmd || !cmd->name || !cmd->handler) {
ESP_LOGE(TAG, "Invalid command registration");
return;
}
if (registry_count >= MAX_COMMANDS) {
ESP_LOGE(TAG, "Command registry full");
return;
}
registry[registry_count++] = cmd;
#ifdef CONFIG_ESPILON_LOG_CMD_REG_VERBOSE
ESPILON_LOGI_PURPLE(TAG, "Registered command: %s", cmd->name);
#endif
}
/* =========================================================
* Summary
* ========================================================= */
void command_log_registry_summary(void)
{
if (registry_count == 0) {
ESPILON_LOGI_PURPLE(TAG, "Registered commands: none");
return;
}
char buf[512];
int off = snprintf(
buf,
sizeof(buf),
"Registered commands (%d): ",
(int)registry_count
);
for (size_t i = 0; i < registry_count; i++) {
const char *name = registry[i] && registry[i]->name
? registry[i]->name : "?";
const char *sub = (registry[i] && registry[i]->sub && registry[i]->sub[0])
? registry[i]->sub : NULL;
const char *sep = (i == 0) ? "" : ", ";
int n;
if (sub) {
n = snprintf(buf + off, sizeof(buf) - (size_t)off,
"%s%s %s", sep, name, sub);
} else {
n = snprintf(buf + off, sizeof(buf) - (size_t)off,
"%s%s", sep, name);
}
if (n < 0 || n >= (int)(sizeof(buf) - (size_t)off)) {
if (off < (int)sizeof(buf) - 4) {
memcpy(buf + (sizeof(buf) - 4), "...", 4);
}
break;
}
off += n;
}
ESPILON_LOGI_PURPLE(TAG, "%s", buf);
}
/* =========================================================
* Helpers: deep-copy argv into one arena + argv[] pointers
* ========================================================= */
static bool deepcopy_argv(char *const *argv_in,
int argc,
char ***argv_out,
char **arena_out,
const char *req_id)
{
*argv_out = NULL;
*arena_out = NULL;
if (argc < 0) {
msg_error("cmd", "Invalid argc", req_id);
return false;
}
if (argc == 0) {
char **argv0 = (char **)calloc(1, sizeof(char *));
if (!argv0) {
msg_error("cmd", "OOM copying argv", req_id);
return false;
}
*argv_out = argv0;
*arena_out = NULL;
return true;
}
size_t total = 0;
for (int i = 0; i < argc; i++) {
const char *s = (argv_in && argv_in[i]) ? argv_in[i] : "";
size_t n = strnlen(s, COMMAND_MAX_ARG_LEN);
total += (n + 1);
}
char *arena = (char *)malloc(total ? total : 1);
char **argv_copy = (char **)malloc((size_t)argc * sizeof(char *));
if (!arena || !argv_copy) {
free(arena);
free(argv_copy);
msg_error("cmd", "OOM copying argv", req_id);
return false;
}
size_t off = 0;
for (int i = 0; i < argc; i++) {
const char *s = (argv_in && argv_in[i]) ? argv_in[i] : "";
size_t n = strnlen(s, COMMAND_MAX_ARG_LEN);
argv_copy[i] = &arena[off];
memcpy(&arena[off], s, n);
arena[off + n] = '\0';
off += (n + 1);
}
*argv_out = argv_copy;
*arena_out = arena;
return true;
}
/* =========================================================
* Dispatch nanopb command
* ========================================================= */
void command_process_pb(const c2_Command *cmd)
{
if (!cmd) return;
/* nanopb: tableaux fixes => jamais NULL */
const char *name = cmd->command_name;
const char *reqid = cmd->request_id;
const char *reqid_or_null = (reqid[0] ? reqid : NULL);
int argc = cmd->argv_count;
for (size_t i = 0; i < registry_count; i++) {
const command_t *c = registry[i];
if (strcmp(c->name, name) != 0)
continue;
/*
* Sub-command matching: if the command has a .sub field,
* argv[0] must match it. The sub is consumed (argv shifted
* by 1) before passing to the handler.
*/
int sub_offset = 0;
if (c->sub && c->sub[0]) {
if (argc < 1 || strcmp(cmd->argv[0], c->sub) != 0)
continue; /* not this sub-command, try next */
sub_offset = 1;
}
int effective_argc = argc - sub_offset;
if (effective_argc < c->min_args || effective_argc > c->max_args) {
msg_error("cmd", "Invalid argument count", reqid_or_null);
return;
}
if (c->sub && c->sub[0]) {
ESP_LOGI(TAG, "Execute: %s %s (argc=%d)", name, c->sub, effective_argc);
} else {
ESP_LOGI(TAG, "Execute: %s (argc=%d)", name, effective_argc);
}
if (c->async) {
command_async_enqueue(c, cmd, sub_offset);
return;
}
/* ================================
* SYNC PATH:
* Build argv_ptrs[] from cmd->argv, skipping sub_offset
* ================================ */
if (effective_argc > COMMAND_MAX_ARGS) {
msg_error("cmd", "Too many args", reqid_or_null);
return;
}
char *argv_ptrs[COMMAND_MAX_ARGS] = {0};
for (int a = 0; a < effective_argc; a++) {
argv_ptrs[a] = (char *)cmd->argv[a + sub_offset];
}
/* Deep-copy pour rendre sync aussi safe que async */
char **argv_copy = NULL;
char *arena = NULL;
if (!deepcopy_argv(argv_ptrs, effective_argc, &argv_copy, &arena, reqid_or_null))
return;
c->handler(effective_argc, argv_copy, reqid_or_null, c->ctx);
free(argv_copy);
free(arena);
return;
}
msg_error("cmd", "Unknown command", reqid_or_null);
}