Add space sync status endpoint with cursor peer discovery

This commit is contained in:
Carl Niklas Rydberg 2026-01-24 17:43:51 +01:00
parent d74884b442
commit 67c837be3c
8 changed files with 1679 additions and 3 deletions

View file

@ -30,7 +30,7 @@ set(amduatd_sources src/amduatd.c src/amduatd_http.c src/amduatd_caps.c
src/amduatd_fed.c src/amduatd_fed_cursor.c
src/amduatd_fed_pull_plan.c src/amduatd_fed_push_plan.c
src/amduatd_fed_pull_apply.c src/amduatd_fed_push_apply.c
src/amduatd_space_doctor.c)
src/amduatd_space_doctor.c src/amduatd_space_roots.c)
if(AMDUATD_ENABLE_UI)
list(APPEND amduatd_sources src/amduatd_ui.c)
endif()
@ -286,3 +286,47 @@ target_link_libraries(amduatd_test_space_doctor
)
add_test(NAME amduatd_space_doctor COMMAND amduatd_test_space_doctor)
add_executable(amduatd_test_space_roots
tests/test_amduatd_space_roots.c
src/amduatd_space_roots.c
src/amduatd_space.c
src/amduatd_fed_cursor.c
src/amduatd_store.c
)
target_include_directories(amduatd_test_space_roots
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/amduat/include
)
target_link_libraries(amduatd_test_space_roots
PRIVATE amduat_asl_store_fs amduat_asl_pointer_fs amduat_asl_record
amduat_asl_store_index_fs amduat_asl amduat_enc amduat_util
amduat_hash_asl1
)
add_test(NAME amduatd_space_roots COMMAND amduatd_test_space_roots)
add_executable(amduatd_test_space_sync_status
tests/test_amduatd_space_sync_status.c
src/amduatd_space_roots.c
src/amduatd_space.c
src/amduatd_fed_cursor.c
src/amduatd_store.c
)
target_include_directories(amduatd_test_space_sync_status
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/amduat/include
)
target_link_libraries(amduatd_test_space_sync_status
PRIVATE amduat_asl_store_fs amduat_asl_pointer_fs amduat_asl_record
amduat_asl_store_index_fs amduat_asl amduat_enc amduat_util
amduat_hash_asl1
)
add_test(NAME amduatd_space_sync_status COMMAND amduatd_test_space_sync_status)

View file

@ -199,6 +199,35 @@ Run the daemon with derivation indexing enabled:
./build/amduatd --root .amduat-asl --sock amduatd.sock --enable-derivation-index
```
## Space roots (GC)
`/v1/space/roots` enumerates the pointer heads that must be treated as GC roots
for the effective space, including federation cursor heads.
GC root sets MUST include federation cursors to avoid trimming artifacts still
reachable via replication state. Use the roots listing to build your root set
before running GC tooling.
Example:
```sh
curl --unix-socket amduatd.sock \
'http://localhost/v1/space/roots' \
-H 'X-Amduat-Space: demo'
```
## Space sync status
`/v1/space/sync/status` is a read-only summary of federation readiness and
per-peer cursor positions for the effective space. Peers are discovered from
cursor head pointers (pull and push) and returned in deterministic order.
```sh
curl --unix-socket amduatd.sock \
'http://localhost/v1/space/sync/status' \
-H 'X-Amduat-Space: demo'
```
To fail `/v1/pel/run` if the derivation index write fails:
```sh

View file

@ -6,6 +6,9 @@
{"method": "GET", "path": "/v1/meta"},
{"method": "HEAD", "path": "/v1/meta"},
{"method": "GET", "path": "/v1/contract"},
{"method": "GET", "path": "/v1/space/doctor"},
{"method": "GET", "path": "/v1/space/roots"},
{"method": "GET", "path": "/v1/space/sync/status"},
{"method": "POST", "path": "/v1/capabilities"},
{"method": "GET", "path": "/v1/cap/resolve"},
{"method": "GET", "path": "/v1/fed/records"},

View file

@ -49,6 +49,7 @@
#include "amduatd_store.h"
#include "amduatd_derivation_index.h"
#include "amduatd_space_doctor.h"
#include "amduatd_space_roots.h"
#include <errno.h>
#include <signal.h>
@ -122,6 +123,8 @@ static const char k_amduatd_contract_v1_json[] =
"{\"method\":\"HEAD\",\"path\":\"/v1/meta\"},"
"{\"method\":\"GET\",\"path\":\"/v1/contract\"},"
"{\"method\":\"GET\",\"path\":\"/v1/space/doctor\"},"
"{\"method\":\"GET\",\"path\":\"/v1/space/roots\"},"
"{\"method\":\"GET\",\"path\":\"/v1/space/sync/status\"},"
"{\"method\":\"POST\",\"path\":\"/v1/capabilities\"},"
"{\"method\":\"GET\",\"path\":\"/v1/cap/resolve\"},"
"{\"method\":\"GET\",\"path\":\"/v1/fed/records\"},"
@ -865,6 +868,380 @@ static bool amduatd_handle_get_space_doctor(int fd,
return ok;
}
static bool amduatd_handle_get_space_roots(int fd,
const amduatd_http_req_t *req,
const amduatd_cfg_t *dcfg,
const amduatd_caps_t *caps,
const char *root_path) {
amduat_asl_pointer_store_t pointer_store;
amduatd_space_roots_list_t roots;
amduatd_strbuf_t b;
bool ok = false;
memset(&roots, 0, sizeof(roots));
memset(&b, 0, sizeof(b));
if (req == NULL || dcfg == NULL || root_path == NULL) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"internal error");
}
if (caps != NULL && caps->enabled && req->x_capability[0] != '\0') {
const char *reason = NULL;
if (!amduatd_caps_check_space(caps, dcfg, req, &reason)) {
if (reason != NULL && strcmp(reason, "wrong-space") == 0) {
return amduatd_send_json_error(fd, 403, "Forbidden",
"space not permitted by capability");
}
return amduatd_send_json_error(fd, 403, "Forbidden",
"invalid capability");
}
}
if (!amduat_asl_pointer_store_init(&pointer_store, root_path)) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"pointer store error");
}
if (!amduatd_space_roots_list(root_path,
&pointer_store,
req->effective_space,
&roots)) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"root enumeration failed");
}
if (!amduatd_strbuf_append_cstr(&b, "{\"effective_space\":{")) {
goto roots_cleanup;
}
if (req->effective_space != NULL && req->effective_space->enabled &&
req->effective_space->space_id.data != NULL) {
const char *space_id = (const char *)req->effective_space->space_id.data;
if (!amduatd_strbuf_append_cstr(&b, "\"mode\":\"scoped\",") ||
!amduatd_strbuf_append_cstr(&b, "\"space_id\":\"") ||
!amduatd_strbuf_append_cstr(&b, space_id) ||
!amduatd_strbuf_append_cstr(&b, "\"")) {
goto roots_cleanup;
}
} else {
if (!amduatd_strbuf_append_cstr(&b, "\"mode\":\"unscoped\",") ||
!amduatd_strbuf_append_cstr(&b, "\"space_id\":null")) {
goto roots_cleanup;
}
}
if (!amduatd_strbuf_append_cstr(&b, "},\"roots\":[")) {
goto roots_cleanup;
}
for (size_t i = 0u; i < roots.len; ++i) {
const char *pointer_name = roots.names[i];
bool exists = false;
amduat_reference_t ref;
amduat_asl_pointer_error_t perr;
char *ref_hex = NULL;
memset(&ref, 0, sizeof(ref));
if (i != 0u) {
if (!amduatd_strbuf_append_char(&b, ',')) {
goto roots_cleanup;
}
}
perr = amduat_asl_pointer_get(&pointer_store,
pointer_name,
&exists,
&ref);
if (perr != AMDUAT_ASL_POINTER_OK) {
amduat_reference_free(&ref);
goto roots_cleanup;
}
if (exists) {
if (!amduat_asl_ref_encode_hex(ref, &ref_hex)) {
amduat_reference_free(&ref);
goto roots_cleanup;
}
}
if (!amduatd_strbuf_append_cstr(&b, "{\"pointer_name\":\"") ||
!amduatd_strbuf_append_cstr(&b, pointer_name) ||
!amduatd_strbuf_append_cstr(&b, "\",\"head_ref_present\":") ||
!amduatd_strbuf_append_cstr(&b, exists ? "true" : "false")) {
free(ref_hex);
amduat_reference_free(&ref);
goto roots_cleanup;
}
if (exists) {
if (!amduatd_strbuf_append_cstr(&b, ",\"head_ref\":\"") ||
!amduatd_strbuf_append_cstr(&b, ref_hex) ||
!amduatd_strbuf_append_cstr(&b, "\"")) {
free(ref_hex);
amduat_reference_free(&ref);
goto roots_cleanup;
}
}
if (!amduatd_strbuf_append_cstr(&b, "}")) {
free(ref_hex);
amduat_reference_free(&ref);
goto roots_cleanup;
}
free(ref_hex);
amduat_reference_free(&ref);
}
if (!amduatd_strbuf_append_cstr(&b, "]}\n")) {
goto roots_cleanup;
}
ok = amduatd_http_send_json(fd, 200, "OK", b.data, false);
roots_cleanup:
if (!ok) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error");
}
amduatd_space_roots_list_free(&roots);
amduatd_strbuf_free(&b);
return ok;
}
static bool amduatd_sync_status_append_cursor(
amduatd_strbuf_t *b,
amduat_asl_store_t *store,
amduat_asl_pointer_store_t *pointer_store,
const amduatd_space_t *effective_space,
const char *peer_key,
bool push) {
amduatd_fed_cursor_record_t cursor;
amduat_reference_t ref;
amduatd_fed_cursor_status_t status;
bool present = false;
const char *error = NULL;
char *ref_hex = NULL;
amduatd_fed_cursor_record_init(&cursor);
ref = amduat_reference(0u, amduat_octets(NULL, 0u));
status = push
? amduatd_fed_push_cursor_get(store,
pointer_store,
effective_space,
peer_key,
&cursor,
&ref)
: amduatd_fed_cursor_get(store,
pointer_store,
effective_space,
peer_key,
&cursor,
&ref);
if (status == AMDUATD_FED_CURSOR_OK) {
present = true;
} else if (status == AMDUATD_FED_CURSOR_ERR_NOT_FOUND) {
present = false;
} else if (status == AMDUATD_FED_CURSOR_ERR_CODEC) {
present = false;
error = "codec";
} else {
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&ref);
return false;
}
if (present && cursor.has_record_ref) {
if (!amduat_asl_ref_encode_hex(cursor.last_record_ref, &ref_hex)) {
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&ref);
return false;
}
}
if (!amduatd_strbuf_append_cstr(b, "{\"present\":") ||
!amduatd_strbuf_append_cstr(b, present ? "true" : "false")) {
free(ref_hex);
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&ref);
return false;
}
if (error != NULL) {
if (!amduatd_strbuf_append_cstr(b, ",\"error\":\"") ||
!amduatd_strbuf_append_cstr(b, error) ||
!amduatd_strbuf_append_cstr(b, "\"")) {
free(ref_hex);
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&ref);
return false;
}
}
if (present && cursor.has_logseq) {
char tmp[32];
int n = snprintf(tmp, sizeof(tmp), "%llu",
(unsigned long long)cursor.last_logseq);
if (n <= 0 || (size_t)n >= sizeof(tmp)) {
free(ref_hex);
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&ref);
return false;
}
if (!amduatd_strbuf_append_cstr(b, ",\"last_logseq\":") ||
!amduatd_strbuf_append_cstr(b, tmp)) {
free(ref_hex);
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&ref);
return false;
}
}
if (present && ref_hex != NULL) {
if (!amduatd_strbuf_append_cstr(b, ",\"ref\":\"") ||
!amduatd_strbuf_append_cstr(b, ref_hex) ||
!amduatd_strbuf_append_cstr(b, "\"")) {
free(ref_hex);
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&ref);
return false;
}
}
if (!amduatd_strbuf_append_cstr(b, "}")) {
free(ref_hex);
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&ref);
return false;
}
free(ref_hex);
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&ref);
return true;
}
static bool amduatd_handle_get_space_sync_status(
int fd,
amduat_asl_store_t *store,
const amduatd_http_req_t *req,
const amduatd_cfg_t *dcfg,
const amduatd_fed_cfg_t *fed_cfg,
const amduatd_caps_t *caps,
const char *root_path,
amduatd_store_backend_t store_backend) {
amduat_asl_pointer_store_t pointer_store;
amduatd_space_roots_list_t peers;
amduatd_strbuf_t b;
bool ok = false;
memset(&peers, 0, sizeof(peers));
memset(&b, 0, sizeof(b));
if (store == NULL || req == NULL || dcfg == NULL || fed_cfg == NULL ||
root_path == NULL) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"internal error");
}
if (caps != NULL && caps->enabled && req->x_capability[0] != '\0') {
const char *reason = NULL;
if (!amduatd_caps_check_space(caps, dcfg, req, &reason)) {
if (reason != NULL && strcmp(reason, "wrong-space") == 0) {
return amduatd_send_json_error(fd, 403, "Forbidden",
"space not permitted by capability");
}
return amduatd_send_json_error(fd, 403, "Forbidden",
"invalid capability");
}
}
if (!amduat_asl_pointer_store_init(&pointer_store, root_path)) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"pointer store error");
}
if (!amduatd_space_roots_list_cursor_peers(root_path,
req->effective_space,
&peers)) {
return amduatd_send_json_error(fd, 500, "Internal Server Error",
"cursor scan failed");
}
if (!amduatd_strbuf_append_cstr(&b, "{\"effective_space\":{")) {
goto sync_cleanup;
}
if (req->effective_space != NULL && req->effective_space->enabled &&
req->effective_space->space_id.data != NULL) {
const char *space_id = (const char *)req->effective_space->space_id.data;
if (!amduatd_strbuf_append_cstr(&b, "\"mode\":\"scoped\",") ||
!amduatd_strbuf_append_cstr(&b, "\"space_id\":\"") ||
!amduatd_strbuf_append_cstr(&b, space_id) ||
!amduatd_strbuf_append_cstr(&b, "\"")) {
goto sync_cleanup;
}
} else {
if (!amduatd_strbuf_append_cstr(&b, "\"mode\":\"unscoped\",") ||
!amduatd_strbuf_append_cstr(&b, "\"space_id\":null")) {
goto sync_cleanup;
}
}
if (!amduatd_strbuf_append_cstr(&b, "},\"store_backend\":\"") ||
!amduatd_strbuf_append_cstr(&b, amduatd_store_backend_name(store_backend)) ||
!amduatd_strbuf_append_cstr(&b, "\",\"federation\":{")) {
goto sync_cleanup;
}
if (!amduatd_strbuf_append_cstr(&b, "\"enabled\":") ||
!amduatd_strbuf_append_cstr(&b, fed_cfg->enabled ? "true" : "false") ||
!amduatd_strbuf_append_cstr(&b, ",\"transport\":\"") ||
!amduatd_strbuf_append_cstr(&b, amduatd_fed_transport_name(
fed_cfg->transport_kind)) ||
!amduatd_strbuf_append_cstr(&b, "\"},\"peers\":[")) {
goto sync_cleanup;
}
for (size_t i = 0u; i < peers.len; ++i) {
const char *peer_key = peers.names[i];
if (i != 0u) {
if (!amduatd_strbuf_append_char(&b, ',')) {
goto sync_cleanup;
}
}
if (!amduatd_strbuf_append_cstr(&b, "{\"peer_key\":\"") ||
!amduatd_strbuf_append_cstr(&b, peer_key) ||
!amduatd_strbuf_append_cstr(&b, "\",\"pull_cursor\":")) {
goto sync_cleanup;
}
if (!amduatd_sync_status_append_cursor(&b,
store,
&pointer_store,
req->effective_space,
peer_key,
false)) {
goto sync_cleanup;
}
if (!amduatd_strbuf_append_cstr(&b, ",\"push_cursor\":")) {
goto sync_cleanup;
}
if (!amduatd_sync_status_append_cursor(&b,
store,
&pointer_store,
req->effective_space,
peer_key,
true)) {
goto sync_cleanup;
}
if (!amduatd_strbuf_append_cstr(&b, "}")) {
goto sync_cleanup;
}
}
if (!amduatd_strbuf_append_cstr(&b, "]}\n")) {
goto sync_cleanup;
}
ok = amduatd_http_send_json(fd, 200, "OK", b.data, false);
sync_cleanup:
if (!ok) {
ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error");
}
amduatd_space_roots_list_free(&peers);
amduatd_strbuf_free(&b);
return ok;
}
static bool amduatd_seed_api_contract(amduat_asl_store_t *store,
const amduat_asl_store_fs_config_t *cfg,
amduat_reference_t *out_ref) {
@ -6512,7 +6889,8 @@ static bool amduatd_handle_conn(int fd,
const amduat_fed_coord_t *coord,
const amduatd_allowlist_t *allowlist,
amduatd_caps_t *caps,
const char *root_path) {
const char *root_path,
amduatd_store_backend_t store_backend) {
amduatd_http_req_t req;
char no_query[1024];
bool ok = false;
@ -6619,6 +6997,27 @@ static bool amduatd_handle_conn(int fd,
root_path);
goto conn_cleanup;
}
if (strcmp(req.method, "GET") == 0 &&
strcmp(no_query, "/v1/space/roots") == 0) {
ok = amduatd_handle_get_space_roots(fd,
&req,
effective_cfg,
caps,
root_path);
goto conn_cleanup;
}
if (strcmp(req.method, "GET") == 0 &&
strcmp(no_query, "/v1/space/sync/status") == 0) {
ok = amduatd_handle_get_space_sync_status(fd,
store,
&req,
effective_cfg,
fed_cfg,
caps,
root_path,
store_backend);
goto conn_cleanup;
}
if (strcmp(req.method, "GET") == 0 &&
strcmp(no_query, "/v1/fed/records") == 0) {
ok = amduatd_handle_get_fed_records(fd, store, fed_cfg, &req, root_path);
@ -7171,7 +7570,8 @@ int main(int argc, char **argv) {
fed_coord,
&allowlist,
&caps,
root);
root,
store_backend);
(void)close(cfd);
if (dcfg.edges_refresh_ms != 0u) {

603
src/amduatd_space_roots.c Normal file
View file

@ -0,0 +1,603 @@
#include "amduatd_space_roots.h"
#include "amduat/asl/asl_pointer_fs.h"
#include <dirent.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static bool amduatd_space_roots_join_path(const char *base,
const char *segment,
char **out_path) {
size_t base_len;
size_t seg_len;
bool needs_sep;
size_t total_len;
char *buffer;
size_t offset = 0u;
if (base == NULL || segment == NULL || out_path == NULL) {
return false;
}
if (base[0] == '\0') {
return false;
}
base_len = strlen(base);
seg_len = strlen(segment);
needs_sep = base[base_len - 1u] != '/';
if (seg_len > SIZE_MAX - base_len - 2u) {
return false;
}
total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u;
buffer = (char *)malloc(total_len);
if (buffer == NULL) {
return false;
}
memcpy(buffer + offset, base, base_len);
offset += base_len;
if (needs_sep) {
buffer[offset++] = '/';
}
if (seg_len != 0u) {
memcpy(buffer + offset, segment, seg_len);
offset += seg_len;
}
buffer[offset] = '\0';
*out_path = buffer;
return true;
}
static bool amduatd_space_roots_segment_valid(const char *segment) {
size_t len;
size_t i;
if (segment == NULL) {
return false;
}
len = strlen(segment);
if (len == 0u) {
return false;
}
if (len == 1u && segment[0] == '.') {
return false;
}
if (len == 2u && segment[0] == '.' && segment[1] == '.') {
return false;
}
for (i = 0u; i < len; ++i) {
unsigned char c = (unsigned char)segment[i];
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-') {
continue;
}
return false;
}
return true;
}
static bool amduatd_space_roots_list_reserve(
amduatd_space_roots_list_t *list,
size_t extra) {
size_t need;
size_t next_cap;
char **next;
if (list == NULL) {
return false;
}
if (extra > (SIZE_MAX - list->len)) {
return false;
}
need = list->len + extra;
if (need <= list->cap) {
return true;
}
next_cap = list->cap != 0u ? list->cap : 8u;
while (next_cap < need) {
if (next_cap > (SIZE_MAX / 2u)) {
next_cap = need;
break;
}
next_cap *= 2u;
}
next = (char **)realloc(list->names, next_cap * sizeof(*next));
if (next == NULL) {
return false;
}
list->names = next;
list->cap = next_cap;
return true;
}
static bool amduatd_space_roots_list_add(amduatd_space_roots_list_t *list,
const char *name) {
size_t len;
char *copy;
if (list == NULL || name == NULL) {
return false;
}
if (!amduat_asl_pointer_name_is_valid(name)) {
return false;
}
len = strlen(name);
if (len > SIZE_MAX - 1u) {
return false;
}
if (!amduatd_space_roots_list_reserve(list, 1u)) {
return false;
}
copy = (char *)malloc(len + 1u);
if (copy == NULL) {
return false;
}
memcpy(copy, name, len);
copy[len] = '\0';
list->names[list->len++] = copy;
return true;
}
static int amduatd_space_roots_list_cmp(const void *a, const void *b) {
const char *const *lhs = (const char *const *)a;
const char *const *rhs = (const char *const *)b;
if (lhs == NULL || rhs == NULL || *lhs == NULL || *rhs == NULL) {
return 0;
}
return strcmp(*lhs, *rhs);
}
static void amduatd_space_roots_list_sort_dedupe(
amduatd_space_roots_list_t *list) {
size_t out = 0u;
if (list == NULL || list->len == 0u) {
return;
}
qsort(list->names, list->len, sizeof(*list->names),
amduatd_space_roots_list_cmp);
for (size_t i = 0u; i < list->len; ++i) {
if (out != 0u && strcmp(list->names[i], list->names[out - 1u]) == 0) {
free(list->names[i]);
continue;
}
list->names[out++] = list->names[i];
}
list->len = out;
}
static bool amduatd_space_roots_collect_dir(
const char *dir_path,
const char *rel_name,
amduatd_space_roots_list_t *list) {
DIR *dir;
struct dirent *entry;
if (dir_path == NULL || rel_name == NULL || list == NULL) {
return false;
}
dir = opendir(dir_path);
if (dir == NULL) {
if (errno == ENOENT) {
return true;
}
return false;
}
while ((entry = readdir(dir)) != NULL) {
const char *name = entry->d_name;
char *child_path = NULL;
struct stat st;
bool is_dir = false;
bool is_file = false;
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
continue;
}
if (!amduatd_space_roots_segment_valid(name)) {
continue;
}
if (!amduatd_space_roots_join_path(dir_path, name, &child_path)) {
closedir(dir);
return false;
}
if (stat(child_path, &st) != 0) {
free(child_path);
closedir(dir);
return false;
}
is_dir = S_ISDIR(st.st_mode);
is_file = S_ISREG(st.st_mode);
if (is_dir) {
char *next_rel = NULL;
size_t rel_len = strlen(rel_name);
size_t name_len = strlen(name);
size_t total_len = rel_len + 1u + name_len + 1u;
if (rel_len == 0u) {
total_len = name_len + 1u;
}
next_rel = (char *)malloc(total_len);
if (next_rel == NULL) {
free(child_path);
closedir(dir);
return false;
}
if (rel_len != 0u) {
memcpy(next_rel, rel_name, rel_len);
next_rel[rel_len] = '/';
memcpy(next_rel + rel_len + 1u, name, name_len);
next_rel[rel_len + 1u + name_len] = '\0';
} else {
memcpy(next_rel, name, name_len);
next_rel[name_len] = '\0';
}
if (!amduatd_space_roots_collect_dir(child_path, next_rel, list)) {
free(next_rel);
free(child_path);
closedir(dir);
return false;
}
free(next_rel);
} else if (is_file && strcmp(name, "head") == 0) {
if (rel_name[0] != '\0') {
if (!amduatd_space_roots_list_add(list, rel_name)) {
free(child_path);
closedir(dir);
return false;
}
}
}
free(child_path);
}
closedir(dir);
return true;
}
static bool amduatd_space_roots_collect_cursor_prefix(
const char *pointers_root,
const char *prefix_name,
amduatd_space_roots_list_t *list) {
char *prefix_path = NULL;
bool ok = false;
if (pointers_root == NULL || prefix_name == NULL || list == NULL) {
return false;
}
if (!amduat_asl_pointer_name_is_valid(prefix_name)) {
return false;
}
if (!amduatd_space_roots_join_path(pointers_root, prefix_name,
&prefix_path)) {
return false;
}
ok = amduatd_space_roots_collect_dir(prefix_path, prefix_name, list);
free(prefix_path);
return ok;
}
static bool amduatd_space_roots_append_cursor_heads(
const char *store_root,
const amduatd_space_t *effective_space,
bool push,
amduatd_space_roots_list_t *list) {
char *pointers_root = NULL;
amduat_octets_t prefix = amduat_octets(NULL, 0u);
bool ok = false;
if (store_root == NULL || list == NULL) {
return false;
}
if (!amduatd_space_scope_name(effective_space,
push ? "fed/push_cursor" : "fed/cursor",
&prefix)) {
return false;
}
if (!amduatd_space_roots_join_path(store_root, "pointers",
&pointers_root)) {
amduat_octets_free(&prefix);
return false;
}
ok = amduatd_space_roots_collect_cursor_prefix(
pointers_root, (const char *)prefix.data, list);
free(pointers_root);
amduat_octets_free(&prefix);
return ok;
}
static bool amduatd_space_roots_cursor_peer_from_name(
const char *prefix,
const char *pointer_name,
char **out_peer) {
const char suffix[] = "/head";
size_t prefix_len;
size_t name_len;
size_t suffix_len = sizeof(suffix) - 1u;
size_t peer_len;
char *peer;
if (prefix == NULL || pointer_name == NULL || out_peer == NULL) {
return false;
}
*out_peer = NULL;
prefix_len = strlen(prefix);
name_len = strlen(pointer_name);
if (name_len <= prefix_len + 1u + suffix_len) {
return false;
}
if (strncmp(pointer_name, prefix, prefix_len) != 0) {
return false;
}
if (pointer_name[prefix_len] != '/') {
return false;
}
if (strcmp(pointer_name + name_len - suffix_len, suffix) != 0) {
return false;
}
peer_len = name_len - prefix_len - 1u - suffix_len;
if (peer_len == 0u) {
return false;
}
peer = (char *)malloc(peer_len + 1u);
if (peer == NULL) {
return false;
}
memcpy(peer, pointer_name + prefix_len + 1u, peer_len);
peer[peer_len] = '\0';
*out_peer = peer;
return true;
}
static bool amduatd_space_roots_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_roots_build_collection_log_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 = 4u + 11u + name_len + 4u + 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;
}
bool amduatd_space_roots_list(
const char *store_root,
const amduat_asl_pointer_store_t *pointer_store,
const amduatd_space_t *effective_space,
amduatd_space_roots_list_t *out_list) {
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 ok = false;
if (out_list != NULL) {
memset(out_list, 0, sizeof(*out_list));
}
if (store_root == NULL || pointer_store == NULL || out_list == NULL) {
return false;
}
(void)pointer_store;
if (!amduatd_space_edges_collection_name(effective_space,
&edges_collection) ||
!amduatd_space_edges_index_head_name(effective_space,
&edges_index_head)) {
goto cleanup;
}
if (!amduatd_space_roots_build_collection_head_name(
(const char *)edges_collection.data, &collection_head) ||
!amduatd_space_roots_build_collection_log_head_name(
(const char *)edges_collection.data, &collection_log_head)) {
goto cleanup;
}
if (!amduatd_space_roots_list_add(out_list,
(const char *)edges_index_head.data) ||
!amduatd_space_roots_list_add(out_list, collection_head) ||
!amduatd_space_roots_list_add(out_list, collection_log_head)) {
goto cleanup;
}
if (!amduatd_space_roots_append_cursor_heads(store_root,
effective_space,
false,
out_list) ||
!amduatd_space_roots_append_cursor_heads(store_root,
effective_space,
true,
out_list)) {
goto cleanup;
}
amduatd_space_roots_list_sort_dedupe(out_list);
ok = true;
cleanup:
free((void *)edges_collection.data);
free((void *)edges_index_head.data);
free(collection_head);
free(collection_log_head);
if (!ok) {
amduatd_space_roots_list_free(out_list);
}
return ok;
}
bool amduatd_space_roots_list_cursor_heads(
const char *store_root,
const amduatd_space_t *effective_space,
bool push,
amduatd_space_roots_list_t *out_list) {
bool ok = false;
if (out_list != NULL) {
memset(out_list, 0, sizeof(*out_list));
}
if (store_root == NULL || out_list == NULL) {
return false;
}
if (!amduatd_space_roots_append_cursor_heads(store_root,
effective_space,
push,
out_list)) {
goto cleanup;
}
amduatd_space_roots_list_sort_dedupe(out_list);
ok = true;
cleanup:
if (!ok) {
amduatd_space_roots_list_free(out_list);
}
return ok;
}
bool amduatd_space_roots_list_cursor_peers(
const char *store_root,
const amduatd_space_t *effective_space,
amduatd_space_roots_list_t *out_list) {
amduatd_space_roots_list_t pull_heads;
amduatd_space_roots_list_t push_heads;
amduat_octets_t pull_prefix = amduat_octets(NULL, 0u);
amduat_octets_t push_prefix = amduat_octets(NULL, 0u);
bool ok = false;
memset(&pull_heads, 0, sizeof(pull_heads));
memset(&push_heads, 0, sizeof(push_heads));
if (out_list != NULL) {
memset(out_list, 0, sizeof(*out_list));
}
if (store_root == NULL || out_list == NULL) {
return false;
}
if (!amduatd_space_scope_name(effective_space,
"fed/cursor",
&pull_prefix) ||
!amduatd_space_scope_name(effective_space,
"fed/push_cursor",
&push_prefix)) {
goto cleanup;
}
if (!amduatd_space_roots_list_cursor_heads(store_root,
effective_space,
false,
&pull_heads) ||
!amduatd_space_roots_list_cursor_heads(store_root,
effective_space,
true,
&push_heads)) {
goto cleanup;
}
for (size_t i = 0u; i < pull_heads.len; ++i) {
char *peer = NULL;
if (amduatd_space_roots_cursor_peer_from_name(
(const char *)pull_prefix.data,
pull_heads.names[i],
&peer)) {
if (!amduatd_space_roots_list_add(out_list, peer)) {
free(peer);
goto cleanup;
}
free(peer);
}
}
for (size_t i = 0u; i < push_heads.len; ++i) {
char *peer = NULL;
if (amduatd_space_roots_cursor_peer_from_name(
(const char *)push_prefix.data,
push_heads.names[i],
&peer)) {
if (!amduatd_space_roots_list_add(out_list, peer)) {
free(peer);
goto cleanup;
}
free(peer);
}
}
amduatd_space_roots_list_sort_dedupe(out_list);
ok = true;
cleanup:
amduatd_space_roots_list_free(&pull_heads);
amduatd_space_roots_list_free(&push_heads);
amduat_octets_free(&pull_prefix);
amduat_octets_free(&push_prefix);
if (!ok) {
amduatd_space_roots_list_free(out_list);
}
return ok;
}
void amduatd_space_roots_list_free(amduatd_space_roots_list_t *list) {
if (list == NULL) {
return;
}
for (size_t i = 0u; i < list->len; ++i) {
free(list->names[i]);
}
free(list->names);
memset(list, 0, sizeof(*list));
}

44
src/amduatd_space_roots.h Normal file
View file

@ -0,0 +1,44 @@
#ifndef AMDUATD_SPACE_ROOTS_H
#define AMDUATD_SPACE_ROOTS_H
#include "amduatd_space.h"
#include "amduat/asl/asl_pointer_fs.h"
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
char **names;
size_t len;
size_t cap;
} amduatd_space_roots_list_t;
bool amduatd_space_roots_list(
const char *store_root,
const amduat_asl_pointer_store_t *pointer_store,
const amduatd_space_t *effective_space,
amduatd_space_roots_list_t *out_list);
bool amduatd_space_roots_list_cursor_heads(
const char *store_root,
const amduatd_space_t *effective_space,
bool push,
amduatd_space_roots_list_t *out_list);
bool amduatd_space_roots_list_cursor_peers(
const char *store_root,
const amduatd_space_t *effective_space,
amduatd_space_roots_list_t *out_list);
void amduatd_space_roots_list_free(amduatd_space_roots_list_t *list);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* AMDUATD_SPACE_ROOTS_H */

View file

@ -0,0 +1,325 @@
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#include "amduatd_space_roots.h"
#include "amduatd_fed_cursor.h"
#include "amduatd_space.h"
#include "amduatd_store.h"
#include "amduat/asl/asl_store_fs_meta.h"
#include "amduat/asl/asl_pointer_fs.h"
#include "amduat/asl/none.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char *amduatd_test_make_temp_dir(void) {
char tmpl[] = "/tmp/amduatd-roots-XXXXXX";
char *dir = mkdtemp(tmpl);
size_t len;
char *copy;
if (dir == NULL) {
perror("mkdtemp");
return NULL;
}
len = strlen(dir);
copy = (char *)malloc(len + 1u);
if (copy == NULL) {
fprintf(stderr, "failed to allocate temp dir copy\n");
return NULL;
}
memcpy(copy, dir, len + 1u);
return copy;
}
static bool amduatd_list_contains(const amduatd_space_roots_list_t *list,
const char *name) {
if (list == NULL || name == NULL) {
return false;
}
for (size_t i = 0u; i < list->len; ++i) {
if (strcmp(list->names[i], name) == 0) {
return true;
}
}
return false;
}
static bool amduatd_list_sorted(const amduatd_space_roots_list_t *list) {
if (list == NULL || list->len < 2u) {
return true;
}
for (size_t i = 1u; i < list->len; ++i) {
if (strcmp(list->names[i - 1u], list->names[i]) > 0) {
return false;
}
}
return true;
}
static bool amduatd_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_build_collection_log_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 = 4u + 11u + name_len + 4u + 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;
}
static int amduatd_test_empty_store(void) {
char *root = amduatd_test_make_temp_dir();
amduat_asl_store_fs_config_t cfg;
amduat_asl_pointer_store_t pointer_store;
amduatd_space_t space;
amduatd_space_roots_list_t list;
if (root == NULL) {
return 1;
}
memset(&cfg, 0, sizeof(cfg));
if (!amduat_asl_store_fs_init_root(root, NULL, &cfg)) {
fprintf(stderr, "failed to init store root\n");
free(root);
return 1;
}
if (!amduat_asl_pointer_store_init(&pointer_store, root)) {
fprintf(stderr, "failed to init pointer store\n");
free(root);
return 1;
}
if (!amduatd_space_init(&space, NULL, false)) {
fprintf(stderr, "failed to init space\n");
free(root);
return 1;
}
if (!amduatd_space_roots_list(root, &pointer_store, &space, &list)) {
fprintf(stderr, "roots list failed\n");
free(root);
return 1;
}
if (list.len != 3u ||
!amduatd_list_contains(&list, "daemon/edges/index/head") ||
!amduatd_list_contains(&list, "collection/daemon/edges/head") ||
!amduatd_list_contains(&list, "log/collection/daemon/edges/log/head") ||
!amduatd_list_sorted(&list)) {
fprintf(stderr, "unexpected empty-store roots list\n");
amduatd_space_roots_list_free(&list);
free(root);
return 1;
}
amduatd_space_roots_list_free(&list);
free(root);
return 0;
}
static int amduatd_test_cursor_roots(void) {
char *root = amduatd_test_make_temp_dir();
amduat_asl_store_fs_config_t cfg;
amduatd_store_ctx_t store_ctx;
amduat_asl_store_t store;
amduat_asl_pointer_store_t pointer_store;
amduatd_space_t space;
amduat_artifact_t none_artifact;
amduat_reference_t none_ref;
amduat_octets_t cursor_name = amduat_octets(NULL, 0u);
amduat_octets_t push_name = amduat_octets(NULL, 0u);
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;
amduatd_space_roots_list_t list;
bool swapped = false;
if (root == NULL) {
return 1;
}
memset(&cfg, 0, sizeof(cfg));
if (!amduat_asl_store_fs_init_root(root, NULL, &cfg)) {
fprintf(stderr, "failed to init store root\n");
free(root);
return 1;
}
memset(&store_ctx, 0, sizeof(store_ctx));
memset(&store, 0, sizeof(store));
if (!amduatd_store_init(&store,
&cfg,
&store_ctx,
root,
AMDUATD_STORE_BACKEND_FS)) {
fprintf(stderr, "failed to init store\n");
free(root);
return 1;
}
if (!amduat_asl_pointer_store_init(&pointer_store, root)) {
fprintf(stderr, "failed to init pointer store\n");
free(root);
return 1;
}
if (!amduatd_space_init(&space, "alpha", false)) {
fprintf(stderr, "failed to init space\n");
free(root);
return 1;
}
if (!amduat_asl_none_artifact(&none_artifact)) {
fprintf(stderr, "failed to build none artifact\n");
free(root);
return 1;
}
if (amduat_asl_store_put(&store, none_artifact, &none_ref) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "failed to store none artifact\n");
amduat_artifact_free(&none_artifact);
free(root);
return 1;
}
amduat_artifact_free(&none_artifact);
if (!amduatd_fed_cursor_pointer_name(&space, "peer-a", &cursor_name) ||
!amduatd_fed_push_cursor_pointer_name(&space, "peer-b", &push_name)) {
fprintf(stderr, "failed to build cursor names\n");
free(root);
return 1;
}
if (amduat_asl_pointer_cas(&pointer_store,
(const char *)cursor_name.data,
false,
NULL,
&none_ref,
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped ||
amduat_asl_pointer_cas(&pointer_store,
(const char *)push_name.data,
false,
NULL,
&none_ref,
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped) {
fprintf(stderr, "failed to seed cursor pointers\n");
amduat_octets_free(&cursor_name);
amduat_octets_free(&push_name);
free(root);
return 1;
}
if (!amduatd_space_roots_list(root, &pointer_store, &space, &list)) {
fprintf(stderr, "roots list failed\n");
amduat_octets_free(&cursor_name);
amduat_octets_free(&push_name);
free(root);
return 1;
}
if (!amduatd_space_edges_collection_name(&space, &edges_collection) ||
!amduatd_space_edges_index_head_name(&space, &edges_index_head) ||
!amduatd_build_collection_head_name(
(const char *)edges_collection.data, &collection_head) ||
!amduatd_build_collection_log_head_name(
(const char *)edges_collection.data, &collection_log_head)) {
fprintf(stderr, "failed to build static root names\n");
amduat_octets_free(&cursor_name);
amduat_octets_free(&push_name);
amduatd_space_roots_list_free(&list);
free(root);
return 1;
}
if (list.len != 5u ||
!amduatd_list_contains(&list, (const char *)cursor_name.data) ||
!amduatd_list_contains(&list, (const char *)push_name.data) ||
!amduatd_list_contains(&list, (const char *)edges_index_head.data) ||
!amduatd_list_contains(&list, collection_head) ||
!amduatd_list_contains(&list, collection_log_head) ||
!amduatd_list_sorted(&list)) {
fprintf(stderr, "unexpected cursor roots list\n");
amduat_octets_free(&cursor_name);
amduat_octets_free(&push_name);
amduat_octets_free(&edges_collection);
amduat_octets_free(&edges_index_head);
free(collection_head);
free(collection_log_head);
amduatd_space_roots_list_free(&list);
free(root);
return 1;
}
amduat_octets_free(&cursor_name);
amduat_octets_free(&push_name);
amduat_octets_free(&edges_collection);
amduat_octets_free(&edges_index_head);
free(collection_head);
free(collection_log_head);
amduatd_space_roots_list_free(&list);
free(root);
return 0;
}
int main(void) {
if (amduatd_test_empty_store() != 0) {
return 1;
}
if (amduatd_test_cursor_roots() != 0) {
return 1;
}
return 0;
}

View file

@ -0,0 +1,228 @@
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#include "amduatd_space_roots.h"
#include "amduatd_fed_cursor.h"
#include "amduatd_space.h"
#include "amduatd_store.h"
#include "amduat/asl/asl_pointer_fs.h"
#include "amduat/asl/asl_store_fs_meta.h"
#include "amduat/asl/none.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char *amduatd_test_make_temp_dir(void) {
char tmpl[] = "/tmp/amduatd-sync-XXXXXX";
char *dir = mkdtemp(tmpl);
size_t len;
char *copy;
if (dir == NULL) {
perror("mkdtemp");
return NULL;
}
len = strlen(dir);
copy = (char *)malloc(len + 1u);
if (copy == NULL) {
fprintf(stderr, "failed to allocate temp dir copy\n");
return NULL;
}
memcpy(copy, dir, len + 1u);
return copy;
}
static bool amduatd_list_contains(const amduatd_space_roots_list_t *list,
const char *name) {
if (list == NULL || name == NULL) {
return false;
}
for (size_t i = 0u; i < list->len; ++i) {
if (strcmp(list->names[i], name) == 0) {
return true;
}
}
return false;
}
static bool amduatd_list_sorted(const amduatd_space_roots_list_t *list) {
if (list == NULL || list->len < 2u) {
return true;
}
for (size_t i = 1u; i < list->len; ++i) {
if (strcmp(list->names[i - 1u], list->names[i]) > 0) {
return false;
}
}
return true;
}
static int amduatd_test_empty_peers(void) {
char *root = amduatd_test_make_temp_dir();
amduat_asl_store_fs_config_t cfg;
amduat_asl_pointer_store_t pointer_store;
amduatd_space_t space;
amduatd_space_roots_list_t peers;
if (root == NULL) {
return 1;
}
memset(&cfg, 0, sizeof(cfg));
if (!amduat_asl_store_fs_init_root(root, NULL, &cfg)) {
fprintf(stderr, "failed to init store root\n");
free(root);
return 1;
}
if (!amduat_asl_pointer_store_init(&pointer_store, root)) {
fprintf(stderr, "failed to init pointer store\n");
free(root);
return 1;
}
if (!amduatd_space_init(&space, "alpha", false)) {
fprintf(stderr, "failed to init space\n");
free(root);
return 1;
}
if (!amduatd_space_roots_list_cursor_peers(root, &space, &peers)) {
fprintf(stderr, "cursor peer list failed\n");
free(root);
return 1;
}
if (peers.len != 0u) {
fprintf(stderr, "expected empty peers list\n");
amduatd_space_roots_list_free(&peers);
free(root);
return 1;
}
amduatd_space_roots_list_free(&peers);
free(root);
return 0;
}
static int amduatd_test_peer_discovery(void) {
char *root = amduatd_test_make_temp_dir();
amduat_asl_store_fs_config_t cfg;
amduatd_store_ctx_t store_ctx;
amduat_asl_store_t store;
amduat_asl_pointer_store_t pointer_store;
amduatd_space_t space;
amduat_artifact_t none_artifact;
amduat_reference_t none_ref;
amduat_octets_t pull_name = amduat_octets(NULL, 0u);
amduat_octets_t push_name = amduat_octets(NULL, 0u);
amduatd_space_roots_list_t peers;
bool swapped = false;
if (root == NULL) {
return 1;
}
memset(&cfg, 0, sizeof(cfg));
if (!amduat_asl_store_fs_init_root(root, NULL, &cfg)) {
fprintf(stderr, "failed to init store root\n");
free(root);
return 1;
}
memset(&store_ctx, 0, sizeof(store_ctx));
memset(&store, 0, sizeof(store));
if (!amduatd_store_init(&store,
&cfg,
&store_ctx,
root,
AMDUATD_STORE_BACKEND_FS)) {
fprintf(stderr, "failed to init store\n");
free(root);
return 1;
}
if (!amduat_asl_pointer_store_init(&pointer_store, root)) {
fprintf(stderr, "failed to init pointer store\n");
free(root);
return 1;
}
if (!amduatd_space_init(&space, "alpha", false)) {
fprintf(stderr, "failed to init space\n");
free(root);
return 1;
}
if (!amduat_asl_none_artifact(&none_artifact)) {
fprintf(stderr, "failed to build none artifact\n");
free(root);
return 1;
}
if (amduat_asl_store_put(&store, none_artifact, &none_ref) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "failed to store none artifact\n");
amduat_artifact_free(&none_artifact);
free(root);
return 1;
}
amduat_artifact_free(&none_artifact);
if (!amduatd_fed_cursor_pointer_name(&space, "1", &pull_name) ||
!amduatd_fed_push_cursor_pointer_name(&space, "2", &push_name)) {
fprintf(stderr, "failed to build cursor names\n");
free(root);
return 1;
}
if (amduat_asl_pointer_cas(&pointer_store,
(const char *)pull_name.data,
false,
NULL,
&none_ref,
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped ||
amduat_asl_pointer_cas(&pointer_store,
(const char *)push_name.data,
false,
NULL,
&none_ref,
&swapped) != AMDUAT_ASL_POINTER_OK || !swapped) {
fprintf(stderr, "failed to seed cursor pointers\n");
amduat_octets_free(&pull_name);
amduat_octets_free(&push_name);
free(root);
return 1;
}
if (!amduatd_space_roots_list_cursor_peers(root, &space, &peers)) {
fprintf(stderr, "cursor peer list failed\n");
amduat_octets_free(&pull_name);
amduat_octets_free(&push_name);
free(root);
return 1;
}
if (peers.len != 2u ||
!amduatd_list_contains(&peers, "1") ||
!amduatd_list_contains(&peers, "2") ||
!amduatd_list_sorted(&peers)) {
fprintf(stderr, "unexpected peers list\n");
amduatd_space_roots_list_free(&peers);
amduat_octets_free(&pull_name);
amduat_octets_free(&push_name);
free(root);
return 1;
}
amduatd_space_roots_list_free(&peers);
amduat_octets_free(&pull_name);
amduat_octets_free(&push_name);
free(root);
return 0;
}
int main(void) {
if (amduatd_test_empty_peers() != 0) {
return 1;
}
if (amduatd_test_peer_discovery() != 0) {
return 1;
}
return 0;
}