[pacman-dev] [PATCH 5/6v2] allow specifying input to scriptlets
Andrew Gregory
andrew.gregory.8 at gmail.com
Tue Oct 27 04:47:34 UTC 2015
Signed-off-by: Andrew Gregory <andrew.gregory.8 at gmail.com>
---
lib/libalpm/hook.c | 2 +-
lib/libalpm/trans.c | 2 +-
lib/libalpm/util.c | 174 ++++++++++++++++++++++++++++++++++++++++++++--------
lib/libalpm/util.h | 5 +-
4 files changed, 154 insertions(+), 29 deletions(-)
diff --git a/lib/libalpm/hook.c b/lib/libalpm/hook.c
index 45c10e1..b449ce0 100644
--- a/lib/libalpm/hook.c
+++ b/lib/libalpm/hook.c
@@ -511,7 +511,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..eed0293 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,40 @@ 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;
+}
+
+static void _alpm_handle_script_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);
+}
+
/** 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 p2c_cb, void *p2c_ctx)
{
pid_t pid;
- int pipefd[2], cwdfd;
+ int c2p_pipefd[2], p2c_pipefd[2], cwdfd;
int retval = 0;
/* save the cwd so we can restore it later */
@@ -476,7 +501,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(c2p_pipefd) == -1) {
+ _alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
+ retval = 1;
+ goto cleanup;
+ }
+
+ if(p2c_cb && pipe(p2c_pipefd) == -1) {
_alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
retval = 1;
goto cleanup;
@@ -495,10 +526,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(c2p_pipefd[1], 1) == -1 && errno == EINTR);
+ while(dup2(c2p_pipefd[1], 2) == -1 && errno == EINTR);
+ if(p2c_cb) {
+ while(dup2(p2c_pipefd[0], 0) == -1 && errno == EINTR);
+ close(p2c_pipefd[0]);
+ close(p2c_pipefd[1]);
+ }
+ close(c2p_pipefd[0]);
+ close(c2p_pipefd[1]);
if(cwdfd >= 0) {
close(cwdfd);
}
@@ -521,29 +557,115 @@ 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 + 2]; /* +2 for appending \n\0 */
+ ssize_t olen = 0, ilen = 0;
+ struct pollfd fds[2], *c2p = &(fds[0]), *p2c = &(fds[1]);
+ nfds_t nfds = 2;
+
+ c2p->fd = c2p_pipefd[0];
+ c2p->events = POLLIN;
+ fcntl(c2p->fd, F_SETFL, O_NONBLOCK);
+ close(c2p_pipefd[1]);
+
+ if(p2c_cb) {
+ p2c->fd = p2c_pipefd[1];
+ p2c->events = POLLOUT;
+ fcntl(p2c->fd, F_SETFL, O_NONBLOCK);
+ close(p2c_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;
+ p2c->fd = -1;
+ p2c->events = 0;
+ }
+
+#define STOP_POLLING(p) do { close(p->fd); p->fd = -1; } while(0)
+
+ while((c2p->fd != -1 || p2c->fd != -1) && poll(fds, nfds, -1) > 0) {
+ if(c2p->revents & POLLIN) {
+ ssize_t space = LINE_MAX - ilen;
+ ssize_t nread = read(c2p->fd, 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_handle_script_output(handle, ibuf);
+ 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 */
+ strcpy(ibuf + ilen, "\n");
+ _alpm_handle_script_output(handle, ibuf);
+ ilen = 0;
+ }
+ } else if(nread == 0) {
+ /* end-of-file */
+ if(ilen) {
+ strcpy(ibuf + ilen, "\n");
+ _alpm_handle_script_output(handle, ibuf);
+ }
+ STOP_POLLING(c2p);
+ } else if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
+ /* nothing read, try again */
+ } else {
+ /* read error */
+ if(ilen) {
+ strcpy(ibuf + ilen, "\n");
+ _alpm_handle_script_output(handle, ibuf);
+ }
+ _alpm_log(handle, ALPM_LOG_ERROR,
+ _("unable to read from pipe (%s)\n"), strerror(errno));
+ STOP_POLLING(c2p);
+ }
+ } else if(c2p->revents) {
+ /* anything but POLLIN indicates an error */
+ STOP_POLLING(c2p);
+ }
+ if(p2c->revents & POLLOUT) {
+ ssize_t nwrite;
+ if(olen == 0) {
+ /* empty buffer, ask the callback for more */
+ if((olen = p2c_cb(obuf, PIPE_BUF, p2c_ctx)) == 0) {
+ /* no more to write, close the pipe */
+ STOP_POLLING(p2c);
+ continue;
+ }
+ }
+ if((nwrite = _alpm_pipe_write(p2c->fd, obuf, olen)) != -1) {
+ olen -= nwrite;
+ memmove(obuf, obuf + nwrite, olen);
+ } else {
+ 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));
+ STOP_POLLING(p2c);
+ }
}
- alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line);
- EVENT(handle, &event);
+ } else if(p2c->revents) {
+ /* anything but POLLOUT indicates an error */
+ STOP_POLLING(p2c);
}
- fclose(pipe_file);
}
+ if(p2c->fd != -1) {
+ close(p2c->fd);
+ }
+ if(c2p->fd != -1) {
+ close(c2p->fd);
+ }
+
+#undef STOP_POLLING
+
while(waitpid(pid, &status, 0) == -1) {
if(errno != EINTR) {
_alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno));
@@ -605,7 +727,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
More information about the pacman-dev
mailing list