From 98a238406ff5765cc6db3e61ba787c27144dad1d Mon Sep 17 00:00:00 2001 From: Matthew Barth Date: Mon, 8 Aug 2016 15:26:29 -0500 Subject: Switch C++ source files to .cpp extension Change-Id: I495186b4afd58ec1e5d9d5c6472394f96541d17d Signed-off-by: Matthew Barth --- ipmid.cpp | 752 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 752 insertions(+) create mode 100644 ipmid.cpp (limited to 'ipmid.cpp') diff --git a/ipmid.cpp b/ipmid.cpp new file mode 100644 index 0000000..7848afe --- /dev/null +++ b/ipmid.cpp @@ -0,0 +1,752 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ipmid.H" +#include +#include +#include +#include "sensorhandler.h" +#include +#include +#include +#include + +sd_bus *bus = NULL; +sd_bus_slot *ipmid_slot = NULL; + +// Initialise restricted mode to true +bool restricted_mode = true; + +FILE *ipmiio, *ipmidbus, *ipmicmddetails; + +void print_usage(void) { + fprintf(stderr, "Options: [-d mask]\n"); + fprintf(stderr, " mask : 0x01 - Print ipmi packets\n"); + fprintf(stderr, " mask : 0x02 - Print DBUS operations\n"); + fprintf(stderr, " mask : 0x04 - Print ipmi command details\n"); + fprintf(stderr, " mask : 0xFF - Print all trace\n"); +} + +// Host settings in DBUS +constexpr char settings_host_bus[] = "org.openbmc.settings.Host"; +constexpr char settings_host_object[] = "/org/openbmc/settings/host0"; +constexpr char settings_host_intf[] = "org.freedesktop.DBus.Properties"; + +const char * DBUS_INTF = "org.openbmc.HostIpmi"; + +const char * FILTER = "type='signal',interface='org.openbmc.HostIpmi',member='ReceivedMessage'"; +constexpr char RESTRICTED_MODE_FILTER[] = "type='signal',interface='org.freedesktop.DBus.Properties',path='/org/openbmc/settings/host0'"; + +typedef std::pair ipmi_fn_cmd_t; +typedef std::pair ipmi_fn_context_t; + +// Global data structure that contains the IPMI command handler's registrations. +std::map g_ipmid_router_map; + +// IPMI Spec, shared Reservation ID. +unsigned short g_sel_reserve = 0xFFFF; + +unsigned short get_sel_reserve_id(void) +{ + return g_sel_reserve; +} + +#ifndef HEXDUMP_COLS +#define HEXDUMP_COLS 16 +#endif + +void hexdump(FILE *s, void *mem, size_t len) +{ + unsigned int i, j; + + for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++) + { + /* print offset */ + if(i % HEXDUMP_COLS == 0) + { + fprintf(s,"0x%06x: ", i); + } + + /* print hex data */ + if(i < len) + { + fprintf(s,"%02x ", 0xFF & ((char*)mem)[i]); + } + else /* end of block, just aligning for ASCII dump */ + { + fprintf(s," "); + } + + /* print ASCII dump */ + if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1)) + { + for(j = i - (HEXDUMP_COLS - 1); j <= i; j++) + { + if(j >= len) /* end of block, not really printing */ + { + fputc(' ', s); + } + else if(isprint(((char*)mem)[j])) /* printable char */ + { + fputc(0xFF & ((char*)mem)[j], s); + } + else /* other char */ + { + fputc('.',s); + } + } + fputc('\n',s); + } + } +} + + +// Method that gets called by shared libraries to get their command handlers registered +void ipmi_register_callback(ipmi_netfn_t netfn, ipmi_cmd_t cmd, + ipmi_context_t context, ipmid_callback_t handler) +{ + // Pack NetFn and Command in one. + auto netfn_and_cmd = std::make_pair(netfn, cmd); + + // Pack Function handler and Data in another. + auto handler_and_context = std::make_pair(handler, context); + + // Check if the registration has already been made.. + auto iter = g_ipmid_router_map.find(netfn_and_cmd); + if(iter != g_ipmid_router_map.end()) + { + fprintf(stderr,"ERROR : Duplicate registration for NetFn [0x%X], Cmd:[0x%X]\n",netfn, cmd); + } + else + { + // This is a fresh registration.. Add it to the map. + g_ipmid_router_map.emplace(netfn_and_cmd, handler_and_context); + } + + return; +} + +// Looks at the map and calls corresponding handler functions. +ipmi_ret_t ipmi_netfn_router(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request, + ipmi_response_t response, ipmi_data_len_t data_len) +{ + // return from the Command handlers. + ipmi_ret_t rc = IPMI_CC_INVALID; + + // If restricted mode is true and command is not whitelisted, don't + // execute the command + if(restricted_mode) + { + if (!std::binary_search(whitelist.cbegin(), whitelist.cend(), + std::make_pair(netfn, cmd))) + { + printf("Net function:[0x%X], Command:[0x%X] is not whitelisted\n", + netfn, cmd); + rc = IPMI_CC_INSUFFICIENT_PRIVILEGE; + memcpy(response, &rc, IPMI_CC_LEN); + *data_len = IPMI_CC_LEN; + return rc; + } + } + + // Walk the map that has the registered handlers and invoke the approprite + // handlers for matching commands. + auto iter = g_ipmid_router_map.find(std::make_pair(netfn, cmd)); + if(iter == g_ipmid_router_map.end()) + { + fprintf(stderr, "No registered handlers for NetFn:[0x%X], Cmd:[0x%X]" + " trying Wilcard implementation \n",netfn, cmd); + + // Now that we did not find any specific [NetFn,Cmd], tuple, check for + // NetFn, WildCard command present. + iter = g_ipmid_router_map.find(std::make_pair(netfn, IPMI_CMD_WILDCARD)); + if(iter == g_ipmid_router_map.end()) + { + fprintf(stderr, "No Registered handlers for NetFn:[0x%X],Cmd:[0x%X]\n",netfn, IPMI_CMD_WILDCARD); + + // Respond with a 0xC1 + memcpy(response, &rc, IPMI_CC_LEN); + *data_len = IPMI_CC_LEN; + return rc; + } + } + +#ifdef __IPMI_DEBUG__ + // We have either a perfect match -OR- a wild card atleast, + printf("Calling Net function:[0x%X], Command:[0x%X]\n", netfn, cmd); +#endif + + // Extract the map data onto appropriate containers + auto handler_and_context = iter->second; + + // Creating a pointer type casted to char* to make sure we advance 1 byte + // when we advance pointer to next's address. advancing void * would not + // make sense. + char *respo = &((char *)response)[IPMI_CC_LEN]; + + // Response message from the plugin goes into a byte post the base response + rc = (handler_and_context.first) (netfn, cmd, request, respo, + data_len, handler_and_context.second); + + // Now copy the return code that we got from handler and pack it in first + // byte. + memcpy(response, &rc, IPMI_CC_LEN); + + // Data length is now actual data + completion code. + *data_len = *data_len + IPMI_CC_LEN; + + return rc; +} + + + + +static int send_ipmi_message(sd_bus_message *req, unsigned char seq, unsigned char netfn, unsigned char lun, unsigned char cmd, unsigned char cc, unsigned char *buf, unsigned char len) { + + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *reply = NULL, *m=NULL; + const char *dest, *path; + int r, pty; + + dest = sd_bus_message_get_sender(req); + path = sd_bus_message_get_path(req); + + r = sd_bus_message_new_method_call(bus,&m,dest,path,DBUS_INTF,"sendMessage"); + if (r < 0) { + fprintf(stderr, "Failed to add the method object: %s\n", strerror(-r)); + return -1; + } + + + // Responses in IPMI require a bit set. So there ya go... + netfn |= 0x01; + + + // Add the bytes needed for the methods to be called + r = sd_bus_message_append(m, "yyyyy", seq, netfn, lun, cmd, cc); + if (r < 0) { + fprintf(stderr, "Failed add the netfn and others : %s\n", strerror(-r)); + goto final; + } + + r = sd_bus_message_append_array(m, 'y', buf, len); + if (r < 0) { + fprintf(stderr, "Failed to add the string of response bytes: %s\n", strerror(-r)); + goto final; + } + + + + // Call the IPMI responder on the bus so the message can be sent to the CEC + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + fprintf(stderr, "Failed to call the method: %s\n", strerror(-r)); + fprintf(stderr, "Dest: %s, Path: %s\n", dest, path); + goto final; + } + + r = sd_bus_message_read(reply, "x", &pty); + if (r < 0) { + fprintf(stderr, "Failed to get a rc from the method: %s\n", strerror(-r)); + } + +final: + sd_bus_error_free(&error); + m = sd_bus_message_unref(m); + reply = sd_bus_message_unref(reply); + + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} + +void cache_restricted_mode() +{ + sd_bus *bus = ipmid_get_sd_bus_connection(); + sd_bus_message *reply = NULL; + sd_bus_error error = SD_BUS_ERROR_NULL; + int rc = 0; + + rc = sd_bus_call_method(bus, + settings_host_bus, + settings_host_object, + settings_host_intf, + "Get", + &error, + &reply, + "ss", + settings_host_bus, + "restricted_mode"); + if(rc < 0) + { + fprintf(stderr, "Failed sd_bus_call_method method for restricted mode: %s\n", + strerror(-rc)); + goto cleanup; + } + + rc = sd_bus_message_read(reply, "v", "b", &restricted_mode); + if(rc < 0) + { + fprintf(stderr, "Failed to parse response message for restricted mode: %s\n", + strerror(-rc)); + // Fail-safe to restricted mode + restricted_mode = true; + } + + printf("Restricted mode = %d\n", restricted_mode); + +cleanup: + sd_bus_error_free(&error); + reply = sd_bus_message_unref(reply); +} + +static int handle_restricted_mode_change(sd_bus_message *m, void *user_data, + sd_bus_error *ret_error) +{ + cache_restricted_mode(); + return 0; +} + +static int handle_ipmi_command(sd_bus_message *m, void *user_data, sd_bus_error + *ret_error) { + int r = 0; + unsigned char sequence, netfn, lun, cmd; + const void *request; + size_t sz; + size_t resplen =MAX_IPMI_BUFFER; + unsigned char response[MAX_IPMI_BUFFER]; + + memset(response, 0, MAX_IPMI_BUFFER); + + r = sd_bus_message_read(m, "yyyy", &sequence, &netfn, &lun, &cmd); + if (r < 0) { + fprintf(stderr, "Failed to parse signal message: %s\n", strerror(-r)); + return -1; + } + + r = sd_bus_message_read_array(m, 'y', &request, &sz ); + if (r < 0) { + fprintf(stderr, "Failed to parse signal message: %s\n", strerror(-r)); + return -1; + } + + fprintf(ipmiio, "IPMI Incoming: Seq 0x%02x, NetFn 0x%02x, CMD: 0x%02x \n", sequence, netfn, cmd); + hexdump(ipmiio, (void*)request, sz); + + // Allow the length field to be used for both input and output of the + // ipmi call + resplen = sz; + + // Now that we have parsed the entire byte array from the caller + // we can call the ipmi router to do the work... + r = ipmi_netfn_router(netfn, cmd, (void *)request, (void *)response, &resplen); + if(r != 0) + { + fprintf(stderr,"ERROR:[0x%X] handling NetFn:[0x%X], Cmd:[0x%X]\n",r, netfn, cmd); + + if(r < 0) { + response[0] = IPMI_CC_UNSPECIFIED_ERROR; + } + } + + fprintf(ipmiio, "IPMI Response:\n"); + hexdump(ipmiio, (void*)response, resplen); + + // Send the response buffer from the ipmi command + r = send_ipmi_message(m, sequence, netfn, lun, cmd, response[0], + ((unsigned char *)response) + 1, resplen - 1); + if (r < 0) { + fprintf(stderr, "Failed to send the response message\n"); + return -1; + } + + + return 0; +} + + +//---------------------------------------------------------------------- +// handler_select +// Select all the files ending with with .so. in the given diretcory +// @d: dirent structure containing the file name +//---------------------------------------------------------------------- +int handler_select(const struct dirent *entry) +{ + // To hold ".so" from entry->d_name; + char dname_copy[4] = {0}; + + // We want to avoid checking for everything and isolate to the ones having + // .so.* or .so in them. + // Check for versioned libraries .so.* + if(strstr(entry->d_name, IPMI_PLUGIN_SONAME_EXTN)) + { + return 1; + } + // Check for non versioned libraries .so + else if(strstr(entry->d_name, IPMI_PLUGIN_EXTN)) + { + // It is possible that .so could be anywhere in the string but unlikely + // But being careful here. Get the base address of the string, move + // until end and come back 3 steps and that gets what we need. + strcpy(dname_copy, (entry->d_name + strlen(entry->d_name)-strlen(IPMI_PLUGIN_EXTN))); + if(strcmp(dname_copy, IPMI_PLUGIN_EXTN) == 0) + { + return 1; + } + } + return 0; +} + +// This will do a dlopen of every .so in ipmi_lib_path and will dlopen everything so that they will +// register a callback handler +void ipmi_register_callback_handlers(const char* ipmi_lib_path) +{ + // For walking the ipmi_lib_path + struct dirent **handler_list; + int num_handlers = 0; + + // This is used to check and abort if someone tries to register a bad one. + void *lib_handler = NULL; + + if(ipmi_lib_path == NULL) + { + fprintf(stderr,"ERROR; No handlers to be registered for ipmi.. Aborting\n"); + assert(0); + } + else + { + // 1: Open ipmi_lib_path. Its usually "/usr/lib/phosphor-host-ipmid" + // 2: Scan the directory for the files that end with .so + // 3: For each one of them, just do a 'dlopen' so that they register + // the handlers for callback routines. + + std::string handler_fqdn = ipmi_lib_path; + + // Append a "/" since we need to add the name of the .so. If there is + // already a .so, adding one more is not any harm. + handler_fqdn += "/"; + + num_handlers = scandir(ipmi_lib_path, &handler_list, handler_select, alphasort); + if (num_handlers < 0) + return; + + while(num_handlers--) + { + handler_fqdn = ipmi_lib_path; + handler_fqdn += handler_list[num_handlers]->d_name; + printf("Registering handler:[%s]\n",handler_fqdn.c_str()); + + lib_handler = dlopen(handler_fqdn.c_str(), RTLD_NOW); + + if(lib_handler == NULL) + { + fprintf(stderr,"ERROR opening [%s]: %s\n", + handler_fqdn.c_str(), dlerror()); + } + // Wipe the memory allocated for this particular entry. + free(handler_list[num_handlers]); + } + + // Done with all registration. + free(handler_list); + } + + // TODO : What to be done on the memory that is given by dlopen ?. + return; +} + +sd_bus *ipmid_get_sd_bus_connection(void) { + return bus; +} + +sd_bus_slot *ipmid_get_sd_bus_slot(void) { + return ipmid_slot; +} + +int main(int argc, char *argv[]) +{ + int r; + unsigned long tvalue; + int c; + + + + // This file and subsequient switch is for turning on levels + // of trace + ipmicmddetails = ipmiio = ipmidbus = fopen("/dev/null", "w"); + + while ((c = getopt (argc, argv, "h:d:")) != -1) + switch (c) { + case 'd': + tvalue = strtoul(optarg, NULL, 16); + if (1&tvalue) { + ipmiio = stdout; + } + if (2&tvalue) { + ipmidbus = stdout; + } + if (4&tvalue) { + ipmicmddetails = stdout; + } + break; + case 'h': + case '?': + print_usage(); + return 1; + } + + + /* Connect to system bus */ + r = sd_bus_open_system(&bus); + if (r < 0) { + fprintf(stderr, "Failed to connect to system bus: %s\n", + strerror(-r)); + goto finish; + } + + // Register all the handlers that provider implementation to IPMI commands. + ipmi_register_callback_handlers(HOST_IPMI_LIB_PATH); + + // Watch for BT messages + r = sd_bus_add_match(bus, &ipmid_slot, FILTER, handle_ipmi_command, NULL); + if (r < 0) { + fprintf(stderr, "Failed: sd_bus_add_match: %s : %s\n", strerror(-r), FILTER); + goto finish; + } + + // Wait for changes on Restricted mode + r = sd_bus_add_match(bus, &ipmid_slot, RESTRICTED_MODE_FILTER, handle_restricted_mode_change, NULL); + if (r < 0) { + fprintf(stderr, "Failed: sd_bus_add_match: %s : %s\n", strerror(-r), RESTRICTED_MODE_FILTER); + goto finish; + } + + // Initialise restricted mode + cache_restricted_mode(); + + for (;;) { + /* Process requests */ + r = sd_bus_process(bus, NULL); + if (r < 0) { + fprintf(stderr, "Failed to process bus: %s\n", strerror(-r)); + goto finish; + } + if (r > 0) { + continue; + } + + r = sd_bus_wait(bus, (uint64_t) - 1); + if (r < 0) { + fprintf(stderr, "Failed to wait on bus: %s\n", strerror(-r)); + goto finish; + } + } + +finish: + sd_bus_slot_unref(ipmid_slot); + sd_bus_unref(bus); + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + +} + +// Use a lookup table to find the interface name of a specific sensor +// This will be used until an alternative is found. this is the first +// step for mapping IPMI +int find_interface_property_fru_type(dbus_interface_t *interface, const char *property_name, char *property_value) { + + char *str1; + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *reply = NULL, *m=NULL; + + + int r; + + r = sd_bus_message_new_method_call(bus,&m,interface->bus,interface->path,"org.freedesktop.DBus.Properties","Get"); + if (r < 0) { + fprintf(stderr, "Failed to create a method call: %s", strerror(-r)); + fprintf(stderr,"Bus: %s Path: %s Interface: %s \n", + interface->bus, interface->path, interface->interface); + goto final; + } + + r = sd_bus_message_append(m, "ss", "org.openbmc.InventoryItem", property_name); + if (r < 0) { + fprintf(stderr, "Failed to create a input parameter: %s", strerror(-r)); + fprintf(stderr,"Bus: %s Path: %s Interface: %s \n", + interface->bus, interface->path, interface->interface); + goto final; + } + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) { + fprintf(stderr, "Failed to call the method: %s", strerror(-r)); + goto final; + } + + r = sd_bus_message_read(reply, "v", "s", &str1) ; + if (r < 0) { + fprintf(stderr, "Failed to get a response: %s", strerror(-r)); + goto final; + } + + strcpy(property_value, str1); + +final: + + sd_bus_error_free(&error); + m = sd_bus_message_unref(m); + reply = sd_bus_message_unref(reply); + + return r; +} + + +// Use a lookup table to find the interface name of a specific sensor +// This will be used until an alternative is found. this is the first +// step for mapping IPMI +int find_openbmc_path(const char *type, const uint8_t num, dbus_interface_t *interface) { + char *busname = NULL; + const char *iface = "org.openbmc.managers.System"; + const char *objname = "/org/openbmc/managers/System"; + char *str1 = NULL, *str2, *str3; + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *reply = NULL; + + + int r; + r = mapper_get_service(bus, objname, &busname); + if (r < 0) { + fprintf(stderr, "Failed to get system manager busname: %s\n", strerror(-r)); + goto final; + } + + r = sd_bus_call_method(bus,busname,objname,iface, "getObjectFromByteId", + &error, &reply, "sy", type, num); + if (r < 0) { + fprintf(stderr, "Failed to create a method call: %s", strerror(-r)); + goto final; + } + + r = sd_bus_message_read(reply, "(ss)", &str2, &str3); + if (r < 0) { + fprintf(stderr, "Failed to get a response: %s", strerror(-r)); + goto final; + } + + r = mapper_get_service(bus, str2, &str1); + if (r < 0) { + fprintf(stderr, "Failed to get item busname: %s\n", strerror(-r)); + goto final; + } + + strncpy(interface->bus, str1, MAX_DBUS_PATH); + strncpy(interface->path, str2, MAX_DBUS_PATH); + strncpy(interface->interface, str3, MAX_DBUS_PATH); + + interface->sensornumber = num; + +final: + + sd_bus_error_free(&error); + reply = sd_bus_message_unref(reply); + free(busname); + free(str1); + + return r; +} + + +///////////////////////////////////////////////////////////////////// +// +// Routines used by ipmi commands wanting to interact on the dbus +// +///////////////////////////////////////////////////////////////////// +int set_sensor_dbus_state_s(uint8_t number, const char *method, const char *value) { + + + dbus_interface_t a; + int r; + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *m=NULL; + + fprintf(ipmidbus, "Attempting to set a dbus Variant Sensor 0x%02x via %s with a value of %s\n", + number, method, value); + + r = find_openbmc_path("SENSOR", number, &a); + + if (r < 0) { + fprintf(stderr, "Failed to find Sensor 0x%02x\n", number); + return 0; + } + + r = sd_bus_message_new_method_call(bus,&m,a.bus,a.path,a.interface,method); + if (r < 0) { + fprintf(stderr, "Failed to create a method call: %s", strerror(-r)); + goto final; + } + + r = sd_bus_message_append(m, "v", "s", value); + if (r < 0) { + fprintf(stderr, "Failed to create a input parameter: %s", strerror(-r)); + goto final; + } + + + r = sd_bus_call(bus, m, 0, &error, NULL); + if (r < 0) { + fprintf(stderr, "Failed to call the method: %s", strerror(-r)); + } + +final: + sd_bus_error_free(&error); + m = sd_bus_message_unref(m); + + return 0; +} +int set_sensor_dbus_state_y(uint8_t number, const char *method, const uint8_t value) { + + + dbus_interface_t a; + int r; + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *m=NULL; + + fprintf(ipmidbus, "Attempting to set a dbus Variant Sensor 0x%02x via %s with a value of 0x%02x\n", + number, method, value); + + r = find_openbmc_path("SENSOR", number, &a); + + if (r < 0) { + fprintf(stderr, "Failed to find Sensor 0x%02x\n", number); + return 0; + } + + r = sd_bus_message_new_method_call(bus,&m,a.bus,a.path,a.interface,method); + if (r < 0) { + fprintf(stderr, "Failed to create a method call: %s", strerror(-r)); + goto final; + } + + r = sd_bus_message_append(m, "v", "i", value); + if (r < 0) { + fprintf(stderr, "Failed to create a input parameter: %s", strerror(-r)); + goto final; + } + + + r = sd_bus_call(bus, m, 0, &error, NULL); + if (r < 0) { + fprintf(stderr, "12 Failed to call the method: %s", strerror(-r)); + } + +final: + sd_bus_error_free(&error); + m = sd_bus_message_unref(m); + + return 0; +} -- cgit v1.2.1