From 28fe4e659f5f8a8dbf7094eb7e5d7e4cf82bf39b Mon Sep 17 00:00:00 2001 From: styan Date: Sun, 24 May 2020 01:42:33 +0000 Subject: [PATCH] Initial commit --- Makefile | 6 ++ example-gemini.sh | 16 +++ fdwrap.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 Makefile create mode 100644 example-gemini.sh create mode 100644 fdwrap.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4800256 --- /dev/null +++ b/Makefile @@ -0,0 +1,6 @@ +.POSIX: +default: all +all: fdwrap +fdwrap: fdwrap.c +clean: + rm -f fdwrap diff --git a/example-gemini.sh b/example-gemini.sh new file mode 100644 index 0000000..dec36c3 --- /dev/null +++ b/example-gemini.sh @@ -0,0 +1,16 @@ +#!/bin/sh +HOST="${HOST:-localhost}" +PORT="${PORT:-1965}" +while getopts h:p: opt; do + case "$opt" in + h) HOST="$OPTARG";; + p) PORT="$OPTARG";; + *) usage;; + esac +done +shift $((OPTIND - 1)) +for x in "$@"; do + fdwrap -f '3>stdin,stdin/dev/null" \ + sh -c 'printf %s\\r\\n "$1" >&3; cat' sh-gemini "$x" +done diff --git a/fdwrap.c b/fdwrap.c new file mode 100644 index 0000000..71760d6 --- /dev/null +++ b/fdwrap.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define FDPAIR_INITIALIZER { NULL, 0, { -1, -1 }, { -1, -1 } } +struct fdpair { + struct fdpair *next; + int direction; + int pipe[2]; + int fd[2]; +}; + +static int plumbfdpairs(struct fdpair *); +static ssize_t strtofdpairs(const char *, struct fdpair **); +static ssize_t strtofdpair(const char *, struct fdpair *); +static ssize_t strtofd(const char *, int *); + +int +main(int argc, char *argv[]) +{ + const char *wargv[] = { "sh", "-c", NULL, NULL }; + const char *wrapper = "exec cat"; + struct fdpair *pairs = NULL; + struct fdpair *p; + ssize_t r; + pid_t pid; + int nullfd; + int c; + + if (strtofdpairs("stdin<-stdout,stdout->stdin", &pairs) < 0) + err(EX_OSERR, "strtofdpairs"); + while ((c = getopt(argc, argv, "f:w:")) != -1) + switch (c) { + case 'w': + wrapper = optarg; + break; + case 'f': + r = strtofdpairs(optarg, &pairs); + if (r >= 0 && optarg[r] != '\0') { + errno = EINVAL; + r = -1; + } + if (r < 0) + err(EX_USAGE, "%s", optarg); + break; + default: + goto usage; + } + argc -= optind; + argv += optind; + wargv[2] = wrapper; + if ((nullfd = open("/dev/null", O_RDWR)) < 0) + err(EX_OSERR, "/dev/null"); + /* + * Reserve the desired descriptors so + * that socketpair(2) does not take them. + */ + for (p = pairs; p != NULL; p = p->next) + if ((p->fd[0] != STDIN_FILENO && + p->fd[0] != STDOUT_FILENO && + p->fd[0] != STDERR_FILENO && + dup2(nullfd, p->fd[0]) < 0) || + (p->fd[1] != STDIN_FILENO && + p->fd[1] != STDOUT_FILENO && + p->fd[1] != STDERR_FILENO && + dup2(nullfd, p->fd[1]) < 0)) + err(EX_OSERR, "dup2"); + (void)close(nullfd); + for (p = pairs; p != NULL; p = p->next) + if (socketpair(PF_LOCAL, SOCK_STREAM, 0, p->pipe) != 0) + err(EX_OSERR, "pipe"); + switch (pid = fork()) { + case -1: + err(EX_OSERR, "fork"); + case 0: + for (p = pairs; p != NULL; p = p->next) + if (p->direction) { + if (close(p->pipe[1]) != 0) + err(EX_OSERR, "close"); + if (dup2(p->pipe[0], p->fd[1]) < 0) + err(EX_OSERR, "dup2"); + if (close(p->pipe[0]) != 0) + err(EX_OSERR, "close"); + } else { + if (close(p->pipe[0]) != 0) + err(EX_OSERR, "close"); + if (dup2(p->pipe[1], p->fd[1]) < 0) + err(EX_OSERR, "dup2"); + if (close(p->pipe[1]) != 0) + err(EX_OSERR, "close"); + } + (void)execvp("sh", (char * const *)wargv); + err(EX_SOFTWARE, "execvp: sh -c %s", wrapper); + default: + for (p = pairs; p != NULL; p = p->next) + if (p->direction) { + if (close(p->pipe[0]) != 0) + err(EX_OSERR, "close"); + if (dup2(p->pipe[1], p->fd[0]) < 0) + err(EX_OSERR, "dup2"); + if (close(p->pipe[1]) != 0) + err(EX_OSERR, "close"); + } else { + if (close(p->pipe[1]) != 0) + err(EX_OSERR, "close"); + if (dup2(p->pipe[0], p->fd[0]) < 0) + err(EX_OSERR, "dup2"); + if (close(p->pipe[0]) != 0) + err(EX_OSERR, "close"); + } + (void)execvp(*argv, argv); + err(EX_SOFTWARE, "execvp: %s ...", *argv); + } + abort(); /* unreachable */ +usage: + (void)fprintf(stderr, + "usage: fdwrap [-f fd-pairs] [-w wrapper-command] " + "command [argument ...]\n"); + return (EX_USAGE); +} + +ssize_t +strtofdpairs(const char *s, struct fdpair **fdp) +{ + struct fdpair *p; + struct fdpair *n; + struct fdpair t = FDPAIR_INITIALIZER; + ssize_t r; + size_t i; + + for (i = 0; s[i] != '\0'; i += r + (s[i + r] == ',')) { + if ((r = strtofdpair(s + i, &t)) < 0) + return (-1); + for (p = NULL, n = *fdp; n != NULL; + p = n, n = n == NULL ? *fdp : n->next) + if (n->fd[0] == t.fd[0] || + n->fd[1] == t.fd[1]) { + if (p == NULL) + *fdp = n->next; + else + p->next = n->next; + free(n); + n = p; + } + if ((n = malloc(sizeof(*n))) == NULL) + return (-1); + *n = t; + if (p == NULL) + *fdp = n; + else + p->next = n; + } + if (i == 0) { + errno = EINVAL; + return (-1); + } + return (i); +} + +ssize_t +strtofdpair(const char *s, struct fdpair *fdp) +{ + int fd[2]; + ssize_t r; + size_t i; + int d = -1; + + if ((r = strtofd(s, &fd[0])) < 0) + return (-1); + i = r; + if (s[i] == '-' && s[i + 1] == '>') { + d = 1; + i += 2; + } else if (s[i] == '<' && s[i + 1] == '-') { + d = 0; + i += 2; + } else if (s[i] == '>') { + d = 1; + i++; + } else if (s[i] == '<') { + d = 0; + i++; + } + if (d == -1) { + errno = EINVAL; + return (-1); + } + if ((r = strtofd(s + i, &fd[1])) < 0) + return (-1); + i += r; + if (fdp != NULL) { + fdp->direction = d; + fdp->fd[0] = fd[0]; + fdp->fd[1] = fd[1]; + } + return (i); +} + +ssize_t +strtofd(const char *s, int *fdp) +{ + size_t i; + int fd = 0; + + if (strncasecmp(s, "stdin", 5) == 0) { + i = 5; + fd = STDIN_FILENO; + } else if (strncasecmp(s, "stdout", 6) == 0) { + i = 6; + fd = STDOUT_FILENO; + } else if (strncasecmp(s, "stderr", 6) == 0) { + i = 6; + fd = STDERR_FILENO; + } else { + for (i = 0; s[i] >= '0' && s[i] <= '9'; + fd = fd * 10 + (s[i] - '0'), i++) + if (fd > (INT_MAX - (s[i] - '0')) / 10) { + errno = ERANGE; + return (-1); + } + } + if (i == 0) { + errno = EINVAL; + return (-1); + } + if (fdp != NULL) + *fdp = fd; + return (i); +}