Implement transport-qualified session identity and immutable owner adapters for SSH console lifecycle and output-drain operations. Add focused host tests covering admission, stale identities, deferred actions, completion races, prompts, backpressure, and slot reuse. Update Phase 8D documentation and current-state tracking.
78 lines
4.2 KiB
C
78 lines
4.2 KiB
C
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <setjmp.h>
|
|
typedef int esp_err_t;
|
|
enum { ESP_OK, ESP_FAIL, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_STATE,
|
|
ESP_ERR_NO_MEM, ESP_ERR_TIMEOUT, ESP_ERR_NOT_SUPPORTED, ESP_ERR_NOT_FOUND };
|
|
enum { USER_ROLE_USER, USER_ROLE_ADMIN };
|
|
typedef struct { int role; } user_principal_t;
|
|
typedef unsigned TickType_t;
|
|
typedef void *TaskHandle_t;
|
|
typedef int portMUX_TYPE;
|
|
typedef struct { size_t size; unsigned count; unsigned char bytes[2048]; } StaticQueue_t;
|
|
typedef StaticQueue_t *QueueHandle_t;
|
|
typedef int StaticSemaphore_t;
|
|
typedef int *SemaphoreHandle_t;
|
|
#define portMUX_INITIALIZER_UNLOCKED 0
|
|
#define pdTRUE 1
|
|
#define pdPASS 1
|
|
#define portMAX_DELAY UINT32_MAX
|
|
#define pdMS_TO_TICKS(x) (x)
|
|
#define CONSOLE_COMPLETION_OUTPUT_CAPACITY 1024U
|
|
static unsigned lock_depth, ticks, runs, actions;
|
|
static bool principal_current = true, queue_full, owner_drained = true;
|
|
static TaskHandle_t current_task = (void *)1;
|
|
static jmp_buf loop_done;
|
|
static void (*delay_hook)(void), (*prompt_hook)(void), (*completion_hook)(void);
|
|
static void (*command_hook)(void);
|
|
#define taskENTER_CRITICAL(p) ((void)(p), ++lock_depth)
|
|
#define taskEXIT_CRITICAL(p) ((void)(p), --lock_depth)
|
|
static void secure_wipe(void *p, size_t n) { memset(p, 0, n); }
|
|
static size_t strlcpy(char *d, const char *s, size_t n) {
|
|
size_t len = strlen(s); if (n) { size_t k = len < n-1 ? len : n-1;
|
|
memcpy(d, s, k); d[k] = 0; } return len;
|
|
}
|
|
static esp_err_t user_database_principal_is_current(const user_principal_t *p, bool *c)
|
|
{ (void)p; *c = principal_current; return ESP_OK; }
|
|
static const char *esp_err_to_name(int e) { (void)e; return "fake"; }
|
|
static TaskHandle_t xTaskGetCurrentTaskHandle(void) { return current_task; }
|
|
static unsigned xTaskGetTickCount(void) { return ticks; }
|
|
static void vTaskDelay(unsigned n) { assert(!lock_depth); ticks += n; if (delay_hook) delay_hook(); }
|
|
static int xTaskCreate(void (*f)(void *), const char *n, unsigned s, void *c,
|
|
unsigned p, TaskHandle_t *t)
|
|
{ (void)f; (void)n; (void)s; (void)c; (void)p; *t = (void *)1; return pdPASS; }
|
|
static void vTaskDelete(TaskHandle_t t) { (void)t; }
|
|
static void xTaskNotifyGive(TaskHandle_t t) { (void)t; }
|
|
static unsigned ulTaskNotifyTake(int b, unsigned t) { (void)b; (void)t; return 1; }
|
|
static QueueHandle_t xQueueCreateStatic(unsigned n, size_t s, uint8_t *b, StaticQueue_t *q)
|
|
{ (void)n; (void)b; q->size = s; return q; }
|
|
static int xQueueSend(QueueHandle_t q, const void *p, unsigned t)
|
|
{ (void)t; if (queue_full) return 0; assert(!q->count); memcpy(q->bytes,p,q->size); q->count=1; return 1; }
|
|
static int xQueueReceive(QueueHandle_t q, void *p, unsigned t)
|
|
{ (void)t; if (!q->count) longjmp(loop_done,1); memcpy(p,q->bytes,q->size); q->count=0; return 1; }
|
|
static SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *s) { return s; }
|
|
static int xSemaphoreTake(SemaphoreHandle_t s, unsigned t)
|
|
{ if (t && prompt_hook) prompt_hook(); int r=*s; *s=0; return r; }
|
|
static int xSemaphoreGive(SemaphoreHandle_t s) { *s=1; return 1; }
|
|
static void linenoiseSetMaxLineLen(unsigned n) { (void)n; }
|
|
static char *linenoise(const char *p) { (void)p; return NULL; }
|
|
static int linenoiseHistoryAdd(const char *p) { (void)p; return 1; }
|
|
static void linenoiseFree(char *p) { (void)p; }
|
|
static bool console_completion_expand(const char *s, char *d, size_t n)
|
|
{ (void)s; (void)d; (void)n; if (completion_hook) completion_hook(); return false; }
|
|
static bool console_completion_format_matches(const char *s, char *d, size_t n, size_t *len)
|
|
{ (void)s; *len=strlcpy(d,"help\r\n",n); return true; }
|
|
static size_t esp_console_split_argv(char *s, char **v, size_t n)
|
|
{ (void)s; (void)v; (void)n; return 0; } /* Real parser tested by admin_ssh_policy. */
|
|
static esp_err_t esp_console_run(const char *s, int *r)
|
|
{ (void)s; ++runs; if (command_hook) command_hook(); *r=0; return ESP_OK; }
|
|
typedef struct { const char *command, *help, *hint; int (*func)(int,char **); void *argtable; } esp_console_cmd_t;
|
|
static int esp_console_cmd_register(const esp_console_cmd_t *c) { (void)c; return 0; }
|
|
static FILE *funopen(void *c, void *r, int (*w)(void *,const char *,int), void *s, void *f)
|
|
{ (void)c; (void)r; (void)w; (void)s; (void)f; return tmpfile(); }
|