|
8 | 8 | #include <string.h> |
9 | 9 | #include <stdlib.h> |
10 | 10 | #include <sys/param.h> |
| 11 | +#include <unistd.h> |
11 | 12 | #include "esp_heap_caps.h" |
12 | 13 | #include "esp_log.h" |
13 | 14 | #include "esp_console.h" |
14 | 15 | #include "esp_system.h" |
| 16 | +#include "freertos/idf_additions.h" |
15 | 17 | #include "linenoise/linenoise.h" |
16 | 18 | #include "argtable3/argtable3.h" |
17 | 19 | #include "sys/queue.h" |
@@ -41,6 +43,8 @@ static const cmd_item_t *find_command_by_name(const char *name); |
41 | 43 |
|
42 | 44 | static esp_console_help_verbose_level_e s_verbose_level = ESP_CONSOLE_HELP_VERBOSE_LEVEL_1; |
43 | 45 |
|
| 46 | + |
| 47 | + |
44 | 48 | const esp_console_cmd_t *esp_console_get_by_name(const char *name) |
45 | 49 | { |
46 | 50 | const cmd_item_t *cmd = find_command_by_name(name); |
@@ -262,6 +266,178 @@ esp_err_t esp_console_run(const char *cmdline, int *cmd_ret) |
262 | 266 | return ESP_OK; |
263 | 267 | } |
264 | 268 |
|
| 269 | + |
| 270 | +#ifdef CONFIG_CONSOLE_COMMAND_ON_TASK |
| 271 | + |
| 272 | +typedef struct esp_console_task_handle { |
| 273 | + const cmd_item_t *cmd; //!< Pointer to the command definition |
| 274 | + TaskHandle_t task_handle; //!< Handle of the created task (protected by lock) |
| 275 | + FILE *_stdin; //!< Pipe for command input |
| 276 | + FILE *_stdout; //!< Pipe for command output |
| 277 | + FILE *_stderr; //!< Pipe for command error output |
| 278 | + int exit_code; //!< Exit code of the command (protected by lock) |
| 279 | + _lock_t lock; //!< Lock to protect task_handle and exit_code |
| 280 | + size_t argc; //!< Number of command line arguments |
| 281 | + char *argv[0]; //!< Command line arguments (flexible array member) |
| 282 | +} esp_console_task_handle_t; |
| 283 | + |
| 284 | + |
| 285 | +static void task_cmd(void *arg) { |
| 286 | + esp_console_task_handle_t *task = (esp_console_task_handle_t *)arg; |
| 287 | + |
| 288 | + |
| 289 | + FILE *old_stdin = __getreent()->_stdin; |
| 290 | + FILE *old_stdout = __getreent()->_stdout; |
| 291 | + FILE *old_stderr = __getreent()->_stderr; |
| 292 | + |
| 293 | + if (task->_stdin) |
| 294 | + __getreent()->_stdin = task->_stdin; |
| 295 | + if (task->_stdout) |
| 296 | + __getreent()->_stdout = task->_stdout; |
| 297 | + if (task->_stderr) |
| 298 | + __getreent()->_stderr = task->_stderr; |
| 299 | + |
| 300 | + int exit_code = -1; |
| 301 | + if (task->cmd->def.func) { |
| 302 | + exit_code = task->cmd->def.func(task->argc, task->argv); |
| 303 | + } |
| 304 | + if (task->cmd->def.func_w_context) { |
| 305 | + exit_code = (*task->cmd->def.func_w_context)(task->cmd->def.context, task->argc, task->argv); |
| 306 | + } |
| 307 | + |
| 308 | + // Close sockets only if not pointing to standard fds |
| 309 | + if (task->_stdin) { |
| 310 | + fclose(task->_stdin); |
| 311 | + __getreent()->_stdin = old_stdin; |
| 312 | + } |
| 313 | + if (task->_stdout) { |
| 314 | + fclose(task->_stdout); |
| 315 | + __getreent()->_stdout = old_stdout; |
| 316 | + } |
| 317 | + if (task->_stderr) { |
| 318 | + fclose(task->_stderr); |
| 319 | + __getreent()->_stderr = old_stderr; |
| 320 | + } |
| 321 | + |
| 322 | + __lock_acquire(task->lock); |
| 323 | + task->exit_code = exit_code; |
| 324 | + task->task_handle = NULL; |
| 325 | + __lock_release(task->lock); |
| 326 | + |
| 327 | + vTaskDelete(NULL); |
| 328 | +} |
| 329 | + |
| 330 | +esp_err_t esp_console_run_on_task(const char *cmdline, FILE *_stdin, FILE *_stdout, FILE *_stderr, esp_console_task_handle_t **out_task) |
| 331 | +{ |
| 332 | + const size_t cmd_len = strlen(cmdline); |
| 333 | + if (!cmd_len || cmd_len >= s_config.max_cmdline_length) { |
| 334 | + return ESP_ERR_INVALID_ARG; |
| 335 | + } |
| 336 | + |
| 337 | + // Try to do all memory allocations in one go |
| 338 | + // Calculate the size of each component for clarity and maintainability |
| 339 | + const size_t task_struct_size = sizeof(esp_console_task_handle_t); // Size of the task struct |
| 340 | + const size_t argv_array_size = sizeof(char *) * s_config.max_cmdline_args; // Size of argv array |
| 341 | + const size_t cmdline_buf_size = cmd_len + 1; // Size of command line buffer (including null terminator) |
| 342 | + const size_t total_size = task_struct_size + argv_array_size + cmdline_buf_size; |
| 343 | + |
| 344 | + esp_console_task_handle_t *task = (esp_console_task_handle_t *) heap_caps_calloc(1, total_size, s_config.heap_alloc_caps); |
| 345 | + if (task == NULL) { |
| 346 | + return ESP_ERR_NO_MEM; |
| 347 | + } |
| 348 | + |
| 349 | + // The line buffer is placed after the struct and argv array |
| 350 | + char *line_buf = (char *) (((char *)task) + task_struct_size + argv_array_size); |
| 351 | + strlcpy(line_buf, cmdline, cmd_len+1); |
| 352 | + |
| 353 | + task->argc = esp_console_split_argv(line_buf, task->argv, |
| 354 | + s_config.max_cmdline_args); |
| 355 | + if (task->argc == 0) { |
| 356 | + free(task); |
| 357 | + return ESP_ERR_INVALID_ARG; |
| 358 | + } |
| 359 | + |
| 360 | + task->exit_code = -1; |
| 361 | + task->cmd = find_command_by_name(task->argv[0]); |
| 362 | + if (task->cmd == NULL) { |
| 363 | + free(task); |
| 364 | + return ESP_ERR_NOT_FOUND; |
| 365 | + } |
| 366 | + |
| 367 | + task->_stdin = _stdin; |
| 368 | + task->_stdout = _stdout; |
| 369 | + task->_stderr = _stderr; |
| 370 | + __lock_init(task->lock); |
| 371 | + |
| 372 | + uint32_t stack_size = task->cmd->def.stack_size ? task->cmd->def.stack_size : (CONFIG_CONSOLE_COMMAND_DEFAULT_TASK_STACK_SIZE); |
| 373 | + UBaseType_t priority = task->cmd->def.priority ? task->cmd->def.priority : (CONFIG_CONSOLE_COMMAND_DEFAULT_TASK_PRIORITY); |
| 374 | + BaseType_t handle = xTaskCreate(&task_cmd, task->argv[0], stack_size, task, priority, &task->task_handle); |
| 375 | + |
| 376 | + if (handle != pdPASS) { |
| 377 | + __lock_close(task->lock); |
| 378 | + free(task); |
| 379 | + return ESP_ERR_NO_MEM; |
| 380 | + } |
| 381 | + *out_task = task; |
| 382 | + |
| 383 | + return ESP_OK; |
| 384 | +} |
| 385 | + |
| 386 | +void esp_console_task_free(esp_console_task_handle_t *task) |
| 387 | +{ |
| 388 | + __lock_acquire(task->lock); |
| 389 | + TaskHandle_t handle = task->task_handle; |
| 390 | + __lock_release(task->lock); |
| 391 | + |
| 392 | + if (handle) { |
| 393 | + // Wait for the task to finish before deleting |
| 394 | + esp_console_wait_task(task, NULL); |
| 395 | + vTaskDelete(handle); |
| 396 | + |
| 397 | + __lock_acquire(task->lock); |
| 398 | + task->task_handle = NULL; |
| 399 | + __lock_release(task->lock); |
| 400 | + } |
| 401 | + __lock_close(task->lock); |
| 402 | + free(task); |
| 403 | +} |
| 404 | + |
| 405 | +bool esp_console_task_is_running(esp_console_task_handle_t *task) |
| 406 | +{ |
| 407 | + __lock_acquire(task->lock); |
| 408 | + TaskHandle_t handle = task->task_handle; |
| 409 | + __lock_release(task->lock); |
| 410 | + |
| 411 | + if (handle) { |
| 412 | + return eTaskGetState(handle) != eDeleted; |
| 413 | + } |
| 414 | + return false; |
| 415 | +} |
| 416 | + |
| 417 | +void esp_console_wait_task(esp_console_task_handle_t *task, int *cmd_ret) |
| 418 | +{ |
| 419 | + TaskHandle_t handle; |
| 420 | + |
| 421 | + __lock_acquire(task->lock); |
| 422 | + handle = task->task_handle; |
| 423 | + __lock_release(task->lock); |
| 424 | + |
| 425 | + if (handle) { |
| 426 | + // Wait until task is done |
| 427 | + while (eTaskGetState(handle) != eDeleted) { |
| 428 | + vTaskDelay(10 / portTICK_PERIOD_MS); |
| 429 | + } |
| 430 | + } |
| 431 | + |
| 432 | + if (cmd_ret) { |
| 433 | + __lock_acquire(task->lock); |
| 434 | + *cmd_ret = task->exit_code; |
| 435 | + __lock_release(task->lock); |
| 436 | + } |
| 437 | +} |
| 438 | + |
| 439 | +#endif // CONFIG_CONSOLE_COMMAND_ON_TASK |
| 440 | + |
265 | 441 | static struct { |
266 | 442 | struct arg_str *help_cmd; |
267 | 443 | struct arg_int *verbose_level; |
|
0 commit comments