X-Git-Url: https://git.xandkar.net/?a=blobdiff_plain;f=x5%2Fkhatus.c;h=37cbccca13a5d23fe703d2cb20083bd5e6fd6795;hb=b6316e94783d975f109c9901e4061ba041f0cb3f;hp=6ecb1e7c5ef4c6f9e0b6497fbc206d4220327a25;hpb=fb8745ba18e5b96680cd612d6a0be78cfcd7eaf3;p=khatus.git diff --git a/x5/khatus.c b/x5/khatus.c index 6ecb1e7..37cbccc 100644 --- a/x5/khatus.c +++ b/x5/khatus.c @@ -8,13 +8,16 @@ #include #include #include +#include #include #include -#define debug(args...) {fprintf(stderr, "[debug] " args);} -#define info( args...) {fprintf(stderr, "[info] " args);} -#define error(args...) {fprintf(stderr, "[error] " args);} +#include "bsdtimespec.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);} @@ -25,6 +28,13 @@ 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 { @@ -45,6 +55,7 @@ struct Config { int fifo_count; int total_width; int output_to_x_root_window; + LogLevel log_level; } defaults = { .interval = 1, .separator = "|", @@ -52,12 +63,13 @@ struct Config { .fifo_count = 0, .total_width = 0, .output_to_x_root_window = 0, + .log_level = Info, }; void -fifo_print_one(Fifo *f) +fifo_print_one(Fifo *f, Config *cfg) { - debug( + info( "Fifo " "{" " name = %s," @@ -79,31 +91,33 @@ fifo_print_one(Fifo *f) } void -fifo_print_all(Fifo *head) +fifo_print_all(Fifo *head, Config *cfg) { for (Fifo *f = head; f; f = f->next) { - fifo_print_one(f); + fifo_print_one(f, cfg); } } void -config_print(Config *c) +config_print(Config *cfg) { - debug( + info( "Config " "{" " interval = %d," " separator = %s," " fifo_count = %d," " total_width = %d," + " log_level = %d," " fifos = ..." " }\n", - c->interval, - c->separator, - c->fifo_count, - c->total_width + cfg->interval, + cfg->separator, + cfg->fifo_count, + cfg->total_width, + cfg->log_level ); - fifo_print_all(c->fifos); + fifo_print_all(cfg->fifos, cfg); } int @@ -130,10 +144,15 @@ print_usage() " 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 + argv0, + Nothing, + Debug ); fprintf( stderr, @@ -174,6 +193,30 @@ parse_opts_opt_s(Config *cfg, int argc, char *argv[], int i) } } +void +parse_opts_opt_l(Config *cfg, int argc, char *argv[], int i) +{ + 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 { + usage("Option -l parameter is missing.\n"); + } +} + void parse_opts_opt(Config *cfg, int argc, char *argv[], int i) { @@ -190,6 +233,10 @@ parse_opts_opt(Config *cfg, int argc, char *argv[], int i) 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]); } @@ -274,7 +321,7 @@ fifo_read_error(Fifo *f, char *buf) } void -fifo_read_one(Fifo *f, char *buf) +fifo_read_one(Fifo *f, char *buf, Config *cfg) { ssize_t current; ssize_t total; @@ -307,7 +354,6 @@ fifo_read_all(Config *cfg, char *buf) struct stat st; FD_ZERO(&fds); - /* TODO: Check TTL */ 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) { @@ -342,7 +388,30 @@ fifo_read_all(Config *cfg, char *buf) 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); + fifo_read_one(f, buf, cfg); + } + } +} + +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)); } } } @@ -360,12 +429,23 @@ main(int argc, char *argv[]) Config *cfg = &cfg0; Display *display = NULL; struct stat st; + struct timespec + t0, /* time stamp. before reading fifos */ + t1, /* time stamp. after reading fifos */ + 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); + + /* TODO: Support interval < 1. i.e. implement timespec_of_float */ + ti.tv_sec = cfg->interval; + ti.tv_nsec = 0; + if (cfg->fifos == NULL) usage("No fifo specs were given!\n"); @@ -410,14 +490,15 @@ main(int argc, char *argv[]) if (cfg->output_to_x_root_window && !(display = XOpenDisplay(NULL))) fatal("XOpenDisplay failed with: %p\n", display); - /* TODO: nanosleep and nano time diff */ /* TODO: Handle signals */ for (;;) { - /* TODO: Check TTL and maybe blank-out */ + 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) @@ -427,6 +508,18 @@ main(int argc, char *argv[]) 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); + 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"); } return EXIT_SUCCESS;