blob: 0b23e50dcacc4f591f8560b170c07a92045993bb [file] [log] [blame]
Patrick Venture95269db2018-08-31 09:19:17 -07001#include <boost/asio.hpp>
Vernon Mauery261e72b2018-09-25 12:34:25 -07002#include <boost/asio/spawn.hpp>
James Feist284a0f92018-04-05 15:28:16 -07003#include <sdbusplus/asio/connection.hpp>
James Feistfce038a2018-04-13 15:43:13 -07004#include <sdbusplus/asio/object_server.hpp>
Vernon Mauery035c73b2018-09-05 12:15:27 -07005#include <sdbusplus/asio/sd_event.hpp>
Patrick Venture95269db2018-08-31 09:19:17 -07006#include <sdbusplus/bus.hpp>
Vernon Mauery076d14a2018-10-02 15:10:20 -07007#include <sdbusplus/exception.hpp>
Patrick Venture95269db2018-08-31 09:19:17 -07008#include <sdbusplus/server.hpp>
Vernon Mauery035c73b2018-09-05 12:15:27 -07009#include <sdbusplus/timer.hpp>
Patrick Williams127b8ab2020-05-21 15:24:19 -050010
11#include <chrono>
12#include <ctime>
13#include <iostream>
William A. Kennington III4274c112018-11-26 09:50:13 -080014#include <variant>
James Feist284a0f92018-04-05 15:28:16 -070015
William A. Kennington III4274c112018-11-26 09:50:13 -080016using variant = std::variant<int, std::string>;
Vernon Mauery076d14a2018-10-02 15:10:20 -070017
James Feistfce038a2018-04-13 15:43:13 -070018int foo(int test)
19{
Vernon Mauery076d14a2018-10-02 15:10:20 -070020 std::cout << "foo(" << test << ") -> " << (test + 1) << "\n";
James Feistfce038a2018-04-13 15:43:13 -070021 return ++test;
22}
23
Vernon Mauery076d14a2018-10-02 15:10:20 -070024// called from coroutine context, can make yielding dbus calls
25int fooYield(boost::asio::yield_context yield,
26 std::shared_ptr<sdbusplus::asio::connection> conn, int test)
27{
28 // fetch the real value from testFunction
29 boost::system::error_code ec;
30 std::cout << "fooYield(yield, " << test << ")...\n";
31 int testCount = conn->yield_method_call<int>(
Vernon Mauery37a5e612019-05-07 16:53:50 -070032 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
Vernon Mauery076d14a2018-10-02 15:10:20 -070033 "xyz.openbmc_project.test", "TestFunction", test);
34 if (ec || testCount != (test + 1))
35 {
36 std::cout << "call to foo failed: ec = " << ec << '\n';
37 return -1;
38 }
39 std::cout << "yielding call to foo OK! (-> " << testCount << ")\n";
40 return testCount;
41}
42
Patrick Williams10d7aa12021-11-19 11:36:18 -060043int methodWithMessage(sdbusplus::message_t& /*m*/, int test)
Vernon Mauery8ce61e42018-08-28 08:54:59 -070044{
Vernon Mauery076d14a2018-10-02 15:10:20 -070045 std::cout << "methodWithMessage(m, " << test << ") -> " << (test + 1)
46 << "\n";
Vernon Mauery8ce61e42018-08-28 08:54:59 -070047 return ++test;
48}
49
Vernon Mauery2c8f93f2018-09-05 12:02:39 -070050int voidBar(void)
51{
Vernon Mauery076d14a2018-10-02 15:10:20 -070052 std::cout << "voidBar() -> 42\n";
Vernon Mauery2c8f93f2018-09-05 12:02:39 -070053 return 42;
54}
55
Vernon Mauery261e72b2018-09-25 12:34:25 -070056void do_start_async_method_call_one(
57 std::shared_ptr<sdbusplus::asio::connection> conn,
58 boost::asio::yield_context yield)
59{
60 boost::system::error_code ec;
61 variant testValue;
Vernon Mauery37a5e612019-05-07 16:53:50 -070062 conn->yield_method_call<>(yield, ec, "xyz.openbmc_project.asio-test",
Vernon Mauery261e72b2018-09-25 12:34:25 -070063 "/xyz/openbmc_project/test",
64 "org.freedesktop.DBus.Properties", "Set",
65 "xyz.openbmc_project.test", "int", variant(24));
66 testValue = conn->yield_method_call<variant>(
Vernon Mauery37a5e612019-05-07 16:53:50 -070067 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
Vernon Mauery261e72b2018-09-25 12:34:25 -070068 "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.test",
69 "int");
William A. Kennington III4274c112018-11-26 09:50:13 -080070 if (!ec && std::get<int>(testValue) == 24)
Vernon Mauery261e72b2018-09-25 12:34:25 -070071 {
72 std::cout << "async call to Properties.Get serialized via yield OK!\n";
73 }
74 else
75 {
William A. Kennington III4274c112018-11-26 09:50:13 -080076 std::cout << "ec = " << ec << ": " << std::get<int>(testValue) << "\n";
Vernon Mauery261e72b2018-09-25 12:34:25 -070077 }
78 conn->yield_method_call<void>(
Vernon Mauery37a5e612019-05-07 16:53:50 -070079 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
Vernon Mauery261e72b2018-09-25 12:34:25 -070080 "org.freedesktop.DBus.Properties", "Set", "xyz.openbmc_project.test",
81 "int", variant(42));
82 testValue = conn->yield_method_call<variant>(
Vernon Mauery37a5e612019-05-07 16:53:50 -070083 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
Vernon Mauery261e72b2018-09-25 12:34:25 -070084 "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.test",
85 "int");
William A. Kennington III4274c112018-11-26 09:50:13 -080086 if (!ec && std::get<int>(testValue) == 42)
Vernon Mauery261e72b2018-09-25 12:34:25 -070087 {
88 std::cout << "async call to Properties.Get serialized via yield OK!\n";
89 }
90 else
91 {
William A. Kennington III4274c112018-11-26 09:50:13 -080092 std::cout << "ec = " << ec << ": " << std::get<int>(testValue) << "\n";
Vernon Mauery261e72b2018-09-25 12:34:25 -070093 }
94}
95
Vernon Mauery076d14a2018-10-02 15:10:20 -070096void do_start_async_ipmi_call(std::shared_ptr<sdbusplus::asio::connection> conn,
97 boost::asio::yield_context yield)
98{
99 auto method = conn->new_method_call("xyz.openbmc_project.asio-test",
100 "/xyz/openbmc_project/test",
101 "xyz.openbmc_project.test", "execute");
102 constexpr uint8_t netFn = 6;
103 constexpr uint8_t lun = 0;
104 constexpr uint8_t cmd = 1;
105 std::map<std::string, variant> options = {{"username", variant("admin")},
106 {"privilege", variant(4)}};
107 std::vector<uint8_t> commandData = {4, 3, 2, 1};
108 method.append(netFn, lun, cmd, commandData, options);
109 boost::system::error_code ec;
Patrick Williams10d7aa12021-11-19 11:36:18 -0600110 sdbusplus::message_t reply = conn->async_send(method, yield[ec]);
Vernon Mauery076d14a2018-10-02 15:10:20 -0700111 std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
112 tupleOut;
113 try
114 {
115 reply.read(tupleOut);
116 }
117 catch (const sdbusplus::exception::SdBusError& e)
118 {
119 std::cerr << "failed to unpack; sig is " << reply.get_signature()
120 << "\n";
121 }
122 auto& [rnetFn, rlun, rcmd, cc, responseData] = tupleOut;
123 std::vector<uint8_t> expRsp = {1, 2, 3, 4};
124 if (rnetFn == uint8_t(netFn + 1) && rlun == lun && rcmd == cmd && cc == 0 &&
125 responseData == expRsp)
126 {
127 std::cerr << "ipmi call returns OK!\n";
128 }
129 else
130 {
131 std::cerr << "ipmi call returns unexpected response\n";
132 }
133}
134
Patrick Williams78b78032020-05-20 10:32:05 -0500135auto ipmiInterface(boost::asio::yield_context /*yield*/, uint8_t netFn,
136 uint8_t lun, uint8_t cmd, std::vector<uint8_t>& /*data*/,
137 const std::map<std::string, variant>& /*options*/)
Vernon Mauery076d14a2018-10-02 15:10:20 -0700138{
139 std::vector<uint8_t> reply = {1, 2, 3, 4};
140 uint8_t cc = 0;
141 std::cerr << "ipmiInterface:execute(" << int(netFn) << int(cmd) << ")\n";
142 return std::make_tuple(uint8_t(netFn + 1), lun, cmd, cc, reply);
143}
144
145void do_start_async_to_yield(std::shared_ptr<sdbusplus::asio::connection> conn,
146 boost::asio::yield_context yield)
Vernon Mauery261e72b2018-09-25 12:34:25 -0700147{
148 boost::system::error_code ec;
Vernon Mauery076d14a2018-10-02 15:10:20 -0700149 int testValue = 0;
Vernon Maueryc0771902019-05-07 16:53:50 -0700150 testValue = conn->yield_method_call<int>(
151 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
152 "xyz.openbmc_project.test", "TestYieldFunction", int(41));
153
Vernon Mauery076d14a2018-10-02 15:10:20 -0700154 if (!ec && testValue == 42)
155 {
156 std::cout
157 << "yielding call to TestYieldFunction serialized via yield OK!\n";
Vernon Mauery261e72b2018-09-25 12:34:25 -0700158 }
159 else
160 {
161 std::cout << "ec = " << ec << ": " << testValue << "\n";
162 }
Vernon Maueryc0771902019-05-07 16:53:50 -0700163
164 ec.clear();
165 auto badValue = conn->yield_method_call<std::string>(
166 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
167 "xyz.openbmc_project.test", "TestYieldFunction", int(41));
168
169 if (!ec)
170 {
171 std::cout
172 << "yielding call to TestYieldFunction returned the wrong type\n";
173 }
174 else
175 {
176 std::cout << "TestYieldFunction expected error: " << ec << "\n";
177 }
178
179 ec.clear();
180 auto unUsedValue = conn->yield_method_call<std::string>(
181 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
182 "xyz.openbmc_project.test", "TestYieldFunctionNotExits", int(41));
183
184 if (!ec)
185 {
186 std::cout << "TestYieldFunctionNotExists returned unexpectedly\n";
187 }
188 else
189 {
190 std::cout << "TestYieldFunctionNotExits expected error: " << ec << "\n";
191 }
Vernon Mauery261e72b2018-09-25 12:34:25 -0700192}
193
Vernon Mauery076d14a2018-10-02 15:10:20 -0700194int server()
James Feist284a0f92018-04-05 15:28:16 -0700195{
James Feist284a0f92018-04-05 15:28:16 -0700196 // setup connection to dbus
Ed Tanousc7d104d2019-01-02 14:15:50 -0800197 boost::asio::io_context io;
James Feist284a0f92018-04-05 15:28:16 -0700198 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
199
James Feistfce038a2018-04-13 15:43:13 -0700200 // test object server
201 conn->request_name("xyz.openbmc_project.asio-test");
202 auto server = sdbusplus::asio::object_server(conn);
203 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
204 server.add_interface("/xyz/openbmc_project/test",
205 "xyz.openbmc_project.test");
206 // test generic properties
207 iface->register_property("int", 33,
208 sdbusplus::asio::PropertyPermission::readWrite);
209 std::vector<std::string> myStringVec = {"some", "test", "data"};
210 std::vector<std::string> myStringVec2 = {"more", "test", "data"};
211
212 iface->register_property("myStringVec", myStringVec,
213 sdbusplus::asio::PropertyPermission::readWrite);
214 iface->register_property("myStringVec2", myStringVec2);
215
216 // test properties with specialized callbacks
217 iface->register_property("lessThan50", 23,
218 // custom set
219 [](const int& req, int& propertyValue) {
220 if (req >= 50)
221 {
222 return -EINVAL;
223 }
224 propertyValue = req;
225 return 1; // success
226 });
227 iface->register_property(
228 "TrailTime", std::string("foo"),
229 // custom set
230 [](const std::string& req, std::string& propertyValue) {
231 propertyValue = req;
232 return 1; // success
233 },
234 // custom get
235 [](const std::string& property) {
236 auto now = std::chrono::system_clock::now();
237 auto timePoint = std::chrono::system_clock::to_time_t(now);
238 return property + std::ctime(&timePoint);
239 });
240
241 // test method creation
242 iface->register_method("TestMethod", [](const int32_t& callCount) {
Vernon Mauery261e72b2018-09-25 12:34:25 -0700243 return std::make_tuple(callCount,
244 "success: " + std::to_string(callCount));
James Feistfce038a2018-04-13 15:43:13 -0700245 });
246
247 iface->register_method("TestFunction", foo);
248
Vernon Mauery076d14a2018-10-02 15:10:20 -0700249 // fooYield has boost::asio::yield_context as first argument
250 // so will be executed in coroutine context if called
251 iface->register_method("TestYieldFunction",
252 [conn](boost::asio::yield_context yield, int val) {
253 return fooYield(yield, conn, val);
254 });
255
Vernon Mauery8ce61e42018-08-28 08:54:59 -0700256 iface->register_method("TestMethodWithMessage", methodWithMessage);
257
Vernon Mauery2c8f93f2018-09-05 12:02:39 -0700258 iface->register_method("VoidFunctionReturnsInt", voidBar);
259
Vernon Mauery076d14a2018-10-02 15:10:20 -0700260 iface->register_method("execute", ipmiInterface);
261
James Feistfce038a2018-04-13 15:43:13 -0700262 iface->initialize();
Vernon Mauery076d14a2018-10-02 15:10:20 -0700263
264 io.run();
265
266 return 0;
267}
268
269int client()
270{
271 using GetSubTreeType = std::vector<std::pair<
272 std::string,
273 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
Patrick Williams10d7aa12021-11-19 11:36:18 -0600274 using message = sdbusplus::message_t;
Vernon Mauery076d14a2018-10-02 15:10:20 -0700275
276 // setup connection to dbus
Ed Tanousc7d104d2019-01-02 14:15:50 -0800277 boost::asio::io_context io;
Vernon Mauery076d14a2018-10-02 15:10:20 -0700278 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
279
280 int ready = 0;
281 while (!ready)
282 {
283 auto readyMsg = conn->new_method_call(
284 "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
285 "xyz.openbmc_project.test", "VoidFunctionReturnsInt");
286 try
287 {
288 message intMsg = conn->call(readyMsg);
289 intMsg.read(ready);
290 }
Patrick Williamsb4667652021-10-06 12:16:17 -0500291 catch (const sdbusplus::exception::SdBusError& e)
Vernon Mauery076d14a2018-10-02 15:10:20 -0700292 {
293 ready = 0;
294 // pause to give the server a chance to start up
295 usleep(10000);
296 }
297 }
298
299 // test async method call and async send
300 auto mesg =
301 conn->new_method_call("xyz.openbmc_project.ObjectMapper",
302 "/xyz/openbmc_project/object_mapper",
303 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
304
305 static const auto depth = 2;
306 static const std::vector<std::string> interfaces = {
307 "xyz.openbmc_project.Sensor.Value"};
308 mesg.append("/xyz/openbmc_project/Sensors", depth, interfaces);
309
310 conn->async_send(mesg, [](boost::system::error_code ec, message& ret) {
311 std::cout << "async_send callback\n";
312 if (ec || ret.is_method_error())
313 {
314 std::cerr << "error with async_send\n";
315 return;
316 }
317 GetSubTreeType data;
318 ret.read(data);
319 for (auto& item : data)
320 {
321 std::cout << item.first << "\n";
322 }
323 });
324
325 conn->async_method_call(
326 [](boost::system::error_code ec, GetSubTreeType& subtree) {
327 std::cout << "async_method_call callback\n";
328 if (ec)
329 {
330 std::cerr << "error with async_method_call\n";
331 return;
332 }
333 for (auto& item : subtree)
334 {
335 std::cout << item.first << "\n";
336 }
337 },
338 "xyz.openbmc_project.ObjectMapper",
339 "/xyz/openbmc_project/object_mapper",
340 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
341 "/org/openbmc/control", 2, std::vector<std::string>());
Vernon Mauery035c73b2018-09-05 12:15:27 -0700342
James Feistc14699f2019-06-04 14:11:48 -0700343 std::string nonConstCapture = "lalalala";
Vernon Maueryc0771902019-05-07 16:53:50 -0700344 conn->async_method_call(
James Feistc14699f2019-06-04 14:11:48 -0700345 [nonConstCapture = std::move(nonConstCapture)](
346 boost::system::error_code ec,
Patrick Williams78b78032020-05-20 10:32:05 -0500347 const std::vector<std::string>& /*things*/) mutable {
Vernon Maueryc0771902019-05-07 16:53:50 -0700348 std::cout << "async_method_call callback\n";
James Feistc14699f2019-06-04 14:11:48 -0700349 nonConstCapture += " stuff";
Vernon Maueryc0771902019-05-07 16:53:50 -0700350 if (ec)
351 {
352 std::cerr << "async_method_call expected failure: " << ec
353 << "\n";
354 }
355 else
356 {
James Feistc14699f2019-06-04 14:11:48 -0700357 std::cerr << "async_method_call should have failed!\n";
Vernon Maueryc0771902019-05-07 16:53:50 -0700358 }
359 },
360 "xyz.openbmc_project.ObjectMapper",
361 "/xyz/openbmc_project/object_mapper",
362 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
363 "/xyz/openbmc_project/sensors", depth, interfaces);
364
Vernon Mauery035c73b2018-09-05 12:15:27 -0700365 // sd_events work too using the default event loop
366 phosphor::Timer t1([]() { std::cerr << "*** tock ***\n"; });
367 t1.start(std::chrono::microseconds(1000000));
368 phosphor::Timer t2([]() { std::cerr << "*** tick ***\n"; });
369 t2.start(std::chrono::microseconds(500000), true);
370 // add the sd_event wrapper to the io object
371 sdbusplus::asio::sd_event_wrapper sdEvents(io);
372
Vernon Mauery261e72b2018-09-25 12:34:25 -0700373 // set up a client to make an async call to the server
374 // using coroutines (userspace cooperative multitasking)
375 boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
376 do_start_async_method_call_one(conn, yield);
377 });
378 boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
Vernon Mauery076d14a2018-10-02 15:10:20 -0700379 do_start_async_ipmi_call(conn, yield);
Vernon Mauery261e72b2018-09-25 12:34:25 -0700380 });
Vernon Mauery076d14a2018-10-02 15:10:20 -0700381 boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
382 do_start_async_to_yield(conn, yield);
383 });
384
385 conn->async_method_call(
386 [](boost::system::error_code ec, int32_t testValue) {
387 if (ec)
388 {
389 std::cerr << "TestYieldFunction returned error with "
390 "async_method_call (ec = "
391 << ec << ")\n";
392 return;
393 }
394 std::cout << "TestYieldFunction return " << testValue << "\n";
395 },
396 "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
397 "xyz.openbmc_project.test", "TestYieldFunction", int32_t(41));
James Feist284a0f92018-04-05 15:28:16 -0700398 io.run();
399
400 return 0;
401}
Vernon Mauery076d14a2018-10-02 15:10:20 -0700402
403int main(int argc, const char* argv[])
404{
405 if (argc == 1)
406 {
407 int pid = fork();
408 if (pid == 0)
409 {
410 return client();
411 }
412 else if (pid > 0)
413 {
414 return server();
415 }
416 return pid;
417 }
418 if (std::string(argv[1]) == "--server")
419 {
420 return server();
421 }
422 if (std::string(argv[1]) == "--client")
423 {
424 return client();
425 }
426 std::cout << "usage: " << argv[0] << " [--server | --client]\n";
427 return -1;
428}