2 * Copyright (c) 2020, The OpenThread Authors.
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.
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.
29 #include "web/web-service/ot_client.hpp"
31 #include <openthread/platform/toolchain.h>
40 #include <sys/socket.h>
44 #include "common/code_utils.hpp"
45 #include "common/logging.hpp"
46 #include "utils/strcpy_utils.hpp"
48 // Temporary solution before posix platform header files are cleaned up.
49 #ifndef OPENTHREAD_POSIX_APP_SOCKET_NAME
50 #define OPENTHREAD_POSIX_APP_SOCKET_NAME "/tmp/openthread.sock"
56 OpenThreadClient::OpenThreadClient(void)
57 : mTimeout(kDefaultTimeout)
62 OpenThreadClient::~OpenThreadClient(void)
67 void OpenThreadClient::Disconnect(void)
76 bool OpenThreadClient::Connect(void)
78 struct sockaddr_un sockname;
81 mSocket = socket(AF_UNIX, SOCK_STREAM, 0);
82 VerifyOrExit(mSocket != -1, perror("socket"); ret = EXIT_FAILURE);
84 memset(&sockname, 0, sizeof(struct sockaddr_un));
85 sockname.sun_family = AF_UNIX;
86 strcpy_safe(sockname.sun_path, sizeof(sockname.sun_path), OPENTHREAD_POSIX_APP_SOCKET_NAME);
88 ret = connect(mSocket, reinterpret_cast<const struct sockaddr *>(&sockname), sizeof(struct sockaddr_un));
92 otbrLog(OTBR_LOG_ERR, "OpenThread daemon is not running.");
99 char *OpenThreadClient::Execute(const char *aFormat, ...)
103 char * rval = nullptr;
107 va_start(args, aFormat);
108 ret = vsnprintf(&mBuffer[1], sizeof(mBuffer) - 1, aFormat, args);
113 otbrLog(OTBR_LOG_ERR, "Failed to generate command: %s", strerror(errno));
119 if (ret == sizeof(mBuffer))
121 otbrLog(OTBR_LOG_ERR, "Command exceeds maximum limit: %d", kBufferSize);
127 count = write(mSocket, mBuffer, ret);
132 otbrLog(OTBR_LOG_ERR, "Failed to send command: %s", mBuffer);
135 for (int i = 0; i < mTimeout; ++i)
138 timeval timeout = {0, 1000};
142 FD_SET(mSocket, &readFdSet);
144 ret = select(mSocket + 1, &readFdSet, nullptr, nullptr, &timeout);
145 VerifyOrExit(ret != -1 || errno == EINTR);
151 count = read(mSocket, &mBuffer[rxLength], sizeof(mBuffer) - rxLength);
152 VerifyOrExit(count > 0);
155 mBuffer[rxLength] = '\0';
156 done = strstr(mBuffer, "Done\r\n");
160 // remove trailing \r\n
175 int OpenThreadClient::Scan(WpanNetworkInfo *aNetworks, int aLength)
181 result = Execute("scan");
182 VerifyOrExit(result != nullptr);
184 for (result = strtok(result, "\r\n"); result != nullptr && rval < aLength; result = strtok(nullptr, "\r\n"))
186 static const char kCliPrompt[] = "> ";
193 if ((cliPrompt = strstr(result, kCliPrompt)) != nullptr)
195 if (cliPrompt == result)
197 result += sizeof(kCliPrompt) - 1;
201 memmove(cliPrompt, cliPrompt + sizeof(kCliPrompt) - 1, strlen(cliPrompt) - sizeof(kCliPrompt) - 1);
205 matched = sscanf(result,
206 "| %d | %s | %" PRIx64
207 " | %hx | %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx | %hu | %hhd | %d |",
208 &joinable, aNetworks[rval].mNetworkName, &aNetworks[rval].mExtPanId, &aNetworks[rval].mPanId,
209 &aNetworks[rval].mHardwareAddress[0], &aNetworks[rval].mHardwareAddress[1],
210 &aNetworks[rval].mHardwareAddress[2], &aNetworks[rval].mHardwareAddress[3],
211 &aNetworks[rval].mHardwareAddress[4], &aNetworks[rval].mHardwareAddress[5],
212 &aNetworks[rval].mHardwareAddress[6], &aNetworks[rval].mHardwareAddress[7],
213 &aNetworks[rval].mChannel, &aNetworks[rval].mRssi, &lqi);
215 // 15 is the number of output arguments of the last sscanf()
221 aNetworks[rval].mAllowingJoin = joinable != 0;
225 mTimeout = kDefaultTimeout;
231 bool OpenThreadClient::FactoryReset(void)
236 typedef sig_t sighandler_t;
238 sighandler_t handler;
240 // Ignore the expected SIGPIPE signal during daemon reset.
241 handler = signal(SIGPIPE, SIG_IGN);
242 Execute("factoryreset");
243 signal(SIGPIPE, handler);
246 VerifyOrExit(rval = Connect());
248 result = Execute("version");
249 VerifyOrExit(result != nullptr);
251 rval = strstr(result, "OPENTHREAD") != nullptr;