blob: d0dfba8b8258062d0327fdfae1356503e5e02a1f [file] [log] [blame]
vishwabmcba0bd5f2015-09-30 16:50:23 +05301#include <stdio.h>
2#include <dlfcn.h>
3#include <iostream>
4#include <unistd.h>
5#include <assert.h>
6#include <dirent.h>
Chris Austen0ba649e2015-10-13 12:28:13 -05007#include <systemd/sd-bus.h>
vishwabmcba0bd5f2015-09-30 16:50:23 +05308#include <string.h>
9#include <stdlib.h>
10#include <map>
11#include "ipmid.H"
Chris Austen0ba649e2015-10-13 12:28:13 -050012#include <sys/time.h>
13#include <errno.h>
14
15
16sd_bus *bus = NULL;
vishwabmcba0bd5f2015-09-30 16:50:23 +053017
18// Channel that is used for OpenBMC Barreleye
19const char * DBUS_NAME = "org.openbmc.HostIpmi";
20const char * OBJ_NAME = "/org/openbmc/HostIpmi/1";
21
Chris Austen0ba649e2015-10-13 12:28:13 -050022const char * FILTER = "type='signal',sender='org.openbmc.HostIpmi',member='ReceivedMessage'";
23
24
vishwabmcba0bd5f2015-09-30 16:50:23 +053025typedef std::pair<ipmi_netfn_t, ipmi_cmd_t> ipmi_fn_cmd_t;
26typedef std::pair<ipmid_callback_t, ipmi_context_t> ipmi_fn_context_t;
27
28// Global data structure that contains the IPMI command handler's registrations.
29std::map<ipmi_fn_cmd_t, ipmi_fn_context_t> g_ipmid_router_map;
30
Chris Austen0ba649e2015-10-13 12:28:13 -050031
32
33#ifndef HEXDUMP_COLS
34#define HEXDUMP_COLS 16
35#endif
36
37void hexdump(void *mem, size_t len)
38{
39 unsigned int i, j;
40
41 for(i = 0; i < len + ((len % HEXDUMP_COLS) ? (HEXDUMP_COLS - len % HEXDUMP_COLS) : 0); i++)
42 {
43 /* print offset */
44 if(i % HEXDUMP_COLS == 0)
45 {
46 printf("0x%06x: ", i);
47 }
48
49 /* print hex data */
50 if(i < len)
51 {
52 printf("%02x ", 0xFF & ((char*)mem)[i]);
53 }
54 else /* end of block, just aligning for ASCII dump */
55 {
56 printf(" ");
57 }
58
59 /* print ASCII dump */
60 if(i % HEXDUMP_COLS == (HEXDUMP_COLS - 1))
61 {
62 for(j = i - (HEXDUMP_COLS - 1); j <= i; j++)
63 {
64 if(j >= len) /* end of block, not really printing */
65 {
66 putchar(' ');
67 }
68 else if(isprint(((char*)mem)[j])) /* printable char */
69 {
70 putchar(0xFF & ((char*)mem)[j]);
71 }
72 else /* other char */
73 {
74 putchar('.');
75 }
76 }
77 putchar('\n');
78 }
79 }
80}
81
82
vishwabmcba0bd5f2015-09-30 16:50:23 +053083// Method that gets called by shared libraries to get their command handlers registered
84void ipmi_register_callback(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
85 ipmi_context_t context, ipmid_callback_t handler)
86{
87 // Pack NetFn and Command in one.
88 auto netfn_and_cmd = std::make_pair(netfn, cmd);
89
90 // Pack Function handler and Data in another.
91 auto handler_and_context = std::make_pair(handler, context);
92
93 // Check if the registration has already been made..
94 auto iter = g_ipmid_router_map.find(netfn_and_cmd);
95 if(iter != g_ipmid_router_map.end())
96 {
97 fprintf(stderr,"ERROR : Duplicate registration for NetFn [0x%X], Cmd:[0x%X]\n",netfn, cmd);
98 }
99 else
100 {
101 // This is a fresh registration.. Add it to the map.
102 g_ipmid_router_map.emplace(netfn_and_cmd, handler_and_context);
103 }
104
105 return;
106}
107
108// Looks at the map and calls corresponding handler functions.
109ipmi_ret_t ipmi_netfn_router(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
110 ipmi_response_t response, ipmi_data_len_t data_len)
111{
112 // return from the Command handlers.
113 ipmi_ret_t rc = IPMI_CC_INVALID;
114
115 // Walk the map that has the registered handlers and invoke the approprite
116 // handlers for matching commands.
117 auto iter = g_ipmid_router_map.find(std::make_pair(netfn, cmd));
118 if(iter == g_ipmid_router_map.end())
119 {
120 printf("No registered handlers for NetFn:[0x%X], Cmd:[0x%X]"
121 " trying Wilcard implementation \n",netfn, cmd);
122
123 // Now that we did not find any specific [NetFn,Cmd], tuple, check for
124 // NetFn, WildCard command present.
125 iter = g_ipmid_router_map.find(std::make_pair(netfn, IPMI_CMD_WILDCARD));
126 if(iter == g_ipmid_router_map.end())
127 {
128 printf("No Registered handlers for NetFn:[0x%X],Cmd:[0x%X]\n",netfn, IPMI_CMD_WILDCARD);
129
130 // Respond with a 0xC1
131 memcpy(response, &rc, IPMI_CC_LEN);
132 *data_len = IPMI_CC_LEN;
133 return rc;
134 }
135 }
136
137#ifdef __IPMI_DEBUG__
138 // We have either a perfect match -OR- a wild card atleast,
139 printf("Calling Net function:[0x%X], Command:[0x%X]\n", netfn, cmd);
140#endif
141
142 // Extract the map data onto appropriate containers
143 auto handler_and_context = iter->second;
144
145 // Creating a pointer type casted to char* to make sure we advance 1 byte
146 // when we advance pointer to next's address. advancing void * would not
147 // make sense.
148 char *respo = &((char *)response)[IPMI_CC_LEN];
149
150 // Response message from the plugin goes into a byte post the base response
151 rc = (handler_and_context.first) (netfn, cmd, request, respo,
152 data_len, handler_and_context.second);
153
154 // Now copy the return code that we got from handler and pack it in first
155 // byte.
156 memcpy(response, &rc, IPMI_CC_LEN);
157
158 // Data length is now actual data + completion code.
159 *data_len = *data_len + IPMI_CC_LEN;
160
161 return rc;
162}
163
vishwabmcba0bd5f2015-09-30 16:50:23 +0530164
vishwabmcba0bd5f2015-09-30 16:50:23 +0530165
vishwabmcba0bd5f2015-09-30 16:50:23 +0530166
Chris Austen0ba649e2015-10-13 12:28:13 -0500167static int send_ipmi_message(unsigned char seq, unsigned char netfn, unsigned char cmd, unsigned char *buf, unsigned char len) {
vishwabmcba0bd5f2015-09-30 16:50:23 +0530168
Chris Austen0ba649e2015-10-13 12:28:13 -0500169 sd_bus_error error = SD_BUS_ERROR_NULL;
170 sd_bus_message *reply = NULL, *m=NULL;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530171
vishwabmcba0bd5f2015-09-30 16:50:23 +0530172
Chris Austen0ba649e2015-10-13 12:28:13 -0500173 const char *path;
174 int r, pty;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530175
vishwabmcba0bd5f2015-09-30 16:50:23 +0530176
Chris Austen0ba649e2015-10-13 12:28:13 -0500177 r = sd_bus_message_new_method_call(bus,&m,DBUS_NAME,OBJ_NAME,DBUS_NAME,"sendMessage");
178 if (r < 0) {
179 fprintf(stderr, "Failed to add the method object: %s\n", strerror(-r));
180 return -1;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530181 }
182
vishwabmcba0bd5f2015-09-30 16:50:23 +0530183
Chris Austenabfb5e82015-10-13 12:29:24 -0500184 // Responses in IPMI require a bit set. So there ya go...
185 netfn |= 0x04;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530186
Chris Austen0ba649e2015-10-13 12:28:13 -0500187
188 // Add the bytes needed for the methods to be called
189 r = sd_bus_message_append(m, "yyy", seq, netfn, cmd);
190 if (r < 0) {
191 fprintf(stderr, "Failed add the netfn and others : %s\n", strerror(-r));
192 return -1;
193 }
194
195 r = sd_bus_message_append_array(m, 'y', buf, len);
196 if (r < 0) {
197 fprintf(stderr, "Failed to add the string of response bytes: %s\n", strerror(-r));
198 return -1;
199 }
200
201
202
203 // Call the IPMI responder on the bus so the message can be sent to the CEC
204 r = sd_bus_call(bus, m, 0, &error, &reply);
205 if (r < 0) {
206 fprintf(stderr, "Failed to call the method: %s", strerror(-r));
207 return -1;
208 }
209
210 r = sd_bus_message_read(reply, "x", &pty);
211#ifdef __IPMI_DEBUG__
212 printf("RC from the ipmi dbus method :%d \n", pty);
213#endif
214 if (r < 0) {
215 fprintf(stderr, "Failed to get a rc from the method: %s\n", strerror(-r));
216
217 }
218
219
220 sd_bus_error_free(&error);
221 sd_bus_message_unref(m);
222
223
224#ifdef __IPMI_DEBUG__
225 printf("%d : %s\n", __LINE__, __PRETTY_FUNCTION__ );
226#endif
227 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
228
229}
230
231static int handle_ipmi_command(sd_bus_message *m, void *user_data, sd_bus_error
232 *ret_error) {
233 int r = 0;
234 const char *msg = NULL;
235 char sequence, netfn, cmd;
236 const void *request;
237 size_t sz;
238 size_t resplen =MAX_IPMI_BUFFER;
239 unsigned char response[MAX_IPMI_BUFFER];
240
241 printf(" *** Received Signal: ");
242
243 memset(response, 0, MAX_IPMI_BUFFER);
244
245 r = sd_bus_message_read(m, "yyy", &sequence, &netfn, &cmd);
246 if (r < 0) {
247 fprintf(stderr, "Failed to parse signal message: %s\n", strerror(-r));
248 return -1;
249 }
250
251 r = sd_bus_message_read_array(m, 'y', &request, &sz );
252 if (r < 0) {
253 fprintf(stderr, "Failed to parse signal message: %s\n", strerror(-r));
254 return -1;
255 }
256
257
258 printf("Seq 0x%02x, NetFn 0x%02x, CMD: 0x%02x \n", sequence, netfn, cmd);
259 hexdump((void*)request, sz);
260
261 // Allow the length field to be used for both input and output of the
262 // ipmi call
263 resplen = sz;
264
vishwabmcba0bd5f2015-09-30 16:50:23 +0530265 // Now that we have parsed the entire byte array from the caller
266 // we can call the ipmi router to do the work...
Chris Austen0ba649e2015-10-13 12:28:13 -0500267 r = ipmi_netfn_router(netfn, cmd, (void *)request, (void *)response, &resplen);
268 if(r != 0)
vishwabmcba0bd5f2015-09-30 16:50:23 +0530269 {
Chris Austen0ba649e2015-10-13 12:28:13 -0500270 fprintf(stderr,"ERROR:[0x%X] handling NetFn:[0x%X], Cmd:[0x%X]\n",r, netfn, cmd);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530271 }
272
Chris Austen0ba649e2015-10-13 12:28:13 -0500273 printf("Response...\n");
274 hexdump((void*)response, resplen);
vishwabmcba0bd5f2015-09-30 16:50:23 +0530275
Chris Austen0ba649e2015-10-13 12:28:13 -0500276 // Send the response buffer from the ipmi command
277 r = send_ipmi_message(sequence, netfn, cmd, response, resplen);
278 if (r < 0) {
279 fprintf(stderr, "Failed to send the response message\n");
280 return -1;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530281 }
282
vishwabmcba0bd5f2015-09-30 16:50:23 +0530283
Chris Austen0ba649e2015-10-13 12:28:13 -0500284 return 0;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530285}
286
Chris Austen0ba649e2015-10-13 12:28:13 -0500287
vishwabmcba0bd5f2015-09-30 16:50:23 +0530288//----------------------------------------------------------------------
289// handler_select
290// Select all the files ending with with .so. in the given diretcory
291// @d: dirent structure containing the file name
292//----------------------------------------------------------------------
293int handler_select(const struct dirent *entry)
294{
295 // To hold ".so" from entry->d_name;
296 char dname_copy[4] = {0};
297
298 // We want to avoid checking for everything and isolate to the ones having
299 // .so in them.
300 if(strstr(entry->d_name, IPMI_PLUGIN_EXTN))
301 {
302 // It is possible that .so could be anywhere in the string but unlikely
303 // But being careful here. Get the base address of the string, move
304 // until end and come back 3 steps and that gets what we need.
305 strcpy(dname_copy, (entry->d_name + strlen(entry->d_name)-strlen(IPMI_PLUGIN_EXTN)));
306 if(strcmp(dname_copy, IPMI_PLUGIN_EXTN) == 0)
307 {
308 return 1;
309 }
310 }
311 return 0;
312}
313
314// This will do a dlopen of every .so in ipmi_lib_path and will dlopen everything so that they will
315// register a callback handler
316void ipmi_register_callback_handlers(const char* ipmi_lib_path)
317{
318 // For walking the ipmi_lib_path
319 struct dirent **handler_list;
320 int num_handlers = 0;
321
322 // This is used to check and abort if someone tries to register a bad one.
323 void *lib_handler = NULL;
324
325 if(ipmi_lib_path == NULL)
326 {
327 fprintf(stderr,"ERROR; No handlers to be registered for ipmi.. Aborting\n");
328 assert(0);
329 }
330 else
331 {
332 // 1: Open ipmi_lib_path. Its usually "/usr/lib/phosphor-host-ipmid"
333 // 2: Scan the directory for the files that end with .so
334 // 3: For each one of them, just do a 'dlopen' so that they register
335 // the handlers for callback routines.
336
337 std::string handler_fqdn = ipmi_lib_path;
338
339 // Append a "/" since we need to add the name of the .so. If there is
340 // already a .so, adding one more is not any harm.
341 handler_fqdn += "/";
342
343 num_handlers = scandir(ipmi_lib_path, &handler_list, handler_select, alphasort);
344 while(num_handlers--)
345 {
Chris Austen54030262015-10-13 12:30:46 -0500346 handler_fqdn = ipmi_lib_path;
vishwabmcba0bd5f2015-09-30 16:50:23 +0530347 handler_fqdn += handler_list[num_handlers]->d_name;
Chris Austen54030262015-10-13 12:30:46 -0500348 printf("Registering handler:[%s]\n",handler_fqdn.c_str());
349
vishwabmcba0bd5f2015-09-30 16:50:23 +0530350 lib_handler = dlopen(handler_fqdn.c_str(), RTLD_NOW);
351 if(lib_handler == NULL)
352 {
Chris Austen54030262015-10-13 12:30:46 -0500353 fprintf(stderr,"ERROR opening:[%s]\n",handler_fqdn.c_str());
vishwabmcba0bd5f2015-09-30 16:50:23 +0530354 dlerror();
355 }
356 // Wipe the memory allocated for this particular entry.
357 free(handler_list[num_handlers]);
358 }
359 // Done with all registration.
360 free(handler_list);
361 }
362
363 // TODO : What to be done on the memory that is given by dlopen ?.
364 return;
365}
366
367int main(int argc, char *argv[])
368{
Chris Austen0ba649e2015-10-13 12:28:13 -0500369 sd_bus_slot *slot = NULL;
370 int r;
371 char *mode = NULL;
372
373
vishwabmcba0bd5f2015-09-30 16:50:23 +0530374 // Register all the handlers that provider implementation to IPMI commands.
375 ipmi_register_callback_handlers(HOST_IPMI_LIB_PATH);
376
377#ifdef __IPMI_DEBUG__
378 printf("Registered Function handlers:\n");
379
380 // Print the registered handlers and their arguments.
381 for(auto& iter : g_ipmid_router_map)
382 {
383 ipmi_fn_cmd_t fn_and_cmd = iter.first;
384 printf("NETFN:[0x%X], cmd[0x%X]\n", fn_and_cmd.first, fn_and_cmd.second);
385 }
386#endif
vishwabmcba0bd5f2015-09-30 16:50:23 +0530387
vishwabmcba0bd5f2015-09-30 16:50:23 +0530388
Chris Austen0ba649e2015-10-13 12:28:13 -0500389 /* Connect to system bus */
390 r = sd_bus_open_system(&bus);
391 if (r < 0) {
392 fprintf(stderr, "Failed to connect to system bus: %s\n",
393 strerror(-r));
394 goto finish;
395 }
vishwabmcba0bd5f2015-09-30 16:50:23 +0530396
Chris Austen0ba649e2015-10-13 12:28:13 -0500397 r = sd_bus_add_match(bus, &slot, FILTER, handle_ipmi_command, NULL);
398 if (r < 0) {
399 fprintf(stderr, "Failed: sd_bus_add_match: %s : %s\n", strerror(-r), FILTER);
400 goto finish;
401 }
vishwabmcba0bd5f2015-09-30 16:50:23 +0530402
vishwabmcba0bd5f2015-09-30 16:50:23 +0530403
Chris Austen0ba649e2015-10-13 12:28:13 -0500404 for (;;) {
405 /* Process requests */
406
407 r = sd_bus_process(bus, NULL);
408 if (r < 0) {
409 fprintf(stderr, "Failed to process bus: %s\n", strerror(-r));
410 goto finish;
411 }
412 if (r > 0) {
413 continue;
414 }
415
416 r = sd_bus_wait(bus, (uint64_t) - 1);
417 if (r < 0) {
418 fprintf(stderr, "Failed to wait on bus: %s\n", strerror(-r));
419 goto finish;
420 }
421 }
422
423finish:
424 sd_bus_slot_unref(slot);
425 sd_bus_unref(bus);
426 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
427
vishwabmcba0bd5f2015-09-30 16:50:23 +0530428}