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 "dbus/server/dbus_thread_object_ncp.hpp"
40 #include "dbus/server/dbus_thread_object_rcp.hpp"
41 #include "mdns/mdns.hpp"
42
43 namespace otbr {
44 namespace DBus {
45
46 const struct timeval DBusAgent::kPollTimeout = {0, 0};
47 constexpr std::chrono::seconds DBusAgent::kDBusWaitAllowance;
48
DBusAgent(otbr::Ncp::ThreadHost & aHost,Mdns::Publisher & aPublisher)49 DBusAgent::DBusAgent(otbr::Ncp::ThreadHost &aHost, Mdns::Publisher &aPublisher)
50 : mInterfaceName(aHost.GetInterfaceName())
51 , mHost(aHost)
52 , mPublisher(aPublisher)
53 {
54 }
55
Init(otbr::BorderAgent & aBorderAgent)56 void DBusAgent::Init(otbr::BorderAgent &aBorderAgent)
57 {
58 otbrError error = OTBR_ERROR_NONE;
59
60 auto connection_deadline = Clock::now() + kDBusWaitAllowance;
61
62 while ((mConnection = PrepareDBusConnection()) == nullptr && Clock::now() < connection_deadline)
63 {
64 otbrLogWarning("Failed to setup DBus connection, will retry after 1 second");
65 std::this_thread::sleep_for(std::chrono::seconds(1));
66 }
67
68 VerifyOrDie(mConnection != nullptr, "Failed to get DBus connection");
69
70 switch (mHost.GetCoprocessorType())
71 {
72 case OT_COPROCESSOR_RCP:
73 mThreadObject = MakeUnique<DBusThreadObjectRcp>(*mConnection, mInterfaceName,
74 static_cast<Ncp::RcpHost &>(mHost), &mPublisher, aBorderAgent);
75 break;
76
77 case OT_COPROCESSOR_NCP:
78 mThreadObject =
79 MakeUnique<DBusThreadObjectNcp>(*mConnection, mInterfaceName, static_cast<Ncp::NcpHost &>(mHost));
80 break;
81
82 default:
83 DieNow("Unknown coprocessor type!");
84 break;
85 }
86
87 error = mThreadObject->Init();
88 VerifyOrDie(error == OTBR_ERROR_NONE, "Failed to initialize DBus Agent");
89 }
90
PrepareDBusConnection(void)91 DBusAgent::UniqueDBusConnection DBusAgent::PrepareDBusConnection(void)
92 {
93 DBusError dbusError;
94 DBusConnection *conn = nullptr;
95 UniqueDBusConnection uniqueConn;
96 int requestReply;
97 std::string serverName = OTBR_DBUS_SERVER_PREFIX + mInterfaceName;
98
99 dbus_error_init(&dbusError);
100
101 conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbusError);
102
103 uniqueConn = UniqueDBusConnection(conn, [](DBusConnection *aConnection) { dbus_connection_unref(aConnection); });
104
105 VerifyOrExit(uniqueConn != nullptr,
106 otbrLogWarning("Failed to get DBus connection: %s: %s", dbusError.name, dbusError.message));
107 dbus_bus_register(uniqueConn.get(), &dbusError);
108
109 requestReply =
110 dbus_bus_request_name(uniqueConn.get(), serverName.c_str(), DBUS_NAME_FLAG_REPLACE_EXISTING, &dbusError);
111 VerifyOrExit(requestReply == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER ||
112 requestReply == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER,
113 {
114 otbrLogWarning("Failed to request DBus name: %s: %s", dbusError.name, dbusError.message);
115 uniqueConn = nullptr;
116 });
117 VerifyOrExit(
118 dbus_connection_set_watch_functions(uniqueConn.get(), AddDBusWatch, RemoveDBusWatch, nullptr, this, nullptr),
119 uniqueConn = nullptr);
120
121 exit:
122 dbus_error_free(&dbusError);
123
124 return uniqueConn;
125 }
126
AddDBusWatch(struct DBusWatch * aWatch,void * aContext)127 dbus_bool_t DBusAgent::AddDBusWatch(struct DBusWatch *aWatch, void *aContext)
128 {
129 static_cast<DBusAgent *>(aContext)->mWatches.insert(aWatch);
130 return TRUE;
131 }
132
RemoveDBusWatch(struct DBusWatch * aWatch,void * aContext)133 void DBusAgent::RemoveDBusWatch(struct DBusWatch *aWatch, void *aContext)
134 {
135 static_cast<DBusAgent *>(aContext)->mWatches.erase(aWatch);
136 }
137
Update(MainloopContext & aMainloop)138 void DBusAgent::Update(MainloopContext &aMainloop)
139 {
140 unsigned int flags;
141 int fd;
142 uint8_t fdSetMask = MainloopContext::kErrorFdSet;
143
144 if (dbus_connection_get_dispatch_status(mConnection.get()) == DBUS_DISPATCH_DATA_REMAINS)
145 {
146 aMainloop.mTimeout = {0, 0};
147 }
148
149 for (const auto &watch : mWatches)
150 {
151 if (!dbus_watch_get_enabled(watch))
152 {
153 continue;
154 }
155
156 flags = dbus_watch_get_flags(watch);
157 fd = dbus_watch_get_unix_fd(watch);
158
159 if (fd < 0)
160 {
161 continue;
162 }
163
164 if (flags & DBUS_WATCH_READABLE)
165 {
166 fdSetMask |= MainloopContext::kReadFdSet;
167 }
168
169 if ((flags & DBUS_WATCH_WRITABLE))
170 {
171 fdSetMask |= MainloopContext::kWriteFdSet;
172 }
173
174 aMainloop.AddFdToSet(fd, fdSetMask);
175 }
176 }
177
Process(const MainloopContext & aMainloop)178 void DBusAgent::Process(const MainloopContext &aMainloop)
179 {
180 unsigned int flags;
181 int fd;
182
183 for (const auto &watch : mWatches)
184 {
185 if (!dbus_watch_get_enabled(watch))
186 {
187 continue;
188 }
189
190 flags = dbus_watch_get_flags(watch);
191 fd = dbus_watch_get_unix_fd(watch);
192
193 if (fd < 0)
194 {
195 continue;
196 }
197
198 if ((flags & DBUS_WATCH_READABLE) && !FD_ISSET(fd, &aMainloop.mReadFdSet))
199 {
200 flags &= static_cast<unsigned int>(~DBUS_WATCH_READABLE);
201 }
202
203 if ((flags & DBUS_WATCH_WRITABLE) && !FD_ISSET(fd, &aMainloop.mWriteFdSet))
204 {
205 flags &= static_cast<unsigned int>(~DBUS_WATCH_WRITABLE);
206 }
207
208 if (FD_ISSET(fd, &aMainloop.mErrorFdSet))
209 {
210 flags |= DBUS_WATCH_ERROR;
211 }
212
213 dbus_watch_handle(watch, flags);
214 }
215
216 while (DBUS_DISPATCH_DATA_REMAINS == dbus_connection_dispatch(mConnection.get()))
217 ;
218 }
219
220 } // namespace DBus
221 } // namespace otbr
222