1 /*
2 * Copyright (c) 2021, 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 /**
30 * @file
31 * The file implements the OTBR Agent.
32 */
33
34 #define OTBR_LOG_TAG "APP"
35
36 #ifdef HAVE_LIBSYSTEMD
37 #include <systemd/sd-daemon.h>
38 #endif
39
40 #include "agent/application.hpp"
41 #include "common/code_utils.hpp"
42 #include "common/mainloop_manager.hpp"
43 #include "host/posix/dnssd.hpp"
44 #include "utils/infra_link_selector.hpp"
45
46 namespace otbr {
47
48 #ifndef OTBR_MAINLOOP_POLL_TIMEOUT_SEC
49 #define OTBR_MAINLOOP_POLL_TIMEOUT_SEC 10
50 #endif
51
52 std::atomic_bool Application::sShouldTerminate(false);
53 const struct timeval Application::kPollTimeout = {OTBR_MAINLOOP_POLL_TIMEOUT_SEC, 0};
54
Application(Host::ThreadHost & aHost,const std::string & aInterfaceName,const std::string & aBackboneInterfaceName,const std::string & aRestListenAddress,int aRestListenPort)55 Application::Application(Host::ThreadHost &aHost,
56 const std::string &aInterfaceName,
57 const std::string &aBackboneInterfaceName,
58 const std::string &aRestListenAddress,
59 int aRestListenPort)
60 : mInterfaceName(aInterfaceName)
61 , mBackboneInterfaceName(aBackboneInterfaceName.c_str())
62 , mHost(aHost)
63 #if OTBR_ENABLE_MDNS
64 , mPublisher(
65 Mdns::Publisher::Create([this](Mdns::Publisher::State aState) { mMdnsStateSubject.UpdateState(aState); }))
66 #endif
67 #if OTBR_ENABLE_DNSSD_PLAT
68 , mDnssdPlatform(*mPublisher)
69 #endif
70 #if OTBR_ENABLE_DBUS_SERVER && OTBR_ENABLE_BORDER_AGENT
71 , mDBusAgent(MakeUnique<DBus::DBusAgent>(mHost, *mPublisher))
72 #endif
73 {
74 if (mHost.GetCoprocessorType() == OT_COPROCESSOR_RCP)
75 {
76 CreateRcpMode(aRestListenAddress, aRestListenPort);
77 }
78 }
79
Init(void)80 void Application::Init(void)
81 {
82 mHost.Init();
83
84 switch (mHost.GetCoprocessorType())
85 {
86 case OT_COPROCESSOR_RCP:
87 InitRcpMode();
88 break;
89 case OT_COPROCESSOR_NCP:
90 InitNcpMode();
91 break;
92 default:
93 DieNow("Unknown coprocessor type!");
94 break;
95 }
96
97 otbrLogInfo("Co-processor version: %s", mHost.GetCoprocessorVersion());
98 }
99
Deinit(void)100 void Application::Deinit(void)
101 {
102 switch (mHost.GetCoprocessorType())
103 {
104 case OT_COPROCESSOR_RCP:
105 DeinitRcpMode();
106 break;
107 case OT_COPROCESSOR_NCP:
108 DeinitNcpMode();
109 break;
110 default:
111 DieNow("Unknown coprocessor type!");
112 break;
113 }
114
115 mHost.Deinit();
116 }
117
Run(void)118 otbrError Application::Run(void)
119 {
120 otbrError error = OTBR_ERROR_NONE;
121
122 #ifdef HAVE_LIBSYSTEMD
123 if (getenv("SYSTEMD_EXEC_PID") != nullptr)
124 {
125 otbrLogInfo("Notify systemd the service is ready.");
126
127 // Ignored return value as systemd recommends.
128 // See https://www.freedesktop.org/software/systemd/man/sd_notify.html
129 sd_notify(0, "READY=1");
130 }
131 #endif
132
133 #if OTBR_ENABLE_NOTIFY_UPSTART
134 if (getenv("UPSTART_JOB") != nullptr)
135 {
136 otbrLogInfo("Notify Upstart the service is ready.");
137 if (raise(SIGSTOP))
138 {
139 otbrLogWarning("Failed to notify Upstart.");
140 }
141 }
142 #endif
143
144 // allow quitting elegantly
145 signal(SIGTERM, HandleSignal);
146
147 // avoid exiting on SIGPIPE
148 signal(SIGPIPE, SIG_IGN);
149
150 while (!sShouldTerminate)
151 {
152 otbr::MainloopContext mainloop;
153 int rval;
154
155 mainloop.mMaxFd = -1;
156 mainloop.mTimeout = kPollTimeout;
157
158 FD_ZERO(&mainloop.mReadFdSet);
159 FD_ZERO(&mainloop.mWriteFdSet);
160 FD_ZERO(&mainloop.mErrorFdSet);
161
162 MainloopManager::GetInstance().Update(mainloop);
163
164 rval = select(mainloop.mMaxFd + 1, &mainloop.mReadFdSet, &mainloop.mWriteFdSet, &mainloop.mErrorFdSet,
165 &mainloop.mTimeout);
166
167 if (rval >= 0)
168 {
169 MainloopManager::GetInstance().Process(mainloop);
170
171 if (mErrorCondition)
172 {
173 error = mErrorCondition();
174 if (error != OTBR_ERROR_NONE)
175 {
176 break;
177 }
178 }
179 }
180 else if (errno != EINTR)
181 {
182 error = OTBR_ERROR_ERRNO;
183 otbrLogErr("select() failed: %s", strerror(errno));
184 break;
185 }
186 }
187
188 return error;
189 }
190
HandleSignal(int aSignal)191 void Application::HandleSignal(int aSignal)
192 {
193 sShouldTerminate = true;
194 signal(aSignal, SIG_DFL);
195 }
196
CreateRcpMode(const std::string & aRestListenAddress,int aRestListenPort)197 void Application::CreateRcpMode(const std::string &aRestListenAddress, int aRestListenPort)
198 {
199 otbr::Host::RcpHost &rcpHost = static_cast<otbr::Host::RcpHost &>(mHost);
200 #if OTBR_ENABLE_BORDER_AGENT
201 mBorderAgent = MakeUnique<BorderAgent>(rcpHost, *mPublisher);
202 #endif
203 #if OTBR_ENABLE_BACKBONE_ROUTER
204 mBackboneAgent = MakeUnique<BackboneRouter::BackboneAgent>(rcpHost, mInterfaceName, mBackboneInterfaceName);
205 #endif
206 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
207 mAdvertisingProxy = MakeUnique<AdvertisingProxy>(rcpHost, *mPublisher);
208 #endif
209 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
210 mDiscoveryProxy = MakeUnique<Dnssd::DiscoveryProxy>(rcpHost, *mPublisher);
211 #endif
212 #if OTBR_ENABLE_TREL
213 mTrelDnssd = MakeUnique<TrelDnssd::TrelDnssd>(rcpHost, *mPublisher);
214 #endif
215 #if OTBR_ENABLE_OPENWRT
216 mUbusAgent = MakeUnique<ubus::UBusAgent>(rcpHost);
217 #endif
218 #if OTBR_ENABLE_REST_SERVER
219 mRestWebServer = MakeUnique<rest::RestWebServer>(rcpHost, aRestListenAddress, aRestListenPort);
220 #endif
221 #if OTBR_ENABLE_VENDOR_SERVER
222 mVendorServer = vendor::VendorServer::newInstance(*this);
223 #endif
224
225 OT_UNUSED_VARIABLE(aRestListenAddress);
226 OT_UNUSED_VARIABLE(aRestListenPort);
227 }
228
InitRcpMode(void)229 void Application::InitRcpMode(void)
230 {
231 Host::RcpHost &rcpHost = static_cast<otbr::Host::RcpHost &>(mHost);
232 OTBR_UNUSED_VARIABLE(rcpHost);
233
234 #if OTBR_ENABLE_BORDER_AGENT
235 mMdnsStateSubject.AddObserver(*mBorderAgent);
236 #endif
237 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
238 mMdnsStateSubject.AddObserver(*mAdvertisingProxy);
239 #endif
240 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
241 mMdnsStateSubject.AddObserver(*mDiscoveryProxy);
242 #endif
243 #if OTBR_ENABLE_TREL
244 mMdnsStateSubject.AddObserver(*mTrelDnssd);
245 #endif
246 #if OTBR_ENABLE_DNSSD_PLAT
247 mMdnsStateSubject.AddObserver(mDnssdPlatform);
248 mDnssdPlatform.SetDnssdStateChangedCallback(([&rcpHost](otPlatDnssdState aState) {
249 OTBR_UNUSED_VARIABLE(aState);
250 otPlatDnssdStateHandleStateChange(rcpHost.GetInstance());
251 }));
252 #endif
253
254 #if OTBR_ENABLE_MDNS
255 mPublisher->Start();
256 #endif
257 #if OTBR_ENABLE_BORDER_AGENT
258 mBorderAgent->Init();
259 // This is for delaying publishing the MeshCoP service until the correct
260 // vendor name and OUI etc. are correctly set by BorderAgent::SetMeshCopServiceValues()
261 #if OTBR_STOP_BORDER_AGENT_ON_INIT
262 mBorderAgent->SetEnabled(false);
263 #else
264 mBorderAgent->SetEnabled(true);
265 #endif
266 #endif
267 #if OTBR_ENABLE_BACKBONE_ROUTER
268 mBackboneAgent->Init();
269 #endif
270 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
271 mAdvertisingProxy->SetEnabled(true);
272 #endif
273 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
274 mDiscoveryProxy->SetEnabled(true);
275 #endif
276 #if OTBR_ENABLE_OPENWRT
277 mUbusAgent->Init();
278 #endif
279 #if OTBR_ENABLE_REST_SERVER
280 mRestWebServer->Init();
281 #endif
282 #if OTBR_ENABLE_DBUS_SERVER
283 mDBusAgent->Init(*mBorderAgent);
284 #endif
285 #if OTBR_ENABLE_VENDOR_SERVER
286 mVendorServer->Init();
287 #endif
288 #if OTBR_ENABLE_DNSSD_PLAT
289 mDnssdPlatform.Start();
290 #endif
291 }
292
DeinitRcpMode(void)293 void Application::DeinitRcpMode(void)
294 {
295 #if OTBR_ENABLE_DNSSD_PLAT
296 mDnssdPlatform.Stop();
297 #endif
298 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
299 mAdvertisingProxy->SetEnabled(false);
300 #endif
301 #if OTBR_ENABLE_DNSSD_DISCOVERY_PROXY
302 mDiscoveryProxy->SetEnabled(false);
303 #endif
304 #if OTBR_ENABLE_BORDER_AGENT
305 mBorderAgent->SetEnabled(false);
306 mBorderAgent->Deinit();
307 #endif
308 #if OTBR_ENABLE_MDNS
309 mMdnsStateSubject.Clear();
310 mPublisher->Stop();
311 #endif
312 }
313
InitNcpMode(void)314 void Application::InitNcpMode(void)
315 {
316 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
317 otbr::Host::NcpHost &ncpHost = static_cast<otbr::Host::NcpHost &>(mHost);
318 ncpHost.SetMdnsPublisher(mPublisher.get());
319 mMdnsStateSubject.AddObserver(ncpHost);
320 mPublisher->Start();
321 #endif
322 #if OTBR_ENABLE_DBUS_SERVER
323 mDBusAgent->Init(*mBorderAgent);
324 #endif
325 }
326
DeinitNcpMode(void)327 void Application::DeinitNcpMode(void)
328 {
329 #if OTBR_ENABLE_SRP_ADVERTISING_PROXY
330 mPublisher->Stop();
331 #endif
332 }
333
334 } // namespace otbr
335