#define _POSIX_C_SOURCE 200809L #include "amduat/asl/artifact_io.h" #include "amduat/asl/asl_store_fs.h" #include "amduat/asl/asl_store_fs_meta.h" #include "amduat/asl/ref_text.h" #include "amduat/asl/store.h" #include "amduat/asl/ref_derive.h" #include "amduat/enc/asl1_core_codec.h" #include "amduat/enc/pel1_result.h" #include "amduat/enc/pel_program_dag.h" #include "amduat/enc/tgk1_edge.h" #include "amduat/format/pel.h" #include "amduat/tgk/core.h" #include "amduat/pel/program_dag.h" #include "amduat/pel/program_dag_desc.h" #include "amduat/pel/opreg_kernel.h" #include "amduat/pel/run.h" #include "amduat/util/hex.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *const AMDUATD_DEFAULT_ROOT = ".amduat-asl"; static const char *const AMDUATD_DEFAULT_SOCK = "amduatd.sock"; static const char *const AMDUATD_EDGES_FILE = ".amduatd.edges"; static const uint32_t AMDUATD_TGK_EDGE_TYPE = 0u; typedef struct { amduat_reference_t *refs; size_t len; size_t cap; } amduatd_ref_list_t; static void amduatd_ref_list_free(amduatd_ref_list_t *list) { size_t i; if (list == NULL) { return; } for (i = 0; i < list->len; ++i) { amduat_reference_free(&list->refs[i]); } free(list->refs); list->refs = NULL; list->len = 0; list->cap = 0; } static bool amduatd_ref_list_push(amduatd_ref_list_t *list, amduat_reference_t ref) { amduat_reference_t cloned; amduat_reference_t *next; if (list == NULL) { return false; } memset(&cloned, 0, sizeof(cloned)); if (!amduat_reference_clone(ref, &cloned)) { return false; } if (list->len == list->cap) { size_t next_cap = list->cap != 0 ? list->cap * 2u : 64u; next = (amduat_reference_t *)realloc(list->refs, next_cap * sizeof(*list->refs)); if (next == NULL) { amduat_reference_free(&cloned); return false; } list->refs = next; list->cap = next_cap; } list->refs[list->len++] = cloned; return true; } typedef struct { const char *root_path; char edges_path[1024]; amduat_reference_t rel_aliases_ref; amduat_reference_t rel_materializes_ref; amduatd_ref_list_t edge_refs; } amduatd_concepts_t; static void amduatd_concepts_free(amduatd_concepts_t *c) { if (c == NULL) { return; } amduat_reference_free(&c->rel_aliases_ref); amduat_reference_free(&c->rel_materializes_ref); amduatd_ref_list_free(&c->edge_refs); } static const char k_amduatd_contract_v1_json[] = "{" "\"contract\":\"AMDUATD/API/1\"," "\"base_path\":\"/v1\"," "\"endpoints\":[" "{\"method\":\"GET\",\"path\":\"/v1/ui\"}," "{\"method\":\"GET\",\"path\":\"/v1/meta\"}," "{\"method\":\"HEAD\",\"path\":\"/v1/meta\"}," "{\"method\":\"GET\",\"path\":\"/v1/contract\"}," "{\"method\":\"POST\",\"path\":\"/v1/concepts\"}," "{\"method\":\"POST\",\"path\":\"/v1/concepts/{name}/publish\"}," "{\"method\":\"GET\",\"path\":\"/v1/resolve/{name}\"}," "{\"method\":\"POST\",\"path\":\"/v1/artifacts\"}," "{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}\"}," "{\"method\":\"HEAD\",\"path\":\"/v1/artifacts/{ref}\"}," "{\"method\":\"POST\",\"path\":\"/v1/pel/run\"}," "{\"method\":\"POST\",\"path\":\"/v1/pel/programs\"}" "]," "\"schemas\":{" "\"pel_run_request\":{" "\"type\":\"object\"," "\"required\":[\"program_ref\",\"input_refs\"]," "\"properties\":{" "\"program_ref\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," "\"input_refs\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}}," "\"params_ref\":{\"type\":\"string\",\"description\":\"hex ref or concept name\"}," "\"scheme_ref\":{\"type\":\"string\",\"description\":\"hex ref or 'dag'\"}" "}" "}," "\"pel_run_response\":{" "\"type\":\"object\"," "\"required\":[\"result_ref\",\"output_refs\",\"status\"]," "\"properties\":{" "\"result_ref\":{\"type\":\"string\",\"description\":\"hex ref\"}," "\"trace_ref\":{\"type\":\"string\",\"description\":\"hex ref\"}," "\"output_refs\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"description\":\"hex ref\"}}," "\"status\":{\"type\":\"string\"}" "}" "}," "\"pel_program_author_request\":{" "\"type\":\"object\"," "\"required\":[\"nodes\",\"roots\"]," "\"properties\":{" "\"nodes\":{\"type\":\"array\"}," "\"roots\":{\"type\":\"array\"}" "}" "}" "}" "}\n"; static const char k_amduatd_ui_html[] = "\n" "\n" "\n" " \n" " \n" " amduatd — PEL Program Builder\n" " \n" "\n" "\n" "
\n" "

amduatd — PEL/PROGRAM-DAG/1 builder

\n" "
POSTs to /v1/pel/programs and /v1/pel/run on this daemon.
\n" "
\n" "
\n" "
\n" "
\n" "
\n" "
Program authoring JSON
\n" "
\n" " \n" " \n" "
\n" "
\n" " \n" "
\n" "\n" "
\n" "
Run
\n" "
program_ref
\n" " \" />\n" "
input_refs (comma-separated hex refs)
\n" " ,,...\" />\n" "
params_ref (optional)
\n" " \" />\n" "
scheme_ref (optional, default dag)
\n" " \n" "
Concept
\n" "
name (lowercase, [a-z0-9_./-])
\n" " \n" "
\n" " \n" " \n" "
\n" "
\n" " \n" " /v1/contract\n" " /v1/meta\n" "
\n" "
Response
\n" "
\n"
    "      
\n" "
\n" "
\n" "\n" " \n" "\n" "\n"; static volatile sig_atomic_t amduatd_should_exit = 0; static void amduatd_on_signal(int signo) { (void)signo; amduatd_should_exit = 1; } static bool amduatd_write_all(int fd, const uint8_t *buf, size_t len) { size_t off = 0; while (off < len) { ssize_t n = write(fd, buf + off, len - off); if (n < 0) { if (errno == EINTR) { continue; } return false; } if (n == 0) { return false; } off += (size_t)n; } return true; } static bool amduatd_read_exact(int fd, uint8_t *buf, size_t len); static bool amduatd_read_file(const char *path, uint8_t **out, size_t *out_len) { uint8_t *buf = NULL; size_t cap = 0; size_t len = 0; FILE *f; if (out != NULL) { *out = NULL; } if (out_len != NULL) { *out_len = 0; } if (path == NULL || out == NULL || out_len == NULL) { return false; } f = fopen(path, "rb"); if (f == NULL) { return false; } for (;;) { size_t n; if (len == cap) { size_t next_cap = cap != 0 ? cap * 2u : 4096u; uint8_t *next = (uint8_t *)realloc(buf, next_cap); if (next == NULL) { free(buf); fclose(f); return false; } buf = next; cap = next_cap; } n = fread(buf + len, 1, cap - len, f); len += n; if (n == 0) { if (ferror(f)) { free(buf); fclose(f); return false; } break; } } fclose(f); *out = buf; *out_len = len; return true; } static bool amduatd_read_urandom(uint8_t *out, size_t len) { int fd; if (out == NULL || len == 0) { return false; } fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { return false; } if (!amduatd_read_exact(fd, out, len)) { close(fd); return false; } close(fd); return true; } static bool amduatd_name_valid(const char *name) { size_t i; size_t len; if (name == NULL) { return false; } len = strlen(name); if (len == 0 || len > 64) { return false; } for (i = 0; i < len; ++i) { unsigned char c = (unsigned char)name[i]; if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_' || c == '-' || c == '.' || c == '/') { continue; } return false; } return true; } static bool amduatd_build_prefixed_bytes(const char *prefix, const char *text, uint8_t **out, size_t *out_len) { size_t p_len; size_t t_len; size_t len; uint8_t *buf; if (out != NULL) { *out = NULL; } if (out_len != NULL) { *out_len = 0; } if (prefix == NULL || text == NULL || out == NULL || out_len == NULL) { return false; } p_len = strlen(prefix); t_len = strlen(text); if (p_len > SIZE_MAX - 1u || t_len > SIZE_MAX - (p_len + 1u)) { return false; } len = p_len + 1u + t_len; buf = (uint8_t *)malloc(len); if (buf == NULL) { return false; } memcpy(buf, prefix, p_len); buf[p_len] = 0; memcpy(buf + p_len + 1u, text, t_len); *out = buf; *out_len = len; return true; } static bool amduatd_concepts_seed_relation(amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, const char *relation_name, amduat_reference_t *out_ref) { uint8_t *bytes = NULL; size_t bytes_len = 0; amduat_artifact_t artifact; if (out_ref == NULL) { return false; } *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); if (store == NULL || cfg == NULL || relation_name == NULL) { return false; } if (!amduatd_build_prefixed_bytes("AMDUATD/RELATION/1", relation_name, &bytes, &bytes_len)) { return false; } artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); if (!amduat_asl_ref_derive(artifact, cfg->config.encoding_profile_id, cfg->config.hash_id, out_ref, NULL)) { free(bytes); return false; } (void)amduat_asl_store_put(store, artifact, out_ref); free(bytes); return true; } static bool amduatd_concepts_init(amduatd_concepts_t *c, amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, const char *root_path) { uint8_t *file_bytes = NULL; size_t file_len = 0; size_t i; if (c == NULL || store == NULL || cfg == NULL || root_path == NULL) { return false; } memset(c, 0, sizeof(*c)); c->root_path = root_path; (void)snprintf(c->edges_path, sizeof(c->edges_path), "%s/%s", root_path, AMDUATD_EDGES_FILE); if (!amduatd_concepts_seed_relation(store, cfg, "aliases", &c->rel_aliases_ref)) { return false; } if (!amduatd_concepts_seed_relation(store, cfg, "materializesAs", &c->rel_materializes_ref)) { return false; } if (!amduatd_read_file(c->edges_path, &file_bytes, &file_len)) { return true; } { char *text = (char *)file_bytes; char *line = text; char *end = text + file_len; while (line < end) { char *nl = memchr(line, '\n', (size_t)(end - line)); size_t n = nl != NULL ? (size_t)(nl - line) : (size_t)(end - line); char *tmp; amduat_reference_t ref; bool ok; while (n != 0 && (line[n - 1] == '\r' || line[n - 1] == ' ' || line[n - 1] == '\t')) { n--; } while (n != 0 && (*line == ' ' || *line == '\t')) { line++; n--; } if (n != 0) { tmp = (char *)malloc(n + 1u); if (tmp == NULL) { free(file_bytes); return false; } memcpy(tmp, line, n); tmp[n] = '\0'; memset(&ref, 0, sizeof(ref)); ok = amduat_asl_ref_decode_hex(tmp, &ref); free(tmp); if (ok) { (void)amduatd_ref_list_push(&c->edge_refs, ref); amduat_reference_free(&ref); } } if (nl == NULL) { break; } line = nl + 1; } } free(file_bytes); for (i = 0; i < c->edge_refs.len; ++i) { if (c->edge_refs.refs[i].hash_id == 0 || c->edge_refs.refs[i].digest.data == NULL || c->edge_refs.refs[i].digest.len == 0) { continue; } } return true; } static bool amduatd_concepts_append_edge_ref(amduatd_concepts_t *c, amduat_reference_t edge_ref) { char *hex = NULL; FILE *f; bool ok; if (c == NULL) { return false; } if (!amduat_asl_ref_encode_hex(edge_ref, &hex)) { return false; } f = fopen(c->edges_path, "ab"); if (f == NULL) { free(hex); return false; } ok = fprintf(f, "%s\n", hex) > 0; fclose(f); if (!ok) { free(hex); return false; } free(hex); return amduatd_ref_list_push(&c->edge_refs, edge_ref); } static bool amduatd_concepts_derive_name_ref( const amduat_asl_store_fs_config_t *cfg, const char *name, amduat_reference_t *out_ref) { uint8_t *bytes = NULL; size_t bytes_len = 0; amduat_artifact_t artifact; if (out_ref == NULL) { return false; } *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); if (cfg == NULL || name == NULL) { return false; } if (!amduatd_build_prefixed_bytes("AMDUATD/NAME/1", name, &bytes, &bytes_len)) { return false; } artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); if (!amduat_asl_ref_derive(artifact, cfg->config.encoding_profile_id, cfg->config.hash_id, out_ref, NULL)) { free(bytes); return false; } free(bytes); return true; } static bool amduatd_concepts_put_name_artifact(amduat_asl_store_t *store, const char *name, amduat_reference_t *out_ref) { uint8_t *bytes = NULL; size_t bytes_len = 0; amduat_artifact_t artifact; if (out_ref != NULL) { *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); } if (store == NULL || name == NULL || out_ref == NULL) { return false; } if (!amduatd_build_prefixed_bytes("AMDUATD/NAME/1", name, &bytes, &bytes_len)) { return false; } artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) { free(bytes); return false; } free(bytes); return true; } static bool amduatd_concepts_put_concept_id(amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, amduat_reference_t *out_ref) { uint8_t rnd[16]; uint8_t *bytes = NULL; size_t bytes_len = 0; amduat_artifact_t artifact; if (out_ref != NULL) { *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); } if (store == NULL || cfg == NULL || out_ref == NULL) { return false; } if (!amduatd_read_urandom(rnd, sizeof(rnd))) { return false; } bytes_len = strlen("AMDUATD/CONCEPT-ID/1") + 1u + sizeof(rnd); bytes = (uint8_t *)malloc(bytes_len); if (bytes == NULL) { return false; } memcpy(bytes, "AMDUATD/CONCEPT-ID/1", strlen("AMDUATD/CONCEPT-ID/1")); bytes[strlen("AMDUATD/CONCEPT-ID/1")] = 0; memcpy(bytes + strlen("AMDUATD/CONCEPT-ID/1") + 1u, rnd, sizeof(rnd)); artifact = amduat_artifact(amduat_octets(bytes, bytes_len)); if (!amduat_asl_ref_derive(artifact, cfg->config.encoding_profile_id, cfg->config.hash_id, out_ref, NULL)) { free(bytes); return false; } if (amduat_asl_store_put(store, artifact, out_ref) != AMDUAT_ASL_STORE_OK) { free(bytes); return false; } free(bytes); return true; } static bool amduatd_concepts_put_edge(amduat_asl_store_t *store, amduatd_concepts_t *c, amduat_reference_t from, amduat_reference_t to, amduat_reference_t relation_concept_ref, amduat_reference_t *out_edge_ref) { amduat_tgk_edge_body_t edge; amduat_reference_t from_arr[1]; amduat_reference_t to_arr[1]; amduat_octets_t bytes; amduat_artifact_t artifact; if (out_edge_ref != NULL) { *out_edge_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); } if (store == NULL || c == NULL || out_edge_ref == NULL) { return false; } memset(&edge, 0, sizeof(edge)); edge.type = AMDUATD_TGK_EDGE_TYPE; from_arr[0] = from; to_arr[0] = to; edge.from = from_arr; edge.from_len = 1; edge.to = to_arr; edge.to_len = 1; edge.payload = relation_concept_ref; bytes = amduat_octets(NULL, 0); if (!amduat_enc_tgk1_edge_encode_v1(&edge, &bytes)) { return false; } artifact = amduat_artifact_with_type(bytes, amduat_type_tag(AMDUAT_TYPE_TAG_TGK1_EDGE_V1)); if (amduat_asl_store_put(store, artifact, out_edge_ref) != AMDUAT_ASL_STORE_OK) { free((void *)bytes.data); return false; } free((void *)bytes.data); return amduatd_concepts_append_edge_ref(c, *out_edge_ref); } static bool amduatd_concepts_lookup_alias(amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, const amduatd_concepts_t *c, const char *name, amduat_reference_t *out_concept_ref) { amduat_reference_t name_ref; size_t i; if (out_concept_ref != NULL) { *out_concept_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); } if (store == NULL || cfg == NULL || c == NULL || name == NULL || out_concept_ref == NULL) { return false; } if (!amduatd_concepts_derive_name_ref(cfg, name, &name_ref)) { return false; } for (i = c->edge_refs.len; i > 0; --i) { amduat_artifact_t artifact; amduat_tgk_edge_body_t edge; amduat_asl_store_error_t err; memset(&artifact, 0, sizeof(artifact)); err = amduat_asl_store_get(store, c->edge_refs.refs[i - 1u], &artifact); if (err != AMDUAT_ASL_STORE_OK) { continue; } if (!artifact.has_type_tag || artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_TGK1_EDGE_V1) { amduat_asl_artifact_free(&artifact); continue; } memset(&edge, 0, sizeof(edge)); if (!amduat_enc_tgk1_edge_decode_v1(artifact.bytes, &edge)) { amduat_asl_artifact_free(&artifact); continue; } if (edge.from_len == 1 && edge.to_len == 1 && amduat_reference_eq(edge.payload, c->rel_aliases_ref) && amduat_reference_eq(edge.from[0], name_ref)) { amduat_reference_clone(edge.to[0], out_concept_ref); amduat_enc_tgk1_edge_free(&edge); amduat_asl_artifact_free(&artifact); amduat_reference_free(&name_ref); return true; } amduat_enc_tgk1_edge_free(&edge); amduat_asl_artifact_free(&artifact); } amduat_reference_free(&name_ref); return false; } static bool amduatd_concepts_resolve_latest(amduat_asl_store_t *store, const amduatd_concepts_t *c, amduat_reference_t concept_ref, amduat_reference_t *out_ref) { size_t i; if (out_ref != NULL) { *out_ref = amduat_reference(0u, amduat_octets(NULL, 0u)); } if (store == NULL || c == NULL || out_ref == NULL) { return false; } for (i = c->edge_refs.len; i > 0; --i) { amduat_artifact_t artifact; amduat_tgk_edge_body_t edge; amduat_asl_store_error_t err; memset(&artifact, 0, sizeof(artifact)); err = amduat_asl_store_get(store, c->edge_refs.refs[i - 1u], &artifact); if (err != AMDUAT_ASL_STORE_OK) { continue; } if (!artifact.has_type_tag || artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_TGK1_EDGE_V1) { amduat_asl_artifact_free(&artifact); continue; } memset(&edge, 0, sizeof(edge)); if (!amduat_enc_tgk1_edge_decode_v1(artifact.bytes, &edge)) { amduat_asl_artifact_free(&artifact); continue; } if (edge.from_len == 1 && edge.to_len == 1 && amduat_reference_eq(edge.payload, c->rel_materializes_ref) && amduat_reference_eq(edge.from[0], concept_ref)) { amduat_reference_clone(edge.to[0], out_ref); amduat_enc_tgk1_edge_free(&edge); amduat_asl_artifact_free(&artifact); return true; } amduat_enc_tgk1_edge_free(&edge); amduat_asl_artifact_free(&artifact); } return false; } static bool amduatd_read_exact(int fd, uint8_t *buf, size_t len) { size_t off = 0; while (off < len) { ssize_t n = read(fd, buf + off, len - off); if (n < 0) { if (errno == EINTR) { continue; } return false; } if (n == 0) { return false; } off += (size_t)n; } return true; } static bool amduatd_read_line(int fd, char *buf, size_t cap, size_t *out_len) { size_t len = 0; while (len + 1 < cap) { char c = 0; ssize_t n = read(fd, &c, 1); if (n < 0) { if (errno == EINTR) { continue; } return false; } if (n == 0) { return false; } if (c == '\n') { break; } buf[len++] = c; } if (len == 0) { return false; } if (len > 0 && buf[len - 1] == '\r') { len--; } buf[len] = '\0'; if (out_len != NULL) { *out_len = len; } return true; } typedef struct { char method[8]; char path[1024]; char content_type[128]; char accept[128]; char x_type_tag[64]; size_t content_length; } amduatd_http_req_t; static void amduatd_http_req_init(amduatd_http_req_t *req) { memset(req, 0, sizeof(*req)); } static bool amduatd_http_parse_request(int fd, amduatd_http_req_t *out_req) { char line[2048]; size_t line_len = 0; char *sp1 = NULL; char *sp2 = NULL; if (out_req == NULL) { return false; } amduatd_http_req_init(out_req); if (!amduatd_read_line(fd, line, sizeof(line), &line_len)) { return false; } sp1 = strchr(line, ' '); if (sp1 == NULL) { return false; } *sp1++ = '\0'; sp2 = strchr(sp1, ' '); if (sp2 == NULL) { return false; } *sp2++ = '\0'; if (strlen(line) >= sizeof(out_req->method)) { return false; } strncpy(out_req->method, line, sizeof(out_req->method) - 1); if (strlen(sp1) >= sizeof(out_req->path)) { return false; } strncpy(out_req->path, sp1, sizeof(out_req->path) - 1); for (;;) { if (!amduatd_read_line(fd, line, sizeof(line), &line_len)) { return false; } if (line_len == 0) { break; } if (strncasecmp(line, "Content-Length:", 15) == 0) { const char *v = line + 15; while (*v == ' ' || *v == '\t') { v++; } out_req->content_length = (size_t)strtoull(v, NULL, 10); } else if (strncasecmp(line, "Content-Type:", 13) == 0) { const char *v = line + 13; while (*v == ' ' || *v == '\t') { v++; } strncpy(out_req->content_type, v, sizeof(out_req->content_type) - 1); } else if (strncasecmp(line, "Accept:", 7) == 0) { const char *v = line + 7; while (*v == ' ' || *v == '\t') { v++; } strncpy(out_req->accept, v, sizeof(out_req->accept) - 1); } else if (strncasecmp(line, "X-Amduat-Type-Tag:", 18) == 0) { const char *v = line + 18; while (*v == ' ' || *v == '\t') { v++; } strncpy(out_req->x_type_tag, v, sizeof(out_req->x_type_tag) - 1); } } return true; } static bool amduatd_http_send_status(int fd, int code, const char *reason, const char *content_type, const uint8_t *body, size_t body_len, bool head_only) { char hdr[512]; int n = snprintf(hdr, sizeof(hdr), "HTTP/1.1 %d %s\r\n" "Content-Length: %zu\r\n" "Content-Type: %s\r\n" "Connection: close\r\n" "\r\n", code, reason != NULL ? reason : "", body_len, content_type != NULL ? content_type : "text/plain"); if (n <= 0 || (size_t)n >= sizeof(hdr)) { return false; } if (!amduatd_write_all(fd, (const uint8_t *)hdr, (size_t)n)) { return false; } if (!head_only && body != NULL && body_len != 0) { return amduatd_write_all(fd, body, body_len); } return true; } static bool amduatd_http_send_text(int fd, int code, const char *reason, const char *text, bool head_only) { const uint8_t *body = (const uint8_t *)(text != NULL ? text : ""); size_t len = text != NULL ? strlen(text) : 0; return amduatd_http_send_status( fd, code, reason, "text/plain; charset=utf-8", body, len, head_only); } static bool amduatd_http_send_json(int fd, int code, const char *reason, const char *json, bool head_only) { const uint8_t *body = (const uint8_t *)(json != NULL ? json : "{}"); size_t len = json != NULL ? strlen(json) : 2; return amduatd_http_send_status( fd, code, reason, "application/json", body, len, head_only); } static bool amduatd_http_req_wants_html(const amduatd_http_req_t *req) { if (req == NULL) { return false; } if (req->accept[0] == '\0') { return false; } return strstr(req->accept, "text/html") != NULL; } static bool amduatd_http_send_not_found(int fd, const amduatd_http_req_t *req) { if (amduatd_http_req_wants_html(req)) { const char *path = (req != NULL && req->path[0] != '\0') ? req->path : "/"; const char *method = (req != NULL && req->method[0] != '\0') ? req->method : "GET"; char html[4096]; int n = snprintf( html, sizeof(html), "\n" "\n" "\n" " \n" " \n" " amduatd — Not Found\n" " \n" "\n" "\n" "
\n" "
\n" "

404 — Not Found

\n" "

amduatd didn’t recognize %s %s.

\n" "

Try one of these:

\n" " \n" "

Artifact bytes: /v1/artifacts/<ref>

\n" "
\n" "
\n" "\n" "\n", method, path); if (n <= 0 || (size_t)n >= sizeof(html)) { return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", false); } return amduatd_http_send_status(fd, 404, "Not Found", "text/html; charset=utf-8", (const uint8_t *)html, (size_t)n, false); } return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", false); } static const char *amduatd_query_param(const char *path, const char *key, char *out_value, size_t out_cap) { const char *q = strchr(path, '?'); size_t key_len = 0; if (out_value != NULL && out_cap != 0) { out_value[0] = '\0'; } if (q == NULL || key == NULL || out_value == NULL || out_cap == 0) { return NULL; } q++; key_len = strlen(key); while (*q != '\0') { const char *k = q; const char *eq = strchr(k, '='); const char *amp = strchr(k, '&'); size_t klen = 0; const char *v = NULL; size_t vlen = 0; if (amp == NULL) { amp = q + strlen(q); } if (eq == NULL || eq > amp) { q = (*amp == '&') ? (amp + 1) : amp; continue; } klen = (size_t)(eq - k); if (klen == key_len && strncmp(k, key, key_len) == 0) { v = eq + 1; vlen = (size_t)(amp - v); if (vlen >= out_cap) { vlen = out_cap - 1; } memcpy(out_value, v, vlen); out_value[vlen] = '\0'; return out_value; } q = (*amp == '&') ? (amp + 1) : amp; } return NULL; } typedef struct { char *data; size_t len; size_t cap; } amduatd_strbuf_t; static void amduatd_strbuf_free(amduatd_strbuf_t *b) { if (b == NULL) { return; } free(b->data); b->data = NULL; b->len = 0; b->cap = 0; } static bool amduatd_strbuf_reserve(amduatd_strbuf_t *b, size_t extra) { size_t need; size_t next_cap; char *next; if (b == NULL) { return false; } if (extra > (SIZE_MAX - b->len)) { return false; } need = b->len + extra; if (need <= b->cap) { return true; } next_cap = b->cap != 0 ? 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_strbuf_append(amduatd_strbuf_t *b, const char *s, size_t n) { if (b == NULL) { return false; } if (n == 0) { return true; } if (s == NULL) { return false; } if (!amduatd_strbuf_reserve(b, n + 1u)) { return false; } memcpy(b->data + b->len, s, n); b->len += n; b->data[b->len] = '\0'; return true; } static bool amduatd_strbuf_append_cstr(amduatd_strbuf_t *b, const char *s) { return amduatd_strbuf_append(b, s != NULL ? s : "", s != NULL ? strlen(s) : 0u); } static bool amduatd_strbuf_append_char(amduatd_strbuf_t *b, char c) { return amduatd_strbuf_append(b, &c, 1u); } static const char *amduatd_json_skip_ws(const char *p, const char *end) { while (p < end) { char c = *p; if (c != ' ' && c != '\t' && c != '\n' && c != '\r') { break; } p++; } return p; } static bool amduatd_json_expect(const char **p, const char *end, char expected) { const char *cur; if (p == NULL || *p == NULL) { return false; } cur = amduatd_json_skip_ws(*p, end); if (cur >= end || *cur != expected) { return false; } *p = cur + 1; return true; } static bool amduatd_json_parse_string_noesc(const char **p, const char *end, const char **out_start, size_t *out_len) { const char *cur; const char *s; if (out_start != NULL) { *out_start = NULL; } if (out_len != NULL) { *out_len = 0; } if (p == NULL || *p == NULL) { return false; } cur = amduatd_json_skip_ws(*p, end); if (cur >= end || *cur != '"') { return false; } cur++; s = cur; while (cur < end) { unsigned char c = (unsigned char)*cur; if (c == '"') { if (out_start != NULL) { *out_start = s; } if (out_len != NULL) { *out_len = (size_t)(cur - s); } *p = cur + 1; return true; } if (c == '\\' || c < 0x20u) { return false; } cur++; } return false; } static bool amduatd_json_parse_u32(const char **p, const char *end, uint32_t *out) { const char *cur; const char *start; unsigned long long v; char *tmp = NULL; char *endp = NULL; size_t n; if (out != NULL) { *out = 0; } if (p == NULL || *p == NULL || out == NULL) { return false; } cur = amduatd_json_skip_ws(*p, end); start = cur; if (cur >= end) { return false; } if (*cur < '0' || *cur > '9') { return false; } cur++; while (cur < end && *cur >= '0' && *cur <= '9') { cur++; } n = (size_t)(cur - start); tmp = (char *)malloc(n + 1u); if (tmp == NULL) { return false; } memcpy(tmp, start, n); tmp[n] = '\0'; errno = 0; v = strtoull(tmp, &endp, 10); free(tmp); if (errno != 0 || endp == NULL || *endp != '\0' || v > 0xffffffffULL) { return false; } *out = (uint32_t)v; *p = cur; return true; } static bool amduatd_json_skip_string(const char **p, const char *end) { const char *cur; if (p == NULL || *p == NULL) { return false; } cur = amduatd_json_skip_ws(*p, end); if (cur >= end || *cur != '"') { return false; } cur++; while (cur < end) { unsigned char c = (unsigned char)*cur++; if (c == '"') { *p = cur; return true; } if (c == '\\') { if (cur >= end) { return false; } cur++; } else if (c < 0x20u) { return false; } } return false; } static bool amduatd_json_skip_value(const char **p, const char *end, int depth); static bool amduatd_json_skip_array(const char **p, const char *end, int depth) { const char *cur; if (!amduatd_json_expect(p, end, '[')) { return false; } cur = amduatd_json_skip_ws(*p, end); if (cur < end && *cur == ']') { *p = cur + 1; return true; } for (;;) { if (!amduatd_json_skip_value(p, end, depth + 1)) { return false; } cur = amduatd_json_skip_ws(*p, end); if (cur >= end) { return false; } if (*cur == ',') { *p = cur + 1; continue; } if (*cur == ']') { *p = cur + 1; return true; } return false; } } static bool amduatd_json_skip_object(const char **p, const char *end, int depth) { const char *cur; if (!amduatd_json_expect(p, end, '{')) { return false; } cur = amduatd_json_skip_ws(*p, end); if (cur < end && *cur == '}') { *p = cur + 1; return true; } for (;;) { if (!amduatd_json_skip_string(p, end)) { return false; } if (!amduatd_json_expect(p, end, ':')) { return false; } if (!amduatd_json_skip_value(p, end, depth + 1)) { return false; } cur = amduatd_json_skip_ws(*p, end); if (cur >= end) { return false; } if (*cur == ',') { *p = cur + 1; continue; } if (*cur == '}') { *p = cur + 1; return true; } return false; } } static bool amduatd_json_skip_value(const char **p, const char *end, int depth) { const char *cur; if (p == NULL || *p == NULL) { return false; } if (depth > 32) { return false; } cur = amduatd_json_skip_ws(*p, end); if (cur >= end) { return false; } if (*cur == '"') { return amduatd_json_skip_string(p, end); } if (*cur == '{') { return amduatd_json_skip_object(p, end, depth); } if (*cur == '[') { return amduatd_json_skip_array(p, end, depth); } if (*cur == 't' && (end - cur) >= 4 && memcmp(cur, "true", 4) == 0) { *p = cur + 4; return true; } if (*cur == 'f' && (end - cur) >= 5 && memcmp(cur, "false", 5) == 0) { *p = cur + 5; return true; } if (*cur == 'n' && (end - cur) >= 4 && memcmp(cur, "null", 4) == 0) { *p = cur + 4; return true; } if (*cur == '-' || (*cur >= '0' && *cur <= '9')) { cur++; while (cur < end) { char c = *cur; if ((c >= '0' && c <= '9') || c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-') { cur++; continue; } break; } *p = cur; return true; } return false; } static bool amduatd_copy_json_str(const char *s, size_t len, char **out) { char *buf; if (out == NULL) { return false; } *out = NULL; if (len > (SIZE_MAX - 1u)) { return false; } buf = (char *)malloc(len + 1u); if (buf == NULL) { return false; } if (len != 0) { memcpy(buf, s, len); } buf[len] = '\0'; *out = buf; return true; } static bool amduatd_decode_ref_hex_str(const char *s, size_t len, amduat_reference_t *out_ref) { char *tmp = NULL; bool ok; if (out_ref == NULL) { return false; } memset(out_ref, 0, sizeof(*out_ref)); if (!amduatd_copy_json_str(s, len, &tmp)) { return false; } ok = amduat_asl_ref_decode_hex(tmp, out_ref); free(tmp); return ok; } typedef enum { AMDUATD_REF_OK = 0, AMDUATD_REF_ERR_INVALID = 1, AMDUATD_REF_ERR_NOT_FOUND = 2, AMDUATD_REF_ERR_INTERNAL = 3 } amduatd_ref_status_t; static amduatd_ref_status_t amduatd_decode_ref_or_name_latest( amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, const amduatd_concepts_t *concepts, const char *s, size_t len, amduat_reference_t *out_ref) { amduat_reference_t concept_ref; char *tmp = NULL; if (out_ref != NULL) { memset(out_ref, 0, sizeof(*out_ref)); } if (store == NULL || cfg == NULL || concepts == NULL || s == NULL || out_ref == NULL) { return AMDUATD_REF_ERR_INTERNAL; } if (amduatd_decode_ref_hex_str(s, len, out_ref)) { return AMDUATD_REF_OK; } if (!amduatd_copy_json_str(s, len, &tmp)) { return AMDUATD_REF_ERR_INTERNAL; } if (!amduatd_name_valid(tmp)) { free(tmp); return AMDUATD_REF_ERR_INVALID; } memset(&concept_ref, 0, sizeof(concept_ref)); if (!amduatd_concepts_lookup_alias(store, cfg, concepts, tmp, &concept_ref)) { free(tmp); return AMDUATD_REF_ERR_NOT_FOUND; } free(tmp); if (!amduatd_concepts_resolve_latest(store, concepts, concept_ref, out_ref)) { amduat_reference_free(&concept_ref); return AMDUATD_REF_ERR_NOT_FOUND; } amduat_reference_free(&concept_ref); return AMDUATD_REF_OK; } static bool amduatd_send_json_error(int fd, int code, const char *reason, const char *msg) { amduatd_strbuf_t b; memset(&b, 0, sizeof(b)); if (!amduatd_strbuf_append_cstr(&b, "{\"error\":\"") || !amduatd_strbuf_append_cstr(&b, msg != NULL ? msg : "error") || !amduatd_strbuf_append_cstr(&b, "\"}\n")) { amduatd_strbuf_free(&b); return amduatd_http_send_text(fd, 500, "Internal Server Error", "error\n", false); } { bool ok = amduatd_http_send_json(fd, code, reason, b.data, false); amduatd_strbuf_free(&b); return ok; } } static void amduatd_path_without_query(const char *path, char *out, size_t cap) { const char *q = NULL; size_t n = 0; if (out == NULL || cap == 0) { return; } out[0] = '\0'; if (path == NULL) { return; } q = strchr(path, '?'); n = q != NULL ? (size_t)(q - path) : strlen(path); if (n >= cap) { n = cap - 1; } memcpy(out, path, n); out[n] = '\0'; } static bool amduatd_parse_type_tag_hex(const char *text, bool *out_has_type_tag, amduat_type_tag_t *out_type_tag) { char *end = NULL; unsigned long v = 0; if (out_has_type_tag != NULL) { *out_has_type_tag = false; } if (text == NULL || text[0] == '\0' || out_type_tag == NULL || out_has_type_tag == NULL) { return true; } errno = 0; v = strtoul(text, &end, 0); if (errno != 0 || end == text || *end != '\0' || v > 0xffffffffUL) { return false; } *out_has_type_tag = true; out_type_tag->tag_id = (uint32_t)v; return true; } static bool amduatd_handle_meta(int fd, const amduat_asl_store_fs_config_t *cfg, amduat_reference_t api_contract_ref, bool head_only) { char json[1024]; char *contract_hex = NULL; int n; if (api_contract_ref.hash_id != 0 && api_contract_ref.digest.data != NULL && api_contract_ref.digest.len != 0) { (void)amduat_asl_ref_encode_hex(api_contract_ref, &contract_hex); } if (contract_hex != NULL) { n = snprintf(json, sizeof(json), "{" "\"store_id\":\"%s\"," "\"encoding_profile_id\":\"0x%04x\"," "\"hash_id\":\"0x%04x\"," "\"api_contract_ref\":\"%s\"" "}\n", cfg != NULL ? cfg->store_id : "", cfg != NULL ? (unsigned int)cfg->config.encoding_profile_id : 0u, cfg != NULL ? (unsigned int)cfg->config.hash_id : 0u, contract_hex); } else { n = snprintf(json, sizeof(json), "{" "\"store_id\":\"%s\"," "\"encoding_profile_id\":\"0x%04x\"," "\"hash_id\":\"0x%04x\"," "\"api_contract_ref\":null" "}\n", cfg != NULL ? cfg->store_id : "", cfg != NULL ? (unsigned int)cfg->config.encoding_profile_id : 0u, cfg != NULL ? (unsigned int)cfg->config.hash_id : 0u); } free(contract_hex); if (n <= 0 || (size_t)n >= sizeof(json)) { return amduatd_http_send_text(fd, 500, "Internal Server Error", "error\n", head_only); } return amduatd_http_send_json(fd, 200, "OK", json, head_only); } static bool amduatd_handle_get_contract(int fd, amduat_asl_store_t *store, const amduatd_http_req_t *req, amduat_reference_t api_contract_ref) { char *hex = NULL; char format[32]; if (api_contract_ref.hash_id == 0 || api_contract_ref.digest.data == NULL || api_contract_ref.digest.len == 0) { return amduatd_http_send_not_found(fd, req); } memset(format, 0, sizeof(format)); if (req != NULL && amduatd_query_param(req->path, "format", format, sizeof(format)) != NULL && strcmp(format, "ref") == 0) { char json[2048]; int n; if (!amduat_asl_ref_encode_hex(api_contract_ref, &hex)) { return amduatd_http_send_text(fd, 500, "Internal Server Error", "encode error\n", false); } n = snprintf(json, sizeof(json), "{\"ref\":\"%s\"}\n", hex); free(hex); if (n <= 0 || (size_t)n >= sizeof(json)) { return amduatd_http_send_text(fd, 500, "Internal Server Error", "error\n", false); } return amduatd_http_send_json(fd, 200, "OK", json, false); } { amduat_artifact_t artifact; amduat_asl_store_error_t err; memset(&artifact, 0, sizeof(artifact)); err = amduat_asl_store_get(store, api_contract_ref, &artifact); if (err == AMDUAT_ASL_STORE_ERR_NOT_FOUND) { return amduatd_http_send_not_found(fd, req); } if (err != AMDUAT_ASL_STORE_OK) { amduat_asl_artifact_free(&artifact); return amduatd_http_send_text(fd, 500, "Internal Server Error", "store error\n", false); } if (artifact.bytes.len != 0 && artifact.bytes.data == NULL) { amduat_asl_artifact_free(&artifact); return amduatd_http_send_text(fd, 500, "Internal Server Error", "store error\n", false); } if (!amduat_asl_ref_encode_hex(api_contract_ref, &hex)) { amduat_asl_artifact_free(&artifact); return amduatd_http_send_text(fd, 500, "Internal Server Error", "encode error\n", false); } { char hdr[600]; int n = snprintf(hdr, sizeof(hdr), "HTTP/1.1 200 OK\r\n" "Content-Length: %zu\r\n" "Content-Type: application/json\r\n" "X-Amduat-Contract-Ref: %s\r\n" "Connection: close\r\n" "\r\n", artifact.bytes.len, hex); free(hex); if (n <= 0 || (size_t)n >= sizeof(hdr)) { amduat_asl_artifact_free(&artifact); return false; } if (!amduatd_write_all(fd, (const uint8_t *)hdr, (size_t)n)) { amduat_asl_artifact_free(&artifact); return false; } if (artifact.bytes.len != 0) { bool ok = amduatd_write_all(fd, artifact.bytes.data, artifact.bytes.len); amduat_asl_artifact_free(&artifact); return ok; } amduat_asl_artifact_free(&artifact); return true; } } } static bool amduatd_seed_api_contract(amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, amduat_reference_t *out_ref) { amduat_artifact_t artifact; amduat_asl_store_error_t err; if (out_ref != NULL) { memset(out_ref, 0, sizeof(*out_ref)); } if (store == NULL || cfg == NULL || out_ref == NULL) { return false; } artifact = amduat_artifact(amduat_octets(k_amduatd_contract_v1_json, strlen(k_amduatd_contract_v1_json))); (void)amduat_asl_ref_derive(artifact, cfg->config.encoding_profile_id, cfg->config.hash_id, out_ref, NULL); err = amduat_asl_store_put(store, artifact, out_ref); if (err != AMDUAT_ASL_STORE_OK) { return false; } return true; } static bool amduatd_handle_get_artifact(int fd, amduat_asl_store_t *store, const amduatd_http_req_t *req, const char *path, bool head_only) { char no_query[1024]; char format[32]; const char *ref_text = NULL; amduat_reference_t ref; amduat_artifact_t artifact; amduat_asl_store_error_t err; bool want_artifact = false; memset(&ref, 0, sizeof(ref)); memset(&artifact, 0, sizeof(artifact)); amduatd_path_without_query(path, no_query, sizeof(no_query)); if (strncmp(no_query, "/v1/artifacts/", 14) != 0) { (void)head_only; return amduatd_http_send_not_found(fd, req); } ref_text = no_query + 14; if (ref_text[0] == '\0') { return amduatd_http_send_text(fd, 400, "Bad Request", "missing ref\n", head_only); } if (!amduat_asl_ref_decode_hex(ref_text, &ref)) { return amduatd_http_send_text(fd, 400, "Bad Request", "invalid ref\n", head_only); } memset(format, 0, sizeof(format)); if (amduatd_query_param(path, "format", format, sizeof(format)) != NULL) { if (strcmp(format, "artifact") == 0) { want_artifact = true; } else if (strcmp(format, "raw") == 0 || format[0] == '\0') { want_artifact = false; } else { free((void *)ref.digest.data); return amduatd_http_send_text(fd, 400, "Bad Request", "invalid format\n", head_only); } } err = amduat_asl_store_get(store, ref, &artifact); free((void *)ref.digest.data); if (err == AMDUAT_ASL_STORE_ERR_NOT_FOUND) { if (!head_only) { return amduatd_http_send_not_found(fd, req); } return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", true); } if (err != AMDUAT_ASL_STORE_OK) { amduat_asl_artifact_free(&artifact); return amduatd_http_send_text(fd, 500, "Internal Server Error", "store error\n", head_only); } if (!want_artifact) { bool ok = amduatd_http_send_status( fd, 200, "OK", "application/octet-stream", artifact.bytes.data, artifact.bytes.len, head_only); amduat_asl_artifact_free(&artifact); return ok; } { amduat_octets_t out = amduat_octets(NULL, 0); bool ok = amduat_enc_asl1_core_encode_artifact_v1(artifact, &out); amduat_asl_artifact_free(&artifact); if (!ok) { return amduatd_http_send_text(fd, 500, "Internal Server Error", "encode error\n", head_only); } ok = amduatd_http_send_status(fd, 200, "OK", "application/vnd.amduat.asl.artifact+v1", out.data, out.len, head_only); free((void *)out.data); return ok; } } static bool amduatd_handle_head_artifact(int fd, amduat_asl_store_t *store, const amduatd_http_req_t *req, const char *path) { return amduatd_handle_get_artifact(fd, store, req, path, true); } static bool amduatd_handle_post_artifacts(int fd, amduat_asl_store_t *store, const amduatd_http_req_t *req) { uint8_t *body = NULL; bool has_type_tag = false; amduat_type_tag_t type_tag; amduat_asl_io_format_t input_format = AMDUAT_ASL_IO_RAW; amduat_artifact_t artifact; amduat_reference_t ref; amduat_asl_store_error_t err; char *ref_hex = NULL; char json[2048]; memset(&artifact, 0, sizeof(artifact)); memset(&ref, 0, sizeof(ref)); memset(&type_tag, 0, sizeof(type_tag)); if (req == NULL) { return amduatd_http_send_text(fd, 400, "Bad Request", "bad request\n", false); } if (req->content_length > (64u * 1024u * 1024u)) { return amduatd_http_send_text(fd, 413, "Payload Too Large", "payload too large\n", false); } if (req->content_length != 0) { body = (uint8_t *)malloc(req->content_length); if (body == NULL) { return amduatd_http_send_text(fd, 500, "Internal Server Error", "oom\n", false); } if (!amduatd_read_exact(fd, body, req->content_length)) { free(body); return false; } } else { body = NULL; } if (req->content_type[0] != '\0' && strstr(req->content_type, "application/vnd.amduat.asl.artifact+v1") != NULL) { input_format = AMDUAT_ASL_IO_ARTIFACT; } if (input_format == AMDUAT_ASL_IO_RAW) { if (!amduatd_parse_type_tag_hex(req->x_type_tag, &has_type_tag, &type_tag)) { free(body); return amduatd_http_send_text(fd, 400, "Bad Request", "invalid X-Amduat-Type-Tag\n", false); } } if (!amduat_asl_artifact_from_bytes(amduat_octets(body, req->content_length), input_format, has_type_tag, type_tag, &artifact)) { free(body); return amduatd_http_send_text(fd, 400, "Bad Request", "invalid artifact\n", false); } err = amduat_asl_store_put(store, artifact, &ref); amduat_asl_artifact_free(&artifact); if (err != AMDUAT_ASL_STORE_OK) { free((void *)ref.digest.data); return amduatd_http_send_text(fd, 500, "Internal Server Error", "store error\n", false); } if (!amduat_asl_ref_encode_hex(ref, &ref_hex)) { free((void *)ref.digest.data); return amduatd_http_send_text(fd, 500, "Internal Server Error", "encode ref error\n", false); } free((void *)ref.digest.data); { int n = snprintf(json, sizeof(json), "{\"ref\":\"%s\"}\n", ref_hex); free(ref_hex); if (n <= 0 || (size_t)n >= sizeof(json)) { return amduatd_http_send_text(fd, 500, "Internal Server Error", "error\n", false); } return amduatd_http_send_json(fd, 200, "OK", json, false); } } static bool amduatd_handle_post_pel_run(int fd, amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, const amduatd_concepts_t *concepts, const amduatd_http_req_t *req) { uint8_t *body = NULL; const char *p = NULL; const char *end = NULL; bool have_program_ref = false; bool have_input_refs = false; bool have_scheme_ref = false; bool scheme_is_dag = false; bool has_params_ref = false; amduat_reference_t scheme_ref; amduat_reference_t program_ref; amduat_reference_t params_ref; amduat_reference_t *input_refs = NULL; size_t input_refs_len = 0; amduat_pel_run_result_t run_result; int status_code = 200; const char *status_reason = "OK"; bool ok = false; memset(&scheme_ref, 0, sizeof(scheme_ref)); memset(&program_ref, 0, sizeof(program_ref)); memset(¶ms_ref, 0, sizeof(params_ref)); memset(&run_result, 0, sizeof(run_result)); if (store == NULL || req == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "internal error"); } if (cfg == NULL || concepts == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "internal error"); } if (req->content_length > (1u * 1024u * 1024u)) { return amduatd_send_json_error(fd, 413, "Payload Too Large", "payload too large"); } if (req->content_length == 0) { return amduatd_send_json_error(fd, 400, "Bad Request", "missing body"); } body = NULL; if (req->content_length != 0) { body = (uint8_t *)malloc(req->content_length); if (body == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); } if (!amduatd_read_exact(fd, body, req->content_length)) { free(body); return false; } } p = (const char *)body; end = (const char *)body + req->content_length; if (!amduatd_json_expect(&p, end, '{')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_run_cleanup; } for (;;) { const char *key = NULL; size_t key_len = 0; const char *sv = NULL; size_t sv_len = 0; const char *cur = NULL; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == '}') { p = cur + 1; break; } if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json key"); goto pel_run_cleanup; } if (!amduatd_json_expect(&p, end, ':')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_run_cleanup; } if (key_len == strlen("program_ref") && memcmp(key, "program_ref", key_len) == 0) { amduatd_ref_status_t st; if (have_program_ref) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "duplicate program_ref"); goto pel_run_cleanup; } if (!amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid program_ref"); goto pel_run_cleanup; } st = amduatd_decode_ref_or_name_latest(store, cfg, concepts, sv, sv_len, &program_ref); if (st == AMDUATD_REF_ERR_NOT_FOUND) { ok = amduatd_send_json_error(fd, 404, "Not Found", "program_ref not found"); goto pel_run_cleanup; } if (st != AMDUATD_REF_OK) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid program_ref"); goto pel_run_cleanup; } have_program_ref = true; } else if (key_len == strlen("scheme_ref") && memcmp(key, "scheme_ref", key_len) == 0) { if (have_scheme_ref) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "duplicate scheme_ref"); goto pel_run_cleanup; } if (!amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid scheme_ref"); goto pel_run_cleanup; } if (sv_len == 3 && memcmp(sv, "dag", 3) == 0) { scheme_ref = amduat_pel_program_dag_scheme_ref(); scheme_is_dag = true; } else if (!amduatd_decode_ref_hex_str(sv, sv_len, &scheme_ref)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid scheme_ref"); goto pel_run_cleanup; } have_scheme_ref = true; } else if (key_len == strlen("params_ref") && memcmp(key, "params_ref", key_len) == 0) { amduatd_ref_status_t st; if (has_params_ref) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "duplicate params_ref"); goto pel_run_cleanup; } if (!amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid params_ref"); goto pel_run_cleanup; } st = amduatd_decode_ref_or_name_latest(store, cfg, concepts, sv, sv_len, ¶ms_ref); if (st == AMDUATD_REF_ERR_NOT_FOUND) { ok = amduatd_send_json_error(fd, 404, "Not Found", "params_ref not found"); goto pel_run_cleanup; } if (st != AMDUATD_REF_OK) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid params_ref"); goto pel_run_cleanup; } has_params_ref = true; } else if (key_len == strlen("input_refs") && memcmp(key, "input_refs", key_len) == 0) { size_t cap = 0; if (have_input_refs) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "duplicate input_refs"); goto pel_run_cleanup; } if (!amduatd_json_expect(&p, end, '[')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid input_refs"); goto pel_run_cleanup; } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ']') { p = cur + 1; have_input_refs = true; } else { for (;;) { amduat_reference_t ref; memset(&ref, 0, sizeof(ref)); if (!amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid input_ref"); goto pel_run_cleanup; } { amduatd_ref_status_t st = amduatd_decode_ref_or_name_latest( store, cfg, concepts, sv, sv_len, &ref); if (st == AMDUATD_REF_ERR_NOT_FOUND) { ok = amduatd_send_json_error(fd, 404, "Not Found", "input_ref not found"); goto pel_run_cleanup; } if (st != AMDUATD_REF_OK) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid input_ref"); goto pel_run_cleanup; } } if (input_refs_len == cap) { size_t next_cap = cap != 0 ? cap * 2u : 4u; amduat_reference_t *next; if (next_cap > (SIZE_MAX / sizeof(*input_refs))) { amduat_reference_free(&ref); ok = amduatd_send_json_error(fd, 400, "Bad Request", "too many input_refs"); goto pel_run_cleanup; } next = (amduat_reference_t *)realloc( input_refs, next_cap * sizeof(*input_refs)); if (next == NULL) { amduat_reference_free(&ref); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_run_cleanup; } input_refs = next; cap = next_cap; } input_refs[input_refs_len++] = ref; cur = amduatd_json_skip_ws(p, end); if (cur >= end) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid input_refs"); goto pel_run_cleanup; } if (*cur == ',') { p = cur + 1; continue; } if (*cur == ']') { p = cur + 1; have_input_refs = true; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid input_refs"); goto pel_run_cleanup; } } } else { if (!amduatd_json_skip_value(&p, end, 0)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_run_cleanup; } } cur = amduatd_json_skip_ws(p, end); if (cur >= end) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_run_cleanup; } if (*cur == ',') { p = cur + 1; continue; } if (*cur == '}') { p = cur + 1; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_run_cleanup; } p = amduatd_json_skip_ws(p, end); if (p != end) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_run_cleanup; } free(body); body = NULL; if (!have_program_ref) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing program_ref"); goto pel_run_cleanup; } if (!have_input_refs) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing input_refs"); goto pel_run_cleanup; } if (!have_scheme_ref) { scheme_ref = amduat_pel_program_dag_scheme_ref(); scheme_is_dag = true; } if (!amduat_pel_surf_run_with_result(store, scheme_ref, program_ref, input_refs, input_refs_len, has_params_ref, params_ref, &run_result)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "pel run failed"); goto pel_run_cleanup; } if (run_result.has_result_value && run_result.result_value.has_store_failure) { if (run_result.result_value.store_failure.error_code == AMDUAT_PEL_STORE_ERROR_NOT_FOUND) { status_code = 404; status_reason = "Not Found"; } else { status_code = 500; status_reason = "Internal Server Error"; } } { amduatd_strbuf_t resp; char *result_hex = NULL; char *trace_hex = NULL; const char *status = "UNKNOWN"; size_t i; memset(&resp, 0, sizeof(resp)); if (!amduat_asl_ref_encode_hex(run_result.result_ref, &result_hex)) { amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "encode error"); goto pel_run_cleanup; } if (run_result.has_result_value) { status = amduat_format_pel_status_name( run_result.result_value.core_result.status); if (run_result.result_value.has_trace_ref) { if (!amduat_asl_ref_encode_hex(run_result.result_value.trace_ref, &trace_hex)) { free(result_hex); amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "encode error"); goto pel_run_cleanup; } } } if (!amduatd_strbuf_append_cstr(&resp, "{\"result_ref\":\"") || !amduatd_strbuf_append_cstr(&resp, result_hex) || !amduatd_strbuf_append_cstr(&resp, "\"")) { free(result_hex); free(trace_hex); amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_run_cleanup; } free(result_hex); if (trace_hex != NULL) { if (!amduatd_strbuf_append_cstr(&resp, ",\"trace_ref\":\"") || !amduatd_strbuf_append_cstr(&resp, trace_hex) || !amduatd_strbuf_append_cstr(&resp, "\"")) { free(trace_hex); amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_run_cleanup; } free(trace_hex); } if (!amduatd_strbuf_append_cstr(&resp, ",\"output_refs\":[")) { amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_run_cleanup; } for (i = 0; i < run_result.output_refs_len; ++i) { char *out_hex = NULL; if (!amduat_asl_ref_encode_hex(run_result.output_refs[i], &out_hex)) { amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "encode error"); goto pel_run_cleanup; } if (i != 0) { if (!amduatd_strbuf_append_char(&resp, ',')) { free(out_hex); amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_run_cleanup; } } if (!amduatd_strbuf_append_cstr(&resp, "\"") || !amduatd_strbuf_append_cstr(&resp, out_hex) || !amduatd_strbuf_append_cstr(&resp, "\"")) { free(out_hex); amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_run_cleanup; } free(out_hex); } if (!amduatd_strbuf_append_cstr(&resp, "],\"status\":\"") || !amduatd_strbuf_append_cstr(&resp, status) || !amduatd_strbuf_append_cstr(&resp, "\"}\n")) { amduatd_strbuf_free(&resp); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_run_cleanup; } ok = amduatd_http_send_json(fd, status_code, status_reason, resp.data, false); amduatd_strbuf_free(&resp); } pel_run_cleanup: if (body != NULL) { free(body); } if (run_result.has_result_value) { amduat_enc_pel1_result_free(&run_result.result_value); } if (run_result.output_refs != NULL) { amduat_pel_surf_free_refs(run_result.output_refs, run_result.output_refs_len); } amduat_pel_surf_free_ref(&run_result.result_ref); if (input_refs != NULL) { size_t i; for (i = 0; i < input_refs_len; ++i) { amduat_reference_free(&input_refs[i]); } free(input_refs); } if (has_params_ref) { amduat_reference_free(¶ms_ref); } if (have_program_ref) { amduat_reference_free(&program_ref); } if (have_scheme_ref && !scheme_is_dag) { amduat_reference_free(&scheme_ref); } return ok; } typedef struct { amduat_pel_node_t *nodes; size_t nodes_len; amduat_pel_root_ref_t *roots; size_t roots_len; } amduatd_pel_program_tmp_t; static void amduatd_pel_program_tmp_free(amduatd_pel_program_tmp_t *tmp) { size_t i; if (tmp == NULL) { return; } if (tmp->nodes != NULL) { for (i = 0; i < tmp->nodes_len; ++i) { free(tmp->nodes[i].inputs); tmp->nodes[i].inputs = NULL; tmp->nodes[i].inputs_len = 0; free((void *)tmp->nodes[i].params.data); tmp->nodes[i].params.data = NULL; tmp->nodes[i].params.len = 0; } } free(tmp->nodes); tmp->nodes = NULL; tmp->nodes_len = 0; free(tmp->roots); tmp->roots = NULL; tmp->roots_len = 0; } static bool amduatd_parse_params_hex(const char *s, size_t len, amduat_octets_t *out) { char *tmp = NULL; uint8_t *bytes = NULL; size_t out_len = 0; if (out != NULL) { *out = amduat_octets(NULL, 0); } if (s == NULL || out == NULL) { return false; } if (!amduatd_copy_json_str(s, len, &tmp)) { return false; } if (tmp[0] == '\0') { free(tmp); *out = amduat_octets(NULL, 0); return true; } if (!amduat_hex_decode_alloc(tmp, &bytes, &out_len)) { free(tmp); return false; } free(tmp); *out = amduat_octets(bytes, out_len); return true; } static bool amduatd_handle_post_pel_programs(int fd, amduat_asl_store_t *store, const amduatd_http_req_t *req) { uint8_t *body = NULL; const char *p = NULL; const char *end = NULL; amduatd_pel_program_tmp_t tmp; amduat_pel_program_t program; amduat_octets_t program_bytes; amduat_artifact_t artifact; amduat_reference_t program_ref; char *ref_hex = NULL; char json[2048]; bool ok = false; memset(&tmp, 0, sizeof(tmp)); memset(&program, 0, sizeof(program)); program_bytes = amduat_octets(NULL, 0); memset(&artifact, 0, sizeof(artifact)); memset(&program_ref, 0, sizeof(program_ref)); if (store == NULL || req == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "internal error"); } if (req->content_length == 0) { return amduatd_send_json_error(fd, 400, "Bad Request", "missing body"); } if (req->content_length > (2u * 1024u * 1024u)) { return amduatd_send_json_error(fd, 413, "Payload Too Large", "payload too large"); } body = (uint8_t *)malloc(req->content_length); if (body == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); } if (!amduatd_read_exact(fd, body, req->content_length)) { free(body); return false; } p = (const char *)body; end = (const char *)body + req->content_length; if (!amduatd_json_expect(&p, end, '{')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_programs_cleanup; } { bool have_nodes = false; bool have_roots = false; for (;;) { const char *key = NULL; size_t key_len = 0; const char *cur = NULL; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == '}') { p = cur + 1; break; } if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) || !amduatd_json_expect(&p, end, ':')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_programs_cleanup; } if (key_len == strlen("nodes") && memcmp(key, "nodes", key_len) == 0) { size_t cap = 0; if (have_nodes) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "duplicate nodes"); goto pel_programs_cleanup; } if (!amduatd_json_expect(&p, end, '[')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid nodes"); goto pel_programs_cleanup; } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ']') { p = cur + 1; have_nodes = true; } else { for (;;) { amduat_pel_node_t node; bool have_id = false; bool have_op = false; bool have_inputs = false; bool have_params_hex = false; amduat_octets_t op_name = amduat_octets(NULL, 0); uint32_t op_ver = 0; memset(&node, 0, sizeof(node)); if (!amduatd_json_expect(&p, end, '{')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid node"); goto pel_programs_cleanup; } for (;;) { const char *nk = NULL; size_t nk_len = 0; const char *sv = NULL; size_t sv_len = 0; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == '}') { p = cur + 1; break; } if (!amduatd_json_parse_string_noesc(&p, end, &nk, &nk_len) || !amduatd_json_expect(&p, end, ':')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid node"); goto pel_programs_cleanup; } if (nk_len == strlen("id") && memcmp(nk, "id", nk_len) == 0) { uint32_t id = 0; if (have_id || !amduatd_json_parse_u32(&p, end, &id)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid node id"); goto pel_programs_cleanup; } node.id = (amduat_pel_node_id_t)id; have_id = true; } else if (nk_len == strlen("op") && memcmp(nk, "op", nk_len) == 0) { bool have_name = false; bool have_version = false; if (have_op || !amduatd_json_expect(&p, end, '{')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid op"); goto pel_programs_cleanup; } for (;;) { const char *okey = NULL; size_t okey_len = 0; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == '}') { p = cur + 1; break; } if (!amduatd_json_parse_string_noesc(&p, end, &okey, &okey_len) || !amduatd_json_expect(&p, end, ':')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid op"); goto pel_programs_cleanup; } if (okey_len == strlen("name") && memcmp(okey, "name", okey_len) == 0) { if (have_name || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid op name"); goto pel_programs_cleanup; } op_name = amduat_octets(sv, sv_len); have_name = true; } else if (okey_len == strlen("version") && memcmp(okey, "version", okey_len) == 0) { if (have_version || !amduatd_json_parse_u32(&p, end, &op_ver)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid op version"); goto pel_programs_cleanup; } have_version = true; } else { if (!amduatd_json_skip_value(&p, end, 0)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid op"); goto pel_programs_cleanup; } } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == '}') { p = cur + 1; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid op"); goto pel_programs_cleanup; } if (!have_name || !have_version) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing op fields"); goto pel_programs_cleanup; } node.op.name = op_name; node.op.version = op_ver; have_op = true; } else if (nk_len == strlen("inputs") && memcmp(nk, "inputs", nk_len) == 0) { size_t icap = 0; if (have_inputs || !amduatd_json_expect(&p, end, '[')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid inputs"); goto pel_programs_cleanup; } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ']') { p = cur + 1; have_inputs = true; } else { for (;;) { amduat_pel_dag_input_t in; memset(&in, 0, sizeof(in)); if (!amduatd_json_expect(&p, end, '{')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid input"); goto pel_programs_cleanup; } { const char *ik = NULL; size_t ik_len = 0; if (!amduatd_json_parse_string_noesc(&p, end, &ik, &ik_len) || !amduatd_json_expect(&p, end, ':') || !amduatd_json_expect(&p, end, '{')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid input"); goto pel_programs_cleanup; } if (ik_len == strlen("external") && memcmp(ik, "external", ik_len) == 0) { uint32_t idx = 0; const char *k2 = NULL; size_t k2_len = 0; if (!amduatd_json_parse_string_noesc(&p, end, &k2, &k2_len) || k2_len != strlen("input_index") || memcmp(k2, "input_index", k2_len) != 0 || !amduatd_json_expect(&p, end, ':') || !amduatd_json_parse_u32(&p, end, &idx) || !amduatd_json_expect(&p, end, '}')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid external input"); goto pel_programs_cleanup; } in.kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; in.value.external.input_index = idx; } else if (ik_len == strlen("node") && memcmp(ik, "node", ik_len) == 0) { bool have_node_id = false; bool have_output_index = false; uint32_t nid = 0; uint32_t oidx = 0; for (;;) { const char *k2 = NULL; size_t k2_len = 0; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == '}') { p = cur + 1; break; } if (!amduatd_json_parse_string_noesc(&p, end, &k2, &k2_len) || !amduatd_json_expect(&p, end, ':')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid node input"); goto pel_programs_cleanup; } if (k2_len == strlen("node_id") && memcmp(k2, "node_id", k2_len) == 0) { if (have_node_id || !amduatd_json_parse_u32(&p, end, &nid)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid node_id"); goto pel_programs_cleanup; } have_node_id = true; } else if (k2_len == strlen("output_index") && memcmp(k2, "output_index", k2_len) == 0) { if (have_output_index || !amduatd_json_parse_u32(&p, end, &oidx)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid output_index"); goto pel_programs_cleanup; } have_output_index = true; } else { if (!amduatd_json_skip_value(&p, end, 0)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid node input"); goto pel_programs_cleanup; } } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == '}') { p = cur + 1; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid node input"); goto pel_programs_cleanup; } if (!have_node_id || !have_output_index) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing node input fields"); goto pel_programs_cleanup; } in.kind = AMDUAT_PEL_DAG_INPUT_NODE; in.value.node.node_id = (amduat_pel_node_id_t)nid; in.value.node.output_index = oidx; } else { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid input kind"); goto pel_programs_cleanup; } if (!amduatd_json_expect(&p, end, '}')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid input"); goto pel_programs_cleanup; } } if (node.inputs_len == icap) { size_t next_cap = icap != 0 ? icap * 2u : 4u; amduat_pel_dag_input_t *next = (amduat_pel_dag_input_t *)realloc( node.inputs, next_cap * sizeof(*node.inputs)); if (next == NULL) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_programs_cleanup; } node.inputs = next; icap = next_cap; } node.inputs[node.inputs_len++] = in; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == ']') { p = cur + 1; have_inputs = true; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid inputs"); goto pel_programs_cleanup; } } } else if (nk_len == strlen("params_hex") && memcmp(nk, "params_hex", nk_len) == 0) { if (have_params_hex || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || !amduatd_parse_params_hex(sv, sv_len, &node.params)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid params_hex"); goto pel_programs_cleanup; } have_params_hex = true; } else { if (!amduatd_json_skip_value(&p, end, 0)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid node"); goto pel_programs_cleanup; } } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == '}') { p = cur + 1; break; } } if (!have_id || !have_op || !have_inputs || !have_params_hex) { free(node.inputs); free((void *)node.params.data); ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing node fields"); goto pel_programs_cleanup; } if (amduat_pel_kernel_op_lookup(node.op.name, node.op.version) == NULL) { free(node.inputs); free((void *)node.params.data); ok = amduatd_send_json_error(fd, 400, "Bad Request", "unknown op"); goto pel_programs_cleanup; } if (tmp.nodes_len == cap) { size_t next_cap = cap != 0 ? cap * 2u : 4u; amduat_pel_node_t *next = (amduat_pel_node_t *)realloc(tmp.nodes, next_cap * sizeof(*tmp.nodes)); if (next == NULL) { free(node.inputs); free((void *)node.params.data); ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_programs_cleanup; } tmp.nodes = next; cap = next_cap; } tmp.nodes[tmp.nodes_len++] = node; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == ']') { p = cur + 1; have_nodes = true; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid nodes"); goto pel_programs_cleanup; } } } else if (key_len == strlen("roots") && memcmp(key, "roots", key_len) == 0) { size_t cap = 0; if (have_roots) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "duplicate roots"); goto pel_programs_cleanup; } if (!amduatd_json_expect(&p, end, '[')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid roots"); goto pel_programs_cleanup; } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ']') { p = cur + 1; have_roots = true; } else { for (;;) { amduat_pel_root_ref_t root; bool have_node_id = false; bool have_output_index = false; memset(&root, 0, sizeof(root)); if (!amduatd_json_expect(&p, end, '{')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid root"); goto pel_programs_cleanup; } for (;;) { const char *rk = NULL; size_t rk_len = 0; uint32_t v = 0; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == '}') { p = cur + 1; break; } if (!amduatd_json_parse_string_noesc(&p, end, &rk, &rk_len) || !amduatd_json_expect(&p, end, ':')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid root"); goto pel_programs_cleanup; } if (rk_len == strlen("node_id") && memcmp(rk, "node_id", rk_len) == 0) { if (have_node_id || !amduatd_json_parse_u32(&p, end, &v)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid root node_id"); goto pel_programs_cleanup; } root.node_id = (amduat_pel_node_id_t)v; have_node_id = true; } else if (rk_len == strlen("output_index") && memcmp(rk, "output_index", rk_len) == 0) { if (have_output_index || !amduatd_json_parse_u32(&p, end, &v)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid root output_index"); goto pel_programs_cleanup; } root.output_index = v; have_output_index = true; } else { if (!amduatd_json_skip_value(&p, end, 0)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid root"); goto pel_programs_cleanup; } } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == '}') { p = cur + 1; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid root"); goto pel_programs_cleanup; } if (!have_node_id || !have_output_index) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing root fields"); goto pel_programs_cleanup; } if (tmp.roots_len == cap) { size_t next_cap = cap != 0 ? cap * 2u : 2u; amduat_pel_root_ref_t *next = (amduat_pel_root_ref_t *)realloc( tmp.roots, next_cap * sizeof(*tmp.roots)); if (next == NULL) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto pel_programs_cleanup; } tmp.roots = next; cap = next_cap; } tmp.roots[tmp.roots_len++] = root; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == ']') { p = cur + 1; have_roots = true; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid roots"); goto pel_programs_cleanup; } } } else { if (!amduatd_json_skip_value(&p, end, 0)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_programs_cleanup; } } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == '}') { p = cur + 1; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_programs_cleanup; } p = amduatd_json_skip_ws(p, end); if (p != end) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto pel_programs_cleanup; } if (!have_nodes || !have_roots) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing nodes/roots"); goto pel_programs_cleanup; } } program.nodes = tmp.nodes; program.nodes_len = tmp.nodes_len; program.roots = tmp.roots; program.roots_len = tmp.roots_len; if (!amduat_pel_program_dag_validate(&program)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid program"); goto pel_programs_cleanup; } if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "encode error"); goto pel_programs_cleanup; } artifact = amduat_artifact_with_type(program_bytes, amduat_type_tag(AMDUAT_PEL_TYPE_TAG_PROGRAM_DAG_1)); if (amduat_asl_store_put(store, artifact, &program_ref) != AMDUAT_ASL_STORE_OK) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "store error"); goto pel_programs_cleanup; } if (!amduat_asl_ref_encode_hex(program_ref, &ref_hex)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "encode ref error"); goto pel_programs_cleanup; } { int n = snprintf(json, sizeof(json), "{\"program_ref\":\"%s\"}\n", ref_hex); free(ref_hex); if (n <= 0 || (size_t)n >= sizeof(json)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); goto pel_programs_cleanup; } ok = amduatd_http_send_json(fd, 200, "OK", json, false); goto pel_programs_cleanup; } pel_programs_cleanup: free(body); free((void *)program_bytes.data); amduat_reference_free(&program_ref); amduatd_pel_program_tmp_free(&tmp); return ok; } static bool amduatd_handle_get_ui(int fd) { return amduatd_http_send_status(fd, 200, "OK", "text/html; charset=utf-8", (const uint8_t *)k_amduatd_ui_html, strlen(k_amduatd_ui_html), false); } static bool amduatd_handle_post_concepts(int fd, amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, amduatd_concepts_t *concepts, const amduatd_http_req_t *req) { uint8_t *body = NULL; const char *p = NULL; const char *end = NULL; char *name = NULL; bool have_name = false; bool have_ref = false; amduat_reference_t target_ref; amduat_reference_t name_ref; amduat_reference_t concept_ref; amduat_reference_t edge_ref; char *concept_hex = NULL; char json[4096]; bool ok = false; memset(&target_ref, 0, sizeof(target_ref)); memset(&name_ref, 0, sizeof(name_ref)); memset(&concept_ref, 0, sizeof(concept_ref)); memset(&edge_ref, 0, sizeof(edge_ref)); if (store == NULL || cfg == NULL || concepts == NULL || req == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "internal error"); } if (req->content_length == 0) { return amduatd_send_json_error(fd, 400, "Bad Request", "missing body"); } if (req->content_length > (256u * 1024u)) { return amduatd_send_json_error(fd, 413, "Payload Too Large", "payload too large"); } body = (uint8_t *)malloc(req->content_length); if (body == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); } if (!amduatd_read_exact(fd, body, req->content_length)) { free(body); return false; } p = (const char *)body; end = (const char *)body + req->content_length; if (!amduatd_json_expect(&p, end, '{')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto concepts_cleanup; } for (;;) { const char *key = NULL; size_t key_len = 0; const char *sv = NULL; size_t sv_len = 0; const char *cur = NULL; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == '}') { p = cur + 1; break; } if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) || !amduatd_json_expect(&p, end, ':')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto concepts_cleanup; } if (key_len == strlen("name") && memcmp(key, "name", key_len) == 0) { if (have_name || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || !amduatd_copy_json_str(sv, sv_len, &name)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); goto concepts_cleanup; } if (!amduatd_name_valid(name)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); goto concepts_cleanup; } have_name = true; } else if (key_len == strlen("ref") && memcmp(key, "ref", key_len) == 0) { if (have_ref || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || !amduatd_decode_ref_hex_str(sv, sv_len, &target_ref)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid ref"); goto concepts_cleanup; } have_ref = true; } else { if (!amduatd_json_skip_value(&p, end, 0)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto concepts_cleanup; } } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == '}') { p = cur + 1; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto concepts_cleanup; } if (!have_name) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing name"); goto concepts_cleanup; } if (!amduatd_concepts_put_name_artifact(store, name, &name_ref)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "store error"); goto concepts_cleanup; } { amduat_reference_t existing; memset(&existing, 0, sizeof(existing)); if (amduatd_concepts_lookup_alias(store, cfg, concepts, name, &existing)) { amduat_reference_free(&existing); ok = amduatd_send_json_error(fd, 409, "Conflict", "name exists"); goto concepts_cleanup; } } if (!amduatd_concepts_put_concept_id(store, cfg, &concept_ref)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "store error"); goto concepts_cleanup; } if (!amduatd_concepts_put_edge(store, concepts, name_ref, concept_ref, concepts->rel_aliases_ref, &edge_ref)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "store error"); goto concepts_cleanup; } if (have_ref) { if (!amduatd_concepts_put_edge(store, concepts, concept_ref, target_ref, concepts->rel_materializes_ref, &edge_ref)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "store error"); goto concepts_cleanup; } } if (!amduat_asl_ref_encode_hex(concept_ref, &concept_hex)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "encode error"); goto concepts_cleanup; } { int n = snprintf(json, sizeof(json), "{" "\"name\":\"%s\"," "\"concept_ref\":\"%s\"" "}\n", name, concept_hex); free(concept_hex); if (n <= 0 || (size_t)n >= sizeof(json)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); goto concepts_cleanup; } ok = amduatd_http_send_json(fd, 200, "OK", json, false); } concepts_cleanup: free(body); free(name); amduat_reference_free(&target_ref); amduat_reference_free(&name_ref); amduat_reference_free(&concept_ref); amduat_reference_free(&edge_ref); return ok; } static bool amduatd_path_extract_name(const char *path, const char *prefix, char *out, size_t cap) { size_t plen; const char *p; size_t len; if (out != NULL && cap != 0) { out[0] = '\0'; } if (path == NULL || prefix == NULL || out == NULL || cap == 0) { return false; } plen = strlen(prefix); if (strncmp(path, prefix, plen) != 0) { return false; } p = path + plen; if (*p == '\0') { return false; } len = strlen(p); if (len >= cap) { len = cap - 1; } memcpy(out, p, len); out[len] = '\0'; return true; } static bool amduatd_handle_post_concept_publish(int fd, amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, amduatd_concepts_t *concepts, const char *name, const amduatd_http_req_t *req) { uint8_t *body = NULL; const char *p = NULL; const char *end = NULL; amduat_reference_t target_ref; amduat_reference_t concept_ref; amduat_reference_t edge_ref; bool have_ref = false; bool ok = false; memset(&target_ref, 0, sizeof(target_ref)); memset(&concept_ref, 0, sizeof(concept_ref)); memset(&edge_ref, 0, sizeof(edge_ref)); if (store == NULL || cfg == NULL || concepts == NULL || name == NULL || req == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "internal error"); } if (!amduatd_name_valid(name)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); } if (req->content_length == 0) { return amduatd_send_json_error(fd, 400, "Bad Request", "missing body"); } if (req->content_length > (256u * 1024u)) { return amduatd_send_json_error(fd, 413, "Payload Too Large", "payload too large"); } if (!amduatd_concepts_lookup_alias(store, cfg, concepts, name, &concept_ref)) { return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept"); } body = (uint8_t *)malloc(req->content_length); if (body == NULL) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "oom"); goto publish_cleanup; } if (!amduatd_read_exact(fd, body, req->content_length)) { free(body); amduat_reference_free(&concept_ref); return false; } p = (const char *)body; end = (const char *)body + req->content_length; if (!amduatd_json_expect(&p, end, '{')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto publish_cleanup; } for (;;) { const char *key = NULL; size_t key_len = 0; const char *sv = NULL; size_t sv_len = 0; const char *cur = NULL; cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == '}') { p = cur + 1; break; } if (!amduatd_json_parse_string_noesc(&p, end, &key, &key_len) || !amduatd_json_expect(&p, end, ':')) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto publish_cleanup; } if (key_len == strlen("ref") && memcmp(key, "ref", key_len) == 0) { if (have_ref || !amduatd_json_parse_string_noesc(&p, end, &sv, &sv_len) || !amduatd_decode_ref_hex_str(sv, sv_len, &target_ref)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid ref"); goto publish_cleanup; } have_ref = true; } else { if (!amduatd_json_skip_value(&p, end, 0)) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto publish_cleanup; } } cur = amduatd_json_skip_ws(p, end); if (cur < end && *cur == ',') { p = cur + 1; continue; } if (cur < end && *cur == '}') { p = cur + 1; break; } ok = amduatd_send_json_error(fd, 400, "Bad Request", "invalid json"); goto publish_cleanup; } if (!have_ref) { ok = amduatd_send_json_error(fd, 400, "Bad Request", "missing ref"); goto publish_cleanup; } if (!amduatd_concepts_put_edge(store, concepts, concept_ref, target_ref, concepts->rel_materializes_ref, &edge_ref)) { ok = amduatd_send_json_error(fd, 500, "Internal Server Error", "store error"); goto publish_cleanup; } ok = amduatd_http_send_json(fd, 200, "OK", "{\"ok\":true}\n", false); publish_cleanup: free(body); amduat_reference_free(&target_ref); amduat_reference_free(&concept_ref); amduat_reference_free(&edge_ref); return ok; } static bool amduatd_handle_get_resolve(int fd, amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, const amduatd_concepts_t *concepts, const char *name) { amduat_reference_t concept_ref; amduat_reference_t latest_ref; char *hex = NULL; char json[2048]; int n; memset(&concept_ref, 0, sizeof(concept_ref)); memset(&latest_ref, 0, sizeof(latest_ref)); if (store == NULL || cfg == NULL || concepts == NULL || name == NULL) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "internal error"); } if (!amduatd_name_valid(name)) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid name"); } if (!amduatd_concepts_lookup_alias(store, cfg, concepts, name, &concept_ref)) { return amduatd_send_json_error(fd, 404, "Not Found", "unknown concept"); } if (!amduatd_concepts_resolve_latest(store, concepts, concept_ref, &latest_ref)) { amduat_reference_free(&concept_ref); return amduatd_send_json_error(fd, 404, "Not Found", "no versions"); } amduat_reference_free(&concept_ref); if (!amduat_asl_ref_encode_hex(latest_ref, &hex)) { amduat_reference_free(&latest_ref); return amduatd_send_json_error(fd, 500, "Internal Server Error", "encode error"); } amduat_reference_free(&latest_ref); n = snprintf(json, sizeof(json), "{\"ref\":\"%s\"}\n", hex); free(hex); if (n <= 0 || (size_t)n >= sizeof(json)) { return amduatd_send_json_error(fd, 500, "Internal Server Error", "error"); } return amduatd_http_send_json(fd, 200, "OK", json, false); } static bool amduatd_handle_conn(int fd, amduat_asl_store_t *store, const amduat_asl_store_fs_config_t *cfg, amduat_reference_t api_contract_ref, amduatd_concepts_t *concepts) { amduatd_http_req_t req; char no_query[1024]; if (!amduatd_http_parse_request(fd, &req)) { return false; } amduatd_path_without_query(req.path, no_query, sizeof(no_query)); if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/ui") == 0) { return amduatd_handle_get_ui(fd); } if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/meta") == 0) { return amduatd_handle_meta(fd, cfg, api_contract_ref, false); } if (strcmp(req.method, "HEAD") == 0 && strcmp(no_query, "/v1/meta") == 0) { return amduatd_handle_meta(fd, cfg, api_contract_ref, true); } if (strcmp(req.method, "GET") == 0 && strcmp(no_query, "/v1/contract") == 0) { return amduatd_handle_get_contract(fd, store, &req, api_contract_ref); } if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/artifacts") == 0) { return amduatd_handle_post_artifacts(fd, store, &req); } if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/pel/run") == 0) { return amduatd_handle_post_pel_run(fd, store, cfg, concepts, &req); } if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/pel/programs") == 0) { return amduatd_handle_post_pel_programs(fd, store, &req); } if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/concepts") == 0) { return amduatd_handle_post_concepts(fd, store, cfg, concepts, &req); } if (strcmp(req.method, "POST") == 0 && strncmp(no_query, "/v1/concepts/", 13) == 0 && strstr(no_query, "/publish") != NULL) { char name[256]; char *slash; if (!amduatd_path_extract_name(no_query, "/v1/concepts/", name, sizeof(name))) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid path"); } slash = strstr(name, "/publish"); if (slash == NULL || strcmp(slash, "/publish") != 0) { return amduatd_send_json_error(fd, 400, "Bad Request", "invalid path"); } *slash = '\0'; return amduatd_handle_post_concept_publish(fd, store, cfg, concepts, name, &req); } if (strcmp(req.method, "GET") == 0 && strncmp(no_query, "/v1/resolve/", 12) == 0) { const char *name = no_query + 12; if (name[0] == '\0') { return amduatd_send_json_error(fd, 400, "Bad Request", "missing name"); } return amduatd_handle_get_resolve(fd, store, cfg, concepts, name); } if (strcmp(req.method, "GET") == 0 && strncmp(no_query, "/v1/artifacts/", 14) == 0) { return amduatd_handle_get_artifact(fd, store, &req, req.path, false); } if (strcmp(req.method, "HEAD") == 0 && strncmp(no_query, "/v1/artifacts/", 14) == 0) { return amduatd_handle_head_artifact(fd, store, &req, req.path); } return amduatd_http_send_not_found(fd, &req); } static void amduatd_print_usage(FILE *stream) { fprintf(stream, "usage:\n" " amduatd [--root PATH] [--sock PATH]\n" "\n" "defaults:\n" " --root %s\n" " --sock %s\n", AMDUATD_DEFAULT_ROOT, AMDUATD_DEFAULT_SOCK); } int main(int argc, char **argv) { const char *root = AMDUATD_DEFAULT_ROOT; const char *sock_path = AMDUATD_DEFAULT_SOCK; amduat_asl_store_fs_config_t cfg; amduat_asl_store_fs_t fs; amduat_asl_store_t store; amduat_reference_t api_contract_ref; amduatd_concepts_t concepts; int i; int sfd = -1; memset(&api_contract_ref, 0, sizeof(api_contract_ref)); memset(&concepts, 0, sizeof(concepts)); for (i = 1; i < argc; ++i) { if (strcmp(argv[i], "--root") == 0) { if (i + 1 >= argc) { fprintf(stderr, "error: --root requires a path\n"); return 2; } root = argv[++i]; } else if (strcmp(argv[i], "--sock") == 0) { if (i + 1 >= argc) { fprintf(stderr, "error: --sock requires a path\n"); return 2; } sock_path = argv[++i]; } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { amduatd_print_usage(stdout); return 0; } else { fprintf(stderr, "error: unknown option: %s\n", argv[i]); return 2; } } memset(&cfg, 0, sizeof(cfg)); if (!amduat_asl_store_fs_load_config(root, &cfg)) { fprintf(stderr, "error: failed to load store config: %s\n", root); return 8; } memset(&fs, 0, sizeof(fs)); if (!amduat_asl_store_fs_init(&fs, cfg.config, root)) { fprintf(stderr, "error: failed to initialize store: %s\n", root); return 8; } amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); if (!amduatd_seed_api_contract(&store, &cfg, &api_contract_ref)) { fprintf(stderr, "error: failed to seed api contract\n"); return 8; } if (!amduatd_concepts_init(&concepts, &store, &cfg, root)) { fprintf(stderr, "error: failed to init concept edges\n"); return 8; } signal(SIGINT, amduatd_on_signal); signal(SIGTERM, amduatd_on_signal); sfd = socket(AF_UNIX, SOCK_STREAM, 0); if (sfd < 0) { perror("socket"); return 8; } { struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; if (strlen(sock_path) >= sizeof(addr.sun_path)) { fprintf(stderr, "error: socket path too long\n"); close(sfd); return 2; } strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path) - 1); (void)unlink(sock_path); if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { perror("bind"); close(sfd); return 8; } (void)chmod(sock_path, 0660); if (listen(sfd, 16) < 0) { perror("listen"); (void)unlink(sock_path); close(sfd); return 8; } } fprintf(stderr, "amduatd: root=%s sock=%s\n", root, sock_path); while (!amduatd_should_exit) { int cfd = accept(sfd, NULL, NULL); if (cfd < 0) { if (errno == EINTR) { continue; } perror("accept"); break; } (void)amduatd_handle_conn(cfd, &store, &cfg, api_contract_ref, &concepts); (void)close(cfd); } amduat_reference_free(&api_contract_ref); amduatd_concepts_free(&concepts); (void)unlink(sock_path); (void)close(sfd); return 0; }