1 /*
2 * Copyright (c) 2017, 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 "AGENT"
30
31 #include <openthread-br/config.h>
32
33 #include <algorithm>
34 #include <fstream>
35 #include <mutex>
36 #include <sstream>
37 #include <string>
38 #include <vector>
39
40 #include <assert.h>
41 #include <getopt.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 #include <openthread/logging.h>
48 #include <openthread/platform/radio.h>
49
50 #if __ANDROID__ && OTBR_CONFIG_ANDROID_PROPERTY_ENABLE
51 #include <cutils/properties.h>
52 #endif
53
54 #include "agent/application.hpp"
55 #include "common/code_utils.hpp"
56 #include "common/logging.hpp"
57 #include "common/mainloop.hpp"
58 #include "common/types.hpp"
59 #include "ncp/ncp_openthread.hpp"
60
61 static const char kSyslogIdent[] = "otbr-agent";
62 static const char kDefaultInterfaceName[] = "wpan0";
63
64 enum
65 {
66 OTBR_OPT_BACKBONE_INTERFACE_NAME = 'B',
67 OTBR_OPT_DEBUG_LEVEL = 'd',
68 OTBR_OPT_HELP = 'h',
69 OTBR_OPT_INTERFACE_NAME = 'I',
70 OTBR_OPT_VERBOSE = 'v',
71 OTBR_OPT_VERSION = 'V',
72 OTBR_OPT_SHORTMAX = 128,
73 OTBR_OPT_RADIO_VERSION,
74 OTBR_OPT_AUTO_ATTACH,
75 };
76
77 static jmp_buf sResetJump;
78 static otbr::Application *gApp = nullptr;
79
80 void __gcov_flush();
81 static const struct option kOptions[] = {
82 {"backbone-ifname", required_argument, nullptr, OTBR_OPT_BACKBONE_INTERFACE_NAME},
83 {"debug-level", required_argument, nullptr, OTBR_OPT_DEBUG_LEVEL},
84 {"help", no_argument, nullptr, OTBR_OPT_HELP},
85 {"thread-ifname", required_argument, nullptr, OTBR_OPT_INTERFACE_NAME},
86 {"verbose", no_argument, nullptr, OTBR_OPT_VERBOSE},
87 {"version", no_argument, nullptr, OTBR_OPT_VERSION},
88 {"radio-version", no_argument, nullptr, OTBR_OPT_RADIO_VERSION},
89 {"auto-attach", optional_argument, nullptr, OTBR_OPT_AUTO_ATTACH},
90 {0, 0, 0, 0}};
91
ParseInteger(const char * aStr,long & aOutResult)92 static bool ParseInteger(const char *aStr, long &aOutResult)
93 {
94 bool successful = true;
95 char *strEnd;
96 long result;
97
98 VerifyOrExit(aStr != nullptr, successful = false);
99 errno = 0;
100 result = strtol(aStr, &strEnd, 0);
101 VerifyOrExit(errno != ERANGE, successful = false);
102 VerifyOrExit(aStr != strEnd, successful = false);
103
104 aOutResult = result;
105
106 exit:
107 return successful;
108 }
109
110 static constexpr char kAutoAttachDisableArg[] = "--auto-attach=0";
111 static char sAutoAttachDisableArgStorage[sizeof(kAutoAttachDisableArg)];
112
AppendAutoAttachDisableArg(int argc,char * argv[])113 static std::vector<char *> AppendAutoAttachDisableArg(int argc, char *argv[])
114 {
115 std::vector<char *> args(argv, argv + argc);
116
117 args.erase(std::remove_if(
118 args.begin(), args.end(),
119 [](const char *arg) { return arg != nullptr && std::string(arg).rfind("--auto-attach", 0) == 0; }),
120 args.end());
121 strcpy(sAutoAttachDisableArgStorage, kAutoAttachDisableArg);
122 args.push_back(sAutoAttachDisableArgStorage);
123 args.push_back(nullptr);
124
125 return args;
126 }
127
PrintHelp(const char * aProgramName)128 static void PrintHelp(const char *aProgramName)
129 {
130 fprintf(stderr,
131 "Usage: %s [-I interfaceName] [-B backboneIfName] [-d DEBUG_LEVEL] [-v] [--auto-attach[=0/1]] RADIO_URL "
132 "[RADIO_URL]\n"
133 " --auto-attach defaults to 1\n",
134 aProgramName);
135 fprintf(stderr, "%s", otSysGetRadioUrlHelpString());
136 }
137
PrintVersion(void)138 static void PrintVersion(void)
139 {
140 printf("%s\n", OTBR_PACKAGE_VERSION);
141 }
142
OnAllocateFailed(void)143 static void OnAllocateFailed(void)
144 {
145 otbrLogCrit("Allocate failure, exiting...");
146 exit(1);
147 }
148
GetDefaultLogLevel(void)149 static otbrLogLevel GetDefaultLogLevel(void)
150 {
151 otbrLogLevel level = OTBR_LOG_INFO;
152
153 #if __ANDROID__ && OTBR_CONFIG_ANDROID_PROPERTY_ENABLE
154 char value[PROPERTY_VALUE_MAX];
155
156 property_get("ro.build.type", value, "user");
157 if (!strcmp(value, "user"))
158 {
159 level = OTBR_LOG_WARNING;
160 }
161 #endif
162
163 return level;
164 }
165
PrintRadioVersionAndExit(const std::vector<const char * > & aRadioUrls)166 static void PrintRadioVersionAndExit(const std::vector<const char *> &aRadioUrls)
167 {
168 otbr::Ncp::ControllerOpenThread ncpOpenThread{/* aInterfaceName */ "", aRadioUrls, /* aBackboneInterfaceName */ "",
169 /* aDryRun */ true, /* aEnableAutoAttach */ false};
170 const char * radioVersion;
171
172 ncpOpenThread.Init();
173
174 radioVersion = otPlatRadioGetVersionString(ncpOpenThread.GetInstance());
175 otbrLogNotice("Radio version: %s", radioVersion);
176 printf("%s\n", radioVersion);
177
178 ncpOpenThread.Deinit();
179
180 exit(EXIT_SUCCESS);
181 }
182
realmain(int argc,char * argv[])183 static int realmain(int argc, char *argv[])
184 {
185 otbrLogLevel logLevel = GetDefaultLogLevel();
186 int opt;
187 int ret = EXIT_SUCCESS;
188 const char * interfaceName = kDefaultInterfaceName;
189 bool verbose = false;
190 bool printRadioVersion = false;
191 bool enableAutoAttach = true;
192 std::vector<const char *> radioUrls;
193 std::vector<const char *> backboneInterfaceNames;
194 long parseResult;
195
196 std::set_new_handler(OnAllocateFailed);
197
198 while ((opt = getopt_long(argc, argv, "B:d:hI:Vv", kOptions, nullptr)) != -1)
199 {
200 switch (opt)
201 {
202 case OTBR_OPT_BACKBONE_INTERFACE_NAME:
203 backboneInterfaceNames.push_back(optarg);
204 otbrLogNotice("Backbone interface: %s", optarg);
205 break;
206
207 case OTBR_OPT_DEBUG_LEVEL:
208 VerifyOrExit(ParseInteger(optarg, parseResult), ret = EXIT_FAILURE);
209 VerifyOrExit(OTBR_LOG_EMERG <= parseResult && parseResult <= OTBR_LOG_DEBUG, ret = EXIT_FAILURE);
210 logLevel = static_cast<otbrLogLevel>(parseResult);
211 break;
212
213 case OTBR_OPT_INTERFACE_NAME:
214 interfaceName = optarg;
215 break;
216
217 case OTBR_OPT_VERBOSE:
218 verbose = true;
219 break;
220
221 case OTBR_OPT_VERSION:
222 PrintVersion();
223 ExitNow();
224 break;
225
226 case OTBR_OPT_HELP:
227 PrintHelp(argv[0]);
228 ExitNow(ret = EXIT_SUCCESS);
229 break;
230
231 case OTBR_OPT_RADIO_VERSION:
232 printRadioVersion = true;
233 break;
234
235 case OTBR_OPT_AUTO_ATTACH:
236 if (optarg == nullptr)
237 {
238 enableAutoAttach = true;
239 }
240 else
241 {
242 VerifyOrExit(ParseInteger(optarg, parseResult), ret = EXIT_FAILURE);
243 enableAutoAttach = parseResult;
244 }
245 break;
246
247 default:
248 PrintHelp(argv[0]);
249 ExitNow(ret = EXIT_FAILURE);
250 break;
251 }
252 }
253
254 otbrLogInit(kSyslogIdent, logLevel, verbose);
255 otbrLogNotice("Running %s", OTBR_PACKAGE_VERSION);
256 otbrLogNotice("Thread version: %s", otbr::Ncp::ControllerOpenThread::GetThreadVersion());
257 otbrLogNotice("Thread interface: %s", interfaceName);
258
259 if (backboneInterfaceNames.empty())
260 {
261 otbrLogNotice("Backbone interface is not specified");
262 }
263
264 for (int i = optind; i < argc; i++)
265 {
266 otbrLogNotice("Radio URL: %s", argv[i]);
267 radioUrls.push_back(argv[i]);
268 }
269
270 if (printRadioVersion)
271 {
272 PrintRadioVersionAndExit(radioUrls);
273 assert(false);
274 }
275
276 {
277 otbr::Application app(interfaceName, backboneInterfaceNames, radioUrls, enableAutoAttach);
278
279 gApp = &app;
280 app.Init();
281
282 ret = app.Run();
283
284 app.Deinit();
285 }
286
287 otbrLogDeinit();
288
289 exit:
290 return ret;
291 }
292
otPlatReset(otInstance * aInstance)293 void otPlatReset(otInstance *aInstance)
294 {
295 OT_UNUSED_VARIABLE(aInstance);
296
297 gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE;
298
299 VerifyOrDie(gApp != nullptr, "gApp is null");
300 gApp->Deinit();
301 gApp = nullptr;
302
303 longjmp(sResetJump, 1);
304 assert(false);
305 }
306
main(int argc,char * argv[])307 int main(int argc, char *argv[])
308 {
309 if (setjmp(sResetJump))
310 {
311 std::vector<char *> args = AppendAutoAttachDisableArg(argc, argv);
312
313 alarm(0);
314 #if OPENTHREAD_ENABLE_COVERAGE
315 __gcov_flush();
316 #endif
317
318 execvp(args[0], args.data());
319 }
320
321 return realmain(argc, argv);
322 }
323