--- src/fritzbox.c +++ src/fritzbox.c @@ -0,0 +1,709 @@ + +//collectd includes +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static const char* CONFIG_KEYS[] = +{ + "RecordEnergyUsage", + "RecordDSLErrors", + "RecordDSLLineInfo", + "RecordDSLRateInfo" +}; + +static int CONFIG_KEYS_COUNT = STATIC_ARRAY_SIZE(CONFIG_KEYS); + +static char record_energy = 1; +static char record_dsl_errors = 1; +static char record_dsl_line_info = 1; +static char record_dsl_rate_info = 1; + +const char* WEBCM_CMD = "/usr/www/cgi-bin/webcm nextpage=%s"; +const char* TMP_FILE_TEMPLATE = "/tmp/statsXXXXXX"; +static char* tmp_file_name; + +/* energy consumption information */ +const char* POWER_PARAM_DSL = "\n"; +const char* POWER_PARAM_PHONE = "\n"; +const char* POWER_PARAM_PROCESSOR = "\n"; +const char* POWER_PARAM_TOTAL = "\n"; +const char* POWER_PARAM_USB = "\n"; +const char* POWER_PARAM_WLAN = "\n"; + +/* dsl downstream rate information */ +const char* DSL_PARAM_DS_DSLAM_MAX = "\n"; +const char* DSL_PARAM_DS_DSLAM_MIN = "\n"; +const char* DSL_PARAM_DS_LINE_CAPACITY = "\n"; +const char* DSL_PARAM_DS_RATE = "\n"; + +/* dsl upstream rate information */ +const char* DSL_PARAM_US_DSLAM_MAX = "\n"; +const char* DSL_PARAM_US_DSLAM_MIN = "\n"; +const char* DSL_PARAM_US_LINE_CAPACITY = "\n"; +const char* DSL_PARAM_US_RATE = "\n"; + +/* dsl downstream error information */ +const char* DSL_PARAM_DS_ERROR_CRC = "\n"; +const char* DSL_PARAM_DS_ERROR_FEC = "\n"; + +/* dsl upstream errror information */ +const char* DSL_PARAM_US_ERROR_CRC = "\n"; +const char* DSL_PARAM_US_ERROR_FEC = "\n"; + +/* dsl downstream signal information */ +const char* DSL_PARAM_DS_ATTENUATION = "\n"; +const char* DSL_PARAM_DS_SNR = "\n"; + +/* dsl upstream signal information */ +const char* DSL_PARAM_US_ATTENUATION = "\n"; +const char* DSL_PARAM_US_SNR = "\n"; + +#define CHECK_WRITE(fd, cstring, cstringlen) \ + { \ + int write_count = 0; \ + do { \ + int write_val = write(fd, cstring + write_count, cstringlen - write_count); \ + if(write_val == -1) { \ + ERROR("Error writing webcm command %s at %s:%d, reason: %s", \ + cstring, __FUNCTION__, __LINE__, strerror(errno)); \ + close(fd); \ + return -1; \ + } \ + write_count += write_val; \ + } while(write_count < cstringlen); \ + } + +static void dispatch_dsl_ds_rate_information(long input[4]) +{ + value_t values[4]; + value_list_t vl = VALUE_LIST_INIT; + values[0].gauge = input[0] * 1000; + values[1].gauge = input[1] * 1000; + values[2].gauge = input[2] * 1000; + values[3].gauge = input[3] * 1000; + + if(values[0].gauge < 0) + { + ERROR("Couldn't read DSL DSLAM downstream max rate"); + return; + } + + if(values[1].gauge < 0) + { + ERROR("Couldn't read DSL DSLAM downstream min rate"); + return; + } + + if(values[2].gauge < 0) + { + ERROR("Couldn't read DSL downstream line capacity"); + return; + } + + if(values[3].gauge < 0) + { + ERROR("Couldn't read DSL downstream current rate"); + return; + } + + vl.values = values; + vl.values_len = 4; + sstrncpy(vl.host, hostname_g, sizeof (vl.host)); + sstrncpy(vl.plugin, "fritzbox", sizeof (vl.plugin)); + sstrncpy(vl.type, "dsl_rates", sizeof (vl.type)); + sstrncpy(vl.type_instance, "downstream", sizeof(vl.type)); + + plugin_dispatch_values (&vl); +} + +static void dispatch_dsl_us_rate_information(long input[4]) +{ + value_t values[4]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = input[0] * 1000; + values[1].gauge = input[1] * 1000; + values[2].gauge = input[2] * 1000; + values[3].gauge = input[3] * 1000; + + if(values[0].gauge < 0) + { + ERROR("Couldn't read DSL DSLAM upstream max rate"); + return; + } + + if(values[1].gauge < 0) + { + ERROR("Couldn't read DSL DSLAM upstream min rate"); + return; + } + + if(values[2].gauge < 0) + { + ERROR("Couldn't read DSL upstream line capacity"); + return; + } + + if(values[3].gauge < 0) + { + ERROR("Couldn't read DSL upstream current rate"); + return; + } + + vl.values = values; + vl.values_len = 4; + sstrncpy(vl.host, hostname_g, sizeof (vl.host)); + sstrncpy(vl.plugin, "fritzbox", sizeof (vl.plugin)); + sstrncpy(vl.type, "dsl_rates", sizeof (vl.type)); + sstrncpy(vl.type_instance, "upstream", sizeof(vl.type)); + + plugin_dispatch_values (&vl); +} + +static void dispatch_dsl_ds_errors(long input[2]) +{ + value_t values[2]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = input[0]; + values[1].gauge = input[1]; + + if(values[0].gauge < 0) + { + WARNING("Couldn't read DSL downstream CRC error rate"); + return; + } + + if(values[1].gauge < 0) + { + WARNING("Couldn't read DSL DSLAM downstream FEC error rate"); + return; + } + + vl.values = values; + vl.values_len = 2; + sstrncpy(vl.host, hostname_g, sizeof (vl.host)); + sstrncpy(vl.plugin, "fritzbox", sizeof (vl.plugin)); + sstrncpy(vl.type, "dsl_errors", sizeof (vl.type)); + sstrncpy(vl.type_instance, "downstream", sizeof(vl.type)); + + plugin_dispatch_values (&vl); +} + +static void dispatch_dsl_us_errors(long input[2]) +{ + value_t values[2]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = input[0]; + values[1].gauge = input[1]; + + if(values[0].gauge < 0) + { + WARNING("Couldn't read DSL upstream CRC error rate"); + return; + } + + if(values[1].gauge < 0) + { + WARNING("Couldn't read DSL DSLAM upstream FEC error rate"); + return; + } + + vl.values = values; + vl.values_len = 2; + sstrncpy(vl.host, hostname_g, sizeof (vl.host)); + sstrncpy(vl.plugin, "fritzbox", sizeof (vl.plugin)); + sstrncpy(vl.type, "dsl_errors", sizeof (vl.type)); + sstrncpy(vl.type_instance, "upstream", sizeof(vl.type)); + + plugin_dispatch_values (&vl); +} + +static void dispatch_dsl_ds_line_info(long input[2]) +{ + value_t values[2]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = input[0]; + values[1].gauge = input[1]; + + if(values[0].gauge < 0) + { + WARNING("Couldn't read DSL downstream attenuation value"); + return; + } + + if(values[1].gauge < 0) + { + WARNING("Couldn't read DSL downstream SNR value"); + return; + } + + vl.values = values; + vl.values_len = 2; + sstrncpy(vl.host, hostname_g, sizeof (vl.host)); + sstrncpy(vl.plugin, "fritzbox", sizeof (vl.plugin)); + sstrncpy(vl.type, "dsl_line", sizeof (vl.type)); + sstrncpy(vl.type_instance, "downstream", sizeof(vl.type)); + + plugin_dispatch_values (&vl); +} + +static void dispatch_dsl_us_line_info(long input[2]) +{ + value_t values[2]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = input[0]; + values[1].gauge = input[1]; + + if(values[0].gauge < 0) + { + WARNING("Couldn't read DSL upstream attenuation value"); + return; + } + + if(values[1].gauge < 0) + { + WARNING("Couldn't read DSL upstream SNR value"); + return; + } + + vl.values = values; + vl.values_len = 2; + sstrncpy(vl.host, hostname_g, sizeof (vl.host)); + sstrncpy(vl.plugin, "fritzbox", sizeof (vl.plugin)); + sstrncpy(vl.type, "dsl_line", sizeof (vl.type)); + sstrncpy(vl.type_instance, "upstream", sizeof(vl.type)); + + plugin_dispatch_values (&vl); +} + +void dispatch_power_values(long input[6]) +{ + value_t values[6]; + value_list_t vl = VALUE_LIST_INIT; + values[0].gauge = input[0]; + values[1].gauge = input[1]; + values[2].gauge = input[2]; + values[3].gauge = input[3]; + values[4].gauge = input[4]; + values[5].gauge = input[5]; + + if(values[0].gauge < 0) + { + WARNING("Couldn't read DSL power information"); + return; + } + else if(values[0].gauge > 100) + values[0].gauge = 100; + + if(values[1].gauge < 0) + { + WARNING("Couldn't read phone power information"); + return; + } + else if(values[1].gauge > 100) + values[1].gauge = 100; + + if(values[2].gauge < 0) + { + WARNING("Couldn't read processor power information"); + return; + } + else if(values[2].gauge > 100) + values[2].gauge = 100; + + if(values[3].gauge < 0) + { + WARNING("Couldn't read total power information"); + return; + } + else if(values[3].gauge > 100) + values[3].gauge = 100; + + if(values[4].gauge < 0) + { + WARNING("Couldn't read USB power information"); + return; + } + else if(values[4].gauge > 100) + values[4].gauge = 100; + + if(values[5].gauge < 0) + { + WARNING("Couldn't read WLAN power information"); + return; + } + else if(values[5].gauge > 100) + values[5].gauge = 100; + + vl.values = values; + vl.values_len = 6; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, "fritzbox", sizeof (vl.plugin)); + sstrncpy (vl.type, "fritzbox_energy", sizeof (vl.type)); + + plugin_dispatch_values (&vl); +} + +static int read_webcm_values(const char* filename) +{ + FILE *cmd_pipe; + char line[1024]; + int ret_val = -1; + + char cmd[strlen(WEBCM_CMD)+strlen(filename)]; + + ssnprintf(cmd, sizeof(cmd), WEBCM_CMD, filename); + + if( !(cmd_pipe = popen(cmd, "r")) ) + { + ERROR("could not open pipe"); + return 1; + } + + while( fgets(line, sizeof(line), cmd_pipe) ) + { + if(strncmp(line, "Energy", strlen("Energy")) == 0) + { + long val[6]; + int i; + ret_val = 0; + + for(i = 0; i < 6; i++) + { + if(fgets(line, sizeof(line), cmd_pipe)) + { + val[i] = strtol(line, NULL, 10); + } + else + { + ERROR("Error reading energy output line from webcm output"); + ret_val = -1; + break; + } + } + + if(ret_val) + break; + + dispatch_power_values(val); + } + else if(strncmp(line, "DSLRateDown", strlen("DSLRateDown")) == 0) + { + long values[4]; + int i; + ret_val = 0; + + for(i = 0; i < 4; i++) + { + + if(fgets(line, sizeof(line), cmd_pipe)) + { + values[i] = strtol(line, NULL, 10); + } + else + { + ERROR("Error reading dsl rate downstream line from webcm output"); + ret_val = -1; + break; + } + } + + if(ret_val) + break; + + dispatch_dsl_ds_rate_information(values); + } + else if(strncmp(line, "DSLRateUp", strlen("DSLRateUp")) == 0) + { + long values[4]; + int i; + ret_val = 0; + + for(i = 0; i < 4; i++) + { + + if(fgets(line, sizeof(line), cmd_pipe)) + { + values[i] = strtol(line, NULL, 10); + } + else + { + ERROR("Error reading dsl rate upstream line from webcm output"); + ret_val = -1; + break; + } + } + + if(ret_val) + break; + + dispatch_dsl_us_rate_information(values); + } + else if(strncmp(line, "DSLErrorDown", strlen("DSLErrorDown")) == 0) + { + long values[2]; + int i; + ret_val = 0; + + for(i = 0; i < 2; i++) + { + + if(fgets(line, sizeof(line), cmd_pipe)) + { + values[i] = strtol(line, NULL, 10); + } + else + { + ERROR("Error reading dsl error downstream line from webcm output"); + ret_val = -1; + break; + } + } + + if(ret_val) + break; + + dispatch_dsl_ds_errors(values); + } + else if(strncmp(line, "DSLErrorUp", strlen("DSLErrorUp")) == 0) + { + long values[2]; + int i; + ret_val = 0; + + for(i = 0; i < 2; i++) + { + + if(fgets(line, sizeof(line), cmd_pipe)) + { + values[i] = strtol(line, NULL, 10); + } + else + { + ERROR("Error reading upstream line from webcm output"); + ret_val = -1; + break; + } + } + + if(ret_val) + break; + + dispatch_dsl_us_errors(values); + } + else if(strncmp(line, "DSLLineDown", strlen("DSLLineDown")) == 0) + { + long values[2]; + int i; + ret_val = 0; + + for(i = 0; i < 2; i++) + { + + if(fgets(line, sizeof(line), cmd_pipe)) + { + values[i] = strtol(line, NULL, 10); + } + else + { + ERROR("Error reading upstream line from webcm output"); + ret_val = -1; + break; + } + } + + if(ret_val) + break; + + dispatch_dsl_ds_line_info(values); + } + else if(strncmp(line, "DSLLineUp", strlen("DSLLineUp")) == 0) + { + long values[2]; + int i; + ret_val = 0; + + for(i = 0; i < 2; i++) + { + + if(fgets(line, sizeof(line), cmd_pipe)) + { + values[i] = strtol(line, NULL, 10); + } + else + { + ERROR("Error reading upstream line from webcm output"); + ret_val = -1; + break; + } + } + + if(ret_val) + break; + + dispatch_dsl_us_line_info(values); + } + else + { + ERROR("Invalid output generated by webcm %s", line); + ret_val = -1; + break; + } + + } + + pclose(cmd_pipe); + return ret_val; +} + +static int fritzbox_read() +{ + int fd; + + fd = open(tmp_file_name, O_CREAT | O_TRUNC | O_WRONLY); + if(fd == -1) + { + ERROR("Could not open temporary file for writing %s %s", tmp_file_name, strerror(errno)); + return -1; + } + + if(record_energy) + { + CHECK_WRITE(fd, "Energy\n", strlen("Energy\n")); + CHECK_WRITE(fd, POWER_PARAM_DSL, strlen(POWER_PARAM_DSL)); + CHECK_WRITE(fd, POWER_PARAM_PHONE, strlen(POWER_PARAM_PHONE)); + CHECK_WRITE(fd, POWER_PARAM_PROCESSOR, strlen(POWER_PARAM_PROCESSOR)); + CHECK_WRITE(fd, POWER_PARAM_TOTAL, strlen(POWER_PARAM_TOTAL)); + CHECK_WRITE(fd, POWER_PARAM_USB, strlen(POWER_PARAM_USB)); + CHECK_WRITE(fd, POWER_PARAM_WLAN, strlen(POWER_PARAM_WLAN)); + } + + if(record_dsl_rate_info) + { + CHECK_WRITE(fd, "DSLRateDown\n", strlen("DSLRateDown\n")); + CHECK_WRITE(fd, DSL_PARAM_DS_DSLAM_MAX, strlen(DSL_PARAM_DS_DSLAM_MAX)); + CHECK_WRITE(fd, DSL_PARAM_DS_DSLAM_MAX, strlen(DSL_PARAM_DS_DSLAM_MAX)); + CHECK_WRITE(fd, DSL_PARAM_DS_LINE_CAPACITY, strlen(DSL_PARAM_DS_LINE_CAPACITY)); + CHECK_WRITE(fd, DSL_PARAM_DS_RATE, strlen(DSL_PARAM_DS_RATE)); + + CHECK_WRITE(fd, "DSLRateUp\n", strlen("DSLRateUp\n")); + CHECK_WRITE(fd, DSL_PARAM_US_DSLAM_MAX, strlen(DSL_PARAM_US_DSLAM_MAX)); + CHECK_WRITE(fd, DSL_PARAM_US_DSLAM_MIN, strlen(DSL_PARAM_US_DSLAM_MIN)); + CHECK_WRITE(fd, DSL_PARAM_US_LINE_CAPACITY, strlen(DSL_PARAM_US_LINE_CAPACITY)); + CHECK_WRITE(fd, DSL_PARAM_US_RATE, strlen(DSL_PARAM_US_RATE)); + + } + + if(record_dsl_errors) + { + CHECK_WRITE(fd, "DSLErrorDown\n", strlen("DSLErrorDown\n")); + CHECK_WRITE(fd, DSL_PARAM_DS_ERROR_CRC, strlen(DSL_PARAM_DS_ERROR_CRC)); + CHECK_WRITE(fd, DSL_PARAM_DS_ERROR_FEC, strlen(DSL_PARAM_DS_ERROR_FEC)); + + CHECK_WRITE(fd, "DSLErrorUp\n", strlen("DSLErrorUp\n")); + CHECK_WRITE(fd, DSL_PARAM_US_ERROR_CRC, strlen(DSL_PARAM_US_ERROR_CRC)); + CHECK_WRITE(fd, DSL_PARAM_US_ERROR_FEC, strlen(DSL_PARAM_US_ERROR_FEC)); + } + + if(record_dsl_line_info) + { + CHECK_WRITE(fd, "DSLLineDown\n", strlen("DSLLineDown\n")); + CHECK_WRITE(fd, DSL_PARAM_DS_ATTENUATION, strlen(DSL_PARAM_DS_ATTENUATION)); + CHECK_WRITE(fd, DSL_PARAM_DS_SNR, strlen(DSL_PARAM_DS_SNR)); + + CHECK_WRITE(fd, "DSLLineUp\n", strlen("DSLLineUp\n")); + CHECK_WRITE(fd, DSL_PARAM_US_ATTENUATION, strlen(DSL_PARAM_US_ATTENUATION)); + CHECK_WRITE(fd, DSL_PARAM_US_SNR, strlen( DSL_PARAM_US_SNR)); + } + + close(fd); + + return read_webcm_values(tmp_file_name); +} + +static int fritzbox_config(const char* key, const char* value) +{ + char val = 0; + if(strncasecmp(value,"true", sizeof("true")) == 0) + val = 1; + else if(strncasecmp(value,"false", sizeof("false")) == 0) + val = 0; + else + return -1; + + if(strncasecmp(key,"RecordEnergyUsage", sizeof("RecordEnergyUsage")) == 0) + { + record_energy = val; + return 0; + } + + if(strncasecmp(key,"RecordDSLErrors", sizeof("RecordDSLErrors")) == 0) + { + record_dsl_errors = val; + return 0; + } + + if(strncasecmp(key,"RecordDSLLineInfo", sizeof("RecordDSLLineInfo")) == 0) + { + record_dsl_line_info = val; + return 0; + } + + if(strncasecmp(key,"RecordDSLRateInfo", sizeof("RecordDSLRateInfo")) == 0) + { + record_dsl_rate_info = val; + return 0; + } + + return -1; +} + +static int fritzbox_init() +{ + tmp_file_name = (char *) malloc(strlen(TMP_FILE_TEMPLATE)+1); + if(!tmp_file_name) + { + ERROR("Could not allocate memory for temp file name"); + return -1; + } + + ssnprintf(tmp_file_name, strlen(TMP_FILE_TEMPLATE)+1, TMP_FILE_TEMPLATE); + + if(!mktemp(tmp_file_name)) + { + ERROR("Could not create temp file name, reason %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int fritzbox_shutdown() +{ + remove(tmp_file_name); + free(tmp_file_name); + return 0; +} + +void module_register (void) +{ + plugin_register_config("fritzbox", fritzbox_config, CONFIG_KEYS, CONFIG_KEYS_COUNT); + plugin_register_init("fritzbox", fritzbox_init); + plugin_register_read("fritzbox", fritzbox_read); + plugin_register_shutdown("fritzbox", fritzbox_shutdown); +} + --- src/types.db +++ src/types.db @@ -53,10 +53,14 @@ dns_transfer value:COUNTER:0:65535 dns_update value:COUNTER:0:65535 dns_zops value:COUNTER:0:65535 +dsl_errors crc:GAUGE:0:U, forward:GAUGE:0:U +dsl_line attenuation:GAUGE:0:U, snr:GAUGE:0:U +dsl_rates dslam_max:GAUGE:0:U, dslam_min:GAUGE:0:U, line_capacity:GAUGE:0:U, line_rate:GAUGE:0:U email_check value:GAUGE:0:U email_count value:GAUGE:0:U email_size value:GAUGE:0:U entropy entropy:GAUGE:0:4294967295 +fritzbox_energy dsl:GAUGE:0:U, phone:GAUGE:0:U, processor:GAUGE:0:U, total:GAUGE:0:U, usb:GAUGE:0:U, wlan:GAUGE:0:U fanspeed value:GAUGE:0:U file_size bytes:GAUGE:0:U files value:GAUGE:0:U