1*a909a822SEd Tanous #include <unistd.h>
2377e76abSEd Tanous #include <dbus/connection.hpp>
3377e76abSEd Tanous #include <dbus/endpoint.hpp>
4377e76abSEd Tanous #include <dbus/filter.hpp>
5377e76abSEd Tanous #include <dbus/match.hpp>
6377e76abSEd Tanous #include <dbus/message.hpp>
7377e76abSEd Tanous #include <dbus/properties.hpp>
8377e76abSEd Tanous #include <functional>
9*a909a822SEd Tanous #include <vector>
10377e76abSEd Tanous #include <gmock/gmock.h>
11377e76abSEd Tanous #include <gtest/gtest.h>
12377e76abSEd Tanous 
13377e76abSEd Tanous static const std::string dbus_boilerplate(
14377e76abSEd Tanous     "<!DOCTYPE node PUBLIC "
15377e76abSEd Tanous     "\"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
16377e76abSEd Tanous     "\"http://www.freedesktop.org/standards/dbus/1.0/"
17377e76abSEd Tanous     "introspect.dtd\">\n");
18377e76abSEd Tanous 
TEST(DbusPropertiesInterface,EmptyObjectServer)19377e76abSEd Tanous TEST(DbusPropertiesInterface, EmptyObjectServer) {
20377e76abSEd Tanous   boost::asio::io_service io;
21377e76abSEd Tanous   auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
22377e76abSEd Tanous 
23377e76abSEd Tanous   // Set up the object server, and send some test events
24377e76abSEd Tanous   dbus::DbusObjectServer foo(system_bus);
25377e76abSEd Tanous 
26377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate + "<node></node>");
27377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate + "<node></node>");
28377e76abSEd Tanous }
29377e76abSEd Tanous 
TEST(DbusPropertiesInterface,BasicObjectServer)30377e76abSEd Tanous TEST(DbusPropertiesInterface, BasicObjectServer) {
31377e76abSEd Tanous   boost::asio::io_service io;
32377e76abSEd Tanous   auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
33377e76abSEd Tanous 
34377e76abSEd Tanous   // Set up the object server, and send some test events
35377e76abSEd Tanous   dbus::DbusObjectServer foo(system_bus);
36377e76abSEd Tanous 
37377e76abSEd Tanous   foo.register_object(std::make_shared<dbus::DbusObject>(
38377e76abSEd Tanous       system_bus, "/org/freedesktop/NetworkManager"));
39377e76abSEd Tanous 
40377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate +
41377e76abSEd Tanous                                            "<node><node "
42377e76abSEd Tanous                                            "name=\"org\"></node></node>");
43377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate +
44377e76abSEd Tanous                                           "<node><node "
45377e76abSEd Tanous                                           "name=\"org\"></node></node>");
46377e76abSEd Tanous 
47377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path("/org"),
48377e76abSEd Tanous             dbus_boilerplate +
49377e76abSEd Tanous                 "<node><node "
50377e76abSEd Tanous                 "name=\"freedesktop\"></node></node>");
51377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"),
52377e76abSEd Tanous             dbus_boilerplate +
53377e76abSEd Tanous                 "<node><node "
54377e76abSEd Tanous                 "name=\"NetworkManager\"></node></node>");
55377e76abSEd Tanous   // TODO(Ed) turn this test back on once the signal interface stabilizes
56377e76abSEd Tanous   /*EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/NetworkManager"),
57377e76abSEd Tanous             dbus_boilerplate + "<node></node>");*/
58377e76abSEd Tanous }
59377e76abSEd Tanous 
TEST(DbusPropertiesInterface,SharedNodeObjectServer)60377e76abSEd Tanous TEST(DbusPropertiesInterface, SharedNodeObjectServer) {
61377e76abSEd Tanous   boost::asio::io_service io;
62377e76abSEd Tanous   auto system_bus = std::make_shared<dbus::connection>(io, dbus::bus::system);
63377e76abSEd Tanous 
64377e76abSEd Tanous   // Set up the object server, and send some test events
65377e76abSEd Tanous   dbus::DbusObjectServer foo(system_bus);
66377e76abSEd Tanous 
67377e76abSEd Tanous   foo.register_object(
68377e76abSEd Tanous       std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test1"));
69377e76abSEd Tanous   foo.register_object(
70377e76abSEd Tanous       std::make_shared<dbus::DbusObject>(system_bus, "/org/freedesktop/test2"));
71377e76abSEd Tanous 
72377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path("/"), dbus_boilerplate +
73377e76abSEd Tanous                                            "<node><node "
74377e76abSEd Tanous                                            "name=\"org\"></node></node>");
75377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path(""), dbus_boilerplate +
76377e76abSEd Tanous                                           "<node><node "
77377e76abSEd Tanous                                           "name=\"org\"></node></node>");
78377e76abSEd Tanous 
79377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path("/org"),
80377e76abSEd Tanous             dbus_boilerplate +
81377e76abSEd Tanous                 "<node><node "
82377e76abSEd Tanous                 "name=\"freedesktop\"></node></node>");
83377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop"),
84377e76abSEd Tanous             dbus_boilerplate +
85377e76abSEd Tanous                 "<node><node "
86377e76abSEd Tanous                 "name=\"test1\"></node><node name=\"test2\"></node></node>");
87377e76abSEd Tanous   // TODO(Ed) turn this test back on once the signal interface stabilizes
88377e76abSEd Tanous   /*
89377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test1"),
90377e76abSEd Tanous             dbus_boilerplate + "<node></node>");
91377e76abSEd Tanous   EXPECT_EQ(foo.get_xml_for_path("/org/freedesktop/test2"),
92377e76abSEd Tanous             dbus_boilerplate + "<node></node>");
93377e76abSEd Tanous             */
94377e76abSEd Tanous }
95377e76abSEd Tanous 
TEST(LambdaDbusMethodTest,Basic)96377e76abSEd Tanous TEST(LambdaDbusMethodTest, Basic) {
97377e76abSEd Tanous   bool lambda_called = false;
98377e76abSEd Tanous   auto lambda = [&](int32_t x) {
99377e76abSEd Tanous     EXPECT_EQ(x, 18);
100377e76abSEd Tanous     lambda_called = true;
101377e76abSEd Tanous     return std::make_tuple<int64_t, int32_t>(4L, 2);
102377e76abSEd Tanous   };
103377e76abSEd Tanous   boost::asio::io_service io;
104377e76abSEd Tanous   auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
105377e76abSEd Tanous   auto dbus_method =
106377e76abSEd Tanous       dbus::LambdaDbusMethod<decltype(lambda)>("foo", bus, lambda);
107377e76abSEd Tanous 
108377e76abSEd Tanous   dbus::message m =
109377e76abSEd Tanous       dbus::message::new_call(dbus::endpoint("org.freedesktop.Avahi", "/",
110377e76abSEd Tanous                                              "org.freedesktop.Avahi.Server"),
111377e76abSEd Tanous                               "GetHostName");
112377e76abSEd Tanous   m.pack(static_cast<int32_t>(18));
113377e76abSEd Tanous   // Small thing that the dbus library normally does for us, but because we're
114377e76abSEd Tanous   // bypassing it, we need to fill it in as if it was done
115377e76abSEd Tanous   m.set_serial(585);
116377e76abSEd Tanous   dbus_method.call(m);
117377e76abSEd Tanous   EXPECT_EQ(lambda_called, true);
118377e76abSEd Tanous }
119377e76abSEd Tanous 
TEST(DbusPropertiesInterface,ObjectServer)120377e76abSEd Tanous TEST(DbusPropertiesInterface, ObjectServer) {
121377e76abSEd Tanous   boost::asio::io_service io;
122377e76abSEd Tanous   auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
123377e76abSEd Tanous 
124377e76abSEd Tanous   // Set up the object server, and send some test objects
125377e76abSEd Tanous   dbus::DbusObjectServer foo(bus);
126377e76abSEd Tanous 
127377e76abSEd Tanous   foo.register_object(
128377e76abSEd Tanous       std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"));
129377e76abSEd Tanous   foo.register_object(
130377e76abSEd Tanous       std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test2"));
131a8b4eac4SEd Tanous   int completion_count(0);
132377e76abSEd Tanous 
133377e76abSEd Tanous   std::array<std::string, 4> paths_to_test(
134377e76abSEd Tanous       {{"/", "/org", "/org/freedesktop", "/org/freedesktop/test1"}});
135377e76abSEd Tanous 
136377e76abSEd Tanous   for (auto& path : paths_to_test) {
137377e76abSEd Tanous     dbus::endpoint test_daemon(bus->get_unique_name(), path,
138377e76abSEd Tanous                                "org.freedesktop.DBus.Introspectable");
139377e76abSEd Tanous     dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
140377e76abSEd Tanous     completion_count++;
141377e76abSEd Tanous     bus->async_send(
142377e76abSEd Tanous         m, [&](const boost::system::error_code ec, dbus::message r) {
143377e76abSEd Tanous           if (ec) {
144377e76abSEd Tanous             std::string error;
145377e76abSEd Tanous             r.unpack(error);
146377e76abSEd Tanous             FAIL() << ec << error;
147377e76abSEd Tanous           } else {
148377e76abSEd Tanous             std::string xml;
149377e76abSEd Tanous             r.unpack(xml);
150377e76abSEd Tanous             EXPECT_EQ(r.get_type(), "method_return");
151377e76abSEd Tanous             if (path == "/") {
152377e76abSEd Tanous               EXPECT_EQ(xml, dbus_boilerplate +
153377e76abSEd Tanous                                  "<node><node "
154377e76abSEd Tanous                                  "name=\"org\"></node></node>");
155377e76abSEd Tanous             } else if (path == "/org") {
156377e76abSEd Tanous               EXPECT_EQ(xml, dbus_boilerplate +
157377e76abSEd Tanous                                  "<node><node "
158377e76abSEd Tanous                                  "name=\"freedesktop\"></node></node>");
159377e76abSEd Tanous             } else if (path == "/org/freedesktop") {
160377e76abSEd Tanous               EXPECT_EQ(xml, dbus_boilerplate +
161377e76abSEd Tanous                                  "<node><node "
162377e76abSEd Tanous                                  "name=\"test1\"></node><node "
163377e76abSEd Tanous                                  "name=\"test2\"></node></node>");
164377e76abSEd Tanous             } else if (path == "/org/freedesktop/test1") {
165377e76abSEd Tanous             } else {
166377e76abSEd Tanous               FAIL() << "Unknown path: " << path;
167377e76abSEd Tanous             }
168377e76abSEd Tanous           }
169377e76abSEd Tanous           completion_count--;
170377e76abSEd Tanous           if (completion_count == 0) {
171377e76abSEd Tanous             io.stop();
172377e76abSEd Tanous           }
173377e76abSEd Tanous         });
174377e76abSEd Tanous   }
175377e76abSEd Tanous   io.run();
176377e76abSEd Tanous }
177377e76abSEd Tanous 
TEST(DbusPropertiesInterface,EmptyMethodServer)178377e76abSEd Tanous TEST(DbusPropertiesInterface, EmptyMethodServer) {
179377e76abSEd Tanous   boost::asio::io_service io;
180377e76abSEd Tanous   auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
181377e76abSEd Tanous 
182377e76abSEd Tanous   // Set up the object server, and send some test objects
183377e76abSEd Tanous   dbus::DbusObjectServer foo(bus);
184377e76abSEd Tanous   foo.register_object(
185377e76abSEd Tanous       std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1"));
186377e76abSEd Tanous 
187377e76abSEd Tanous   dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1",
188377e76abSEd Tanous                              "org.freedesktop.DBus.Introspectable");
189377e76abSEd Tanous   dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
190377e76abSEd Tanous 
191377e76abSEd Tanous   bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) {
192377e76abSEd Tanous     if (ec) {
193377e76abSEd Tanous       std::string error;
194377e76abSEd Tanous       r.unpack(error);
195377e76abSEd Tanous       FAIL() << ec << error;
196377e76abSEd Tanous     } else {
197377e76abSEd Tanous       std::cout << r;
198377e76abSEd Tanous       std::string xml;
199377e76abSEd Tanous       r.unpack(xml);
200377e76abSEd Tanous       EXPECT_EQ(r.get_type(), "method_return");
201377e76abSEd Tanous       // TODO(ed) turn back on when method interface stabilizes
202377e76abSEd Tanous       // EXPECT_EQ(xml, dbus_boilerplate + "<node></node>");
203377e76abSEd Tanous     }
204377e76abSEd Tanous 
205377e76abSEd Tanous     io.stop();
206377e76abSEd Tanous 
207377e76abSEd Tanous   });
208377e76abSEd Tanous 
209377e76abSEd Tanous   io.run();
210377e76abSEd Tanous }
211377e76abSEd Tanous 
test_method(uint32_t x)212e3b0bf5bSEd Tanous std::tuple<int> test_method(uint32_t x) {
213e3b0bf5bSEd Tanous   std::cout << "method called.\n";
214e3b0bf5bSEd Tanous   return std::make_tuple<int>(42);
215e3b0bf5bSEd Tanous }
216e3b0bf5bSEd Tanous 
217e3b0bf5bSEd Tanous class TestClass {
218e3b0bf5bSEd Tanous  public:
test_method(uint32_t x)219e3b0bf5bSEd Tanous   static std::tuple<int> test_method(uint32_t x) {
220e3b0bf5bSEd Tanous     std::cout << "method called.\n";
221e3b0bf5bSEd Tanous     return std::make_tuple<int>(42);
222e3b0bf5bSEd Tanous   }
223e3b0bf5bSEd Tanous };
224e3b0bf5bSEd Tanous 
TEST(DbusPropertiesInterface,MethodServer)225377e76abSEd Tanous TEST(DbusPropertiesInterface, MethodServer) {
226377e76abSEd Tanous   boost::asio::io_service io;
227377e76abSEd Tanous   auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
228377e76abSEd Tanous 
229377e76abSEd Tanous   // Set up the object server, and send some test objects
230377e76abSEd Tanous   dbus::DbusObjectServer foo(bus);
231377e76abSEd Tanous   auto object =
232377e76abSEd Tanous       std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1");
233377e76abSEd Tanous   foo.register_object(object);
234377e76abSEd Tanous 
235377e76abSEd Tanous   auto iface = std::make_shared<dbus::DbusInterface>(
236377e76abSEd Tanous       "org.freedesktop.My.Interface", bus);
237377e76abSEd Tanous   object->register_interface(iface);
238377e76abSEd Tanous 
239e3b0bf5bSEd Tanous   // Test multiple ways to register methods
240e3b0bf5bSEd Tanous   // Basic lambda
241e3b0bf5bSEd Tanous   iface->register_method("MyMethodLambda", [](uint32_t x) {
242377e76abSEd Tanous 
243377e76abSEd Tanous     std::cout << "method called.  Got:" << x << "\n";
244377e76abSEd Tanous     return std::make_tuple<int>(42);
245377e76abSEd Tanous   });
246377e76abSEd Tanous 
247e3b0bf5bSEd Tanous   // std::function
248e3b0bf5bSEd Tanous   std::function<typename std::tuple<int>(uint32_t)> my_function =
249e3b0bf5bSEd Tanous       [](uint32_t x) {
250e3b0bf5bSEd Tanous 
251e3b0bf5bSEd Tanous         std::cout << "method called.  Got:" << x << "\n";
252e3b0bf5bSEd Tanous         return std::make_tuple<int>(42);
253e3b0bf5bSEd Tanous       };
254e3b0bf5bSEd Tanous 
255e3b0bf5bSEd Tanous   iface->register_method("MyMethodStdFunction", my_function);
256e3b0bf5bSEd Tanous 
257e3b0bf5bSEd Tanous   // Function pointer
258e3b0bf5bSEd Tanous   iface->register_method("MyMethodFunctionPointer", &test_method);
259e3b0bf5bSEd Tanous 
260e3b0bf5bSEd Tanous   // Class function pointer
261e3b0bf5bSEd Tanous   TestClass t;
262e3b0bf5bSEd Tanous   iface->register_method("MyClassFunctionPointer", t.test_method);
263e3b0bf5bSEd Tanous 
264e3b0bf5bSEd Tanous   // const class function pointer
265e3b0bf5bSEd Tanous   const TestClass t2;
266e3b0bf5bSEd Tanous   iface->register_method("MyClassFunctionPointer", t2.test_method);
267e3b0bf5bSEd Tanous 
268e3b0bf5bSEd Tanous   iface->register_method("VoidMethod", []() {
269e3b0bf5bSEd Tanous 
270e3b0bf5bSEd Tanous     std::cout << "method called.\n";
271e3b0bf5bSEd Tanous     return (uint32_t)42;
272e3b0bf5bSEd Tanous   });
273e3b0bf5bSEd Tanous 
274377e76abSEd Tanous   iface->register_method("VoidMethod", []() {
275377e76abSEd Tanous 
276377e76abSEd Tanous     std::cout << "method called.\n";
277377e76abSEd Tanous     return std::make_tuple<int>(42);
278377e76abSEd Tanous   });
279377e76abSEd Tanous 
280e3b0bf5bSEd Tanous   // Test multiple ways to register methods
281e3b0bf5bSEd Tanous   // Basic lambda
282e3b0bf5bSEd Tanous   iface->register_method("MyMethodLambda", {"x"}, {"return_value_name"},
283e3b0bf5bSEd Tanous                          [](uint32_t x) {
284e3b0bf5bSEd Tanous                            std::cout << "method called.  Got:" << x << "\n";
285e3b0bf5bSEd Tanous                            return 42;
286e3b0bf5bSEd Tanous                          });
287377e76abSEd Tanous 
288377e76abSEd Tanous   dbus::endpoint test_daemon(bus->get_unique_name(), "/org/freedesktop/test1",
289377e76abSEd Tanous                              "org.freedesktop.DBus.Introspectable");
290377e76abSEd Tanous   dbus::message m = dbus::message::new_call(test_daemon, "Introspect");
291377e76abSEd Tanous 
292377e76abSEd Tanous   bus->async_send(m, [&](const boost::system::error_code ec, dbus::message r) {
293377e76abSEd Tanous     if (ec) {
294377e76abSEd Tanous       std::string error;
295377e76abSEd Tanous       r.unpack(error);
296377e76abSEd Tanous 
297377e76abSEd Tanous       FAIL() << ec << error;
298377e76abSEd Tanous     } else {
299377e76abSEd Tanous       std::string xml;
300377e76abSEd Tanous       r.unpack(xml);
301377e76abSEd Tanous       EXPECT_EQ(r.get_type(), "method_return");
302377e76abSEd Tanous       // todo(ED)
303377e76abSEd Tanous       /*
304377e76abSEd Tanous       EXPECT_EQ(xml, dbus_boilerplate +
305377e76abSEd Tanous                          "<node><interface "
306377e76abSEd Tanous                          "name=\"MyInterface\"><method "
307377e76abSEd Tanous                          "name=\"MyMethod\"></method></interface></node>");
308377e76abSEd Tanous                          */
309377e76abSEd Tanous     }
310377e76abSEd Tanous     io.stop();
311377e76abSEd Tanous   });
312377e76abSEd Tanous 
313377e76abSEd Tanous   io.run();
314377e76abSEd Tanous }
315377e76abSEd Tanous 
TEST(DbusPropertiesInterface,PropertiesInterface)316377e76abSEd Tanous TEST(DbusPropertiesInterface, PropertiesInterface) {
317377e76abSEd Tanous   boost::asio::io_service io;
318377e76abSEd Tanous   auto bus = std::make_shared<dbus::connection>(io, dbus::bus::session);
319377e76abSEd Tanous 
320377e76abSEd Tanous   // Set up the object server, and send some test objects
321377e76abSEd Tanous   dbus::DbusObjectServer foo(bus);
322377e76abSEd Tanous   auto object =
323377e76abSEd Tanous       std::make_shared<dbus::DbusObject>(bus, "/org/freedesktop/test1");
324377e76abSEd Tanous   foo.register_object(object);
325377e76abSEd Tanous 
326377e76abSEd Tanous   auto iface = std::make_shared<dbus::DbusInterface>(
327377e76abSEd Tanous       "org.freedesktop.My.Interface", bus);
328377e76abSEd Tanous   object->register_interface(iface);
329377e76abSEd Tanous 
330377e76abSEd Tanous   iface->set_property("foo", (uint32_t)26);
331377e76abSEd Tanous 
332*a909a822SEd Tanous   dbus::endpoint get_dbus_properties(bus->get_unique_name(),
333*a909a822SEd Tanous                                      "/org/freedesktop/test1",
334*a909a822SEd Tanous                                      "org.freedesktop.DBus.Properties", "Get");
335*a909a822SEd Tanous   size_t outstanding_async_calls = 0;
336377e76abSEd Tanous 
337*a909a822SEd Tanous   outstanding_async_calls++;
338*a909a822SEd Tanous   bus->async_method_call(
339*a909a822SEd Tanous       [&](const boost::system::error_code ec, dbus::dbus_variant value) {
340*a909a822SEd Tanous         outstanding_async_calls--;
341377e76abSEd Tanous         if (ec) {
342*a909a822SEd Tanous           FAIL() << ec;
343377e76abSEd Tanous         } else {
344377e76abSEd Tanous           EXPECT_EQ(boost::get<uint32_t>(value), 26);
345377e76abSEd Tanous         }
346*a909a822SEd Tanous         if (outstanding_async_calls == 0) {
347377e76abSEd Tanous           io.stop();
348*a909a822SEd Tanous         }
349*a909a822SEd Tanous       },
350*a909a822SEd Tanous       get_dbus_properties, "org.freedesktop.My.Interface", "foo");
351*a909a822SEd Tanous 
352*a909a822SEd Tanous   dbus::endpoint getall_dbus_properties(
353*a909a822SEd Tanous       bus->get_unique_name(), "/org/freedesktop/test1",
354*a909a822SEd Tanous       "org.freedesktop.DBus.Properties", "GetAll");
355*a909a822SEd Tanous 
356*a909a822SEd Tanous   outstanding_async_calls++;
357*a909a822SEd Tanous   bus->async_method_call(
358*a909a822SEd Tanous       [&](const boost::system::error_code ec,
359*a909a822SEd Tanous           std::vector<std::pair<std::string, dbus::dbus_variant>> value) {
360*a909a822SEd Tanous         outstanding_async_calls--;
361*a909a822SEd Tanous         if (ec) {
362*a909a822SEd Tanous           FAIL() << ec;
363*a909a822SEd Tanous         } else {
364*a909a822SEd Tanous           EXPECT_EQ(value.size(), 1);
365*a909a822SEd Tanous           EXPECT_EQ(value[0].first, "foo");
366*a909a822SEd Tanous           EXPECT_EQ(value[0].second, dbus::dbus_variant((uint32_t)26));
367*a909a822SEd Tanous         }
368*a909a822SEd Tanous         if (outstanding_async_calls == 0) {
369*a909a822SEd Tanous           io.stop();
370*a909a822SEd Tanous         }
371*a909a822SEd Tanous       },
372*a909a822SEd Tanous       getall_dbus_properties, "org.freedesktop.My.Interface");
373377e76abSEd Tanous 
374377e76abSEd Tanous   io.run();
375377e76abSEd Tanous }
376