diff --git a/.travis/script.sh b/.travis/script.sh index 52b5888a4..b0a025d83 100755 --- a/.travis/script.sh +++ b/.travis/script.sh @@ -428,7 +428,8 @@ build_samr21() { ./bootstrap || die # enable code coverage for OpenThread transceiver only CERT_LOG=1 COVERAGE=1 VIRTUAL_TIME_UART=1 make -f examples/Makefile-posix || die - CERT_LOG=1 COVERAGE=1 make -f src/posix/Makefile-posix || die + # readline supports pipe, editline does not + CERT_LOG=1 COVERAGE=1 READLINE=readline make -f src/posix/Makefile-posix || die CERT_LOG=1 COVERAGE=1 PYTHONUNBUFFERED=1 OT_CLI_PATH="$(pwd)/$(ls output/posix/*/bin/ot-cli)" RADIO_DEVICE="$(pwd)/$(ls output/*/bin/ot-ncp-radio)" make -f src/posix/Makefile-posix check || die } @@ -451,7 +452,7 @@ build_samr21() { ./bootstrap || die CERT_LOG=1 COVERAGE=1 VIRTUAL_TIME_UART=1 make -f examples/Makefile-posix || die # enable code coverage for OpenThread posix radio - CERT_LOG=1 COVERAGE=1 make -f src/posix/Makefile-posix || die + CERT_LOG=1 COVERAGE=1 READLINE=readline make -f src/posix/Makefile-posix || die CERT_LOG=1 COVERAGE=1 PYTHONUNBUFFERED=1 OT_NCP_PATH="$(pwd)/$(ls output/posix/*/bin/ot-ncp)" RADIO_DEVICE="$(pwd)/$(ls output/*/bin/ot-ncp-radio)" NODE_TYPE=ncp-sim make -f src/posix/Makefile-posix check || die } diff --git a/configure.ac b/configure.ac index 965dafa12..29d0aba81 100644 --- a/configure.ac +++ b/configure.ac @@ -745,6 +745,30 @@ AC_DEFINE_UNQUOTED([OPENTHREAD_ENABLE_NCP_UART],[${OPENTHREAD_ENABLE_NCP_UART}], AC_MSG_CHECKING([should NCP support UART]) AC_MSG_RESULT([${OPENTHREAD_ENABLE_UART}]) +# +# Readline - readline library to use for POSIX CLI +# + +AC_ARG_WITH( + [readline], + [AS_HELP_STRING([--with-readline],[Specify the readline library (none|readline|edit) @<:@default=none@:>@.])], + [], + [with_readline=none]) + +case "${with_readline}" in +"none") + ;; +"readline") + AC_CHECK_LIB([readline], [rl_callback_handler_install]) + ;; +"edit") + AC_CHECK_LIB([edit], [rl_callback_handler_install]) + ;; +*) + AC_MSG_ERROR([unexpected --with-readline=${with_readline}]) + ;; +esac + # # Thread UDP forward # diff --git a/src/posix/Makefile-posix b/src/posix/Makefile-posix index 06c7c114c..491e5da8e 100644 --- a/src/posix/Makefile-posix +++ b/src/posix/Makefile-posix @@ -56,6 +56,7 @@ LEGACY ?= 1 LINK_RAW ?= 0 MAC_FILTER ?= 1 MTD_NETDIAG ?= 1 +READLINE ?= edit SERVICE ?= 1 SNTP_CLIENT ?= 1 UDP_FORWARD ?= 1 @@ -92,6 +93,10 @@ ifeq ($(PLATFORM_NETIF),1) configure_OPTIONS += --enable-platform-netif endif +ifneq ($(READLINE),) +configure_OPTIONS += --with-readline=$(READLINE) +endif + ifeq ($(VIRTUAL_TIME),1) COMMONCFLAGS += -DOPENTHREAD_POSIX_VIRTUAL_TIME=1 endif diff --git a/src/posix/Makefile.am b/src/posix/Makefile.am index 091c8dd76..4fe0db35f 100644 --- a/src/posix/Makefile.am +++ b/src/posix/Makefile.am @@ -160,6 +160,7 @@ ot_cli_CPPFLAGS = \ ot_cli_SOURCES = \ main.c \ + console_cli.c \ $(NULL) ot_cli_LDADD = \ @@ -168,6 +169,7 @@ ot_cli_LDADD = \ $(LDADD_COMMON) \ $(top_builddir)/src/core/libopenthread-ftd.a \ $(LDADD_COMMON) \ + $(top_builddir)/src/cli/libopenthread-cli-ftd.a \ $(NULL) ot_cli_LDFLAGS = \ @@ -192,4 +194,8 @@ CLEANFILES = $(wildc endif # OPENTHREAD_BUILD_COVERAGE endif # OPENTHREAD_ENABLE_EXECUTABLE +noinst_HEADERS = \ + console_cli.h \ + $(NULL) + include $(abs_top_nlbuild_autotools_dir)/automake/post.am diff --git a/src/posix/console_cli.c b/src/posix/console_cli.c new file mode 100644 index 000000000..6d0082b5e --- /dev/null +++ b/src/posix/console_cli.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2019, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "console_cli.h" + +#include "openthread/config.h" + +#if HAVE_LIBEDIT || HAVE_LIBREADLINE +#if HAVE_LIBEDIT +#include +#elif HAVE_LIBREADLINE +#include +#include +#else +#error "Missing readline library" +#endif + +#include +#include +#include +#include + +#include + +#include "openthread-core-config.h" +#include "platform-posix.h" + +static const char sPrompt[] = "> "; + +static int sReadFd; + +static void InputCallback(char *aLine) +{ + if (aLine != NULL) + { + size_t len; + + if ((len = strlen(aLine)) > 0) + { + add_history(aLine); + otCliConsoleInputLine(aLine, (uint16_t)len); + } + free(aLine); + } + else + { + exit(OT_EXIT_SUCCESS); + } +} + +static int OutputCallback(const char *aBuffer, uint16_t aLength, void *aContext) +{ + (void)aContext; + + return (int)write(STDOUT_FILENO, aBuffer, aLength); +} + +void otxConsoleInit(otInstance *aInstance) +{ + rl_instream = stdin; + rl_outstream = stdout; + rl_inhibit_completion = true; + sReadFd = fileno(rl_instream); + rl_callback_handler_install(sPrompt, InputCallback); + otCliConsoleInit(aInstance, OutputCallback, NULL); +} + +void otxConsoleDeinit(void) +{ + rl_callback_handler_remove(); +} + +void otxConsoleUpdate(otSysMainloopContext *aMainloop) +{ + FD_SET(sReadFd, &aMainloop->mReadFdSet); + FD_SET(sReadFd, &aMainloop->mErrorFdSet); + + if (aMainloop->mMaxFd < sReadFd) + { + aMainloop->mMaxFd = sReadFd; + } +} + +void otxConsoleProcess(const otSysMainloopContext *aMainloop) +{ + if (FD_ISSET(sReadFd, &aMainloop->mErrorFdSet)) + { + perror("console error"); + exit(OT_EXIT_FAILURE); + } + + if (FD_ISSET(sReadFd, &aMainloop->mReadFdSet)) + { + rl_callback_read_char(); + } +} + +#endif // HAVE_LIBEDIT || HAVE_LIBREADLINE diff --git a/src/posix/console_cli.h b/src/posix/console_cli.h new file mode 100644 index 000000000..41b0f9855 --- /dev/null +++ b/src/posix/console_cli.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, The OpenThread Authors. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OPENTHREAD_CONSOLE_CLI_H_ +#define OPENTHREAD_CONSOLE_CLI_H_ + +#include + +#include "openthread-system.h" + +/** + * This function initializes CLI console. + * + * @param[in] aInstance A pointer to the OpenThread instance. + * + */ +void otxConsoleInit(otInstance *aInstance); + +/** + * This function deinitializes CLI console + * + */ +void otxConsoleDeinit(void); + +/** + * This function updates the file descriptor sets with file descriptors used by console. + * + * @param[inout] aMainloop A pointer to the mainloop context. + * + */ +void otxConsoleUpdate(otSysMainloopContext *aMainloop); + +/** + * This function performs console driver processing. + * + * @param[in] aMainloop A pointer to the mainloop context. + * + */ +void otxConsoleProcess(const otSysMainloopContext *aMainloop); + +#endif // OPENTHREAD_CONSOLE_CLI_H_ diff --git a/src/posix/main.c b/src/posix/main.c index 988dbd62d..9e3232a2e 100644 --- a/src/posix/main.c +++ b/src/posix/main.c @@ -49,6 +49,9 @@ #include #elif OPENTHREAD_POSIX_APP_TYPE == OPENTHREAD_POSIX_APP_TYPE_CLI #include +#if HAVE_LIBEDIT || HAVE_LIBREADLINE +#include "console_cli.h" +#endif #else #error "Unknown posix app type!" #endif @@ -92,8 +95,12 @@ int main(int argc, char *argv[]) #if OPENTHREAD_ENABLE_PLATFORM_NETIF otSysInitNetif(instance); #endif +#if HAVE_LIBEDIT || HAVE_LIBREADLINE + otxConsoleInit(instance); +#else otCliUartInit(instance); #endif +#endif #if OPENTHREAD_ENABLE_DIAG otDiagInit(instance); @@ -113,10 +120,18 @@ int main(int argc, char *argv[]) mainloop.mTimeout.tv_sec = 10; mainloop.mTimeout.tv_usec = 0; +#if OPENTHREAD_POSIX_APP_TYPE == OPENTHREAD_POSIX_APP_TYPE_CLI && (HAVE_LIBEDIT || HAVE_LIBREADLINE) + otxConsoleUpdate(&mainloop); +#endif + otSysMainloopUpdate(instance, &mainloop); + if (otSysMainloopPoll(&mainloop) >= 0) { otSysMainloopProcess(instance, &mainloop); +#if OPENTHREAD_POSIX_APP_TYPE == OPENTHREAD_POSIX_APP_TYPE_CLI && (HAVE_LIBEDIT || HAVE_LIBREADLINE) + otxConsoleProcess(&mainloop); +#endif } else if (errno != EINTR) { @@ -125,6 +140,9 @@ int main(int argc, char *argv[]) } } +#if OPENTHREAD_POSIX_APP_TYPE == OPENTHREAD_POSIX_APP_TYPE_CLI && (HAVE_LIBEDIT || HAVE_LIBREADLINE) + otxConsoleDeinit(); +#endif otInstanceFinalize(instance); return 0; diff --git a/src/posix/platform/openthread-system.h b/src/posix/platform/openthread-system.h index 9eb10d12a..3a40357af 100644 --- a/src/posix/platform/openthread-system.h +++ b/src/posix/platform/openthread-system.h @@ -35,6 +35,8 @@ #ifndef OPENTHREAD_SYSTEM_H_ #define OPENTHREAD_SYSTEM_H_ +#include + #include #ifdef __cplusplus diff --git a/src/posix/platform/sim.c b/src/posix/platform/sim.c index 51e83a8ce..3b1d5da65 100644 --- a/src/posix/platform/sim.c +++ b/src/posix/platform/sim.c @@ -97,7 +97,6 @@ void otSimInit(void) perror("bind"); exit(OT_EXIT_FAILURE); } - // fprintf(stderr, "[%s] sim fd is %d\r\n", getenv("NODE_ID"), sSockFd); } void otSimDeinit(void) @@ -139,8 +138,6 @@ void otSimReceiveEvent(struct Event *aEvent) } sNow += aEvent->mDelay; - - // fprintf(stderr, "--[%s] now is %lu type is %d\n", getenv("NODE_ID"), sNow, aEvent->mEvent); } void otSimSendSleepEvent(const struct timeval *aTimeout) @@ -196,7 +193,6 @@ void otSimProcess(otInstance *aInstance, const fd_set *aReadFdSet, const fd_set if (FD_ISSET(sSockFd, aReadFdSet)) { otSimReceiveEvent(&event); - // fprintf(stderr, "[%s] receive event\r\n", getenv("NODE_ID")); } otSimRadioSpinelProcess(aInstance, &event); diff --git a/src/posix/platform/uart.c b/src/posix/platform/uart.c index 9d90d31b7..78942b509 100644 --- a/src/posix/platform/uart.c +++ b/src/posix/platform/uart.c @@ -30,7 +30,6 @@ #include "platform-posix.h" #if OPENTHREAD_ENABLE_POSIX_APP_DAEMON -#include #include #include #include @@ -39,6 +38,7 @@ #include #endif +#include #include #include #include @@ -56,6 +56,7 @@ static int sUartLock = -1; static int sSessionSocket = -1; #endif +static bool sEnabled = false; static const uint8_t *sWriteBuffer = NULL; static uint16_t sWriteLength = 0; @@ -129,12 +130,15 @@ otError otPlatUartEnable(void) exit(OT_EXIT_FAILURE); } #endif // OPENTHREAD_ENABLE_POSIX_APP_DAEMON + + sEnabled = true; return error; } otError otPlatUartDisable(void) { otError error = OT_ERROR_NONE; + sEnabled = false; #if OPENTHREAD_ENABLE_POSIX_APP_DAEMON if (sSessionSocket != -1) @@ -163,6 +167,7 @@ otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength) { otError error = OT_ERROR_NONE; + assert(sEnabled); otEXPECT_ACTION(sWriteLength == 0, error = OT_ERROR_BUSY); sWriteBuffer = aBuf; @@ -174,6 +179,8 @@ exit: void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd) { + otEXPECT(sEnabled); + if (aReadFdSet != NULL) { #if OPENTHREAD_ENABLE_POSIX_APP_DAEMON @@ -214,6 +221,9 @@ void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aE *aMaxFd = fd; } } + +exit: + return; } void platformUartProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, const fd_set *aErrorFdSet) @@ -221,6 +231,7 @@ void platformUartProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, co ssize_t rval; int fd; + otEXPECT(sEnabled); #if OPENTHREAD_ENABLE_POSIX_APP_DAEMON if (FD_ISSET(sUartSocket, aErrorFdSet)) {