1 #include <arpa/inet.h>
2 #include <dirent.h>
3 #include <fcntl.h>
4 #include <linux/i2c-dev.h>
5 #include <linux/i2c.h>
6 #include <net/if.h>
7 #include <sys/ioctl.h>
8 #include <sys/types.h>
9 #include <unistd.h>
10 
11 #include <ipmid/utils.hpp>
12 #include <phosphor-logging/elog-errors.hpp>
13 #include <phosphor-logging/lg2.hpp>
14 #include <sdbusplus/message/types.hpp>
15 #include <xyz/openbmc_project/Common/error.hpp>
16 
17 #include <algorithm>
18 #include <chrono>
19 
20 namespace ipmi
21 {
22 
23 using namespace phosphor::logging;
24 using namespace sdbusplus::error::xyz::openbmc_project::common;
25 
26 namespace network
27 {
28 
29 /** @brief checks if the given ip is Link Local Ip or not.
30  *  @param[in] ipaddress - IPAddress.
31  */
32 bool isLinkLocalIP(const std::string& ipaddress);
33 
34 } // namespace network
35 
36 // TODO There may be cases where an interface is implemented by multiple
37 //  objects,to handle such cases we are interested on that object
38 //  which are on interested busname.
39 //  Currently mapper doesn't give the readable busname(gives busid) so we can't
40 //  use busname to find the object,will do later once the support is there.
41 
42 DbusObjectInfo
getDbusObject(sdbusplus::bus_t & bus,const std::string & interface,const std::string & serviceRoot,const std::string & match)43     getDbusObject(sdbusplus::bus_t& bus, const std::string& interface,
44                   const std::string& serviceRoot, const std::string& match)
45 {
46     std::vector<DbusInterface> interfaces;
47     interfaces.emplace_back(interface);
48 
49     ObjectTree objectTree = getSubTree(bus, interfaces, serviceRoot);
50     if (objectTree.empty())
51     {
52         lg2::error("No Object has implemented the interface: {INTERFACE}",
53                    "INTERFACE", interface);
54         elog<InternalFailure>();
55     }
56 
57     DbusObjectInfo objectInfo;
58 
59     // if match is empty then return the first object
60     if (match == "")
61     {
62         objectInfo = std::make_pair(
63             objectTree.begin()->first,
64             std::move(objectTree.begin()->second.begin()->first));
65         return objectInfo;
66     }
67 
68     // else search the match string in the object path
69     auto found = std::find_if(
70         objectTree.begin(), objectTree.end(), [&match](const auto& object) {
71             return (object.first.find(match) != std::string::npos);
72         });
73 
74     if (found == objectTree.end())
75     {
76         lg2::error("Failed to find object which matches: {MATCH}", "MATCH",
77                    match);
78         elog<InternalFailure>();
79         // elog<> throws an exception.
80     }
81 
82     return make_pair(found->first, std::move(found->second.begin()->first));
83 }
84 
getDbusProperty(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,std::chrono::microseconds timeout)85 Value getDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
86                       const std::string& objPath, const std::string& interface,
87                       const std::string& property,
88                       std::chrono::microseconds timeout)
89 {
90     Value value;
91 
92     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
93                                       PROP_INTF, METHOD_GET);
94 
95     method.append(interface, property);
96 
97     auto reply = bus.call(method, timeout.count());
98     reply.read(value);
99 
100     return value;
101 }
102 
getAllDbusProperties(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,std::chrono::microseconds timeout)103 PropertyMap getAllDbusProperties(
104     sdbusplus::bus_t& bus, const std::string& service,
105     const std::string& objPath, const std::string& interface,
106     std::chrono::microseconds timeout)
107 {
108     PropertyMap properties;
109 
110     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
111                                       PROP_INTF, METHOD_GET_ALL);
112 
113     method.append(interface);
114 
115     auto reply = bus.call(method, timeout.count());
116     reply.read(properties);
117 
118     return properties;
119 }
120 
getManagedObjects(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath)121 ObjectValueTree getManagedObjects(sdbusplus::bus_t& bus,
122                                   const std::string& service,
123                                   const std::string& objPath)
124 {
125     ipmi::ObjectValueTree interfaces;
126 
127     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
128                                       "org.freedesktop.DBus.ObjectManager",
129                                       "GetManagedObjects");
130     auto reply = bus.call(method);
131     reply.read(interfaces);
132 
133     return interfaces;
134 }
135 
setDbusProperty(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,const Value & value,std::chrono::microseconds timeout)136 void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
137                      const std::string& objPath, const std::string& interface,
138                      const std::string& property, const Value& value,
139                      std::chrono::microseconds timeout)
140 {
141     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
142                                       PROP_INTF, METHOD_SET);
143 
144     method.append(interface, property, value);
145 
146     if (!bus.call(method, timeout.count()))
147     {
148         lg2::error("Failed to set {PROPERTY}, path: {PATH}, "
149                    "interface: {INTERFACE}",
150                    "PROPERTY", property, "PATH", objPath, "INTERFACE",
151                    interface);
152         elog<InternalFailure>();
153     }
154 }
155 
ServiceCache(const std::string & intf,const std::string & path)156 ServiceCache::ServiceCache(const std::string& intf, const std::string& path) :
157     intf(intf), path(path), cachedService(std::nullopt),
158     cachedBusName(std::nullopt)
159 {}
160 
ServiceCache(std::string && intf,std::string && path)161 ServiceCache::ServiceCache(std::string&& intf, std::string&& path) :
162     intf(std::move(intf)), path(std::move(path)), cachedService(std::nullopt),
163     cachedBusName(std::nullopt)
164 {}
165 
getService(sdbusplus::bus_t & bus)166 const std::string& ServiceCache::getService(sdbusplus::bus_t& bus)
167 {
168     if (!isValid(bus))
169     {
170         cachedBusName = bus.get_unique_name();
171         cachedService = ::ipmi::getService(bus, intf, path);
172     }
173     return cachedService.value();
174 }
175 
invalidate()176 void ServiceCache::invalidate()
177 {
178     cachedBusName = std::nullopt;
179     cachedService = std::nullopt;
180 }
181 
newMethodCall(sdbusplus::bus_t & bus,const char * intf,const char * method)182 sdbusplus::message_t ServiceCache::newMethodCall(
183     sdbusplus::bus_t& bus, const char* intf, const char* method)
184 {
185     return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf,
186                                method);
187 }
188 
isValid(sdbusplus::bus_t & bus) const189 bool ServiceCache::isValid(sdbusplus::bus_t& bus) const
190 {
191     return cachedService && cachedBusName == bus.get_unique_name();
192 }
193 
getService(sdbusplus::bus_t & bus,const std::string & intf,const std::string & path)194 std::string getService(sdbusplus::bus_t& bus, const std::string& intf,
195                        const std::string& path)
196 {
197     auto mapperCall =
198         bus.new_method_call("xyz.openbmc_project.ObjectMapper",
199                             "/xyz/openbmc_project/object_mapper",
200                             "xyz.openbmc_project.ObjectMapper", "GetObject");
201 
202     mapperCall.append(path);
203     mapperCall.append(std::vector<std::string>({intf}));
204 
205     auto mapperResponseMsg = bus.call(mapperCall);
206 
207     std::map<std::string, std::vector<std::string>> mapperResponse;
208     mapperResponseMsg.read(mapperResponse);
209 
210     if (mapperResponse.begin() == mapperResponse.end())
211     {
212         throw std::runtime_error("ERROR in reading the mapper response");
213     }
214 
215     return mapperResponse.begin()->first;
216 }
217 
getSubTree(sdbusplus::bus_t & bus,const InterfaceList & interfaces,const std::string & subtreePath,int32_t depth)218 ObjectTree getSubTree(sdbusplus::bus_t& bus, const InterfaceList& interfaces,
219                       const std::string& subtreePath, int32_t depth)
220 {
221     auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
222                                           MAPPER_INTF, "GetSubTree");
223 
224     mapperCall.append(subtreePath, depth, interfaces);
225 
226     auto mapperReply = bus.call(mapperCall);
227     ObjectTree objectTree;
228     mapperReply.read(objectTree);
229 
230     return objectTree;
231 }
232 
233 ipmi::ObjectTree
getAllDbusObjects(sdbusplus::bus_t & bus,const std::string & serviceRoot,const std::string & interface,const std::string & match)234     getAllDbusObjects(sdbusplus::bus_t& bus, const std::string& serviceRoot,
235                       const std::string& interface, const std::string& match)
236 {
237     std::vector<std::string> interfaces;
238     interfaces.emplace_back(interface);
239 
240     ObjectTree objectTree = getSubTree(bus, interfaces, serviceRoot);
241     for (auto it = objectTree.begin(); it != objectTree.end();)
242     {
243         if (it->first.find(match) == std::string::npos)
244         {
245             it = objectTree.erase(it);
246         }
247         else
248         {
249             ++it;
250         }
251     }
252 
253     return objectTree;
254 }
255 
deleteAllDbusObjects(sdbusplus::bus_t & bus,const std::string & serviceRoot,const std::string & interface,const std::string & match)256 void deleteAllDbusObjects(sdbusplus::bus_t& bus, const std::string& serviceRoot,
257                           const std::string& interface,
258                           const std::string& match)
259 {
260     try
261     {
262         auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
263 
264         for (auto& object : objectTree)
265         {
266             method_no_args::callDbusMethod(bus, object.second.begin()->first,
267                                            object.first, DELETE_INTERFACE,
268                                            "Delete");
269         }
270     }
271     catch (const sdbusplus::exception_t& e)
272     {
273         lg2::info("sdbusplus exception - Unable to delete the objects, "
274                   "service: {SERVICE}, interface: {INTERFACE}, error: {ERROR}",
275                   "SERVICE", serviceRoot, "INTERFACE", interface, "ERROR", e);
276     }
277 }
278 
convertToString(const InterfaceList & interfaces)279 static inline std::string convertToString(const InterfaceList& interfaces)
280 {
281     std::string intfStr;
282     for (const auto& intf : interfaces)
283     {
284         intfStr += "," + intf;
285     }
286     return intfStr;
287 }
288 
getAllAncestors(sdbusplus::bus_t & bus,const std::string & path,InterfaceList && interfaces)289 ObjectTree getAllAncestors(sdbusplus::bus_t& bus, const std::string& path,
290                            InterfaceList&& interfaces)
291 {
292     auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
293                                           MAPPER_INTF, "GetAncestors");
294     mapperCall.append(path, interfaces);
295 
296     auto mapperReply = bus.call(mapperCall);
297     ObjectTree objectTree;
298     mapperReply.read(objectTree);
299 
300     if (objectTree.empty())
301     {
302         lg2::error("No Object has implemented the interface: {INTERFACE}, "
303                    "path: {PATH}",
304                    "INTERFACE", convertToString(interfaces), "PATH", path);
305         elog<InternalFailure>();
306     }
307 
308     return objectTree;
309 }
310 
311 namespace method_no_args
312 {
313 
callDbusMethod(sdbusplus::bus_t & bus,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & method)314 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service,
315                     const std::string& objPath, const std::string& interface,
316                     const std::string& method)
317 
318 {
319     auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
320                                          interface.c_str(), method.c_str());
321     auto reply = bus.call(busMethod);
322 }
323 
324 } // namespace method_no_args
325 
326 /********* Begin co-routine yielding alternatives ***************/
327 
328 boost::system::error_code
getService(Context::ptr ctx,const std::string & intf,const std::string & path,std::string & service)329     getService(Context::ptr ctx, const std::string& intf,
330                const std::string& path, std::string& service)
331 {
332     boost::system::error_code ec;
333     std::map<std::string, std::vector<std::string>> mapperResponse =
334         ctx->bus->yield_method_call<decltype(mapperResponse)>(
335             ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
336             "/xyz/openbmc_project/object_mapper",
337             "xyz.openbmc_project.ObjectMapper", "GetObject", path,
338             std::vector<std::string>({intf}));
339 
340     if (!ec)
341     {
342         service = std::move(mapperResponse.begin()->first);
343     }
344     return ec;
345 }
346 
getSubTree(Context::ptr ctx,const InterfaceList & interfaces,const std::string & subtreePath,int32_t depth,ObjectTree & objectTree)347 boost::system::error_code getSubTree(
348     Context::ptr ctx, const InterfaceList& interfaces,
349     const std::string& subtreePath, int32_t depth, ObjectTree& objectTree)
350 {
351     boost::system::error_code ec;
352     objectTree = ctx->bus->yield_method_call<ObjectTree>(
353         ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree",
354         subtreePath, depth, interfaces);
355 
356     return ec;
357 }
358 
359 boost::system::error_code
getDbusObject(Context::ptr ctx,const std::string & interface,const std::string & subtreePath,const std::string & match,DbusObjectInfo & dbusObject)360     getDbusObject(Context::ptr ctx, const std::string& interface,
361                   const std::string& subtreePath, const std::string& match,
362                   DbusObjectInfo& dbusObject)
363 {
364     std::vector<DbusInterface> interfaces;
365     interfaces.emplace_back(interface);
366 
367     auto depth = 0;
368     ObjectTree objectTree;
369     boost::system::error_code ec =
370         getSubTree(ctx, interfaces, subtreePath, depth, objectTree);
371 
372     if (ec)
373     {
374         return ec;
375     }
376 
377     if (objectTree.empty())
378     {
379         lg2::error("No Object has implemented the interface: {INTERFACE}, "
380                    "NetFn: {NETFN}, Cmd: {CMD}",
381                    "INTERFACE", interface, "NETFN", lg2::hex, ctx->netFn, "CMD",
382                    lg2::hex, ctx->cmd);
383         return boost::system::errc::make_error_code(
384             boost::system::errc::no_such_process);
385     }
386 
387     // if match is empty then return the first object
388     if (match == "")
389     {
390         dbusObject = std::make_pair(
391             std::move(objectTree.begin()->first),
392             std::move(objectTree.begin()->second.begin()->first));
393         return ec;
394     }
395 
396     // else search the match string in the object path
397     auto found = std::find_if(
398         objectTree.begin(), objectTree.end(), [&match](const auto& object) {
399             return (object.first.find(match) != std::string::npos);
400         });
401 
402     if (found == objectTree.end())
403     {
404         lg2::error("Failed to find object which matches: {MATCH}, "
405                    "NetFn: {NETFN}, Cmd: {CMD}",
406                    "MATCH", match, "NETFN", lg2::hex, ctx->netFn, "CMD",
407                    lg2::hex, ctx->cmd);
408         // set ec
409         return boost::system::errc::make_error_code(
410             boost::system::errc::no_such_file_or_directory);
411     }
412 
413     dbusObject = std::make_pair(std::move(found->first),
414                                 std::move(found->second.begin()->first));
415     return ec;
416 }
417 
getAllDbusProperties(Context::ptr ctx,const std::string & service,const std::string & objPath,const std::string & interface,PropertyMap & properties)418 boost::system::error_code getAllDbusProperties(
419     Context::ptr ctx, const std::string& service, const std::string& objPath,
420     const std::string& interface, PropertyMap& properties)
421 {
422     boost::system::error_code ec;
423     properties = ctx->bus->yield_method_call<PropertyMap>(
424         ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF,
425         METHOD_GET_ALL, interface);
426     return ec;
427 }
428 
429 boost::system::error_code
setDbusProperty(Context::ptr ctx,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & property,const Value & value)430     setDbusProperty(Context::ptr ctx, const std::string& service,
431                     const std::string& objPath, const std::string& interface,
432                     const std::string& property, const Value& value)
433 {
434     boost::system::error_code ec;
435     ctx->bus->yield_method_call(ctx->yield, ec, service.c_str(),
436                                 objPath.c_str(), PROP_INTF, METHOD_SET,
437                                 interface, property, value);
438     return ec;
439 }
440 
441 boost::system::error_code
getAllDbusObjects(Context::ptr ctx,const std::string & serviceRoot,const std::string & interface,const std::string & match,ObjectTree & objectTree)442     getAllDbusObjects(Context::ptr ctx, const std::string& serviceRoot,
443                       const std::string& interface, const std::string& match,
444                       ObjectTree& objectTree)
445 {
446     std::vector<std::string> interfaces;
447     interfaces.emplace_back(interface);
448 
449     auto depth = 0;
450     boost::system::error_code ec =
451         getSubTree(ctx, interfaces, serviceRoot, depth, objectTree);
452     if (ec)
453     {
454         return ec;
455     }
456 
457     for (auto it = objectTree.begin(); it != objectTree.end();)
458     {
459         if (it->first.find(match) == std::string::npos)
460         {
461             it = objectTree.erase(it);
462         }
463         else
464         {
465             ++it;
466         }
467     }
468 
469     return ec;
470 }
471 
472 boost::system::error_code
deleteAllDbusObjects(Context::ptr ctx,const std::string & serviceRoot,const std::string & interface,const std::string & match)473     deleteAllDbusObjects(Context::ptr ctx, const std::string& serviceRoot,
474                          const std::string& interface, const std::string& match)
475 {
476     ObjectTree objectTree;
477     boost::system::error_code ec =
478         getAllDbusObjects(ctx, serviceRoot, interface, match, objectTree);
479     if (ec)
480     {
481         return ec;
482     }
483 
484     for (auto& object : objectTree)
485     {
486         ctx->bus->yield_method_call(ctx->yield, ec,
487                                     object.second.begin()->first, object.first,
488                                     DELETE_INTERFACE, "Delete");
489         if (ec)
490         {
491             lg2::error("Failed to delete all objects, service: {SERVICE}, "
492                        "interface: {INTERFACE}, NetFn: {NETFN}, "
493                        "Cmd: {CMD}, Error: {ERROR}",
494                        "SERVICE", serviceRoot, "INTERFACE", interface, "NETFN",
495                        lg2::hex, ctx->netFn, "CMD", lg2::hex, ctx->cmd, "ERROR",
496                        ec.message());
497             break;
498         }
499     }
500     return ec;
501 }
502 
503 boost::system::error_code
getManagedObjects(Context::ptr ctx,const std::string & service,const std::string & objPath,ObjectValueTree & objects)504     getManagedObjects(Context::ptr ctx, const std::string& service,
505                       const std::string& objPath, ObjectValueTree& objects)
506 {
507     boost::system::error_code ec;
508     objects = ctx->bus->yield_method_call<ipmi::ObjectValueTree>(
509         ctx->yield, ec, service.c_str(), objPath.c_str(),
510         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
511     return ec;
512 }
513 
514 boost::system::error_code
getAllAncestors(Context::ptr ctx,const std::string & path,const InterfaceList & interfaces,ObjectTree & objectTree)515     getAllAncestors(Context::ptr ctx, const std::string& path,
516                     const InterfaceList& interfaces, ObjectTree& objectTree)
517 {
518     std::string interfaceList = convertToString(interfaces);
519 
520     boost::system::error_code ec;
521     objectTree = ctx->bus->yield_method_call<ObjectTree>(
522         ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
523         "GetAncestors", path, interfaceList);
524 
525     if (ec)
526     {
527         return ec;
528     }
529 
530     if (objectTree.empty())
531     {
532         lg2::error("No Object has implemented the interface: {INTERFACE}, "
533                    "path: {PATH}",
534                    "INTERFACE", interfaceList, "PATH", path);
535         elog<InternalFailure>();
536     }
537 
538     return ec;
539 }
540 
callDbusMethod(Context::ptr ctx,const std::string & service,const std::string & objPath,const std::string & interface,const std::string & method)541 boost::system::error_code callDbusMethod(
542     Context::ptr ctx, const std::string& service, const std::string& objPath,
543     const std::string& interface, const std::string& method)
544 {
545     boost::system::error_code ec;
546     ctx->bus->yield_method_call(ctx->yield, ec, service, objPath, interface,
547                                 method);
548     return ec;
549 }
550 
551 /********* End co-routine yielding alternatives ***************/
552 
i2cWriteRead(std::string i2cBus,const uint8_t targetAddr,std::vector<uint8_t> writeData,std::vector<uint8_t> & readBuf)553 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t targetAddr,
554                       std::vector<uint8_t> writeData,
555                       std::vector<uint8_t>& readBuf)
556 {
557     // Open the i2c device, for low-level combined data write/read
558     int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
559     if (i2cDev < 0)
560     {
561         lg2::error("Failed to open i2c bus: {BUS}", "BUS", i2cBus);
562         return ipmi::ccInvalidFieldRequest;
563     }
564 
565     const size_t writeCount = writeData.size();
566     const size_t readCount = readBuf.size();
567     int msgCount = 0;
568     i2c_msg i2cmsg[2] = {};
569     if (writeCount)
570     {
571         // Data will be writtern to the target address
572         i2cmsg[msgCount].addr = targetAddr;
573         i2cmsg[msgCount].flags = 0x00;
574         i2cmsg[msgCount].len = writeCount;
575         i2cmsg[msgCount].buf = writeData.data();
576         msgCount++;
577     }
578     if (readCount)
579     {
580         // Data will be read into the buffer from the target address
581         i2cmsg[msgCount].addr = targetAddr;
582         i2cmsg[msgCount].flags = I2C_M_RD;
583         i2cmsg[msgCount].len = readCount;
584         i2cmsg[msgCount].buf = readBuf.data();
585         msgCount++;
586     }
587 
588     i2c_rdwr_ioctl_data msgReadWrite = {};
589     msgReadWrite.msgs = i2cmsg;
590     msgReadWrite.nmsgs = msgCount;
591 
592     // Perform the combined write/read
593     int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
594     ::close(i2cDev);
595 
596     if (ret < 0)
597     {
598         lg2::error("I2C WR Failed! {RET}", "RET", ret);
599         return ipmi::ccUnspecifiedError;
600     }
601     if (readCount)
602     {
603         readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
604     }
605 
606     return ipmi::ccSuccess;
607 }
608 
609 } // namespace ipmi
610