diff options
Diffstat (limited to 'src/bidilink.c')
-rw-r--r-- | src/bidilink.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/src/bidilink.c b/src/bidilink.c new file mode 100644 index 0000000..3f421bf --- /dev/null +++ b/src/bidilink.c @@ -0,0 +1,222 @@ +#define _GNU_SOURCE + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <sys/select.h> +#include <signal.h> +#include <inttypes.h> + +#include "stream.h" + +#define BUFSIZE (64*1024) + +volatile int quit = 0; + +static void signal_handler(int s) { + quit = 1; +} + +static void usage(FILE *f, const char *argv0) { + fprintf(f, "%s [STREAM1] [STREAM2]\n", argv0); +} + +static int read_fd(struct stream *s, void *buf, size_t *buf_fill, size_t *buf_index) { + ssize_t r; + assert(s && buf && buf_fill && buf_index); + + if ((r = read(s->input_fd, buf, BUFSIZE)) <= 0) { + + if (r == 0) + return 0; + + fprintf(stderr, "Failure to read(): %s\n", strerror(errno)); + return -1; + } + + *buf_fill = r; + *buf_index = 0; + return 1; +} + + +static int write_fd(struct stream *s, void *buf, size_t *buf_fill, size_t *buf_index) { + ssize_t r; + assert(s && buf && buf_fill && buf_index); + + if ((r = write(s->output_fd, buf+*buf_index, *buf_fill)) < 0) { + + if (r == EPIPE) + return 0; + + fprintf(stderr, "Failure to write(): %s\n", strerror(errno)); + return -1; + } + + *buf_fill -= r; + *buf_index += r; + return 1; +} + +const char *byte_str(uint64_t l) { + static char c[32]; + + if (l <= 1024) + snprintf(c, sizeof(c), "%llu B", l); + else if (l <= 1024*1024) + snprintf(c, sizeof(c), "%llu KB", l/1024); + else if (l <= 1024*1024*1024) + snprintf(c, sizeof(c), "%llu MB", l/1024/1024); + else + snprintf(c, sizeof(c), "%llu GB", l/1024/1024/1024); + + return c; +}; + +int main(int argc, char *argv[]) { + struct stream *a = NULL, *b = NULL; + + void *ab_buf = NULL, *ba_buf = NULL; + size_t ab_buf_fill = 0, ab_buf_index = 0, + ba_buf_fill = 0, ba_buf_index = 0; + + uint64_t ab_count = 0, ba_count = 0; + + int a_readable = 0, + a_writable = 0, + b_readable = 0, + b_writable = 0, + verbose = 0; + + int ret = -1; + int ai; + + if (argc < 2) { + usage(stderr, argv[0]); + ret = 0; + goto finish; + } + + signal(SIGINT, signal_handler); + siginterrupt(SIGINT, 1); + signal(SIGTERM, signal_handler); + siginterrupt(SIGTERM, 1); + signal(SIGPIPE, SIG_IGN); + + ai = 1; + + if (!strcmp(argv[ai], "-v")) { + verbose = 1; + ai++; + } + + if (!(a = stream_open(argv[ai++]))) + goto finish; + + if (!(b = stream_open(argc > ai ? argv[ai++] : NULL))) + goto finish; + + ab_buf = malloc(BUFSIZE); + assert(ab_buf); + ba_buf = malloc(BUFSIZE); + assert(ba_buf); + + for (;;) { + fd_set ifds, ofds; + + + FD_ZERO(&ifds); + + if (!a_readable) + FD_SET(a->input_fd, &ifds); + if (!b_readable) + FD_SET(b->input_fd, &ifds); + + FD_ZERO(&ofds); + + if (!a_writable) + FD_SET(a->output_fd, &ofds); + if (!b_writable) + FD_SET(b->output_fd, &ofds); + + if (select(FD_SETSIZE, &ifds, &ofds, NULL, 0) < 0) { + if (errno == EINTR) + continue; + + fprintf(stderr, "select() failed: %s\n", strerror(errno)); + ret = -1; + break; + } + + if (FD_ISSET(a->input_fd, &ifds)) + a_readable = 1; + if (FD_ISSET(b->input_fd, &ifds)) + b_readable = 1; + + if (FD_ISSET(a->output_fd, &ofds)) + a_writable = 1; + if (FD_ISSET(b->output_fd, &ofds)) + b_writable = 1; + + + if (!ab_buf_fill && a_readable && b_writable) { + int r; + if ((r = read_fd(a, ab_buf, &ab_buf_fill, &ab_buf_index)) <= 0) { + ret = r < 0 ? 1 : 0; + break; + } + a_readable = 0; + + ab_count += ab_buf_fill; + } + + if (ab_buf_fill && b_writable) { + int r; + if ((r = write_fd(b, ab_buf, &ab_buf_fill, &ab_buf_index)) <= 0) { + ret = r < 0 ? 1 : 0; + break; + } + b_writable = 0; + } + + if (!ba_buf_fill && b_readable && a_writable) { + int r; + if ((r = read_fd(b, ba_buf, &ba_buf_fill, &ba_buf_index)) <= 0) { + ret = r < 0 ? 1 : 0; + break; + } + b_readable = 0; + ba_count += ba_buf_fill; + } + + if (ba_buf_fill && a_writable) { + int r; + if ((r = write_fd(a, ba_buf, &ba_buf_fill, &ba_buf_index)) <= 0) { + ret = r < 0 ? 1 : 0; + break; + } + a_writable = 0; + } + + if (verbose) { + fprintf(stderr, "A: %s; ", byte_str(ab_count)); + fprintf(stderr, "B: %s \r", byte_str(ba_count)); + } + } + +finish: + + free(ab_buf); + free(ba_buf); + + if (a) + stream_close(a); + if (b) + stream_close(b); + + return ret; + +} |