1 #include <boost/asio.hpp>
2 #include <sdbusplus/asio/connection.hpp>
3 #include <sdbusplus/asio/object_server.hpp>
4 #include <sdbusplus/asio/property.hpp>
5 #include <sdbusplus/bus.hpp>
6 #include <sdbusplus/unpack_properties.hpp>
7 
8 #include <iostream>
9 
10 const std::string demoServiceName = "demo.service";
11 const std::string demoObjectPath = "/xyz/demo";
12 const std::string demoInterfaceName = "xyz.demo";
13 const std::string propertyGrettingName = "Greetings";
14 const std::string propertyGoodbyesName = "Goodbyes";
15 const std::string propertyValueName = "Value";
16 
17 class Application
18 {
19   public:
20     Application(boost::asio::io_context& ioc, sdbusplus::asio::connection& bus,
21                 sdbusplus::asio::object_server& objServer) :
22         ioc_(ioc),
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 1;
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 logBadProperty(const std::string& badProperty)
65     {
66         std::cerr << "BadProperty: " << badProperty << "\n";
67         ++fatalErrors_;
68     }
69 
70     void logExpectedException(
71         const sdbusplus::exception::UnpackPropertyError& error)
72     {
73         std::cout << "As expected " << error.what() << " => "
74                   << error.propertyName << " is missing because "
75                   << error.reason << "\n";
76     }
77 
78     void asyncGetAllPropertiesStringTypeOnly()
79     {
80         sdbusplus::asio::getAllProperties(
81             bus_, demoServiceName, demoObjectPath, demoInterfaceName,
82             [this](boost::system::error_code ec,
83                    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                     std::string greetings;
93                     std::string goodbyes;
94                     std::optional<std::string> badProperty =
95                         sdbusplus::unpackPropertiesNoThrow(
96                             properties, propertyGrettingName, greetings,
97                             propertyGoodbyesName, goodbyes);
98 
99                     if (badProperty)
100                     {
101                         logBadProperty(*badProperty);
102                     }
103                     else
104                     {
105                         std::cout << "value of greetings: " << greetings
106                                   << "\n";
107                         std::cout << "value of goodbyes: " << goodbyes << "\n";
108                     }
109                 }
110 
111                 try
112                 {
113                     std::string value;
114                     sdbusplus::unpackProperties(properties, propertyValueName,
115                                                 value);
116 
117                     std::cerr << "Error: it should fail because of "
118                                  "not matched type\n";
119                     ++fatalErrors_;
120                 }
121                 catch (const sdbusplus::exception::UnpackPropertyError& error)
122                 {
123                     logExpectedException(error);
124                 }
125             });
126     }
127 
128     void asyncGetAllProperties()
129     {
130         sdbusplus::asio::getAllProperties(
131             bus_, demoServiceName, demoObjectPath, demoInterfaceName,
132             [this](boost::system::error_code ec,
133                    std::vector<std::pair<
134                        std::string,
135                        std::variant<std::monostate, std::string, uint32_t>>>&
136                        properties) -> void {
137                 if (ec)
138                 {
139                     logSystemErrorCode(ec);
140                     return;
141                 }
142                 try
143                 {
144                     std::string greetings;
145                     std::string goodbyes;
146                     uint32_t value = 0u;
147                     sdbusplus::unpackProperties(properties,
148                                                 propertyGrettingName, greetings,
149                                                 propertyGoodbyesName, goodbyes,
150                                                 propertyValueName, value);
151 
152                     std::cout << "value of greetings: " << greetings << "\n";
153                     std::cout << "value of goodbyes: " << goodbyes << "\n";
154                     std::cout << "value of value: " << value << "\n";
155                 }
156                 catch (const sdbusplus::exception::UnpackPropertyError& error)
157                 {
158                     logException(error);
159                 }
160 
161                 try
162                 {
163                     std::string unknownProperty;
164                     sdbusplus::unpackProperties(
165                         properties, "UnknownPropertyName", unknownProperty);
166 
167                     std::cerr << "Error: it should fail because of "
168                                  "missing property\n";
169                     ++fatalErrors_;
170                 }
171                 catch (const sdbusplus::exception::UnpackPropertyError& error)
172                 {
173                     logExpectedException(error);
174                 }
175 
176                 try
177                 {
178                     uint32_t notMatchingType;
179                     sdbusplus::unpackProperties(
180                         properties, propertyGrettingName, notMatchingType);
181 
182                     std::cerr << "Error: it should fail because of "
183                                  "not matched type\n";
184                     ++fatalErrors_;
185                 }
186                 catch (const sdbusplus::exception::UnpackPropertyError& error)
187                 {
188                     logExpectedException(error);
189                 }
190             });
191     }
192 
193   private:
194     boost::asio::io_context& ioc_;
195     sdbusplus::asio::connection& bus_;
196     sdbusplus::asio::object_server& objServer_;
197 
198     std::unique_ptr<sdbusplus::asio::dbus_interface> demo_;
199     std::string greetings_ = "Hello";
200     std::string goodbyes_ = "Bye";
201 
202     uint32_t fatalErrors_ = 0u;
203 };
204 
205 int main(int, char**)
206 {
207     boost::asio::io_context ioc;
208     boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);
209 
210     signals.async_wait(
211         [&ioc](const boost::system::error_code&, const int&) { ioc.stop(); });
212 
213     auto bus = std::make_shared<sdbusplus::asio::connection>(ioc);
214     auto objServer = std::make_unique<sdbusplus::asio::object_server>(bus);
215 
216     bus->request_name(demoServiceName.c_str());
217 
218     Application app(ioc, *bus, *objServer);
219 
220     boost::asio::post(ioc,
221                       [&app] { app.asyncGetAllPropertiesStringTypeOnly(); });
222     boost::asio::post(ioc, [&app] { app.asyncGetAllProperties(); });
223 
224     ioc.run();
225 
226     std::cout << "Fatal errors count: " << app.fatalErrors() << "\n";
227 
228     return app.fatalErrors();
229 }
230