From f266bab3d154e9e082403b89ea60d0fe35fe4096 Mon Sep 17 00:00:00 2001 From: "Costa Tsaousis (ktsaou)" Date: Fri, 3 Mar 2017 01:11:12 +0200 Subject: [PATCH] freeipmi.plugin; fixes #1799; fixes #1794; fixes #1309; fixes #1729 --- CMakeLists.txt | 28 +- configure.ac | 25 +- netdata-installer.sh | 22 +- src/Makefile.am | 15 +- src/freeipmi_plugin.c | 1552 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1638 insertions(+), 4 deletions(-) create mode 100644 src/freeipmi_plugin.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 8931d8f8..20aceb3c 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,9 @@ set(NETDATA_SOURCE_FILES src/global_statistics.h src/health.c src/health.h + src/health_config.c + src/health_json.c + src/health_log.c src/inlined.h src/ipc.c src/ipc.h @@ -103,6 +106,19 @@ set(NETDATA_SOURCE_FILES src/rrd.h src/rrd2json.c src/rrd2json.h + src/rrd2json_api_old.c + src/rrd2json_api_old.h + src/rrdcalc.c + src/rrdcalctemplate.c + src/rrddim.c + src/rrddimvar.c + src/rrdfamily.c + src/rrdhost.c + src/rrdpush.c + src/rrdpush.h + src/rrdset.c + src/rrdsetvar.c + src/rrdvar.c src/simple_pattern.c src/simple_pattern.h src/socket.c src/socket.h @@ -116,6 +132,10 @@ set(NETDATA_SOURCE_FILES src/unit_test.h src/url.c src/url.h + src/web_api_old.c + src/web_api_old.h + src/web_api_v1.c + src/web_api_v1.h src/web_buffer.c src/web_buffer.h src/web_buffer_svg.c @@ -124,7 +144,7 @@ set(NETDATA_SOURCE_FILES src/web_client.h src/web_server.c src/web_server.h - src/rrdhost.c src/rrdfamily.c src/rrdset.c src/rrddim.c src/health_log.c src/health_config.c src/health_json.c src/rrdcalc.c src/rrdcalctemplate.c src/rrdvar.c src/rrddimvar.c src/rrdsetvar.c src/rrdpush.c src/rrdpush.h src/web_api_old.c src/web_api_old.h src/web_api_v1.c src/web_api_v1.h src/rrd2json_api_old.c src/rrd2json_api_old.h) + ) set(APPS_PLUGIN_SOURCE_FILES src/appconfig.c @@ -142,6 +162,9 @@ set(APPS_PLUGIN_SOURCE_FILES src/web_buffer.h config.h) +set(FREEIPMI_PLUGIN_SOURCE_FILES + src/plugin_freeipmi.c) + include_directories(AFTER .) add_definitions(-DHAVE_CONFIG_H -DCACHE_DIR="/var/cache/netdata" -DCONFIG_DIR="/etc/netdata" -DLOG_DIR="/var/log/netdata" -DPLUGINS_DIR="/usr/libexec/netdata" -DWEB_DIR="/usr/share/netdata" -DVARLIB_DIR="/var/lib/netdata") @@ -151,3 +174,6 @@ target_link_libraries (netdata m z uuid ${CMAKE_THREAD_LIBS_INIT}) add_executable(apps.plugin ${APPS_PLUGIN_SOURCE_FILES} src/inlined.h) target_link_libraries (apps.plugin m ${CMAKE_THREAD_LIBS_INIT}) + +add_executable(freeipmi.plugin ${FREEIPMI_PLUGIN_SOURCE_FILES}) +target_link_libraries (freeipmi.plugin ipmimonitoring) diff --git a/configure.ac b/configure.ac index 1dffbe0f..2aa742df 100644 --- a/configure.ac +++ b/configure.ac @@ -77,6 +77,12 @@ AC_ARG_ENABLE( , [enable_plugin_nfacct="no"] ) +AC_ARG_ENABLE( + [plugin-freeipmi], + [AS_HELP_STRING([--enable-plugin-freeipmi], [freeipmi plugin, requires root])], + , + [enable_plugin_freeipmi="no"] +) AC_ARG_ENABLE( [pedantic], [AS_HELP_STRING([--enable-pedantic], [enable pedantic compiler warnings])], @@ -191,6 +197,19 @@ AC_DEFINE([NETDATA_WITH_UUID], [1], [uuid settings]) OPTIONAL_UUID_CLFAGS="${UUID_CFLAGS}" OPTIONAL_UUID_LIBS="${UUID_LIBS}" +if test "${enable_plugin_freeipmi}" = "yes"; then + PKG_CHECK_MODULES( + [IPMIMONITORING], + [libipmimonitoring], + ) + test -z "${IPMIMONITORING_LIBS}" && AC_MSG_ERROR([ipmimonitoring required but not found. Try installing 'libipmimonitoring-dev' or 'libipmimonitoring-devel']) + freeipmi=yes + AC_DEFINE([HAVE_FREEIPMI], [1], [ipmimonitoring usability]) + OPTIONAL_IPMIMONITORING_CLFAGS="${IPMIMONITORING_CFLAGS}" + OPTIONAL_IPMIMONITORING_LIBS="${IPMIMONITORING_LIBS}" +fi +AM_CONDITIONAL([FREEIPMI], [test x$freeipmi = xyes]) + if test "${enable_plugin_nfacct}" = "yes"; then PKG_CHECK_MODULES( [NFACCT], @@ -206,6 +225,7 @@ if test "${enable_plugin_nfacct}" = "yes"; then OPTIONAL_NFACCT_CLFAGS="${NFACCT_CFLAGS} ${LIBMNL_CFLAGS}" OPTIONAL_NFACCT_LIBS="${NFACCT_LIBS} ${LIBMNL_LIBS}" fi + if test "${with_zlib}" = "yes"; then PKG_CHECK_MODULES( [ZLIB], @@ -216,6 +236,7 @@ if test "${with_zlib}" = "yes"; then OPTIONAL_ZLIB_CLFAGS="${ZLIB_CFLAGS}" OPTIONAL_ZLIB_LIBS="${ZLIB_LIBS}" fi + if test "${with_math}" = "yes"; then test -z "${MATH_LIBS}" && AC_MSG_ERROR([math required but not found]) AC_DEFINE([STORAGE_WITH_MATH], [1], [math settings]) @@ -258,6 +279,8 @@ AC_SUBST([OPTIONAL_ZLIB_LIBS]) AC_SUBST([OPTIONAL_UUID_CLFAGS]) AC_SUBST([OPTIONAL_UUID_LIBS]) AC_SUBST([OPTIONAL_CAP_LIBS]) +AC_SUBST([OPTIONAL_IPMIMONITORING_CFLAGS]) +AC_SUBST([OPTIONAL_IPMIMONITORING_LIBS]) AC_CONFIG_FILES([ Makefile @@ -275,4 +298,4 @@ AC_CONFIG_FILES([ AC_OUTPUT test "${with_math}" != "yes" && AC_MSG_WARN([You are building without math. math allows accurate calculations. It should be enabled.]) || : -test "${with_zlib}" != "yes" && AC_MSG_WARN([You are building without zlib. zlib allows netdata to trasnfer a lot less data with web clients. It should be enabled.]) || : +test "${with_zlib}" != "yes" && AC_MSG_WARN([You are building without zlib. zlib allows netdata to transfer a lot less data with web clients. It should be enabled.]) || : diff --git a/netdata-installer.sh b/netdata-installer.sh index 51fc2582..680772c7 100755 --- a/netdata-installer.sh +++ b/netdata-installer.sh @@ -73,6 +73,7 @@ DONOTSTART=0 DONOTWAIT=0 NETDATA_PREFIX= LIBS_ARE_HERE=0 +NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS-}" usage() { netdata_banner "installer command line options" @@ -97,6 +98,10 @@ Valid are: Do not wait for the user to press ENTER. Start immediately building it. + --enable-plugin-freeipmi + + Enable the FreeIPMI plugin. + --zlib-is-really-here --libs-are-really-here @@ -184,6 +189,10 @@ do then DONOTWAIT=1 shift 1 + elif [ "$1" = "--enable-plugin-freeipmi" ] + then + NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS} --enable-plugin-freeipmi" + shift 1 elif [ "$1" = "--help" -o "$1" = "-h" ] then usage @@ -402,7 +411,10 @@ run ./configure \ --prefix="${NETDATA_PREFIX}/usr" \ --sysconfdir="${NETDATA_PREFIX}/etc" \ --localstatedir="${NETDATA_PREFIX}/var" \ - --with-zlib --with-math --with-user=netdata \ + --with-zlib \ + --with-math \ + --with-user=netdata \ + ${NETDATA_CONFIGURE_OPTIONS} \ CFLAGS="${CFLAGS}" || exit 1 # remove the build_error hook @@ -734,6 +746,13 @@ if [ ${UID} -eq 0 ] run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" run chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/apps.plugin" fi + + if [ -f "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/freeipmi.plugin" ] + then + run chown root "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/freeipmi.plugin" + run chmod 4755 "${NETDATA_PREFIX}/usr/libexec/netdata/plugins.d/freeipmi.plugin" + fi + else run chown "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_LOG_DIR}" run chown -R "${NETDATA_USER}:${NETDATA_USER}" "${NETDATA_PREFIX}/usr/libexec/netdata" @@ -1243,6 +1262,7 @@ force=0 export PATH="\${PATH}:${PATH}" export CFLAGS="${CFLAGS}" +export NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS}" INSTALL_UID="${UID}" if [ "\${INSTALL_UID}" != "\${UID}" ] diff --git a/src/Makefile.am b/src/Makefile.am index 3e6a79c1..2c672391 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,6 +12,7 @@ AM_CPPFLAGS = \ -DRUN_DIR="\"$(localstatedir)/run/netdata\"" \ -DWEB_DIR="\"$(webdir)\"" \ $(NULL) + AM_CFLAGS = \ $(OPTIONAL_MATH_CFLAGS) \ $(OPTIONAL_NFACCT_CLFAGS) \ @@ -24,8 +25,9 @@ dist_cache_DATA = .keep dist_varlib_DATA = .keep dist_registry_DATA = .keep dist_log_DATA = .keep +plugins_PROGRAMS = if !MACOS -plugins_PROGRAMS = apps.plugin +plugins_PROGRAMS += apps.plugin endif netdata_SOURCES = \ @@ -159,3 +161,14 @@ apps_plugin_LDADD = \ $(OPTIONAL_CAP_LIBS) \ $(NULL) +if FREEIPMI +plugins_PROGRAMS += freeipmi.plugin + +freeipmi_plugin_SOURCES = \ + freeipmi_plugin.c \ + $(NULL) + +freeipmi_plugin_LDADD = \ + $(OPTIONAL_IPMIMONITORING_LIBS) \ + $(NULL) +endif diff --git a/src/freeipmi_plugin.c b/src/freeipmi_plugin.c new file mode 100644 index 00000000..d5f6ea95 --- /dev/null +++ b/src/freeipmi_plugin.c @@ -0,0 +1,1552 @@ +/*****************************************************************************\ + * $Id: ipmimonitoring-sensors.c,v 1.51 2016/11/02 23:46:24 chu11 Exp $ + * $Id: ipmimonitoring-sel.c,v 1.51 2016/11/02 23:46:24 chu11 Exp $ + ***************************************************************************** + * Copyright (C) 2007-2015 Lawrence Livermore National Security, LLC. + * Copyright (C) 2006-2007 The Regents of the University of California. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Albert Chu + * UCRL-CODE-222073 + * + * This file is part of Ipmimonitoring, an IPMI sensor monitoring + * library. For details, see http://www.llnl.gov/linux/. + * + * Ipmimonitoring is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * Ipmimonitoring is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with Ipmimonitoring. If not, see . +\*****************************************************************************/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_FREEIPMI + +#include +#include + +/* Communication Configuration - Initialize accordingly */ + +/* Hostname, NULL for In-band communication, non-null for a hostname */ +char *hostname = NULL; + +/* In-band Communication Configuration */ +int driver_type = IPMI_MONITORING_DRIVER_TYPE_KCS; /* or -1 for default */ +int disable_auto_probe = 0; /* probe for in-band device */ +unsigned int driver_address = 0; /* not used if probing */ +unsigned int register_spacing = 0; /* not used if probing */ +char *driver_device = NULL; /* not used if probing */ + +/* Out-of-band Communication Configuration */ +int protocol_version = IPMI_MONITORING_PROTOCOL_VERSION_1_5; /* or -1 for default */ +char *username = "foousername"; +char *password = "foopassword"; +unsigned char *k_g = NULL; +unsigned int k_g_len = 0; +int privilege_level = IPMI_MONITORING_PRIVILEGE_LEVEL_USER; /* or -1 for default */ +int authentication_type = IPMI_MONITORING_AUTHENTICATION_TYPE_MD5; /* or -1 for default */ +int cipher_suite_id = 0; /* or -1 for default */ +int session_timeout = 0; /* 0 for default */ +int retransmission_timeout = 0; /* 0 for default */ + +/* Workarounds - specify workaround flags if necessary */ +unsigned int workaround_flags = 0; + +/* Initialize w/ record id numbers to only monitor specific record ids */ +unsigned int record_ids[] = {0}; +unsigned int record_ids_length = 0; + +/* Initialize w/ sensor types to only monitor specific sensor types + * see ipmi_monitoring.h sensor types list. + */ +unsigned int sensor_types[] = {0}; +unsigned int sensor_types_length = 0; + +/* Set to an appropriate alternate if desired */ +char *sdr_cache_directory = "/tmp"; +char *sensor_config_file = NULL; + +/* Set to 1 or 0 to enable these sensor reading flags + * - See ipmi_monitoring.h for descriptions of these flags. + */ +int reread_sdr_cache = 0; +int ignore_non_interpretable_sensors = 1; +int bridge_sensors = 0; +int interpret_oem_data = 0; +int shared_sensors = 0; +int discrete_reading = 0; +int ignore_scanning_disabled = 0; +int assume_bmc_owner = 0; +int entity_sensor_names = 0; + +/* Initialization flags + * + * Most commonly bitwise OR IPMI_MONITORING_FLAGS_DEBUG and/or + * IPMI_MONITORING_FLAGS_DEBUG_IPMI_PACKETS for extra debugging + * information. + */ +unsigned int ipmimonitoring_init_flags = 0; + +int errnum; + +// ---------------------------------------------------------------------------- +// SEL only variables + +/* Initialize w/ date range to only monitoring specific date range */ +char *date_begin = NULL; /* use MM/DD/YYYY format */ +char *date_end = NULL; /* use MM/DD/YYYY format */ + +int assume_system_event_record = 0; + +char *sel_config_file = NULL; + + +// ---------------------------------------------------------------------------- +// functions common to sensors and SEL + +static void +_init_ipmi_config (struct ipmi_monitoring_ipmi_config *ipmi_config) +{ + assert (ipmi_config); + + ipmi_config->driver_type = driver_type; + ipmi_config->disable_auto_probe = disable_auto_probe; + ipmi_config->driver_address = driver_address; + ipmi_config->register_spacing = register_spacing; + ipmi_config->driver_device = driver_device; + + ipmi_config->protocol_version = protocol_version; + ipmi_config->username = username; + ipmi_config->password = password; + ipmi_config->k_g = k_g; + ipmi_config->k_g_len = k_g_len; + ipmi_config->privilege_level = privilege_level; + ipmi_config->authentication_type = authentication_type; + ipmi_config->cipher_suite_id = cipher_suite_id; + ipmi_config->session_timeout_len = session_timeout; + ipmi_config->retransmission_timeout_len = retransmission_timeout; + + ipmi_config->workaround_flags = workaround_flags; +} + +#ifdef NETDATA_COMMENTED +static const char * +_get_sensor_type_string (int sensor_type) +{ + switch (sensor_type) + { + case IPMI_MONITORING_SENSOR_TYPE_RESERVED: + return ("Reserved"); + case IPMI_MONITORING_SENSOR_TYPE_TEMPERATURE: + return ("Temperature"); + case IPMI_MONITORING_SENSOR_TYPE_VOLTAGE: + return ("Voltage"); + case IPMI_MONITORING_SENSOR_TYPE_CURRENT: + return ("Current"); + case IPMI_MONITORING_SENSOR_TYPE_FAN: + return ("Fan"); + case IPMI_MONITORING_SENSOR_TYPE_PHYSICAL_SECURITY: + return ("Physical Security"); + case IPMI_MONITORING_SENSOR_TYPE_PLATFORM_SECURITY_VIOLATION_ATTEMPT: + return ("Platform Security Violation Attempt"); + case IPMI_MONITORING_SENSOR_TYPE_PROCESSOR: + return ("Processor"); + case IPMI_MONITORING_SENSOR_TYPE_POWER_SUPPLY: + return ("Power Supply"); + case IPMI_MONITORING_SENSOR_TYPE_POWER_UNIT: + return ("Power Unit"); + case IPMI_MONITORING_SENSOR_TYPE_COOLING_DEVICE: + return ("Cooling Device"); + case IPMI_MONITORING_SENSOR_TYPE_OTHER_UNITS_BASED_SENSOR: + return ("Other Units Based Sensor"); + case IPMI_MONITORING_SENSOR_TYPE_MEMORY: + return ("Memory"); + case IPMI_MONITORING_SENSOR_TYPE_DRIVE_SLOT: + return ("Drive Slot"); + case IPMI_MONITORING_SENSOR_TYPE_POST_MEMORY_RESIZE: + return ("POST Memory Resize"); + case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_FIRMWARE_PROGRESS: + return ("System Firmware Progress"); + case IPMI_MONITORING_SENSOR_TYPE_EVENT_LOGGING_DISABLED: + return ("Event Logging Disabled"); + case IPMI_MONITORING_SENSOR_TYPE_WATCHDOG1: + return ("Watchdog 1"); + case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_EVENT: + return ("System Event"); + case IPMI_MONITORING_SENSOR_TYPE_CRITICAL_INTERRUPT: + return ("Critical Interrupt"); + case IPMI_MONITORING_SENSOR_TYPE_BUTTON_SWITCH: + return ("Button/Switch"); + case IPMI_MONITORING_SENSOR_TYPE_MODULE_BOARD: + return ("Module/Board"); + case IPMI_MONITORING_SENSOR_TYPE_MICROCONTROLLER_COPROCESSOR: + return ("Microcontroller/Coprocessor"); + case IPMI_MONITORING_SENSOR_TYPE_ADD_IN_CARD: + return ("Add In Card"); + case IPMI_MONITORING_SENSOR_TYPE_CHASSIS: + return ("Chassis"); + case IPMI_MONITORING_SENSOR_TYPE_CHIP_SET: + return ("Chip Set"); + case IPMI_MONITORING_SENSOR_TYPE_OTHER_FRU: + return ("Other Fru"); + case IPMI_MONITORING_SENSOR_TYPE_CABLE_INTERCONNECT: + return ("Cable/Interconnect"); + case IPMI_MONITORING_SENSOR_TYPE_TERMINATOR: + return ("Terminator"); + case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_BOOT_INITIATED: + return ("System Boot Initiated"); + case IPMI_MONITORING_SENSOR_TYPE_BOOT_ERROR: + return ("Boot Error"); + case IPMI_MONITORING_SENSOR_TYPE_OS_BOOT: + return ("OS Boot"); + case IPMI_MONITORING_SENSOR_TYPE_OS_CRITICAL_STOP: + return ("OS Critical Stop"); + case IPMI_MONITORING_SENSOR_TYPE_SLOT_CONNECTOR: + return ("Slot/Connector"); + case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_ACPI_POWER_STATE: + return ("System ACPI Power State"); + case IPMI_MONITORING_SENSOR_TYPE_WATCHDOG2: + return ("Watchdog 2"); + case IPMI_MONITORING_SENSOR_TYPE_PLATFORM_ALERT: + return ("Platform Alert"); + case IPMI_MONITORING_SENSOR_TYPE_ENTITY_PRESENCE: + return ("Entity Presence"); + case IPMI_MONITORING_SENSOR_TYPE_MONITOR_ASIC_IC: + return ("Monitor ASIC/IC"); + case IPMI_MONITORING_SENSOR_TYPE_LAN: + return ("LAN"); + case IPMI_MONITORING_SENSOR_TYPE_MANAGEMENT_SUBSYSTEM_HEALTH: + return ("Management Subsystem Health"); + case IPMI_MONITORING_SENSOR_TYPE_BATTERY: + return ("Battery"); + case IPMI_MONITORING_SENSOR_TYPE_SESSION_AUDIT: + return ("Session Audit"); + case IPMI_MONITORING_SENSOR_TYPE_VERSION_CHANGE: + return ("Version Change"); + case IPMI_MONITORING_SENSOR_TYPE_FRU_STATE: + return ("FRU State"); + } + + return ("Unrecognized"); +} +#endif // NETDATA_COMMENTED + + +// ---------------------------------------------------------------------------- +// BEGIN NETDATA CODE + +static int debug = 0; + +static int netdata_update_every = 5; +static int netdata_priority = 90000; + +static size_t netdata_sensors_updated = 0; +static size_t netdata_sensors_collected = 0; +static size_t netdata_sel_events = 0; + +struct sensor { + int record_id; + int sensor_number; + int sensor_type; + int sensor_state; + int sensor_units; + char *sensor_name; + + int sensor_reading_type; + union { + uint8_t bool_value; + uint32_t uint32_value; + double double_value; + } sensor_reading; + + int sent; + int ignore; + int exposed; + int updated; + struct sensor *next; +} *sensors_root = NULL; + +static void netdata_mark_as_not_updated() { + struct sensor *sn; + for(sn = sensors_root; sn ;sn = sn->next) + sn->updated = sn->sent = 0; + + netdata_sensors_updated = 0; + netdata_sensors_collected = 0; + netdata_sel_events = 0; +} + +static void send_chart_to_netdata_for_units(int units) { + struct sensor *sn; + + switch(units) { + case IPMI_MONITORING_SENSOR_UNITS_CELSIUS: + printf("CHART ipmi.temperatures_c '' 'System Celcius Temperatures read by IPMI' 'Celcius' 'temperatures' 'ipmi.temperatures_c' 'line' %d %d\n" + , netdata_priority + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_FAHRENHEIT: + printf("CHART ipmi.temperatures_f '' 'System Fahrenheit Temperatures read by IPMI' 'Fahrenheit' 'temperatures' 'ipmi.temperatures_f' 'line' %d %d\n" + , netdata_priority + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_VOLTS: + printf("CHART ipmi.volts '' 'System Voltages read by IPMI' 'Volts' 'voltages' 'ipmi.voltages' 'line' %d %d\n" + , netdata_priority + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_AMPS: + printf("CHART ipmi.amps '' 'System Current read by IPMI' 'Amps' 'current' 'ipmi.amps' 'line' %d %d\n" + , netdata_priority + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_RPM: + printf("CHART ipmi.rpm '' 'System Fans read by IPMI' 'RPM' 'fans' 'ipmi.rpm' 'line' %d %d\n" + , netdata_priority + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_WATTS: + printf("CHART ipmi.watts '' 'System Power read by IPMI' 'Watts' 'power' 'ipmi.watts' 'line' %d %d\n" + , netdata_priority + , netdata_update_every + ); + break; + + case IPMI_MONITORING_SENSOR_UNITS_PERCENT: + printf("CHART ipmi.percent '' 'System Metrics read by IPMI' '%%' 'other' 'ipmi.percent' 'line' %d %d\n" + , netdata_priority + , netdata_update_every + ); + break; + + default: + for(sn = sensors_root; sn; sn = sn->next) + if(sn->sensor_units == units) + sn->ignore = 1; + return; + } + + for(sn = sensors_root; sn; sn = sn->next) { + if(sn->sensor_units == units && sn->updated && !sn->ignore) { + sn->exposed = 1; + + switch(sn->sensor_reading_type) { + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: + printf("DIMENSION i%d_n%d_r%d '%s i%d' absolute 1 1\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , sn->sensor_name + , sn->sensor_number + ); + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: + printf("DIMENSION i%d_n%d_r%d '%s i%d' absolute 1 1000\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , sn->sensor_name + , sn->sensor_number + ); + break; + + default: + sn->ignore = 1; + break; + } + } + } +} + +static void send_metrics_to_netdata_for_units(int units) { + struct sensor *sn; + + switch(units) { + case IPMI_MONITORING_SENSOR_UNITS_CELSIUS: + printf("BEGIN ipmi.temperatures_c\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_FAHRENHEIT: + printf("BEGIN ipmi.temperatures_f\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_VOLTS: + printf("BEGIN ipmi.volts\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_AMPS: + printf("BEGIN ipmi.amps\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_RPM: + printf("BEGIN ipmi.rpm\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_WATTS: + printf("BEGIN ipmi.watts\n"); + break; + + case IPMI_MONITORING_SENSOR_UNITS_PERCENT: + printf("BEGIN ipmi.percent\n"); + break; + + default: + for(sn = sensors_root; sn; sn = sn->next) + if(sn->sensor_units == units) + sn->ignore = 1; + return; + } + + for(sn = sensors_root; sn; sn = sn->next) { + if(sn->sensor_units == units && sn->updated && !sn->sent && !sn->ignore) { + netdata_sensors_updated++; + + sn->sent = 1; + + switch(sn->sensor_reading_type) { + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: + printf("SET i%d_n%d_r%d = %u\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , sn->sensor_reading.bool_value + ); + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: + printf("SET i%d_n%d_r%d = %u\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , sn->sensor_reading.uint32_value + ); + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: + printf("SET i%d_n%d_r%d = %lld\n" + , sn->sensor_number + , sn->record_id + , sn->sensor_reading_type + , (long long int)(sn->sensor_reading.double_value * 1000) + ); + break; + + default: + sn->ignore = 1; + break; + } + } + } + + printf("END\n"); +} + +static void send_metrics_to_netdata() { + static int sel_chart_generated = 0; + struct sensor *sn; + + if(!sel_chart_generated) { + printf("CHART ipmi.events '' 'IPMI Events' 'events' 'events' 'ipmi.sel' 'area' %d %d\n" + , netdata_priority + , netdata_update_every + ); + printf("DIMENSION events '' absolute 1 1\n"); + } + + // generate the CHART/DIMENSION lines, if we have to + for(sn = sensors_root; sn; sn = sn->next) + if(sn->updated && !sn->exposed && !sn->ignore) + send_chart_to_netdata_for_units(sn->sensor_units); + + printf("BEGIN ipmi.events\nSET events = %zu\nEND\n", netdata_sel_events); + + // send metrics to netdata + for(sn = sensors_root; sn; sn = sn->next) + if(sn->updated && sn->exposed && !sn->sent && !sn->ignore) + send_metrics_to_netdata_for_units(sn->sensor_units); + +} + +static void netdata_get_sensor( + int record_id + , int sensor_number + , int sensor_type + , int sensor_state + , int sensor_units + , int sensor_reading_type + , char *sensor_name + , void *sensor_reading +) { + // find the sensor record + struct sensor *sn; + for(sn = sensors_root; sn ;sn = sn->next) + if( sn->record_id == record_id && + sn->sensor_number == sensor_number && + sn->sensor_reading_type == sensor_reading_type && + sn->sensor_units == sensor_units && + !strcmp(sn->sensor_name, sensor_name) + ) + break; + + if(!sn) { + // not found, create it + + sn = calloc(1, sizeof(struct sensor)); + if(!sn) { + fprintf(stderr, "freeipmi.plugin: cannot allocate %zu bytes of memory.", sizeof(struct sensor)); + exit(1); + } + + sn->record_id = record_id; + sn->sensor_number = sensor_number; + sn->sensor_type = sensor_type; + sn->sensor_state = sensor_state; + sn->sensor_units = sensor_units; + sn->sensor_reading_type = sensor_reading_type; + sn->sensor_name = strdup(sensor_name); + if(!sn->sensor_name) { + fprintf(stderr, "freeipmi.plugin: cannot allocate %zu bytes of memory.", strlen(sensor_name)); + exit(1); + } + + sn->next = sensors_root; + sensors_root = sn; + } + + switch(sensor_reading_type) { + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: + sn->sensor_reading.bool_value = *((uint8_t *)sensor_reading); + sn->updated = 1; + netdata_sensors_collected++; + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: + sn->sensor_reading.uint32_value = *((uint32_t *)sensor_reading); + sn->updated = 1; + netdata_sensors_collected++; + break; + + case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: + sn->sensor_reading.double_value = *((double *)sensor_reading); + sn->updated = 1; + netdata_sensors_collected++; + break; + + default: + sn->ignore = 1; + break; + } + +/* switch(sensor_state) { + case IPMI_MONITORING_STATE_NOMINAL: + case IPMI_MONITORING_STATE_WARNING: + case IPMI_MONITORING_STATE_CRITICAL: + default: + break; + } +*/ +} + +static void netdata_get_sel( + int record_id + , int record_type_class + , int sel_state +) { + (void)record_id; + (void)record_type_class; + (void)sel_state; + + netdata_sel_events++; +} + + +static unsigned long long now_realtime_usec() { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000ULL + tv.tv_usec; +} + +// END NETDATA CODE +// ---------------------------------------------------------------------------- + + +/* This is an example of how to use the libipmimonitoring library to + * read and monitor sensors. + * + * At the top of this file, you'll find a number of variables for + * configuration of IPMI communication and what sensors you are + * interested in monitoring. Those variables are used in the + * libipmimonitoring calls below. + * + * Hopefully this example will be sufficient to help anyone program + * IPMI monitoring software for their environment. + * + * To compile, linking against the library should be sufficient for + * most environments. e.g. + * + * gcc -o freeipmi.plugin freeipmi_plugin.c -lipmimonitoring + */ + +static int +_ipmimonitoring_sensors (struct ipmi_monitoring_ipmi_config *ipmi_config) +{ + ipmi_monitoring_ctx_t ctx = NULL; + unsigned int sensor_reading_flags = 0; + int i; + int sensor_count; + int rv = -1; + + if (!(ctx = ipmi_monitoring_ctx_create ())) + { + perror ("ipmi_monitoring_ctx_create:"); + goto cleanup; + } + + if (sdr_cache_directory) + { + if (ipmi_monitoring_ctx_sdr_cache_directory (ctx, + sdr_cache_directory) < 0) + { + fprintf (stderr, + "ipmi_monitoring_ctx_sdr_cache_directory: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + + /* Must call otherwise only default interpretations ever used */ + if (sensor_config_file) + { + if (ipmi_monitoring_ctx_sensor_config_file (ctx, + sensor_config_file) < 0) + { + fprintf (stderr, + "ipmi_monitoring_ctx_sensor_config_file: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else + { + if (ipmi_monitoring_ctx_sensor_config_file (ctx, NULL) < 0) + { + fprintf (stderr, + "ipmi_monitoring_ctx_sensor_config_file: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + + if (reread_sdr_cache) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_REREAD_SDR_CACHE; + + if (ignore_non_interpretable_sensors) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_NON_INTERPRETABLE_SENSORS; + + if (bridge_sensors) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_BRIDGE_SENSORS; + + if (interpret_oem_data) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_INTERPRET_OEM_DATA; + + if (shared_sensors) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_SHARED_SENSORS; + + if (discrete_reading) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_DISCRETE_READING; + + if (ignore_scanning_disabled) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_SCANNING_DISABLED; + + if (assume_bmc_owner) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_ASSUME_BMC_OWNER; + +#ifdef IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES + if (entity_sensor_names) + sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES; +#endif // IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES + + if (!record_ids_length && !sensor_types_length) + { + if ((sensor_count = ipmi_monitoring_sensor_readings_by_record_id (ctx, + hostname, + ipmi_config, + sensor_reading_flags, + NULL, + 0, + NULL, + NULL)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_readings_by_record_id: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else if (record_ids_length) + { + if ((sensor_count = ipmi_monitoring_sensor_readings_by_record_id (ctx, + hostname, + ipmi_config, + sensor_reading_flags, + record_ids, + record_ids_length, + NULL, + NULL)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_readings_by_record_id: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else + { + if ((sensor_count = ipmi_monitoring_sensor_readings_by_sensor_type (ctx, + hostname, + ipmi_config, + sensor_reading_flags, + sensor_types, + sensor_types_length, + NULL, + NULL)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_readings_by_sensor_type: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + +#ifdef NETDATA_COMMENTED + printf ("%s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", + "Record ID", + "Sensor Name", + "Sensor Number", + "Sensor Type", + "Sensor State", + "Sensor Reading", + "Sensor Units", + "Sensor Event/Reading Type Code", + "Sensor Event Bitmask", + "Sensor Event String"); +#endif // NETDATA_COMMENTED + + for (i = 0; i < sensor_count; i++, ipmi_monitoring_sensor_iterator_next (ctx)) + { + int record_id, sensor_number, sensor_type, sensor_state, sensor_units, + sensor_reading_type; + +#ifdef NETDATA_COMMENTED + int sensor_bitmask_type, sensor_bitmask, event_reading_type_code; + char **sensor_bitmask_strings = NULL; + const char *sensor_type_str; + const char *sensor_state_str; +#endif // NETDATA_COMMENTED + + char *sensor_name = NULL; + void *sensor_reading; + + if ((record_id = ipmi_monitoring_sensor_read_record_id (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_record_id: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_number = ipmi_monitoring_sensor_read_sensor_number (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_sensor_number: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_type = ipmi_monitoring_sensor_read_sensor_type (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_sensor_type: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (!(sensor_name = ipmi_monitoring_sensor_read_sensor_name (ctx))) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_sensor_name: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_state = ipmi_monitoring_sensor_read_sensor_state (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_sensor_state: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_units = ipmi_monitoring_sensor_read_sensor_units (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_sensor_units: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + +#ifdef NETDATA_COMMENTED + if ((sensor_bitmask_type = ipmi_monitoring_sensor_read_sensor_bitmask_type (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_sensor_bitmask_type: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + if ((sensor_bitmask = ipmi_monitoring_sensor_read_sensor_bitmask (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_sensor_bitmask: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (!(sensor_bitmask_strings = ipmi_monitoring_sensor_read_sensor_bitmask_strings (ctx))) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_sensor_bitmask_strings: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } +#endif // NETDATA_COMMENTED + + if ((sensor_reading_type = ipmi_monitoring_sensor_read_sensor_reading_type (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_sensor_reading_type: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + sensor_reading = ipmi_monitoring_sensor_read_sensor_reading (ctx); + +#ifdef NETDATA_COMMENTED + if ((event_reading_type_code = ipmi_monitoring_sensor_read_event_reading_type_code (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sensor_read_event_reading_type_code: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } +#endif // NETDATA_COMMENTED + + netdata_get_sensor( + record_id + , sensor_number + , sensor_type + , sensor_state + , sensor_units + , sensor_reading_type + , sensor_name + , sensor_reading + ); + +#ifdef NETDATA_COMMENTED + if (!strlen (sensor_name)) + sensor_name = "N/A"; + + sensor_type_str = _get_sensor_type_string (sensor_type); + + printf ("%u, %s, %u, %s", + record_id, + sensor_name, + sensor_number, + sensor_type_str); + + if (sensor_state == IPMI_MONITORING_STATE_NOMINAL) + sensor_state_str = "Nominal"; + else if (sensor_state == IPMI_MONITORING_STATE_WARNING) + sensor_state_str = "Warning"; + else if (sensor_state == IPMI_MONITORING_STATE_CRITICAL) + sensor_state_str = "Critical"; + else + sensor_state_str = "N/A"; + + printf (", %s", sensor_state_str); + + if (sensor_reading) + { + const char *sensor_units_str; + + if (sensor_reading_type == IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL) + printf (", %s", + (*((uint8_t *)sensor_reading) ? "true" : "false")); + else if (sensor_reading_type == IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32) + printf (", %u", + *((uint32_t *)sensor_reading)); + else if (sensor_reading_type == IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE) + printf (", %.2f", + *((double *)sensor_reading)); + else + printf (", N/A"); + + if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_CELSIUS) + sensor_units_str = "C"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_FAHRENHEIT) + sensor_units_str = "F"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_VOLTS) + sensor_units_str = "V"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_AMPS) + sensor_units_str = "A"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_RPM) + sensor_units_str = "RPM"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_WATTS) + sensor_units_str = "W"; + else if (sensor_units == IPMI_MONITORING_SENSOR_UNITS_PERCENT) + sensor_units_str = "%"; + else + sensor_units_str = "N/A"; + + printf (", %s", sensor_units_str); + } + else + printf (", N/A, N/A"); + + printf (", %Xh", event_reading_type_code); + + /* It is possible you may want to monitor specific event + * conditions that may occur. If that is the case, you may want + * to check out what specific bitmask type and bitmask events + * occurred. See ipmi_monitoring_bitmasks.h for a list of + * bitmasks and types. + */ + + if (sensor_bitmask_type != IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN) + printf (", %Xh", sensor_bitmask); + else + printf (", N/A"); + + if (sensor_bitmask_type != IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN) + { + unsigned int i = 0; + + printf (","); + + while (sensor_bitmask_strings[i]) + { + printf (" "); + + printf ("'%s'", + sensor_bitmask_strings[i]); + + i++; + } + } + else + printf (", N/A"); + + printf ("\n"); +#endif // NETDATA_COMMENTED + } + + rv = 0; + cleanup: + if (ctx) + ipmi_monitoring_ctx_destroy (ctx); + return (rv); +} + + +/* This is an example of how to use the libipmimonitoring library to + * read and monitor the SEL. + * + * At the top of this file, you'll find a number of variables for + * configuration of IPMI communication and what SEL records you are + * interested in monitoring. Those variables are used in the + * libipmimonitoring calls below. + * + * Hopefully this example will be sufficient to help anyone program + * IPMI monitoring software for their environment. + * + * To compile, linking against the library should be sufficient for + * most environments. e.g. + * + * gcc -o ipmimonitoring-sel ipmimonitoring-sel.c -lipmimonitoring + */ + +/* Communication Configuration - Initialize accordingly */ + +static int +_ipmimonitoring_sel (struct ipmi_monitoring_ipmi_config *ipmi_config) +{ + ipmi_monitoring_ctx_t ctx = NULL; + unsigned int sel_flags = 0; + int i; + int sel_count; + int rv = -1; + + if (!(ctx = ipmi_monitoring_ctx_create ())) + { + perror ("ipmi_monitoring_ctx_create:"); + goto cleanup; + } + + if (sdr_cache_directory) + { + if (ipmi_monitoring_ctx_sdr_cache_directory (ctx, + sdr_cache_directory) < 0) + { + fprintf (stderr, + "ipmi_monitoring_ctx_sdr_cache_directory: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + + /* Must call otherwise only default interpretations ever used */ + if (sel_config_file) + { + if (ipmi_monitoring_ctx_sel_config_file (ctx, + sel_config_file) < 0) + { + fprintf (stderr, + "ipmi_monitoring_ctx_sel_config_file: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else + { + if (ipmi_monitoring_ctx_sel_config_file (ctx, NULL) < 0) + { + fprintf (stderr, + "ipmi_monitoring_ctx_sel_config_file: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + + if (reread_sdr_cache) + sel_flags |= IPMI_MONITORING_SEL_FLAGS_REREAD_SDR_CACHE; + + if (interpret_oem_data) + sel_flags |= IPMI_MONITORING_SEL_FLAGS_INTERPRET_OEM_DATA; + + if (assume_system_event_record) + sel_flags |= IPMI_MONITORING_SEL_FLAGS_ASSUME_SYSTEM_EVENT_RECORD; + +#ifdef IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES + if (entity_sensor_names) + sel_flags |= IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES; +#endif // IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES + + if (record_ids_length) + { + if ((sel_count = ipmi_monitoring_sel_by_record_id (ctx, + hostname, + ipmi_config, + sel_flags, + record_ids, + record_ids_length, + NULL, + NULL)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_by_record_id: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else if (sensor_types_length) + { + if ((sel_count = ipmi_monitoring_sel_by_sensor_type (ctx, + hostname, + ipmi_config, + sel_flags, + sensor_types, + sensor_types_length, + NULL, + NULL)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_by_sensor_type: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else if (date_begin + || date_end) + { + if ((sel_count = ipmi_monitoring_sel_by_date_range (ctx, + hostname, + ipmi_config, + sel_flags, + date_begin, + date_end, + NULL, + NULL)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_by_sensor_type: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + else + { + if ((sel_count = ipmi_monitoring_sel_by_record_id (ctx, + hostname, + ipmi_config, + sel_flags, + NULL, + 0, + NULL, + NULL)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_by_record_id: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + } + +#ifdef NETDATA_COMMENTED + printf ("%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s\n", + "Record ID", + "Record Type", + "SEL State", + "Timestamp", + "Sensor Name", + "Sensor Type", + "Event Direction", + "Event Type Code", + "Event Data", + "Event Offset", + "Event Offset String"); +#endif // NETDATA_COMMENTED + + for (i = 0; i < sel_count; i++, ipmi_monitoring_sel_iterator_next (ctx)) + { + int record_id, record_type, sel_state, record_type_class; +#ifdef NETDATA_COMMENTED + int sensor_type, sensor_number, event_direction, + event_offset_type, event_offset, event_type_code, manufacturer_id; + unsigned int timestamp, event_data1, event_data2, event_data3; + char *event_offset_string = NULL; + const char *sensor_type_str; + const char *event_direction_str; + const char *sel_state_str; + char *sensor_name = NULL; + unsigned char oem_data[64]; + int oem_data_len; + unsigned int j; +#endif // NETDATA_COMMENTED + + if ((record_id = ipmi_monitoring_sel_read_record_id (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_record_id: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((record_type = ipmi_monitoring_sel_read_record_type (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_record_type: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((record_type_class = ipmi_monitoring_sel_read_record_type_class (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_record_type_class: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sel_state = ipmi_monitoring_sel_read_sel_state (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_sel_state: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + netdata_get_sel( + record_id + , record_type_class + , sel_state + ); + +#ifdef NETDATA_COMMENTED + if (sel_state == IPMI_MONITORING_STATE_NOMINAL) + sel_state_str = "Nominal"; + else if (sel_state == IPMI_MONITORING_STATE_WARNING) + sel_state_str = "Warning"; + else if (sel_state == IPMI_MONITORING_STATE_CRITICAL) + sel_state_str = "Critical"; + else + sel_state_str = "N/A"; + + printf ("%u, %u, %s", + record_id, + record_type, + sel_state_str); + + if (record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_SYSTEM_EVENT_RECORD + || record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_TIMESTAMPED_OEM_RECORD) + { + + if (ipmi_monitoring_sel_read_timestamp (ctx, ×tamp) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_timestamp: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + /* XXX: This should be converted to a nice date output using + * your favorite timestamp -> string conversion functions. + */ + printf (", %u", timestamp); + } + else + printf (", N/A"); + + if (record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_SYSTEM_EVENT_RECORD) + { + /* If you are integrating ipmimonitoring SEL into a monitoring application, + * you may wish to count the number of times a specific error occurred + * and report that to the monitoring application. + * + * In this particular case, you'll probably want to check out + * what sensor type each SEL event is reporting, the + * event offset type, and the specific event offset that occurred. + * + * See ipmi_monitoring_offsets.h for a list of event offsets + * and types. + */ + + if (!(sensor_name = ipmi_monitoring_sel_read_sensor_name (ctx))) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_sensor_name: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_type = ipmi_monitoring_sel_read_sensor_type (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_sensor_type: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((sensor_number = ipmi_monitoring_sel_read_sensor_number (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_sensor_number: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((event_direction = ipmi_monitoring_sel_read_event_direction (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_event_direction: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((event_type_code = ipmi_monitoring_sel_read_event_type_code (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_event_type_code: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (ipmi_monitoring_sel_read_event_data (ctx, + &event_data1, + &event_data2, + &event_data3) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_event_data: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((event_offset_type = ipmi_monitoring_sel_read_event_offset_type (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_event_offset_type: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if ((event_offset = ipmi_monitoring_sel_read_event_offset (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_event_offset: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (!(event_offset_string = ipmi_monitoring_sel_read_event_offset_string (ctx))) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_event_offset_string: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + if (!strlen (sensor_name)) + sensor_name = "N/A"; + + sensor_type_str = _get_sensor_type_string (sensor_type); + + if (event_direction == IPMI_MONITORING_SEL_EVENT_DIRECTION_ASSERTION) + event_direction_str = "Assertion"; + else + event_direction_str = "Deassertion"; + + printf (", %s, %s, %u, %s, %Xh, %Xh-%Xh-%Xh", + sensor_name, + sensor_type_str, + sensor_number, + event_direction_str, + event_type_code, + event_data1, + event_data2, + event_data3); + + if (event_offset_type != IPMI_MONITORING_EVENT_OFFSET_TYPE_UNKNOWN) + printf (", %Xh", event_offset); + else + printf (", N/A"); + + if (event_offset_type != IPMI_MONITORING_EVENT_OFFSET_TYPE_UNKNOWN) + printf (", %s", event_offset_string); + else + printf (", N/A"); + } + else if (record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_TIMESTAMPED_OEM_RECORD + || record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_NON_TIMESTAMPED_OEM_RECORD) + { + if (record_type_class == IPMI_MONITORING_SEL_RECORD_TYPE_CLASS_TIMESTAMPED_OEM_RECORD) + { + if ((manufacturer_id = ipmi_monitoring_sel_read_manufacturer_id (ctx)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_manufacturer_id: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + printf (", Manufacturer ID = %Xh", manufacturer_id); + } + + if ((oem_data_len = ipmi_monitoring_sel_read_oem_data (ctx, oem_data, 1024)) < 0) + { + fprintf (stderr, + "ipmi_monitoring_sel_read_oem_data: %s\n", + ipmi_monitoring_ctx_errormsg (ctx)); + goto cleanup; + } + + printf (", OEM Data = "); + + for (j = 0; j < oem_data_len; j++) + printf ("%02Xh ", oem_data[j]); + } + else + printf (", N/A, N/A, N/A, N/A, N/A, N/A, N/A"); + + printf ("\n"); +#endif // NETDATA_COMMENTED + } + + rv = 0; + cleanup: + if (ctx) + ipmi_monitoring_ctx_destroy (ctx); + return (rv); +} + +// ---------------------------------------------------------------------------- +// MAIN PROGRAM FOR NETDATA PLUGIN + +int ipmi_collect_data(struct ipmi_monitoring_ipmi_config *ipmi_config) { + + if (_ipmimonitoring_sensors(ipmi_config) < 0) return -1; + if (_ipmimonitoring_sel (ipmi_config) < 0) return -2; + + return 0; +} + +int ipmi_detect_speed_secs(struct ipmi_monitoring_ipmi_config *ipmi_config) { + int i, checks = 10; + unsigned long long total = 0; + + for(i = 0 ; i < checks ; i++) { + if(debug) fprintf(stderr, "freeipmi.plugin: checking data collection speed iteration %d of %d\n", i+1, checks); + + // measure the time a data collection needs + unsigned long long start = now_realtime_usec(); + if(ipmi_collect_data(ipmi_config) < 0) { + fprintf(stderr, "freeipmi.plugin: data collection failed.\n"); + exit(1); + } + unsigned long long end = now_realtime_usec(); + + if(debug) fprintf(stderr, "freeipmi.plugin: data collection speed was %llu usec\n", end - start); + + // add it to our total + total += end - start; + + // wait the same time + // to avoid flooding the IPMI processor with requests + usleep(end - start); + } + + // so, we assume it needed 3x the time + // we find the average in microseconds + // and we round-up to the closest second + + return (( total * 3 / checks / 1000000 ) + 1); +} + +int main (int argc, char **argv) { + struct ipmi_monitoring_ipmi_config ipmi_config; + + // parse command line parameters + + int i, freq = 0; + for(i = 1; i < argc ; i++) { + if(!freq) { + int n = atoi(argv[i]); + if(n > 0) { + freq = n; + continue; + } + } + + if(strcmp("debug", argv[i]) == 0) { + debug = 1; + continue; + } + + fprintf(stderr, "freeipmi.plugin: ignoring parameter '%s'\n", argv[i]); + } + + if(freq > 0 && freq < netdata_update_every) + netdata_update_every = freq; + + else if(freq) + fprintf(stderr, "freeipmi.plugin: update frequency %d seconds is too small for IPMI. Using %d", freq, netdata_update_every); + + + // initialize IPMI + + if(debug) fprintf(stderr, "freeipmi.plugin: calling _init_ipmi_config()\n"); + + _init_ipmi_config(&ipmi_config); + + if(debug) fprintf(stderr, "freeipmi.plugin: calling ipmi_monitoring_init()\n"); + + if(ipmi_monitoring_init(ipmimonitoring_init_flags, &errnum) < 0) { + fprintf(stderr, "ipmi_monitoring_init: %s\n", ipmi_monitoring_ctx_strerror(errnum)); + exit(1); + } + + if(debug) fprintf(stderr, "freeipmi.plugin: detecting IPMI minimum update frequency...\n"); + freq = ipmi_detect_speed_secs(&ipmi_config); + if(debug) fprintf(stderr, "freeipmi.plugin: IPMI minimum update frequency was calculated to %d seconds.\n", freq); + + if(netdata_update_every < freq) { + fprintf(stderr, "freeipmi.plugin: enforcing minimum data collection frequency, calculated to %d seconds.\n", freq); + netdata_update_every = freq; + } + + // the main loop + if(debug) fprintf(stderr, "freeipmi.plugin: starting data collection\n"); + + size_t iteration = 0; + unsigned long long step = netdata_update_every * 1000000ULL; + unsigned long long now = now_realtime_usec(); + unsigned long long next = now - (now % step) + step; + while(1) { + unsigned long long last = now; + now = now_realtime_usec(); + if(debug && iteration) + fprintf(stderr, "freeipmi.plugin: iteration %zu, dt %llu usec, sensors collected %zu, sensors sent to netdata %zu \n" + , iteration + , now - last + , netdata_sensors_collected + , netdata_sensors_updated + ); + + while(now < next) { + if(debug) fprintf(stderr, "freeipmi.plugin: sleeping for %llu usec\n", next - now); + usleep(next - now); + now = now_realtime_usec(); + } + next = now - (now % step) + step; + + netdata_mark_as_not_updated(); + + if(debug) fprintf(stderr, "freeipmi.plugin: calling ipmi_collect_data()\n"); + if(ipmi_collect_data(&ipmi_config) < 0) { + fprintf(stderr, "freeipmi.plugin: data collection failed.\n"); + exit(1); + } + + if(debug) fprintf(stderr, "freeipmi.plugin: calling send_metrics_to_netdata()\n"); + send_metrics_to_netdata(); + fflush(stdout); + + iteration++; + } + exit(0); +} + +#else // !HAVE_FREEIPMI + +int main(int argc, char **argv) { + fprintf(stderr, "freeipmi.plugin: not compiled."); + exit(1); +} + +#endif -- 2.39.2