Break up io_callback() into read_callback() and write_callback()

to make it clear that we can't get an event with both read and write
set.
This commit is contained in:
Todd C. Miller
2016-05-09 10:53:20 -06:00
parent 094854adfe
commit b04c49dbd3

View File

@@ -537,130 +537,137 @@ terminate_command(pid_t pid, bool use_pgrp)
} }
/* /*
* Read/write an iobuf that is ready. * Read an iobuf that is ready.
*/ */
static void static void
io_callback(int fd, int what, void *v) read_callback(int fd, int what, void *v)
{ {
struct io_buffer *iob = v; struct io_buffer *iob = v;
struct sudo_event_base *evbase; struct sudo_event_base *evbase;
int n; int n;
debug_decl(io_callback, SUDO_DEBUG_EXEC); debug_decl(read_callback, SUDO_DEBUG_EXEC);
if (ISSET(what, SUDO_EV_WRITE)) { evbase = sudo_ev_get_base(iob->revent);
evbase = sudo_ev_get_base(iob->wevent); do {
do { n = read(fd, iob->buf + iob->len, sizeof(iob->buf) - iob->len);
n = write(fd, iob->buf + iob->off, iob->len - iob->off); } while (n == -1 && errno == EINTR);
} while (n == -1 && errno == EINTR); switch (n) {
if (n == -1) { case -1:
switch (errno) { if (errno == EAGAIN)
case EPIPE: break;
case ENXIO: /* treat read error as fatal and close the fd */
case EIO: sudo_debug_printf(SUDO_DEBUG_ERROR,
case EBADF: "error reading fd %d: %s", fd, strerror(errno));
/* other end of pipe closed or pty revoked */ /* FALLTHROUGH */
case 0:
/* got EOF or pty has gone away */
if (n == 0) {
sudo_debug_printf(SUDO_DEBUG_INFO, sudo_debug_printf(SUDO_DEBUG_INFO,
"unable to write %d bytes to fd %d", "read EOF from fd %d", fd);
iob->len - iob->off, fd);
/* Close reader if there is one. */
if (iob->revent != NULL) {
safe_close(sudo_ev_get_fd(iob->revent));
ev_free_by_fd(evbase, sudo_ev_get_fd(iob->revent));
}
CLR(what, SUDO_EV_READ);
safe_close(fd);
ev_free_by_fd(evbase, fd);
break;
case EAGAIN:
/* not an error */
break;
default:
#if 0 /* XXX -- how to set cstat? stash in iobufs instead? */
if (cstat != NULL) {
cstat->type = CMD_ERRNO;
cstat->val = errno;
}
#endif /* XXX */
sudo_debug_printf(SUDO_DEBUG_ERROR,
"error writing fd %d: %s", fd, strerror(errno));
sudo_ev_loopbreak(evbase);
break;
} }
} else { safe_close(fd);
sudo_debug_printf(SUDO_DEBUG_INFO, ev_free_by_fd(evbase, fd);
"wrote %d bytes to fd %d", n, fd); /* If writer already consumed the buffer, close it too. */
iob->off += n; if (iob->wevent != NULL && iob->off == iob->len) {
/* Reset buffer if fully consumed. */ safe_close(sudo_ev_get_fd(iob->wevent));
if (iob->off == iob->len) { ev_free_by_fd(evbase, sudo_ev_get_fd(iob->wevent));
iob->off = iob->len = 0; iob->off = iob->len = 0;
/* Forward the EOF from reader to writer. */
if (iob->revent == NULL) {
CLR(what, SUDO_EV_READ);
safe_close(fd);
ev_free_by_fd(evbase, fd);
}
} }
/* Re-enable writer if buffer is not empty. */ break;
if (iob->len > iob->off) { default:
sudo_debug_printf(SUDO_DEBUG_INFO,
"read %d bytes from fd %d", n, fd);
if (!iob->action(iob->buf + iob->len, n, iob))
terminate_command(cmnd_pid, true);
iob->len += n;
/* Enable writer if not /dev/tty or we are foreground pgrp. */
if (iob->wevent != NULL &&
(foreground || !USERTTY_EVENT(iob->wevent))) {
if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1) if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
sudo_fatal(U_("unable to add event to queue")); sudo_fatal(U_("unable to add event to queue"));
} }
/* Enable reader if buffer is not full. */ /* Re-enable reader if buffer is not full. */
if (iob->revent != NULL && if (iob->len != sizeof(iob->buf)) {
(ttymode == TERM_RAW || !USERTTY_EVENT(iob->revent))) { if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
if (iob->len != sizeof(iob->buf)) { sudo_fatal(U_("unable to add event to queue"));
if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
sudo_fatal(U_("unable to add event to queue"));
}
} }
} break;
} }
if (ISSET(what, SUDO_EV_READ)) { }
evbase = sudo_ev_get_base(iob->revent);
do { /*
n = read(fd, iob->buf + iob->len, sizeof(iob->buf) - iob->len); * Write an iobuf that is ready.
} while (n == -1 && errno == EINTR); */
switch (n) { static void
case -1: write_callback(int fd, int what, void *v)
if (errno == EAGAIN) {
break; struct io_buffer *iob = v;
/* treat read error as fatal and close the fd */ struct sudo_event_base *evbase;
sudo_debug_printf(SUDO_DEBUG_ERROR, int n;
"error reading fd %d: %s", fd, strerror(errno)); debug_decl(write_callback, SUDO_DEBUG_EXEC);
/* FALLTHROUGH */
case 0: evbase = sudo_ev_get_base(iob->wevent);
/* got EOF or pty has gone away */ do {
if (n == 0) { n = write(fd, iob->buf + iob->off, iob->len - iob->off);
sudo_debug_printf(SUDO_DEBUG_INFO, } while (n == -1 && errno == EINTR);
"read EOF from fd %d", fd); if (n == -1) {
} switch (errno) {
case EPIPE:
case ENXIO:
case EIO:
case EBADF:
/* other end of pipe closed or pty revoked */
sudo_debug_printf(SUDO_DEBUG_INFO,
"unable to write %d bytes to fd %d",
iob->len - iob->off, fd);
/* Close reader if there is one. */
if (iob->revent != NULL) {
safe_close(sudo_ev_get_fd(iob->revent));
ev_free_by_fd(evbase, sudo_ev_get_fd(iob->revent));
}
safe_close(fd);
ev_free_by_fd(evbase, fd);
break;
case EAGAIN:
/* not an error */
break;
default:
#if 0 /* XXX -- how to set cstat? stash in iobufs instead? */
if (cstat != NULL) {
cstat->type = CMD_ERRNO;
cstat->val = errno;
}
#endif /* XXX */
sudo_debug_printf(SUDO_DEBUG_ERROR,
"error writing fd %d: %s", fd, strerror(errno));
sudo_ev_loopbreak(evbase);
break;
}
} else {
sudo_debug_printf(SUDO_DEBUG_INFO,
"wrote %d bytes to fd %d", n, fd);
iob->off += n;
/* Reset buffer if fully consumed. */
if (iob->off == iob->len) {
iob->off = iob->len = 0;
/* Forward the EOF from reader to writer. */
if (iob->revent == NULL) {
safe_close(fd); safe_close(fd);
ev_free_by_fd(evbase, fd); ev_free_by_fd(evbase, fd);
/* If writer already consumed the buffer, close it too. */ }
if (iob->wevent != NULL && iob->off == iob->len) { }
safe_close(sudo_ev_get_fd(iob->wevent)); /* Re-enable writer if buffer is not empty. */
ev_free_by_fd(evbase, sudo_ev_get_fd(iob->wevent)); if (iob->len > iob->off) {
iob->off = iob->len = 0; if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
} sudo_fatal(U_("unable to add event to queue"));
break; }
default: /* Enable reader if buffer is not full. */
sudo_debug_printf(SUDO_DEBUG_INFO, if (iob->revent != NULL &&
"read %d bytes from fd %d", n, fd); (ttymode == TERM_RAW || !USERTTY_EVENT(iob->revent))) {
if (!iob->action(iob->buf + iob->len, n, iob)) if (iob->len != sizeof(iob->buf)) {
terminate_command(cmnd_pid, true); if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
iob->len += n; sudo_fatal(U_("unable to add event to queue"));
/* Enable writer if not /dev/tty or we are foreground pgrp. */ }
if (iob->wevent != NULL &&
(foreground || !USERTTY_EVENT(iob->wevent))) {
if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
sudo_fatal(U_("unable to add event to queue"));
}
/* Re-enable reader if buffer is not full. */
if (iob->len != sizeof(iob->buf)) {
if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
sudo_fatal(U_("unable to add event to queue"));
}
break;
} }
} }
} }
@@ -684,8 +691,8 @@ io_buf_new(int rfd, int wfd, bool (*action)(const char *, unsigned int, struct i
/* Allocate and add to head of list. */ /* Allocate and add to head of list. */
if ((iob = malloc(sizeof(*iob))) == NULL) if ((iob = malloc(sizeof(*iob))) == NULL)
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory")); sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
iob->revent = sudo_ev_alloc(rfd, SUDO_EV_READ, io_callback, iob); iob->revent = sudo_ev_alloc(rfd, SUDO_EV_READ, read_callback, iob);
iob->wevent = sudo_ev_alloc(wfd, SUDO_EV_WRITE, io_callback, iob); iob->wevent = sudo_ev_alloc(wfd, SUDO_EV_WRITE, write_callback, iob);
iob->len = 0; iob->len = 0;
iob->off = 0; iob->off = 0;
iob->action = action; iob->action = action;