bbfdmd: Add support for cli command

This commit is contained in:
Amin Ben Romdhane 2023-04-20 15:08:35 +02:00
parent 96b305926f
commit 8d00536689
18 changed files with 957 additions and 249 deletions

View file

@ -41,6 +41,115 @@ When a ubus method is called it first fills `usp_data_t` structure with the nece
In short, it covers/supports all methods introduced in `TR-069` and `TR-369` by using the `bbf_entry_method` API from `libbbfdm-api` with the differents methods and the existing data-model available with `libbbfdm`.
## BBFDMD Command Line Arguments
`bbfdmd` supports two modes, a daemon mode (seen above) and a command (or CLI) mode, which supports interactively querying the data model and setting values in the configuration via the data model.
Actually, the CLI mode is an utility to simplify the interaction with data model that gives to customer the visibility to expose any kind of data model (which can be a `DotSo` plugin, `JSON` plugin, `UBUS` command or `UNIX` socket) with a specific format (`CLI` or `JSON`).
The CLI mode is specified with the `-c` option and can be run using `cwmp` or `usp` protocol.
All of the above configurations should be done by json file which can be located anywhere, just don't forget to pass the path with `-I` option.
```console
root@iopsys:~# bbfdmd -I /tmp/test.json -c help
Valid commands:
help
get [path-expr]
set [path-expr] [value]
add [object]
del [path-expr]
instances [path-expr]
schema [path-expr]
```
Below is an example of json file:
```json
{
"client": {
"config": {
"proto": "usp", // usp, cwmp
"instance_mode": 0 // 0:number, 1:alias
},
"input": {
"type": "UBUS", // JSON, UBUS, DotSO, UNIX
"name": "bbfdm"
},
"output": {
"type": "CLI" // CLI, JSON
}
}
}
```
> NOTE: If `-I` option is not passed when starting `bbfdmd`, so configuration options will be loaded from the default [INPUT.JSON](../../json/input.json) located in '/etc/bbfdm/input.json'.
* To see a list of arguments supported by `bbfdmd` use:
```console
root@iopsys:~# bbfdmd -h
Usage: bbfdmd [options]
options:
-s <socket path> ubus socket
-I <json path> json input configuration
-c <command input> Run cli command
-h Displays this help
```
* To see a list of commands supported by `bbfdmd` in CLI mode use:
```console
root@iopsys:~# bbfdmd -c help
Valid commands:
help
get [path-expr]
set [path-expr] [value]
add [object]
del [path-expr]
instances [path-expr]
schema [path-expr]
```
* To see the currently implemented data model use:
```console
$ bbfdmd -c schema Device.
```
* To query the value of a parameter use:
```console
root@iopsys:~# bbfdmd -c get Device.Time.
Device.Time.Enable => 0
Device.Time.Status => Disabled
Device.Time.NTPServer1 => ntp1.sth.netnod.se
Device.Time.NTPServer2 => ntp1.gbg.netnod.se
Device.Time.NTPServer3 =>
Device.Time.NTPServer4 =>
Device.Time.NTPServer5 =>
Device.Time.CurrentLocalTime => 2023-04-22T13:45:01+00:00
Device.Time.LocalTimeZone => CET-1CEST,M3.5.0,M10.5.0/3
root@iopsys:~# bbfdmd -c get Device.WiFi.SSID.1.SSID
Device.WiFi.SSID.1.SSID => test-5g
```
> Note: The "parameter" may contain wildcard intance and partial paths.
* To set the value of a data model parameter use:
```console
$ bbfdmd -c set "parameter" "value"
```
> Note: The CLI mode also supports adding and deleting instances of data model objects and supported instances.
## Important topics
* [UBUS methods](../docs/guide/bbfdm_ubus_methods.md)
* [UBUS Errors](../docs/guide/bbfdm_ubus_errors.md)

View file

@ -12,5 +12,6 @@ ENDIF()
FILE(GLOB BBF_SOURCES *.c)
ADD_EXECUTABLE(bbfdmd ${BBF_SOURCES})
TARGET_LINK_LIBRARIES(bbfdmd uci ubus ubox blobmsg_json bbfdm-api bbfdm)
TARGET_LINK_LIBRARIES(bbfdmd ubus ubox blobmsg_json bbfdm-api bbfdm)
INSTALL(TARGETS bbfdmd DESTINATION usr/sbin)
INSTALL(FILES ../../json/input.json DESTINATION etc/bbfdm)

View file

@ -39,12 +39,16 @@
#include "events.h"
#include "pretty_print.h"
#include "get_helper.h"
#include "cli.h"
#include "libbbfdm-api/dmentry.h"
#include "libbbfdm-api/dmjson.h"
#define USP_SUBPROCESS_DEPTH (2)
#define BBF_SCHEMA_UPDATE_TIMEOUT (60 * 1000)
#define BBF_INSTANCES_UPDATE_TIMEOUT (25 * 1000)
#define DEFAULT_JSON_INPUT "/etc/bbfdm/input.json"
// Global variables
static unsigned int g_refresh_time = BBF_INSTANCES_UPDATE_TIMEOUT;
static int g_subprocess_level = USP_SUBPROCESS_DEPTH;
@ -69,8 +73,10 @@ static void usage(char *prog)
fprintf(stderr, "Usage: %s [options]\n", prog);
fprintf(stderr, "\n");
fprintf(stderr, "options:\n");
fprintf(stderr, " -s <socket path> ubus socket\n");
fprintf(stderr, " -t <timeout> Transaction timeout in sec\n");
fprintf(stderr, " -s <socket path> ubus socket\n");
fprintf(stderr, " -I <json path> json input configuration\n");
fprintf(stderr, " -c <command input> Run cli command\n");
fprintf(stderr, " -h Displays this help\n");
fprintf(stderr, "\n");
}
@ -101,37 +107,6 @@ static bool is_subprocess_required(const char *path)
return ret;
}
static int get_proto_type(struct blob_attr *proto)
{
int type = BBFDM_BOTH;
if (proto) {
const char *val = blobmsg_get_string(proto);
if (is_str_eq("cwmp", val))
type = BBFDM_CWMP;
else if (is_str_eq("usp", val))
type = BBFDM_USP;
else
type = BBFDM_BOTH;
}
return type;
}
static int get_instance_mode(struct blob_attr *ins)
{
int instance_mode = INSTANCE_MODE_NUMBER;
if (ins)
instance_mode = blobmsg_get_u32(ins);
if (instance_mode > INSTANCE_MODE_ALIAS)
instance_mode = INSTANCE_MODE_NUMBER;
return instance_mode;
}
static void fill_optional_data(usp_data_t *data, struct blob_attr *msg)
{
struct blob_attr *attr;
@ -141,11 +116,16 @@ static void fill_optional_data(usp_data_t *data, struct blob_attr *msg)
return;
blobmsg_for_each_attr(attr, msg, rem) {
if (is_str_eq(blobmsg_name(attr), "proto"))
data->bbf_ctx.dm_type = get_proto_type(attr);
if (is_str_eq(blobmsg_name(attr), "instance_mode"))
data->bbf_ctx.instance_mode = get_instance_mode(attr);
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), "instance_mode")) {
int instance_mode = blobmsg_get_u32(attr);
data->bbf_ctx.instance_mode = get_instance_mode(instance_mode);
}
if (is_str_eq(blobmsg_name(attr), "transaction_id"))
data->trans_id = blobmsg_get_u32(attr);
@ -1185,82 +1165,69 @@ static void periodic_instance_updater(struct uloop_timeout *t)
fork_instance_checker(u);
}
static int usp_get_config(void)
static int bbfdm_load_config(const char *json_path)
{
struct uci_context *ctx = NULL;
struct uci_package *pkg = NULL;
struct uci_element *e = NULL;
json_object *json_obj = NULL;
char *opt_val = NULL;
ctx = uci_alloc_context();
if (!ctx)
if (!json_path || !strlen(json_path))
return -1;
if (uci_load(ctx, "bbfdm", &pkg)) {
uci_free_context(ctx);
json_obj = json_object_from_file(json_path);
if (!json_obj)
return -1;
opt_val = dmjson_get_value(json_obj, 3, "daemon", "config", "loglevel");
if (opt_val && strlen(opt_val)) {
uint8_t log_level = (uint8_t) strtoul(opt_val, NULL, 10);
set_debug_level(log_level);
}
uci_foreach_element(&pkg->sections, e) {
struct uci_section *s = uci_to_section(e);
if (s == NULL || s->type == NULL)
continue;
if (strcmp(s->type, "bbfdmd") == 0) {
struct uci_option *opn = NULL;
opn = uci_lookup_option(ctx, s, "loglevel");
if (opn) {
uint8_t log_level = (uint8_t) strtoul(opn->v.string, NULL, 10);
set_debug_level(log_level);
}
opn = uci_lookup_option(ctx, s, "subprocess_level");
if (opn) {
g_subprocess_level = (unsigned int) strtoul(opn->v.string, NULL, 10);
}
opn = uci_lookup_option(ctx, s, "refresh_time");
if (opn) {
unsigned int refresh_time = (unsigned int) strtoul(opn->v.string, NULL, 10);
g_refresh_time = refresh_time * 1000;
}
}
opt_val = dmjson_get_value(json_obj, 3, "daemon", "config", "refresh_time");
if (opt_val && strlen(opt_val)) {
unsigned int refresh_time = (unsigned int) strtoul(opt_val, NULL, 10);
g_refresh_time = refresh_time * 1000;
}
uci_unload(ctx, pkg);
uci_free_context(ctx);
opt_val = dmjson_get_value(json_obj, 3, "daemon", "config", "transaction_timeout");
if (opt_val && strlen(opt_val)) {
int trans_timeout = (int) strtol(opt_val, NULL, 10);
configure_transaction_timeout(trans_timeout);
}
opt_val = dmjson_get_value(json_obj, 3, "daemon", "config", "subprocess_level");
if (opt_val && strlen(opt_val)) {
g_subprocess_level = (unsigned int) strtoul(opt_val, NULL, 10);
}
json_object_put(json_obj);
return 0;
}
static int usp_init(struct usp_context *u)
{
int ret;
ret = usp_get_config();
if (ret)
return ret;
INFO("Registering ubus objects....");
ret = ubus_add_object(&u->ubus_ctx, &bbf_object);
return ret;
return ubus_add_object(&u->ubus_ctx, &bbf_object);
}
int main(int argc, char **argv)
{
struct usp_context usp_ctx;
const char *input_json = DEFAULT_JSON_INPUT;
const char *ubus_socket = NULL;
int ret = 0, ch;
int err = 0, ch;
while ((ch = getopt(argc, argv, "hs:t:")) != -1) {
while ((ch = getopt(argc, argv, "hs:I:c:")) != -1) {
switch (ch) {
case 's':
ubus_socket = optarg;
break;
case 't':
configure_transaction_timeout(strtol(optarg, NULL, 10));
case 'I':
input_json = optarg;
break;
case 'c':
err = bbfdm_cli_exec_command(input_json, argc-optind+1, &argv[optind-1]);
exit(err);
case 'h':
usage(argv[0]);
exit(0);
@ -1269,6 +1236,12 @@ int main(int argc, char **argv)
}
}
err = bbfdm_load_config(input_json);
if (err != UBUS_STATUS_OK) {
fprintf(stderr, "Failed to load bbfdm config from json file '%s'\n", input_json);
return -1;
}
openlog("bbfdm", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
memset(&usp_ctx, 0, sizeof(struct usp_context));
@ -1279,26 +1252,23 @@ int main(int argc, char **argv)
uloop_init();
ret = ubus_connect_ctx(&usp_ctx.ubus_ctx, ubus_socket);
if (ret != UBUS_STATUS_OK) {
err = ubus_connect_ctx(&usp_ctx.ubus_ctx, ubus_socket);
if (err != UBUS_STATUS_OK) {
fprintf(stderr, "Failed to connect to ubus\n");
return -1;
}
signal_init();
ret = register_events_to_ubus(&usp_ctx.ubus_ctx, &usp_ctx.event_handlers);
if (ret != 0) {
err = register_events_to_ubus(&usp_ctx.ubus_ctx, &usp_ctx.event_handlers);
if (err != 0)
goto exit;
}
ubus_add_uloop(&usp_ctx.ubus_ctx);
ret = usp_init(&usp_ctx);
if (ret != UBUS_STATUS_OK) {
ret = UBUS_STATUS_UNKNOWN_ERROR;
err = usp_init(&usp_ctx);
if (err != UBUS_STATUS_OK)
goto exit;
}
usp_ctx.schema_timer.cb = periodic_schema_updater;
uloop_timeout_set(&usp_ctx.schema_timer, BBF_SCHEMA_UPDATE_TIMEOUT);
@ -1317,5 +1287,5 @@ exit:
usp_cleanup(&usp_ctx);
closelog();
return ret;
return err;
}

677
bbfdmd/src/cli.c Normal file
View file

@ -0,0 +1,677 @@
/*
* cli.c: Cli command for bbfdmd
*
* Copyright (C) 2023 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <stdlib.h>
#include <stdio.h>
#include "common.h"
#include "libbbfdm-api/dmapi.h"
#include "libbbfdm-api/dmjson.h"
#include "libbbfdm-api/dmentry.h"
#define UNUSED __attribute__((unused))
typedef struct {
struct dmctx bbf_ctx;
unsigned int instance_mode;
unsigned int proto;
char in_type[128];
char in_name[128];
char out_type[128];
char *cmd;
bool ubus_status;
} client_data_t;
typedef struct {
char *name;
int num_args;
int (*exec_cmd)(client_data_t *client_data, char *argv[]);
char *usage;
} cli_cmd_t;
static int cli_exec_help(client_data_t *client_data UNUSED, char *argv[] UNUSED);
static int cli_exec_get(client_data_t *client_data, char *argv[]);
static int cli_exec_set(client_data_t *client_data, char *argv[]);
static int cli_exec_add(client_data_t *client_data, char *argv[]);
static int cli_exec_del(client_data_t *client_data, char *argv[]);
static int cli_exec_instances(client_data_t *client_data, char *argv[]);
static int cli_exec_schema(client_data_t *client_data, char *argv[]);
cli_cmd_t cli_commands[] = {
// Name NumArgs Exec callback Usage String
{ "help", 0, cli_exec_help, "help" },
{ "get", 1, cli_exec_get, "get [path-expr]" },
{ "set", 2, cli_exec_set, "set [path-expr] [value]"},
{ "add", 1, cli_exec_add, "add [object]"},
{ "del", 1, cli_exec_del, "del [path-expr]"},
{ "instances", 1, cli_exec_instances, "instances [path-expr]" },
{ "schema", 1, cli_exec_schema, "schema [path-expr]"},
};
typedef void (*__ubus_cb)(struct ubus_request *req, int type, struct blob_attr *msg);
static int bbfdm_ubus_invoke(const char *obj, const char *method, struct blob_attr *msg, __ubus_cb bbfdm_ubus_callback, void *callback_arg)
{
struct ubus_context *ctx = NULL;
uint32_t id;
int rc = 0;
ctx = ubus_connect(NULL);
if (ctx == NULL) {
printf("Can't create ubus context\n");
return -1;
}
if (!ubus_lookup_id(ctx, obj, &id))
rc = ubus_invoke(ctx, id, method, msg, bbfdm_ubus_callback, callback_arg, 20000);
else
rc = -1;
ubus_free(ctx);
ctx = NULL;
return rc;
}
static struct blob_attr *get_results_array(struct blob_attr *msg)
{
struct blob_attr *tb[1] = {0};
const struct blobmsg_policy p[1] = {
{ "results", BLOBMSG_TYPE_ARRAY }
};
if (msg == NULL)
return NULL;
blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
return tb[0];
}
static void __ubus_callback(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
{
struct blob_attr *cur = NULL;
bool print_msg = false;
int rem = 0;
const struct blobmsg_policy p[6] = {
{ "path", BLOBMSG_TYPE_STRING },
{ "data", BLOBMSG_TYPE_STRING },
{ "type", BLOBMSG_TYPE_STRING },
{ "fault", BLOBMSG_TYPE_INT32 },
{ "input", BLOBMSG_TYPE_ARRAY },
{ "output", BLOBMSG_TYPE_ARRAY },
};
if (msg == NULL || req == NULL)
return;
client_data_t *client_data = (client_data_t *)req->priv;
struct blob_attr *parameters = get_results_array(msg);
if (parameters == NULL) {
client_data->ubus_status = false;
return;
}
blobmsg_for_each_attr(cur, parameters, rem) {
struct blob_attr *tb[6] = {0};
blobmsg_parse(p, 6, tb, blobmsg_data(cur), blobmsg_len(cur));
char *name = tb[0] ? blobmsg_get_string(tb[0]) : "";
char *data = tb[1] ? blobmsg_get_string(tb[1]) : "";
if (tb[3]) {
printf("ERROR: %u retrieving %s\n", blobmsg_get_u32(tb[3]), name);
client_data->ubus_status = false;
return;
}
if (strcmp(client_data->cmd, "get") == 0)
printf("%s => %s\n", name, data);
else if (strcmp(client_data->cmd, "set") == 0) {
printf("%s => Set value is successfully done\n", name);
} else if (strcmp(client_data->cmd, "add") == 0) {
printf("Added %s%s.\n", name, data);
} else if (strcmp(client_data->cmd, "del") == 0) {
printf("Deleted %s\n", name);
} else if (strcmp(client_data->cmd, "instances") == 0) {
printf("%s\n", name);
} else if (strcmp(client_data->cmd, "schema") == 0) {
char *type = tb[2] ? blobmsg_get_string(tb[2]) : "";
int cmd = get_dm_type(type);
if (print_msg == false) {
printf("\nDumping %s Schema...\n\n", name);
print_msg = true;
}
printf("%s\n", name);
if (cmd == DMT_COMMAND) {
struct blob_attr *input = tb[4];
struct blob_attr *output = tb[5];
struct blob_attr *in_cur = NULL, *out_cur = NULL;
int in_rem = 0, out_rem = 0;
if (input) {
blobmsg_for_each_attr(in_cur, input, in_rem) {
struct blob_attr *in_tb[6] = {0};
blobmsg_parse(p, 6, in_tb, blobmsg_data(in_cur), blobmsg_len(in_cur));
char *arg = in_tb[0] ? blobmsg_get_string(in_tb[0]) : "";
printf("%s input:%s\n", name, arg);
}
}
if (output) {
blobmsg_for_each_attr(out_cur, output, out_rem) {
struct blob_attr *out_tb[6] = {0};
blobmsg_parse(p, 6, out_tb, blobmsg_data(out_cur), blobmsg_len(out_cur));
char *arg = out_tb[0] ? blobmsg_get_string(out_tb[0]) : "";
printf("%s output:%s\n", name, arg);
}
}
} else if (cmd == DMT_EVENT) {
struct blob_attr *input = tb[4];
struct blob_attr *in_cur = NULL;
int in_rem = 0;
if (input) {
blobmsg_for_each_attr(in_cur, input, in_rem) {
struct blob_attr *in_tb[6] = {0};
blobmsg_parse(p, 6, in_tb, blobmsg_data(in_cur), blobmsg_len(in_cur));
char *arg = in_tb[0] ? blobmsg_get_string(in_tb[0]) : "";
printf("%s event_arg:%s\n", name, arg);
}
}
}
}
client_data->ubus_status = true;
}
}
static int in_ubus_out_cli_exec_cmd(client_data_t *client_data, const char *path, const char *value)
{
struct blob_buf b = {0};
void *table = NULL;
int err = EXIT_SUCCESS;
memset(&b, 0, sizeof(struct blob_buf));
blob_buf_init(&b, 0);
blobmsg_add_string(&b, "path", path);
if (value) blobmsg_add_string(&b, "value", value);
table = blobmsg_open_table(&b, "optional");
blobmsg_add_string(&b, "proto", (client_data->proto == BBFDM_CWMP) ? "cwmp" : "usp");
blobmsg_add_string(&b, "format", "raw");
blobmsg_add_u32(&b, "instance_mode", client_data->instance_mode);
blobmsg_close_table(&b, table);
int e = bbfdm_ubus_invoke(client_data->in_name, client_data->cmd, b.head, __ubus_callback, client_data);
if (e < 0) {
printf("ERROR: ubus invoke for [object:%s method:%s] exit with error(%d)\n", client_data->in_name, client_data->cmd, e);
err = EXIT_FAILURE;
}
if (client_data->ubus_status == false) {
printf("ERROR: ubus call for [object:%s method:%s] exit with error\n", client_data->in_name, client_data->cmd);
err = EXIT_FAILURE;
}
blob_buf_free(&b);
return err;
}
static int bbfdm_load_client_config(const char *json_path, client_data_t *client_data)
{
json_object *json_obj = NULL;
char *opt_val = NULL;
if (!json_path || !strlen(json_path)) {
printf("ERROR: json file not specified\n");
return -1;
}
json_obj = json_object_from_file(json_path);
if (!json_obj) {
printf("ERROR: not possible to load json file (%s)\n", json_path);
return -1;
}
opt_val = dmjson_get_value(json_obj, 3, "client", "config", "proto");
if (opt_val && strlen(opt_val)) {
client_data->proto = get_proto_type(opt_val);
} else {
client_data->proto = BBFDM_BOTH;
}
opt_val = dmjson_get_value(json_obj, 3, "client", "config", "instance_mode");
if (opt_val && strlen(opt_val)) {
int inst_mode = (int) strtol(opt_val, NULL, 10);
client_data->instance_mode = get_instance_mode(inst_mode);
} else {
client_data->instance_mode = INSTANCE_MODE_NUMBER;
}
opt_val = dmjson_get_value(json_obj, 3, "client", "input", "type");
if (opt_val && strlen(opt_val)) {
snprintf(client_data->in_type, sizeof(client_data->in_type), "%s", opt_val);
} else {
printf("ERROR: [client.input.type] not specified\n");
return -1;
}
opt_val = dmjson_get_value(json_obj, 3, "client", "input", "name");
if (opt_val && strlen(opt_val)) {
snprintf(client_data->in_name, sizeof(client_data->in_name), "%s", opt_val);
} else {
printf("ERROR: [client.input.name] not specified\n");
return -1;
}
opt_val = dmjson_get_value(json_obj, 3, "client", "output", "type");
if (opt_val && strlen(opt_val)) {
snprintf(client_data->out_type, sizeof(client_data->out_type), "%s", opt_val);
} else {
printf("ERROR: [client.output.type] not specified\n");
return -1;
}
json_object_put(json_obj);
return 0;
}
static int cli_exec_help(client_data_t *client_data UNUSED, char *argv[] UNUSED)
{
cli_cmd_t *cli_cmd;
printf("Valid commands:\n");
// Print out the help usage of all commands
for (size_t i = 0; i < ARRAY_SIZE(cli_commands); i++) {
cli_cmd = &cli_commands[i];
printf(" %s\n", cli_cmd->usage);
}
return EXIT_SUCCESS;
}
static int in_dotso_out_cli_exec_get(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
client_data->bbf_ctx.in_param = argv[0];
err = bbf_entry_method(&client_data->bbf_ctx, BBF_GET_VALUE);
if (!err) {
struct dm_parameter *n;
list_for_each_entry(n, &client_data->bbf_ctx.list_parameter, list) {
printf("%s => %s\n", n->name, n->data);
}
} else {
printf("ERROR: %d retrieving %s\n", err, client_data->bbf_ctx.in_param);
err = EXIT_FAILURE;
}
return err;
}
static int in_ubus_out_cli_exec_get(client_data_t *client_data, char *argv[])
{
return in_ubus_out_cli_exec_cmd(client_data, argv[0], argv[1]);
}
static int cli_exec_get(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
if (strcasecmp(client_data->in_type, "DotSO") == 0)
err = in_dotso_out_cli_exec_get(client_data, argv);
else if (strcasecmp(client_data->in_type, "UBUS") == 0)
err = in_ubus_out_cli_exec_get(client_data, argv);
else
err = EXIT_FAILURE;
return err;
}
static int in_dotso_out_cli_exec_set(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
client_data->bbf_ctx.in_param = argv[0];
client_data->bbf_ctx.in_value = argv[1];
err = bbf_entry_method(&client_data->bbf_ctx, BBF_SET_VALUE);
if (!err) {
printf("%s => Set value is successfully done\n", client_data->bbf_ctx.in_param);
bbf_entry_restart_services(NULL, true);
} else {
printf("ERROR: %d retrieving %s => %s\n", err, client_data->bbf_ctx.in_param, client_data->bbf_ctx.in_value);
bbf_entry_revert_changes(NULL);
err = EXIT_FAILURE;
}
return err;
}
static int in_ubus_out_cli_exec_set(client_data_t *client_data, char *argv[])
{
return in_ubus_out_cli_exec_cmd(client_data, argv[0], argv[1]);
}
static int cli_exec_set(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
if (strcasecmp(client_data->in_type, "DotSO") == 0)
err = in_dotso_out_cli_exec_set(client_data, argv);
else if (strcasecmp(client_data->in_type, "UBUS") == 0)
err = in_ubus_out_cli_exec_set(client_data, argv);
else
err = EXIT_FAILURE;
return err;
}
static int in_dotso_out_cli_exec_add(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
client_data->bbf_ctx.in_param = argv[0];
err = bbf_entry_method(&client_data->bbf_ctx, BBF_ADD_OBJECT);
if (!err) {
printf("Added %s%s.\n", client_data->bbf_ctx.in_param, client_data->bbf_ctx.addobj_instance);
bbf_entry_restart_services(NULL, true);
} else {
printf("ERROR: %d retrieving %s\n", err, client_data->bbf_ctx.in_param);
bbf_entry_revert_changes(NULL);
err = EXIT_FAILURE;
}
return err;
}
static int in_ubus_out_cli_exec_add(client_data_t *client_data, char *argv[])
{
return in_ubus_out_cli_exec_cmd(client_data, argv[0], argv[1]);
}
static int cli_exec_add(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
if (strcasecmp(client_data->in_type, "DotSO") == 0)
err = in_dotso_out_cli_exec_add(client_data, argv);
else if (strcasecmp(client_data->in_type, "UBUS") == 0)
err = in_ubus_out_cli_exec_add(client_data, argv);
else
err = EXIT_FAILURE;
return err;
}
static int in_dotso_out_cli_exec_del(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
client_data->bbf_ctx.in_param = argv[0];
err = bbf_entry_method(&client_data->bbf_ctx, BBF_DEL_OBJECT);
if (!err) {
printf("Deleted %s\n", client_data->bbf_ctx.in_param);
bbf_entry_restart_services(NULL, true);
} else {
printf("ERROR: %d retrieving %s\n", err, client_data->bbf_ctx.in_param);
bbf_entry_revert_changes(NULL);
err = EXIT_FAILURE;
}
return err;
}
static int in_ubus_out_cli_exec_del(client_data_t *client_data, char *argv[])
{
return in_ubus_out_cli_exec_cmd(client_data, argv[0], argv[1]);
}
static int cli_exec_del(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
if (strcasecmp(client_data->in_type, "DotSO") == 0)
err = in_dotso_out_cli_exec_del(client_data, argv);
else if (strcasecmp(client_data->in_type, "UBUS") == 0)
err = in_ubus_out_cli_exec_del(client_data, argv);
else
err = EXIT_FAILURE;
return err;
}
static int in_dotso_out_cli_exec_instances(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
client_data->bbf_ctx.in_param = argv[0];
client_data->bbf_ctx.nextlevel = false;
err = bbf_entry_method(&client_data->bbf_ctx, BBF_INSTANCES);
if (!err) {
struct dm_parameter *n;
list_for_each_entry(n, &client_data->bbf_ctx.list_parameter, list) {
printf("%s\n", n->name);
}
} else {
printf("ERROR: %d retrieving %s\n", err, client_data->bbf_ctx.in_param);
err = EXIT_FAILURE;
}
return err;
}
static int in_ubus_out_cli_exec_instances(client_data_t *client_data, char *argv[])
{
return in_ubus_out_cli_exec_cmd(client_data, argv[0], argv[1]);
}
static int cli_exec_instances(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
if (strcasecmp(client_data->in_type, "DotSO") == 0)
err = in_dotso_out_cli_exec_instances(client_data, argv);
else if (strcasecmp(client_data->in_type, "UBUS") == 0)
err = in_ubus_out_cli_exec_instances(client_data, argv);
else
err = EXIT_FAILURE;
return err;
}
static int in_dotso_out_cli_exec_schema(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
client_data->bbf_ctx.in_param = argv[0];
client_data->bbf_ctx.nextlevel = false;
client_data->bbf_ctx.iscommand = true;
client_data->bbf_ctx.isevent = true;
client_data->bbf_ctx.isinfo = true;
err = bbf_entry_method(&client_data->bbf_ctx, BBF_SCHEMA);
if (!err) {
struct dm_parameter *n;
printf("\nDumping %s Schema...\n\n", client_data->bbf_ctx.in_param);
list_for_each_entry(n, &client_data->bbf_ctx.list_parameter, list) {
int cmd = get_dm_type(n->type);
printf("%s\n", n->name);
if (cmd == DMT_COMMAND) {
if (n->data) {
const char **in, **out;
operation_args *args;
int i;
args = (operation_args *) n->data;
in = args->in;
if (in) {
for (i = 0; in[i] != NULL; i++)
printf("%s input:%s\n", n->name, in[i]);
}
out = args->out;
if (out) {
for (i = 0; out[i] != NULL; i++)
printf("%s output:%s\n", n->name, out[i]);
}
}
} else if (cmd == DMT_EVENT) {
if (n->data) {
event_args *ev;
ev = (event_args *)n->data;
if (ev->param) {
const char **in = ev->param;
for (int i = 0; in[i] != NULL; i++)
printf("%s event_arg:%s\n", n->name, in[i]);
}
}
}
}
} else {
printf("ERROR: %d retrieving %s\n", err, client_data->bbf_ctx.in_param);
err = EXIT_FAILURE;
}
return err;
}
static int in_ubus_out_cli_exec_schema(client_data_t *client_data, char *argv[])
{
return in_ubus_out_cli_exec_cmd(client_data, argv[0], argv[1]);
}
static int cli_exec_schema(client_data_t *client_data, char *argv[])
{
int err = EXIT_SUCCESS;
if (strcasecmp(client_data->in_type, "DotSO") == 0)
err = in_dotso_out_cli_exec_schema(client_data, argv);
else if (strcasecmp(client_data->in_type, "UBUS") == 0)
err = in_ubus_out_cli_exec_schema(client_data, argv);
else
err = EXIT_FAILURE;
return err;
}
static int cli_exec_command(client_data_t *client_data, int argc, char *argv[])
{
cli_cmd_t *cli_cmd = NULL;
int err = EXIT_SUCCESS;
bool registred_command = false;
client_data->cmd = argv[0];
if (!client_data->cmd || strlen(client_data->cmd) == 0)
return EXIT_FAILURE;
if (strcasecmp(client_data->in_type, "DotSO") == 0) {
bbf_ctx_init(&client_data->bbf_ctx, DM_ROOT_OBJ, DM_VENDOR_EXTENSION, DM_VENDOR_EXTENSION_EXCLUDE);
client_data->bbf_ctx.dm_type = client_data->proto;
client_data->bbf_ctx.instance_mode = client_data->instance_mode;
}
for (size_t i = 0; i < ARRAY_SIZE(cli_commands); i++) {
cli_cmd = &cli_commands[i];
if (strcmp(client_data->cmd, cli_cmd->name)==0) {
if (argc-1 < cli_cmd->num_args) {
printf("ERROR: Number of arguments for %s method is wrong(%d), it should be %d\n", cli_cmd->name, argc-1, cli_cmd->num_args);
cli_commands[0].exec_cmd(client_data, NULL);
err = EXIT_FAILURE;
goto end;
}
err = cli_cmd->exec_cmd(client_data, &argv[1]);
registred_command = true;
break;
}
}
if (!registred_command) {
printf("ERROR: Unknown command: %s\n", client_data->cmd);
cli_commands[0].exec_cmd(client_data, NULL);
return EXIT_FAILURE;
}
end:
if (strcasecmp(client_data->in_type, "DotSO") == 0) {
bbf_ctx_clean(&client_data->bbf_ctx);
bbf_global_clean(DM_ROOT_OBJ);
}
return err;
}
int bbfdm_cli_exec_command(const char *json_path, int argc, char *argv[])
{
client_data_t client_data = {0};
int err = EXIT_SUCCESS;
memset(&client_data, 0, sizeof(client_data_t));
err = bbfdm_load_client_config(json_path, &client_data);
if (err)
return EXIT_FAILURE;
// Exit if no command specified
if (argc < 1) {
printf("ERROR: command name not specified\n");
return EXIT_FAILURE;
}
return cli_exec_command(&client_data, argc, argv);
}

23
bbfdmd/src/cli.h Normal file
View file

@ -0,0 +1,23 @@
/*
* cli.h: Cli command for bbfdmd
*
* Copyright (C) 2023 iopsys Software Solutions AB. All rights reserved.
*
* Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
int bbfdm_cli_exec_command(const char *json_path, int argc, char *argv[]);

View file

@ -181,7 +181,7 @@ bool validate_msglen(usp_data_t *data)
return true;
}
int bbf_get_dm_type(char *dm_type)
int get_dm_type(char *dm_type)
{
if (dm_type == NULL)
return DMT_STRING;
@ -213,3 +213,27 @@ int bbf_get_dm_type(char *dm_type)
return DMT_STRING;
}
int get_proto_type(const char *proto)
{
int type = BBFDM_BOTH;
if (proto) {
if (is_str_eq("cwmp", proto))
type = BBFDM_CWMP;
else if (is_str_eq("usp", proto))
type = BBFDM_USP;
else
type = BBFDM_BOTH;
}
return type;
}
int get_instance_mode(int instance_mode)
{
if (instance_mode > INSTANCE_MODE_ALIAS)
instance_mode = INSTANCE_MODE_NUMBER;
return instance_mode;
}

View file

@ -34,6 +34,10 @@
#define USP_ERR_OK 0
extern DMOBJ *DM_ROOT_OBJ;
extern DM_MAP_VENDOR *DM_VENDOR_EXTENSION[2];
extern DM_MAP_VENDOR_EXCLUDE *DM_VENDOR_EXTENSION_EXCLUDE;
bool match(const char *string, const char *pattern);
bool is_str_eq(const char *s1, const char *s2);
bool is_node_instance(char *path);
@ -46,7 +50,10 @@ void print_info(const char *format, ...);
void print_debug(const char *format, ...);
bool get_boolean_string(char *value);
bool validate_msglen(usp_data_t *data);
int bbf_get_dm_type(char *dm_type);
int get_dm_type(char *dm_type);
int get_proto_type(const char *proto);
int get_instance_mode(int instance_mode);
#define DEBUG(fmt, args...) \
print_debug("[%s:%d]"fmt, __func__, __LINE__, ##args)

View file

@ -342,7 +342,7 @@ int bbf_dm_get_supported_dm(usp_data_t *data)
INFO("Preparing result for(%s)", data->bbf_ctx.in_param);
list_for_each_entry(param, &data->bbf_ctx.list_parameter, list) {
int cmd = bbf_get_dm_type(param->type);
int cmd = get_dm_type(param->type);
void *table = blobmsg_open_table(&data->bb, NULL);
if (cmd == DMT_COMMAND) {

View file

@ -19,8 +19,6 @@ struct pathNode {
char path[MAX_DM_PATH];
};
extern DMOBJ *DM_ROOT_OBJ;
void handle_pending_signal(int sig);
void print_last_dm_object(void);

View file

@ -205,7 +205,7 @@ static void add_data_blob(struct blob_buf *bb, char *param, char *value, char *t
return;
DEBUG("# Adding BLOB (%s)::(%s)", param, value);
switch (bbf_get_dm_type(type)) {
switch (get_dm_type(type)) {
case DMT_UNINT:
blobmsg_add_u64(bb, param, (uint32_t)strtoul(value, NULL, 10));
break;

View file

@ -1,6 +1,6 @@
# BBFDM Dynamic Object/Parameter/Operate/Event
`bbf_dm` library allows all applications installed on the box to import its own Data Model parameters at run time in two formats:
`bbfdm` library allows all applications installed on the box to import its own Data Model parameters at run time in two formats:
- **Shared library**

View file

@ -16,41 +16,41 @@ supervisorctl status all
function run_valgrind()
{
echo "Running # bbf_dm $@ #"
exec_cmd valgrind -q --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes ./test/bbf_test/bbf_dm $@
echo "Running # bbfdmd $@ #"
exec_cmd valgrind -q --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes /usr/sbin/bbfdmd $@
}
function run_valgrind_verbose()
{
echo "Running # bbf_dm $@ #"
exec_cmd_verbose valgrind -q --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes ./test/bbf_test/bbf_dm $@
echo "Running # bbfdmd $@ #"
exec_cmd_verbose valgrind -q --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes /usr/sbin/bbfdmd $@
}
function run_valgrind_redirect()
{
echo "Running # bbf_dm $@ #" >> output-report-device-get.txt
exec_cmd_verbose valgrind -q --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes ./test/bbf_test/bbf_dm $@ | tee -a output-report-device-get.txt
echo "Running # bbfdmd $@ #" >> output-report-device-get.txt
exec_cmd_verbose valgrind -q --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes /usr/sbin/bbfdmd $@ | tee -a output-report-device-get.txt
}
echo "Running memory check on datamodel"
run_valgrind_verbose -u get Device.RootDataModelVersion
run_valgrind_verbose -c get Device.RootDataModelVersion
run_valgrind -u get Device.
run_valgrind -c get Device.
run_valgrind -u instances Device.
run_valgrind -c instances Device.
run_valgrind -u schema Device.
run_valgrind -c schema Device.
run_valgrind_verbose -u get Device.IP.Interface.*.IPv4Address.
run_valgrind_redirect -c get Device.
run_valgrind_redirect -c schema Device.
run_valgrind_verbose -c get Device.RootDataModelVersion
run_valgrind_verbose -c get Device.IP.Interface.*.IPv6Address.*.IPAddress
run_valgrind_redirect -u get Device.
run_valgrind_redirect -c get Device.
run_valgrind -c set Device.WiFi.SSID.1.Enable 1
run_valgrind -c add Device.WiFi.SSID.
run_valgrind -c del Device.WiFi.SSID.3.
supervisorctl stop all
supervisorctl status

View file

@ -78,10 +78,6 @@ echo "Validate datamodel_default generated XML file"
xmllint --schema test/tools/cwmp-datamodel-1-8.xsd tools/out/datamodel_default.xml --noout
check_ret $?
echo "********* Validate C File *********"
## TODO
date +%s > timestamp.log
echo "Tools Test :: PASS"

27
json/input.json Executable file
View file

@ -0,0 +1,27 @@
{
"daemon": {
"config": {
},
"input": {
"type": "DotSO",
"name": "/usr/lib/libbbfdm.so"
},
"output": {
"type": "UBUS",
"name": "bbfdm"
}
},
"client": {
"config": {
"proto": "usp",
"instance_mode": 0
},
"input": {
"type": "UBUS",
"name": "bbfdm"
},
"output": {
"type": "CLI"
}
}
}

View file

@ -327,7 +327,6 @@ int bbf_entry_method(struct dmctx *ctx, int cmd)
if (!fault)
dmuci_change_packages(&head_package_change);
break;
break;
case BBF_ADD_OBJECT:
fault = dm_entry_add_object(ctx);
if (!fault)

View file

@ -1,22 +1,16 @@
LIB = libbbf_test.so
BIN = bbf_dm
BIN_OBJ = bbf_dm.o
LIB_OBJS = libbbf_test.o
LIB_CFLAGS = $(CFLAGS) -Wall -Werror -fPIC -I /usr/local/include/
LIB_LDFLAGS = $(LDFLAGS) -lbbfdm-api
BIN_LDFLAGS = $(LDFLAGS) -lbbfdm-api -lbbfdm
%.o: %.c
$(CC) $(LIB_CFLAGS) $(FPIC) -c -o $@ $<
all: $(LIB) $(BIN)
all: $(LIB)
$(LIB): $(LIB_OBJS)
$(CC) $(LIB_CFLAGS) $(LIB_LDFLAGS) -shared -o $@ $^
$(BIN): $(BIN_OBJ)
$(CC) -o $@ $^ $(BIN_LDFLAGS)
clean:
rm -fv *.o $(LIB) $(BIN)
rm -fv *.o $(LIB)

View file

@ -1,112 +0,0 @@
#include <stdio.h>
#include <libbbfdm-api/dmapi.h>
#include <libbbfdm-api/dmentry.h>
#include <libbbfdm/device.h>
#include <libbbfdm/vendor.h>
static DMOBJ *TR181_ROOT_TREE = tEntryRoot;
static DM_MAP_VENDOR *TR181_VENDOR_EXTENSION[2] = {
tVendorExtension,
tVendorExtensionOverwrite
};
static DM_MAP_VENDOR_EXCLUDE *TR181_VENDOR_EXTENSION_EXCLUDE = tVendorExtensionExclude;
typedef struct {
int id;
char *str;
} cmd_t;
cmd_t CMD[] = {
{ BBF_GET_VALUE, "get"},
{ BBF_SCHEMA, "schema"},
{ BBF_INSTANCES, "instances"},
//{ BBF_SET_VALUE, "set"},
//{ BBF_ADD_OBJECT, "add"},
//{ BBF_DEL_OBJECT, "del"},
//{ BBF_USP_OPERATE, "operate"},
};
static int get_cmd_from_str(char *str)
{
int i, cmd = BBF_GET_VALUE;
for (i = 0; i < ARRAY_SIZE(CMD); i++) {
if (DM_STRCMP(CMD[i].str, str) == 0) {
cmd = CMD[i].id;
break;
}
}
return cmd;
}
static void print_help(char *prog)
{
printf("Valid commands:\n");
printf("%s -c => Run with cwmp proto\n", prog);
printf("%s -u => Run with usp proto\n", prog);
exit(0);
}
int bbf_dm_exec(int argc, char *argv[])
{
struct dmctx bbf_ctx;
int fault = 0;
int cmd = 0;
memset(&bbf_ctx, 0, sizeof(struct dmctx));
cmd = get_cmd_from_str(argv[2]);
bbf_ctx.instance_mode = INSTANCE_MODE_NUMBER;
if (DM_STRCMP(argv[1], "-c") == 0)
bbf_ctx.dm_type = BBFDM_CWMP;
else
bbf_ctx.dm_type = BBFDM_USP;
if (argc > 3 && DM_STRLEN(argv[3]))
bbf_ctx.in_param = argv[3];
if (cmd == 1) {
bbf_ctx.nextlevel = false;
bbf_ctx.iscommand = true;
bbf_ctx.isevent = true;
bbf_ctx.isinfo = true;
}
if (cmd == 2) {
bbf_ctx.nextlevel = false;
}
if (cmd == 3 && argc > 4 && DM_STRLEN(argv[4]))
bbf_ctx.in_value = argv[4];
bbf_ctx_init(&bbf_ctx, TR181_ROOT_TREE, TR181_VENDOR_EXTENSION, TR181_VENDOR_EXTENSION_EXCLUDE);
fault = bbf_entry_method(&bbf_ctx, cmd);
if (!fault) {
struct dm_parameter *n;
list_for_each_entry(n, &bbf_ctx.list_parameter, list) {
printf(" %s::%s::%s\n", n->name, n->data, n->type);
}
} else {
printf("Fault %d\n", fault);
}
bbf_ctx_clean(&bbf_ctx);
return fault;
}
int main(int argc, char *argv[])
{
if (argc < 3)
print_help(argv[0]);
bbf_dm_exec(argc, argv);
bbf_global_clean(TR181_ROOT_TREE);
}

View file

@ -1,5 +0,0 @@
config bbfdmd 'bbfdmd'
option loglevel '1'
option refresh_time '10'
option transaction_timeout '10'