blob: 29b605fc2644ac5b8b0291467b4bd408667531db [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>
James Feist284a0f92018-04-05 15:28:16 -070013
Vernon Mauery261e72b2018-09-25 12:34:25 -070014using variant = sdbusplus::message::variant<int, std::string>;
Vernon Mauery076d14a2018-10-02 15:10:20 -070015
James Feistfce038a2018-04-13 15:43:13 -070016int foo(int test)
17{
Vernon Mauery076d14a2018-10-02 15:10:20 -070018 std::cout << "foo(" << test << ") -> " << (test + 1) << "\n";
James Feistfce038a2018-04-13 15:43:13 -070019 return ++test;
20}
21
Vernon Mauery076d14a2018-10-02 15:10:20 -070022// called from coroutine context, can make yielding dbus calls
23int fooYield(boost::asio::yield_context yield,
24 std::shared_ptr<sdbusplus::asio::connection> conn, int test)
25{
26 // fetch the real value from testFunction
27 boost::system::error_code ec;
28 std::cout << "fooYield(yield, " << test << ")...\n";
29 int testCount = conn->yield_method_call<int>(
30 yield[ec], "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
31 "xyz.openbmc_project.test", "TestFunction", test);
32 if (ec || testCount != (test + 1))
33 {
34 std::cout << "call to foo failed: ec = " << ec << '\n';
35 return -1;
36 }
37 std::cout << "yielding call to foo OK! (-> " << testCount << ")\n";
38 return testCount;
39}
40
Vernon Mauery8ce61e42018-08-28 08:54:59 -070041int methodWithMessage(sdbusplus::message::message& m, int test)
42{
Vernon Mauery076d14a2018-10-02 15:10:20 -070043 std::cout << "methodWithMessage(m, " << test << ") -> " << (test + 1)
44 << "\n";
Vernon Mauery8ce61e42018-08-28 08:54:59 -070045 return ++test;
46}
47
Vernon Mauery2c8f93f2018-09-05 12:02:39 -070048int voidBar(void)
49{
Vernon Mauery076d14a2018-10-02 15:10:20 -070050 std::cout << "voidBar() -> 42\n";
Vernon Mauery2c8f93f2018-09-05 12:02:39 -070051 return 42;
52}
53
Vernon Mauery261e72b2018-09-25 12:34:25 -070054void do_start_async_method_call_one(
55 std::shared_ptr<sdbusplus::asio::connection> conn,
56 boost::asio::yield_context yield)
57{
58 boost::system::error_code ec;
59 variant testValue;
60 conn->yield_method_call<>(yield[ec], "xyz.openbmc_project.asio-test",
61 "/xyz/openbmc_project/test",
62 "org.freedesktop.DBus.Properties", "Set",
63 "xyz.openbmc_project.test", "int", variant(24));
64 testValue = conn->yield_method_call<variant>(
65 yield[ec], "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
66 "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.test",
67 "int");
Yong Li6ee43dc2018-12-11 17:44:46 +080068 if (!ec && sdbusplus::message::variant_ns::get<int>(testValue) == 24)
Vernon Mauery261e72b2018-09-25 12:34:25 -070069 {
70 std::cout << "async call to Properties.Get serialized via yield OK!\n";
71 }
72 else
73 {
Yong Li6ee43dc2018-12-11 17:44:46 +080074 std::cout << "ec = " << ec << ": "
75 << sdbusplus::message::variant_ns::get<int>(testValue)
76 << "\n";
Vernon Mauery261e72b2018-09-25 12:34:25 -070077 }
78 conn->yield_method_call<void>(
79 yield[ec], "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
80 "org.freedesktop.DBus.Properties", "Set", "xyz.openbmc_project.test",
81 "int", variant(42));
82 testValue = conn->yield_method_call<variant>(
83 yield[ec], "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
84 "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.test",
85 "int");
Yong Li6ee43dc2018-12-11 17:44:46 +080086 if (!ec && sdbusplus::message::variant_ns::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 {
Yong Li6ee43dc2018-12-11 17:44:46 +080092 std::cout << "ec = " << ec << ": "
93 << sdbusplus::message::variant_ns::get<int>(testValue)
94 << "\n";
Vernon Mauery261e72b2018-09-25 12:34:25 -070095 }
96}
97
Vernon Mauery076d14a2018-10-02 15:10:20 -070098void do_start_async_ipmi_call(std::shared_ptr<sdbusplus::asio::connection> conn,
99 boost::asio::yield_context yield)
100{
101 auto method = conn->new_method_call("xyz.openbmc_project.asio-test",
102 "/xyz/openbmc_project/test",
103 "xyz.openbmc_project.test", "execute");
104 constexpr uint8_t netFn = 6;
105 constexpr uint8_t lun = 0;
106 constexpr uint8_t cmd = 1;
107 std::map<std::string, variant> options = {{"username", variant("admin")},
108 {"privilege", variant(4)}};
109 std::vector<uint8_t> commandData = {4, 3, 2, 1};
110 method.append(netFn, lun, cmd, commandData, options);
111 boost::system::error_code ec;
112 sdbusplus::message::message reply = conn->async_send(method, yield[ec]);
113 std::tuple<uint8_t, uint8_t, uint8_t, uint8_t, std::vector<uint8_t>>
114 tupleOut;
115 try
116 {
117 reply.read(tupleOut);
118 }
119 catch (const sdbusplus::exception::SdBusError& e)
120 {
121 std::cerr << "failed to unpack; sig is " << reply.get_signature()
122 << "\n";
123 }
124 auto& [rnetFn, rlun, rcmd, cc, responseData] = tupleOut;
125 std::vector<uint8_t> expRsp = {1, 2, 3, 4};
126 if (rnetFn == uint8_t(netFn + 1) && rlun == lun && rcmd == cmd && cc == 0 &&
127 responseData == expRsp)
128 {
129 std::cerr << "ipmi call returns OK!\n";
130 }
131 else
132 {
133 std::cerr << "ipmi call returns unexpected response\n";
134 }
135}
136
137auto ipmiInterface(boost::asio::yield_context yield, uint8_t netFn, uint8_t lun,
138 uint8_t cmd, std::vector<uint8_t>& data,
139 const std::map<std::string, variant>& options)
140{
141 std::vector<uint8_t> reply = {1, 2, 3, 4};
142 uint8_t cc = 0;
143 std::cerr << "ipmiInterface:execute(" << int(netFn) << int(cmd) << ")\n";
144 return std::make_tuple(uint8_t(netFn + 1), lun, cmd, cc, reply);
145}
146
147void do_start_async_to_yield(std::shared_ptr<sdbusplus::asio::connection> conn,
148 boost::asio::yield_context yield)
Vernon Mauery261e72b2018-09-25 12:34:25 -0700149{
150 boost::system::error_code ec;
Vernon Mauery076d14a2018-10-02 15:10:20 -0700151 int testValue = 0;
152 try
153 {
154 testValue = conn->yield_method_call<int>(
Vernon Mauery261e72b2018-09-25 12:34:25 -0700155 yield[ec], "xyz.openbmc_project.asio-test",
156 "/xyz/openbmc_project/test", "xyz.openbmc_project.test",
Vernon Mauery076d14a2018-10-02 15:10:20 -0700157 "TestYieldFunction", int(41));
158 }
159 catch (sdbusplus::exception::SdBusError& e)
Vernon Mauery261e72b2018-09-25 12:34:25 -0700160 {
Vernon Mauery076d14a2018-10-02 15:10:20 -0700161 std::cout << "oops: " << e.what() << "\n";
162 }
163 if (!ec && testValue == 42)
164 {
165 std::cout
166 << "yielding call to TestYieldFunction serialized via yield OK!\n";
Vernon Mauery261e72b2018-09-25 12:34:25 -0700167 }
168 else
169 {
170 std::cout << "ec = " << ec << ": " << testValue << "\n";
171 }
172}
173
Vernon Mauery076d14a2018-10-02 15:10:20 -0700174int server()
James Feist284a0f92018-04-05 15:28:16 -0700175{
James Feist284a0f92018-04-05 15:28:16 -0700176 // setup connection to dbus
177 boost::asio::io_service io;
178 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
179
James Feistfce038a2018-04-13 15:43:13 -0700180 // test object server
181 conn->request_name("xyz.openbmc_project.asio-test");
182 auto server = sdbusplus::asio::object_server(conn);
183 std::shared_ptr<sdbusplus::asio::dbus_interface> iface =
184 server.add_interface("/xyz/openbmc_project/test",
185 "xyz.openbmc_project.test");
186 // test generic properties
187 iface->register_property("int", 33,
188 sdbusplus::asio::PropertyPermission::readWrite);
189 std::vector<std::string> myStringVec = {"some", "test", "data"};
190 std::vector<std::string> myStringVec2 = {"more", "test", "data"};
191
192 iface->register_property("myStringVec", myStringVec,
193 sdbusplus::asio::PropertyPermission::readWrite);
194 iface->register_property("myStringVec2", myStringVec2);
195
196 // test properties with specialized callbacks
197 iface->register_property("lessThan50", 23,
198 // custom set
199 [](const int& req, int& propertyValue) {
200 if (req >= 50)
201 {
202 return -EINVAL;
203 }
204 propertyValue = req;
205 return 1; // success
206 });
207 iface->register_property(
208 "TrailTime", std::string("foo"),
209 // custom set
210 [](const std::string& req, std::string& propertyValue) {
211 propertyValue = req;
212 return 1; // success
213 },
214 // custom get
215 [](const std::string& property) {
216 auto now = std::chrono::system_clock::now();
217 auto timePoint = std::chrono::system_clock::to_time_t(now);
218 return property + std::ctime(&timePoint);
219 });
220
221 // test method creation
222 iface->register_method("TestMethod", [](const int32_t& callCount) {
Vernon Mauery261e72b2018-09-25 12:34:25 -0700223 return std::make_tuple(callCount,
224 "success: " + std::to_string(callCount));
James Feistfce038a2018-04-13 15:43:13 -0700225 });
226
227 iface->register_method("TestFunction", foo);
228
Vernon Mauery076d14a2018-10-02 15:10:20 -0700229 // fooYield has boost::asio::yield_context as first argument
230 // so will be executed in coroutine context if called
231 iface->register_method("TestYieldFunction",
232 [conn](boost::asio::yield_context yield, int val) {
233 return fooYield(yield, conn, val);
234 });
235
Vernon Mauery8ce61e42018-08-28 08:54:59 -0700236 iface->register_method("TestMethodWithMessage", methodWithMessage);
237
Vernon Mauery2c8f93f2018-09-05 12:02:39 -0700238 iface->register_method("VoidFunctionReturnsInt", voidBar);
239
Vernon Mauery076d14a2018-10-02 15:10:20 -0700240 iface->register_method("execute", ipmiInterface);
241
James Feistfce038a2018-04-13 15:43:13 -0700242 iface->initialize();
Vernon Mauery076d14a2018-10-02 15:10:20 -0700243
244 io.run();
245
246 return 0;
247}
248
249int client()
250{
251 using GetSubTreeType = std::vector<std::pair<
252 std::string,
253 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
254 using message = sdbusplus::message::message;
255
256 // setup connection to dbus
257 boost::asio::io_service io;
258 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
259
260 int ready = 0;
261 while (!ready)
262 {
263 auto readyMsg = conn->new_method_call(
264 "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
265 "xyz.openbmc_project.test", "VoidFunctionReturnsInt");
266 try
267 {
268 message intMsg = conn->call(readyMsg);
269 intMsg.read(ready);
270 }
271 catch (sdbusplus::exception::SdBusError& e)
272 {
273 ready = 0;
274 // pause to give the server a chance to start up
275 usleep(10000);
276 }
277 }
278
279 // test async method call and async send
280 auto mesg =
281 conn->new_method_call("xyz.openbmc_project.ObjectMapper",
282 "/xyz/openbmc_project/object_mapper",
283 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
284
285 static const auto depth = 2;
286 static const std::vector<std::string> interfaces = {
287 "xyz.openbmc_project.Sensor.Value"};
288 mesg.append("/xyz/openbmc_project/Sensors", depth, interfaces);
289
290 conn->async_send(mesg, [](boost::system::error_code ec, message& ret) {
291 std::cout << "async_send callback\n";
292 if (ec || ret.is_method_error())
293 {
294 std::cerr << "error with async_send\n";
295 return;
296 }
297 GetSubTreeType data;
298 ret.read(data);
299 for (auto& item : data)
300 {
301 std::cout << item.first << "\n";
302 }
303 });
304
305 conn->async_method_call(
306 [](boost::system::error_code ec, GetSubTreeType& subtree) {
307 std::cout << "async_method_call callback\n";
308 if (ec)
309 {
310 std::cerr << "error with async_method_call\n";
311 return;
312 }
313 for (auto& item : subtree)
314 {
315 std::cout << item.first << "\n";
316 }
317 },
318 "xyz.openbmc_project.ObjectMapper",
319 "/xyz/openbmc_project/object_mapper",
320 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
321 "/org/openbmc/control", 2, std::vector<std::string>());
Vernon Mauery035c73b2018-09-05 12:15:27 -0700322
323 // sd_events work too using the default event loop
324 phosphor::Timer t1([]() { std::cerr << "*** tock ***\n"; });
325 t1.start(std::chrono::microseconds(1000000));
326 phosphor::Timer t2([]() { std::cerr << "*** tick ***\n"; });
327 t2.start(std::chrono::microseconds(500000), true);
328 // add the sd_event wrapper to the io object
329 sdbusplus::asio::sd_event_wrapper sdEvents(io);
330
Vernon Mauery261e72b2018-09-25 12:34:25 -0700331 // set up a client to make an async call to the server
332 // using coroutines (userspace cooperative multitasking)
333 boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
334 do_start_async_method_call_one(conn, yield);
335 });
336 boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
Vernon Mauery076d14a2018-10-02 15:10:20 -0700337 do_start_async_ipmi_call(conn, yield);
Vernon Mauery261e72b2018-09-25 12:34:25 -0700338 });
Vernon Mauery076d14a2018-10-02 15:10:20 -0700339 boost::asio::spawn(io, [conn](boost::asio::yield_context yield) {
340 do_start_async_to_yield(conn, yield);
341 });
342
343 conn->async_method_call(
344 [](boost::system::error_code ec, int32_t testValue) {
345 if (ec)
346 {
347 std::cerr << "TestYieldFunction returned error with "
348 "async_method_call (ec = "
349 << ec << ")\n";
350 return;
351 }
352 std::cout << "TestYieldFunction return " << testValue << "\n";
353 },
354 "xyz.openbmc_project.asio-test", "/xyz/openbmc_project/test",
355 "xyz.openbmc_project.test", "TestYieldFunction", int32_t(41));
James Feist284a0f92018-04-05 15:28:16 -0700356 io.run();
357
358 return 0;
359}
Vernon Mauery076d14a2018-10-02 15:10:20 -0700360
361int main(int argc, const char* argv[])
362{
363 if (argc == 1)
364 {
365 int pid = fork();
366 if (pid == 0)
367 {
368 return client();
369 }
370 else if (pid > 0)
371 {
372 return server();
373 }
374 return pid;
375 }
376 if (std::string(argv[1]) == "--server")
377 {
378 return server();
379 }
380 if (std::string(argv[1]) == "--client")
381 {
382 return client();
383 }
384 std::cout << "usage: " << argv[0] << " [--server | --client]\n";
385 return -1;
386}