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