mirror of
https://dev.iopsys.eu/bbf/bbfdm.git
synced 2025-12-10 07:44:39 +01:00
1454 lines
39 KiB
C
1454 lines
39 KiB
C
/*
|
|
* bbfdm-ubus.c: bbfdm-ubus API to expose Data Model over ubus
|
|
*
|
|
* Copyright (C) 2023-2024 IOPSYS Software Solutions AB. All rights reserved.
|
|
*
|
|
* Author: Vivek Dutta <vivek.dutta@iopsys.eu>
|
|
* Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
|
|
*
|
|
* See LICENSE file for license related information.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <libubox/blobmsg.h>
|
|
#include <libubox/uloop.h>
|
|
#include <libubus.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include "bbfdm-ubus.h"
|
|
#include "set.h"
|
|
#include "get.h"
|
|
#include "operate.h"
|
|
#include "add_delete.h"
|
|
#include "events.h"
|
|
#include "pretty_print.h"
|
|
#include "get_helper.h"
|
|
#include "plugin.h"
|
|
|
|
#define BBFDM_DEFAULT_MICROSERVICE_INPUT_PATH "/etc/bbfdm/micro_services"
|
|
#define BBFDM_DEFAULT_MODULES_PATH "/usr/share/bbfdm"
|
|
#define BBFDM_DEFAULT_PLUGINS_PATH BBFDM_DEFAULT_MODULES_PATH"/plugins"
|
|
#define BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH BBFDM_DEFAULT_MODULES_PATH"/micro_services"
|
|
|
|
LIST_HEAD(head_registered_service);
|
|
|
|
static void run_schema_updater(struct bbfdm_context *u);
|
|
static void periodic_instance_updater(struct uloop_timeout *t);
|
|
static void bbfdm_register_instance_refresh_timer(struct ubus_context *ctx, int start_in);
|
|
static void bbfdm_cancel_instance_refresh_timer(struct ubus_context *ctx);
|
|
|
|
// Global variables
|
|
static void *deamon_lib_handle = NULL;
|
|
|
|
static void bbfdm_ctx_cleanup(struct bbfdm_context *u)
|
|
{
|
|
bbf_global_clean(DEAMON_DM_ROOT_OBJ);
|
|
|
|
free_path_list(&u->instances);
|
|
free_path_list(&u->old_instances);
|
|
free_path_list(&u->config.list_objs);
|
|
|
|
/* Main daemon */
|
|
if (dm_is_micro_service() == false) {
|
|
free_services_from_list(&head_registered_service);
|
|
}
|
|
|
|
/* DotSo Plugin */
|
|
bbfdm_free_dotso_plugin(u, &deamon_lib_handle);
|
|
|
|
/* JSON Plugin */
|
|
bbfdm_free_json_plugin();
|
|
}
|
|
|
|
static bool is_sync_operate_cmd(bbfdm_data_t *data __attribute__((unused)))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static void fill_optional_data(bbfdm_data_t *data, struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *attr;
|
|
size_t rem;
|
|
|
|
if (!data || !msg)
|
|
return;
|
|
|
|
blobmsg_for_each_attr(attr, msg, rem) {
|
|
|
|
if (is_str_eq(blobmsg_name(attr), "proto")) {
|
|
const char *val = blobmsg_get_string(attr);
|
|
data->bbf_ctx.dm_type = get_proto_type(val);
|
|
}
|
|
|
|
if (is_str_eq(blobmsg_name(attr), "format"))
|
|
data->is_raw = is_str_eq(blobmsg_get_string(attr), "raw") ? true : false;
|
|
}
|
|
|
|
char *proto = (data->bbf_ctx.dm_type == BBFDM_BOTH) ? "both" : (data->bbf_ctx.dm_type == BBFDM_CWMP) ? "cwmp" : "usp";
|
|
BBF_DEBUG("Proto:|%s|, is_raw:|%d|", proto, data->is_raw);
|
|
}
|
|
|
|
static void async_req_free(struct bbfdm_async_req *r)
|
|
{
|
|
free(r);
|
|
}
|
|
|
|
static void async_complete_cb(struct uloop_process *p, __attribute__((unused)) int ret)
|
|
{
|
|
struct bbfdm_async_req *r = container_of(p, struct bbfdm_async_req, process);
|
|
|
|
if (r) {
|
|
BBF_INFO("Async call with pid(%d) completes", r->process.pid);
|
|
struct blob_buf *bb = (struct blob_buf *)&r->result;
|
|
|
|
ubus_send_reply(r->ctx, &r->req, bb->head);
|
|
BBF_INFO("pid(%d) blob data sent raw(%zu)", r->process.pid, blob_raw_len(bb->head));
|
|
ubus_complete_deferred_request(r->ctx, &r->req, 0);
|
|
if (r->is_operate) {
|
|
bbfdm_register_instance_refresh_timer(r->ctx, 0);
|
|
}
|
|
munmap(r->result, DEF_IPC_DATA_LEN);
|
|
async_req_free(r);
|
|
}
|
|
|
|
}
|
|
|
|
static struct bbfdm_async_req *async_req_new(void)
|
|
{
|
|
struct bbfdm_async_req *r = (struct bbfdm_async_req *)calloc(1, sizeof(*r));
|
|
|
|
if (r) {
|
|
memset(&r->process, 0, sizeof(r->process));
|
|
r->result = NULL;
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static int bbfdm_start_deferred(bbfdm_data_t *data, void (*EXEC_CB)(bbfdm_data_t *data, void *d), bool is_operate)
|
|
{
|
|
struct bbfdm_async_req *r = NULL;
|
|
pid_t child;
|
|
struct bbfdm_context *u;
|
|
void *result = NULL;
|
|
|
|
result = mmap(NULL, DEF_IPC_DATA_LEN, PROT_READ| PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
|
|
if (result == MAP_FAILED) {
|
|
BBF_ERR("Error creating memory map for result");
|
|
goto err_out;
|
|
}
|
|
memset(result, 0, DEF_IPC_DATA_LEN);
|
|
r = async_req_new();
|
|
if (r == NULL) {
|
|
BBF_ERR("Error allocating async req");
|
|
goto err_out;
|
|
}
|
|
|
|
child = fork();
|
|
if (child == -1) {
|
|
BBF_ERR("fork error");
|
|
goto err_out;
|
|
} else if (child == 0) {
|
|
u = container_of(data->ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("{fork} Failed to get the bbfdm context");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/* free fd's and memory inherited from parent */
|
|
uloop_done();
|
|
ubus_shutdown(data->ctx);
|
|
async_req_free(r);
|
|
fclose(stdin);
|
|
fclose(stdout);
|
|
fclose(stderr);
|
|
|
|
BBF_INFO("{fork} Calling from subprocess");
|
|
EXEC_CB(data, result);
|
|
|
|
bbfdm_ctx_cleanup(u);
|
|
closelog();
|
|
/* write result and exit */
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
// parent
|
|
BBF_INFO("Creating bbfdm(%d) sub process(%d) for path(%s)", getpid(), child, data->bbf_ctx.in_param);
|
|
r->result = result;
|
|
r->ctx = data->ctx;
|
|
r->process.pid = child;
|
|
r->process.cb = async_complete_cb;
|
|
r->is_operate = is_operate;
|
|
uloop_process_add(&r->process);
|
|
ubus_defer_request(data->ctx, data->req, &r->req);
|
|
return 0;
|
|
|
|
err_out:
|
|
if (r)
|
|
async_req_free(r);
|
|
|
|
if (result)
|
|
munmap(result, DEF_IPC_DATA_LEN);
|
|
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
static bool is_object_schema_update_available(struct bbfdm_context *u)
|
|
{
|
|
bool ret = false;
|
|
LIST_HEAD(paths_list);
|
|
bbfdm_data_t data = {
|
|
.ctx = 0,
|
|
.req = 0,
|
|
.is_raw = true,
|
|
.plist = &paths_list,
|
|
.bbf_ctx.nextlevel = false,
|
|
.bbf_ctx.iscommand = true,
|
|
.bbf_ctx.isevent = true,
|
|
.bbf_ctx.isinfo = true,
|
|
.bbf_ctx.dm_type = BBFDM_USP
|
|
};
|
|
|
|
int old_schema_len = u->schema_len;
|
|
|
|
// If new parameter gets added it would be a minimum tuple of three params
|
|
int min_len = 100;
|
|
|
|
add_path_list(ROOT_NODE, &paths_list);
|
|
int new_schema_len = bbfdm_get_supported_dm(&data);
|
|
if (new_schema_len == 0) {
|
|
BBF_WARNING("Failed to get schema");
|
|
free_path_list(&paths_list);
|
|
return ret;
|
|
}
|
|
|
|
if (new_schema_len - old_schema_len > min_len) {
|
|
BBF_DEBUG("DM Schema update available old:new[%d:%d]", old_schema_len, new_schema_len);
|
|
if (old_schema_len != 0) {
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
free_path_list(&paths_list);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct blobmsg_policy dm_get_policy[] = {
|
|
[DM_GET_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_GET_PATHS] = { .name = "paths", .type = BLOBMSG_TYPE_ARRAY },
|
|
[DM_GET_MAXDEPTH] = { .name = "maxdepth", .type = BLOBMSG_TYPE_INT32 },
|
|
[DM_GET_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE},
|
|
};
|
|
|
|
static int bbfdm_get_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)),
|
|
struct ubus_request_data *req, const char *method __attribute__((unused)),
|
|
struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *tb[__DM_GET_MAX];
|
|
LIST_HEAD(paths_list);
|
|
bbfdm_data_t data;
|
|
uint8_t maxdepth = 0;
|
|
struct bbfdm_context *u;
|
|
|
|
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("Failed to get the bbfdm context");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
memset(&data, 0, sizeof(bbfdm_data_t));
|
|
|
|
if (blobmsg_parse(dm_get_policy, __DM_GET_MAX, tb, blob_data(msg), blob_len(msg))) {
|
|
BBF_ERR("Failed to parse blob");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!(tb[DM_GET_PATH]) && !(tb[DM_GET_PATHS]))
|
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
|
|
if (tb[DM_GET_PATH]) {
|
|
char *path = blobmsg_get_string(tb[DM_GET_PATH]);
|
|
add_path_list(path, &paths_list);
|
|
}
|
|
|
|
if (tb[DM_GET_PATHS]) {
|
|
struct blob_attr *paths = tb[DM_GET_PATHS];
|
|
struct blob_attr *path = NULL;
|
|
size_t rem;
|
|
|
|
blobmsg_for_each_attr(path, paths, rem) {
|
|
char *path_str = blobmsg_get_string(path);
|
|
|
|
add_path_list(path_str, &paths_list);
|
|
}
|
|
}
|
|
|
|
if (tb[DM_GET_MAXDEPTH])
|
|
maxdepth = blobmsg_get_u32(tb[DM_GET_MAXDEPTH]);
|
|
|
|
data.ctx = ctx;
|
|
data.req = req;
|
|
data.plist = &paths_list;
|
|
data.depth = maxdepth;
|
|
|
|
fill_optional_data(&data, tb[DM_GET_OPTIONAL]);
|
|
|
|
bbfdm_get_value(&data, NULL);
|
|
|
|
free_path_list(&paths_list);
|
|
return 0;
|
|
}
|
|
|
|
static const struct blobmsg_policy dm_schema_policy[] = {
|
|
[DM_SCHEMA_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_SCHEMA_PATHS] = { .name = "paths", .type = BLOBMSG_TYPE_ARRAY },
|
|
[DM_SCHEMA_FIRST_LEVEL] = { .name = "first_level", .type = BLOBMSG_TYPE_BOOL},
|
|
[DM_SCHEMA_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE},
|
|
};
|
|
|
|
static int bbfdm_schema_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)),
|
|
struct ubus_request_data *req, const char *method __attribute__((unused)),
|
|
struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *tb[__DM_SCHEMA_MAX];
|
|
LIST_HEAD(paths_list);
|
|
bbfdm_data_t data;
|
|
struct bbfdm_context *u;
|
|
|
|
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("Failed to get the bbfdm context");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
memset(&data, 0, sizeof(bbfdm_data_t));
|
|
|
|
if (blobmsg_parse(dm_schema_policy, __DM_SCHEMA_MAX, tb, blob_data(msg), blob_len(msg))) {
|
|
BBF_ERR("Failed to parse blob");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!(tb[DM_SCHEMA_PATH]) && !(tb[DM_SCHEMA_PATHS]))
|
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
|
|
if (tb[DM_SCHEMA_PATH]) {
|
|
char *path = blobmsg_get_string(tb[DM_SCHEMA_PATH]);
|
|
|
|
add_path_list(path, &paths_list);
|
|
}
|
|
|
|
if (tb[DM_SCHEMA_PATHS]) {
|
|
struct blob_attr *paths = tb[DM_GET_PATHS];
|
|
struct blob_attr *path = NULL;
|
|
size_t rem;
|
|
|
|
blobmsg_for_each_attr(path, paths, rem) {
|
|
char *path_str = blobmsg_get_string(path);
|
|
|
|
add_path_list(path_str, &paths_list);
|
|
}
|
|
}
|
|
|
|
fill_optional_data(&data, tb[DM_SCHEMA_OPTIONAL]);
|
|
|
|
unsigned int dm_type = data.bbf_ctx.dm_type;
|
|
|
|
data.ctx = ctx;
|
|
data.req = req;
|
|
data.bbf_ctx.nextlevel = (tb[DM_SCHEMA_FIRST_LEVEL]) ? blobmsg_get_bool(tb[DM_SCHEMA_FIRST_LEVEL]) : false;
|
|
data.bbf_ctx.iscommand = (dm_type == BBFDM_CWMP) ? false : true;
|
|
data.bbf_ctx.isevent = (dm_type == BBFDM_CWMP) ? false : true;
|
|
data.bbf_ctx.isinfo = (dm_type == BBFDM_CWMP) ? false : true;
|
|
data.plist = &paths_list;
|
|
|
|
#ifdef BBF_SCHEMA_FULL_TREE
|
|
data.bbf_ctx.isinfo = true;
|
|
bbfdm_get_supported_dm(&data);
|
|
#else
|
|
if (dm_type == BBFDM_CWMP)
|
|
bbfdm_get_names(&data);
|
|
else
|
|
bbfdm_get_supported_dm(&data);
|
|
#endif
|
|
|
|
free_path_list(&paths_list);
|
|
return 0;
|
|
}
|
|
|
|
static const struct blobmsg_policy dm_instances_policy[] = {
|
|
[DM_INSTANCES_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_INSTANCES_PATHS] = { .name = "paths", .type = BLOBMSG_TYPE_ARRAY },
|
|
[DM_INSTANCES_FIRST_LEVEL] = { .name = "first_level", .type = BLOBMSG_TYPE_BOOL },
|
|
[DM_INSTANCES_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE },
|
|
};
|
|
|
|
static int bbfdm_instances_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)),
|
|
struct ubus_request_data *req, const char *method __attribute__((unused)),
|
|
struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *tb[__DM_INSTANCES_MAX];
|
|
LIST_HEAD(paths_list);
|
|
bbfdm_data_t data;
|
|
|
|
memset(&data, 0, sizeof(bbfdm_data_t));
|
|
|
|
if (blobmsg_parse(dm_instances_policy, __DM_INSTANCES_MAX, tb, blob_data(msg), blob_len(msg))) {
|
|
BBF_ERR("Failed to parse blob");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!(tb[DM_INSTANCES_PATH]) && !(tb[DM_INSTANCES_PATHS]))
|
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
|
|
if (tb[DM_INSTANCES_PATH]) {
|
|
char *path = blobmsg_get_string(tb[DM_INSTANCES_PATH]);
|
|
add_path_list(path, &paths_list);
|
|
}
|
|
|
|
if (tb[DM_INSTANCES_PATHS]) {
|
|
struct blob_attr *paths = tb[DM_INSTANCES_PATHS];
|
|
struct blob_attr *path = NULL;
|
|
size_t rem;
|
|
|
|
blobmsg_for_each_attr(path, paths, rem) {
|
|
char *path_str = blobmsg_get_string(path);
|
|
|
|
add_path_list(path_str, &paths_list);
|
|
}
|
|
}
|
|
|
|
data.ctx = ctx;
|
|
data.req = req;
|
|
data.bbf_ctx.nextlevel = (tb[DM_INSTANCES_FIRST_LEVEL]) ? blobmsg_get_bool(tb[DM_INSTANCES_FIRST_LEVEL]) : false;
|
|
data.plist = &paths_list;
|
|
|
|
fill_optional_data(&data, tb[DM_INSTANCES_OPTIONAL]);
|
|
|
|
bbfdm_get_instances(&data);
|
|
|
|
free_path_list(&paths_list);
|
|
return 0;
|
|
}
|
|
|
|
static const struct blobmsg_policy dm_set_policy[] = {
|
|
[DM_SET_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_SET_VALUE] = { .name = "value", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_SET_TYPE] = { .name = "datatype", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_SET_OBJ_PATH] = { .name = "obj_path", .type = BLOBMSG_TYPE_TABLE },
|
|
[DM_SET_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE },
|
|
};
|
|
|
|
int bbfdm_set_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
|
struct ubus_request_data *req, const char *method,
|
|
struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *tb[__DM_SET_MAX] = {NULL};
|
|
char path[PATH_MAX] = {'\0'};
|
|
bbfdm_data_t data;
|
|
int fault = 0;
|
|
LIST_HEAD(pv_list);
|
|
|
|
memset(&data, 0, sizeof(bbfdm_data_t));
|
|
|
|
if (blobmsg_parse(dm_set_policy, __DM_SET_MAX, tb, blob_data(msg), blob_len(msg))) {
|
|
BBF_ERR("Failed to parse blob");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!tb[DM_SET_PATH])
|
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
|
|
if (!tb[DM_SET_VALUE] && !tb[DM_SET_OBJ_PATH])
|
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
|
|
snprintf(path, PATH_MAX, "%s", (char *)blobmsg_data(tb[DM_SET_PATH]));
|
|
|
|
fill_optional_data(&data, tb[DM_SET_OPTIONAL]);
|
|
|
|
BBF_INFO("ubus method|%s|, name|%s|, path(%s)", method, obj->name, path);
|
|
|
|
blob_buf_init(&data.bb, 0);
|
|
bbf_init(&data.bbf_ctx);
|
|
|
|
data.ctx = ctx;
|
|
data.bbf_ctx.in_param = path;
|
|
|
|
char *value = tb[DM_SET_VALUE] ? blobmsg_get_string(tb[DM_SET_VALUE]) : NULL;
|
|
char *type = tb[DM_SET_TYPE] ? blobmsg_get_string(tb[DM_SET_TYPE]) : NULL;
|
|
|
|
fault = fill_pvlist_set(&data, path, value, type, tb[DM_SET_OBJ_PATH], &pv_list);
|
|
if (fault) {
|
|
BBF_ERR("Fault in fill pvlist set path |%s| : |%d|", data.bbf_ctx.in_param, fault);
|
|
fill_err_code_array(&data, fault);
|
|
goto end;
|
|
}
|
|
|
|
if (list_empty(&pv_list)) {
|
|
BBF_ERR("Fault in fill pvlist set path |%s| : |list is empty|", data.bbf_ctx.in_param);
|
|
fill_err_code_array(&data, USP_FAULT_INTERNAL_ERROR);
|
|
fault = USP_FAULT_INTERNAL_ERROR;
|
|
goto end;
|
|
}
|
|
|
|
data.plist = &pv_list;
|
|
|
|
fault = bbfdm_set_value(&data);
|
|
|
|
end:
|
|
if ((data.bbf_ctx.dm_type == BBFDM_BOTH) && (dm_is_micro_service() == false)) {
|
|
bbf_entry_services(data.bbf_ctx.dm_type, (!fault) ? true : false, true);
|
|
}
|
|
|
|
bbf_cleanup(&data.bbf_ctx);
|
|
free_pv_list(&pv_list);
|
|
|
|
ubus_send_reply(ctx, req, data.bb.head);
|
|
blob_buf_free(&data.bb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct blobmsg_policy dm_operate_policy[__DM_OPERATE_MAX] = {
|
|
[DM_OPERATE_COMMAND] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_OPERATE_COMMAND_KEY] = { .name = "command_key", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_OPERATE_INPUT] = { .name = "input", .type = BLOBMSG_TYPE_TABLE },
|
|
[DM_OPERATE_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE },
|
|
};
|
|
|
|
static int bbfdm_operate_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)),
|
|
struct ubus_request_data *req, const char *method __attribute__((unused)),
|
|
struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *tb[__DM_OPERATE_MAX] = {NULL};
|
|
char path[PATH_MAX] = {0};
|
|
char *str = NULL;
|
|
bbfdm_data_t data;
|
|
|
|
memset(&data, 0, sizeof(bbfdm_data_t));
|
|
|
|
if (blobmsg_parse(dm_operate_policy, __DM_OPERATE_MAX, tb, blob_data(msg), blob_len(msg))) {
|
|
BBF_ERR("Failed to parse blob");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!(tb[DM_OPERATE_COMMAND]))
|
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
|
|
snprintf(path, PATH_MAX, "%s", (char *)blobmsg_data(tb[DM_OPERATE_COMMAND]));
|
|
|
|
data.ctx = ctx;
|
|
data.req = req;
|
|
data.bbf_ctx.in_param = path;
|
|
data.bbf_ctx.linker = tb[DM_OPERATE_COMMAND_KEY] ? blobmsg_get_string(tb[DM_OPERATE_COMMAND_KEY]) : "";
|
|
|
|
if (tb[DM_OPERATE_INPUT]) {
|
|
str = blobmsg_format_json(tb[DM_OPERATE_INPUT], true);
|
|
data.bbf_ctx.in_value = str;
|
|
}
|
|
|
|
fill_optional_data(&data, tb[DM_OPERATE_OPTIONAL]);
|
|
|
|
BBF_INFO("ubus method|%s|, name|%s|, path(%s)", method, obj->name, data.bbf_ctx.in_param);
|
|
|
|
if (is_sync_operate_cmd(&data)) {
|
|
bbfdm_operate_cmd(&data, NULL);
|
|
} else {
|
|
bbfdm_cancel_instance_refresh_timer(ctx);
|
|
bbfdm_start_deferred(&data, bbfdm_operate_cmd, true);
|
|
}
|
|
|
|
FREE(str);
|
|
return 0;
|
|
}
|
|
|
|
static const struct blobmsg_policy dm_add_policy[] = {
|
|
[DM_ADD_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_ADD_OBJ_PATH] = { .name = "obj_path", .type = BLOBMSG_TYPE_TABLE },
|
|
[DM_ADD_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE },
|
|
};
|
|
|
|
int bbfdm_add_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
|
struct ubus_request_data *req, const char *method,
|
|
struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *tb[__DM_ADD_MAX];
|
|
char path[PATH_MAX];
|
|
bbfdm_data_t data;
|
|
int fault = 0;
|
|
|
|
memset(&data, 0, sizeof(bbfdm_data_t));
|
|
|
|
if (blobmsg_parse(dm_add_policy, __DM_ADD_MAX, tb, blob_data(msg), blob_len(msg))) {
|
|
BBF_ERR("Failed to parse blob");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!tb[DM_ADD_PATH])
|
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
|
|
snprintf(path, PATH_MAX, "%s", (char *)blobmsg_data(tb[DM_ADD_PATH]));
|
|
|
|
data.ctx = ctx;
|
|
data.bbf_ctx.in_param = path;
|
|
|
|
fill_optional_data(&data, tb[DM_ADD_OPTIONAL]);
|
|
|
|
BBF_INFO("ubus method|%s|, name|%s|, path(%s)", method, obj->name, data.bbf_ctx.in_param);
|
|
|
|
blob_buf_init(&data.bb, 0);
|
|
bbf_init(&data.bbf_ctx);
|
|
|
|
fault = create_add_response(&data);
|
|
if (fault) {
|
|
BBF_ERR("Fault in add path |%s|", data.bbf_ctx.in_param);
|
|
goto end;
|
|
}
|
|
|
|
if (tb[DM_ADD_OBJ_PATH]) {
|
|
LIST_HEAD(pv_list);
|
|
|
|
snprintf(path, PATH_MAX, "%s%s.", (char *)blobmsg_data(tb[DM_ADD_PATH]), data.bbf_ctx.addobj_instance);
|
|
|
|
fault = fill_pvlist_set(&data, path, NULL, NULL, tb[DM_ADD_OBJ_PATH], &pv_list);
|
|
if (fault) {
|
|
BBF_ERR("Fault in fill pvlist set path |%s|", path);
|
|
fill_err_code_array(&data, USP_FAULT_INTERNAL_ERROR);
|
|
free_pv_list(&pv_list);
|
|
goto end;
|
|
}
|
|
|
|
data.plist = &pv_list;
|
|
|
|
bbfdm_set_value(&data);
|
|
|
|
free_pv_list(&pv_list);
|
|
}
|
|
|
|
end:
|
|
if ((data.bbf_ctx.dm_type == BBFDM_BOTH) && (dm_is_micro_service() == false)) {
|
|
bbf_entry_services(data.bbf_ctx.dm_type, (!fault) ? true : false, true);
|
|
}
|
|
|
|
bbf_cleanup(&data.bbf_ctx);
|
|
|
|
ubus_send_reply(ctx, req, data.bb.head);
|
|
blob_buf_free(&data.bb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct blobmsg_policy dm_del_policy[] = {
|
|
[DM_DEL_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
|
[DM_DEL_PATHS] = { .name = "paths", .type = BLOBMSG_TYPE_ARRAY },
|
|
[DM_DEL_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE },
|
|
};
|
|
|
|
int bbfdm_del_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
|
struct ubus_request_data *req, const char *method,
|
|
struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *tb[__DM_DEL_MAX];
|
|
LIST_HEAD(paths_list);
|
|
bbfdm_data_t data;
|
|
int fault = 0;
|
|
|
|
memset(&data, 0, sizeof(bbfdm_data_t));
|
|
|
|
if (blobmsg_parse(dm_del_policy, __DM_DEL_MAX, tb, blob_data(msg), blob_len(msg))) {
|
|
BBF_ERR("Failed to parse blob");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!tb[DM_DEL_PATH] && !tb[DM_DEL_PATHS])
|
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
|
|
if (tb[DM_DEL_PATH]) {
|
|
char *path = blobmsg_get_string(tb[DM_DEL_PATH]);
|
|
add_path_list(path, &paths_list);
|
|
}
|
|
|
|
if (tb[DM_DEL_PATHS]) {
|
|
struct blob_attr *paths = tb[DM_DEL_PATHS];
|
|
struct blob_attr *path = NULL;
|
|
size_t rem;
|
|
|
|
blobmsg_for_each_attr(path, paths, rem) {
|
|
char *path_str = blobmsg_get_string(path);
|
|
|
|
add_path_list(path_str, &paths_list);
|
|
}
|
|
}
|
|
|
|
data.ctx = ctx;
|
|
data.plist = &paths_list;
|
|
|
|
fill_optional_data(&data, tb[DM_DEL_OPTIONAL]);
|
|
|
|
BBF_INFO("ubus method|%s|, name|%s|", method, obj->name);
|
|
|
|
blob_buf_init(&data.bb, 0);
|
|
bbf_init(&data.bbf_ctx);
|
|
|
|
data.bbf_ctx.in_param = tb[DM_DEL_PATH] ? blobmsg_get_string(tb[DM_DEL_PATH]) : "";
|
|
|
|
fault = create_del_response(&data);
|
|
|
|
if ((data.bbf_ctx.dm_type == BBFDM_BOTH) && (dm_is_micro_service() == false)) {
|
|
bbf_entry_services(data.bbf_ctx.dm_type, (!fault) ? true : false, true);
|
|
}
|
|
|
|
bbf_cleanup(&data.bbf_ctx);
|
|
free_path_list(&paths_list);
|
|
|
|
ubus_send_reply(ctx, req, data.bb.head);
|
|
blob_buf_free(&data.bb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum {
|
|
BBF_SERVICE_CMD,
|
|
BBF_SERVICE_NAME,
|
|
BBF_SERVICE_PARENT_DM,
|
|
BBF_SERVICE_OBJECTS,
|
|
__BBF_SERVICE_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy service_policy[] = {
|
|
[BBF_SERVICE_CMD] = { .name = "cmd", .type = BLOBMSG_TYPE_STRING },
|
|
[BBF_SERVICE_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
|
|
[BBF_SERVICE_PARENT_DM] = { .name = "parent_dm", .type = BLOBMSG_TYPE_STRING },
|
|
[BBF_SERVICE_OBJECTS] = { .name = "objects", .type = BLOBMSG_TYPE_ARRAY },
|
|
};
|
|
|
|
static void service_list(struct blob_buf *bb)
|
|
{
|
|
void *array;
|
|
|
|
array = blobmsg_open_array(bb, "supported_cmd");
|
|
blobmsg_add_string(bb, NULL, "register");
|
|
blobmsg_add_string(bb, NULL, "list");
|
|
blobmsg_close_array(bb, array);
|
|
|
|
array = blobmsg_open_array(bb, "registered_service");
|
|
get_list_of_registered_service(&head_registered_service, bb);
|
|
blobmsg_close_array(bb, array);
|
|
}
|
|
|
|
static int bbfdm_service_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
|
struct ubus_request_data *req __attribute__((unused)), const char *method,
|
|
struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *tb[__BBF_SERVICE_MAX] = {NULL};
|
|
struct blob_buf bb;
|
|
struct bbfdm_context *u;
|
|
|
|
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("Failed to get the bbfdm context");
|
|
return 0;
|
|
}
|
|
|
|
memset(&bb, 0, sizeof(struct blob_buf));
|
|
blob_buf_init(&bb, 0);
|
|
|
|
if (blobmsg_parse(service_policy, __BBF_SERVICE_MAX, tb, blob_data(msg), blob_len(msg))) {
|
|
service_list(&bb);
|
|
goto end;
|
|
}
|
|
|
|
BBF_INFO("ubus method|%s|, name|%s|", method, obj->name);
|
|
|
|
if (dm_is_micro_service() == true) { // It's a micro-service instance
|
|
blobmsg_add_string(&bb, "error", "Its not allowed to register a micro-service for another micro-service!!");
|
|
goto end;
|
|
}
|
|
|
|
if (!tb[BBF_SERVICE_CMD]) {
|
|
service_list(&bb);
|
|
goto end;
|
|
}
|
|
|
|
char *srv_cmd = blobmsg_get_string(tb[BBF_SERVICE_CMD]);
|
|
|
|
if (is_str_eq(srv_cmd, "register")) {
|
|
|
|
if (!tb[BBF_SERVICE_NAME]) {
|
|
blobmsg_add_string(&bb, "error", "service name should be defined!!");
|
|
goto end;
|
|
}
|
|
|
|
if (!tb[BBF_SERVICE_PARENT_DM]) {
|
|
blobmsg_add_string(&bb, "error", "service parent dm should be defined!!");
|
|
goto end;
|
|
}
|
|
|
|
if (!tb[BBF_SERVICE_OBJECTS]) {
|
|
blobmsg_add_string(&bb, "error", "service objects should be defined!!");
|
|
goto end;
|
|
}
|
|
|
|
char *srv_name = blobmsg_get_string(tb[BBF_SERVICE_NAME]);
|
|
char *srv_parent_dm = blobmsg_get_string(tb[BBF_SERVICE_PARENT_DM]);
|
|
bool res = true;
|
|
|
|
if (tb[BBF_SERVICE_OBJECTS]) {
|
|
struct blob_attr *objs = tb[BBF_SERVICE_OBJECTS];
|
|
struct blob_attr *attr_obj = NULL;
|
|
size_t rem;
|
|
|
|
blobmsg_for_each_attr(attr_obj, objs, rem) {
|
|
char *srv_obj = blobmsg_get_string(attr_obj);
|
|
res |= load_service(DEAMON_DM_ROOT_OBJ, &head_registered_service, srv_name, srv_parent_dm, srv_obj);
|
|
}
|
|
} else {
|
|
res = false;
|
|
}
|
|
|
|
blobmsg_add_u8(&bb, "status", res);
|
|
run_schema_updater(u);
|
|
} else {
|
|
service_list(&bb);
|
|
}
|
|
|
|
end:
|
|
ubus_send_reply(ctx, req, bb.head);
|
|
blob_buf_free(&bb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
enum {
|
|
BBF_NOTIFY_NAME,
|
|
BBF_NOTIFY_PRAMS,
|
|
__BBF_NOTIFY_MAX,
|
|
};
|
|
|
|
static const struct blobmsg_policy dm_notify_event_policy[] = {
|
|
[BBF_NOTIFY_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
|
|
[BBF_NOTIFY_PRAMS] = { .name = "input", .type = BLOBMSG_TYPE_ARRAY },
|
|
};
|
|
|
|
static int bbfdm_notify_event(struct ubus_context *ctx, struct ubus_object *obj,
|
|
struct ubus_request_data *req __attribute__((unused)), const char *method,
|
|
struct blob_attr *msg)
|
|
{
|
|
struct blob_attr *tb[__BBF_NOTIFY_MAX] = {NULL};
|
|
char method_name[256] = {0};
|
|
struct bbfdm_context *u;
|
|
|
|
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("failed to get the bbfdm context");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (blobmsg_parse(dm_notify_event_policy, __BBF_NOTIFY_MAX, tb, blob_data(msg), blob_len(msg))) {
|
|
BBF_ERR("Failed to parse blob");
|
|
return UBUS_STATUS_UNKNOWN_ERROR;
|
|
}
|
|
|
|
if (!tb[BBF_NOTIFY_NAME])
|
|
return UBUS_STATUS_INVALID_ARGUMENT;
|
|
|
|
BBF_INFO("ubus method|%s|, name|%s|", method, obj->name);
|
|
snprintf(method_name, sizeof(method_name), "%s.%s", DM_STRLEN(u->config.out_root_obj) ? u->config.out_root_obj : u->config.out_name, BBF_EVENT_NAME);
|
|
ubus_send_event(ctx, method_name, msg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct ubus_method bbf_methods[] = {
|
|
UBUS_METHOD("get", bbfdm_get_handler, dm_get_policy),
|
|
UBUS_METHOD("schema", bbfdm_schema_handler, dm_schema_policy),
|
|
UBUS_METHOD("instances", bbfdm_instances_handler, dm_instances_policy),
|
|
UBUS_METHOD("set", bbfdm_set_handler, dm_set_policy),
|
|
UBUS_METHOD("operate", bbfdm_operate_handler, dm_operate_policy),
|
|
UBUS_METHOD("add", bbfdm_add_handler, dm_add_policy),
|
|
UBUS_METHOD("del", bbfdm_del_handler, dm_del_policy),
|
|
UBUS_METHOD("service", bbfdm_service_handler, service_policy),
|
|
UBUS_METHOD("notify_event", bbfdm_notify_event, dm_notify_event_policy),
|
|
};
|
|
|
|
static struct ubus_object_type bbf_type = UBUS_OBJECT_TYPE("", bbf_methods);
|
|
|
|
static struct ubus_object bbf_object = {
|
|
.name = "",
|
|
.type = &bbf_type,
|
|
.methods = bbf_methods,
|
|
.n_methods = ARRAY_SIZE(bbf_methods)
|
|
};
|
|
|
|
static void run_schema_updater(struct bbfdm_context *u)
|
|
{
|
|
bool ret = false;
|
|
char method_name[256] = {0};
|
|
|
|
ret = is_object_schema_update_available(u);
|
|
if (ret && (dm_is_micro_service() == false)) {
|
|
struct blob_buf bb;
|
|
|
|
memset(&bb, 0, sizeof(struct blob_buf));
|
|
BBF_INFO("Schema update available");
|
|
snprintf(method_name, sizeof(method_name), "%s.%s", u->config.out_name, BBF_UPDATE_SCHEMA_EVENT);
|
|
blob_buf_init(&bb, 0);
|
|
ubus_send_event(&u->ubus_ctx, method_name, bb.head);
|
|
blob_buf_free(&bb);
|
|
}
|
|
}
|
|
|
|
static void broadcast_add_del_event(struct ubus_context *ctx, const char *method, struct list_head *inst, bool is_add)
|
|
{
|
|
struct blob_buf bb;
|
|
struct pathNode *ptr;
|
|
char method_name[40];
|
|
void *a;
|
|
|
|
if (list_empty(inst)) {
|
|
return;
|
|
}
|
|
|
|
memset(&bb, 0, sizeof(struct blob_buf));
|
|
blob_buf_init(&bb, 0);
|
|
|
|
a = blobmsg_open_array(&bb, "instances");
|
|
list_for_each_entry(ptr, inst, list) {
|
|
blobmsg_add_string(&bb, NULL, ptr->path);
|
|
BBF_DEBUG("#%s:: %s, method %s #", (is_add)?"Add":"Del", ptr->path, method);
|
|
}
|
|
blobmsg_close_array(&bb, a);
|
|
|
|
snprintf(method_name, sizeof(method_name), "%s.%s", method, is_add ? BBF_ADD_EVENT : BBF_DEL_EVENT);
|
|
|
|
if (is_add)
|
|
ubus_send_event(ctx, method_name, bb.head);
|
|
else
|
|
ubus_send_event(ctx, method_name, bb.head);
|
|
|
|
blob_buf_free(&bb);
|
|
}
|
|
|
|
static void update_instances_list(struct list_head *inst)
|
|
{
|
|
int ret;
|
|
struct dmctx bbf_ctx = {
|
|
.in_param = ROOT_NODE,
|
|
.nextlevel = false,
|
|
.disable_mservice_browse = true,
|
|
.dm_type = BBFDM_USP
|
|
};
|
|
|
|
bbf_init(&bbf_ctx);
|
|
|
|
ret = bbfdm_cmd_exec(&bbf_ctx, BBF_INSTANCES);
|
|
if (ret == 0) {
|
|
struct blob_attr *cur = NULL;
|
|
size_t rem = 0;
|
|
|
|
// Apply all bbfdm changes
|
|
dmuci_commit_bbfdm();
|
|
|
|
blobmsg_for_each_attr(cur, bbf_ctx.bb.head, rem) {
|
|
struct blob_attr *tb[1] = {0};
|
|
const struct blobmsg_policy p[1] = {
|
|
{ "path", BLOBMSG_TYPE_STRING }
|
|
};
|
|
|
|
blobmsg_parse(p, 1, tb, blobmsg_data(cur), blobmsg_len(cur));
|
|
|
|
char *name = (tb[0]) ? blobmsg_get_string(tb[0]) : "";
|
|
|
|
add_path_list(name, inst);
|
|
}
|
|
} else {
|
|
BBF_WARNING("Failed to get instances, err code %d", ret);
|
|
}
|
|
|
|
bbf_cleanup(&bbf_ctx);
|
|
}
|
|
|
|
static void instance_compare_publish(struct bbfdm_context *daemon_ctx)
|
|
{
|
|
struct pathNode *ptr;
|
|
LIST_HEAD(inst_list);
|
|
struct list_head *new_inst, *old_inst;
|
|
const char *method;
|
|
|
|
new_inst = &daemon_ctx->instances;
|
|
old_inst = &daemon_ctx->old_instances;
|
|
|
|
method = DM_STRLEN(daemon_ctx->config.out_root_obj) ? daemon_ctx->config.out_root_obj : daemon_ctx->config.out_name;
|
|
list_for_each_entry(ptr, old_inst, list) {
|
|
if (!present_in_path_list(new_inst, ptr->path)) {
|
|
add_path_list(ptr->path, &inst_list);
|
|
}
|
|
}
|
|
broadcast_add_del_event(&daemon_ctx->ubus_ctx, method, &inst_list, false);
|
|
free_path_list(&inst_list);
|
|
|
|
list_for_each_entry(ptr, new_inst, list) {
|
|
if (!present_in_path_list(old_inst, ptr->path)) {
|
|
add_path_list(ptr->path, &inst_list);
|
|
}
|
|
}
|
|
broadcast_add_del_event(&daemon_ctx->ubus_ctx, method, &inst_list, true);
|
|
free_path_list(&inst_list);
|
|
}
|
|
|
|
static void periodic_instance_updater(struct uloop_timeout *t)
|
|
{
|
|
struct bbfdm_context *u;
|
|
|
|
u = container_of(t, struct bbfdm_context, instance_timer);
|
|
if (u == NULL) {
|
|
BBF_ERR("Failed to get the bbfdm context");
|
|
return;
|
|
}
|
|
|
|
if (list_empty(&u->instances)) {
|
|
if (!list_empty(&u->old_instances)) {
|
|
list_splice_init(&u->old_instances, &u->instances);
|
|
} else {
|
|
update_instances_list(&u->instances);
|
|
return;
|
|
}
|
|
}
|
|
|
|
free_path_list(&u->old_instances);
|
|
list_splice_init(&u->instances, &u->old_instances);
|
|
|
|
update_instances_list(&u->instances);
|
|
if (!list_empty(&u->instances) && !list_empty(&u->old_instances)) {
|
|
BBF_INFO("Comparing instances ...");
|
|
instance_compare_publish(u);
|
|
}
|
|
}
|
|
|
|
static int register_service(struct ubus_context *ctx)
|
|
{
|
|
struct blob_buf bb = {0};
|
|
uint32_t ubus_id;
|
|
struct bbfdm_context *u;
|
|
struct pathNode *pn = NULL;
|
|
|
|
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("failed to get the bbfdm context");
|
|
return false;
|
|
}
|
|
// check if object already present
|
|
int ret = ubus_lookup_id(ctx, u->config.out_root_obj, &ubus_id);
|
|
if (ret != 0)
|
|
return ret;
|
|
|
|
memset(&bb, 0, sizeof(struct blob_buf));
|
|
blob_buf_init(&bb, 0);
|
|
|
|
blobmsg_add_string(&bb, "cmd", "register");
|
|
blobmsg_add_string(&bb, "name", u->config.out_name);
|
|
blobmsg_add_string(&bb, "parent_dm", u->config.out_parent_dm);
|
|
|
|
void *arr = blobmsg_open_array(&bb, "objects");
|
|
|
|
list_for_each_entry(pn, &u->config.list_objs, list) {
|
|
blobmsg_add_string(&bb, NULL, pn->path);
|
|
}
|
|
|
|
blobmsg_close_array(&bb, arr);
|
|
|
|
ubus_invoke(ctx, ubus_id, "service", bb.head, NULL, NULL, 5000);
|
|
blob_buf_free(&bb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _fill_daemon_input_option(json_object *daemon_obj, bbfdm_config_t *config)
|
|
{
|
|
char opt_val[MAX_DM_PATH] = {0};
|
|
|
|
if (!config || strlen(config->service_name) == 0) {
|
|
BBF_ERR("Invalid input options for service name \n");
|
|
return -1;
|
|
}
|
|
|
|
if (strchr(config->service_name, '/')) { // absolute path
|
|
char *srv_name = dmjson_get_value(daemon_obj, 1, "service_name");
|
|
if (strlen(srv_name)) {
|
|
strncpyt(config->service_name, srv_name, sizeof(config->service_name));
|
|
}
|
|
}
|
|
|
|
// check if the service plugin is DotSO plugin
|
|
snprintf(opt_val, MAX_DM_PATH, "%s/%s.so", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, config->service_name);
|
|
if (!file_exists(opt_val)) {
|
|
snprintf(opt_val, MAX_DM_PATH, "%s/%s.json", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, config->service_name);
|
|
}
|
|
|
|
if (!file_exists(opt_val)) {
|
|
BBF_ERR("Failed to load service plugin %s opt_val=%s\n", config->service_name, opt_val);
|
|
return -1;
|
|
}
|
|
|
|
strncpyt(config->in_name, opt_val, sizeof(config->in_name));
|
|
|
|
snprintf(opt_val, MAX_DM_PATH, "%s/%s", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, config->service_name);
|
|
if (folder_exists(opt_val)) {
|
|
strncpyt(config->in_plugin_dir, opt_val, sizeof(config->in_plugin_dir));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _fill_daemon_output_options(json_object *daemon_obj, bbfdm_config_t *config)
|
|
{
|
|
char *opt_val = NULL;
|
|
|
|
if (!config || !daemon_obj) {
|
|
BBF_ERR("Invalid input options \n");
|
|
return -1;
|
|
}
|
|
|
|
opt_val = dmjson_get_value(daemon_obj, 2, "output", "root_obj");
|
|
if (DM_STRLEN(opt_val)) {
|
|
strncpyt(config->out_root_obj, opt_val, sizeof(config->out_root_obj));
|
|
} else { // for main process, there is no root obj
|
|
strncpyt(config->out_root_obj, BBFDM_DEFAULT_UBUS_OBJ, sizeof(config->out_root_obj));
|
|
}
|
|
|
|
opt_val = dmjson_get_value(daemon_obj, 2, "output", "name");
|
|
if (strlen(opt_val)) {
|
|
snprintf(config->out_name, sizeof(config->out_name), "%s.%s", BBFDM_DEFAULT_UBUS_OBJ, opt_val);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int daemon_load_config_external_plugin(bbfdm_config_t *config)
|
|
{
|
|
json_object *json_obj = NULL;
|
|
char json_path[MAX_DM_PATH] = {0};
|
|
int err = 0;
|
|
|
|
if (!config || !strlen(config->service_name))
|
|
return -1;
|
|
|
|
if (strchr(config->service_name, '/')) { // absolute path
|
|
strncpyt(json_path, config->service_name, MAX_DM_PATH);
|
|
} else {
|
|
snprintf(json_path, MAX_DM_PATH, "%s/%s.json", BBFDM_DEFAULT_MICROSERVICE_INPUT_PATH, config->service_name);
|
|
}
|
|
|
|
json_obj = json_object_from_file(json_path);
|
|
if (!json_obj) {
|
|
BBF_ERR("Failed to read input %s file \n", json_path);
|
|
return -1;
|
|
}
|
|
|
|
json_object *daemon_obj = dmjson_get_obj(json_obj, 1, "daemon");
|
|
if (!daemon_obj) {
|
|
err = -1;
|
|
goto exit;
|
|
}
|
|
|
|
err = _fill_daemon_input_option(daemon_obj, config);
|
|
if (err == -1) {
|
|
goto exit;
|
|
}
|
|
|
|
err = _fill_daemon_output_options(daemon_obj, config);
|
|
|
|
exit:
|
|
if (json_obj) {
|
|
json_object_put(json_obj);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int daemon_load_config_internal_plugin(bbfdm_config_t *config)
|
|
{
|
|
char opt_val[MAX_DM_PATH] = {0};
|
|
|
|
if (strlen(config->service_name) == 0 && dm_is_micro_service() == false) { // default value for main process
|
|
strncpyt(config->service_name, BBFDM_DEFAULT_UBUS_OBJ, sizeof(config->service_name));
|
|
}
|
|
|
|
if (!config || strlen(config->service_name) == 0) {
|
|
BBF_ERR("Invalid input options for service name \n");
|
|
return -1;
|
|
}
|
|
|
|
if(dm_is_micro_service() == false) {
|
|
strncpyt(config->in_plugin_dir, BBFDM_DEFAULT_PLUGINS_PATH, sizeof(config->in_plugin_dir));
|
|
snprintf(config->out_name, sizeof(config->out_name), "%s", BBFDM_DEFAULT_UBUS_OBJ);
|
|
} else {
|
|
snprintf(opt_val, MAX_DM_PATH, "%s/%s", BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH, config->service_name);
|
|
if (folder_exists(opt_val)) {
|
|
strncpyt(config->in_plugin_dir, opt_val, sizeof(config->in_plugin_dir));
|
|
}
|
|
|
|
strncpyt(config->out_root_obj, BBFDM_DEFAULT_UBUS_OBJ, sizeof(config->out_root_obj));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int daemon_load_config(bbfdm_config_t *config)
|
|
{
|
|
int err = 0;
|
|
|
|
if (INTERNAL_ROOT_TREE) {
|
|
err = daemon_load_config_internal_plugin(config);
|
|
BBF_INFO("Loading Config Internal plugin (%s)", config->service_name);
|
|
} else {
|
|
// This API will only be called with micro-services
|
|
err = daemon_load_config_external_plugin(config);
|
|
BBF_INFO("Loading Config External plugin (%s)", config->service_name);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int regiter_ubus_object(struct ubus_context *ctx)
|
|
{
|
|
int ret;
|
|
struct bbfdm_context *u;
|
|
|
|
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("failed to get the bbfdm context");
|
|
return -1;
|
|
}
|
|
|
|
bbf_object.name = u->config.out_name;
|
|
bbf_object.type->name = u->config.out_name;
|
|
if (dm_is_micro_service()) {
|
|
bbf_object.n_methods = bbf_object.n_methods - 2;
|
|
bbf_object.type->n_methods = bbf_object.n_methods;
|
|
ret = ubus_add_object(ctx, &bbf_object);
|
|
} else {
|
|
ret = ubus_add_object(ctx, &bbf_object);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void lookup_event_cb(struct ubus_context *ctx,
|
|
struct ubus_event_handler *ev __attribute__((unused)),
|
|
const char *type, struct blob_attr *msg)
|
|
{
|
|
const struct blobmsg_policy policy = {
|
|
"path", BLOBMSG_TYPE_STRING
|
|
};
|
|
struct blob_attr *attr;
|
|
const char *path;
|
|
struct bbfdm_context *u;
|
|
|
|
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("failed to get the bbfdm context");
|
|
return;
|
|
}
|
|
|
|
if (type && strcmp(type, "ubus.object.add") != 0)
|
|
return;
|
|
|
|
blobmsg_parse(&policy, 1, &attr, blob_data(msg), blob_len(msg));
|
|
|
|
if (!attr)
|
|
return;
|
|
|
|
path = blobmsg_data(attr);
|
|
if (path && strcmp(path, u->config.out_root_obj) == 0) {
|
|
// register micro-service
|
|
register_service(ctx);
|
|
}
|
|
}
|
|
|
|
static void bbfdm_register_instance_refresh_timer(struct ubus_context *ctx, int start_in_sec)
|
|
{
|
|
struct bbfdm_context *u;
|
|
|
|
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("Failed to get the bbfdm context");
|
|
return;
|
|
}
|
|
|
|
if (start_in_sec >= 0) {
|
|
BBF_INFO("Register instance refresh timer in %d sec...", start_in_sec);
|
|
u->instance_timer.cb = periodic_instance_updater;
|
|
uloop_timeout_set(&u->instance_timer, start_in_sec * 1000);
|
|
}
|
|
}
|
|
|
|
static void bbfdm_cancel_instance_refresh_timer(struct ubus_context *ctx)
|
|
{
|
|
struct bbfdm_context *u;
|
|
|
|
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
|
if (u == NULL) {
|
|
BBF_ERR("Failed to get the bbfdm context");
|
|
return;
|
|
}
|
|
|
|
BBF_DEBUG("Cancelling Instance refresh timer");
|
|
uloop_timeout_cancel(&u->instance_timer);
|
|
}
|
|
|
|
static void bbf_config_change_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
|
const char *type, struct blob_attr *msg)
|
|
{
|
|
(void)ev;
|
|
(void)ctx;
|
|
(void)msg;
|
|
|
|
BBF_INFO("Config updated, Scheduling instance refresh timers");
|
|
|
|
bbfdm_schedule_instance_refresh_timer(ctx, 0);
|
|
}
|
|
|
|
static void bbfdm_ctx_init(struct bbfdm_context *bbfdm_ctx)
|
|
{
|
|
INIT_LIST_HEAD(&bbfdm_ctx->instances);
|
|
INIT_LIST_HEAD(&bbfdm_ctx->old_instances);
|
|
INIT_LIST_HEAD(&bbfdm_ctx->event_handlers);
|
|
INIT_LIST_HEAD(&bbfdm_ctx->config.list_objs);
|
|
}
|
|
|
|
static int daemon_load_data_model(struct bbfdm_context *daemon_ctx)
|
|
{
|
|
int err = 0;
|
|
|
|
if (INTERNAL_ROOT_TREE) {
|
|
BBF_INFO("Loading Data Model Internal plugin (%s)", daemon_ctx->config.service_name);
|
|
err = bbfdm_load_internal_plugin(daemon_ctx, INTERNAL_ROOT_TREE, &daemon_ctx->config, &DEAMON_DM_ROOT_OBJ);
|
|
} else {
|
|
BBF_INFO("Loading Data Model External plugin (%s)", daemon_ctx->config.service_name);
|
|
err = bbfdm_load_external_plugin(daemon_ctx, &deamon_lib_handle, &daemon_ctx->config, &DEAMON_DM_ROOT_OBJ);
|
|
}
|
|
|
|
if (err)
|
|
return err;
|
|
|
|
BBF_INFO("Loading sub-modules %s", daemon_ctx->config.in_plugin_dir);
|
|
bbf_global_init(DEAMON_DM_ROOT_OBJ, daemon_ctx->config.in_plugin_dir);
|
|
|
|
if (DM_STRLEN(daemon_ctx->config.out_name) == 0) {
|
|
BBF_ERR("output name not defined");
|
|
return -1;
|
|
}
|
|
|
|
if (dm_is_micro_service()) {
|
|
if (DM_STRLEN(daemon_ctx->config.out_parent_dm) == 0) {
|
|
BBF_ERR("output parent dm not defined");
|
|
return -1;
|
|
}
|
|
|
|
if (list_empty(&daemon_ctx->config.list_objs)) {
|
|
BBF_ERR("output objects is not defined");
|
|
return -1;
|
|
}
|
|
|
|
if (DM_STRLEN(daemon_ctx->config.out_root_obj) == 0) {
|
|
BBF_ERR("output root obj not defined");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct ubus_event_handler add_event = { .cb = lookup_event_cb };
|
|
static struct ubus_event_handler config_change_handler = { .cb = bbf_config_change_cb };
|
|
|
|
int bbfdm_ubus_regiter_init(struct bbfdm_context *bbfdm_ctx)
|
|
{
|
|
int err = 0;
|
|
|
|
err = ubus_connect_ctx(&bbfdm_ctx->ubus_ctx, NULL);
|
|
if (err != UBUS_STATUS_OK) {
|
|
BBF_ERR("Failed to connect to ubus");
|
|
return -5; // Error code -5 indicating that ubus_ctx is connected
|
|
}
|
|
|
|
uloop_init();
|
|
ubus_add_uloop(&bbfdm_ctx->ubus_ctx);
|
|
|
|
bbfdm_ctx_init(bbfdm_ctx);
|
|
|
|
err = daemon_load_config(&bbfdm_ctx->config);
|
|
if (err) {
|
|
BBF_ERR("Failed to load config");
|
|
return err;
|
|
}
|
|
|
|
err = daemon_load_data_model(bbfdm_ctx);
|
|
if (err) {
|
|
BBF_ERR("Failed to load data_model");
|
|
return err;
|
|
}
|
|
|
|
err = regiter_ubus_object(&bbfdm_ctx->ubus_ctx);
|
|
if (err != UBUS_STATUS_OK)
|
|
return -1;
|
|
|
|
run_schema_updater(bbfdm_ctx);
|
|
bbfdm_register_instance_refresh_timer(&bbfdm_ctx->ubus_ctx, 1);
|
|
|
|
err = register_events_to_ubus(&bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers);
|
|
if (err != 0)
|
|
return err;
|
|
|
|
err = ubus_register_event_handler(&bbfdm_ctx->ubus_ctx, &config_change_handler, "bbf.config.change");
|
|
if (err != 0)
|
|
return err;
|
|
|
|
if (dm_is_micro_service() == true) {
|
|
// Register the micro-service
|
|
register_service(&bbfdm_ctx->ubus_ctx);
|
|
|
|
// If the micro-service is not registered, listen for "ubus.object.add" event
|
|
// and register the micro-service using event handler for it
|
|
err = ubus_register_event_handler(&bbfdm_ctx->ubus_ctx, &add_event, "ubus.object.add");
|
|
if (err != 0)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int bbfdm_ubus_regiter_free(struct bbfdm_context *bbfdm_ctx)
|
|
{
|
|
free_ubus_event_handler(&bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers);
|
|
bbfdm_ctx_cleanup(bbfdm_ctx);
|
|
uloop_done();
|
|
ubus_shutdown(&bbfdm_ctx->ubus_ctx);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void bbfdm_ubus_set_service_name(struct bbfdm_context *bbfdm_ctx, const char *srv_name)
|
|
{
|
|
strncpyt(bbfdm_ctx->config.service_name, srv_name, sizeof(bbfdm_ctx->config.service_name));
|
|
dm_set_micro_service();
|
|
}
|
|
|
|
void bbfdm_ubus_set_log_level(int log_level)
|
|
{
|
|
setlogmask(LOG_UPTO(log_level));
|
|
}
|
|
|
|
void bbfdm_ubus_load_data_model(DM_MAP_OBJ *DynamicObj)
|
|
{
|
|
INTERNAL_ROOT_TREE = DynamicObj;
|
|
}
|
|
|
|
void bbfdm_schedule_instance_refresh_timer(struct ubus_context *ctx, int start_in_sec)
|
|
{
|
|
bbfdm_cancel_instance_refresh_timer(ctx);
|
|
bbfdm_register_instance_refresh_timer(ctx, start_in_sec);
|
|
}
|