about summary refs log tree commit diff
path: root/Src/Zle/zle_main.c
diff options
context:
space:
mode:
authorBarton E. Schaefer <schaefer@zsh.org>2014-02-23 18:14:12 -0800
committerBarton E. Schaefer <schaefer@zsh.org>2014-02-23 18:14:12 -0800
commit7e04c1a53ddada7a848753d151e18f9469788b98 (patch)
treed2cc380a273575369ccf8e2ef964aa0311c9cb0e /Src/Zle/zle_main.c
parent73db206838e427348cb91f1955692da5d2820d00 (diff)
downloadzsh-7e04c1a53ddada7a848753d151e18f9469788b98.tar.gz
zsh-7e04c1a53ddada7a848753d151e18f9469788b98.tar.xz
zsh-7e04c1a53ddada7a848753d151e18f9469788b98.zip
32427: avoid busy loop on closed descriptors for "zle -F" handlers
Also assure the handlers are called on error conditions and document the
extra argument that is passed in the error case.
Diffstat (limited to 'Src/Zle/zle_main.c')
-rw-r--r--Src/Zle/zle_main.c39
1 files changed, 36 insertions, 3 deletions
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index b0010fc33..442c31995 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -525,7 +525,8 @@ raw_getbyte(long do_keytmout, char *cptr)
 #endif
 #ifndef HAVE_POLL
 # ifdef HAVE_SELECT
-    fd_set foofd;
+    fd_set foofd, errfd;
+    FD_ZERO(&errfd);
 # endif
 #endif
 
@@ -613,11 +614,14 @@ raw_getbyte(long do_keytmout, char *cptr)
 	    if (!errtry) {
 		for (i = 0; i < nwatch; i++) {
 		    int fd = watch_fds[i].fd;
+		    if (FD_ISSET(fd, &errfd))
+			continue;
 		    FD_SET(fd, &foofd);
 		    if (fd > fdmax)
 			fdmax = fd;
 		}
 	    }
+	    FD_ZERO(&errfd);
 
 	    if (tmout.tp != ZTM_NONE) {
 		expire_tv.tv_sec = tmout.exp100ths / 100;
@@ -732,9 +736,10 @@ raw_getbyte(long do_keytmout, char *cptr)
 		    Watch_fd lwatch_fd = lwatch_fds + i;
 		    if (
 # ifdef HAVE_POLL
-			(fds[i+1].revents & POLLIN)
+			(fds[i+1].revents & (POLLIN|POLLERR|POLLHUP|POLLNVAL))
 # else
-			FD_ISSET(lwatch_fd->fd, &foofd)
+			FD_ISSET(lwatch_fd->fd, &foofd) ||
+			FD_ISSET(lwatch_fd->fd, &errfd)
 # endif
 			) {
 			/* Handle the fd. */
@@ -765,6 +770,9 @@ raw_getbyte(long do_keytmout, char *cptr)
 			    if (fds[i+1].revents & POLLNVAL)
 				zaddlinknode(funcargs, ztrdup("nval"));
 #  endif
+# else
+			    if (FD_ISSET(lwatch_fd->fd, &errfd))
+				zaddlinknode(funcargs, ztrdup("err"));
 # endif
 			    callhookfunc(lwatch_fd->func, funcargs, 0, NULL);
 			    freelinklist(funcargs, freestr);
@@ -786,6 +794,31 @@ raw_getbyte(long do_keytmout, char *cptr)
 		for (i = 0; i < lnwatch; i++)
 		    zsfree(lwatch_fds[i].func);
 		zfree(lwatch_fds, lnwatch*sizeof(struct watch_fd));
+
+# ifdef HAVE_POLL
+		/* Function may have added or removed handlers */
+		nfds = 1 + nwatch;
+		if (nfds > 1) {
+		    fds = zrealloc(fds, sizeof(struct pollfd) * nfds);
+		    for (i = 0; i < nwatch; i++) {
+			/*
+			 * This is imperfect because it assumes fds[] and
+			 * watch_fds[] remain in sync, which may be false
+			 * if handlers are shuffled.  However, it should
+			 * be harmless (e.g., produce one extra pass of
+			 * the loop) in the event they fall out of sync.
+			 */
+			if (fds[i+1].fd == watch_fds[i].fd &&
+			    (fds[i+1].revents & (POLLERR|POLLHUP|POLLNVAL))) {
+			    fds[i+1].events = 0;	/* Don't poll this */
+			} else {
+			    fds[i+1].fd = watch_fds[i].fd;
+			    fds[i+1].events = POLLIN;
+			}
+			fds[i+1].revents = 0;
+		    }
+		}
+# endif
 	    }
 	}
 # ifdef HAVE_POLL