1 #include <boost/asio/io_context.hpp>
2 #include <boost/asio/signal_set.hpp>
3 #include <sdbusplus/asio/connection.hpp>
4 #include <sdbusplus/asio/object_server.hpp>
5 #include <sdbusplus/asio/property.hpp>
6 #include <sdbusplus/bus.hpp>
7 #include <sdbusplus/unpack_properties.hpp>
8 
9 #include <iostream>
10 
11 const std::string demoServiceName = "demo.service";
12 const std::string demoObjectPath = "/xyz/demo";
13 const std::string demoInterfaceName = "xyz.demo";
14 const std::string propertyGrettingName = "Greetings";
15 const std::string propertyGoodbyesName = "Goodbyes";
16 const std::string propertyValueName = "Value";
17 
18 class Application
19 {
20   public:
21     Application(sdbusplus::asio::connection& bus,
22                 sdbusplus::asio::object_server& objServer) :
23         bus_(bus), objServer_(objServer)
24     {
25         demo_ =
26             objServer_.add_unique_interface(demoObjectPath, demoInterfaceName);
27 
28         demo_->register_property_r<std::string>(
29             propertyGrettingName, sdbusplus::vtable::property_::const_,
30             [this](const auto&) { return greetings_; });
31 
32         demo_->register_property_rw<std::string>(
33             propertyGoodbyesName, sdbusplus::vtable::property_::emits_change,
34             [this](const auto& newPropertyValue, const auto&) {
35                 goodbyes_ = newPropertyValue;
36                 return true;
37             },
38             [this](const auto&) { return goodbyes_; });
39 
40         demo_->register_property_r<uint32_t>(
41             propertyValueName, sdbusplus::vtable::property_::const_,
42             [](const auto& value) -> uint32_t { return value; });
43 
44         demo_->initialize();
45     }
46 
47     uint32_t fatalErrors() const
48     {
49         return fatalErrors_;
50     }
51 
52     auto logSystemErrorCode(boost::system::error_code ec)
53     {
54         std::cerr << "Error: " << ec << "\n";
55         ++fatalErrors_;
56     }
57 
58     void logException(const std::exception& e)
59     {
60         std::cerr << "Error: " << e.what() << "\n";
61         ++fatalErrors_;
62     }
63 
64     void logUnpackError(const sdbusplus::UnpackErrorReason reason,
65                         const std::string& property)
66     {
67         std::cerr << "UnpackError: " << static_cast<int>(reason) << ", "
68                   << property << "\n";
69         ++fatalErrors_;
70     }
71 
72     void logExpectedException(
73         const sdbusplus::exception::UnpackPropertyError& error)
74     {
75         std::cout << "As expected " << error.what() << "\n";
76     }
77 
78     void asyncGetAllPropertiesStringTypeOnly()
79     {
80         sdbusplus::asio::getAllProperties(
81             bus_, demoServiceName, demoObjectPath, demoInterfaceName,
82             [this](const boost::system::error_code ec,
83                    const std::vector<std::pair<
84                        std::string, std::variant<std::monostate, std::string>>>&
85                        properties) -> void {
86                 if (ec)
87                 {
88                     logSystemErrorCode(ec);
89                     return;
90                 }
91                 {
92                     const std::string* greetings = nullptr;
93                     const std::string* goodbyes = nullptr;
94                     const bool success = sdbusplus::unpackPropertiesNoThrow(
95                         [this](const sdbusplus::UnpackErrorReason reason,
96                                const std::string& property) {
97                             logUnpackError(reason, property);
98                         },
99                         properties, propertyGrettingName, greetings,
100                         propertyGoodbyesName, goodbyes);
101 
102                     if (success)
103                     {
104                         std::cout
105                             << "value of greetings: " << *greetings << "\n";
106                         std::cout << "value of goodbyes: " << *goodbyes << "\n";
107                     }
108                     else
109                     {
110                         ++fatalErrors_;
111                     }
112                 }
113 
114                 try
115                 {
116                     std::string value;
117                     sdbusplus::unpackProperties(properties, propertyValueName,
118                                                 value);
119 
120                     std::cerr << "Error: it should fail because of "
121                                  "not matched type\n";
122                     ++fatalErrors_;
123                 }
124                 catch (const sdbusplus::exception::UnpackPropertyError& error)
125                 {
126                     logExpectedException(error);
127                 }
128             });
129     }
130 
131     void asyncGetAllProperties()
132     {
133         sdbusplus::asio::getAllProperties(
134             bus_, demoServiceName, demoObjectPath, demoInterfaceName,
135             [this](const boost::system::error_code ec,
136                    const std::vector<std::pair<
137                        std::string,
138                        std::variant<std::monostate, std::string, uint32_t>>>&
139                        properties) -> void {
140                 if (ec)
141                 {
142                     logSystemErrorCode(ec);
143                     return;
144                 }
145                 try
146                 {
147                     std::string greetings;
148                     std::string goodbyes;
149                     uint32_t value = 0u;
150                     sdbusplus::unpackProperties(
151                         properties, propertyGrettingName, greetings,
152                         propertyGoodbyesName, goodbyes, propertyValueName,
153                         value);
154 
155                     std::cout << "value of greetings: " << greetings << "\n";
156                     std::cout << "value of goodbyes: " << goodbyes << "\n";
157                     std::cout << "value of value: " << value << "\n";
158                 }
159                 catch (const sdbusplus::exception::UnpackPropertyError& error)
160                 {
161                     logException(error);
162                 }
163 
164                 try
165                 {
166                     std::string unknownProperty;
167                     sdbusplus::unpackProperties(
168                         properties, "UnknownPropertyName", unknownProperty);
169 
170                     std::cerr << "Error: it should fail because of "
171                                  "missing property\n";
172                     ++fatalErrors_;
173                 }
174                 catch (const sdbusplus::exception::UnpackPropertyError& error)
175                 {
176                     logExpectedException(error);
177                 }
178 
179                 try
180                 {
181                     uint32_t notMatchingType;
182                     sdbusplus::unpackProperties(
183                         properties, propertyGrettingName, notMatchingType);
184 
185                     std::cerr << "Error: it should fail because of "
186                                  "not matched type\n";
187                     ++fatalErrors_;
188                 }
189                 catch (const sdbusplus::exception::UnpackPropertyError& error)
190                 {
191                     logExpectedException(error);
192                 }
193             });
194     }
195 
196   private:
197     sdbusplus::asio::connection& bus_;
198     sdbusplus::asio::object_server& objServer_;
199 
200     std::unique_ptr<sdbusplus::asio::dbus_interface> demo_;
201     std::string greetings_ = "Hello";
202     std::string goodbyes_ = "Bye";
203 
204     uint32_t fatalErrors_ = 0u;
205 };
206 
207 int main(int, char**)
208 {
209     boost::asio::io_context ioc;
210     boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);
211 
212     signals.async_wait([&ioc](const boost::system::error_code&, const int&) {
213         ioc.stop();
214     });
215 
216     auto bus = std::make_shared<sdbusplus::asio::connection>(ioc);
217     auto objServer = std::make_unique<sdbusplus::asio::object_server>(bus);
218 
219     bus->request_name(demoServiceName.c_str());
220 
221     Application app(*bus, *objServer);
222 
223     boost::asio::post(ioc,
224                       [&app] { app.asyncGetAllPropertiesStringTypeOnly(); });
225     boost::asio::post(ioc, [&app] { app.asyncGetAllProperties(); });
226 
227     ioc.run();
228 
229     std::cout << "Fatal errors count: " << app.fatalErrors() << "\n";
230 
231     return app.fatalErrors();
232 }
233