X-Git-Url: https://git.xandkar.net/?a=blobdiff_plain;f=x5%2Fkhatus.c;h=382c8731aaeee3c6494955567ecbd806468bb783;hb=900b9a6b1cf64ec6fc67bca260784ab93aec4fa1;hp=37cbccca13a5d23fe703d2cb20083bd5e6fd6795;hpb=b6316e94783d975f109c9901e4061ba041f0cb3f;p=khatus.git diff --git a/x5/khatus.c b/x5/khatus.c index 37cbccc..382c873 100644 --- a/x5/khatus.c +++ b/x5/khatus.c @@ -14,13 +14,14 @@ #include #include "bsdtimespec.h" +#include "khlib_log.h" +#include "khlib_time.h" -#define debug(args...) if (cfg->log_level >= Debug) {fprintf(stderr, "[debug] " args); fflush(stderr);} -#define info( args...) if (cfg->log_level >= Info ) {fprintf(stderr, "[info] " args); fflush(stderr);} -#define error(args...) if (cfg->log_level >= Error) {fprintf(stderr, "[error] " args); fflush(stderr);} -#define fatal(args...) {fprintf(stderr, "[fatal] " args); exit(EXIT_FAILURE);} -#define usage(args...) {print_usage(); fatal("[usage] " args);} - +#define usage(...) { \ + print_usage(); \ + fprintf(stderr, "Error:\n " __VA_ARGS__); \ + exit(EXIT_FAILURE); \ +} #define ERRMSG "ERROR" static const char errmsg[] = ERRMSG; @@ -28,217 +29,223 @@ static const int errlen = sizeof(ERRMSG) - 1; char *argv0; -typedef enum LogLevel { - Nothing, - Error, - Info, - Debug -} LogLevel; - -/* TODO: Convert fifo list to fifo array. */ -typedef struct Fifo Fifo; -struct Fifo { - char *name; - int fd; - int width; - int last_read; - int ttl; - int pos; /* Position on the output buffer. */ - Fifo *next; +/* TODO: Convert slot list to slot array. */ +typedef struct Slot Slot; +struct Slot { + char *in_fifo; + int in_fd; + struct timespec in_last_read; + struct timespec out_ttl; + int out_width; + int out_pos_lo; /* Lowest position on the output buffer. */ + int out_pos_cur; /* Current position on the output buffer. */ + int out_pos_hi; /* Highest position on the output buffer. */ + Slot *next; }; typedef struct Config Config; struct Config { - int interval; + double interval; char * separator; - Fifo * fifos; - int fifo_count; + Slot * slots; + int slot_count; int total_width; int output_to_x_root_window; - LogLevel log_level; -} defaults = { - .interval = 1, - .separator = "|", - .fifos = NULL, - .fifo_count = 0, - .total_width = 0, - .output_to_x_root_window = 0, - .log_level = Info, +}; + +enum read_status { + END_OF_FILE, + END_OF_MESSAGE, + RETRY, + FAILURE }; void -fifo_print_one(Fifo *f, Config *cfg) +slot_print_one(Slot *s) { - info( - "Fifo " - "{" - " name = %s," - " fd = %d," - " width = %d," - " last_read = %d," - " ttl = %d," - " pos = %d," - " next = %p," - " }\n", - f->name, - f->fd, - f->width, - f->last_read, - f->ttl, - f->pos, - f->next + khlib_info("Slot " + "{" + " in_fifo = %s," + " in_fd = %d," + " out_width = %d," + " in_last_read = {tv_sec = %ld, tv_nsec = %ld}" + " out_ttl = {tv_sec = %ld, tv_nsec = %ld}," + " out_pos_lo = %d," + " out_pos_cur = %d," + " out_pos_hi = %d," + " next = %p," + " }\n", + s->in_fifo, + s->in_fd, + s->out_width, + s->in_last_read.tv_sec, + s->in_last_read.tv_nsec, + s->out_ttl.tv_sec, + s->out_ttl.tv_nsec, + s->out_pos_lo, + s->out_pos_cur, + s->out_pos_hi, + s->next ); } void -fifo_print_all(Fifo *head, Config *cfg) +slot_print_all(Slot *head) { - for (Fifo *f = head; f; f = f->next) { - fifo_print_one(f, cfg); + for (Slot *s = head; s; s = s->next) { + slot_print_one(s); } } void config_print(Config *cfg) { - info( - "Config " - "{" - " interval = %d," - " separator = %s," - " fifo_count = %d," - " total_width = %d," - " log_level = %d," - " fifos = ..." - " }\n", - cfg->interval, - cfg->separator, - cfg->fifo_count, - cfg->total_width, - cfg->log_level + khlib_info( + "Config " + "{" + " interval = %f," + " separator = %s," + " slot_count = %d," + " total_width = %d," + " slots = ..." + " }\n", + cfg->interval, + cfg->separator, + cfg->slot_count, + cfg->total_width ); - fifo_print_all(cfg->fifos, cfg); + slot_print_all(cfg->slots); } int -is_pos_num(char *s) +is_pos_num(char *str) { - while (*s != '\0') - if (!isdigit(*(s++))) + while (*str != '\0') + if (!isdigit(*(str++))) return 0; return 1; } +int +is_decimal(char *str) +{ + char c; + int seen = 0; + + while ((c = *(str++)) != '\0') + if (!isdigit(c)) { + if (c == '.' && !seen++) + continue; + else + return 0; + } + return 1; +} + void print_usage() { assert(argv0); fprintf( - stderr, - "\n" - "Usage: %s [OPTION ...] SPEC [SPEC ...]\n" - "\n" - " SPEC = FILE_PATH DATA_WIDTH DATA_TTL\n" - " FILE_PATH = string\n" - " DATA_WIDTH = int (* (positive) number of characters *)\n" - " DATA_TTL = int (* (positive) number of seconds *)\n" - " OPTION = -i INTERVAL\n" - " | -s SEPARATOR\n" - " | -x (* Output to X root window *)\n" - " | -l LOG_LEVEL\n" - " SEPARATOR = string\n" - " INTERVAL = int (* (positive) number of seconds *)\n" - " LOG_LEVEL = int (* %d through %d *)\n" - "\n", - argv0, - Nothing, - Debug + stderr, + "\n" + "Usage: %s [OPTION ...] SPEC [SPEC ...]\n" + "\n" + " SPEC = FILE_PATH DATA_WIDTH DATA_TTL\n" + " FILE_PATH = string\n" + " DATA_WIDTH = int (* (positive) number of characters *)\n" + " DATA_TTL = float (* (positive) number of seconds *)\n" + " OPTION = -i INTERVAL\n" + " | -s SEPARATOR\n" + " | -x (* Output to X root window *)\n" + " | -l LOG_LEVEL\n" + " SEPARATOR = string\n" + " INTERVAL = float (* (positive) number of seconds *)\n" + " LOG_LEVEL = int (* %d through %d *)\n" + "\n", + argv0, + Nothing, + Debug ); fprintf( - stderr, - "Example: %s -i 1 /dev/shm/khatus/khatus_sensor_x 4 10\n" - "\n", - argv0 + stderr, + "Example: %s -i 1 /dev/shm/khatus/khatus_sensor_x 4 10\n" + "\n", + argv0 ); } -void opts_parse_any(Config *, int, char *[], int); /* For mutually-recursive calls. */ +/* For mutually-recursive calls. */ +void opts_parse_any(Config *, int, char *[], int); void parse_opts_opt_i(Config *cfg, int argc, char *argv[], int i) { - if (i < argc) { - char *param = argv[i++]; + char *param; - if (is_pos_num(param)) { - cfg->interval = atoi(param); - opts_parse_any(cfg, argc, argv, i); - } else { - usage("Option -i parameter is invalid: \"%s\"\n", param); - } - } else { + if (i >= argc) usage("Option -i parameter is missing.\n"); - } + param = argv[i++]; + if (!is_decimal(param)) + usage("Option -i parameter is invalid: \"%s\"\n", param); + cfg->interval = atof(param); + opts_parse_any(cfg, argc, argv, i); } void parse_opts_opt_s(Config *cfg, int argc, char *argv[], int i) { - if (i < argc) { - cfg->separator = calloc((strlen(argv[i]) + 1), sizeof(char)); - strcpy(cfg->separator, argv[i]); - opts_parse_any(cfg, argc, argv, ++i); - } else { + if (i >= argc) usage("Option -s parameter is missing.\n"); - } + cfg->separator = calloc((strlen(argv[i]) + 1), sizeof(char)); + strcpy(cfg->separator, argv[i]); + opts_parse_any(cfg, argc, argv, ++i); } void parse_opts_opt_l(Config *cfg, int argc, char *argv[], int i) { + char *param; int log_level; - if (i < argc) { - char *param = argv[i++]; - - if (is_pos_num(param)) { - log_level = atoi(param); - if (log_level <= Debug) { - cfg->log_level = log_level; - opts_parse_any(cfg, argc, argv, i); - } else { - usage("Option -l value (%d) exceeds maximum (%d)\n", log_level, Debug); - } - } else { - usage("Option -l parameter is invalid: \"%s\"\n", param); - } - } else { + if (i >= argc) usage("Option -l parameter is missing.\n"); - } + param = argv[i++]; + if (!is_pos_num(param)) + usage("Option -l parameter is invalid: \"%s\"\n", param); + log_level = atoi(param); + if (log_level > Debug) + usage( + "Option -l value (%d) exceeds maximum (%d)\n", + log_level, + Debug + ); + _khlib_log_level = log_level; + opts_parse_any(cfg, argc, argv, i); } void parse_opts_opt(Config *cfg, int argc, char *argv[], int i) { switch (argv[i][1]) { - case 'i': - /* TODO: Generic set_int */ - parse_opts_opt_i(cfg, argc, argv, ++i); - break; - case 's': - /* TODO: Generic set_str */ - parse_opts_opt_s(cfg, argc, argv, ++i); - break; - case 'x': - cfg->output_to_x_root_window = 1; - opts_parse_any(cfg, argc, argv, ++i); - break; - case 'l': - /* TODO: Generic set_int */ - parse_opts_opt_l(cfg, argc, argv, ++i); - break; - default : - usage("Option \"%s\" is invalid\n", argv[i]); + case 'i': + /* TODO: Generic set_int */ + parse_opts_opt_i(cfg, argc, argv, ++i); + break; + case 's': + /* TODO: Generic set_str */ + parse_opts_opt_s(cfg, argc, argv, ++i); + break; + case 'x': + cfg->output_to_x_root_window = 1; + opts_parse_any(cfg, argc, argv, ++i); + break; + case 'l': + /* TODO: Generic set_int */ + parse_opts_opt_l(cfg, argc, argv, ++i); + break; + default : + usage("Option \"%s\" is invalid\n", argv[i]); } } @@ -246,31 +253,40 @@ void parse_opts_spec(Config *cfg, int argc, char *argv[], int i) { if ((i + 3) > argc) - usage("[spec] Parameter(s) missing for fifo \"%s\".\n", argv[i]); + usage( + "[spec] Parameter(s) missing for fifo \"%s\".\n", + argv[i] + ); char *n = argv[i++]; char *w = argv[i++]; char *t = argv[i++]; + struct timespec in_last_read; + if (!is_pos_num(w)) usage("[spec] Invalid width: \"%s\", for fifo \"%s\"\n", w, n); - if (!is_pos_num(t)) + if (!is_decimal(t)) usage("[spec] Invalid TTL: \"%s\", for fifo \"%s\"\n", t, n); - Fifo *f = calloc(1, sizeof(struct Fifo)); - if (f) { - f->name = n; - f->fd = -1; - f->width = atoi(w); - f->ttl = atoi(t); - f->last_read = 0; - f->pos = cfg->total_width; - f->next = cfg->fifos; - - cfg->fifos = f; - cfg->total_width += f->width; - cfg->fifo_count++; + in_last_read.tv_sec = 0; + in_last_read.tv_nsec = 0; + Slot *s = calloc(1, sizeof(struct Slot)); + if (s) { + s->in_fifo = n; + s->in_fd = -1; + s->out_width = atoi(w); + s->out_ttl = khlib_timespec_of_float(atof(t)); + s->in_last_read = in_last_read; + s->out_pos_lo = cfg->total_width; + s->out_pos_cur = s->out_pos_lo; + s->out_pos_hi = s->out_pos_lo + s->out_width - 1; + s->next = cfg->slots; + + cfg->slots = s; + cfg->total_width += s->out_width; + cfg->slot_count++; } else { - fatal("[memory] Allocation failure."); + khlib_fatal("[memory] Allocation failure."); } opts_parse_any(cfg, argc, argv, i); } @@ -280,11 +296,11 @@ opts_parse_any(Config *cfg, int argc, char *argv[], int i) { if (i < argc) { switch (argv[i][0]) { - case '-': - parse_opts_opt(cfg, argc, argv, i); - break; - default : - parse_opts_spec(cfg, argc, argv, i); + case '-': + parse_opts_opt(cfg, argc, argv, i); + break; + default : + parse_opts_spec(cfg, argc, argv, i); } } } @@ -294,232 +310,335 @@ opts_parse(Config *cfg, int argc, char *argv[]) { opts_parse_any(cfg, argc, argv, 1); - Fifo *last = cfg->fifos; - cfg->fifos = NULL; - for (Fifo *f = last; f; ) { - Fifo *next = f->next; - f->next = cfg->fifos; - cfg->fifos = f; - f = next; + Slot *last = cfg->slots; + cfg->slots = NULL; + for (Slot *s = last; s; ) { + Slot *next = s->next; + s->next = cfg->slots; + cfg->slots = s; + s = next; } } void -fifo_read_error(Fifo *f, char *buf) +slot_expire(Slot *s, struct timespec t, char *buf) +{ + struct timespec td; + + timespecsub(&t, &(s->in_last_read), &td); + if (timespeccmp(&td, &(s->out_ttl), >=)) { + /* TODO: Maybe configurable expiry character. */ + memset(buf + s->out_pos_lo, '_', s->out_pos_hi - s->out_pos_lo); + khlib_warn("Slot expired: \"%s\"\n", s->in_fifo); + } +} + +void +slot_read_error(Slot *s, char *buf) { char *b; int i; - b = buf + f->pos; + b = buf + s->out_pos_lo; /* Copy as much of the error message as possible. - * EXCLUDING the reminating \0. */ - for (i = 0; i < errlen && i < f->width; i++) + * EXCLUDING the terminating \0. */ + for (i = 0; i < errlen && i < s->out_width; i++) b[i] = errmsg[i]; /* Any remaining slots: */ - for (; i < f->width; i++) + for (; i < s->out_width; i++) b[i] = '_'; } -void -fifo_read_one(Fifo *f, char *buf, Config *cfg) +enum read_status +slot_read_one(Slot *s, struct timespec t, char *buf) { - ssize_t current; - ssize_t total; - char *b; - char c; + char c; /* Character read. */ + int r; /* Remaining unused slots in buffer range. */ - current = 0; - total = 0; - c = '\0'; - b = buf + f->pos; - while ((current = read(f->fd, &c, 1)) && c != '\n' && c != '\0' && total++ < f->width) - *b++ = c; - if (current == -1) { - error("Failed to read: \"%s\". Error: %s\n", f->name, strerror(errno)); - fifo_read_error(f, buf); - } else - while (total++ < f->width) - *b++ = ' '; - /* TODO Record timestamp read */ - close(f->fd); - f->fd = -1; + for (;;) { + switch (read(s->in_fd, &c, 1)) { + case -1: + khlib_error( + "Failed to read: \"%s\". errno: %d, msg: %s\n", + s->in_fifo, + errno, + strerror(errno) + ); + switch (errno) { + case EINTR: + case EAGAIN: + return RETRY; + default: + return FAILURE; + } + case 0: + khlib_debug("%s: End of FILE\n", s->in_fifo); + s->out_pos_cur = s->out_pos_lo; + return END_OF_FILE; + case 1: + /* TODO: Consider making msg term char a CLI option */ + if (c == '\n' || c == '\0') { + r = s->out_pos_hi - s->out_pos_cur; + if (r > 0) + memset(buf + s->out_pos_cur, ' ', r); + s->out_pos_cur = s->out_pos_lo; + s->in_last_read = t; + return END_OF_MESSAGE; + } else { + if (s->out_pos_cur <= s->out_pos_hi) + buf[s->out_pos_cur++] = c; + /* Drop beyond available range. */ + /* + * TODO Define max after which we stop reading. + * To ensure that a rogue large message + * doesn't trap us here. + */ + } + break; + default: + assert(0); + } + } } void -fifo_read_all(Config *cfg, char *buf) +slot_read_all(Config *cfg, struct timespec *ti, char *buf) { fd_set fds; int maxfd = -1; - int ready; + int ready = 0; struct stat st; + struct timespec t; FD_ZERO(&fds); - for (Fifo *f = cfg->fifos; f; f = f->next) { + for (Slot *s = cfg->slots; s; s = s->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); + if (lstat(s->in_fifo, &st) < 0) { + khlib_error( + "Cannot stat \"%s\". Error: %s\n", + s->in_fifo, + strerror(errno) + ); + slot_read_error(s, buf); continue; } if (!(st.st_mode & S_IFIFO)) { - error("\"%s\" is not a FIFO\n", f->name); - fifo_read_error(f, buf); + khlib_error("\"%s\" is not a FIFO\n", s->in_fifo); + slot_read_error(s, buf); continue; } - debug("opening: %s\n", f->name); - if (f->fd < 0) - f->fd = open(f->name, O_RDONLY | O_NONBLOCK); - 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); + if (s->in_fd < 0) { + khlib_debug( + "%s: closed. opening. in_fd: %d\n", + s->in_fifo, + s->in_fd + ); + s->in_fd = open(s->in_fifo, O_RDONLY | O_NONBLOCK); + } else { + khlib_debug( + "%s: already openned. in_fd: %d\n", + s->in_fifo, + s->in_fd + ); + } + if (s->in_fd == -1) { + /* TODO Consider backing off retries for failed slots */ + khlib_error("Failed to open \"%s\"\n", s->in_fifo); + slot_read_error(s, buf); continue; } - if (f->fd > maxfd) - maxfd = f->fd; - FD_SET(f->fd, &fds); + khlib_debug("%s: open. in_fd: %d\n", s->in_fifo, s->in_fd); + if (s->in_fd > maxfd) + maxfd = s->in_fd; + FD_SET(s->in_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)) { - debug("reading: %s\n", f->name); - fifo_read_one(f, buf, cfg); + khlib_debug("selecting...\n"); + ready = pselect(maxfd + 1, &fds, NULL, NULL, ti, NULL); + khlib_debug("ready: %d\n", ready); + clock_gettime(CLOCK_MONOTONIC, &t); + if (ready == -1) { + switch (errno) { + case EINTR: + khlib_error( + "pselect temp failure: %d, errno: %d, msg: %s\n", + ready, + errno, + strerror(errno) + ); + /* TODO: Reconsider what to do here. */ + return; + default: + khlib_fatal( + "pselect failed: %d, errno: %d, msg: %s\n", + ready, + errno, + strerror(errno) + ); } } -} - -void -snooze(struct timespec *t, Config *cfg) -{ - struct timespec remainder; - int result; - - result = nanosleep(t, &remainder); - - if (result < 0) { - if (errno == EINTR) { - info( - "nanosleep interrupted. Remainder: " - "{ tv_sec = %ld, tv_nsec = %ld }", - remainder.tv_sec, remainder.tv_nsec); - /* No big deal if we occasionally sleep less, - * so not attempting to correct after an interruption. - */ - } else { - fatal("nanosleep: %s\n", strerror(errno)); + /* At-least-once ensures that expiries are still checked on timeouts. */ + do { + for (Slot *s = cfg->slots; s; s = s->next) { + if (FD_ISSET(s->in_fd, &fds)) { + khlib_debug("reading: %s\n", s->in_fifo); + switch (slot_read_one(s, 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(s->in_fd); + s->in_fd = -1; + ready--; + break; + case RETRY: + break; + default: + assert(0); + } + } else { + slot_expire(s, t, buf); + } } - } + } while (ready); + assert(ready == 0); } int main(int argc, char *argv[]) { + Config cfg = { + .interval = 1.0, + .separator = "|", + .slots = NULL, + .slot_count = 0, + .total_width = 0, + .output_to_x_root_window = 0, + }; + int width = 0; - int nfifos = 0; + int nslots = 0; int seplen = 0; int prefix = 0; int errors = 0; char *buf; - Config cfg0 = defaults; - Config *cfg = &cfg0; - Display *display = NULL; + Display *d = NULL; struct stat st; struct timespec - t0, /* time stamp. before reading fifos */ - t1, /* time stamp. after reading fifos */ + t0, /* time stamp. before reading slots */ + t1, /* time stamp. after reading slots */ ti, /* time interval desired (t1 - t0) */ td, /* time interval measured (t1 - t0) */ tc; /* time interval correction (ti - td) when td < ti */ argv0 = argv[0]; - opts_parse(cfg, argc, argv); - debug("argv0 = %s\n", argv0); - config_print(cfg); + opts_parse(&cfg, argc, argv); + khlib_debug("argv0 = %s\n", argv0); + config_print(&cfg); - /* TODO: Support interval < 1. i.e. implement timespec_of_float */ - ti.tv_sec = cfg->interval; - ti.tv_nsec = 0; + ti = khlib_timespec_of_float(cfg.interval); - if (cfg->fifos == NULL) - usage("No fifo specs were given!\n"); + if (cfg.slots == NULL) + usage("No slot specs were given!\n"); /* 1st pass to check file existence and type */ - for (Fifo *f = cfg->fifos; f; f = f->next) { - if (lstat(f->name, &st) < 0) { - error("Cannot stat \"%s\". Error: %s\n", f->name, strerror(errno)); + for (Slot *s = cfg.slots; s; s = s->next) { + if (lstat(s->in_fifo, &st) < 0) { + khlib_error( + "Cannot stat \"%s\". Error: %s\n", + s->in_fifo, + strerror(errno) + ); errors++; continue; } if (!(st.st_mode & S_IFIFO)) { - error("\"%s\" is not a FIFO\n", f->name); + khlib_error("\"%s\" is not a FIFO\n", s->in_fifo); errors++; continue; } } if (errors) - fatal("Encountered errors with the given file paths. See log.\n"); + khlib_fatal( + "Encountered errors with given file paths. See log.\n" + ); - width = cfg->total_width; - seplen = strlen(cfg->separator); + width = cfg.total_width; + seplen = strlen(cfg.separator); /* 2nd pass to make space for separators */ - for (Fifo *f = cfg->fifos; f; f = f->next) { - f->pos += prefix; + for (Slot *s = cfg.slots; s; s = s->next) { + s->out_pos_lo += prefix; + s->out_pos_hi += prefix; + s->out_pos_cur = s->out_pos_lo; prefix += seplen; - nfifos++; + nslots++; } - width += (seplen * (nfifos - 1)); + width += (seplen * (nslots - 1)); buf = calloc(1, width + 1); if (buf == NULL) - fatal("[memory] Failed to allocate buffer of %d bytes", width); + khlib_fatal( + "[memory] Failed to allocate buffer of %d bytes", + width + ); memset(buf, ' ', width); buf[width] = '\0'; /* 3rd pass to set the separators */ - for (Fifo *f = cfg->fifos; f; f = f->next) { - if (f->pos) { /* Skip the first, left-most */ + for (Slot *s = cfg.slots; s; s = s->next) { + if (s->out_pos_lo) { /* Skip the first, left-most */ /* Copying only seplen ensures we omit the '\0' byte. */ - strncpy(buf + (f->pos - seplen), cfg->separator, seplen); + strncpy( + buf + (s->out_pos_lo - seplen), + cfg.separator, + seplen + ); } } - if (cfg->output_to_x_root_window && !(display = XOpenDisplay(NULL))) - fatal("XOpenDisplay failed with: %p\n", display); + if (cfg.output_to_x_root_window && !(d = XOpenDisplay(NULL))) + khlib_fatal("XOpenDisplay failed with: %p\n", d); /* TODO: Handle signals */ for (;;) { clock_gettime(CLOCK_MONOTONIC, &t0); // FIXME: check errors - /* TODO: Cache expiration. i.e. use the TTL */ - /* TODO: How to trigger TTL check? On select? Alarm signal? */ - /* TODO: Set timeout on fifo_read_all based on diff of last time of - * fifo_read_all and desired time of next TTL check. - */ - /* TODO: How long to wait on IO? Max TTL? */ - fifo_read_all(cfg, buf); - if (cfg->output_to_x_root_window) { - if (XStoreName(display, DefaultRootWindow(display), buf) < 0) - fatal("XStoreName failed.\n"); - XFlush(display); + slot_read_all(&cfg, &ti, buf); + if (cfg.output_to_x_root_window) { + if (XStoreName(d, DefaultRootWindow(d), buf) < 0) + khlib_fatal("XStoreName failed.\n"); + XFlush(d); } else { puts(buf); fflush(stdout); } clock_gettime(CLOCK_MONOTONIC, &t1); // FIXME: check errors timespecsub(&t1, &t0, &td); - debug("td {tv_sec = %ld, tv_nsec = %ld}\n", td.tv_sec, td.tv_nsec); + khlib_debug( + "td {tv_sec = %ld, tv_nsec = %ld}\n", + td.tv_sec, + td.tv_nsec + ); if (timespeccmp(&td, &ti, <)) { /* Pushback on data producers by refusing to read the * pipe more frequently than the interval. */ timespecsub(&ti, &td, &tc); - debug("snooze YES\n"); - snooze(&tc, cfg); - } else - debug("snooze NO\n"); + khlib_debug("khlib_sleep YES\n"); + khlib_sleep(&tc); + } else { + khlib_debug("khlib_sleep NO\n"); + } } return EXIT_SUCCESS;