-fifo_read_error(Fifo *f, char *buf)
-{
- char *b;
- int i;
-
- b = buf + f->pos_init;
- /* Copy as much of the error message as possible.
- * EXCLUDING the reminating \0. */
- for (i = 0; i < errlen && i < f->width; i++)
- b[i] = errmsg[i];
- /* Any remaining slots: */
- for (; i < f->width; i++)
- b[i] = '_';
-}
-
-enum read_status
-fifo_read_one(Fifo *f, char *buf)
-{
- char c; /* Character read. */
- int r; /* Remaining unused slots in buffer range. */
-
- for (;;) {
- switch (read(f->fd, &c, 1)) {
- case -1:
- error("Failed to read: \"%s\". errno: %d, msg: %s\n",
- f->name, errno, strerror(errno));
- switch (errno) {
- case EINTR:
- case EAGAIN:
- return RETRY;
- default:
- return FAILURE;
- }
- case 0:
- debug("%s: End of FILE\n", f->name);
- f->pos_curr = f->pos_init;
- return END_OF_FILE;
- case 1:
- /* TODO: Consider making msg term char a CLI option */
- if (c == '\n' || c == '\0') {
- r = f->pos_final - f->pos_curr;
- if (r > 0)
- memset(buf + f->pos_curr, ' ', r);
- f->pos_curr = f->pos_init;
- return END_OF_MESSAGE;
- } else {
- if (f->pos_curr <= f->pos_final)
- buf[f->pos_curr++] = c;
- /* Drop beyond available range. */
- }
- break;
- default:
- assert(0);
- }
- }
- /* TODO Record timestamp read */
-}
-
-int
-fifo_read_all(Config *cfg, char *buf)
-{
- fd_set fds;
- int maxfd = -1;
- int reading = 0; /* Number of FDs with unfinished reads */
- int ready;
- struct stat st;
-
- 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 = select(maxfd + 1, &fds, NULL, NULL, NULL);
- debug("ready: %d\n", ready);
- assert(ready != 0);
- if (ready < 0)
- fatal("%s", strerror(errno));
- for (Fifo *f = cfg->fifos; f; f = f->next) {
- if (FD_ISSET(f->fd, &fds)) {
- reading++;
- debug("reading: %s\n", f->name);
- switch (fifo_read_one(f, 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;
- reading--;
- break;
- case RETRY:
- break;
- default:
- assert(0);
- }
- }
- }
- assert(reading >= 0);
- return reading;
-}
-
-int
-main(int argc, char *argv[])