master
c 2,126 lines 82.4 KB
Raw
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 }