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/log.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::xyz::openbmc_project::Common::Error;
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,
43                              const std::string& interface,
44                              const std::string& serviceRoot,
45                              const std::string& match)
46 {
47     std::vector<DbusInterface> interfaces;
48     interfaces.emplace_back(interface);
49 
50     auto depth = 0;
51 
52     auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
53                                           MAPPER_INTF, "GetSubTree");
54 
55     mapperCall.append(serviceRoot, depth, interfaces);
56 
57     auto mapperReply = bus.call(mapperCall);
58     if (mapperReply.is_method_error())
59     {
60         log<level::ERR>("Error in mapper call");
61         elog<InternalFailure>();
62     }
63 
64     ObjectTree objectTree;
65     mapperReply.read(objectTree);
66 
67     if (objectTree.empty())
68     {
69         log<level::ERR>("No Object has implemented the interface",
70                         entry("INTERFACE=%s", interface.c_str()));
71         elog<InternalFailure>();
72     }
73 
74     DbusObjectInfo objectInfo;
75 
76     // if match is empty then return the first object
77     if (match == "")
78     {
79         objectInfo = std::make_pair(
80             objectTree.begin()->first,
81             std::move(objectTree.begin()->second.begin()->first));
82         return objectInfo;
83     }
84 
85     // else search the match string in the object path
86     auto found = std::find_if(objectTree.begin(), objectTree.end(),
87                               [&match](const auto& object) {
88         return (object.first.find(match) != std::string::npos);
89     });
90 
91     if (found == objectTree.end())
92     {
93         log<level::ERR>("Failed to find object which matches",
94                         entry("MATCH=%s", match.c_str()));
95         elog<InternalFailure>();
96         // elog<> throws an exception.
97     }
98 
99     return make_pair(found->first, std::move(found->second.begin()->first));
100 }
101 
102 Value getDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
103                       const std::string& objPath, const std::string& interface,
104                       const std::string& property,
105                       std::chrono::microseconds timeout)
106 {
107     Value value;
108 
109     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
110                                       PROP_INTF, METHOD_GET);
111 
112     method.append(interface, property);
113 
114     auto reply = bus.call(method, timeout.count());
115 
116     if (reply.is_method_error())
117     {
118         log<level::ERR>("Failed to get property",
119                         entry("PROPERTY=%s", property.c_str()),
120                         entry("PATH=%s", objPath.c_str()),
121                         entry("INTERFACE=%s", interface.c_str()));
122         elog<InternalFailure>();
123     }
124 
125     reply.read(value);
126 
127     return value;
128 }
129 
130 PropertyMap getAllDbusProperties(sdbusplus::bus_t& bus,
131                                  const std::string& service,
132                                  const std::string& objPath,
133                                  const std::string& interface,
134                                  std::chrono::microseconds timeout)
135 {
136     PropertyMap properties;
137 
138     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
139                                       PROP_INTF, METHOD_GET_ALL);
140 
141     method.append(interface);
142 
143     auto reply = bus.call(method, timeout.count());
144 
145     if (reply.is_method_error())
146     {
147         log<level::ERR>("Failed to get all properties",
148                         entry("PATH=%s", objPath.c_str()),
149                         entry("INTERFACE=%s", interface.c_str()));
150         elog<InternalFailure>();
151     }
152 
153     reply.read(properties);
154     return properties;
155 }
156 
157 ObjectValueTree getManagedObjects(sdbusplus::bus_t& bus,
158                                   const std::string& service,
159                                   const std::string& objPath)
160 {
161     ipmi::ObjectValueTree interfaces;
162 
163     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
164                                       "org.freedesktop.DBus.ObjectManager",
165                                       "GetManagedObjects");
166 
167     auto reply = bus.call(method);
168 
169     if (reply.is_method_error())
170     {
171         log<level::ERR>("Failed to get managed objects",
172                         entry("PATH=%s", objPath.c_str()));
173         elog<InternalFailure>();
174     }
175 
176     reply.read(interfaces);
177     return interfaces;
178 }
179 
180 void setDbusProperty(sdbusplus::bus_t& bus, const std::string& service,
181                      const std::string& objPath, const std::string& interface,
182                      const std::string& property, const Value& value,
183                      std::chrono::microseconds timeout)
184 {
185     auto method = bus.new_method_call(service.c_str(), objPath.c_str(),
186                                       PROP_INTF, METHOD_SET);
187 
188     method.append(interface, property, value);
189 
190     if (!bus.call(method, timeout.count()))
191     {
192         log<level::ERR>("Failed to set property",
193                         entry("PROPERTY=%s", property.c_str()),
194                         entry("PATH=%s", objPath.c_str()),
195                         entry("INTERFACE=%s", interface.c_str()));
196         elog<InternalFailure>();
197     }
198 }
199 
200 ServiceCache::ServiceCache(const std::string& intf, const std::string& path) :
201     intf(intf), path(path), cachedService(std::nullopt),
202     cachedBusName(std::nullopt)
203 {}
204 
205 ServiceCache::ServiceCache(std::string&& intf, std::string&& path) :
206     intf(std::move(intf)), path(std::move(path)), cachedService(std::nullopt),
207     cachedBusName(std::nullopt)
208 {}
209 
210 const std::string& ServiceCache::getService(sdbusplus::bus_t& bus)
211 {
212     if (!isValid(bus))
213     {
214         cachedBusName = bus.get_unique_name();
215         cachedService = ::ipmi::getService(bus, intf, path);
216     }
217     return cachedService.value();
218 }
219 
220 void ServiceCache::invalidate()
221 {
222     cachedBusName = std::nullopt;
223     cachedService = std::nullopt;
224 }
225 
226 sdbusplus::message_t ServiceCache::newMethodCall(sdbusplus::bus_t& bus,
227                                                  const char* intf,
228                                                  const char* method)
229 {
230     return bus.new_method_call(getService(bus).c_str(), path.c_str(), intf,
231                                method);
232 }
233 
234 bool ServiceCache::isValid(sdbusplus::bus_t& bus) const
235 {
236     return cachedService && cachedBusName == bus.get_unique_name();
237 }
238 
239 std::string getService(sdbusplus::bus_t& bus, const std::string& intf,
240                        const std::string& path)
241 {
242     auto mapperCall = bus.new_method_call("xyz.openbmc_project.ObjectMapper",
243                                           "/xyz/openbmc_project/object_mapper",
244                                           "xyz.openbmc_project.ObjectMapper",
245                                           "GetObject");
246 
247     mapperCall.append(path);
248     mapperCall.append(std::vector<std::string>({intf}));
249 
250     auto mapperResponseMsg = bus.call(mapperCall);
251 
252     if (mapperResponseMsg.is_method_error())
253     {
254         throw std::runtime_error("ERROR in mapper call");
255     }
256 
257     std::map<std::string, std::vector<std::string>> mapperResponse;
258     mapperResponseMsg.read(mapperResponse);
259 
260     if (mapperResponse.begin() == mapperResponse.end())
261     {
262         throw std::runtime_error("ERROR in reading the mapper response");
263     }
264 
265     return mapperResponse.begin()->first;
266 }
267 
268 ipmi::ObjectTree getAllDbusObjects(sdbusplus::bus_t& bus,
269                                    const std::string& serviceRoot,
270                                    const std::string& interface,
271                                    const std::string& match)
272 {
273     std::vector<std::string> interfaces;
274     interfaces.emplace_back(interface);
275 
276     auto depth = 0;
277 
278     auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
279                                           MAPPER_INTF, "GetSubTree");
280 
281     mapperCall.append(serviceRoot, depth, interfaces);
282 
283     auto mapperReply = bus.call(mapperCall);
284     if (mapperReply.is_method_error())
285     {
286         log<level::ERR>("Error in mapper call",
287                         entry("SERVICEROOT=%s", serviceRoot.c_str()),
288                         entry("INTERFACE=%s", interface.c_str()));
289 
290         elog<InternalFailure>();
291     }
292 
293     ObjectTree objectTree;
294     mapperReply.read(objectTree);
295 
296     for (auto it = objectTree.begin(); it != objectTree.end();)
297     {
298         if (it->first.find(match) == std::string::npos)
299         {
300             it = objectTree.erase(it);
301         }
302         else
303         {
304             ++it;
305         }
306     }
307 
308     return objectTree;
309 }
310 
311 void deleteAllDbusObjects(sdbusplus::bus_t& bus, const std::string& serviceRoot,
312                           const std::string& interface,
313                           const std::string& match)
314 {
315     try
316     {
317         auto objectTree = getAllDbusObjects(bus, serviceRoot, interface, match);
318 
319         for (auto& object : objectTree)
320         {
321             method_no_args::callDbusMethod(bus, object.second.begin()->first,
322                                            object.first, DELETE_INTERFACE,
323                                            "Delete");
324         }
325     }
326     catch (const sdbusplus::exception_t& e)
327     {
328         log<level::INFO>("sdbusplus exception - Unable to delete the objects",
329                          entry("ERROR=%s", e.what()),
330                          entry("INTERFACE=%s", interface.c_str()),
331                          entry("SERVICE=%s", serviceRoot.c_str()));
332     }
333 }
334 
335 static inline std::string convertToString(const InterfaceList& interfaces)
336 {
337     std::string intfStr;
338     for (const auto& intf : interfaces)
339     {
340         intfStr += "," + intf;
341     }
342     return intfStr;
343 }
344 
345 ObjectTree getAllAncestors(sdbusplus::bus_t& bus, const std::string& path,
346                            InterfaceList&& interfaces)
347 {
348     auto mapperCall = bus.new_method_call(MAPPER_BUS_NAME, MAPPER_OBJ,
349                                           MAPPER_INTF, "GetAncestors");
350     mapperCall.append(path, interfaces);
351 
352     auto mapperReply = bus.call(mapperCall);
353     if (mapperReply.is_method_error())
354     {
355         log<level::ERR>(
356             "Error in mapper call", entry("PATH=%s", path.c_str()),
357             entry("INTERFACES=%s", convertToString(interfaces).c_str()));
358 
359         elog<InternalFailure>();
360     }
361 
362     ObjectTree objectTree;
363     mapperReply.read(objectTree);
364 
365     if (objectTree.empty())
366     {
367         log<level::ERR>(
368             "No Object has implemented the interface",
369             entry("PATH=%s", path.c_str()),
370             entry("INTERFACES=%s", convertToString(interfaces).c_str()));
371         elog<InternalFailure>();
372     }
373 
374     return objectTree;
375 }
376 
377 namespace method_no_args
378 {
379 
380 void callDbusMethod(sdbusplus::bus_t& bus, const std::string& service,
381                     const std::string& objPath, const std::string& interface,
382                     const std::string& method)
383 
384 {
385     auto busMethod = bus.new_method_call(service.c_str(), objPath.c_str(),
386                                          interface.c_str(), method.c_str());
387 
388     auto reply = bus.call(busMethod);
389 
390     if (reply.is_method_error())
391     {
392         log<level::ERR>("Failed to execute method",
393                         entry("METHOD=%s", method.c_str()),
394                         entry("PATH=%s", objPath.c_str()),
395                         entry("INTERFACE=%s", interface.c_str()));
396         elog<InternalFailure>();
397     }
398 }
399 
400 } // namespace method_no_args
401 
402 /********* Begin co-routine yielding alternatives ***************/
403 
404 boost::system::error_code getService(Context::ptr ctx, const std::string& intf,
405                                      const std::string& path,
406                                      std::string& service)
407 {
408     boost::system::error_code ec;
409     std::map<std::string, std::vector<std::string>> mapperResponse =
410         ctx->bus->yield_method_call<decltype(mapperResponse)>(
411             ctx->yield, ec, "xyz.openbmc_project.ObjectMapper",
412             "/xyz/openbmc_project/object_mapper",
413             "xyz.openbmc_project.ObjectMapper", "GetObject", path,
414             std::vector<std::string>({intf}));
415 
416     if (!ec)
417     {
418         service = std::move(mapperResponse.begin()->first);
419     }
420     return ec;
421 }
422 
423 boost::system::error_code getDbusObject(Context::ptr ctx,
424                                         const std::string& interface,
425                                         const std::string& subtreePath,
426                                         const std::string& match,
427                                         DbusObjectInfo& dbusObject)
428 {
429     std::vector<DbusInterface> interfaces;
430     interfaces.emplace_back(interface);
431 
432     auto depth = 0;
433     boost::system::error_code ec;
434     ObjectTree objectTree = ctx->bus->yield_method_call<ObjectTree>(
435         ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree",
436         subtreePath, depth, interfaces);
437 
438     if (ec)
439     {
440         return ec;
441     }
442 
443     if (objectTree.empty())
444     {
445         log<level::ERR>("No Object has implemented the interface",
446                         entry("INTERFACE=%s", interface.c_str()),
447                         entry("NETFN=%x", ctx->netFn),
448                         entry("CMD=%x,", ctx->cmd));
449         return boost::system::errc::make_error_code(
450             boost::system::errc::no_such_process);
451     }
452 
453     // if match is empty then return the first object
454     if (match == "")
455     {
456         dbusObject = std::make_pair(
457             std::move(objectTree.begin()->first),
458             std::move(objectTree.begin()->second.begin()->first));
459         return ec;
460     }
461 
462     // else search the match string in the object path
463     auto found = std::find_if(objectTree.begin(), objectTree.end(),
464                               [&match](const auto& object) {
465         return (object.first.find(match) != std::string::npos);
466     });
467 
468     if (found == objectTree.end())
469     {
470         log<level::ERR>("Failed to find object which matches",
471                         entry("MATCH=%s", match.c_str()),
472                         entry("NETFN=%x", ctx->netFn),
473                         entry("CMD=%x,", ctx->cmd));
474         // set ec
475         return boost::system::errc::make_error_code(
476             boost::system::errc::no_such_file_or_directory);
477     }
478 
479     dbusObject = std::make_pair(std::move(found->first),
480                                 std::move(found->second.begin()->first));
481     return ec;
482 }
483 
484 boost::system::error_code getAllDbusProperties(Context::ptr ctx,
485                                                const std::string& service,
486                                                const std::string& objPath,
487                                                const std::string& interface,
488                                                PropertyMap& properties)
489 {
490     boost::system::error_code ec;
491     properties = ctx->bus->yield_method_call<PropertyMap>(
492         ctx->yield, ec, service.c_str(), objPath.c_str(), PROP_INTF,
493         METHOD_GET_ALL, interface);
494     return ec;
495 }
496 
497 boost::system::error_code
498     setDbusProperty(Context::ptr ctx, const std::string& service,
499                     const std::string& objPath, const std::string& interface,
500                     const std::string& property, const Value& value)
501 {
502     boost::system::error_code ec;
503     ctx->bus->yield_method_call(ctx->yield, ec, service.c_str(),
504                                 objPath.c_str(), PROP_INTF, METHOD_SET,
505                                 interface, property, value);
506     return ec;
507 }
508 
509 boost::system::error_code getAllDbusObjects(Context::ptr ctx,
510                                             const std::string& serviceRoot,
511                                             const std::string& interface,
512                                             const std::string& match,
513                                             ObjectTree& objectTree)
514 {
515     boost::system::error_code ec;
516     std::vector<std::string> interfaces;
517     interfaces.emplace_back(interface);
518 
519     auto depth = 0;
520 
521     objectTree = ctx->bus->yield_method_call<ObjectTree>(
522         ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF, "GetSubTree",
523         serviceRoot, depth, interfaces);
524 
525     if (ec)
526     {
527         return ec;
528     }
529 
530     for (auto it = objectTree.begin(); it != objectTree.end();)
531     {
532         if (it->first.find(match) == std::string::npos)
533         {
534             it = objectTree.erase(it);
535         }
536         else
537         {
538             ++it;
539         }
540     }
541 
542     return ec;
543 }
544 
545 boost::system::error_code deleteAllDbusObjects(Context::ptr ctx,
546                                                const std::string& serviceRoot,
547                                                const std::string& interface,
548                                                const std::string& match)
549 {
550     ObjectTree objectTree;
551     boost::system::error_code ec =
552         getAllDbusObjects(ctx, serviceRoot, interface, match, objectTree);
553     if (ec)
554     {
555         return ec;
556     }
557 
558     for (auto& object : objectTree)
559     {
560         ctx->bus->yield_method_call(ctx->yield, ec,
561                                     object.second.begin()->first, object.first,
562                                     DELETE_INTERFACE, "Delete");
563         if (ec)
564         {
565             log<level::ERR>("Failed to delete all objects",
566                             entry("INTERFACE=%s", interface.c_str()),
567                             entry("SERVICE=%s", serviceRoot.c_str()),
568                             entry("NETFN=%x", ctx->netFn),
569                             entry("CMD=%x,", ctx->cmd),
570                             entry("ERROR=%s", ec.message().c_str()));
571             break;
572         }
573     }
574     return ec;
575 }
576 
577 boost::system::error_code getManagedObjects(Context::ptr ctx,
578                                             const std::string& service,
579                                             const std::string& objPath,
580                                             ObjectValueTree& objects)
581 {
582     boost::system::error_code ec;
583     objects = ctx->bus->yield_method_call<ipmi::ObjectValueTree>(
584         ctx->yield, ec, service.c_str(), objPath.c_str(),
585         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
586     return ec;
587 }
588 
589 boost::system::error_code getAllAncestors(Context::ptr ctx,
590                                           const std::string& path,
591                                           const InterfaceList& interfaces,
592                                           ObjectTree& objectTree)
593 {
594     std::string interfaceList = convertToString(interfaces);
595 
596     boost::system::error_code ec;
597     objectTree = ctx->bus->yield_method_call<ObjectTree>(
598         ctx->yield, ec, MAPPER_BUS_NAME, MAPPER_OBJ, MAPPER_INTF,
599         "GetAncestors", path, interfaceList);
600 
601     if (ec)
602     {
603         return ec;
604     }
605 
606     if (objectTree.empty())
607     {
608         log<level::ERR>("No Object has implemented the interface",
609                         entry("PATH=%s", path.c_str()),
610                         entry("INTERFACES=%s", interfaceList.c_str()));
611         elog<InternalFailure>();
612     }
613 
614     return ec;
615 }
616 
617 boost::system::error_code callDbusMethod(Context::ptr ctx,
618                                          const std::string& service,
619                                          const std::string& objPath,
620                                          const std::string& interface,
621                                          const std::string& method)
622 {
623     boost::system::error_code ec;
624     ctx->bus->yield_method_call(ctx->yield, ec, service, objPath, interface,
625                                 method);
626     return ec;
627 }
628 
629 /********* End co-routine yielding alternatives ***************/
630 
631 ipmi::Cc i2cWriteRead(std::string i2cBus, const uint8_t slaveAddr,
632                       std::vector<uint8_t> writeData,
633                       std::vector<uint8_t>& readBuf)
634 {
635     // Open the i2c device, for low-level combined data write/read
636     int i2cDev = ::open(i2cBus.c_str(), O_RDWR | O_CLOEXEC);
637     if (i2cDev < 0)
638     {
639         log<level::ERR>("Failed to open i2c bus",
640                         phosphor::logging::entry("BUS=%s", i2cBus.c_str()));
641         return ipmi::ccInvalidFieldRequest;
642     }
643 
644     const size_t writeCount = writeData.size();
645     const size_t readCount = readBuf.size();
646     int msgCount = 0;
647     i2c_msg i2cmsg[2] = {};
648     if (writeCount)
649     {
650         // Data will be writtern to the slave address
651         i2cmsg[msgCount].addr = slaveAddr;
652         i2cmsg[msgCount].flags = 0x00;
653         i2cmsg[msgCount].len = writeCount;
654         i2cmsg[msgCount].buf = writeData.data();
655         msgCount++;
656     }
657     if (readCount)
658     {
659         // Data will be read into the buffer from the slave address
660         i2cmsg[msgCount].addr = slaveAddr;
661         i2cmsg[msgCount].flags = I2C_M_RD;
662         i2cmsg[msgCount].len = readCount;
663         i2cmsg[msgCount].buf = readBuf.data();
664         msgCount++;
665     }
666 
667     i2c_rdwr_ioctl_data msgReadWrite = {};
668     msgReadWrite.msgs = i2cmsg;
669     msgReadWrite.nmsgs = msgCount;
670 
671     // Perform the combined write/read
672     int ret = ::ioctl(i2cDev, I2C_RDWR, &msgReadWrite);
673     ::close(i2cDev);
674 
675     if (ret < 0)
676     {
677         log<level::ERR>("I2C WR Failed!",
678                         phosphor::logging::entry("RET=%d", ret));
679         return ipmi::ccUnspecifiedError;
680     }
681     if (readCount)
682     {
683         readBuf.resize(msgReadWrite.msgs[msgCount - 1].len);
684     }
685 
686     return ipmi::ccSuccess;
687 }
688 
689 } // namespace ipmi
690