X-Git-Url: https://git.xandkar.net/?a=blobdiff_plain;f=x5%2Fkhatus.c;h=b6f66f7a1134923d8733a18016bb7404d9f4ecd3;hb=348d5f367c12e60de79ac4647417aa7184996d06;hp=b21336d8d4720acecb798a9536707620f4966dfe;hpb=e6c523cd4c30a1be6ac358ab6da910aaf9b6b70e;p=khatus.git diff --git a/x5/khatus.c b/x5/khatus.c index b21336d..b6f66f7 100644 --- a/x5/khatus.c +++ b/x5/khatus.c @@ -8,16 +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);} -#define fatal(args...) {fprintf(stderr, "[fatal] " args); exit(EXIT_FAILURE);} -#define usage(args...) {print_usage(); fatal("[usage] " args);} +#include "bsdtimespec.h" +#include "khatus_lib_log.h" +#include "khatus_lib_time.h" +#define usage(...) {print_usage(); fprintf(stderr, "Error:\n " __VA_ARGS__); exit(EXIT_FAILURE);} #define ERRMSG "ERROR" static const char errmsg[] = ERRMSG; @@ -39,14 +39,14 @@ struct Fifo { typedef struct Config Config; struct Config { - int interval; + double interval; char * separator; Fifo * fifos; int fifo_count; int total_width; int output_to_x_root_window; } defaults = { - .interval = 1, + .interval = 1.0, .separator = "|", .fifos = NULL, .fifo_count = 0, @@ -57,24 +57,23 @@ struct Config { void fifo_print_one(Fifo *f) { - debug( - "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 + 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 ); } @@ -87,23 +86,23 @@ fifo_print_all(Fifo *head) } void -config_print(Config *c) +config_print(Config *cfg) { - debug( - "Config " - "{" - " interval = %d," - " separator = %s," - " fifo_count = %d," - " total_width = %d," - " fifos = ..." - " }\n", - c->interval, - c->separator, - c->fifo_count, - c->total_width + info( + "Config " + "{" + " interval = %f," + " separator = %s," + " fifo_count = %d," + " total_width = %d," + " fifos = ..." + " }\n", + cfg->interval, + cfg->separator, + cfg->fifo_count, + cfg->total_width ); - fifo_print_all(c->fifos); + fifo_print_all(cfg->fifos); } int @@ -115,31 +114,52 @@ is_pos_num(char *s) return 1; } +int +is_decimal(char *s) +{ + char c; + int seen = 0; + + while ((c = *(s++)) != '\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" - " SEPARATOR = string\n" - " INTERVAL = int (* (positive) number of seconds *)\n" - "\n", - argv0 + 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 ); 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 ); } @@ -148,44 +168,67 @@ void opts_parse_any(Config *, int, char *[], int); /* For mutually-recursive ca 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) + 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); + _khatus_lib_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': parse_opts_opt_i(cfg, argc, argv, ++i); break; /* TODO: Generic set_int */ - case 's': parse_opts_opt_s(cfg, argc, argv, ++i); break; /* TODO: Generic set_str */ - case 'x': { - cfg->output_to_x_root_window = 1; - opts_parse_any(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]); } } @@ -227,8 +270,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); } } } @@ -249,7 +295,7 @@ opts_parse(Config *cfg, int argc, char *argv[]) } void -read_error(Fifo *f, char *buf) +fifo_read_error(Fifo *f, char *buf) { char *b; int i; @@ -265,7 +311,7 @@ read_error(Fifo *f, char *buf) } void -read_one(Fifo *f, char *buf) +fifo_read_one(Fifo *f, char *buf) { ssize_t current; ssize_t total; @@ -276,13 +322,14 @@ read_one(Fifo *f, char *buf) total = 0; c = '\0'; b = buf + f->pos; - /* TODO: Could optimize here and only blank-out the remaining slots. */ - memset(b, ' ', f->width); 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)); - read_error(f, buf); + fifo_read_error(f, buf); + } else { + while (total++ < f->width) + *b++ = ' '; } /* TODO Record timestamp read */ close(f->fd); @@ -290,7 +337,7 @@ read_one(Fifo *f, char *buf) } void -read_all(Config *cfg, char *buf) +fifo_read_all(Config *cfg, char *buf) { fd_set fds; int maxfd = -1; @@ -298,17 +345,16 @@ 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) { error("Cannot stat \"%s\". Error: %s\n", f->name, strerror(errno)); - read_error(f, buf); + fifo_read_error(f, buf); continue; } if (!(st.st_mode & S_IFIFO)) { error("\"%s\" is not a FIFO\n", f->name); - read_error(f, buf); + fifo_read_error(f, buf); continue; } debug("opening: %s\n", f->name); @@ -317,7 +363,7 @@ read_all(Config *cfg, char *buf) if (f->fd == -1) { /* TODO: Consider backing off retries for failed fifos. */ error("Failed to open \"%s\"\n", f->name); - read_error(f, buf); + fifo_read_error(f, buf); continue; } if (f->fd > maxfd) @@ -333,7 +379,7 @@ 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); - read_one(f, buf); + fifo_read_one(f, buf); } } } @@ -351,12 +397,21 @@ 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); + + ti = timespec_of_float(cfg->interval); + if (cfg->fifos == NULL) usage("No fifo specs were given!\n"); @@ -401,15 +456,16 @@ 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 read_all based on diff of last time of - * read_all and desired time of next TTL check. - * */ - read_all(cfg, buf); + /* 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"); @@ -418,6 +474,19 @@ 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); + } else { + debug("snooze NO\n"); + } } return EXIT_SUCCESS;