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