+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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;
+}