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