blob: 833371049cdf3ffcd55c0653df3670a36cc0b709 [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>
7#include <gio/gio.h>
8#include <string.h>
9#include <stdlib.h>
10#include <map>
11#include "ipmid.H"
12
13// Channel that is used for OpenBMC Barreleye
14const char * DBUS_NAME = "org.openbmc.HostIpmi";
15const char * OBJ_NAME = "/org/openbmc/HostIpmi/1";
16
17typedef std::pair<ipmi_netfn_t, ipmi_cmd_t> ipmi_fn_cmd_t;
18typedef std::pair<ipmid_callback_t, ipmi_context_t> ipmi_fn_context_t;
19
20// Global data structure that contains the IPMI command handler's registrations.
21std::map<ipmi_fn_cmd_t, ipmi_fn_context_t> g_ipmid_router_map;
22
23// Method that gets called by shared libraries to get their command handlers registered
24void ipmi_register_callback(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
25 ipmi_context_t context, ipmid_callback_t handler)
26{
27 // Pack NetFn and Command in one.
28 auto netfn_and_cmd = std::make_pair(netfn, cmd);
29
30 // Pack Function handler and Data in another.
31 auto handler_and_context = std::make_pair(handler, context);
32
33 // Check if the registration has already been made..
34 auto iter = g_ipmid_router_map.find(netfn_and_cmd);
35 if(iter != g_ipmid_router_map.end())
36 {
37 fprintf(stderr,"ERROR : Duplicate registration for NetFn [0x%X], Cmd:[0x%X]\n",netfn, cmd);
38 }
39 else
40 {
41 // This is a fresh registration.. Add it to the map.
42 g_ipmid_router_map.emplace(netfn_and_cmd, handler_and_context);
43 }
44
45 return;
46}
47
48// Looks at the map and calls corresponding handler functions.
49ipmi_ret_t ipmi_netfn_router(ipmi_netfn_t netfn, ipmi_cmd_t cmd, ipmi_request_t request,
50 ipmi_response_t response, ipmi_data_len_t data_len)
51{
52 // return from the Command handlers.
53 ipmi_ret_t rc = IPMI_CC_INVALID;
54
55 // Walk the map that has the registered handlers and invoke the approprite
56 // handlers for matching commands.
57 auto iter = g_ipmid_router_map.find(std::make_pair(netfn, cmd));
58 if(iter == g_ipmid_router_map.end())
59 {
60 printf("No registered handlers for NetFn:[0x%X], Cmd:[0x%X]"
61 " trying Wilcard implementation \n",netfn, cmd);
62
63 // Now that we did not find any specific [NetFn,Cmd], tuple, check for
64 // NetFn, WildCard command present.
65 iter = g_ipmid_router_map.find(std::make_pair(netfn, IPMI_CMD_WILDCARD));
66 if(iter == g_ipmid_router_map.end())
67 {
68 printf("No Registered handlers for NetFn:[0x%X],Cmd:[0x%X]\n",netfn, IPMI_CMD_WILDCARD);
69
70 // Respond with a 0xC1
71 memcpy(response, &rc, IPMI_CC_LEN);
72 *data_len = IPMI_CC_LEN;
73 return rc;
74 }
75 }
76
77#ifdef __IPMI_DEBUG__
78 // We have either a perfect match -OR- a wild card atleast,
79 printf("Calling Net function:[0x%X], Command:[0x%X]\n", netfn, cmd);
80#endif
81
82 // Extract the map data onto appropriate containers
83 auto handler_and_context = iter->second;
84
85 // Creating a pointer type casted to char* to make sure we advance 1 byte
86 // when we advance pointer to next's address. advancing void * would not
87 // make sense.
88 char *respo = &((char *)response)[IPMI_CC_LEN];
89
90 // Response message from the plugin goes into a byte post the base response
91 rc = (handler_and_context.first) (netfn, cmd, request, respo,
92 data_len, handler_and_context.second);
93
94 // Now copy the return code that we got from handler and pack it in first
95 // byte.
96 memcpy(response, &rc, IPMI_CC_LEN);
97
98 // Data length is now actual data + completion code.
99 *data_len = *data_len + IPMI_CC_LEN;
100
101 return rc;
102}
103
104// This gets called by Glib loop on seeing a Dbus signal
105static void handle_ipmi_command(GDBusProxy *proxy,
106 gchar *sender_name,
107 gchar *signal_name,
108 GVariant *parameters,
109 gpointer user_data)
110{
111 // Used to re-construct the message into IPMI specific ones.
112 guchar *parameters_str;
113 unsigned char sequence, netfn, cmd;
114
115 // Request and Response buffer.
116 unsigned char request[MAX_IPMI_BUFFER] = {0};
117 unsigned char response[MAX_IPMI_BUFFER] = {0};
118
119 size_t msg_length = 0;
120 size_t data_len = 0;
121
122 // Response from Net Function Router
123 ipmi_ret_t rc = 0;
124
125 // Variables to marshall and unmarshall the messages.
126 GVariantIter *iter;
127 guchar data;
128 GVariant *dbus_response;
129 GVariantBuilder *builder;
130
131 // Pretty print the message that came on Dbus
132 parameters_str = (guchar *) g_variant_print (parameters, TRUE);
133 printf ("*** Received Signal: %s: %s :%s\n",
134 signal_name,
135 sender_name,
136 parameters_str);
137
138 // Consume the data pattern "<bYte><bYte><bYte><Array_of_bYtes>
139 g_variant_get(parameters, "(yyyay)", &sequence, &netfn, &cmd, &iter);
140
141 printf("Sequence: %x\n",sequence );
142 printf("Netfn : %x\n",netfn );
143 printf("Cmd : %x\n",cmd );
144
145 // Further break down the GVariant byte array
146 while (g_variant_iter_loop (iter, "y", &data))
147 {
148 request[msg_length++] = data;
149 }
150
151 // Done with consuming data.
152 g_free (parameters_str);
153
154 // Needed to see what we get back from the handlers.
155 data_len = msg_length;
156
157 // Now that we have parsed the entire byte array from the caller
158 // we can call the ipmi router to do the work...
159 rc = ipmi_netfn_router(netfn, cmd, (void *)request, (void *)response, &data_len);
160 if(rc == 0)
161 {
162 printf("SUCCESS handling NetFn:[0x%X], Cmd:[0x%X]\n",netfn, cmd);
163 }
164 else
165 {
166 fprintf(stderr,"ERROR:[0x%X] handling NetFn:[0x%X], Cmd:[0x%X]\n",rc, netfn, cmd);
167 }
168
169 // Now build a response Gvariant package
170 // This example may help
171 // http://stackoverflow.com/questions/22937588/how-to-send-byte-array-over-gdbus
172
173 printf("Bytes to return\n");
174 // hexdump(response,data_len);
175
176 // Now we need to put the data as "Array Of Bytes" as we got them.
177 builder = g_variant_builder_new (G_VARIANT_TYPE ("ay"));
178
179 for (uint out_data = 0; out_data < data_len; out_data++)
180 {
181 g_variant_builder_add (builder, "y", response[out_data]);
182 }
183
184 dbus_response = g_variant_new ("(yyyay)", sequence, netfn+1, cmd, builder);
185
186 // Variant builder is no longer needed.
187 g_variant_builder_unref (builder);
188
189 parameters_str = (guchar *) g_variant_print (dbus_response, TRUE);
190 printf (" *** Response Signal :%s\n", parameters_str);
191
192 // Done packing the data.
193 g_free (parameters_str);
194
195 // NOW send the respone message in the Dbus calling "sendMessage" interface.
196 g_dbus_proxy_call_sync (proxy,
197 "sendMessage",
198 dbus_response,
199 G_DBUS_CALL_FLAGS_NONE,
200 -1,
201 NULL,
202 NULL);
203}
204
205//----------------------------------------------------------------------
206// handler_select
207// Select all the files ending with with .so. in the given diretcory
208// @d: dirent structure containing the file name
209//----------------------------------------------------------------------
210int handler_select(const struct dirent *entry)
211{
212 // To hold ".so" from entry->d_name;
213 char dname_copy[4] = {0};
214
215 // We want to avoid checking for everything and isolate to the ones having
216 // .so in them.
217 if(strstr(entry->d_name, IPMI_PLUGIN_EXTN))
218 {
219 // It is possible that .so could be anywhere in the string but unlikely
220 // But being careful here. Get the base address of the string, move
221 // until end and come back 3 steps and that gets what we need.
222 strcpy(dname_copy, (entry->d_name + strlen(entry->d_name)-strlen(IPMI_PLUGIN_EXTN)));
223 if(strcmp(dname_copy, IPMI_PLUGIN_EXTN) == 0)
224 {
225 return 1;
226 }
227 }
228 return 0;
229}
230
231// This will do a dlopen of every .so in ipmi_lib_path and will dlopen everything so that they will
232// register a callback handler
233void ipmi_register_callback_handlers(const char* ipmi_lib_path)
234{
235 // For walking the ipmi_lib_path
236 struct dirent **handler_list;
237 int num_handlers = 0;
238
239 // This is used to check and abort if someone tries to register a bad one.
240 void *lib_handler = NULL;
241
242 if(ipmi_lib_path == NULL)
243 {
244 fprintf(stderr,"ERROR; No handlers to be registered for ipmi.. Aborting\n");
245 assert(0);
246 }
247 else
248 {
249 // 1: Open ipmi_lib_path. Its usually "/usr/lib/phosphor-host-ipmid"
250 // 2: Scan the directory for the files that end with .so
251 // 3: For each one of them, just do a 'dlopen' so that they register
252 // the handlers for callback routines.
253
254 std::string handler_fqdn = ipmi_lib_path;
255
256 // Append a "/" since we need to add the name of the .so. If there is
257 // already a .so, adding one more is not any harm.
258 handler_fqdn += "/";
259
260 num_handlers = scandir(ipmi_lib_path, &handler_list, handler_select, alphasort);
261 while(num_handlers--)
262 {
263 printf("Registering handler:[%s]\n",handler_list[num_handlers]->d_name);
264
265 handler_fqdn += handler_list[num_handlers]->d_name;
266 lib_handler = dlopen(handler_fqdn.c_str(), RTLD_NOW);
267 if(lib_handler == NULL)
268 {
269 fprintf(stderr,"ERROR opening:[%s]\n",handler_list[num_handlers]->d_name);
270 dlerror();
271 }
272 // Wipe the memory allocated for this particular entry.
273 free(handler_list[num_handlers]);
274 }
275 // Done with all registration.
276 free(handler_list);
277 }
278
279 // TODO : What to be done on the memory that is given by dlopen ?.
280 return;
281}
282
283int main(int argc, char *argv[])
284{
285 // Register all the handlers that provider implementation to IPMI commands.
286 ipmi_register_callback_handlers(HOST_IPMI_LIB_PATH);
287
288#ifdef __IPMI_DEBUG__
289 printf("Registered Function handlers:\n");
290
291 // Print the registered handlers and their arguments.
292 for(auto& iter : g_ipmid_router_map)
293 {
294 ipmi_fn_cmd_t fn_and_cmd = iter.first;
295 printf("NETFN:[0x%X], cmd[0x%X]\n", fn_and_cmd.first, fn_and_cmd.second);
296 }
297#endif
298
299 // Infrastructure that will wait for IPMi Dbus messages and will call
300 // into the corresponding IPMI providers.
301 GDBusProxy *proxy;
302 GMainLoop *loop;
303
304 loop = g_main_loop_new (NULL, FALSE);
305
306 // Proxy to use GDbus for OpenBMC channel.
307 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
308 G_DBUS_PROXY_FLAGS_NONE,
309 NULL, /* GDBusInterfaceInfo */
310 DBUS_NAME,
311 OBJ_NAME,
312 DBUS_NAME,
313 NULL,
314 NULL);
315
316 // On receiving the Dbus Signal, handle_ipmi_command gets invoked.
317 g_signal_connect (proxy,
318 "g-signal",
319 G_CALLBACK (handle_ipmi_command),
320 NULL);
321
322 // This will not return unless we return false from the upmi_handler function.
323 g_main_loop_run (loop);
324
325 return 0;
326}