From 9201d7498dadd0230ab2326759884adeff977f03 Mon Sep 17 00:00:00 2001 From: Timo Kokkonen Date: Mon, 19 Nov 2012 20:43:52 +0200 Subject: [PATCH] Parsers: Implement framework for registering and querying parsers This framework can be used to register parsers and get parsers by a string name. This allows all need for hard coding string and parser function names together. All existing parsers are converted to use the new framework. Signed-off-by: Timo Kokkonen --- Makefile | 2 +- built_in_parsers.c | 327 +++++++++++++++++++++++++++++++++++++++++++++ built_in_parsers.h | 8 ++ config.c | 34 +---- main.c | 2 + parser.c | 272 +++---------------------------------- parser.h | 20 +-- rrdtool.c | 4 +- rrdtool.h | 2 +- utils.h | 2 + 10 files changed, 373 insertions(+), 300 deletions(-) create mode 100644 built_in_parsers.c create mode 100644 built_in_parsers.h diff --git a/Makefile b/Makefile index db48947..3a89e91 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ CC=gcc LD=ld CFLAGS=-Wall -O2 -g -RRDD_OBJS= main.o process.o rrdtool.o parser.o string.o \ +RRDD_OBJS= main.o process.o rrdtool.o parser.o built_in_parsers.o string.o \ debug.o config.o onewire_parser.o plugin_manager.o ALL_OBJS = $(RRDD_OBJS) diff --git a/built_in_parsers.c b/built_in_parsers.c new file mode 100644 index 0000000..f3a8c79 --- /dev/null +++ b/built_in_parsers.c @@ -0,0 +1,327 @@ +#include +#include +#include + +#include "parser.h" +#include "process.h" +#include "string.h" +#include "debug.h" +#include "utils.h" +#include "built_in_parsers.h" + +#define STATFILE "/proc/stat" + +static int cpu_parser(char *data, const char **p) +{ + char buf[1024]; + char *str = buf; + FILE *file = fopen(STATFILE, "r"); + long long user, nice, sys, idle, wait, irq, softirq; + + if (file == NULL) { + pr_err("Failed to open file %s\n", STATFILE); + return -1; + } + + if (!fgets(buf, 1024, file)) { + pr_err("Failed to read file %s\n", STATFILE); + fclose(file); + return -1; + } + + user = dec_to_longlong(str, &str); + nice = dec_to_longlong(str, &str); + sys = dec_to_longlong(str, &str); + idle = dec_to_longlong(str, &str); + wait = dec_to_longlong(str, &str); + irq = dec_to_longlong(str, &str); + softirq = dec_to_longlong(str, &str); + + snprintf(data, RRD_DATA_MAX_LEN, "%lld:%lld:%lld:%lld:%lld:%lld:%lld", + user, nice, sys, idle, wait, irq, softirq); + + fclose(file); + return 0; +} + +#define MEMFILE "/proc/meminfo" + +static int mem_parser(char *data, const char **p) +{ + char buf[1024], word[1024]; + int free = 0, buffered = 0, cache = 0, active = 0, inactive = 0, + swapfree = 0, anon = 0, slab = 0, tables = 0, other = 0, + swaptotal = 0, total = 0; + FILE *file = fopen(MEMFILE, "r"); + + if (file == NULL) { + pr_err("Failed to open file %s\n", MEMFILE); + return -1; + } + + while (fgets(buf, 1024, file)) { + get_word(buf, 0, word, 1024); + + if (!strcmp(word, "MemFree:")) { + free = dec_to_int(buf, NULL); + } else if (!strcmp(word, "MemTotal:")) { + total = dec_to_int(buf, NULL); + } else if (!strcmp(word, "Buffers:")) { + buffered = dec_to_int(buf, NULL); + } else if (!strcmp(word, "Cached:")) { + cache = dec_to_int(buf, NULL); + } else if (!strcmp(word, "Active:")) { + active = dec_to_int(buf, NULL); + } else if (!strcmp(word, "Inactive:")) { + inactive = dec_to_int(buf, NULL); + } else if (!strcmp(word, "SwapFree:")) { + swapfree = dec_to_int(buf, NULL); + } else if (!strcmp(word, "AnonPages:")) { + anon = dec_to_int(buf, NULL); + } else if (!strcmp(word, "Slab:")) { + slab = dec_to_int(buf, NULL); + } else if (!strcmp(word, "PageTables:")) { + tables = dec_to_int(buf, NULL); + } else if (!strcmp(word, "SwapTotal:")) { + swaptotal = dec_to_int(buf, NULL); + } + } + fclose(file); + + other = total - free - buffered - cache - anon - slab - tables; + + snprintf(data, RRD_DATA_MAX_LEN, "%f:%f:%f:%f:%f:%f:%f:%f:%f:%f:%f", + free / 1024.0, + buffered / 1024.0, + cache / 1024.0, + active / 1024.0, + inactive / 1024.0, + swapfree / 1024.0, + anon / 1024.0, + slab / 1024.0, + tables / 1024.0, + other / 1024.0, + (swaptotal - swapfree) / 1024.0); + + return 0; +} + +int cpu_mem_parser(char *data, const char **p) +{ + char cpu[1024], mem[1024]; + + cpu_parser(cpu, p); + mem_parser(mem, p); + snprintf(data, RRD_DATA_MAX_LEN, "%s:%s", cpu, mem); + + return 0; +} + +static int digitemp_parser(char *data, const char **p) +{ + const char digitemp_cmd[] = "/usr/bin/digitemp"; + char *const digitemp_args[] = { "", "-o2", "-a", "-q", 0 }; + FILE *readf; + int pid, ret; + float t1 = 0, t2 = 0, t3 = 0; + char buf[1024]; + + pid = run_piped_stream(digitemp_cmd, digitemp_args, 0, &readf, 0); + if (pid < 0) { + pr_err("Failed to parse digitemp\n"); + sprintf(data, "U:U"); + return -1; + } + + ret = fscanf(readf, "%f %f %f", &t1, &t2, &t3); + + if (ret != 3) { + pr_err("Failed to parse digitemp output: %m\n"); + sprintf(data, "U:U"); + return 1; + } + + t2 += 2.16; + t3 += -0.44; + + /* Read whatever the process might be still printing out */ + while (fgets(buf, 1024, readf)); + + harvest_zombies(pid); + snprintf(data, RRD_DATA_MAX_LEN, "%.2f:%.2f", t3, t2); + return 0; +} + +static int digitemp_parser_mod(char *data, const char **p) +{ + char buf[1024]; + int ret; + + ret = digitemp_parser(buf, p); + snprintf(data, RRD_DATA_MAX_LEN, "U:%s", buf); + + return ret; +} + +/* Run a command and feed the output from stdout directly to rrdtool */ +static int script_parser(char *rrd_data, const char **parser_data) +{ + FILE *readf; + int pid, ret; + void *tmp = parser_data; + char **cmd = tmp; + + pid = run_piped_stream(cmd[0], &cmd[1], NULL, &readf, NULL); + ret = fread(rrd_data, 1, RRD_DATA_MAX_LEN, readf); + + pr_info("Read %d bytes :%s\n", ret, rrd_data); + fclose(readf); + + harvest_zombies(pid); + + return 0; +} + +struct iface_stats { + long long rx_bytes; + long long rx_packets; + long long tx_bytes; + long long tx_packets; +}; + +#define PROC_NETDEV "/proc/net/dev" + +static int get_iface_stats(const char *iface, struct iface_stats *stat) +{ + FILE *netdev; + char buf[1024]; + char if_name[16]; + char *str; + int error; + + netdev = fopen(PROC_NETDEV, "r"); + + if (netdev == NULL) { + error = errno; + pr_err("Failed to open file %s: %d (%m)\n", + PROC_NETDEV, error); + return error; + } + + while (fgets(buf, sizeof(buf), netdev)) { + get_word(buf, &str, if_name, sizeof(if_name)); + + /* Remove the ':' at the end of the if_name */ + if_name[strlen(if_name) - 1] = 0; + if (strncmp(iface, if_name, sizeof(if_name))) + continue; + + stat->rx_bytes = dec_to_longlong(str, &str); + stat->rx_packets = dec_to_longlong(str, &str); + /* errs */ dec_to_longlong(str, &str); + /* drop */ dec_to_longlong(str, &str); + /* fifo */ dec_to_longlong(str, &str); + /* frame */ dec_to_longlong(str, &str); + /* compressed */ dec_to_longlong(str, &str); + /* multicast */ dec_to_longlong(str, &str); + stat->tx_bytes = dec_to_longlong(str, &str); + stat->tx_packets = dec_to_longlong(str, &str); + + pr_info("rx_b %lld rx_p %lld tx_b %lld tx_p %lld\n", + stat->rx_bytes, stat->rx_packets, + stat->tx_bytes, stat->tx_packets); + + return 0; + } + + pr_err("Interface %s not found\n", iface); + return -ENODEV; +} + +int netstats_parser(char *rrd_data, const char **parser_data) +{ + struct iface_stats stat; + const char **iface_name = parser_data; + int max_str = RRD_DATA_MAX_LEN; + int ret; + + if (!parser_data) { + pr_err("No device names specified\n"); + return -1; + } + + while(*iface_name) { + pr_info("getting data for iface %s\n", *iface_name); + ret = get_iface_stats(*iface_name, &stat); + + if (!ret) { + ret = snprintf(rrd_data, max_str, "%lld:%lld:%lld:%lld", + stat.rx_bytes, stat.rx_packets, + stat.tx_bytes, stat.tx_packets); + } else { + ret = snprintf(rrd_data, max_str, "U:U:U:U"); + } + + max_str -= ret; + rrd_data += ret; + + iface_name++; + if (!*iface_name) + break; + + ret = snprintf(rrd_data, max_str, ":"); + max_str -= ret; + rrd_data += ret; + } + + return 0; +} + +static struct parser_info built_in_parsers[] = { + { + .name = "cpu", + .parse = cpu_parser, + }, + { + .name = "mem", + .parse = mem_parser, + }, + { + .name = "cpu_mem", + .parse = cpu_mem_parser, + }, + { + .name = "digitemp", + .parse = digitemp_parser, + }, + { + .name = "digitemp_mod", + .parse = digitemp_parser_mod, + }, + { + .name = "script", + .parse = script_parser, + }, + { + .name = "netstats", + .parse = netstats_parser, + }, + { + .name = "onewire", + .parse = onewire_parser, + }, +}; + +int register_built_in_parsers(void) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(built_in_parsers); i++) { + ret = register_parser(&built_in_parsers[i]); + + if (ret) + break; + } + + return ret; +} diff --git a/built_in_parsers.h b/built_in_parsers.h new file mode 100644 index 0000000..76b3a98 --- /dev/null +++ b/built_in_parsers.h @@ -0,0 +1,8 @@ +#ifndef _BUILT_IN_PARSERS_H +#define _BUILT_IN_PARSERS_H + +int register_built_in_parsers(void); + +int onewire_parser(char *rrd_data, const char **parser_data); + +#endif diff --git a/config.c b/config.c index 2e28c33..939a8c6 100644 --- a/config.c +++ b/config.c @@ -145,38 +145,6 @@ static int parse_images(config_setting_t *list, struct rrd_database *db) return 0; } -static int (*str_to_parser(const char *str))(char *rrd_data, const char **parser_data) -{ - if (!str) - return NULL; - - if (!strcmp(str, "cpu")) - return cpu_parser; - - if (!strcmp(str, "mem")) - return mem_parser; - - if (!strcmp(str, "cpu_mem")) - return cpu_mem_parser; - - if (!strcmp(str, "digitemp")) - return digitemp_parser; - - if (!strcmp(str, "digitemp_mod")) - return digitemp_parser_mod; - - if (!strcmp(str, "script")) - return script_parser; - - if (!strcmp(str, "netstats")) - return netstats_parser; - - if (!strcmp(str, "onewire")) - return onewire_parser; - - return NULL; -} - static int parse_data_sources(config_setting_t *rrd, struct rrd_database *db) { config_setting_t *list, *group; @@ -286,7 +254,7 @@ static int parse_database(config_setting_t *rrd, struct rrd_database *db) /* Parser is not a mandatory parameter */ config_setting_lookup_string(rrd, "parser", &parser); - db->parse = str_to_parser(parser); + db->parser = str_to_parser(parser); list = config_setting_get_member(rrd, "image"); parse_images(list, db); diff --git a/main.c b/main.c index 49adc28..1d26539 100644 --- a/main.c +++ b/main.c @@ -9,6 +9,7 @@ #include "debug.h" #include "config.h" +#include "built_in_parsers.h" struct user_options { int max_jobs; @@ -60,6 +61,7 @@ int main(int argc, char *argv[]) if (read_args(argc, argv, &opts) < 0) return -1; + register_built_in_parsers(); if (!opts.config_file) { pr_err("No database config file given. Nothing to do\n"); diff --git a/parser.c b/parser.c index 887b524..4a5e535 100644 --- a/parser.c +++ b/parser.c @@ -1,277 +1,43 @@ -#include -#include #include #include "parser.h" -#include "process.h" -#include "string.h" #include "debug.h" -#include "utils.h" -#define STATFILE "/proc/stat" +static struct parser_info *parser_list; -int cpu_parser(char *data, const char **p) +int register_parser(struct parser_info *info) { - char buf[1024]; - char *str = buf; - FILE *file = fopen(STATFILE, "r"); - long long user, nice, sys, idle, wait, irq, softirq; + struct parser_info *parser; - if (file == NULL) { - pr_err("Failed to open file %s\n", STATFILE); + if (!info->name) { + pr_err("Unable to register parser without a name\n"); return -1; } - if (!fgets(buf, 1024, file)) { - pr_err("Failed to read file %s\n", STATFILE); - fclose(file); - return -1; - } - - user = dec_to_longlong(str, &str); - nice = dec_to_longlong(str, &str); - sys = dec_to_longlong(str, &str); - idle = dec_to_longlong(str, &str); - wait = dec_to_longlong(str, &str); - irq = dec_to_longlong(str, &str); - softirq = dec_to_longlong(str, &str); - - snprintf(data, RRD_DATA_MAX_LEN, "%lld:%lld:%lld:%lld:%lld:%lld:%lld", - user, nice, sys, idle, wait, irq, softirq); - - fclose(file); - return 0; -} - -#define MEMFILE "/proc/meminfo" - -int mem_parser(char *data, const char **p) -{ - char buf[1024], word[1024]; - int free = 0, buffered = 0, cache = 0, active = 0, inactive = 0, - swapfree = 0, anon = 0, slab = 0, tables = 0, other = 0, - swaptotal = 0, total = 0; - FILE *file = fopen(MEMFILE, "r"); + pr_info("Registering parser %s\n", info->name); - if (file == NULL) { - pr_err("Failed to open file %s\n", MEMFILE); - return -1; - } - - while (fgets(buf, 1024, file)) { - get_word(buf, 0, word, 1024); - - if (!strcmp(word, "MemFree:")) { - free = dec_to_int(buf, NULL); - } else if (!strcmp(word, "MemTotal:")) { - total = dec_to_int(buf, NULL); - } else if (!strcmp(word, "Buffers:")) { - buffered = dec_to_int(buf, NULL); - } else if (!strcmp(word, "Cached:")) { - cache = dec_to_int(buf, NULL); - } else if (!strcmp(word, "Active:")) { - active = dec_to_int(buf, NULL); - } else if (!strcmp(word, "Inactive:")) { - inactive = dec_to_int(buf, NULL); - } else if (!strcmp(word, "SwapFree:")) { - swapfree = dec_to_int(buf, NULL); - } else if (!strcmp(word, "AnonPages:")) { - anon = dec_to_int(buf, NULL); - } else if (!strcmp(word, "Slab:")) { - slab = dec_to_int(buf, NULL); - } else if (!strcmp(word, "PageTables:")) { - tables = dec_to_int(buf, NULL); - } else if (!strcmp(word, "SwapTotal:")) { - swaptotal = dec_to_int(buf, NULL); - } - } - fclose(file); - - other = total - free - buffered - cache - anon - slab - tables; - - snprintf(data, RRD_DATA_MAX_LEN, "%f:%f:%f:%f:%f:%f:%f:%f:%f:%f:%f", - free / 1024.0, - buffered / 1024.0, - cache / 1024.0, - active / 1024.0, - inactive / 1024.0, - swapfree / 1024.0, - anon / 1024.0, - slab / 1024.0, - tables / 1024.0, - other / 1024.0, - (swaptotal - swapfree) / 1024.0); - - return 0; -} - -int cpu_mem_parser(char *data, const char **p) -{ - char cpu[1024], mem[1024]; - - cpu_parser(cpu, p); - mem_parser(mem, p); - snprintf(data, RRD_DATA_MAX_LEN, "%s:%s", cpu, mem); - - return 0; -} - -int digitemp_parser(char *data, const char **p) -{ - const char digitemp_cmd[] = "/usr/bin/digitemp"; - char *const digitemp_args[] = { "", "-o2", "-a", "-q", 0 }; - FILE *readf; - int pid, ret; - float t1 = 0, t2 = 0, t3 = 0; - char buf[1024]; - - pid = run_piped_stream(digitemp_cmd, digitemp_args, 0, &readf, 0); - if (pid < 0) { - pr_err("Failed to parse digitemp\n"); - sprintf(data, "U:U"); - return -1; - } - - ret = fscanf(readf, "%f %f %f", &t1, &t2, &t3); - - if (ret != 3) { - pr_err("Failed to parse digitemp output: %m\n"); - sprintf(data, "U:U"); - return 1; + if (!parser_list) { + parser_list = info; + return 0; } - t2 += 2.16; - t3 += -0.44; - - /* Read whatever the process might be still printing out */ - while (fgets(buf, 1024, readf)); - - harvest_zombies(pid); - snprintf(data, RRD_DATA_MAX_LEN, "%.2f:%.2f", t3, t2); - return 0; -} - -int digitemp_parser_mod(char *data, const char **p) -{ - char buf[1024]; - int ret; - - ret = digitemp_parser(buf, p); - snprintf(data, RRD_DATA_MAX_LEN, "U:%s", buf); - - return ret; -} - -/* Run a command and feed the output from stdout directly to rrdtool */ -int script_parser(char *rrd_data, const char **parser_data) -{ - FILE *readf; - int pid, ret; - void *tmp = parser_data; - char **cmd = tmp; + for (parser = parser_list; parser->next; parser = parser->next) + ; - pid = run_piped_stream(cmd[0], &cmd[1], NULL, &readf, NULL); - ret = fread(rrd_data, 1, RRD_DATA_MAX_LEN, readf); - - pr_info("Read %d bytes :%s\n", ret, rrd_data); - fclose(readf); - - harvest_zombies(pid); + parser->next = info; + info->next = NULL; return 0; } -struct iface_stats { - long long rx_bytes; - long long rx_packets; - long long tx_bytes; - long long tx_packets; -}; - -#define PROC_NETDEV "/proc/net/dev" - -static int get_iface_stats(const char *iface, struct iface_stats *stat) +struct parser_info *str_to_parser(const char *str) { - FILE *netdev; - char buf[1024]; - char if_name[16]; - char *str; - int error; - - netdev = fopen(PROC_NETDEV, "r"); + struct parser_info *parser; - if (netdev == NULL) { - error = errno; - pr_err("Failed to open file %s: %d (%m)\n", - PROC_NETDEV, error); - return error; + for (parser = parser_list; parser; parser = parser->next) { + if (!strcmp(str, parser->name)) + return parser; } - while (fgets(buf, sizeof(buf), netdev)) { - get_word(buf, &str, if_name, sizeof(if_name)); - - /* Remove the ':' at the end of the if_name */ - if_name[strlen(if_name) - 1] = 0; - if (strncmp(iface, if_name, sizeof(if_name))) - continue; - - stat->rx_bytes = dec_to_longlong(str, &str); - stat->rx_packets = dec_to_longlong(str, &str); - /* errs */ dec_to_longlong(str, &str); - /* drop */ dec_to_longlong(str, &str); - /* fifo */ dec_to_longlong(str, &str); - /* frame */ dec_to_longlong(str, &str); - /* compressed */ dec_to_longlong(str, &str); - /* multicast */ dec_to_longlong(str, &str); - stat->tx_bytes = dec_to_longlong(str, &str); - stat->tx_packets = dec_to_longlong(str, &str); - - pr_info("rx_b %lld rx_p %lld tx_b %lld tx_p %lld\n", - stat->rx_bytes, stat->rx_packets, - stat->tx_bytes, stat->tx_packets); - - return 0; - } - - pr_err("Interface %s not found\n", iface); - return -ENODEV; -} - -int netstats_parser(char *rrd_data, const char **parser_data) -{ - struct iface_stats stat; - const char **iface_name = parser_data; - int max_str = RRD_DATA_MAX_LEN; - int ret; - - if (!parser_data) { - pr_err("No device names specified\n"); - return -1; - } - - while(*iface_name) { - pr_info("getting data for iface %s\n", *iface_name); - ret = get_iface_stats(*iface_name, &stat); - - if (!ret) { - ret = snprintf(rrd_data, max_str, "%lld:%lld:%lld:%lld", - stat.rx_bytes, stat.rx_packets, - stat.tx_bytes, stat.tx_packets); - } else { - ret = snprintf(rrd_data, max_str, "U:U:U:U"); - } - - max_str -= ret; - rrd_data += ret; - - iface_name++; - if (!*iface_name) - break; - - ret = snprintf(rrd_data, max_str, ":"); - max_str -= ret; - rrd_data += ret; - } - - return 0; + return NULL; } diff --git a/parser.h b/parser.h index 5ec6a8e..b5718e0 100644 --- a/parser.h +++ b/parser.h @@ -1,17 +1,17 @@ #ifndef _PARSER_H #define _PARSER_H -#define RRD_DATA_MAX_LEN 4096 +typedef int (parse_fn_t)(char *rrd_data, const char **parser_data); + +struct parser_info { + struct parser_info *next; + const char *name; + parse_fn_t *parse; +}; -int cpu_parser(char *data, const char **p); -int mem_parser(char *data, const char **p); -int cpu_mem_parser(char *data, const char **p); -int digitemp_parser(char *data, const char **p); -int digitemp_parser_mod(char *data, const char **p); -int script_parser(char *rrd_data, const char **parser_data); -int netstats_parser(char *rrd_data, const char **parser_data); -int onewire_parser(char *rrd_data, const char **parser_data); +int register_parser(struct parser_info *info); +struct parser_info *str_to_parser(const char *str); -#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define RRD_DATA_MAX_LEN 4096 #endif diff --git a/rrdtool.c b/rrdtool.c index d2fd024..53db261 100644 --- a/rrdtool.c +++ b/rrdtool.c @@ -220,8 +220,8 @@ int rrdtool_update_data(struct rrd_database *rrd) l = sprintf(data, "N:"); - if (rrd->parse) { - rrd->parse(data + l, rrd->parser_data); + if (rrd->parser && rrd->parser->parse) { + rrd->parser->parse(data + l, rrd->parser_data); data[RRD_DATA_MAX_LEN + 2] = '\0'; pr_info("Data: %s\n", data); diff --git a/rrdtool.h b/rrdtool.h index fa1df37..ae0c507 100644 --- a/rrdtool.h +++ b/rrdtool.h @@ -37,7 +37,7 @@ struct rrd_database { int interval; /* Update interval */ /* Parser to aquire data for rrd */ - int (*parse)(char *rrd_data, const char **parser_data); + struct parser_info *parser; const char **parser_data; /* data to be fed to the parser */ char *const *pre_draw_cmd; /* Command to execute prior drawing images*/ diff --git a/utils.h b/utils.h index c3d639a..3269e7d 100644 --- a/utils.h +++ b/utils.h @@ -3,4 +3,6 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define ARRAY_SIZE(a) (sizeof(a) / (sizeof((a)[0]))) + #endif -- 2.44.0