[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