861 lines
29 KiB
C
861 lines
29 KiB
C
#include "amduatd_space_doctor.h"
|
|
|
|
#include "amduat/asl/record.h"
|
|
#include "amduat/enc/asl_log.h"
|
|
#include "amduat/enc/asl1_core_codec.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
typedef struct {
|
|
char *data;
|
|
size_t len;
|
|
size_t cap;
|
|
} amduatd_doctor_strbuf_t;
|
|
|
|
static void amduatd_doctor_strbuf_free(amduatd_doctor_strbuf_t *b) {
|
|
if (b == NULL) {
|
|
return;
|
|
}
|
|
free(b->data);
|
|
b->data = NULL;
|
|
b->len = 0u;
|
|
b->cap = 0u;
|
|
}
|
|
|
|
static bool amduatd_doctor_strbuf_reserve(amduatd_doctor_strbuf_t *b,
|
|
size_t extra) {
|
|
size_t need;
|
|
size_t next_cap;
|
|
char *next;
|
|
|
|
if (b == NULL) {
|
|
return false;
|
|
}
|
|
if (extra == 0u) {
|
|
return true;
|
|
}
|
|
if (b->len > SIZE_MAX - extra) {
|
|
return false;
|
|
}
|
|
need = b->len + extra;
|
|
if (need <= b->cap) {
|
|
return true;
|
|
}
|
|
next_cap = b->cap != 0u ? b->cap : 256u;
|
|
while (next_cap < need) {
|
|
if (next_cap > (SIZE_MAX / 2u)) {
|
|
next_cap = need;
|
|
break;
|
|
}
|
|
next_cap *= 2u;
|
|
}
|
|
next = (char *)realloc(b->data, next_cap);
|
|
if (next == NULL) {
|
|
return false;
|
|
}
|
|
b->data = next;
|
|
b->cap = next_cap;
|
|
return true;
|
|
}
|
|
|
|
static bool amduatd_doctor_strbuf_append(amduatd_doctor_strbuf_t *b,
|
|
const char *data,
|
|
size_t len) {
|
|
if (b == NULL) {
|
|
return false;
|
|
}
|
|
if (data == NULL) {
|
|
len = 0u;
|
|
}
|
|
if (!amduatd_doctor_strbuf_reserve(b, len + 1u)) {
|
|
return false;
|
|
}
|
|
if (len != 0u) {
|
|
memcpy(b->data + b->len, data, len);
|
|
}
|
|
b->len += len;
|
|
b->data[b->len] = '\0';
|
|
return true;
|
|
}
|
|
|
|
static bool amduatd_doctor_strbuf_append_cstr(amduatd_doctor_strbuf_t *b,
|
|
const char *s) {
|
|
return amduatd_doctor_strbuf_append(b,
|
|
s != NULL ? s : "",
|
|
s != NULL ? strlen(s) : 0u);
|
|
}
|
|
|
|
static bool amduatd_doctor_strbuf_append_char(amduatd_doctor_strbuf_t *b,
|
|
char c) {
|
|
return amduatd_doctor_strbuf_append(b, &c, 1u);
|
|
}
|
|
|
|
static char *amduatd_doctor_strdup(const char *s) {
|
|
size_t len;
|
|
char *copy;
|
|
if (s == NULL) {
|
|
return NULL;
|
|
}
|
|
len = strlen(s);
|
|
copy = (char *)malloc(len + 1u);
|
|
if (copy == NULL) {
|
|
return NULL;
|
|
}
|
|
memcpy(copy, s, len);
|
|
copy[len] = '\0';
|
|
return copy;
|
|
}
|
|
|
|
static const char *amduatd_space_doctor_status_name(
|
|
amduatd_space_doctor_status_t status) {
|
|
switch (status) {
|
|
case AMDUATD_DOCTOR_OK:
|
|
return "ok";
|
|
case AMDUATD_DOCTOR_WARN:
|
|
return "warn";
|
|
case AMDUATD_DOCTOR_FAIL:
|
|
return "fail";
|
|
case AMDUATD_DOCTOR_SKIPPED:
|
|
return "skipped";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
static void amduatd_space_doctor_report_init(
|
|
amduatd_space_doctor_report_t *report) {
|
|
if (report == NULL) {
|
|
return;
|
|
}
|
|
memset(report, 0, sizeof(*report));
|
|
report->backend = AMDUATD_STORE_BACKEND_FS;
|
|
}
|
|
|
|
static void amduatd_space_doctor_report_clear(
|
|
amduatd_space_doctor_report_t *report) {
|
|
if (report == NULL) {
|
|
return;
|
|
}
|
|
for (size_t i = 0u; i < report->checks_len; ++i) {
|
|
free(report->checks[i].name);
|
|
free(report->checks[i].detail);
|
|
}
|
|
free(report->checks);
|
|
amduatd_space_doctor_report_init(report);
|
|
}
|
|
|
|
void amduatd_space_doctor_report_free(amduatd_space_doctor_report_t *report) {
|
|
amduatd_space_doctor_report_clear(report);
|
|
}
|
|
|
|
static bool amduatd_space_doctor_report_add(
|
|
amduatd_space_doctor_report_t *report,
|
|
const char *name,
|
|
amduatd_space_doctor_status_t status,
|
|
const char *detail) {
|
|
size_t next_len;
|
|
amduatd_space_doctor_check_t *next;
|
|
amduatd_space_doctor_check_t *entry;
|
|
|
|
if (report == NULL || name == NULL) {
|
|
return false;
|
|
}
|
|
if (report->checks_len > SIZE_MAX - 1u) {
|
|
return false;
|
|
}
|
|
next_len = report->checks_len + 1u;
|
|
next = (amduatd_space_doctor_check_t *)realloc(
|
|
report->checks, next_len * sizeof(*next));
|
|
if (next == NULL) {
|
|
return false;
|
|
}
|
|
report->checks = next;
|
|
entry = &report->checks[report->checks_len];
|
|
memset(entry, 0, sizeof(*entry));
|
|
entry->name = amduatd_doctor_strdup(name);
|
|
entry->detail = amduatd_doctor_strdup(detail != NULL ? detail : "");
|
|
if (entry->name == NULL || entry->detail == NULL) {
|
|
free(entry->name);
|
|
free(entry->detail);
|
|
return false;
|
|
}
|
|
entry->status = status;
|
|
report->checks_len = next_len;
|
|
switch (status) {
|
|
case AMDUATD_DOCTOR_OK:
|
|
report->ok_count++;
|
|
break;
|
|
case AMDUATD_DOCTOR_WARN:
|
|
report->warn_count++;
|
|
break;
|
|
case AMDUATD_DOCTOR_FAIL:
|
|
report->fail_count++;
|
|
break;
|
|
case AMDUATD_DOCTOR_SKIPPED:
|
|
report->skipped_count++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool amduatd_space_doctor_build_collection_head_name(
|
|
const char *name,
|
|
char **out_name) {
|
|
size_t name_len;
|
|
size_t total_len;
|
|
char *buffer;
|
|
size_t offset = 0u;
|
|
|
|
if (name == NULL || out_name == NULL) {
|
|
return false;
|
|
}
|
|
if (!amduat_asl_pointer_name_is_valid(name)) {
|
|
return false;
|
|
}
|
|
name_len = strlen(name);
|
|
total_len = 11u + name_len + 5u + 1u;
|
|
buffer = (char *)malloc(total_len);
|
|
if (buffer == NULL) {
|
|
return false;
|
|
}
|
|
memcpy(buffer + offset, "collection/", 11u);
|
|
offset += 11u;
|
|
memcpy(buffer + offset, name, name_len);
|
|
offset += name_len;
|
|
memcpy(buffer + offset, "/head", 5u);
|
|
offset += 5u;
|
|
buffer[offset] = '\0';
|
|
*out_name = buffer;
|
|
return true;
|
|
}
|
|
|
|
static bool amduatd_space_doctor_build_collection_log_head_name(
|
|
const char *name,
|
|
char **out_name) {
|
|
size_t name_len;
|
|
size_t log_name_len;
|
|
size_t total_len;
|
|
char *buffer;
|
|
size_t offset = 0u;
|
|
|
|
if (name == NULL || out_name == NULL) {
|
|
return false;
|
|
}
|
|
if (!amduat_asl_pointer_name_is_valid(name)) {
|
|
return false;
|
|
}
|
|
name_len = strlen(name);
|
|
log_name_len = 11u + name_len + 4u;
|
|
total_len = 4u + log_name_len + 5u + 1u;
|
|
buffer = (char *)malloc(total_len);
|
|
if (buffer == NULL) {
|
|
return false;
|
|
}
|
|
memcpy(buffer + offset, "log/", 4u);
|
|
offset += 4u;
|
|
memcpy(buffer + offset, "collection/", 11u);
|
|
offset += 11u;
|
|
memcpy(buffer + offset, name, name_len);
|
|
offset += name_len;
|
|
memcpy(buffer + offset, "/log", 4u);
|
|
offset += 4u;
|
|
memcpy(buffer + offset, "/head", 5u);
|
|
offset += 5u;
|
|
buffer[offset] = '\0';
|
|
*out_name = buffer;
|
|
return true;
|
|
}
|
|
|
|
enum {
|
|
AMDUATD_EDGE_INDEX_MAGIC_LEN = 8,
|
|
AMDUATD_EDGE_INDEX_VERSION = 1
|
|
};
|
|
|
|
static const uint8_t k_amduatd_edge_index_magic[AMDUATD_EDGE_INDEX_MAGIC_LEN] = {
|
|
'A', 'S', 'L', 'E', 'I', 'X', '1', '\0'
|
|
};
|
|
|
|
typedef struct {
|
|
bool has_graph_ref;
|
|
amduat_reference_t graph_ref;
|
|
} amduatd_edge_index_state_view_t;
|
|
|
|
static void amduatd_edge_index_state_view_free(
|
|
amduatd_edge_index_state_view_t *state) {
|
|
if (state == NULL) {
|
|
return;
|
|
}
|
|
if (state->has_graph_ref) {
|
|
amduat_reference_free(&state->graph_ref);
|
|
}
|
|
memset(state, 0, sizeof(*state));
|
|
}
|
|
|
|
static bool amduatd_doctor_read_u32_le(const uint8_t *data,
|
|
size_t len,
|
|
size_t *offset,
|
|
uint32_t *out) {
|
|
if (len - *offset < 4u) {
|
|
return false;
|
|
}
|
|
*out = (uint32_t)data[*offset] |
|
|
((uint32_t)data[*offset + 1u] << 8) |
|
|
((uint32_t)data[*offset + 2u] << 16) |
|
|
((uint32_t)data[*offset + 3u] << 24);
|
|
*offset += 4u;
|
|
return true;
|
|
}
|
|
|
|
static bool amduatd_doctor_read_u64_le(const uint8_t *data,
|
|
size_t len,
|
|
size_t *offset,
|
|
uint64_t *out) {
|
|
if (len - *offset < 8u) {
|
|
return false;
|
|
}
|
|
*out = (uint64_t)data[*offset] |
|
|
((uint64_t)data[*offset + 1u] << 8) |
|
|
((uint64_t)data[*offset + 2u] << 16) |
|
|
((uint64_t)data[*offset + 3u] << 24) |
|
|
((uint64_t)data[*offset + 4u] << 32) |
|
|
((uint64_t)data[*offset + 5u] << 40) |
|
|
((uint64_t)data[*offset + 6u] << 48) |
|
|
((uint64_t)data[*offset + 7u] << 56);
|
|
*offset += 8u;
|
|
return true;
|
|
}
|
|
|
|
static bool amduatd_edge_index_state_view_decode(
|
|
amduat_octets_t payload,
|
|
amduatd_edge_index_state_view_t *out_state) {
|
|
size_t offset = 0u;
|
|
uint32_t version = 0u;
|
|
uint32_t ref_len = 0u;
|
|
amduat_octets_t ref_bytes;
|
|
uint64_t ignored_offset = 0u;
|
|
|
|
if (out_state == NULL) {
|
|
return false;
|
|
}
|
|
memset(out_state, 0, sizeof(*out_state));
|
|
if (payload.data == NULL ||
|
|
payload.len < AMDUATD_EDGE_INDEX_MAGIC_LEN + 4u + 8u + 4u) {
|
|
return false;
|
|
}
|
|
if (memcmp(payload.data, k_amduatd_edge_index_magic,
|
|
AMDUATD_EDGE_INDEX_MAGIC_LEN) != 0) {
|
|
return false;
|
|
}
|
|
offset += AMDUATD_EDGE_INDEX_MAGIC_LEN;
|
|
if (!amduatd_doctor_read_u32_le(payload.data, payload.len, &offset, &version) ||
|
|
version != AMDUATD_EDGE_INDEX_VERSION) {
|
|
return false;
|
|
}
|
|
if (!amduatd_doctor_read_u64_le(payload.data, payload.len, &offset,
|
|
&ignored_offset) ||
|
|
!amduatd_doctor_read_u32_le(payload.data, payload.len, &offset, &ref_len)) {
|
|
return false;
|
|
}
|
|
(void)ignored_offset;
|
|
if (payload.len - offset < ref_len) {
|
|
return false;
|
|
}
|
|
if (ref_len != 0u) {
|
|
ref_bytes = amduat_octets(payload.data + offset, ref_len);
|
|
if (!amduat_enc_asl1_core_decode_reference_v1(ref_bytes,
|
|
&out_state->graph_ref)) {
|
|
amduatd_edge_index_state_view_free(out_state);
|
|
return false;
|
|
}
|
|
out_state->has_graph_ref = true;
|
|
offset += ref_len;
|
|
}
|
|
return offset == payload.len;
|
|
}
|
|
|
|
static amduatd_store_backend_t amduatd_space_doctor_backend_from_store(
|
|
const amduat_asl_store_t *store) {
|
|
if (store == NULL) {
|
|
return AMDUATD_STORE_BACKEND_FS;
|
|
}
|
|
if (store->ops.log_scan != NULL && store->ops.current_state != NULL) {
|
|
return AMDUATD_STORE_BACKEND_INDEX;
|
|
}
|
|
return AMDUATD_STORE_BACKEND_FS;
|
|
}
|
|
|
|
typedef struct {
|
|
const char *label;
|
|
char *pointer_name;
|
|
bool pointer_exists;
|
|
amduat_reference_t pointer_ref;
|
|
} amduatd_doctor_pointer_t;
|
|
|
|
static void amduatd_space_doctor_pointer_free(amduatd_doctor_pointer_t *ptr) {
|
|
if (ptr == NULL) {
|
|
return;
|
|
}
|
|
free(ptr->pointer_name);
|
|
ptr->pointer_name = NULL;
|
|
ptr->pointer_exists = false;
|
|
amduat_reference_free(&ptr->pointer_ref);
|
|
}
|
|
|
|
bool amduatd_space_doctor_run(amduat_asl_store_t *store,
|
|
amduat_asl_pointer_store_t *pointer_store,
|
|
const amduatd_space_t *effective_space,
|
|
const amduatd_cfg_t *cfg,
|
|
const amduatd_fed_cfg_t *fed_cfg,
|
|
amduatd_space_doctor_report_t *out_report) {
|
|
amduatd_doctor_pointer_t pointers[3];
|
|
amduat_octets_t edges_collection = amduat_octets(NULL, 0u);
|
|
amduat_octets_t edges_index_head = amduat_octets(NULL, 0u);
|
|
char *collection_head = NULL;
|
|
char *collection_log_head = NULL;
|
|
bool names_ok = true;
|
|
bool ok = false;
|
|
size_t i;
|
|
|
|
if (out_report == NULL) {
|
|
return false;
|
|
}
|
|
amduatd_space_doctor_report_init(out_report);
|
|
if (store == NULL || pointer_store == NULL || cfg == NULL) {
|
|
return false;
|
|
}
|
|
(void)cfg;
|
|
|
|
out_report->backend = amduatd_space_doctor_backend_from_store(store);
|
|
if (effective_space != NULL &&
|
|
effective_space->enabled &&
|
|
effective_space->space_id.data != NULL) {
|
|
out_report->scoped = true;
|
|
snprintf(out_report->space_id, sizeof(out_report->space_id), "%s",
|
|
(const char *)effective_space->space_id.data);
|
|
}
|
|
|
|
memset(pointers, 0, sizeof(pointers));
|
|
pointers[0].label = "edges_index_head";
|
|
pointers[1].label = "edges_collection_snapshot_head";
|
|
pointers[2].label = "edges_collection_log_head";
|
|
for (i = 0u; i < 3u; ++i) {
|
|
memset(&pointers[i].pointer_ref, 0, sizeof(pointers[i].pointer_ref));
|
|
}
|
|
|
|
if (!amduatd_space_edges_collection_name(effective_space, &edges_collection) ||
|
|
!amduatd_space_edges_index_head_name(effective_space, &edges_index_head)) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"pointer_name_validation",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"failed to scope pointer names");
|
|
names_ok = false;
|
|
goto doctor_after_names;
|
|
}
|
|
if (!amduatd_space_doctor_build_collection_head_name(
|
|
(const char *)edges_collection.data, &collection_head) ||
|
|
!amduatd_space_doctor_build_collection_log_head_name(
|
|
(const char *)edges_collection.data, &collection_log_head)) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"pointer_name_validation",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"failed to build collection heads");
|
|
names_ok = false;
|
|
goto doctor_after_names;
|
|
}
|
|
if (!amduat_asl_pointer_name_is_valid(
|
|
(const char *)edges_collection.data) ||
|
|
!amduat_asl_pointer_name_is_valid(
|
|
(const char *)edges_index_head.data) ||
|
|
!amduat_asl_pointer_name_is_valid(collection_head) ||
|
|
!amduat_asl_pointer_name_is_valid(collection_log_head)) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"pointer_name_validation",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"invalid pointer name encoding");
|
|
names_ok = false;
|
|
goto doctor_after_names;
|
|
}
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"pointer_name_validation",
|
|
AMDUATD_DOCTOR_OK,
|
|
"scoped pointer names valid");
|
|
|
|
doctor_after_names:
|
|
if (!names_ok) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edges_index_head",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"invalid pointer names");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edges_collection_snapshot_head",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"invalid pointer names");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edges_collection_log_head",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"invalid pointer names");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"cas_edges_index_head",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"invalid pointer names");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"cas_edges_collection_snapshot_head",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"invalid pointer names");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"cas_edges_collection_log_head",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"invalid pointer names");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_state_parse",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"invalid pointer names");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_graph_ref",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"invalid pointer names");
|
|
goto doctor_index_checks;
|
|
}
|
|
|
|
pointers[0].pointer_name = amduatd_doctor_strdup(
|
|
(const char *)edges_index_head.data);
|
|
pointers[1].pointer_name = amduatd_doctor_strdup(collection_head);
|
|
pointers[2].pointer_name = amduatd_doctor_strdup(collection_log_head);
|
|
if (pointers[0].pointer_name == NULL ||
|
|
pointers[1].pointer_name == NULL ||
|
|
pointers[2].pointer_name == NULL) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"pointer_store",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"oom");
|
|
goto doctor_cleanup;
|
|
}
|
|
|
|
for (i = 0u; i < 3u; ++i) {
|
|
bool exists = false;
|
|
amduat_asl_pointer_error_t perr =
|
|
amduat_asl_pointer_get(pointer_store,
|
|
pointers[i].pointer_name,
|
|
&exists,
|
|
&pointers[i].pointer_ref);
|
|
if (perr != AMDUAT_ASL_POINTER_OK) {
|
|
char detail[256];
|
|
snprintf(detail, sizeof(detail), "pointer error: %s",
|
|
pointers[i].pointer_name);
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
pointers[i].label,
|
|
AMDUATD_DOCTOR_FAIL,
|
|
detail);
|
|
continue;
|
|
}
|
|
pointers[i].pointer_exists = exists;
|
|
if (exists) {
|
|
char detail[256];
|
|
snprintf(detail, sizeof(detail), "present: %s",
|
|
pointers[i].pointer_name);
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
pointers[i].label,
|
|
AMDUATD_DOCTOR_OK,
|
|
detail);
|
|
} else {
|
|
char detail[256];
|
|
snprintf(detail, sizeof(detail), "missing: %s",
|
|
pointers[i].pointer_name);
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
pointers[i].label,
|
|
AMDUATD_DOCTOR_WARN,
|
|
detail);
|
|
}
|
|
}
|
|
|
|
for (i = 0u; i < 3u; ++i) {
|
|
const char *check_name = NULL;
|
|
switch (i) {
|
|
case 0u:
|
|
check_name = "cas_edges_index_head";
|
|
break;
|
|
case 1u:
|
|
check_name = "cas_edges_collection_snapshot_head";
|
|
break;
|
|
case 2u:
|
|
check_name = "cas_edges_collection_log_head";
|
|
break;
|
|
default:
|
|
check_name = "cas_pointer_ref";
|
|
break;
|
|
}
|
|
if (!pointers[i].pointer_exists) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
check_name,
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"pointer missing");
|
|
continue;
|
|
}
|
|
{
|
|
amduat_artifact_t artifact;
|
|
memset(&artifact, 0, sizeof(artifact));
|
|
if (amduat_asl_store_get(store, pointers[i].pointer_ref, &artifact) !=
|
|
AMDUAT_ASL_STORE_OK) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
check_name,
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"store get failed");
|
|
} else {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
check_name,
|
|
AMDUATD_DOCTOR_OK,
|
|
"ref readable");
|
|
}
|
|
amduat_artifact_free(&artifact);
|
|
}
|
|
}
|
|
|
|
if (!pointers[0].pointer_exists) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_state_parse",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"pointer missing");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_graph_ref",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"pointer missing");
|
|
} else {
|
|
amduat_asl_record_t record;
|
|
amduatd_edge_index_state_view_t state;
|
|
const char *schema = "tgk/edge_index_state";
|
|
bool parsed = false;
|
|
memset(&record, 0, sizeof(record));
|
|
memset(&state, 0, sizeof(state));
|
|
if (amduat_asl_record_store_get(store,
|
|
pointers[0].pointer_ref,
|
|
&record) != AMDUAT_ASL_STORE_OK) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_state_parse",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"record load failed");
|
|
} else if (record.schema.len != strlen(schema) ||
|
|
memcmp(record.schema.data, schema, record.schema.len) != 0) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_state_parse",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"unexpected schema");
|
|
} else if (!amduatd_edge_index_state_view_decode(record.payload, &state)) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_state_parse",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"payload decode failed");
|
|
} else {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_state_parse",
|
|
AMDUATD_DOCTOR_OK,
|
|
"edge index state decoded");
|
|
parsed = true;
|
|
}
|
|
|
|
if (parsed && state.has_graph_ref) {
|
|
amduat_artifact_t graph_artifact;
|
|
memset(&graph_artifact, 0, sizeof(graph_artifact));
|
|
if (amduat_asl_store_get(store, state.graph_ref, &graph_artifact) !=
|
|
AMDUAT_ASL_STORE_OK) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_graph_ref",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"graph ref missing");
|
|
} else {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_graph_ref",
|
|
AMDUATD_DOCTOR_OK,
|
|
"graph ref readable");
|
|
}
|
|
amduat_artifact_free(&graph_artifact);
|
|
} else if (parsed) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_graph_ref",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"no graph ref");
|
|
} else {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"edge_index_graph_ref",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"edge index state unreadable");
|
|
}
|
|
|
|
amduatd_edge_index_state_view_free(&state);
|
|
amduat_asl_record_free(&record);
|
|
}
|
|
|
|
doctor_index_checks:
|
|
if (out_report->backend == AMDUATD_STORE_BACKEND_INDEX) {
|
|
amduat_asl_index_state_t state;
|
|
amduat_asl_log_record_t *records = NULL;
|
|
size_t record_count = 0u;
|
|
if (!amduat_asl_index_current_state(store, &state)) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"index_current_state",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"current_state failed");
|
|
} else {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"index_current_state",
|
|
AMDUATD_DOCTOR_OK,
|
|
"current_state ok");
|
|
}
|
|
{
|
|
amduat_asl_store_error_t scan_err =
|
|
amduat_asl_log_scan(store, &records, &record_count);
|
|
if (scan_err == AMDUAT_ASL_STORE_OK) {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"index_log_scan",
|
|
AMDUATD_DOCTOR_OK,
|
|
"log_scan ok");
|
|
} else {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"index_log_scan",
|
|
AMDUATD_DOCTOR_FAIL,
|
|
"log_scan failed");
|
|
}
|
|
if (records != NULL) {
|
|
amduat_enc_asl_log_free(records, record_count);
|
|
}
|
|
}
|
|
} else {
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"index_current_state",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"requires index backend");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"index_log_scan",
|
|
AMDUATD_DOCTOR_SKIPPED,
|
|
"requires index backend");
|
|
}
|
|
|
|
{
|
|
char detail[256];
|
|
bool fed_enabled = fed_cfg != NULL && fed_cfg->enabled;
|
|
bool require_index_backend = fed_enabled;
|
|
snprintf(detail, sizeof(detail),
|
|
"enabled=%s require_index_backend=%s",
|
|
fed_enabled ? "true" : "false",
|
|
require_index_backend ? "true" : "false");
|
|
(void)amduatd_space_doctor_report_add(out_report,
|
|
"federation",
|
|
AMDUATD_DOCTOR_OK,
|
|
detail);
|
|
}
|
|
|
|
ok = true;
|
|
|
|
doctor_cleanup:
|
|
for (i = 0u; i < 3u; ++i) {
|
|
amduatd_space_doctor_pointer_free(&pointers[i]);
|
|
}
|
|
free((void *)edges_collection.data);
|
|
free((void *)edges_index_head.data);
|
|
free(collection_head);
|
|
free(collection_log_head);
|
|
if (!ok) {
|
|
amduatd_space_doctor_report_clear(out_report);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
bool amduatd_space_doctor_report_json(
|
|
const amduatd_space_doctor_report_t *report,
|
|
char **out_json) {
|
|
amduatd_doctor_strbuf_t b;
|
|
char header[256];
|
|
|
|
if (out_json != NULL) {
|
|
*out_json = NULL;
|
|
}
|
|
if (report == NULL || out_json == NULL) {
|
|
return false;
|
|
}
|
|
|
|
memset(&b, 0, sizeof(b));
|
|
if (!amduatd_doctor_strbuf_append_cstr(&b, "{")) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
|
|
if (!amduatd_doctor_strbuf_append_cstr(&b, "\"effective_space\":{")) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
if (report->scoped) {
|
|
if (snprintf(header, sizeof(header),
|
|
"\"mode\":\"scoped\",\"space_id\":\"%s\"",
|
|
report->space_id) <= 0 ||
|
|
!amduatd_doctor_strbuf_append_cstr(&b, header)) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!amduatd_doctor_strbuf_append_cstr(&b, "\"mode\":\"unscoped\"")) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
}
|
|
if (!amduatd_doctor_strbuf_append_cstr(&b, "},")) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
|
|
if (snprintf(header, sizeof(header),
|
|
"\"store_backend\":\"%s\",",
|
|
amduatd_store_backend_name(report->backend)) <= 0 ||
|
|
!amduatd_doctor_strbuf_append_cstr(&b, header)) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
|
|
if (!amduatd_doctor_strbuf_append_cstr(&b, "\"checks\":[")) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
for (size_t i = 0u; i < report->checks_len; ++i) {
|
|
const amduatd_space_doctor_check_t *check = &report->checks[i];
|
|
if (i != 0u) {
|
|
(void)amduatd_doctor_strbuf_append_char(&b, ',');
|
|
}
|
|
if (!amduatd_doctor_strbuf_append_cstr(&b, "{\"name\":\"") ||
|
|
!amduatd_doctor_strbuf_append_cstr(&b, check->name) ||
|
|
!amduatd_doctor_strbuf_append_cstr(&b, "\",\"status\":\"") ||
|
|
!amduatd_doctor_strbuf_append_cstr(
|
|
&b,
|
|
amduatd_space_doctor_status_name(check->status)) ||
|
|
!amduatd_doctor_strbuf_append_cstr(&b, "\",\"detail\":\"") ||
|
|
!amduatd_doctor_strbuf_append_cstr(&b, check->detail) ||
|
|
!amduatd_doctor_strbuf_append_cstr(&b, "\"}")) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
}
|
|
if (!amduatd_doctor_strbuf_append_cstr(&b, "],")) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
|
|
if (snprintf(header, sizeof(header),
|
|
"\"summary\":{"
|
|
"\"ok_count\":%llu,"
|
|
"\"warn_count\":%llu,"
|
|
"\"fail_count\":%llu,"
|
|
"\"skipped_count\":%llu"
|
|
"}}\n",
|
|
(unsigned long long)report->ok_count,
|
|
(unsigned long long)report->warn_count,
|
|
(unsigned long long)report->fail_count,
|
|
(unsigned long long)report->skipped_count) <= 0 ||
|
|
!amduatd_doctor_strbuf_append_cstr(&b, header)) {
|
|
amduatd_doctor_strbuf_free(&b);
|
|
return false;
|
|
}
|
|
|
|
*out_json = b.data;
|
|
return true;
|
|
}
|