blob: f7ec6b05d64eca841c6315820012d81ac598faa0 [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 Feistfce038a2018-04-13 15:43:13 -07003#include <chrono>
Patrick Venture95269db2018-08-31 09:19:17 -07004#include <ctime>
5#include <iostream>
James Feist284a0f92018-04-05 15:28:16 -07006#include <sdbusplus/asio/connection.hpp>
James Feistfce038a2018-04-13 15:43:13 -07007#include <sdbusplus/asio/object_server.hpp>
Vernon Mauery035c73b2018-09-05 12:15:27 -07008#include <sdbusplus/asio/sd_event.hpp>
Patrick Venture95269db2018-08-31 09:19:17 -07009#include <sdbusplus/bus.hpp>
Vernon Mauery076d14a2018-10-02 15:10:20 -070010#include <sdbusplus/exception.hpp>
Patrick Venture95269db2018-08-31 09:19:17 -070011#include <sdbusplus/server.hpp>
Vernon Mauery035c73b2018-09-05 12:15:27 -070012#include <sdbusplus/timer.hpp>
William A. Kennington III4274c112018-11-26 09:50:13 -080013#include <variant>
James Feist284a0f92018-04-05 15:28:16 -070014
William A. Kennington III4274c112018-11-26 09:50:13 -080015using variant = std::variant<int, std::string>;
Vernon Mauery076d14a2018-10-02 15:10:20 -070016
James Feistfce038a2018-04-13 15:43:13 -070017int foo(int test)
18{
Vernon Mauery076d14a2018-10-02 15:10:20 -070019 std::cout << "foo(" << test << ") -> " << (test + 1) << "\n";
James Feistfce038a2018-04-13 15:43:13 -070020 return ++test;
21}
22
Vernon Mauery076d14a2018-10-02 15:10:20 -070023// called from coroutine context, can make yielding dbus calls
24int fooYield(boost::asio::yield_context yield,
25 std::shared_ptr<sdbusplus::asio::connection> conn, int test)
26{
27 // fetch the real value from testFunction
28 boost::system::error_code ec;
29 std::cout << "fooYield(yield, " << test << ")...\n";
30 int testCount = conn->yield_method_call<int>(
Vernon Mauery37a5e612019-05-07 16:53:50 -070031 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
Vernon Mauery076d14a2018-10-02 15:10:20 -070032 "xyz.openbmc_project.test", "TestFunction", test);
33 if (ec || testCount != (test + 1))
34 {
35 std::cout << "call to foo failed: ec = " << ec << '\n';
36 return -1;
37 }
38 std::cout << "yielding call to foo OK! (-> " << testCount << ")\n";
39 return testCount;
40}
41
Vernon Mauery8ce61e42018-08-28 08:54:59 -070042int methodWithMessage(sdbusplus::message::message& m, int test)
43{
Vernon Mauery076d14a2018-10-02 15:10:20 -070044 std::cout << "methodWithMessage(m, " << test << ") -> " << (test + 1)
45 << "\n";
Vernon Mauery8ce61e42018-08-28 08:54:59 -070046 return ++test;
47}
48
Vernon Mauery2c8f93f2018-09-05 12:02:39 -070049int voidBar(void)
50{
Vernon Mauery076d14a2018-10-02 15:10:20 -070051 std::cout << "voidBar() -> 42\n";
Vernon Mauery2c8f93f2018-09-05 12:02:39 -070052 return 42;
53}
54
Vernon Mauery261e72b2018-09-25 12:34:25 -070055void do_start_async_method_call_one(
56 std::shared_ptr<sdbusplus::asio::connection> conn,
57 boost::asio::yield_context yield)
58{
59 boost::system::error_code ec;
60 variant testValue;
Vernon Mauery37a5e612019-05-07 16:53:50 -070061 conn->yield_method_call<>(yield, ec, "xyz.openbmc_project.asio-test",
Vernon Mauery261e72b2018-09-25 12:34:25 -070062 "/xyz/openbmc_project/test",
63 "org.freedesktop.DBus.Properties", "Set",
64 "xyz.openbmc_project.test", "int", variant(24));
65 testValue = conn->yield_method_call<variant>(
Vernon Mauery37a5e612019-05-07 16:53:50 -070066 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
Vernon Mauery261e72b2018-09-25 12:34:25 -070067 "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.test",
68 "int");
William A. Kennington III4274c112018-11-26 09:50:13 -080069 if (!ec && std::get<int>(testValue) == 24)
Vernon Mauery261e72b2018-09-25 12:34:25 -070070 {
71 std::cout << "async call to Properties.Get serialized via yield OK!\n";
72 }
73 else
74 {
William A. Kennington III4274c112018-11-26 09:50:13 -080075 std::cout << "ec = " << ec << ": " << std::get<int>(testValue) << "\n";
Vernon Mauery261e72b2018-09-25 12:34:25 -070076 }
77 conn->yield_method_call<void>(
Vernon Mauery37a5e612019-05-07 16:53:50 -070078 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
Vernon Mauery261e72b2018-09-25 12:34:25 -070079 "org.freedesktop.DBus.Properties", "Set", "xyz.openbmc_project.test",
80 "int", variant(42));
81 testValue = conn->yield_method_call<variant>(
Vernon Mauery37a5e612019-05-07 16:53:50 -070082 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
Vernon Mauery261e72b2018-09-25 12:34:25 -070083 "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.test",
84 "int");
William A. Kennington III4274c112018-11-26 09:50:13 -080085 if (!ec && std::get<int>(testValue) == 42)
Vernon Mauery261e72b2018-09-25 12:34:25 -070086 {
87 std::cout << "async call to Properties.Get serialized via yield OK!\n";
88 }
89 else
90 {
William A. Kennington III4274c112018-11-26 09:50:13 -080091 std::cout << "ec = " << ec << ": " << std::get<int>(testValue) << "\n";
Vernon Mauery261e72b2018-09-25 12:34:25 -070092 }
93}
94
Vernon Mauery076d14a2018-10-02 15:10:20 -070095void do_start_async_ipmi_call(std::shared_ptr<sdbusplus::asio::connection> conn,
96 boost::asio::yield_context yield)
97{
98 auto method = conn->new_method_call("xyz.openbmc_project.asio-test",
99 "/xyz/openbmc_project/test",
100 "xyz.openbmc_project.test", "execute");
101 constexpr uint8_t netFn = 6;
102 constexpr uint8_t lun = 0;
103 constexpr uint8_t cmd = 1;
104 std::map<std::string, variant> options = {{"username", variant("admin")},
105 {"privilege", variant(4)}};
106 std::vector<uint8_t> commandData = {4, 3, 2, 1};
107 method.append(netFn, lun, cmd, commandData, options);
108 boost::system::error_code ec;
109 sdbusplus::message::message reply = conn->async_send(method, yield[ec]);
110 std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
111 tupleOut;
112 try
113 {
114 reply.read(tupleOut);
115 }
116 catch (const sdbusplus::exception::SdBusError& e)
117 {
118 std::cerr << "failed to unpack; sig is " << reply.get_signature()
119 << "\n";
120 }
121 auto& [rnetFn, rlun, rcmd, cc, responseData] = tupleOut;
122 std::vector<uint8_t> expRsp = {1, 2, 3, 4};
123 if (rnetFn == uint8_t(netFn + 1) && rlun == lun && rcmd == cmd && cc == 0 &&
124 responseData == expRsp)
125 {
126 std::cerr << "ipmi call returns OK!\n";
127 }
128 else
129 {
130 std::cerr << "ipmi call returns unexpected response\n";
131 }
132}
133
134auto ipmiInterface(boost::asio::yield_context yield, uint8_t netFn, uint8_t lun,
135 uint8_t cmd, std::vector<uint8_t>& data,
136 const std::map<std::string, variant>& options)
137{
138 std::vector<uint8_t> reply = {1, 2, 3, 4};
139 uint8_t cc = 0;
140 std::cerr << "ipmiInterface:execute(" << int(netFn) << int(cmd) << ")\n";
141 return std::make_tuple(uint8_t(netFn + 1), lun, cmd, cc, reply);
142}
143
144void do_start_async_to_yield(std::shared_ptr<sdbusplus::asio::connection> conn,
145 boost::asio::yield_context yield)
Vernon Mauery261e72b2018-09-25 12:34:25 -0700146{
147 boost::system::error_code ec;
Vernon Mauery076d14a2018-10-02 15:10:20 -0700148 int testValue = 0;
Vernon Maueryc0771902019-05-07 16:53:50 -0700149 testValue = conn->yield_method_call<int>(
150 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
151 "xyz.openbmc_project.test", "TestYieldFunction", int(41));
152
Vernon Mauery076d14a2018-10-02 15:10:20 -0700153 if (!ec && testValue == 42)
154 {
155 std::cout
156 << "yielding call to TestYieldFunction serialized via yield OK!\n";
Vernon Mauery261e72b2018-09-25 12:34:25 -0700157 }
158 else
159 {
160 std::cout << "ec = " << ec << ": " << testValue << "\n";
161 }
Vernon Maueryc0771902019-05-07 16:53:50 -0700162
163 ec.clear();
164 auto badValue = conn->yield_method_call<std::string>(
165 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
166 "xyz.openbmc_project.test", "TestYieldFunction", int(41));
167
168 if (!ec)
169 {
170 std::cout
171 << "yielding call to TestYieldFunction returned the wrong type\n";
172 }
173 else
174 {
175 std::cout << "TestYieldFunction expected error: " << ec << "\n";
176 }
177
178 ec.clear();
179 auto unUsedValue = conn->yield_method_call<std::string>(
180 yield, ec, "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
181 "xyz.openbmc_project.test", "TestYieldFunctionNotExits", int(41));
182
183 if (!ec)
184 {
185 std::cout << "TestYieldFunctionNotExists returned unexpectedly\n";
186 }
187 else
188 {
189 std::cout << "TestYieldFunctionNotExits expected error: " << ec << "\n";
190 }
Vernon Mauery261e72b2018-09-25 12:34:25 -0700191}
192
Vernon Mauery076d14a2018-10-02 15:10:20 -0700193int server()
James Feist284a0f92018-04-05 15:28:16 -0700194{
James Feist284a0f92018-04-05 15:28:16 -0700195 // setup connection to dbus
Ed Tanousc7d104d2019-01-02 14:15:50 -0800196 boost::asio::io_context io;
James Feist284a0f92018-04-05 15:28:16 -0700197 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
198
James Feistfce038a2018-04-13 15:43:13 -0700199 // test object server
200 conn->request_name("xyz.openbmc_project.asio-test");
201 auto server = sdbusplus::asio::object_server(conn);
202 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
203 server.add_interface("/xyz/openbmc_project/test",
204 "xyz.openbmc_project.test");
205 // test generic properties
206 iface->register_property("int", 33,
207 sdbusplus::asio::PropertyPermission::readWrite);
208 std::vector<std::string> myStringVec = {"some", "test", "data"};
209 std::vector<std::string> myStringVec2 = {"more", "test", "data"};
210
211 iface->register_property("myStringVec", myStringVec,
212 sdbusplus::asio::PropertyPermission::readWrite);
213 iface->register_property("myStringVec2", myStringVec2);
214
215 // test properties with specialized callbacks
216 iface->register_property("lessThan50", 23,
217 // custom set
218 [](const int& req, int& propertyValue) {
219 if (req >= 50)
220 {
221 return -EINVAL;
222 }
223 propertyValue = req;
224 return 1; // success
225 });
226 iface->register_property(
227 "TrailTime", std::string("foo"),
228 // custom set
229 [](const std::string& req, std::string& propertyValue) {
230 propertyValue = req;
231 return 1; // success
232 },
233 // custom get
234 [](const std::string& property) {
235 auto now = std::chrono::system_clock::now();
236 auto timePoint = std::chrono::system_clock::to_time_t(now);
237 return property + std::ctime(&timePoint);
238 });
239
240 // test method creation
241 iface->register_method("TestMethod", [](const int32_t& callCount) {
Vernon Mauery261e72b2018-09-25 12:34:25 -0700242 return std::make_tuple(callCount,
243 "success: " + std::to_string(callCount));
James Feistfce038a2018-04-13 15:43:13 -0700244 });
245
246 iface->register_method("TestFunction", foo);
247
Vernon Mauery076d14a2018-10-02 15:10:20 -0700248 // fooYield has boost::asio::yield_context as first argument
249 // so will be executed in coroutine context if called
250 iface->register_method("TestYieldFunction",
251 [conn](boost::asio::yield_context yield, int val) {
252 return fooYield(yield, conn, val);
253 });
254
Vernon Mauery8ce61e42018-08-28 08:54:59 -0700255 iface->register_method("TestMethodWithMessage", methodWithMessage);
256
Vernon Mauery2c8f93f2018-09-05 12:02:39 -0700257 iface->register_method("VoidFunctionReturnsInt", voidBar);
258
Vernon Mauery076d14a2018-10-02 15:10:20 -0700259 iface->register_method("execute", ipmiInterface);
260
James Feistfce038a2018-04-13 15:43:13 -0700261 iface->initialize();
Vernon Mauery076d14a2018-10-02 15:10:20 -0700262
263 io.run();
264
265 return 0;
266}
267
268int client()
269{
270 using GetSubTreeType = std::vector<std::pair<
271 std::string,
272 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
273 using message = sdbusplus::message::message;
274
275 // setup connection to dbus
Ed Tanousc7d104d2019-01-02 14:15:50 -0800276 boost::asio::io_context io;
Vernon Mauery076d14a2018-10-02 15:10:20 -0700277 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
278
279 int ready = 0;
280 while (!ready)
281 {
282 auto readyMsg = conn->new_method_call(
283 "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
284 "xyz.openbmc_project.test", "VoidFunctionReturnsInt");
285 try
286 {
287 message intMsg = conn->call(readyMsg);
288 intMsg.read(ready);
289 }
290 catch (sdbusplus::exception::SdBusError& e)
291 {
292 ready = 0;
293 // pause to give the server a chance to start up
294 usleep(10000);
295 }
296 }
297
298 // test async method call and async send
299 auto mesg =
300 conn->new_method_call("xyz.openbmc_project.ObjectMapper",
301 "/xyz/openbmc_project/object_mapper",
302 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
303
304 static const auto depth = 2;
305 static const std::vector<std::string> interfaces = {
306 "xyz.openbmc_project.Sensor.Value"};
307 mesg.append("/xyz/openbmc_project/Sensors", depth, interfaces);
308
309 conn->async_send(mesg, [](boost::system::error_code ec, message& ret) {
310 std::cout << "async_send callback\n";
311 if (ec || ret.is_method_error())
312 {
313 std::cerr << "error with async_send\n";
314 return;
315 }
316 GetSubTreeType data;
317 ret.read(data);
318 for (auto& item : data)
319 {
320 std::cout << item.first << "\n";
321 }
322 });
323
324 conn->async_method_call(
325 [](boost::system::error_code ec, GetSubTreeType& subtree) {
326 std::cout << "async_method_call callback\n";
327 if (ec)
328 {
329 std::cerr << "error with async_method_call\n";
330 return;
331 }
332 for (auto& item : subtree)
333 {
334 std::cout << item.first << "\n";
335 }
336 },
337 "xyz.openbmc_project.ObjectMapper",
338 "/xyz/openbmc_project/object_mapper",
339 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
340 "/org/openbmc/control", 2, std::vector<std::string>());
Vernon Mauery035c73b2018-09-05 12:15:27 -0700341
James Feistc14699f2019-06-04 14:11:48 -0700342 std::string nonConstCapture = "lalalala";
Vernon Maueryc0771902019-05-07 16:53:50 -0700343 conn->async_method_call(
James Feistc14699f2019-06-04 14:11:48 -0700344 [nonConstCapture = std::move(nonConstCapture)](
345 boost::system::error_code ec,
346 const std::vector<std::string>& things) mutable {
Vernon Maueryc0771902019-05-07 16:53:50 -0700347 std::cout << "async_method_call callback\n";
James Feistc14699f2019-06-04 14:11:48 -0700348 nonConstCapture += " stuff";
Vernon Maueryc0771902019-05-07 16:53:50 -0700349 if (ec)
350 {
351 std::cerr << "async_method_call expected failure: " << ec
352 << "\n";
353 }
354 else
355 {
James Feistc14699f2019-06-04 14:11:48 -0700356 std::cerr << "async_method_call should have failed!\n";
Vernon Maueryc0771902019-05-07 16:53:50 -0700357 }
358 },
359 "xyz.openbmc_project.ObjectMapper",
360 "/xyz/openbmc_project/object_mapper",
361 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
362 "/xyz/openbmc_project/sensors", depth, interfaces);
363
Vernon Mauery035c73b2018-09-05 12:15:27 -0700364 // sd_events work too using the default event loop
365 phosphor::Timer t1([]() { std::cerr << "*** tock ***\n"; });
366 t1.start(std::chrono::microseconds(1000000));
367 phosphor::Timer t2([]() { std::cerr << "*** tick ***\n"; });
368 t2.start(std::chrono::microseconds(500000), true);
369 // add the sd_event wrapper to the io object
370 sdbusplus::asio::sd_event_wrapper sdEvents(io);
371
Vernon Mauery261e72b2018-09-25 12:34:25 -0700372 // set up a client to make an async call to the server
373 // using coroutines (userspace cooperative multitasking)
374 boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
375 do_start_async_method_call_one(conn, yield);
376 });
377 boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
Vernon Mauery076d14a2018-10-02 15:10:20 -0700378 do_start_async_ipmi_call(conn, yield);
Vernon Mauery261e72b2018-09-25 12:34:25 -0700379 });
Vernon Mauery076d14a2018-10-02 15:10:20 -0700380 boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
381 do_start_async_to_yield(conn, yield);
382 });
383
384 conn->async_method_call(
385 [](boost::system::error_code ec, int32_t testValue) {
386 if (ec)
387 {
388 std::cerr << "TestYieldFunction returned error with "
389 "async_method_call (ec = "
390 << ec << ")\n";
391 return;
392 }
393 std::cout << "TestYieldFunction return " << testValue << "\n";
394 },
395 "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
396 "xyz.openbmc_project.test", "TestYieldFunction", int32_t(41));
James Feist284a0f92018-04-05 15:28:16 -0700397 io.run();
398
399 return 0;
400}
Vernon Mauery076d14a2018-10-02 15:10:20 -0700401
402int main(int argc, const char* argv[])
403{
404 if (argc == 1)
405 {
406 int pid = fork();
407 if (pid == 0)
408 {
409 return client();
410 }
411 else if (pid > 0)
412 {
413 return server();
414 }
415 return pid;
416 }
417 if (std::string(argv[1]) == "--server")
418 {
419 return server();
420 }
421 if (std::string(argv[1]) == "--client")
422 {
423 return client();
424 }
425 std::cout << "usage: " << argv[0] << " [--server | --client]\n";
426 return -1;
427}