14-Openwrt ubus

Ubus is the inter-process communication mechanism in OpenWrt. ubusd implements the server, and other processes implement the client, such as ubus (cli), netifd, and procd. The communication between the two clients needs to be forwarded through the server.

ubus provides an interface for creating socket clients, and provides three ready-made clients for users to use directly:

  1. Client side for shell scripts.
  2. Client interface provided for lua scripts.
  3. Client interface for C language.

Here mainly explain the C language and shell

1 ubus method

ubusd has been implemented by openwrt, so we implement ubus on the client side, which is also based on uloop, so its three steps are also necessary:

void ubus_test(void)
{    
    uloop_init();
    ztest_ubus_init();
    uloop_run();
    ztest_ubus_clean();
    uloop_done();
}

Ubus calling steps ubus_connect, ubus_add_uloop, ubus_add_object

int ztest_ubus_init(void)
{
    int ret;

    g_ubus_ctx = ubus_connect(NULL);
    if (!g_ubus_ctx) {
        ULOG_ERR("Failed to connect to ubus\n");
        return -1;
    }

    g_ubus_ctx->connection_lost = ztest_ubus_connection_lost;
    ubus_add_uloop(g_ubus_ctx);

    ret = ubus_add_object(g_ubus_ctx, &ztest_object);
    if (ret)
        ULOG_ERR("Failed to add zboard object: %s\n", ubus_strerror(ret));

    return ret;
}

We mainly implement the contents of the methods in the object. Ztest_methods defines all the methods of the current ubus and the callback functions that are accessed when each method is called.

ubus abstracts the concepts of "object" and "method" for message processing on the client side. An object contains multiple methods, and the client needs to register with the server when it receives a specific json message. Objects and methods have their own names, and the sender only needs to specify the names of the objects and methods to be called in the message.

static const struct ubus_method ztest_methods[] = {
    { .name = "set_test", .handler = ztest_set_test },
    { .name = "get_test", .handler = ztest_get_test },
};

static struct ubus_object_type ztest_object_type =
    UBUS_OBJECT_TYPE("ztest", ztest_methods);

static struct ubus_object ztest_object = {
    .name = "ztest",
    .type = &ztest_object_type,
    .methods = ztest_methods,
    .n_methods = ARRAY_SIZE(ztest_methods),
};

After the methods are defined, it is the content of the actual method. At this time, the interfaces such as blob and blobmsg in the libubox are used to encapsulate and parse the data format.

ubus defines the message format for communication between client and server: both client and server must encapsulate the message into json message format

enum {
    ZTEST_STATUS,
    ZTEST_PORT,
    ZTEST_MAX
};

static const struct blobmsg_policy zboard_policy[ZTEST_MAX] = {
    [ZTEST_STATUS] = { .name = "status", .type = BLOBMSG_TYPE_INT32 },
    [ZTEST_PORT] = { .name = "port", .type = BLOBMSG_TYPE_INT32 },
};

static uint32_t g_wan_status;
static uint32_t g_wan_port;

static int ztest_set_test(struct ubus_context *ctx, struct ubus_object *obj,
                        struct ubus_request_data *req, const char *method,
                        struct blob_attr *msg)
{
    struct blob_attr *tb[ZTEST_MAX];

    blobmsg_parse(zboard_policy, ZTEST_MAX, tb, blob_data(msg), blob_len(msg));

    if (!tb[ZTEST_STATUS] || !tb[ZTEST_PORT])
        return UBUS_STATUS_INVALID_ARGUMENT;

    g_wan_status = blobmsg_get_u32(tb[ZTEST_STATUS]);
    g_wan_port = blobmsg_get_u32(tb[ZTEST_PORT]);

    return 0;
}

static int ztest_get_test(struct ubus_context *ctx, struct ubus_object *obj,
                        struct ubus_request_data *req, const char *method,
                        struct blob_attr *msg)
{
    blob_buf_init(&g_bbuf, 0);
    blobmsg_add_u32(&g_bbuf, "status", g_wan_status);
    blobmsg_add_u32(&g_bbuf, "port", g_wan_port);
    ubus_send_reply(ctx, req, g_bbuf.head);

    return 0;
}

For the testing of ubus, we generally use the shell method to test directly with the ubus tool at the command line, as follows:

root@zihome:/usr/sbin# ubus list
log
network
network.device
network.interface
network.interface.lan
network.interface.loopback
network.interface.wan
network.wireless
nlwifi.wireless
service
session
system
uci
zboard
zdetect
ztest

Get method:

root@zihome:/usr/sbin# ubus list ztest -v
'ztest' @78782e2e
        "set_test":{}
        "get_test":{}

Set the message message:

root@zihome:/usr/sbin# ubus call ztest get_test
{
        "status": 0,
        "port": 0
}
root@zihome:/usr/sbin# ubus call ztest set_test '{"status": 1, "port": 1}'
root@zihome:/usr/sbin# ubus call ztest get_test
{
        "status": 1,
        "port": 1
}

2.ubus_event

The ubus monitoring event is often used. After a process is set to listen, when another process sends out a notification event, it will enter the monitoring callback function, which is very useful for communication between processes.

static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
                          const char *type, struct blob_attr *msg)
{
    struct blob_attr *tb[ZTEST_MAX];

    ULOG_INFO("receive_event\n");
    blobmsg_parse(zboard_policy, ZTEST_MAX, tb, blob_data(msg), blob_len(msg));

    if (!tb[ZTEST_STATUS] || !tb[ZTEST_PORT])
        return;

    g_wan_status = blobmsg_get_u32(tb[ZTEST_STATUS]);
    g_wan_port = blobmsg_get_u32(tb[ZTEST_PORT]);
}

int ztest_ubus_event_init(void)
{
    int ret;
    static struct ubus_event_handler listener;
    
    memset(&listener, 0, sizeof(listener));
    listener.cb = receive_event;
    ret = ubus_register_event_handler(g_ubus_ctx, &listener, "test_event");
    if (ret){
        ULOG_ERR("ubus_register_event_handler error %s", ubus_strerror(ret));
    }
    return ret;
}

Use the shell to send events for testing

root@zihome:/usr/sbin# ubus call ztest get_test
{
        "status": 0,
        "port": 0
}
root@zihome:/usr/sbin# ubus send test_event '{"status": 1, "port": 3}'
root@zihome:/usr/sbin# ubus call ztest get_test
{
        "status": 1,
        "port": 3
}

Use the shell to listen for listen events

root@zihome:/usr/sbin# ubus listen
{ "test_event": {"status":1,"port":3} }
{ "test_event": {"status":1,"port":5} }

The code is at github: https://github.com/creatorly/TestCode/tree/master/openwrt/ztest

Reference blog:
https://blog.csdn.net/iampisfan/article/details/78107903

https://blog.csdn.net/jasonchen_gbd/article/details/45627967

https://blog.csdn.net/iampisfan/article/details/78107903
ubus is also based on this set of processes, in which ubusd implements the server, and other processes implement the client, such as ubus (cli), netifd, prodc;
two client communication needs Server forwarding.

OpenWrt source code analysis of libubox:
https://blog.csdn.net/iampisfan/article/details/78108100

OpenWrt netifd study notes:
https://blog.csdn.net/jasonchen_gbd/article/details/74990247

Published 106 original articles · praised 76 · 130,000 visits +

Guess you like

Origin blog.csdn.net/Creator_Ly/article/details/95798464