[pacman-dev] [PATCH 12/11][WIP] allow specifying input to chroot

Andrew Gregory andrew.gregory.8 at gmail.com
Sun Oct 18 01:39:04 UTC 2015


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


More information about the pacman-dev mailing list