1240b186cSVernon Mauery /**
2240b186cSVernon Mauery * Copyright © 2018 Intel Corporation
3240b186cSVernon Mauery *
4240b186cSVernon Mauery * Licensed under the Apache License, Version 2.0 (the "License");
5240b186cSVernon Mauery * you may not use this file except in compliance with the License.
6240b186cSVernon Mauery * You may obtain a copy of the License at
7240b186cSVernon Mauery *
8240b186cSVernon Mauery * http://www.apache.org/licenses/LICENSE-2.0
9240b186cSVernon Mauery *
10240b186cSVernon Mauery * Unless required by applicable law or agreed to in writing, software
11240b186cSVernon Mauery * distributed under the License is distributed on an "AS IS" BASIS,
12240b186cSVernon Mauery * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13240b186cSVernon Mauery * See the License for the specific language governing permissions and
14240b186cSVernon Mauery * limitations under the License.
15240b186cSVernon Mauery */
16240b186cSVernon Mauery #include "config.h"
17240b186cSVernon Mauery
18240b186cSVernon Mauery #include "settings.hpp"
19240b186cSVernon Mauery
20240b186cSVernon Mauery #include <dlfcn.h>
21240b186cSVernon Mauery
22735ee953SVernon Mauery #include <boost/algorithm/string.hpp>
23778418daSEd Tanous #include <boost/asio/io_context.hpp>
2426a386ffSEd Tanous #include <boost/asio/spawn.hpp>
25240b186cSVernon Mauery #include <host-cmd-manager.hpp>
26240b186cSVernon Mauery #include <ipmid-host/cmd.hpp>
27240b186cSVernon Mauery #include <ipmid/api.hpp>
28240b186cSVernon Mauery #include <ipmid/handler.hpp>
29240b186cSVernon Mauery #include <ipmid/message.hpp>
30240b186cSVernon Mauery #include <ipmid/oemrouter.hpp>
3133250240SVernon Mauery #include <ipmid/types.hpp>
32e808bae0SVernon Mauery #include <phosphor-logging/lg2.hpp>
33240b186cSVernon Mauery #include <sdbusplus/asio/connection.hpp>
34240b186cSVernon Mauery #include <sdbusplus/asio/object_server.hpp>
353b301a3aSPatrick Williams #include <sdbusplus/asio/sd_event.hpp>
36240b186cSVernon Mauery #include <sdbusplus/bus.hpp>
37240b186cSVernon Mauery #include <sdbusplus/bus/match.hpp>
38240b186cSVernon Mauery #include <sdbusplus/timer.hpp>
39fbc6c9d7SPatrick Williams
40fbc6c9d7SPatrick Williams #include <algorithm>
41fbc6c9d7SPatrick Williams #include <any>
42fbc6c9d7SPatrick Williams #include <exception>
43fbc6c9d7SPatrick Williams #include <filesystem>
44fbc6c9d7SPatrick Williams #include <forward_list>
45fbc6c9d7SPatrick Williams #include <map>
46fbc6c9d7SPatrick Williams #include <memory>
47fbc6c9d7SPatrick Williams #include <optional>
48240b186cSVernon Mauery #include <tuple>
49240b186cSVernon Mauery #include <unordered_map>
50240b186cSVernon Mauery #include <utility>
51240b186cSVernon Mauery #include <vector>
52240b186cSVernon Mauery
53240b186cSVernon Mauery namespace fs = std::filesystem;
54240b186cSVernon Mauery
55240b186cSVernon Mauery using namespace phosphor::logging;
56240b186cSVernon Mauery
57240b186cSVernon Mauery // IPMI Spec, shared Reservation ID.
58240b186cSVernon Mauery static unsigned short selReservationID = 0xFFFF;
59240b186cSVernon Mauery static bool selReservationValid = false;
60240b186cSVernon Mauery
reserveSel(void)61240b186cSVernon Mauery unsigned short reserveSel(void)
62240b186cSVernon Mauery {
63240b186cSVernon Mauery // IPMI spec, Reservation ID, the value simply increases against each
64240b186cSVernon Mauery // execution of the Reserve SEL command.
65240b186cSVernon Mauery if (++selReservationID == 0)
66240b186cSVernon Mauery {
67240b186cSVernon Mauery selReservationID = 1;
68240b186cSVernon Mauery }
69240b186cSVernon Mauery selReservationValid = true;
70240b186cSVernon Mauery return selReservationID;
71240b186cSVernon Mauery }
72240b186cSVernon Mauery
checkSELReservation(unsigned short id)73240b186cSVernon Mauery bool checkSELReservation(unsigned short id)
74240b186cSVernon Mauery {
75240b186cSVernon Mauery return (selReservationValid && selReservationID == id);
76240b186cSVernon Mauery }
77240b186cSVernon Mauery
cancelSELReservation(void)78240b186cSVernon Mauery void cancelSELReservation(void)
79240b186cSVernon Mauery {
80240b186cSVernon Mauery selReservationValid = false;
81240b186cSVernon Mauery }
82240b186cSVernon Mauery
getInterfaceIndex(void)83240b186cSVernon Mauery EInterfaceIndex getInterfaceIndex(void)
84240b186cSVernon Mauery {
85240b186cSVernon Mauery return interfaceKCS;
86240b186cSVernon Mauery }
87240b186cSVernon Mauery
88240b186cSVernon Mauery sd_bus* bus;
893b301a3aSPatrick Williams sd_event* events = nullptr;
ipmid_get_sd_event_connection(void)903b301a3aSPatrick Williams sd_event* ipmid_get_sd_event_connection(void)
913b301a3aSPatrick Williams {
923b301a3aSPatrick Williams return events;
933b301a3aSPatrick Williams }
ipmid_get_sd_bus_connection(void)94240b186cSVernon Mauery sd_bus* ipmid_get_sd_bus_connection(void)
95240b186cSVernon Mauery {
96240b186cSVernon Mauery return bus;
97240b186cSVernon Mauery }
98240b186cSVernon Mauery
99240b186cSVernon Mauery namespace ipmi
100240b186cSVernon Mauery {
101240b186cSVernon Mauery
makeCmdKey(unsigned int cluster,unsigned int cmd)102240b186cSVernon Mauery static inline unsigned int makeCmdKey(unsigned int cluster, unsigned int cmd)
103240b186cSVernon Mauery {
104240b186cSVernon Mauery return (cluster << 8) | cmd;
105240b186cSVernon Mauery }
106240b186cSVernon Mauery
107240b186cSVernon Mauery using HandlerTuple = std::tuple<int, /* prio */
108240b186cSVernon Mauery Privilege, HandlerBase::ptr /* handler */
109240b186cSVernon Mauery >;
110240b186cSVernon Mauery
111240b186cSVernon Mauery /* map to handle standard registered commands */
112240b186cSVernon Mauery static std::unordered_map<unsigned int, /* key is NetFn/Cmd */
113240b186cSVernon Mauery HandlerTuple>
114240b186cSVernon Mauery handlerMap;
115240b186cSVernon Mauery
116f984a01fSVernon Mauery /* special map for decoding Group registered commands (NetFn 2Ch) */
117f984a01fSVernon Mauery static std::unordered_map<unsigned int, /* key is Group/Cmd (NetFn is 2Ch) */
118f984a01fSVernon Mauery HandlerTuple>
119f984a01fSVernon Mauery groupHandlerMap;
120f984a01fSVernon Mauery
121f984a01fSVernon Mauery /* special map for decoding OEM registered commands (NetFn 2Eh) */
122f984a01fSVernon Mauery static std::unordered_map<unsigned int, /* key is Iana/Cmd (NetFn is 2Eh) */
123f984a01fSVernon Mauery HandlerTuple>
124f984a01fSVernon Mauery oemHandlerMap;
125f984a01fSVernon Mauery
12608a70aa5SVernon Mauery using FilterTuple = std::tuple<int, /* prio */
12708a70aa5SVernon Mauery FilterBase::ptr /* filter */
12808a70aa5SVernon Mauery >;
12908a70aa5SVernon Mauery
13008a70aa5SVernon Mauery /* list to hold all registered ipmi command filters */
13108a70aa5SVernon Mauery static std::forward_list<FilterTuple> filterList;
13208a70aa5SVernon Mauery
133240b186cSVernon Mauery namespace impl
134240b186cSVernon Mauery {
135240b186cSVernon Mauery /* common function to register all standard IPMI handlers */
registerHandler(int prio,NetFn netFn,Cmd cmd,Privilege priv,HandlerBase::ptr handler)136240b186cSVernon Mauery bool registerHandler(int prio, NetFn netFn, Cmd cmd, Privilege priv,
137240b186cSVernon Mauery HandlerBase::ptr handler)
138240b186cSVernon Mauery {
139240b186cSVernon Mauery // check for valid NetFn: even; 00-0Ch, 30-3Eh
140240b186cSVernon Mauery if (netFn & 1 || (netFn > netFnTransport && netFn < netFnGroup) ||
141240b186cSVernon Mauery netFn > netFnOemEight)
142240b186cSVernon Mauery {
143240b186cSVernon Mauery return false;
144240b186cSVernon Mauery }
145240b186cSVernon Mauery
146240b186cSVernon Mauery // create key and value for this handler
147240b186cSVernon Mauery unsigned int netFnCmd = makeCmdKey(netFn, cmd);
148240b186cSVernon Mauery HandlerTuple item(prio, priv, handler);
149240b186cSVernon Mauery
150240b186cSVernon Mauery // consult the handler map and look for a match
151240b186cSVernon Mauery auto& mapCmd = handlerMap[netFnCmd];
152240b186cSVernon Mauery if (!std::get<HandlerBase::ptr>(mapCmd) || std::get<int>(mapCmd) <= prio)
153240b186cSVernon Mauery {
154240b186cSVernon Mauery mapCmd = item;
155240b186cSVernon Mauery return true;
156240b186cSVernon Mauery }
157240b186cSVernon Mauery return false;
158240b186cSVernon Mauery }
159240b186cSVernon Mauery
160f984a01fSVernon Mauery /* common function to register all Group IPMI handlers */
registerGroupHandler(int prio,Group group,Cmd cmd,Privilege priv,HandlerBase::ptr handler)161f984a01fSVernon Mauery bool registerGroupHandler(int prio, Group group, Cmd cmd, Privilege priv,
162f984a01fSVernon Mauery HandlerBase::ptr handler)
163f984a01fSVernon Mauery {
164f984a01fSVernon Mauery // create key and value for this handler
165f984a01fSVernon Mauery unsigned int netFnCmd = makeCmdKey(group, cmd);
166f984a01fSVernon Mauery HandlerTuple item(prio, priv, handler);
167f984a01fSVernon Mauery
168f984a01fSVernon Mauery // consult the handler map and look for a match
169f984a01fSVernon Mauery auto& mapCmd = groupHandlerMap[netFnCmd];
170f984a01fSVernon Mauery if (!std::get<HandlerBase::ptr>(mapCmd) || std::get<int>(mapCmd) <= prio)
171f984a01fSVernon Mauery {
172f984a01fSVernon Mauery mapCmd = item;
173f984a01fSVernon Mauery return true;
174f984a01fSVernon Mauery }
175f984a01fSVernon Mauery return false;
176f984a01fSVernon Mauery }
177f984a01fSVernon Mauery
178f984a01fSVernon Mauery /* common function to register all OEM IPMI handlers */
registerOemHandler(int prio,Iana iana,Cmd cmd,Privilege priv,HandlerBase::ptr handler)179f984a01fSVernon Mauery bool registerOemHandler(int prio, Iana iana, Cmd cmd, Privilege priv,
180f984a01fSVernon Mauery HandlerBase::ptr handler)
181f984a01fSVernon Mauery {
182f984a01fSVernon Mauery // create key and value for this handler
183f984a01fSVernon Mauery unsigned int netFnCmd = makeCmdKey(iana, cmd);
184f984a01fSVernon Mauery HandlerTuple item(prio, priv, handler);
185f984a01fSVernon Mauery
186f984a01fSVernon Mauery // consult the handler map and look for a match
187f984a01fSVernon Mauery auto& mapCmd = oemHandlerMap[netFnCmd];
188f984a01fSVernon Mauery if (!std::get<HandlerBase::ptr>(mapCmd) || std::get<int>(mapCmd) <= prio)
189f984a01fSVernon Mauery {
190f984a01fSVernon Mauery mapCmd = item;
191e808bae0SVernon Mauery lg2::debug("registered OEM Handler: NetFn/Cmd={NETFNCMD}", "IANA",
192e808bae0SVernon Mauery lg2::hex, iana, "CMD", lg2::hex, cmd, "NETFNCMD", lg2::hex,
193e808bae0SVernon Mauery netFnCmd);
194f984a01fSVernon Mauery return true;
195f984a01fSVernon Mauery }
1967197b347SAlexander Hansen
197e808bae0SVernon Mauery lg2::warning("could not register OEM Handler: NetFn/Cmd={NETFNCMD}", "IANA",
198e808bae0SVernon Mauery lg2::hex, iana, "CMD", lg2::hex, cmd, "NETFNCMD", lg2::hex,
199e808bae0SVernon Mauery netFnCmd);
200f984a01fSVernon Mauery return false;
201f984a01fSVernon Mauery }
202f984a01fSVernon Mauery
20308a70aa5SVernon Mauery /* common function to register all IPMI filter handlers */
registerFilter(int prio,FilterBase::ptr filter)20408a70aa5SVernon Mauery void registerFilter(int prio, FilterBase::ptr filter)
20508a70aa5SVernon Mauery {
20608a70aa5SVernon Mauery // check for initial placement
20708a70aa5SVernon Mauery if (filterList.empty() || std::get<int>(filterList.front()) < prio)
20808a70aa5SVernon Mauery {
20908a70aa5SVernon Mauery filterList.emplace_front(std::make_tuple(prio, filter));
210be063232SYong Li return;
21108a70aa5SVernon Mauery }
21208a70aa5SVernon Mauery // walk the list and put it in the right place
21308a70aa5SVernon Mauery auto j = filterList.begin();
21408a70aa5SVernon Mauery for (auto i = j; i != filterList.end() && std::get<int>(*i) > prio; i++)
21508a70aa5SVernon Mauery {
21608a70aa5SVernon Mauery j = i;
21708a70aa5SVernon Mauery }
21808a70aa5SVernon Mauery filterList.emplace_after(j, std::make_tuple(prio, filter));
21908a70aa5SVernon Mauery }
22008a70aa5SVernon Mauery
221240b186cSVernon Mauery } // namespace impl
222240b186cSVernon Mauery
filterIpmiCommand(message::Request::ptr request)22308a70aa5SVernon Mauery message::Response::ptr filterIpmiCommand(message::Request::ptr request)
22408a70aa5SVernon Mauery {
22508a70aa5SVernon Mauery // pass the command through the filter mechanism
22608a70aa5SVernon Mauery // This can be the firmware firewall or any OEM mechanism like
22708a70aa5SVernon Mauery // whitelist filtering based on operational mode
22808a70aa5SVernon Mauery for (auto& item : filterList)
22908a70aa5SVernon Mauery {
23008a70aa5SVernon Mauery FilterBase::ptr filter = std::get<FilterBase::ptr>(item);
23108a70aa5SVernon Mauery ipmi::Cc cc = filter->call(request);
23208a70aa5SVernon Mauery if (ipmi::ccSuccess != cc)
23308a70aa5SVernon Mauery {
23408a70aa5SVernon Mauery return errorResponse(request, cc);
23508a70aa5SVernon Mauery }
23608a70aa5SVernon Mauery }
23708a70aa5SVernon Mauery return message::Response::ptr();
23808a70aa5SVernon Mauery }
23908a70aa5SVernon Mauery
executeIpmiCommandCommon(std::unordered_map<unsigned int,HandlerTuple> & handlers,unsigned int keyCommon,message::Request::ptr request)240240b186cSVernon Mauery message::Response::ptr executeIpmiCommandCommon(
241240b186cSVernon Mauery std::unordered_map<unsigned int, HandlerTuple>& handlers,
242240b186cSVernon Mauery unsigned int keyCommon, message::Request::ptr request)
243240b186cSVernon Mauery {
24408a70aa5SVernon Mauery // filter the command first; a non-null message::Response::ptr
24508a70aa5SVernon Mauery // means that the message has been rejected for some reason
24651f78141SVernon Mauery message::Response::ptr filterResponse = filterIpmiCommand(request);
24708a70aa5SVernon Mauery
248240b186cSVernon Mauery Cmd cmd = request->ctx->cmd;
249240b186cSVernon Mauery unsigned int key = makeCmdKey(keyCommon, cmd);
250240b186cSVernon Mauery auto cmdIter = handlers.find(key);
251240b186cSVernon Mauery if (cmdIter != handlers.end())
252240b186cSVernon Mauery {
25351f78141SVernon Mauery // only return the filter response if the command is found
25451f78141SVernon Mauery if (filterResponse)
25551f78141SVernon Mauery {
256e808bae0SVernon Mauery lg2::debug("request for NetFn/Cmd {NETFN}/{CMD} has been filtered",
257e808bae0SVernon Mauery "NETFN", lg2::hex, keyCommon, "CMD", lg2::hex, cmd);
25851f78141SVernon Mauery return filterResponse;
25951f78141SVernon Mauery }
260240b186cSVernon Mauery HandlerTuple& chosen = cmdIter->second;
261240b186cSVernon Mauery if (request->ctx->priv < std::get<Privilege>(chosen))
262240b186cSVernon Mauery {
263240b186cSVernon Mauery return errorResponse(request, ccInsufficientPrivilege);
264240b186cSVernon Mauery }
265240b186cSVernon Mauery return std::get<HandlerBase::ptr>(chosen)->call(request);
266240b186cSVernon Mauery }
267240b186cSVernon Mauery else
268240b186cSVernon Mauery {
269240b186cSVernon Mauery unsigned int wildcard = makeCmdKey(keyCommon, cmdWildcard);
270240b186cSVernon Mauery cmdIter = handlers.find(wildcard);
271240b186cSVernon Mauery if (cmdIter != handlers.end())
272240b186cSVernon Mauery {
27351f78141SVernon Mauery // only return the filter response if the command is found
27451f78141SVernon Mauery if (filterResponse)
27551f78141SVernon Mauery {
276e808bae0SVernon Mauery lg2::debug(
277e808bae0SVernon Mauery "request for NetFn/Cmd {NETFN}/{CMD} has been filtered",
278e808bae0SVernon Mauery "NETFN", lg2::hex, keyCommon, "CMD", lg2::hex, cmd);
27951f78141SVernon Mauery return filterResponse;
28051f78141SVernon Mauery }
281240b186cSVernon Mauery HandlerTuple& chosen = cmdIter->second;
282240b186cSVernon Mauery if (request->ctx->priv < std::get<Privilege>(chosen))
283240b186cSVernon Mauery {
284240b186cSVernon Mauery return errorResponse(request, ccInsufficientPrivilege);
285240b186cSVernon Mauery }
286240b186cSVernon Mauery return std::get<HandlerBase::ptr>(chosen)->call(request);
287240b186cSVernon Mauery }
288240b186cSVernon Mauery }
289240b186cSVernon Mauery return errorResponse(request, ccInvalidCommand);
290240b186cSVernon Mauery }
291240b186cSVernon Mauery
executeIpmiGroupCommand(message::Request::ptr request)292f984a01fSVernon Mauery message::Response::ptr executeIpmiGroupCommand(message::Request::ptr request)
293f984a01fSVernon Mauery {
294f984a01fSVernon Mauery // look up the group for this request
295d10d9056SWilliam A. Kennington III uint8_t bytes;
296d10d9056SWilliam A. Kennington III if (0 != request->payload.unpack(bytes))
297f984a01fSVernon Mauery {
298f984a01fSVernon Mauery return errorResponse(request, ccReqDataLenInvalid);
299f984a01fSVernon Mauery }
300d10d9056SWilliam A. Kennington III auto group = static_cast<Group>(bytes);
3011318a5edSPatrick Williams message::Response::ptr response =
3021318a5edSPatrick Williams executeIpmiCommandCommon(groupHandlerMap, group, request);
303da31f9a6SWilliam A. Kennington III ipmi::message::Payload prefix;
304da31f9a6SWilliam A. Kennington III prefix.pack(bytes);
305da31f9a6SWilliam A. Kennington III response->prepend(prefix);
306f984a01fSVernon Mauery return response;
307f984a01fSVernon Mauery }
308f984a01fSVernon Mauery
executeIpmiOemCommand(message::Request::ptr request)309f984a01fSVernon Mauery message::Response::ptr executeIpmiOemCommand(message::Request::ptr request)
310f984a01fSVernon Mauery {
311f984a01fSVernon Mauery // look up the iana for this request
312d10d9056SWilliam A. Kennington III uint24_t bytes;
313d10d9056SWilliam A. Kennington III if (0 != request->payload.unpack(bytes))
314f984a01fSVernon Mauery {
315f984a01fSVernon Mauery return errorResponse(request, ccReqDataLenInvalid);
316f984a01fSVernon Mauery }
317d10d9056SWilliam A. Kennington III auto iana = static_cast<Iana>(bytes);
3187197b347SAlexander Hansen
319e808bae0SVernon Mauery lg2::debug("unpack IANA {IANA}", "IANA", lg2::hex, iana);
3207197b347SAlexander Hansen
3211318a5edSPatrick Williams message::Response::ptr response =
3221318a5edSPatrick Williams executeIpmiCommandCommon(oemHandlerMap, iana, request);
323da31f9a6SWilliam A. Kennington III ipmi::message::Payload prefix;
324da31f9a6SWilliam A. Kennington III prefix.pack(bytes);
325da31f9a6SWilliam A. Kennington III response->prepend(prefix);
326f984a01fSVernon Mauery return response;
327f984a01fSVernon Mauery }
328f984a01fSVernon Mauery
executeIpmiCommand(message::Request::ptr request)329240b186cSVernon Mauery message::Response::ptr executeIpmiCommand(message::Request::ptr request)
330240b186cSVernon Mauery {
331240b186cSVernon Mauery NetFn netFn = request->ctx->netFn;
332f984a01fSVernon Mauery if (netFnGroup == netFn)
333f984a01fSVernon Mauery {
334f984a01fSVernon Mauery return executeIpmiGroupCommand(request);
335f984a01fSVernon Mauery }
336f984a01fSVernon Mauery else if (netFnOem == netFn)
337f984a01fSVernon Mauery {
338f984a01fSVernon Mauery return executeIpmiOemCommand(request);
339f984a01fSVernon Mauery }
340240b186cSVernon Mauery return executeIpmiCommandCommon(handlerMap, netFn, request);
341240b186cSVernon Mauery }
342240b186cSVernon Mauery
343735ee953SVernon Mauery namespace utils
344735ee953SVernon Mauery {
345735ee953SVernon Mauery template <typename AssocContainer, typename UnaryPredicate>
assoc_erase_if(AssocContainer & c,UnaryPredicate p)346735ee953SVernon Mauery void assoc_erase_if(AssocContainer& c, UnaryPredicate p)
347735ee953SVernon Mauery {
348735ee953SVernon Mauery typename AssocContainer::iterator next = c.begin();
349735ee953SVernon Mauery typename AssocContainer::iterator last = c.end();
350735ee953SVernon Mauery while ((next = std::find_if(next, last, p)) != last)
351735ee953SVernon Mauery {
352735ee953SVernon Mauery c.erase(next++);
353735ee953SVernon Mauery }
354735ee953SVernon Mauery }
355735ee953SVernon Mauery } // namespace utils
356735ee953SVernon Mauery
357735ee953SVernon Mauery namespace
358735ee953SVernon Mauery {
359735ee953SVernon Mauery std::unordered_map<std::string, uint8_t> uniqueNameToChannelNumber;
360735ee953SVernon Mauery
361735ee953SVernon Mauery // sdbusplus::bus::match::rules::arg0namespace() wants the prefix
362735ee953SVernon Mauery // to match without any trailing '.'
363735ee953SVernon Mauery constexpr const char ipmiDbusChannelMatch[] =
364735ee953SVernon Mauery "xyz.openbmc_project.Ipmi.Channel";
updateOwners(sdbusplus::asio::connection & conn,const std::string & name)365735ee953SVernon Mauery void updateOwners(sdbusplus::asio::connection& conn, const std::string& name)
366735ee953SVernon Mauery {
367735ee953SVernon Mauery conn.async_method_call(
368735ee953SVernon Mauery [name](const boost::system::error_code ec,
369735ee953SVernon Mauery const std::string& nameOwner) {
370735ee953SVernon Mauery if (ec)
371735ee953SVernon Mauery {
3721318a5edSPatrick Williams lg2::error("Error getting dbus owner for {INTERFACE}",
3731318a5edSPatrick Williams "INTERFACE", name);
374735ee953SVernon Mauery return;
375735ee953SVernon Mauery }
376735ee953SVernon Mauery // start after ipmiDbusChannelPrefix (after the '.')
3771318a5edSPatrick Williams std::string chName =
3781318a5edSPatrick Williams name.substr(std::strlen(ipmiDbusChannelMatch) + 1);
379735ee953SVernon Mauery try
380735ee953SVernon Mauery {
381735ee953SVernon Mauery uint8_t channel = getChannelByName(chName);
382735ee953SVernon Mauery uniqueNameToChannelNumber[nameOwner] = channel;
3831318a5edSPatrick Williams lg2::info(
3841318a5edSPatrick Williams "New interface mapping: {INTERFACE} -> channel {CHANNEL}",
385e808bae0SVernon Mauery "INTERFACE", name, "CHANNEL", channel);
386735ee953SVernon Mauery }
387735ee953SVernon Mauery catch (const std::exception& e)
388735ee953SVernon Mauery {
389e808bae0SVernon Mauery lg2::info("Failed interface mapping, no such name: {INTERFACE}",
390e808bae0SVernon Mauery "INTERFACE", name);
391735ee953SVernon Mauery }
392735ee953SVernon Mauery },
393735ee953SVernon Mauery "org.freedesktop.DBus", "/", "org.freedesktop.DBus", "GetNameOwner",
394735ee953SVernon Mauery name);
395735ee953SVernon Mauery }
396735ee953SVernon Mauery
doListNames(sdbusplus::asio::connection & conn)397*9f3073a6SJayanth Othayoth void doListNames(sdbusplus::asio::connection& conn)
398735ee953SVernon Mauery {
399735ee953SVernon Mauery conn.async_method_call(
400*9f3073a6SJayanth Othayoth [&conn](const boost::system::error_code ec,
401735ee953SVernon Mauery std::vector<std::string> busNames) {
402735ee953SVernon Mauery if (ec)
403735ee953SVernon Mauery {
404e808bae0SVernon Mauery lg2::error("Error getting dbus names: {ERROR}", "ERROR",
405e808bae0SVernon Mauery ec.message());
406735ee953SVernon Mauery std::exit(EXIT_FAILURE);
407735ee953SVernon Mauery return;
408735ee953SVernon Mauery }
409735ee953SVernon Mauery // Try to make startup consistent
410735ee953SVernon Mauery std::sort(busNames.begin(), busNames.end());
411735ee953SVernon Mauery
4121318a5edSPatrick Williams const std::string channelPrefix =
4131318a5edSPatrick Williams std::string(ipmiDbusChannelMatch) + ".";
414735ee953SVernon Mauery for (const std::string& busName : busNames)
415735ee953SVernon Mauery {
416735ee953SVernon Mauery if (busName.find(channelPrefix) == 0)
417735ee953SVernon Mauery {
418735ee953SVernon Mauery updateOwners(conn, busName);
419735ee953SVernon Mauery }
420735ee953SVernon Mauery }
421735ee953SVernon Mauery },
422735ee953SVernon Mauery "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus",
423735ee953SVernon Mauery "ListNames");
424735ee953SVernon Mauery }
425735ee953SVernon Mauery
nameChangeHandler(sdbusplus::message_t & message)4265d82f474SPatrick Williams void nameChangeHandler(sdbusplus::message_t& message)
427735ee953SVernon Mauery {
428735ee953SVernon Mauery std::string name;
429735ee953SVernon Mauery std::string oldOwner;
430735ee953SVernon Mauery std::string newOwner;
431735ee953SVernon Mauery
432735ee953SVernon Mauery message.read(name, oldOwner, newOwner);
433735ee953SVernon Mauery
434735ee953SVernon Mauery if (!oldOwner.empty())
435735ee953SVernon Mauery {
436735ee953SVernon Mauery if (boost::starts_with(oldOwner, ":"))
437735ee953SVernon Mauery {
438735ee953SVernon Mauery // Connection removed
439735ee953SVernon Mauery auto it = uniqueNameToChannelNumber.find(oldOwner);
440735ee953SVernon Mauery if (it != uniqueNameToChannelNumber.end())
441735ee953SVernon Mauery {
442735ee953SVernon Mauery uniqueNameToChannelNumber.erase(it);
443735ee953SVernon Mauery }
444735ee953SVernon Mauery }
445735ee953SVernon Mauery }
446735ee953SVernon Mauery if (!newOwner.empty())
447735ee953SVernon Mauery {
448735ee953SVernon Mauery // start after ipmiDbusChannelMatch (and after the '.')
449735ee953SVernon Mauery std::string chName = name.substr(std::strlen(ipmiDbusChannelMatch) + 1);
450735ee953SVernon Mauery try
451735ee953SVernon Mauery {
452735ee953SVernon Mauery uint8_t channel = getChannelByName(chName);
453735ee953SVernon Mauery uniqueNameToChannelNumber[newOwner] = channel;
454e808bae0SVernon Mauery lg2::info("New interface mapping: {INTERFACE} -> channel {CHANNEL}",
455e808bae0SVernon Mauery "INTERFACE", name, "CHANNEL", channel);
456735ee953SVernon Mauery }
457735ee953SVernon Mauery catch (const std::exception& e)
458735ee953SVernon Mauery {
459e808bae0SVernon Mauery lg2::info("Failed interface mapping, no such name: {INTERFACE}",
460e808bae0SVernon Mauery "INTERFACE", name);
461735ee953SVernon Mauery }
462735ee953SVernon Mauery }
463735ee953SVernon Mauery };
464735ee953SVernon Mauery
465735ee953SVernon Mauery } // anonymous namespace
466735ee953SVernon Mauery
467735ee953SVernon Mauery static constexpr const char intraBmcName[] = "INTRABMC";
channelFromMessage(sdbusplus::message_t & msg)4685d82f474SPatrick Williams uint8_t channelFromMessage(sdbusplus::message_t& msg)
469735ee953SVernon Mauery {
470735ee953SVernon Mauery // channel name for ipmitool to resolve to
471735ee953SVernon Mauery std::string sender = msg.get_sender();
472735ee953SVernon Mauery auto chIter = uniqueNameToChannelNumber.find(sender);
473735ee953SVernon Mauery if (chIter != uniqueNameToChannelNumber.end())
474735ee953SVernon Mauery {
475735ee953SVernon Mauery return chIter->second;
476735ee953SVernon Mauery }
477735ee953SVernon Mauery // FIXME: currently internal connections are ephemeral and hard to pin down
478735ee953SVernon Mauery try
479735ee953SVernon Mauery {
480735ee953SVernon Mauery return getChannelByName(intraBmcName);
481735ee953SVernon Mauery }
482735ee953SVernon Mauery catch (const std::exception& e)
483735ee953SVernon Mauery {
484735ee953SVernon Mauery return invalidChannel;
485735ee953SVernon Mauery }
486735ee953SVernon Mauery } // namespace ipmi
487735ee953SVernon Mauery
488240b186cSVernon Mauery /* called from sdbus async server context */
executionEntry(boost::asio::yield_context yield,sdbusplus::message_t & m,NetFn netFn,uint8_t lun,Cmd cmd,ipmi::SecureBuffer & data,std::map<std::string,ipmi::Value> & options)4895d82f474SPatrick Williams auto executionEntry(boost::asio::yield_context yield, sdbusplus::message_t& m,
4905d82f474SPatrick Williams NetFn netFn, uint8_t lun, Cmd cmd, ipmi::SecureBuffer& data,
491240b186cSVernon Mauery std::map<std::string, ipmi::Value>& options)
492240b186cSVernon Mauery {
493735ee953SVernon Mauery const auto dbusResponse =
494997952afSVernon Mauery [netFn, lun, cmd](Cc cc, const ipmi::SecureBuffer& data = {}) {
495735ee953SVernon Mauery constexpr uint8_t netFnResponse = 0x01;
496735ee953SVernon Mauery uint8_t retNetFn = netFn | netFnResponse;
497735ee953SVernon Mauery return std::make_tuple(retNetFn, lun, cmd, cc, data);
498735ee953SVernon Mauery };
499735ee953SVernon Mauery std::string sender = m.get_sender();
500735ee953SVernon Mauery Privilege privilege = Privilege::None;
501d6a2da07SVernon Mauery int rqSA = 0;
502f7d081f6SKumar Thangavel int hostIdx = 0;
503735ee953SVernon Mauery uint8_t userId = 0; // undefined user
5044d22640aSRajashekar Gade Reddy uint32_t sessionId = 0;
505735ee953SVernon Mauery
506735ee953SVernon Mauery // figure out what channel the request came in on
507735ee953SVernon Mauery uint8_t channel = channelFromMessage(m);
508735ee953SVernon Mauery if (channel == invalidChannel)
509735ee953SVernon Mauery {
510735ee953SVernon Mauery // unknown sender channel; refuse to service the request
511e808bae0SVernon Mauery lg2::error("ERROR determining source IPMI channel from "
512e808bae0SVernon Mauery "{SENDER} NetFn/Cmd {NETFN}/{CMD}",
513e808bae0SVernon Mauery "SENDER", sender, "NETFN", lg2::hex, netFn, "CMD", lg2::hex,
514e808bae0SVernon Mauery cmd);
515735ee953SVernon Mauery return dbusResponse(ipmi::ccDestinationUnavailable);
516735ee953SVernon Mauery }
517735ee953SVernon Mauery
5184d22640aSRajashekar Gade Reddy // session-based channels are required to provide userId, privilege and
5194d22640aSRajashekar Gade Reddy // sessionId
520735ee953SVernon Mauery if (getChannelSessionSupport(channel) != EChannelSessSupported::none)
521735ee953SVernon Mauery {
522735ee953SVernon Mauery try
523735ee953SVernon Mauery {
524735ee953SVernon Mauery Value requestPriv = options.at("privilege");
525735ee953SVernon Mauery Value requestUserId = options.at("userId");
5264d22640aSRajashekar Gade Reddy Value requestSessionId = options.at("currentSessionId");
527735ee953SVernon Mauery privilege = static_cast<Privilege>(std::get<int>(requestPriv));
528735ee953SVernon Mauery userId = static_cast<uint8_t>(std::get<int>(requestUserId));
5294d22640aSRajashekar Gade Reddy sessionId =
5304d22640aSRajashekar Gade Reddy static_cast<uint32_t>(std::get<uint32_t>(requestSessionId));
531735ee953SVernon Mauery }
532735ee953SVernon Mauery catch (const std::exception& e)
533735ee953SVernon Mauery {
534e808bae0SVernon Mauery lg2::error("ERROR determining IPMI session credentials on "
535e808bae0SVernon Mauery "channel {CHANNEL} for userid {USERID}",
536e808bae0SVernon Mauery "CHANNEL", channel, "USERID", userId, "NETFN", lg2::hex,
537e808bae0SVernon Mauery netFn, "CMD", lg2::hex, cmd);
538735ee953SVernon Mauery return dbusResponse(ipmi::ccUnspecifiedError);
539735ee953SVernon Mauery }
540735ee953SVernon Mauery }
541735ee953SVernon Mauery else
542735ee953SVernon Mauery {
543735ee953SVernon Mauery // get max privilege for session-less channels
544735ee953SVernon Mauery // For now, there is not a way to configure this, default to Admin
545735ee953SVernon Mauery privilege = Privilege::Admin;
546d6a2da07SVernon Mauery
547d6a2da07SVernon Mauery // ipmb should supply rqSA
548d6a2da07SVernon Mauery ChannelInfo chInfo;
549d6a2da07SVernon Mauery getChannelInfo(channel, chInfo);
550d6a2da07SVernon Mauery if (static_cast<EChannelMediumType>(chInfo.mediumType) ==
551d6a2da07SVernon Mauery EChannelMediumType::ipmb)
552d6a2da07SVernon Mauery {
553d6a2da07SVernon Mauery const auto iter = options.find("rqSA");
554d6a2da07SVernon Mauery if (iter != options.end())
555d6a2da07SVernon Mauery {
556d6a2da07SVernon Mauery if (std::holds_alternative<int>(iter->second))
557d6a2da07SVernon Mauery {
558d6a2da07SVernon Mauery rqSA = std::get<int>(iter->second);
559d6a2da07SVernon Mauery }
560d6a2da07SVernon Mauery }
561f7d081f6SKumar Thangavel const auto iteration = options.find("hostId");
562f7d081f6SKumar Thangavel if (iteration != options.end())
563f7d081f6SKumar Thangavel {
564f7d081f6SKumar Thangavel if (std::holds_alternative<int>(iteration->second))
565f7d081f6SKumar Thangavel {
566f7d081f6SKumar Thangavel hostIdx = std::get<int>(iteration->second);
567f7d081f6SKumar Thangavel }
568f7d081f6SKumar Thangavel }
569d6a2da07SVernon Mauery }
570735ee953SVernon Mauery }
571735ee953SVernon Mauery // check to see if the requested priv/username is valid
572e808bae0SVernon Mauery lg2::debug("Set up ipmi context: Ch:NetFn/Cmd={CHANNEL}:{NETFN}/{CMD}",
573e808bae0SVernon Mauery "SENDER", sender, "NETFN", lg2::hex, netFn, "LUN", lg2::hex, lun,
574e808bae0SVernon Mauery "CMD", lg2::hex, cmd, "CHANNEL", channel, "USERID", userId,
575e808bae0SVernon Mauery "SESSIONID", lg2::hex, sessionId, "PRIVILEGE",
576e808bae0SVernon Mauery static_cast<uint8_t>(privilege), "RQSA", lg2::hex, rqSA);
577735ee953SVernon Mauery
5781318a5edSPatrick Williams auto ctx = std::make_shared<ipmi::Context>(
5791318a5edSPatrick Williams getSdBus(), netFn, lun, cmd, channel, userId, sessionId, privilege,
5801318a5edSPatrick Williams rqSA, hostIdx, yield);
581240b186cSVernon Mauery auto request = std::make_shared<ipmi::message::Request>(
582997952afSVernon Mauery ctx, std::forward<ipmi::SecureBuffer>(data));
583240b186cSVernon Mauery message::Response::ptr response = executeIpmiCommand(request);
584240b186cSVernon Mauery
585735ee953SVernon Mauery return dbusResponse(response->cc, response->payload.raw);
586240b186cSVernon Mauery }
587240b186cSVernon Mauery
588240b186cSVernon Mauery /** @struct IpmiProvider
589240b186cSVernon Mauery *
590240b186cSVernon Mauery * RAII wrapper for dlopen so that dlclose gets called on exit
591240b186cSVernon Mauery */
592240b186cSVernon Mauery struct IpmiProvider
593240b186cSVernon Mauery {
594240b186cSVernon Mauery public:
595240b186cSVernon Mauery /** @brief address of the opened library */
596240b186cSVernon Mauery void* addr;
597240b186cSVernon Mauery std::string name;
598240b186cSVernon Mauery
599240b186cSVernon Mauery IpmiProvider() = delete;
600240b186cSVernon Mauery IpmiProvider(const IpmiProvider&) = delete;
601240b186cSVernon Mauery IpmiProvider& operator=(const IpmiProvider&) = delete;
602240b186cSVernon Mauery IpmiProvider(IpmiProvider&&) = delete;
603240b186cSVernon Mauery IpmiProvider& operator=(IpmiProvider&&) = delete;
604240b186cSVernon Mauery
605240b186cSVernon Mauery /** @brief dlopen a shared object file by path
606240b186cSVernon Mauery * @param[in] filename - path of shared object to open
607240b186cSVernon Mauery */
IpmiProvideripmi::IpmiProvider608240b186cSVernon Mauery explicit IpmiProvider(const char* fname) : addr(nullptr), name(fname)
609240b186cSVernon Mauery {
610e808bae0SVernon Mauery lg2::debug("Open IPMI provider library: {PROVIDER}", "PROVIDER", name);
611240b186cSVernon Mauery try
612240b186cSVernon Mauery {
613240b186cSVernon Mauery addr = dlopen(name.c_str(), RTLD_NOW);
614240b186cSVernon Mauery }
615a2ad2da8SPatrick Williams catch (const std::exception& e)
616240b186cSVernon Mauery {
617e808bae0SVernon Mauery lg2::error("ERROR opening IPMI provider {PROVIDER}: {ERROR}",
618e808bae0SVernon Mauery "PROVIDER", name, "ERROR", e);
619240b186cSVernon Mauery }
620240b186cSVernon Mauery catch (...)
621240b186cSVernon Mauery {
62203d7a4beSVernon Mauery const char* what = currentExceptionType();
623e808bae0SVernon Mauery lg2::error("ERROR opening IPMI provider {PROVIDER}: {ERROR}",
624e808bae0SVernon Mauery "PROVIDER", name, "ERROR", what);
625240b186cSVernon Mauery }
626240b186cSVernon Mauery if (!isOpen())
627240b186cSVernon Mauery {
628e808bae0SVernon Mauery lg2::error("ERROR opening IPMI provider {PROVIDER}: {ERROR}",
629e808bae0SVernon Mauery "PROVIDER", name, "ERROR", dlerror());
630240b186cSVernon Mauery }
631240b186cSVernon Mauery }
632240b186cSVernon Mauery
~IpmiProvideripmi::IpmiProvider633240b186cSVernon Mauery ~IpmiProvider()
634240b186cSVernon Mauery {
635240b186cSVernon Mauery if (isOpen())
636240b186cSVernon Mauery {
637240b186cSVernon Mauery dlclose(addr);
638240b186cSVernon Mauery }
639240b186cSVernon Mauery }
isOpenipmi::IpmiProvider640240b186cSVernon Mauery bool isOpen() const
641240b186cSVernon Mauery {
642240b186cSVernon Mauery return (nullptr != addr);
643240b186cSVernon Mauery }
644240b186cSVernon Mauery };
645240b186cSVernon Mauery
646240b186cSVernon Mauery // Plugin libraries need to contain .so either at the end or in the middle
647240b186cSVernon Mauery constexpr const char ipmiPluginExtn[] = ".so";
648240b186cSVernon Mauery
649240b186cSVernon Mauery /* return a list of self-closing library handles */
loadProviders(const fs::path & ipmiLibsPath)650240b186cSVernon Mauery std::forward_list<IpmiProvider> loadProviders(const fs::path& ipmiLibsPath)
651240b186cSVernon Mauery {
652240b186cSVernon Mauery std::vector<fs::path> libs;
653240b186cSVernon Mauery for (const auto& libPath : fs::directory_iterator(ipmiLibsPath))
654240b186cSVernon Mauery {
655b0ab5fe8SVernon Mauery std::error_code ec;
656240b186cSVernon Mauery fs::path fname = libPath.path();
657b0ab5fe8SVernon Mauery if (fs::is_symlink(fname, ec) || ec)
658b0ab5fe8SVernon Mauery {
659b0ab5fe8SVernon Mauery // it's a symlink or some other error; skip it
660b0ab5fe8SVernon Mauery continue;
661b0ab5fe8SVernon Mauery }
662240b186cSVernon Mauery while (fname.has_extension())
663240b186cSVernon Mauery {
664240b186cSVernon Mauery fs::path extn = fname.extension();
665240b186cSVernon Mauery if (extn == ipmiPluginExtn)
666240b186cSVernon Mauery {
667240b186cSVernon Mauery libs.push_back(libPath.path());
668240b186cSVernon Mauery break;
669240b186cSVernon Mauery }
670240b186cSVernon Mauery fname.replace_extension();
671240b186cSVernon Mauery }
672240b186cSVernon Mauery }
673240b186cSVernon Mauery std::sort(libs.begin(), libs.end());
674240b186cSVernon Mauery
675240b186cSVernon Mauery std::forward_list<IpmiProvider> handles;
676240b186cSVernon Mauery for (auto& lib : libs)
677240b186cSVernon Mauery {
678240b186cSVernon Mauery #ifdef __IPMI_DEBUG__
679e808bae0SVernon Mauery lg2::debug("Registering handler {HANDLER}", "HANDLER", lib);
680240b186cSVernon Mauery #endif
681240b186cSVernon Mauery handles.emplace_front(lib.c_str());
682240b186cSVernon Mauery }
683240b186cSVernon Mauery return handles;
684240b186cSVernon Mauery }
685240b186cSVernon Mauery
686240b186cSVernon Mauery } // namespace ipmi
687240b186cSVernon Mauery
688240b186cSVernon Mauery #ifdef ALLOW_DEPRECATED_API
689240b186cSVernon Mauery /* legacy registration */
ipmi_register_callback(ipmi_netfn_t netFn,ipmi_cmd_t cmd,ipmi_context_t context,ipmid_callback_t handler,ipmi_cmd_privilege_t priv)690240b186cSVernon Mauery void ipmi_register_callback(ipmi_netfn_t netFn, ipmi_cmd_t cmd,
691240b186cSVernon Mauery ipmi_context_t context, ipmid_callback_t handler,
692240b186cSVernon Mauery ipmi_cmd_privilege_t priv)
693240b186cSVernon Mauery {
694be376306SVernon Mauery auto h = ipmi::makeLegacyHandler(handler, context);
695240b186cSVernon Mauery // translate priv from deprecated enum to current
696240b186cSVernon Mauery ipmi::Privilege realPriv;
697240b186cSVernon Mauery switch (priv)
698240b186cSVernon Mauery {
699240b186cSVernon Mauery case PRIVILEGE_CALLBACK:
700240b186cSVernon Mauery realPriv = ipmi::Privilege::Callback;
701240b186cSVernon Mauery break;
702240b186cSVernon Mauery case PRIVILEGE_USER:
703240b186cSVernon Mauery realPriv = ipmi::Privilege::User;
704240b186cSVernon Mauery break;
705240b186cSVernon Mauery case PRIVILEGE_OPERATOR:
706240b186cSVernon Mauery realPriv = ipmi::Privilege::Operator;
707240b186cSVernon Mauery break;
708240b186cSVernon Mauery case PRIVILEGE_ADMIN:
709240b186cSVernon Mauery realPriv = ipmi::Privilege::Admin;
710240b186cSVernon Mauery break;
711240b186cSVernon Mauery case PRIVILEGE_OEM:
712240b186cSVernon Mauery realPriv = ipmi::Privilege::Oem;
713240b186cSVernon Mauery break;
714240b186cSVernon Mauery case SYSTEM_INTERFACE:
715240b186cSVernon Mauery realPriv = ipmi::Privilege::Admin;
716240b186cSVernon Mauery break;
717240b186cSVernon Mauery default:
718240b186cSVernon Mauery realPriv = ipmi::Privilege::Admin;
719240b186cSVernon Mauery break;
720240b186cSVernon Mauery }
721e8d43235SVernon Mauery // The original ipmi_register_callback allowed for group OEM handlers
722e8d43235SVernon Mauery // to be registered via this same interface. It just so happened that
723e8d43235SVernon Mauery // all the handlers were part of the DCMI group, so default to that.
724e8d43235SVernon Mauery if (netFn == NETFUN_GRPEXT)
725e8d43235SVernon Mauery {
72682cffccdSVernon Mauery ipmi::impl::registerGroupHandler(ipmi::prioOpenBmcBase, ipmi::groupDCMI,
72782cffccdSVernon Mauery cmd, realPriv, h);
728e8d43235SVernon Mauery }
729e8d43235SVernon Mauery else
730e8d43235SVernon Mauery {
731e8d43235SVernon Mauery ipmi::impl::registerHandler(ipmi::prioOpenBmcBase, netFn, cmd, realPriv,
732e8d43235SVernon Mauery h);
733e8d43235SVernon Mauery }
734240b186cSVernon Mauery }
735240b186cSVernon Mauery
736f984a01fSVernon Mauery namespace oem
737f984a01fSVernon Mauery {
738f984a01fSVernon Mauery
739f984a01fSVernon Mauery class LegacyRouter : public oem::Router
740f984a01fSVernon Mauery {
741f984a01fSVernon Mauery public:
~LegacyRouter()742fbc6c9d7SPatrick Williams virtual ~LegacyRouter() {}
743f984a01fSVernon Mauery
744f984a01fSVernon Mauery /// Enable message routing to begin.
activate()745fbc6c9d7SPatrick Williams void activate() override {}
746f984a01fSVernon Mauery
registerHandler(Number oen,ipmi_cmd_t cmd,Handler handler)747f984a01fSVernon Mauery void registerHandler(Number oen, ipmi_cmd_t cmd, Handler handler) override
748f984a01fSVernon Mauery {
749f984a01fSVernon Mauery auto h = ipmi::makeLegacyHandler(std::forward<Handler>(handler));
750f984a01fSVernon Mauery ipmi::impl::registerOemHandler(ipmi::prioOpenBmcBase, oen, cmd,
751f984a01fSVernon Mauery ipmi::Privilege::Admin, h);
752f984a01fSVernon Mauery }
753f984a01fSVernon Mauery };
754f984a01fSVernon Mauery static LegacyRouter legacyRouter;
755f984a01fSVernon Mauery
mutableRouter()756f984a01fSVernon Mauery Router* mutableRouter()
757f984a01fSVernon Mauery {
758f984a01fSVernon Mauery return &legacyRouter;
759f984a01fSVernon Mauery }
760f984a01fSVernon Mauery
761f984a01fSVernon Mauery } // namespace oem
762f984a01fSVernon Mauery
763240b186cSVernon Mauery /* legacy alternative to executionEntry */
handleLegacyIpmiCommand(sdbusplus::message_t & m)7645d82f474SPatrick Williams void handleLegacyIpmiCommand(sdbusplus::message_t& m)
765240b186cSVernon Mauery {
76623b7021cSVernon Mauery // make a copy so the next two moves don't wreak havoc on the stack
7675d82f474SPatrick Williams sdbusplus::message_t b{m};
76826a386ffSEd Tanous (void)boost::asio::spawn(
769531223fbSJayanth Othayoth *getIoContext(),
770531223fbSJayanth Othayoth [b = std::move(b)](boost::asio::yield_context yield) {
7715d82f474SPatrick Williams sdbusplus::message_t m{std::move(b)};
77255f5d53cSSnehalatha Venkatesh unsigned char seq = 0, netFn = 0, lun = 0, cmd = 0;
773997952afSVernon Mauery ipmi::SecureBuffer data;
774240b186cSVernon Mauery
775240b186cSVernon Mauery m.read(seq, netFn, lun, cmd, data);
77633298af1SVernon Mauery std::shared_ptr<sdbusplus::asio::connection> bus = getSdBus();
77723b7021cSVernon Mauery auto ctx = std::make_shared<ipmi::Context>(
778531223fbSJayanth Othayoth bus, netFn, lun, cmd, 0, 0, 0, ipmi::Privilege::Admin, 0, 0,
779531223fbSJayanth Othayoth yield);
780240b186cSVernon Mauery auto request = std::make_shared<ipmi::message::Request>(
781997952afSVernon Mauery ctx, std::forward<ipmi::SecureBuffer>(data));
78223b7021cSVernon Mauery ipmi::message::Response::ptr response =
78323b7021cSVernon Mauery ipmi::executeIpmiCommand(request);
784240b186cSVernon Mauery
785240b186cSVernon Mauery // Responses in IPMI require a bit set. So there ya go...
786240b186cSVernon Mauery netFn |= 0x01;
787240b186cSVernon Mauery
788240b186cSVernon Mauery const char *dest, *path;
789240b186cSVernon Mauery constexpr const char* DBUS_INTF = "org.openbmc.HostIpmi";
790240b186cSVernon Mauery
791240b186cSVernon Mauery dest = m.get_sender();
792240b186cSVernon Mauery path = m.get_path();
79323b7021cSVernon Mauery boost::system::error_code ec;
794531223fbSJayanth Othayoth bus->yield_method_call(yield, ec, dest, path, DBUS_INTF,
795531223fbSJayanth Othayoth "sendMessage", seq, netFn, lun, cmd,
796531223fbSJayanth Othayoth response->cc, response->payload.raw);
79723b7021cSVernon Mauery if (ec)
79823b7021cSVernon Mauery {
799e808bae0SVernon Mauery lg2::error(
800e808bae0SVernon Mauery "Failed to send response to requestor ({NETFN}/{CMD}): {ERROR}",
801531223fbSJayanth Othayoth "ERROR", ec.message(), "SENDER", dest, "NETFN", lg2::hex,
802531223fbSJayanth Othayoth netFn, "CMD", lg2::hex, cmd);
80323b7021cSVernon Mauery }
804531223fbSJayanth Othayoth },
805531223fbSJayanth Othayoth {});
806240b186cSVernon Mauery }
807240b186cSVernon Mauery
808240b186cSVernon Mauery #endif /* ALLOW_DEPRECATED_API */
809240b186cSVernon Mauery
810240b186cSVernon Mauery // Calls host command manager to do the right thing for the command
811240b186cSVernon Mauery using CommandHandler = phosphor::host::command::CommandHandler;
812240b186cSVernon Mauery std::unique_ptr<phosphor::host::command::Manager> cmdManager;
ipmid_send_cmd_to_host(CommandHandler && cmd)813240b186cSVernon Mauery void ipmid_send_cmd_to_host(CommandHandler&& cmd)
814240b186cSVernon Mauery {
8155e096a28SVernon Mauery cmdManager->execute(std::forward<CommandHandler>(cmd));
816240b186cSVernon Mauery }
817240b186cSVernon Mauery
ipmid_get_host_cmd_manager()818240b186cSVernon Mauery std::unique_ptr<phosphor::host::command::Manager>& ipmid_get_host_cmd_manager()
819240b186cSVernon Mauery {
820240b186cSVernon Mauery return cmdManager;
821240b186cSVernon Mauery }
822240b186cSVernon Mauery
82320ff333dSVernon Mauery // These are symbols that are present in libipmid, but not expected
82420ff333dSVernon Mauery // to be used except here (or maybe a unit test), so declare them here
82520ff333dSVernon Mauery extern void setIoContext(std::shared_ptr<boost::asio::io_context>& newIo);
82620ff333dSVernon Mauery extern void setSdBus(std::shared_ptr<sdbusplus::asio::connection>& newBus);
82720ff333dSVernon Mauery
main(int argc,char * argv[])828240b186cSVernon Mauery int main(int argc, char* argv[])
829240b186cSVernon Mauery {
830240b186cSVernon Mauery // Connect to system bus
83120ff333dSVernon Mauery auto io = std::make_shared<boost::asio::io_context>();
83220ff333dSVernon Mauery setIoContext(io);
833240b186cSVernon Mauery if (argc > 1 && std::string(argv[1]) == "-session")
834240b186cSVernon Mauery {
835240b186cSVernon Mauery sd_bus_default_user(&bus);
836240b186cSVernon Mauery }
837240b186cSVernon Mauery else
838240b186cSVernon Mauery {
839240b186cSVernon Mauery sd_bus_default_system(&bus);
840240b186cSVernon Mauery }
84120ff333dSVernon Mauery auto sdbusp = std::make_shared<sdbusplus::asio::connection>(*io, bus);
84220ff333dSVernon Mauery setSdBus(sdbusp);
843240b186cSVernon Mauery
8443b301a3aSPatrick Williams // TODO: Hack to keep the sdEvents running.... Not sure why the sd_event
8453b301a3aSPatrick Williams // queue stops running if we don't have a timer that keeps re-arming
8463b301a3aSPatrick Williams sdbusplus::Timer t2([]() { ; });
8473b301a3aSPatrick Williams t2.start(std::chrono::microseconds(500000), true);
8483b301a3aSPatrick Williams
8493b301a3aSPatrick Williams // TODO: Remove all vestiges of sd_event from phosphor-host-ipmid
8503b301a3aSPatrick Williams // until that is done, add the sd_event wrapper to the io object
8513b301a3aSPatrick Williams sdbusplus::asio::sd_event_wrapper sdEvents(*io);
8523b301a3aSPatrick Williams
853240b186cSVernon Mauery cmdManager = std::make_unique<phosphor::host::command::Manager>(*sdbusp);
854240b186cSVernon Mauery
855240b186cSVernon Mauery // Register all command providers and filters
8564ec4e40aSVernon Mauery std::forward_list<ipmi::IpmiProvider> providers =
8574ec4e40aSVernon Mauery ipmi::loadProviders(HOST_IPMI_LIB_PATH);
858240b186cSVernon Mauery
859240b186cSVernon Mauery #ifdef ALLOW_DEPRECATED_API
860240b186cSVernon Mauery // listen on deprecated signal interface for kcs/bt commands
861240b186cSVernon Mauery constexpr const char* FILTER = "type='signal',interface='org.openbmc."
862240b186cSVernon Mauery "HostIpmi',member='ReceivedMessage'";
8635d82f474SPatrick Williams sdbusplus::bus::match_t oldIpmiInterface(*sdbusp, FILTER,
864240b186cSVernon Mauery handleLegacyIpmiCommand);
865240b186cSVernon Mauery #endif /* ALLOW_DEPRECATED_API */
866240b186cSVernon Mauery
867735ee953SVernon Mauery // set up bus name watching to match channels with bus names
8685d82f474SPatrick Williams sdbusplus::bus::match_t nameOwnerChanged(
869735ee953SVernon Mauery *sdbusp,
870735ee953SVernon Mauery sdbusplus::bus::match::rules::nameOwnerChanged() +
871735ee953SVernon Mauery sdbusplus::bus::match::rules::arg0namespace(
872735ee953SVernon Mauery ipmi::ipmiDbusChannelMatch),
873735ee953SVernon Mauery ipmi::nameChangeHandler);
874*9f3073a6SJayanth Othayoth ipmi::doListNames(*sdbusp);
875735ee953SVernon Mauery
876b0094a72SJames Feist int exitCode = 0;
8771b7f6f2dSVernon Mauery // set up boost::asio signal handling
8781318a5edSPatrick Williams std::function<SignalResponse(int)> stopAsioRunLoop = [&io, &exitCode](
8791318a5edSPatrick Williams int signalNumber) {
880e808bae0SVernon Mauery lg2::info("Received signal {SIGNAL}; quitting", "SIGNAL", signalNumber);
8811b7f6f2dSVernon Mauery io->stop();
882b0094a72SJames Feist exitCode = signalNumber;
8831b7f6f2dSVernon Mauery return SignalResponse::breakExecution;
8841b7f6f2dSVernon Mauery };
8851b7f6f2dSVernon Mauery registerSignalHandler(ipmi::prioOpenBmcBase, SIGINT, stopAsioRunLoop);
8861b7f6f2dSVernon Mauery registerSignalHandler(ipmi::prioOpenBmcBase, SIGTERM, stopAsioRunLoop);
8871b7f6f2dSVernon Mauery
888369406e6SRichard Marian Thomaiyar sdbusp->request_name("xyz.openbmc_project.Ipmi.Host");
889369406e6SRichard Marian Thomaiyar // Add bindings for inbound IPMI requests
890369406e6SRichard Marian Thomaiyar auto server = sdbusplus::asio::object_server(sdbusp);
891369406e6SRichard Marian Thomaiyar auto iface = server.add_interface("/xyz/openbmc_project/Ipmi",
892369406e6SRichard Marian Thomaiyar "xyz.openbmc_project.Ipmi.Server");
893369406e6SRichard Marian Thomaiyar iface->register_method("execute", ipmi::executionEntry);
894369406e6SRichard Marian Thomaiyar iface->initialize();
895369406e6SRichard Marian Thomaiyar
896240b186cSVernon Mauery io->run();
897240b186cSVernon Mauery
8981b7f6f2dSVernon Mauery // destroy all the IPMI handlers so the providers can unload safely
8991b7f6f2dSVernon Mauery ipmi::handlerMap.clear();
9001b7f6f2dSVernon Mauery ipmi::groupHandlerMap.clear();
9011b7f6f2dSVernon Mauery ipmi::oemHandlerMap.clear();
9021b7f6f2dSVernon Mauery ipmi::filterList.clear();
9031b7f6f2dSVernon Mauery // unload the provider libraries
9044ec4e40aSVernon Mauery providers.clear();
9051b7f6f2dSVernon Mauery
906b0094a72SJames Feist std::exit(exitCode);
907240b186cSVernon Mauery }
908