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