1 /*
2 * Copyright (c) 2018, 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 #include "platform/openthread-posix-config.h"
30
31 #include <openthread/config.h>
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <getopt.h>
36 #include <libgen.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <unistd.h>
43 #ifdef __linux__
44 #include <sys/prctl.h>
45 #endif
46
47 #ifndef HAVE_LIBEDIT
48 #define HAVE_LIBEDIT 0
49 #endif
50
51 #ifndef HAVE_LIBREADLINE
52 #define HAVE_LIBREADLINE 0
53 #endif
54
55 #include <openthread/cli.h>
56 #include <openthread/diag.h>
57 #include <openthread/logging.h>
58 #include <openthread/tasklet.h>
59 #include <openthread/thread.h>
60 #include <openthread/platform/radio.h>
61 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
62 #include <openthread/cli.h>
63 #include "cli/cli_config.h"
64 #endif
65 #include <common/code_utils.hpp>
66 #include <lib/platform/exit_code.h>
67 #include <openthread/openthread-system.h>
68 #include <openthread/platform/misc.h>
69
70 #include "lib/platform/reset_util.h"
71
72 /**
73 * Initializes NCP app.
74 *
75 * @param[in] aInstance A pointer to the OpenThread instance.
76 *
77 */
78 void otAppNcpInit(otInstance *aInstance);
79
80 /**
81 * Deinitializes NCP app.
82 *
83 */
84 void otAppNcpUpdate(otSysMainloopContext *aContext);
85
86 /**
87 * Updates the file descriptor sets with file descriptors used by console.
88 *
89 * @param[in,out] aMainloop A pointer to the mainloop context.
90 *
91 */
92 void otAppNcpProcess(const otSysMainloopContext *aContext);
93
94 /**
95 * Initializes CLI app.
96 *
97 * @param[in] aInstance A pointer to the OpenThread instance.
98 *
99 */
100 void otAppCliInit(otInstance *aInstance);
101
102 /**
103 * Deinitializes CLI app.
104 *
105 */
106 void otAppCliDeinit(void);
107
108 /**
109 * Updates the file descriptor sets with file descriptors used by console.
110 *
111 * @param[in,out] aMainloop A pointer to the mainloop context.
112 *
113 */
114 void otAppCliUpdate(otSysMainloopContext *aMainloop);
115
116 /**
117 * Performs console driver processing.
118 *
119 * @param[in] aMainloop A pointer to the mainloop context.
120 *
121 */
122 void otAppCliProcess(const otSysMainloopContext *aMainloop);
123
124 typedef struct PosixConfig
125 {
126 otPlatformConfig mPlatformConfig; ///< Platform configuration.
127 otLogLevel mLogLevel; ///< Debug level of logging.
128 bool mPrintRadioVersion; ///< Whether to print radio firmware version.
129 bool mIsVerbose; ///< Whether to print log to stderr.
130 } PosixConfig;
131
132 /**
133 * Defines the argument return values.
134 *
135 */
136 enum
137 {
138 OT_POSIX_OPT_BACKBONE_INTERFACE_NAME = 'B',
139 OT_POSIX_OPT_DEBUG_LEVEL = 'd',
140 OT_POSIX_OPT_DRY_RUN = 'n',
141 OT_POSIX_OPT_HELP = 'h',
142 OT_POSIX_OPT_INTERFACE_NAME = 'I',
143 OT_POSIX_OPT_PERSISTENT_INTERFACE = 'p',
144 OT_POSIX_OPT_TIME_SPEED = 's',
145 OT_POSIX_OPT_VERBOSE = 'v',
146
147 OT_POSIX_OPT_SHORT_MAX = 128,
148
149 OT_POSIX_OPT_RADIO_VERSION,
150 OT_POSIX_OPT_REAL_TIME_SIGNAL,
151 };
152
153 static const struct option kOptions[] = {
154 {"backbone-interface-name", required_argument, NULL, OT_POSIX_OPT_BACKBONE_INTERFACE_NAME},
155 {"debug-level", required_argument, NULL, OT_POSIX_OPT_DEBUG_LEVEL},
156 {"dry-run", no_argument, NULL, OT_POSIX_OPT_DRY_RUN},
157 {"help", no_argument, NULL, OT_POSIX_OPT_HELP},
158 {"interface-name", required_argument, NULL, OT_POSIX_OPT_INTERFACE_NAME},
159 {"persistent-interface", no_argument, NULL, OT_POSIX_OPT_PERSISTENT_INTERFACE},
160 {"radio-version", no_argument, NULL, OT_POSIX_OPT_RADIO_VERSION},
161 {"real-time-signal", required_argument, NULL, OT_POSIX_OPT_REAL_TIME_SIGNAL},
162 {"time-speed", required_argument, NULL, OT_POSIX_OPT_TIME_SPEED},
163 {"verbose", no_argument, NULL, OT_POSIX_OPT_VERBOSE},
164 {0, 0, 0, 0}};
165
PrintUsage(const char * aProgramName,FILE * aStream,int aExitCode)166 static void PrintUsage(const char *aProgramName, FILE *aStream, int aExitCode)
167 {
168 fprintf(aStream,
169 "Syntax:\n"
170 " %s [Options] RadioURL [RadioURL]\n"
171 "Options:\n"
172 " -B --backbone-interface-name Backbone network interface name.\n"
173 " -d --debug-level Debug level of logging.\n"
174 " -h --help Display this usage information.\n"
175 " -I --interface-name name Thread network interface name.\n"
176 " -n --dry-run Just verify if arguments is valid and radio spinel is compatible.\n"
177 " --radio-version Print radio firmware version.\n"
178 " -p --persistent-interface Persistent the created thread network interface\n"
179 " -s --time-speed factor Time speed up factor.\n"
180 " -v --verbose Also log to stderr.\n",
181 aProgramName);
182 #ifdef __linux__
183 fprintf(aStream,
184 " --real-time-signal (Linux only) The real-time signal number for microsecond timer.\n"
185 " Use +N for relative value to SIGRTMIN, and use N for absolute value.\n");
186
187 #endif
188 fprintf(aStream, "%s", otSysGetRadioUrlHelpString());
189 exit(aExitCode);
190 }
191
ParseArg(int aArgCount,char * aArgVector[],PosixConfig * aConfig)192 static void ParseArg(int aArgCount, char *aArgVector[], PosixConfig *aConfig)
193 {
194 memset(aConfig, 0, sizeof(*aConfig));
195
196 aConfig->mPlatformConfig.mPersistentInterface = false;
197 aConfig->mPlatformConfig.mSpeedUpFactor = 1;
198 aConfig->mLogLevel = OT_LOG_LEVEL_CRIT;
199 aConfig->mPlatformConfig.mInterfaceName = OPENTHREAD_POSIX_CONFIG_THREAD_NETIF_DEFAULT_NAME;
200 #ifdef __linux__
201 aConfig->mPlatformConfig.mRealTimeSignal = SIGRTMIN;
202 #endif
203
204 optind = 1;
205
206 while (true)
207 {
208 int index = 0;
209 int option = getopt_long(aArgCount, aArgVector, "B:d:hI:nps:v", kOptions, &index);
210
211 if (option == -1)
212 {
213 break;
214 }
215
216 switch (option)
217 {
218 case OT_POSIX_OPT_DEBUG_LEVEL:
219 aConfig->mLogLevel = (otLogLevel)atoi(optarg);
220 break;
221 case OT_POSIX_OPT_HELP:
222 PrintUsage(aArgVector[0], stdout, OT_EXIT_SUCCESS);
223 break;
224 case OT_POSIX_OPT_INTERFACE_NAME:
225 aConfig->mPlatformConfig.mInterfaceName = optarg;
226 break;
227 case OT_POSIX_OPT_PERSISTENT_INTERFACE:
228 aConfig->mPlatformConfig.mPersistentInterface = true;
229 break;
230 case OT_POSIX_OPT_BACKBONE_INTERFACE_NAME:
231 aConfig->mPlatformConfig.mBackboneInterfaceName = optarg;
232 break;
233 case OT_POSIX_OPT_DRY_RUN:
234 aConfig->mPlatformConfig.mDryRun = true;
235 break;
236 case OT_POSIX_OPT_TIME_SPEED:
237 {
238 char *endptr = NULL;
239
240 aConfig->mPlatformConfig.mSpeedUpFactor = (uint32_t)strtol(optarg, &endptr, 0);
241
242 if (*endptr != '\0' || aConfig->mPlatformConfig.mSpeedUpFactor == 0)
243 {
244 fprintf(stderr, "Invalid value for TimerSpeedUpFactor: %s\n", optarg);
245 exit(OT_EXIT_INVALID_ARGUMENTS);
246 }
247 break;
248 }
249 case OT_POSIX_OPT_VERBOSE:
250 aConfig->mIsVerbose = true;
251 break;
252 case OT_POSIX_OPT_RADIO_VERSION:
253 aConfig->mPrintRadioVersion = true;
254 break;
255 #ifdef __linux__
256 case OT_POSIX_OPT_REAL_TIME_SIGNAL:
257 if (optarg[0] == '+')
258 {
259 aConfig->mPlatformConfig.mRealTimeSignal = SIGRTMIN + atoi(&optarg[1]);
260 }
261 else
262 {
263 aConfig->mPlatformConfig.mRealTimeSignal = atoi(optarg);
264 }
265 break;
266 #endif // __linux__
267 case '?':
268 PrintUsage(aArgVector[0], stderr, OT_EXIT_INVALID_ARGUMENTS);
269 break;
270 default:
271 assert(false);
272 break;
273 }
274 }
275
276 for (; optind < aArgCount; optind++)
277 {
278 VerifyOrDie(aConfig->mPlatformConfig.mRadioUrlNum < OT_ARRAY_LENGTH(aConfig->mPlatformConfig.mRadioUrls),
279 OT_EXIT_INVALID_ARGUMENTS);
280 aConfig->mPlatformConfig.mRadioUrls[aConfig->mPlatformConfig.mRadioUrlNum++] = aArgVector[optind];
281 }
282
283 if (aConfig->mPlatformConfig.mRadioUrlNum == 0)
284 {
285 PrintUsage(aArgVector[0], stderr, OT_EXIT_INVALID_ARGUMENTS);
286 }
287 }
288
InitInstance(PosixConfig * aConfig)289 static otInstance *InitInstance(PosixConfig *aConfig)
290 {
291 otInstance *instance = NULL;
292
293 syslog(LOG_INFO, "Running %s", otGetVersionString());
294 syslog(LOG_INFO, "Thread version: %hu", otThreadGetVersion());
295 IgnoreError(otLoggingSetLevel(aConfig->mLogLevel));
296
297 instance = otSysInit(&aConfig->mPlatformConfig);
298 VerifyOrDie(instance != NULL, OT_EXIT_FAILURE);
299 syslog(LOG_INFO, "Thread interface: %s", otSysGetThreadNetifName());
300
301 if (aConfig->mPrintRadioVersion)
302 {
303 printf("%s\n", otPlatRadioGetVersionString(instance));
304 }
305 else
306 {
307 syslog(LOG_INFO, "RCP version: %s", otPlatRadioGetVersionString(instance));
308 }
309
310 if (aConfig->mPlatformConfig.mDryRun)
311 {
312 exit(OT_EXIT_SUCCESS);
313 }
314
315 return instance;
316 }
317
otTaskletsSignalPending(otInstance * aInstance)318 void otTaskletsSignalPending(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); }
319
otPlatReset(otInstance * aInstance)320 void otPlatReset(otInstance *aInstance)
321 {
322 OT_UNUSED_VARIABLE(aInstance);
323
324 gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE;
325
326 otSysDeinit();
327
328 longjmp(gResetJump, 1);
329 assert(false);
330 }
331
ProcessNetif(void * aContext,uint8_t aArgsLength,char * aArgs[])332 static otError ProcessNetif(void *aContext, uint8_t aArgsLength, char *aArgs[])
333 {
334 OT_UNUSED_VARIABLE(aContext);
335 OT_UNUSED_VARIABLE(aArgsLength);
336 OT_UNUSED_VARIABLE(aArgs);
337
338 otCliOutputFormat("%s:%u\r\n", otSysGetThreadNetifName(), otSysGetThreadNetifIndex());
339
340 return OT_ERROR_NONE;
341 }
342
343 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
ProcessExit(void * aContext,uint8_t aArgsLength,char * aArgs[])344 static otError ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[])
345 {
346 OT_UNUSED_VARIABLE(aContext);
347 OT_UNUSED_VARIABLE(aArgsLength);
348 OT_UNUSED_VARIABLE(aArgs);
349
350 exit(EXIT_SUCCESS);
351 }
352 #endif
353
354 static const otCliCommand kCommands[] = {
355 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
356 {"exit", ProcessExit},
357 #endif
358 {"netif", ProcessNetif},
359 };
360
main(int argc,char * argv[])361 int main(int argc, char *argv[])
362 {
363 otInstance *instance;
364 int rval = 0;
365 PosixConfig config;
366
367 #ifdef __linux__
368 // Ensure we terminate this process if the
369 // parent process dies.
370 prctl(PR_SET_PDEATHSIG, SIGHUP);
371 #endif
372
373 OT_SETUP_RESET_JUMP(argv);
374
375 ParseArg(argc, argv, &config);
376 openlog(argv[0], LOG_PID | (config.mIsVerbose ? LOG_PERROR : 0), LOG_DAEMON);
377 setlogmask(setlogmask(0) & LOG_UPTO(LOG_DEBUG));
378 instance = InitInstance(&config);
379
380 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
381 otAppCliInit(instance);
382 #endif
383
384 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE || OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
385 IgnoreError(otCliSetUserCommands(kCommands, OT_ARRAY_LENGTH(kCommands), instance));
386 #endif
387
388 while (true)
389 {
390 otSysMainloopContext mainloop;
391
392 otTaskletsProcess(instance);
393
394 FD_ZERO(&mainloop.mReadFdSet);
395 FD_ZERO(&mainloop.mWriteFdSet);
396 FD_ZERO(&mainloop.mErrorFdSet);
397
398 mainloop.mMaxFd = -1;
399 mainloop.mTimeout.tv_sec = 10;
400 mainloop.mTimeout.tv_usec = 0;
401
402 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
403 otAppCliUpdate(&mainloop);
404 #endif
405
406 otSysMainloopUpdate(instance, &mainloop);
407
408 if (otSysMainloopPoll(&mainloop) >= 0)
409 {
410 otSysMainloopProcess(instance, &mainloop);
411 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
412 otAppCliProcess(&mainloop);
413 #endif
414 }
415 else if (errno != EINTR)
416 {
417 perror("select");
418 ExitNow(rval = OT_EXIT_FAILURE);
419 }
420 }
421
422 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
423 otAppCliDeinit();
424 #endif
425
426 exit:
427 otSysDeinit();
428
429 return rval;
430 }
431