1 /*
2 * Copyright (c) 2020, The OpenThread Authors.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. Neither the name of the copyright holder nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #define OTBR_LOG_TAG "DBUS"
30
31 #include "dbus/server/dbus_agent.hpp"
32
33 #include <chrono>
34 #include <thread>
35 #include <unistd.h>
36
37 #include "common/logging.hpp"
38 #include "dbus/common/constants.hpp"
39 #include "mdns/mdns.hpp"
40
41 namespace otbr {
42 namespace DBus {
43
44 const struct timeval DBusAgent::kPollTimeout = {0, 0};
45 constexpr std::chrono::seconds DBusAgent::kDBusWaitAllowance;
46
DBusAgent(otbr::Ncp::ControllerOpenThread & aNcp,Mdns::Publisher & aPublisher)47 DBusAgent::DBusAgent(otbr::Ncp::ControllerOpenThread &aNcp, Mdns::Publisher &aPublisher)
48 : mInterfaceName(aNcp.GetInterfaceName())
49 , mNcp(aNcp)
50 , mPublisher(aPublisher)
51 {
52 }
53
Init(void)54 void DBusAgent::Init(void)
55 {
56 otbrError error = OTBR_ERROR_NONE;
57
58 auto connection_deadline = Clock::now() + kDBusWaitAllowance;
59
60 while ((mConnection = PrepareDBusConnection()) == nullptr && Clock::now() < connection_deadline)
61 {
62 otbrLogWarning("Failed to setup DBus connection, will retry after 1 second");
63 std::this_thread::sleep_for(std::chrono::seconds(1));
64 }
65
66 VerifyOrDie(mConnection != nullptr, "Failed to get DBus connection");
67
68 mThreadObject =
69 std::unique_ptr<DBusThreadObject>(new DBusThreadObject(mConnection.get(), mInterfaceName, &mNcp, &mPublisher));
70 error = mThreadObject->Init();
71 VerifyOrDie(error == OTBR_ERROR_NONE, "Failed to initialize DBus Agent");
72 }
73
PrepareDBusConnection(void)74 DBusAgent::UniqueDBusConnection DBusAgent::PrepareDBusConnection(void)
75 {
76 DBusError dbusError;
77 DBusConnection * conn = nullptr;
78 UniqueDBusConnection uniqueConn;
79 int requestReply;
80 std::string serverName = OTBR_DBUS_SERVER_PREFIX + mInterfaceName;
81
82 dbus_error_init(&dbusError);
83
84 conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbusError);
85
86 uniqueConn = UniqueDBusConnection(conn, [](DBusConnection *aConnection) { dbus_connection_unref(aConnection); });
87
88 VerifyOrExit(uniqueConn != nullptr,
89 otbrLogWarning("Failed to get DBus connection: %s: %s", dbusError.name, dbusError.message));
90 dbus_bus_register(uniqueConn.get(), &dbusError);
91
92 requestReply =
93 dbus_bus_request_name(uniqueConn.get(), serverName.c_str(), DBUS_NAME_FLAG_REPLACE_EXISTING, &dbusError);
94 VerifyOrExit(requestReply == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ||
95 requestReply == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER,
96 {
97 otbrLogWarning("Failed to request DBus name: %s: %s", dbusError.name, dbusError.message);
98 uniqueConn = nullptr;
99 });
100 VerifyOrExit(
101 dbus_connection_set_watch_functions(uniqueConn.get(), AddDBusWatch, RemoveDBusWatch, nullptr, this, nullptr),
102 uniqueConn = nullptr);
103
104 exit:
105 dbus_error_free(&dbusError);
106
107 return uniqueConn;
108 }
109
AddDBusWatch(struct DBusWatch * aWatch,void * aContext)110 dbus_bool_t DBusAgent::AddDBusWatch(struct DBusWatch *aWatch, void *aContext)
111 {
112 static_cast<DBusAgent *>(aContext)->mWatches.insert(aWatch);
113 return TRUE;
114 }
115
RemoveDBusWatch(struct DBusWatch * aWatch,void * aContext)116 void DBusAgent::RemoveDBusWatch(struct DBusWatch *aWatch, void *aContext)
117 {
118 static_cast<DBusAgent *>(aContext)->mWatches.erase(aWatch);
119 }
120
Update(MainloopContext & aMainloop)121 void DBusAgent::Update(MainloopContext &aMainloop)
122 {
123 unsigned int flags;
124 int fd;
125
126 if (dbus_connection_get_dispatch_status(mConnection.get()) == DBUS_DISPATCH_DATA_REMAINS)
127 {
128 aMainloop.mTimeout = {0, 0};
129 }
130
131 for (const auto &watch : mWatches)
132 {
133 if (!dbus_watch_get_enabled(watch))
134 {
135 continue;
136 }
137
138 flags = dbus_watch_get_flags(watch);
139 fd = dbus_watch_get_unix_fd(watch);
140
141 if (fd < 0)
142 {
143 continue;
144 }
145
146 if (flags & DBUS_WATCH_READABLE)
147 {
148 FD_SET(fd, &aMainloop.mReadFdSet);
149 }
150
151 if ((flags & DBUS_WATCH_WRITABLE))
152 {
153 FD_SET(fd, &aMainloop.mWriteFdSet);
154 }
155
156 FD_SET(fd, &aMainloop.mErrorFdSet);
157
158 aMainloop.mMaxFd = std::max(aMainloop.mMaxFd, fd);
159 }
160 }
161
Process(const MainloopContext & aMainloop)162 void DBusAgent::Process(const MainloopContext &aMainloop)
163 {
164 unsigned int flags;
165 int fd;
166
167 for (const auto &watch : mWatches)
168 {
169 if (!dbus_watch_get_enabled(watch))
170 {
171 continue;
172 }
173
174 flags = dbus_watch_get_flags(watch);
175 fd = dbus_watch_get_unix_fd(watch);
176
177 if (fd < 0)
178 {
179 continue;
180 }
181
182 if ((flags & DBUS_WATCH_READABLE) && !FD_ISSET(fd, &aMainloop.mReadFdSet))
183 {
184 flags &= static_cast<unsigned int>(~DBUS_WATCH_READABLE);
185 }
186
187 if ((flags & DBUS_WATCH_WRITABLE) && !FD_ISSET(fd, &aMainloop.mWriteFdSet))
188 {
189 flags &= static_cast<unsigned int>(~DBUS_WATCH_WRITABLE);
190 }
191
192 if (FD_ISSET(fd, &aMainloop.mErrorFdSet))
193 {
194 flags |= DBUS_WATCH_ERROR;
195 }
196
197 dbus_watch_handle(watch, flags);
198 }
199
200 while (DBUS_DISPATCH_DATA_REMAINS == dbus_connection_dispatch(mConnection.get()))
201 ;
202 }
203
204 } // namespace DBus
205 } // namespace otbr
206