/* nut-libfreeipmi.c - NUT IPMI backend, using FreeIPMI * * Copyright (C) * 2011 - 2012 Arnaud Quette * 2011 - Albert Chu * * Based on the sample codes 'ipmi-fru-example.c', 'frulib.c' and * 'ipmimonitoring-sensors.c', from FreeIPMI * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * TODO: * add power control support (ipmipower): seems OOB only! -n, --on Power on the target hosts. -f, --off Power off the target hosts. -c, --cycle Power cycle the target hosts. -r, --reset Reset the target hosts. -s, --stat Get power status of the target hosts. --pulse Send power diagnostic interrupt to target hosts. --soft Initiate a soft-shutdown of the OS via ACPI. --on-if-off Issue a power on command instead of a power cycle or hard reset command if the remote machine's power is currently off. --wait-until-off Regularly query the remote BMC and return only after the machine has powered off. --wait-until-on Regularly query the remote BMC and return only */ #include "config.h" /* must be first */ #include #include #include "timehead.h" #include "common.h" #include #include #if HAVE_FREEIPMI_MONITORING #include #endif #include "nut-ipmi.h" #include "nut_stdint.h" #include "dstate.h" /* FreeIPMI defines */ #define IPMI_FRU_STR_BUFLEN 1024 /* haven't seen a motherboard with more than 2-3 so far, * 64 should be more than enough */ #define IPMI_FRU_CUSTOM_FIELDS 64 /* FreeIPMI contexts and configuration*/ static ipmi_ctx_t ipmi_ctx = NULL; static ipmi_monitoring_ctx_t mon_ctx = NULL; /* static struct ipmi_monitoring_ipmi_config ipmi_config; */ /* SDR management API has changed with 1.1.X and later */ #ifdef HAVE_FREEIPMI_11X_12X static ipmi_sdr_ctx_t sdr_ctx = NULL; static ipmi_fru_ctx_t fru_ctx = NULL; #define SDR_PARSE_CTX sdr_ctx #define NUT_IPMI_SDR_CACHE_DEFAULTS IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT #else static ipmi_sdr_cache_ctx_t sdr_ctx = NULL; static ipmi_sdr_parse_ctx_t sdr_parse_ctx = NULL; #define SDR_PARSE_CTX sdr_parse_ctx static ipmi_fru_parse_ctx_t fru_ctx = NULL; /* Functions remapping */ #define ipmi_sdr_ctx_create ipmi_sdr_cache_ctx_create #define ipmi_sdr_ctx_destroy ipmi_sdr_cache_ctx_destroy #define ipmi_sdr_ctx_errnum ipmi_sdr_cache_ctx_errnum #define ipmi_sdr_ctx_errormsg ipmi_sdr_cache_ctx_errormsg #define ipmi_fru_ctx_create ipmi_fru_parse_ctx_create #define ipmi_fru_ctx_destroy ipmi_fru_parse_ctx_destroy #define ipmi_fru_ctx_set_flags ipmi_fru_parse_ctx_set_flags #define ipmi_fru_ctx_strerror ipmi_fru_parse_ctx_strerror #define ipmi_fru_ctx_errnum ipmi_fru_parse_ctx_errnum #define ipmi_fru_open_device_id ipmi_fru_parse_open_device_id #define ipmi_fru_close_device_id ipmi_fru_parse_close_device_id #define ipmi_fru_ctx_errormsg ipmi_fru_parse_ctx_errormsg #define ipmi_fru_read_data_area ipmi_fru_parse_read_data_area #define ipmi_fru_next ipmi_fru_parse_next #define ipmi_fru_type_length_field_to_string ipmi_fru_parse_type_length_field_to_string #define ipmi_fru_multirecord_power_supply_information ipmi_fru_parse_multirecord_power_supply_information #define ipmi_fru_board_info_area ipmi_fru_parse_board_info_area #define ipmi_fru_field_t ipmi_fru_parse_field_t /* Constants */ #define IPMI_SDR_MAX_RECORD_LENGTH IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH #define IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST IPMI_SDR_CACHE_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST #define IPMI_FRU_AREA_SIZE_MAX IPMI_FRU_PARSE_AREA_SIZE_MAX #define IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS IPMI_FRU_PARSE_FLAGS_SKIP_CHECKSUM_CHECKS #define IPMI_FRU_AREA_TYPE_BOARD_INFO_AREA IPMI_FRU_PARSE_AREA_TYPE_BOARD_INFO_AREA #define IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION #define IPMI_FRU_AREA_STRING_MAX IPMI_FRU_PARSE_AREA_STRING_MAX #define NUT_IPMI_SDR_CACHE_DEFAULTS IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT, IPMI_SDR_CACHE_VALIDATION_FLAGS_DEFAULT #endif /* HAVE_FREEIPMI_11X_12X */ /* FIXME: freeipmi auto selects a cache based on the hostname you are * connecting too, but this is probably fine for you */ #define CACHE_LOCATION "/tmp/sdrcache" /* Support functions */ static const char* libfreeipmi_getfield (uint8_t language_code, ipmi_fru_field_t *field); static void libfreeipmi_cleanup(void); static int libfreeipmi_get_psu_info (const void *areabuf, uint8_t area_length, IPMIDevice_t *ipmi_dev); static int libfreeipmi_get_board_info (const void *areabuf, uint8_t area_length, IPMIDevice_t *ipmi_dev); static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev); /******************************************************************************* * Implementation ******************************************************************************/ int nut_ipmi_open(int ipmi_id, IPMIDevice_t *ipmi_dev) { int ret = -1; uint8_t areabuf[IPMI_FRU_AREA_SIZE_MAX+1]; unsigned int area_type = 0; unsigned int area_length = 0; upsdebugx(1, "nut-libfreeipmi: nutipmi_open()..."); /* FIXME? Check arg types for ipmi_fru_open_device_id() in configure? * At this time it is uint8_t for libfreeipmi implementation of IPMI. */ if (ipmi_id > (int)UINT8_MAX) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "nut_ipmi_open: ipmi_id %d is too large for libfreeipmi", ipmi_id); } /* Initialize the FreeIPMI library. */ if (!(ipmi_ctx = ipmi_ctx_create ())) { /* we have to force cleanup, since exit handler is not yet installed */ libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_ctx_create"); } if ((ret = ipmi_ctx_find_inband (ipmi_ctx, NULL, 0, /* don't disable auto-probe */ 0, 0, NULL, 0, /* workaround flags, none by default */ 0 /* flags */ )) < 0) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "ipmi_ctx_find_inband: %s", ipmi_ctx_errormsg (ipmi_ctx)); } if (!ret) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "could not find inband device"); } upsdebugx(1, "FreeIPMI initialized..."); /* Parse FRU information */ if (!(fru_ctx = ipmi_fru_ctx_create (ipmi_ctx))) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_fru_ctx_create()"); } /* lots of motherboards calculate checksums incorrectly */ if (ipmi_fru_ctx_set_flags (fru_ctx, IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS) < 0) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "ipmi_fru_ctx_set_flags: %s\n", ipmi_fru_ctx_strerror (ipmi_fru_ctx_errnum (fru_ctx))); } /* Now open the requested (local) PSU */ if (ipmi_fru_open_device_id (fru_ctx, (uint8_t)ipmi_id) < 0) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "ipmi_fru_open_device_id: %s\n", ipmi_fru_ctx_errormsg (fru_ctx)); } /* Set IPMI identifier */ ipmi_dev->ipmi_id = ipmi_id; do { /* clear fields */ area_type = 0; area_length = 0; memset (areabuf, '\0', IPMI_FRU_AREA_SIZE_MAX + 1); /* parse FRU buffer */ if (ipmi_fru_read_data_area (fru_ctx, &area_type, &area_length, areabuf, IPMI_FRU_AREA_SIZE_MAX) < 0) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_fru_read_data_area: %s\n", ipmi_fru_ctx_errormsg (fru_ctx)); } if (area_length) { if (area_length > (int)UINT8_MAX) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "nut_ipmi_open: got area_length %d is too large for libfreeipmi", area_length); } switch (area_type) { /* get generic board information */ case IPMI_FRU_AREA_TYPE_BOARD_INFO_AREA: if(libfreeipmi_get_board_info (areabuf, (uint8_t)area_length, ipmi_dev) < 0) { upsdebugx(1, "Can't retrieve board information"); } break; /* get specific PSU information */ case IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION: if(libfreeipmi_get_psu_info (areabuf, (uint8_t)area_length, ipmi_dev) < 0) { upsdebugx(1, "Can't retrieve PSU information"); } break; default: upsdebugx (5, "FRU: discarding FRU Area Type Read: %02Xh", area_type); break; } } } while ((ret = ipmi_fru_next (fru_ctx)) == 1); /* check for errors */ if (ret < 0) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_fru_next: %s", ipmi_fru_ctx_errormsg (fru_ctx)); } else { /* Get all related sensors information */ libfreeipmi_get_sensors_info (ipmi_dev); } /* cleanup context */ libfreeipmi_cleanup(); return (0); } void nut_ipmi_close(void) { upsdebugx(1, "nutipmi_close..."); libfreeipmi_cleanup(); } static const char* libfreeipmi_getfield (uint8_t language_code, ipmi_fru_field_t *field) { static char strbuf[IPMI_FRU_AREA_STRING_MAX + 1]; unsigned int strbuflen = IPMI_FRU_AREA_STRING_MAX; if (!field->type_length_field_length) return NULL; memset (strbuf, '\0', IPMI_FRU_AREA_STRING_MAX + 1); if (ipmi_fru_type_length_field_to_string (fru_ctx, field->type_length_field, field->type_length_field_length, language_code, strbuf, &strbuflen) < 0) { upsdebugx (2, "ipmi_fru_type_length_field_to_string: %s", ipmi_fru_ctx_errormsg (fru_ctx)); return NULL; } if (strbuflen) return strbuf; return NULL; } /* Get voltage value from the IPMI voltage code */ static float libfreeipmi_get_voltage (uint8_t voltage_code) { if (voltage_code == IPMI_FRU_VOLTAGE_12V) return 12; else if (voltage_code == IPMI_FRU_VOLTAGE_MINUS12V) return -12; else if (voltage_code == IPMI_FRU_VOLTAGE_5V) return 5; else if (voltage_code == IPMI_FRU_VOLTAGE_3_3V) return 3.3; else return 0; } /* Cleanup IPMI contexts */ static void libfreeipmi_cleanup() { /* cleanup */ if (fru_ctx) { ipmi_fru_close_device_id (fru_ctx); ipmi_fru_ctx_destroy (fru_ctx); } if (sdr_ctx) { ipmi_sdr_ctx_destroy (sdr_ctx); } #ifndef HAVE_FREEIPMI_11X_12X if (sdr_parse_ctx) { ipmi_sdr_parse_ctx_destroy (sdr_parse_ctx); } #endif if (ipmi_ctx) { ipmi_ctx_close (ipmi_ctx); ipmi_ctx_destroy (ipmi_ctx); } if (mon_ctx) { ipmi_monitoring_ctx_destroy (mon_ctx); } } /* Get generic board information (manufacturer and model names, serial, ...) * from IPMI FRU */ static int libfreeipmi_get_psu_info (const void *areabuf, uint8_t area_length, IPMIDevice_t *ipmi_dev) { /* FIXME: directly use ipmi_dev fields */ unsigned int overall_capacity; input_voltage_range_t low_end_input_voltage_range_1; input_voltage_range_t high_end_input_voltage_range_1; unsigned int low_end_input_frequency_range; unsigned int high_end_input_frequency_range; unsigned int voltage_1; /* code for conversion into a float */ /* FIXME: check for the interest and capability to use these data */ unsigned int peak_va; unsigned int inrush_current; unsigned int inrush_interval; input_voltage_range_t low_end_input_voltage_range_2; input_voltage_range_t high_end_input_voltage_range_2; unsigned int ac_dropout_tolerance; unsigned int predictive_fail_support; unsigned int power_factor_correction; unsigned int autoswitch; unsigned int hot_swap_support; unsigned int tachometer_pulses_per_rotation_predictive_fail_polarity; unsigned int peak_capacity; unsigned int hold_up_time; unsigned int voltage_2; unsigned int total_combined_wattage; unsigned int predictive_fail_tachometer_lower_threshold; upsdebugx(1, "entering libfreeipmi_get_psu_info()"); if (ipmi_fru_multirecord_power_supply_information (fru_ctx, areabuf, area_length, &overall_capacity, &peak_va, &inrush_current, &inrush_interval, &low_end_input_voltage_range_1, &high_end_input_voltage_range_1, &low_end_input_voltage_range_2, &high_end_input_voltage_range_2, &low_end_input_frequency_range, &high_end_input_frequency_range, &ac_dropout_tolerance, &predictive_fail_support, &power_factor_correction, &autoswitch, &hot_swap_support, &tachometer_pulses_per_rotation_predictive_fail_polarity, &peak_capacity, &hold_up_time, &voltage_1, &voltage_2, &total_combined_wattage, &predictive_fail_tachometer_lower_threshold) < 0) { fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: %s", ipmi_fru_ctx_errormsg (fru_ctx)); } if (overall_capacity > (int)INT_MAX) { fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: overall_capacity exceeds expected range: %u", overall_capacity); } ipmi_dev->overall_capacity = (int)overall_capacity; /* Voltages are in mV! */ ipmi_dev->input_minvoltage = low_end_input_voltage_range_1 / 1000; ipmi_dev->input_maxvoltage = high_end_input_voltage_range_1 / 1000; if (low_end_input_frequency_range > (int)INT_MAX) { fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: low_end_input_frequency_range exceeds expected range: %u", low_end_input_frequency_range); } if (high_end_input_frequency_range > (int)INT_MAX) { fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: high_end_input_frequency_range exceeds expected range: %u", high_end_input_frequency_range); } ipmi_dev->input_minfreq = (int)low_end_input_frequency_range; ipmi_dev->input_maxfreq = (int)high_end_input_frequency_range; if (voltage_1 > (int)UINT8_MAX) { fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: voltage_1 code exceeds expected range: %u", voltage_1); } ipmi_dev->voltage = libfreeipmi_get_voltage((uint8_t)voltage_1); upsdebugx(1, "libfreeipmi_get_psu_info() retrieved successfully"); return (0); } /* Get specific PSU information from IPMI FRU */ static int libfreeipmi_get_board_info (const void *areabuf, uint8_t area_length, IPMIDevice_t *ipmi_dev) { uint8_t language_code; uint32_t mfg_date_time; ipmi_fru_field_t board_manufacturer; ipmi_fru_field_t board_product_name; ipmi_fru_field_t board_serial_number; ipmi_fru_field_t board_part_number; ipmi_fru_field_t board_fru_file_id; ipmi_fru_field_t board_custom_fields[IPMI_FRU_CUSTOM_FIELDS]; const char *string = NULL; time_t timetmp; struct tm mfg_date_time_tm; char mfg_date_time_buf[IPMI_FRU_STR_BUFLEN + 1]; upsdebugx(1, "entering libfreeipmi_get_board_info()"); /* clear fields */ memset (&board_manufacturer, '\0', sizeof (ipmi_fru_field_t)); memset (&board_product_name, '\0', sizeof (ipmi_fru_field_t)); memset (&board_serial_number, '\0', sizeof (ipmi_fru_field_t)); memset (&board_fru_file_id, '\0', sizeof (ipmi_fru_field_t)); memset (&board_custom_fields[0], '\0', sizeof (ipmi_fru_field_t) * IPMI_FRU_CUSTOM_FIELDS); /* parse FRU buffer */ if (ipmi_fru_board_info_area (fru_ctx, areabuf, area_length, &language_code, &mfg_date_time, &board_manufacturer, &board_product_name, &board_serial_number, &board_part_number, &board_fru_file_id, board_custom_fields, IPMI_FRU_CUSTOM_FIELDS) < 0) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "ipmi_fru_board_info_area: %s", ipmi_fru_ctx_errormsg (fru_ctx)); } if (IPMI_FRU_LANGUAGE_CODE_VALID (language_code)) { upsdebugx (5, "FRU Board Language: %s", ipmi_fru_language_codes[language_code]); } else { upsdebugx (5, "FRU Board Language Code: %02Xh", language_code); } /* Posix says individual calls need not clear/set all portions of * 'struct tm', thus passing 'struct tm' between functions could * have issues. So we need to memset */ memset (&mfg_date_time_tm, '\0', sizeof (struct tm)); /* Without a standard TIME_MAX, signedness may suffer; * but we can at least check the number should fit */ #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic push #endif #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic ignored "-Wunreachable-code" #endif #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunreachable-code" #endif /* Stay ahead of possible redefinitions... */ if (sizeof(mfg_date_time) > sizeof(timetmp)) { libfreeipmi_cleanup(); fatalx(EXIT_FAILURE, "libfreeipmi_get_board_info: mfg_date_time type too large to process into a time_t"); } /* NOTE: Code until the end of method would also be "unreachable" * for compilers or static analyzers that care about this, if the * sizeof() check above fails on some architecture; build warnings * should expose that so we look for a fix - so do not just blindly * move the closing pragmas to end of method ;) */ #ifdef __clang__ #pragma clang diagnostic pop #endif #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE #pragma GCC diagnostic pop #endif timetmp = (time_t)mfg_date_time; localtime_r (&timetmp, &mfg_date_time_tm); memset (mfg_date_time_buf, '\0', IPMI_FRU_STR_BUFLEN + 1); strftime (mfg_date_time_buf, IPMI_FRU_STR_BUFLEN, "%D - %T", &mfg_date_time_tm); /* Store values */ ipmi_dev->date = xstrdup(mfg_date_time_buf); upsdebugx(2, "FRU Board Manufacturing Date/Time: %s", ipmi_dev->date); if ((string = libfreeipmi_getfield (language_code, &board_manufacturer)) != NULL) ipmi_dev->manufacturer = xstrdup(string); else ipmi_dev->manufacturer = xstrdup("Generic IPMI manufacturer"); if ((string = libfreeipmi_getfield (language_code, &board_product_name)) != NULL) ipmi_dev->product = xstrdup(string); else ipmi_dev->product = xstrdup("Generic PSU"); if ((string = libfreeipmi_getfield (language_code, &board_serial_number)) != NULL) ipmi_dev->serial = xstrdup(string); else ipmi_dev->serial = NULL; if ((string = libfreeipmi_getfield (language_code, &board_part_number)) != NULL) ipmi_dev->part = xstrdup(string); else ipmi_dev->part = NULL; return (0); } /* Get the sensors list & values, specific to the given FRU ID * Return -1 on error, or the number of sensors found otherwise */ static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev) { uint8_t sdr_record[IPMI_SDR_MAX_RECORD_LENGTH]; uint8_t record_type, logical_physical_fru_device, logical_fru_device_device_slave_address; uint8_t tmp_entity_id, tmp_entity_instance; int sdr_record_len; uint16_t record_count; int found_device_id = 0; uint16_t record_id; uint8_t entity_id = 0, entity_instance = 0; int i; if (ipmi_ctx == NULL) return (-1); /* Clear the sensors list */ ipmi_dev->sensors_count = 0; memset(ipmi_dev->sensors_id_list, 0, sizeof(ipmi_dev->sensors_id_list)); if (!(sdr_ctx = ipmi_sdr_ctx_create ())) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_ctx_create()"); } #ifndef HAVE_FREEIPMI_11X_12X if (!(sdr_parse_ctx = ipmi_sdr_parse_ctx_create ())) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_parse_ctx_create()"); } #endif if (ipmi_sdr_cache_open (sdr_ctx, ipmi_ctx, CACHE_LOCATION) < 0) { if (ipmi_sdr_ctx_errnum (sdr_ctx) != IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_open: %s", ipmi_sdr_ctx_errormsg (sdr_ctx)); } } if (ipmi_sdr_ctx_errnum (sdr_ctx) == IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) { if (ipmi_sdr_cache_create (sdr_ctx, ipmi_ctx, CACHE_LOCATION, NUT_IPMI_SDR_CACHE_DEFAULTS, NULL, NULL) < 0) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_create: %s", ipmi_sdr_ctx_errormsg (sdr_ctx)); } if (ipmi_sdr_cache_open (sdr_ctx, ipmi_ctx, CACHE_LOCATION) < 0) { if (ipmi_sdr_ctx_errnum (sdr_ctx) != IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_open: %s", ipmi_sdr_ctx_errormsg (sdr_ctx)); } } } if (ipmi_sdr_cache_record_count (sdr_ctx, &record_count) < 0) { fprintf (stderr, "ipmi_sdr_cache_record_count: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } upsdebugx(3, "Found %i records in SDR cache", record_count); for (i = 0; i < record_count; i++, ipmi_sdr_cache_next (sdr_ctx)) { memset (sdr_record, '\0', IPMI_SDR_MAX_RECORD_LENGTH); if ((sdr_record_len = ipmi_sdr_cache_record_read (sdr_ctx, sdr_record, IPMI_SDR_MAX_RECORD_LENGTH)) < 0) { fprintf (stderr, "ipmi_sdr_cache_record_read: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } if (ipmi_sdr_parse_record_id_and_type (SDR_PARSE_CTX, sdr_record, (unsigned int)sdr_record_len, NULL, &record_type) < 0) { fprintf (stderr, "ipmi_sdr_parse_record_id_and_type: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } upsdebugx (5, "Checking record %i (/%i)", i, record_count); if (record_type != IPMI_SDR_FORMAT_FRU_DEVICE_LOCATOR_RECORD) { upsdebugx(1, "=======> not device locator (%i)!!", record_type); continue; } if (ipmi_sdr_parse_fru_device_locator_parameters (SDR_PARSE_CTX, sdr_record, (unsigned int)sdr_record_len, NULL, &logical_fru_device_device_slave_address, NULL, NULL, &logical_physical_fru_device, NULL) < 0) { fprintf (stderr, "ipmi_sdr_parse_fru_device_locator_parameters: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } upsdebugx(2, "Checking device %i/%i", logical_physical_fru_device, logical_fru_device_device_slave_address); if (logical_physical_fru_device && logical_fru_device_device_slave_address == ipmi_dev->ipmi_id) { found_device_id++; if (ipmi_sdr_parse_fru_entity_id_and_instance (SDR_PARSE_CTX, sdr_record, (unsigned int)sdr_record_len, &entity_id, &entity_instance) < 0) { fprintf (stderr, "ipmi_sdr_parse_fru_entity_id_and_instance: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } break; } } if (!found_device_id) { fprintf (stderr, "Couldn't find device id %d\n", ipmi_dev->ipmi_id); goto cleanup; } else upsdebugx(1, "Found device id %d", ipmi_dev->ipmi_id); if (ipmi_sdr_cache_first (sdr_ctx) < 0) { fprintf (stderr, "ipmi_sdr_cache_first: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } for (i = 0; i < record_count; i++, ipmi_sdr_cache_next (sdr_ctx)) { /* uint8_t sdr_record[IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH]; uint8_t record_type, tmp_entity_id, tmp_entity_instance; int sdr_record_len; */ memset (sdr_record, '\0', IPMI_SDR_MAX_RECORD_LENGTH); if ((sdr_record_len = ipmi_sdr_cache_record_read (sdr_ctx, sdr_record, IPMI_SDR_MAX_RECORD_LENGTH)) < 0) { fprintf (stderr, "ipmi_sdr_cache_record_read: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } if (ipmi_sdr_parse_record_id_and_type (SDR_PARSE_CTX, sdr_record, (unsigned int)sdr_record_len, &record_id, &record_type) < 0) { fprintf (stderr, "ipmi_sdr_parse_record_id_and_type: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } upsdebugx (5, "Checking record %i (/%i)", record_id, record_count); if (record_type != IPMI_SDR_FORMAT_FULL_SENSOR_RECORD && record_type != IPMI_SDR_FORMAT_COMPACT_SENSOR_RECORD && record_type != IPMI_SDR_FORMAT_EVENT_ONLY_RECORD) { continue; } if (ipmi_sdr_parse_entity_id_instance_type (SDR_PARSE_CTX, sdr_record, (unsigned int)sdr_record_len, &tmp_entity_id, &tmp_entity_instance, NULL) < 0) { fprintf (stderr, "ipmi_sdr_parse_entity_instance_type: %s\n", ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } if (tmp_entity_id == entity_id && tmp_entity_instance == entity_instance) { upsdebugx (1, "Found record id = %u for device id %u", record_id, ipmi_dev->ipmi_id); /* Add it to the tracked list */ ipmi_dev->sensors_id_list[ipmi_dev->sensors_count] = record_id; ipmi_dev->sensors_count++; } } cleanup: /* Cleanup */ if (sdr_ctx) { ipmi_sdr_ctx_destroy (sdr_ctx); } #ifndef HAVE_FREEIPMI_11X_12X if (sdr_parse_ctx) { ipmi_sdr_parse_ctx_destroy (sdr_parse_ctx); } #endif /* HAVE_FREEIPMI_11X_12X */ if (ipmi_dev->sensors_count > INT_MAX) { upsdebugx(1, "%s: Found %i sensors which is too many", __func__, ipmi_dev->sensors_count); } return (int)ipmi_dev->sensors_count; } /* => Nominal conditions Record ID, Sensor Name, Sensor Number, Sensor Type, Sensor State, Sensor Reading, Sensor Units, Sensor Event/Reading Type Code, Sensor Event Bitmask, Sensor Event String 52, Presence, 84, Entity Presence, Nominal, N/A, N/A, 6Fh, 1h, 'Entity Present' 57, Status, 100, Power Supply, Nominal, N/A, N/A, 6Fh, 1h, 'Presence detected' 116, Current, 148, Current, Nominal, 0.20, A, 1h, C0h, 'OK' 118, Voltage, 150, Voltage, Nominal, 236.00, V, 1h, C0h, 'OK' => Power failure conditions Record ID, Sensor Name, Sensor Number, Sensor Type, Sensor State, Sensor Reading, Sensor Units, Sensor Event/Reading Type Code, Sensor Event Bitmask, Sensor Event String 52, Presence, 84, Entity Presence, Nominal, N/A, N/A, 6Fh, 1h, 'Entity Present' 57, Status, 100, Power Supply, Critical, N/A, N/A, 6Fh, 9h, 'Presence detected' 'Power Supply input lost (AC/DC)' => PSU removed Record ID, Sensor Name, Sensor Number, Sensor Type, Sensor State, Sensor Reading, Sensor Units, Sensor Event/Reading Type Code, Sensor Event Bitmask, Sensor Event String 52, Presence, 84, Entity Presence, Critical, N/A, N/A, 6Fh, 2h, 'Entity Absent' 57, Status, 100, Power Supply, Critical, N/A, N/A, 6Fh, 8h, 'Power Supply input lost (AC/DC)' */ int nut_ipmi_monitoring_init() { int errnum; if (ipmi_monitoring_init (0, &errnum) < 0) { upsdebugx (1, "ipmi_monitoring_init() error: %s", ipmi_monitoring_ctx_strerror (errnum)); return -1; } if (!(mon_ctx = ipmi_monitoring_ctx_create ())) { upsdebugx (1, "ipmi_monitoring_ctx_create() failed"); return -1; } #if HAVE_FREEIPMI_MONITORING /* FIXME: replace "/tmp" by a proper place, using mkdtemp() or similar */ if (ipmi_monitoring_ctx_sdr_cache_directory (mon_ctx, "/tmp") < 0) { upsdebugx (1, "ipmi_monitoring_ctx_sdr_cache_directory() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); return -1; } if (ipmi_monitoring_ctx_sensor_config_file (mon_ctx, NULL) < 0) { upsdebugx (1, "ipmi_monitoring_ctx_sensor_config_file() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); return -1; } #endif /* HAVE_FREEIPMI_MONITORING */ return 0; } int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) { int retval = -1; #if HAVE_FREEIPMI_MONITORING /* It seems we don't need more! */ unsigned int sensor_reading_flags = IPMI_MONITORING_SENSOR_READING_FLAGS_IGNORE_NON_INTERPRETABLE_SENSORS; int sensor_count, i, str_count; int psu_status = PSU_STATUS_UNKNOWN; if (mon_ctx == NULL) { upsdebugx (1, "Monitoring context not initialized!"); return -1; } /* Monitor only the list of sensors found previously */ if ((sensor_count = ipmi_monitoring_sensor_readings_by_record_id (mon_ctx, NULL, /* hostname is NULL for In-band communication */ NULL, /* FIXME: needed? ipmi_config */ sensor_reading_flags, ipmi_dev->sensors_id_list, ipmi_dev->sensors_count, NULL, NULL)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_readings_by_record_id() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); return -1; } upsdebugx (1, "nut_ipmi_get_sensors_status: %i sensors to check", sensor_count); for (i = 0; i < sensor_count; i++, ipmi_monitoring_sensor_iterator_next (mon_ctx)) { int record_id, sensor_type; int sensor_bitmask_type = -1; /* int sensor_reading_type, sensor_state; */ char **sensor_bitmask_strings = NULL; void *sensor_reading = NULL; if ((record_id = ipmi_monitoring_sensor_read_record_id (mon_ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_record_id() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); continue; } if ((sensor_type = ipmi_monitoring_sensor_read_sensor_type (mon_ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_type() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); continue; } upsdebugx (1, "checking sensor #%i, type %i", record_id, sensor_type); /* should we consider this for ALARM? * IPMI_MONITORING_STATE_NOMINAL * IPMI_MONITORING_STATE_WARNING * IPMI_MONITORING_STATE_CRITICAL * if ((sensor_state = ipmi_monitoring_sensor_read_sensor_state (mon_ctx)) < 0) * ... */ if ((sensor_reading = ipmi_monitoring_sensor_read_sensor_reading (mon_ctx)) == NULL) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_reading() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); } /* This can be needed to interpret sensor_reading format! if ((sensor_reading_type = ipmi_monitoring_sensor_read_sensor_reading_type (ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_reading_type() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); } */ if ((sensor_bitmask_type = ipmi_monitoring_sensor_read_sensor_bitmask_type (mon_ctx)) < 0) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_bitmask_type() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); continue; } if ((sensor_bitmask_strings = ipmi_monitoring_sensor_read_sensor_bitmask_strings (mon_ctx)) == NULL) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_bitmask_strings() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); continue; } /* Only the few possibly interesting sensors are considered */ switch (sensor_type) { case IPMI_MONITORING_SENSOR_TYPE_TEMPERATURE: ipmi_dev->temperature = *((double *)sensor_reading); upsdebugx (3, "Temperature: %.2f", *((double *)sensor_reading)); dstate_setinfo("ambient.temperature", "%.2f", *((double *)sensor_reading)); retval = 0; break; case IPMI_MONITORING_SENSOR_TYPE_VOLTAGE: ipmi_dev->voltage = *((double *)sensor_reading); upsdebugx (3, "Voltage: %.2f", *((double *)sensor_reading)); dstate_setinfo("input.voltage", "%.2f", *((double *)sensor_reading)); retval = 0; break; case IPMI_MONITORING_SENSOR_TYPE_CURRENT: ipmi_dev->input_current = *((double *)sensor_reading); upsdebugx (3, "Current: %.2f", *((double *)sensor_reading)); dstate_setinfo("input.current", "%.2f", *((double *)sensor_reading)); retval = 0; break; case IPMI_MONITORING_SENSOR_TYPE_POWER_SUPPLY: /* Possible values: * 'Presence detected' * 'Power Supply input lost (AC/DC)' => maps to status:OFF */ upsdebugx (3, "Power Supply: status string"); if (sensor_bitmask_type == IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN) { upsdebugx(3, "No status string"); } str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); if (!strncmp("Power Supply input lost (AC/DC)", sensor_bitmask_strings[str_count], strlen("Power Supply input lost (AC/DC)"))) { /* Don't override PSU absence! */ if (psu_status != PSU_ABSENT) { psu_status = PSU_POWER_FAILURE; /* = status OFF */ } } str_count++; } break; case IPMI_MONITORING_SENSOR_TYPE_ENTITY_PRESENCE: /* Possible values: * 'Entity Present' => maps to status:OL * 'Entity Absent' (PSU has been removed!) => declare staleness */ upsdebugx (3, "Entity Presence: status string"); if (sensor_bitmask_type == IPMI_MONITORING_SENSOR_BITMASK_TYPE_UNKNOWN) { upsdebugx(3, "No status string"); } str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); if (!strncmp("Entity Present", sensor_bitmask_strings[str_count], strlen("Entity Present"))) { psu_status = PSU_PRESENT; } else if (!strncmp("Entity Absent", sensor_bitmask_strings[str_count], strlen("Entity Absent"))) { psu_status = PSU_ABSENT; } str_count++; } break; /* Not sure of the values of these, so get as much as possible... */ case IPMI_MONITORING_SENSOR_TYPE_POWER_UNIT: upsdebugx (3, "Power Unit: status string"); str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); str_count++; } break; case IPMI_MONITORING_SENSOR_TYPE_SYSTEM_ACPI_POWER_STATE: upsdebugx (3, "System ACPI Power State: status string"); str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); str_count++; } break; case IPMI_MONITORING_SENSOR_TYPE_BATTERY: upsdebugx (3, "Battery: status string"); str_count = 0; while (sensor_bitmask_strings[str_count]) { upsdebugx (3, "\t'%s'", sensor_bitmask_strings[str_count]); str_count++; } break; } } /* Process status if needed */ if (psu_status != PSU_STATUS_UNKNOWN) { status_init(); switch (psu_status) { case PSU_PRESENT: status_set("OL"); retval = 0; break; case PSU_ABSENT: status_set("OFF"); /* Declare stale */ retval = -1; break; case PSU_POWER_FAILURE: status_set("OFF"); retval = 0; break; } status_commit(); } #endif /* HAVE_FREEIPMI_MONITORING */ return retval; } /* --chassis-control=CONTROL Control the chassis. This command provides power-up, power-down, and reset control. Supported values: POWER-DOWN, POWER-UP, POWER-CYCLE, HARD-RESET, DIAGNOS‐ TIC-INTERRUPT, SOFT-SHUTDOWN. */