| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | /* |
| 3 | * Based on: |
| 4 | * ipmimonitoring-sensors.c,v 1.51 2016/11/02 23:46:24 chu11 Exp |
| 5 | * ipmimonitoring-sel.c,v 1.51 2016/11/02 23:46:24 chu11 Exp |
| 6 | * |
| 7 | * Copyright (C) 2007-2015 Lawrence Livermore National Security, LLC. |
| 8 | * Copyright (C) 2006-2007 The Regents of the University of California. |
| 9 | * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| 10 | * Written by Albert Chu <chu11@llnl.gov> |
| 11 | * UCRL-CODE-222073 |
| 12 | */ |
| 13 | |
| 14 | // ---------------------------------------------------------------------------- |
| 15 | // BEGIN NETDATA CODE |
| 16 | |
| 17 | // #define NETDATA_TIMING_REPORT 1 |
| 18 | #include "libnetdata/libnetdata.h" |
| 19 | |
| 20 | #define FREEIPMI_GLOBAL_FUNCTION_SENSORS() do { \ |
| 21 | fprintf(stdout, PLUGINSD_KEYWORD_FUNCTION " GLOBAL \"ipmi-sensors\" %d \"%s\" \"top\" "HTTP_ACCESS_FORMAT" %d\n", \ |
| 22 | 5, "Shows IPMI hardware sensor readings including temperatures, voltages, fan speeds, and component health status.", \ |
| 23 | (HTTP_ACCESS_FORMAT_CAST)(HTTP_ACCESS_NONE), 100); \ |
| 24 | } while(0) |
| 25 | |
| 26 | // component names, based on our patterns |
| 27 | #define NETDATA_SENSOR_COMPONENT_MEMORY_MODULE "Memory Module" |
| 28 | #define NETDATA_SENSOR_COMPONENT_MEMORY "Memory" |
| 29 | #define NETDATA_SENSOR_COMPONENT_PROCESSOR "Processor" |
| 30 | #define NETDATA_SENSOR_COMPONENT_IPU "Image Processor" |
| 31 | #define NETDATA_SENSOR_COMPONENT_STORAGE "Storage" |
| 32 | #define NETDATA_SENSOR_COMPONENT_MOTHERBOARD "Motherboard" |
| 33 | #define NETDATA_SENSOR_COMPONENT_NETWORK "Network" |
| 34 | #define NETDATA_SENSOR_COMPONENT_POWER_SUPPLY "Power Supply" |
| 35 | #define NETDATA_SENSOR_COMPONENT_SYSTEM "System" |
| 36 | #define NETDATA_SENSOR_COMPONENT_PERIPHERAL "Peripheral" |
| 37 | |
| 38 | // netdata plugin defaults |
| 39 | #define SENSORS_DICT_KEY_SIZE 2048 // the max size of the key for the dictionary of sensors |
| 40 | #define SPEED_TEST_ITERATIONS 5 // how many times to repeat data collection to decide latency |
| 41 | #define IPMI_SENSORS_DASHBOARD_PRIORITY 90000 // the priority of the sensors charts on the dashboard |
| 42 | #define IPMI_SEL_DASHBOARD_PRIORITY 99000 // the priority of the SEL events chart on the dashboard |
| 43 | #define IPMI_SENSORS_MIN_UPDATE_EVERY 5 // the minimum data collection frequency for sensors |
| 44 | #define IPMI_SEL_MIN_UPDATE_EVERY 30 // the minimum data collection frequency for SEL events |
| 45 | #define IPMI_ENABLE_SEL_BY_DEFAULT true // true/false, to enable/disable SEL by default |
| 46 | #define IPMI_RESTART_EVERY_SECONDS 14400 // restart the plugin every this many seconds |
| 47 | // this is to prevent possible bugs/leaks in ipmi libraries |
| 48 | #define IPMI_RESTART_IF_SENSORS_DONT_ITERATE_EVERY_SECONDS (10 * 60) // stale data collection detection time |
| 49 | |
| 50 | // forward definition of functions and structures |
| 51 | struct netdata_ipmi_state; |
| 52 | static void netdata_update_ipmi_sensor_reading( |
| 53 | int record_id |
| 54 | , int sensor_number |
| 55 | , int sensor_type |
| 56 | , int sensor_state |
| 57 | , int sensor_units |
| 58 | , int sensor_reading_type |
| 59 | , char *sensor_name |
| 60 | , void *sensor_reading |
| 61 | , int event_reading_type_code |
| 62 | , int sensor_bitmask_type |
| 63 | , int sensor_bitmask |
| 64 | , char **sensor_bitmask_strings |
| 65 | , struct netdata_ipmi_state *stt); |
| 66 | static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *stt, uint32_t events); |
| 67 | |
| 68 | // END NETDATA CODE |
| 69 | // ---------------------------------------------------------------------------- |
| 70 | |
| 71 | #include <stdio.h> |
| 72 | #include <stdlib.h> |
| 73 | #include <stdint.h> |
| 74 | #include <string.h> |
| 75 | #include <assert.h> |
| 76 | #include <errno.h> |
| 77 | #include <unistd.h> |
| 78 | #include <sys/time.h> |
| 79 | |
| 80 | #include <ipmi_monitoring.h> |
| 81 | #include <ipmi_monitoring_bitmasks.h> |
| 82 | #include <ipmi_monitoring_offsets.h> |
| 83 | |
| 84 | /* Communication Configuration - Initialize accordingly */ |
| 85 | |
| 86 | static netdata_mutex_t stdout_mutex; |
| 87 | |
| 88 | static void __attribute__((constructor)) init_mutex(void) { |
| 89 | netdata_mutex_init(&stdout_mutex); |
| 90 | } |
| 91 | |
| 92 | static void __attribute__((destructor)) destroy_mutex(void) { |
| 93 | netdata_mutex_destroy(&stdout_mutex); |
| 94 | } |
| 95 | |
| 96 | static bool function_plugin_should_exit = false; |
| 97 | |
| 98 | int update_every = IPMI_SENSORS_MIN_UPDATE_EVERY; // this is the minimum update frequency |
| 99 | int update_every_sel = IPMI_SEL_MIN_UPDATE_EVERY; // this is the minimum update frequency for SEL events |
| 100 | |
| 101 | /* Hostname, NULL for In-band communication, non-null for a hostname */ |
| 102 | char *hostname = NULL; |
| 103 | |
| 104 | /* In-band Communication Configuration */ |
| 105 | int driver_type = -1; // IPMI_MONITORING_DRIVER_TYPE_KCS, etc. or -1 for default |
| 106 | int disable_auto_probe = 0; /* probe for in-band device */ |
| 107 | unsigned int driver_address = 0; /* not used if probing */ |
| 108 | unsigned int register_spacing = 0; /* not used if probing */ |
| 109 | char *driver_device = NULL; /* not used if probing */ |
| 110 | |
| 111 | /* Out-of-band Communication Configuration */ |
| 112 | int freeimpi_protocol_version = -1; // IPMI_MONITORING_PROTOCOL_VERSION_1_5, etc. or -1 for default |
| 113 | char *username = ""; |
| 114 | char *password = ""; |
| 115 | unsigned char *k_g = NULL; |
| 116 | unsigned int k_g_len = 0; |
| 117 | int privilege_level = -1; // IPMI_MONITORING_PRIVILEGE_LEVEL_USER, etc. or -1 for default |
| 118 | int authentication_type = -1; // IPMI_MONITORING_AUTHENTICATION_TYPE_MD5, etc. or -1 for default |
| 119 | int cipher_suite_id = -1; /* 0 or -1 for default */ |
| 120 | int session_timeout = 0; /* 0 for default */ |
| 121 | int retransmission_timeout = 0; /* 0 for default */ |
| 122 | |
| 123 | /* Workarounds - specify workaround flags if necessary */ |
| 124 | unsigned int workaround_flags = 0; |
| 125 | |
| 126 | /* Set to an appropriate alternate if desired */ |
| 127 | char *sdr_cache_directory = "/tmp"; |
| 128 | char *sdr_sensors_cache_format = ".netdata-freeipmi-sensors-%H-on-%L.sdr"; |
| 129 | char *sdr_sel_cache_format = ".netdata-freeipmi-sel-%H-on-%L.sdr"; |
| 130 | char *sensor_config_file = NULL; |
| 131 | char *sel_config_file = NULL; |
| 132 | |
| 133 | // controlled via command line options |
| 134 | unsigned int global_sel_flags = IPMI_MONITORING_SEL_FLAGS_REREAD_SDR_CACHE; |
| 135 | unsigned int global_sensor_reading_flags = IPMI_MONITORING_SENSOR_READING_FLAGS_DISCRETE_READING|IPMI_MONITORING_SENSOR_READING_FLAGS_REREAD_SDR_CACHE; |
| 136 | bool remove_reread_sdr_after_first_use = true; |
| 137 | |
| 138 | /* Initialization flags |
| 139 | * |
| 140 | * Most commonly bitwise OR IPMI_MONITORING_FLAGS_DEBUG and/or |
| 141 | * IPMI_MONITORING_FLAGS_DEBUG_IPMI_PACKETS for extra debugging |
| 142 | * information. |
| 143 | */ |
| 144 | unsigned int ipmimonitoring_init_flags = 0; |
| 145 | |
| 146 | // ---------------------------------------------------------------------------- |
| 147 | // functions common to sensors and SEL |
| 148 | |
| 149 | static void initialize_ipmi_config (struct ipmi_monitoring_ipmi_config *ipmi_config) { |
| 150 | fatal_assert(ipmi_config); |
| 151 | |
| 152 | ipmi_config->driver_type = driver_type; |
| 153 | ipmi_config->disable_auto_probe = disable_auto_probe; |
| 154 | ipmi_config->driver_address = driver_address; |
| 155 | ipmi_config->register_spacing = register_spacing; |
| 156 | ipmi_config->driver_device = driver_device; |
| 157 | |
| 158 | ipmi_config->protocol_version = freeimpi_protocol_version; |
| 159 | ipmi_config->username = username; |
| 160 | ipmi_config->password = password; |
| 161 | ipmi_config->k_g = k_g; |
| 162 | ipmi_config->k_g_len = k_g_len; |
| 163 | ipmi_config->privilege_level = privilege_level; |
| 164 | ipmi_config->authentication_type = authentication_type; |
| 165 | ipmi_config->cipher_suite_id = cipher_suite_id; |
| 166 | ipmi_config->session_timeout_len = session_timeout; |
| 167 | ipmi_config->retransmission_timeout_len = retransmission_timeout; |
| 168 | |
| 169 | ipmi_config->workaround_flags = workaround_flags; |
| 170 | } |
| 171 | |
| 172 | static const char *netdata_ipmi_get_sensor_type_string (int sensor_type, const char **component) { |
| 173 | switch (sensor_type) { |
| 174 | case IPMI_MONITORING_SENSOR_TYPE_RESERVED: |
| 175 | return ("Reserved"); |
| 176 | |
| 177 | case IPMI_MONITORING_SENSOR_TYPE_TEMPERATURE: |
| 178 | return ("Temperature"); |
| 179 | |
| 180 | case IPMI_MONITORING_SENSOR_TYPE_VOLTAGE: |
| 181 | return ("Voltage"); |
| 182 | |
| 183 | case IPMI_MONITORING_SENSOR_TYPE_CURRENT: |
| 184 | return ("Current"); |
| 185 | |
| 186 | case IPMI_MONITORING_SENSOR_TYPE_FAN: |
| 187 | return ("Fan"); |
| 188 | |
| 189 | case IPMI_MONITORING_SENSOR_TYPE_PHYSICAL_SECURITY: |
| 190 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 191 | return ("Physical Security"); |
| 192 | |
| 193 | case IPMI_MONITORING_SENSOR_TYPE_PLATFORM_SECURITY_VIOLATION_ATTEMPT: |
| 194 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 195 | return ("Platform Security Violation Attempt"); |
| 196 | |
| 197 | case IPMI_MONITORING_SENSOR_TYPE_PROCESSOR: |
| 198 | *component = NETDATA_SENSOR_COMPONENT_PROCESSOR; |
| 199 | return ("Processor"); |
| 200 | |
| 201 | case IPMI_MONITORING_SENSOR_TYPE_POWER_SUPPLY: |
| 202 | *component = NETDATA_SENSOR_COMPONENT_POWER_SUPPLY; |
| 203 | return ("Power Supply"); |
| 204 | |
| 205 | case IPMI_MONITORING_SENSOR_TYPE_POWER_UNIT: |
| 206 | *component = NETDATA_SENSOR_COMPONENT_POWER_SUPPLY; |
| 207 | return ("Power Unit"); |
| 208 | |
| 209 | case IPMI_MONITORING_SENSOR_TYPE_COOLING_DEVICE: |
| 210 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 211 | return ("Cooling Device"); |
| 212 | |
| 213 | case IPMI_MONITORING_SENSOR_TYPE_OTHER_UNITS_BASED_SENSOR: |
| 214 | return ("Other Units Based Sensor"); |
| 215 | |
| 216 | case IPMI_MONITORING_SENSOR_TYPE_MEMORY: |
| 217 | *component = NETDATA_SENSOR_COMPONENT_MEMORY; |
| 218 | return ("Memory"); |
| 219 | |
| 220 | case IPMI_MONITORING_SENSOR_TYPE_DRIVE_SLOT: |
| 221 | *component = NETDATA_SENSOR_COMPONENT_STORAGE; |
| 222 | return ("Drive Slot"); |
| 223 | |
| 224 | case IPMI_MONITORING_SENSOR_TYPE_POST_MEMORY_RESIZE: |
| 225 | *component = NETDATA_SENSOR_COMPONENT_MEMORY; |
| 226 | return ("POST Memory Resize"); |
| 227 | |
| 228 | case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_FIRMWARE_PROGRESS: |
| 229 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 230 | return ("System Firmware Progress"); |
| 231 | |
| 232 | case IPMI_MONITORING_SENSOR_TYPE_EVENT_LOGGING_DISABLED: |
| 233 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 234 | return ("Event Logging Disabled"); |
| 235 | |
| 236 | case IPMI_MONITORING_SENSOR_TYPE_WATCHDOG1: |
| 237 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 238 | return ("Watchdog 1"); |
| 239 | |
| 240 | case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_EVENT: |
| 241 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 242 | return ("System Event"); |
| 243 | |
| 244 | case IPMI_MONITORING_SENSOR_TYPE_CRITICAL_INTERRUPT: |
| 245 | return ("Critical Interrupt"); |
| 246 | |
| 247 | case IPMI_MONITORING_SENSOR_TYPE_BUTTON_SWITCH: |
| 248 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 249 | return ("Button/Switch"); |
| 250 | |
| 251 | case IPMI_MONITORING_SENSOR_TYPE_MODULE_BOARD: |
| 252 | return ("Module/Board"); |
| 253 | |
| 254 | case IPMI_MONITORING_SENSOR_TYPE_MICROCONTROLLER_COPROCESSOR: |
| 255 | *component = NETDATA_SENSOR_COMPONENT_PROCESSOR; |
| 256 | return ("Microcontroller/Coprocessor"); |
| 257 | |
| 258 | case IPMI_MONITORING_SENSOR_TYPE_ADD_IN_CARD: |
| 259 | return ("Add In Card"); |
| 260 | |
| 261 | case IPMI_MONITORING_SENSOR_TYPE_CHASSIS: |
| 262 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 263 | return ("Chassis"); |
| 264 | |
| 265 | case IPMI_MONITORING_SENSOR_TYPE_CHIP_SET: |
| 266 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 267 | return ("Chip Set"); |
| 268 | |
| 269 | case IPMI_MONITORING_SENSOR_TYPE_OTHER_FRU: |
| 270 | return ("Other Fru"); |
| 271 | |
| 272 | case IPMI_MONITORING_SENSOR_TYPE_CABLE_INTERCONNECT: |
| 273 | return ("Cable/Interconnect"); |
| 274 | |
| 275 | case IPMI_MONITORING_SENSOR_TYPE_TERMINATOR: |
| 276 | return ("Terminator"); |
| 277 | |
| 278 | case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_BOOT_INITIATED: |
| 279 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 280 | return ("System Boot Initiated"); |
| 281 | |
| 282 | case IPMI_MONITORING_SENSOR_TYPE_BOOT_ERROR: |
| 283 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 284 | return ("Boot Error"); |
| 285 | |
| 286 | case IPMI_MONITORING_SENSOR_TYPE_OS_BOOT: |
| 287 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 288 | return ("OS Boot"); |
| 289 | |
| 290 | case IPMI_MONITORING_SENSOR_TYPE_OS_CRITICAL_STOP: |
| 291 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 292 | return ("OS Critical Stop"); |
| 293 | |
| 294 | case IPMI_MONITORING_SENSOR_TYPE_SLOT_CONNECTOR: |
| 295 | return ("Slot/Connector"); |
| 296 | |
| 297 | case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_ACPI_POWER_STATE: |
| 298 | return ("System ACPI Power State"); |
| 299 | |
| 300 | case IPMI_MONITORING_SENSOR_TYPE_WATCHDOG2: |
| 301 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 302 | return ("Watchdog 2"); |
| 303 | |
| 304 | case IPMI_MONITORING_SENSOR_TYPE_PLATFORM_ALERT: |
| 305 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 306 | return ("Platform Alert"); |
| 307 | |
| 308 | case IPMI_MONITORING_SENSOR_TYPE_ENTITY_PRESENCE: |
| 309 | return ("Entity Presence"); |
| 310 | |
| 311 | case IPMI_MONITORING_SENSOR_TYPE_MONITOR_ASIC_IC: |
| 312 | return ("Monitor ASIC/IC"); |
| 313 | |
| 314 | case IPMI_MONITORING_SENSOR_TYPE_LAN: |
| 315 | *component = NETDATA_SENSOR_COMPONENT_NETWORK; |
| 316 | return ("LAN"); |
| 317 | |
| 318 | case IPMI_MONITORING_SENSOR_TYPE_MANAGEMENT_SUBSYSTEM_HEALTH: |
| 319 | *component = NETDATA_SENSOR_COMPONENT_SYSTEM; |
| 320 | return ("Management Subsystem Health"); |
| 321 | |
| 322 | case IPMI_MONITORING_SENSOR_TYPE_BATTERY: |
| 323 | return ("Battery"); |
| 324 | |
| 325 | case IPMI_MONITORING_SENSOR_TYPE_SESSION_AUDIT: |
| 326 | return ("Session Audit"); |
| 327 | |
| 328 | case IPMI_MONITORING_SENSOR_TYPE_VERSION_CHANGE: |
| 329 | return ("Version Change"); |
| 330 | |
| 331 | case IPMI_MONITORING_SENSOR_TYPE_FRU_STATE: |
| 332 | return ("FRU State"); |
| 333 | |
| 334 | case IPMI_MONITORING_SENSOR_TYPE_UNKNOWN: |
| 335 | return ("Unknown"); |
| 336 | |
| 337 | default: |
| 338 | if(sensor_type >= IPMI_MONITORING_SENSOR_TYPE_OEM_MIN && sensor_type <= IPMI_MONITORING_SENSOR_TYPE_OEM_MAX) |
| 339 | return ("OEM"); |
| 340 | |
| 341 | return ("Unrecognized"); |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | #define netdata_ipmi_get_value_int(var, func, ctx) do { \ |
| 346 | (var) = func(ctx); \ |
| 347 | if( (var) < 0) { \ |
| 348 | collector_error("%s(): call to " #func " failed: %s", \ |
| 349 | __FUNCTION__, ipmi_monitoring_ctx_errormsg(ctx)); \ |
| 350 | goto cleanup; \ |
| 351 | } \ |
| 352 | timing_step(TIMING_STEP_FREEIPMI_READ_ ## var); \ |
| 353 | } while(0) |
| 354 | |
| 355 | #define netdata_ipmi_get_value_ptr(var, func, ctx) do { \ |
| 356 | (var) = func(ctx); \ |
| 357 | if(!(var)) { \ |
| 358 | collector_error("%s(): call to " #func " failed: %s", \ |
| 359 | __FUNCTION__, ipmi_monitoring_ctx_errormsg(ctx)); \ |
| 360 | goto cleanup; \ |
| 361 | } \ |
| 362 | timing_step(TIMING_STEP_FREEIPMI_READ_ ## var); \ |
| 363 | } while(0) |
| 364 | |
| 365 | #define netdata_ipmi_get_value_no_check(var, func, ctx) do { \ |
| 366 | (var) = func(ctx); \ |
| 367 | timing_step(TIMING_STEP_FREEIPMI_READ_ ## var); \ |
| 368 | } while(0) |
| 369 | |
| 370 | static int netdata_read_ipmi_sensors(struct ipmi_monitoring_ipmi_config *ipmi_config, struct netdata_ipmi_state *state) { |
| 371 | timing_init(); |
| 372 | |
| 373 | ipmi_monitoring_ctx_t ctx = NULL; |
| 374 | unsigned int sensor_reading_flags = global_sensor_reading_flags; |
| 375 | int i; |
| 376 | int sensor_count; |
| 377 | int rv = -1; |
| 378 | |
| 379 | if (!(ctx = ipmi_monitoring_ctx_create ())) { |
| 380 | collector_error("ipmi_monitoring_ctx_create()"); |
| 381 | goto cleanup; |
| 382 | } |
| 383 | |
| 384 | timing_step(TIMING_STEP_FREEIPMI_CTX_CREATE); |
| 385 | |
| 386 | if (sdr_cache_directory) { |
| 387 | if (ipmi_monitoring_ctx_sdr_cache_directory (ctx, sdr_cache_directory) < 0) { |
| 388 | collector_error("ipmi_monitoring_ctx_sdr_cache_directory(): %s\n", ipmi_monitoring_ctx_errormsg (ctx)); |
| 389 | goto cleanup; |
| 390 | } |
| 391 | } |
| 392 | if (sdr_sensors_cache_format) { |
| 393 | if (ipmi_monitoring_ctx_sdr_cache_filenames(ctx, sdr_sensors_cache_format) < 0) { |
| 394 | collector_error("ipmi_monitoring_ctx_sdr_cache_filenames(): %s\n", ipmi_monitoring_ctx_errormsg (ctx)); |
| 395 | goto cleanup; |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | timing_step(TIMING_STEP_FREEIPMI_DSR_CACHE_DIR); |
| 400 | |
| 401 | // Must call otherwise only default interpretations ever used |
| 402 | // sensor_config_file can be NULL |
| 403 | if (ipmi_monitoring_ctx_sensor_config_file (ctx, sensor_config_file) < 0) { |
| 404 | collector_error( "ipmi_monitoring_ctx_sensor_config_file(): %s\n", ipmi_monitoring_ctx_errormsg (ctx)); |
| 405 | goto cleanup; |
| 406 | } |
| 407 | |
| 408 | timing_step(TIMING_STEP_FREEIPMI_SENSOR_CONFIG_FILE); |
| 409 | |
| 410 | if ((sensor_count = ipmi_monitoring_sensor_readings_by_record_id (ctx, |
| 411 | hostname, |
| 412 | ipmi_config, |
| 413 | sensor_reading_flags, |
| 414 | NULL, |
| 415 | 0, |
| 416 | NULL, |
| 417 | NULL)) < 0) { |
| 418 | collector_error( "ipmi_monitoring_sensor_readings_by_record_id(): %s", |
| 419 | ipmi_monitoring_ctx_errormsg (ctx)); |
| 420 | goto cleanup; |
| 421 | } |
| 422 | |
| 423 | timing_step(TIMING_STEP_FREEIPMI_SENSOR_READINGS_BY_X); |
| 424 | |
| 425 | for (i = 0; i < sensor_count; i++, ipmi_monitoring_sensor_iterator_next (ctx)) { |
| 426 | int record_id, sensor_number, sensor_type, sensor_state, sensor_units, |
| 427 | sensor_bitmask_type, sensor_bitmask, event_reading_type_code, sensor_reading_type; |
| 428 | |
| 429 | char **sensor_bitmask_strings = NULL; |
| 430 | char *sensor_name = NULL; |
| 431 | void *sensor_reading; |
| 432 | |
| 433 | netdata_ipmi_get_value_int(record_id, ipmi_monitoring_sensor_read_record_id, ctx); |
| 434 | netdata_ipmi_get_value_int(sensor_number, ipmi_monitoring_sensor_read_sensor_number, ctx); |
| 435 | netdata_ipmi_get_value_int(sensor_type, ipmi_monitoring_sensor_read_sensor_type, ctx); |
| 436 | netdata_ipmi_get_value_ptr(sensor_name, ipmi_monitoring_sensor_read_sensor_name, ctx); |
| 437 | netdata_ipmi_get_value_int(sensor_state, ipmi_monitoring_sensor_read_sensor_state, ctx); |
| 438 | netdata_ipmi_get_value_int(sensor_units, ipmi_monitoring_sensor_read_sensor_units, ctx); |
| 439 | netdata_ipmi_get_value_int(sensor_bitmask_type, ipmi_monitoring_sensor_read_sensor_bitmask_type, ctx); |
| 440 | netdata_ipmi_get_value_int(sensor_bitmask, ipmi_monitoring_sensor_read_sensor_bitmask, ctx); |
| 441 | // it's ok for this to be NULL, i.e. sensor_bitmask == IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN |
| 442 | netdata_ipmi_get_value_no_check(sensor_bitmask_strings, ipmi_monitoring_sensor_read_sensor_bitmask_strings, ctx); |
| 443 | netdata_ipmi_get_value_int(sensor_reading_type, ipmi_monitoring_sensor_read_sensor_reading_type, ctx); |
| 444 | // whatever we read from the sensor, it is ok |
| 445 | netdata_ipmi_get_value_no_check(sensor_reading, ipmi_monitoring_sensor_read_sensor_reading, ctx); |
| 446 | netdata_ipmi_get_value_int(event_reading_type_code, ipmi_monitoring_sensor_read_event_reading_type_code, ctx); |
| 447 | |
| 448 | netdata_update_ipmi_sensor_reading( |
| 449 | record_id, sensor_number, sensor_type, sensor_state, sensor_units, sensor_reading_type, sensor_name, |
| 450 | sensor_reading, event_reading_type_code, sensor_bitmask_type, sensor_bitmask, sensor_bitmask_strings, |
| 451 | state |
| 452 | ); |
| 453 | |
| 454 | #ifdef NETDATA_COMMENTED |
| 455 | /* It is possible you may want to monitor specific event |
| 456 | * conditions that may occur. If that is the case, you may want |
| 457 | * to check out what specific bitmask type and bitmask events |
| 458 | * occurred. See ipmi_monitoring_bitmasks.h for a list of |
| 459 | * bitmasks and types. |
| 460 | */ |
| 461 | |
| 462 | if (sensor_bitmask_type != IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN) |
| 463 | printf (", %Xh", sensor_bitmask); |
| 464 | else |
| 465 | printf (", N/A"); |
| 466 | |
| 467 | if (sensor_bitmask_type != IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN |
| 468 | && sensor_bitmask_strings) |
| 469 | { |
| 470 | unsigned int i = 0; |
| 471 | |
| 472 | printf (","); |
| 473 | |
| 474 | while (sensor_bitmask_strings[i]) |
| 475 | { |
| 476 | printf (" "); |
| 477 | |
| 478 | printf ("'%s'", |
| 479 | sensor_bitmask_strings[i]); |
| 480 | |
| 481 | i++; |
| 482 | } |
| 483 | } |
| 484 | else |
| 485 | printf (", N/A"); |
| 486 | |
| 487 | printf ("\n"); |
| 488 | #endif // NETDATA_COMMENTED |
| 489 | } |
| 490 | |
| 491 | rv = 0; |
| 492 | |
| 493 | cleanup: |
| 494 | if (ctx) |
| 495 | ipmi_monitoring_ctx_destroy (ctx); |
| 496 | |
| 497 | timing_report(); |
| 498 | |
| 499 | if(remove_reread_sdr_after_first_use) |
| 500 | global_sensor_reading_flags &= ~(IPMI_MONITORING_SENSOR_READING_FLAGS_REREAD_SDR_CACHE); |
| 501 | |
| 502 | return (rv); |
| 503 | } |
| 504 | |
| 505 | |
| 506 | static int netdata_get_ipmi_sel_events_count(struct ipmi_monitoring_ipmi_config *ipmi_config, struct netdata_ipmi_state *state) { |
| 507 | timing_init(); |
| 508 | |
| 509 | ipmi_monitoring_ctx_t ctx = NULL; |
| 510 | unsigned int sel_flags = global_sel_flags; |
| 511 | int sel_count; |
| 512 | int rv = -1; |
| 513 | |
| 514 | if (!(ctx = ipmi_monitoring_ctx_create ())) { |
| 515 | collector_error("ipmi_monitoring_ctx_create()"); |
| 516 | goto cleanup; |
| 517 | } |
| 518 | |
| 519 | if (sdr_cache_directory) { |
| 520 | if (ipmi_monitoring_ctx_sdr_cache_directory (ctx, sdr_cache_directory) < 0) { |
| 521 | collector_error( "ipmi_monitoring_ctx_sdr_cache_directory(): %s", ipmi_monitoring_ctx_errormsg (ctx)); |
| 522 | goto cleanup; |
| 523 | } |
| 524 | } |
| 525 | if (sdr_sel_cache_format) { |
| 526 | if (ipmi_monitoring_ctx_sdr_cache_filenames(ctx, sdr_sel_cache_format) < 0) { |
| 527 | collector_error("ipmi_monitoring_ctx_sdr_cache_filenames(): %s\n", ipmi_monitoring_ctx_errormsg (ctx)); |
| 528 | goto cleanup; |
| 529 | } |
| 530 | } |
| 531 | |
| 532 | // Must call otherwise only default interpretations ever used |
| 533 | // sel_config_file can be NULL |
| 534 | if (ipmi_monitoring_ctx_sel_config_file (ctx, sel_config_file) < 0) { |
| 535 | collector_error( "ipmi_monitoring_ctx_sel_config_file(): %s", |
| 536 | ipmi_monitoring_ctx_errormsg (ctx)); |
| 537 | goto cleanup; |
| 538 | } |
| 539 | |
| 540 | if ((sel_count = ipmi_monitoring_sel_by_record_id (ctx, |
| 541 | hostname, |
| 542 | ipmi_config, |
| 543 | sel_flags, |
| 544 | NULL, |
| 545 | 0, |
| 546 | NULL, |
| 547 | NULL)) < 0) { |
| 548 | collector_error( "ipmi_monitoring_sel_by_record_id(): %s", |
| 549 | ipmi_monitoring_ctx_errormsg (ctx)); |
| 550 | goto cleanup; |
| 551 | } |
| 552 | |
| 553 | netdata_update_ipmi_sel_events_count(state, sel_count); |
| 554 | |
| 555 | rv = 0; |
| 556 | |
| 557 | cleanup: |
| 558 | if (ctx) |
| 559 | ipmi_monitoring_ctx_destroy (ctx); |
| 560 | |
| 561 | timing_report(); |
| 562 | |
| 563 | if(remove_reread_sdr_after_first_use) |
| 564 | global_sel_flags &= ~(IPMI_MONITORING_SEL_FLAGS_REREAD_SDR_CACHE); |
| 565 | |
| 566 | return (rv); |
| 567 | } |
| 568 | |
| 569 | // ---------------------------------------------------------------------------- |
| 570 | // copied from freeipmi codebase commit 8dea6dec4012d0899901e595f2c868a05e1cefed |
| 571 | // added netdata_ in-front to not overwrite library functions |
| 572 | |
| 573 | // FROM: common/miscutil/network.c |
| 574 | static int netdata_host_is_localhost (const char *host) { |
| 575 | /* Ordered by my assumption of most popular */ |
| 576 | if (!strcasecmp (host, "localhost") |
| 577 | || !strcmp (host, "127.0.0.1") |
| 578 | || !strcasecmp (host, "ipv6-localhost") |
| 579 | || !strcmp (host, "::1") |
| 580 | || !strcasecmp (host, "ip6-localhost") |
| 581 | || !strcmp (host, "0:0:0:0:0:0:0:1")) |
| 582 | return (1); |
| 583 | |
| 584 | return (0); |
| 585 | } |
| 586 | |
| 587 | // FROM: common/parsecommon/parse-common.h |
| 588 | #define IPMI_PARSE_DEVICE_LAN_STR "lan" |
| 589 | #define IPMI_PARSE_DEVICE_LAN_2_0_STR "lan_2_0" |
| 590 | #define IPMI_PARSE_DEVICE_LAN_2_0_STR2 "lan20" |
| 591 | #define IPMI_PARSE_DEVICE_LAN_2_0_STR3 "lan_20" |
| 592 | #define IPMI_PARSE_DEVICE_LAN_2_0_STR4 "lan2_0" |
| 593 | #define IPMI_PARSE_DEVICE_LAN_2_0_STR5 "lanplus" |
| 594 | #define IPMI_PARSE_DEVICE_KCS_STR "kcs" |
| 595 | #define IPMI_PARSE_DEVICE_SSIF_STR "ssif" |
| 596 | #define IPMI_PARSE_DEVICE_OPENIPMI_STR "openipmi" |
| 597 | #define IPMI_PARSE_DEVICE_OPENIPMI_STR2 "open" |
| 598 | #define IPMI_PARSE_DEVICE_SUNBMC_STR "sunbmc" |
| 599 | #define IPMI_PARSE_DEVICE_SUNBMC_STR2 "bmc" |
| 600 | #define IPMI_PARSE_DEVICE_INTELDCMI_STR "inteldcmi" |
| 601 | |
| 602 | // FROM: common/parsecommon/parse-common.c |
| 603 | // changed the return values to match ipmi_monitoring.h |
| 604 | static int netdata_parse_outofband_driver_type (const char *str) { |
| 605 | if (strcasecmp (str, IPMI_PARSE_DEVICE_LAN_STR) == 0) |
| 606 | return (IPMI_MONITORING_PROTOCOL_VERSION_1_5); |
| 607 | |
| 608 | /* support "lanplus" for those that might be used to ipmitool. |
| 609 | * support typo variants to ease. |
| 610 | */ |
| 611 | else if (strcasecmp (str, IPMI_PARSE_DEVICE_LAN_2_0_STR) == 0 |
| 612 | || strcasecmp (str, IPMI_PARSE_DEVICE_LAN_2_0_STR2) == 0 |
| 613 | || strcasecmp (str, IPMI_PARSE_DEVICE_LAN_2_0_STR3) == 0 |
| 614 | || strcasecmp (str, IPMI_PARSE_DEVICE_LAN_2_0_STR4) == 0 |
| 615 | || strcasecmp (str, IPMI_PARSE_DEVICE_LAN_2_0_STR5) == 0) |
| 616 | return (IPMI_MONITORING_PROTOCOL_VERSION_2_0); |
| 617 | |
| 618 | return (-1); |
| 619 | } |
| 620 | |
| 621 | // FROM: common/parsecommon/parse-common.c |
| 622 | // changed the return values to match ipmi_monitoring.h |
| 623 | static int netdata_parse_inband_driver_type (const char *str) { |
| 624 | if (strcasecmp (str, IPMI_PARSE_DEVICE_KCS_STR) == 0) |
| 625 | return (IPMI_MONITORING_DRIVER_TYPE_KCS); |
| 626 | else if (strcasecmp (str, IPMI_PARSE_DEVICE_SSIF_STR) == 0) |
| 627 | return (IPMI_MONITORING_DRIVER_TYPE_SSIF); |
| 628 | /* support "open" for those that might be used to |
| 629 | * ipmitool. |
| 630 | */ |
| 631 | else if (strcasecmp (str, IPMI_PARSE_DEVICE_OPENIPMI_STR) == 0 |
| 632 | || strcasecmp (str, IPMI_PARSE_DEVICE_OPENIPMI_STR2) == 0) |
| 633 | return (IPMI_MONITORING_DRIVER_TYPE_OPENIPMI); |
| 634 | /* support "bmc" for those that might be used to |
| 635 | * ipmitool. |
| 636 | */ |
| 637 | else if (strcasecmp (str, IPMI_PARSE_DEVICE_SUNBMC_STR) == 0 |
| 638 | || strcasecmp (str, IPMI_PARSE_DEVICE_SUNBMC_STR2) == 0) |
| 639 | return (IPMI_MONITORING_DRIVER_TYPE_SUNBMC); |
| 640 | |
| 641 | #ifdef IPMI_MONITORING_DRIVER_TYPE_INTELDCMI |
| 642 | else if (strcasecmp (str, IPMI_PARSE_DEVICE_INTELDCMI_STR) == 0) |
| 643 | return (IPMI_MONITORING_DRIVER_TYPE_INTELDCMI); |
| 644 | #endif // IPMI_MONITORING_DRIVER_TYPE_INTELDCMI |
| 645 | |
| 646 | return (-1); |
| 647 | } |
| 648 | |
| 649 | // ---------------------------------------------------------------------------- |
| 650 | // BEGIN NETDATA CODE |
| 651 | |
| 652 | typedef enum __attribute__((packed)) { |
| 653 | IPMI_COLLECT_TYPE_SENSORS = (1 << 0), |
| 654 | IPMI_COLLECT_TYPE_SEL = (1 << 1), |
| 655 | } IPMI_COLLECTION_TYPE; |
| 656 | |
| 657 | struct sensor { |
| 658 | int sensor_type; |
| 659 | int sensor_state; |
| 660 | int sensor_units; |
| 661 | char *sensor_name; |
| 662 | |
| 663 | int sensor_reading_type; |
| 664 | union { |
| 665 | uint8_t bool_value; |
| 666 | uint32_t uint32_value; |
| 667 | double double_value; |
| 668 | } sensor_reading; |
| 669 | |
| 670 | // netdata provided |
| 671 | const char *context; |
| 672 | const char *title; |
| 673 | const char *units; |
| 674 | const char *family; |
| 675 | const char *chart_type; |
| 676 | const char *dimension; |
| 677 | int priority; |
| 678 | |
| 679 | const char *type; |
| 680 | const char *component; |
| 681 | |
| 682 | int multiplier; |
| 683 | bool do_metric; |
| 684 | bool do_state; |
| 685 | bool metric_chart_sent; |
| 686 | bool state_chart_sent; |
| 687 | usec_t last_collected_metric_ut; |
| 688 | usec_t last_collected_state_ut; |
| 689 | }; |
| 690 | |
| 691 | typedef enum __attribute__((packed)) { |
| 692 | ICS_INIT, |
| 693 | ICS_INIT_FAILED, |
| 694 | ICS_RUNNING, |
| 695 | ICS_FAILED, |
| 696 | } IPMI_COLLECTOR_STATUS; |
| 697 | |
| 698 | struct netdata_ipmi_state { |
| 699 | bool debug; |
| 700 | |
| 701 | struct { |
| 702 | IPMI_COLLECTOR_STATUS status; |
| 703 | usec_t last_iteration_ut; |
| 704 | size_t collected; |
| 705 | usec_t now_ut; |
| 706 | usec_t freq_ut; |
| 707 | int priority; |
| 708 | DICTIONARY *dict; |
| 709 | } sensors; |
| 710 | |
| 711 | struct { |
| 712 | IPMI_COLLECTOR_STATUS status; |
| 713 | usec_t last_iteration_ut; |
| 714 | size_t events; |
| 715 | usec_t now_ut; |
| 716 | usec_t freq_ut; |
| 717 | int priority; |
| 718 | } sel; |
| 719 | |
| 720 | struct { |
| 721 | usec_t now_ut; |
| 722 | } updates; |
| 723 | }; |
| 724 | |
| 725 | struct netdata_ipmi_state state = {0}; |
| 726 | |
| 727 | // ---------------------------------------------------------------------------- |
| 728 | // excluded record ids maintenance (both for sensor data and state) |
| 729 | |
| 730 | static int *excluded_record_ids = NULL; |
| 731 | size_t excluded_record_ids_length = 0; |
| 732 | |
| 733 | static void excluded_record_ids_parse(const char *s, bool debug) { |
| 734 | if(!s) return; |
| 735 | |
| 736 | while(*s) { |
| 737 | while(*s && !isdigit(*s)) s++; |
| 738 | |
| 739 | if(isdigit(*s)) { |
| 740 | char *e; |
| 741 | unsigned long n = strtoul(s, &e, 10); |
| 742 | s = e; |
| 743 | |
| 744 | if(n != 0) { |
| 745 | excluded_record_ids = reallocz(excluded_record_ids, (excluded_record_ids_length + 1) * sizeof(int)); |
| 746 | excluded_record_ids[excluded_record_ids_length++] = (int)n; |
| 747 | } |
| 748 | } |
| 749 | } |
| 750 | |
| 751 | if(debug) { |
| 752 | fprintf(stderr, "%s: excluded record ids:", program_name); |
| 753 | size_t i; |
| 754 | for(i = 0; i < excluded_record_ids_length; i++) { |
| 755 | fprintf(stderr, " %d", excluded_record_ids[i]); |
| 756 | } |
| 757 | fprintf(stderr, "\n"); |
| 758 | } |
| 759 | } |
| 760 | |
| 761 | static int *excluded_status_record_ids = NULL; |
| 762 | size_t excluded_status_record_ids_length = 0; |
| 763 | |
| 764 | static void excluded_status_record_ids_parse(const char *s, bool debug) { |
| 765 | if(!s) return; |
| 766 | |
| 767 | while(*s) { |
| 768 | while(*s && !isdigit(*s)) s++; |
| 769 | |
| 770 | if(isdigit(*s)) { |
| 771 | char *e; |
| 772 | unsigned long n = strtoul(s, &e, 10); |
| 773 | s = e; |
| 774 | |
| 775 | if(n != 0) { |
| 776 | excluded_status_record_ids = reallocz(excluded_status_record_ids, (excluded_status_record_ids_length + 1) * sizeof(int)); |
| 777 | excluded_status_record_ids[excluded_status_record_ids_length++] = (int)n; |
| 778 | } |
| 779 | } |
| 780 | } |
| 781 | |
| 782 | if(debug) { |
| 783 | fprintf(stderr, "%s: excluded status record ids:", program_name); |
| 784 | size_t i; |
| 785 | for(i = 0; i < excluded_status_record_ids_length; i++) { |
| 786 | fprintf(stderr, " %d", excluded_status_record_ids[i]); |
| 787 | } |
| 788 | fprintf(stderr, "\n"); |
| 789 | } |
| 790 | } |
| 791 | |
| 792 | |
| 793 | static int excluded_record_ids_check(int record_id) { |
| 794 | size_t i; |
| 795 | |
| 796 | for(i = 0; i < excluded_record_ids_length; i++) { |
| 797 | if(excluded_record_ids[i] == record_id) |
| 798 | return 1; |
| 799 | } |
| 800 | |
| 801 | return 0; |
| 802 | } |
| 803 | |
| 804 | static int excluded_status_record_ids_check(int record_id) { |
| 805 | size_t i; |
| 806 | |
| 807 | for(i = 0; i < excluded_status_record_ids_length; i++) { |
| 808 | if(excluded_status_record_ids[i] == record_id) |
| 809 | return 1; |
| 810 | } |
| 811 | |
| 812 | return 0; |
| 813 | } |
| 814 | |
| 815 | // ---------------------------------------------------------------------------- |
| 816 | // data collection functions |
| 817 | |
| 818 | struct { |
| 819 | const char *search; |
| 820 | SIMPLE_PATTERN *pattern; |
| 821 | const char *label; |
| 822 | } sensors_component_patterns[] = { |
| 823 | |
| 824 | // The order is important! |
| 825 | // They are evaluated top to bottom |
| 826 | // The first the matches is used |
| 827 | |
| 828 | { |
| 829 | .search = "*DIMM*|*_DIM*|*VTT*|*VDDQ*|*ECC*|*MEM*CRC*|*MEM*BD*", |
| 830 | .label = NETDATA_SENSOR_COMPONENT_MEMORY_MODULE, |
| 831 | }, |
| 832 | { |
| 833 | .search = "*CPU*|SOC_*|*VDDCR*|P*_VDD*|*_DTS|*VCORE*|*PROC*", |
| 834 | .label = NETDATA_SENSOR_COMPONENT_PROCESSOR, |
| 835 | }, |
| 836 | { |
| 837 | .search = "IPU*", |
| 838 | .label = NETDATA_SENSOR_COMPONENT_IPU, |
| 839 | }, |
| 840 | { |
| 841 | .search = "M2_*|*SSD*|*HSC*|*HDD*|*NVME*", |
| 842 | .label = NETDATA_SENSOR_COMPONENT_STORAGE, |
| 843 | }, |
| 844 | { |
| 845 | .search = "MB_*|*PCH*|*VBAT*|*I/O*BD*|*IO*BD*", |
| 846 | .label = NETDATA_SENSOR_COMPONENT_MOTHERBOARD, |
| 847 | }, |
| 848 | { |
| 849 | .search = "Watchdog|SEL|SYS_*|*CHASSIS*", |
| 850 | .label = NETDATA_SENSOR_COMPONENT_SYSTEM, |
| 851 | }, |
| 852 | { |
| 853 | .search = "PS*|P_*|*PSU*|*PWR*|*TERMV*|*D2D*", |
| 854 | .label = NETDATA_SENSOR_COMPONENT_POWER_SUPPLY, |
| 855 | }, |
| 856 | |
| 857 | // fallback components |
| 858 | { |
| 859 | .search = "VR_P*|*VRMP*", |
| 860 | .label = NETDATA_SENSOR_COMPONENT_PROCESSOR, |
| 861 | }, |
| 862 | { |
| 863 | .search = "*VSB*|*PS*", |
| 864 | .label = NETDATA_SENSOR_COMPONENT_POWER_SUPPLY, |
| 865 | }, |
| 866 | { |
| 867 | .search = "*MEM*|*MEM*RAID*", |
| 868 | .label = NETDATA_SENSOR_COMPONENT_MEMORY, |
| 869 | }, |
| 870 | { |
| 871 | .search = "*RAID*", // there is also "Memory RAID", so keep this after memory |
| 872 | .label = NETDATA_SENSOR_COMPONENT_STORAGE, |
| 873 | }, |
| 874 | { |
| 875 | .search = "*PERIPHERAL*|*USB*", |
| 876 | .label = NETDATA_SENSOR_COMPONENT_PERIPHERAL, |
| 877 | }, |
| 878 | { |
| 879 | .search = "*FAN*|*12V*|*VCC*|*PCI*|*CHIPSET*|*AMP*|*BD*", |
| 880 | .label = NETDATA_SENSOR_COMPONENT_SYSTEM, |
| 881 | }, |
| 882 | |
| 883 | // terminator |
| 884 | { |
| 885 | .search = NULL, |
| 886 | .label = NULL, |
| 887 | } |
| 888 | }; |
| 889 | |
| 890 | static const char *netdata_sensor_name_to_component(const char *sensor_name) { |
| 891 | for(int i = 0; sensors_component_patterns[i].search ;i++) { |
| 892 | if(!sensors_component_patterns[i].pattern) |
| 893 | sensors_component_patterns[i].pattern = simple_pattern_create(sensors_component_patterns[i].search, "|", SIMPLE_PATTERN_EXACT, false); |
| 894 | |
| 895 | if(simple_pattern_matches(sensors_component_patterns[i].pattern, sensor_name)) |
| 896 | return sensors_component_patterns[i].label; |
| 897 | } |
| 898 | |
| 899 | return "Other"; |
| 900 | } |
| 901 | |
| 902 | const char *netdata_collect_type_to_string(IPMI_COLLECTION_TYPE type) { |
| 903 | if((type & (IPMI_COLLECT_TYPE_SENSORS|IPMI_COLLECT_TYPE_SEL)) == (IPMI_COLLECT_TYPE_SENSORS|IPMI_COLLECT_TYPE_SEL)) |
| 904 | return "sensors,sel"; |
| 905 | if(type & IPMI_COLLECT_TYPE_SEL) |
| 906 | return "sel"; |
| 907 | if(type & IPMI_COLLECT_TYPE_SENSORS) |
| 908 | return "sensors"; |
| 909 | |
| 910 | return "unknown"; |
| 911 | } |
| 912 | |
| 913 | static void netdata_sensor_set_value(struct sensor *sn, void *sensor_reading, struct netdata_ipmi_state *stt __maybe_unused) { |
| 914 | switch(sn->sensor_reading_type) { |
| 915 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: |
| 916 | sn->sensor_reading.bool_value = *((uint8_t *)sensor_reading); |
| 917 | break; |
| 918 | |
| 919 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: |
| 920 | sn->sensor_reading.uint32_value = *((uint32_t *)sensor_reading); |
| 921 | break; |
| 922 | |
| 923 | case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: |
| 924 | sn->sensor_reading.double_value = *((double *)sensor_reading); |
| 925 | break; |
| 926 | |
| 927 | default: |
| 928 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNKNOWN: |
| 929 | sn->do_metric = false; |
| 930 | break; |
| 931 | } |
| 932 | } |
| 933 | |
| 934 | static void netdata_update_ipmi_sensor_reading( |
| 935 | int record_id |
| 936 | , int sensor_number |
| 937 | , int sensor_type |
| 938 | , int sensor_state |
| 939 | , int sensor_units |
| 940 | , int sensor_reading_type |
| 941 | , char *sensor_name |
| 942 | , void *sensor_reading |
| 943 | , int event_reading_type_code __maybe_unused |
| 944 | , int sensor_bitmask_type __maybe_unused |
| 945 | , int sensor_bitmask __maybe_unused |
| 946 | , char **sensor_bitmask_strings __maybe_unused |
| 947 | , struct netdata_ipmi_state *stt) { |
| 948 | if(unlikely(sensor_state == IPMI_MONITORING_STATE_UNKNOWN && |
| 949 | sensor_type == IPMI_MONITORING_SENSOR_TYPE_UNKNOWN && |
| 950 | sensor_units == IPMI_MONITORING_SENSOR_UNITS_UNKNOWN && |
| 951 | sensor_reading_type == IPMI_MONITORING_SENSOR_READING_TYPE_UNKNOWN && |
| 952 | (!sensor_name || !*sensor_name))) |
| 953 | // we can't do anything about this sensor - everything is unknown |
| 954 | return; |
| 955 | |
| 956 | if(unlikely(!sensor_name || !*sensor_name)) |
| 957 | sensor_name = "UNNAMED"; |
| 958 | |
| 959 | stt->sensors.collected++; |
| 960 | |
| 961 | char key[SENSORS_DICT_KEY_SIZE + 1]; |
| 962 | snprintfz(key, SENSORS_DICT_KEY_SIZE, "i%d_n%d_t%d_u%d_%s", |
| 963 | record_id, sensor_number, sensor_reading_type, sensor_units, sensor_name); |
| 964 | |
| 965 | // find the sensor record |
| 966 | const DICTIONARY_ITEM *item = dictionary_get_and_acquire_item(stt->sensors.dict, key); |
| 967 | if(likely(item)) { |
| 968 | // recurring collection |
| 969 | |
| 970 | if(stt->debug) |
| 971 | fprintf(stderr, "%s: reusing sensor record for sensor '%s', id %d, number %d, type %d, state %d, units %d, reading_type %d\n", |
| 972 | program_name, sensor_name, record_id, sensor_number, sensor_type, sensor_state, sensor_units, sensor_reading_type); |
| 973 | |
| 974 | struct sensor *sn = dictionary_acquired_item_value(item); |
| 975 | |
| 976 | if(sensor_reading) { |
| 977 | netdata_sensor_set_value(sn, sensor_reading, stt); |
| 978 | sn->last_collected_metric_ut = stt->sensors.now_ut; |
| 979 | } |
| 980 | |
| 981 | sn->sensor_state = sensor_state; |
| 982 | |
| 983 | sn->last_collected_state_ut = stt->sensors.now_ut; |
| 984 | |
| 985 | dictionary_acquired_item_release(stt->sensors.dict, item); |
| 986 | |
| 987 | return; |
| 988 | } |
| 989 | |
| 990 | if(stt->debug) |
| 991 | fprintf(stderr, "Allocating new sensor data record for sensor '%s', id %d, number %d, type %d, state %d, units %d, reading_type %d\n", |
| 992 | sensor_name, record_id, sensor_number, sensor_type, sensor_state, sensor_units, sensor_reading_type); |
| 993 | |
| 994 | // check if it is excluded |
| 995 | bool excluded_metric = excluded_record_ids_check(record_id); |
| 996 | bool excluded_state = excluded_status_record_ids_check(record_id); |
| 997 | |
| 998 | if(excluded_metric) { |
| 999 | if(stt->debug) |
| 1000 | fprintf(stderr, "Sensor '%s' is excluded by excluded_record_ids_check()\n", sensor_name); |
| 1001 | } |
| 1002 | |
| 1003 | if(excluded_state) { |
| 1004 | if(stt->debug) |
| 1005 | fprintf(stderr, "Sensor '%s' is excluded for status check, by excluded_status_record_ids_check()\n", sensor_name); |
| 1006 | } |
| 1007 | |
| 1008 | struct sensor t = { |
| 1009 | .sensor_type = sensor_type, |
| 1010 | .sensor_state = sensor_state, |
| 1011 | .sensor_units = sensor_units, |
| 1012 | .sensor_reading_type = sensor_reading_type, |
| 1013 | .sensor_name = strdupz(sensor_name), |
| 1014 | .component = netdata_sensor_name_to_component(sensor_name), |
| 1015 | .do_state = !excluded_state, |
| 1016 | .do_metric = !excluded_metric, |
| 1017 | }; |
| 1018 | |
| 1019 | t.type = netdata_ipmi_get_sensor_type_string(t.sensor_type, &t.component); |
| 1020 | |
| 1021 | switch(t.sensor_units) { |
| 1022 | case IPMI_MONITORING_SENSOR_UNITS_CELSIUS: |
| 1023 | t.dimension = "temperature"; |
| 1024 | t.context = "ipmi.sensor_temperature_c"; |
| 1025 | t.title = "IPMI Sensor Temperature Celsius"; |
| 1026 | t.units = "Celsius"; |
| 1027 | t.family = "temperatures"; |
| 1028 | t.chart_type = "line"; |
| 1029 | t.priority = stt->sensors.priority + 10; |
| 1030 | break; |
| 1031 | |
| 1032 | case IPMI_MONITORING_SENSOR_UNITS_FAHRENHEIT: |
| 1033 | t.dimension = "temperature"; |
| 1034 | t.context = "ipmi.sensor_temperature_f"; |
| 1035 | t.title = "IPMI Sensor Temperature Fahrenheit"; |
| 1036 | t.units = "Fahrenheit"; |
| 1037 | t.family = "temperatures"; |
| 1038 | t.chart_type = "line"; |
| 1039 | t.priority = stt->sensors.priority + 20; |
| 1040 | break; |
| 1041 | |
| 1042 | case IPMI_MONITORING_SENSOR_UNITS_VOLTS: |
| 1043 | t.dimension = "voltage"; |
| 1044 | t.context = "ipmi.sensor_voltage"; |
| 1045 | t.title = "IPMI Sensor Voltage"; |
| 1046 | t.units = "Volts"; |
| 1047 | t.family = "voltages"; |
| 1048 | t.chart_type = "line"; |
| 1049 | t.priority = stt->sensors.priority + 30; |
| 1050 | break; |
| 1051 | |
| 1052 | case IPMI_MONITORING_SENSOR_UNITS_AMPS: |
| 1053 | t.dimension = "ampere"; |
| 1054 | t.context = "ipmi.sensor_ampere"; |
| 1055 | t.title = "IPMI Sensor Current"; |
| 1056 | t.units = "Amps"; |
| 1057 | t.family = "current"; |
| 1058 | t.chart_type = "line"; |
| 1059 | t.priority = stt->sensors.priority + 40; |
| 1060 | break; |
| 1061 | |
| 1062 | case IPMI_MONITORING_SENSOR_UNITS_RPM: |
| 1063 | t.dimension = "rotations"; |
| 1064 | t.context = "ipmi.sensor_fan_speed"; |
| 1065 | t.title = "IPMI Sensor Fans Speed"; |
| 1066 | t.units = "RPM"; |
| 1067 | t.family = "fans"; |
| 1068 | t.chart_type = "line"; |
| 1069 | t.priority = stt->sensors.priority + 50; |
| 1070 | break; |
| 1071 | |
| 1072 | case IPMI_MONITORING_SENSOR_UNITS_WATTS: |
| 1073 | t.dimension = "power"; |
| 1074 | t.context = "ipmi.sensor_power"; |
| 1075 | t.title = "IPMI Sensor Power"; |
| 1076 | t.units = "Watts"; |
| 1077 | t.family = "power"; |
| 1078 | t.chart_type = "line"; |
| 1079 | t.priority = stt->sensors.priority + 60; |
| 1080 | break; |
| 1081 | |
| 1082 | case IPMI_MONITORING_SENSOR_UNITS_PERCENT: |
| 1083 | t.dimension = "percentage"; |
| 1084 | t.context = "ipmi.sensor_reading_percent"; |
| 1085 | t.title = "IPMI Sensor Reading Percentage"; |
| 1086 | t.units = "%%"; |
| 1087 | t.family = "other"; |
| 1088 | t.chart_type = "line"; |
| 1089 | t.priority = stt->sensors.priority + 70; |
| 1090 | break; |
| 1091 | |
| 1092 | default: |
| 1093 | t.priority = stt->sensors.priority + 80; |
| 1094 | t.do_metric = false; |
| 1095 | break; |
| 1096 | } |
| 1097 | |
| 1098 | switch(sensor_reading_type) { |
| 1099 | case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: |
| 1100 | t.multiplier = 1000; |
| 1101 | break; |
| 1102 | |
| 1103 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: |
| 1104 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: |
| 1105 | t.multiplier = 1; |
| 1106 | break; |
| 1107 | |
| 1108 | default: |
| 1109 | t.do_metric = false; |
| 1110 | break; |
| 1111 | } |
| 1112 | |
| 1113 | if(sensor_reading) { |
| 1114 | netdata_sensor_set_value(&t, sensor_reading, stt); |
| 1115 | t.last_collected_metric_ut = stt->sensors.now_ut; |
| 1116 | } |
| 1117 | t.last_collected_state_ut = stt->sensors.now_ut; |
| 1118 | |
| 1119 | dictionary_set(stt->sensors.dict, key, &t, sizeof(t)); |
| 1120 | } |
| 1121 | |
| 1122 | static void netdata_update_ipmi_sel_events_count(struct netdata_ipmi_state *stt, uint32_t events) { |
| 1123 | stt->sel.events = events; |
| 1124 | } |
| 1125 | |
| 1126 | int netdata_ipmi_collect_data(struct ipmi_monitoring_ipmi_config *ipmi_config, IPMI_COLLECTION_TYPE type, struct netdata_ipmi_state *stt) { |
| 1127 | errno_clear(); |
| 1128 | |
| 1129 | if(type & IPMI_COLLECT_TYPE_SENSORS) { |
| 1130 | stt->sensors.collected = 0; |
| 1131 | stt->sensors.now_ut = now_monotonic_usec(); |
| 1132 | |
| 1133 | if (netdata_read_ipmi_sensors(ipmi_config, stt) < 0) return -1; |
| 1134 | } |
| 1135 | |
| 1136 | if(type & IPMI_COLLECT_TYPE_SEL) { |
| 1137 | stt->sel.events = 0; |
| 1138 | stt->sel.now_ut = now_monotonic_usec(); |
| 1139 | if(netdata_get_ipmi_sel_events_count(ipmi_config, stt) < 0) return -2; |
| 1140 | } |
| 1141 | |
| 1142 | return 0; |
| 1143 | } |
| 1144 | |
| 1145 | int netdata_ipmi_detect_speed_secs(struct ipmi_monitoring_ipmi_config *ipmi_config, IPMI_COLLECTION_TYPE type, struct netdata_ipmi_state *stt) { |
| 1146 | int i, checks = SPEED_TEST_ITERATIONS, successful = 0; |
| 1147 | usec_t total = 0; |
| 1148 | |
| 1149 | for(i = 0 ; i < checks ; i++) { |
| 1150 | if(unlikely(stt->debug)) |
| 1151 | fprintf(stderr, "%s: checking %s data collection speed iteration %d of %d\n", |
| 1152 | program_name, netdata_collect_type_to_string(type), i + 1, checks); |
| 1153 | |
| 1154 | // measure the time a data collection needs |
| 1155 | usec_t start = now_realtime_usec(); |
| 1156 | |
| 1157 | if(netdata_ipmi_collect_data(ipmi_config, type, stt) < 0) |
| 1158 | continue; |
| 1159 | |
| 1160 | usec_t end = now_realtime_usec(); |
| 1161 | |
| 1162 | successful++; |
| 1163 | |
| 1164 | if(unlikely(stt->debug)) |
| 1165 | fprintf(stderr, "%s: %s data collection speed was %"PRIu64" usec\n", |
| 1166 | program_name, netdata_collect_type_to_string(type), end - start); |
| 1167 | |
| 1168 | // add it to our total |
| 1169 | total += end - start; |
| 1170 | |
| 1171 | // wait the same time |
| 1172 | // to avoid flooding the IPMI processor with requests |
| 1173 | sleep_usec(end - start); |
| 1174 | } |
| 1175 | |
| 1176 | if(!successful) |
| 1177 | return 0; |
| 1178 | |
| 1179 | // so, we assume it needed 2x the time |
| 1180 | // we find the average in microseconds |
| 1181 | // and we round-up to the closest second |
| 1182 | |
| 1183 | return (int)(( total * 2 / successful / USEC_PER_SEC ) + 1); |
| 1184 | } |
| 1185 | |
| 1186 | // ---------------------------------------------------------------------------- |
| 1187 | // data collection threads |
| 1188 | |
| 1189 | struct ipmi_collection_thread { |
| 1190 | struct ipmi_monitoring_ipmi_config ipmi_config; |
| 1191 | int freq_s; |
| 1192 | bool debug; |
| 1193 | IPMI_COLLECTION_TYPE type; |
| 1194 | SPINLOCK spinlock; |
| 1195 | struct netdata_ipmi_state state; |
| 1196 | }; |
| 1197 | |
| 1198 | void netdata_ipmi_collection_thread(void *ptr) { |
| 1199 | struct ipmi_collection_thread *t = ptr; |
| 1200 | |
| 1201 | if(t->debug) fprintf(stderr, "%s: calling initialize_ipmi_config() for %s\n", |
| 1202 | program_name, netdata_collect_type_to_string(t->type)); |
| 1203 | |
| 1204 | initialize_ipmi_config(&t->ipmi_config); |
| 1205 | |
| 1206 | if(t->debug) fprintf(stderr, "%s: detecting IPMI minimum update frequency for %s...\n", |
| 1207 | program_name, netdata_collect_type_to_string(t->type)); |
| 1208 | |
| 1209 | int freq_s = netdata_ipmi_detect_speed_secs(&t->ipmi_config, t->type, &t->state); |
| 1210 | if(!freq_s) { |
| 1211 | if(t->type & IPMI_COLLECT_TYPE_SENSORS) { |
| 1212 | t->state.sensors.status = ICS_INIT_FAILED; |
| 1213 | t->state.sensors.last_iteration_ut = 0; |
| 1214 | } |
| 1215 | |
| 1216 | if(t->type & IPMI_COLLECT_TYPE_SEL) { |
| 1217 | t->state.sel.status = ICS_INIT_FAILED; |
| 1218 | t->state.sel.last_iteration_ut = 0; |
| 1219 | } |
| 1220 | |
| 1221 | return; |
| 1222 | } |
| 1223 | else { |
| 1224 | if(t->type & IPMI_COLLECT_TYPE_SENSORS) { |
| 1225 | t->state.sensors.status = ICS_RUNNING; |
| 1226 | } |
| 1227 | |
| 1228 | if(t->type & IPMI_COLLECT_TYPE_SEL) { |
| 1229 | t->state.sel.status = ICS_RUNNING; |
| 1230 | } |
| 1231 | } |
| 1232 | |
| 1233 | t->freq_s = freq_s = MAX(t->freq_s, freq_s); |
| 1234 | |
| 1235 | if(t->debug) { |
| 1236 | fprintf(stderr, "%s: IPMI minimum update frequency of %s was calculated to %d seconds.\n", |
| 1237 | program_name, netdata_collect_type_to_string(t->type), t->freq_s); |
| 1238 | |
| 1239 | fprintf(stderr, "%s: starting data collection of %s\n", |
| 1240 | program_name, netdata_collect_type_to_string(t->type)); |
| 1241 | } |
| 1242 | |
| 1243 | size_t iteration = 0, failures = 0; |
| 1244 | usec_t step = t->freq_s * USEC_PER_SEC; |
| 1245 | |
| 1246 | heartbeat_t hb; |
| 1247 | heartbeat_init(&hb, step); |
| 1248 | while(++iteration) { |
| 1249 | heartbeat_next(&hb); |
| 1250 | |
| 1251 | if(t->debug) |
| 1252 | fprintf(stderr, "%s: calling netdata_ipmi_collect_data() for %s\n", |
| 1253 | program_name, netdata_collect_type_to_string(t->type)); |
| 1254 | |
| 1255 | struct netdata_ipmi_state tmp_state = t->state; |
| 1256 | |
| 1257 | if(t->type & IPMI_COLLECT_TYPE_SENSORS) { |
| 1258 | tmp_state.sensors.last_iteration_ut = now_monotonic_usec(); |
| 1259 | tmp_state.sensors.freq_ut = t->freq_s * USEC_PER_SEC; |
| 1260 | } |
| 1261 | |
| 1262 | if(t->type & IPMI_COLLECT_TYPE_SEL) { |
| 1263 | tmp_state.sel.last_iteration_ut = now_monotonic_usec(); |
| 1264 | tmp_state.sel.freq_ut = t->freq_s * USEC_PER_SEC; |
| 1265 | } |
| 1266 | |
| 1267 | if(netdata_ipmi_collect_data(&t->ipmi_config, t->type, &tmp_state) != 0) |
| 1268 | failures++; |
| 1269 | else |
| 1270 | failures = 0; |
| 1271 | |
| 1272 | if(failures > 10) { |
| 1273 | collector_error("%s() failed to collect %s data for %zu consecutive times, having made %zu iterations.", |
| 1274 | __FUNCTION__, netdata_collect_type_to_string(t->type), failures, iteration); |
| 1275 | |
| 1276 | if(t->type & IPMI_COLLECT_TYPE_SENSORS) { |
| 1277 | t->state.sensors.status = ICS_FAILED; |
| 1278 | t->state.sensors.last_iteration_ut = 0; |
| 1279 | } |
| 1280 | |
| 1281 | if(t->type & IPMI_COLLECT_TYPE_SEL) { |
| 1282 | t->state.sel.status = ICS_FAILED; |
| 1283 | t->state.sel.last_iteration_ut = 0; |
| 1284 | } |
| 1285 | |
| 1286 | break; |
| 1287 | } |
| 1288 | |
| 1289 | spinlock_lock(&t->spinlock); |
| 1290 | t->state = tmp_state; |
| 1291 | spinlock_unlock(&t->spinlock); |
| 1292 | } |
| 1293 | } |
| 1294 | |
| 1295 | // ---------------------------------------------------------------------------- |
| 1296 | // sending data to netdata |
| 1297 | |
| 1298 | static inline bool is_sensor_updated(usec_t last_collected_ut, usec_t now_ut, usec_t freq) { |
| 1299 | return (now_ut - last_collected_ut < freq * 2) ? true : false; |
| 1300 | } |
| 1301 | |
| 1302 | static size_t send_ipmi_sensor_metrics_to_netdata(struct netdata_ipmi_state *stt) { |
| 1303 | if(stt->sensors.status != ICS_RUNNING) { |
| 1304 | if(unlikely(stt->debug)) |
| 1305 | fprintf(stderr, "%s: %s() sensors state is not RUNNING\n", |
| 1306 | program_name, __FUNCTION__ ); |
| 1307 | return 0; |
| 1308 | } |
| 1309 | |
| 1310 | size_t total_sensors_sent = 0; |
| 1311 | int update_every_s = (int)(stt->sensors.freq_ut / USEC_PER_SEC); |
| 1312 | struct sensor *sn; |
| 1313 | |
| 1314 | netdata_mutex_lock(&stdout_mutex); |
| 1315 | // generate the CHART/DIMENSION lines, if we have to |
| 1316 | dfe_start_reentrant(stt->sensors.dict, sn) { |
| 1317 | if(unlikely(!sn->do_metric && !sn->do_state)) |
| 1318 | continue; |
| 1319 | |
| 1320 | bool did_metric = false, did_state = false; |
| 1321 | |
| 1322 | if(likely(sn->do_metric)) { |
| 1323 | if(unlikely(!is_sensor_updated(sn->last_collected_metric_ut, stt->updates.now_ut, stt->sensors.freq_ut))) { |
| 1324 | if(unlikely(stt->debug)) |
| 1325 | fprintf(stderr, "%s: %s() sensor '%s' metric is not UPDATED (last updated %"PRIu64", now %"PRIu64", freq %"PRIu64"\n", |
| 1326 | program_name, __FUNCTION__, sn->sensor_name, sn->last_collected_metric_ut, |
| 1327 | stt->updates.now_ut, stt->sensors.freq_ut); |
| 1328 | } |
| 1329 | else { |
| 1330 | if (unlikely(!sn->metric_chart_sent)) { |
| 1331 | sn->metric_chart_sent = true; |
| 1332 | |
| 1333 | printf("CHART '%s_%s' '' '%s' '%s' '%s' '%s' '%s' %d %d '' '%s' '%s'\n", |
| 1334 | sn->context, sn_dfe.name, sn->title, sn->units, sn->family, sn->context, |
| 1335 | sn->chart_type, sn->priority + 1, |
| 1336 | update_every_s, program_name, "sensors"); |
| 1337 | |
| 1338 | printf("CLABEL 'sensor' '%s' 1\n", sn->sensor_name); |
| 1339 | printf("CLABEL 'type' '%s' 1\n", sn->type); |
| 1340 | printf("CLABEL 'component' '%s' 1\n", sn->component); |
| 1341 | printf("CLABEL_COMMIT\n"); |
| 1342 | |
| 1343 | printf("DIMENSION '%s' '' absolute 1 %d\n", sn->dimension, sn->multiplier); |
| 1344 | } |
| 1345 | |
| 1346 | printf("BEGIN '%s_%s'\n", sn->context, sn_dfe.name); |
| 1347 | |
| 1348 | switch (sn->sensor_reading_type) { |
| 1349 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: |
| 1350 | printf("SET '%s' = %u\n", sn->dimension, sn->sensor_reading.uint32_value); |
| 1351 | break; |
| 1352 | |
| 1353 | case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: |
| 1354 | printf("SET '%s' = %lld\n", sn->dimension, |
| 1355 | (long long int) (sn->sensor_reading.double_value * sn->multiplier)); |
| 1356 | break; |
| 1357 | |
| 1358 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: |
| 1359 | printf("SET '%s' = %u\n", sn->dimension, sn->sensor_reading.bool_value); |
| 1360 | break; |
| 1361 | |
| 1362 | default: |
| 1363 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNKNOWN: |
| 1364 | // this should never happen because we also do the same check at netdata_get_sensor() |
| 1365 | sn->do_metric = false; |
| 1366 | break; |
| 1367 | } |
| 1368 | |
| 1369 | printf("END\n"); |
| 1370 | did_metric = true; |
| 1371 | } |
| 1372 | } |
| 1373 | |
| 1374 | if(likely(sn->do_state)) { |
| 1375 | if(unlikely(!is_sensor_updated(sn->last_collected_state_ut, stt->updates.now_ut, stt->sensors.freq_ut))) { |
| 1376 | if (unlikely(stt->debug)) |
| 1377 | fprintf(stderr, "%s: %s() sensor '%s' state is not UPDATED (last updated %"PRIu64", now %"PRIu64", freq %"PRIu64"\n", |
| 1378 | program_name, __FUNCTION__, sn->sensor_name, sn->last_collected_state_ut, |
| 1379 | stt->updates.now_ut, stt->sensors.freq_ut); |
| 1380 | } |
| 1381 | else { |
| 1382 | if (unlikely(!sn->state_chart_sent)) { |
| 1383 | sn->state_chart_sent = true; |
| 1384 | |
| 1385 | printf("CHART 'ipmi.sensor_state_%s' '' 'IPMI Sensor State' 'state' 'states' 'ipmi.sensor_state' 'line' %d %d '' '%s' '%s'\n", |
| 1386 | sn_dfe.name, sn->priority, update_every_s, program_name, "sensors"); |
| 1387 | |
| 1388 | printf("CLABEL 'sensor' '%s' 1\n", sn->sensor_name); |
| 1389 | printf("CLABEL 'type' '%s' 1\n", sn->type); |
| 1390 | printf("CLABEL 'component' '%s' 1\n", sn->component); |
| 1391 | printf("CLABEL_COMMIT\n"); |
| 1392 | |
| 1393 | printf("DIMENSION 'nominal' '' absolute 1 1\n"); |
| 1394 | printf("DIMENSION 'warning' '' absolute 1 1\n"); |
| 1395 | printf("DIMENSION 'critical' '' absolute 1 1\n"); |
| 1396 | printf("DIMENSION 'unknown' '' absolute 1 1\n"); |
| 1397 | } |
| 1398 | |
| 1399 | printf("BEGIN 'ipmi.sensor_state_%s'\n", sn_dfe.name); |
| 1400 | printf("SET 'nominal' = %lld\n", sn->sensor_state == IPMI_MONITORING_STATE_NOMINAL ? 1LL : 0LL); |
| 1401 | printf("SET 'warning' = %lld\n", sn->sensor_state == IPMI_MONITORING_STATE_WARNING ? 1LL : 0LL); |
| 1402 | printf("SET 'critical' = %lld\n", sn->sensor_state == IPMI_MONITORING_STATE_CRITICAL ? 1LL : 0LL); |
| 1403 | printf("SET 'unknown' = %lld\n", sn->sensor_state == IPMI_MONITORING_STATE_UNKNOWN ? 1LL : 0LL); |
| 1404 | printf("END\n"); |
| 1405 | did_state = true; |
| 1406 | } |
| 1407 | } |
| 1408 | |
| 1409 | if(likely(did_metric || did_state)) |
| 1410 | total_sensors_sent++; |
| 1411 | } |
| 1412 | dfe_done(sn); |
| 1413 | |
| 1414 | netdata_mutex_unlock(&stdout_mutex); |
| 1415 | |
| 1416 | return total_sensors_sent; |
| 1417 | } |
| 1418 | |
| 1419 | static size_t send_ipmi_sel_metrics_to_netdata(struct netdata_ipmi_state *stt) { |
| 1420 | static bool sel_chart_generated = false; |
| 1421 | |
| 1422 | netdata_mutex_lock(&stdout_mutex); |
| 1423 | |
| 1424 | if(likely(stt->sel.status == ICS_RUNNING)) { |
| 1425 | if(unlikely(!sel_chart_generated)) { |
| 1426 | sel_chart_generated = true; |
| 1427 | printf("CHART ipmi.events '' 'IPMI Events' 'events' 'events' ipmi.sel area %d %d '' '%s' '%s'\n" |
| 1428 | , stt->sel.priority + 2 |
| 1429 | , (int)(stt->sel.freq_ut / USEC_PER_SEC) |
| 1430 | , program_name |
| 1431 | , "sel" |
| 1432 | ); |
| 1433 | printf("DIMENSION events '' absolute 1 1\n"); |
| 1434 | } |
| 1435 | |
| 1436 | printf( |
| 1437 | "BEGIN ipmi.events\n" |
| 1438 | "SET events = %zu\n" |
| 1439 | "END\n" |
| 1440 | , |
| 1441 | stt->sel.events |
| 1442 | ); |
| 1443 | } |
| 1444 | |
| 1445 | netdata_mutex_unlock(&stdout_mutex); |
| 1446 | |
| 1447 | return stt->sel.events; |
| 1448 | } |
| 1449 | |
| 1450 | // ---------------------------------------------------------------------------- |
| 1451 | |
| 1452 | static const char *get_sensor_state_string(struct sensor *sn) { |
| 1453 | switch (sn->sensor_state) { |
| 1454 | case IPMI_MONITORING_STATE_NOMINAL: |
| 1455 | return "nominal"; |
| 1456 | case IPMI_MONITORING_STATE_WARNING: |
| 1457 | return "warning"; |
| 1458 | case IPMI_MONITORING_STATE_CRITICAL: |
| 1459 | return "critical"; |
| 1460 | default: |
| 1461 | return "unknown"; |
| 1462 | } |
| 1463 | } |
| 1464 | |
| 1465 | static const char *get_sensor_function_priority(struct sensor *sn) { |
| 1466 | switch (sn->sensor_state) { |
| 1467 | case IPMI_MONITORING_STATE_WARNING: |
| 1468 | return "warning"; |
| 1469 | case IPMI_MONITORING_STATE_CRITICAL: |
| 1470 | return "critical"; |
| 1471 | default: |
| 1472 | return "normal"; |
| 1473 | } |
| 1474 | } |
| 1475 | |
| 1476 | static void freeimi_function_sensors(const char *transaction, char *function __maybe_unused, |
| 1477 | usec_t *stop_monotonic_ut __maybe_unused, bool *cancelled __maybe_unused, |
| 1478 | BUFFER *payload __maybe_unused, HTTP_ACCESS access __maybe_unused, |
| 1479 | const char *source __maybe_unused, void *data __maybe_unused) { |
| 1480 | time_t now_s = now_realtime_sec(); |
| 1481 | |
| 1482 | BUFFER *wb = buffer_create(4096, NULL); |
| 1483 | buffer_json_initialize(wb, "\"", "\"", 0, true, BUFFER_JSON_OPTIONS_NEWLINE_ON_ARRAY_ITEMS); |
| 1484 | buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK); |
| 1485 | buffer_json_member_add_string(wb, "type", "table"); |
| 1486 | buffer_json_member_add_time_t(wb, "update_every", update_every); |
| 1487 | buffer_json_member_add_boolean(wb, "has_history", false); |
| 1488 | buffer_json_member_add_string(wb, "help", "View IPMI sensor readings and its state"); |
| 1489 | |
| 1490 | char *function_copy = strdupz(function); |
| 1491 | char *words[1024]; |
| 1492 | size_t num_words = quoted_strings_splitter_whitespace(function_copy, words, 1024); |
| 1493 | for(size_t i = 1; i < num_words ;i++) { |
| 1494 | char *param = get_word(words, num_words, i); |
| 1495 | if(strcmp(param, "info") == 0) { |
| 1496 | buffer_json_member_add_array(wb, "accepted_params"); |
| 1497 | buffer_json_array_close(wb); // accepted_params |
| 1498 | buffer_json_member_add_array(wb, "required_params"); |
| 1499 | buffer_json_array_close(wb); // required_params |
| 1500 | freez(function_copy); |
| 1501 | goto close_and_send; |
| 1502 | } |
| 1503 | } |
| 1504 | |
| 1505 | freez(function_copy); |
| 1506 | |
| 1507 | buffer_json_member_add_array(wb, "data"); |
| 1508 | |
| 1509 | struct sensor *sn; |
| 1510 | dfe_start_reentrant(state.sensors.dict, sn) { |
| 1511 | if (unlikely(!sn->do_metric && !sn->do_state)) |
| 1512 | continue; |
| 1513 | |
| 1514 | double reading = NAN; |
| 1515 | switch (sn->sensor_reading_type) { |
| 1516 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER32: |
| 1517 | reading = (double)sn->sensor_reading.uint32_value; |
| 1518 | break; |
| 1519 | case IPMI_MONITORING_SENSOR_READING_TYPE_DOUBLE: |
| 1520 | reading = (double)(sn->sensor_reading.double_value); |
| 1521 | break; |
| 1522 | case IPMI_MONITORING_SENSOR_READING_TYPE_UNSIGNED_INTEGER8_BOOL: |
| 1523 | reading = (double)sn->sensor_reading.bool_value; |
| 1524 | break; |
| 1525 | } |
| 1526 | |
| 1527 | buffer_json_add_array_item_array(wb); |
| 1528 | |
| 1529 | buffer_json_add_array_item_string(wb, sn->sensor_name); |
| 1530 | buffer_json_add_array_item_string(wb, sn->type); |
| 1531 | buffer_json_add_array_item_string(wb, sn->component); |
| 1532 | buffer_json_add_array_item_double(wb, reading); |
| 1533 | buffer_json_add_array_item_string(wb, sn->units); |
| 1534 | buffer_json_add_array_item_string(wb, get_sensor_state_string(sn)); |
| 1535 | |
| 1536 | buffer_json_add_array_item_object(wb); |
| 1537 | buffer_json_member_add_string(wb, "severity", get_sensor_function_priority(sn)); |
| 1538 | buffer_json_object_close(wb); |
| 1539 | |
| 1540 | buffer_json_array_close(wb); |
| 1541 | } |
| 1542 | dfe_done(sn); |
| 1543 | |
| 1544 | buffer_json_array_close(wb); // data |
| 1545 | buffer_json_member_add_object(wb, "columns"); |
| 1546 | { |
| 1547 | size_t field_id = 0; |
| 1548 | |
| 1549 | buffer_rrdf_table_add_field(wb, field_id++, "Sensor", "Sensor Name", |
| 1550 | RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, |
| 1551 | 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, |
| 1552 | RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, |
| 1553 | RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY | RRDF_FIELD_OPTS_STICKY | RRDF_FIELD_OPTS_FULL_WIDTH, |
| 1554 | NULL); |
| 1555 | buffer_rrdf_table_add_field(wb, field_id++, "Type", "Sensor Type", |
| 1556 | RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, |
| 1557 | 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, |
| 1558 | RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, |
| 1559 | RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY, |
| 1560 | NULL); |
| 1561 | buffer_rrdf_table_add_field(wb, field_id++, "Component", "Sensor Component", |
| 1562 | RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, |
| 1563 | 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, |
| 1564 | RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, |
| 1565 | RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY, |
| 1566 | NULL); |
| 1567 | buffer_rrdf_table_add_field(wb, field_id++, "Reading", "Sensor Current Reading", |
| 1568 | RRDF_FIELD_TYPE_INTEGER, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, |
| 1569 | 2, NULL, 0, RRDF_FIELD_SORT_DESCENDING, NULL, |
| 1570 | RRDF_FIELD_SUMMARY_SUM, RRDF_FIELD_FILTER_NONE, |
| 1571 | RRDF_FIELD_OPTS_VISIBLE, |
| 1572 | NULL); |
| 1573 | buffer_rrdf_table_add_field(wb, field_id++, "Units", "Sensor Reading Units", |
| 1574 | RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, |
| 1575 | 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, |
| 1576 | RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, |
| 1577 | RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY, |
| 1578 | NULL); |
| 1579 | buffer_rrdf_table_add_field(wb, field_id++, "State", "Sensor State", |
| 1580 | RRDF_FIELD_TYPE_STRING, RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, |
| 1581 | 0, NULL, NAN, RRDF_FIELD_SORT_ASCENDING, NULL, |
| 1582 | RRDF_FIELD_SUMMARY_COUNT, RRDF_FIELD_FILTER_MULTISELECT, |
| 1583 | RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_UNIQUE_KEY, |
| 1584 | NULL); |
| 1585 | buffer_rrdf_table_add_field( |
| 1586 | wb, field_id++, |
| 1587 | "rowOptions", "rowOptions", |
| 1588 | RRDF_FIELD_TYPE_NONE, |
| 1589 | RRDR_FIELD_VISUAL_ROW_OPTIONS, |
| 1590 | RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN, |
| 1591 | RRDF_FIELD_SORT_FIXED, |
| 1592 | NULL, |
| 1593 | RRDF_FIELD_SUMMARY_COUNT, |
| 1594 | RRDF_FIELD_FILTER_NONE, |
| 1595 | RRDF_FIELD_OPTS_DUMMY, |
| 1596 | NULL); |
| 1597 | } |
| 1598 | |
| 1599 | buffer_json_object_close(wb); // columns |
| 1600 | buffer_json_member_add_string(wb, "default_sort_column", "Type"); |
| 1601 | |
| 1602 | buffer_json_member_add_object(wb, "charts"); |
| 1603 | { |
| 1604 | buffer_json_member_add_object(wb, "Sensors"); |
| 1605 | { |
| 1606 | buffer_json_member_add_string(wb, "name", "Sensors"); |
| 1607 | buffer_json_member_add_string(wb, "type", "stacked-bar"); |
| 1608 | buffer_json_member_add_array(wb, "columns"); |
| 1609 | { |
| 1610 | buffer_json_add_array_item_string(wb, "Sensor"); |
| 1611 | } |
| 1612 | buffer_json_array_close(wb); |
| 1613 | } |
| 1614 | buffer_json_object_close(wb); |
| 1615 | } |
| 1616 | buffer_json_object_close(wb); // charts |
| 1617 | |
| 1618 | buffer_json_member_add_array(wb, "default_charts"); |
| 1619 | { |
| 1620 | buffer_json_add_array_item_array(wb); |
| 1621 | buffer_json_add_array_item_string(wb, "Sensors"); |
| 1622 | buffer_json_add_array_item_string(wb, "Component"); |
| 1623 | buffer_json_array_close(wb); |
| 1624 | |
| 1625 | buffer_json_add_array_item_array(wb); |
| 1626 | buffer_json_add_array_item_string(wb, "Sensors"); |
| 1627 | buffer_json_add_array_item_string(wb, "State"); |
| 1628 | buffer_json_array_close(wb); |
| 1629 | } |
| 1630 | buffer_json_array_close(wb); |
| 1631 | |
| 1632 | close_and_send: |
| 1633 | buffer_json_member_add_time_t(wb, "expires", now_s + update_every); |
| 1634 | buffer_json_finalize(wb); |
| 1635 | |
| 1636 | wb->response_code = HTTP_RESP_OK; |
| 1637 | wb->content_type = CT_APPLICATION_JSON; |
| 1638 | wb->expires = now_s + update_every; |
| 1639 | pluginsd_function_result_to_stdout(transaction, wb); |
| 1640 | |
| 1641 | buffer_free(wb); |
| 1642 | } |
| 1643 | |
| 1644 | // ---------------------------------------------------------------------------- |
| 1645 | // main, command line arguments parsing |
| 1646 | |
| 1647 | static void plugin_exit(int code) { |
| 1648 | fflush(stdout); |
| 1649 | __atomic_store_n(&function_plugin_should_exit, true, __ATOMIC_RELEASE); |
| 1650 | exit(code); |
| 1651 | } |
| 1652 | |
| 1653 | int main (int argc, char **argv) { |
| 1654 | nd_log_initialize_for_external_plugins("freeipmi.plugin"); |
| 1655 | netdata_threads_init_for_external_plugins(0); // set the default threads stack size here |
| 1656 | |
| 1657 | bool netdata_do_sel = IPMI_ENABLE_SEL_BY_DEFAULT; |
| 1658 | |
| 1659 | bool debug = false; |
| 1660 | |
| 1661 | // TODO: Workaround for https://github.com/netdata/netdata/issues/17931 |
| 1662 | // This variable will be removed once the issue is fixed. |
| 1663 | bool restart_every = true; |
| 1664 | |
| 1665 | // ------------------------------------------------------------------------ |
| 1666 | // parse command line parameters |
| 1667 | |
| 1668 | int i, freq_s = 0; |
| 1669 | for(i = 1; i < argc ; i++) { |
| 1670 | if(isdigit(*argv[i]) && !freq_s) { |
| 1671 | int n = str2i(argv[i]); |
| 1672 | if(n > 0 && n < 86400) { |
| 1673 | freq_s = n; |
| 1674 | continue; |
| 1675 | } |
| 1676 | } |
| 1677 | else if(strcmp("version", argv[i]) == 0 || strcmp("-version", argv[i]) == 0 || strcmp("--version", argv[i]) == 0 || strcmp("-v", argv[i]) == 0 || strcmp("-V", argv[i]) == 0) { |
| 1678 | printf("%s %s\n", program_name, NETDATA_VERSION); |
| 1679 | exit(0); |
| 1680 | } |
| 1681 | else if(strcmp("debug", argv[i]) == 0) { |
| 1682 | debug = true; |
| 1683 | continue; |
| 1684 | } |
| 1685 | else if(strcmp("no-restart", argv[i]) == 0) { |
| 1686 | restart_every = false; |
| 1687 | continue; |
| 1688 | } |
| 1689 | else if(strcmp("sel", argv[i]) == 0) { |
| 1690 | netdata_do_sel = true; |
| 1691 | continue; |
| 1692 | } |
| 1693 | else if(strcmp("no-sel", argv[i]) == 0) { |
| 1694 | netdata_do_sel = false; |
| 1695 | continue; |
| 1696 | } |
| 1697 | else if(strcmp("reread-sdr-cache", argv[i]) == 0) { |
| 1698 | global_sel_flags |= IPMI_MONITORING_SEL_FLAGS_REREAD_SDR_CACHE; |
| 1699 | global_sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_REREAD_SDR_CACHE; |
| 1700 | remove_reread_sdr_after_first_use = false; |
| 1701 | if (debug) fprintf(stderr, "%s: reread-sdr-cache enabled for both sensors and SEL\n", program_name); |
| 1702 | } |
| 1703 | else if(strcmp("interpret-oem-data", argv[i]) == 0) { |
| 1704 | global_sel_flags |= IPMI_MONITORING_SEL_FLAGS_INTERPRET_OEM_DATA; |
| 1705 | global_sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_INTERPRET_OEM_DATA; |
| 1706 | if (debug) fprintf(stderr, "%s: interpret-oem-data enabled for both sensors and SEL\n", program_name); |
| 1707 | } |
| 1708 | else if(strcmp("assume-system-event-record", argv[i]) == 0) { |
| 1709 | global_sel_flags |= IPMI_MONITORING_SEL_FLAGS_ASSUME_SYSTEM_EVENT_RECORD; |
| 1710 | if (debug) fprintf(stderr, "%s: assume-system-event-record enabled\n", program_name); |
| 1711 | } |
| 1712 | else if(strcmp("ignore-non-interpretable-sensors", argv[i]) == 0) { |
| 1713 | global_sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_NON_INTERPRETABLE_SENSORS; |
| 1714 | if (debug) fprintf(stderr, "%s: ignore-non-interpretable-sensors enabled\n", program_name); |
| 1715 | } |
| 1716 | else if(strcmp("bridge-sensors", argv[i]) == 0) { |
| 1717 | global_sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_BRIDGE_SENSORS; |
| 1718 | if (debug) fprintf(stderr, "%s: bridge-sensors enabled\n", program_name); |
| 1719 | } |
| 1720 | else if(strcmp("shared-sensors", argv[i]) == 0) { |
| 1721 | global_sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_SHARED_SENSORS; |
| 1722 | if (debug) fprintf(stderr, "%s: shared-sensors enabled\n", program_name); |
| 1723 | } |
| 1724 | else if(strcmp("no-discrete-reading", argv[i]) == 0) { |
| 1725 | global_sensor_reading_flags &= ~(IPMI_MONITORING_SENSOR_READING_FLAGS_DISCRETE_READING); |
| 1726 | if (debug) fprintf(stderr, "%s: discrete-reading disabled\n", program_name); |
| 1727 | } |
| 1728 | else if(strcmp("ignore-scanning-disabled", argv[i]) == 0) { |
| 1729 | global_sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_SCANNING_DISABLED; |
| 1730 | if (debug) fprintf(stderr, "%s: ignore-scanning-disabled enabled\n", program_name); |
| 1731 | } |
| 1732 | else if(strcmp("assume-bmc-owner", argv[i]) == 0) { |
| 1733 | global_sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_ASSUME_BMC_OWNER; |
| 1734 | if (debug) fprintf(stderr, "%s: assume-bmc-owner enabled\n", program_name); |
| 1735 | } |
| 1736 | #if defined(IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES) && defined(IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES) |
| 1737 | else if(strcmp("entity-sensor-names", argv[i]) == 0) { |
| 1738 | global_sel_flags |= IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES; |
| 1739 | global_sensor_reading_flags |= IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES; |
| 1740 | if (debug) fprintf(stderr, "%s: entity-sensor-names enabled for both sensors and SEL\n", program_name); |
| 1741 | } |
| 1742 | #endif |
| 1743 | else if(strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) { |
| 1744 | fprintf(stderr, |
| 1745 | "\n" |
| 1746 | " netdata %s %s\n" |
| 1747 | " Copyright 2018-2025 Netdata Inc.\n" |
| 1748 | " Released under GNU General Public License v3 or later.\n" |
| 1749 | "\n" |
| 1750 | " This program is a data collector plugin for netdata.\n" |
| 1751 | "\n" |
| 1752 | " Available command line options:\n" |
| 1753 | "\n" |
| 1754 | " SECONDS data collection frequency\n" |
| 1755 | " minimum: %d\n" |
| 1756 | "\n" |
| 1757 | " debug enable verbose output\n" |
| 1758 | " default: disabled\n" |
| 1759 | "\n" |
| 1760 | " sel\n" |
| 1761 | " no-sel enable/disable SEL collection\n" |
| 1762 | " default: %s\n" |
| 1763 | "\n" |
| 1764 | " reread-sdr-cache re-read SDR cache on every iteration\n" |
| 1765 | " default: disabled\n" |
| 1766 | "\n" |
| 1767 | " interpret-oem-data attempt to parse OEM data\n" |
| 1768 | " default: disabled\n" |
| 1769 | "\n" |
| 1770 | " assume-system-event-record \n" |
| 1771 | " tread illegal SEL events records as normal\n" |
| 1772 | " default: disabled\n" |
| 1773 | "\n" |
| 1774 | " ignore-non-interpretable-sensors \n" |
| 1775 | " do not read sensors that cannot be interpreted\n" |
| 1776 | " default: disabled\n" |
| 1777 | "\n" |
| 1778 | " bridge-sensors bridge sensors not owned by the BMC\n" |
| 1779 | " default: disabled\n" |
| 1780 | "\n" |
| 1781 | " shared-sensors enable shared sensors, if found\n" |
| 1782 | " default: disabled\n" |
| 1783 | "\n" |
| 1784 | " no-discrete-reading do not read sensors that their event/reading type code is invalid\n" |
| 1785 | " default: enabled\n" |
| 1786 | "\n" |
| 1787 | " ignore-scanning-disabled \n" |
| 1788 | " Ignore the scanning bit and read sensors no matter what\n" |
| 1789 | " default: disabled\n" |
| 1790 | "\n" |
| 1791 | " assume-bmc-owner assume the BMC is the sensor owner no matter what\n" |
| 1792 | " (usually bridging is required too)\n" |
| 1793 | " default: disabled\n" |
| 1794 | "\n" |
| 1795 | #if defined(IPMI_MONITORING_SEL_FLAGS_ENTITY_SENSOR_NAMES) && defined(IPMI_MONITORING_SENSOR_READING_FLAGS_ENTITY_SENSOR_NAMES) |
| 1796 | " entity-sensor-names sensor names prefixed with entity id and instance\n" |
| 1797 | " default: disabled\n" |
| 1798 | "\n" |
| 1799 | #endif |
| 1800 | " hostname HOST\n" |
| 1801 | " username USER\n" |
| 1802 | " password PASS connect to remote IPMI host\n" |
| 1803 | " default: local IPMI processor\n" |
| 1804 | "\n" |
| 1805 | " no-auth-code-check\n" |
| 1806 | " noauthcodecheck don't check the authentication codes returned\n" |
| 1807 | "\n" |
| 1808 | " driver-type IPMIDRIVER\n" |
| 1809 | " Specify the driver type to use instead of doing an auto selection. \n" |
| 1810 | " The currently available outofband drivers are LAN and LAN_2_0,\n" |
| 1811 | " which perform IPMI 1.5 and IPMI 2.0 respectively. \n" |
| 1812 | " The currently available inband drivers are KCS, SSIF, OPENIPMI and SUNBMC.\n" |
| 1813 | "\n" |
| 1814 | " sdr-cache-dir PATH directory for SDR cache files\n" |
| 1815 | " default: %s\n" |
| 1816 | "\n" |
| 1817 | " sensor-config-file FILE filename to read sensor configuration\n" |
| 1818 | " default: %s\n" |
| 1819 | "\n" |
| 1820 | " sel-config-file FILE filename to read sel configuration\n" |
| 1821 | " default: %s\n" |
| 1822 | "\n" |
| 1823 | " ignore N1,N2,N3,... sensor IDs to ignore\n" |
| 1824 | " default: none\n" |
| 1825 | "\n" |
| 1826 | " ignore-status N1,N2,N3,... sensor IDs to ignore status (nominal/warning/critical)\n" |
| 1827 | " default: none\n" |
| 1828 | "\n" |
| 1829 | " -v\n" |
| 1830 | " -V\n" |
| 1831 | " version print version and exit\n" |
| 1832 | "\n" |
| 1833 | " Linux kernel module for IPMI is CPU hungry.\n" |
| 1834 | " On Linux run this to lower kipmiN CPU utilization:\n" |
| 1835 | " # echo 10 > /sys/module/ipmi_si/parameters/kipmid_max_busy_us\n" |
| 1836 | "\n" |
| 1837 | " or create: /etc/modprobe.d/ipmi.conf with these contents:\n" |
| 1838 | " options ipmi_si kipmid_max_busy_us=10\n" |
| 1839 | "\n" |
| 1840 | " For more information:\n" |
| 1841 | " https://github.com/netdata/netdata/tree/master/src/collectors/freeipmi.plugin\n" |
| 1842 | "\n" |
| 1843 | , program_name, NETDATA_VERSION |
| 1844 | , update_every |
| 1845 | , netdata_do_sel?"enabled":"disabled" |
| 1846 | , sdr_cache_directory?sdr_cache_directory:"system default" |
| 1847 | , sensor_config_file?sensor_config_file:"system default" |
| 1848 | , sel_config_file?sel_config_file:"system default" |
| 1849 | ); |
| 1850 | exit(1); |
| 1851 | } |
| 1852 | else if(i < argc && strcmp("hostname", argv[i]) == 0) { |
| 1853 | hostname = strdupz(argv[++i]); |
| 1854 | char *s = argv[i]; |
| 1855 | // mask it be hidden from the process tree |
| 1856 | while(*s) *s++ = 'x'; |
| 1857 | if(debug) fprintf(stderr, "%s: hostname set to '%s'\n", program_name, hostname); |
| 1858 | continue; |
| 1859 | } |
| 1860 | else if(i < argc && strcmp("username", argv[i]) == 0) { |
| 1861 | username = strdupz(argv[++i]); |
| 1862 | char *s = argv[i]; |
| 1863 | // mask it be hidden from the process tree |
| 1864 | while(*s) *s++ = 'x'; |
| 1865 | if(debug) fprintf(stderr, "%s: username set to '%s'\n", program_name, username); |
| 1866 | continue; |
| 1867 | } |
| 1868 | else if(i < argc && strcmp("password", argv[i]) == 0) { |
| 1869 | password = strdupz(argv[++i]); |
| 1870 | char *s = argv[i]; |
| 1871 | // mask it be hidden from the process tree |
| 1872 | while(*s) *s++ = 'x'; |
| 1873 | if(debug) fprintf(stderr, "%s: password set to '%s'\n", program_name, password); |
| 1874 | continue; |
| 1875 | } |
| 1876 | else if(strcmp("driver-type", argv[i]) == 0) { |
| 1877 | if (hostname) { |
| 1878 | freeimpi_protocol_version = netdata_parse_outofband_driver_type(argv[++i]); |
| 1879 | if(debug) fprintf(stderr, "%s: outband FreeIMPI protocol version set to '%d'\n", |
| 1880 | program_name, freeimpi_protocol_version); |
| 1881 | } |
| 1882 | else { |
| 1883 | driver_type = netdata_parse_inband_driver_type(argv[++i]); |
| 1884 | if(debug) fprintf(stderr, "%s: inband driver type set to '%d'\n", |
| 1885 | program_name, driver_type); |
| 1886 | } |
| 1887 | continue; |
| 1888 | } else if (i < argc && (strcmp("noauthcodecheck", argv[i]) == 0 || strcmp("no-auth-code-check", argv[i]) == 0)) { |
| 1889 | if (!hostname || netdata_host_is_localhost(hostname)) { |
| 1890 | if (debug) |
| 1891 | fprintf(stderr, "%s: noauthcodecheck workaround flag is ignored for inband configuration\n", |
| 1892 | program_name); |
| 1893 | |
| 1894 | } |
| 1895 | else if (freeimpi_protocol_version < 0 || freeimpi_protocol_version == IPMI_MONITORING_PROTOCOL_VERSION_1_5) { |
| 1896 | workaround_flags |= IPMI_MONITORING_WORKAROUND_FLAGS_PROTOCOL_VERSION_1_5_NO_AUTH_CODE_CHECK; |
| 1897 | |
| 1898 | if (debug) |
| 1899 | fprintf(stderr, "%s: noauthcodecheck workaround flag enabled\n", program_name); |
| 1900 | } |
| 1901 | else { |
| 1902 | if (debug) |
| 1903 | fprintf(stderr, "%s: noauthcodecheck workaround flag is ignored for protocol version 2.0\n", |
| 1904 | program_name); |
| 1905 | } |
| 1906 | continue; |
| 1907 | } |
| 1908 | else if(i < argc && strcmp("sdr-cache-dir", argv[i]) == 0) { |
| 1909 | sdr_cache_directory = argv[++i]; |
| 1910 | |
| 1911 | if(debug) |
| 1912 | fprintf(stderr, "%s: SDR cache directory set to '%s'\n", program_name, sdr_cache_directory); |
| 1913 | |
| 1914 | continue; |
| 1915 | } |
| 1916 | else if(i < argc && strcmp("sensor-config-file", argv[i]) == 0) { |
| 1917 | sensor_config_file = argv[++i]; |
| 1918 | if(debug) fprintf(stderr, "%s: sensor config file set to '%s'\n", program_name, sensor_config_file); |
| 1919 | continue; |
| 1920 | } |
| 1921 | else if(i < argc && strcmp("sel-config-file", argv[i]) == 0) { |
| 1922 | sel_config_file = argv[++i]; |
| 1923 | if(debug) fprintf(stderr, "%s: sel config file set to '%s'\n", program_name, sel_config_file); |
| 1924 | continue; |
| 1925 | } |
| 1926 | else if(i < argc && strcmp("ignore", argv[i]) == 0) { |
| 1927 | excluded_record_ids_parse(argv[++i], debug); |
| 1928 | continue; |
| 1929 | } |
| 1930 | else if(i < argc && strcmp("ignore-status", argv[i]) == 0) { |
| 1931 | excluded_status_record_ids_parse(argv[++i], debug); |
| 1932 | continue; |
| 1933 | } |
| 1934 | |
| 1935 | collector_error("%s(): ignoring parameter '%s'", __FUNCTION__, argv[i]); |
| 1936 | } |
| 1937 | |
| 1938 | errno_clear(); |
| 1939 | |
| 1940 | if(freq_s && freq_s < update_every) |
| 1941 | collector_info("%s(): update frequency %d seconds is too small for IPMI. Using %d.", |
| 1942 | __FUNCTION__, freq_s, update_every); |
| 1943 | |
| 1944 | update_every = freq_s = MAX(freq_s, update_every); |
| 1945 | update_every_sel = MAX(update_every, update_every_sel); |
| 1946 | |
| 1947 | // ------------------------------------------------------------------------ |
| 1948 | // initialize IPMI |
| 1949 | |
| 1950 | if(debug) { |
| 1951 | fprintf(stderr, "%s: calling ipmi_monitoring_init()\n", program_name); |
| 1952 | ipmimonitoring_init_flags |= IPMI_MONITORING_FLAGS_DEBUG|IPMI_MONITORING_FLAGS_DEBUG_IPMI_PACKETS; |
| 1953 | } |
| 1954 | |
| 1955 | int rc; |
| 1956 | if(ipmi_monitoring_init(ipmimonitoring_init_flags, &rc) < 0) |
| 1957 | fatal("ipmi_monitoring_init: %s", ipmi_monitoring_ctx_strerror(rc)); |
| 1958 | |
| 1959 | // ------------------------------------------------------------------------ |
| 1960 | // create the data collection threads |
| 1961 | |
| 1962 | struct ipmi_collection_thread sensors_data = { |
| 1963 | .type = IPMI_COLLECT_TYPE_SENSORS, |
| 1964 | .freq_s = update_every, |
| 1965 | .spinlock = SPINLOCK_INITIALIZER, |
| 1966 | .debug = debug, |
| 1967 | .state = { |
| 1968 | .debug = debug, |
| 1969 | .sensors = { |
| 1970 | .status = ICS_INIT, |
| 1971 | .last_iteration_ut = now_monotonic_usec(), |
| 1972 | .freq_ut = update_every * USEC_PER_SEC, |
| 1973 | .priority = IPMI_SENSORS_DASHBOARD_PRIORITY, |
| 1974 | .dict = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE|DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct sensor)), |
| 1975 | }, |
| 1976 | }, |
| 1977 | }, sel_data = { |
| 1978 | .type = IPMI_COLLECT_TYPE_SEL, |
| 1979 | .freq_s = update_every_sel, |
| 1980 | .spinlock = SPINLOCK_INITIALIZER, |
| 1981 | .debug = debug, |
| 1982 | .state = { |
| 1983 | .debug = debug, |
| 1984 | .sel = { |
| 1985 | .status = ICS_INIT, |
| 1986 | .last_iteration_ut = now_monotonic_usec(), |
| 1987 | .freq_ut = update_every_sel * USEC_PER_SEC, |
| 1988 | .priority = IPMI_SEL_DASHBOARD_PRIORITY, |
| 1989 | }, |
| 1990 | }, |
| 1991 | }; |
| 1992 | |
| 1993 | nd_thread_create("IPMI[sensors]", NETDATA_THREAD_OPTION_DONT_LOG, netdata_ipmi_collection_thread, &sensors_data); |
| 1994 | if (netdata_do_sel) |
| 1995 | nd_thread_create("IPMI[sel]", NETDATA_THREAD_OPTION_DONT_LOG, netdata_ipmi_collection_thread, &sel_data); |
| 1996 | |
| 1997 | // ------------------------------------------------------------------------ |
| 1998 | // the main loop |
| 1999 | |
| 2000 | if(debug) fprintf(stderr, "%s: starting data collection\n", program_name); |
| 2001 | |
| 2002 | time_t started_t = now_monotonic_sec(); |
| 2003 | |
| 2004 | size_t iteration = 0; |
| 2005 | bool global_chart_created = false; |
| 2006 | bool tty = isatty(fileno(stdout)) == 1; |
| 2007 | |
| 2008 | heartbeat_t hb; |
| 2009 | heartbeat_init(&hb, update_every * USEC_PER_SEC); |
| 2010 | for(iteration = 0; !__atomic_load_n(&function_plugin_should_exit, __ATOMIC_ACQUIRE) ; iteration++) { |
| 2011 | usec_t dt = heartbeat_next(&hb); |
| 2012 | |
| 2013 | if (!tty) { |
| 2014 | netdata_mutex_lock(&stdout_mutex); |
| 2015 | fprintf(stdout, "\n"); // keepalive to avoid parser read timeout (2 minutes) during ipmi_detect_speed_secs() |
| 2016 | fflush(stdout); |
| 2017 | netdata_mutex_unlock(&stdout_mutex); |
| 2018 | } |
| 2019 | |
| 2020 | spinlock_lock(&sensors_data.spinlock); |
| 2021 | state.sensors = sensors_data.state.sensors; |
| 2022 | spinlock_unlock(&sensors_data.spinlock); |
| 2023 | |
| 2024 | spinlock_lock(&sel_data.spinlock); |
| 2025 | state.sel = sel_data.state.sel; |
| 2026 | spinlock_unlock(&sel_data.spinlock); |
| 2027 | |
| 2028 | switch(state.sensors.status) { |
| 2029 | case ICS_RUNNING: |
| 2030 | if(now_monotonic_usec() > state.sensors.last_iteration_ut + IPMI_RESTART_IF_SENSORS_DONT_ITERATE_EVERY_SECONDS * USEC_PER_SEC) { |
| 2031 | collector_error("%s(): sensors have not be collected for %zu seconds. Exiting to restart.", |
| 2032 | __FUNCTION__, (size_t)((now_monotonic_usec() - state.sensors.last_iteration_ut) / USEC_PER_SEC)); |
| 2033 | |
| 2034 | fprintf(stdout, "EXIT\n"); |
| 2035 | plugin_exit(0); |
| 2036 | } |
| 2037 | break; |
| 2038 | |
| 2039 | case ICS_INIT: |
| 2040 | continue; |
| 2041 | |
| 2042 | case ICS_INIT_FAILED: |
| 2043 | collector_error("%s(): sensors failed to initialize. Calling DISABLE.", __FUNCTION__); |
| 2044 | fprintf(stdout, "DISABLE\n"); |
| 2045 | plugin_exit(0); |
| 2046 | break; |
| 2047 | |
| 2048 | case ICS_FAILED: |
| 2049 | collector_error("%s(): sensors fails repeatedly to collect metrics. Exiting to restart.", __FUNCTION__); |
| 2050 | fprintf(stdout, "EXIT\n"); |
| 2051 | plugin_exit(0); |
| 2052 | break; |
| 2053 | } |
| 2054 | |
| 2055 | if(netdata_do_sel) { |
| 2056 | switch (state.sensors.status) { |
| 2057 | case ICS_RUNNING: |
| 2058 | case ICS_INIT: |
| 2059 | break; |
| 2060 | |
| 2061 | case ICS_INIT_FAILED: |
| 2062 | case ICS_FAILED: |
| 2063 | collector_error("%s(): SEL fails to collect events. Disabling SEL collection.", __FUNCTION__); |
| 2064 | netdata_do_sel = false; |
| 2065 | break; |
| 2066 | } |
| 2067 | } |
| 2068 | |
| 2069 | if(unlikely(debug)) |
| 2070 | fprintf(stderr, "%s: calling send_ipmi_sensor_metrics_to_netdata()\n", program_name); |
| 2071 | |
| 2072 | static bool add_func_sensors = true; |
| 2073 | if (add_func_sensors) { |
| 2074 | add_func_sensors = false; |
| 2075 | struct functions_evloop_globals *wg = |
| 2076 | functions_evloop_init(1, "FREEIPMI", &stdout_mutex, &function_plugin_should_exit, NULL); |
| 2077 | functions_evloop_add_function( |
| 2078 | wg, "ipmi-sensors", freeimi_function_sensors, PLUGINS_FUNCTIONS_TIMEOUT_DEFAULT, NULL); |
| 2079 | FREEIPMI_GLOBAL_FUNCTION_SENSORS(); |
| 2080 | } |
| 2081 | |
| 2082 | state.updates.now_ut = now_monotonic_usec(); |
| 2083 | send_ipmi_sensor_metrics_to_netdata(&state); |
| 2084 | |
| 2085 | if(netdata_do_sel) |
| 2086 | send_ipmi_sel_metrics_to_netdata(&state); |
| 2087 | |
| 2088 | if(unlikely(debug)) |
| 2089 | fprintf(stderr, "%s: iteration %zu, dt %"PRIu64" usec, sensors ever collected %zu, sensors last collected %zu \n" |
| 2090 | , program_name |
| 2091 | , iteration |
| 2092 | , dt |
| 2093 | , dictionary_entries(state.sensors.dict) |
| 2094 | , state.sensors.collected |
| 2095 | ); |
| 2096 | |
| 2097 | netdata_mutex_lock(&stdout_mutex); |
| 2098 | |
| 2099 | if (!global_chart_created) { |
| 2100 | global_chart_created = true; |
| 2101 | |
| 2102 | fprintf(stdout, |
| 2103 | "CHART netdata.freeipmi_availability_status '' 'Plugin availability status' 'status' " |
| 2104 | "plugins netdata.plugin_availability_status line 146000 %d '' '%s' '%s'\n" |
| 2105 | "DIMENSION available '' absolute 1 1\n", |
| 2106 | update_every, program_name, ""); |
| 2107 | } |
| 2108 | |
| 2109 | fprintf(stdout, |
| 2110 | "BEGIN netdata.freeipmi_availability_status\n" |
| 2111 | "SET available = 1\n" |
| 2112 | "END\n"); |
| 2113 | |
| 2114 | // restart check (14400 seconds) |
| 2115 | if (restart_every && (now_monotonic_sec() - started_t > IPMI_RESTART_EVERY_SECONDS)) { |
| 2116 | collector_info("%s(): reached my lifetime expectancy. Exiting to restart.", __FUNCTION__); |
| 2117 | fprintf(stdout, "EXIT\n"); |
| 2118 | netdata_mutex_unlock(&stdout_mutex); |
| 2119 | plugin_exit(0); |
| 2120 | } |
| 2121 | |
| 2122 | fflush(stdout); |
| 2123 | |
| 2124 | netdata_mutex_unlock(&stdout_mutex); |
| 2125 | } |
| 2126 | } |