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

Reply via email to