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 <lib/platform/reset_util.h>
68 #include <lib/spinel/coprocessor_type.h>
69 #include <openthread/openthread-system.h>
70 #include <openthread/platform/misc.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.mCoprocessorUrls.mNum <
279 OT_ARRAY_LENGTH(aConfig->mPlatformConfig.mCoprocessorUrls.mUrls),
280 OT_EXIT_INVALID_ARGUMENTS);
281 aConfig->mPlatformConfig.mCoprocessorUrls.mUrls[aConfig->mPlatformConfig.mCoprocessorUrls.mNum++] =
282 aArgVector[optind];
283 }
284
285 if (aConfig->mPlatformConfig.mCoprocessorUrls.mNum == 0)
286 {
287 PrintUsage(aArgVector[0], stderr, OT_EXIT_INVALID_ARGUMENTS);
288 }
289 }
290
InitInstance(PosixConfig * aConfig)291 static otInstance *InitInstance(PosixConfig *aConfig)
292 {
293 otInstance *instance = NULL;
294
295 syslog(LOG_INFO, "Running %s", otGetVersionString());
296 syslog(LOG_INFO, "Thread version: %hu", otThreadGetVersion());
297 IgnoreError(otLoggingSetLevel(aConfig->mLogLevel));
298
299 instance = otSysInit(&aConfig->mPlatformConfig);
300 VerifyOrDie(instance != NULL, OT_EXIT_FAILURE);
301 syslog(LOG_INFO, "Thread interface: %s", otSysGetThreadNetifName());
302
303 if (aConfig->mPlatformConfig.mCoprocessorType != OT_COPROCESSOR_RCP)
304 {
305 printf("Only RCP is supported by posix app now!\n");
306 exit(OT_EXIT_FAILURE);
307 }
308
309 if (aConfig->mPrintRadioVersion)
310 {
311 printf("%s\n", otPlatRadioGetVersionString(instance));
312 }
313 else
314 {
315 syslog(LOG_INFO, "RCP version: %s", otPlatRadioGetVersionString(instance));
316 }
317
318 if (aConfig->mPlatformConfig.mDryRun)
319 {
320 exit(OT_EXIT_SUCCESS);
321 }
322
323 return instance;
324 }
325
otTaskletsSignalPending(otInstance * aInstance)326 void otTaskletsSignalPending(otInstance *aInstance) { OT_UNUSED_VARIABLE(aInstance); }
327
otPlatReset(otInstance * aInstance)328 void otPlatReset(otInstance *aInstance)
329 {
330 OT_UNUSED_VARIABLE(aInstance);
331
332 gPlatResetReason = OT_PLAT_RESET_REASON_SOFTWARE;
333
334 otSysDeinit();
335
336 longjmp(gResetJump, 1);
337 assert(false);
338 }
339
ProcessNetif(void * aContext,uint8_t aArgsLength,char * aArgs[])340 static otError ProcessNetif(void *aContext, uint8_t aArgsLength, char *aArgs[])
341 {
342 OT_UNUSED_VARIABLE(aContext);
343 OT_UNUSED_VARIABLE(aArgsLength);
344 OT_UNUSED_VARIABLE(aArgs);
345
346 otCliOutputFormat("%s:%u\r\n", otSysGetThreadNetifName(), otSysGetThreadNetifIndex());
347
348 return OT_ERROR_NONE;
349 }
350
351 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
ProcessExit(void * aContext,uint8_t aArgsLength,char * aArgs[])352 static otError ProcessExit(void *aContext, uint8_t aArgsLength, char *aArgs[])
353 {
354 OT_UNUSED_VARIABLE(aContext);
355 OT_UNUSED_VARIABLE(aArgsLength);
356 OT_UNUSED_VARIABLE(aArgs);
357
358 exit(EXIT_SUCCESS);
359 }
360 #endif
361
362 static const otCliCommand kCommands[] = {
363 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
364 {"exit", ProcessExit},
365 #endif
366 {"netif", ProcessNetif},
367 };
368
main(int argc,char * argv[])369 int main(int argc, char *argv[])
370 {
371 otInstance *instance;
372 int rval = 0;
373 PosixConfig config;
374
375 #ifdef __linux__
376 // Ensure we terminate this process if the
377 // parent process dies.
378 prctl(PR_SET_PDEATHSIG, SIGHUP);
379 #endif
380
381 OT_SETUP_RESET_JUMP(argv);
382
383 ParseArg(argc, argv, &config);
384 openlog(argv[0], LOG_PID | (config.mIsVerbose ? LOG_PERROR : 0), LOG_DAEMON);
385 setlogmask(setlogmask(0) & LOG_UPTO(LOG_DEBUG));
386 instance = InitInstance(&config);
387
388 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
389 otAppCliInit(instance);
390 #endif
391
392 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE || OPENTHREAD_POSIX_CONFIG_DAEMON_CLI_ENABLE
393 IgnoreError(otCliSetUserCommands(kCommands, OT_ARRAY_LENGTH(kCommands), instance));
394 #endif
395
396 while (true)
397 {
398 otSysMainloopContext mainloop;
399
400 otTaskletsProcess(instance);
401
402 FD_ZERO(&mainloop.mReadFdSet);
403 FD_ZERO(&mainloop.mWriteFdSet);
404 FD_ZERO(&mainloop.mErrorFdSet);
405
406 mainloop.mMaxFd = -1;
407 mainloop.mTimeout.tv_sec = 10;
408 mainloop.mTimeout.tv_usec = 0;
409
410 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
411 otAppCliUpdate(&mainloop);
412 #endif
413
414 otSysMainloopUpdate(instance, &mainloop);
415
416 if (otSysMainloopPoll(&mainloop) >= 0)
417 {
418 otSysMainloopProcess(instance, &mainloop);
419 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
420 otAppCliProcess(&mainloop);
421 #endif
422 }
423 else if (errno != EINTR)
424 {
425 perror("select");
426 ExitNow(rval = OT_EXIT_FAILURE);
427 }
428 }
429
430 #if !OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE
431 otAppCliDeinit();
432 #endif
433
434 exit:
435 otSysDeinit();
436
437 return rval;
438 }
439