242 lines
5.1 KiB
C
242 lines
5.1 KiB
C
|
#include <errno.h>
|
||
|
#include <limits.h>
|
||
|
#include <stddef.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <strings.h>
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
|
||
|
#include <sysexits.h>
|
||
|
#include <err.h>
|
||
|
|
||
|
#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);
|
||
|
}
|