amduatd: add opt-in federation config, space scoping, and tests
This commit is contained in:
parent
db3c4b4c93
commit
af11665a35
|
|
@ -26,7 +26,8 @@ target_link_libraries(amduat_federation
|
|||
|
||||
set(amduatd_sources src/amduatd.c src/amduatd_http.c src/amduatd_caps.c
|
||||
src/amduatd_space.c src/amduatd_concepts.c
|
||||
src/amduatd_store.c src/amduatd_derivation_index.c)
|
||||
src/amduatd_store.c src/amduatd_derivation_index.c
|
||||
src/amduatd_fed.c)
|
||||
if(AMDUATD_ENABLE_UI)
|
||||
list(APPEND amduatd_sources src/amduatd_ui.c)
|
||||
endif()
|
||||
|
|
@ -117,3 +118,21 @@ target_link_libraries(amduatd_test_derivation_index
|
|||
)
|
||||
|
||||
add_test(NAME amduatd_derivation_index COMMAND amduatd_test_derivation_index)
|
||||
|
||||
add_executable(amduatd_test_fed_cfg
|
||||
tests/test_amduatd_fed_cfg.c
|
||||
src/amduatd_fed.c
|
||||
src/amduatd_space.c
|
||||
)
|
||||
|
||||
target_include_directories(amduatd_test_fed_cfg
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/vendor/amduat/include
|
||||
)
|
||||
|
||||
target_link_libraries(amduatd_test_fed_cfg
|
||||
PRIVATE amduat_asl amduat_enc amduat_util amduat_asl_pointer_fs
|
||||
)
|
||||
|
||||
add_test(NAME amduatd_fed_cfg COMMAND amduatd_test_fed_cfg)
|
||||
|
|
|
|||
24
README.md
24
README.md
|
|
@ -47,6 +47,30 @@ Run the daemon with the index-backed store:
|
|||
|
||||
Note: `/v1/fed/records` requires the index backend.
|
||||
|
||||
## Federation (dev)
|
||||
|
||||
Federation is opt-in and disabled by default. Enabling federation requires the
|
||||
index-backed store and explicit flags:
|
||||
|
||||
```sh
|
||||
./build/amduatd --root .amduat-asl --sock amduatd.sock --store-backend index \
|
||||
--fed-enable --fed-transport unix --fed-unix-sock peer.sock \
|
||||
--fed-domain-id 1 --fed-registry-ref <registry_ref_hex>
|
||||
```
|
||||
|
||||
Flags:
|
||||
|
||||
- `--fed-enable` turns on the coordinator tick loop.
|
||||
- `--fed-transport stub|unix` selects transport (`stub` by default).
|
||||
- `--fed-unix-sock PATH` configures the unix transport socket path.
|
||||
- `--fed-domain-id ID` sets the local domain id.
|
||||
- `--fed-registry-ref REF` seeds the registry reference (hex ref).
|
||||
- `--fed-require-space` rejects `/v1/fed/*` requests that do not resolve a space.
|
||||
|
||||
`X-Amduat-Space` is honored for `/v1/fed/*` requests the same way as other
|
||||
endpoints. If `--space` is configured, unix transport requests will include the
|
||||
same `X-Amduat-Space` header when contacting peers.
|
||||
|
||||
Run the daemon with derivation indexing enabled:
|
||||
|
||||
```sh
|
||||
|
|
|
|||
|
|
@ -687,7 +687,9 @@ static int amduat_fed_transport_unix_get_records(void *ctx,
|
|||
amduat_fed_record_t **out_records,
|
||||
size_t *out_len) {
|
||||
amduat_fed_transport_unix_t *transport = (amduat_fed_transport_unix_t *)ctx;
|
||||
char req[512];
|
||||
char req[2048];
|
||||
char space_header[AMDUAT_ASL_POINTER_NAME_MAX + 32u];
|
||||
const char *space_line = "";
|
||||
int fd;
|
||||
uint8_t *buf = NULL;
|
||||
size_t buf_len = 0;
|
||||
|
|
@ -703,13 +705,23 @@ static int amduat_fed_transport_unix_get_records(void *ctx,
|
|||
*out_records = NULL;
|
||||
*out_len = 0;
|
||||
|
||||
if (transport->has_space) {
|
||||
snprintf(space_header,
|
||||
sizeof(space_header),
|
||||
"X-Amduat-Space: %s\r\n",
|
||||
transport->space_id);
|
||||
space_line = space_header;
|
||||
}
|
||||
|
||||
snprintf(req, sizeof(req),
|
||||
"GET /v1/fed/records?domain_id=%u&from_logseq=%llu HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"%s"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
(unsigned int)domain_id,
|
||||
(unsigned long long)from_logseq);
|
||||
(unsigned long long)from_logseq,
|
||||
space_line);
|
||||
|
||||
fd = amduat_fed_transport_unix_connect(transport->socket_path);
|
||||
if (fd < 0) {
|
||||
|
|
@ -767,7 +779,9 @@ static int amduat_fed_transport_unix_get_artifact(void *ctx,
|
|||
amduat_octets_t *out_bytes) {
|
||||
amduat_fed_transport_unix_t *transport = (amduat_fed_transport_unix_t *)ctx;
|
||||
char *ref_hex = NULL;
|
||||
char req[512];
|
||||
char req[2048];
|
||||
char space_header[AMDUAT_ASL_POINTER_NAME_MAX + 32u];
|
||||
const char *space_line = "";
|
||||
int fd;
|
||||
uint8_t *buf = NULL;
|
||||
size_t buf_len = 0;
|
||||
|
|
@ -783,12 +797,21 @@ static int amduat_fed_transport_unix_get_artifact(void *ctx,
|
|||
if (!amduat_asl_ref_encode_hex(ref, &ref_hex)) {
|
||||
return -1;
|
||||
}
|
||||
if (transport->has_space) {
|
||||
snprintf(space_header,
|
||||
sizeof(space_header),
|
||||
"X-Amduat-Space: %s\r\n",
|
||||
transport->space_id);
|
||||
space_line = space_header;
|
||||
}
|
||||
snprintf(req, sizeof(req),
|
||||
"GET /v1/fed/artifacts/%s HTTP/1.1\r\n"
|
||||
"Host: localhost\r\n"
|
||||
"%s"
|
||||
"Connection: close\r\n"
|
||||
"\r\n",
|
||||
ref_hex);
|
||||
ref_hex,
|
||||
space_line);
|
||||
free(ref_hex);
|
||||
|
||||
fd = amduat_fed_transport_unix_connect(transport->socket_path);
|
||||
|
|
@ -837,6 +860,34 @@ bool amduat_fed_transport_unix_init(amduat_fed_transport_unix_t *transport,
|
|||
memset(transport->socket_path, 0, sizeof(transport->socket_path));
|
||||
strncpy(transport->socket_path, socket_path,
|
||||
AMDUAT_FED_TRANSPORT_UNIX_PATH_MAX - 1u);
|
||||
memset(transport->space_id, 0, sizeof(transport->space_id));
|
||||
transport->has_space = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool amduat_fed_transport_unix_set_space(amduat_fed_transport_unix_t *transport,
|
||||
const char *space_id) {
|
||||
size_t len;
|
||||
size_t i;
|
||||
if (transport == NULL) {
|
||||
return false;
|
||||
}
|
||||
memset(transport->space_id, 0, sizeof(transport->space_id));
|
||||
transport->has_space = false;
|
||||
if (space_id == NULL || space_id[0] == '\0') {
|
||||
return true;
|
||||
}
|
||||
len = strlen(space_id);
|
||||
if (len >= sizeof(transport->space_id)) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (space_id[i] == '\r' || space_id[i] == '\n') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
memcpy(transport->space_id, space_id, len);
|
||||
transport->has_space = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#define AMDUAT_FED_TRANSPORT_UNIX_H
|
||||
|
||||
#include "federation/coord.h"
|
||||
#include "amduat/asl/asl_pointer_fs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -11,11 +12,16 @@ enum { AMDUAT_FED_TRANSPORT_UNIX_PATH_MAX = 1024 };
|
|||
|
||||
typedef struct {
|
||||
char socket_path[AMDUAT_FED_TRANSPORT_UNIX_PATH_MAX];
|
||||
char space_id[AMDUAT_ASL_POINTER_NAME_MAX + 1u];
|
||||
bool has_space;
|
||||
} amduat_fed_transport_unix_t;
|
||||
|
||||
bool amduat_fed_transport_unix_init(amduat_fed_transport_unix_t *transport,
|
||||
const char *socket_path);
|
||||
|
||||
bool amduat_fed_transport_unix_set_space(amduat_fed_transport_unix_t *transport,
|
||||
const char *space_id);
|
||||
|
||||
amduat_fed_transport_t amduat_fed_transport_unix_ops(
|
||||
amduat_fed_transport_unix_t *transport);
|
||||
|
||||
|
|
|
|||
134
src/amduatd.c
134
src/amduatd.c
|
|
@ -26,6 +26,7 @@
|
|||
#include "amduat/tgk/core.h"
|
||||
#include "federation/coord.h"
|
||||
#include "federation/transport_stub.h"
|
||||
#include "federation/transport_unix.h"
|
||||
#include "amduat/pel/program_dag.h"
|
||||
#include "amduat/pel/program_dag_desc.h"
|
||||
#include "amduat/pel/opreg_kernel.h"
|
||||
|
|
@ -38,6 +39,7 @@
|
|||
#include "amduatd_ui.h"
|
||||
#include "amduatd_caps.h"
|
||||
#include "amduatd_space.h"
|
||||
#include "amduatd_fed.h"
|
||||
#include "amduatd_store.h"
|
||||
#include "amduatd_derivation_index.h"
|
||||
|
||||
|
|
@ -975,8 +977,37 @@ static bool amduatd_parse_u32_str(const char *s, uint32_t *out) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool amduatd_fed_require_space(int fd,
|
||||
const amduatd_fed_cfg_t *fed_cfg,
|
||||
const amduatd_http_req_t *req) {
|
||||
amduat_octets_t scoped = amduat_octets(NULL, 0u);
|
||||
const char *err = NULL;
|
||||
char msg[128];
|
||||
|
||||
if (fed_cfg == NULL || req == NULL) {
|
||||
return amduatd_http_send_text(fd, 500, "Internal Server Error",
|
||||
"internal error\n", false);
|
||||
}
|
||||
if (!amduatd_fed_scope_names(fed_cfg,
|
||||
req->effective_space,
|
||||
"fed",
|
||||
&scoped,
|
||||
&err)) {
|
||||
const char *reason = err != NULL ? err : "invalid X-Amduat-Space";
|
||||
int n = snprintf(msg, sizeof(msg), "%s\n", reason);
|
||||
if (n <= 0 || (size_t)n >= sizeof(msg)) {
|
||||
return amduatd_http_send_text(fd, 400, "Bad Request",
|
||||
"invalid X-Amduat-Space\n", false);
|
||||
}
|
||||
return amduatd_http_send_text(fd, 400, "Bad Request", msg, false);
|
||||
}
|
||||
amduat_octets_free(&scoped);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool amduatd_handle_get_fed_records(int fd,
|
||||
amduat_asl_store_t *store,
|
||||
const amduatd_fed_cfg_t *fed_cfg,
|
||||
const amduatd_http_req_t *req) {
|
||||
char domain_buf[32];
|
||||
char from_buf[32];
|
||||
|
|
@ -997,6 +1028,9 @@ static bool amduatd_handle_get_fed_records(int fd,
|
|||
return amduatd_http_send_text(fd, 500, "Internal Server Error",
|
||||
"internal error\n", false);
|
||||
}
|
||||
if (!amduatd_fed_require_space(fd, fed_cfg, req)) {
|
||||
return false;
|
||||
}
|
||||
if (amduatd_query_param(req->path, "domain_id",
|
||||
domain_buf, sizeof(domain_buf)) == NULL ||
|
||||
!amduatd_parse_u32_str(domain_buf, &domain_id)) {
|
||||
|
|
@ -1179,6 +1213,7 @@ fed_records_oom:
|
|||
|
||||
static bool amduatd_handle_get_fed_artifact(int fd,
|
||||
amduat_asl_store_t *store,
|
||||
const amduatd_fed_cfg_t *fed_cfg,
|
||||
const amduatd_http_req_t *req,
|
||||
const char *path) {
|
||||
char no_query[1024];
|
||||
|
|
@ -1191,6 +1226,9 @@ static bool amduatd_handle_get_fed_artifact(int fd,
|
|||
return amduatd_http_send_text(fd, 500, "Internal Server Error",
|
||||
"internal error\n", false);
|
||||
}
|
||||
if (!amduatd_fed_require_space(fd, fed_cfg, req)) {
|
||||
return false;
|
||||
}
|
||||
amduatd_path_without_query(path, no_query, sizeof(no_query));
|
||||
if (strncmp(no_query, "/v1/fed/artifacts/", 18) != 0) {
|
||||
return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", false);
|
||||
|
|
@ -1228,13 +1266,20 @@ static bool amduatd_handle_get_fed_artifact(int fd,
|
|||
|
||||
static bool amduatd_handle_get_fed_status(int fd,
|
||||
const amduat_fed_coord_t *coord,
|
||||
const amduatd_fed_cfg_t *fed_cfg,
|
||||
const amduatd_http_req_t *req) {
|
||||
amduat_fed_coord_status_t status;
|
||||
char *ref_hex = NULL;
|
||||
char json[512];
|
||||
int n;
|
||||
|
||||
(void)req;
|
||||
if (!amduatd_fed_require_space(fd, fed_cfg, req)) {
|
||||
return false;
|
||||
}
|
||||
if (coord == NULL || fed_cfg == NULL || !fed_cfg->enabled) {
|
||||
return amduatd_http_send_text(fd, 503, "Service Unavailable",
|
||||
"federation disabled\n", false);
|
||||
}
|
||||
amduat_fed_coord_get_status(coord, &status);
|
||||
if (status.registry_ref.hash_id != 0 &&
|
||||
status.registry_ref.digest.data != NULL) {
|
||||
|
|
@ -3870,6 +3915,7 @@ static bool amduatd_handle_conn(int fd,
|
|||
amduat_reference_t ui_ref,
|
||||
amduatd_concepts_t *concepts,
|
||||
const amduatd_cfg_t *dcfg,
|
||||
const amduatd_fed_cfg_t *fed_cfg,
|
||||
const amduat_fed_coord_t *coord,
|
||||
const amduatd_allowlist_t *allowlist,
|
||||
amduatd_caps_t *caps,
|
||||
|
|
@ -3971,12 +4017,12 @@ static bool amduatd_handle_conn(int fd,
|
|||
}
|
||||
if (strcmp(req.method, "GET") == 0 &&
|
||||
strcmp(no_query, "/v1/fed/records") == 0) {
|
||||
ok = amduatd_handle_get_fed_records(fd, store, &req);
|
||||
ok = amduatd_handle_get_fed_records(fd, store, fed_cfg, &req);
|
||||
goto conn_cleanup;
|
||||
}
|
||||
if (strcmp(req.method, "GET") == 0 &&
|
||||
strcmp(no_query, "/v1/fed/status") == 0) {
|
||||
ok = amduatd_handle_get_fed_status(fd, coord, &req);
|
||||
ok = amduatd_handle_get_fed_status(fd, coord, fed_cfg, &req);
|
||||
goto conn_cleanup;
|
||||
}
|
||||
|
||||
|
|
@ -4042,7 +4088,7 @@ static bool amduatd_handle_conn(int fd,
|
|||
}
|
||||
if (strcmp(req.method, "GET") == 0 &&
|
||||
strncmp(no_query, "/v1/fed/artifacts/", 18) == 0) {
|
||||
ok = amduatd_handle_get_fed_artifact(fd, store, &req, req.path);
|
||||
ok = amduatd_handle_get_fed_artifact(fd, store, fed_cfg, &req, req.path);
|
||||
goto conn_cleanup;
|
||||
}
|
||||
if (strcmp(req.method, "GET") == 0 &&
|
||||
|
|
@ -4089,6 +4135,10 @@ static void amduatd_print_usage(FILE *stream) {
|
|||
" [--space SPACE_ID] [--migrate-unscoped-edges]\n"
|
||||
" [--edges-refresh-ms MS]\n"
|
||||
" [--store-backend fs|index]\n"
|
||||
" [--fed-enable] [--fed-transport stub|unix]\n"
|
||||
" [--fed-unix-sock PATH]\n"
|
||||
" [--fed-domain-id ID] [--fed-registry-ref REF]\n"
|
||||
" [--fed-require-space]\n"
|
||||
" [--allow-uid UID] [--allow-user NAME]\n"
|
||||
" [--enable-cap-reads]\n"
|
||||
" [--enable-derivation-index]\n"
|
||||
|
|
@ -4116,14 +4166,17 @@ int main(int argc, char **argv) {
|
|||
amduat_reference_t api_contract_ref;
|
||||
amduat_reference_t ui_ref;
|
||||
amduatd_concepts_t concepts;
|
||||
amduatd_fed_cfg_t fed_cfg;
|
||||
amduat_fed_transport_stub_t fed_stub;
|
||||
amduat_fed_coord_cfg_t fed_cfg;
|
||||
amduat_fed_transport_unix_t fed_unix;
|
||||
amduat_fed_coord_cfg_t fed_coord_cfg;
|
||||
amduat_fed_coord_t *fed_coord = NULL;
|
||||
amduatd_allowlist_t allowlist;
|
||||
int i;
|
||||
int sfd = -1;
|
||||
uint64_t last_tick_ms = 0;
|
||||
uint64_t last_edges_refresh_ms = 0;
|
||||
const char *fed_err = NULL;
|
||||
|
||||
memset(&api_contract_ref, 0, sizeof(api_contract_ref));
|
||||
memset(&ui_ref, 0, sizeof(ui_ref));
|
||||
|
|
@ -4131,8 +4184,18 @@ int main(int argc, char **argv) {
|
|||
memset(&allowlist, 0, sizeof(allowlist));
|
||||
memset(&dcfg, 0, sizeof(dcfg));
|
||||
memset(&caps, 0, sizeof(caps));
|
||||
amduatd_fed_cfg_init(&fed_cfg);
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
amduatd_fed_parse_result_t fed_rc;
|
||||
fed_rc = amduatd_fed_cfg_parse_arg(&fed_cfg, argc, argv, &i, &fed_err);
|
||||
if (fed_rc == AMDUATD_FED_PARSE_OK) {
|
||||
continue;
|
||||
}
|
||||
if (fed_rc == AMDUATD_FED_PARSE_ERROR) {
|
||||
fprintf(stderr, "error: %s\n", fed_err != NULL ? fed_err : "fed parse error");
|
||||
return 2;
|
||||
}
|
||||
if (strcmp(argv[i], "--root") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "error: --root requires a path\n");
|
||||
|
|
@ -4231,6 +4294,13 @@ int main(int argc, char **argv) {
|
|||
return 2;
|
||||
}
|
||||
|
||||
if (!amduatd_fed_requirements_check(store_backend, &fed_cfg, &fed_err)) {
|
||||
fprintf(stderr,
|
||||
"error: %s\n",
|
||||
fed_err != NULL ? fed_err : "invalid federation config");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (!amduatd_store_init(&store, &cfg, &store_ctx, root, store_backend)) {
|
||||
fprintf(stderr, "error: failed to initialize store: %s\n", root);
|
||||
return 8;
|
||||
|
|
@ -4268,14 +4338,53 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
char *registry_hex = NULL;
|
||||
if (fed_cfg.registry_ref_set) {
|
||||
if (!amduat_asl_ref_encode_hex(fed_cfg.registry_ref, ®istry_hex)) {
|
||||
registry_hex = NULL;
|
||||
}
|
||||
}
|
||||
amduat_log(AMDUAT_LOG_INFO,
|
||||
"federation enabled=%s transport=%s domain_id=%u registry_ref=%s require_space=%s",
|
||||
fed_cfg.enabled ? "on" : "off",
|
||||
amduatd_fed_transport_name(fed_cfg.transport_kind),
|
||||
(unsigned int)fed_cfg.local_domain_id,
|
||||
registry_hex != NULL ? registry_hex : "null",
|
||||
fed_cfg.require_space ? "on" : "off");
|
||||
free(registry_hex);
|
||||
}
|
||||
|
||||
amduat_fed_transport_stub_init(&fed_stub);
|
||||
memset(&fed_cfg, 0, sizeof(fed_cfg));
|
||||
fed_cfg.local_domain_id = 0;
|
||||
fed_cfg.authoritative_store = &store;
|
||||
fed_cfg.cache_store = NULL;
|
||||
fed_cfg.transport = amduat_fed_transport_stub_ops(&fed_stub);
|
||||
if (amduat_fed_coord_open(&fed_cfg, &fed_coord) != AMDUAT_FED_COORD_OK) {
|
||||
fed_coord = NULL;
|
||||
memset(&fed_coord_cfg, 0, sizeof(fed_coord_cfg));
|
||||
if (fed_cfg.enabled) {
|
||||
fed_coord_cfg.local_domain_id = fed_cfg.local_domain_id;
|
||||
fed_coord_cfg.authoritative_store = &store;
|
||||
fed_coord_cfg.cache_store = NULL;
|
||||
if (fed_cfg.registry_ref_set) {
|
||||
fed_coord_cfg.registry_ref = fed_cfg.registry_ref;
|
||||
} else {
|
||||
fed_coord_cfg.registry_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
|
||||
}
|
||||
if (fed_cfg.transport_kind == AMDUATD_FED_TRANSPORT_UNIX) {
|
||||
if (!amduat_fed_transport_unix_init(&fed_unix,
|
||||
fed_cfg.unix_socket_path)) {
|
||||
fprintf(stderr, "error: invalid --fed-unix-sock\n");
|
||||
return 2;
|
||||
}
|
||||
if (dcfg.space.enabled) {
|
||||
(void)amduat_fed_transport_unix_set_space(&fed_unix,
|
||||
dcfg.space.space_id_buf);
|
||||
}
|
||||
fed_coord_cfg.transport = amduat_fed_transport_unix_ops(&fed_unix);
|
||||
} else {
|
||||
fed_coord_cfg.transport = amduat_fed_transport_stub_ops(&fed_stub);
|
||||
}
|
||||
if (amduat_fed_coord_open(&fed_coord_cfg, &fed_coord) !=
|
||||
AMDUAT_FED_COORD_OK) {
|
||||
fprintf(stderr, "error: failed to init federation coordinator\n");
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
signal(SIGINT, amduatd_on_signal);
|
||||
|
|
@ -4378,6 +4487,7 @@ int main(int argc, char **argv) {
|
|||
ui_ref,
|
||||
&concepts,
|
||||
&dcfg,
|
||||
&fed_cfg,
|
||||
fed_coord,
|
||||
&allowlist,
|
||||
&caps,
|
||||
|
|
|
|||
236
src/amduatd_fed.c
Normal file
236
src/amduatd_fed.c
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
#include "amduatd_fed.h"
|
||||
|
||||
#include "amduat/asl/ref_text.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static bool amduatd_fed_parse_u32(const char *s, uint32_t *out) {
|
||||
unsigned long val;
|
||||
char *endp = NULL;
|
||||
if (s == NULL || out == NULL) {
|
||||
return false;
|
||||
}
|
||||
errno = 0;
|
||||
val = strtoul(s, &endp, 10);
|
||||
if (errno != 0 || endp == s || *endp != '\0' || val > UINT32_MAX) {
|
||||
return false;
|
||||
}
|
||||
*out = (uint32_t)val;
|
||||
return true;
|
||||
}
|
||||
|
||||
void amduatd_fed_cfg_init(amduatd_fed_cfg_t *cfg) {
|
||||
if (cfg == NULL) {
|
||||
return;
|
||||
}
|
||||
memset(cfg, 0, sizeof(*cfg));
|
||||
cfg->transport_kind = AMDUATD_FED_TRANSPORT_STUB;
|
||||
cfg->registry_ref = amduat_reference(0u, amduat_octets(NULL, 0u));
|
||||
}
|
||||
|
||||
void amduatd_fed_cfg_free(amduatd_fed_cfg_t *cfg) {
|
||||
if (cfg == NULL) {
|
||||
return;
|
||||
}
|
||||
if (cfg->registry_ref_set) {
|
||||
amduat_reference_free(&cfg->registry_ref);
|
||||
cfg->registry_ref_set = false;
|
||||
}
|
||||
amduatd_fed_cfg_init(cfg);
|
||||
}
|
||||
|
||||
const char *amduatd_fed_transport_name(amduatd_fed_transport_kind_t kind) {
|
||||
switch (kind) {
|
||||
case AMDUATD_FED_TRANSPORT_STUB:
|
||||
return "stub";
|
||||
case AMDUATD_FED_TRANSPORT_UNIX:
|
||||
return "unix";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
amduatd_fed_parse_result_t amduatd_fed_cfg_parse_arg(amduatd_fed_cfg_t *cfg,
|
||||
int argc,
|
||||
char **argv,
|
||||
int *io_index,
|
||||
const char **out_err) {
|
||||
const char *arg = NULL;
|
||||
const char *value = NULL;
|
||||
|
||||
if (out_err != NULL) {
|
||||
*out_err = NULL;
|
||||
}
|
||||
if (cfg == NULL || argv == NULL || io_index == NULL || argc <= 0) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "invalid fed config parse inputs";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
if (*io_index < 0 || *io_index >= argc) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "fed config parse index out of range";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
|
||||
arg = argv[*io_index];
|
||||
if (strcmp(arg, "--fed-enable") == 0) {
|
||||
cfg->enabled = true;
|
||||
return AMDUATD_FED_PARSE_OK;
|
||||
}
|
||||
if (strcmp(arg, "--fed-require-space") == 0) {
|
||||
cfg->require_space = true;
|
||||
return AMDUATD_FED_PARSE_OK;
|
||||
}
|
||||
if (strcmp(arg, "--fed-transport") == 0) {
|
||||
if (*io_index + 1 >= argc) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "--fed-transport requires a value";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
value = argv[++(*io_index)];
|
||||
if (strcmp(value, "stub") == 0) {
|
||||
cfg->transport_kind = AMDUATD_FED_TRANSPORT_STUB;
|
||||
return AMDUATD_FED_PARSE_OK;
|
||||
}
|
||||
if (strcmp(value, "unix") == 0) {
|
||||
cfg->transport_kind = AMDUATD_FED_TRANSPORT_UNIX;
|
||||
return AMDUATD_FED_PARSE_OK;
|
||||
}
|
||||
if (out_err != NULL) {
|
||||
*out_err = "invalid --fed-transport";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
if (strcmp(arg, "--fed-unix-sock") == 0) {
|
||||
size_t len;
|
||||
if (*io_index + 1 >= argc) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "--fed-unix-sock requires a path";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
value = argv[++(*io_index)];
|
||||
len = strlen(value);
|
||||
if (len == 0 || len >= sizeof(cfg->unix_socket_path)) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "invalid --fed-unix-sock";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
memset(cfg->unix_socket_path, 0, sizeof(cfg->unix_socket_path));
|
||||
memcpy(cfg->unix_socket_path, value, len);
|
||||
cfg->unix_socket_set = true;
|
||||
return AMDUATD_FED_PARSE_OK;
|
||||
}
|
||||
if (strcmp(arg, "--fed-domain-id") == 0) {
|
||||
uint32_t domain_id = 0;
|
||||
if (*io_index + 1 >= argc) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "--fed-domain-id requires a value";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
value = argv[++(*io_index)];
|
||||
if (!amduatd_fed_parse_u32(value, &domain_id)) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "invalid --fed-domain-id";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
cfg->local_domain_id = domain_id;
|
||||
return AMDUATD_FED_PARSE_OK;
|
||||
}
|
||||
if (strcmp(arg, "--fed-registry-ref") == 0) {
|
||||
amduat_reference_t ref;
|
||||
if (*io_index + 1 >= argc) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "--fed-registry-ref requires a value";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
value = argv[++(*io_index)];
|
||||
memset(&ref, 0, sizeof(ref));
|
||||
if (!amduat_asl_ref_decode_hex(value, &ref)) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "invalid --fed-registry-ref";
|
||||
}
|
||||
return AMDUATD_FED_PARSE_ERROR;
|
||||
}
|
||||
if (cfg->registry_ref_set) {
|
||||
amduat_reference_free(&cfg->registry_ref);
|
||||
}
|
||||
cfg->registry_ref = ref;
|
||||
cfg->registry_ref_set = true;
|
||||
return AMDUATD_FED_PARSE_OK;
|
||||
}
|
||||
|
||||
return AMDUATD_FED_PARSE_NOT_HANDLED;
|
||||
}
|
||||
|
||||
bool amduatd_fed_requirements_check(amduatd_store_backend_t backend,
|
||||
const amduatd_fed_cfg_t *cfg,
|
||||
const char **out_err) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = NULL;
|
||||
}
|
||||
if (cfg == NULL) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "missing fed config";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!cfg->enabled) {
|
||||
return true;
|
||||
}
|
||||
if (backend != AMDUATD_STORE_BACKEND_INDEX) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "federation requires --store-backend index";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (cfg->transport_kind == AMDUATD_FED_TRANSPORT_UNIX &&
|
||||
!cfg->unix_socket_set) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "unix transport requires --fed-unix-sock";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool amduatd_fed_scope_names(const amduatd_fed_cfg_t *cfg,
|
||||
const amduatd_space_t *space,
|
||||
const char *name,
|
||||
amduat_octets_t *out_scoped,
|
||||
const char **out_err) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = NULL;
|
||||
}
|
||||
if (out_scoped != NULL) {
|
||||
*out_scoped = amduat_octets(NULL, 0u);
|
||||
}
|
||||
if (cfg == NULL || name == NULL || out_scoped == NULL) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "invalid fed scope inputs";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (cfg->require_space && (space == NULL || !space->enabled)) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "missing X-Amduat-Space";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!amduatd_space_scope_name(space, name, out_scoped)) {
|
||||
if (out_err != NULL) {
|
||||
*out_err = "failed to scope name";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
65
src/amduatd_fed.h
Normal file
65
src/amduatd_fed.h
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef AMDUATD_FED_H
|
||||
#define AMDUATD_FED_H
|
||||
|
||||
#include "amduatd_space.h"
|
||||
#include "amduatd_store.h"
|
||||
#include "federation/transport_unix.h"
|
||||
|
||||
#include "amduat/asl/core.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
AMDUATD_FED_TRANSPORT_STUB = 0,
|
||||
AMDUATD_FED_TRANSPORT_UNIX = 1
|
||||
} amduatd_fed_transport_kind_t;
|
||||
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
bool require_space;
|
||||
amduatd_fed_transport_kind_t transport_kind;
|
||||
uint32_t local_domain_id;
|
||||
bool registry_ref_set;
|
||||
amduat_reference_t registry_ref;
|
||||
bool unix_socket_set;
|
||||
char unix_socket_path[AMDUAT_FED_TRANSPORT_UNIX_PATH_MAX];
|
||||
} amduatd_fed_cfg_t;
|
||||
|
||||
typedef enum {
|
||||
AMDUATD_FED_PARSE_OK = 0,
|
||||
AMDUATD_FED_PARSE_NOT_HANDLED = 1,
|
||||
AMDUATD_FED_PARSE_ERROR = 2
|
||||
} amduatd_fed_parse_result_t;
|
||||
|
||||
void amduatd_fed_cfg_init(amduatd_fed_cfg_t *cfg);
|
||||
|
||||
void amduatd_fed_cfg_free(amduatd_fed_cfg_t *cfg);
|
||||
|
||||
const char *amduatd_fed_transport_name(amduatd_fed_transport_kind_t kind);
|
||||
|
||||
amduatd_fed_parse_result_t amduatd_fed_cfg_parse_arg(amduatd_fed_cfg_t *cfg,
|
||||
int argc,
|
||||
char **argv,
|
||||
int *io_index,
|
||||
const char **out_err);
|
||||
|
||||
bool amduatd_fed_requirements_check(amduatd_store_backend_t backend,
|
||||
const amduatd_fed_cfg_t *cfg,
|
||||
const char **out_err);
|
||||
|
||||
bool amduatd_fed_scope_names(const amduatd_fed_cfg_t *cfg,
|
||||
const amduatd_space_t *space,
|
||||
const char *name,
|
||||
amduat_octets_t *out_scoped,
|
||||
const char **out_err);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* AMDUATD_FED_H */
|
||||
121
tests/test_amduatd_fed_cfg.c
Normal file
121
tests/test_amduatd_fed_cfg.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
#include "amduatd_fed.h"
|
||||
|
||||
#include "amduat/asl/ref_text.h"
|
||||
#include "amduat/hash/asl1.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void expect(bool cond, const char *msg) {
|
||||
if (!cond) {
|
||||
fprintf(stderr, "FAIL: %s\n", msg);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
amduatd_fed_cfg_t cfg;
|
||||
amduatd_space_t space;
|
||||
amduat_octets_t scoped = amduat_octets(NULL, 0u);
|
||||
uint8_t digest_bytes[32];
|
||||
amduat_octets_t digest;
|
||||
amduat_reference_t ref;
|
||||
char *ref_hex = NULL;
|
||||
const char *err = NULL;
|
||||
int i;
|
||||
|
||||
memset(digest_bytes, 0x2a, sizeof(digest_bytes));
|
||||
if (!amduat_octets_clone(amduat_octets(digest_bytes, sizeof(digest_bytes)),
|
||||
&digest)) {
|
||||
fprintf(stderr, "FAIL: digest clone\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ref = amduat_reference(AMDUAT_HASH_ASL1_ID_SHA256, digest);
|
||||
if (!amduat_asl_ref_encode_hex(ref, &ref_hex)) {
|
||||
fprintf(stderr, "FAIL: ref encode\n");
|
||||
amduat_reference_free(&ref);
|
||||
return 1;
|
||||
}
|
||||
|
||||
amduatd_fed_cfg_init(&cfg);
|
||||
expect(!cfg.enabled, "default disabled");
|
||||
expect(cfg.transport_kind == AMDUATD_FED_TRANSPORT_STUB, "default transport");
|
||||
expect(!cfg.require_space, "default require_space");
|
||||
|
||||
{
|
||||
char *argv[] = {
|
||||
"amduatd",
|
||||
"--fed-enable",
|
||||
"--fed-transport",
|
||||
"unix",
|
||||
"--fed-unix-sock",
|
||||
"amduatd.sock",
|
||||
"--fed-domain-id",
|
||||
"42",
|
||||
"--fed-registry-ref",
|
||||
ref_hex,
|
||||
"--fed-require-space",
|
||||
};
|
||||
int argc = (int)(sizeof(argv) / sizeof(argv[0]));
|
||||
for (i = 1; i < argc; ++i) {
|
||||
amduatd_fed_parse_result_t rc;
|
||||
rc = amduatd_fed_cfg_parse_arg(&cfg, argc, argv, &i, &err);
|
||||
expect(rc == AMDUATD_FED_PARSE_OK, "fed parse ok");
|
||||
}
|
||||
}
|
||||
|
||||
expect(cfg.enabled, "parsed enabled");
|
||||
expect(cfg.transport_kind == AMDUATD_FED_TRANSPORT_UNIX,
|
||||
"parsed transport");
|
||||
expect(cfg.unix_socket_set, "parsed unix socket");
|
||||
expect(cfg.local_domain_id == 42u, "parsed domain id");
|
||||
expect(cfg.registry_ref_set, "parsed registry ref");
|
||||
expect(cfg.require_space, "parsed require space");
|
||||
|
||||
expect(!amduatd_fed_requirements_check(AMDUATD_STORE_BACKEND_FS,
|
||||
&cfg,
|
||||
&err),
|
||||
"requirements reject fs backend");
|
||||
expect(amduatd_fed_requirements_check(AMDUATD_STORE_BACKEND_INDEX,
|
||||
&cfg,
|
||||
&err),
|
||||
"requirements accept index backend");
|
||||
|
||||
if (!amduatd_space_init(&space, NULL, false)) {
|
||||
fprintf(stderr, "FAIL: space init\n");
|
||||
amduatd_fed_cfg_free(&cfg);
|
||||
amduat_reference_free(&ref);
|
||||
free(ref_hex);
|
||||
return 1;
|
||||
}
|
||||
expect(!amduatd_fed_scope_names(&cfg, &space, "fed", &scoped, &err),
|
||||
"scope requires space");
|
||||
amduat_octets_free(&scoped);
|
||||
|
||||
if (!amduatd_space_init(&space, "alpha", false)) {
|
||||
fprintf(stderr, "FAIL: space init alpha\n");
|
||||
amduatd_fed_cfg_free(&cfg);
|
||||
amduat_reference_free(&ref);
|
||||
free(ref_hex);
|
||||
return 1;
|
||||
}
|
||||
expect(amduatd_fed_scope_names(&cfg, &space, "fed", &scoped, &err),
|
||||
"scope with space");
|
||||
expect(scoped.data != NULL &&
|
||||
scoped.len == strlen("space/alpha/fed") &&
|
||||
memcmp(scoped.data,
|
||||
"space/alpha/fed",
|
||||
scoped.len) == 0,
|
||||
"scoped name");
|
||||
amduat_octets_free(&scoped);
|
||||
|
||||
amduatd_fed_cfg_free(&cfg);
|
||||
amduat_reference_free(&ref);
|
||||
free(ref_hex);
|
||||
return failures == 0 ? 0 : 1;
|
||||
}
|
||||
Loading…
Reference in a new issue