blob: b6c6c0f92c0b9f448060f0c047c3e5974b7da78d [file] [log] [blame]
Vernon Mauerye7329c72018-10-08 12:05:16 -07001/**
2 * Copyright © 2018 Intel Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#pragma once
17#include <algorithm>
18#include <boost/asio/spawn.hpp>
19#include <boost/callable_traits.hpp>
20#include <cstdint>
21#include <exception>
Vernon Mauerye08fbff2019-04-03 09:19:34 -070022#include <ipmid/api-types.hpp>
Vernon Mauerye7329c72018-10-08 12:05:16 -070023#include <ipmid/message.hpp>
24#include <memory>
25#include <optional>
26#include <phosphor-logging/log.hpp>
27#include <tuple>
28#include <user_channel/channel_layer.hpp>
29#include <utility>
30
31#ifdef ALLOW_DEPRECATED_API
32#include <ipmid/api.h>
33
34#include <ipmid/oemrouter.hpp>
35#endif /* ALLOW_DEPRECATED_API */
36
37namespace ipmi
38{
39
40template <typename... Args>
41static inline message::Response::ptr
42 errorResponse(message::Request::ptr request, ipmi::Cc cc, Args&&... args)
43{
44 message::Response::ptr response = request->makeResponse();
Vernon Mauerybfe55a12019-03-08 13:48:40 -080045 response->cc = cc;
46 response->pack(args...);
Vernon Mauerye7329c72018-10-08 12:05:16 -070047 return response;
48}
49static inline message::Response::ptr
50 errorResponse(message::Request::ptr request, ipmi::Cc cc)
51{
52 message::Response::ptr response = request->makeResponse();
Vernon Mauerybfe55a12019-03-08 13:48:40 -080053 response->cc = cc;
Vernon Mauerye7329c72018-10-08 12:05:16 -070054 return response;
55}
56
57/**
58 * @brief Handler base class for dealing with IPMI request/response
59 *
60 * The subclasses are all templated so they can provide access to any type
61 * of command callback functions.
62 */
63class HandlerBase
64{
65 public:
66 using ptr = std::shared_ptr<HandlerBase>;
67
68 /** @brief wrap the call to the registered handler with the request
69 *
70 * This is called from the running queue context after it has already
71 * created a request object that contains all the information required to
72 * execute the ipmi command. This function will return the response object
73 * pointer that owns the response object that will ultimately get sent back
74 * to the requester.
75 *
76 * This is a non-virtual function wrapper to the virtualized executeCallback
77 * function that actually does the work. This is required because of how
78 * templates and virtualization work together.
79 *
80 * @param request a shared_ptr to a Request object
81 *
82 * @return a shared_ptr to a Response object
83 */
84 message::Response::ptr call(message::Request::ptr request)
85 {
86 return executeCallback(request);
87 }
88
89 private:
90 /** @brief call the registered handler with the request
91 *
92 * This is called from the running queue context after it has already
93 * created a request object that contains all the information required to
94 * execute the ipmi command. This function will return the response object
95 * pointer that owns the response object that will ultimately get sent back
96 * to the requester.
97 *
98 * @param request a shared_ptr to a Request object
99 *
100 * @return a shared_ptr to a Response object
101 */
102 virtual message::Response::ptr
103 executeCallback(message::Request::ptr request) = 0;
104};
105
106/**
107 * @brief Main IPMI handler class
108 *
109 * New IPMI handlers will resolve into this class, which will read the signature
110 * of the registering function, attempt to extract the appropriate arguments
111 * from a request, pass the arguments to the function, and then pack the
112 * response of the function back into an IPMI response.
113 */
114template <typename Handler>
115class IpmiHandler final : public HandlerBase
116{
117 public:
118 explicit IpmiHandler(Handler&& handler) :
119 handler_(std::forward<Handler>(handler))
120 {
121 }
122
123 private:
124 Handler handler_;
125
126 /** @brief call the registered handler with the request
127 *
128 * This is called from the running queue context after it has already
129 * created a request object that contains all the information required to
130 * execute the ipmi command. This function will return the response object
131 * pointer that owns the response object that will ultimately get sent back
132 * to the requester.
133 *
134 * Because this is the new variety of IPMI handler, this is the function
135 * that attempts to extract the requested parameters in order to pass them
136 * onto the callback function and then packages up the response into a plain
137 * old vector to pass back to the caller.
138 *
139 * @param request a shared_ptr to a Request object
140 *
141 * @return a shared_ptr to a Response object
142 */
143 message::Response::ptr
144 executeCallback(message::Request::ptr request) override
145 {
146 message::Response::ptr response = request->makeResponse();
147
148 using CallbackSig = boost::callable_traits::args_t<Handler>;
149 using InputArgsType = typename utility::DecayTuple<CallbackSig>::type;
150 using UnpackArgsType = typename utility::StripFirstArgs<
151 utility::NonIpmiArgsCount<InputArgsType>::size(),
152 InputArgsType>::type;
153 using ResultType = boost::callable_traits::return_type_t<Handler>;
154
155 UnpackArgsType unpackArgs;
William A. Kennington III51694c22019-04-24 01:44:44 -0700156 request->payload.trailingOk = false;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700157 ipmi::Cc unpackError = request->unpack(unpackArgs);
158 if (unpackError != ipmi::ccSuccess)
159 {
160 response->cc = unpackError;
161 return response;
162 }
163 /* callbacks can contain an optional first argument of one of:
164 * 1) boost::asio::yield_context
165 * 2) ipmi::Context::ptr
166 * 3) ipmi::message::Request::ptr
167 *
168 * If any of those is part of the callback signature as the first
169 * argument, it will automatically get packed into the parameter pack
170 * here.
171 *
172 * One more special optional argument is an ipmi::message::Payload.
173 * This argument can be in any position, though logically it makes the
174 * most sense if it is the last. If this class is included in the
175 * handler signature, it will allow for the handler to unpack optional
176 * parameters. For example, the Set LAN Configuration Parameters
177 * command takes variable length (and type) values for each of the LAN
178 * parameters. This means that the only fixed data is the channel and
179 * parameter selector. All the remaining data can be extracted using
180 * the Payload class and the unpack API available to the Payload class.
181 */
Vernon Mauerye7329c72018-10-08 12:05:16 -0700182 ResultType result;
183 try
184 {
William A. Kennington IIIf2fd17a2019-04-24 01:53:52 -0700185 std::optional<InputArgsType> inputArgs;
186 if constexpr (std::tuple_size<InputArgsType>::value > 0)
187 {
188 if constexpr (std::is_same<
189 std::tuple_element_t<0, InputArgsType>,
190 boost::asio::yield_context>::value)
191 {
192 inputArgs.emplace(std::tuple_cat(
James Feistcb09aa02019-09-06 13:41:59 -0700193 std::forward_as_tuple(request->ctx->yield),
William A. Kennington IIIf2fd17a2019-04-24 01:53:52 -0700194 std::move(unpackArgs)));
195 }
196 else if constexpr (std::is_same<
197 std::tuple_element_t<0, InputArgsType>,
198 ipmi::Context::ptr>::value)
199 {
200 inputArgs.emplace(
201 std::tuple_cat(std::forward_as_tuple(request->ctx),
202 std::move(unpackArgs)));
203 }
204 else if constexpr (std::is_same<
205 std::tuple_element_t<0, InputArgsType>,
206 ipmi::message::Request::ptr>::value)
207 {
208 inputArgs.emplace(std::tuple_cat(
209 std::forward_as_tuple(request), std::move(unpackArgs)));
210 }
211 else
212 {
213 // no special parameters were requested (but others were)
214 inputArgs.emplace(std::move(unpackArgs));
215 }
216 }
217 else
218 {
219 // no parameters were requested
220 inputArgs = std::move(unpackArgs);
221 }
222
Vernon Mauerye7329c72018-10-08 12:05:16 -0700223 // execute the registered callback function and get the
224 // ipmi::RspType<>
225 result = std::apply(handler_, *inputArgs);
226 }
227 catch (const std::exception& e)
228 {
229 phosphor::logging::log<phosphor::logging::level::ERR>(
230 "Handler failed to catch exception",
231 phosphor::logging::entry("EXCEPTION=%s", e.what()),
232 phosphor::logging::entry("NETFN=%x", request->ctx->netFn),
233 phosphor::logging::entry("CMD=%x", request->ctx->cmd));
234 return errorResponse(request, ccUnspecifiedError);
235 }
236 catch (...)
237 {
238 std::exception_ptr eptr;
239 try
240 {
241 eptr = std::current_exception();
242 if (eptr)
243 {
244 std::rethrow_exception(eptr);
245 }
246 }
247 catch (const std::exception& e)
248 {
249 phosphor::logging::log<phosphor::logging::level::ERR>(
250 "Handler failed to catch exception",
251 phosphor::logging::entry("EXCEPTION=%s", e.what()),
252 phosphor::logging::entry("NETFN=%x", request->ctx->netFn),
253 phosphor::logging::entry("CMD=%x", request->ctx->cmd));
254 return errorResponse(request, ccUnspecifiedError);
255 }
256 }
257
258 response->cc = std::get<0>(result);
259 auto payload = std::get<1>(result);
260 // check for optional payload
261 if (payload)
262 {
263 response->pack(*payload);
264 }
265 return response;
266 }
267};
268
269#ifdef ALLOW_DEPRECATED_API
Vernon Mauery48408b62019-08-22 14:41:50 -0700270static constexpr size_t maxLegacyBufferSize = 64 * 1024;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700271/**
272 * @brief Legacy IPMI handler class
273 *
274 * Legacy IPMI handlers will resolve into this class, which will behave the same
275 * way as the legacy IPMI queue, passing in a big buffer for the request and a
276 * big buffer for the response.
277 *
278 * As soon as all the handlers have been rewritten, this class will be marked as
279 * deprecated and eventually removed.
280 */
281template <>
282class IpmiHandler<ipmid_callback_t> final : public HandlerBase
283{
284 public:
Vernon Mauerybe376302019-03-21 13:02:05 -0700285 explicit IpmiHandler(const ipmid_callback_t& handler, void* ctx = nullptr) :
286 handler_(handler), handlerCtx(ctx)
Vernon Mauerye7329c72018-10-08 12:05:16 -0700287 {
288 }
289
290 private:
291 ipmid_callback_t handler_;
Vernon Mauerybe376302019-03-21 13:02:05 -0700292 void* handlerCtx;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700293
294 /** @brief call the registered handler with the request
295 *
296 * This is called from the running queue context after it has already
297 * created a request object that contains all the information required to
298 * execute the ipmi command. This function will return the response object
299 * pointer that owns the response object that will ultimately get sent back
300 * to the requester.
301 *
302 * Because this is the legacy variety of IPMI handler, this function does
303 * not really have to do much other than pass the payload to the callback
304 * and return response to the caller.
305 *
306 * @param request a shared_ptr to a Request object
307 *
308 * @return a shared_ptr to a Response object
309 */
310 message::Response::ptr
311 executeCallback(message::Request::ptr request) override
312 {
313 message::Response::ptr response = request->makeResponse();
Vernon Mauerye7329c72018-10-08 12:05:16 -0700314 // allocate a big response buffer here
Vernon Mauery48408b62019-08-22 14:41:50 -0700315 response->payload.resize(maxLegacyBufferSize);
Vernon Mauerye7329c72018-10-08 12:05:16 -0700316
William A. Kennington III5d06cc62019-04-25 02:10:55 -0700317 size_t len = request->payload.size() - request->payload.rawIndex;
Vernon Mauerye7329c72018-10-08 12:05:16 -0700318 Cc ccRet{ccSuccess};
319 try
320 {
William A. Kennington III5d06cc62019-04-25 02:10:55 -0700321 ccRet =
322 handler_(request->ctx->netFn, request->ctx->cmd,
323 request->payload.data() + request->payload.rawIndex,
324 response->payload.data(), &len, handlerCtx);
Vernon Mauerye7329c72018-10-08 12:05:16 -0700325 }
326 catch (const std::exception& e)
327 {
328 phosphor::logging::log<phosphor::logging::level::ERR>(
329 "Legacy Handler failed to catch exception",
330 phosphor::logging::entry("EXCEPTION=%s", e.what()),
331 phosphor::logging::entry("NETFN=%x", request->ctx->netFn),
332 phosphor::logging::entry("CMD=%x", request->ctx->cmd));
333 return errorResponse(request, ccUnspecifiedError);
334 }
335 catch (...)
336 {
337 std::exception_ptr eptr;
338 try
339 {
340 eptr = std::current_exception();
341 if (eptr)
342 {
343 std::rethrow_exception(eptr);
344 }
345 }
346 catch (const std::exception& e)
347 {
348 phosphor::logging::log<phosphor::logging::level::ERR>(
349 "Handler failed to catch exception",
350 phosphor::logging::entry("EXCEPTION=%s", e.what()),
351 phosphor::logging::entry("NETFN=%x", request->ctx->netFn),
352 phosphor::logging::entry("CMD=%x", request->ctx->cmd));
353 return errorResponse(request, ccUnspecifiedError);
354 }
355 }
356 response->cc = ccRet;
357 response->payload.resize(len);
358 return response;
359 }
360};
361
362/**
Vernon Maueryf984a012018-10-08 12:05:18 -0700363 * @brief Legacy IPMI OEM handler class
364 *
365 * Legacy IPMI OEM handlers will resolve into this class, which will behave the
366 * same way as the legacy IPMI queue, passing in a big buffer for the request
367 * and a big buffer for the response.
368 *
369 * As soon as all the handlers have been rewritten, this class will be marked as
370 * deprecated and eventually removed.
371 */
372template <>
373class IpmiHandler<oem::Handler> final : public HandlerBase
374{
375 public:
376 explicit IpmiHandler(const oem::Handler& handler) : handler_(handler)
377 {
378 }
379
380 private:
381 oem::Handler handler_;
382
383 /** @brief call the registered handler with the request
384 *
385 * This is called from the running queue context after it has already
386 * created a request object that contains all the information required to
387 * execute the ipmi command. This function will return the response object
388 * pointer that owns the response object that will ultimately get sent back
389 * to the requester.
390 *
391 * Because this is the legacy variety of IPMI handler, this function does
392 * not really have to do much other than pass the payload to the callback
393 * and return response to the caller.
394 *
395 * @param request a shared_ptr to a Request object
396 *
397 * @return a shared_ptr to a Response object
398 */
399 message::Response::ptr
400 executeCallback(message::Request::ptr request) override
401 {
402 message::Response::ptr response = request->makeResponse();
Vernon Maueryf984a012018-10-08 12:05:18 -0700403 // allocate a big response buffer here
Vernon Mauery48408b62019-08-22 14:41:50 -0700404 response->payload.resize(maxLegacyBufferSize);
Vernon Maueryf984a012018-10-08 12:05:18 -0700405
William A. Kennington III5d06cc62019-04-25 02:10:55 -0700406 size_t len = request->payload.size() - request->payload.rawIndex;
Vernon Maueryf984a012018-10-08 12:05:18 -0700407 Cc ccRet{ccSuccess};
408 try
409 {
William A. Kennington III5d06cc62019-04-25 02:10:55 -0700410 ccRet =
411 handler_(request->ctx->cmd,
412 request->payload.data() + request->payload.rawIndex,
413 response->payload.data(), &len);
Vernon Maueryf984a012018-10-08 12:05:18 -0700414 }
415 catch (const std::exception& e)
416 {
417 phosphor::logging::log<phosphor::logging::level::ERR>(
418 "Legacy OEM Handler failed to catch exception",
419 phosphor::logging::entry("EXCEPTION=%s", e.what()),
420 phosphor::logging::entry("NETFN=%x", request->ctx->netFn),
421 phosphor::logging::entry("CMD=%x", request->ctx->cmd));
422 return errorResponse(request, ccUnspecifiedError);
423 }
424 catch (...)
425 {
426 std::exception_ptr eptr;
427 try
428 {
429 eptr = std::current_exception();
430 if (eptr)
431 {
432 std::rethrow_exception(eptr);
433 }
434 }
435 catch (const std::exception& e)
436 {
437 phosphor::logging::log<phosphor::logging::level::ERR>(
438 "Handler failed to catch exception",
439 phosphor::logging::entry("EXCEPTION=%s", e.what()),
440 phosphor::logging::entry("NETFN=%x", request->ctx->netFn),
441 phosphor::logging::entry("CMD=%x", request->ctx->cmd));
442 return errorResponse(request, ccUnspecifiedError);
443 }
444 }
445 response->cc = ccRet;
446 response->payload.resize(len);
447 return response;
448 }
449};
450
451/**
Vernon Mauerye7329c72018-10-08 12:05:16 -0700452 * @brief create a legacy IPMI handler class and return a shared_ptr
453 *
454 * The queue uses a map of pointers to do the lookup. This function returns the
455 * shared_ptr that owns the Handler object.
456 *
457 * This is called internally via the ipmi_register_callback function.
458 *
459 * @param handler the function pointer to the callback
460 *
461 * @return A shared_ptr to the created handler object
462 */
Vernon Mauerybe376302019-03-21 13:02:05 -0700463inline auto makeLegacyHandler(const ipmid_callback_t& handler,
464 void* ctx = nullptr)
Vernon Mauerye7329c72018-10-08 12:05:16 -0700465{
Vernon Mauerybe376302019-03-21 13:02:05 -0700466 HandlerBase::ptr ptr(new IpmiHandler<ipmid_callback_t>(handler, ctx));
Vernon Mauerye7329c72018-10-08 12:05:16 -0700467 return ptr;
468}
469
Vernon Maueryf984a012018-10-08 12:05:18 -0700470/**
471 * @brief create a legacy IPMI OEM handler class and return a shared_ptr
472 *
473 * The queue uses a map of pointers to do the lookup. This function returns the
474 * shared_ptr that owns the Handler object.
475 *
476 * This is called internally via the Router::registerHandler method.
477 *
478 * @param handler the function pointer to the callback
479 *
480 * @return A shared_ptr to the created handler object
481 */
482inline auto makeLegacyHandler(oem::Handler&& handler)
483{
484 HandlerBase::ptr ptr(
485 new IpmiHandler<oem::Handler>(std::forward<oem::Handler>(handler)));
486 return ptr;
487}
Vernon Mauerye7329c72018-10-08 12:05:16 -0700488#endif // ALLOW_DEPRECATED_API
489
490/**
491 * @brief create an IPMI handler class and return a shared_ptr
492 *
493 * The queue uses a map of pointers to do the lookup. This function returns the
494 * shared_ptr that owns the Handler object.
495 *
496 * This is called internally via the ipmi::registerHandler function.
497 *
498 * @param handler the function pointer to the callback
499 *
500 * @return A shared_ptr to the created handler object
501 */
502template <typename Handler>
503inline auto makeHandler(Handler&& handler)
504{
505 HandlerBase::ptr ptr(
506 new IpmiHandler<Handler>(std::forward<Handler>(handler)));
507 return ptr;
508}
509
Vernon Mauerye08fbff2019-04-03 09:19:34 -0700510namespace impl
511{
512
513// IPMI command handler registration implementation
514bool registerHandler(int prio, NetFn netFn, Cmd cmd, Privilege priv,
515 ::ipmi::HandlerBase::ptr handler);
516bool registerGroupHandler(int prio, Group group, Cmd cmd, Privilege priv,
517 ::ipmi::HandlerBase::ptr handler);
518bool registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv,
519 ::ipmi::HandlerBase::ptr handler);
520
521} // namespace impl
522
523/**
524 * @brief main IPMI handler registration function
525 *
526 * This function should be used to register all new-style IPMI handler
527 * functions. This function just passes the callback to makeHandler, which
528 * creates a new wrapper object that will automatically extract the appropriate
529 * parameters for the callback function as well as pack up the response.
530 *
531 * @param prio - priority at which to register; see api.hpp
532 * @param netFn - the IPMI net function number to register
533 * @param cmd - the IPMI command number to register
534 * @param priv - the IPMI user privilige required for this command
535 * @param handler - the callback function that will handle this request
536 *
537 * @return bool - success of registering the handler
538 */
539template <typename Handler>
540bool registerHandler(int prio, NetFn netFn, Cmd cmd, Privilege priv,
541 Handler&& handler)
542{
543 auto h = ipmi::makeHandler(std::forward<Handler>(handler));
544 return impl::registerHandler(prio, netFn, cmd, priv, h);
545}
546
547/**
548 * @brief register a IPMI OEM group handler
549 *
550 * From IPMI 2.0 spec Network Function Codes Table (Row 2Ch):
551 * The first data byte position in requests and responses under this network
552 * function identifies the defining body that specifies command functionality.
553 * Software assumes that the command and completion code field positions will
554 * hold command and completion code values.
555 *
556 * The following values are used to identify the defining body:
557 * 00h PICMG - PCI Industrial Computer Manufacturer’s Group. (www.picmg.com)
558 * 01h DMTF Pre-OS Working Group ASF Specification (www.dmtf.org)
559 * 02h Server System Infrastructure (SSI) Forum (www.ssiforum.org)
560 * 03h VITA Standards Organization (VSO) (www.vita.com)
561 * DCh DCMI Specifications (www.intel.com/go/dcmi)
562 * all other Reserved
563 *
564 * When this network function is used, the ID for the defining body occupies
565 * the first data byte in a request, and the second data byte (following the
566 * completion code) in a response.
567 *
568 * @tparam Handler - implicitly specified callback function type
569 * @param prio - priority at which to register; see api.hpp
570 * @param netFn - the IPMI net function number to register
571 * @param cmd - the IPMI command number to register
572 * @param priv - the IPMI user privilige required for this command
573 * @param handler - the callback function that will handle this request
574 *
575 * @return bool - success of registering the handler
576 *
577 */
578template <typename Handler>
579void registerGroupHandler(int prio, Group group, Cmd cmd, Privilege priv,
580 Handler&& handler)
581{
582 auto h = ipmi::makeHandler(handler);
583 impl::registerGroupHandler(prio, group, cmd, priv, h);
584}
585
586/**
587 * @brief register a IPMI OEM IANA handler
588 *
589 * From IPMI spec Network Function Codes Table (Row 2Eh):
590 * The first three data bytes of requests and responses under this network
591 * function explicitly identify the OEM or non-IPMI group that specifies the
592 * command functionality. While the OEM or non-IPMI group defines the
593 * functional semantics for the cmd and remaining data fields, the cmd field
594 * is required to hold the same value in requests and responses for a given
595 * operation in order to be supported under the IPMI message handling and
596 * transport mechanisms.
597 *
598 * When this network function is used, the IANA Enterprise Number for the
599 * defining body occupies the first three data bytes in a request, and the
600 * first three data bytes following the completion code position in a
601 * response.
602 *
603 * @tparam Handler - implicitly specified callback function type
604 * @param prio - priority at which to register; see api.hpp
605 * @param netFn - the IPMI net function number to register
606 * @param cmd - the IPMI command number to register
607 * @param priv - the IPMI user privilige required for this command
608 * @param handler - the callback function that will handle this request
609 *
610 * @return bool - success of registering the handler
611 *
612 */
613template <typename Handler>
614void registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv,
615 Handler&& handler)
616{
617 auto h = ipmi::makeHandler(handler);
618 impl::registerOemHandler(prio, iana, cmd, priv, h);
619}
620
Vernon Mauerye7329c72018-10-08 12:05:16 -0700621} // namespace ipmi
Vernon Mauerye08fbff2019-04-03 09:19:34 -0700622
623#ifdef ALLOW_DEPRECATED_API
624/**
625 * @brief legacy IPMI handler registration function
626 *
627 * This function should be used to register all legacy IPMI handler
628 * functions. This function just behaves just as the legacy registration
629 * mechanism did, silently replacing any existing handler with a new one.
630 *
631 * @param netFn - the IPMI net function number to register
632 * @param cmd - the IPMI command number to register
633 * @param context - ignored
634 * @param handler - the callback function that will handle this request
635 * @param priv - the IPMI user privilige required for this command
636 */
637// [[deprecated("Use ipmi::registerHandler() instead")]]
638void ipmi_register_callback(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
639 ipmi_context_t context, ipmid_callback_t handler,
640 ipmi_cmd_privilege_t priv);
641
642#endif /* ALLOW_DEPRECATED_API */