Signed-off-by: Andrew Gregory <[email protected]>
---

* split read/write sections into _alpm_chroot_{write_to_child,read_from_child}
* fully spell out parent2child/child2parent
* replaced pthread_sigmask with sigaction
* renamed p2c_cb and p2c_ctx -> stdin_cb and stdin_ctx and updated doxygen
* stdin_ctx was left as void* rather than alpm_list_t* because
  void* is traditional for caller-supplied callback context

 lib/libalpm/hook.c  |   2 +-
 lib/libalpm/trans.c |   2 +-
 lib/libalpm/util.c  | 199 +++++++++++++++++++++++++++++++++++++++++++++-------
 lib/libalpm/util.h  |   5 +-
 4 files changed, 179 insertions(+), 29 deletions(-)

diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c
index bff072d..6d238d8 100644
--- a/lib/libalpm/hook.c
+++ b/lib/libalpm/hook.c
@@ -512,7 +512,7 @@ static int _alpm_hook_run_hook(alpm_handle_t *handle, 
struct _alpm_hook_t *hook)
                }
        }
 
-       return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd);
+       return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, 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..948fd47 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,119 @@ ssize_t _alpm_files_in_directory(alpm_handle_t *handle, 
const char *path,
        return files;
 }
 
+static int _alpm_chroot_write_to_child(alpm_handle_t *handle, int fd,
+               char *buf, ssize_t *buf_size, ssize_t buf_limit,
+               _alpm_cb_io out_cb, void *cb_ctx)
+{
+       ssize_t nwrite;
+       struct sigaction newaction, oldaction;
+
+       if(*buf_size == 0) {
+               /* empty buffer, ask the callback for more */
+               if((*buf_size = out_cb(buf, buf_limit, cb_ctx)) == 0) {
+                       /* no more to write, close the pipe */
+                       return -1;
+               }
+       }
+
+       /* ignore SIGPIPE in case the pipe has been closed */
+       newaction.sa_handler = SIG_IGN;
+       sigemptyset(&newaction.sa_mask);
+       newaction.sa_flags = 0;
+       sigaction(SIGPIPE, &newaction, &oldaction);
+
+       nwrite = write(fd, buf, *buf_size);
+
+       /* restore previous SIGPIPE handler */
+       sigaction(SIGPIPE, &oldaction, NULL);
+
+       if(nwrite != -1) {
+               /* write was successful, remove the written data from the 
buffer */
+               *buf_size -= nwrite;
+               memmove(buf, buf + nwrite, *buf_size);
+       } else if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+               /* nothing written, try again later */
+       } else {
+               _alpm_log(handle, ALPM_LOG_ERROR,
+                               _("unable to write to pipe (%s)\n"), 
strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+static void _alpm_chroot_process_output(alpm_handle_t *handle, const char 
*line)
+{
+       alpm_event_scriptlet_info_t event = {
+               .type = ALPM_EVENT_SCRIPTLET_INFO,
+               .line = line
+       };
+       alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line);
+       EVENT(handle, &event);
+}
+
+static int _alpm_chroot_read_from_child(alpm_handle_t *handle, int fd,
+               char *buf, ssize_t *buf_size, ssize_t buf_limit)
+{
+       ssize_t space = buf_limit - *buf_size - 2; /* reserve 2 for "\n\0" */
+       ssize_t nread = read(fd, buf + *buf_size, space);
+       if(nread > 0) {
+               char *newline = memchr(buf + *buf_size, '\n', nread);
+               *buf_size += nread;
+               if(newline) {
+                       while(newline) {
+                               size_t linelen = newline - buf + 1;
+                               char old = buf[linelen];
+                               buf[linelen] = '\0';
+                               _alpm_chroot_process_output(handle, buf);
+                               buf[linelen] = old;
+
+                               *buf_size -= linelen;
+                               memmove(buf, buf + linelen, *buf_size);
+                               newline = memchr(buf, '\n', *buf_size);
+                       }
+               } else if(nread == space) {
+                       /* we didn't read a full line, but we're out of space */
+                       strcpy(buf + *buf_size, "\n");
+                       _alpm_chroot_process_output(handle, buf);
+                       *buf_size = 0;
+               }
+       } else if(nread == 0) {
+               /* end-of-file */
+               if(*buf_size) {
+                       strcpy(buf + *buf_size, "\n");
+                       _alpm_chroot_process_output(handle, buf);
+               }
+               return -1;
+       } else if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+               /* nothing read, try again */
+       } else {
+               /* read error */
+               if(*buf_size) {
+                       strcpy(buf + *buf_size, "\n");
+                       _alpm_chroot_process_output(handle, buf);
+               }
+               _alpm_log(handle, ALPM_LOG_ERROR,
+                               _("unable to read from pipe (%s)\n"), 
strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
 /** 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
+ * @param stdin_cb callback to provide input to the chroot on stdin
+ * @param stdin_ctx context to be passed to @a stdin_cb
  * @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 stdin_cb, void *stdin_ctx)
 {
        pid_t pid;
-       int pipefd[2], cwdfd;
+       int child2parent_pipefd[2], parent2child_pipefd[2];
+       int cwdfd;
        int retval = 0;
 
        /* save the cwd so we can restore it later */
@@ -476,7 +580,13 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char 
*cmd, char *const argv[])
        /* Flush open fds before fork() to avoid cloning buffers */
        fflush(NULL);
 
-       if(pipe(pipefd) == -1) {
+       if(pipe(child2parent_pipefd) == -1) {
+               _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe 
(%s)\n"), strerror(errno));
+               retval = 1;
+               goto cleanup;
+       }
+
+       if(stdin_cb && pipe(parent2child_pipefd) == -1) {
                _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe 
(%s)\n"), strerror(errno));
                retval = 1;
                goto cleanup;
@@ -495,10 +605,15 @@ int _alpm_run_chroot(alpm_handle_t *handle, const char 
*cmd, char *const argv[])
                close(0);
                close(1);
                close(2);
-               while(dup2(pipefd[1], 1) == -1 && errno == EINTR);
-               while(dup2(pipefd[1], 2) == -1 && errno == EINTR);
-               close(pipefd[0]);
-               close(pipefd[1]);
+               while(dup2(child2parent_pipefd[1], 1) == -1 && errno == EINTR);
+               while(dup2(child2parent_pipefd[1], 2) == -1 && errno == EINTR);
+               if(stdin_cb) {
+                       while(dup2(parent2child_pipefd[0], 0) == -1 && errno == 
EINTR);
+                       close(parent2child_pipefd[0]);
+                       close(parent2child_pipefd[1]);
+               }
+               close(child2parent_pipefd[0]);
+               close(child2parent_pipefd[1]);
                if(cwdfd >= 0) {
                        close(cwdfd);
                }
@@ -521,27 +636,59 @@ 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;
-
-               close(pipefd[1]);
-               pipe_file = fdopen(pipefd[0], "r");
-               if(pipe_file == NULL) {
-                       close(pipefd[0]);
-                       retval = 1;
+               char obuf[PIPE_BUF]; /* writes <= PIPE_BUF are guaranteed 
atomic */
+               char ibuf[LINE_MAX];
+               ssize_t olen = 0, ilen = 0;
+               nfds_t nfds = 2;
+               struct pollfd fds[2], *child2parent = &(fds[0]), *parent2child 
= &(fds[1]);
+
+               child2parent->fd = child2parent_pipefd[0];
+               child2parent->events = POLLIN;
+               fcntl(child2parent->fd, F_SETFL, O_NONBLOCK);
+               close(child2parent_pipefd[1]);
+
+               if(stdin_cb) {
+                       parent2child->fd = parent2child_pipefd[1];
+                       parent2child->events = POLLOUT;
+                       fcntl(parent2child->fd, F_SETFL, O_NONBLOCK);
+                       close(parent2child_pipefd[0]);
                } else {
-                       while(!feof(pipe_file)) {
-                               char line[PATH_MAX];
-                               alpm_event_scriptlet_info_t event = {
-                                       .type = ALPM_EVENT_SCRIPTLET_INFO,
-                                       .line = line
-                               };
-                               if(safe_fgets(line, PATH_MAX, pipe_file) == 
NULL) {
-                                       break;
+                       parent2child->fd = -1;
+                       parent2child->events = 0;
+               }
+
+#define STOP_POLLING(p) do { close(p->fd); p->fd = -1; } while(0)
+
+               while((child2parent->fd != -1 || parent2child->fd != -1)
+                               && poll(fds, nfds, -1) > 0) {
+                       if(child2parent->revents & POLLIN) {
+                               if(_alpm_chroot_read_from_child(handle, 
child2parent->fd,
+                                                       ibuf, &ilen, 
sizeof(ibuf)) != 0) {
+                                       /* we encountered end-of-file or an 
error */
+                                       STOP_POLLING(child2parent);
                                }
-                               alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", 
line);
-                               EVENT(handle, &event);
+                       } else if(child2parent->revents) {
+                               /* anything but POLLIN indicates an error */
+                               STOP_POLLING(child2parent);
                        }
-                       fclose(pipe_file);
+                       if(parent2child->revents & POLLOUT) {
+                               if(_alpm_chroot_write_to_child(handle, 
parent2child->fd, obuf, &olen,
+                                                       sizeof(obuf), stdin_cb, 
stdin_ctx) != 0) {
+                                       STOP_POLLING(parent2child);
+                               }
+                       } else if(parent2child->revents) {
+                               /* anything but POLLOUT indicates an error */
+                               STOP_POLLING(parent2child);
+                       }
+               }
+
+#undef STOP_POLLING
+
+               if(parent2child->fd != -1) {
+                       close(parent2child->fd);
+               }
+               if(child2parent->fd != -1) {
+                       close(child2parent->fd);
                }
 
                while(waitpid(pid, &status, 0) == -1) {
@@ -605,7 +752,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.2

Reply via email to