diff --git a/.github/workflows/simulation.yml b/.github/workflows/simulation.yml index dc021a197..68a5a3ae4 100644 --- a/.github/workflows/simulation.yml +++ b/.github/workflows/simulation.yml @@ -173,8 +173,8 @@ jobs: expects: runs-on: ubuntu-18.04 env: - CFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 - CXXFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 + CFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 -DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=15 + CXXFLAGS: -DCLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER=1 -DOPENTHREAD_CONFIG_MLE_MAX_CHILDREN=15 steps: - uses: actions/checkout@v2 - name: Bootstrap diff --git a/src/posix/platform/uart.cpp b/src/posix/platform/uart.cpp index 42f20dcfb..825e4226e 100644 --- a/src/posix/platform/uart.cpp +++ b/src/posix/platform/uart.cpp @@ -162,11 +162,6 @@ exit: return error; } -otError otPlatUartFlush(void) -{ - return OT_ERROR_NOT_IMPLEMENTED; -} - void platformUartUpdateFdSet(fd_set *aReadFdSet, fd_set *aWriteFdSet, fd_set *aErrorFdSet, int *aMaxFd) { VerifyOrExit(sEnabled, OT_NOOP); @@ -236,6 +231,19 @@ static void InitializeSessionSocket(void) VerifyOrExit((rval = fcntl(sSessionSocket, F_SETFD, rval)) != -1, OT_NOOP); +#ifndef __linux__ + // some platforms (macOS, Solaris) don't have MSG_NOSIGNAL + // SOME of those (macOS, but NOT Solaris) support SO_NOSIGPIPE + // if we have SO_NOSIGPIPE, then set it. Otherwise, we're going + // to simply ignore it. +#if defined(SO_NOSIGPIPE) + rval = 1; + VerifyOrExit((rval = setsockopt(sSessionSocket, SOL_SOCKET, SO_NOSIGPIPE, &rval, sizeof(rval)) != -1, OT_NOOP)); +#else +#warning "no support for MSG_NOSIGNAL or SO_NOSIGPIPE" +#endif +#endif // __linux__ + exit: if (rval == -1) { @@ -249,6 +257,47 @@ exit: } #endif +static otError UartWrite(int aFd) +{ + otError error = OT_ERROR_NONE; + ssize_t rval; + + VerifyOrExit(sWriteLength > 0, error = OT_ERROR_INVALID_STATE); + +#if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE && defined(__linux__) + if (aFd == sSessionSocket) + { + // Don't die on SIGPIPE + rval = send(aFd, sWriteBuffer, sWriteLength, MSG_NOSIGNAL); + } + else +#endif // OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE && defined(__linux__) + { + rval = write(aFd, sWriteBuffer, sWriteLength); + } + + if (rval < 0) + { +#if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE + otLogWarnPlat("UART write: %s", strerror(errno)); + if (aFd == sSessionSocket) + { + close(sSessionSocket); + sSessionSocket = -1; + } + ExitNow(); +#else + DieNow(OT_EXIT_ERROR_ERRNO); +#endif + } + + sWriteBuffer += rval; + sWriteLength -= static_cast(rval); + +exit: + return error; +} + void platformUartProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, const fd_set *aErrorFdSet) { ssize_t rval; @@ -328,48 +377,11 @@ void platformUartProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, co fd = STDOUT_FILENO; #endif - if ((sWriteLength > 0) && (FD_ISSET(fd, aWriteFdSet))) + if ((FD_ISSET(fd, aWriteFdSet))) { -#if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE - // Don't die on SIGPIPE -#if !defined(MSG_NOSIGNAL) - // some platforms (mac OS, Solaris) don't have MSG_NOSIGNAL - // SOME of those (mac OS, but NOT Solaris) support SO_NOSIGPIPE - // if we have SO_NOSIGPIPE, then set it. otherwise, we're going - // to simply ignore it. -#if defined(SO_NOSIGPIPE) - int err; - int flag; + otError error = UartWrite(fd); - flag = 1; - err = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &flag, sizeof(flag)); - VerifyOrDie(err == 0, OT_EXIT_ERROR_ERRNO); -#else -#warning "no support for MSG_NOSIGNAL or SO_NOSIGPIPE" -#endif -#define MSG_NOSIGNAL 0 -#endif // !defined(MSG_NOSIGNAL) - rval = send(fd, sWriteBuffer, sWriteLength, MSG_NOSIGNAL); -#else - rval = write(fd, sWriteBuffer, sWriteLength); -#endif // OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE - - if (rval < 0) - { -#if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE - perror("UART write"); - close(sSessionSocket); - sSessionSocket = -1; - ExitNow(); -#else - DieNowWithMessage("UART write", OT_EXIT_ERROR_ERRNO); -#endif - } - - VerifyOrExit(rval > 0, OT_NOOP); - - sWriteBuffer += (uint16_t)rval; - sWriteLength -= (uint16_t)rval; + VerifyOrExit(error == OT_ERROR_NONE, otLogWarnPlat("UART write: %s", otThreadErrorToString(error))); if (sWriteLength == 0) { @@ -380,3 +392,49 @@ void platformUartProcess(const fd_set *aReadFdSet, const fd_set *aWriteFdSet, co exit: return; } + +otError otPlatUartFlush(void) +{ + otError error = OT_ERROR_NONE; + + while (sWriteLength > 0) + { + int fd = +#if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE + sSessionSocket != -1 ? sSessionSocket : +#endif + STDOUT_FILENO; + int rval; + + fd_set writeFdSet; + FD_ZERO(&writeFdSet); + FD_SET(fd, &writeFdSet); + + rval = select(fd + 1, nullptr, &writeFdSet, nullptr, nullptr); + + assert(rval != 0); + + if (rval > 0) + { + assert(FD_ISSET(fd, &writeFdSet)); + SuccessOrExit(error = UartWrite(fd)); + } + else if (errno != EINTR) + { +#if OPENTHREAD_POSIX_CONFIG_DAEMON_ENABLE + if (sSessionSocket == fd) + { + close(sSessionSocket); + sSessionSocket = -1; + } + else +#endif + { + DieNow(OT_EXIT_ERROR_ERRNO); + } + } + } + +exit: + return error; +} diff --git a/tests/scripts/expect/cli-big-table.exp b/tests/scripts/expect/cli-big-table.exp new file mode 100755 index 000000000..ee0631191 --- /dev/null +++ b/tests/scripts/expect/cli-big-table.exp @@ -0,0 +1,78 @@ +#!/usr/bin/expect -f +# +# Copyright (c) 2020, 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. +# + +source "tests/scripts/expect/_common.exp" + +set max_node 15 +set spawn_ids(1) [spawn_node 1] +set spawn_id $spawn_ids(1) +expect_after { + timeout { exit 1 } +} +send "panid 0xface\n" +expect "Done" +send "ifconfig up\n" +expect "Done" +send "thread start\n" +expect "Done" +wait_for "state" "leader" +expect "Done" + +for {set i 2} {$i <= $max_node} {incr i} { + set spawn_ids($i) [spawn_node $i] + set spawn_id $spawn_ids($i) + expect_after { + timeout { exit 1 } + } + send "mode rs\n" + expect "Done" + send "panid 0xface\n" + expect "Done" + send "ifconfig up\n" + expect "Done" + send "thread start\n" + expect "Done" + wait_for "state" "child" + expect "Done" +} + +set spawn_id $spawn_ids(1) +expect_after { + timeout { exit 1 } +} +send "child table\n" +expect "Done" + +for {set i 1} {$i <= $max_node} {incr i} { + set spawn_id $spawn_ids($i) + expect_after { + timeout { exit 1 } + } + dispose +}