/*
 *  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 "common/logging.hpp"
#include "ncp/rcp_host.hpp"

namespace otbr {
namespace ubus {

static UbusServer *sUbusServerInstance = nullptr;
static int         sUbusEfd            = -1;
static void       *sJsonUri            = nullptr;
static int         sBufNum;

const static int PANID_LENGTH      = 10;
const static int XPANID_LENGTH     = 64;
const static int NETWORKKEY_LENGTH = 64;

UbusServer::UbusServer(Ncp::RcpHost *aHost, std::mutex *aMutex)
    : mIfFinishScan(false)
    , mContext(nullptr)
    , mSockPath(nullptr)
    , mHost(aHost)
    , mHostMutex(aMutex)
    , 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::RcpHost *aHost, std::mutex *aMutex)
{
    sUbusServerInstance = new UbusServer(aHost, aMutex);
}

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)},
    {"interfacename", &UbusServer::UbusInterfaceNameHandler, 0, 0, nullptr, 0},
};

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;

    mHostMutex->lock();
    SuccessOrExit(error = otLinkActiveScan(mHost->GetInstance(), scanChannels, scanDuration,
                                           &UbusServer::HandleActiveScanResult, this));
exit:
    mHostMutex->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_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::UbusInterfaceNameHandler(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, "interfacename");
}

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;

    mHostMutex->lock();
    otInstanceFactoryReset(mHost->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:
    mHostMutex->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"))
    {
        mHostMutex->lock();
        SuccessOrExit(error = otIp6SetEnabled(mHost->GetInstance(), true));
        SuccessOrExit(error = otThreadSetEnabled(mHost->GetInstance(), true));
    }
    else if (!strcmp(aAction, "stop"))
    {
        mHostMutex->lock();
        SuccessOrExit(error = otThreadSetEnabled(mHost->GetInstance(), false));
        SuccessOrExit(error = otIp6SetEnabled(mHost->GetInstance(), false));
    }

exit:
    mHostMutex->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);

    mHostMutex->lock();
    SuccessOrExit(error = otThreadGetParentInfo(mHost->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:
    mHostMutex->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");

    mHostMutex->lock();
    while (otThreadGetNextNeighborInfo(mHost->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);

    mHostMutex->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(mHost->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.mSeconds++;
    if (otCommissionerGetState(mHost->GetInstance()) == OT_COMMISSIONER_STATE_DISABLED)
    {
        otCommissionerStop(mHost->GetInstance());
    }
    SuccessOrExit(error = otDatasetSendMgmtActiveSet(mHost->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;

    mHostMutex->lock();

    if (!strcmp(aAction, "start"))
    {
        if (otCommissionerGetState(mHost->GetInstance()) == OT_COMMISSIONER_STATE_DISABLED)
        {
            error = otCommissionerStart(mHost->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(mHost->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(mHost->GetInstance(), addrPtr));
    }

exit:
    mHostMutex->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);

    mHostMutex->lock();
    if (!strcmp(aAction, "networkname"))
        blobmsg_add_string(&mBuf, "NetworkName", otThreadGetNetworkName(mHost->GetInstance()));
    else if (!strcmp(aAction, "interfacename"))
    {
        blobmsg_add_string(&mBuf, "InterfaceName", mHost->GetInterfaceName());
    }
    else if (!strcmp(aAction, "state"))
    {
        char state[10];
        GetState(mHost->GetInstance(), state);
        blobmsg_add_string(&mBuf, "State", state);
    }
    else if (!strcmp(aAction, "channel"))
        blobmsg_add_u32(&mBuf, "Channel", otLinkGetChannel(mHost->GetInstance()));
    else if (!strcmp(aAction, "panid"))
    {
        char panIdString[PANID_LENGTH];
        sprintf(panIdString, "0x%04x", otLinkGetPanId(mHost->GetInstance()));
        blobmsg_add_string(&mBuf, "PanId", panIdString);
    }
    else if (!strcmp(aAction, "rloc16"))
    {
        char rloc[PANID_LENGTH];
        sprintf(rloc, "0x%04x", otThreadGetRloc16(mHost->GetInstance()));
        blobmsg_add_string(&mBuf, "rloc16", rloc);
    }
    else if (!strcmp(aAction, "networkkey"))
    {
        char         outputKey[NETWORKKEY_LENGTH] = "";
        otNetworkKey key;

        otThreadGetNetworkKey(mHost->GetInstance(), &key);
        OutputBytes(key.m8, OT_NETWORK_KEY_SIZE, outputKey);
        blobmsg_add_string(&mBuf, "Networkkey", outputKey);
    }
    else if (!strcmp(aAction, "pskc"))
    {
        char   outputPskc[NETWORKKEY_LENGTH] = "";
        otPskc pskc;

        otThreadGetPskc(mHost->GetInstance(), &pskc);
        OutputBytes(pskc.m8, OT_PSKC_MAX_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(mHost->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(mHost->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(mHost->GetInstance()));
    }
    else if (!strcmp(aAction, "leaderdata"))
    {
        otLeaderData leaderData;

        SuccessOrExit(error = otThreadGetLeaderData(mHost->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)
        {
            static constexpr uint16_t kMaxTlvs = 35;

            struct otIp6Address address;
            uint8_t             tlvTypes[kMaxTlvs];
            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(mHost->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(mHost->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(mHost->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(mHost->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:
    mHostMutex->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);

    mHostMutex->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(mHost->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(mHost->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(mHost->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(mHost->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(mHost->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(mHost->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(mHost->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(mHost->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(mHost->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(mHost->GetInstance(), OT_MAC_FILTER_ADDRESS_MODE_DISABLED);
            }
            else if (strcmp(state, "allowlist") == 0)
            {
                otLinkFilterSetAddressMode(mHost->GetInstance(), OT_MAC_FILTER_ADDRESS_MODE_ALLOWLIST);
            }
            else if (strcmp(state, "denylist") == 0)
            {
                otLinkFilterSetAddressMode(mHost->GetInstance(), OT_MAC_FILTER_ADDRESS_MODE_DENYLIST);
            }
        }
    }
    else if (!strcmp(aAction, "macfilterclear"))
    {
        otLinkFilterClearAddresses(mHost->GetInstance());
    }
    else
    {
        perror("invalid argument in get information ubus\n");
    }

exit:
    mHostMutex->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;
}

void UBusAgent::Init(void)
{
    otbr::ubus::sUbusEfd = eventfd(0, 0);

    otbr::ubus::UbusServer::Initialize(&mHost, &mThreadMutex);

    if (otbr::ubus::sUbusEfd == -1)
    {
        perror("Failed to create eventfd for ubus");
        exit(EXIT_FAILURE);
    }

    std::thread(UbusServerRun).detach();
}

void UBusAgent::Update(MainloopContext &aMainloop)
{
    VerifyOrExit(otbr::ubus::sUbusEfd != -1);

    aMainloop.AddFdToReadSet(otbr::ubus::sUbusEfd);
exit:
    mThreadMutex.unlock();
    return;
}

void UBusAgent::Process(const MainloopContext &aMainloop)
{
    ssize_t  retval;
    uint64_t num;

    mThreadMutex.lock();

    VerifyOrExit(otbr::ubus::sUbusEfd != -1);

    if (FD_ISSET(otbr::ubus::sUbusEfd, &aMainloop.mReadFdSet))
    {
        retval = read(otbr::ubus::sUbusEfd, &num, sizeof(uint64_t));
        if (retval != sizeof(uint64_t))
        {
            perror("read ubus eventfd failed\n");
            exit(EXIT_FAILURE);
        }
    }

exit:
    return;
}

} // namespace ubus
} // namespace otbr
