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