+void
+fifo_read_all(Config *cfg, struct timespec *ti, char *buf)
+{
+ fd_set fds;
+ int maxfd = -1;
+ int ready = 0;
+ struct stat st;
+ struct timespec t;
+
+ FD_ZERO(&fds);
+ for (Fifo *f = cfg->fifos; f; f = f->next) {
+ /* TODO: Create the FIFO if it doesn't already exist. */
+ if (lstat(f->name, &st) < 0) {
+ error("Cannot stat \"%s\". Error: %s\n", f->name, strerror(errno));
+ fifo_read_error(f, buf);
+ continue;
+ }
+ if (!(st.st_mode & S_IFIFO)) {
+ error("\"%s\" is not a FIFO\n", f->name);
+ fifo_read_error(f, buf);
+ continue;
+ }
+ if (f->fd < 0) {
+ debug("%s: closed. opening. fd: %d\n", f->name, f->fd);
+ f->fd = open(f->name, O_RDONLY | O_NONBLOCK);
+ } else {
+ debug("%s: already openned. fd: %d\n", f->name, f->fd);
+ }
+ if (f->fd == -1) {
+ /* TODO: Consider backing off retries for failed fifos. */
+ error("Failed to open \"%s\"\n", f->name);
+ fifo_read_error(f, buf);
+ continue;
+ }
+ debug("%s: open. fd: %d\n", f->name, f->fd);
+ if (f->fd > maxfd)
+ maxfd = f->fd;
+ FD_SET(f->fd, &fds);
+ }
+ debug("selecting...\n");
+ ready = pselect(maxfd + 1, &fds, NULL, NULL, ti, NULL);
+ debug("ready: %d\n", ready);
+ assert(ready >= 0);
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ /* At-least-once ensures that expiries are still checked on timeouts. */
+ do {
+ for (Fifo *f = cfg->fifos; f; f = f->next) {
+ if (FD_ISSET(f->fd, &fds)) {
+ debug("reading: %s\n", f->name);
+ switch (fifo_read_one(f, t, buf)) {
+ /*
+ * ### MESSAGE LOSS ###
+ * is introduced by closing at EOM in addition
+ * to EOF, since there may be unread messages
+ * remaining in the pipe. However,
+ *
+ * ### INTER-MESSAGE PUSHBACK ###
+ * is also gained, since pipes block at the
+ * "open" call.
+ *
+ * This is an acceptable trade-off because we
+ * are a stateless reporter of a _most-recent_
+ * status, not a stateful accumulator.
+ */
+ case END_OF_MESSAGE:
+ case END_OF_FILE:
+ case FAILURE:
+ close(f->fd);
+ f->fd = -1;
+ ready--;
+ break;
+ case RETRY:
+ break;
+ default:
+ assert(0);
+ }
+ } else {
+ fifo_expire(f, t, buf);
+ }
+ }
+ } while (ready);
+ assert(ready == 0);
+}
+