text: /*
   *  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.
   */
  
  #if __ORDER_BIG_ENDIAN__
  #define BYTE_ORDER_BIG_ENDIAN 1
  #endif
  
  #define OTBR_LOG_TAG "UBUS"
  
  #include "openwrt/ubus/otubus.hpp"
  
  #include <mutex>
  
  #include <arpa/inet.h>
  #include <sys/eventfd.h>
  
  #include <openthread/commissioner.h>
  #include <openthread/thread.h>
  #include <openthread/thread_ftd.h>
  
  #include "agent/ncp_openthread.hpp"
  #include "common/logging.hpp"
  
  namespace otbr {
  namespace ubus {
  
  static UbusServer *sUbusServerInstance = nullptr;
  static int         sUbusEfd            = -1;
  static void *      sJsonUri            = nullptr;
  static int         sBufNum;
  static std::mutex *sNcpThreadMutex;
  
  const static int PANID_LENGTH      = 10;
  const static int XPANID_LENGTH     = 64;
  const static int NETWORKKEY_LENGTH = 64;
  
  UbusServer::UbusServer(Ncp::ControllerOpenThread *aController)
      : mIfFinishScan(false)
      , mContext(nullptr)
      , mSockPath(nullptr)
      , mController(aController)
      , mSecond(0)
  {
      memset(&mNetworkdataBuf, 0, sizeof(mNetworkdataBuf));
      memset(&mBuf, 0, sizeof(mBuf));
  
      blob_buf_init(&mBuf, 0);
      blob_buf_init(&mNetworkdataBuf, 0);
  }
  
  UbusServer &UbusServer::GetInstance(void)
  {
      return *sUbusServerInstance;
  }
  
  void UbusServer::Initialize(Ncp::ControllerOpenThread *aController)
  {
      sUbusServerInstance = new UbusServer(aController);
  }
  
  enum
  {
      SETNETWORK,
      SET_NETWORK_MAX,
  };
  
  enum
  {
      PSKD,
      EUI64,
      ADD_JOINER_MAX,
  };
  
  enum
  {
      NETWORKKEY,
      NETWORKNAME,
      EXTPANID,
      PANID,
      CHANNEL,
      PSKC,
      MGMTSET_MAX,
  };
  
  static const struct blobmsg_policy setNetworknamePolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "networkname", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy setPanIdPolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "panid", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy setExtPanIdPolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "extpanid", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy setChannelPolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "channel", .type = BLOBMSG_TYPE_INT32},
  };
  
  static const struct blobmsg_policy setPskcPolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "pskc", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy setNetworkkeyPolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "networkkey", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy setModePolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "mode", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy macfilterAddPolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "addr", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy macfilterRemovePolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "addr", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy macfilterSetStatePolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "state", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy removeJoinerPolicy[SET_NETWORK_MAX] = {
      [SETNETWORK] = {.name = "eui64", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy addJoinerPolicy[ADD_JOINER_MAX] = {
      [PSKD]  = {.name = "pskd", .type = BLOBMSG_TYPE_STRING},
      [EUI64] = {.name = "eui64", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct blobmsg_policy mgmtsetPolicy[MGMTSET_MAX] = {
      [NETWORKKEY]  = {.name = "networkkey", .type = BLOBMSG_TYPE_STRING},
      [NETWORKNAME] = {.name = "networkname", .type = BLOBMSG_TYPE_STRING},
      [EXTPANID]    = {.name = "extpanid", .type = BLOBMSG_TYPE_STRING},
      [PANID]       = {.name = "panid", .type = BLOBMSG_TYPE_STRING},
      [CHANNEL]     = {.name = "channel", .type = BLOBMSG_TYPE_STRING},
      [PSKC]        = {.name = "pskc", .type = BLOBMSG_TYPE_STRING},
  };
  
  static const struct ubus_method otbrMethods[] = {
      {"scan", &UbusServer::UbusScanHandler, 0, 0, nullptr, 0},
      {"channel", &UbusServer::UbusChannelHandler, 0, 0, nullptr, 0},
      {"setchannel", &UbusServer::UbusSetChannelHandler, 0, 0, setChannelPolicy, ARRAY_SIZE(setChannelPolicy)},
      {"networkname", &UbusServer::UbusNetworknameHandler, 0, 0, nullptr, 0},
      {"setnetworkname", &UbusServer::UbusSetNetworknameHandler, 0, 0, setNetworknamePolicy,
       ARRAY_SIZE(setNetworknamePolicy)},
      {"state", &UbusServer::UbusStateHandler, 0, 0, nullptr, 0},
      {"panid", &UbusServer::UbusPanIdHandler, 0, 0, nullptr, 0},
      {"setpanid", &UbusServer::UbusSetPanIdHandler, 0, 0, setPanIdPolicy, ARRAY_SIZE(setPanIdPolicy)},
      {"rloc16", &UbusServer::UbusRloc16Handler, 0, 0, nullptr, 0},
      {"extpanid", &UbusServer::UbusExtPanIdHandler, 0, 0, nullptr, 0},
      {"setextpanid", &UbusServer::UbusSetExtPanIdHandler, 0, 0, setExtPanIdPolicy, ARRAY_SIZE(setExtPanIdPolicy)},
      {"networkkey", &UbusServer::UbusNetworkkeyHandler, 0, 0, nullptr, 0},
      {"setnetworkkey", &UbusServer::UbusSetNetworkkeyHandler, 0, 0, setNetworkkeyPolicy,
       ARRAY_SIZE(setNetworkkeyPolicy)},
      {"pskc", &UbusServer::UbusPskcHandler, 0, 0, nullptr, 0},
      {"setpskc", &UbusServer::UbusSetPskcHandler, 0, 0, setPskcPolicy, ARRAY_SIZE(setPskcPolicy)},
      {"threadstart", &UbusServer::UbusThreadStartHandler, 0, 0, nullptr, 0},
      {"threadstop", &UbusServer::UbusThreadStopHandler, 0, 0, nullptr, 0},
      {"neighbor", &UbusServer::UbusNeighborHandler, 0, 0, nullptr, 0},
      {"parent", &UbusServer::UbusParentHandler, 0, 0, nullptr, 0},
      {"mode", &UbusServer::UbusModeHandler, 0, 0, nullptr, 0},
      {"setmode", &UbusServer::UbusSetModeHandler, 0, 0, setModePolicy, ARRAY_SIZE(setModePolicy)},
      {"partitionid", &UbusServer::UbusPartitionIdHandler, 0, 0, nullptr, 0},
      {"leave", &UbusServer::UbusLeaveHandler, 0, 0, nullptr, 0},
      {"leaderdata", &UbusServer::UbusLeaderdataHandler, 0, 0, nullptr, 0},
      {"networkdata", &UbusServer::UbusNetworkdataHandler, 0, 0, nullptr, 0},
      {"commissionerstart", &UbusServer::UbusCommissionerStartHandler, 0, 0, nullptr, 0},
      {"joinernum", &UbusServer::UbusJoinerNumHandler, 0, 0, nullptr, 0},
      {"joinerremove", &UbusServer::UbusJoinerRemoveHandler, 0, 0, nullptr, 0},
      {"macfiltersetstate", &UbusServer::UbusMacfilterSetStateHandler, 0, 0, macfilterSetStatePolicy,
       ARRAY_SIZE(macfilterSetStatePolicy)},
      {"macfilteradd", &UbusServer::UbusMacfilterAddHandler, 0, 0, macfilterAddPolicy, ARRAY_SIZE(macfilterAddPolicy)},
      {"macfilterremove", &UbusServer::UbusMacfilterRemoveHandler, 0, 0, macfilterRemovePolicy,
       ARRAY_SIZE(macfilterRemovePolicy)},
      {"macfilterclear", &UbusServer::UbusMacfilterClearHandler, 0, 0, nullptr, 0},
      {"macfilterstate", &UbusServer::UbusMacfilterStateHandler, 0, 0, nullptr, 0},
      {"macfilteraddr", &UbusServer::UbusMacfilterAddrHandler, 0, 0, nullptr, 0},
      {"joineradd", &UbusServer::UbusJoinerAddHandler, 0, 0, addJoinerPolicy, ARRAY_SIZE(addJoinerPolicy)},
      {"mgmtset", &UbusServer::UbusMgmtsetHandler, 0, 0, mgmtsetPolicy, ARRAY_SIZE(mgmtsetPolicy)},
  };
  
  static struct ubus_object_type otbrObjType = {"otbr_prog", 0, otbrMethods, ARRAY_SIZE(otbrMethods)};
  
  static struct ubus_object otbr = {
      avl : {},
      name : "otbr",
      id : 0,
      path : nullptr,
      type : &otbrObjType,
      subscribe_cb : nullptr,
      has_subscribers : false,
      methods : otbrMethods,
      n_methods : ARRAY_SIZE(otbrMethods),
  };
  
  void UbusServer::ProcessScan(void)
  {
      otError  error        = OT_ERROR_NONE;
      uint32_t scanChannels = 0;
      uint16_t scanDuration = 0;
  
      sNcpThreadMutex->lock();
      SuccessOrExit(error = otLinkActiveScan(mController->GetInstance(), scanChannels, scanDuration,
                                             &UbusServer::HandleActiveScanResult, this));
  exit:
      sNcpThreadMutex->unlock();
      return;
  }
  
  void UbusServer::HandleActiveScanResult(otActiveScanResult *aResult, void *aContext)
  {
      static_cast<UbusServer *>(aContext)->HandleActiveScanResultDetail(aResult);
  }
  
  void UbusServer::OutputBytes(const uint8_t *aBytes, uint8_t aLength, char *aOutput)
  {
      char byte2char[5] = "";
      for (int i = 0; i < aLength; i++)
      {
          sprintf(byte2char, "%02x", aBytes[i]);
          strcat(aOutput, byte2char);
      }
  }
  
  void UbusServer::AppendResult(otError aError, struct ubus_context *aContext, struct ubus_request_data *aRequest)
  {
      blobmsg_add_u16(&mBuf, "Error", aError);
      ubus_send_reply(aContext, aRequest, mBuf.head);
  }
  
  void UbusServer::HandleActiveScanResultDetail(otActiveScanResult *aResult)
  {
      void *jsonList = nullptr;
  
      char panidstring[PANID_LENGTH];
      char xpanidstring[XPANID_LENGTH] = "";
  
      if (aResult == nullptr)
      {
          blobmsg_close_array(&mBuf, sJsonUri);
          mIfFinishScan = true;
          goto exit;
      }
  
      jsonList = blobmsg_open_table(&mBuf, nullptr);
  
      blobmsg_add_u32(&mBuf, "IsJoinable", aResult->mIsJoinable);
  
      blobmsg_add_string(&mBuf, "NetworkName", aResult->mNetworkName.m8);
  
      OutputBytes(aResult->mExtendedPanId.m8, OT_EXT_PAN_ID_SIZE, xpanidstring);
      blobmsg_add_string(&mBuf, "ExtendedPanId", xpanidstring);
  
      sprintf(panidstring, "0x%04x", aResult->mPanId);
      blobmsg_add_string(&mBuf, "PanId", panidstring);
  
      blobmsg_add_u32(&mBuf, "Channel", aResult->mChannel);
  
      blobmsg_add_u32(&mBuf, "Rssi", aResult->mRssi);
  
      blobmsg_add_u32(&mBuf, "Lqi", aResult->mLqi);
  
      blobmsg_close_table(&mBuf, jsonList);
  
  exit:
      return;
  }
  
  int UbusServer::UbusScanHandler(struct ubus_context *     aContext,
                                  struct ubus_object *      aObj,
                                  struct ubus_request_data *aRequest,
                                  const char *              aMethod,
                                  struct blob_attr *        aMsg)
  {
      return GetInstance().UbusScanHandlerDetail(aContext, aObj, aRequest, aMethod, aMsg);
  }
  
  int UbusServer::UbusScanHandlerDetail(struct ubus_context *     aContext,
                                        struct ubus_object *      aObj,
                                        struct ubus_request_data *aRequest,
                                        const char *              aMethod,
                                        struct blob_attr *        aMsg)
  {
      OT_UNUSED_VARIABLE(aObj);
      OT_UNUSED_VARIABLE(aMethod);
      OT_UNUSED_VARIABLE(aMsg);
  
      otError  error = OT_ERROR_NONE;
      uint64_t eventNum;
      ssize_t  retval;
  
      blob_buf_init(&mBuf, 0);
      sJsonUri = blobmsg_open_array(&mBuf, "scan_list");
  
      mIfFinishScan = 0;
      sUbusServerInstance->ProcessScan();
  
      eventNum = 1;
      retval   = write(sUbusEfd, &eventNum, sizeof(uint64_t));
      if (retval != sizeof(uint64_t))
      {
          error = OT_ERROR_FAILED;
          goto exit;
      }
  
      while (!mIfFinishScan)
      {
          sleep(1);
      }
  
  exit:
      AppendResult(error, aContext, aRequest);
      return 0;
  }
  
  int UbusServer::UbusChannelHandler(struct ubus_context *     aContext,
                                     struct ubus_object *      aObj,
                                     struct ubus_request_data *aRequest,
                                     const char *              aMethod,
                                     struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "channel");
  }
  
  int UbusServer::UbusSetChannelHandler(struct ubus_context *     aContext,
                                        struct ubus_object *      aObj,
                                        struct ubus_request_data *aRequest,
                                        const char *              aMethod,
                                        struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "channel");
  }
  
  int UbusServer::UbusJoinerNumHandler(struct ubus_context *     aContext,
                                       struct ubus_object *      aObj,
                                       struct ubus_request_data *aRequest,
                                       const char *              aMethod,
                                       struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "joinernum");
  }
  
  int UbusServer::UbusNetworknameHandler(struct ubus_context *     aContext,
                                         struct ubus_object *      aObj,
                                         struct ubus_request_data *aRequest,
                                         const char *              aMethod,
                                         struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "networkname");
  }
  
  int UbusServer::UbusSetNetworknameHandler(struct ubus_context *     aContext,
                                            struct ubus_object *      aObj,
                                            struct ubus_request_data *aRequest,
                                            const char *              aMethod,
                                            struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "networkname");
  }
  
  int UbusServer::UbusStateHandler(struct ubus_context *     aContext,
                                   struct ubus_object *      aObj,
                                   struct ubus_request_data *aRequest,
                                   const char *              aMethod,
                                   struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "state");
  }
  
  int UbusServer::UbusRloc16Handler(struct ubus_context *     aContext,
                                    struct ubus_object *      aObj,
                                    struct ubus_request_data *aRequest,
                                    const char *              aMethod,
                                    struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "rloc16");
  }
  
  int UbusServer::UbusPanIdHandler(struct ubus_context *     aContext,
                                   struct ubus_object *      aObj,
                                   struct ubus_request_data *aRequest,
                                   const char *              aMethod,
                                   struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "panid");
  }
  
  int UbusServer::UbusSetPanIdHandler(struct ubus_context *     aContext,
                                      struct ubus_object *      aObj,
                                      struct ubus_request_data *aRequest,
                                      const char *              aMethod,
                                      struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "panid");
  }
  
  int UbusServer::UbusExtPanIdHandler(struct ubus_context *     aContext,
                                      struct ubus_object *      aObj,
                                      struct ubus_request_data *aRequest,
                                      const char *              aMethod,
                                      struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "extpanid");
  }
  
  int UbusServer::UbusSetExtPanIdHandler(struct ubus_context *     aContext,
                                         struct ubus_object *      aObj,
                                         struct ubus_request_data *aRequest,
                                         const char *              aMethod,
                                         struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "extpanid");
  }
  
  int UbusServer::UbusPskcHandler(struct ubus_context *     aContext,
                                  struct ubus_object *      aObj,
                                  struct ubus_request_data *aRequest,
                                  const char *              aMethod,
                                  struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "pskc");
  }
  
  int UbusServer::UbusSetPskcHandler(struct ubus_context *     aContext,
                                     struct ubus_object *      aObj,
                                     struct ubus_request_data *aRequest,
                                     const char *              aMethod,
                                     struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "pskc");
  }
  
  int UbusServer::UbusNetworkkeyHandler(struct ubus_context *     aContext,
                                        struct ubus_object *      aObj,
                                        struct ubus_request_data *aRequest,
                                        const char *              aMethod,
                                        struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "networkkey");
  }
  
  int UbusServer::UbusSetNetworkkeyHandler(struct ubus_context *     aContext,
                                           struct ubus_object *      aObj,
                                           struct ubus_request_data *aRequest,
                                           const char *              aMethod,
                                           struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "networkkey");
  }
  
  int UbusServer::UbusThreadStartHandler(struct ubus_context *     aContext,
                                         struct ubus_object *      aObj,
                                         struct ubus_request_data *aRequest,
                                         const char *              aMethod,
                                         struct blob_attr *        aMsg)
  {
      return GetInstance().UbusThreadHandler(aContext, aObj, aRequest, aMethod, aMsg, "start");
  }
  
  int UbusServer::UbusThreadStopHandler(struct ubus_context *     aContext,
                                        struct ubus_object *      aObj,
                                        struct ubus_request_data *aRequest,
                                        const char *              aMethod,
                                        struct blob_attr *        aMsg)
  {
      return GetInstance().UbusThreadHandler(aContext, aObj, aRequest, aMethod, aMsg, "stop");
  }
  
  int UbusServer::UbusParentHandler(struct ubus_context *     aContext,
                                    struct ubus_object *      aObj,
                                    struct ubus_request_data *aRequest,
                                    const char *              aMethod,
                                    struct blob_attr *        aMsg)
  {
      return GetInstance().UbusParentHandlerDetail(aContext, aObj, aRequest, aMethod, aMsg);
  }
  
  int UbusServer::UbusNeighborHandler(struct ubus_context *     aContext,
                                      struct ubus_object *      aObj,
                                      struct ubus_request_data *aRequest,
                                      const char *              aMethod,
                                      struct blob_attr *        aMsg)
  {
      return GetInstance().UbusNeighborHandlerDetail(aContext, aObj, aRequest, aMethod, aMsg);
  }
  
  int UbusServer::UbusModeHandler(struct ubus_context *     aContext,
                                  struct ubus_object *      aObj,
                                  struct ubus_request_data *aRequest,
                                  const char *              aMethod,
                                  struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "mode");
  }
  
  int UbusServer::UbusSetModeHandler(struct ubus_context *     aContext,
                                     struct ubus_object *      aObj,
                                     struct ubus_request_data *aRequest,
                                     const char *              aMethod,
                                     struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "mode");
  }
  
  int UbusServer::UbusPartitionIdHandler(struct ubus_context *     aContext,
                                         struct ubus_object *      aObj,
                                         struct ubus_request_data *aRequest,
                                         const char *              aMethod,
                                         struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "partitionid");
  }
  
  int UbusServer::UbusLeaveHandler(struct ubus_context *     aContext,
                                   struct ubus_object *      aObj,
                                   struct ubus_request_data *aRequest,
                                   const char *              aMethod,
                                   struct blob_attr *        aMsg)
  {
      return GetInstance().UbusLeaveHandlerDetail(aContext, aObj, aRequest, aMethod, aMsg);
  }
  
  int UbusServer::UbusLeaderdataHandler(struct ubus_context *     aContext,
                                        struct ubus_object *      aObj,
                                        struct ubus_request_data *aRequest,
                                        const char *              aMethod,
                                        struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "leaderdata");
  }
  
  int UbusServer::UbusNetworkdataHandler(struct ubus_context *     aContext,
                                         struct ubus_object *      aObj,
                                         struct ubus_request_data *aRequest,
                                         const char *              aMethod,
                                         struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "networkdata");
  }
  
  int UbusServer::UbusCommissionerStartHandler(struct ubus_context *     aContext,
                                               struct ubus_object *      aObj,
                                               struct ubus_request_data *aRequest,
                                               const char *              aMethod,
                                               struct blob_attr *        aMsg)
  {
      return GetInstance().UbusCommissioner(aContext, aObj, aRequest, aMethod, aMsg, "start");
  }
  
  int UbusServer::UbusJoinerRemoveHandler(struct ubus_context *     aContext,
                                          struct ubus_object *      aObj,
                                          struct ubus_request_data *aRequest,
                                          const char *              aMethod,
                                          struct blob_attr *        aMsg)
  {
      return GetInstance().UbusCommissioner(aContext, aObj, aRequest, aMethod, aMsg, "joinerremove");
  }
  
  int UbusServer::UbusMgmtsetHandler(struct ubus_context *     aContext,
                                     struct ubus_object *      aObj,
                                     struct ubus_request_data *aRequest,
                                     const char *              aMethod,
                                     struct blob_attr *        aMsg)
  {
      return GetInstance().UbusMgmtset(aContext, aObj, aRequest, aMethod, aMsg);
  }
  
  int UbusServer::UbusJoinerAddHandler(struct ubus_context *     aContext,
                                       struct ubus_object *      aObj,
                                       struct ubus_request_data *aRequest,
                                       const char *              aMethod,
                                       struct blob_attr *        aMsg)
  {
      return GetInstance().UbusCommissioner(aContext, aObj, aRequest, aMethod, aMsg, "joineradd");
  }
  
  int UbusServer::UbusMacfilterAddrHandler(struct ubus_context *     aContext,
                                           struct ubus_object *      aObj,
                                           struct ubus_request_data *aRequest,
                                           const char *              aMethod,
                                           struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "macfilteraddr");
  }
  
  int UbusServer::UbusMacfilterStateHandler(struct ubus_context *     aContext,
                                            struct ubus_object *      aObj,
                                            struct ubus_request_data *aRequest,
                                            const char *              aMethod,
                                            struct blob_attr *        aMsg)
  {
      return GetInstance().UbusGetInformation(aContext, aObj, aRequest, aMethod, aMsg, "macfilterstate");
  }
  
  int UbusServer::UbusMacfilterAddHandler(struct ubus_context *     aContext,
                                          struct ubus_object *      aObj,
                                          struct ubus_request_data *aRequest,
                                          const char *              aMethod,
                                          struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "macfilteradd");
  }
  
  int UbusServer::UbusMacfilterRemoveHandler(struct ubus_context *     aContext,
                                             struct ubus_object *      aObj,
                                             struct ubus_request_data *aRequest,
                                             const char *              aMethod,
                                             struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "macfilterremove");
  }
  
  int UbusServer::UbusMacfilterSetStateHandler(struct ubus_context *     aContext,
                                               struct ubus_object *      aObj,
                                               struct ubus_request_data *aRequest,
                                               const char *              aMethod,
                                               struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "macfiltersetstate");
  }
  
  int UbusServer::UbusMacfilterClearHandler(struct ubus_context *     aContext,
                                            struct ubus_object *      aObj,
                                            struct ubus_request_data *aRequest,
                                            const char *              aMethod,
                                            struct blob_attr *        aMsg)
  {
      return GetInstance().UbusSetInformation(aContext, aObj, aRequest, aMethod, aMsg, "macfilterclear");
  }
  
  int UbusServer::UbusLeaveHandlerDetail(struct ubus_context *     aContext,
                                         struct ubus_object *      aObj,
                                         struct ubus_request_data *aRequest,
                                         const char *              aMethod,
                                         struct blob_attr *        aMsg)
  {
      OT_UNUSED_VARIABLE(aObj);
      OT_UNUSED_VARIABLE(aMethod);
      OT_UNUSED_VARIABLE(aMsg);
  
      otError  error = OT_ERROR_NONE;
      uint64_t eventNum;
      ssize_t  retval;
  
      sNcpThreadMutex->lock();
      otInstanceFactoryReset(mController->GetInstance());
  
      eventNum = 1;
      retval   = write(sUbusEfd, &eventNum, sizeof(uint64_t));
      if (retval != sizeof(uint64_t))
      {
          error = OT_ERROR_FAILED;
          goto exit;
      }
  
      blob_buf_init(&mBuf, 0);
  
  exit:
      sNcpThreadMutex->unlock();
      AppendResult(error, aContext, aRequest);
      return 0;
  }
  int UbusServer::UbusThreadHandler(struct ubus_context *     aContext,
                                    struct ubus_object *      aObj,
                                    struct ubus_request_data *aRequest,
                                    const char *              aMethod,
                                    struct blob_attr *        aMsg,
                                    const char *              aAction)
  {
      OT_UNUSED_VARIABLE(aObj);
      OT_UNUSED_VARIABLE(aMethod);
      OT_UNUSED_VARIABLE(aMsg);
  
      otError error = OT_ERROR_NONE;
  
      blob_buf_init(&mBuf, 0);
  
      if (!strcmp(aAction, "start"))
      {
          sNcpThreadMutex->lock();
          SuccessOrExit(error = otIp6SetEnabled(mController->GetInstance(), true));
          SuccessOrExit(error = otThreadSetEnabled(mController->GetInstance(), true));
      }
      else if (!strcmp(aAction, "stop"))
      {
          sNcpThreadMutex->lock();
          SuccessOrExit(error = otThreadSetEnabled(mController->GetInstance(), false));
          SuccessOrExit(error = otIp6SetEnabled(mController->GetInstance(), false));
      }
  
  exit:
      sNcpThreadMutex->unlock();
      AppendResult(error, aContext, aRequest);
      return 0;
  }
  
  int UbusServer::UbusParentHandlerDetail(struct ubus_context *     aContext,
                                          struct ubus_object *      aObj,
                                          struct ubus_request_data *aRequest,
                                          const char *              aMethod,
                                          struct blob_attr *        aMsg)
  {
      OT_UNUSED_VARIABLE(aObj);
      OT_UNUSED_VARIABLE(aMethod);
      OT_UNUSED_VARIABLE(aMsg);
  
      otError      error = OT_ERROR_NONE;
      otRouterInfo parentInfo;
      char         extAddress[XPANID_LENGTH] = "";
      char         transfer[XPANID_LENGTH]   = "";
      void *       jsonList                  = nullptr;
      void *       jsonArray                 = nullptr;
  
      blob_buf_init(&mBuf, 0);
  
      sNcpThreadMutex->lock();
      SuccessOrExit(error = otThreadGetParentInfo(mController->GetInstance(), &parentInfo));
  
      jsonArray = blobmsg_open_array(&mBuf, "parent_list");
      jsonList  = blobmsg_open_table(&mBuf, "parent");
      blobmsg_add_string(&mBuf, "Role", "R");
  
      sprintf(transfer, "0x%04x", parentInfo.mRloc16);
      blobmsg_add_string(&mBuf, "Rloc16", transfer);
  
      sprintf(transfer, "%3d", parentInfo.mAge);
      blobmsg_add_string(&mBuf, "Age", transfer);
  
      OutputBytes(parentInfo.mExtAddress.m8, sizeof(parentInfo.mExtAddress.m8), extAddress);
      blobmsg_add_string(&mBuf, "ExtAddress", extAddress);
  
      blobmsg_add_u16(&mBuf, "LinkQualityIn", parentInfo.mLinkQualityIn);
  
      blobmsg_close_table(&mBuf, jsonList);
      blobmsg_close_array(&mBuf, jsonArray);
  
  exit:
      sNcpThreadMutex->unlock();
      AppendResult(error, aContext, aRequest);
      return error;
  }
  
  int UbusServer::UbusNeighborHandlerDetail(struct ubus_context *     aContext,
                                            struct ubus_object *      aObj,
                                            struct ubus_request_data *aRequest,
                                            const char *              aMethod,
                                            struct blob_attr *        aMsg)
  {
      OT_UNUSED_VARIABLE(aObj);
      OT_UNUSED_VARIABLE(aMethod);
      OT_UNUSED_VARIABLE(aMsg);
  
      otError                error = OT_ERROR_NONE;
      otNeighborInfo         neighborInfo;
      otNeighborInfoIterator iterator                  = OT_NEIGHBOR_INFO_ITERATOR_INIT;
      char                   transfer[XPANID_LENGTH]   = "";
      void *                 jsonList                  = nullptr;
      char                   mode[5]                   = "";
      char                   extAddress[XPANID_LENGTH] = "";
  
      blob_buf_init(&mBuf, 0);
  
      sJsonUri = blobmsg_open_array(&mBuf, "neighbor_list");
  
      sNcpThreadMutex->lock();
      while (otThreadGetNextNeighborInfo(mController->GetInstance(), &iterator, &neighborInfo) == OT_ERROR_NONE)
      {
          jsonList = blobmsg_open_table(&mBuf, nullptr);
  
          blobmsg_add_string(&mBuf, "Role", neighborInfo.mIsChild ? "C" : "R");
  
          sprintf(transfer, "0x%04x", neighborInfo.mRloc16);
          blobmsg_add_string(&mBuf, "Rloc16", transfer);
  
          sprintf(transfer, "%3d", neighborInfo.mAge);
          blobmsg_add_string(&mBuf, "Age", transfer);
  
          sprintf(transfer, "%8d", neighborInfo.mAverageRssi);
          blobmsg_add_string(&mBuf, "AvgRssi", transfer);
  
          sprintf(transfer, "%9d", neighborInfo.mLastRssi);
          blobmsg_add_string(&mBuf, "LastRssi", transfer);
  
          if (neighborInfo.mRxOnWhenIdle)
          {
              strcat(mode, "r");
          }
  
          if (neighborInfo.mFullThreadDevice)
          {
              strcat(mode, "d");
          }
  
          if (neighborInfo.mFullNetworkData)
          {
              strcat(mode, "n");
          }
          blobmsg_add_string(&mBuf, "Mode", mode);
  
          OutputBytes(neighborInfo.mExtAddress.m8, sizeof(neighborInfo.mExtAddress.m8), extAddress);
          blobmsg_add_string(&mBuf, "ExtAddress", extAddress);
  
          blobmsg_add_u16(&mBuf, "LinkQualityIn", neighborInfo.mLinkQualityIn);
  
          blobmsg_close_table(&mBuf, jsonList);
  
          memset(mode, 0, sizeof(mode));
          memset(extAddress, 0, sizeof(extAddress));
      }
  
      blobmsg_close_array(&mBuf, sJsonUri);
  
      sNcpThreadMutex->unlock();
  
      AppendResult(error, aContext, aRequest);
      return 0;
  }
  
  int UbusServer::UbusMgmtset(struct ubus_context *     aContext,
                              struct ubus_object *      aObj,
                              struct ubus_request_data *aRequest,
                              const char *              aMethod,
                              struct blob_attr *        aMsg)
  {
      OT_UNUSED_VARIABLE(aObj);
      OT_UNUSED_VARIABLE(aMethod);
      OT_UNUSED_VARIABLE(aMsg);
  
      otError              error = OT_ERROR_NONE;
      struct blob_attr *   tb[MGMTSET_MAX];
      otOperationalDataset dataset;
      uint8_t              tlvs[128];
      long                 value;
      int                  length = 0;
  
      SuccessOrExit(error = otDatasetGetActive(mController->GetInstance(), &dataset));
  
      blobmsg_parse(mgmtsetPolicy, MGMTSET_MAX, tb, blob_data(aMsg), blob_len(aMsg));
      if (tb[NETWORKKEY] != nullptr)
      {
          dataset.mComponents.mIsNetworkKeyPresent = true;
          VerifyOrExit((length = Hex2Bin(blobmsg_get_string(tb[NETWORKKEY]), dataset.mNetworkKey.m8,
                                         sizeof(dataset.mNetworkKey.m8))) == OT_NETWORK_KEY_SIZE,
                       error = OT_ERROR_PARSE);
          length = 0;
      }
      if (tb[NETWORKNAME] != nullptr)
      {
          dataset.mComponents.mIsNetworkNamePresent = true;
          VerifyOrExit((length = static_cast<int>(strlen(blobmsg_get_string(tb[NETWORKNAME])))) <=
                           OT_NETWORK_NAME_MAX_SIZE,
                       error = OT_ERROR_PARSE);
          memset(&dataset.mNetworkName, 0, sizeof(dataset.mNetworkName));
          memcpy(dataset.mNetworkName.m8, blobmsg_get_string(tb[NETWORKNAME]), static_cast<size_t>(length));
          length = 0;
      }
      if (tb[EXTPANID] != nullptr)
      {
          dataset.mComponents.mIsExtendedPanIdPresent = true;
          VerifyOrExit(Hex2Bin(blobmsg_get_string(tb[EXTPANID]), dataset.mExtendedPanId.m8,
                               sizeof(dataset.mExtendedPanId.m8)) >= 0,
                       error = OT_ERROR_PARSE);
      }
      if (tb[PANID] != nullptr)
      {
          dataset.mComponents.mIsPanIdPresent = true;
          SuccessOrExit(error = ParseLong(blobmsg_get_string(tb[PANID]), value));
          dataset.mPanId = static_cast<otPanId>(value);
      }
      if (tb[CHANNEL] != nullptr)
      {
          dataset.mComponents.mIsChannelPresent = true;
          SuccessOrExit(error = ParseLong(blobmsg_get_string(tb[CHANNEL]), value));
          dataset.mChannel = static_cast<uint16_t>(value);
      }
      if (tb[PSKC] != nullptr)
      {
          dataset.mComponents.mIsPskcPresent = true;
          VerifyOrExit((length = Hex2Bin(blobmsg_get_string(tb[PSKC]), dataset.mPskc.m8, sizeof(dataset.mPskc.m8))) ==
                           OT_PSKC_MAX_SIZE,
                       error = OT_ERROR_PARSE);
          length = 0;
      }
      dataset.mActiveTimestamp++;
      if (otCommissionerGetState(mController->GetInstance()) == OT_COMMISSIONER_STATE_DISABLED)
      {
          otCommissionerStop(mController->GetInstance());
      }
      SuccessOrExit(error = otDatasetSendMgmtActiveSet(mController->GetInstance(), &dataset, tlvs,
                                                       static_cast<uint8_t>(length), /* aCallback */ nullptr,
                                                       /* aContext */ nullptr));
  exit:
      AppendResult(error, aContext, aRequest);
      return 0;
  }
  
  int UbusServer::UbusCommissioner(struct ubus_context *     aContext,
                                   struct ubus_object *      aObj,
                                   struct ubus_request_data *aRequest,
                                   const char *              aMethod,
                                   struct blob_attr *        aMsg,
                                   const char *              aAction)
  {
      OT_UNUSED_VARIABLE(aObj);
      OT_UNUSED_VARIABLE(aMethod);
      OT_UNUSED_VARIABLE(aMsg);
  
      otError error = OT_ERROR_NONE;
  
      sNcpThreadMutex->lock();
  
      if (!strcmp(aAction, "start"))
      {
          if (otCommissionerGetState(mController->GetInstance()) == OT_COMMISSIONER_STATE_DISABLED)
          {
              error = otCommissionerStart(mController->GetInstance(), &UbusServer::HandleStateChanged,
                                          &UbusServer::HandleJoinerEvent, this);
          }
      }
      else if (!strcmp(aAction, "joineradd"))
      {
          struct blob_attr *  tb[ADD_JOINER_MAX];
          otExtAddress        addr;
          const otExtAddress *addrPtr = nullptr;
          char *              pskd    = nullptr;
  
          blobmsg_parse(addJoinerPolicy, ADD_JOINER_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[PSKD] != nullptr)
          {
              pskd = blobmsg_get_string(tb[PSKD]);
          }
          if (tb[EUI64] != nullptr)
          {
              if (!strcmp(blobmsg_get_string(tb[EUI64]), "*"))
              {
                  addrPtr = nullptr;
                  memset(&addr, 0, sizeof(addr));
              }
              else
              {
                  VerifyOrExit(Hex2Bin(blobmsg_get_string(tb[EUI64]), addr.m8, sizeof(addr)) == sizeof(addr),
                               error = OT_ERROR_PARSE);
                  addrPtr = &addr;
              }
          }
  
          unsigned long timeout = kDefaultJoinerTimeout;
          SuccessOrExit(
              error = otCommissionerAddJoiner(mController->GetInstance(), addrPtr, pskd, static_cast<uint32_t>(timeout)));
      }
      else if (!strcmp(aAction, "joinerremove"))
      {
          struct blob_attr *  tb[SET_NETWORK_MAX];
          otExtAddress        addr;
          const otExtAddress *addrPtr = nullptr;
  
          blobmsg_parse(removeJoinerPolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              if (strcmp(blobmsg_get_string(tb[SETNETWORK]), "*") == 0)
              {
                  addrPtr = nullptr;
              }
              else
              {
                  VerifyOrExit(Hex2Bin(blobmsg_get_string(tb[SETNETWORK]), addr.m8, sizeof(addr)) == sizeof(addr),
                               error = OT_ERROR_PARSE);
                  addrPtr = &addr;
              }
          }
  
          SuccessOrExit(error = otCommissionerRemoveJoiner(mController->GetInstance(), addrPtr));
      }
  
  exit:
      sNcpThreadMutex->unlock();
      blob_buf_init(&mBuf, 0);
      AppendResult(error, aContext, aRequest);
      return 0;
  }
  
  void UbusServer::HandleStateChanged(otCommissionerState aState, void *aContext)
  {
      static_cast<UbusServer *>(aContext)->HandleStateChanged(aState);
  }
  
  void UbusServer::HandleStateChanged(otCommissionerState aState)
  {
      switch (aState)
      {
      case OT_COMMISSIONER_STATE_DISABLED:
          otbrLogInfo("Commissioner state disabled");
          break;
      case OT_COMMISSIONER_STATE_ACTIVE:
          otbrLogInfo("Commissioner state active");
          break;
      case OT_COMMISSIONER_STATE_PETITION:
          otbrLogInfo("Commissioner state petition");
          break;
      }
  }
  
  void UbusServer::HandleJoinerEvent(otCommissionerJoinerEvent aEvent,
                                     const otJoinerInfo *      aJoinerInfo,
                                     const otExtAddress *      aJoinerId,
                                     void *                    aContext)
  {
      static_cast<UbusServer *>(aContext)->HandleJoinerEvent(aEvent, aJoinerInfo, aJoinerId);
  }
  
  void UbusServer::HandleJoinerEvent(otCommissionerJoinerEvent aEvent,
                                     const otJoinerInfo *      aJoinerInfo,
                                     const otExtAddress *      aJoinerId)
  {
      OT_UNUSED_VARIABLE(aJoinerInfo);
      OT_UNUSED_VARIABLE(aJoinerId);
  
      switch (aEvent)
      {
      case OT_COMMISSIONER_JOINER_START:
          otbrLogInfo("Joiner start");
          break;
      case OT_COMMISSIONER_JOINER_CONNECTED:
          otbrLogInfo("Joiner connected");
          break;
      case OT_COMMISSIONER_JOINER_FINALIZE:
          otbrLogInfo("Joiner finalize");
          break;
      case OT_COMMISSIONER_JOINER_END:
          otbrLogInfo("Joiner end");
          break;
      case OT_COMMISSIONER_JOINER_REMOVED:
          otbrLogInfo("Joiner remove");
          break;
      }
  }
  
  int UbusServer::UbusGetInformation(struct ubus_context *     aContext,
                                     struct ubus_object *      aObj,
                                     struct ubus_request_data *aRequest,
                                     const char *              aMethod,
                                     struct blob_attr *        aMsg,
                                     const char *              aAction)
  {
      OT_UNUSED_VARIABLE(aObj);
      OT_UNUSED_VARIABLE(aMethod);
      OT_UNUSED_VARIABLE(aMsg);
  
      otError error = OT_ERROR_NONE;
  
      blob_buf_init(&mBuf, 0);
  
      sNcpThreadMutex->lock();
      if (!strcmp(aAction, "networkname"))
          blobmsg_add_string(&mBuf, "NetworkName", otThreadGetNetworkName(mController->GetInstance()));
      else if (!strcmp(aAction, "state"))
      {
          char state[10];
          GetState(mController->GetInstance(), state);
          blobmsg_add_string(&mBuf, "State", state);
      }
      else if (!strcmp(aAction, "channel"))
          blobmsg_add_u32(&mBuf, "Channel", otLinkGetChannel(mController->GetInstance()));
      else if (!strcmp(aAction, "panid"))
      {
          char panIdString[PANID_LENGTH];
          sprintf(panIdString, "0x%04x", otLinkGetPanId(mController->GetInstance()));
          blobmsg_add_string(&mBuf, "PanId", panIdString);
      }
      else if (!strcmp(aAction, "rloc16"))
      {
          char rloc[PANID_LENGTH];
          sprintf(rloc, "0x%04x", otThreadGetRloc16(mController->GetInstance()));
          blobmsg_add_string(&mBuf, "rloc16", rloc);
      }
      else if (!strcmp(aAction, "networkkey"))
      {
          char           outputKey[NETWORKKEY_LENGTH] = "";
          const uint8_t *key = reinterpret_cast<const uint8_t *>(otThreadGetNetworkKey(mController->GetInstance()));
          OutputBytes(key, OT_NETWORK_KEY_SIZE, outputKey);
          blobmsg_add_string(&mBuf, "Networkkey", outputKey);
      }
      else if (!strcmp(aAction, "pskc"))
      {
          char          outputPskc[NETWORKKEY_LENGTH] = "";
          const otPskc *pskc                          = otThreadGetPskc(mController->GetInstance());
          OutputBytes(pskc->m8, OT_NETWORK_KEY_SIZE, outputPskc);
          blobmsg_add_string(&mBuf, "pskc", outputPskc);
      }
      else if (!strcmp(aAction, "extpanid"))
      {
          char           outputExtPanId[XPANID_LENGTH] = "";
          const uint8_t *extPanId =
              reinterpret_cast<const uint8_t *>(otThreadGetExtendedPanId(mController->GetInstance()));
          OutputBytes(extPanId, OT_EXT_PAN_ID_SIZE, outputExtPanId);
          blobmsg_add_string(&mBuf, "ExtPanId", outputExtPanId);
      }
      else if (!strcmp(aAction, "mode"))
      {
          otLinkModeConfig linkMode;
          char             mode[5] = "";
  
          memset(&linkMode, 0, sizeof(otLinkModeConfig));
  
          linkMode = otThreadGetLinkMode(mController->GetInstance());
  
          if (linkMode.mRxOnWhenIdle)
          {
              strcat(mode, "r");
          }
  
          if (linkMode.mDeviceType)
          {
              strcat(mode, "d");
          }
  
          if (linkMode.mNetworkData)
          {
              strcat(mode, "n");
          }
          blobmsg_add_string(&mBuf, "Mode", mode);
      }
      else if (!strcmp(aAction, "partitionid"))
      {
          blobmsg_add_u32(&mBuf, "Partitionid", otThreadGetPartitionId(mController->GetInstance()));
      }
      else if (!strcmp(aAction, "leaderdata"))
      {
          otLeaderData leaderData;
  
          SuccessOrExit(error = otThreadGetLeaderData(mController->GetInstance(), &leaderData));
  
          sJsonUri = blobmsg_open_table(&mBuf, "leaderdata");
  
          blobmsg_add_u32(&mBuf, "PartitionId", leaderData.mPartitionId);
          blobmsg_add_u32(&mBuf, "Weighting", leaderData.mWeighting);
          blobmsg_add_u32(&mBuf, "DataVersion", leaderData.mDataVersion);
          blobmsg_add_u32(&mBuf, "StableDataVersion", leaderData.mStableDataVersion);
          blobmsg_add_u32(&mBuf, "LeaderRouterId", leaderData.mLeaderRouterId);
  
          blobmsg_close_table(&mBuf, sJsonUri);
      }
      else if (!strcmp(aAction, "networkdata"))
      {
          ubus_send_reply(aContext, aRequest, mNetworkdataBuf.head);
          if (time(nullptr) - mSecond > 10)
          {
              struct otIp6Address address;
              uint8_t             tlvTypes[OT_NETWORK_DIAGNOSTIC_TYPELIST_MAX_ENTRIES];
              uint8_t             count             = 0;
              char                multicastAddr[10] = "ff03::2";
  
              blob_buf_init(&mNetworkdataBuf, 0);
  
              SuccessOrExit(error = otIp6AddressFromString(multicastAddr, &address));
  
              tlvTypes[count++] = static_cast<uint8_t>(OT_NETWORK_DIAGNOSTIC_TLV_ROUTE);
              tlvTypes[count++] = static_cast<uint8_t>(OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE);
  
              sBufNum = 0;
              otThreadSendDiagnosticGet(mController->GetInstance(), &address, tlvTypes, count,
                                        &UbusServer::HandleDiagnosticGetResponse, this);
              mSecond = time(nullptr);
          }
          goto exit;
      }
      else if (!strcmp(aAction, "joinernum"))
      {
          void *       jsonTable = nullptr;
          void *       jsonArray = nullptr;
          otJoinerInfo joinerInfo;
          uint16_t     iterator        = 0;
          int          joinerNum       = 0;
          char         eui64[EXTPANID] = "";
  
          blob_buf_init(&mBuf, 0);
  
          jsonArray = blobmsg_open_array(&mBuf, "joinerList");
          while (otCommissionerGetNextJoinerInfo(mController->GetInstance(), &iterator, &joinerInfo) == OT_ERROR_NONE)
          {
              memset(eui64, 0, sizeof(eui64));
  
              jsonTable = blobmsg_open_table(&mBuf, nullptr);
  
              blobmsg_add_string(&mBuf, "pskd", joinerInfo.mPskd.m8);
  
              switch (joinerInfo.mType)
              {
              case OT_JOINER_INFO_TYPE_ANY:
                  blobmsg_add_u16(&mBuf, "isAny", 1);
                  break;
              case OT_JOINER_INFO_TYPE_EUI64:
                  blobmsg_add_u16(&mBuf, "isAny", 0);
                  OutputBytes(joinerInfo.mSharedId.mEui64.m8, sizeof(joinerInfo.mSharedId.mEui64.m8), eui64);
                  blobmsg_add_string(&mBuf, "eui64", eui64);
                  break;
              case OT_JOINER_INFO_TYPE_DISCERNER:
                  blobmsg_add_u16(&mBuf, "isAny", 0);
                  blobmsg_add_u64(&mBuf, "discernerValue", joinerInfo.mSharedId.mDiscerner.mValue);
                  blobmsg_add_u16(&mBuf, "discernerLength", joinerInfo.mSharedId.mDiscerner.mLength);
                  break;
              }
  
              blobmsg_close_table(&mBuf, jsonTable);
  
              joinerNum++;
          }
          blobmsg_close_array(&mBuf, jsonArray);
  
          blobmsg_add_u32(&mBuf, "joinernum", joinerNum);
      }
      else if (!strcmp(aAction, "macfilterstate"))
      {
          otMacFilterAddressMode mode = otLinkFilterGetAddressMode(mController->GetInstance());
  
          blob_buf_init(&mBuf, 0);
  
          if (mode == OT_MAC_FILTER_ADDRESS_MODE_DISABLED)
          {
              blobmsg_add_string(&mBuf, "state", "disable");
          }
          else if (mode == OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST)
          {
              blobmsg_add_string(&mBuf, "state", "allowlist");
          }
          else if (mode == OT_MAC_FILTER_ADDRESS_MODE_DENYLIST)
          {
              blobmsg_add_string(&mBuf, "state", "denylist");
          }
          else
          {
              blobmsg_add_string(&mBuf, "state", "error");
          }
      }
      else if (!strcmp(aAction, "macfilteraddr"))
      {
          otMacFilterEntry    entry;
          otMacFilterIterator iterator = OT_MAC_FILTER_ITERATOR_INIT;
  
          blob_buf_init(&mBuf, 0);
  
          sJsonUri = blobmsg_open_array(&mBuf, "addrlist");
  
          while (otLinkFilterGetNextAddress(mController->GetInstance(), &iterator, &entry) == OT_ERROR_NONE)
          {
              char extAddress[XPANID_LENGTH] = "";
              OutputBytes(entry.mExtAddress.m8, sizeof(entry.mExtAddress.m8), extAddress);
              blobmsg_add_string(&mBuf, "addr", extAddress);
          }
  
          blobmsg_close_array(&mBuf, sJsonUri);
      }
      else
      {
          perror("invalid argument in get information ubus\n");
      }
  
      AppendResult(error, aContext, aRequest);
  exit:
      sNcpThreadMutex->unlock();
      return 0;
  }
  
  void UbusServer::HandleDiagnosticGetResponse(otError              aError,
                                               otMessage *          aMessage,
                                               const otMessageInfo *aMessageInfo,
                                               void *               aContext)
  {
      static_cast<UbusServer *>(aContext)->HandleDiagnosticGetResponse(aError, aMessage, aMessageInfo);
  }
  
  static bool IsRoutingLocator(const otIp6Address *aAddress)
  {
      enum
      {
          kAloc16Mask            = 0xfc, ///< The mask for Aloc16.
          kRloc16ReservedBitMask = 0x02, ///< The mask for the reserved bit of Rloc16.
      };
  
      return (aAddress->mFields.m32[2] == htonl(0x000000ff) && aAddress->mFields.m16[6] == htons(0xfe00) &&
              aAddress->mFields.m8[14] < kAloc16Mask && (aAddress->mFields.m8[14] & kRloc16ReservedBitMask) == 0);
  }
  
  void UbusServer::HandleDiagnosticGetResponse(otError aError, otMessage *aMessage, const otMessageInfo *aMessageInfo)
  {
      uint16_t              rloc16;
      uint16_t              sockRloc16 = 0;
      void *                jsonArray  = nullptr;
      void *                jsonItem   = nullptr;
      char                  xrloc[10];
      otNetworkDiagTlv      diagTlv;
      otNetworkDiagIterator iterator = OT_NETWORK_DIAGNOSTIC_ITERATOR_INIT;
  
      SuccessOrExit(aError);
  
      char networkdata[20];
      sprintf(networkdata, "networkdata%d", sBufNum);
      sJsonUri = blobmsg_open_table(&mNetworkdataBuf, networkdata);
      sBufNum++;
  
      if (IsRoutingLocator(&aMessageInfo->mSockAddr))
      {
          sockRloc16 = ntohs(aMessageInfo->mPeerAddr.mFields.m16[7]);
          sprintf(xrloc, "0x%04x", sockRloc16);
          blobmsg_add_string(&mNetworkdataBuf, "rloc", xrloc);
      }
  
      while (otThreadGetNextDiagnosticTlv(aMessage, &iterator, &diagTlv) == OT_ERROR_NONE)
      {
          switch (diagTlv.mType)
          {
          case OT_NETWORK_DIAGNOSTIC_TLV_ROUTE:
          {
              const otNetworkDiagRoute &route = diagTlv.mData.mRoute;
  
              jsonArray = blobmsg_open_array(&mNetworkdataBuf, "routedata");
  
              for (uint16_t i = 0; i < route.mRouteCount; ++i)
              {
                  uint8_t in, out;
                  in  = route.mRouteData[i].mLinkQualityIn;
                  out = route.mRouteData[i].mLinkQualityOut;
                  if (in != 0 && out != 0)
                  {
                      jsonItem = blobmsg_open_table(&mNetworkdataBuf, "router");
                      rloc16   = route.mRouteData[i].mRouterId << 10;
                      blobmsg_add_u32(&mNetworkdataBuf, "routerid", route.mRouteData[i].mRouterId);
                      sprintf(xrloc, "0x%04x", rloc16);
                      blobmsg_add_string(&mNetworkdataBuf, "rloc", xrloc);
                      blobmsg_close_table(&mNetworkdataBuf, jsonItem);
                  }
              }
              blobmsg_close_array(&mNetworkdataBuf, jsonArray);
              break;
          }
  
          case OT_NETWORK_DIAGNOSTIC_TLV_CHILD_TABLE:
          {
              jsonArray = blobmsg_open_array(&mNetworkdataBuf, "childdata");
              for (uint16_t i = 0; i < diagTlv.mData.mChildTable.mCount; ++i)
              {
                  enum
                  {
                      kModeRxOnWhenIdle     = 1 << 3, ///< If the device has its receiver on when not transmitting.
                      kModeFullThreadDevice = 1 << 1, ///< If the device is an FTD.
                      kModeFullNetworkData  = 1 << 0, ///< If the device requires the full Network Data.
                  };
                  const otNetworkDiagChildEntry &entry = diagTlv.mData.mChildTable.mTable[i];
  
                  uint8_t mode = 0;
  
                  jsonItem = blobmsg_open_table(&mNetworkdataBuf, "child");
                  sprintf(xrloc, "0x%04x", (sockRloc16 | entry.mChildId));
                  blobmsg_add_string(&mNetworkdataBuf, "rloc", xrloc);
  
                  mode = (entry.mMode.mRxOnWhenIdle ? kModeRxOnWhenIdle : 0) |
                         (entry.mMode.mDeviceType ? kModeFullThreadDevice : 0) |
                         (entry.mMode.mNetworkData ? kModeFullNetworkData : 0);
                  blobmsg_add_u16(&mNetworkdataBuf, "mode", mode);
                  blobmsg_close_table(&mNetworkdataBuf, jsonItem);
              }
              blobmsg_close_array(&mNetworkdataBuf, jsonArray);
              break;
          }
  
          default:
              // Ignore other network diagnostics data.
              break;
          }
      }
  
      blobmsg_close_table(&mNetworkdataBuf, sJsonUri);
  
  exit:
      if (aError != OT_ERROR_NONE)
      {
          otbrLogWarning("Failed to receive diagnostic response: %s", otThreadErrorToString(aError));
      }
  }
  
  int UbusServer::UbusSetInformation(struct ubus_context *     aContext,
                                     struct ubus_object *      aObj,
                                     struct ubus_request_data *aRequest,
                                     const char *              aMethod,
                                     struct blob_attr *        aMsg,
                                     const char *              aAction)
  {
      OT_UNUSED_VARIABLE(aObj);
      OT_UNUSED_VARIABLE(aMethod);
      OT_UNUSED_VARIABLE(aMsg);
  
      otError error = OT_ERROR_NONE;
  
      blob_buf_init(&mBuf, 0);
  
      sNcpThreadMutex->lock();
      if (!strcmp(aAction, "networkname"))
      {
          struct blob_attr *tb[SET_NETWORK_MAX];
  
          blobmsg_parse(setNetworknamePolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              char *newName = blobmsg_get_string(tb[SETNETWORK]);
              SuccessOrExit(error = otThreadSetNetworkName(mController->GetInstance(), newName));
          }
      }
      else if (!strcmp(aAction, "channel"))
      {
          struct blob_attr *tb[SET_NETWORK_MAX];
  
          blobmsg_parse(setChannelPolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              uint32_t channel = blobmsg_get_u32(tb[SETNETWORK]);
              SuccessOrExit(error = otLinkSetChannel(mController->GetInstance(), static_cast<uint8_t>(channel)));
          }
      }
      else if (!strcmp(aAction, "panid"))
      {
          struct blob_attr *tb[SET_NETWORK_MAX];
  
          blobmsg_parse(setPanIdPolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              long  value;
              char *panid = blobmsg_get_string(tb[SETNETWORK]);
              SuccessOrExit(error = ParseLong(panid, value));
              error = otLinkSetPanId(mController->GetInstance(), static_cast<otPanId>(value));
          }
      }
      else if (!strcmp(aAction, "networkkey"))
      {
          struct blob_attr *tb[SET_NETWORK_MAX];
  
          blobmsg_parse(setNetworkkeyPolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              otNetworkKey key;
              char *       networkkey = blobmsg_get_string(tb[SETNETWORK]);
  
              VerifyOrExit(Hex2Bin(networkkey, key.m8, sizeof(key.m8)) == OT_NETWORK_KEY_SIZE, error = OT_ERROR_PARSE);
              SuccessOrExit(error = otThreadSetNetworkKey(mController->GetInstance(), &key));
          }
      }
      else if (!strcmp(aAction, "pskc"))
      {
          struct blob_attr *tb[SET_NETWORK_MAX];
  
          blobmsg_parse(setPskcPolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              otPskc pskc;
  
              VerifyOrExit(Hex2Bin(blobmsg_get_string(tb[SETNETWORK]), pskc.m8, sizeof(pskc)) == OT_PSKC_MAX_SIZE,
                           error = OT_ERROR_PARSE);
              SuccessOrExit(error = otThreadSetPskc(mController->GetInstance(), &pskc));
          }
      }
      else if (!strcmp(aAction, "extpanid"))
      {
          struct blob_attr *tb[SET_NETWORK_MAX];
  
          blobmsg_parse(setExtPanIdPolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              otExtendedPanId extPanId;
              char *          input = blobmsg_get_string(tb[SETNETWORK]);
              VerifyOrExit(Hex2Bin(input, extPanId.m8, sizeof(extPanId)) >= 0, error = OT_ERROR_PARSE);
              error = otThreadSetExtendedPanId(mController->GetInstance(), &extPanId);
          }
      }
      else if (!strcmp(aAction, "mode"))
      {
          otLinkModeConfig  linkMode;
          struct blob_attr *tb[SET_NETWORK_MAX];
  
          blobmsg_parse(setModePolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              char *inputMode = blobmsg_get_string(tb[SETNETWORK]);
              for (char *ch = inputMode; *ch != '\0'; ch++)
              {
                  switch (*ch)
                  {
                  case 'r':
                      linkMode.mRxOnWhenIdle = 1;
                      break;
  
                  case 'd':
                      linkMode.mDeviceType = 1;
                      break;
  
                  case 'n':
                      linkMode.mNetworkData = 1;
                      break;
  
                  default:
                      ExitNow(error = OT_ERROR_PARSE);
                  }
              }
  
              SuccessOrExit(error = otThreadSetLinkMode(mController->GetInstance(), linkMode));
          }
      }
      else if (!strcmp(aAction, "macfilteradd"))
      {
          struct blob_attr *tb[SET_NETWORK_MAX];
          otExtAddress      extAddr;
  
          blobmsg_parse(macfilterAddPolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              char *addr = blobmsg_get_string(tb[SETNETWORK]);
  
              VerifyOrExit(Hex2Bin(addr, extAddr.m8, OT_EXT_ADDRESS_SIZE) == OT_EXT_ADDRESS_SIZE, error = OT_ERROR_PARSE);
  
              error = otLinkFilterAddAddress(mController->GetInstance(), &extAddr);
  
              VerifyOrExit(error == OT_ERROR_NONE || error == OT_ERROR_ALREADY);
          }
      }
      else if (!strcmp(aAction, "macfilterremove"))
      {
          struct blob_attr *tb[SET_NETWORK_MAX];
          otExtAddress      extAddr;
  
          blobmsg_parse(macfilterRemovePolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              char *addr = blobmsg_get_string(tb[SETNETWORK]);
              VerifyOrExit(Hex2Bin(addr, extAddr.m8, OT_EXT_ADDRESS_SIZE) == OT_EXT_ADDRESS_SIZE, error = OT_ERROR_PARSE);
  
              otLinkFilterRemoveAddress(mController->GetInstance(), &extAddr);
          }
      }
      else if (!strcmp(aAction, "macfiltersetstate"))
      {
          struct blob_attr *tb[SET_NETWORK_MAX];
  
          blobmsg_parse(macfilterSetStatePolicy, SET_NETWORK_MAX, tb, blob_data(aMsg), blob_len(aMsg));
          if (tb[SETNETWORK] != nullptr)
          {
              char *state = blobmsg_get_string(tb[SETNETWORK]);
  
              if (strcmp(state, "disable") == 0)
              {
                  otLinkFilterSetAddressMode(mController->GetInstance(), OT_MAC_FILTER_ADDRESS_MODE_DISABLED);
              }
              else if (strcmp(state, "allowlist") == 0)
              {
                  otLinkFilterSetAddressMode(mController->GetInstance(), OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST);
              }
              else if (strcmp(state, "denylist") == 0)
              {
                  otLinkFilterSetAddressMode(mController->GetInstance(), OT_MAC_FILTER_ADDRESS_MODE_DENYLIST);
              }
          }
      }
      else if (!strcmp(aAction, "macfilterclear"))
      {
          otLinkFilterClearAddresses(mController->GetInstance());
      }
      else
      {
          perror("invalid argument in get information ubus\n");
      }
  
  exit:
      sNcpThreadMutex->unlock();
      AppendResult(error, aContext, aRequest);
      return 0;
  }
  
  void UbusServer::GetState(otInstance *aInstance, char *aState)
  {
      switch (otThreadGetDeviceRole(aInstance))
      {
      case OT_DEVICE_ROLE_DISABLED:
          strcpy(aState, "disabled");
          break;
  
      case OT_DEVICE_ROLE_DETACHED:
          strcpy(aState, "detached");
          break;
  
      case OT_DEVICE_ROLE_CHILD:
          strcpy(aState, "child");
          break;
  
      case OT_DEVICE_ROLE_ROUTER:
          strcpy(aState, "router");
          break;
  
      case OT_DEVICE_ROLE_LEADER:
          strcpy(aState, "leader");
          break;
      default:
          strcpy(aState, "invalid aState");
          break;
      }
  }
  
  void UbusServer::UbusAddFd()
  {
      // ubus library function
      ubus_add_uloop(mContext);
  
  #ifdef FD_CLOEXEC
      fcntl(mContext->sock.fd, F_SETFD, fcntl(mContext->sock.fd, F_GETFD) | FD_CLOEXEC);
  #endif
  }
  
  void UbusServer::UbusReconnTimer(struct uloop_timeout *aTimeout)
  {
      GetInstance().UbusReconnTimerDetail(aTimeout);
  }
  
  void UbusServer::UbusReconnTimerDetail(struct uloop_timeout *aTimeout)
  {
      OT_UNUSED_VARIABLE(aTimeout);
  
      static struct uloop_timeout retry = {
          list : {},
          pending : false,
          cb : UbusReconnTimer,
          time : {},
      };
      int time = 2;
  
      if (ubus_reconnect(mContext, mSockPath) != 0)
      {
          uloop_timeout_set(&retry, time * 1000);
          return;
      }
  
      UbusAddFd();
  }
  
  void UbusServer::UbusConnectionLost(struct ubus_context *aContext)
  {
      OT_UNUSED_VARIABLE(aContext);
  
      UbusReconnTimer(nullptr);
  }
  
  int UbusServer::DisplayUbusInit(const char *aPath)
  {
      uloop_init();
      signal(SIGPIPE, SIG_IGN);
  
      mSockPath = aPath;
  
      mContext = ubus_connect(aPath);
      if (!mContext)
      {
          otbrLogErr("Ubus connect failed");
          return -1;
      }
  
      otbrLogInfo("Connected as %08x\n", mContext->local_id);
      mContext->connection_lost = UbusConnectionLost;
  
      /* file description */
      UbusAddFd();
  
      /* Add a object */
      if (ubus_add_object(mContext, &otbr) != 0)
      {
          otbrLogErr("Ubus add obj failed");
          return -1;
      }
  
      return 0;
  }
  
  void UbusServer::DisplayUbusDone(void)
  {
      if (mContext)
      {
          ubus_free(mContext);
          mContext = nullptr;
      }
  }
  
  void UbusServer::InstallUbusObject(void)
  {
      char *path = nullptr;
  
      if (-1 == DisplayUbusInit(path))
      {
          otbrLogErr("Ubus connect failed");
          return;
      }
  
      otbrLogInfo("Uloop run");
      uloop_run();
  
      DisplayUbusDone();
  
      uloop_done();
  }
  
  otError UbusServer::ParseLong(char *aString, long &aLong)
  {
      char *endptr;
      aLong = strtol(aString, &endptr, 0);
      return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_PARSE;
  }
  
  int UbusServer::Hex2Bin(const char *aHex, uint8_t *aBin, uint16_t aBinLength)
  {
      size_t      hexLength = strlen(aHex);
      const char *hexEnd    = aHex + hexLength;
      uint8_t *   cur       = aBin;
      uint8_t     numChars  = hexLength & 1;
      uint8_t     byte      = 0;
      int         rval;
  
      VerifyOrExit((hexLength + 1) / 2 <= aBinLength, rval = -1);
  
      while (aHex < hexEnd)
      {
          if ('A' <= *aHex && *aHex <= 'F')
          {
              byte |= 10 + (*aHex - 'A');
          }
          else if ('a' <= *aHex && *aHex <= 'f')
          {
              byte |= 10 + (*aHex - 'a');
          }
          else if ('0' <= *aHex && *aHex <= '9')
          {
              byte |= *aHex - '0';
          }
          else
          {
              ExitNow(rval = -1);
          }
  
          aHex++;
          numChars++;
  
          if (numChars >= 2)
          {
              numChars = 0;
              *cur++   = byte;
              byte     = 0;
          }
          else
          {
              byte <<= 4;
          }
      }
  
      rval = static_cast<int>(cur - aBin);
  
  exit:
      return rval;
  }
  
  } // namespace ubus
  } // namespace otbr
  
  void UbusServerInit(otbr::Ncp::ControllerOpenThread *aController, std::mutex *aNcpThreadMutex)
  {
      otbr::ubus::sNcpThreadMutex = aNcpThreadMutex;
      otbr::ubus::sUbusEfd        = eventfd(0, 0);
  
      otbr::ubus::UbusServer::Initialize(aController);
  
      if (otbr::ubus::sUbusEfd == -1)
      {
          perror("Failed to create eventfd for ubus");
          exit(EXIT_FAILURE);
      }
  }
  
  void UbusServerRun(void)
  {
      otbr::ubus::UbusServer::GetInstance().InstallUbusObject();
  }
  
  void UbusUpdateFdSet(fd_set &aReadFdSet, int &aMaxFd)
  {
      VerifyOrExit(otbr::ubus::sUbusEfd != -1);
  
      FD_SET(otbr::ubus::sUbusEfd, &aReadFdSet);
  
      if (aMaxFd < otbr::ubus::sUbusEfd)
      {
          aMaxFd = otbr::ubus::sUbusEfd;
      }
  
  exit:
      return;
  }
  
  void UbusProcess(const fd_set &aReadFdSet)
  {
      ssize_t  retval;
      uint64_t num;
  
      VerifyOrExit(otbr::ubus::sUbusEfd != -1);
  
      if (FD_ISSET(otbr::ubus::sUbusEfd, &aReadFdSet))
      {
          retval = read(otbr::ubus::sUbusEfd, &num, sizeof(uint64_t));
          if (retval != sizeof(uint64_t))
          {
              perror("read ubus eventfd failed\n");
              exit(EXIT_FAILURE);
          }
          goto exit;
      }
  
  exit:
      return;
  }
  