[pacman-dev] [PATCH 4/5 v3] allow specifying input to scriptlets

Andrew Gregory andrew.gregory.8 at gmail.com
Thu Nov 12 00:20:00 UTC 2015


Signed-off-by: Andrew Gregory <andrew.gregory.8 at gmail.com>
---

* 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


More information about the pacman-dev mailing list