#include "handler.hpp"
#include <sys/wait.h>
#include <fcntl.h>
int handler(const char* bin, const char* arg, int wd, int outfd, char*& outbuf) {
outbuf = NULL;
static int nullfd = -1;
if (nullfd == -1) {
nullfd = open("/dev/null", O_RDWR);
if (nullfd == -1) {
return -errno;
}
}
int infd = -1;
if (outfd == -1) {
int fds[2];
if (pipe(fds) == -1) {
return -errno;
}
infd = fds[0];
outfd = fds[1];
}
const pid_t pid = fork();
if (pid == -1) {
return -errno;
}
if (pid == 0) {
dup2(nullfd, STDIN_FILENO);
dup2(nullfd, STDERR_FILENO);
dup2(outfd, STDOUT_FILENO);
if (fchdir(wd) == -1) {
_exit(1);
}
for (int i=3; i<getdtablesize(); ++i) {
close(i);
}
const char* argv[] = {
bin, arg, NULL
};
(void)execvpe(bin, (char* const*)argv, NULL);
_exit(1);
}
LOG_DEBUG("spawned #%d: '%s' '%s'", pid, bin, arg);
int rv = 0;
if (infd != -1) {
(void)close(outfd);
size_t len = 0;
size_t alloc_len = 0;
while (true) {
if (alloc_len - len < 4096) {
alloc_len += 4096;
outbuf = (char*)realloc(outbuf, alloc_len);
}
ssize_t r = read(infd, outbuf+len, alloc_len-len-1);
int e = errno;
errno = e;
if (r == -1) {
rv = -errno;
break;
} else if (r == 0) {
rv = len;
outbuf[len] = '\0';
break;
} else {
len += (size_t)r;
}
}
(void)close(infd);
}
int status;
if (waitpid(pid, &status, 0) == -1) {
rv = -errno;
} else if (WIFEXITED(status)) {
rv = (WEXITSTATUS(status) == 0)? rv: -EFAULT;
} else if (WIFSIGNALED(status)) {
rv = -EINTR;
} else {
rv = -EINVAL;
}
if (rv < 0 && outbuf) {
free(outbuf);
outbuf = NULL;
}
if (rv < 0) {
LOG_ERR(-rv, "'%s' done", bin);
} else {
LOG_DEBUG_ERR(0, "'%s' done: %d", bin, rv);
}
return rv;
}