diff --git a/sys-utils/libcmt/Makefile b/sys-utils/libcmt/Makefile index 969aea88..b84e75f7 100644 --- a/sys-utils/libcmt/Makefile +++ b/sys-utils/libcmt/Makefile @@ -59,6 +59,9 @@ $(ioctl_OBJDIR)io_echo: src/tests/io_echo.c $(ioctl_LIB) $(ioctl_OBJDIR)rollup_echo: src/tests/rollup_echo.c $(ioctl_LIB) $(TARGET_CC) $(TARGET_CFLAGS) -o $@ $^ +$(ioctl_OBJDIR)dehash: src/tests/dehash.c $(ioctl_LIB) + $(TARGET_CC) $(TARGET_CFLAGS) -o $@ $^ + ioctl.build: $(ioctl_LIB) $(ioctl_OBJDIR)io_echo $(ioctl_OBJDIR)rollup_echo ioctl.install: $(ioctl_LIB) mkdir -p $(TARGET_DESTDIR)$(TARGET_PREFIX)/lib @@ -78,6 +81,7 @@ mock_SRC := \ src/merkle.c \ src/merkle-table.c \ src/rollup.c \ + src/mock/hook.c \ src/mock/io.c mock_OBJDIR := build/mock/ @@ -97,6 +101,9 @@ $(mock_OBJDIR)io_echo: src/tests/io_echo.c $(mock_LIB) $(mock_OBJDIR)rollup_echo: src/tests/rollup_echo.c $(mock_LIB) $(CC) $(CFLAGS) -o $@ $^ +$(mock_OBJDIR)dehash: src/tests/dehash.c $(mock_LIB) + $(CC) $(CFLAGS) -o $@ $^ + mock.build: $(mock_LIB) $(mock_OBJDIR)io_echo $(mock_OBJDIR)rollup_echo mock.install: $(mock_LIB) diff --git a/sys-utils/libcmt/hook.lua b/sys-utils/libcmt/hook.lua new file mode 100755 index 00000000..6cf2454b --- /dev/null +++ b/sys-utils/libcmt/hook.lua @@ -0,0 +1,8 @@ +#!/usr/bin/env lua + +local line = io.read('*a') +io.stderr:write(string.format("%8s:%d req %s\n", + debug.getinfo(1).source, + debug.getinfo(1).currentline, + '>|' .. line .. '|<')) +io.write('that is all she wrote') diff --git a/sys-utils/libcmt/include/libcmt/io.h b/sys-utils/libcmt/include/libcmt/io.h index 7aa58873..af007fe3 100644 --- a/sys-utils/libcmt/include/libcmt/io.h +++ b/sys-utils/libcmt/include/libcmt/io.h @@ -102,6 +102,9 @@ typedef struct { int output_seq; int report_seq; int exception_seq; + + int hooks_num; + void *hooks; } cmt_io_driver_mock_t; /** Implementation specific cmio state. */ diff --git a/sys-utils/libcmt/src/mock/hook.c b/sys-utils/libcmt/src/mock/hook.c new file mode 100644 index 00000000..93eeb4c9 --- /dev/null +++ b/sys-utils/libcmt/src/mock/hook.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libcmt/io.h" + +typedef struct { + char *exe; + uint16_t key; +} hook_entry_t; + +static void *array_push(void **data, size_t m, size_t *n, size_t *max) { + if ((*n == *max) || (*data == NULL)) { + *max = *data == NULL ? 32 : *max * 2; + *data = realloc(*data, *max * m); + if (*data == NULL) { + perror("Failed to resize array with:"); + exit(1); + } + } + return &((uint8_t *) *data)[(*n)++ * m]; +} + +static int __hook_entry_cmp(const hook_entry_t *a, const hook_entry_t *b) { + return (int) a->key - (int) b->key; +} +static int hook_entry_cmp(const void *a, const void *b) { + return __hook_entry_cmp(a, b); +} + +static int write_whole_buffer(int wr, uint32_t n, cmt_buf_t *tx) { + ssize_t offset = 0; + ssize_t left = n; + + while (left) { + ssize_t ret = write(wr, tx->begin + offset, left); + if (ret >= 0) { + offset += ret; + left -= ret; + } else + switch (errno) { + case EINTR: + case EAGAIN: + continue; + default: + return -errno; + } + } + return 0; +} + +static int read_whole_buffer(int rd, uint32_t *n, cmt_buf_t *rx) { + ssize_t offset = 0; + ssize_t max = cmt_buf_length(rx); + + while (max) { + ssize_t ret = read(rd, rx->begin + offset, max); + if (ret == 0) + break; + if (ret > 0) { + offset += ret; + max -= ret; + } else + switch (errno) { + case EINTR: + case EAGAIN: + continue; + default: + return -errno; + } + } + *n = offset; + return 0; +} + +static int parent(cmt_io_driver_mock_t *me, struct cmt_io_yield *rr, pid_t child, int pipes[2][2]) { + int rc; + int rd = pipes[1][0]; + if (close(pipes[1][1]) < 0) + perror("Failed to close pipes[1][1]: "); + + int wr = pipes[0][1]; + if (close(pipes[0][0]) < 0) + perror("Failed to close pipes[0][0]: "); + + if ((rc = write_whole_buffer(wr, rr->data, me->tx))) + fprintf(stderr, "Failed to write whole buffer: %s\n", strerror(-rc)); + if (close(wr) < 0) + perror("Failed to close wr: "); + + if ((rc = read_whole_buffer(rd, &rr->data, me->rx))) + fprintf(stderr, "Failed to write whole buffer: %s\n", strerror(-rc)); + if (close(rd) < 0) + perror("Failed to close rd: "); + + if ((rc = waitpid(child, NULL, 0)) < 0) + return -errno; + return 0; +} + +static int child(int pipes[2][2], uint16_t key, char *exe) { + int rd = pipes[0][0]; + if (dup2(rd, STDIN_FILENO) < 0) + perror("dup2 failed: "); + if (close(pipes[0][1]) < 0) + perror("Failed close(pipes[0][1]): "); + + int wr = pipes[1][1]; + if (dup2(wr, STDOUT_FILENO) < 0) + perror("dup2 failed: "); + if (close(pipes[1][0]) < 0) + perror("Failed close(pipes[1][0]): "); + + char skey[16]; + snprintf(skey, sizeof skey, "%hu", key); + char *const argv[] = {exe, skey, NULL}; + char *const envp[] = {NULL}; + if (execve(argv[0], argv, envp) < 0) + perror("Failed: "); + return -errno; +} + +int cmt_io_driver_mock_hook_load(cmt_io_driver_mock_t *me, char *env) { + cmt_buf_t x; + cmt_buf_t xs; + hook_entry_t *hooks = NULL; + size_t n = 0, max = 0; + + if (!env) + goto end; + size_t envlen = strlen(env); + + cmt_buf_init(&xs, envlen, env); + while (cmt_buf_split_by_comma(&x, &xs)) { + uint16_t key = 0; + char exe[128] = ""; + + if (sscanf((char *) x.begin, "%hu:%127[^,]", &key, exe) != 2) { + fprintf(stderr, "Failed to parse: `%.*s', skipped.\n", (int) cmt_buf_length(&x), x.begin); + continue; + } + fprintf(stderr, "Adding hook: [%d] = \"%s\"\n", key, exe); + hook_entry_t *hook = array_push((void **) &hooks, sizeof hooks[0], &n, &max); + hook->key = key; + hook->exe = strdup(exe); + } + qsort(hooks, n, sizeof hooks[0], hook_entry_cmp); +end: + fprintf(stderr, "Added %lu hook%s\n", n, n > 1 ? "s" : ""); + me->hooks = hooks; + me->hooks_num = n; + return 0; +} + +int cmt_io_driver_mock_hook_dispatch(cmt_io_driver_mock_t *me, struct cmt_io_yield *rr) { + hook_entry_t key[1] = {{NULL, rr->reason}}; + hook_entry_t *hook = bsearch(key, me->hooks, me->hooks_num, sizeof key, hook_entry_cmp); + if (!hook) + return -ENOKEY; + + int pipes[2][2]; + if (pipe(pipes[0]) < 0) + perror("Failed to create pipes[0]"); + if (pipe(pipes[1]) < 0) + perror("Failed to create pipes[1]"); + + pid_t pid = fork(); + return pid ? parent(me, rr, pid, pipes) : child(pipes, hook->key, hook->exe); +} diff --git a/sys-utils/libcmt/src/mock/io.c b/sys-utils/libcmt/src/mock/io.c index 46f3ab70..f71253fd 100644 --- a/sys-utils/libcmt/src/mock/io.c +++ b/sys-utils/libcmt/src/mock/io.c @@ -7,6 +7,9 @@ #include "libcmt/io.h" +int cmt_io_driver_mock_hook_load(cmt_io_driver_mock_t *me, char *env); +int cmt_io_driver_mock_hook_dispatch(cmt_io_driver_mock_t *me, struct cmt_io_yield *rr); + static int read_whole_file(const char *name, size_t max, void *data, size_t *length); static int write_whole_file(const char *name, size_t length, const void *data); @@ -32,6 +35,8 @@ int cmt_io_init(cmt_io_driver_t *_me) { else cmt_buf_init(&me->inputs_left, 0, ""); + cmt_io_driver_mock_hook_load(me, getenv("CMT_HOOKS")); + // in case the user writes something before loading any input strcpy(me->input_filename, "none"); strcpy(me->input_fileext, ".bin"); @@ -51,6 +56,7 @@ void cmt_io_fini(cmt_io_driver_t *_me) { free(me->tx->begin); free(me->rx->begin); + free(me->hooks); } cmt_buf_t cmt_io_get_tx(cmt_io_driver_t *me) { @@ -99,10 +105,6 @@ static int load_next_input(cmt_io_driver_mock_t *me, struct cmt_io_yield *rr) { static int store_output(cmt_io_driver_mock_t *me, const char *filepath, struct cmt_io_yield *rr) { if (rr->data > cmt_buf_length(me->tx)) return -ENOBUFS; - - //char filepath[128 + 1 + 8 + 16]; - //snprintf(filepath, sizeof filepath, "%s.%s%d%s", me->input_filename, ns, *seq, me->input_fileext); - int rc = write_whole_file(filepath, rr->data, me->tx->begin); if (rc) { fprintf(stderr, "failed to store \"%s\". %s\n", filepath, strerror(-rc)); @@ -111,8 +113,6 @@ static int store_output(cmt_io_driver_mock_t *me, const char *filepath, struct c if (getenv("CMT_DEBUG")) { fprintf(stderr, "wrote filename: \"%s\" (%u)\n", filepath, rr->data); } - - //seq[0] += 1; return 0; } @@ -139,10 +139,10 @@ static int mock_rx_accepted(cmt_io_driver_mock_t *me, struct cmt_io_yield *rr) { } if (me->input_seq++) { // skip the first char filepath[128 + 32 + 8 + 16]; - snprintf(filepath, sizeof filepath, - "%s.outputs_root_hash%s", me->input_filename, me->input_fileext); + snprintf(filepath, sizeof filepath, "%s.outputs_root_hash%s", me->input_filename, me->input_fileext); int rc = store_output(me, filepath, rr); - if (rc) return rc; + if (rc) + return rc; } if (load_next_input(me, rr)) return -ENODATA; @@ -215,7 +215,7 @@ int cmt_io_yield(cmt_io_driver_t *_me, struct cmt_io_yield *rr) { case CMT_IO_REASON_TX_EXCEPTION: return mock_tx_exception(me, rr); default: - return -EINVAL; + return cmt_io_driver_mock_hook_dispatch(me, rr); } return 0; } diff --git a/sys-utils/libcmt/src/tests/dehash.c b/sys-utils/libcmt/src/tests/dehash.c new file mode 100644 index 00000000..cb0d109e --- /dev/null +++ b/sys-utils/libcmt/src/tests/dehash.c @@ -0,0 +1,26 @@ +#include +#include +#include "libcmt/rollup.h" + +int main() +{ + cmt_rollup_t rollup; + if (cmt_rollup_init(&rollup)) + return -1; + + cmt_buf_t tx[1] = {cmt_io_get_tx(rollup.io)}; + cmt_buf_t rx[1] = {cmt_io_get_rx(rollup.io)}; + + int m = snprintf((char *)tx->begin, cmt_buf_length(tx), "request"); + struct cmt_io_yield req[1] = {{ + .dev = CMT_IO_DEV, + .cmd = CMT_IO_CMD_MANUAL, + .reason = 10, + .data = m, + }}; + int rc = cmt_io_yield(rollup.io, req); + printf("%d:%s \"%.*s\"\n", rc, strerror(rc), req->data, (char *)rx->begin); + + return 0; +} +