Allows callers to provide a callback to provide strings to pass to the chroot on stdin. --- lib/libalpm/hook.c | 2 +- lib/libalpm/trans.c | 2 +- lib/libalpm/util.c | 153 +++++++++++++++++++++++++++++++++++++++++++++------- lib/libalpm/util.h | 5 +- 4 files changed, 141 insertions(+), 21 deletions(-) diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c index fe4b204..3dd80be 100644 --- a/lib/libalpm/hook.c +++ b/lib/libalpm/hook.c @@ -388,7 +388,7 @@ static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook) } } - return _alpm_run_chroot(handle, hook->cmd, argv); + return _alpm_run_chroot(handle, hook->cmd, argv, NULL, NULL); } int _alpm_hook_run(alpm_handle_t *handle, enum _alpm_hook_when_t when) diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c index a6b1aef..06997a0 100644 --- a/lib/libalpm/trans.c +++ b/lib/libalpm/trans.c @@ -391,7 +391,7 @@ int _alpm_runscriptlet(alpm_handle_t *handle, const char *filepath, _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\"\n", cmdline); - retval = _alpm_run_chroot(handle, SCRIPTLET_SHELL, argv); + retval = _alpm_run_chroot(handle, SCRIPTLET_SHELL, argv, NULL, NULL); cleanup: if(scriptfn && unlink(scriptfn)) { diff --git a/lib/libalpm/util.c b/lib/libalpm/util.c index 66a2742..d55126c 100644 --- a/lib/libalpm/util.c +++ b/lib/libalpm/util.c @@ -31,6 +31,7 @@ #include <limits.h> #include <sys/wait.h> #include <fnmatch.h> +#include <poll.h> /* libarchive */ #include <archive.h> @@ -445,16 +446,30 @@ ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, return files; } +/* write wrapper that ignores SIGPIPE */ +static ssize_t _alpm_pipe_write(int fd, const void *buf, size_t count) +{ + sigset_t new, old; + ssize_t ret; + sigemptyset(&new); + sigaddset(&new, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &new, &old); + ret = write(fd, buf, count); + pthread_sigmask(SIG_SETMASK, &old, NULL); + return ret; +} + /** Execute a command with arguments in a chroot. * @param handle the context handle * @param cmd command to execute * @param argv arguments to pass to cmd * @return 0 on success, 1 on error */ -int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) +int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[], + _alpm_cb_io in_cb, void *in_ctx) { pid_t pid; - int pipefd[2], cwdfd; + int pipefd[2], ipipefd[2], cwdfd; int retval = 0; /* save the cwd so we can restore it later */ @@ -482,6 +497,12 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) goto cleanup; } + if(in_cb && pipe(ipipefd) == -1) { + _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno)); + retval = 1; + goto cleanup; + } + /* fork- parent and child each have separate code blocks below */ pid = fork(); if(pid == -1) { @@ -497,6 +518,11 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) close(2); while(dup2(pipefd[1], 1) == -1 && errno == EINTR); while(dup2(pipefd[1], 2) == -1 && errno == EINTR); + if(in_cb) { + while(dup2(ipipefd[0], 0) == -1 && errno == EINTR); + close(ipipefd[0]); + close(ipipefd[1]); + } close(pipefd[0]); close(pipefd[1]); if(cwdfd >= 0) { @@ -521,27 +547,118 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]) } else { /* this code runs for the parent only (wait on the child) */ int status; - FILE *pipe_file; - + char obuf[PIPE_BUF + 1], ibuf[PIPE_BUF + 1]; + ssize_t olen = 0, ilen = 0; + int in = pipefd[0], out = in_cb ? ipipefd[1] : -1; + struct pollfd fds[2]; + nfds_t nfds = 2; + int timeout = -1; + + fds[0].fd = in; + fds[0].events = POLLIN; + fcntl(in, F_SETFL, O_NONBLOCK); close(pipefd[1]); - pipe_file = fdopen(pipefd[0], "r"); - if(pipe_file == NULL) { - close(pipefd[0]); - retval = 1; - } else { - while(!feof(pipe_file)) { - char line[PATH_MAX]; + + fds[1].fd = out; + fds[1].events = POLLOUT; + if(in_cb) { + fcntl(out, F_SETFL, O_NONBLOCK); + close(ipipefd[0]); + } + + while((in != -1 || out != -1) && poll(fds, nfds, timeout) > 0) { + if(fds[0].revents & POLLIN) { alpm_event_scriptlet_info_t event = { .type = ALPM_EVENT_SCRIPTLET_INFO, - .line = line + .line = ibuf }; - if(safe_fgets(line, PATH_MAX, pipe_file) == NULL) { - break; + ssize_t space = PIPE_BUF - ilen; + ssize_t nread = read(in, ibuf + ilen, space); + if(nread > 0) { + char *newline = memchr(ibuf + ilen, '\n', nread); + ilen += nread; + if(newline) { + while(newline) { + size_t linelen = newline - ibuf + 1; + char old = ibuf[linelen]; + ibuf[linelen] = '\0'; + alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", ibuf); + EVENT(handle, &event); + ibuf[linelen] = old; + ilen -= linelen; + memmove(ibuf, ibuf + linelen, ilen); + newline = memchr(ibuf, '\n', ilen); + } + } else if(nread == space) { + /* we didn't read a full line, but we're out of space */ + ibuf[PIPE_BUF] = '\0'; + alpm_logaction(handle, "ALPM-SCRIPTLET", "%s\n", ibuf); + EVENT(handle, &event); + ilen = 0; + } + } else { + if(nread == -1 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) { + /* nothing read, try again */ + } else { + /* we either encountered an error or the pipe was closed */ + if(nread == -1) { + _alpm_log(handle, ALPM_LOG_ERROR, + _("unable to read from pipe (%s)\n"), strerror(errno)); + } + if(ilen) { + ibuf[ilen] = '\0'; + alpm_logaction(handle, "ALPM-SCRIPTLET", "%s\n", ibuf); + EVENT(handle, &event); + } + close(in); + in = -1; + fds[0].fd = -1; + } } - alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line); - EVENT(handle, &event); + } else if(fds[0].revents) { + close(in); + in = -1; + fds[0].fd = -1; } - fclose(pipe_file); + if(fds[1].revents) { + ssize_t nwrite; + if(olen == 0) { + olen = in_cb(obuf, PIPE_BUF, in_ctx); + if(olen == 0) { + /* no more to write, close the pipe */ + close(out); + out = -1; + fds[1].fd = -1; + continue; + } + } + if(olen && (nwrite = _alpm_pipe_write(out, obuf, olen)) == -1) { + if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { + /* nothing written, try again later */ + } else { + /* something went wrong, close the pipe */ + _alpm_log(handle, ALPM_LOG_ERROR, + _("unable to write to pipe (%s)\n"), strerror(errno)); + close(out); + out = -1; + fds[1].fd = -1; + } + } else { + olen -= nwrite; + memmove(obuf, obuf + nwrite, olen); + } + } else if(fds[1].revents) { + close(out); + out = -1; + fds[1].fd = -1; + } + } + + if(out != -1) { + close(out); + } + if(in != -1) { + close(in); } while(waitpid(pid, &status, 0) == -1) { @@ -605,7 +722,7 @@ int _alpm_ldconfig(alpm_handle_t *handle) char arg0[32]; char *argv[] = { arg0, NULL }; strcpy(arg0, "ldconfig"); - return _alpm_run_chroot(handle, LDCONFIG, argv); + return _alpm_run_chroot(handle, LDCONFIG, argv, NULL, NULL); } } diff --git a/lib/libalpm/util.h b/lib/libalpm/util.h index 95112cf..c0c9ff0 100644 --- a/lib/libalpm/util.h +++ b/lib/libalpm/util.h @@ -119,7 +119,10 @@ int _alpm_unpack(alpm_handle_t *handle, const char *archive, const char *prefix, ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path, int full_count); -int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[]); +typedef ssize_t (*_alpm_cb_io)(void *buf, ssize_t len, void *ctx); + +int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[], + _alpm_cb_io in_cb, void *in_ctx); int _alpm_ldconfig(alpm_handle_t *handle); int _alpm_str_cmp(const void *s1, const void *s2); char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename); -- 2.6.1