Route bounded admin mutations through the existing administration dispatcher, covering apply, lifecycle, persistence, authorization, and result tracking. Add the browser controls, automatic result refresh, regression coverage, and phase documentation.
93 lines
4.8 KiB
C
93 lines
4.8 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 };
|
|
#define USER_DATABASE_USERNAME_CAPACITY 16U
|
|
typedef struct {
|
|
uint32_t user_id, auth_generation;
|
|
int role, method;
|
|
size_t username_length;
|
|
char username[USER_DATABASE_USERNAME_CAPACITY + 1U];
|
|
} user_principal_t;
|
|
typedef unsigned TickType_t;
|
|
typedef void *TaskHandle_t;
|
|
typedef int portMUX_TYPE;
|
|
typedef struct { size_t size; unsigned count, capacity; 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 uint32_t serial_settings_executed;
|
|
static unsigned serial_settings_preceding_runs, queue_send_wait;
|
|
static void web_serial_settings_execute(uint32_t id) {
|
|
assert(!lock_depth);
|
|
serial_settings_executed = id;
|
|
serial_settings_preceding_runs = runs;
|
|
}
|
|
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; assert(!lock_depth); *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)b; q->size = s; q->capacity = n; assert(n*s <= sizeof(q->bytes)); return q; }
|
|
static int xQueueSend(QueueHandle_t q, const void *p, unsigned t)
|
|
{ queue_send_wait=t; if (queue_full || q->count==q->capacity) return 0;
|
|
memcpy(q->bytes+q->count*q->size,p,q->size); ++q->count; 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; memmove(q->bytes,q->bytes+q->size,q->count*q->size); return 1; }
|
|
static SemaphoreHandle_t xSemaphoreCreateBinaryStatic(StaticSemaphore_t *s) { return s; }
|
|
static int xSemaphoreTake(SemaphoreHandle_t s, unsigned t)
|
|
{ assert(!lock_depth); if (t && !*s) { ticks+=t; if (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; }
|
|
size_t esp_console_split_argv(char *s, char **v, size_t n);
|
|
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(); }
|