]> arthur.barton.de Git - netdata.git/commitdiff
freeipmi.plugin; fixes #1799; fixes #1794; fixes #1309; fixes #1729
authorCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Thu, 2 Mar 2017 23:11:12 +0000 (01:11 +0200)
committerCosta Tsaousis (ktsaou) <costa@tsaousis.gr>
Thu, 2 Mar 2017 23:11:12 +0000 (01:11 +0200)
CMakeLists.txt
configure.ac
netdata-installer.sh
src/Makefile.am
src/freeipmi_plugin.c [new file with mode: 0644]

index 8931d8f8060066513a837a54a1242e7fd4147170..20aceb3cd6d2ac8aa575c69d634804684553f4e8 100755 (executable)
@@ -33,6 +33,9 @@ set(NETDATA_SOURCE_FILES
         src/global_statistics.h
         src/health.c
         src/health.h
         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
         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/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
         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/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
         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/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
 
 set(APPS_PLUGIN_SOURCE_FILES
         src/appconfig.c
@@ -142,6 +162,9 @@ set(APPS_PLUGIN_SOURCE_FILES
         src/web_buffer.h
         config.h)
 
         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")
 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(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)
index 1dffbe0f304133bdd1a205da6e3d72c0f2b699d5..2aa742df86756ac7b82ffdf9bc2ed2cb41be85e6 100644 (file)
@@ -77,6 +77,12 @@ AC_ARG_ENABLE(
        ,
        [enable_plugin_nfacct="no"]
 )
        ,
        [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])],
 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}"
 
 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],
 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
        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],
 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
        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])
 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_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
 
 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.]) || :
 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.]) || :
index 51fc25820e54a8e301b5c00ab7f17d8e75a64246..680772c7ec489b2333ad117217f648aacd3f2fda 100755 (executable)
@@ -73,6 +73,7 @@ DONOTSTART=0
 DONOTWAIT=0
 NETDATA_PREFIX=
 LIBS_ARE_HERE=0
 DONOTWAIT=0
 NETDATA_PREFIX=
 LIBS_ARE_HERE=0
+NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS-}"
 
 usage() {
     netdata_banner "installer command line options"
 
 usage() {
     netdata_banner "installer command line options"
@@ -97,6 +98,10 @@ Valid <installer options> are:
         Do not wait for the user to press ENTER.
         Start immediately building it.
 
         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
 
    --zlib-is-really-here
    --libs-are-really-here
 
@@ -184,6 +189,10 @@ do
         then
         DONOTWAIT=1
         shift 1
         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
     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" \
     --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
     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
         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"
 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 PATH="\${PATH}:${PATH}"
 export CFLAGS="${CFLAGS}"
+export NETDATA_CONFIGURE_OPTIONS="${NETDATA_CONFIGURE_OPTIONS}"
 
 INSTALL_UID="${UID}"
 if [ "\${INSTALL_UID}" != "\${UID}" ]
 
 INSTALL_UID="${UID}"
 if [ "\${INSTALL_UID}" != "\${UID}" ]
index 3e6a79c191fe5e05e44941625eedcaab35697d54..2c672391e3fd1b173a53722a743cb6e7bf28c18c 100644 (file)
@@ -12,6 +12,7 @@ AM_CPPFLAGS = \
        -DRUN_DIR="\"$(localstatedir)/run/netdata\"" \
        -DWEB_DIR="\"$(webdir)\"" \
        $(NULL)
        -DRUN_DIR="\"$(localstatedir)/run/netdata\"" \
        -DWEB_DIR="\"$(webdir)\"" \
        $(NULL)
+
 AM_CFLAGS = \
        $(OPTIONAL_MATH_CFLAGS) \
        $(OPTIONAL_NFACCT_CLFAGS) \
 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
 dist_varlib_DATA = .keep
 dist_registry_DATA = .keep
 dist_log_DATA = .keep
+plugins_PROGRAMS =
 if !MACOS
 if !MACOS
-plugins_PROGRAMS = apps.plugin
+plugins_PROGRAMS += apps.plugin
 endif
 
 netdata_SOURCES = \
 endif
 
 netdata_SOURCES = \
@@ -159,3 +161,14 @@ apps_plugin_LDADD = \
        $(OPTIONAL_CAP_LIBS) \
        $(NULL)
 
        $(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 (file)
index 0000000..d5f6ea9
--- /dev/null
@@ -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 <chu11@llnl.gov>
+ *  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 <http://www.gnu.org/licenses/>.
+\*****************************************************************************/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#ifdef HAVE_FREEIPMI
+
+#include <ipmi_monitoring.h>
+#include <ipmi_monitoring_bitmasks.h>
+
+/* 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, &timestamp) < 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