diff --git a/src/tools/amduat_pel_cli.c b/src/tools/amduat_pel_cli.c index 055caf0..b0033ee 100644 --- a/src/tools/amduat_pel_cli.c +++ b/src/tools/amduat_pel_cli.c @@ -53,6 +53,17 @@ enum { }; static const char *const AMDUAT_PEL_CLI_DEFAULT_ROOT = ".amduat-asl"; +static const char *const AMDUAT_PEL_CLI_EDGE_SCHEMA = "tgk/edge"; + +enum { + AMDUAT_PEL_CLI_EDGE_MAGIC_LEN = 8, + AMDUAT_PEL_CLI_EDGE_VERSION = 1 +}; + +static const uint8_t k_amduat_pel_cli_edge_magic[ + AMDUAT_PEL_CLI_EDGE_MAGIC_LEN] = { + 'A', 'S', 'L', 'E', 'D', 'G', '1', '\0' +}; typedef struct { const char *root; @@ -81,6 +92,7 @@ static void amduat_pel_cli_print_usage(FILE *stream) { " matcache Materialization cache tools (get, sid).\n" " ptr Named pointer tools (get, cas).\n" " scheme Scheme refs, type tags, profile IDs.\n" + " edges Edge record tools (dump).\n" " help Show help for a command.\n" "\n" "global options:\n" @@ -157,6 +169,9 @@ static void amduat_pel_cli_print_usage(FILE *stream) { " amduat-pel col view --name NAME --from OFFSET --limit N\n" " [--dump-program-ref]\n" "\n" + "edges:\n" + " amduat-pel edges dump [--limit N]\n" + "\n" "scheme:\n" " amduat-pel scheme show [--format text|json] [--ref-format hex|bytes]\n" " amduat-pel scheme dag-ref [--format hex|bytes]\n" @@ -3378,6 +3393,123 @@ static void amduat_pel_cli_program_free(amduat_pel_program_t *program) { program->roots_len = 0u; } +static bool amduat_pel_cli_read_u32_le(const uint8_t *data, + size_t len, + size_t *offset, + uint32_t *out) { + if (len - *offset < 4u) { + return false; + } + *out = (uint32_t)data[*offset] | + ((uint32_t)data[*offset + 1u] << 8) | + ((uint32_t)data[*offset + 2u] << 16) | + ((uint32_t)data[*offset + 3u] << 24); + *offset += 4u; + return true; +} + +static bool amduat_pel_cli_is_ascii(amduat_octets_t bytes) { + if (bytes.len == 0u || bytes.data == NULL) { + return false; + } + for (size_t i = 0u; i < bytes.len; ++i) { + uint8_t c = bytes.data[i]; + if (c < 0x20u || c > 0x7eu) { + return false; + } + } + return true; +} + +static bool amduat_pel_cli_edge_payload_decode(amduat_octets_t payload, + amduat_reference_t *out_src, + amduat_reference_t *out_dst, + char **out_rel) { + size_t offset = 0u; + uint32_t version = 0u; + uint32_t src_len = 0u; + uint32_t dst_len = 0u; + uint32_t rel_len = 0u; + uint8_t flags = 0u; + amduat_octets_t src_bytes; + amduat_octets_t dst_bytes; + char *rel = NULL; + + if (out_src == NULL || out_dst == NULL || out_rel == NULL) { + return false; + } + *out_src = amduat_reference(0u, amduat_octets(NULL, 0u)); + *out_dst = amduat_reference(0u, amduat_octets(NULL, 0u)); + *out_rel = NULL; + + if (payload.len < AMDUAT_PEL_CLI_EDGE_MAGIC_LEN + 4u + 1u || + payload.data == NULL) { + return false; + } + if (memcmp(payload.data, k_amduat_pel_cli_edge_magic, + AMDUAT_PEL_CLI_EDGE_MAGIC_LEN) != 0) { + return false; + } + offset += AMDUAT_PEL_CLI_EDGE_MAGIC_LEN; + if (!amduat_pel_cli_read_u32_le(payload.data, payload.len, &offset, + &version) || + version != AMDUAT_PEL_CLI_EDGE_VERSION) { + return false; + } + if (!amduat_pel_cli_read_u32_le(payload.data, payload.len, &offset, + &src_len)) { + return false; + } + if (payload.len - offset < src_len) { + return false; + } + src_bytes = amduat_octets(payload.data + offset, src_len); + offset += src_len; + if (!amduat_pel_cli_read_u32_le(payload.data, payload.len, &offset, + &dst_len)) { + return false; + } + if (payload.len - offset < dst_len) { + return false; + } + dst_bytes = amduat_octets(payload.data + offset, dst_len); + offset += dst_len; + if (!amduat_pel_cli_read_u32_le(payload.data, payload.len, &offset, + &rel_len)) { + return false; + } + if (payload.len - offset < rel_len + 1u) { + return false; + } + if (!amduat_pel_cli_is_ascii( + amduat_octets(payload.data + offset, rel_len))) { + return false; + } + rel = (char *)malloc(rel_len + 1u); + if (rel == NULL) { + return false; + } + memcpy(rel, payload.data + offset, rel_len); + rel[rel_len] = '\0'; + offset += rel_len; + flags = payload.data[offset++]; + (void)flags; + + if (offset != payload.len) { + free(rel); + return false; + } + if (!amduat_enc_asl1_core_decode_reference_v1(src_bytes, out_src) || + !amduat_enc_asl1_core_decode_reference_v1(dst_bytes, out_dst)) { + amduat_pel_cli_free_reference(out_src); + amduat_pel_cli_free_reference(out_dst); + free(rel); + return false; + } + *out_rel = rel; + return true; +} + static bool amduat_pel_cli_build_collection_view_program( amduat_pel_program_t *out_program) { static const char k_op_snapshot[] = "collection.snapshot_decode_v1"; @@ -3469,6 +3601,159 @@ static bool amduat_pel_cli_build_collection_view_program( return true; } +static bool amduat_pel_cli_collection_view( + amduat_asl_store_t *store, + amduat_asl_pointer_store_t *pointer_store, + const char *name, + uint64_t from, + uint32_t limit, + amduat_asl_collection_view_t *out_view) { + amduat_reference_t snapshot_ref; + amduat_reference_t log_head_ref; + amduat_reference_t none_ref; + amduat_reference_t params_ref; + amduat_reference_t program_ref; + amduat_reference_t input_refs[4]; + amduat_pel_program_t program; + amduat_octets_t program_bytes = amduat_octets(NULL, 0u); + amduat_pel_run_result_t run_result; + amduat_artifact_t view_artifact; + amduat_asl_collection_view_t view; + amduat_type_tag_t expected_type_tag; + amduat_asl_encoding_profile_id_t expected_profile; + char *snapshot_name = NULL; + char *log_head_name = NULL; + bool snapshot_exists = false; + bool log_head_exists = false; + bool ok = false; + + if (out_view == NULL) { + return false; + } + memset(out_view, 0, sizeof(*out_view)); + memset(&snapshot_ref, 0, sizeof(snapshot_ref)); + memset(&log_head_ref, 0, sizeof(log_head_ref)); + memset(&none_ref, 0, sizeof(none_ref)); + memset(¶ms_ref, 0, sizeof(params_ref)); + memset(&program_ref, 0, sizeof(program_ref)); + memset(&run_result, 0, sizeof(run_result)); + memset(&view, 0, sizeof(view)); + + if (store == NULL || pointer_store == NULL || name == NULL) { + return false; + } + if (!amduat_pel_cli_get_none_ref(store, &none_ref)) { + return false; + } + if (!amduat_pel_cli_build_collection_head_name(name, &snapshot_name) || + !amduat_pel_cli_build_collection_log_head_name(name, &log_head_name)) { + goto view_cleanup; + } + if (amduat_asl_pointer_get(pointer_store, snapshot_name, + &snapshot_exists, &snapshot_ref) != + AMDUAT_ASL_POINTER_OK) { + goto view_cleanup; + } + if (!snapshot_exists && + !amduat_reference_clone(none_ref, &snapshot_ref)) { + goto view_cleanup; + } + if (amduat_asl_pointer_get(pointer_store, log_head_name, + &log_head_exists, &log_head_ref) != + AMDUAT_ASL_POINTER_OK) { + goto view_cleanup; + } + if (!log_head_exists && + !amduat_reference_clone(none_ref, &log_head_ref)) { + goto view_cleanup; + } + if (!amduat_pel_cli_put_view_params(store, from, limit, ¶ms_ref)) { + goto view_cleanup; + } + if (!amduat_pel_cli_build_collection_view_program(&program)) { + goto view_cleanup; + } + if (!amduat_enc_pel_program_dag_encode_v1(&program, &program_bytes)) { + amduat_pel_cli_program_free(&program); + goto view_cleanup; + } + amduat_pel_cli_program_free(&program); + + if (!amduat_pel_program_dag_desc_get_program_binding(&expected_type_tag, + &expected_profile)) { + goto view_cleanup; + } + if (expected_profile != AMDUAT_PEL_ENC_PROGRAM_DAG_V1) { + goto view_cleanup; + } + { + amduat_artifact_t program_artifact = + amduat_artifact_with_type(program_bytes, expected_type_tag); + if (amduat_asl_store_put(store, program_artifact, &program_ref) != + AMDUAT_ASL_STORE_OK) { + goto view_cleanup; + } + } + + input_refs[0] = snapshot_ref; + input_refs[1] = log_head_ref; + input_refs[2] = params_ref; + input_refs[3] = params_ref; + + if (!amduat_pel_surf_run_with_result(store, + amduat_pel_program_dag_scheme_ref(), + program_ref, + input_refs, + 4u, + true, + params_ref, + &run_result)) { + goto view_cleanup; + } + if (!run_result.has_result_value || + run_result.result_value.core_result.status != AMDUAT_PEL_EXEC_STATUS_OK || + run_result.output_refs_len != 1u) { + goto view_cleanup; + } + + memset(&view_artifact, 0, sizeof(view_artifact)); + if (amduat_asl_store_get(store, run_result.output_refs[0], + &view_artifact) != AMDUAT_ASL_STORE_OK) { + goto view_cleanup; + } + if (!view_artifact.has_type_tag || + view_artifact.type_tag.tag_id != AMDUAT_TYPE_TAG_ASL_COLLECTION_VIEW_1 || + !amduat_asl_collection_view_decode_v1(view_artifact.bytes, &view)) { + amduat_artifact_free(&view_artifact); + goto view_cleanup; + } + amduat_artifact_free(&view_artifact); + + *out_view = view; + memset(&view, 0, sizeof(view)); + ok = true; + +view_cleanup: + amduat_asl_collection_view_free(&view); + 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); + amduat_pel_cli_free_reference(&snapshot_ref); + amduat_pel_cli_free_reference(&log_head_ref); + amduat_pel_cli_free_reference(&none_ref); + amduat_pel_cli_free_reference(¶ms_ref); + amduat_pel_cli_free_reference(&program_ref); + free((void *)program_bytes.data); + free(snapshot_name); + free(log_head_name); + return ok; +} + static int amduat_pel_cli_cmd_log_append( int argc, char **argv, @@ -4435,6 +4720,140 @@ static int amduat_pel_cli_cmd_col( return AMDUAT_PEL_CLI_EXIT_USAGE; } +static int amduat_pel_cli_cmd_edges_dump( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + uint32_t limit = UINT32_MAX; + amduat_asl_store_t store; + amduat_asl_store_fs_t fs; + amduat_asl_pointer_store_t pointer_store; + amduat_asl_collection_view_t view; + int i; + + if (global == NULL) { + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + + for (i = 0; i < argc; ++i) { + if (strcmp(argv[i], "--limit") == 0) { + unsigned long long tmp; + if (i + 1 >= argc) { + fprintf(stderr, "error: --limit requires a value\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + tmp = strtoull(argv[++i], NULL, 10); + if (tmp > UINT32_MAX) { + fprintf(stderr, "error: --limit out of range\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + limit = (uint32_t)tmp; + } else if (strcmp(argv[i], "--help") == 0 || + strcmp(argv[i], "-h") == 0) { + amduat_pel_cli_print_usage(stdout); + return AMDUAT_PEL_CLI_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + } + + if (!amduat_pel_cli_init_store(global->root, &store, &fs)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + if (!amduat_asl_pointer_store_init(&pointer_store, global->root)) { + fprintf(stderr, "error: failed to init pointer store\n"); + return AMDUAT_PEL_CLI_EXIT_CONFIG; + } + + memset(&view, 0, sizeof(view)); + if (!amduat_pel_cli_collection_view(&store, + &pointer_store, + "daemon/edges", + 0u, + limit, + &view)) { + fprintf(stderr, "error: failed to compute collection view\n"); + return AMDUAT_PEL_CLI_EXIT_STORE; + } + + for (size_t idx = 0u; idx < view.refs_len; ++idx) { + amduat_reference_t record_ref = view.refs[idx]; + amduat_asl_record_t record; + amduat_reference_t src_ref; + amduat_reference_t dst_ref; + char *rel = NULL; + amduat_asl_store_error_t err; + bool schema_ok = false; + + memset(&record, 0, sizeof(record)); + err = amduat_asl_record_store_get(&store, record_ref, &record); + if (err != AMDUAT_ASL_STORE_OK) { + continue; + } + if (record.schema.len == strlen(AMDUAT_PEL_CLI_EDGE_SCHEMA) && + memcmp(record.schema.data, + AMDUAT_PEL_CLI_EDGE_SCHEMA, + record.schema.len) == 0) { + schema_ok = true; + } + if (!schema_ok || + !amduat_pel_cli_edge_payload_decode(record.payload, + &src_ref, + &dst_ref, + &rel)) { + amduat_asl_record_free(&record); + continue; + } + amduat_asl_record_free(&record); + + fputs(rel, stdout); + fputc(' ', stdout); + if (!amduat_format_ref_write_text(stdout, src_ref, + global->ref_format)) { + fprintf(stderr, "error: failed to format ref\n"); + free(rel); + amduat_pel_cli_free_reference(&src_ref); + amduat_pel_cli_free_reference(&dst_ref); + amduat_asl_collection_view_free(&view); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + fputs(" -> ", stdout); + if (!amduat_format_ref_write_text(stdout, dst_ref, + global->ref_format)) { + fprintf(stderr, "error: failed to format ref\n"); + free(rel); + amduat_pel_cli_free_reference(&src_ref); + amduat_pel_cli_free_reference(&dst_ref); + amduat_asl_collection_view_free(&view); + return AMDUAT_PEL_CLI_EXIT_CODEC; + } + fputc('\n', stdout); + free(rel); + amduat_pel_cli_free_reference(&src_ref); + amduat_pel_cli_free_reference(&dst_ref); + } + + amduat_asl_collection_view_free(&view); + return AMDUAT_PEL_CLI_EXIT_OK; +} + +static int amduat_pel_cli_cmd_edges( + int argc, + char **argv, + const amduat_pel_cli_global_opts_t *global) { + if (argc < 1) { + fprintf(stderr, "error: edges requires a subcommand\n"); + return AMDUAT_PEL_CLI_EXIT_USAGE; + } + if (strcmp(argv[0], "dump") == 0) { + return amduat_pel_cli_cmd_edges_dump(argc - 1, argv + 1, global); + } + fprintf(stderr, "error: unknown edges subcommand: %s\n", argv[0]); + return AMDUAT_PEL_CLI_EXIT_USAGE; +} + static int amduat_pel_cli_cmd_help(int argc, char **argv) { (void)argc; (void)argv; @@ -4531,6 +4950,9 @@ int main(int argc, char **argv) { if (strcmp(command, "col") == 0) { return amduat_pel_cli_cmd_col(argc - i, argv + i, &global); } + if (strcmp(command, "edges") == 0) { + return amduat_pel_cli_cmd_edges(argc - i, argv + i, &global); + } if (strcmp(command, "matcache") == 0) { return amduat_pel_cli_cmd_matcache(argc - i, argv + i, &global); }