2025-12-22 15:23:54 +01:00
|
|
|
|
#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"
|
2025-12-22 19:37:41 +01:00
|
|
|
|
#include "amduat/asl/ref_derive.h"
|
2025-12-22 15:23:54 +01:00
|
|
|
|
#include "amduat/enc/asl1_core_codec.h"
|
2025-12-22 19:10:14 +01:00
|
|
|
|
#include "amduat/enc/pel1_result.h"
|
|
|
|
|
|
#include "amduat/format/pel.h"
|
|
|
|
|
|
#include "amduat/pel/program_dag_desc.h"
|
|
|
|
|
|
#include "amduat/pel/run.h"
|
2025-12-22 15:23:54 +01:00
|
|
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
#include <strings.h>
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
|
|
static const char *const AMDUATD_DEFAULT_ROOT = ".amduat-asl";
|
|
|
|
|
|
static const char *const AMDUATD_DEFAULT_SOCK = "amduatd.sock";
|
|
|
|
|
|
|
2025-12-22 19:37:41 +01:00
|
|
|
|
static const char k_amduatd_contract_v1_json[] =
|
|
|
|
|
|
"{"
|
|
|
|
|
|
"\"contract\":\"AMDUATD/API/1\","
|
|
|
|
|
|
"\"base_path\":\"/v1\","
|
|
|
|
|
|
"\"endpoints\":["
|
|
|
|
|
|
"{\"method\":\"GET\",\"path\":\"/v1/meta\"},"
|
|
|
|
|
|
"{\"method\":\"HEAD\",\"path\":\"/v1/meta\"},"
|
|
|
|
|
|
"{\"method\":\"GET\",\"path\":\"/v1/contract\"},"
|
|
|
|
|
|
"{\"method\":\"POST\",\"path\":\"/v1/artifacts\"},"
|
|
|
|
|
|
"{\"method\":\"GET\",\"path\":\"/v1/artifacts/{ref}\"},"
|
|
|
|
|
|
"{\"method\":\"HEAD\",\"path\":\"/v1/artifacts/{ref}\"},"
|
|
|
|
|
|
"{\"method\":\"POST\",\"path\":\"/v1/pel/run\"}"
|
|
|
|
|
|
"],"
|
|
|
|
|
|
"\"schemas\":{"
|
|
|
|
|
|
"\"pel_run_request\":{"
|
|
|
|
|
|
"\"type\":\"object\","
|
|
|
|
|
|
"\"required\":[\"program_ref\",\"input_refs\"],"
|
|
|
|
|
|
"\"properties\":{"
|
|
|
|
|
|
"\"program_ref\":{\"type\":\"string\",\"description\":\"hex ref\"},"
|
|
|
|
|
|
"\"input_refs\":{\"type\":\"array\",\"items\":{\"type\":\"string\",\"description\":\"hex ref\"}},"
|
|
|
|
|
|
"\"params_ref\":{\"type\":\"string\",\"description\":\"hex ref\"},"
|
|
|
|
|
|
"\"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\"}"
|
|
|
|
|
|
"}"
|
|
|
|
|
|
"}"
|
|
|
|
|
|
"}"
|
|
|
|
|
|
"}\n";
|
|
|
|
|
|
|
2025-12-22 15:23:54 +01:00
|
|
|
|
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) {
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 19:46:59 +01:00
|
|
|
|
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),
|
|
|
|
|
|
"<!doctype html>\n"
|
|
|
|
|
|
"<html lang=\"en\">\n"
|
|
|
|
|
|
"<head>\n"
|
|
|
|
|
|
" <meta charset=\"utf-8\" />\n"
|
|
|
|
|
|
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n"
|
|
|
|
|
|
" <title>amduatd — Not Found</title>\n"
|
|
|
|
|
|
" <style>\n"
|
|
|
|
|
|
" :root{color-scheme:light dark;}\n"
|
|
|
|
|
|
" body{font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif;"
|
|
|
|
|
|
" margin:0;line-height:1.4;}\n"
|
|
|
|
|
|
" .wrap{max-width:860px;margin:0 auto;padding:40px 20px;}\n"
|
|
|
|
|
|
" .card{border:1px solid rgba(127,127,127,.35);border-radius:14px;padding:22px;"
|
|
|
|
|
|
" background:rgba(127,127,127,.06);}\n"
|
|
|
|
|
|
" h1{margin:0 0 6px;font-size:22px;}\n"
|
|
|
|
|
|
" p{margin:8px 0;opacity:.9;}\n"
|
|
|
|
|
|
" code{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,monospace;"
|
|
|
|
|
|
" font-size:13px;}\n"
|
|
|
|
|
|
" ul{margin:10px 0 0;padding-left:18px;}\n"
|
|
|
|
|
|
" a{color:inherit;}\n"
|
|
|
|
|
|
" .muted{opacity:.75;}\n"
|
|
|
|
|
|
" </style>\n"
|
|
|
|
|
|
"</head>\n"
|
|
|
|
|
|
"<body>\n"
|
|
|
|
|
|
" <div class=\"wrap\">\n"
|
|
|
|
|
|
" <div class=\"card\">\n"
|
|
|
|
|
|
" <h1>404 — Not Found</h1>\n"
|
|
|
|
|
|
" <p>amduatd didn’t recognize <code>%s %s</code>.</p>\n"
|
|
|
|
|
|
" <p class=\"muted\">Try one of these:</p>\n"
|
|
|
|
|
|
" <ul>\n"
|
|
|
|
|
|
" <li><a href=\"/v1/meta\">/v1/meta</a></li>\n"
|
|
|
|
|
|
" <li><a href=\"/v1/contract\">/v1/contract</a></li>\n"
|
|
|
|
|
|
" </ul>\n"
|
|
|
|
|
|
" <p class=\"muted\">Artifact bytes: <code>/v1/artifacts/<ref></code></p>\n"
|
|
|
|
|
|
" </div>\n"
|
|
|
|
|
|
" </div>\n"
|
|
|
|
|
|
"</body>\n"
|
|
|
|
|
|
"</html>\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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 15:23:54 +01:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 19:10:14 +01:00
|
|
|
|
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_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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 15:23:54 +01:00
|
|
|
|
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,
|
2025-12-22 19:37:41 +01:00
|
|
|
|
amduat_reference_t api_contract_ref,
|
2025-12-22 15:23:54 +01:00
|
|
|
|
bool head_only) {
|
2025-12-22 19:37:41 +01:00
|
|
|
|
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);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 19:37:41 +01:00
|
|
|
|
static bool amduatd_handle_get_contract(int fd,
|
|
|
|
|
|
amduat_reference_t api_contract_ref) {
|
|
|
|
|
|
char *hex = NULL;
|
|
|
|
|
|
char json[2048];
|
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
|
|
if (api_contract_ref.hash_id == 0 || api_contract_ref.digest.data == NULL ||
|
|
|
|
|
|
api_contract_ref.digest.len == 0) {
|
|
|
|
|
|
return amduatd_http_send_json(fd, 404, "Not Found", "{\"ref\":null}\n",
|
|
|
|
|
|
false);
|
|
|
|
|
|
}
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 15:23:54 +01:00
|
|
|
|
static bool amduatd_handle_get_artifact(int fd,
|
|
|
|
|
|
amduat_asl_store_t *store,
|
2025-12-22 19:46:59 +01:00
|
|
|
|
const amduatd_http_req_t *req,
|
2025-12-22 15:23:54 +01:00
|
|
|
|
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) {
|
2025-12-22 19:46:59 +01:00
|
|
|
|
(void)head_only;
|
|
|
|
|
|
return amduatd_http_send_not_found(fd, req);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
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) {
|
2025-12-22 19:46:59 +01:00
|
|
|
|
if (!head_only) {
|
|
|
|
|
|
return amduatd_http_send_not_found(fd, req);
|
|
|
|
|
|
}
|
|
|
|
|
|
return amduatd_http_send_text(fd, 404, "Not Found", "not found\n", true);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
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,
|
2025-12-22 19:46:59 +01:00
|
|
|
|
const amduatd_http_req_t *req,
|
2025-12-22 15:23:54 +01:00
|
|
|
|
const char *path) {
|
2025-12-22 19:46:59 +01:00
|
|
|
|
return amduatd_handle_get_artifact(fd, store, req, path, true);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 19:10:14 +01:00
|
|
|
|
static bool amduatd_handle_post_pel_run(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;
|
|
|
|
|
|
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 (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) {
|
|
|
|
|
|
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) ||
|
|
|
|
|
|
!amduatd_decode_ref_hex_str(sv, sv_len, &program_ref)) {
|
|
|
|
|
|
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) {
|
|
|
|
|
|
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) ||
|
|
|
|
|
|
!amduatd_decode_ref_hex_str(sv, sv_len, ¶ms_ref)) {
|
|
|
|
|
|
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) ||
|
|
|
|
|
|
!amduatd_decode_ref_hex_str(sv, sv_len, &ref)) {
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 15:23:54 +01:00
|
|
|
|
static bool amduatd_handle_conn(int fd,
|
|
|
|
|
|
amduat_asl_store_t *store,
|
2025-12-22 19:37:41 +01:00
|
|
|
|
const amduat_asl_store_fs_config_t *cfg,
|
|
|
|
|
|
amduat_reference_t api_contract_ref) {
|
2025-12-22 15:23:54 +01:00
|
|
|
|
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/meta") == 0) {
|
2025-12-22 19:37:41 +01:00
|
|
|
|
return amduatd_handle_meta(fd, cfg, api_contract_ref, false);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
if (strcmp(req.method, "HEAD") == 0 && strcmp(no_query, "/v1/meta") == 0) {
|
2025-12-22 19:37:41 +01:00
|
|
|
|
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, api_contract_ref);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/artifacts") == 0) {
|
|
|
|
|
|
return amduatd_handle_post_artifacts(fd, store, &req);
|
|
|
|
|
|
}
|
2025-12-22 19:10:14 +01:00
|
|
|
|
if (strcmp(req.method, "POST") == 0 && strcmp(no_query, "/v1/pel/run") == 0) {
|
|
|
|
|
|
return amduatd_handle_post_pel_run(fd, store, &req);
|
|
|
|
|
|
}
|
2025-12-22 15:23:54 +01:00
|
|
|
|
if (strcmp(req.method, "GET") == 0 &&
|
|
|
|
|
|
strncmp(no_query, "/v1/artifacts/", 14) == 0) {
|
2025-12-22 19:46:59 +01:00
|
|
|
|
return amduatd_handle_get_artifact(fd, store, &req, req.path, false);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
if (strcmp(req.method, "HEAD") == 0 &&
|
|
|
|
|
|
strncmp(no_query, "/v1/artifacts/", 14) == 0) {
|
2025-12-22 19:46:59 +01:00
|
|
|
|
return amduatd_handle_head_artifact(fd, store, &req, req.path);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 19:46:59 +01:00
|
|
|
|
return amduatd_http_send_not_found(fd, &req);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2025-12-22 19:37:41 +01:00
|
|
|
|
amduat_reference_t api_contract_ref;
|
2025-12-22 15:23:54 +01:00
|
|
|
|
int i;
|
|
|
|
|
|
int sfd = -1;
|
|
|
|
|
|
|
2025-12-22 19:37:41 +01:00
|
|
|
|
memset(&api_contract_ref, 0, sizeof(api_contract_ref));
|
|
|
|
|
|
|
2025-12-22 15:23:54 +01:00
|
|
|
|
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);
|
|
|
|
|
|
|
2025-12-22 19:37:41 +01:00
|
|
|
|
if (!amduatd_seed_api_contract(&store, &cfg, &api_contract_ref)) {
|
|
|
|
|
|
fprintf(stderr, "error: failed to seed api contract\n");
|
|
|
|
|
|
return 8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 15:23:54 +01:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 19:37:41 +01:00
|
|
|
|
(void)amduatd_handle_conn(cfd, &store, &cfg, api_contract_ref);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
(void)close(cfd);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-22 19:37:41 +01:00
|
|
|
|
amduat_reference_free(&api_contract_ref);
|
2025-12-22 15:23:54 +01:00
|
|
|
|
(void)unlink(sock_path);
|
|
|
|
|
|
(void)close(sfd);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|