xref: /aosp_15_r20/external/ot-br-posix/src/web/web-service/ot_client.cpp (revision 4a64e381480ef79f0532b2421e44e6ee336b8e0d)
1 /*
2  *  Copyright (c) 2020, 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 #define OTBR_LOG_TAG "WEB"
30 
31 #include "web/web-service/ot_client.hpp"
32 
33 #include <openthread/platform/toolchain.h>
34 
35 #include <errno.h>
36 #include <inttypes.h>
37 #include <signal.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/select.h>
43 #include <sys/socket.h>
44 #include <sys/un.h>
45 #include <unistd.h>
46 
47 #include "common/code_utils.hpp"
48 #include "common/logging.hpp"
49 
50 // Temporary solution before posix platform header files are cleaned up.
51 #ifndef OPENTHREAD_POSIX_DAEMON_SOCKET_NAME
52 #ifdef __linux__
53 #define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/run/openthread-%s"
54 #else
55 #define OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME "/tmp/openthread-%s"
56 #endif
57 #define OPENTHREAD_POSIX_DAEMON_SOCKET_NAME OPENTHREAD_POSIX_CONFIG_DAEMON_SOCKET_BASENAME ".sock"
58 #endif
59 
60 namespace otbr {
61 namespace Web {
62 
OpenThreadClient(const char * aNetifName)63 OpenThreadClient::OpenThreadClient(const char *aNetifName)
64 
65     : mNetifName(aNetifName)
66     , mTimeout(kDefaultTimeout)
67     , mSocket(-1)
68 {
69 }
70 
~OpenThreadClient(void)71 OpenThreadClient::~OpenThreadClient(void)
72 {
73     Disconnect();
74 }
75 
Disconnect(void)76 void OpenThreadClient::Disconnect(void)
77 {
78     if (mSocket != -1)
79     {
80         close(mSocket);
81         mSocket = -1;
82     }
83 }
84 
Connect(void)85 bool OpenThreadClient::Connect(void)
86 {
87     struct sockaddr_un sockname;
88     int                ret;
89 
90     mSocket = socket(AF_UNIX, SOCK_STREAM, 0);
91     VerifyOrExit(mSocket != -1, perror("socket"); ret = EXIT_FAILURE);
92 
93     memset(&sockname, 0, sizeof(struct sockaddr_un));
94     sockname.sun_family = AF_UNIX;
95     ret = snprintf(sockname.sun_path, sizeof(sockname.sun_path), OPENTHREAD_POSIX_DAEMON_SOCKET_NAME, mNetifName);
96 
97     VerifyOrExit(ret >= 0 && static_cast<size_t>(ret) < sizeof(sockname.sun_path), {
98         errno = EINVAL;
99         ret   = -1;
100     });
101 
102     ret = connect(mSocket, reinterpret_cast<const struct sockaddr *>(&sockname), sizeof(struct sockaddr_un));
103 
104     if (ret == -1)
105     {
106         otbrLogErr("OpenThread daemon is not running.");
107     }
108 
109 exit:
110     return ret == 0;
111 }
112 
DiscardRead(void)113 void OpenThreadClient::DiscardRead(void)
114 {
115     fd_set  readFdSet;
116     timeval timeout = {0, 0};
117     ssize_t count;
118     int     ret;
119 
120     for (;;)
121     {
122         FD_ZERO(&readFdSet);
123         FD_SET(mSocket, &readFdSet);
124 
125         ret = select(mSocket + 1, &readFdSet, nullptr, nullptr, &timeout);
126         if (ret <= 0)
127         {
128             break;
129         }
130 
131         count = read(mSocket, mBuffer, sizeof(mBuffer));
132         if (count <= 0)
133         {
134             break;
135         }
136     }
137 }
138 
Execute(const char * aFormat,...)139 char *OpenThreadClient::Execute(const char *aFormat, ...)
140 {
141     va_list args;
142     int     ret;
143     char   *rval = nullptr;
144     ssize_t count;
145     size_t  rxLength = 0;
146 
147     DiscardRead();
148 
149     va_start(args, aFormat);
150     ret = vsnprintf(&mBuffer[1], sizeof(mBuffer) - 2, aFormat, args);
151     va_end(args);
152 
153     if (ret < 0)
154     {
155         otbrLogErr("Failed to generate command: %s", strerror(errno));
156         ExitNow();
157     }
158     if (static_cast<size_t>(ret) >= sizeof(mBuffer) - 2)
159     {
160         otbrLogErr("Command exceeds maximum limit: %d", kBufferSize);
161         ExitNow();
162     }
163 
164     mBuffer[0]       = '\n';
165     mBuffer[ret + 1] = '\n';
166     ret += 2;
167 
168     count = write(mSocket, mBuffer, ret);
169 
170     if (count != ret)
171     {
172         mBuffer[ret] = '\0';
173         otbrLogErr("Failed to send command: %s", mBuffer);
174         ExitNow();
175     }
176 
177     for (int i = 0; i < mTimeout; ++i)
178     {
179         fd_set  readFdSet;
180         timeval timeout = {0, 1000};
181         char   *done;
182 
183         FD_ZERO(&readFdSet);
184         FD_SET(mSocket, &readFdSet);
185 
186         ret = select(mSocket + 1, &readFdSet, nullptr, nullptr, &timeout);
187         VerifyOrExit(ret != -1 || errno == EINTR);
188         if (ret <= 0)
189         {
190             continue;
191         }
192 
193         count = read(mSocket, &mBuffer[rxLength], sizeof(mBuffer) - rxLength);
194         VerifyOrExit(count > 0);
195         rxLength += count;
196 
197         mBuffer[rxLength] = '\0';
198         done              = strstr(mBuffer, "Done\r\n> ");
199 
200         if (done != nullptr)
201         {
202             // remove trailing \r\n
203             if (done - mBuffer > 2)
204             {
205                 done[-2] = '\0';
206             }
207 
208             rval = mBuffer;
209             break;
210         }
211     }
212 
213 exit:
214     return rval;
215 }
216 
Read(const char * aResponse,int aTimeout)217 char *OpenThreadClient::Read(const char *aResponse, int aTimeout)
218 {
219     ssize_t count    = 0;
220     size_t  rxLength = 0;
221     char   *found;
222     char   *rval = nullptr;
223 
224     for (int i = 0; i < aTimeout; ++i)
225     {
226         count = read(mSocket, &mBuffer[rxLength], sizeof(mBuffer) - rxLength);
227         VerifyOrExit(count > 0);
228         rxLength += count;
229 
230         mBuffer[rxLength] = '\0';
231         found             = strstr(mBuffer, aResponse);
232 
233         if (found != nullptr)
234         {
235             rval = mBuffer;
236             break;
237         }
238     }
239 
240 exit:
241     return rval;
242 }
243 
Scan(WpanNetworkInfo * aNetworks,int aLength)244 int OpenThreadClient::Scan(WpanNetworkInfo *aNetworks, int aLength)
245 {
246     char *result;
247     int   rval = 0;
248 
249     mTimeout = 5000;
250     result   = Execute("scan");
251     VerifyOrExit(result != nullptr);
252 
253     for (result = strtok(result, "\r\n"); result != nullptr && rval < aLength; result = strtok(nullptr, "\r\n"))
254     {
255         static const char kCliPrompt[] = "> ";
256         char             *cliPrompt;
257         int               matched;
258         int               lqi;
259 
260         // remove prompt
261         if ((cliPrompt = strstr(result, kCliPrompt)) != nullptr)
262         {
263             if (cliPrompt == result)
264             {
265                 result += sizeof(kCliPrompt) - 1;
266             }
267             else
268             {
269                 memmove(cliPrompt, cliPrompt + sizeof(kCliPrompt) - 1, strlen(cliPrompt) - sizeof(kCliPrompt) - 1);
270             }
271         }
272 
273         matched = sscanf(result, "| %hx | %02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx | %hu | %hhd | %d |",
274                          &aNetworks[rval].mPanId, &aNetworks[rval].mHardwareAddress[0],
275                          &aNetworks[rval].mHardwareAddress[1], &aNetworks[rval].mHardwareAddress[2],
276                          &aNetworks[rval].mHardwareAddress[3], &aNetworks[rval].mHardwareAddress[4],
277                          &aNetworks[rval].mHardwareAddress[5], &aNetworks[rval].mHardwareAddress[6],
278                          &aNetworks[rval].mHardwareAddress[7], &aNetworks[rval].mChannel, &aNetworks[rval].mRssi, &lqi);
279 
280         // 15 is the number of output arguments of the last sscanf()
281         if (matched != 12)
282         {
283             continue;
284         }
285 
286         ++rval;
287     }
288 
289     mTimeout = kDefaultTimeout;
290 
291 exit:
292     return rval;
293 }
294 
FactoryReset(void)295 bool OpenThreadClient::FactoryReset(void)
296 {
297     const char *result;
298     bool        rval = false;
299 #if __APPLE__
300     typedef sig_t sighandler_t;
301 #endif
302     sighandler_t handler;
303 
304     // Ignore the expected SIGPIPE signal during daemon reset.
305     handler = signal(SIGPIPE, SIG_IGN);
306     Execute("factoryreset");
307     signal(SIGPIPE, handler);
308     Disconnect();
309     sleep(4);
310     VerifyOrExit(rval = Connect());
311 
312     result = Execute("version");
313     VerifyOrExit(result != nullptr);
314 
315     rval = strstr(result, "OPENTHREAD") != nullptr;
316 
317 exit:
318     return rval;
319 }
320 
321 } // namespace Web
322 } // namespace otbr
323