1f1549183Sopenharmony_ci/* 2f1549183Sopenharmony_ci * Copyright (C) 2021-2022 Huawei Device Co., Ltd. 3f1549183Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4f1549183Sopenharmony_ci * you may not use this file except in compliance with the License. 5f1549183Sopenharmony_ci * You may obtain a copy of the License at 6f1549183Sopenharmony_ci * 7f1549183Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8f1549183Sopenharmony_ci * 9f1549183Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10f1549183Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11f1549183Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12f1549183Sopenharmony_ci * See the License for the specific language governing permissions and 13f1549183Sopenharmony_ci * limitations under the License. 14f1549183Sopenharmony_ci */ 15f1549183Sopenharmony_ci 16f1549183Sopenharmony_ci#include "server.h" 17f1549183Sopenharmony_ci#include <fcntl.h> 18f1549183Sopenharmony_ci#include <pthread.h> 19f1549183Sopenharmony_ci#include <unistd.h> 20f1549183Sopenharmony_ci#include <stdlib.h> 21f1549183Sopenharmony_ci#include <sys/epoll.h> 22f1549183Sopenharmony_ci#include <sys/socket.h> 23f1549183Sopenharmony_ci#include <sys/un.h> 24f1549183Sopenharmony_ci#include "common.h" 25f1549183Sopenharmony_ci#include "log.h" 26f1549183Sopenharmony_ci#include "net.h" 27f1549183Sopenharmony_ci 28f1549183Sopenharmony_ci#undef LOG_TAG 29f1549183Sopenharmony_ci#define LOG_TAG "WifiRpcServer" 30f1549183Sopenharmony_ci 31f1549183Sopenharmony_ciconst int DEFAULT_LISTEN_QUEUE_SIZE = 10; 32f1549183Sopenharmony_ciconst int MAX_SUPPORT_CLIENT_FD_SIZE = 256; /* support max clients online */ 33f1549183Sopenharmony_ciconst int DEFAULT_HASHTABLE_SLOTS = 7; 34f1549183Sopenharmony_ciconst int SERIAL_DATA_HEAD_SIZE = 2; /* RPC message head size: N| / C| just 2 */ 35f1549183Sopenharmony_ci 36f1549183Sopenharmony_cistatic int BeforeLoop(RpcServer *server); 37f1549183Sopenharmony_cistatic int RemoveCallback(RpcServer *server, const Context *context); 38f1549183Sopenharmony_ci 39f1549183Sopenharmony_cistatic int OnAccept(RpcServer *server, unsigned int mask) 40f1549183Sopenharmony_ci{ 41f1549183Sopenharmony_ci if (server == NULL) { 42f1549183Sopenharmony_ci return -1; 43f1549183Sopenharmony_ci } 44f1549183Sopenharmony_ci 45f1549183Sopenharmony_ci if ((mask & READ_EVENT) == 0) { 46f1549183Sopenharmony_ci return 0; 47f1549183Sopenharmony_ci } 48f1549183Sopenharmony_ci int fd = accept(server->listenFd, NULL, NULL); 49f1549183Sopenharmony_ci if (fd < 0) { 50f1549183Sopenharmony_ci return -1; 51f1549183Sopenharmony_ci } 52f1549183Sopenharmony_ci if (SetNonBlock(fd, 1) != 0) { 53f1549183Sopenharmony_ci LOGE("OnAccept SetNonBlock failed!"); 54f1549183Sopenharmony_ci close(fd); 55f1549183Sopenharmony_ci return -1; 56f1549183Sopenharmony_ci } 57f1549183Sopenharmony_ci if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) { 58f1549183Sopenharmony_ci LOGE("OnAccept fcntl failed!"); 59f1549183Sopenharmony_ci close(fd); 60f1549183Sopenharmony_ci return -1; 61f1549183Sopenharmony_ci } 62f1549183Sopenharmony_ci Context *context = CreateContext(CONTEXT_BUFFER_MIN_SIZE); 63f1549183Sopenharmony_ci if (context != NULL) { 64f1549183Sopenharmony_ci context->fd = fd; 65f1549183Sopenharmony_ci InsertHashTable(server->clients, context); 66f1549183Sopenharmony_ci AddFdEvent(server->loop, fd, READ_EVENT | WRIT_EVENT); 67f1549183Sopenharmony_ci } else { 68f1549183Sopenharmony_ci close(fd); 69f1549183Sopenharmony_ci LOGE("Init Client context failed!"); 70f1549183Sopenharmony_ci return -1; 71f1549183Sopenharmony_ci } 72f1549183Sopenharmony_ci return 0; 73f1549183Sopenharmony_ci} 74f1549183Sopenharmony_ci 75f1549183Sopenharmony_ciRpcServer *CreateRpcServer(const char *path) 76f1549183Sopenharmony_ci{ 77f1549183Sopenharmony_ci if (path == NULL) { 78f1549183Sopenharmony_ci return NULL; 79f1549183Sopenharmony_ci } 80f1549183Sopenharmony_ci RpcServer *server = (RpcServer *)calloc(1, sizeof(RpcServer)); 81f1549183Sopenharmony_ci if (server == NULL) { 82f1549183Sopenharmony_ci return NULL; 83f1549183Sopenharmony_ci } 84f1549183Sopenharmony_ci int flag = 1; 85f1549183Sopenharmony_ci do { 86f1549183Sopenharmony_ci int ret = CreateUnixServer(path, DEFAULT_LISTEN_QUEUE_SIZE); 87f1549183Sopenharmony_ci if (ret < 0) { 88f1549183Sopenharmony_ci break; 89f1549183Sopenharmony_ci } 90f1549183Sopenharmony_ci server->listenFd = ret; 91f1549183Sopenharmony_ci server->isHandlingMsg = false; 92f1549183Sopenharmony_ci server->loop = CreateEventLoop(MAX_SUPPORT_CLIENT_FD_SIZE); 93f1549183Sopenharmony_ci if (server->loop == NULL) { 94f1549183Sopenharmony_ci break; 95f1549183Sopenharmony_ci } 96f1549183Sopenharmony_ci server->clients = InitHashTable(DEFAULT_HASHTABLE_SLOTS); 97f1549183Sopenharmony_ci if (server->clients == NULL) { 98f1549183Sopenharmony_ci break; 99f1549183Sopenharmony_ci } 100f1549183Sopenharmony_ci if (AddFdEvent(server->loop, server->listenFd, READ_EVENT) < 0) { 101f1549183Sopenharmony_ci break; 102f1549183Sopenharmony_ci } 103f1549183Sopenharmony_ci pthread_mutex_init(&server->mutex, NULL); 104f1549183Sopenharmony_ci flag = 0; 105f1549183Sopenharmony_ci } while (0); 106f1549183Sopenharmony_ci if (flag) { 107f1549183Sopenharmony_ci ReleaseRpcServer(server); 108f1549183Sopenharmony_ci return NULL; 109f1549183Sopenharmony_ci } 110f1549183Sopenharmony_ci return server; 111f1549183Sopenharmony_ci} 112f1549183Sopenharmony_ci 113f1549183Sopenharmony_cistatic int DealReadMessage(RpcServer *server, Context *client) 114f1549183Sopenharmony_ci{ 115f1549183Sopenharmony_ci if ((server == NULL) || (client == NULL)) { 116f1549183Sopenharmony_ci return 0; 117f1549183Sopenharmony_ci } 118f1549183Sopenharmony_ci char *buf = ContextGetReadRecord(client); 119f1549183Sopenharmony_ci if (buf == NULL) { 120f1549183Sopenharmony_ci return 0; 121f1549183Sopenharmony_ci } 122f1549183Sopenharmony_ci client->oneProcess = buf; 123f1549183Sopenharmony_ci client->nPos = SERIAL_DATA_HEAD_SIZE; /* N| */ 124f1549183Sopenharmony_ci client->nSize = strlen(buf); 125f1549183Sopenharmony_ci OnTransact(server, client); 126f1549183Sopenharmony_ci free(buf); 127f1549183Sopenharmony_ci buf = NULL; 128f1549183Sopenharmony_ci AddFdEvent(server->loop, client->fd, WRIT_EVENT); 129f1549183Sopenharmony_ci return 1; 130f1549183Sopenharmony_ci} 131f1549183Sopenharmony_ci 132f1549183Sopenharmony_cistatic unsigned int CheckEventMask(const struct epoll_event *e) 133f1549183Sopenharmony_ci{ 134f1549183Sopenharmony_ci if (e == NULL) { 135f1549183Sopenharmony_ci return 0; 136f1549183Sopenharmony_ci } 137f1549183Sopenharmony_ci unsigned int mask = NONE_EVENT; 138f1549183Sopenharmony_ci if ((e->events & EPOLLERR) || (e->events & EPOLLHUP)) { 139f1549183Sopenharmony_ci mask |= READ_EVENT | WRIT_EVENT | EXCP_EVENT; 140f1549183Sopenharmony_ci } else { 141f1549183Sopenharmony_ci if (e->events & EPOLLIN) { 142f1549183Sopenharmony_ci mask |= READ_EVENT; 143f1549183Sopenharmony_ci } 144f1549183Sopenharmony_ci if (e->events & EPOLLOUT) { 145f1549183Sopenharmony_ci mask |= WRIT_EVENT; 146f1549183Sopenharmony_ci } 147f1549183Sopenharmony_ci } 148f1549183Sopenharmony_ci return mask; 149f1549183Sopenharmony_ci} 150f1549183Sopenharmony_ci 151f1549183Sopenharmony_cistatic void DealFdReadEvent(RpcServer *server, Context *client, unsigned int mask) 152f1549183Sopenharmony_ci{ 153f1549183Sopenharmony_ci if ((server == NULL) || (client == NULL)) { 154f1549183Sopenharmony_ci return; 155f1549183Sopenharmony_ci } 156f1549183Sopenharmony_ci DealReadMessage(server, client); 157f1549183Sopenharmony_ci int ret = ContextReadNet(client); 158f1549183Sopenharmony_ci if ((ret == SOCK_ERR) || ((ret == SOCK_CLOSE) && (mask & EXCP_EVENT))) { 159f1549183Sopenharmony_ci LOGE("ContextReadNet failed: %{public}d", ret); 160f1549183Sopenharmony_ci DelFdEvent(server->loop, client->fd, READ_EVENT | WRIT_EVENT); 161f1549183Sopenharmony_ci } else if (ret == SOCK_CLOSE) { 162f1549183Sopenharmony_ci LOGE("Socket close."); 163f1549183Sopenharmony_ci DelFdEvent(server->loop, client->fd, READ_EVENT); 164f1549183Sopenharmony_ci } else if (ret > 0) { 165f1549183Sopenharmony_ci int haveMsg; 166f1549183Sopenharmony_ci do { 167f1549183Sopenharmony_ci haveMsg = DealReadMessage(server, client); 168f1549183Sopenharmony_ci } while (haveMsg); 169f1549183Sopenharmony_ci } 170f1549183Sopenharmony_ci return; 171f1549183Sopenharmony_ci} 172f1549183Sopenharmony_ci 173f1549183Sopenharmony_cistatic void DealFdWriteEvent(RpcServer *server, Context *client) 174f1549183Sopenharmony_ci{ 175f1549183Sopenharmony_ci if ((server == NULL) || (client == NULL)) { 176f1549183Sopenharmony_ci return; 177f1549183Sopenharmony_ci } 178f1549183Sopenharmony_ci 179f1549183Sopenharmony_ci if (client->wBegin != client->wEnd) { 180f1549183Sopenharmony_ci int tmp = ContextWriteNet(client); 181f1549183Sopenharmony_ci if (tmp < 0) { 182f1549183Sopenharmony_ci LOGE("ContextWriteNet failed: %{public}d", tmp); 183f1549183Sopenharmony_ci DelFdEvent(server->loop, client->fd, READ_EVENT | WRIT_EVENT); 184f1549183Sopenharmony_ci } 185f1549183Sopenharmony_ci } else { 186f1549183Sopenharmony_ci LOGE("Del write event."); 187f1549183Sopenharmony_ci DelFdEvent(server->loop, client->fd, WRIT_EVENT); 188f1549183Sopenharmony_ci } 189f1549183Sopenharmony_ci return; 190f1549183Sopenharmony_ci} 191f1549183Sopenharmony_ci 192f1549183Sopenharmony_cistatic void DealFdEvents(RpcServer *server, int fd, unsigned int mask) 193f1549183Sopenharmony_ci{ 194f1549183Sopenharmony_ci if (server == NULL) { 195f1549183Sopenharmony_ci return; 196f1549183Sopenharmony_ci } 197f1549183Sopenharmony_ci Context *client = FindContext(server->clients, fd); 198f1549183Sopenharmony_ci if (client == NULL) { 199f1549183Sopenharmony_ci LOGD("not find %{public}d clients!", fd); 200f1549183Sopenharmony_ci return; 201f1549183Sopenharmony_ci } 202f1549183Sopenharmony_ci if (mask & READ_EVENT) { 203f1549183Sopenharmony_ci DealFdReadEvent(server, client, mask); 204f1549183Sopenharmony_ci } 205f1549183Sopenharmony_ci if (mask & WRIT_EVENT) { 206f1549183Sopenharmony_ci DealFdWriteEvent(server, client); 207f1549183Sopenharmony_ci } 208f1549183Sopenharmony_ci if (server->loop->fdMasks[fd].mask == NONE_EVENT) { 209f1549183Sopenharmony_ci close(fd); 210f1549183Sopenharmony_ci DeleteHashTable(server->clients, client); 211f1549183Sopenharmony_ci RemoveCallback(server, client); 212f1549183Sopenharmony_ci ReleaseContext(client); 213f1549183Sopenharmony_ci } 214f1549183Sopenharmony_ci return; 215f1549183Sopenharmony_ci} 216f1549183Sopenharmony_ci 217f1549183Sopenharmony_ciint RunRpcLoop(RpcServer *server) 218f1549183Sopenharmony_ci{ 219f1549183Sopenharmony_ci if (server == NULL) { 220f1549183Sopenharmony_ci return -1; 221f1549183Sopenharmony_ci } 222f1549183Sopenharmony_ci 223f1549183Sopenharmony_ci EventLoop *loop = server->loop; 224f1549183Sopenharmony_ci while (!loop->stop) { 225f1549183Sopenharmony_ci BeforeLoop(server); 226f1549183Sopenharmony_ci server->isHandlingMsg = false; 227f1549183Sopenharmony_ci int retval = epoll_wait(loop->epfd, loop->epEvents, loop->setSize, -1); 228f1549183Sopenharmony_ci server->isHandlingMsg = true; 229f1549183Sopenharmony_ci for (int i = 0; i < retval; ++i) { 230f1549183Sopenharmony_ci struct epoll_event *e = loop->epEvents + i; 231f1549183Sopenharmony_ci int fd = e->data.fd; 232f1549183Sopenharmony_ci unsigned int mask = CheckEventMask(e); 233f1549183Sopenharmony_ci if (fd == server->listenFd) { 234f1549183Sopenharmony_ci OnAccept(server, mask); 235f1549183Sopenharmony_ci } else { 236f1549183Sopenharmony_ci DealFdEvents(server, fd, mask); 237f1549183Sopenharmony_ci } 238f1549183Sopenharmony_ci } 239f1549183Sopenharmony_ci } 240f1549183Sopenharmony_ci return 0; 241f1549183Sopenharmony_ci} 242f1549183Sopenharmony_ci 243f1549183Sopenharmony_civoid ReleaseRpcServer(RpcServer *server) 244f1549183Sopenharmony_ci{ 245f1549183Sopenharmony_ci if (server != NULL) { 246f1549183Sopenharmony_ci if (server->clients != NULL) { 247f1549183Sopenharmony_ci DestroyHashTable(server->clients); 248f1549183Sopenharmony_ci } 249f1549183Sopenharmony_ci if (server->loop != NULL) { 250f1549183Sopenharmony_ci DestroyEventLoop(server->loop); 251f1549183Sopenharmony_ci } 252f1549183Sopenharmony_ci if (server->listenFd > 0) { 253f1549183Sopenharmony_ci close(server->listenFd); 254f1549183Sopenharmony_ci } 255f1549183Sopenharmony_ci pthread_mutex_destroy(&server->mutex); 256f1549183Sopenharmony_ci free(server); 257f1549183Sopenharmony_ci server = NULL; 258f1549183Sopenharmony_ci } 259f1549183Sopenharmony_ci} 260f1549183Sopenharmony_ci 261f1549183Sopenharmony_cistatic int BeforeLoop(RpcServer *server) 262f1549183Sopenharmony_ci{ 263f1549183Sopenharmony_ci if (server == NULL) { 264f1549183Sopenharmony_ci return -1; 265f1549183Sopenharmony_ci } 266f1549183Sopenharmony_ci pthread_mutex_lock(&server->mutex); 267f1549183Sopenharmony_ci for (int i = 0; i < server->nEvents; ++i) { 268f1549183Sopenharmony_ci int event = server->events[i]; 269f1549183Sopenharmony_ci uint32_t num = sizeof(server->eventNode) / sizeof(server->eventNode[0]); 270f1549183Sopenharmony_ci int pos = event % num; 271f1549183Sopenharmony_ci struct Node *p = server->eventNode[pos].head; 272f1549183Sopenharmony_ci while (p != NULL) { 273f1549183Sopenharmony_ci Context *context = p->context; 274f1549183Sopenharmony_ci OnCallbackTransact(server, event, context); 275f1549183Sopenharmony_ci AddFdEvent(server->loop, context->fd, WRIT_EVENT); 276f1549183Sopenharmony_ci p = p->next; 277f1549183Sopenharmony_ci } 278f1549183Sopenharmony_ci EndCallbackTransact(server, event); 279f1549183Sopenharmony_ci } 280f1549183Sopenharmony_ci server->nEvents = 0; 281f1549183Sopenharmony_ci pthread_mutex_unlock(&server->mutex); 282f1549183Sopenharmony_ci return 0; 283f1549183Sopenharmony_ci} 284f1549183Sopenharmony_ci 285f1549183Sopenharmony_ciint EmitEvent(RpcServer *server, int event) 286f1549183Sopenharmony_ci{ 287f1549183Sopenharmony_ci if (server == NULL) { 288f1549183Sopenharmony_ci return -1; 289f1549183Sopenharmony_ci } 290f1549183Sopenharmony_ci int num = sizeof(server->events) / sizeof(server->events[0]); 291f1549183Sopenharmony_ci pthread_mutex_lock(&server->mutex); 292f1549183Sopenharmony_ci if (server->nEvents >= num) { 293f1549183Sopenharmony_ci pthread_mutex_unlock(&server->mutex); 294f1549183Sopenharmony_ci return -1; 295f1549183Sopenharmony_ci } 296f1549183Sopenharmony_ci server->events[server->nEvents] = event; 297f1549183Sopenharmony_ci ++server->nEvents; 298f1549183Sopenharmony_ci pthread_mutex_unlock(&server->mutex); 299f1549183Sopenharmony_ci /* Triger write to socket */ 300f1549183Sopenharmony_ci if (server->isHandlingMsg == false) { 301f1549183Sopenharmony_ci BeforeLoop(server); 302f1549183Sopenharmony_ci } 303f1549183Sopenharmony_ci return 0; 304f1549183Sopenharmony_ci} 305f1549183Sopenharmony_ci 306f1549183Sopenharmony_ciint RegisterCallback(RpcServer *server, int event, Context *context) 307f1549183Sopenharmony_ci{ 308f1549183Sopenharmony_ci if ((server == NULL) || (context == NULL)) { 309f1549183Sopenharmony_ci return -1; 310f1549183Sopenharmony_ci } 311f1549183Sopenharmony_ci 312f1549183Sopenharmony_ci uint32_t num = sizeof(server->eventNode) / sizeof(server->eventNode[0]); 313f1549183Sopenharmony_ci int pos = event % num; 314f1549183Sopenharmony_ci if (pos >= MAX_EVENT_NODE_COUNT) { 315f1549183Sopenharmony_ci return -1; 316f1549183Sopenharmony_ci } 317f1549183Sopenharmony_ci server->eventNode[pos].event = event; 318f1549183Sopenharmony_ci struct Node *p = server->eventNode[pos].head; 319f1549183Sopenharmony_ci while (p != NULL && p->context->fd != context->fd) { 320f1549183Sopenharmony_ci p = p->next; 321f1549183Sopenharmony_ci } 322f1549183Sopenharmony_ci if (p == NULL) { 323f1549183Sopenharmony_ci p = (struct Node *)calloc(1, sizeof(struct Node)); 324f1549183Sopenharmony_ci if (p != NULL) { 325f1549183Sopenharmony_ci p->next = server->eventNode[pos].head; 326f1549183Sopenharmony_ci p->context = context; 327f1549183Sopenharmony_ci server->eventNode[pos].head = p; 328f1549183Sopenharmony_ci } 329f1549183Sopenharmony_ci } 330f1549183Sopenharmony_ci return 0; 331f1549183Sopenharmony_ci} 332f1549183Sopenharmony_ci 333f1549183Sopenharmony_ciint UnRegisterCallback(RpcServer *server, int event, const Context *context) 334f1549183Sopenharmony_ci{ 335f1549183Sopenharmony_ci if ((server == NULL) || (context == NULL)) { 336f1549183Sopenharmony_ci return -1; 337f1549183Sopenharmony_ci } 338f1549183Sopenharmony_ci 339f1549183Sopenharmony_ci uint32_t num = sizeof(server->eventNode) / sizeof(server->eventNode[0]); 340f1549183Sopenharmony_ci int pos = event % num; 341f1549183Sopenharmony_ci if (pos >= MAX_EVENT_NODE_COUNT) { 342f1549183Sopenharmony_ci return -1; 343f1549183Sopenharmony_ci } 344f1549183Sopenharmony_ci server->eventNode[pos].event = event; 345f1549183Sopenharmony_ci struct Node *p = server->eventNode[pos].head; 346f1549183Sopenharmony_ci struct Node *q = p; 347f1549183Sopenharmony_ci while (p != NULL && p->context->fd != context->fd) { 348f1549183Sopenharmony_ci q = p; 349f1549183Sopenharmony_ci p = p->next; 350f1549183Sopenharmony_ci } 351f1549183Sopenharmony_ci if (p != NULL) { 352f1549183Sopenharmony_ci if (p == server->eventNode[pos].head) { 353f1549183Sopenharmony_ci server->eventNode[pos].head = p->next; 354f1549183Sopenharmony_ci } else { 355f1549183Sopenharmony_ci q->next = p->next; 356f1549183Sopenharmony_ci } 357f1549183Sopenharmony_ci free(p); 358f1549183Sopenharmony_ci p = NULL; 359f1549183Sopenharmony_ci } 360f1549183Sopenharmony_ci return 0; 361f1549183Sopenharmony_ci} 362f1549183Sopenharmony_ci 363f1549183Sopenharmony_cistatic int RemoveCallback(RpcServer *server, const Context *context) 364f1549183Sopenharmony_ci{ 365f1549183Sopenharmony_ci if ((server == NULL) || (context == NULL)) { 366f1549183Sopenharmony_ci return -1; 367f1549183Sopenharmony_ci } 368f1549183Sopenharmony_ci 369f1549183Sopenharmony_ci uint32_t num = sizeof(server->eventNode) / sizeof(server->eventNode[0]); 370f1549183Sopenharmony_ci for (int i = 0; i < num; ++i) { 371f1549183Sopenharmony_ci struct Node *p = server->eventNode[i].head; 372f1549183Sopenharmony_ci if (p == NULL) { 373f1549183Sopenharmony_ci continue; 374f1549183Sopenharmony_ci } 375f1549183Sopenharmony_ci struct Node *q = p; 376f1549183Sopenharmony_ci while (p != NULL && p->context->fd != context->fd) { 377f1549183Sopenharmony_ci q = p; 378f1549183Sopenharmony_ci p = p->next; 379f1549183Sopenharmony_ci } 380f1549183Sopenharmony_ci if (p != NULL) { 381f1549183Sopenharmony_ci if (p == server->eventNode[i].head) { 382f1549183Sopenharmony_ci server->eventNode[i].head = p->next; 383f1549183Sopenharmony_ci } else { 384f1549183Sopenharmony_ci q->next = p->next; 385f1549183Sopenharmony_ci } 386f1549183Sopenharmony_ci free(p); 387f1549183Sopenharmony_ci p = NULL; 388f1549183Sopenharmony_ci } 389f1549183Sopenharmony_ci } 390f1549183Sopenharmony_ci return 0; 391f1549183Sopenharmony_ci} 392