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),
24         objServer_(objServer)
25     {
26         demo_ = objServer_.add_unique_interface(demoObjectPath,
27                                                 demoInterfaceName);
28 
29         demo_->register_property_r<std::string>(
30             propertyGrettingName, sdbusplus::vtable::property_::const_,
31             [this](const auto&) { return greetings_; });
32 
33         demo_->register_property_rw<std::string>(
34             propertyGoodbyesName, sdbusplus::vtable::property_::emits_change,
35             [this](const auto& newPropertyValue, const auto&) {
36             goodbyes_ = newPropertyValue;
37             return true;
38         },
39             [this](const auto&) { return goodbyes_; });
40 
41         demo_->register_property_r<uint32_t>(
42             propertyValueName, sdbusplus::vtable::property_::const_,
43             [](const auto& value) -> uint32_t { return value; });
44 
45         demo_->initialize();
46     }
47 
48     uint32_t fatalErrors() const
49     {
50         return fatalErrors_;
51     }
52 
53     auto logSystemErrorCode(boost::system::error_code ec)
54     {
55         std::cerr << "Error: " << ec << "\n";
56         ++fatalErrors_;
57     }
58 
59     void logException(const std::exception& e)
60     {
61         std::cerr << "Error: " << e.what() << "\n";
62         ++fatalErrors_;
63     }
64 
65     void logUnpackError(const sdbusplus::UnpackErrorReason reason,
66                         const std::string& property)
67     {
68         std::cerr << "UnpackError: " << static_cast<int>(reason) << ", "
69                   << property << "\n";
70         ++fatalErrors_;
71     }
72 
73     void logExpectedException(
74         const sdbusplus::exception::UnpackPropertyError& error)
75     {
76         std::cout << "As expected " << error.what() << "\n";
77     }
78 
79     void asyncGetAllPropertiesStringTypeOnly()
80     {
81         sdbusplus::asio::getAllProperties(
82             bus_, demoServiceName, demoObjectPath, demoInterfaceName,
83             [this](const boost::system::error_code ec,
84                    const std::vector<std::pair<
85                        std::string, std::variant<std::monostate, std::string>>>&
86                        properties) -> void {
87             if (ec)
88             {
89                 logSystemErrorCode(ec);
90                 return;
91             }
92             {
93                 const std::string* greetings = nullptr;
94                 const std::string* goodbyes = nullptr;
95                 const bool success = sdbusplus::unpackPropertiesNoThrow(
96                     [this](const sdbusplus::UnpackErrorReason reason,
97                            const std::string& property) {
98                     logUnpackError(reason, property);
99                 },
100                     properties, propertyGrettingName, greetings,
101                     propertyGoodbyesName, goodbyes);
102 
103                 if (success)
104                 {
105                     std::cout << "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(properties, propertyGrettingName,
151                                             greetings, propertyGoodbyesName,
152                                             goodbyes, propertyValueName, value);
153 
154                 std::cout << "value of greetings: " << greetings << "\n";
155                 std::cout << "value of goodbyes: " << goodbyes << "\n";
156                 std::cout << "value of value: " << value << "\n";
157             }
158             catch (const sdbusplus::exception::UnpackPropertyError& error)
159             {
160                 logException(error);
161             }
162 
163             try
164             {
165                 std::string unknownProperty;
166                 sdbusplus::unpackProperties(properties, "UnknownPropertyName",
167                                             unknownProperty);
168 
169                 std::cerr << "Error: it should fail because of "
170                              "missing property\n";
171                 ++fatalErrors_;
172             }
173             catch (const sdbusplus::exception::UnpackPropertyError& error)
174             {
175                 logExpectedException(error);
176             }
177 
178             try
179             {
180                 uint32_t notMatchingType;
181                 sdbusplus::unpackProperties(properties, propertyGrettingName,
182                                             notMatchingType);
183 
184                 std::cerr << "Error: it should fail because of "
185                              "not matched type\n";
186                 ++fatalErrors_;
187             }
188             catch (const sdbusplus::exception::UnpackPropertyError& error)
189             {
190                 logExpectedException(error);
191             }
192         });
193     }
194 
195   private:
196     sdbusplus::asio::connection& bus_;
197     sdbusplus::asio::object_server& objServer_;
198 
199     std::unique_ptr<sdbusplus::asio::dbus_interface> demo_;
200     std::string greetings_ = "Hello";
201     std::string goodbyes_ = "Bye";
202 
203     uint32_t fatalErrors_ = 0u;
204 };
205 
206 int main(int, char**)
207 {
208     boost::asio::io_context ioc;
209     boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);
210 
211     signals.async_wait(
212         [&ioc](const boost::system::error_code&, const int&) { ioc.stop(); });
213 
214     auto bus = std::make_shared<sdbusplus::asio::connection>(ioc);
215     auto objServer = std::make_unique<sdbusplus::asio::object_server>(bus);
216 
217     bus->request_name(demoServiceName.c_str());
218 
219     Application app(*bus, *objServer);
220 
221     boost::asio::post(ioc,
222                       [&app] { app.asyncGetAllPropertiesStringTypeOnly(); });
223     boost::asio::post(ioc, [&app] { app.asyncGetAllProperties(); });
224 
225     ioc.run();
226 
227     std::cout << "Fatal errors count: " << app.fatalErrors() << "\n";
228 
229     return app.fatalErrors();
230 }
231