mirror of
https://dev.iopsys.eu/bbf/bbfdm.git
synced 2025-12-10 07:44:39 +01:00
Compare commits
88 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72c3307651 | ||
|
|
8f72146f0f | ||
|
|
a0347e59b6 | ||
|
|
880a7583ca | ||
|
|
adfdb54d62 | ||
|
|
4855d91797 | ||
|
|
1a86b8a443 | ||
|
|
293474bf1f | ||
|
|
165deef400 | ||
|
|
21f4b19cda | ||
|
|
d45e57afd6 | ||
|
|
3e9faeff2b | ||
|
|
a332ebef29 | ||
|
|
69134df069 | ||
|
|
1596a6a8c1 | ||
|
|
acb6b1cbf2 | ||
|
|
ba2fc7dc21 | ||
|
|
252da0b834 | ||
|
|
786863cf0e | ||
|
|
2cd0a1ef6b | ||
|
|
227d121ee5 | ||
|
|
19db3c2d17 | ||
|
|
2f3a0805b0 | ||
|
|
ca8707b320 | ||
|
|
ebe5ada3d0 | ||
|
|
c9af4e50ce | ||
|
|
2e6debc04b | ||
|
|
09f95d7edd | ||
|
|
97320a95ab | ||
|
|
42d1e14a91 | ||
|
|
5d7b4d759e | ||
|
|
16ed3a79e3 | ||
|
|
8c1517dab6 | ||
|
|
51af53e2be | ||
|
|
47087c5941 | ||
|
|
d4bc6eea94 | ||
|
|
a4f6108138 | ||
|
|
7acb5cb2f4 | ||
|
|
b6fdc87df8 | ||
|
|
b9b9743cf0 | ||
|
|
549a296cbf | ||
|
|
8d55820ed1 | ||
|
|
928443c5c8 | ||
|
|
aa74995f9b | ||
|
|
ae1c44524d | ||
|
|
22cc348d27 | ||
|
|
4bfeedf7bc | ||
|
|
ad2602f114 | ||
|
|
25e2d0bab6 | ||
|
|
571a4335a9 | ||
|
|
67c6e502a2 | ||
|
|
cb405b3b88 | ||
|
|
7cce094e03 | ||
|
|
dd0c6433aa | ||
|
|
6cbfa27973 | ||
|
|
08cf6a9725 | ||
|
|
a20a15888b | ||
|
|
79a91515e5 | ||
|
|
cbe727e54e | ||
|
|
d017aa025c | ||
|
|
cba4ccc25c | ||
|
|
3f56ad4595 | ||
|
|
7d89e82de3 | ||
|
|
a32a9a2bfc | ||
|
|
ea3fa033e7 | ||
|
|
c7e783aecd | ||
|
|
c4701f0968 | ||
|
|
ccc15bd9e1 | ||
|
|
2f5059d7de | ||
|
|
805bfc19d7 | ||
|
|
4a3a7ca0b1 | ||
|
|
ab5cde2c42 | ||
|
|
4e32948acc | ||
|
|
0448fbc7e9 | ||
|
|
56937cd498 | ||
|
|
cecb673d82 | ||
|
|
4ec1489532 | ||
|
|
cb75a1e027 | ||
|
|
4d520ceb62 | ||
|
|
45a6d7e0c7 | ||
|
|
e3757b5f37 | ||
|
|
bd9f7db4fb | ||
|
|
09882ba799 | ||
|
|
d8842b9968 | ||
|
|
948a3771c1 | ||
|
|
53fdfcfa28 | ||
|
|
1fa64da324 | ||
|
|
95848b941b |
99 changed files with 60795 additions and 47835 deletions
|
|
@ -4,6 +4,15 @@ variables:
|
|||
COMPILATION_FIXUP: "cmake -DBBF_VENDOR_PREFIX=\\\"X_IOPSYS_EU_\\\" -DBBF_MAX_OBJECT_INSTANCES=255 -DBBFDMD_MAX_MSG_LEN=1048576 -DCMAKE_INSTALL_PREFIX=/"
|
||||
CPPCHECK_OPTIONS: "--suppress=cert-MSC24-C -DBBF_VENDOR_PREFIX=X_IOPSYS_EU_"
|
||||
CPD_OPTIONS: "--exclude ./build/ --minimum-tokens 200"
|
||||
BRANCH: 'devel'
|
||||
|
||||
before_script:
|
||||
- |
|
||||
echo "
|
||||
machine dev.iopsys.eu
|
||||
login gitlab-ci-token
|
||||
password $CI_JOB_TOKEN
|
||||
" > ~/.netrc
|
||||
|
||||
include:
|
||||
- project: 'iopsys/gitlab-ci-pipeline'
|
||||
|
|
@ -15,8 +24,8 @@ include:
|
|||
- if: $CI_COMMIT_BRANCH == "devel"
|
||||
|
||||
stages:
|
||||
- static_code_analysis
|
||||
- unit_test
|
||||
- static_code_analysis
|
||||
- functional_test
|
||||
- deploy
|
||||
|
||||
|
|
@ -26,8 +35,8 @@ run_unit_test:
|
|||
allow_failure: false
|
||||
script:
|
||||
- "./gitlab-ci/pipeline_setup.sh"
|
||||
- "./gitlab-ci/install-dependencies.sh ms"
|
||||
- "./gitlab-ci/setup.sh ms"
|
||||
- "./gitlab-ci/install-dependencies-ms.sh bbfdm"
|
||||
- "./gitlab-ci/setup.sh bbfdm"
|
||||
- "./gitlab-ci/unit-test.sh"
|
||||
|
||||
artifacts:
|
||||
|
|
@ -40,20 +49,14 @@ run_tools_test:
|
|||
image: ${COMMON_IMAGE}
|
||||
allow_failure: false
|
||||
script:
|
||||
- |
|
||||
echo "
|
||||
machine dev.iopsys.eu
|
||||
login gitlab-ci-token
|
||||
password $CI_JOB_TOKEN
|
||||
" > ~/.netrc
|
||||
- "./gitlab-ci/pipeline_setup.sh"
|
||||
- "./gitlab-ci/setup.sh ms"
|
||||
- "./gitlab-ci/install-dependencies-ms.sh tools"
|
||||
- "./gitlab-ci/tools-test.sh"
|
||||
- "./gitlab-ci/generate_supported_dm.sh"
|
||||
|
||||
artifacts:
|
||||
when: always
|
||||
paths:
|
||||
- debug.log
|
||||
- out/datamodel_default.xml
|
||||
- out/datamodel_hdm.xml
|
||||
- out/datamodel.xls
|
||||
|
|
@ -64,8 +67,8 @@ run_libbbfdm_api_functional_test:
|
|||
allow_failure: false
|
||||
script:
|
||||
- "./gitlab-ci/pipeline_setup.sh"
|
||||
- "./gitlab-ci/install-dependencies.sh ms"
|
||||
- "./gitlab-ci/setup.sh ms"
|
||||
- "./gitlab-ci/install-dependencies-ms.sh bbfdm"
|
||||
- "./gitlab-ci/setup.sh bbfdm"
|
||||
- "./gitlab-ci/functional-api-test.sh"
|
||||
|
||||
artifacts:
|
||||
|
|
@ -79,8 +82,8 @@ run_libbbfdm_functional_test:
|
|||
allow_failure: false
|
||||
script:
|
||||
- "./gitlab-ci/pipeline_setup.sh"
|
||||
- "./gitlab-ci/install-dependencies.sh ms"
|
||||
- "./gitlab-ci/setup.sh ms"
|
||||
- "./gitlab-ci/install-dependencies-ms.sh bbfdm"
|
||||
- "./gitlab-ci/setup.sh bbfdm"
|
||||
- "./gitlab-ci/functional-test.sh"
|
||||
|
||||
artifacts:
|
||||
|
|
@ -94,8 +97,8 @@ run_libbbfdm_memory_test:
|
|||
allow_failure: false
|
||||
script:
|
||||
- "./gitlab-ci/pipeline_setup.sh"
|
||||
- "./gitlab-ci/install-dependencies.sh ms"
|
||||
- "./gitlab-ci/setup.sh ms"
|
||||
- "./gitlab-ci/install-dependencies-ms.sh bbfdm"
|
||||
- "./gitlab-ci/setup.sh bbfdm"
|
||||
- "./gitlab-ci/memory-test.sh"
|
||||
artifacts:
|
||||
when: always
|
||||
|
|
@ -109,8 +112,8 @@ run_bbfd_functional_test:
|
|||
allow_failure: false
|
||||
script:
|
||||
- "./gitlab-ci/pipeline_setup.sh"
|
||||
- "./gitlab-ci/install-dependencies.sh ms"
|
||||
- "./gitlab-ci/setup.sh ms"
|
||||
- "./gitlab-ci/install-dependencies-ms.sh bbfdm"
|
||||
- "./gitlab-ci/setup.sh bbfdm"
|
||||
- "./gitlab-ci/bbfdmd-functional-test.sh"
|
||||
artifacts:
|
||||
when: always
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ add_subdirectory(dm-service)
|
|||
set(MY_CC "$ENV{CC}")
|
||||
set(MY_CFLAGS "$ENV{CFLAGS}")
|
||||
set(MY_LDFLAGS "$ENV{LDFLAGS}")
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
|
||||
# Define a custom target to build the utilities using Makefile
|
||||
add_custom_target(
|
||||
|
|
|
|||
85
README.md
85
README.md
|
|
@ -1,49 +1,51 @@
|
|||
# BroadBand Forum Data Models (BBFDM)
|
||||
|
||||
`bbfdm` is a suite to provide TR181 datamodel backend for Higher layer management protocols like [TR-069/CWMP](https://cwmp-data-models.broadband-forum.org/) or [TR-369/USP](https://usp.technology/). It is designed in a hardware agnostic way and provides the available datamodel parameters over ubus on the northbound interface and creates the datamodel mapping based on uci and ubus on southbound interface.
|
||||
`bbfdm` is a suite to provide a TR181 data model backend for higher-layer management protocols like [TR-069/CWMP](https://cwmp-data-models.broadband-forum.org/) or [TR-369/USP](https://usp.technology/). It is designed in a hardware-agnostic way and provides the available data model parameters over Ubus on the northbound interface and creates the data model mapping based on UCI and Ubus on the southbound interface.
|
||||
|
||||
`bbfdm` has five main components:
|
||||
|
||||
| Component | Description |
|
||||
| ------------ | ------------------------------------------------- |
|
||||
| bbfdmd | A daemon to expose data model objects over ubus |
|
||||
| dm-service | A daemon to expose data model objects as micro-service over ubus |
|
||||
| libbbfdm-api | API library to create and parse datamodel tree |
|
||||
| libbbfdm-ubus | API library to expose datamodel over ubus |
|
||||
| libbbfdm | A static library that contains the core data model of TR181 |
|
||||
|
||||
| bbfdmd | A daemon to expose data model objects over Ubus |
|
||||
| dm-service | A daemon to expose data model objects as a microservice over Ubus |
|
||||
| libbbfdm-api | API library to create and parse data model trees |
|
||||
| libbbfdm-ubus | API library to expose data model over Ubus |
|
||||
| libbbfdm | A shared library containing the core data model of TR181, running as a microservice |
|
||||
|
||||
## Directory Structure
|
||||
|
||||
`bbfdm` package is structured as follow:
|
||||
`bbfdm` package is structured as follows:
|
||||
|
||||
```bash
|
||||
├── bbfdmd -- This directory contains daemon code to expose the datamodel tree on northbound
|
||||
│ └── ubus - Daemon to expose datamodel over ubus
|
||||
├── dm-service -- This directory contains daemon code to expose the datamodel tree as micro-service
|
||||
├── docs -- More detailed explanation of datamodel and user guide
|
||||
├── gitlab-ci -- Used for CI/CD pipeline test
|
||||
├── libbbfdm -- Minimal TR181 core datamodel implementation
|
||||
├── libbbfdm-api -- API library to create datamodel definition and parse the datamodel definition to form a datamodel tree
|
||||
├── libbbfdm-ubus -- API library to expose datamodel over ubus
|
||||
├── tools -- Tools to convert xml datamodel definition to json, generate c code and many more
|
||||
└── utilities -- Small helper utilities to complete/optimize the datamodel deployment
|
||||
├── bbfdmd -- This directory contains daemon code to expose the data model tree on the northbound
|
||||
│ └── ubus - Daemon to expose data model over Ubus
|
||||
├── dm-service -- This directory contains daemon code to expose the data model tree as a microservice
|
||||
├── docs -- More detailed explanation of the data model and user guide
|
||||
├── gitlab-ci -- Used for CI/CD pipeline tests
|
||||
├── libbbfdm -- Minimal TR181 core data model implementation
|
||||
├── libbbfdm-api -- API library to create data model definitions and parse the data model definition to form a data model tree
|
||||
│ └── legacy - Legacy version of `libbbfdm-api` containing APIs for creating and parsing data model definitions to build a data model tree
|
||||
│ └── version-2 - New version of `libbbfdm-api` with a more flexible and generic implementation, making it easier to use in dm-service and unified daemon
|
||||
├── libbbfdm-ubus -- API library to expose data model over Ubus
|
||||
├── tools -- Tools to convert XML data model definitions to JSON, generate C code, and more
|
||||
└── utilities -- Small helper utilities to complete/optimize the data model deployment
|
||||
```
|
||||
|
||||
## Important Topics
|
||||
|
||||
* [BBFDMD Design](./docs/guide/bbfdmd.md)
|
||||
* [Micro-Service Design](./docs/guide/dm-service.md)
|
||||
* [LIBBBFDM-API Documentation](./docs/guide/libbbfdm-api.md)
|
||||
* [Microservice Design](./docs/guide/dm-service.md)
|
||||
* [LIBBBFDM-API/Legacy Documentation](./docs/guide/libbbfdm-api-legacy.md)
|
||||
* [LIBBBFDM-API/Vesrion-2 Documentation](./docs/guide/libbbfdm-api-version-2.md)
|
||||
* [LIBBBFDM-UBUS Documentation](./docs/guide/libbbfdm-ubus.md)
|
||||
* [Utilities Documentation](./utilities/README.md)
|
||||
* [Tools](./tools/README.md)
|
||||
* [How to extend datamodel with C Code](./docs/guide/How_to_extend_datamodel_with_C_Code.md)
|
||||
* [How to extend datamodel with JSON](./docs/guide/How_to_extend_datamodel_with_JSON.md)
|
||||
* [How to extend data model with C Code](./docs/guide/How_to_extend_datamodel_with_C_Code.md)
|
||||
* [How to extend data model with JSON](./docs/guide/How_to_extend_datamodel_with_JSON.md)
|
||||
|
||||
## Good To Know
|
||||
|
||||
- The current data model implementation follows the latest version of the data model, version `2.18``.
|
||||
- The current data model implementation follows the latest version of the data model, version `2.18`.
|
||||
|
||||
- Instance alias handling has been moved to the icwmp repository since `bbfdm` repository only supports the common functionality provided by CWMP and USP protocols.
|
||||
|
||||
|
|
@ -52,28 +54,27 @@
|
|||
- The data model implementation uses different directories to store temporary UCI configurations based on the protocol being used. The details are as follows:
|
||||
|
||||
| Protocol | Save Config Directory | Config Directory | Save Dmmap Directory | Dmmap Directory |
|
||||
| -------- | ------------------------ ---------------- | ---------------------- | ---------------- |
|
||||
| -------- | ------------------------ | ---------------- | ---------------------- | ---------------- |
|
||||
| cwmp | /tmp/bbfdm/.cwmp/config | /etc/config | /tmp/bbfdm/.cwmp/dmmap | /etc/bbfdm/dmmap |
|
||||
| usp | /tmp/bbfdm/.usp/config | /etc/config | /tmp/bbfdm/.usp/dmmap | /etc/bbfdm/dmmap |
|
||||
| both | /tmp/bbfdm/.bbfdm/config | /etc/config | /tmp/bbfdm/.cwmp/dmmap | /etc/bbfdm/dmmap |
|
||||
|
||||
|
||||
### Compilation helper utilities
|
||||
### Compilation Helper Utilities
|
||||
|
||||
* [Readme](https://dev.iopsys.eu/feed/iopsys/-/blob/devel/bbfdm/README.md)
|
||||
* [Compilation Helper utility](https://dev.iopsys.eu/feed/iopsys/-/blob/devel/bbfdm/bbfdm.mk)
|
||||
* [Compilation Helper Utility](https://dev.iopsys.eu/feed/iopsys/-/blob/devel/bbfdm/bbfdm.mk)
|
||||
* [JSON Plugin Validator](https://dev.iopsys.eu/feed/iopsys/-/blob/devel/bbfdm/tools/validate_plugins.py)
|
||||
|
||||
## Additional datamodel objects
|
||||
## Additional Data Model Objects
|
||||
|
||||
This repository has bare minimal TR181 datamodel integrated, each service has their own datamodel additions, which they expose using plugins and micro-services.
|
||||
List of IOWRT provided service datamodel set available in [tools_input.json](./tools/tools_input.json)
|
||||
This repository has a bare minimum TR181 data model integrated. Each service has its own data model additions, which they expose using plugins and microservices.
|
||||
A list of IOWRT-provided service data model sets is available in [tools_input.json](./tools/tools_input.json).
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Build-Time Dependencies
|
||||
|
||||
To successfully build bbfdmd, following libraries are needed:
|
||||
To successfully build `bbfdmd`, the following libraries are required:
|
||||
|
||||
| Dependency | Link | License |
|
||||
| ------------ | ------------------------------------------- | -------- |
|
||||
|
|
@ -83,21 +84,19 @@ To successfully build bbfdmd, following libraries are needed:
|
|||
| libjson-c | https://s3.amazonaws.com/json-c_releases | MIT |
|
||||
| libbbfdm-api | https://dev.iopsys.eu/bbf/bbfdm.git | BSD-3 |
|
||||
| libbbfdm-ubus | https://dev.iopsys.eu/bbf/bbfdm.git | BSD-3 |
|
||||
| libbbfdm | https://dev.iopsys.eu/bbf/bbfdm.git | BSD-3 |
|
||||
|
||||
|
||||
### Run-Time Dependencies
|
||||
|
||||
In order to run the `bbfdmd`, following dependencies are needed to be running/available before `bbfdmd`.
|
||||
|
||||
| Dependency | Link | License |
|
||||
| ------------ | ---------------------------------------- | -------- |
|
||||
| ubusd | https://git.openwrt.org/project/ubus.git | LGPL 2.1 |
|
||||
| libbbfdm-api | https://dev.iopsys.eu/bbf/bbfdm.git | BSD-3 |
|
||||
| libbbfdm-ubus | https://dev.iopsys.eu/bbf/bbfdm.git | BSD-3 |
|
||||
| libbbfdm | https://dev.iopsys.eu/bbf/bbfdm.git | BSD-3 |
|
||||
|
||||
In order to run the `dm-service`, following dependencies are needed to be running/available before `dm-service`.
|
||||
To run `bbfdmd`, the following dependencies are required:
|
||||
|
||||
| Dependency | Link | License |
|
||||
| ------------ | ---------------------------------------- | -------- |
|
||||
| ubusd | https://git.openwrt.org/project/ubus.git | LGPL 2.1 |
|
||||
| libubox | https://git.openwrt.org/project/libubox.git | BSD |
|
||||
| libjson-c | https://s3.amazonaws.com/json-c_releases | MIT |
|
||||
| libbbfdm-api/version-2 | https://dev.iopsys.eu/bbf/bbfdm.git | BSD-3 |
|
||||
|
||||
To run `dm-service`, the following dependencies are required:
|
||||
|
||||
| Dependency | Link | License |
|
||||
| ------------ | ---------------------------------------- | -------- |
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <libubus.h>
|
||||
#include <libubox/blobmsg_json.h>
|
||||
|
|
@ -24,9 +25,227 @@
|
|||
#include "get.h"
|
||||
#include "cli.h"
|
||||
|
||||
struct ubus_context g_ubus_ctx = {0};
|
||||
|
||||
extern struct list_head registered_services;
|
||||
extern int g_log_level;
|
||||
|
||||
static void schedule_blacklisted_service_recovery(struct ubus_context *ctx);
|
||||
|
||||
static void blacklisted_recovery_timer_cb(struct uloop_timeout *timeout __attribute__((unused)))
|
||||
{
|
||||
schedule_blacklisted_service_recovery(&g_ubus_ctx);
|
||||
}
|
||||
|
||||
static struct uloop_timeout blacklisted_recovery_timer = {
|
||||
.cb = blacklisted_recovery_timer_cb
|
||||
};
|
||||
|
||||
struct service_request_tracker {
|
||||
struct ubus_context *ubus_ctx;
|
||||
service_entry_t *service;
|
||||
struct ubus_request async_request;
|
||||
struct uloop_timeout timeout;
|
||||
};
|
||||
|
||||
static void service_request_timeout(struct uloop_timeout *timeout)
|
||||
{
|
||||
struct service_request_tracker *tracker = container_of(timeout, struct service_request_tracker, timeout);
|
||||
if (tracker == NULL) {
|
||||
BBFDM_ERR("Timeout occurred but tracker is not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
BBFDM_WARNING("Timeout occurred for request: '%s get'", tracker->service ? tracker->service->name : "unknown");
|
||||
ubus_abort_request(tracker->ubus_ctx, &tracker->async_request);
|
||||
BBFDM_FREE(tracker);
|
||||
}
|
||||
|
||||
static void service_request_complete(struct ubus_request *req, int ret)
|
||||
{
|
||||
struct service_request_tracker *tracker = container_of(req, struct service_request_tracker, async_request);
|
||||
if (tracker == NULL) {
|
||||
BBFDM_ERR("Request completed but tracker is not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
BBFDM_DEBUG("Request completed for '%s get' with status: '%d'", tracker->service ? tracker->service->name : "", ret);
|
||||
uloop_timeout_cancel(&tracker->timeout);
|
||||
|
||||
if (tracker->service && ret == UBUS_STATUS_OK) {
|
||||
tracker->service->is_blacklisted = false;
|
||||
tracker->service->consecutive_timeouts = 0;
|
||||
BBFDM_INFO("Recovered blacklisted service: '%s'", tracker->service->name);
|
||||
} else {
|
||||
BBFDM_DEBUG("Service '%s' still unreachable", tracker->service ? tracker->service->name : "unknown");
|
||||
}
|
||||
|
||||
BBFDM_FREE(tracker);
|
||||
}
|
||||
|
||||
static void verify_service(struct ubus_context *ubus_ctx, service_entry_t *service)
|
||||
{
|
||||
struct blob_buf req_buf = {0};
|
||||
uint32_t id = 0;
|
||||
|
||||
if (!ubus_ctx || !service || !service->name) {
|
||||
BBFDM_WARNING("Invalid arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, service->name, &id)) {
|
||||
BBFDM_INFO("Failed to lookup object: %s", service->name);
|
||||
return;
|
||||
}
|
||||
|
||||
struct service_request_tracker *tracker = (struct service_request_tracker *)calloc(1, sizeof(struct service_request_tracker));
|
||||
if (!tracker) {
|
||||
BBFDM_ERR("Failed to allocate memory for request tracker");
|
||||
return;
|
||||
}
|
||||
|
||||
tracker->ubus_ctx = ubus_ctx;
|
||||
tracker->service = service;
|
||||
|
||||
tracker->timeout.cb = service_request_timeout;
|
||||
uloop_timeout_set(&tracker->timeout, service->timeout);
|
||||
|
||||
memset(&req_buf, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&req_buf, 0);
|
||||
|
||||
blobmsg_add_string(&req_buf, "path", BBFDM_ROOT_OBJECT);
|
||||
|
||||
if (ubus_invoke_async(ubus_ctx, id, "get", req_buf.head, &tracker->async_request)) {
|
||||
BBFDM_WARNING("Failed to invoke async method for object: '%s get'", service->name);
|
||||
uloop_timeout_cancel(&tracker->timeout);
|
||||
BBFDM_FREE(tracker);
|
||||
} else {
|
||||
tracker->async_request.complete_cb = service_request_complete;
|
||||
ubus_complete_request_async(ubus_ctx, &tracker->async_request);
|
||||
}
|
||||
|
||||
blob_buf_free(&req_buf);
|
||||
}
|
||||
|
||||
static void schedule_blacklisted_service_recovery(struct ubus_context *ubus_ctx)
|
||||
{
|
||||
service_entry_t *service = NULL;
|
||||
|
||||
list_for_each_entry(service, ®istered_services, list) {
|
||||
if (service->is_blacklisted) {
|
||||
verify_service(ubus_ctx, service);
|
||||
}
|
||||
}
|
||||
|
||||
int next_check_time = rand_in_range(30, 60) * 1000;
|
||||
BBFDM_DEBUG("Next blacklisted service recovery scheduled in %d msecs", next_check_time);
|
||||
uloop_timeout_set(&blacklisted_recovery_timer, next_check_time);
|
||||
}
|
||||
|
||||
static void stop_blacklisted_service_recovery(void)
|
||||
{
|
||||
uloop_timeout_cancel(&blacklisted_recovery_timer);
|
||||
}
|
||||
|
||||
static void bbfdm_ubus_add_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
|
||||
};
|
||||
service_entry_t *service = NULL;
|
||||
struct blob_attr *attr = NULL;
|
||||
bool service_found = false;
|
||||
const char *path;
|
||||
|
||||
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 && strncmp(path, BBFDM_UBUS_OBJECT".", strlen(BBFDM_UBUS_OBJECT) + 1) == 0) {
|
||||
|
||||
BBFDM_INFO("Detected new service registration: '%s'", path);
|
||||
|
||||
list_for_each_entry(service, ®istered_services, list) {
|
||||
// Check if the service is present in the registred services list
|
||||
if (strcmp(service->name, path) == 0) {
|
||||
service->is_blacklisted = false;
|
||||
service->consecutive_timeouts = 0;
|
||||
service_found = true;
|
||||
fill_service_schema(ctx, 5000, service->name, &service->dm_schema);
|
||||
BBFDM_INFO("Service '%s' found in registry. Resetting blacklist and timeout counters.", path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!service_found) {
|
||||
BBFDM_WARNING("Newly registered service '%s' is not recognized in the registry."
|
||||
" Possible missing configuration JSON file under '%s'.",
|
||||
path, BBFDM_MICROSERVICE_INPUT_PATH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void bbfdm_handle_schema_request(struct ubus_context *ctx, struct ubus_request_data *req,
|
||||
const char *requested_path, unsigned int requested_proto)
|
||||
{
|
||||
struct blob_buf bb = {0};
|
||||
bool schema_found = false;
|
||||
int len = strlen(requested_path);
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
void *array = blobmsg_open_array(&bb, "results");
|
||||
|
||||
if (len > 0 && requested_path[len - 1] == '.') {
|
||||
service_entry_t *service = NULL;
|
||||
|
||||
list_for_each_entry(service, ®istered_services, list) {
|
||||
|
||||
if (service->is_blacklisted ||
|
||||
!service_path_match(requested_path, requested_proto, service) ||
|
||||
!service->dm_schema)
|
||||
continue;
|
||||
|
||||
struct blob_attr *attr = NULL;
|
||||
size_t remaining = 0;
|
||||
const struct blobmsg_policy policy[] = {
|
||||
{ "path", BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
blobmsg_for_each_attr(attr, service->dm_schema->head, remaining) {
|
||||
struct blob_attr *fields[1];
|
||||
|
||||
blobmsg_parse(policy, 1, fields, blobmsg_data(attr), blobmsg_len(attr));
|
||||
|
||||
char *path = fields[0] ? blobmsg_get_string(fields[0]) : "";
|
||||
|
||||
if (strlen(path) == 0)
|
||||
continue;
|
||||
|
||||
if (strncmp(requested_path, path, len) == 0) {
|
||||
blobmsg_add_blob(&bb, attr);
|
||||
schema_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!schema_found)
|
||||
print_fault_message(&bb, requested_path, 7026, "Path is not present in the data model schema");
|
||||
|
||||
blobmsg_close_array(&bb, array);
|
||||
|
||||
ubus_send_reply(ctx, req, bb.head);
|
||||
blob_buf_free(&bb);
|
||||
}
|
||||
|
||||
static const struct blobmsg_policy bbfdm_policy[] = {
|
||||
[BBFDM_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
|
||||
[BBFDM_VALUE] = { .name = "value", .type = BLOBMSG_TYPE_STRING },
|
||||
|
|
@ -39,54 +258,58 @@ static int bbfdm_handler_async(struct ubus_context *ctx, struct ubus_object *obj
|
|||
struct blob_attr *tb[__BBFDM_MAX];
|
||||
service_entry_t *service = NULL;
|
||||
unsigned int requested_proto = BBFDMD_BOTH;
|
||||
bool raw_format = false;
|
||||
|
||||
if (blobmsg_parse(bbfdm_policy, __BBFDM_MAX, tb, blob_data(msg), blob_len(msg))) {
|
||||
BBFDM_ERR("Failed to parse input message");
|
||||
BBFDM_WARNING("Failed to parse input message");
|
||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!tb[BBFDM_PATH]) {
|
||||
BBFDM_ERR("%s: path must be defined", method);
|
||||
BBFDM_WARNING("%s: path must be defined", method);
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
struct async_request_context *context = calloc(1, sizeof(struct async_request_context));
|
||||
char *requested_path = blobmsg_get_string(tb[BBFDM_PATH]);
|
||||
fill_optional_input(tb[BBFDM_INPUT], &requested_proto, &raw_format);
|
||||
|
||||
if (strcmp(method, "schema") == 0 && requested_proto != BBFDMD_CWMP) {
|
||||
BBFDM_INFO("START: ubus method|%s|, name|%s|, path|%s|, proto|%u|", method, obj->name, requested_path, requested_proto);
|
||||
bbfdm_handle_schema_request(ctx, req, requested_path, requested_proto);
|
||||
BBFDM_INFO("END: ubus method|%s|, name|%s|, path|%s|, proto|%u|", method, obj->name, requested_path, requested_proto);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct async_request_context *context = (struct async_request_context *)calloc(1, sizeof(struct async_request_context));
|
||||
if (!context) {
|
||||
BBFDM_ERR("Failed to allocate memory");
|
||||
BBFDM_WARNING("Failed to allocate memory");
|
||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
BBFDM_INFO("ubus method|%s|, name|%s|", method, obj->name);
|
||||
BBFDM_INFO("START: ubus method|%s|, name|%s|, path|%s|, proto|%u|", method, obj->name, requested_path, requested_proto);
|
||||
|
||||
snprintf(context->requested_path, sizeof(context->requested_path), "%s", blobmsg_get_string(tb[BBFDM_PATH]));
|
||||
snprintf(context->requested_path, sizeof(context->requested_path), "%s", requested_path);
|
||||
snprintf(context->ubus_method, sizeof(context->ubus_method), "%s", method);
|
||||
|
||||
context->ubus_ctx = ctx;
|
||||
context->raw_format = raw_format;
|
||||
|
||||
memset(&context->tmp_bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&context->tmp_bb, 0);
|
||||
|
||||
if (strcmp(method, "get") == 0) {
|
||||
INIT_LIST_HEAD(&context->linker_list);
|
||||
|
||||
// Send linker cleanup event for all services
|
||||
send_linker_cleanup_event(ctx);
|
||||
|
||||
// Event handler to wait for linker response
|
||||
context->linker_handler.cb = linker_response_callback;
|
||||
ubus_register_event_handler(ctx, &context->linker_handler, "bbfdm.linker.response");
|
||||
}
|
||||
|
||||
fill_optional_input(tb[BBFDM_INPUT], &requested_proto, &context->raw_format);
|
||||
context->array = blobmsg_open_array(&context->tmp_bb, "results");
|
||||
|
||||
ubus_defer_request(ctx, req, &context->request_data);
|
||||
|
||||
list_for_each_entry(service, ®istered_services, list) {
|
||||
|
||||
if (!is_path_match(context->requested_path, requested_proto, service))
|
||||
if (service->is_blacklisted)
|
||||
continue;
|
||||
|
||||
run_async_call(context, service->name, msg);
|
||||
if (!service_path_match(context->requested_path, requested_proto, service))
|
||||
continue;
|
||||
|
||||
run_async_call(context, service, msg);
|
||||
}
|
||||
|
||||
context->service_list_processed = true;
|
||||
|
|
@ -108,12 +331,12 @@ static int bbfdm_handler_sync(struct ubus_context *ctx, struct ubus_object *obj,
|
|||
struct blob_buf bb = {0};
|
||||
|
||||
if (blobmsg_parse(bbfdm_policy, __BBFDM_MAX, tb, blob_data(msg), blob_len(msg))) {
|
||||
BBFDM_ERR("Failed to parse input message");
|
||||
BBFDM_WARNING("Failed to parse input message");
|
||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
if (!tb[BBFDM_PATH]) {
|
||||
BBFDM_ERR("%s: path must be defined", method);
|
||||
BBFDM_WARNING("%s: path must be defined", method);
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +351,10 @@ static int bbfdm_handler_sync(struct ubus_context *ctx, struct ubus_object *obj,
|
|||
|
||||
list_for_each_entry(service, ®istered_services, list) {
|
||||
|
||||
if (!is_path_match(requested_path, requested_proto, service))
|
||||
if (service->is_blacklisted)
|
||||
continue;
|
||||
|
||||
if (!service_path_match(requested_path, requested_proto, service))
|
||||
continue;
|
||||
|
||||
run_sync_call(service->name, method, msg, &bb);
|
||||
|
|
@ -191,7 +417,10 @@ static void usage(char *prog)
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ubus_context ubus_ctx = {0};
|
||||
struct ubus_event_handler add_event = {
|
||||
.cb = bbfdm_ubus_add_event_cb,
|
||||
};
|
||||
|
||||
char *cli_argv[4] = {0};
|
||||
int err = 0, ch, cli_argc = 0, i;
|
||||
|
||||
|
|
@ -207,7 +436,7 @@ int main(int argc, char **argv)
|
|||
if (optarg) {
|
||||
g_log_level = (int)strtod(optarg, NULL);
|
||||
if (g_log_level < 0 || g_log_level > 7)
|
||||
g_log_level = 3;
|
||||
g_log_level = 7;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
|
|
@ -226,35 +455,44 @@ int main(int argc, char **argv)
|
|||
|
||||
setlogmask(LOG_UPTO(g_log_level));
|
||||
|
||||
err = ubus_connect_ctx(&ubus_ctx, NULL);
|
||||
init_rand_seed(); // Seed the random number generator
|
||||
|
||||
err = ubus_connect_ctx(&g_ubus_ctx, NULL);
|
||||
if (err != UBUS_STATUS_OK) {
|
||||
BBFDM_ERR("Failed to connect to ubus");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uloop_init();
|
||||
ubus_add_uloop(&ubus_ctx);
|
||||
ubus_add_uloop(&g_ubus_ctx);
|
||||
|
||||
err = register_services(&ubus_ctx);
|
||||
err = register_services(&g_ubus_ctx);
|
||||
if (err) {
|
||||
BBFDM_ERR("Failed to load micro-services");
|
||||
goto end;
|
||||
}
|
||||
|
||||
err = ubus_add_object(&ubus_ctx, &bbfdm_object);
|
||||
err = ubus_add_object(&g_ubus_ctx, &bbfdm_object);
|
||||
if (err != UBUS_STATUS_OK) {
|
||||
BBFDM_ERR("Failed to add ubus object: %s", ubus_strerror(err));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (ubus_register_event_handler(&g_ubus_ctx, &add_event, "ubus.object.add"))
|
||||
goto end;
|
||||
|
||||
schedule_blacklisted_service_recovery(&g_ubus_ctx);
|
||||
|
||||
BBFDM_INFO("Waiting on uloop....");
|
||||
uloop_run();
|
||||
|
||||
end:
|
||||
BBFDM_ERR("Free context");
|
||||
BBFDM_DEBUG("BBFDMD exits");
|
||||
stop_blacklisted_service_recovery();
|
||||
ubus_unregister_event_handler(&g_ubus_ctx, &add_event);
|
||||
unregister_services();
|
||||
uloop_done();
|
||||
ubus_shutdown(&ubus_ctx);
|
||||
ubus_shutdown(&g_ubus_ctx);
|
||||
|
||||
closelog();
|
||||
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ static int cli_exec_cmd(cli_data_t *cli_data, const char *path, const char *valu
|
|||
int e = bbfdm_ubus_invoke(BBFDM_UBUS_OBJECT, cli_data->cmd, b.head, __ubus_callback, cli_data);
|
||||
|
||||
if (e < 0) {
|
||||
printf("ERROR: ubus invoke for [object:%s method:%s] exit with error(%d)\n", BBFDM_UBUS_OBJECT, cli_data->cmd, e);
|
||||
printf("ERROR: [bbfdmd-cli] ubus invoke for [object:%s method:%s] exit with error(%d)\n", BBFDM_UBUS_OBJECT, cli_data->cmd, e);
|
||||
err = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,27 @@
|
|||
|
||||
int g_log_level = LOG_ERR;
|
||||
|
||||
void init_rand_seed(void)
|
||||
{
|
||||
srandom((unsigned int)time(NULL));
|
||||
}
|
||||
|
||||
int rand_in_range(int min, int max)
|
||||
{
|
||||
int range;
|
||||
|
||||
if (min >= max)
|
||||
return -1;
|
||||
|
||||
if (min == (max - 1))
|
||||
return min;
|
||||
|
||||
range = max - min;
|
||||
|
||||
return min + ((int)(((double)range) * ((double)random()) /
|
||||
(((double)RAND_MAX) + 1.0)));
|
||||
}
|
||||
|
||||
unsigned int get_proto_type(const char *proto)
|
||||
{
|
||||
int type = BBFDMD_BOTH;
|
||||
|
|
@ -88,11 +109,40 @@ struct blob_attr *get_results_array(struct blob_attr *msg)
|
|||
return tb[0];
|
||||
}
|
||||
|
||||
bool proto_matches(unsigned int dm_type, const enum bbfdmd_type_enum type)
|
||||
bool str_match(const char *string, const char *pattern, size_t nmatch, regmatch_t pmatch[])
|
||||
{
|
||||
regex_t re;
|
||||
|
||||
if (!string || !pattern)
|
||||
return false;
|
||||
|
||||
if (regcomp(&re, pattern, REG_EXTENDED) != 0)
|
||||
return false;
|
||||
|
||||
int status = regexec(&re, string, nmatch, pmatch, 0);
|
||||
|
||||
regfree(&re);
|
||||
|
||||
return (status != 0) ? false : true;
|
||||
}
|
||||
|
||||
bool proto_match(unsigned int dm_type, const enum bbfdmd_type_enum type)
|
||||
{
|
||||
return (dm_type == BBFDMD_BOTH || type == BBFDMD_BOTH || dm_type == type) && type != BBFDMD_NONE;
|
||||
}
|
||||
|
||||
void print_fault_message(struct blob_buf *blob_buf, const char *path, uint32_t fault_code, const char *fault_msg)
|
||||
{
|
||||
if (!blob_buf || !path || !fault_msg)
|
||||
return;
|
||||
|
||||
void *table = blobmsg_open_table(blob_buf, NULL);
|
||||
blobmsg_add_string(blob_buf, "path", path);
|
||||
blobmsg_add_u32(blob_buf, "fault", fault_code);
|
||||
blobmsg_add_string(blob_buf, "fault_msg", fault_msg);
|
||||
blobmsg_close_table(blob_buf, table);
|
||||
}
|
||||
|
||||
static void sync_callback(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *attr = NULL;
|
||||
|
|
@ -111,33 +161,6 @@ static void sync_callback(struct ubus_request *req, int type __attribute__((unus
|
|||
}
|
||||
}
|
||||
|
||||
static void generate_reference_to_set(const char *in_value, char *output_str, size_t output_str_len)
|
||||
{
|
||||
char token_buffer[MAX_VALUE_LENGTH] = {0};
|
||||
char *token = NULL, *saveptr = NULL;
|
||||
unsigned pos = 0;
|
||||
|
||||
if (!output_str || output_str_len == 0)
|
||||
return;
|
||||
|
||||
output_str[0] = '\0'; // Ensure output buffer is initialized
|
||||
|
||||
if (!in_value || in_value[0] == '\0') // Empty value, nothing to make
|
||||
return;
|
||||
|
||||
bbfdm_strncpy(token_buffer, in_value, sizeof(token_buffer));
|
||||
|
||||
for (token = strtok_r(token_buffer, ",", &saveptr); token; token = strtok_r(NULL, ",", &saveptr)) {
|
||||
char *reference_value = get_reference_data(token, "reference_value");
|
||||
pos += snprintf(&output_str[pos], output_str_len - pos, "%s=>%s##,", token, reference_value ? reference_value : "");
|
||||
BBFDM_FREE(reference_value);
|
||||
}
|
||||
|
||||
if (pos > 0) {
|
||||
output_str[pos - 1] = 0; // Remove trailing comma
|
||||
}
|
||||
}
|
||||
|
||||
void run_sync_call(const char *ubus_obj, const char *ubus_method, struct blob_attr *msg, struct blob_buf *bb_response)
|
||||
{
|
||||
struct blob_buf req_buf = {0};
|
||||
|
|
@ -151,37 +174,7 @@ void run_sync_call(const char *ubus_obj, const char *ubus_method, struct blob_at
|
|||
blob_buf_init(&req_buf, 0);
|
||||
|
||||
blob_for_each_attr(attr, msg, remaining) {
|
||||
if (strcmp(ubus_method, "set") == 0 &&
|
||||
strcmp(blobmsg_name(attr), "value") == 0 &&
|
||||
blobmsg_type(attr) == BLOBMSG_TYPE_STRING &&
|
||||
strncmp(BBFDM_ROOT_OBJECT, blobmsg_get_string(attr), strlen(BBFDM_ROOT_OBJECT)) == 0) {
|
||||
char reference_to_set[MAX_VALUE_LENGTH] = {0};
|
||||
|
||||
generate_reference_to_set(blobmsg_get_string(attr), reference_to_set, sizeof(reference_to_set));
|
||||
blobmsg_add_string(&req_buf, blobmsg_name(attr), reference_to_set);
|
||||
} if (strcmp(ubus_method, "set") == 0 &&
|
||||
strcmp(blobmsg_name(attr), "obj_path") == 0 &&
|
||||
blobmsg_type(attr) == BLOBMSG_TYPE_TABLE) {
|
||||
struct blob_attr *__attr = NULL;
|
||||
int rem = 0;
|
||||
|
||||
void *table = blobmsg_open_table(&req_buf, "obj_path");
|
||||
|
||||
blobmsg_for_each_attr(__attr, attr, rem) {
|
||||
if (blobmsg_type(__attr) == BLOBMSG_TYPE_STRING && strncmp(BBFDM_ROOT_OBJECT, blobmsg_get_string(__attr), strlen(BBFDM_ROOT_OBJECT)) == 0) {
|
||||
char reference_to_set[MAX_VALUE_LENGTH] = {0};
|
||||
|
||||
generate_reference_to_set(blobmsg_get_string(__attr), reference_to_set, sizeof(reference_to_set));
|
||||
blobmsg_add_string(&req_buf, blobmsg_name(__attr), reference_to_set);
|
||||
} else {
|
||||
blobmsg_add_string(&req_buf, blobmsg_name(__attr), blobmsg_get_string(__attr));
|
||||
}
|
||||
}
|
||||
|
||||
blobmsg_close_table(&req_buf, table);
|
||||
} else {
|
||||
blobmsg_add_field(&req_buf, blobmsg_type(attr), blobmsg_name(attr), blobmsg_data(attr), blobmsg_len(attr));
|
||||
}
|
||||
blobmsg_add_field(&req_buf, blobmsg_type(attr), blobmsg_name(attr), blobmsg_data(attr), blobmsg_len(attr));
|
||||
}
|
||||
|
||||
if (g_log_level == LOG_DEBUG) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#ifndef BBFDMD_COMMON_H
|
||||
#define BBFDMD_COMMON_H
|
||||
|
||||
#include <regex.h>
|
||||
#include "libbbfdm-api/version-2/bbfdm_api.h"
|
||||
|
||||
#define BBFDM_ROOT_OBJECT "Device."
|
||||
|
|
@ -19,8 +20,9 @@
|
|||
#define BBFDM_MICROSERVICE_INPUT_PATH "/etc/bbfdm/services"
|
||||
#define MAX_PATH_LENGTH 1024
|
||||
#define MAX_VALUE_LENGTH 1024 * 4
|
||||
#define SERVICE_CALL_TIMEOUT 5000 // 5 secs
|
||||
#define SERVICE_CALL_TIMEOUT 10000 // 10 secs
|
||||
#define SERVICE_CALL_OPERATE_TIMEOUT 1800000 // 30 mins
|
||||
#define SERVICE_MAX_CONSECUTIVE_TIMEOUTS 10
|
||||
|
||||
enum bbfdmd_type_enum {
|
||||
BBFDMD_NONE = 0,
|
||||
|
|
@ -29,15 +31,19 @@ enum bbfdmd_type_enum {
|
|||
BBFDMD_BOTH = BBFDMD_CWMP | BBFDMD_USP,
|
||||
};
|
||||
|
||||
void init_rand_seed(void);
|
||||
int rand_in_range(int min, int max);
|
||||
|
||||
unsigned int get_proto_type(const char *proto);
|
||||
|
||||
void fill_optional_input(struct blob_attr *msg, unsigned int *proto, bool *raw_format);
|
||||
|
||||
struct blob_attr *get_results_array(struct blob_attr *msg);
|
||||
|
||||
bool proto_matches(unsigned int dm_type, const enum bbfdmd_type_enum type);
|
||||
bool str_match(const char *string, const char *pattern, size_t nmatch, regmatch_t pmatch[]);
|
||||
bool proto_match(unsigned int dm_type, const enum bbfdmd_type_enum type);
|
||||
|
||||
char *get_reference_data(const char *path, const char *method_name);
|
||||
void print_fault_message(struct blob_buf *blob_buf, const char *path, uint32_t fault_code, const char *fault_msg);
|
||||
|
||||
void run_sync_call(const char *ubus_obj, const char *ubus_method, struct blob_attr *msg, struct blob_buf *bb_response);
|
||||
|
||||
|
|
|
|||
|
|
@ -19,194 +19,16 @@
|
|||
|
||||
extern int g_log_level;
|
||||
|
||||
static void add_linker_entry(struct async_request_context *ctx, const char *linker_path, const char *linker_value)
|
||||
{
|
||||
struct linker_args *linker = calloc(1, sizeof(struct linker_args));
|
||||
if (!linker)
|
||||
return;
|
||||
|
||||
list_add_tail(&linker->list, &ctx->linker_list);
|
||||
linker->path = strdup(linker_path ? linker_path : "");
|
||||
linker->value = strdup(linker_value ? linker_value : "");
|
||||
}
|
||||
|
||||
static void free_linker_entries(struct async_request_context *ctx)
|
||||
{
|
||||
struct linker_args *linker = NULL, *tmp = NULL;
|
||||
|
||||
list_for_each_entry_safe(linker, tmp, &ctx->linker_list, list) {
|
||||
list_del(&linker->list);
|
||||
BBFDM_FREE(linker->path);
|
||||
BBFDM_FREE(linker->value);
|
||||
BBFDM_FREE(linker);
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_reference_value(struct blob_attr *flags)
|
||||
{
|
||||
struct blob_attr *flag = NULL;
|
||||
int rem = 0;
|
||||
|
||||
if (!flags || blobmsg_type(flags) != BLOBMSG_TYPE_ARRAY)
|
||||
return false;
|
||||
|
||||
blobmsg_for_each_attr(flag, flags, rem) {
|
||||
if (strcmp(blobmsg_get_string(flag), "Reference") == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void fill_blob_param(struct blob_buf *bb, struct blob_attr *path, const char *data, struct blob_attr *type, struct blob_attr *flags)
|
||||
{
|
||||
if (!bb || !path || !data || !type)
|
||||
return;
|
||||
|
||||
void *table = blobmsg_open_table(bb, NULL);
|
||||
|
||||
if (path) {
|
||||
blobmsg_add_field(bb, blobmsg_type(path), blobmsg_name(path), blobmsg_data(path), blobmsg_len(path));
|
||||
}
|
||||
|
||||
blobmsg_add_string(bb, "data", data);
|
||||
|
||||
if (type) {
|
||||
blobmsg_add_field(bb, blobmsg_type(type), blobmsg_name(type), blobmsg_data(type), blobmsg_len(type));
|
||||
}
|
||||
|
||||
if (flags) {
|
||||
blobmsg_add_field(bb, blobmsg_type(flags), blobmsg_name(flags), blobmsg_data(flags), blobmsg_len(flags));
|
||||
}
|
||||
|
||||
blobmsg_close_table(bb, table);
|
||||
}
|
||||
|
||||
static void resolve_reference_path(struct async_request_context *ctx, struct blob_attr *data, char *output, size_t output_len)
|
||||
{
|
||||
if (!ctx || !output || output_len == 0) {
|
||||
BBFDM_ERR("Invalid arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
output[0] = 0; // Ensure output buffer is initialized
|
||||
|
||||
if (!data) {
|
||||
BBFDM_ERR("Invalid data value");
|
||||
return;
|
||||
}
|
||||
|
||||
char *ref_path = blobmsg_get_string(data);
|
||||
if (!ref_path || ref_path[0] == '\0') // Empty reference path, nothing to resolve
|
||||
return;
|
||||
|
||||
char buffer[MAX_VALUE_LENGTH] = {0};
|
||||
snprintf(buffer, sizeof(buffer), "%s", ref_path);
|
||||
|
||||
// Check if it is a reference path (separator ',') or list paths (separator ';')
|
||||
bool is_ref_list = strchr(ref_path, ';') != NULL;
|
||||
char *token = NULL, *saveptr = NULL;
|
||||
unsigned pos = 0;
|
||||
|
||||
for (token = strtok_r(buffer, is_ref_list ? ";" : ",", &saveptr);
|
||||
token;
|
||||
token = strtok_r(NULL, is_ref_list ? ";" : ",", &saveptr)) {
|
||||
|
||||
// If token does not contain '[', it’s a direct reference
|
||||
if (!strchr(token, '[')) {
|
||||
pos += snprintf(&output[pos], output_len - pos, "%s,", token);
|
||||
if (!is_ref_list) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Search for token in the linker list
|
||||
struct linker_args *linker = NULL;
|
||||
bool linker_found = false;
|
||||
list_for_each_entry(linker, &ctx->linker_list, list) {
|
||||
if (strcmp(linker->path, token) == 0 && linker->value[0] != '\0') {
|
||||
pos += snprintf(&output[pos], output_len - pos, "%s,", linker->value);
|
||||
linker_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (linker_found) {
|
||||
if (!is_ref_list) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If not found, attempt to resolve via micro-services
|
||||
{
|
||||
// Try to get reference value from micro-services directly
|
||||
char *reference_path = get_reference_data(token, "reference_path");
|
||||
|
||||
// Add path to list in order to be used by other parameters
|
||||
add_linker_entry(ctx, token, reference_path ? reference_path : "");
|
||||
|
||||
// Reference value is found
|
||||
if (reference_path != NULL) {
|
||||
pos += snprintf(&output[pos], output_len - pos, "%s,", reference_path);
|
||||
BBFDM_FREE(reference_path);
|
||||
if (!is_ref_list) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pos > 0) {
|
||||
output[pos - 1] = 0; // Remove trailing comma
|
||||
} else {
|
||||
BBFDM_INFO("Can't resolve reference path '%s' -> Set its value to empty", ref_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void prepare_and_send_response(struct async_request_context *ctx)
|
||||
{
|
||||
struct blob_attr *attr = NULL;
|
||||
struct blob_buf bb_raw = {0};
|
||||
size_t remaining = 0;
|
||||
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
memset(&bb_raw, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb_raw, 0);
|
||||
|
||||
void *array = blobmsg_open_array(&bb_raw, "results");
|
||||
|
||||
if (ctx->path_matched == false) {
|
||||
void *table = blobmsg_open_table(&bb_raw, NULL);
|
||||
blobmsg_add_string(&bb_raw, "path", ctx->requested_path);
|
||||
blobmsg_add_u32(&bb_raw, "fault", 9005);
|
||||
blobmsg_add_string(&bb_raw, "fault_msg", "Invalid parameter name");
|
||||
blobmsg_close_table(&bb_raw, table);
|
||||
} else {
|
||||
blobmsg_for_each_attr(attr, ctx->tmp_bb.head, remaining) {
|
||||
|
||||
if (strcmp(ctx->ubus_method, "get") == 0) {
|
||||
struct blob_attr *fields[4];
|
||||
const struct blobmsg_policy policy[4] = {
|
||||
{ "path", BLOBMSG_TYPE_STRING },
|
||||
{ "data", BLOBMSG_TYPE_STRING },
|
||||
{ "type", BLOBMSG_TYPE_STRING },
|
||||
{ "flags", BLOBMSG_TYPE_ARRAY },
|
||||
};
|
||||
|
||||
blobmsg_parse(policy, 4, fields, blobmsg_data(attr), blobmsg_len(attr));
|
||||
|
||||
if (is_reference_value(fields[3])) {
|
||||
char data[MAX_VALUE_LENGTH] = {0};
|
||||
resolve_reference_path(ctx, fields[1], data, sizeof(data));
|
||||
fill_blob_param(&bb_raw, fields[0], data, fields[2], fields[3]);
|
||||
} else {
|
||||
blobmsg_add_blob(&bb_raw, attr);
|
||||
}
|
||||
} else {
|
||||
blobmsg_add_blob(&bb_raw, attr);
|
||||
}
|
||||
}
|
||||
print_fault_message(&ctx->tmp_bb, ctx->requested_path, 9005, "Invalid parameter name");
|
||||
}
|
||||
|
||||
blobmsg_close_array(&bb_raw, array);
|
||||
blobmsg_close_array(&ctx->tmp_bb, ctx->array);
|
||||
|
||||
if (strcmp(ctx->ubus_method, "get") == 0 && ctx->raw_format == false) { // Pretty Format
|
||||
struct blob_buf bb_pretty = {0};
|
||||
|
|
@ -214,29 +36,23 @@ static void prepare_and_send_response(struct async_request_context *ctx)
|
|||
memset(&bb_pretty, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb_pretty, 0);
|
||||
|
||||
prepare_pretty_response(ctx->requested_path, bb_raw.head, &bb_pretty);
|
||||
prepare_pretty_response(ctx->requested_path, ctx->tmp_bb.head, &bb_pretty);
|
||||
|
||||
ubus_send_reply(ctx->ubus_ctx, &ctx->request_data, bb_pretty.head);
|
||||
blob_buf_free(&bb_pretty);
|
||||
} else { // Raw Format
|
||||
ubus_send_reply(ctx->ubus_ctx, &ctx->request_data, bb_raw.head);
|
||||
ubus_send_reply(ctx->ubus_ctx, &ctx->request_data, ctx->tmp_bb.head);
|
||||
}
|
||||
|
||||
blob_buf_free(&bb_raw);
|
||||
}
|
||||
|
||||
void send_response(struct async_request_context *ctx)
|
||||
{
|
||||
prepare_and_send_response(ctx);
|
||||
|
||||
if (strcmp(ctx->ubus_method, "get") == 0) {
|
||||
ubus_unregister_event_handler(ctx->ubus_ctx, &ctx->linker_handler);
|
||||
send_linker_cleanup_event(ctx->ubus_ctx);
|
||||
free_linker_entries(ctx);
|
||||
}
|
||||
|
||||
ubus_complete_deferred_request(ctx->ubus_ctx, &ctx->request_data, UBUS_STATUS_OK);
|
||||
blob_buf_free(&ctx->tmp_bb);
|
||||
|
||||
BBFDM_INFO("END: ubus method|%s|, name|bbfdm|, path|%s|", ctx->ubus_method, ctx->requested_path);
|
||||
BBFDM_FREE(ctx);
|
||||
}
|
||||
|
||||
|
|
@ -260,13 +76,23 @@ static void append_response_data(struct ubus_request_tracker *tracker, struct bl
|
|||
static void handle_request_timeout(struct uloop_timeout *timeout)
|
||||
{
|
||||
struct ubus_request_tracker *tracker = container_of(timeout, struct ubus_request_tracker, timeout);
|
||||
BBFDM_ERR("Timeout occurred for request: '%s'", tracker->request_name);
|
||||
BBFDM_WARNING("Timeout occurred for request: '%s %s'", tracker->request_name, tracker->ctx->requested_path);
|
||||
|
||||
ubus_abort_request(tracker->ctx->ubus_ctx, &tracker->async_request);
|
||||
tracker->ctx->pending_requests--;
|
||||
|
||||
service_entry_t *service = tracker->service;
|
||||
|
||||
if (service) {
|
||||
service->consecutive_timeouts++;
|
||||
if (service->consecutive_timeouts >= SERVICE_MAX_CONSECUTIVE_TIMEOUTS) {
|
||||
service->is_blacklisted = true;
|
||||
BBFDM_ERR("Service '%s' has been blacklisted due to repeated timeouts", service->name);
|
||||
}
|
||||
}
|
||||
|
||||
if (tracker->ctx->pending_requests == 0 && tracker->ctx->service_list_processed) {
|
||||
BBFDM_ERR("All requests completed after timeout");
|
||||
BBFDM_WARNING("All requests completed after timeout");
|
||||
send_response(tracker->ctx);
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +104,7 @@ static void ubus_result_callback(struct ubus_request *req, int type __attribute_
|
|||
struct ubus_request_tracker *tracker = container_of(req, struct ubus_request_tracker, async_request);
|
||||
|
||||
if (msg) {
|
||||
BBFDM_DEBUG("Response from object '%s'", tracker->request_name);
|
||||
BBFDM_DEBUG("Response from object '%s %s'", tracker->request_name, tracker->ctx->requested_path);
|
||||
append_response_data(tracker, msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -286,11 +112,14 @@ static void ubus_result_callback(struct ubus_request *req, int type __attribute_
|
|||
static void ubus_request_complete(struct ubus_request *req, int ret)
|
||||
{
|
||||
struct ubus_request_tracker *tracker = container_of(req, struct ubus_request_tracker, async_request);
|
||||
BBFDM_DEBUG("Request completed for '%s' with status: '%d'", tracker->request_name, ret);
|
||||
BBFDM_DEBUG("Request completed for '%s %s' with status: '%d'", tracker->request_name, tracker->ctx->requested_path, ret);
|
||||
|
||||
uloop_timeout_cancel(&tracker->timeout);
|
||||
tracker->ctx->pending_requests--;
|
||||
|
||||
if (tracker->service && ret == UBUS_STATUS_OK)
|
||||
tracker->service->consecutive_timeouts = 0;
|
||||
|
||||
if (tracker->ctx->pending_requests == 0 && tracker->ctx->service_list_processed) {
|
||||
BBFDM_DEBUG("Result Callback: All requests completed");
|
||||
send_response(tracker->ctx);
|
||||
|
|
@ -299,30 +128,31 @@ static void ubus_request_complete(struct ubus_request *req, int ret)
|
|||
BBFDM_FREE(tracker);
|
||||
}
|
||||
|
||||
void run_async_call(struct async_request_context *ctx, const char *ubus_obj, struct blob_attr *msg)
|
||||
void run_async_call(struct async_request_context *ctx, service_entry_t *service, struct blob_attr *msg)
|
||||
{
|
||||
struct blob_buf req_buf = {0};
|
||||
struct blob_attr *attr = NULL;
|
||||
int remaining = 0;
|
||||
uint32_t id = 0;
|
||||
|
||||
if (!ctx || !ubus_obj || !msg) {
|
||||
if (!ctx || !service || !msg || !service->name) {
|
||||
BBFDM_ERR("Invalid arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ctx->ubus_ctx, ubus_obj, &id)) {
|
||||
BBFDM_ERR("Failed to lookup object: %s", ubus_obj);
|
||||
if (ubus_lookup_id(ctx->ubus_ctx, service->name, &id)) {
|
||||
BBFDM_INFO("Failed to lookup object: %s", service->name);
|
||||
return;
|
||||
}
|
||||
|
||||
struct ubus_request_tracker *tracker = calloc(1, sizeof(struct ubus_request_tracker));
|
||||
struct ubus_request_tracker *tracker = (struct ubus_request_tracker *)calloc(1, sizeof(struct ubus_request_tracker));
|
||||
if (!tracker) {
|
||||
BBFDM_ERR("Failed to allocate memory for request tracker");
|
||||
return;
|
||||
}
|
||||
|
||||
tracker->ctx = ctx;
|
||||
tracker->service = service;
|
||||
ctx->pending_requests++;
|
||||
ctx->path_matched = true;
|
||||
|
||||
|
|
@ -333,19 +163,19 @@ void run_async_call(struct async_request_context *ctx, const char *ubus_obj, str
|
|||
blobmsg_add_field(&req_buf, blobmsg_type(attr), blobmsg_name(attr), blobmsg_data(attr), blobmsg_len(attr));
|
||||
}
|
||||
|
||||
snprintf(tracker->request_name, sizeof(tracker->request_name), "%s->%s", ubus_obj, ctx->ubus_method);
|
||||
snprintf(tracker->request_name, sizeof(tracker->request_name), "%s->%s", service->name, ctx->ubus_method);
|
||||
|
||||
tracker->timeout.cb = handle_request_timeout;
|
||||
uloop_timeout_set(&tracker->timeout, !strcmp(ctx->ubus_method, "operate") ? SERVICE_CALL_OPERATE_TIMEOUT : SERVICE_CALL_TIMEOUT);
|
||||
uloop_timeout_set(&tracker->timeout, !strcmp(ctx->ubus_method, "operate") ? SERVICE_CALL_OPERATE_TIMEOUT : service->timeout);
|
||||
|
||||
if (g_log_level == LOG_DEBUG) {
|
||||
char *json_str = blobmsg_format_json_indent(req_buf.head, true, -1);
|
||||
BBFDM_DEBUG("### ubus call %s %s '%s' ###", ubus_obj, ctx->ubus_method, json_str);
|
||||
BBFDM_DEBUG("### ubus call %s %s '%s' ###", service->name, ctx->ubus_method, json_str);
|
||||
BBFDM_FREE(json_str);
|
||||
}
|
||||
|
||||
if (ubus_invoke_async(ctx->ubus_ctx, id, ctx->ubus_method, req_buf.head, &tracker->async_request)) {
|
||||
BBFDM_ERR("Failed to invoke async method for object: %s", tracker->request_name);
|
||||
BBFDM_WARNING("Failed to invoke async method for object: %s", tracker->request_name);
|
||||
uloop_timeout_cancel(&tracker->timeout);
|
||||
BBFDM_FREE(tracker);
|
||||
} else {
|
||||
|
|
@ -356,34 +186,3 @@ void run_async_call(struct async_request_context *ctx, const char *ubus_obj, str
|
|||
|
||||
blob_buf_free(&req_buf);
|
||||
}
|
||||
|
||||
void send_linker_cleanup_event(struct ubus_context *ctx)
|
||||
{
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
ubus_send_event(ctx, "bbfdm.linker.cleanup", bb.head);
|
||||
blob_buf_free(&bb);
|
||||
}
|
||||
|
||||
void linker_response_callback(struct ubus_context *ctx __attribute__((unused)), struct ubus_event_handler *ev, const char *type __attribute__((unused)), struct blob_attr *msg)
|
||||
{
|
||||
struct async_request_context *context = NULL;
|
||||
struct blob_attr *attr = NULL;
|
||||
size_t rem = 0;
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
context = container_of(ev, struct async_request_context, linker_handler);
|
||||
if (context == NULL) {
|
||||
BBFDM_ERR("Failed to get the request context");
|
||||
return;
|
||||
}
|
||||
|
||||
blobmsg_for_each_attr(attr, msg, rem) {
|
||||
BBFDM_DEBUG("LINKER RESPONSE: '%s' <=> '%s'", blobmsg_name(attr), blobmsg_get_string(attr));
|
||||
add_linker_entry(context, blobmsg_name(attr), blobmsg_get_string(attr));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,18 +19,11 @@ enum {
|
|||
__BBFDM_MAX
|
||||
};
|
||||
|
||||
struct linker_args {
|
||||
struct list_head list;
|
||||
char *path;
|
||||
char *value;
|
||||
};
|
||||
|
||||
struct async_request_context {
|
||||
struct ubus_context *ubus_ctx;
|
||||
struct ubus_request_data request_data;
|
||||
struct ubus_event_handler linker_handler;
|
||||
struct list_head linker_list;
|
||||
struct blob_buf tmp_bb;
|
||||
void *array;
|
||||
bool service_list_processed;
|
||||
bool path_matched;
|
||||
bool raw_format;
|
||||
|
|
@ -41,15 +34,13 @@ struct async_request_context {
|
|||
|
||||
struct ubus_request_tracker {
|
||||
struct async_request_context *ctx;
|
||||
service_entry_t *service;
|
||||
struct ubus_request async_request;
|
||||
struct uloop_timeout timeout;
|
||||
char request_name[128];
|
||||
};
|
||||
|
||||
void send_linker_cleanup_event(struct ubus_context *ctx);
|
||||
void linker_response_callback(struct ubus_context *ctx, struct ubus_event_handler *ev, const char *type, struct blob_attr *msg);
|
||||
|
||||
void run_async_call(struct async_request_context *ctx, const char *ubus_obj, struct blob_attr *msg);
|
||||
void run_async_call(struct async_request_context *ctx, service_entry_t *service, struct blob_attr *msg);
|
||||
void send_response(struct async_request_context *ctx);
|
||||
|
||||
#endif /* BBFDMD_GET_H */
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <regex.h>
|
||||
#include <sys/param.h>
|
||||
#include <libubus.h>
|
||||
#include <libubox/blobmsg_json.h>
|
||||
|
|
@ -81,23 +80,6 @@ static void free_pv_list(struct list_head *pv_list)
|
|||
}
|
||||
}
|
||||
|
||||
static bool match(const char *string, const char *pattern, size_t nmatch, regmatch_t pmatch[])
|
||||
{
|
||||
regex_t re;
|
||||
|
||||
if (!string || !pattern)
|
||||
return 0;
|
||||
|
||||
if (regcomp(&re, pattern, REG_EXTENDED) != 0)
|
||||
return 0;
|
||||
|
||||
int status = regexec(&re, string, nmatch, pmatch, 0);
|
||||
|
||||
regfree(&re);
|
||||
|
||||
return (status != 0) ? false : true;
|
||||
}
|
||||
|
||||
static bool is_node_instance(const char *path)
|
||||
{
|
||||
if (!path)
|
||||
|
|
@ -473,7 +455,7 @@ static void prepare_result_blob(struct blob_buf *bb, struct list_head *pv_list)
|
|||
|
||||
static bool is_res_required(const char *str, size_t s_len, size_t *start, size_t *len)
|
||||
{
|
||||
if (match(str, GLOB_CHAR, 0, NULL)) {
|
||||
if (str_match(str, GLOB_CHAR, 0, NULL)) {
|
||||
char *star = strchr(str, '*');
|
||||
|
||||
*start = (star) ? (size_t)labs(star - str) : s_len;
|
||||
|
|
|
|||
|
|
@ -22,9 +22,8 @@
|
|||
|
||||
LIST_HEAD(registered_services);
|
||||
|
||||
extern int g_log_level;
|
||||
|
||||
static void add_service_to_list(const char *name, int service_proto, service_object_t *objects, size_t count, bool is_unified)
|
||||
static void add_service_to_list(const char *name, struct blob_buf *dm_schema, int service_proto, int service_timeout,
|
||||
service_object_t *objects, size_t count, bool is_unified)
|
||||
{
|
||||
service_entry_t *service = NULL;
|
||||
|
||||
|
|
@ -33,16 +32,88 @@ static void add_service_to_list(const char *name, int service_proto, service_obj
|
|||
return;
|
||||
}
|
||||
|
||||
service = calloc(1, sizeof(service_entry_t));
|
||||
service = (service_entry_t *)calloc(1, sizeof(service_entry_t));
|
||||
if (!service) {
|
||||
BBFDM_ERR("Failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
|
||||
list_add_tail(&service->list, ®istered_services);
|
||||
|
||||
service->name = strdup(name);
|
||||
service->dm_schema = dm_schema;
|
||||
service->protocol = service_proto;
|
||||
service->timeout = service_timeout;
|
||||
service->objects = objects;
|
||||
service->object_count = count;
|
||||
service->is_unified = is_unified;
|
||||
}
|
||||
|
||||
static void receive_schema_result(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *attr = NULL;
|
||||
int remaining = 0;
|
||||
|
||||
if (msg == NULL || req == NULL)
|
||||
return;
|
||||
|
||||
struct blob_buf *srv_schema = (struct blob_buf *)req->priv;
|
||||
if (!srv_schema)
|
||||
return;
|
||||
|
||||
struct blob_attr *results = get_results_array(msg);
|
||||
if (!results)
|
||||
return;
|
||||
|
||||
blobmsg_for_each_attr(attr, results, remaining) {
|
||||
blobmsg_add_blob(srv_schema, attr);
|
||||
}
|
||||
}
|
||||
|
||||
void fill_service_schema(struct ubus_context *ubus_ctx, int ubus_timeout, const char *service_name, struct blob_buf **service_schema)
|
||||
{
|
||||
uint32_t ubus_id;
|
||||
|
||||
if (!ubus_ctx || !service_name || !service_schema)
|
||||
return;
|
||||
|
||||
if (*service_schema != NULL) {
|
||||
blob_buf_free(*service_schema);
|
||||
BBFDM_FREE(*service_schema);
|
||||
}
|
||||
|
||||
if (!ubus_lookup_id(ubus_ctx, service_name, &ubus_id)) {
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
*service_schema = (struct blob_buf *)calloc(1, sizeof(struct blob_buf));
|
||||
if (*service_schema == NULL) {
|
||||
BBFDM_ERR("Failed to allocate memory");
|
||||
return;
|
||||
}
|
||||
|
||||
blob_buf_init(*service_schema, 0);
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
blobmsg_add_string(&bb, "path", BBFDM_ROOT_OBJECT);
|
||||
|
||||
void *table = blobmsg_open_table(&bb, "optional");
|
||||
blobmsg_add_string(&bb, "proto", "usp");
|
||||
blobmsg_close_table(&bb, table);
|
||||
|
||||
int err = ubus_invoke(ubus_ctx, ubus_id, "schema", bb.head, receive_schema_result, (void *)*service_schema, ubus_timeout);
|
||||
|
||||
if (err != 0) {
|
||||
BBFDM_ERR("UBUS invoke failed [object: %s, method: schema] with error (%d)", service_name, err);
|
||||
}
|
||||
|
||||
blob_buf_free(&bb);
|
||||
} else {
|
||||
BBFDM_WARNING("Failed to lookup UBUS object: %s", service_name);
|
||||
}
|
||||
}
|
||||
|
||||
static int load_service_from_file(struct ubus_context *ubus_ctx, const char *filename, const char *file_path)
|
||||
{
|
||||
size_t num_objs = 0;
|
||||
|
|
@ -52,13 +123,13 @@ static int load_service_from_file(struct ubus_context *ubus_ctx, const char *fil
|
|||
return -1;
|
||||
}
|
||||
|
||||
json_object *json_root = json_object_from_file(file_path);
|
||||
if (!json_root) {
|
||||
BBFDM_ERR("Failed to read JSON file: %s", file_path);
|
||||
return -1;
|
||||
}
|
||||
json_object *json_root = json_object_from_file(file_path);
|
||||
if (!json_root) {
|
||||
BBFDM_ERR("Failed to read JSON file: %s", file_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_object *daemon_config = NULL;
|
||||
json_object *daemon_config = NULL;
|
||||
json_object_object_get_ex(json_root, "daemon", &daemon_config);
|
||||
if (!daemon_config) {
|
||||
BBFDM_ERR("Failed to find daemon object");
|
||||
|
|
@ -66,7 +137,7 @@ static int load_service_from_file(struct ubus_context *ubus_ctx, const char *fil
|
|||
return -1;
|
||||
}
|
||||
|
||||
json_object *enable_jobj = NULL;
|
||||
json_object *enable_jobj = NULL;
|
||||
json_object_object_get_ex(daemon_config, "enable", &enable_jobj);
|
||||
bool enable = enable_jobj ? json_object_get_boolean(enable_jobj) : false;
|
||||
if (!enable) {
|
||||
|
|
@ -75,13 +146,11 @@ static int load_service_from_file(struct ubus_context *ubus_ctx, const char *fil
|
|||
return -1;
|
||||
}
|
||||
|
||||
struct blob_buf *service_schema = NULL;
|
||||
char service_name[MAX_PATH_LENGTH] = {0};
|
||||
snprintf(service_name, sizeof(service_name), "%s.%.*s", BBFDM_UBUS_OBJECT, (int)(strlen(filename) - 5), filename);
|
||||
|
||||
uint32_t ubus_id;
|
||||
if (ubus_lookup_id(ubus_ctx, service_name, &ubus_id)) {
|
||||
BBFDM_ERR("Failed to lookup UBUS object: %s", service_name);
|
||||
}
|
||||
snprintf(service_name, sizeof(service_name), "%s.%.*s", BBFDM_UBUS_OBJECT, (int)(strlen(filename) - 5), filename);
|
||||
fill_service_schema(ubus_ctx, 2000, service_name, &service_schema);
|
||||
|
||||
json_object *unified_daemon_jobj = NULL;
|
||||
json_object_object_get_ex(daemon_config, "unified_daemon", &unified_daemon_jobj);
|
||||
|
|
@ -91,6 +160,10 @@ static int load_service_from_file(struct ubus_context *ubus_ctx, const char *fil
|
|||
json_object_object_get_ex(daemon_config, "proto", &proto_jobj);
|
||||
int service_proto = get_proto_type(proto_jobj ? json_object_get_string(proto_jobj) : "");
|
||||
|
||||
json_object *timeout_jobj = NULL;
|
||||
json_object_object_get_ex(daemon_config, "timeout", &timeout_jobj);
|
||||
int service_timeout = timeout_jobj ? json_object_get_int(timeout_jobj) : SERVICE_CALL_TIMEOUT;
|
||||
|
||||
json_object *services_array = NULL;
|
||||
if (!json_object_object_get_ex(daemon_config, "services", &services_array) || json_object_get_type(services_array) != json_type_array) {
|
||||
json_object_put(json_root);
|
||||
|
|
@ -99,12 +172,17 @@ static int load_service_from_file(struct ubus_context *ubus_ctx, const char *fil
|
|||
|
||||
size_t service_count = json_object_array_length(services_array);
|
||||
if (service_count == 0) {
|
||||
BBFDM_ERR("Skipping service '%s' due to no objects defined", service_name);
|
||||
BBFDM_WARNING("Skipping service '%s' due to no objects defined", service_name);
|
||||
json_object_put(json_root);
|
||||
return -1;
|
||||
}
|
||||
|
||||
service_object_t *objects = calloc(service_count, sizeof(service_object_t));
|
||||
service_object_t *objects = (service_object_t *)calloc(service_count, sizeof(service_object_t));
|
||||
if (!objects) {
|
||||
BBFDM_ERR("Failed to allocate memory");
|
||||
json_object_put(json_root);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < service_count; i++) {
|
||||
json_object *service_obj = json_object_array_get_idx(services_array, i);
|
||||
|
|
@ -118,7 +196,7 @@ static int load_service_from_file(struct ubus_context *ubus_ctx, const char *fil
|
|||
snprintf(objects[num_objs].object_name, sizeof(objects[num_objs].object_name), "%s", object ? json_object_get_string(object) : "");
|
||||
|
||||
if (strlen(objects[num_objs].parent_path) == 0 || strlen(objects[num_objs].object_name) == 0) {
|
||||
BBFDM_ERR("Skip empty registration parent_dm[%s] or object[%s]", objects[num_objs].parent_path, objects[num_objs].object_name);
|
||||
BBFDM_WARNING("Skip empty registration parent_dm[%s] or object[%s]", objects[num_objs].parent_path, objects[num_objs].object_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -127,7 +205,7 @@ static int load_service_from_file(struct ubus_context *ubus_ctx, const char *fil
|
|||
}
|
||||
|
||||
BBFDM_INFO("Registering [%s :: %lu :: %d]", service_name, num_objs, is_unified);
|
||||
add_service_to_list(service_name, service_proto, objects, num_objs, is_unified);
|
||||
add_service_to_list(service_name, service_schema, service_proto, service_timeout, objects, num_objs, is_unified);
|
||||
json_object_put(json_root);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -184,6 +262,12 @@ void unregister_services(void)
|
|||
|
||||
list_for_each_entry_safe(service, tmp, ®istered_services, list) {
|
||||
list_del(&service->list);
|
||||
|
||||
if (service->dm_schema) {
|
||||
blob_buf_free(service->dm_schema);
|
||||
BBFDM_FREE(service->dm_schema);
|
||||
}
|
||||
|
||||
BBFDM_FREE(service->name);
|
||||
BBFDM_FREE(service->objects);
|
||||
BBFDM_FREE(service);
|
||||
|
|
@ -201,27 +285,45 @@ void list_registered_services(struct blob_buf *bb)
|
|||
|
||||
list_for_each_entry(service, ®istered_services, list) {
|
||||
void *table = blobmsg_open_table(bb, NULL);
|
||||
|
||||
blobmsg_add_string(bb, "name", service->name ? service->name : "");
|
||||
blobmsg_add_string(bb, "proto", service->protocol == BBFDMD_USP ? "usp" : service->protocol == BBFDMD_CWMP ? "cwmp" : "both");
|
||||
blobmsg_add_string(bb, "proto",
|
||||
service->protocol == BBFDMD_USP ? "usp" :
|
||||
service->protocol == BBFDMD_CWMP ? "cwmp" : "both");
|
||||
|
||||
blobmsg_add_u8(bb, "unified_daemon", service->is_unified);
|
||||
blobmsg_add_u8(bb, "blacklisted", service->is_blacklisted);
|
||||
blobmsg_add_u32(bb, "timeout", service->timeout);
|
||||
|
||||
void *objects_array = blobmsg_open_array(bb, "objects");
|
||||
for (size_t i = 0; i < service->object_count; i++) {
|
||||
void *obj_table = blobmsg_open_table(bb, NULL);
|
||||
blobmsg_add_string(bb, "parent_dm", service->objects[i].parent_path);
|
||||
blobmsg_add_string(bb, "object", service->objects[i].object_name);
|
||||
blobmsg_add_string(bb, "proto", service->objects[i].protocol == BBFDMD_USP ? "usp" : service->objects[i].protocol == BBFDMD_CWMP ? "cwmp" : "both");
|
||||
|
||||
if (service->protocol == BBFDMD_USP) {
|
||||
blobmsg_add_string(bb, "proto", "usp");
|
||||
} else if (service->protocol == BBFDMD_CWMP) {
|
||||
blobmsg_add_string(bb, "proto", "cwmp");
|
||||
} else {
|
||||
blobmsg_add_string(bb, "proto",
|
||||
service->objects[i].protocol == BBFDMD_USP ? "usp" :
|
||||
service->objects[i].protocol == BBFDMD_CWMP ? "cwmp" : "both");
|
||||
}
|
||||
|
||||
blobmsg_close_table(bb, obj_table);
|
||||
}
|
||||
blobmsg_close_array(bb, objects_array);
|
||||
|
||||
blobmsg_close_table(bb, table);
|
||||
}
|
||||
|
||||
blobmsg_close_array(bb, array);
|
||||
}
|
||||
|
||||
bool is_path_match(const char *requested_path, unsigned int requested_proto, service_entry_t *service)
|
||||
bool service_path_match(const char *requested_path, unsigned int requested_proto, service_entry_t *service)
|
||||
{
|
||||
if (!proto_matches(requested_proto, service->protocol))
|
||||
if (!proto_match(requested_proto, service->protocol))
|
||||
return false;
|
||||
|
||||
if (strlen(requested_path) == 0 || strcmp(requested_path, BBFDM_ROOT_OBJECT) == 0)
|
||||
|
|
@ -233,7 +335,7 @@ bool is_path_match(const char *requested_path, unsigned int requested_proto, ser
|
|||
for (size_t idx = 0; idx < service->object_count; idx++) {
|
||||
char current_obj[MAX_PATH_LENGTH] = {0};
|
||||
|
||||
if (!proto_matches(requested_proto, service->objects[idx].protocol))
|
||||
if (!proto_match(requested_proto, service->objects[idx].protocol))
|
||||
continue;
|
||||
|
||||
snprintf(current_obj, sizeof(current_obj), "%s%s", service->objects[idx].parent_path, service->objects[idx].object_name);
|
||||
|
|
@ -247,73 +349,3 @@ bool is_path_match(const char *requested_path, unsigned int requested_proto, ser
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
static char *get_ubus_object_name(const char *path)
|
||||
{
|
||||
service_entry_t *service = NULL;
|
||||
|
||||
list_for_each_entry(service, ®istered_services, list) {
|
||||
|
||||
if (!is_path_match(path, BBFDMD_BOTH, service))
|
||||
continue;
|
||||
|
||||
return service->name;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void reference_data_callback(struct ubus_request *req, int type __attribute__((unused)), struct blob_attr *msg)
|
||||
{
|
||||
struct blob_attr *fields[1];
|
||||
const struct blobmsg_policy policy[1] = {
|
||||
{ "data", BLOBMSG_TYPE_STRING },
|
||||
};
|
||||
|
||||
if (!req || !msg)
|
||||
return;
|
||||
|
||||
char *reference_data = (char *)req->priv;
|
||||
|
||||
if (!reference_data)
|
||||
return;
|
||||
|
||||
blobmsg_parse(policy, 1, fields, blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
if (fields[0]) {
|
||||
snprintf(reference_data, MAX_PATH_LENGTH - 1, "%s", blobmsg_get_string(fields[0]));
|
||||
BBFDM_DEBUG("reference_data '%s'", reference_data);
|
||||
}
|
||||
}
|
||||
|
||||
char *get_reference_data(const char *path, const char *method_name)
|
||||
{
|
||||
struct blob_buf req_buf = {0};
|
||||
char reference_value[MAX_PATH_LENGTH] = {0};
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
char *ubus_obj = get_ubus_object_name(path);
|
||||
if (!ubus_obj)
|
||||
return NULL;
|
||||
|
||||
reference_value[0] = 0;
|
||||
|
||||
memset(&req_buf, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&req_buf, 0);
|
||||
|
||||
blobmsg_add_string(&req_buf, "path", path);
|
||||
|
||||
if (g_log_level == LOG_DEBUG) {
|
||||
char *json_str = blobmsg_format_json_indent(req_buf.head, true, -1);
|
||||
BBFDM_DEBUG("### ubus call %s %s '%s' ###", ubus_obj, method_name, json_str);
|
||||
BBFDM_FREE(json_str);
|
||||
}
|
||||
|
||||
BBFDM_UBUS_INVOKE_SYNC(ubus_obj, method_name, req_buf.head, 2000, reference_data_callback, &reference_value);
|
||||
|
||||
blob_buf_free(&req_buf);
|
||||
|
||||
return (reference_value[0] != 0) ? strdup(reference_value) : NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,17 +20,22 @@ typedef struct {
|
|||
|
||||
typedef struct service_entry {
|
||||
struct list_head list;
|
||||
struct blob_buf *dm_schema;
|
||||
char *name;
|
||||
enum bbfdmd_type_enum protocol;
|
||||
bool is_unified;
|
||||
size_t object_count;
|
||||
service_object_t *objects;
|
||||
int timeout; // Ubus timeout used to get data from lower layer
|
||||
int consecutive_timeouts; // Tracks successive timeouts
|
||||
bool is_blacklisted; // Marks if the service is blacklisted
|
||||
} service_entry_t;
|
||||
|
||||
int register_services(struct ubus_context *ctx);
|
||||
void unregister_services(void);
|
||||
void list_registered_services(struct blob_buf *bb);
|
||||
void fill_service_schema(struct ubus_context *ubus_ctx, int ubus_timeout, const char *service_name, struct blob_buf **service_schema);
|
||||
|
||||
bool is_path_match(const char *requested_path, unsigned int requested_proto, service_entry_t *service);
|
||||
bool service_path_match(const char *requested_path, unsigned int requested_proto, service_entry_t *service);
|
||||
|
||||
#endif /* BBFDMD_SERVICE_H */
|
||||
|
|
|
|||
|
|
@ -8,5 +8,5 @@ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SOURCE_DIR} -I${CMAKE_SOURCE_DIR}/
|
|||
|
||||
FILE(GLOB BBF_SOURCES *.c)
|
||||
ADD_EXECUTABLE(dm-service ${BBF_SOURCES})
|
||||
TARGET_LINK_LIBRARIES(dm-service bbfdm-ubus)
|
||||
TARGET_LINK_LIBRARIES(dm-service bbfdm-ubus easy)
|
||||
INSTALL(TARGETS dm-service DESTINATION usr/sbin)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ static void usage(char *prog)
|
|||
fprintf(stderr, "options:\n");
|
||||
fprintf(stderr, " -m <ms name> micro-service name\n");
|
||||
fprintf(stderr, " -l <loglevel> log verbosity value as per standard syslog\n");
|
||||
fprintf(stderr, " -h Displays this help\n");
|
||||
fprintf(stderr, " -d Display the schema data model supported by micro-service\n");
|
||||
fprintf(stderr, " -h Display this help\n");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
|
|
@ -29,11 +30,11 @@ int main(int argc, char **argv)
|
|||
struct bbfdm_context bbfdm_ctx = {0};
|
||||
char proc_name[64] = {0};
|
||||
int log_level = LOG_ERR;
|
||||
int err = 0, ch;
|
||||
int err = 0, ch, dm_type = 0;
|
||||
|
||||
memset(&bbfdm_ctx, 0, sizeof(struct bbfdm_context));
|
||||
|
||||
while ((ch = getopt(argc, argv, "hl:m:")) != -1) {
|
||||
while ((ch = getopt(argc, argv, "hdl:m:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'm':
|
||||
bbfdm_ubus_set_service_name(&bbfdm_ctx, optarg);
|
||||
|
|
@ -42,9 +43,12 @@ int main(int argc, char **argv)
|
|||
if (optarg) {
|
||||
log_level = (int)strtod(optarg, NULL);
|
||||
if (log_level < 0 || log_level > 7)
|
||||
log_level = 3;
|
||||
log_level = 7;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
dm_type++;
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
exit(0);
|
||||
|
|
@ -58,11 +62,16 @@ int main(int argc, char **argv)
|
|||
exit(-1);
|
||||
}
|
||||
|
||||
bbfdm_ubus_set_log_level(log_level);
|
||||
|
||||
if (dm_type > 0) {
|
||||
int res = bbfdm_print_data_model_schema(&bbfdm_ctx, dm_type);
|
||||
exit(res);
|
||||
}
|
||||
openlog(bbfdm_ctx.config.service_name, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
|
||||
|
||||
err = bbfdm_ubus_regiter_init(&bbfdm_ctx);
|
||||
bbfdm_ubus_set_log_level(log_level);
|
||||
bbfdm_ubus_load_data_model(NULL);
|
||||
|
||||
err = bbfdm_ubus_register_init(&bbfdm_ctx);
|
||||
if (err != 0)
|
||||
goto exit;
|
||||
|
||||
|
|
@ -76,9 +85,7 @@ int main(int argc, char **argv)
|
|||
uloop_run();
|
||||
|
||||
exit:
|
||||
if (err != -5) // Error code is not -5, indicating that ubus_ctx is connected, proceed with shutdown
|
||||
bbfdm_ubus_regiter_free(&bbfdm_ctx);
|
||||
|
||||
bbfdm_ubus_register_free(&bbfdm_ctx);
|
||||
closelog();
|
||||
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -68,12 +68,6 @@
|
|||
"Device.WiFi."
|
||||
]
|
||||
},
|
||||
"instance_t": {
|
||||
"description": "Multi object instances",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 256
|
||||
},
|
||||
"proto_t": {
|
||||
"type": "string",
|
||||
"default": "both",
|
||||
|
|
@ -104,22 +98,6 @@
|
|||
"minimum": 7000,
|
||||
"maximum": 9050
|
||||
},
|
||||
"trans_type_t": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"start",
|
||||
"commit",
|
||||
"abort",
|
||||
"status"
|
||||
]
|
||||
},
|
||||
"srv_type_t": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"register",
|
||||
"list"
|
||||
]
|
||||
},
|
||||
"format_t": {
|
||||
"type": "string",
|
||||
"default": "pretty",
|
||||
|
|
@ -150,31 +128,10 @@
|
|||
"path": {
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
},
|
||||
"paths": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": [
|
||||
{
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
}
|
||||
]
|
||||
},
|
||||
"first_level": {
|
||||
"type": "boolean",
|
||||
"description": "gets only first level objects if true"
|
||||
},
|
||||
"commands": {
|
||||
"type": "boolean",
|
||||
"description": "includes commands in the list if true"
|
||||
},
|
||||
"events": {
|
||||
"type": "boolean",
|
||||
"description": "includes events in the list if true"
|
||||
},
|
||||
"params": {
|
||||
"type": "boolean",
|
||||
"description": "includes objs/params in the list if true"
|
||||
},
|
||||
"optional": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -278,19 +235,6 @@
|
|||
"path": {
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
},
|
||||
"paths": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": [
|
||||
{
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
}
|
||||
]
|
||||
},
|
||||
"maxdepth": {
|
||||
"type": "integer",
|
||||
"description": "Integer to decide the depth of data model to be parsed"
|
||||
},
|
||||
"optional": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -358,10 +302,6 @@
|
|||
"path": {
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
},
|
||||
"first_level": {
|
||||
"type": "boolean",
|
||||
"description": "gets only first level objects if true"
|
||||
},
|
||||
"optional": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -625,10 +565,10 @@
|
|||
"input": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"command"
|
||||
"path"
|
||||
],
|
||||
"properties": {
|
||||
"command": {
|
||||
"path": {
|
||||
"$ref": "#/definitions/operate_path_t"
|
||||
},
|
||||
"command_key": {
|
||||
|
|
@ -645,9 +585,6 @@
|
|||
"optional": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"format": {
|
||||
"$ref": "#/definitions/format_t"
|
||||
},
|
||||
"proto": {
|
||||
"$ref": "#/definitions/proto_t"
|
||||
}
|
||||
|
|
@ -709,8 +646,8 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"service": {
|
||||
"title": "show the list of micro-service registred in the core Data Model",
|
||||
"services": {
|
||||
"title": "show the list of micro-service registred in the Data Model",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"output"
|
||||
|
|
@ -724,13 +661,21 @@
|
|||
"type": "string",
|
||||
"Description": "Name of the micro-service ubus object"
|
||||
},
|
||||
"parent_dm": {
|
||||
"type": "string",
|
||||
"Description": "Object path where the micro-service object will be added"
|
||||
},
|
||||
"object": {
|
||||
"type": "string",
|
||||
"Description": "Name of the micro-service object"
|
||||
"objects": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parent_dm": {
|
||||
"type": "string",
|
||||
"Description": "Object path where the micro-service object will be added"
|
||||
},
|
||||
"object": {
|
||||
"type": "string",
|
||||
"Description": "Name of object will be added"
|
||||
},
|
||||
"proto": {
|
||||
"$ref": "#/definitions/proto_t"
|
||||
}
|
||||
}
|
||||
},
|
||||
"proto": {
|
||||
"$ref": "#/definitions/proto_t"
|
||||
|
|
|
|||
|
|
@ -10,17 +10,16 @@ https://dev.iopsys.eu/bbf/bbfdm/-/blob/devel/docs/api/ubus/bbfdm.md
|
|||
|
||||
# bbf
|
||||
|
||||
| List of Methods |
|
||||
| ----------------------------- |
|
||||
| [add](#add) | Method | bbf (this schema) |
|
||||
| [del](#del) | Method | bbf (this schema) |
|
||||
| [get](#get) | Method | bbf (this schema) |
|
||||
| [instances](#instances) | Method | bbf (this schema) |
|
||||
| [notify_event](#notify_event) | Method | bbf (this schema) |
|
||||
| [operate](#operate) | Method | bbf (this schema) |
|
||||
| [schema](#schema) | Method | bbf (this schema) |
|
||||
| [service](#service) | Method | bbf (this schema) |
|
||||
| [set](#set) | Method | bbf (this schema) |
|
||||
| List of Methods |
|
||||
| ----------------------- |
|
||||
| [add](#add) | Method | bbf (this schema) |
|
||||
| [del](#del) | Method | bbf (this schema) |
|
||||
| [get](#get) | Method | bbf (this schema) |
|
||||
| [instances](#instances) | Method | bbf (this schema) |
|
||||
| [operate](#operate) | Method | bbf (this schema) |
|
||||
| [schema](#schema) | Method | bbf (this schema) |
|
||||
| [services](#services) | Method | bbf (this schema) |
|
||||
| [set](#set) | Method | bbf (this schema) |
|
||||
|
||||
## add
|
||||
|
||||
|
|
@ -109,7 +108,7 @@ Device.WiFi.
|
|||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf add {"path":"eu commodo Ut ut ","obj_path":{}}
|
||||
ubus call bbf add {"path":"pariatur","obj_path":{}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -119,7 +118,7 @@ ubus call bbf add {"path":"eu commodo Ut ut ","obj_path":{}}
|
|||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "call",
|
||||
"params": ["<SID>", "bbf", "add", { "path": "eu commodo Ut ut ", "obj_path": {} }]
|
||||
"params": ["<SID>", "bbf", "add", { "path": "pariatur", "obj_path": {} }]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -186,10 +185,10 @@ All items must be of the type: Unknown type ``.
|
|||
{
|
||||
"results": [
|
||||
{
|
||||
"path": "eused exercitation",
|
||||
"data": "pariatur nostrud in aute Excepteur",
|
||||
"fault": 7415,
|
||||
"fault_msg": "dolor magna"
|
||||
"path": "enim ut voluptate eu ",
|
||||
"data": "Ut in exercitation officia Excepteur",
|
||||
"fault": 7966,
|
||||
"fault_msg": "Ut"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -293,7 +292,7 @@ All items must be of the type: Unknown type ``.
|
|||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf del {"path":"fugiat adipisicing","paths":["do laborum occaecat et"]}
|
||||
ubus call bbf del {"path":"qui nisi id","paths":["adipis"]}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -303,7 +302,7 @@ ubus call bbf del {"path":"fugiat adipisicing","paths":["do laborum occaecat et"
|
|||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "call",
|
||||
"params": ["<SID>", "bbf", "del", { "path": "fugiat adipisicing", "paths": ["do laborum occaecat et"] }]
|
||||
"params": ["<SID>", "bbf", "del", { "path": "qui nisi id", "paths": ["adipis"] }]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -367,7 +366,7 @@ All items must be of the type: Unknown type ``.
|
|||
### Output Example
|
||||
|
||||
```json
|
||||
{ "results": [{ "path": "occaecat sit elit i", "data": "non", "fault": 7119, "fault_msg": "elit sunt" }] }
|
||||
{ "results": [{ "path": "sint elit", "data": "cupidatat do sit ", "fault": 8574, "fault_msg": "dolore et Duis" }] }
|
||||
```
|
||||
|
||||
## get
|
||||
|
|
@ -400,25 +399,10 @@ Query the datamodel object
|
|||
|
||||
`object` with following properties:
|
||||
|
||||
| Property | Type | Required |
|
||||
| ---------- | ------- | ------------ |
|
||||
| `maxdepth` | integer | Optional |
|
||||
| `optional` | object | Optional |
|
||||
| `path` | string | **Required** |
|
||||
| `paths` | array | Optional |
|
||||
|
||||
#### maxdepth
|
||||
|
||||
Integer to decide the depth of data model to be parsed
|
||||
|
||||
`maxdepth`
|
||||
|
||||
- is optional
|
||||
- type: `integer`
|
||||
|
||||
##### maxdepth Type
|
||||
|
||||
`integer`
|
||||
| Property | Type | Required |
|
||||
| ---------- | ------ | ------------ |
|
||||
| `optional` | object | Optional |
|
||||
| `path` | string | **Required** |
|
||||
|
||||
#### optional
|
||||
|
||||
|
|
@ -517,36 +501,10 @@ Device.WiFi.SSID.*.BSSID
|
|||
Device.WiFi.
|
||||
```
|
||||
|
||||
#### paths
|
||||
|
||||
`paths`
|
||||
|
||||
- is optional
|
||||
- type: `array`
|
||||
|
||||
##### paths Type
|
||||
|
||||
Array type: `array`
|
||||
|
||||
All items must be of the type: Unknown type ``.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": [
|
||||
{
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
}
|
||||
],
|
||||
"simpletype": "`array`"
|
||||
}
|
||||
```
|
||||
|
||||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf get {"path":"occaecat aliqua mollit","paths":["occaecat Duis Lorem velit aliq"],"maxdepth":-48387650,"optional":{"format":"raw","proto":"usp"}}
|
||||
ubus call bbf get {"path":"magna consequat ut","optional":{"format":"pretty","proto":"both"}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -560,12 +518,7 @@ ubus call bbf get {"path":"occaecat aliqua mollit","paths":["occaecat Duis Lorem
|
|||
"<SID>",
|
||||
"bbf",
|
||||
"get",
|
||||
{
|
||||
"path": "occaecat aliqua mollit",
|
||||
"paths": ["occaecat Duis Lorem velit aliq"],
|
||||
"maxdepth": -48387650,
|
||||
"optional": { "format": "raw", "proto": "usp" }
|
||||
}
|
||||
{ "path": "magna consequat ut", "optional": { "format": "pretty", "proto": "both" } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
@ -633,7 +586,17 @@ All items must be of the type: Unknown type ``.
|
|||
### Output Example
|
||||
|
||||
```json
|
||||
{ "results": [{ "path": "dolore dolor", "data": "in", "type": "xsd:int", "fault": 7367, "fault_msg": "laborum nis" }] }
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"path": "id sunt Duis",
|
||||
"data": "id",
|
||||
"type": "xsd:command",
|
||||
"fault": 8916,
|
||||
"fault_msg": "dolor amet irure sed nulla"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## instances
|
||||
|
|
@ -666,24 +629,10 @@ Get the instances of multi object
|
|||
|
||||
`object` with following properties:
|
||||
|
||||
| Property | Type | Required |
|
||||
| ------------- | ------- | ------------ |
|
||||
| `first_level` | boolean | Optional |
|
||||
| `optional` | object | Optional |
|
||||
| `path` | string | **Required** |
|
||||
|
||||
#### first_level
|
||||
|
||||
gets only first level objects if true
|
||||
|
||||
`first_level`
|
||||
|
||||
- is optional
|
||||
- type: `boolean`
|
||||
|
||||
##### first_level Type
|
||||
|
||||
`boolean`
|
||||
| Property | Type | Required |
|
||||
| ---------- | ------ | ------------ |
|
||||
| `optional` | object | Optional |
|
||||
| `path` | string | **Required** |
|
||||
|
||||
#### optional
|
||||
|
||||
|
|
@ -763,7 +712,7 @@ Device.WiFi.
|
|||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf instances {"path":"commodo aliqu","first_level":true,"optional":{"proto":"cwmp"}}
|
||||
ubus call bbf instances {"path":"dolore n","optional":{"proto":"usp"}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -773,12 +722,7 @@ ubus call bbf instances {"path":"commodo aliqu","first_level":true,"optional":{"
|
|||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "call",
|
||||
"params": [
|
||||
"<SID>",
|
||||
"bbf",
|
||||
"instances",
|
||||
{ "path": "commodo aliqu", "first_level": true, "optional": { "proto": "cwmp" } }
|
||||
]
|
||||
"params": ["<SID>", "bbf", "instances", { "path": "dolore n", "optional": { "proto": "usp" } }]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -839,256 +783,7 @@ All items must be of the type: Unknown type ``.
|
|||
### Output Example
|
||||
|
||||
```json
|
||||
{ "results": [{ "path": "ametest minim ut sit ex", "fault": 7415, "fault_msg": "nostrud" }] }
|
||||
```
|
||||
|
||||
## notify_event
|
||||
|
||||
### notify occurance of an event on ubus
|
||||
|
||||
`notify_event`
|
||||
|
||||
- type: `Method`
|
||||
|
||||
### notify_event Type
|
||||
|
||||
`object` with following properties:
|
||||
|
||||
| Property | Type | Required |
|
||||
| -------- | ------ | ------------ |
|
||||
| `input` | object | **Required** |
|
||||
| `output` | | Optional |
|
||||
|
||||
#### input
|
||||
|
||||
`input`
|
||||
|
||||
- is **required**
|
||||
- type: `object`
|
||||
|
||||
##### input Type
|
||||
|
||||
`object` with following properties:
|
||||
|
||||
| Property | Type | Required |
|
||||
| -------- | ------ | ------------ |
|
||||
| `input` | array | Optional |
|
||||
| `name` | string | **Required** |
|
||||
|
||||
#### input
|
||||
|
||||
`input`
|
||||
|
||||
- is optional
|
||||
- type: `array`
|
||||
|
||||
##### input Type
|
||||
|
||||
Array type: `array`
|
||||
|
||||
#### name
|
||||
|
||||
`name`
|
||||
|
||||
- is **required**
|
||||
- type: `string`
|
||||
|
||||
##### name Type
|
||||
|
||||
`string`
|
||||
|
||||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf notify_event {"name":"Duis dolor officia anim Ut","input":[]}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "call",
|
||||
"params": ["<SID>", "bbf", "notify_event", { "name": "Duis dolor officia anim Ut", "input": [] }]
|
||||
}
|
||||
```
|
||||
|
||||
#### output
|
||||
|
||||
`output`
|
||||
|
||||
- is optional
|
||||
- type: complex
|
||||
|
||||
##### output Type
|
||||
|
||||
Unknown type ``.
|
||||
|
||||
```json
|
||||
{
|
||||
"definitions": {
|
||||
"path_t": {
|
||||
"description": "Complete object element path as per TR181",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 1024,
|
||||
"examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."]
|
||||
},
|
||||
"schema_path_t": {
|
||||
"description": "Datamodel object schema path",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 1024,
|
||||
"examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"]
|
||||
},
|
||||
"boolean_t": {
|
||||
"type": "string",
|
||||
"enum": ["0", "1"]
|
||||
},
|
||||
"datatype_t": {
|
||||
"type": "string",
|
||||
"enum": ["int", "unsignedInt", "long", "unsignedLong", "string", "boolean", "dateTime", "base64", "hexBinary"]
|
||||
},
|
||||
"operate_path_t": {
|
||||
"description": "Datamodel object schema path",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 1024,
|
||||
"examples": ["Device.IP.Diagnostics.IPPing()", "Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"]
|
||||
},
|
||||
"query_path_t": {
|
||||
"description": "DM object path with search queries",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 1024,
|
||||
"examples": [
|
||||
"Device.",
|
||||
"Device.DeviceInfo.Manufacturer",
|
||||
"Device.WiFi.SSID.1.BSSID",
|
||||
"Device.WiFi.SSID.*.BSSID",
|
||||
"Device.WiFi."
|
||||
]
|
||||
},
|
||||
"instance_t": {
|
||||
"description": "Multi object instances",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 256
|
||||
},
|
||||
"proto_t": {
|
||||
"type": "string",
|
||||
"default": "both",
|
||||
"enum": ["usp", "cwmp", "both"]
|
||||
},
|
||||
"type_t": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"xsd:string",
|
||||
"xsd:unsignedInt",
|
||||
"xsd:int",
|
||||
"xsd:unsignedLong",
|
||||
"xsd:long",
|
||||
"xsd:boolean",
|
||||
"xsd:dateTime",
|
||||
"xsd:hexBinary",
|
||||
"xsd:object",
|
||||
"xsd:command",
|
||||
"xsd:event"
|
||||
]
|
||||
},
|
||||
"fault_t": {
|
||||
"type": "integer",
|
||||
"minimum": 7000,
|
||||
"maximum": 9050
|
||||
},
|
||||
"trans_type_t": {
|
||||
"type": "string",
|
||||
"enum": ["start", "commit", "abort", "status"]
|
||||
},
|
||||
"srv_type_t": {
|
||||
"type": "string",
|
||||
"enum": ["register", "list"]
|
||||
},
|
||||
"format_t": {
|
||||
"type": "string",
|
||||
"default": "pretty",
|
||||
"enum": ["raw", "pretty"]
|
||||
}
|
||||
},
|
||||
"out": "{\"definitions\":{\"path_t\":{\"description\":\"Complete object element path as per TR181\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.1.\",\"Device.WiFi.\"]},\"schema_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.Bridging.Bridge.{i}.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.{i}.SSID\"]},\"boolean_t\":{\"type\":\"string\",\"enum\":[\"0\",\"1\"]},\"datatype_t\":{\"type\":\"string\",\"enum\":[\"int\",\"unsignedInt\",\"long\",\"unsignedLong\",\"string\",\"boolean\",\"dateTime\",\"base64\",\"hexBinary\"]},\"operate_path_t\":{\"description\":\"Datamodel object schema path\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.IP.Diagnostics.IPPing()\",\"Device.DHCPv4.Client.{i}.Renew()\",\"Device.FactoryReset()\"]},\"query_path_t\":{\"description\":\"DM object path with search queries\",\"type\":\"string\",\"minLength\":6,\"maxLength\":1024,\"examples\":[\"Device.\",\"Device.DeviceInfo.Manufacturer\",\"Device.WiFi.SSID.1.BSSID\",\"Device.WiFi.SSID.*.BSSID\",\"Device.WiFi.\"]},\"instance_t\":{\"description\":\"Multi object instances\",\"type\":\"string\",\"minLength\":6,\"maxLength\":256},\"proto_t\":{\"type\":\"string\",\"default\":\"both\",\"enum\":[\"usp\",\"cwmp\",\"both\"]},\"type_t\":{\"type\":\"string\",\"enum\":[\"xsd:string\",\"xsd:unsignedInt\",\"xsd:int\",\"xsd:unsignedLong\",\"xsd:long\",\"xsd:boolean\",\"xsd:dateTime\",\"xsd:hexBinary\",\"xsd:object\",\"xsd:command\",\"xsd:event\"]},\"fault_t\":{\"type\":\"integer\",\"minimum\":7000,\"maximum\":9050},\"trans_type_t\":{\"type\":\"string\",\"enum\":[\"start\",\"commit\",\"abort\",\"status\"]},\"srv_type_t\":{\"type\":\"string\",\"enum\":[\"register\",\"list\"]},\"format_t\":{\"type\":\"string\",\"default\":\"pretty\",\"enum\":[\"raw\",\"pretty\"]}}}",
|
||||
"simpletype": "complex"
|
||||
}
|
||||
```
|
||||
|
||||
### Output Example
|
||||
|
||||
```json
|
||||
{
|
||||
"definitions": {
|
||||
"path_t": {
|
||||
"description": "Complete object element path as per TR181",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 1024,
|
||||
"examples": ["Device.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.1.", "Device.WiFi."]
|
||||
},
|
||||
"schema_path_t": {
|
||||
"description": "Datamodel object schema path",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 1024,
|
||||
"examples": ["Device.Bridging.Bridge.{i}.", "Device.DeviceInfo.Manufacturer", "Device.WiFi.SSID.{i}.SSID"]
|
||||
},
|
||||
"boolean_t": { "type": "string", "enum": ["0", "1"] },
|
||||
"datatype_t": {
|
||||
"type": "string",
|
||||
"enum": ["int", "unsignedInt", "long", "unsignedLong", "string", "boolean", "dateTime", "base64", "hexBinary"]
|
||||
},
|
||||
"operate_path_t": {
|
||||
"description": "Datamodel object schema path",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 1024,
|
||||
"examples": ["Device.IP.Diagnostics.IPPing()", "Device.DHCPv4.Client.{i}.Renew()", "Device.FactoryReset()"]
|
||||
},
|
||||
"query_path_t": {
|
||||
"description": "DM object path with search queries",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 1024,
|
||||
"examples": [
|
||||
"Device.",
|
||||
"Device.DeviceInfo.Manufacturer",
|
||||
"Device.WiFi.SSID.1.BSSID",
|
||||
"Device.WiFi.SSID.*.BSSID",
|
||||
"Device.WiFi."
|
||||
]
|
||||
},
|
||||
"instance_t": { "description": "Multi object instances", "type": "string", "minLength": 6, "maxLength": 256 },
|
||||
"proto_t": { "type": "string", "default": "both", "enum": ["usp", "cwmp", "both"] },
|
||||
"type_t": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"xsd:string",
|
||||
"xsd:unsignedInt",
|
||||
"xsd:int",
|
||||
"xsd:unsignedLong",
|
||||
"xsd:long",
|
||||
"xsd:boolean",
|
||||
"xsd:dateTime",
|
||||
"xsd:hexBinary",
|
||||
"xsd:object",
|
||||
"xsd:command",
|
||||
"xsd:event"
|
||||
]
|
||||
},
|
||||
"fault_t": { "type": "integer", "minimum": 7000, "maximum": 9050 },
|
||||
"trans_type_t": { "type": "string", "enum": ["start", "commit", "abort", "status"] },
|
||||
"srv_type_t": { "type": "string", "enum": ["register", "list"] },
|
||||
"format_t": { "type": "string", "default": "pretty", "enum": ["raw", "pretty"] }
|
||||
}
|
||||
}
|
||||
{ "results": [{ "path": "veniam amet", "fault": 7810, "fault_msg": "quis" }] }
|
||||
```
|
||||
|
||||
## operate
|
||||
|
|
@ -1123,40 +818,10 @@ Operate on object element provided in path
|
|||
|
||||
| Property | Type | Required |
|
||||
| ------------- | ------ | ------------ |
|
||||
| `command` | string | **Required** |
|
||||
| `command_key` | string | Optional |
|
||||
| `input` | object | Optional |
|
||||
| `optional` | object | Optional |
|
||||
|
||||
#### command
|
||||
|
||||
Datamodel object schema path
|
||||
|
||||
`command`
|
||||
|
||||
- is **required**
|
||||
- type: reference
|
||||
|
||||
##### command Type
|
||||
|
||||
`string`
|
||||
|
||||
- minimum length: 6 characters
|
||||
- maximum length: 1024 characters
|
||||
|
||||
##### command Examples
|
||||
|
||||
```json
|
||||
Device.IP.Diagnostics.IPPing()
|
||||
```
|
||||
|
||||
```json
|
||||
Device.DHCPv4.Client.{i}.Renew()
|
||||
```
|
||||
|
||||
```json
|
||||
Device.FactoryReset()
|
||||
```
|
||||
| `path` | string | **Required** |
|
||||
|
||||
#### command_key
|
||||
|
||||
|
|
@ -1203,31 +868,9 @@ Input arguments for the operate command as defined in TR-181-2.13
|
|||
|
||||
`object` with following properties:
|
||||
|
||||
| Property | Type | Required | Default |
|
||||
| -------- | ------ | -------- | ---------- |
|
||||
| `format` | string | Optional | `"pretty"` |
|
||||
| `proto` | string | Optional | `"both"` |
|
||||
|
||||
#### format
|
||||
|
||||
`format`
|
||||
|
||||
- is optional
|
||||
- type: reference
|
||||
- default: `"pretty"`
|
||||
|
||||
##### format Type
|
||||
|
||||
`string`
|
||||
|
||||
The value of this property **must** be equal to one of the [known values below](#operate-known-values).
|
||||
|
||||
##### format Known Values
|
||||
|
||||
| Value |
|
||||
| ------ |
|
||||
| raw |
|
||||
| pretty |
|
||||
| Property | Type | Required | Default |
|
||||
| -------- | ------ | -------- | -------- |
|
||||
| `proto` | string | Optional | `"both"` |
|
||||
|
||||
#### proto
|
||||
|
||||
|
|
@ -1251,10 +894,40 @@ The value of this property **must** be equal to one of the [known values below](
|
|||
| cwmp |
|
||||
| both |
|
||||
|
||||
#### path
|
||||
|
||||
Datamodel object schema path
|
||||
|
||||
`path`
|
||||
|
||||
- is **required**
|
||||
- type: reference
|
||||
|
||||
##### path Type
|
||||
|
||||
`string`
|
||||
|
||||
- minimum length: 6 characters
|
||||
- maximum length: 1024 characters
|
||||
|
||||
##### path Examples
|
||||
|
||||
```json
|
||||
Device.IP.Diagnostics.IPPing()
|
||||
```
|
||||
|
||||
```json
|
||||
Device.DHCPv4.Client.{i}.Renew()
|
||||
```
|
||||
|
||||
```json
|
||||
Device.FactoryReset()
|
||||
```
|
||||
|
||||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf operate {"command":"dolore anim dolor","command_key":"in eiusmod in culpa non","input":{},"optional":{"format":"pretty","proto":"both"}}
|
||||
ubus call bbf operate {"path":"velit nostrud ullamco","command_key":"ullamco officia nulla pariatur","input":{},"optional":{"proto":"both"}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -1269,10 +942,10 @@ ubus call bbf operate {"command":"dolore anim dolor","command_key":"in eiusmod i
|
|||
"bbf",
|
||||
"operate",
|
||||
{
|
||||
"command": "dolore anim dolor",
|
||||
"command_key": "in eiusmod in culpa non",
|
||||
"path": "velit nostrud ullamco",
|
||||
"command_key": "ullamco officia nulla pariatur",
|
||||
"input": {},
|
||||
"optional": { "format": "pretty", "proto": "both" }
|
||||
"optional": { "proto": "both" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1360,11 +1033,11 @@ All items must be of the type: Unknown type ``.
|
|||
{
|
||||
"results": [
|
||||
{
|
||||
"path": "eteu veniam fugiat al",
|
||||
"data": "1",
|
||||
"fault": 8377,
|
||||
"fault_msg": "cillum magna",
|
||||
"output": [{ "path": "irurein", "data": "0", "type": "xsd:boolean" }]
|
||||
"path": "sitveniam pa",
|
||||
"data": "0",
|
||||
"fault": 8302,
|
||||
"fault_msg": "eiusmod laborum",
|
||||
"output": [{ "path": "dolore pariatur qui", "data": "0", "type": "xsd:dateTime" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1402,39 +1075,9 @@ Schema will have all the nodes/objects supported by libbbf
|
|||
|
||||
| Property | Type | Required |
|
||||
| ------------- | ------- | -------- |
|
||||
| `commands` | boolean | Optional |
|
||||
| `events` | boolean | Optional |
|
||||
| `first_level` | boolean | Optional |
|
||||
| `optional` | object | Optional |
|
||||
| `params` | boolean | Optional |
|
||||
| `path` | string | Optional |
|
||||
| `paths` | array | Optional |
|
||||
|
||||
#### commands
|
||||
|
||||
includes commands in the list if true
|
||||
|
||||
`commands`
|
||||
|
||||
- is optional
|
||||
- type: `boolean`
|
||||
|
||||
##### commands Type
|
||||
|
||||
`boolean`
|
||||
|
||||
#### events
|
||||
|
||||
includes events in the list if true
|
||||
|
||||
`events`
|
||||
|
||||
- is optional
|
||||
- type: `boolean`
|
||||
|
||||
##### events Type
|
||||
|
||||
`boolean`
|
||||
|
||||
#### first_level
|
||||
|
||||
|
|
@ -1486,19 +1129,6 @@ The value of this property **must** be equal to one of the [known values below](
|
|||
| cwmp |
|
||||
| both |
|
||||
|
||||
#### params
|
||||
|
||||
includes objs/params in the list if true
|
||||
|
||||
`params`
|
||||
|
||||
- is optional
|
||||
- type: `boolean`
|
||||
|
||||
##### params Type
|
||||
|
||||
`boolean`
|
||||
|
||||
#### path
|
||||
|
||||
DM object path with search queries
|
||||
|
|
@ -1537,36 +1167,10 @@ Device.WiFi.SSID.*.BSSID
|
|||
Device.WiFi.
|
||||
```
|
||||
|
||||
#### paths
|
||||
|
||||
`paths`
|
||||
|
||||
- is optional
|
||||
- type: `array`
|
||||
|
||||
##### paths Type
|
||||
|
||||
Array type: `array`
|
||||
|
||||
All items must be of the type: Unknown type ``.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": [
|
||||
{
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
}
|
||||
],
|
||||
"simpletype": "`array`"
|
||||
}
|
||||
```
|
||||
|
||||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf schema {"path":"ipsum aliqua","paths":["enim quis laborum"],"first_level":false,"commands":true,"events":false,"params":false,"optional":{"proto":"usp"}}
|
||||
ubus call bbf schema {"path":"idnisi Lorem","first_level":true,"optional":{"proto":"both"}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -1580,15 +1184,7 @@ ubus call bbf schema {"path":"ipsum aliqua","paths":["enim quis laborum"],"first
|
|||
"<SID>",
|
||||
"bbf",
|
||||
"schema",
|
||||
{
|
||||
"path": "ipsum aliqua",
|
||||
"paths": ["enim quis laborum"],
|
||||
"first_level": false,
|
||||
"commands": true,
|
||||
"events": false,
|
||||
"params": false,
|
||||
"optional": { "proto": "usp" }
|
||||
}
|
||||
{ "path": "idnisi Lorem", "first_level": true, "optional": { "proto": "both" } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
@ -1697,27 +1293,27 @@ All items must be of the type: Unknown type ``.
|
|||
{
|
||||
"results": [
|
||||
{
|
||||
"path": "et sunt id",
|
||||
"path": "deserunt sed",
|
||||
"data": "0",
|
||||
"type": "xsd:command",
|
||||
"fault": 8958,
|
||||
"fault_msg": "vel",
|
||||
"input": [{ "path": "nisi elit amet", "data": "0", "type": "xsd:object" }],
|
||||
"output": [{ "path": "anim pariatur ipsum et", "data": "1", "type": "xsd:unsignedLong" }]
|
||||
"type": "xsd:event",
|
||||
"fault": 7839,
|
||||
"fault_msg": "velit sed",
|
||||
"input": [{ "path": "ullamco magna aliquip", "data": "0", "type": "xsd:dateTime" }],
|
||||
"output": [{ "path": "aliqua", "data": "0", "type": "xsd:long" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## service
|
||||
## services
|
||||
|
||||
### show the list of micro-service registred in the core Data Model
|
||||
### show the list of micro-service registred in the Data Model
|
||||
|
||||
`service`
|
||||
`services`
|
||||
|
||||
- type: `Method`
|
||||
|
||||
### service Type
|
||||
### services Type
|
||||
|
||||
`object` with following properties:
|
||||
|
||||
|
|
@ -1757,8 +1353,7 @@ Unknown type ``.
|
|||
| Property | Type | Required | Default |
|
||||
| ---------------- | ------- | -------- | -------- |
|
||||
| `name` | string | Optional | |
|
||||
| `object` | string | Optional | |
|
||||
| `parent_dm` | string | Optional | |
|
||||
| `objects` | object | Optional | |
|
||||
| `proto` | string | Optional | `"both"` |
|
||||
| `unified_daemon` | boolean | Optional | |
|
||||
|
||||
|
|
@ -1773,6 +1368,23 @@ Unknown type ``.
|
|||
|
||||
`string`
|
||||
|
||||
#### objects
|
||||
|
||||
`objects`
|
||||
|
||||
- is optional
|
||||
- type: `object`
|
||||
|
||||
##### objects Type
|
||||
|
||||
`object` with following properties:
|
||||
|
||||
| Property | Type | Required | Default |
|
||||
| ----------- | ------ | -------- | -------- |
|
||||
| `object` | string | Optional | |
|
||||
| `parent_dm` | string | Optional | |
|
||||
| `proto` | string | Optional | `"both"` |
|
||||
|
||||
#### object
|
||||
|
||||
`object`
|
||||
|
|
@ -1807,7 +1419,29 @@ Unknown type ``.
|
|||
|
||||
`string`
|
||||
|
||||
The value of this property **must** be equal to one of the [known values below](#service-known-values).
|
||||
The value of this property **must** be equal to one of the [known values below](#services-known-values).
|
||||
|
||||
##### proto Known Values
|
||||
|
||||
| Value |
|
||||
| ----- |
|
||||
| usp |
|
||||
| cwmp |
|
||||
| both |
|
||||
|
||||
#### proto
|
||||
|
||||
`proto`
|
||||
|
||||
- is optional
|
||||
- type: reference
|
||||
- default: `"both"`
|
||||
|
||||
##### proto Type
|
||||
|
||||
`string`
|
||||
|
||||
The value of this property **must** be equal to one of the [known values below](#services-known-values).
|
||||
|
||||
##### proto Known Values
|
||||
|
||||
|
|
@ -1832,9 +1466,8 @@ The value of this property **must** be equal to one of the [known values below](
|
|||
|
||||
```json
|
||||
{
|
||||
"name": "incididunt cillum Excepteur ipsum laborum",
|
||||
"parent_dm": "consectetur Excepteur eiusmod aliqua minim",
|
||||
"object": "nostrud incididunt",
|
||||
"name": "reprehenderit proident nisi",
|
||||
"objects": { "parent_dm": "adipisicing culpa in", "object": "elit", "proto": "both" },
|
||||
"proto": "usp",
|
||||
"unified_daemon": false
|
||||
}
|
||||
|
|
@ -2039,7 +1672,7 @@ value of the object element provided in path, path should contains valid writabl
|
|||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf set {"path":"aliqua aliquip","value":"aliqua","datatype":"int","obj_path":{}}
|
||||
ubus call bbf set {"path":"consectetur proident Ut consequat tempor","value":"cillum quis sed aliquip","datatype":"boolean","obj_path":{}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -2049,7 +1682,17 @@ ubus call bbf set {"path":"aliqua aliquip","value":"aliqua","datatype":"int","ob
|
|||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "call",
|
||||
"params": ["<SID>", "bbf", "set", { "path": "aliqua aliquip", "value": "aliqua", "datatype": "int", "obj_path": {} }]
|
||||
"params": [
|
||||
"<SID>",
|
||||
"bbf",
|
||||
"set",
|
||||
{
|
||||
"path": "consectetur proident Ut consequat tempor",
|
||||
"value": "cillum quis sed aliquip",
|
||||
"datatype": "boolean",
|
||||
"obj_path": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -2113,5 +1756,5 @@ All items must be of the type: Unknown type ``.
|
|||
### Output Example
|
||||
|
||||
```json
|
||||
{ "results": [{ "path": "in est veniam incididunt", "data": "1", "fault": 7720, "fault_msg": "anim" }] }
|
||||
{ "results": [{ "path": "quis u", "data": "1", "fault": 7028, "fault_msg": "qui pariatur ipsum nisi" }] }
|
||||
```
|
||||
|
|
|
|||
|
|
@ -68,12 +68,6 @@
|
|||
"Device.WiFi."
|
||||
]
|
||||
},
|
||||
"instance_t": {
|
||||
"description": "Multi object instances",
|
||||
"type": "string",
|
||||
"minLength": 6,
|
||||
"maxLength": 256
|
||||
},
|
||||
"proto_t": {
|
||||
"type": "string",
|
||||
"default": "both",
|
||||
|
|
@ -104,22 +98,6 @@
|
|||
"minimum": 7000,
|
||||
"maximum": 9050
|
||||
},
|
||||
"trans_type_t": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"start",
|
||||
"commit",
|
||||
"abort",
|
||||
"status"
|
||||
]
|
||||
},
|
||||
"srv_type_t": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"register",
|
||||
"list"
|
||||
]
|
||||
},
|
||||
"format_t": {
|
||||
"type": "string",
|
||||
"default": "pretty",
|
||||
|
|
@ -150,31 +128,10 @@
|
|||
"path": {
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
},
|
||||
"paths": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": [
|
||||
{
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
}
|
||||
]
|
||||
},
|
||||
"first_level": {
|
||||
"type": "boolean",
|
||||
"description": "gets only first level objects if true"
|
||||
},
|
||||
"commands": {
|
||||
"type": "boolean",
|
||||
"description": "includes commands in the list if true"
|
||||
},
|
||||
"events": {
|
||||
"type": "boolean",
|
||||
"description": "includes events in the list if true"
|
||||
},
|
||||
"params": {
|
||||
"type": "boolean",
|
||||
"description": "includes objs/params in the list if true"
|
||||
},
|
||||
"optional": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -278,19 +235,6 @@
|
|||
"path": {
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
},
|
||||
"paths": {
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": [
|
||||
{
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
}
|
||||
]
|
||||
},
|
||||
"maxdepth": {
|
||||
"type": "integer",
|
||||
"description": "Integer to decide the depth of data model to be parsed"
|
||||
},
|
||||
"optional": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -358,10 +302,6 @@
|
|||
"path": {
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
},
|
||||
"first_level": {
|
||||
"type": "boolean",
|
||||
"description": "gets only first level objects if true"
|
||||
},
|
||||
"optional": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -625,10 +565,10 @@
|
|||
"input": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"command"
|
||||
"path"
|
||||
],
|
||||
"properties": {
|
||||
"command": {
|
||||
"path": {
|
||||
"$ref": "#/definitions/operate_path_t"
|
||||
},
|
||||
"command_key": {
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ Device.WiFi.
|
|||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf add {"path":"eu qui","obj_path":{}}
|
||||
ubus call bbf add {"path":"eucommodo voluptate cillum","obj_path":{}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -117,7 +117,7 @@ ubus call bbf add {"path":"eu qui","obj_path":{}}
|
|||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "call",
|
||||
"params": ["<SID>", "bbf", "add", { "path": "eu qui", "obj_path": {} }]
|
||||
"params": ["<SID>", "bbf", "add", { "path": "eucommodo voluptate cillum", "obj_path": {} }]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -183,7 +183,12 @@ All items must be of the type: Unknown type ``.
|
|||
```json
|
||||
{
|
||||
"results": [
|
||||
{ "path": "magna enim", "data": "in aliquip id eu deserunt", "fault": 8757, "fault_msg": "quis id ut in" }
|
||||
{
|
||||
"path": "eiusmod consequ",
|
||||
"data": "adipisicing incidid",
|
||||
"fault": 8484,
|
||||
"fault_msg": "in labore nostrud voluptate est"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
@ -286,7 +291,7 @@ All items must be of the type: Unknown type ``.
|
|||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf del {"path":"incididunt tempor","paths":["dolore aliqua labore labori"]}
|
||||
ubus call bbf del {"path":"eiusmod c","paths":["utamet incididunt fugiat in labore"]}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -296,7 +301,7 @@ ubus call bbf del {"path":"incididunt tempor","paths":["dolore aliqua labore lab
|
|||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "call",
|
||||
"params": ["<SID>", "bbf", "del", { "path": "incididunt tempor", "paths": ["dolore aliqua labore labori"] }]
|
||||
"params": ["<SID>", "bbf", "del", { "path": "eiusmod c", "paths": ["utamet incididunt fugiat in labore"] }]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -362,7 +367,12 @@ All items must be of the type: Unknown type ``.
|
|||
```json
|
||||
{
|
||||
"results": [
|
||||
{ "path": "aliqua id deserunt", "data": "reprehenderit eu sed esse", "fault": 8791, "fault_msg": "occaecat" }
|
||||
{
|
||||
"path": "eiusmod ipsum",
|
||||
"data": "Lorem sunt laboris occaecat dolor",
|
||||
"fault": 7023,
|
||||
"fault_msg": "aute proident"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
@ -397,25 +407,10 @@ Query the datamodel object
|
|||
|
||||
`object` with following properties:
|
||||
|
||||
| Property | Type | Required |
|
||||
| ---------- | ------- | ------------ |
|
||||
| `maxdepth` | integer | Optional |
|
||||
| `optional` | object | Optional |
|
||||
| `path` | string | **Required** |
|
||||
| `paths` | array | Optional |
|
||||
|
||||
#### maxdepth
|
||||
|
||||
Integer to decide the depth of data model to be parsed
|
||||
|
||||
`maxdepth`
|
||||
|
||||
- is optional
|
||||
- type: `integer`
|
||||
|
||||
##### maxdepth Type
|
||||
|
||||
`integer`
|
||||
| Property | Type | Required |
|
||||
| ---------- | ------ | ------------ |
|
||||
| `optional` | object | Optional |
|
||||
| `path` | string | **Required** |
|
||||
|
||||
#### optional
|
||||
|
||||
|
|
@ -514,36 +509,10 @@ Device.WiFi.SSID.*.BSSID
|
|||
Device.WiFi.
|
||||
```
|
||||
|
||||
#### paths
|
||||
|
||||
`paths`
|
||||
|
||||
- is optional
|
||||
- type: `array`
|
||||
|
||||
##### paths Type
|
||||
|
||||
Array type: `array`
|
||||
|
||||
All items must be of the type: Unknown type ``.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": [
|
||||
{
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
}
|
||||
],
|
||||
"simpletype": "`array`"
|
||||
}
|
||||
```
|
||||
|
||||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf get {"path":"auteaute","paths":["culpa amet elit irure"],"maxdepth":-44630385,"optional":{"format":"raw","proto":"cwmp"}}
|
||||
ubus call bbf get {"path":"Lorem c","optional":{"format":"raw","proto":"both"}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -553,17 +522,7 @@ ubus call bbf get {"path":"auteaute","paths":["culpa amet elit irure"],"maxdepth
|
|||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "call",
|
||||
"params": [
|
||||
"<SID>",
|
||||
"bbf",
|
||||
"get",
|
||||
{
|
||||
"path": "auteaute",
|
||||
"paths": ["culpa amet elit irure"],
|
||||
"maxdepth": -44630385,
|
||||
"optional": { "format": "raw", "proto": "cwmp" }
|
||||
}
|
||||
]
|
||||
"params": ["<SID>", "bbf", "get", { "path": "Lorem c", "optional": { "format": "raw", "proto": "both" } }]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -633,11 +592,11 @@ All items must be of the type: Unknown type ``.
|
|||
{
|
||||
"results": [
|
||||
{
|
||||
"path": "dolore eu",
|
||||
"data": "et sit exercitation cupid",
|
||||
"type": "xsd:string",
|
||||
"fault": 7957,
|
||||
"fault_msg": "sit l"
|
||||
"path": "eiusmod consectetu",
|
||||
"data": "laborum non",
|
||||
"type": "xsd:hexBinary",
|
||||
"fault": 7671,
|
||||
"fault_msg": "enim ea fugiat velit nulla"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -673,24 +632,10 @@ Get the instances of multi object
|
|||
|
||||
`object` with following properties:
|
||||
|
||||
| Property | Type | Required |
|
||||
| ------------- | ------- | ------------ |
|
||||
| `first_level` | boolean | Optional |
|
||||
| `optional` | object | Optional |
|
||||
| `path` | string | **Required** |
|
||||
|
||||
#### first_level
|
||||
|
||||
gets only first level objects if true
|
||||
|
||||
`first_level`
|
||||
|
||||
- is optional
|
||||
- type: `boolean`
|
||||
|
||||
##### first_level Type
|
||||
|
||||
`boolean`
|
||||
| Property | Type | Required |
|
||||
| ---------- | ------ | ------------ |
|
||||
| `optional` | object | Optional |
|
||||
| `path` | string | **Required** |
|
||||
|
||||
#### optional
|
||||
|
||||
|
|
@ -770,7 +715,7 @@ Device.WiFi.
|
|||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf instances {"path":"laborum magna Excepteur est","first_level":false,"optional":{"proto":"usp"}}
|
||||
ubus call bbf instances {"path":"sint culpa laborum","optional":{"proto":"usp"}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -780,12 +725,7 @@ ubus call bbf instances {"path":"laborum magna Excepteur est","first_level":fals
|
|||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "call",
|
||||
"params": [
|
||||
"<SID>",
|
||||
"bbf",
|
||||
"instances",
|
||||
{ "path": "laborum magna Excepteur est", "first_level": false, "optional": { "proto": "usp" } }
|
||||
]
|
||||
"params": ["<SID>", "bbf", "instances", { "path": "sint culpa laborum", "optional": { "proto": "usp" } }]
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -846,7 +786,7 @@ All items must be of the type: Unknown type ``.
|
|||
### Output Example
|
||||
|
||||
```json
|
||||
{ "results": [{ "path": "Duis dolor e", "fault": 8621, "fault_msg": "et nulla" }] }
|
||||
{ "results": [{ "path": "labore Lorem in", "fault": 8069, "fault_msg": "adipisicing non anim cupidatat amet" }] }
|
||||
```
|
||||
|
||||
## operate
|
||||
|
|
@ -881,40 +821,10 @@ Operate on object element provided in path
|
|||
|
||||
| Property | Type | Required |
|
||||
| ------------- | ------ | ------------ |
|
||||
| `command` | string | **Required** |
|
||||
| `command_key` | string | Optional |
|
||||
| `input` | object | Optional |
|
||||
| `optional` | object | Optional |
|
||||
|
||||
#### command
|
||||
|
||||
Datamodel object schema path
|
||||
|
||||
`command`
|
||||
|
||||
- is **required**
|
||||
- type: reference
|
||||
|
||||
##### command Type
|
||||
|
||||
`string`
|
||||
|
||||
- minimum length: 6 characters
|
||||
- maximum length: 1024 characters
|
||||
|
||||
##### command Examples
|
||||
|
||||
```json
|
||||
Device.IP.Diagnostics.IPPing()
|
||||
```
|
||||
|
||||
```json
|
||||
Device.DHCPv4.Client.{i}.Renew()
|
||||
```
|
||||
|
||||
```json
|
||||
Device.FactoryReset()
|
||||
```
|
||||
| `path` | string | **Required** |
|
||||
|
||||
#### command_key
|
||||
|
||||
|
|
@ -1009,10 +919,40 @@ The value of this property **must** be equal to one of the [known values below](
|
|||
| cwmp |
|
||||
| both |
|
||||
|
||||
#### path
|
||||
|
||||
Datamodel object schema path
|
||||
|
||||
`path`
|
||||
|
||||
- is **required**
|
||||
- type: reference
|
||||
|
||||
##### path Type
|
||||
|
||||
`string`
|
||||
|
||||
- minimum length: 6 characters
|
||||
- maximum length: 1024 characters
|
||||
|
||||
##### path Examples
|
||||
|
||||
```json
|
||||
Device.IP.Diagnostics.IPPing()
|
||||
```
|
||||
|
||||
```json
|
||||
Device.DHCPv4.Client.{i}.Renew()
|
||||
```
|
||||
|
||||
```json
|
||||
Device.FactoryReset()
|
||||
```
|
||||
|
||||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf operate {"command":"in cupidatat minim reprehenderit","command_key":"cillum aliqua anim et veniam","input":{},"optional":{"format":"pretty","proto":"usp"}}
|
||||
ubus call bbf operate {"path":"aliqua elit deserunt","command_key":"nostrud aute sed","input":{},"optional":{"format":"raw","proto":"cwmp"}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -1027,10 +967,10 @@ ubus call bbf operate {"command":"in cupidatat minim reprehenderit","command_key
|
|||
"bbf",
|
||||
"operate",
|
||||
{
|
||||
"command": "in cupidatat minim reprehenderit",
|
||||
"command_key": "cillum aliqua anim et veniam",
|
||||
"path": "aliqua elit deserunt",
|
||||
"command_key": "nostrud aute sed",
|
||||
"input": {},
|
||||
"optional": { "format": "pretty", "proto": "usp" }
|
||||
"optional": { "format": "raw", "proto": "cwmp" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1118,11 +1058,11 @@ All items must be of the type: Unknown type ``.
|
|||
{
|
||||
"results": [
|
||||
{
|
||||
"path": "doDuis",
|
||||
"data": "0",
|
||||
"fault": 8434,
|
||||
"fault_msg": "co",
|
||||
"output": [{ "path": "anim vel", "data": "1", "type": "xsd:unsignedLong" }]
|
||||
"path": "do cillum",
|
||||
"data": "1",
|
||||
"fault": 8145,
|
||||
"fault_msg": "velit irure dolore qui proident",
|
||||
"output": [{ "path": "labore", "data": "1", "type": "xsd:int" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1160,39 +1100,9 @@ Schema will have all the nodes/objects supported by libbbf
|
|||
|
||||
| Property | Type | Required |
|
||||
| ------------- | ------- | -------- |
|
||||
| `commands` | boolean | Optional |
|
||||
| `events` | boolean | Optional |
|
||||
| `first_level` | boolean | Optional |
|
||||
| `optional` | object | Optional |
|
||||
| `params` | boolean | Optional |
|
||||
| `path` | string | Optional |
|
||||
| `paths` | array | Optional |
|
||||
|
||||
#### commands
|
||||
|
||||
includes commands in the list if true
|
||||
|
||||
`commands`
|
||||
|
||||
- is optional
|
||||
- type: `boolean`
|
||||
|
||||
##### commands Type
|
||||
|
||||
`boolean`
|
||||
|
||||
#### events
|
||||
|
||||
includes events in the list if true
|
||||
|
||||
`events`
|
||||
|
||||
- is optional
|
||||
- type: `boolean`
|
||||
|
||||
##### events Type
|
||||
|
||||
`boolean`
|
||||
|
||||
#### first_level
|
||||
|
||||
|
|
@ -1244,19 +1154,6 @@ The value of this property **must** be equal to one of the [known values below](
|
|||
| cwmp |
|
||||
| both |
|
||||
|
||||
#### params
|
||||
|
||||
includes objs/params in the list if true
|
||||
|
||||
`params`
|
||||
|
||||
- is optional
|
||||
- type: `boolean`
|
||||
|
||||
##### params Type
|
||||
|
||||
`boolean`
|
||||
|
||||
#### path
|
||||
|
||||
DM object path with search queries
|
||||
|
|
@ -1295,36 +1192,10 @@ Device.WiFi.SSID.*.BSSID
|
|||
Device.WiFi.
|
||||
```
|
||||
|
||||
#### paths
|
||||
|
||||
`paths`
|
||||
|
||||
- is optional
|
||||
- type: `array`
|
||||
|
||||
##### paths Type
|
||||
|
||||
Array type: `array`
|
||||
|
||||
All items must be of the type: Unknown type ``.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "array",
|
||||
"uniqueItems": true,
|
||||
"items": [
|
||||
{
|
||||
"$ref": "#/definitions/query_path_t"
|
||||
}
|
||||
],
|
||||
"simpletype": "`array`"
|
||||
}
|
||||
```
|
||||
|
||||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf schema {"path":"Excepteur dolor","paths":["adipisicing veniam"],"first_level":true,"commands":false,"events":true,"params":false,"optional":{"proto":"both"}}
|
||||
ubus call bbf schema {"path":"eiusmod in nostrud Excepteur","first_level":false,"optional":{"proto":"usp"}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -1338,15 +1209,7 @@ ubus call bbf schema {"path":"Excepteur dolor","paths":["adipisicing veniam"],"f
|
|||
"<SID>",
|
||||
"bbf",
|
||||
"schema",
|
||||
{
|
||||
"path": "Excepteur dolor",
|
||||
"paths": ["adipisicing veniam"],
|
||||
"first_level": true,
|
||||
"commands": false,
|
||||
"events": true,
|
||||
"params": false,
|
||||
"optional": { "proto": "both" }
|
||||
}
|
||||
{ "path": "eiusmod in nostrud Excepteur", "first_level": false, "optional": { "proto": "usp" } }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
@ -1455,13 +1318,13 @@ All items must be of the type: Unknown type ``.
|
|||
{
|
||||
"results": [
|
||||
{
|
||||
"path": "ad dolore eiusmo",
|
||||
"path": "id deserunt exer",
|
||||
"data": "1",
|
||||
"type": "xsd:unsignedInt",
|
||||
"fault": 8099,
|
||||
"fault_msg": "amet laboris",
|
||||
"input": [{ "path": "dolor cillum nulla", "data": "1", "type": "xsd:int" }],
|
||||
"output": [{ "path": "ut eu i", "data": "0", "type": "xsd:object" }]
|
||||
"type": "xsd:string",
|
||||
"fault": 9040,
|
||||
"fault_msg": "velit",
|
||||
"input": [{ "path": "innostrud quis sit", "data": "0", "type": "xsd:string" }],
|
||||
"output": [{ "path": "sint quis aliqua", "data": "0", "type": "xsd:unsignedLong" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1666,7 +1529,7 @@ value of the object element provided in path, path should contains valid writabl
|
|||
### Ubus CLI Example
|
||||
|
||||
```
|
||||
ubus call bbf set {"path":"sint nulla nisi reprehenderit dol","value":"aliqua sit aliquip","datatype":"long","obj_path":{}}
|
||||
ubus call bbf set {"path":"aliquip consequat fugiat","value":"esse","datatype":"hexBinary","obj_path":{}}
|
||||
```
|
||||
|
||||
### JSONRPC Example
|
||||
|
|
@ -1680,7 +1543,7 @@ ubus call bbf set {"path":"sint nulla nisi reprehenderit dol","value":"aliqua si
|
|||
"<SID>",
|
||||
"bbf",
|
||||
"set",
|
||||
{ "path": "sint nulla nisi reprehenderit dol", "value": "aliqua sit aliquip", "datatype": "long", "obj_path": {} }
|
||||
{ "path": "aliquip consequat fugiat", "value": "esse", "datatype": "hexBinary", "obj_path": {} }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
|
@ -1745,5 +1608,5 @@ All items must be of the type: Unknown type ``.
|
|||
### Output Example
|
||||
|
||||
```json
|
||||
{ "results": [{ "path": "in esse et", "data": "1", "fault": 8550, "fault_msg": "incididunt in Ut nisi" }] }
|
||||
{ "results": [{ "path": "auteconsequat in", "data": "0", "fault": 8847, "fault_msg": "irure esse Lorem" }] }
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,89 +1,143 @@
|
|||
# BBFDM Daemon (bbfdmd)
|
||||
# BBFDM Daemon (`bbfdmd`)
|
||||
|
||||
`bbfdmd` daemon responsible for creating a datamodel layer between system resources and exposing TR181 data-model objects over ubus for higher layer application protocols like [TR-069/cwmp](https://cwmp-data-models.broadband-forum.org/) or [TR-369/USP](https://usp.technology/).
|
||||
`bbfdmd` daemon creates a data model layer between system resources and exposes TR-181 data model objects over `ubus` for higher-layer application protocols such as [TR-069/CWMP](https://cwmp-data-models.broadband-forum.org/) or [TR-369/USP](https://usp.technology/).
|
||||
|
||||
> Note 1: The command outputs shown in this document are examples only, actual output may differ based on device and configuration.
|
||||
> **Note:** The command outputs shown in this document are examples. The actual output may vary depending on the device and configuration.
|
||||
|
||||
---
|
||||
|
||||
## Concepts and Workflow
|
||||
|
||||
`bbfdmd` daemon gets started by `/etc/init.d/bbfdmd` service, bbfdmd init script reads the input from `bbfdm` uci file and then it starts using the APIs provided by `libbbfdm-ubus` and `libbbfdm-api`.
|
||||
`bbfdmd` daemon collects data models from various microservices and exposes them over `ubus`. It does not handle the data model internally or validate its existence.
|
||||
|
||||
`bbfdmd` daemon use `libbbfdm-api` library to traverse the core datamodel tree added by static library `libbbfdm` or other plugins defined in `/usr/share/bbfdm/plugins`.
|
||||
### Initialization
|
||||
|
||||
`bbfdmd` daemon use `libbbfdm-ubus` library to expose datamodel over ubus.
|
||||
1. The `bbfdmd` daemon is started by the `/etc/init.d/bbfdmd` service.
|
||||
2. During startup, it registers all available microservices listed under `/etc/bbfdm/service/` by parsing each JSON file to extract information such as:
|
||||
- `service_name`
|
||||
- Data model paths exposed by the service
|
||||
- Protocol visibility (CWMP or USP)
|
||||
3. After processing the JSON files, it registers the supported `bbfdm` ubus methods.
|
||||
|
||||
When a ubus method is called it first fills `bbfdm_data_t` structure with the necessary information, then register the micro-services information defined for each JSON service file after that proceeds the `Get/Set/Operate/Add/Del` operation based on that information.
|
||||
### Handling Ubus Calls
|
||||
|
||||
To load the datamodel definitions from a DotSO file, it looks for a 'tDynamicObj' symbol and use it to create the base entry object, for datamodel operations it rely on libbbfdm-api's `bbf_entry_method` which process the datamodel operation on input path and produces result in list/blob, which further gets responded over ubus.
|
||||
When a `bbfdm` ubus object is called, the daemon determines the handling type based on the ubus method:
|
||||
|
||||
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 different methods and the existing data-model available with `libbbfdm`.
|
||||
- **Asynchronous methods** (`get`, `schema`, `instances`, `operate`)
|
||||
These methods are executed asynchronously to collect data model output from multiple microservices simultaneously.
|
||||
|
||||
> Note1: In general, bbfdmd does not reload the services after updating the configs, higher layer applications (i.e. icwmp, obuspa) usages `bbf.config` to apply the configs and reloads the services, please check `bbf.config` documentation for more details.
|
||||
- **Synchronous methods** (`set`, `add`, `delete`)
|
||||
These methods are executed synchronously because they only target a specific microservice.
|
||||
|
||||
> Note2: All RPC method's output is stored directly in a blob buffer, which can be used at the end by Ubus reply API to expose the data and reducing CPU usage of `bbfdmd` daemon.
|
||||
### Microservice Identification
|
||||
|
||||
## Input and output Schema(s)
|
||||
To identify which microservice to call, `bbfdm`:
|
||||
- Extracts the requested path from the `"path"` input.
|
||||
- Retrieves the protocol from `"optional.proto"` input.
|
||||
- Matches the extracted path and protocol against the list of registered services.
|
||||
|
||||
`bbfdmd` basic configuration can be updated with uci, a guide and uci schema available in following links
|
||||
- [bbfdm uci guide](../api/uci/bbfdm.md)
|
||||
- [bbfdm uci schema](../api/uci/bbfdm.json)
|
||||
---
|
||||
|
||||
`bbfdmd` ubus guide and schema
|
||||
- [bbfdm ubus guide](../api/ubus/bbfdm.md)
|
||||
- [bbfdm ubus schema](../api/ubus/bbfdm.json)
|
||||
## Example Configuration
|
||||
|
||||
### Ubus methods
|
||||
```json
|
||||
{
|
||||
"daemon": {
|
||||
"enable": "1",
|
||||
"service_name": "dhcpmngr",
|
||||
"unified_daemon": false,
|
||||
"services": [
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "DHCPv4"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "DHCPv6"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"loglevel": "3"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Following are the ubus methods exposed by `bbfdmd` main process:
|
||||
### Explanation
|
||||
|
||||
- **service_name** → `dhcpmngr` → `ubus` service name is `bbfdm.dhcpmngr`
|
||||
- **Data model paths** → `Device.DHCPv4.` and `Device.DHCPv6.`
|
||||
- **Protocol** → Both CWMP and USP since no restriction is defined in the JSON file.
|
||||
|
||||
Any call to `bbfdm` with the path `Device.DHCPv4.XXXX` or `Device.DHCPv6.XXXX` will be directed to the `dhcpmngr` microservice.
|
||||
|
||||
> **Note:** `bbfdmd` does not automatically reload services after updating the configuration. Higher-layer applications (e.g., `icwmp`, `obuspa`) use `bbf.config` to apply updates and reload services.
|
||||
|
||||
---
|
||||
|
||||
## Input and Output Schema
|
||||
|
||||
### Configuration
|
||||
- `bbfdmd` configuration can be updated using `uci`:
|
||||
- [bbfdm uci guide](../api/uci/bbfdm.md)
|
||||
- [bbfdm uci schema](../api/uci/bbfdm.json)
|
||||
|
||||
### Ubus Schema
|
||||
- `bbfdmd` exposes a detailed schema for ubus methods:
|
||||
- [bbfdm ubus guide](../api/ubus/bbfdm.md)
|
||||
- [bbfdm ubus schema](../api/ubus/bbfdm.json)
|
||||
|
||||
---
|
||||
|
||||
## Ubus Methods
|
||||
|
||||
### Available Methods
|
||||
|
||||
```bash
|
||||
# ubus -v list bbfdm
|
||||
'bbfdm' @b93b62aa
|
||||
"get":{"path":"String","paths":"Array","maxdepth":"Integer","optional":"Table"}
|
||||
"schema":{"path":"String","paths":"Array","first_level":"Boolean","optional":"Table"}
|
||||
"instances":{"path":"String","paths":"Array","first_level":"Boolean","optional":"Table"}
|
||||
"set":{"path":"String","value":"String","datatype":"String","obj_path":"Table","optional":"Table"}
|
||||
"operate":{"command":"String","command_key":"String","input":"Table","optional":"Table"}
|
||||
"add":{"path":"String","obj_path":"Table","optional":"Table"}
|
||||
"del":{"path":"String","paths":"Array","optional":"Table"}
|
||||
"notify_event":{"name":"String","input":"Array"}
|
||||
"service":{}
|
||||
'bbfdm' @7971a201
|
||||
"get":{"path":"String","value":"String","optional":"Table"}
|
||||
"schema":{"path":"String","value":"String","optional":"Table"}
|
||||
"instances":{"path":"String","value":"String","optional":"Table"}
|
||||
"operate":{"path":"String","value":"String","optional":"Table"}
|
||||
"set":{"path":"String","value":"String","optional":"Table"}
|
||||
"add":{"path":"String","value":"String","optional":"Table"}
|
||||
"del":{"path":"String","value":"String","optional":"Table"}
|
||||
"services":{}
|
||||
```
|
||||
|
||||
> Note1: `optional` table are present in all methods and it supports below options:
|
||||
### `optional` Table Options
|
||||
|
||||
```console
|
||||
"optional":{"proto":"String", "format":"String"}
|
||||
```json
|
||||
"optional": {"proto": "String", "format": "String"}
|
||||
```
|
||||
|
||||
- `proto` in each method specify the data-model prototype('cwmp', 'usp') to use, if not provided default data-model will be used.
|
||||
- **proto** → Specifies the data model protocol (`cwmp`, `usp`). Defaults to the standard model if not provided.
|
||||
- **format** → Specifies the output format (`raw`, `pretty`). Defaults to `pretty` if not provided.
|
||||
|
||||
- `format` could be 'raw' or 'pretty', to specify the format to use as output, if not provided 'pretty' format will be used.
|
||||
> **Note:** See the ubus schema document for more details.
|
||||
|
||||
> Note2: `first_level` true means only get next level objects and false means get all objects recursively
|
||||
---
|
||||
|
||||
> Note3: `maxdepth` is measured on max number of .(Dot) present in object name
|
||||
## Debugging Tools
|
||||
|
||||
> Note4: Check ubus schema document for more details
|
||||
### Command Line Interface (CLI)
|
||||
|
||||
|
||||
## Datamodel debugging tools
|
||||
|
||||
To debug the datamodel objects/parameters, `bbfdmd` provides a command line interface, which can gets the data from an ubus object and then show it on the CLI. This command line tool is part of `bbfdmd` binary itself and can be accessed with command line argument option '-c' along with binary.
|
||||
`bbfdmd` provides a CLI to debug the data model using the `-c` option:
|
||||
|
||||
```bash
|
||||
# bbfdmd -h
|
||||
Usage: bbfdmd [options]
|
||||
|
||||
options:
|
||||
-c <command input> Run cli command
|
||||
-l <loglevel> log verbosity value as per standard syslog
|
||||
-h Displays this help
|
||||
|
||||
#
|
||||
Options:
|
||||
-c <command input> Run CLI command
|
||||
-l <loglevel> Log verbosity value (0-7) as per syslog
|
||||
-h Displays this help
|
||||
```
|
||||
If no command line option provided along with `bbfdmd` command then it starts in daemon mode.
|
||||
In command (or CLI) mode, it supports interactively querying the data model and setting values in the configuration via the data model.
|
||||
|
||||
If no options are provided, `bbfdmd` starts in daemon mode.
|
||||
|
||||
### CLI Commands
|
||||
|
||||
```bash
|
||||
# bbfdmd -c help
|
||||
|
|
@ -95,63 +149,28 @@ Valid commands:
|
|||
del [path-expr]
|
||||
instances [path-expr]
|
||||
schema [path-expr]
|
||||
#
|
||||
```
|
||||
|
||||
To debug datamodel mapping/description code, user/developer can use below API to add debug logs
|
||||
### Debugging Logs
|
||||
|
||||
```bash
|
||||
BBF_ERR(MESSAGE, ...)
|
||||
BBF_WARNING(MESSAGE, ...)
|
||||
BBF_INFO(MESSAGE, ...)
|
||||
BBF_DEBUG(MESSAGE, ...)
|
||||
To enable debugging logs, use the following macros:
|
||||
|
||||
```c
|
||||
BBFDM_ERR(MESSAGE, ...)
|
||||
BBFDM_WARNING(MESSAGE, ...)
|
||||
BBFDM_INFO(MESSAGE, ...)
|
||||
BBFDM_DEBUG(MESSAGE, ...)
|
||||
```
|
||||
|
||||
Above API logs gets logged using syslog, based on log_level defined in `bbfdm`.
|
||||
To configure the log_level use `bbfdm.bbfdmd.loglevel` uci option.
|
||||
Logs are written to `syslog` based on the `log_level` defined in the `bbfdm` configuration.
|
||||
To configure the log level, use the `bbfdm.bbfdmd.loglevel` option.
|
||||
|
||||
## How to extend datamodel
|
||||
---
|
||||
|
||||
Although `bbfdm/iowrt` provides datamodels for major services, but still for deployment user might need to add some vendor extensions or needs to add the missing datamodel support. To do the same `bbfdm` or more precisely `libbfdm-api` provides the infrastructure to easily define a new datamodel tree.
|
||||
## Notes
|
||||
|
||||
As per TR106 description, a datamodel is, "A hierarchical set of Objects, Parameters, Commands and/or Events that define the managed Objects accessible via a particular Agent."
|
||||
1. `bbfdmd` does not handle RPC method output.
|
||||
2. The actual arguments expected by `bbfdmd` may differ from the ones shown in the ubus schema because `bbfdmd` only routes the calls to the correct microservice.
|
||||
|
||||
Please check [TR106](https://www.broadband-forum.org/pdfs/tr-106-1-13-0.pdf) for more details about datamodel terminology.
|
||||
---
|
||||
|
||||
`bbfdm` provide the tools and utilities to further extend/overwrite/disable the datamodel using C-code or simply by using JSON datamodel definition.
|
||||
|
||||
### JSON datamodel
|
||||
|
||||
Pro:
|
||||
- Easy to add (compilation not required)
|
||||
- Least maintenance (Change in libbbfdm-api has minimal impact)
|
||||
|
||||
Con:
|
||||
- Can only support easy one to one mappings with uci and ubus
|
||||
- Invalid plugin syntax might cause faults
|
||||
|
||||
### C Based datamodel
|
||||
|
||||
Pro:
|
||||
- Support complex mapping and data sharing between nodes
|
||||
- Lots of references available
|
||||
- Tools available to auto-generate the template code
|
||||
- All core operations supported
|
||||
|
||||
Con:
|
||||
- Moderate maintenance (Change in libbbfdm-api requires adaptation/alignment)
|
||||
|
||||
|
||||
Both ways of extending datamodel covered at length with examples in following links
|
||||
* [How to extend datamodel with C Code](How_to_extend_datamodel_with_C_Code.md)
|
||||
* [How to extend datamodel with JSON](How_to_extend_datamodel_with_JSON.md)
|
||||
|
||||
After creating the datamodel definition, it can be installed the help of `bbfdm.mk` APIs to run them from main `bbfdmd` instance or from micro-service instance.
|
||||
|
||||
> Note: If a datamodel object added by a micro-service, all datamodel extensions below that path needed to be hanlded in the same micro-service. Like a wifi extension needed to be installed in wifidmd micro-service as a plugin.
|
||||
|
||||
### How to choose C or JSON for datamodel extensions
|
||||
|
||||
C/JSON both datamodel definition support defining all datamodel operations, but JSON should be used with simple datamodel deployments.
|
||||
|
||||
If its requires to perform more than one step to retrieve data from lowerlayer, it is suggested to use C-Based datamodel definitions, as it gives more control over the data and its mapping, or simply put if JSON plugin does not meets the requirement of datamodel mapping use C-Based definition.
|
||||
|
|
|
|||
|
|
@ -1,26 +1,32 @@
|
|||
# Data Model Micro-Service (dm-service
|
||||
# Data Model Micro-Service (dm-service)
|
||||
|
||||
`dm-service` daemon is designed to expose a specific module or sub-tree of a data model as datamodel micro-service.
|
||||
`dm-service` daemon is designed to expose a specific module or sub-tree of a data model as a datamodel micro-service.
|
||||
|
||||
> Note: The command outputs shown in this document are examples and may vary depending on your device and configuration.
|
||||
|
||||
## Concepts and Workflow
|
||||
|
||||
`dm-service` daemon gets started by `/etc/init.d/bbfdm.services` service. This init script reads input from `bbfdm` UCI file, particularly from `micro_services` section. It then parses each micro-service configuration file located in `/etc/bbfdm/services/`. Each micro-service’s configuration, written in JSON, is used to start the service using the APIs provided by `libbbfdm-ubus` and `libbbfdm-api` libraries.
|
||||
`dm-service` daemon is started by `/etc/init.d/bbfdm.services` service. This init script reads input from `bbfdm` UCI file, particularly from the `micro_services` section. It then parses each micro-service configuration file located in `/etc/bbfdm/services/`. Each micro-service’s configuration, written in JSON, is used to start the service using the APIs provided by `libbbfdm-ubus` and `libbbfdm-api` libraries.
|
||||
|
||||
`dm-service` daemon use `libbbfdm-api` library to traverse the datamodel tree defined by DotSo or JSON plugin located in `/usr/share/bbfdm/micro_services/$micro-service-name{.so,.json}` and its plugins defined in `/usr/share/bbfdm/micro_services/$micro-service-name/`.
|
||||
`dm-service` daemon uses the `libbbfdm-api/legacy` library to traverse the datamodel tree defined by DotSo or JSON plugins located in `/usr/share/bbfdm/micro_services/$micro-service-name{.so,.json}` and its plugins defined in `/usr/share/bbfdm/micro_services/$micro-service-name/`.
|
||||
|
||||
`dm-service` daemon use `libbbfdm-ubus` library to expose datamodel over UBUS.
|
||||
`dm-service` daemon uses the `libbbfdm-ubus` library to expose the datamodel over UBUS.
|
||||
|
||||
Datamodel micro-service is nothing but another `bbfdmd` instance running with smaller data sub-set, and can be identified at runtime by running a `ps` command
|
||||
A datamodel micro-service is a data model instance running with a smaller data subset and can be identified at runtime by running a `ps` command:
|
||||
|
||||
```bash
|
||||
# ps|grep dm-service
|
||||
12163 root 7664 S {dm_bulkdata} /usr/sbin/dm-service -m bulkdata -l 3
|
||||
12164 root 7620 S {dm_ddnsmngr} /usr/sbin/dm-service -m ddnsmngr -l 3
|
||||
12165 root 7736 S {dm_dhcpmngr} /usr/sbin/dm-service -m dhcpmngr -l 3
|
||||
12166 root 7760 S {dm_dnsmngr} /usr/sbin/dm-service -m dnsmngr -l 3
|
||||
12167 root 7800 S {dm_ethmngr} /usr/sbin/dm-service -m ethmngr -l 3
|
||||
11371 root 4904 S {dm_bridgemngr} /usr/sbin/dm-service -m bridgemngr -l 3
|
||||
11372 root 5052 S {dm_core} /usr/sbin/dm-service -m core -l 3
|
||||
11373 root 4856 S {dm_ddnsmngr} /usr/sbin/dm-service -m ddnsmngr -l 3
|
||||
11374 root 4848 S {dm_dhcp-on-boar} /usr/sbin/dm-service -m dhcp-on-boarding -l 3
|
||||
11375 root 4924 S {dm_dhcpmngr} /usr/sbin/dm-service -m dhcpmngr -l 3
|
||||
11376 root 4896 S {dm_dnsmngr} /usr/sbin/dm-service -m dnsmngr -l 3
|
||||
11377 root 4896 S {dm_firewallmngr} /usr/sbin/dm-service -m firewallmngr -l 3
|
||||
11378 root 4844 S {dm_gateway-info} /usr/sbin/dm-service -m gateway-info -l 3
|
||||
11379 root 4848 S {dm_gnx-ux-manag} /usr/sbin/dm-service -m gnx-ux-manager -l 3
|
||||
11380 root 4868 S {dm_hostmngr} /usr/sbin/dm-service -m hostmngr -l 3
|
||||
11381 root 4900 S {dm_icwmp} /usr/sbin/dm-service -m icwmp -l 3
|
||||
```
|
||||
|
||||
Each `dm-service` instance must be started with -m input to define its service name and run its module(sub-tree) datamodel. These micro-services exposed their own ubus objects.
|
||||
|
|
@ -29,11 +35,14 @@ Each `dm-service` instance must be started with -m input to define its service n
|
|||
# ubus list bbfdm.*
|
||||
bbfdm.bridgemngr
|
||||
bbfdm.bulkdata
|
||||
bbfdm.core
|
||||
bbfdm.ddnsmngr
|
||||
bbfdm.dhcp-on-boarding
|
||||
bbfdm.dhcpmngr
|
||||
bbfdm.dnsmngr
|
||||
bbfdm.ethmngr
|
||||
bbfdm.firewallmngr
|
||||
bbfdm.gateway-info
|
||||
```
|
||||
|
||||
## Setting Up a Datamodel as a Micro-Service
|
||||
|
|
@ -43,53 +52,183 @@ Before starting a datamodel as a micro-service, ensure that the datamodel defini
|
|||
|
||||
This file must include the following required fields:
|
||||
|
||||
- service_name: The name of the service.
|
||||
- unified_daemon: A boolean indicating whether the service uses a unified daemon.
|
||||
- services: An array containing sub-options:
|
||||
- parent_dm: Specifies the parent data model.
|
||||
- object: Defines the object type.
|
||||
- proto: Indicates the protocol (e.g., cwmp, both).
|
||||
- `enable`: Enables or disables the microservice.
|
||||
- `service_name`: The name of the service.
|
||||
- `unified_daemon`: A boolean indicating whether the service uses a unified daemon.
|
||||
- `proto`: Indicates the protocol (e.g., cwmp, both) where the micro-service will be visible.
|
||||
- `services`: An array containing multiple JSON objects, each providing information about the object that will be exposed from this micro-service and supports three possible options:
|
||||
- `parent_dm`: Specifies the parent data model where the object will be linked.
|
||||
- `object`: Defines the object or parameter name exposed from the micro-service.
|
||||
- `proto`: Indicates the protocol (e.g., cwmp, both) where the object will be visible.
|
||||
- `loglevel`: The log level used for the microservice
|
||||
|
||||
Once all required fields are defined, the datamodel can be registered and started when the `bbfdmd` is initialized
|
||||
Once all required fields are defined, the datamodel can be registered and started when the `bbfdmd` is initialized.
|
||||
|
||||
```bash
|
||||
# cat /etc/bbfdm/services/core.json
|
||||
{
|
||||
"daemon": {
|
||||
"enable": "1",
|
||||
"service_name": "core",
|
||||
"unified_daemon": false,
|
||||
"services": [
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "LANConfigSecurity"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "Schedules"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "Security",
|
||||
"proto": "cwmp"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "PacketCaptureDiagnostics"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "SelfTestDiagnostics"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "Syslog"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "X_IOWRT_EU_OpenVPN",
|
||||
"proto": "usp"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "RootDataModelVersion"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "Reboot()"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "FactoryReset()"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"loglevel": "3"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Verifying Micro-Service Registration
|
||||
|
||||
After defining the datamodel, wait for the `bbfdmd` to start. Then, verify that the micro-service has been successfully registered to the core data model. You can do this by listing the registered services using the following command:
|
||||
|
||||
```bash
|
||||
# ubus call bbfdm service
|
||||
# ubus call bbfdm services
|
||||
{
|
||||
"registered_service": [
|
||||
"registered_services": [
|
||||
{
|
||||
"name": "bbfdm.bridgemngr",
|
||||
"parent_dm": "Device.",
|
||||
"object": "Bridging",
|
||||
"name": "bbfdm.icwmp",
|
||||
"proto": "both",
|
||||
"unified_daemon": false
|
||||
"unified_daemon": false,
|
||||
"objects": [
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "ManagementServer",
|
||||
"proto": "both"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "CWMPManagementServer",
|
||||
"proto": "usp"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "XMPP",
|
||||
"proto": "both"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bbfdm.bulkdata",
|
||||
"parent_dm": "Device.",
|
||||
"object": "BulkData",
|
||||
"name": "bbfdm.ssdpd",
|
||||
"proto": "both",
|
||||
"unified_daemon": false,
|
||||
"objects": [
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "UPnP",
|
||||
"proto": "both"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bbfdm.tr104",
|
||||
"proto": "both",
|
||||
"unified_daemon": false,
|
||||
"objects": [
|
||||
{
|
||||
"parent_dm": "Device.Services.",
|
||||
"object": "VoiceService",
|
||||
"proto": "both"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bbfdm.obuspa",
|
||||
"proto": "cwmp",
|
||||
"unified_daemon": true
|
||||
"unified_daemon": false,
|
||||
"objects": [
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "USPAgent",
|
||||
"proto": "both"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "MQTT",
|
||||
"proto": "both"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "STOMP",
|
||||
"proto": "both"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bbfdm.ddnsmngr",
|
||||
"parent_dm": "Device.",
|
||||
"object": "DynamicDNS",
|
||||
"name": "bbfdm.swmodd",
|
||||
"proto": "both",
|
||||
"unified_daemon": false
|
||||
"unified_daemon": false,
|
||||
"objects": [
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "SoftwareModules",
|
||||
"proto": "both"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bbfdm.dnsmngr",
|
||||
"proto": "both",
|
||||
"unified_daemon": false,
|
||||
"objects": [
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "DNS",
|
||||
"proto": "both"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example:
|
||||
|
||||
- Each entry in `registered_service` represents a micro-service.
|
||||
- Each entry in `registered_services` represents a micro-service.
|
||||
- Verify that your service is included in this list, and that its properties (`name`, `parent_dm`, `object`, `proto`, `unified_daemon`) are correctly configured.
|
||||
|
||||
|
||||
## Input and output Schema(s)
|
||||
|
||||
`dm-service` is used to expose sub-tree using different ubus object, UBUS guide and schema for datamodel micro-services
|
||||
|
|
@ -119,7 +258,6 @@ A typical micro-service input file looks like below:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Ubus methods
|
||||
|
|
@ -127,20 +265,22 @@ A typical micro-service input file looks like below:
|
|||
Following are the UBUS methods exposed by `dm-service` process:
|
||||
|
||||
```bash
|
||||
# ubus -v list bbfdm.Network
|
||||
'bbfdm.Network' @9dc36737
|
||||
"get":{"path":"String","paths":"Array","maxdepth":"Integer","optional":"Table"}
|
||||
"schema":{"path":"String","paths":"Array","first_level":"Boolean","optional":"Table"}
|
||||
"instances":{"path":"String","paths":"Array","first_level":"Boolean","optional":"Table"}
|
||||
"set":{"path":"String","value":"String","datatype":"String","obj_path":"Table","optional":"Table"}
|
||||
"operate":{"command":"String","command_key":"String","input":"Table","optional":"Table"}
|
||||
"add":{"path":"String","obj_path":"Table","optional":"Table"}
|
||||
"del":{"path":"String","paths":"Array","optional":"Table"}
|
||||
# ubus -v list bbfdm.netmngr
|
||||
'bbfdm.netmngr' @e93fc046
|
||||
"get":{"path":"String","optional":"Table"}
|
||||
"schema":{"path":"String","first_level":"Boolean","optional":"Table"}
|
||||
"instances":{"path":"String","optional":"Table"}
|
||||
"set":{"path":"String","value":"String","datatype":"String","obj_path":"Table","optional":"Table"}
|
||||
"operate":{"path":"String","command_key":"String","input":"Table","optional":"Table"}
|
||||
"add":{"path":"String","obj_path":"Table","optional":"Table"}
|
||||
"del":{"path":"String","paths":"Array","optional":"Table"}
|
||||
"reference_path":{"path":"String","optional":"Table"}
|
||||
"reference_value":{"path":"String","optional":"Table"}
|
||||
```
|
||||
|
||||
> Note1: `optional` table are present in all methods and it supports below options:
|
||||
|
||||
```console
|
||||
```json
|
||||
"optional":{"proto":"String", "format":"String"}
|
||||
```
|
||||
|
||||
|
|
@ -150,14 +290,12 @@ Following are the UBUS methods exposed by `dm-service` process:
|
|||
|
||||
> Note2: `first_level` true means only get next level objects and false means get all objects recursively
|
||||
|
||||
> Note3: `maxdepth` is measured on max number of .(Dot) present in object name
|
||||
|
||||
> Note4: Check ubus schema document for more details
|
||||
|
||||
|
||||
## Pros and Cons of Data Model Micro-Service
|
||||
|
||||
Data model micro-service is nothing but running a partial datamodel sub-set with another instance of `bbfdmd` binary.
|
||||
Data model micro-service is running a partial datamodel sub-set.
|
||||
|
||||
Benefit:
|
||||
- Instead of having a huge datamodel tree, it split the tree based on modules, which reduce the cost of operation on the tree
|
||||
|
|
@ -207,7 +345,7 @@ Micro-service approach, disintegrate the plugins further and run them as individ
|
|||
|
||||
## Datamodel debugging tools
|
||||
|
||||
To configure the log_level in micro-service, update the `loglevel` module json file,
|
||||
To configure the log_level in a micro-service, update the `loglevel` value in the module JSON file:
|
||||
|
||||
```json
|
||||
# cat /etc/bbfdm/services/netmngr.json
|
||||
|
|
@ -245,4 +383,14 @@ To configure the log_level in micro-service, update the `loglevel` module json f
|
|||
}
|
||||
```
|
||||
|
||||
and then restart the bbfdm.services
|
||||
Then restart `bbfdm.services` for that specific micro-service:
|
||||
|
||||
```bash
|
||||
/etc/init.d/bbfdm.services restart netmngr
|
||||
```
|
||||
|
||||
To restart all micro-services:
|
||||
|
||||
```bash
|
||||
/etc/init.d/bbfdm.services restart
|
||||
```
|
||||
|
|
|
|||
45
docs/guide/extend_datamodel.md
Normal file
45
docs/guide/extend_datamodel.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# How to extend datamodel
|
||||
|
||||
Although `bbfdm/iowrt` provides datamodels for major services, but still for deployment user might need to add some vendor extensions or needs to add the missing datamodel support. To do the same `bbfdm.XXXX` or more precisely `libbfdm-api/legacy` provides the infrastructure to easily define a new datamodel tree.
|
||||
|
||||
As per TR106 description, a datamodel is, "A hierarchical set of Objects, Parameters, Commands and/or Events that define the managed Objects accessible via a particular Agent."
|
||||
|
||||
Please check [TR106](https://www.broadband-forum.org/pdfs/tr-106-1-13-0.pdf) for more details about datamodel terminology.
|
||||
|
||||
`bbfdm` provide the tools and utilities to further extend/overwrite/disable the datamodel using C-code or simply by using JSON datamodel definition.
|
||||
|
||||
## JSON datamodel
|
||||
|
||||
Pro:
|
||||
- Easy to add (compilation not required)
|
||||
- Least maintenance (Change in libbbfdm-api has minimal impact)
|
||||
|
||||
Con:
|
||||
- Can only support easy one to one mappings with uci and ubus
|
||||
- Invalid plugin syntax might cause faults
|
||||
|
||||
## C Based datamodel
|
||||
|
||||
Pro:
|
||||
- Support complex mapping and data sharing between nodes
|
||||
- Lots of references available
|
||||
- Tools available to auto-generate the template code
|
||||
- All core operations supported
|
||||
|
||||
Con:
|
||||
- Moderate maintenance (Change in libbbfdm-api requires adaptation/alignment)
|
||||
|
||||
|
||||
Both ways of extending datamodel covered at length with examples in following links
|
||||
* [How to extend datamodel with C Code](How_to_extend_datamodel_with_C_Code.md)
|
||||
* [How to extend datamodel with JSON](How_to_extend_datamodel_with_JSON.md)
|
||||
|
||||
After creating the datamodel definition, it can be installed the help of `bbfdm.mk` APIs to run them from any specific micro-service instance or with a new micro-service.
|
||||
|
||||
> Note: If the new data model added for specific micro service, all datamodel extension need to be hanlded in the same micro-service. Like a wifi extension needed to be installed in wifidmd micro-service as a plugin.
|
||||
|
||||
# How to choose C or JSON for datamodel extensions
|
||||
|
||||
C/JSON both datamodel definition support defining all datamodel operations, but JSON should be used with simple datamodel deployments.
|
||||
|
||||
If its requires to perform more than one step to retrieve data from lowerlayer, it is suggested to use C-Based datamodel definitions, as it gives more control over the data and its mapping, or simply put if JSON plugin does not meets the requirement of datamodel mapping use C-Based definition.
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
# API guide and usages
|
||||
|
||||
`libbbfdm-api` provides API to define datamodel objects as well as it also provides APIs to traverse the datamodel definitions.
|
||||
`libbbfdm-api-legacy` provides API to define datamodel objects as well as it also provides APIs to traverse the datamodel definitions.
|
||||
|
||||
Most used datamodel APIs described in [libbbfdm_api.h](../../libbbfdm-api/include/libbbfdm_api.h)
|
||||
Most used datamodel APIs described in [libbbfdm_api.h](../../libbbfdm-api/legacy/include/libbbfdm_api.h)
|
||||
|
||||
Following is the list of APIs used by bbfdmd for tree traversal:
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ return
|
|||
|
||||
# Deprecated/removed APIs and user defined datatypes
|
||||
|
||||
To support new feature sometimes old APIs provided by libbbfdm-api library needs to be updated, this guide provides a better context to the migration.
|
||||
To support new feature sometimes old APIs provided by `libbbfdm-api-legacy` library needs to be updated, this guide provides a better context to the migration.
|
||||
|
||||
Following table has APIs/datatypes which are now deprecated:
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ Following table has APIs/datatypes which are now deprecated:
|
|||
| stucture | `dmmap_dup` | `dm_data` | Replaced to support the extension for Obj/Param/Operate using JSON plugin |
|
||||
|
||||
|
||||
Following table has list of APIs/datatypes which no longer exists in libbbfdm-api, along with new revised APIs replacement:
|
||||
Following table has list of APIs/datatypes which no longer exists in `libbbfdm-api-legacy`, along with new revised APIs replacement:
|
||||
|
||||
| Type | Removed API | New API | Comment |
|
||||
| -------- | ---------------------------------- | ------------------------------------ | -------------------------------------- |
|
||||
19
docs/guide/libbbfdm-api-version-2.md
Normal file
19
docs/guide/libbbfdm-api-version-2.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# API Guide and Usage
|
||||
|
||||
`libbbfdm-api-version-2` is the latest version of the `libbbfdm-api`, offering a streamlined set of generic APIs that simplify interaction with the data model, both internally and externally. This version manages memory allocation internally, reducing complexity and making it easier to integrate into various applications.
|
||||
|
||||
## Key Improvements
|
||||
|
||||
- **Simplified Memory Management:** No need for manual memory handling.
|
||||
- **Consistent API Calls:** Unified and easier-to-use interfaces.
|
||||
- **Enhanced Flexibility:** Suitable for both internal and external data model calls.
|
||||
|
||||
## API Overview
|
||||
|
||||
Detailed information about each API’s functionality is available in the corresponding header files:
|
||||
|
||||
- **[Global APIs](../../libbbfdm-api/version-2/bbfdm_api.h)**
|
||||
- **[UCI APIs](../../libbbfdm-api/version-2/bbfdm_uci.h)**
|
||||
- **[UBUS APIs](../../libbbfdm-api/version-2/bbfdm_ubus.h)**
|
||||
- **[Memory APIs](../../libbbfdm-api/version-2/bbfdm_mem.h)**
|
||||
- **[System APIs](../../libbbfdm-api/version-2/bbfdm_ubus.h)**
|
||||
|
|
@ -1,127 +1,122 @@
|
|||
# LIBBBFDM Ubus (libbbfdm-ubus)
|
||||
|
||||
`libbbfdm-ubus` is a library that provides APIs to expose datamodel over ubus. It is used to manage various data models in different micro-services and higher-level applications.
|
||||
`libbbfdm-ubus` is a library that provides APIs to expose the data model over ubus as microservices. It is used to manage various data models in different microservices and higher-level applications.
|
||||
|
||||
## Use Cases
|
||||
|
||||
`libbbfdm-ubus` library can be used by:
|
||||
The `libbbfdm-ubus` library can be used by:
|
||||
|
||||
- `bbfdmd`: to expose the core data model
|
||||
- `dm-service`: to expose a sub-data model as micro-service, example: [Netmngr](https://dev.iopsys.eu/network/netmngr/-/blob/devel/src/net_plugin.c)
|
||||
- Higher-level applications(unified daemon): to expose custom data models as microservices within their daemon, example: [Timemngr](https://dev.iopsys.eu/bbf/timemngr/-/blob/devel/src/main.c)
|
||||
- `dm-service`: To expose a sub-data model as a microservice, e.g., [Network Manager](https://dev.iopsys.eu/network/netmngr/-/blob/devel/src/net_plugin.c), [Bridge Manager](https://dev.iopsys.eu/network/bridgemngr/-/blob/devel/src/driver_vlan_backend/bridging.c)
|
||||
- Higher-level applications (unified daemon): To expose custom data models as microservices within their daemon, e.g., [Time Manager](https://dev.iopsys.eu/bbf/timemngr/-/blob/devel/src/main.c), [System Manager](https://dev.iopsys.eu/system/sysmngr/-/blob/devel/src/sysmngr.c)
|
||||
|
||||
## libbbfdm-ubus APIs
|
||||
|
||||
The following APIs are provided by `libbbfdm-ubus` to expose data model over ubus:
|
||||
The following APIs are provided by `libbbfdm-ubus` to expose the data model over ubus:
|
||||
|
||||
### bbfdm_ubus_regiter_init
|
||||
### bbfdm_ubus_register_init
|
||||
|
||||
This method is used to initialize the bbfdm_context structure object and register ubus data model methods.
|
||||
This method initializes the `bbfdm_context` structure object and registers ubus data model methods.
|
||||
|
||||
```
|
||||
int bbfdm_ubus_regiter_init(struct bbfdm_context *bbfdm_ctx)
|
||||
```c
|
||||
int bbfdm_ubus_register_init(struct bbfdm_context *bbfdm_ctx)
|
||||
|
||||
inputs
|
||||
struct bbfdm_context *bbfdm_ctx
|
||||
pointer to struct bbfdm_context structure to be initialized.
|
||||
Inputs:
|
||||
struct bbfdm_context *bbfdm_ctx
|
||||
Pointer to the `bbfdm_context` structure to be initialized.
|
||||
|
||||
return
|
||||
int fault
|
||||
returns 0 on success, or an error code if the registration fails.
|
||||
Returns:
|
||||
int fault
|
||||
Returns 0 on success, or an error code if the registration fails.
|
||||
```
|
||||
|
||||
### bbfdm_ubus_register_free
|
||||
|
||||
### bbfdm_ubus_regiter_free
|
||||
This method frees the `bbfdm_context` structure object.
|
||||
|
||||
This method is used to free the bbfdm_context structure object
|
||||
```c
|
||||
int bbfdm_ubus_register_free(struct bbfdm_context *bbfdm_ctx)
|
||||
|
||||
Inputs:
|
||||
struct bbfdm_context *bbfdm_ctx
|
||||
Pointer to the `bbfdm_context` structure to be freed.
|
||||
|
||||
Returns:
|
||||
int fault
|
||||
Returns 0 on success, or an error code if freeing fails.
|
||||
```
|
||||
int bbfdm_ubus_regiter_free(struct bbfdm_context *bbfdm_ctx)
|
||||
|
||||
inputs
|
||||
struct bbfdm_context *bbfdm_ctx
|
||||
pointer to struct bbfdm_context structure to be freed.
|
||||
|
||||
return
|
||||
int fault
|
||||
returns 0 on success, or an error code if freeing fails.
|
||||
```
|
||||
|
||||
|
||||
### bbfdm_ubus_set_service_name
|
||||
|
||||
This method is used the service name for the daemon running as a microservice
|
||||
This method sets the service name for the daemon running as a microservice.
|
||||
|
||||
```
|
||||
```c
|
||||
void bbfdm_ubus_set_service_name(struct bbfdm_context *bbfdm_ctx, const char *srv_name)
|
||||
|
||||
input
|
||||
struct bbfdm_context *bbfdm_ctx
|
||||
pointer to struct bbfdm_context structure
|
||||
Inputs:
|
||||
struct bbfdm_context *bbfdm_ctx
|
||||
Pointer to the `bbfdm_context` structure.
|
||||
|
||||
const char *srv_name
|
||||
pointer to service name to set
|
||||
|
||||
return
|
||||
None
|
||||
const char *srv_name
|
||||
Pointer to the service name to set.
|
||||
|
||||
Returns:
|
||||
None
|
||||
```
|
||||
|
||||
|
||||
### bbfdm_ubus_set_log_level
|
||||
|
||||
This method is used to set the log level according to the standard syslog levels
|
||||
This method sets the log level according to the standard syslog levels.
|
||||
|
||||
```
|
||||
```c
|
||||
void bbfdm_ubus_set_log_level(int log_level)
|
||||
|
||||
input
|
||||
int log_level
|
||||
desired log level to set
|
||||
|
||||
return
|
||||
None
|
||||
```
|
||||
Inputs:
|
||||
int log_level
|
||||
Desired log level to set.
|
||||
|
||||
Returns:
|
||||
None
|
||||
```
|
||||
|
||||
### bbfdm_ubus_load_data_model
|
||||
|
||||
This method is used to load an internal data model, allowing you to use an internal model instead of external plugins (e.g., DotSo or JSON).
|
||||
This method loads an internal data model, allowing you to use an internal model instead of external plugins (e.g., DotSo or JSON).
|
||||
|
||||
```
|
||||
```c
|
||||
void bbfdm_ubus_load_data_model(DM_MAP_OBJ *DynamicObj)
|
||||
|
||||
input
|
||||
DM_MAP_OBJ *DynamicObj
|
||||
pointer to internal data model
|
||||
|
||||
return
|
||||
None
|
||||
Inputs:
|
||||
DM_MAP_OBJ *DynamicObj
|
||||
Pointer to the internal data model.
|
||||
|
||||
Returns:
|
||||
None
|
||||
```
|
||||
|
||||
## libbbfdm-ubus methods
|
||||
## libbbfdm-ubus Methods
|
||||
|
||||
Following are the ubus methods exposed by `libbbfdm-ubus` when registering a new module:
|
||||
|
||||
```bash
|
||||
# ubus -v list bbfdm
|
||||
bbfdm' @b93b62aa
|
||||
"get":{"path":"String","paths":"Array","maxdepth":"Integer","optional":"Table"}
|
||||
"schema":{"path":"String","paths":"Array","first_level":"Boolean","optional":"Table"}
|
||||
"instances":{"path":"String","paths":"Array","first_level":"Boolean","optional":"Table"}
|
||||
# ubus -v list bbfdm.sysmngr
|
||||
'bbfdm.sysmngr' @6931a0bb
|
||||
"get":{"path":"String","optional":"Table"}
|
||||
"schema":{"path":"String","first_level":"Boolean","optional":"Table"}
|
||||
"instances":{"path":"String","optional":"Table"}
|
||||
"set":{"path":"String","value":"String","datatype":"String","obj_path":"Table","optional":"Table"}
|
||||
"operate":{"command":"String","command_key":"String","input":"Table","optional":"Table"}
|
||||
"operate":{"path":"String","command_key":"String","input":"Table","optional":"Table"}
|
||||
"add":{"path":"String","obj_path":"Table","optional":"Table"}
|
||||
"del":{"path":"String","paths":"Array","optional":"Table"}
|
||||
"notify_event":{"name":"String","input":"Array"}
|
||||
"service":{}
|
||||
"reference_path":{"path":"String","optional":"Table"}
|
||||
"reference_value":{"path":"String","optional":"Table"}
|
||||
```
|
||||
|
||||
## libbbfdm-ubus example(s)
|
||||
## libbbfdm-ubus Examples
|
||||
|
||||
1. The requested value is correct as per TR181 standard, but there is a limitation in the device.
|
||||
### 1. The requested value is correct as per the TR181 standard, but there is a limitation in the device.
|
||||
|
||||
```console
|
||||
root@iopsys:~# ubus call bbfdm set '{"path":"Device.Firewall.Config", "value":"High"}'
|
||||
```bash
|
||||
root@iowrt:~# ubus call bbfdm.firewallmngr set '{"path":"Device.Firewall.Config", "value":"High"}'
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
|
|
@ -133,10 +128,10 @@ root@iopsys:~# ubus call bbfdm set '{"path":"Device.Firewall.Config", "value":"H
|
|||
}
|
||||
```
|
||||
|
||||
2. The requested value is outside the allowed range.
|
||||
### 2. The requested value is outside the allowed range.
|
||||
|
||||
```console
|
||||
root@iopsys:~# ubus call bbfdm set '{"path":"Device.Firewall.Chain.1.Rule.9.DestPort", "value":"123456"}'
|
||||
```bash
|
||||
root@iowrt:~# ubus call bbfdm.firewallmngr set '{"path":"Device.Firewall.Chain.1.Rule.9.DestPort", "value":"123456"}'
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
|
|
@ -148,26 +143,30 @@ root@iopsys:~# ubus call bbfdm set '{"path":"Device.Firewall.Chain.1.Rule.9.Dest
|
|||
}
|
||||
```
|
||||
|
||||
3. Some arguments should be defined to perform the requested operation.
|
||||
### 3. Some arguments should be defined to perform the requested operation.
|
||||
|
||||
```console
|
||||
root@iopsys:~# ubus call bbfdm operate '{"command":"Device.IP.Diagnostics.IPPing()", "command_key":"ipping_test", "input":{}}'
|
||||
```bash
|
||||
root@iowrt:~# ubus call bbfdm.netmngr operate '{"path":"Device.IP.Diagnostics.IPPing()", "command_key":"ipping_test", "input":{}}'
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"path": "Device.IP.Diagnostics.IPPing()",
|
||||
"data": "ipping_test",
|
||||
"fault": 7004,
|
||||
"fault_msg": "IPPing: 'Host' input should be defined"
|
||||
"output": [
|
||||
{
|
||||
"fault": 7004,
|
||||
"IPPing: 'Host' input should be defined"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
4. The path parameter value must start with 'Device.'. The command below doesn't have Device before path "Users.User."
|
||||
### 4. The path parameter value must start with 'Device.'. The command below doesn't have Device before path "Users.User."
|
||||
|
||||
```console
|
||||
root@iopsys:~# ubus call bbfdm get '{"path":"Users.User.", "optional": {"format":"raw", "proto":"usp"}}'
|
||||
root@iowrt:~# ubus call bbfdm.usermngr get '{"path":"Users.User.", "optional": {"format":"raw", "proto":"usp"}}'
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
|
|
@ -181,50 +180,22 @@ root@iopsys:~# ubus call bbfdm get '{"path":"Users.User.", "optional": {"format"
|
|||
|
||||
These fault messages defined in datamodel handlers, users can add such fault message using `bbfdm_set_fault_message` libbbfdm-api's API, if no specific fault message defined for particular obj/param, datamodel returns standard error messages that are defined in CWMP and USP protocols as the fault message value.
|
||||
|
||||
### Fault handling
|
||||
## Fault Handling
|
||||
|
||||
To indicate a fault and source of fault, `libbbfdm-ubus` provides `fault` along with `fault_msg` in the response in case of faults, which then handled by higher layer applications (i.e icwmp, obuspa).
|
||||
To indicate a fault and its source, `libbbfdm-ubus` provides `fault` along with `fault_msg` in the response in case of faults, which are then handled by higher-layer applications (e.g., icwmp, obuspa).
|
||||
|
||||
This provides a clear inside on the root cause of the fault, and based on `fault_msg` it's easily to understand what the issue is and how to fix it and find out the limitations(if there are any on the device).
|
||||
This provides a clear insight into the root cause of the fault. Based on `fault_msg`, it’s easy to understand the issue, how to fix it, and identify limitations (if any) in the device.
|
||||
|
||||
##### Errors Codes
|
||||
### Error Codes
|
||||
|
||||
| Error Code | Meaning |
|
||||
|------------|--------------------------------------------------------------|
|
||||
| 7003 | Message failed due to an internal error. |
|
||||
| 7004 | Message failed due to invalid values in the request elements and/or failure to update one or more parameters during Add or Set requests. |
|
||||
| 7005 | Message failed due to memory or processing limitations. |
|
||||
| 7008 | Requested path was invalid or a reference was invalid. |
|
||||
| 7010 | Requested Path Name associated with this ParamError did not match any instantiated parameters. |
|
||||
| 7011 | Unable to convert string value to correct data type. |
|
||||
| 7012 | Out of range or invalid enumeration. |
|
||||
| 7022 | Command failed to operate. |
|
||||
| 7026 | Path is not present in the data model schema. |
|
||||
|
||||
|
||||
## Parallel calls over Ubus
|
||||
|
||||
Some datamodel operations takes less time to execute compared to other, like
|
||||
- Get on sub-set of datamodel or an individual datamodel parameter takes very less, where as
|
||||
- Get on complete Device. and Async operate commands takes much longer
|
||||
|
||||
executing/serializing operations simplifies the code from developer perspective, but its not suitable for deployments. To make it suitable `bbfdmd` support parallel calls.
|
||||
|
||||
- All datamodel `operate` commands are running in parallel
|
||||
|
||||
example(s):
|
||||
|
||||
```console
|
||||
root@iopsys:~# time ubus call bbfdm get '{"path":"Device."}' >/dev/null &
|
||||
root@iopsys:~# time ubus call bbfdm get '{"path":"Device.Users."}' >/dev/null
|
||||
real 0m 0.07s
|
||||
user 0m 0.00s
|
||||
sys 0m 0.00s
|
||||
root@iopsys:~#
|
||||
real 0m 1.86s
|
||||
user 0m 0.05s
|
||||
sys 0m 0.00s
|
||||
|
||||
[1]+ Done time ubus call bbfdm get "{\"path\":\"Device.\"}" >/dev/null
|
||||
root@iopsys:~#
|
||||
```
|
||||
| Error Code | Meaning |
|
||||
|------------|---------|
|
||||
| 7003 | Message failed due to an internal error. |
|
||||
| 7004 | Message failed due to invalid values in the request elements and/or failure to update one or more parameters during Add or Set requests. |
|
||||
| 7005 | Message failed due to memory or processing limitations. |
|
||||
| 7008 | Requested path was invalid or a reference was invalid. |
|
||||
| 7010 | Requested Path Name associated with this ParamError did not match any instantiated parameters. |
|
||||
| 7011 | Unable to convert string value to correct data type. |
|
||||
| 7012 | Out of range or invalid enumeration. |
|
||||
| 7022 | Command failed to operate. |
|
||||
| 7026 | Path is not present in the data model schema. |
|
||||
|
|
|
|||
|
|
@ -24,15 +24,6 @@ echo "Checking system resources"
|
|||
free -h
|
||||
df -h
|
||||
|
||||
# Check if the specified log file exists, which indicates errors during plugin loading
|
||||
if [ -f ${BBFDM_LOG_FILE} ]; then
|
||||
echo "Some plugins failed to load! Please check the errors below"
|
||||
echo "*****************************************************"
|
||||
cat "${BBFDM_LOG_FILE}"
|
||||
echo "*****************************************************"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "## Running python based verification of functionalities ##"
|
||||
echo > ./funl-result.log
|
||||
num=0
|
||||
|
|
|
|||
139
gitlab-ci/full_micro_service.conf
Normal file
139
gitlab-ci/full_micro_service.conf
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
[program:sysmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-sysmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/sysmngr -l 3"
|
||||
|
||||
[program:netmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-netmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m netmngr -l 3"
|
||||
|
||||
[program:wifidmd]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-wifidmd-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/wifidmd -l 3"
|
||||
|
||||
[program:core]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-core-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m core -l 3"
|
||||
|
||||
[program:ethmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-ethmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/ethmngr -l 3"
|
||||
|
||||
[program:icwmp]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-icwmp-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m icwmp -l 3"
|
||||
|
||||
[program:bulkdata]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-bulkdata-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/bulkdatad -l 3"
|
||||
|
||||
[program:periodicstats]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-periodicstats-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/periodicstatsd -l 3"
|
||||
|
||||
[program:ponmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-ponmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m ponmngr -l 3"
|
||||
|
||||
[program:ssdpd]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-ssdpd-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m ssdpd -l 3"
|
||||
|
||||
[program:swmodd]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-swmodd-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m swmodd -l 3"
|
||||
|
||||
[program:usermngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-usermngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/usermngr -l 3"
|
||||
|
||||
[program:parentalcontrol]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-parentalcontrol-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/urlfilter -l 3"
|
||||
|
||||
[program:hostmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-hostmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m hostmngr -l 3"
|
||||
|
||||
[program:timemngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-timemngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/timemngr -l 3"
|
||||
|
||||
[program:dnsmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-dnsmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m dnsmngr -l 3"
|
||||
|
||||
[program:dhcpmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-dhcpmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m dhcpmngr -l 3"
|
||||
|
||||
[program:qosmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-qosmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/qosmngr -l 3"
|
||||
|
||||
[program:tr104]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-tr104-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m tr104 -l 3"
|
||||
|
||||
[program:mcastmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-mcastmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m mcastmngr -l 3"
|
||||
|
||||
[program:ieee1905]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-ieee1905-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m ieee1905 -l 3"
|
||||
|
||||
[program:bridgemngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-bridgemngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m bridgemngr -l 3"
|
||||
|
||||
[program:ddnsmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-ddnsmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m ddnsmngr -l 3"
|
||||
|
||||
[program:sshmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-sshmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m sshmngr -l 3"
|
||||
|
||||
[program:firewallmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-firewallmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m firewallmngr -l 3"
|
||||
|
||||
[program:dslmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-dslmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m dslmngr -l 3"
|
||||
|
||||
[program:usbmngr]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-usbmngr-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m usbmngr -l 3"
|
||||
|
||||
[program:obuspa]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-obuspa-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m obuspa -l 3"
|
||||
|
||||
[program:netmode]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-netmode-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m netmode -l 3"
|
||||
|
||||
[program:gateway-info]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-gateway-info-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m gateway-info -l 3"
|
||||
|
||||
[program:gnx-ux-manager]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-gnx-ux-manager-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m gnx-ux-manager -l 3"
|
||||
|
||||
[program:gnx-catv]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-gnx-catv-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m gnx-catv -l 3"
|
||||
|
||||
[program:dhcp-on-boarding]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-dhcp-on-boarding-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m dhcp-on-boarding -l 3"
|
||||
|
||||
[program:gnx-loop-detector]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-gnx-loop-detector-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m gnx-loop-detector -l 3"
|
||||
|
||||
[program:gnx-sfp]
|
||||
priority=10
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-gnx-sfp-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m gnx-sfp -l 3"
|
||||
|
|
@ -5,8 +5,8 @@ echo "Generate xml and xls artifacts"
|
|||
source ./gitlab-ci/shared.sh
|
||||
|
||||
# install required packages
|
||||
exec_cmd apt update
|
||||
exec_cmd apt install -y python3-pip libxml2-utils
|
||||
exec_cmd sudo apt update
|
||||
exec_cmd sudo apt install -y python3-pip libxml2-utils
|
||||
exec_cmd pip3 install xlwt
|
||||
|
||||
if [ -n "${CI_SERVER_HOST}" ]; then
|
||||
|
|
@ -15,15 +15,6 @@ if [ -n "${CI_SERVER_HOST}" ]; then
|
|||
echo "password ${CI_JOB_TOKEN}" >>~/.netrc
|
||||
fi
|
||||
|
||||
install_cmph
|
||||
install_libeasy
|
||||
install_libethernet
|
||||
|
||||
[ ! -d "${BBFDM_MS_DIR}" ] && mkdir -p "${BBFDM_MS_DIR}"
|
||||
rm -rf ${BBFDM_MS_DIR}/*
|
||||
|
||||
mkdir -p ${BBFDM_MS_DIR}/core
|
||||
|
||||
if [ -z "${1}" ]; then
|
||||
./tools/generate_dm.py tools/tools_input.json
|
||||
else
|
||||
|
|
|
|||
142
gitlab-ci/install-dependencies-ms.sh
Executable file
142
gitlab-ci/install-dependencies-ms.sh
Executable file
|
|
@ -0,0 +1,142 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "install dependencies of bbfdm"
|
||||
|
||||
source ./gitlab-ci/shared.sh
|
||||
|
||||
# install required packages
|
||||
exec_cmd apt update
|
||||
exec_cmd pip3 install xlwt
|
||||
|
||||
mkdir -p /etc/bbfdm/dmmap
|
||||
# Create directories for micro-service configuration and shared files
|
||||
[ ! -d "${BBFDM_MS_CONF}" ] && mkdir -p "${BBFDM_MS_CONF}"
|
||||
[ ! -d "${BBFDM_MS_DIR}" ] && mkdir -p "${BBFDM_MS_DIR}"
|
||||
|
||||
# Clean up generated files
|
||||
rm -rf "${BBFDM_MS_DIR:?}"/*
|
||||
rm -f "${BBFDM_MS_CONF}"/*
|
||||
rm -f "${BBFDM_DMMAP_DIR}"/*
|
||||
|
||||
install_libeasy
|
||||
# compile and install Core Data Model as a micro-service
|
||||
install_libbbf "${1}"
|
||||
|
||||
# Install datamodel plugins/micro-service only when pipeline trigger for bbfdm
|
||||
if [ -z "${1}" ]; then
|
||||
echo "Skip installation of micro-services ...."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$1" == "bbfdm" ]; then
|
||||
#compile and install libbbf_test dynamic extension library
|
||||
install_libbbf_test
|
||||
fi
|
||||
|
||||
JSON_FILE=$BBFDM_TOOLS_INPUT_FILE
|
||||
JSON_DESC_PATH="/tmp/desc_files"
|
||||
plugin_count=$(jq '.plugins | length' "$JSON_FILE")
|
||||
|
||||
# Create desc_files directory
|
||||
[ -d "${JSON_DESC_PATH}" ] && rm -f ${JSON_DESC_PATH}
|
||||
mkdir -p ${JSON_DESC_PATH}
|
||||
|
||||
ret=0
|
||||
for i in $(seq 0 $((plugin_count - 1))); do
|
||||
echo "==== Processing plugin [$i] ===="
|
||||
|
||||
repo=$(jq -r ".plugins[$i].repo" "$JSON_FILE")
|
||||
version=$(jq -r ".plugins[$i].version // empty" "$JSON_FILE")
|
||||
if [ -z "${version}" ]; then
|
||||
version=${BRANCH:-devel}
|
||||
fi
|
||||
|
||||
plugin_name=$(basename "$repo" .git)
|
||||
dest="$BBFDM_PLUGIN_DEST/$plugin_name"
|
||||
|
||||
# Adjust repo URL based on ~/.netrc existence
|
||||
NETRC_PATH="$HOME/.netrc"
|
||||
if [ ! -f "$NETRC_PATH" ]; then
|
||||
repo=${repo/https:\/\/dev.iopsys.eu\//git@dev.iopsys.eu:}
|
||||
fi
|
||||
|
||||
echo "Repo path: $repo"
|
||||
echo "Plugin name: $plugin_name"
|
||||
echo "Destination: $dest"
|
||||
echo "Version: $version"
|
||||
|
||||
# Install dependencies
|
||||
if [ "$plugin_name" == "ethmngr" ]; then
|
||||
install_libethernet
|
||||
install_libqos
|
||||
fi
|
||||
|
||||
if [ "$plugin_name" == "parental-control" ]; then
|
||||
install_cmph
|
||||
fi
|
||||
|
||||
if [ -d "$dest" ]; then
|
||||
echo "Directory $dest already exists, checkout $version"
|
||||
(cd "$dest" && git checkout . && git checkout "$version")
|
||||
else
|
||||
echo "Cloning $repo into $dest, branch ${version} ..."
|
||||
exec_cmd git clone -b "$version" "$repo" "$dest"
|
||||
fi
|
||||
|
||||
cd "$dest" || exit 5
|
||||
|
||||
# Compilation
|
||||
compile="$(jq -r ".plugins[$i].compile[]" "$JSON_FILE" 2>/dev/null)"
|
||||
if [ -n "${compile}" ]; then
|
||||
echo "Starting compilation..."
|
||||
while read -r cmd; do
|
||||
echo "Executing: $cmd"
|
||||
if ! eval "$cmd" > debug.log 2>&1; then
|
||||
echo "❌ Compilation command [$cmd] failed"
|
||||
ret=1
|
||||
break
|
||||
fi
|
||||
done <<< ${compile}
|
||||
[ $ret -eq 1 ] && break
|
||||
fi
|
||||
|
||||
# Post-install
|
||||
post_install="$(jq -r ".plugins[$i].post_install[]" "$JSON_FILE" 2>/dev/null)"
|
||||
if [ -n "${post_install}" ]; then
|
||||
echo "Running post-install steps..."
|
||||
while read -r post_cmd; do
|
||||
echo "Executing: $post_cmd"
|
||||
if ! eval "$post_cmd" > debug.log 2>&1; then
|
||||
ret=1
|
||||
echo "❌ Post-install command [$post_cmd] failed"
|
||||
break
|
||||
fi
|
||||
done <<< ${post_install}
|
||||
[ $ret -eq "1" ] && break
|
||||
fi
|
||||
|
||||
# Save dm_info_file if defined
|
||||
dm_info_file=$(jq -r ".plugins[$i].dm_info_file // empty" "$JSON_FILE")
|
||||
if [ -n "$dm_info_file" ]; then
|
||||
src_file="$dest/$dm_info_file"
|
||||
out_file="/tmp/desc_files/${i}_${plugin_name}.json"
|
||||
if [ -f "$src_file" ]; then
|
||||
echo "Saving dm_info_file to $out_file"
|
||||
cp -f "$src_file" "$out_file"
|
||||
else
|
||||
echo "❌ dm_info_file not found: $src_file"
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
|
||||
cd .. || exit 7
|
||||
|
||||
if [ ${ret} -eq 0 ]; then
|
||||
echo "✅ Plugin [$plugin_name] build complete."
|
||||
else
|
||||
echo "❌ Plugin [$plugin_name] build failed"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
exit $ret
|
||||
|
|
@ -8,6 +8,7 @@ source ./gitlab-ci/shared.sh
|
|||
exec_cmd apt update
|
||||
exec_cmd pip3 install xlwt
|
||||
|
||||
mkdir -p /etc/bbfdm/dmmap
|
||||
# Create directories for micro-service configuration and shared files
|
||||
[ ! -d "${BBFDM_MS_CONF}" ] && mkdir -p "${BBFDM_MS_CONF}"
|
||||
[ ! -d "${BBFDM_MS_DIR}" ] && mkdir -p "${BBFDM_MS_DIR}"
|
||||
|
|
@ -16,8 +17,8 @@ exec_cmd pip3 install xlwt
|
|||
rm -rf ${BBFDM_MS_DIR}/*
|
||||
rm -f ${BBFDM_MS_CONF}/*
|
||||
rm -f ${BBFDM_DMMAP_DIR}/*
|
||||
rm -f ${BBFDM_LOG_FILE}
|
||||
|
||||
install_libeasy
|
||||
# compile and install Core Data Model as a micro-service
|
||||
install_libbbf ${1}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,8 +58,9 @@ supervisorctl stop all
|
|||
supervisorctl status
|
||||
|
||||
cp /tmp/memory-*.xml .
|
||||
check_valgrind_xml "memory-report.xml" "bbfdmd"
|
||||
check_valgrind_xml "memory-config-report.xml" "bbf.config"
|
||||
for file in memory-*.xml; do
|
||||
check_valgrind_xml "$file"
|
||||
done
|
||||
|
||||
#report part
|
||||
#GitLab-CI output
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-netmngr
|
|||
|
||||
[program:wifidmd]
|
||||
priority=7
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-wifidmd-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/dm-service -m wifidmd"
|
||||
command=/bin/bash -c "/usr/bin/valgrind --xml=yes --xml-file=/tmp/memory-wifidmd-report.xml --leak-check=full --show-reachable=yes --show-leak-kinds=all --errors-for-leak-kinds=all --error-exitcode=1 --track-origins=yes --leak-resolution=high --show-error-list=yes --child-silent-after-fork=yes /usr/sbin/wifidmd"
|
||||
|
||||
[program:core]
|
||||
priority=8
|
||||
|
|
|
|||
|
|
@ -26,7 +26,11 @@ cp ./gitlab-ci/core_service.conf /etc/supervisor/conf.d/
|
|||
cp ./gitlab-ci/reload_service.conf /etc/supervisor/conf.d/
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
cp ./gitlab-ci/micro_service.conf /etc/supervisor/conf.d/
|
||||
if [ "$1" == "bbfdm" ]; then
|
||||
cp ./gitlab-ci/full_micro_service.conf /etc/supervisor/conf.d/
|
||||
else
|
||||
cp ./gitlab-ci/micro_service.conf /etc/supervisor/conf.d/
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f /etc/bbfdm/dmmap/*
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
BBFDM_TOOLS_INPUT_FILE="$(pwd)/tools/tools_input.json"
|
||||
BBFDM_PLUGIN_DEST="/opt/dev"
|
||||
BBFDM_MS_DIR="/usr/share/bbfdm/micro_services"
|
||||
BBFDM_MS_CONF="/etc/bbfdm/services"
|
||||
BBFDM_DMMAP_DIR="etc/bbfdm/dmmap/"
|
||||
BBFDM_LOG_FILE="/tmp/bbfdm.log"
|
||||
|
||||
if [ -z "${CI_PROJECT_PATH}" ]; then
|
||||
CI_PROJECT_PATH=${PWD}
|
||||
|
|
@ -55,10 +56,15 @@ function install_ms_plugin()
|
|||
exec_cmd cp -f "${1}" ${BBFDM_MS_DIR}/${2}/
|
||||
}
|
||||
|
||||
function install_ms_config()
|
||||
{
|
||||
exec_cmd cp -f "${1}" ${BBFDM_MS_CONF}/${2}.json
|
||||
}
|
||||
|
||||
function install_libbbf()
|
||||
{
|
||||
# Enable coverage flags only for test
|
||||
if [ -z "${1}" ]; then
|
||||
# Enable coverage flags only for bbfdm test
|
||||
if [ "$1" == "bbfdm" ]; then
|
||||
COV_CFLAGS='-fprofile-arcs -ftest-coverage'
|
||||
COV_LDFLAGS='--coverage'
|
||||
fi
|
||||
|
|
@ -72,7 +78,27 @@ function install_libbbf()
|
|||
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake ../ -DCMAKE_C_FLAGS="$COV_CFLAGS " -DCMAKE_EXE_LINKER_FLAGS="$COV_LDFLAGS -lm" -DBBF_VENDOR_PREFIX="$VENDOR_PREFIX" -DBBF_MAX_OBJECT_INSTANCES=255 -DBBFDMD_MAX_MSG_LEN=1048576 -DCMAKE_INSTALL_PREFIX=/
|
||||
|
||||
# Construct the CMake command as an array for safety
|
||||
cmake_args=(
|
||||
../
|
||||
-DCMAKE_C_FLAGS="$COV_CFLAGS"
|
||||
-DCMAKE_EXE_LINKER_FLAGS="$COV_LDFLAGS -lm"
|
||||
-DBBF_VENDOR_PREFIX="$VENDOR_PREFIX"
|
||||
-DBBF_MAX_OBJECT_INSTANCES=255
|
||||
-DBBFDMD_MAX_MSG_LEN=1048576
|
||||
-DCMAKE_INSTALL_PREFIX=/
|
||||
)
|
||||
|
||||
# Add this flag only if $1 is "tools"
|
||||
if [ "$1" == "tools" ]; then
|
||||
cmake_args+=(-DBBF_SCHEMA_FULL_TREE=ON)
|
||||
fi
|
||||
|
||||
# Run cmake with arguments
|
||||
cmake "${cmake_args[@]}"
|
||||
|
||||
# Compile and install
|
||||
exec_cmd_verbose make
|
||||
|
||||
echo "installing libbbf"
|
||||
|
|
@ -80,7 +106,8 @@ function install_libbbf()
|
|||
echo "371d530c95a17d1ca223a29b7a6cdc97e1135c1e0959b51106cca91a0b148b5e42742d372a359760742803f2a44bd88fca67ccdcfaeed26d02ce3b6049cb1e04" > /etc/bbfdm/.secure_hash
|
||||
cd ..
|
||||
exec_cmd cp utilities/bbf_configd /usr/sbin/
|
||||
install_ms /usr/lib/libcore.so core
|
||||
exec_cmd cp -f utilities/files/usr/share/bbfdm/scripts/bbf_api /usr/share/bbfdm/scripts/
|
||||
install_ms build/libbbfdm/libcore.so core
|
||||
}
|
||||
|
||||
function install_libbbf_test()
|
||||
|
|
@ -96,94 +123,109 @@ function install_libbbf_test()
|
|||
|
||||
function install_wifidmd_as_micro_service()
|
||||
{
|
||||
[ -d "/opt/dev/wifidmd" ] && return 0
|
||||
[ -d "${BBFDM_PLUGIN_DEST}/wifidmd" ] && return 0
|
||||
|
||||
exec_cmd git clone -b devel https://dev.iopsys.eu/bbf/wifidmd.git /opt/dev/wifidmd
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/bbf/wifidmd.git ${BBFDM_PLUGIN_DEST}/wifidmd
|
||||
|
||||
exec_cmd make -C /opt/dev/wifidmd/src/ clean && make -C /opt/dev/wifidmd/src/ CFLAGS="-D'BBF_VENDOR_PREFIX=\"X_IOWRT_EU_\"'" WIFIDMD_WIFI_DATAELEMENTS='y'
|
||||
install_ms /opt/dev/wifidmd/src/libwifi.so wifidmd
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/wifidmd/src/ clean && make -C ${BBFDM_PLUGIN_DEST}/wifidmd/src/ WIFIDMD_ENABLE_WIFI_DATAELEMENTS='y'
|
||||
exec_cmd cp -f ${BBFDM_PLUGIN_DEST}/wifidmd/src/wifidmd /usr/sbin/
|
||||
}
|
||||
|
||||
function install_libeasy()
|
||||
{
|
||||
[ -d "/opt/dev/libeasy" ] && return 0
|
||||
[ -d "${BBFDM_PLUGIN_DEST}/libeasy" ] && return 0
|
||||
|
||||
exec_cmd git clone https://dev.iopsys.eu/iopsys/libeasy.git /opt/dev/libeasy
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/iopsys/libeasy.git ${BBFDM_PLUGIN_DEST}/libeasy
|
||||
(
|
||||
|
||||
cd /opt/dev/libeasy
|
||||
cd ${BBFDM_PLUGIN_DEST}/libeasy
|
||||
exec_cmd cmake -DCMAKE_INSTALL_PREFIX=/usr .
|
||||
exec_cmd make
|
||||
mkdir -p /usr/include/easy
|
||||
cp -a libeasy*.so* /usr/lib
|
||||
cp -a *.h /usr/include/easy/
|
||||
exec_cmd make install
|
||||
)
|
||||
}
|
||||
|
||||
function install_libqos()
|
||||
{
|
||||
[ -d "${BBFDM_PLUGIN_DEST}/libqos" ] && return 0
|
||||
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/hal/libqos.git ${BBFDM_PLUGIN_DEST}/libqos
|
||||
(
|
||||
|
||||
cd ${BBFDM_PLUGIN_DEST}/libqos
|
||||
exec_cmd make
|
||||
sudo mkdir -p /usr/include/
|
||||
sudo cp -a libqos*.so* /usr/lib/
|
||||
sudo cp -a include/*.h /usr/include/
|
||||
)
|
||||
}
|
||||
|
||||
function install_libethernet()
|
||||
{
|
||||
[ -d "/opt/dev/libethernet" ] && return 0
|
||||
[ -d "${BBFDM_PLUGIN_DEST}/libethernet" ] && return 0
|
||||
|
||||
exec_cmd git clone https://dev.iopsys.eu/iopsys/libethernet.git /opt/dev/libethernet
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/iopsys/libethernet.git ${BBFDM_PLUGIN_DEST}/libethernet
|
||||
(
|
||||
cd /opt/dev/libethernet
|
||||
cd ${BBFDM_PLUGIN_DEST}/libethernet
|
||||
make PLATFORM=TEST
|
||||
cp ethernet.h /usr/include
|
||||
cp -a libethernet*.so* /usr/lib
|
||||
sudo cp ethernet.h /usr/include
|
||||
sudo cp -a libethernet*.so* /usr/lib
|
||||
sudo ldconfig
|
||||
)
|
||||
}
|
||||
|
||||
function install_ethmngr_as_micro_service()
|
||||
{
|
||||
[ -d "/opt/dev/ethmngr" ] && return 0
|
||||
[ -d "${BBFDM_PLUGIN_DEST}/ethmngr" ] && return 0
|
||||
|
||||
install_libeasy
|
||||
install_libethernet
|
||||
install_libqos
|
||||
|
||||
exec_cmd git clone https://dev.iopsys.eu/hal/ethmngr.git /opt/dev/ethmngr
|
||||
exec_cmd make -C /opt/dev/ethmngr
|
||||
exec_cmd cp -f /opt/dev/ethmngr/ethmngr /usr/sbin/ethmngr
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/hal/ethmngr.git ${BBFDM_PLUGIN_DEST}/ethmngr
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/ethmngr
|
||||
exec_cmd sudo cp -f ${BBFDM_PLUGIN_DEST}/ethmngr/ethmngr /usr/sbin/ethmngr
|
||||
}
|
||||
|
||||
function install_netmngr_as_micro_service()
|
||||
{
|
||||
[ -d "/opt/dev/netmngr" ] && return 0
|
||||
[ -d "${BBFDM_PLUGIN_DEST}/netmngr" ] && return 0
|
||||
|
||||
exec_cmd git clone -b devel https://dev.iopsys.eu/network/netmngr.git /opt/dev/netmngr
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/network/netmngr.git ${BBFDM_PLUGIN_DEST}/netmngr
|
||||
|
||||
exec_cmd apt install iproute2 -y
|
||||
|
||||
exec_cmd make -C /opt/dev/netmngr/src/ clean
|
||||
exec_cmd make -C /opt/dev/netmngr/src/ NETMNGR_GRE_OBJ=y NETMNGR_IP_OBJ=y NETMNGR_ROUTING_OBJ=y NETMNGR_PPP_OBJ=y NETMNGR_ROUTER_ADVERTISEMENT_OBJ=y NETMNGR_IPV6RD_OBJ=y
|
||||
install_ms /opt/dev/netmngr/src/libnetmngr.so netmngr
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/netmngr/src/ clean
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/netmngr/src/ NETMNGR_GRE_OBJ=y NETMNGR_IP_OBJ=y NETMNGR_ROUTING_OBJ=y NETMNGR_PPP_OBJ=y NETMNGR_ROUTER_ADVERTISEMENT_OBJ=y NETMNGR_IPV6RD_OBJ=y
|
||||
install_ms ${BBFDM_PLUGIN_DEST}/netmngr/src/libnetmngr.so netmngr
|
||||
|
||||
exec_cmd git clone https://dev.iopsys.eu/bbf/tr143d.git /opt/dev/tr143d
|
||||
exec_cmd make -C /opt/dev/tr143d/src/ clean && make -C /opt/dev/tr143d/src/
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/bbf/tr143d.git ${BBFDM_PLUGIN_DEST}/tr143d
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/tr143d/src/ clean && make -C ${BBFDM_PLUGIN_DEST}/tr143d/src/
|
||||
exec_cmd cp -f utilities/files/usr/share/bbfdm/scripts/bbf_api /usr/share/bbfdm/scripts/
|
||||
exec_cmd cp -rf /opt/dev/tr143d/scripts/* /usr/share/bbfdm/scripts/
|
||||
install_ms_plugin /opt/dev/tr143d/src/libtr143d.so netmngr
|
||||
exec_cmd cp -rf ${BBFDM_PLUGIN_DEST}/tr143d/scripts/* /usr/share/bbfdm/scripts/
|
||||
install_ms_plugin ${BBFDM_PLUGIN_DEST}/tr143d/src/libtr143d.so netmngr
|
||||
|
||||
exec_cmd git clone https://dev.iopsys.eu/bbf/tr471d.git /opt/dev/tr471d
|
||||
exec_cmd make -C /opt/dev/tr471d/src/ clean && make -C /opt/dev/tr471d/src/
|
||||
install_ms_plugin /opt/dev/tr471d/src/libtr471d.so netmngr
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/bbf/tr471d.git ${BBFDM_PLUGIN_DEST}/tr471d
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/tr471d/src/ clean && make -C ${BBFDM_PLUGIN_DEST}/tr471d/src/
|
||||
install_ms_plugin ${BBFDM_PLUGIN_DEST}/tr471d/src/libtr471d.so netmngr
|
||||
|
||||
exec_cmd git clone https://dev.iopsys.eu/bbf/twamp-light.git /opt/dev/twamp
|
||||
exec_cmd make -C /opt/dev/twamp clean && make -C /opt/dev/twamp
|
||||
install_ms_plugin /opt/dev/twamp/libtwamp.so netmngr
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/bbf/twamp-light.git ${BBFDM_PLUGIN_DEST}/twamp
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/twamp clean && make -C ${BBFDM_PLUGIN_DEST}/twamp
|
||||
install_ms_plugin ${BBFDM_PLUGIN_DEST}/twamp/libtwamp.so netmngr
|
||||
|
||||
exec_cmd git clone https://dev.iopsys.eu/bbf/udpecho.git /opt/dev/udpecho
|
||||
exec_cmd make -C /opt/dev/udpecho/src/ clean && make -C /opt/dev/udpecho/src/
|
||||
install_ms_plugin /opt/dev/udpecho/src/libudpechoserver.so netmngr
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/bbf/udpecho.git ${BBFDM_PLUGIN_DEST}/udpecho
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/udpecho/src/ clean && make -C ${BBFDM_PLUGIN_DEST}/udpecho/src/
|
||||
install_ms_plugin ${BBFDM_PLUGIN_DEST}/udpecho/src/libudpechoserver.so netmngr
|
||||
}
|
||||
|
||||
function install_sysmngr_as_micro_service()
|
||||
{
|
||||
[ -d "/opt/dev/sysmngr" ] && return 0
|
||||
[ -d "${BBFDM_PLUGIN_DEST}/sysmngr" ] && return 0
|
||||
|
||||
exec_cmd git clone -b devel https://dev.iopsys.eu/system/sysmngr.git /opt/dev/sysmngr
|
||||
exec_cmd git clone -b ${BRANCH:-devel} --depth=1 https://dev.iopsys.eu/system/sysmngr.git ${BBFDM_PLUGIN_DEST}/sysmngr
|
||||
|
||||
exec_cmd make -C /opt/dev/sysmngr/src/ clean && \
|
||||
exec_cmd make -C /opt/dev/sysmngr/src/ \
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/sysmngr/src/ clean && \
|
||||
exec_cmd make -C ${BBFDM_PLUGIN_DEST}/sysmngr/src/ \
|
||||
CFLAGS+="-DBBF_VENDOR_PREFIX=\\\"X_IOWRT_EU_\\\"" \
|
||||
SYSMNGR_VENDOR_CONFIG_FILE='y' \
|
||||
SYSMNGR_MEMORY_STATUS='y' \
|
||||
|
|
@ -195,7 +237,7 @@ function install_sysmngr_as_micro_service()
|
|||
SYSMNGR_VENDOR_EXTENSIONS='y' \
|
||||
SYSMNGR_FWBANK_UBUS_SUPPORT='y'
|
||||
|
||||
exec_cmd cp -f /opt/dev/sysmngr/src/sysmngr /usr/sbin/
|
||||
exec_cmd cp -f ${BBFDM_PLUGIN_DEST}/sysmngr/src/sysmngr /usr/sbin/
|
||||
exec_cmd mkdir /etc/sysmngr
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +245,7 @@ function error_on_zero()
|
|||
{
|
||||
ret=$1
|
||||
if [ "$ret" -eq 0 ]; then
|
||||
echo "Validation of last command failed, ret(${ret})"
|
||||
echo "Validation of Last command failed, ret(${ret})"
|
||||
cp /tmp/memory-*.xml .
|
||||
exit $ret
|
||||
fi
|
||||
|
|
@ -223,15 +265,14 @@ function generate_report()
|
|||
|
||||
function install_cmph()
|
||||
{
|
||||
[ -d "/opt/dev/cmph" ] && return 0
|
||||
[ -d "${BBFDM_PLUGIN_DEST}/cmph" ] && return 0
|
||||
|
||||
exec_cmd git clone https://git.code.sf.net/p/cmph/git /opt/dev/cmph
|
||||
exec_cmd git clone https://git.code.sf.net/p/cmph/git ${BBFDM_PLUGIN_DEST}/cmph
|
||||
(
|
||||
cd /opt/dev/cmph
|
||||
cd ${BBFDM_PLUGIN_DEST}/cmph
|
||||
exec_cmd autoreconf -i
|
||||
exec_cmd ./configure
|
||||
exec_cmd make
|
||||
exec_cmd sudo make install
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ for plugin in $(ls -1 test/vendor_test/*); do
|
|||
check_ret $?
|
||||
done
|
||||
|
||||
echo "Validate Data Model JSON Plugin after generating from TR-181, TR-104 and TR-135 XML Files"
|
||||
echo "Validate Data Model JSON Plugin after generating from TR-181 and TR-104 XML Files"
|
||||
json_path=$(./tools/convert_dm_xml_to_json.py -d test/tools/)
|
||||
./tools/validate_json_plugin.py $json_path
|
||||
check_ret $?
|
||||
|
|
|
|||
|
|
@ -222,20 +222,214 @@ int bbf_set_alias(struct dmctx *ctx, struct uci_section *s, const char *option_n
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void send_linker_request_event(struct ubus_context *ctx, const char *path)
|
||||
/**
|
||||
* @brief Safely read a JSON object from a file with shared locking.
|
||||
*
|
||||
* This function opens a JSON file and reads its contents into memory
|
||||
* using a shared lock (`flock` with `LOCK_SH`) to prevent concurrent
|
||||
* writes during the read operation. It replaces the use of
|
||||
* `json_object_from_file()` from json-c in scenarios where the file
|
||||
* may be modified by other processes.
|
||||
*
|
||||
* Key behavior:
|
||||
* - Acquires a shared lock (`LOCK_SH`) to ensure the file isn't being
|
||||
* modified by a writer holding an exclusive lock (`LOCK_EX`).
|
||||
* - Reads the entire file into a buffer and parses it using
|
||||
* `json_tokener_parse()`.
|
||||
* - Ensures memory safety and proper resource cleanup.
|
||||
*
|
||||
* @param file_path Path to the JSON file to read.
|
||||
* @return Pointer to the parsed `json_object`, or NULL on failure.
|
||||
*
|
||||
* @note All writers must acquire an exclusive lock (`LOCK_EX`) before
|
||||
* modifying the file to ensure this function reads consistent data.
|
||||
* Readers that bypass locking (e.g., using `json_object_from_file()`)
|
||||
* risk reading partial or corrupt data.
|
||||
*/
|
||||
|
||||
static struct json_object *bbfdm_json_object_from_file(const char *file_path)
|
||||
{
|
||||
struct blob_buf bb;
|
||||
int fd = open(file_path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
BBF_DEBUG("Cannot open file: %s", file_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (DM_STRLEN(path) == 0)
|
||||
return;
|
||||
// Acquire shared lock
|
||||
if (flock(fd, LOCK_SH) < 0) {
|
||||
BBF_ERR("Failed to acquire shared lock on: %s", file_path);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
// Read file into buffer
|
||||
off_t len = lseek(fd, 0, SEEK_END);
|
||||
if (len == -1 || lseek(fd, 0, SEEK_SET) == -1) {
|
||||
BBF_ERR("Failed to seek in file: %s", file_path);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blobmsg_add_string(&bb, "path", path);
|
||||
char *buffer = malloc(len + 1);
|
||||
if (!buffer) {
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ubus_send_event(ctx, "bbfdm.linker.request", bb.head);
|
||||
blob_buf_free(&bb);
|
||||
if (read(fd, buffer, len) != len) {
|
||||
BBF_ERR("Failed to read file: %s", file_path);
|
||||
free(buffer);
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer[len] = '\0'; // null-terminate
|
||||
|
||||
json_object *jobj = json_tokener_parse(buffer);
|
||||
|
||||
free(buffer);
|
||||
close(fd); // releases lock
|
||||
|
||||
return jobj;
|
||||
}
|
||||
|
||||
static char *join_path(const char *prefix, const char *key)
|
||||
{
|
||||
size_t len = strlen(prefix) + strlen(key) + 2; // +1 for dot, +1 for null
|
||||
|
||||
char *buf = dmmalloc(len);
|
||||
snprintf(buf, len, "%s%s.", prefix, key);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static char *find_path_recursive(json_object *curr, char **parts, int index, int total, const char *target_value, const char *current_path)
|
||||
{
|
||||
if (index >= total || curr == NULL)
|
||||
return NULL;
|
||||
|
||||
const char *part = parts[index];
|
||||
|
||||
if (strcmp(part, "*") == 0) {
|
||||
json_object_object_foreach(curr, key, val) {
|
||||
if (!key || json_object_get_type(val) != json_type_object)
|
||||
continue;
|
||||
|
||||
char *new_path = join_path(current_path, key);
|
||||
char *res = find_path_recursive(val, parts, index + 1, total, target_value, new_path);
|
||||
dmfree(new_path);
|
||||
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
json_object *next = NULL;
|
||||
|
||||
if (!json_object_object_get_ex(curr, part, &next))
|
||||
return NULL;
|
||||
|
||||
if (index == total - 1) {
|
||||
if (json_object_get_type(next) == json_type_string &&
|
||||
strcmp(json_object_get_string(next), target_value) == 0) {
|
||||
char *reference_path = dmstrdup(current_path);
|
||||
int len = DM_STRLEN(reference_path);
|
||||
|
||||
if (len > 0 && reference_path[len - 1] == '.')
|
||||
reference_path[len - 1] = 0;
|
||||
|
||||
return reference_path;
|
||||
}
|
||||
} else if (json_object_get_type(next) == json_type_object) {
|
||||
char *new_path = join_path(current_path, part);
|
||||
char *res = find_path_recursive(next, parts, index + 1, total, target_value, new_path);
|
||||
dmfree(new_path);
|
||||
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *bbfdm_resolve_external_reference_via_json(struct dmctx *ctx, const char *linker_path, const char *linker_value)
|
||||
{
|
||||
char file_path[256] = {0};
|
||||
char *reference_path = NULL;
|
||||
size_t count = 0;
|
||||
|
||||
if (!ctx || DM_STRLEN(linker_path) == 0 || !linker_value)
|
||||
return NULL;
|
||||
|
||||
char **parts = strsplit(linker_path, ".", &count);
|
||||
if (count < 2)
|
||||
return NULL;
|
||||
|
||||
snprintf(file_path, sizeof(file_path), "%s/%s.json", DATA_MODEL_DB_PATH, parts[1]);
|
||||
if (strlen(file_path) == 0)
|
||||
return NULL;
|
||||
|
||||
json_object *root = bbfdm_json_object_from_file(file_path);
|
||||
if (!root)
|
||||
return NULL;
|
||||
|
||||
reference_path = find_path_recursive(root, parts, 0, count, linker_value, "");
|
||||
|
||||
json_object_put(root);
|
||||
|
||||
return reference_path;
|
||||
}
|
||||
|
||||
static char *bbfdm_resolve_external_reference_via_dmmap(struct dmctx *ctx, const char *linker_path, const char *linker_name, const char *linker_value)
|
||||
{
|
||||
struct uci_section *dmmap_obj = NULL;
|
||||
size_t count = 0;
|
||||
|
||||
if (DM_STRLEN(linker_path) == 0 || DM_STRLEN(linker_name) == 0 || DM_STRLEN(linker_value) == 0)
|
||||
return NULL;
|
||||
|
||||
char **parts = strsplit(linker_path, ".", &count);
|
||||
if (count < 2)
|
||||
return NULL;
|
||||
|
||||
uci_path_foreach_sections(bbfdm, parts[1], parts[count - 1], dmmap_obj) {
|
||||
char *curr_value = NULL;
|
||||
|
||||
dmuci_get_value_by_section_string(dmmap_obj, linker_name, &curr_value);
|
||||
if (DM_STRCMP(curr_value, linker_value) == 0) {
|
||||
char reconstructed_path[1024] = {0};
|
||||
char *linker_instance = NULL;
|
||||
char *reference_path = NULL;
|
||||
char *option_value = NULL;
|
||||
int path_offset = 0;
|
||||
|
||||
dmuci_get_value_by_section_string(dmmap_obj, "__instance__", &linker_instance);
|
||||
|
||||
/* Parse linker path to extract instance numbers from instance wildcard if exists
|
||||
* Example: linker path is Device.Bridging.Bridge.*.Port.
|
||||
* We need to replace * with the parent instance number
|
||||
*/
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
|
||||
if (i > 0 && strcmp(parts[i], "*") == 0) {
|
||||
dmuci_get_value_by_section_string(dmmap_obj, parts[i - 1], &option_value);
|
||||
path_offset += snprintf(reconstructed_path + path_offset,
|
||||
sizeof(reconstructed_path) - path_offset,
|
||||
"%s.", option_value);
|
||||
continue;
|
||||
}
|
||||
|
||||
path_offset += snprintf(reconstructed_path + path_offset,
|
||||
sizeof(reconstructed_path) - path_offset,
|
||||
"%s.", parts[i]);
|
||||
}
|
||||
|
||||
/* Append the final instance number */
|
||||
dmasprintf(&reference_path, "%s%s", reconstructed_path, linker_instance);
|
||||
return reference_path;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bbfdm_get_references(struct dmctx *ctx, int match_action, const char *base_path, const char *key_name, char *key_value, char *out, size_t out_len)
|
||||
|
|
@ -243,6 +437,16 @@ int bbfdm_get_references(struct dmctx *ctx, int match_action, const char *base_p
|
|||
char param_path[1024] = {0};
|
||||
char *value = NULL;
|
||||
|
||||
if (!out || !out_len) {
|
||||
BBF_ERR("Output buffer is NULL or has zero length. A valid buffer with sufficient size is required");
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t len = strlen(out);
|
||||
|
||||
if (match_action == MATCH_FIRST && len > 0) // Reference path is already resolved
|
||||
return 0;
|
||||
|
||||
if (DM_STRLEN(base_path) == 0) {
|
||||
BBF_ERR("Reference base path should not be empty!!!");
|
||||
return -1;
|
||||
|
|
@ -258,17 +462,10 @@ int bbfdm_get_references(struct dmctx *ctx, int match_action, const char *base_p
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (!out || !out_len) {
|
||||
BBF_ERR("Output buffer is NULL or has zero length. A valid buffer with sufficient size is required");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(param_path, sizeof(param_path), "%s*.%s", base_path, key_name);
|
||||
|
||||
adm_entry_get_reference_param(ctx, param_path, key_value, &value);
|
||||
|
||||
size_t len = strlen(out);
|
||||
|
||||
if (DM_STRLEN(value) != 0) {
|
||||
|
||||
if (out_len - len < strlen(value)) {
|
||||
|
|
@ -276,22 +473,31 @@ int bbfdm_get_references(struct dmctx *ctx, int match_action, const char *base_p
|
|||
return -1;
|
||||
}
|
||||
|
||||
snprintf(&out[len], out_len - len, "%s%s", len ? (match_action == MATCH_FIRST ? "," : ";") : "", value);
|
||||
snprintf(&out[len], out_len - len, "%s%s", (len > 0) ? "," : "", value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (out_len - len < strlen(base_path) + strlen(key_name) + strlen(key_value) + 9) { // 9 = 'path[key_name==\"key_value\"].'
|
||||
BBF_ERR("Buffer overflow detected. The output buffer is not large enough to hold the additional data!!!");
|
||||
return -1;
|
||||
char *external_reference = NULL;
|
||||
|
||||
// Try dmmap first
|
||||
external_reference = bbfdm_resolve_external_reference_via_dmmap(ctx, base_path, key_name, key_value);
|
||||
|
||||
// Fall back to JSON if dmmap failed
|
||||
if (external_reference == NULL)
|
||||
external_reference = bbfdm_resolve_external_reference_via_json(ctx, param_path, key_value);
|
||||
|
||||
if (external_reference != NULL) {
|
||||
|
||||
if (out_len - len < strlen(external_reference)) {
|
||||
BBF_ERR("Buffer overflow detected. The output buffer is not large enough to hold the additional data!!!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(&out[len], out_len - len, "%s%s", (len > 0) ? "," : "", external_reference);
|
||||
return 0;
|
||||
}
|
||||
|
||||
snprintf(param_path, sizeof(param_path), "%s[%s==\"%s\"].", base_path, key_name, key_value);
|
||||
|
||||
send_linker_request_event(ctx->ubus_ctx, param_path);
|
||||
|
||||
snprintf(&out[len], out_len - len, "%s%s", len ? (match_action == MATCH_FIRST ? "," : ";") : "", param_path);
|
||||
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _bbfdm_get_references(struct dmctx *ctx, const char *base_path, const char *key_name, char *key_value, char **value)
|
||||
|
|
@ -302,38 +508,131 @@ int _bbfdm_get_references(struct dmctx *ctx, const char *base_path, const char *
|
|||
|
||||
*value = (!res) ? dmstrdup(buf): "";
|
||||
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
static json_object *get_node(json_object *root, const char *path)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
if (!root || !path)
|
||||
return NULL;
|
||||
|
||||
char **parts = strsplit(path, ".", &count);
|
||||
|
||||
if (count == 0)
|
||||
return NULL;
|
||||
|
||||
json_object *curr = root;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!json_object_object_get_ex(curr, parts[i], &curr)) {
|
||||
curr = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return curr;
|
||||
}
|
||||
|
||||
static char *construct_section_name_from_path(char **parts, int count)
|
||||
{
|
||||
char section_name[256] = {0};
|
||||
int offset = 0;
|
||||
int i;
|
||||
|
||||
// Start from index 2 (skip "Device" and top-level like "IP")
|
||||
// Build section name: Type_Instance pairs
|
||||
// Example: Device.IP.Interface.1.IPv4Address.2 -> Interface_1IPv4Address_2
|
||||
for (i = 2; i < count; i++) {
|
||||
if (isdigit_str(parts[i])) {
|
||||
// This is an instance number, append it with underscore
|
||||
offset += snprintf(section_name + offset, sizeof(section_name) - offset, "_%s", parts[i]);
|
||||
} else {
|
||||
// This is an object type, only append if followed by an index number
|
||||
if (i + 1 < count && isdigit_str(parts[i + 1])) {
|
||||
offset += snprintf(section_name + offset, sizeof(section_name) - offset, "%s", parts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (offset > 0)
|
||||
return dmstrdup(section_name);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int bbfdm_get_reference_linker(struct dmctx *ctx, char *reference_path, struct dm_reference *reference_args)
|
||||
{
|
||||
if (DM_STRLEN(reference_path) == 0) {
|
||||
bbfdm_set_fault_message(ctx, "%s: reference path should not be empty", __func__);
|
||||
char file_path[256] = {0};
|
||||
size_t count = 0;
|
||||
|
||||
if (!reference_path || !reference_args)
|
||||
return -1;
|
||||
}
|
||||
|
||||
reference_args->path = reference_path;
|
||||
|
||||
char *separator = strstr(reference_path, "=>");
|
||||
if (!separator) {
|
||||
bbfdm_set_fault_message(ctx, "%s: reference path must contain '=>' symbol to separate the path and value", __func__);
|
||||
if (DM_STRLEN(reference_args->path) == 0)
|
||||
return 0;
|
||||
|
||||
char **parts = strsplit(reference_path, ".", &count);
|
||||
if (count < 2)
|
||||
return -1;
|
||||
|
||||
// Try to resolve from dmmap first (UCI)
|
||||
char *section_name = construct_section_name_from_path(parts, count);
|
||||
|
||||
if (section_name) {
|
||||
// Package name is the second level in the path (e.g., "IP" in Device.IP.Interface.1)
|
||||
const char *package_name = parts[1];
|
||||
char *name_value = NULL;
|
||||
|
||||
// Get the Name option value directly using package name and section name
|
||||
int ret = dmuci_get_option_value_string_bbfdm(package_name, section_name, "Name", &name_value);
|
||||
|
||||
if (ret == 0 && DM_STRLEN(name_value) > 0) {
|
||||
reference_args->value = dmstrdup(name_value);
|
||||
reference_args->is_valid_path = true;
|
||||
dmfree(section_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dmfree(section_name);
|
||||
}
|
||||
|
||||
*separator = 0;
|
||||
// Fallback to JSON resolution
|
||||
snprintf(file_path, sizeof(file_path), "%s/%s.json", DATA_MODEL_DB_PATH, parts[1]);
|
||||
if (strlen(file_path) == 0)
|
||||
return -1;
|
||||
|
||||
reference_args->value = separator + 2;
|
||||
json_object *root = bbfdm_json_object_from_file(file_path);
|
||||
if (!root)
|
||||
return -1;
|
||||
|
||||
char *valid_path = strstr(separator + 2, "##");
|
||||
if (valid_path) {
|
||||
json_object *node_obj = get_node(root, reference_path);
|
||||
if (node_obj == NULL) {
|
||||
reference_args->value = dmstrdup("");
|
||||
reference_args->is_valid_path = false;
|
||||
} else {
|
||||
char *value = NULL;
|
||||
|
||||
json_object_object_foreach(node_obj, key, val) {
|
||||
(void)key; // Suppress unused variable warning
|
||||
|
||||
if (json_object_get_type(val) != json_type_string)
|
||||
continue;
|
||||
|
||||
value = dmstrdup(json_object_get_string(val));
|
||||
break;
|
||||
}
|
||||
|
||||
reference_args->value = dmstrdup(value ? value : "");
|
||||
reference_args->is_valid_path = true;
|
||||
*valid_path = 0;
|
||||
}
|
||||
|
||||
json_object_put(root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bbfdm_operate_reference_linker(struct dmctx *ctx, const char *reference_path, char **reference_value)
|
||||
int bbfdm_operate_reference_linker(struct dmctx *ctx, const char *reference_path, char **reference_value) //TO be removed
|
||||
{
|
||||
if (!ctx) {
|
||||
BBF_ERR("%s: ctx should not be null", __func__);
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ typedef struct dm_map_obj {
|
|||
struct dm_leaf_s *root_leaf;
|
||||
int (*init_module)(void *data);
|
||||
int (*clean_module)(void *data);
|
||||
int (*uci_sync_handler)(void *data);
|
||||
} DM_MAP_OBJ;
|
||||
|
||||
struct dm_reference {
|
||||
|
|
@ -170,6 +171,11 @@ struct dm_reference {
|
|||
bool is_valid_path;
|
||||
};
|
||||
|
||||
enum {
|
||||
BBFDM_API_V0,
|
||||
BBFDM_API_V1
|
||||
};
|
||||
|
||||
struct dmctx {
|
||||
bool stop;
|
||||
bool match;
|
||||
|
|
@ -188,6 +194,7 @@ struct dmctx {
|
|||
int faultcode;
|
||||
int setaction;
|
||||
unsigned int dm_type;
|
||||
unsigned int bbfdm_api_version;
|
||||
unsigned char inparam_isparam;
|
||||
unsigned char findparam;
|
||||
|
||||
|
|
@ -206,8 +213,10 @@ struct dmctx {
|
|||
struct uci_context *varstate_uci_ctx;
|
||||
struct ubus_context *ubus_ctx;
|
||||
struct list_head *memhead;
|
||||
struct list_head *modified_uci_head;
|
||||
|
||||
char *inst_buf[16];
|
||||
const char *obj_buf[16];
|
||||
const char *inst_buf[16];
|
||||
char fault_msg[256];
|
||||
};
|
||||
|
||||
|
|
@ -215,6 +224,7 @@ typedef struct dmnode {
|
|||
DMOBJ *obj;
|
||||
struct dmnode *parent;
|
||||
char *current_object;
|
||||
char *current_object_file;
|
||||
void *prev_data;
|
||||
char *prev_instance;
|
||||
unsigned char instance_level;
|
||||
|
|
@ -273,6 +283,7 @@ enum {
|
|||
BBF_DEL_OBJECT,
|
||||
BBF_OPERATE,
|
||||
BBF_EVENT,
|
||||
BBF_REFERENCES_DB
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
|
|||
|
|
@ -330,6 +330,7 @@ static bool check_dependency(const char *conf_obj)
|
|||
/* multiple ubus => "ubus:system->info,dsl->status,wifi" */
|
||||
/* one package => "opkg:icwmp" */
|
||||
/* multiple packages => "opkg:icwmp,obuspa" */
|
||||
/* directory => "dir:/sys/class/ieee80211/" */
|
||||
/* common (files, ubus and opkg) => "file:/etc/config/network,/etc/config/dhcp;ubus:system,dsl->status;opkg:icwmp" */
|
||||
|
||||
char *pch = NULL, *spch = NULL;
|
||||
|
|
@ -351,6 +352,9 @@ static bool check_dependency(const char *conf_obj)
|
|||
if (!strcmp(pch, "file") && !file_exists(token))
|
||||
return false;
|
||||
|
||||
if (!strcmp(pch, "dir") && !bbfdm_folder_exists(token))
|
||||
return false;
|
||||
|
||||
if (!strcmp(pch, "ubus") && !dmubus_object_method_exists(token))
|
||||
return false;
|
||||
|
||||
|
|
@ -427,6 +431,7 @@ static void dm_browse_entry(struct dmctx *dmctx, DMNODE *parent_node, DMOBJ *ent
|
|||
node.matched = parent_node->matched;
|
||||
node.prev_data = data;
|
||||
node.prev_instance = instance;
|
||||
node.current_object_file = parent_node->current_object_file;
|
||||
|
||||
if (!bbfdatamodel_matches(dmctx->dm_type, entryobj->bbfdm_type)) {
|
||||
*err = FAULT_9005;
|
||||
|
|
@ -443,6 +448,18 @@ static void dm_browse_entry(struct dmctx *dmctx, DMNODE *parent_node, DMOBJ *ent
|
|||
else
|
||||
dmasprintf(&(node.current_object), "%s%s.", parent_obj, entryobj->obj);
|
||||
|
||||
if (DM_STRCMP(parent_obj, ROOT_NODE) == 0) { // Case1: parent object is 'Device.'
|
||||
node.current_object_file = entryobj->obj;
|
||||
} else if (parent_node->parent && DM_STRLEN(parent_node->parent->current_object) == 0) { // Case2: parent object is 'Device.X.X.'
|
||||
size_t count = 0;
|
||||
|
||||
char **parts = strsplit(parent_obj, ".", &count);
|
||||
if (count < 2)
|
||||
return;
|
||||
|
||||
node.current_object_file = parts[1];
|
||||
}
|
||||
|
||||
if (dmctx->checkobj) {
|
||||
*err = dmctx->checkobj(dmctx, &node, entryobj->permission, entryobj->addobj, entryobj->delobj, entryobj->get_linker, data, instance);
|
||||
if (*err)
|
||||
|
|
@ -522,7 +539,7 @@ int dm_link_inst_obj(struct dmctx *dmctx, DMNODE *parent_node, void *data, char
|
|||
char *parent_obj;
|
||||
DMNODE node = {0};
|
||||
|
||||
if (parent_node->browse_type == BROWSE_FIND_MAX_INST) {
|
||||
if (parent_node->browse_type == BROWSE_FIND_MAX_INST) { // To be removed later!!!!!!!!!!!!
|
||||
int curr_inst = (instance && *instance != '\0') ? DM_STRTOL(instance) : 0;
|
||||
if (curr_inst > parent_node->max_instance)
|
||||
parent_node->max_instance = curr_inst;
|
||||
|
|
@ -530,7 +547,7 @@ int dm_link_inst_obj(struct dmctx *dmctx, DMNODE *parent_node, void *data, char
|
|||
}
|
||||
|
||||
parent_node->num_of_entries++;
|
||||
if (parent_node->browse_type == BROWSE_NUM_OF_ENTRIES)
|
||||
if (parent_node->browse_type == BROWSE_NUM_OF_ENTRIES) // To be removed later!!!!!!!!!!!!
|
||||
return 0;
|
||||
|
||||
DMOBJ *prevobj = parent_node->obj;
|
||||
|
|
@ -542,6 +559,7 @@ int dm_link_inst_obj(struct dmctx *dmctx, DMNODE *parent_node, void *data, char
|
|||
node.instance_level = parent_node->instance_level + 1;
|
||||
node.is_instanceobj = 1;
|
||||
node.matched = parent_node->matched;
|
||||
node.current_object_file = parent_node->current_object_file;
|
||||
|
||||
parent_obj = parent_node->current_object;
|
||||
if (instance == NULL)
|
||||
|
|
@ -591,8 +609,53 @@ int get_number_of_entries(struct dmctx *ctx, void *data, char *instance, int (*b
|
|||
{
|
||||
DMNODE node = {0};
|
||||
|
||||
node.browse_type = BROWSE_NUM_OF_ENTRIES;
|
||||
(browseinstobj)(ctx, &node, data, instance);
|
||||
if (browseinstobj == NULL) {
|
||||
int instance_level = 0;
|
||||
size_t count = 0;
|
||||
|
||||
if (!ctx->addobj_instance)
|
||||
return 0;
|
||||
|
||||
char **parts = strsplit(ctx->addobj_instance, ".", &count);
|
||||
if (count < 2)
|
||||
return -1;
|
||||
|
||||
for (int idx = 0; idx < count - 1; idx++) {
|
||||
|
||||
int i = 0;
|
||||
bool is_instance = true;
|
||||
while (parts[idx][i] != 0) {
|
||||
if (isdigit(parts[idx][i]) == false) {
|
||||
is_instance = false;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (is_instance) instance_level++;
|
||||
}
|
||||
|
||||
char *p = DM_STRSTR(parts[count - 1], "NumberOfEntries");
|
||||
if (p) *p = 0;
|
||||
|
||||
node.obj = dmcalloc(1, sizeof(struct dm_obj_s));
|
||||
if (node.obj == NULL) {
|
||||
BBF_ERR("Failed to allocate memory");
|
||||
return 0;
|
||||
}
|
||||
|
||||
node.current_object_file = parts[1];
|
||||
node.obj->obj = parts[count - 1];
|
||||
node.instance_level = instance_level;
|
||||
|
||||
node.browse_type = BROWSE_NUM_OF_ENTRIES;
|
||||
generic_browse(ctx,&node, data, instance);
|
||||
} else {
|
||||
node.browse_type = BROWSE_NUM_OF_ENTRIES;
|
||||
(browseinstobj)(ctx, &node, data, instance);
|
||||
}
|
||||
|
||||
node.browse_type = BROWSE_NORMAL;
|
||||
return node.num_of_entries;
|
||||
}
|
||||
|
||||
|
|
@ -633,6 +696,108 @@ char *handle_instance(struct dmctx *dmctx, DMNODE *parent_node, struct uci_secti
|
|||
return instance ? instance : "";
|
||||
}
|
||||
|
||||
struct uci_section *create_dmmap_obj(struct dmctx *dmctx, unsigned char instance_level,
|
||||
const char *obj_file, const char *obj_name, struct uci_section *config_sec,
|
||||
char **instance)
|
||||
{
|
||||
struct uci_section *s = NULL, *dmmap_section = NULL;
|
||||
char config_sec_name[128] = {0};
|
||||
int max_instance = 0;
|
||||
|
||||
if (!dmctx || !obj_file || !obj_name || !instance)
|
||||
return NULL;
|
||||
|
||||
if (config_sec != NULL) {
|
||||
snprintf(config_sec_name, sizeof(config_sec_name), "%s.%s", section_config(config_sec), section_name(config_sec));
|
||||
}
|
||||
|
||||
uci_path_foreach_sections(bbfdm, obj_file, obj_name, s) {
|
||||
bool is_same_parent = true;
|
||||
|
||||
for (int i = 0; i < instance_level; i++) {
|
||||
char *curr_obj_inst = NULL;
|
||||
dmuci_get_value_by_section_string(s, dmctx->obj_buf[i], &curr_obj_inst);
|
||||
if (DM_STRCMP(curr_obj_inst, dmctx->inst_buf[i]) != 0) {
|
||||
is_same_parent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_same_parent == false)
|
||||
continue;
|
||||
|
||||
char *curr_instance = NULL;
|
||||
dmuci_get_value_by_section_string(s, "__instance__", &curr_instance);
|
||||
if (DM_STRLEN(curr_instance) == 0) {
|
||||
BBF_ERR("Found section without __instance__ in package: %s, section type: %s section name: %s. Deleting this entry",
|
||||
section_config(s), section_type(s), section_name(s));
|
||||
dmuci_delete_by_section(s, NULL, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
int curr_instance_int = DM_STRTOL(curr_instance);
|
||||
if (curr_instance_int > max_instance)
|
||||
max_instance = curr_instance_int;
|
||||
|
||||
if (config_sec != NULL) {
|
||||
char *curr_sec_name = NULL;
|
||||
dmuci_get_value_by_section_string(s, "__section_name__", &curr_sec_name);
|
||||
if (DM_STRCMP(curr_sec_name, config_sec_name) == 0) {
|
||||
dmctx->obj_buf[instance_level] = obj_name;
|
||||
dmctx->inst_buf[instance_level] = curr_instance;
|
||||
*instance = curr_instance;
|
||||
return dmmap_section;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dmasprintf(instance, "%d", max_instance + 1);
|
||||
|
||||
if (dmmap_section == NULL) {
|
||||
// Section not found -> create it
|
||||
char s_name[64] = {0};
|
||||
int pos = 0;
|
||||
|
||||
for (int i = 0; i < instance_level; i++) {
|
||||
pos += snprintf(&s_name[pos], sizeof(s_name) - pos, "%s_%s", dmctx->obj_buf[i], dmctx->inst_buf[i]);
|
||||
}
|
||||
|
||||
snprintf(&s_name[pos], sizeof(s_name) - pos, "%s_%s", obj_name, *instance);
|
||||
|
||||
dmuci_add_named_section_bbfdm(obj_file, obj_name, s_name, &dmmap_section);
|
||||
|
||||
dmuci_set_value_by_section(dmmap_section, "__section_name__", config_sec_name);
|
||||
dmuci_set_value_by_section(dmmap_section, "__instance__", *instance);
|
||||
|
||||
for (int i = 0; i < instance_level; i++) {
|
||||
dmuci_set_value_by_section(dmmap_section, dmctx->obj_buf[i], dmctx->inst_buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
dmctx->obj_buf[instance_level] = obj_name;
|
||||
dmctx->inst_buf[instance_level] = *instance;
|
||||
|
||||
return dmmap_section;
|
||||
}
|
||||
|
||||
char *uci_handle_instance(struct dmctx *dmctx, DMNODE *parent_node, struct dm_data *data)
|
||||
{
|
||||
char *instance = NULL;
|
||||
|
||||
switch(parent_node->browse_type) {
|
||||
case BROWSE_NORMAL: // To be removed later!!!!!!!!!!!!
|
||||
dmuci_get_value_by_section_string(data->dmmap_section, "__instance__", &instance);
|
||||
dmctx->obj_buf[parent_node->instance_level] = parent_node->obj->obj;
|
||||
dmctx->inst_buf[parent_node->instance_level] = instance ? instance : "";
|
||||
break;
|
||||
case BROWSE_FIND_MAX_INST: // To be removed later!!!!!!!!!!!!
|
||||
case BROWSE_NUM_OF_ENTRIES: // To be removed later!!!!!!!!!!!!
|
||||
break;
|
||||
}
|
||||
|
||||
return instance ? instance : "";
|
||||
}
|
||||
|
||||
char *handle_instance_without_section(struct dmctx *dmctx, DMNODE *parent_node, int inst_nbr)
|
||||
{
|
||||
char *instance = NULL;
|
||||
|
|
@ -640,17 +805,61 @@ char *handle_instance_without_section(struct dmctx *dmctx, DMNODE *parent_node,
|
|||
switch(parent_node->browse_type) {
|
||||
case BROWSE_NORMAL:
|
||||
dmasprintf(&instance, "%d", inst_nbr);
|
||||
dmctx->obj_buf[parent_node->instance_level] = parent_node->obj->obj;
|
||||
dmctx->inst_buf[parent_node->instance_level] = instance ? instance : "";
|
||||
break;
|
||||
case BROWSE_FIND_MAX_INST:
|
||||
case BROWSE_NUM_OF_ENTRIES:
|
||||
break;
|
||||
}
|
||||
|
||||
dmctx->inst_buf[parent_node->instance_level] = instance ? instance : "";
|
||||
|
||||
return instance ? instance : "";
|
||||
}
|
||||
|
||||
int generic_browse(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
|
||||
{
|
||||
struct dm_data curr_data = {0};
|
||||
char *instance = NULL;
|
||||
|
||||
// This check is added to prevent crashes if the object is updated to new design but forgot to update its NumberOfEntries parameter maps to it
|
||||
if (parent_node->obj == NULL)
|
||||
return 0;
|
||||
|
||||
uci_path_foreach_sections(bbfdm, parent_node->current_object_file, parent_node->obj->obj, curr_data.dmmap_section) {
|
||||
char *config_sec_name = NULL;
|
||||
bool is_same_parent = true;
|
||||
|
||||
// skip instances which has no __instance__
|
||||
dmuci_get_value_by_section_string(curr_data.dmmap_section, "__instance__", &instance);
|
||||
if (DM_STRLEN(instance) == 0) {
|
||||
BBF_WARNING("Skipping object with no instance number in package: %s, section type: %s, section name: %s",
|
||||
section_config(curr_data.dmmap_section), section_type(curr_data.dmmap_section), section_name(curr_data.dmmap_section));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < parent_node->instance_level; i++) {
|
||||
char *curr_obj_inst = NULL;
|
||||
dmuci_get_value_by_section_string(curr_data.dmmap_section, dmctx->obj_buf[i], &curr_obj_inst);
|
||||
if (DM_STRCMP(curr_obj_inst, dmctx->inst_buf[i]) != 0) {
|
||||
is_same_parent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_same_parent == false)
|
||||
continue;
|
||||
|
||||
dmuci_get_value_by_section_string(curr_data.dmmap_section, "__section_name__", &config_sec_name);
|
||||
curr_data.config_section = get_config_section_from_dmmap_section_name(config_sec_name);
|
||||
|
||||
instance = uci_handle_instance(dmctx, parent_node, &curr_data);
|
||||
if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, instance) == DM_STOP)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int get_empty(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
|
||||
{
|
||||
*value = dmstrdup("");
|
||||
|
|
@ -887,125 +1096,6 @@ static char *get_default_value_by_type(const char *param_name, int type)
|
|||
}
|
||||
}
|
||||
|
||||
static void convert_to_regex(const char *input, char *output)
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; input[i] != '\0'; i++) {
|
||||
if (input[i] == '.') {
|
||||
output[j++] = '\\'; // Escape '.'
|
||||
output[j++] = '.';
|
||||
} else if (input[i] == '*') {
|
||||
output[j++] = '['; // Replace '*' with '[^.]+'
|
||||
output[j++] = '^';
|
||||
output[j++] = '.';
|
||||
output[j++] = ']';
|
||||
output[j++] = '+';
|
||||
} else {
|
||||
output[j++] = input[i]; // Copy other characters
|
||||
}
|
||||
}
|
||||
|
||||
output[j++] = '.'; // Allow anything after the base match
|
||||
output[j++] = '*';
|
||||
output[j] = '\0'; // Null-terminate the string
|
||||
}
|
||||
|
||||
static bool is_same_reference_path(const char *curr_value, const char *in_value, char *out, size_t out_len)
|
||||
{
|
||||
char *pch = NULL, *pchr = NULL;
|
||||
char buf[2048] = {0};
|
||||
|
||||
if (!curr_value || !in_value || !out || !out_len)
|
||||
return false;
|
||||
|
||||
if (strcmp(curr_value, in_value) == 0)
|
||||
return true;
|
||||
|
||||
if (DM_STRLEN(in_value) == 0) {
|
||||
DM_STRNCPY(out, "=>", sizeof(out_len));
|
||||
return false;
|
||||
}
|
||||
|
||||
char *in_value_list = strchr(in_value, ',');
|
||||
if (in_value_list) {
|
||||
char formatted_value[2048] = {0};
|
||||
long int pos = 0;
|
||||
|
||||
DM_STRNCPY(buf, in_value, sizeof(buf));
|
||||
|
||||
formatted_value[0] = '\0';
|
||||
|
||||
for (pch = strtok_r(buf, ",", &pchr); pch != NULL; pch = strtok_r(NULL, ",", &pchr)) {
|
||||
|
||||
if (formatted_value[0] == '\0') {
|
||||
pos += snprintf(formatted_value, sizeof(formatted_value), "%s", pch);
|
||||
} else {
|
||||
pos += snprintf(&formatted_value[pos], sizeof(formatted_value) - pos, ";%s", pch);
|
||||
}
|
||||
|
||||
char *delimiter_pos = DM_STRSTR(formatted_value, "=>");
|
||||
if (delimiter_pos) {
|
||||
pos = labs(delimiter_pos - formatted_value);
|
||||
*delimiter_pos = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(curr_value, formatted_value) == 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
DM_STRNCPY(buf, curr_value, sizeof(buf));
|
||||
|
||||
for (pch = strtok_r(buf, ",", &pchr); pch != NULL; pch = strtok_r(NULL, ",", &pchr)) {
|
||||
|
||||
char *p = strchr(pch, '[');
|
||||
if (p) {
|
||||
char regex_pattern[MAX_DM_PATH * 2] = {0};
|
||||
char path[MAX_DM_PATH] = {0};
|
||||
char key_name[256], key_value[256];
|
||||
regmatch_t pmatch[2];
|
||||
regmatch_t p_match[1];
|
||||
|
||||
if (!match(pch, "\\[(.*?)\\]", 2, pmatch))
|
||||
continue;
|
||||
|
||||
snprintf(path, pmatch[0].rm_so + 1, "%s", pch);
|
||||
int len = DM_STRLEN(path);
|
||||
if (!len)
|
||||
continue;
|
||||
|
||||
char *match_str = pch + pmatch[1].rm_so;
|
||||
if (DM_STRLEN(match_str) == 0)
|
||||
continue;
|
||||
|
||||
int n = sscanf(match_str, "%255[^=]==\"%255[^\"]\"", key_name, key_value);
|
||||
if (n != 2) {
|
||||
n = sscanf(match_str, "%255[^=]==%255[^]]", key_name, key_value);
|
||||
if (n != 2)
|
||||
continue;
|
||||
}
|
||||
|
||||
char *tag = strstr(in_value, "=>");
|
||||
if (!tag)
|
||||
continue;
|
||||
|
||||
convert_to_regex(path, regex_pattern);
|
||||
|
||||
if (match(in_value, regex_pattern, 1, p_match) && strncmp(key_value, tag + 2, strlen(key_value)) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (strncmp(pch, in_value, strlen(pch)) == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* **********
|
||||
* get value
|
||||
* **********/
|
||||
|
|
@ -1016,10 +1106,12 @@ static int get_value_obj(DMOBJECT_ARGS)
|
|||
|
||||
static int get_value_param(DMPARAM_ARGS)
|
||||
{
|
||||
char full_param[MAX_DM_PATH] = {0};
|
||||
char *full_param = NULL;
|
||||
char *value = dmstrdup("");
|
||||
|
||||
snprintf(full_param, sizeof(full_param), "%s%s", node->current_object, leaf->parameter);
|
||||
dmasprintf(&full_param, "%s%s", node->current_object, leaf->parameter);
|
||||
|
||||
dmctx->addobj_instance = full_param; // This assignment is needed to pass the refparam in order to calculate the NumberOfEntries param
|
||||
|
||||
(leaf->getvalue)(full_param, dmctx, data, instance, &value);
|
||||
|
||||
|
|
@ -1041,10 +1133,10 @@ static int mobj_get_value_in_param(DMOBJECT_ARGS)
|
|||
}
|
||||
static int mparam_get_value_in_param(DMPARAM_ARGS)
|
||||
{
|
||||
char full_param[MAX_DM_PATH] = {0};
|
||||
char *full_param = NULL;
|
||||
char *value = dmstrdup("");
|
||||
|
||||
snprintf(full_param, sizeof(full_param), "%s%s", node->current_object, leaf->parameter);
|
||||
dmasprintf(&full_param, "%s%s", node->current_object, leaf->parameter);
|
||||
|
||||
if (dmctx->iswildcard) {
|
||||
if (dm_strcmp_wildcard(dmctx->in_param, full_param) != 0)
|
||||
|
|
@ -1054,6 +1146,8 @@ static int mparam_get_value_in_param(DMPARAM_ARGS)
|
|||
return FAULT_9005;
|
||||
}
|
||||
|
||||
dmctx->addobj_instance = full_param; // This assignment is needed to pass the refparam in order to calculate the NumberOfEntries param
|
||||
|
||||
(leaf->getvalue)(full_param, dmctx, data, instance, &value);
|
||||
|
||||
if ((leaf->dm_flags & DM_FLAG_SECURE) && (dmctx->dm_type == BBFDM_CWMP)) {
|
||||
|
|
@ -1466,6 +1560,7 @@ static int mobj_add_object(DMOBJECT_ARGS)
|
|||
char *refparam = node->current_object;
|
||||
char *perm = permission->val;
|
||||
char *new_instance = NULL;
|
||||
char file_path[64] = {0};
|
||||
int fault = 0;
|
||||
|
||||
if (DM_STRCMP(refparam, dmctx->in_param) != 0)
|
||||
|
|
@ -1480,16 +1575,43 @@ static int mobj_add_object(DMOBJECT_ARGS)
|
|||
if (perm[0] == '0' || addobj == NULL)
|
||||
return FAULT_9005;
|
||||
|
||||
int max_inst = find_max_instance(dmctx, node);
|
||||
fault = dmasprintf(&new_instance, "%d", max_inst);
|
||||
if (fault)
|
||||
return fault;
|
||||
snprintf(file_path, sizeof(file_path), "/etc/bbfdm/dmmap/%s", node->current_object_file);
|
||||
|
||||
dmctx->stop = 1;
|
||||
if (file_exists(file_path)) {
|
||||
struct dm_data curr_data = {0};
|
||||
|
||||
fault = (addobj)(refparam, dmctx, data, &new_instance);
|
||||
if (fault)
|
||||
return fault;
|
||||
curr_data.dmmap_section = create_dmmap_obj(dmctx, node->instance_level, node->current_object_file, node->obj->obj, NULL, &new_instance);
|
||||
if (DM_STRLEN(new_instance) == 0 || curr_data.dmmap_section == NULL)
|
||||
return FAULT_9005;
|
||||
|
||||
dmctx->stop = 1;
|
||||
|
||||
// cppcheck-suppress autoVariables
|
||||
dmctx->addobj_instance = (char *)&curr_data;
|
||||
|
||||
fault = (addobj)(refparam, dmctx, data, &new_instance);
|
||||
if (fault)
|
||||
return fault;
|
||||
|
||||
if (curr_data.config_section != NULL) {
|
||||
char sec_name[128] = {0};
|
||||
|
||||
snprintf(sec_name, sizeof(sec_name), "%s.%s", section_config(curr_data.config_section), section_name(curr_data.config_section));
|
||||
dmuci_set_value_by_section(curr_data.dmmap_section, "__section_name__", sec_name);
|
||||
}
|
||||
} else {
|
||||
int max_inst = find_max_instance(dmctx, node);
|
||||
fault = dmasprintf(&new_instance, "%d", max_inst);
|
||||
if (fault)
|
||||
return fault;
|
||||
|
||||
dmctx->stop = 1;
|
||||
|
||||
fault = (addobj)(refparam, dmctx, data, &new_instance);
|
||||
if (fault)
|
||||
return fault;
|
||||
|
||||
}
|
||||
|
||||
dmctx->addobj_instance = new_instance;
|
||||
return 0;
|
||||
|
|
@ -1623,7 +1745,7 @@ static int get_datatype(char *type)
|
|||
static int mparam_set_value(DMPARAM_ARGS)
|
||||
{
|
||||
char refparam[MAX_DM_PATH] = {0};
|
||||
char param_value[2048] = {0};
|
||||
char param_value[4096] = {0};
|
||||
char *value = dmstrdup("");
|
||||
|
||||
snprintf(refparam, MAX_DM_PATH, "%s%s", node->current_object, leaf->parameter);
|
||||
|
|
@ -1662,23 +1784,9 @@ static int mparam_set_value(DMPARAM_ARGS)
|
|||
BBF_DEBUG("Requested value (%s) is same as current value (%s).", dmctx->in_value, value);
|
||||
return 0;
|
||||
}
|
||||
} else if (leaf->dm_flags & DM_FLAG_REFERENCE) {
|
||||
if (is_same_reference_path(value, dmctx->in_value, param_value, sizeof(param_value))) {
|
||||
BBF_DEBUG("Requested value (%s) is same as current value (%s)..", dmctx->in_value, value);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
int len = DM_STRLEN(param_value);
|
||||
|
||||
// Remove linker value from the provided value if it is not marked as reference value 'Device.XXX.=>XX##'
|
||||
if (len > 7 && DM_STRNCMP(param_value, ROOT_NODE, strlen(ROOT_NODE)) == 0 &&
|
||||
param_value[len - 1] == '#' && param_value[len - 2] == '#') {
|
||||
char *p = DM_STRSTR(param_value, "=>");
|
||||
if (p) *p = 0;
|
||||
}
|
||||
|
||||
if (DM_STRCMP(value, param_value) == 0) {
|
||||
BBF_DEBUG("Requested value (%s) is same as current value (%s)...", param_value, value);
|
||||
if (DM_STRCMP(dmctx->in_value, value) == 0) {
|
||||
BBF_DEBUG("Requested value (%s) is same as current value (%s)...", dmctx->in_value, value);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1972,3 +2080,282 @@ int dm_entry_event(struct dmctx *dmctx)
|
|||
|
||||
return (dmctx->stop) ? err : USP_FAULT_INVALID_PATH;
|
||||
}
|
||||
|
||||
/* **********
|
||||
* get instances data base
|
||||
* **********/
|
||||
typedef struct db_entry {
|
||||
struct list_head list;
|
||||
json_object *json_obj;
|
||||
char obj_name[32];
|
||||
} db_entry_t;
|
||||
|
||||
struct retry_context {
|
||||
json_object *json_obj;
|
||||
struct uloop_timeout retry_timer;
|
||||
char file_path[128];
|
||||
};
|
||||
|
||||
static json_object *find_db_json_obj(struct list_head *registered_db, const char *obj_name)
|
||||
{
|
||||
db_entry_t *db_obj = NULL;
|
||||
|
||||
if (list_empty(registered_db))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(db_obj, registered_db, list) {
|
||||
if (DM_STRCMP(db_obj->obj_name, obj_name) == 0)
|
||||
return db_obj->json_obj;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static json_object *register_new_db_json_obj(struct list_head *registered_db, const char *obj_name)
|
||||
{
|
||||
db_entry_t *db_obj = NULL;
|
||||
|
||||
if (!obj_name) {
|
||||
BBF_ERR("Invalid object name");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
db_obj = (db_entry_t *)calloc(1, sizeof(db_entry_t));
|
||||
if (!db_obj) {
|
||||
BBF_ERR("Failed to allocate memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_add_tail(&db_obj->list, registered_db);
|
||||
|
||||
db_obj->json_obj = json_object_new_object();
|
||||
DM_STRNCPY(db_obj->obj_name, obj_name, sizeof(db_obj->obj_name));
|
||||
|
||||
return db_obj->json_obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a JSON object to a file safely using exclusive locking.
|
||||
*
|
||||
* This function serializes the given `json_object` to the specified file path
|
||||
* using json-c's pretty formatting. It ensures safe concurrent access by
|
||||
* acquiring an exclusive file lock (`LOCK_EX`) before writing, preventing
|
||||
* other processes from reading or writing the file during the operation.
|
||||
*
|
||||
* Key behavior:
|
||||
* - Opens the file for writing (creates it if it does not exist).
|
||||
* - Acquires an exclusive lock (`LOCK_EX`) using `flock()` to ensure
|
||||
* no other process reads or writes during the write.
|
||||
* - Serializes the JSON object using json-c with pretty formatting.
|
||||
* - Ensures that any readers using shared locks (`LOCK_SH`) are blocked
|
||||
* during the write to avoid partial or inconsistent reads.
|
||||
* - Writes the data to the file stream (`FILE*`) derived from the file
|
||||
* descriptor.
|
||||
* - Automatically flushes and closes the file, releasing the lock.
|
||||
*
|
||||
* @param file_path Full path to the JSON file to write.
|
||||
* @param json_obj Pointer to the `json_object` to serialize and store.
|
||||
* @return 0 on success, -1 on failure (file open, locking, or writing error).
|
||||
*
|
||||
* @note Any readers accessing this file should use `flock()` with `LOCK_SH`
|
||||
* to avoid reading partial or inconsistent data while a write is in
|
||||
* progress.
|
||||
*/
|
||||
static int bbfdm_json_object_to_file(const char *file_path, json_object *json_obj)
|
||||
{
|
||||
// Open file for writing (create if it doesn't exist, truncate if it does)
|
||||
int fd = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd == -1) {
|
||||
BBF_ERR("Failed to open file for writing: %s", file_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Acquire exclusive lock to prevent simultaneous writes
|
||||
if (flock(fd, LOCK_EX) == -1) {
|
||||
BBF_ERR("Failed to lock file: %s", file_path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Associate a FILE* stream with the file descriptor
|
||||
FILE *fp = fdopen(fd, "w");
|
||||
if (!fp) {
|
||||
BBF_ERR("fdopen failed on file: %s", file_path);
|
||||
close(fd); // Releases the lock as well
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Serialize JSON object to string
|
||||
const char *json_str = json_object_to_json_string_ext(json_obj, JSON_C_TO_STRING_PRETTY);
|
||||
if (!json_str) {
|
||||
BBF_ERR("Failed to serialize JSON object");
|
||||
fclose(fp); // Closes fd and releases lock
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Write JSON string to file
|
||||
if (fprintf(fp, "%s\n", json_str) < 0) {
|
||||
BBF_ERR("Failed to write JSON to file: %s", file_path);
|
||||
fclose(fp); // Closes fd and releases lock
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Flush FILE* buffer and sync file descriptor to disk
|
||||
if (fflush(fp) != 0 || fsync(fd) != 0) {
|
||||
BBF_ERR("Failed to flush/sync JSON file: %s", file_path);
|
||||
fclose(fp); // Closes fd and releases lock
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Close stream (also closes file descriptor and releases lock)
|
||||
if (fclose(fp) != 0) {
|
||||
BBF_ERR("Failed to close file: %s", file_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void retry_write_cb(struct uloop_timeout *t)
|
||||
{
|
||||
struct retry_context *ctx = container_of(t, struct retry_context, retry_timer);
|
||||
|
||||
if (!ctx || !ctx->json_obj)
|
||||
return;
|
||||
|
||||
int ret = bbfdm_json_object_to_file(ctx->file_path, ctx->json_obj);
|
||||
|
||||
if (ret == 0) {
|
||||
BBF_INFO("Retry write succeeded: %s", ctx->file_path);
|
||||
} else {
|
||||
BBF_ERR("Retry write failed: %s", ctx->file_path);
|
||||
}
|
||||
|
||||
json_object_put(ctx->json_obj);
|
||||
dmfree(ctx);
|
||||
}
|
||||
|
||||
static void write_unregister_db_json_objs(struct list_head *registered_db)
|
||||
{
|
||||
db_entry_t *db_obj = NULL, *tmp = NULL;
|
||||
|
||||
list_for_each_entry_safe(db_obj, tmp, registered_db, list) {
|
||||
|
||||
if (db_obj->json_obj) {
|
||||
char file_path[128] = {0};
|
||||
|
||||
snprintf(file_path, sizeof(file_path), "%s/%s.json", DATA_MODEL_DB_PATH, db_obj->obj_name);
|
||||
|
||||
int ret = bbfdm_json_object_to_file(file_path, db_obj->json_obj);
|
||||
|
||||
if (ret != 0) {
|
||||
struct retry_context *ctx = dmcalloc(1, sizeof(struct retry_context));
|
||||
if (!ctx) {
|
||||
BBF_ERR("Failed to allocate retry context");
|
||||
json_object_put(db_obj->json_obj);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
BBF_ERR("Initial write to file failed: (%s). Scheduling retry in 500ms.", file_path);
|
||||
|
||||
DM_STRNCPY(ctx->file_path, file_path, sizeof(ctx->file_path));
|
||||
ctx->json_obj = db_obj->json_obj;
|
||||
|
||||
ctx->retry_timer.cb = retry_write_cb;
|
||||
uloop_timeout_set(&ctx->retry_timer, 500); // Retry after 500ms
|
||||
|
||||
} else {
|
||||
json_object_put(db_obj->json_obj);
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
list_del(&db_obj->list);
|
||||
FREE(db_obj);
|
||||
}
|
||||
}
|
||||
|
||||
static int mobj_get_references_db(DMOBJECT_ARGS)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void add_path(struct list_head *registered_db, const char *path, const char *value)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
if (!path || !value)
|
||||
return;
|
||||
|
||||
char **parts = strsplit(path, ".", &count);
|
||||
|
||||
if (count < 2)
|
||||
return;
|
||||
|
||||
// Path should be like: Device.X.Y.Z, so file name should use the second level which is X.json
|
||||
json_object *curr = find_db_json_obj(registered_db, parts[1]);
|
||||
if (curr == NULL) {
|
||||
curr = register_new_db_json_obj(registered_db, parts[1]);
|
||||
}
|
||||
|
||||
if (curr == NULL)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
const char *key = parts[i];
|
||||
|
||||
if (i == count - 1) {
|
||||
json_object_object_add(curr, key, json_object_new_string(value));
|
||||
} else {
|
||||
json_object *next = NULL;
|
||||
|
||||
if (!json_object_object_get_ex(curr, key, &next)) {
|
||||
next = json_object_new_object();
|
||||
json_object_object_add(curr, key, next);
|
||||
}
|
||||
curr = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int mparam_get_references_db(DMPARAM_ARGS)
|
||||
{
|
||||
if (node->is_instanceobj == 0)
|
||||
return 0;
|
||||
|
||||
if (leaf->dm_flags & DM_FLAG_LINKER) {
|
||||
char full_param[MAX_DM_PATH] = {0};
|
||||
char *value = dmstrdup("");
|
||||
|
||||
snprintf(full_param, sizeof(full_param), "%s%s", node->current_object, leaf->parameter);
|
||||
|
||||
(leaf->getvalue)(full_param, dmctx, data, instance, &value);
|
||||
|
||||
add_path((struct list_head *)dmctx->addobj_instance, full_param, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_entry_references_db(struct dmctx *ctx)
|
||||
{
|
||||
DMOBJ *root = ctx->dm_entryobj;
|
||||
DMNODE node = {.current_object = ""};
|
||||
LIST_HEAD(registered_db);
|
||||
int err = 0;
|
||||
|
||||
ctx->inparam_isparam = 0;
|
||||
ctx->findparam = 1;
|
||||
ctx->stop = 0;
|
||||
ctx->checkobj = NULL;
|
||||
ctx->checkleaf = NULL;
|
||||
ctx->method_obj = mobj_get_references_db;
|
||||
ctx->method_param = mparam_get_references_db;
|
||||
ctx->addobj_instance = (void *)®istered_db; // This argument is used as internal variable to pass the address of registred DB list
|
||||
|
||||
err = dm_browse(ctx, &node, root, NULL, NULL);
|
||||
|
||||
write_unregister_db_json_objs(®istered_db);
|
||||
|
||||
return (ctx->findparam == 0) ? err : 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,17 @@
|
|||
#include "dmmem.h"
|
||||
#include "dmapi.h"
|
||||
|
||||
int get_number_of_entries(struct dmctx *ctx, void *data, char *instance, int (*browseinstobj)(struct dmctx *ctx, struct dmnode *node, void *data, char *instance));
|
||||
int get_number_of_entries(struct dmctx *ctx, void *data, char *instance, int (*browseinstobj)(struct dmctx *ctx, struct dmnode *node, void *data, char *instance)); // To be removed later!!!!!!!!!!!!
|
||||
|
||||
char *handle_instance(struct dmctx *dmctx, DMNODE *parent_node, struct uci_section *s, const char *inst_opt, const char *alias_opt);
|
||||
char *handle_instance_without_section(struct dmctx *dmctx, DMNODE *parent_node, int inst_nbr);
|
||||
|
||||
struct uci_section *create_dmmap_obj(struct dmctx *dmctx, unsigned char instance_level,
|
||||
const char *obj_file, const char *obj_name, struct uci_section *config_sec,
|
||||
char **instance);
|
||||
int generic_browse(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance);
|
||||
char *uci_handle_instance(struct dmctx *dmctx, DMNODE *parent_node, struct dm_data *data);
|
||||
|
||||
int get_empty(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value);
|
||||
|
||||
void fill_blob_param(struct blob_buf *bb, const char *path, const char *data, const char *type, uint32_t dm_flags);
|
||||
|
|
@ -47,6 +55,7 @@ int dm_entry_set_value(struct dmctx *dmctx);
|
|||
int dm_entry_object_exists(struct dmctx *dmctx);
|
||||
int dm_entry_operate(struct dmctx *dmctx);
|
||||
int dm_entry_event(struct dmctx *dmctx);
|
||||
int dm_entry_references_db(struct dmctx *ctx);
|
||||
int dm_entry_get_reference_param(struct dmctx *dmctx);
|
||||
int dm_entry_get_reference_value(struct dmctx *dmctx);
|
||||
int dm_link_inst_obj(struct dmctx *dmctx, DMNODE *parent_node, void *data, char *instance);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@
|
|||
|
||||
#include "dmcommon.h"
|
||||
|
||||
static struct dmctx *g_dm_ctx = NULL;
|
||||
|
||||
char *DiagnosticsState[] = {"None", "Requested", "Canceled", "Complete", "Error", NULL};
|
||||
|
||||
char *IPv4Address[] = {"^$", "^((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])$", NULL};
|
||||
|
|
@ -314,6 +316,23 @@ void free_dmmap_config_dup_list(struct list_head *dup_list)
|
|||
/*
|
||||
* Function allows to synchronize config section with dmmap config
|
||||
*/
|
||||
|
||||
struct uci_section *get_config_section_from_dmmap_section_name(const char *config_sec_name)
|
||||
{
|
||||
int len = DM_STRLEN(config_sec_name);
|
||||
|
||||
if (len == 0)
|
||||
return NULL;
|
||||
|
||||
char tmp[len + 1];
|
||||
snprintf(tmp, sizeof(tmp), "%s", config_sec_name);
|
||||
|
||||
char *p = strchr(tmp, '.');
|
||||
if (p) *p = 0;
|
||||
|
||||
return dmuci_get_section(tmp, p ? p + 1 : "");
|
||||
}
|
||||
|
||||
struct uci_section *get_origin_section_from_config(const char *package, const char *section_type, const char *orig_section_name)
|
||||
{
|
||||
struct uci_section *s = NULL;
|
||||
|
|
@ -413,12 +432,22 @@ void synchronize_specific_config_sections_with_dmmap(const char *package, const
|
|||
char *v = NULL;
|
||||
|
||||
uci_foreach_sections(package, section_type, s) {
|
||||
char sec_name[64] = {0};
|
||||
|
||||
snprintf(sec_name, sizeof(sec_name), "%s_%s", section_type, section_name(s));
|
||||
|
||||
/*
|
||||
* create/update corresponding dmmap section that have same config_section link and using param_value_array
|
||||
*/
|
||||
if ((dmmap_sect = get_dup_section_in_dmmap(dmmap_package, section_type, section_name(s))) == NULL) {
|
||||
dmuci_add_section_bbfdm(dmmap_package, section_type, &dmmap_sect);
|
||||
dmuci_rename_section_by_section(dmmap_sect, sec_name);
|
||||
dmuci_set_value_by_section_bbfdm(dmmap_sect, "section_name", section_name(s));
|
||||
} else {
|
||||
const char *reg_exp = "^cfg[0-9a-fA-F]{6}$";
|
||||
if (match(section_name(dmmap_sect), reg_exp, 0, NULL) == true) {
|
||||
dmuci_rename_section_by_section(dmmap_sect, sec_name);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2177,9 +2206,9 @@ int get_proto_type(const char *proto)
|
|||
int type = BBFDM_BOTH;
|
||||
|
||||
if (proto) {
|
||||
if (is_str_eq("cwmp", proto))
|
||||
if (strcmp("cwmp", proto) == 0)
|
||||
type = BBFDM_CWMP;
|
||||
else if (is_str_eq("usp", proto))
|
||||
else if (strcmp("usp", proto) == 0)
|
||||
type = BBFDM_USP;
|
||||
else
|
||||
type = BBFDM_BOTH;
|
||||
|
|
@ -2188,11 +2217,89 @@ int get_proto_type(const char *proto)
|
|||
return type;
|
||||
}
|
||||
|
||||
bool is_str_eq(const char *s1, const char *s2)
|
||||
void dm_init_modified_uci(struct dmctx *ctx)
|
||||
{
|
||||
if (strcmp(s1, s2) == 0)
|
||||
return true;
|
||||
if (ctx == NULL) {
|
||||
BBFDM_DEBUG("dmctx is NULL!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
return false;
|
||||
ctx->modified_uci_head = calloc(1, sizeof(struct list_head));
|
||||
|
||||
// Check if memory allocation was successful
|
||||
if (ctx->modified_uci_head == NULL) {
|
||||
BBFDM_INFO("failed to allocate memory!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the list head
|
||||
INIT_LIST_HEAD(ctx->modified_uci_head);
|
||||
}
|
||||
|
||||
void dm_clean_modified_uci(struct dmctx *ctx)
|
||||
{
|
||||
if (ctx == NULL || ctx->modified_uci_head == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct dm_modified_uci *dmm = NULL, *tmp = NULL;
|
||||
|
||||
list_for_each_entry_safe(dmm, tmp, ctx->modified_uci_head, list) {
|
||||
list_del(&dmm->list);
|
||||
FREE(dmm);
|
||||
}
|
||||
|
||||
FREE(ctx->modified_uci_head);
|
||||
}
|
||||
|
||||
void add_list_modified_uci(struct dmctx *ctx, const char *dir, const char *file)
|
||||
{
|
||||
if (ctx == NULL) {
|
||||
BBFDM_DEBUG("dmctx is NULL!");
|
||||
return;
|
||||
}
|
||||
|
||||
struct list_head *head = ctx->modified_uci_head;
|
||||
if (head == NULL) {
|
||||
BBFDM_INFO("head is NULL!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (DM_STRLEN(dir) == 0 || DM_STRLEN(file) == 0) {
|
||||
BBFDM_DEBUG("dir name or file name is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
char uci_file[128] = {0};
|
||||
snprintf(uci_file, sizeof(uci_file), "%s%s", dir, file);
|
||||
|
||||
struct dm_modified_uci *m;
|
||||
list_for_each_entry(m, head, list) {
|
||||
if (DM_STRCMP(m->uci_file, uci_file) == 0) {
|
||||
// config file already added in list
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m = malloc(sizeof(struct dm_modified_uci));
|
||||
if (m == NULL) {
|
||||
BBFDM_INFO("memory allocation failed");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(m->uci_file, sizeof(m->uci_file), "%s", uci_file);
|
||||
|
||||
list_add(&m->list, head);
|
||||
}
|
||||
|
||||
/* !! TO BE REMOVED LATER START */
|
||||
struct dmctx *get_bbfdm_global_dmctx(void)
|
||||
{
|
||||
return g_dm_ctx;
|
||||
}
|
||||
|
||||
void set_bbfdm_global_dmctx(struct dmctx *ctx)
|
||||
{
|
||||
g_dm_ctx = ctx;
|
||||
}
|
||||
/* !! TO BE REMOVED LATER END */
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
#include <sys/klog.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/file.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <ifaddrs.h>
|
||||
|
|
@ -88,6 +89,7 @@ extern char *IPv6Prefix[];
|
|||
#define FILE_URI "file://"
|
||||
#define FILE_LOCALHOST_URI "file://localhost"
|
||||
#define BBFDM_SCRIPTS_PATH "/usr/share/bbfdm/scripts"
|
||||
#define DATA_MODEL_DB_PATH "/var/run/bbfdm"
|
||||
|
||||
#define DM_ASSERT(X, Y) \
|
||||
do { \
|
||||
|
|
@ -127,6 +129,11 @@ struct dhcp_options_type {
|
|||
uint8_t len;
|
||||
};
|
||||
|
||||
struct dm_modified_uci {
|
||||
struct list_head list;
|
||||
char uci_file[128];
|
||||
};
|
||||
|
||||
pid_t get_pid(const char *pname);
|
||||
int compare_strings(const void *a, const void *b);
|
||||
char *get_uptime(void);
|
||||
|
|
@ -171,6 +178,7 @@ bool value_exists_in_uci_list(struct uci_list *list, const char *value);
|
|||
bool value_exits_in_str_list(const char *str_list, const char *delimitor, const char *str);
|
||||
char *add_str_to_str_list(const char *str_list, const char *delimitor, const char *str);
|
||||
char *remove_str_from_str_list(const char *str_list, const char *delimitor, const char *str);
|
||||
struct uci_section *get_config_section_from_dmmap_section_name(const char *config_sec_name);
|
||||
struct uci_section *get_origin_section_from_config(const char *package, const char *section_type, const char *orig_section_name);
|
||||
struct uci_section *get_origin_section_from_dmmap(const char *package, const char *section_type, const char *orig_section_name);
|
||||
struct uci_section *get_dup_section_in_dmmap(const char *dmmap_package, const char *section_type, const char *orig_section_name);
|
||||
|
|
@ -210,6 +218,7 @@ int bbfdm_validate_string_list(struct dmctx *ctx, const char *value, int min_ite
|
|||
int bbfdm_validate_hexBinary_list(struct dmctx *ctx, const char *value, int min_item, int max_item, int max_size, struct range_args r_args[], int r_args_size);
|
||||
int bbf_get_alias(struct dmctx *ctx, struct uci_section *s, const char *option_name, const char *instance, char **value);
|
||||
int bbf_set_alias(struct dmctx *ctx, struct uci_section *s, const char *option_name, const char *instance, const char *value);
|
||||
char *bbfdm_resolve_external_reference_via_json(struct dmctx *ctx, const char *linker_path, const char *linker_value);
|
||||
int bbfdm_get_references(struct dmctx *ctx, int match_action, const char *base_path, const char *key_name, char *key_value, char *out, size_t out_len);
|
||||
int _bbfdm_get_references(struct dmctx *ctx, const char *base_path, const char *key_name, char *key_value, char **value);
|
||||
int bbfdm_get_reference_linker(struct dmctx *ctx, char *reference_path, struct dm_reference *reference_args);
|
||||
|
|
@ -237,6 +246,11 @@ char *diagnostics_get_interface_name(struct dmctx *ctx, const char *value);
|
|||
long download_file(char *file_path, const char *url, const char *username, const char *password);
|
||||
long upload_file(const char *file_path, const char *url, const char *username, const char *password);
|
||||
int get_proto_type(const char *proto);
|
||||
bool is_str_eq(const char *s1, const char *s2);
|
||||
|
||||
void dm_init_modified_uci(struct dmctx *ctx);
|
||||
void dm_clean_modified_uci(struct dmctx *ctx);
|
||||
void add_list_modified_uci(struct dmctx *ctx, const char *dir, const char *file);
|
||||
|
||||
struct dmctx *get_bbfdm_global_dmctx(); // !! TO BE REMOVED LATER
|
||||
void set_bbfdm_global_dmctx(struct dmctx *ctx); // !! TO BE REMOVED LATER
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ void bbf_ctx_init(struct dmctx *ctx, DMOBJ *tEntryObj)
|
|||
dm_init_mem(ctx);
|
||||
dm_uci_init(ctx);
|
||||
dm_ubus_init(ctx);
|
||||
dm_init_modified_uci(ctx);
|
||||
}
|
||||
|
||||
void bbf_ctx_clean(struct dmctx *ctx)
|
||||
|
|
@ -59,6 +60,7 @@ void bbf_ctx_clean(struct dmctx *ctx)
|
|||
dm_uci_exit(ctx);
|
||||
dm_clean_mem(ctx);
|
||||
dm_ubus_free(ctx);
|
||||
dm_clean_modified_uci(ctx);
|
||||
}
|
||||
|
||||
void bbf_ctx_init_sub(struct dmctx *ctx, DMOBJ *tEntryObj)
|
||||
|
|
@ -117,7 +119,7 @@ int bbf_fault_map(struct dmctx *ctx, int fault)
|
|||
out_fault = USP_FAULT_INVALID_TYPE;
|
||||
break;
|
||||
case FAULT_9007:
|
||||
bbfdm_set_fault_message(ctx, "%s", get_fault_message(USP_FAULT_PARAM_READ_ONLY));
|
||||
bbfdm_set_fault_message(ctx, "%s", get_fault_message(USP_FAULT_INVALID_VALUE));
|
||||
out_fault = USP_FAULT_INVALID_VALUE;
|
||||
break;
|
||||
case FAULT_9008:
|
||||
|
|
@ -201,6 +203,8 @@ int bbf_entry_method(struct dmctx *ctx, int cmd)
|
|||
return bbf_fault_map(ctx, FAULT_9005);
|
||||
}
|
||||
|
||||
set_bbfdm_global_dmctx(ctx); // !! TO BE REMOVED LATER
|
||||
|
||||
switch(cmd) {
|
||||
case BBF_GET_VALUE:
|
||||
fault = dm_entry_get_value(ctx);
|
||||
|
|
@ -229,20 +233,29 @@ int bbf_entry_method(struct dmctx *ctx, int cmd)
|
|||
case BBF_EVENT:
|
||||
fault = dm_entry_event(ctx);
|
||||
break;
|
||||
case BBF_REFERENCES_DB:
|
||||
fault = dm_entry_references_db(ctx);
|
||||
break;
|
||||
}
|
||||
|
||||
set_bbfdm_global_dmctx(NULL); // !! TO BE REMOVED LATER
|
||||
|
||||
return bbf_fault_map(ctx, fault);
|
||||
}
|
||||
|
||||
void bbf_global_init(DMOBJ *dm_entryobj, const char *plugin_path)
|
||||
{
|
||||
bbfdm_ensure_folder_exists(DATA_MODEL_DB_PATH);
|
||||
|
||||
dm_dynamic_initmem(&global_memhead);
|
||||
dm_ubus_cache_init();
|
||||
load_plugins(dm_entryobj, plugin_path);
|
||||
}
|
||||
|
||||
void bbf_global_clean(DMOBJ *dm_entryobj)
|
||||
{
|
||||
free_plugins(dm_entryobj);
|
||||
dm_ubus_cache_free();
|
||||
dm_dynamic_cleanmem(&global_memhead);
|
||||
}
|
||||
|
||||
|
|
@ -282,6 +295,7 @@ int adm_entry_get_reference_param(struct dmctx *ctx, char *param, char *linker,
|
|||
dmctx.inparam_isparam = 1;
|
||||
dmctx.in_param = param;
|
||||
dmctx.linker = linker;
|
||||
dmctx.dm_type = ctx->dm_type;
|
||||
|
||||
dm_entry_get_reference_param(&dmctx);
|
||||
|
||||
|
|
@ -305,6 +319,7 @@ int adm_entry_get_reference_value(struct dmctx *ctx, const char *param, char **v
|
|||
bbf_ctx_init_sub(&dmctx, ctx->dm_entryobj);
|
||||
|
||||
dmctx.in_param = linker;
|
||||
dmctx.dm_type = ctx->dm_type;
|
||||
|
||||
dm_entry_get_reference_value(&dmctx);
|
||||
|
||||
|
|
@ -329,6 +344,7 @@ bool adm_entry_object_exists(struct dmctx *ctx, const char *param) // To be remo
|
|||
blob_buf_init(&dmctx.bb, 0);
|
||||
|
||||
dmctx.in_param = linker;
|
||||
dmctx.dm_type = ctx->dm_type;
|
||||
|
||||
dm_entry_object_exists(&dmctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,13 +17,21 @@
|
|||
|
||||
#define UBUS_TIMEOUT 5000
|
||||
#define UBUS_MAX_BLOCK_TIME (120000) // 2 min
|
||||
#define UBUS_MAX_CONSECUTIVE_TIMEOUTS 10
|
||||
|
||||
static LIST_HEAD(dmubus_cache);
|
||||
static LIST_HEAD(dm_ubus_cache);
|
||||
|
||||
struct dm_ubus_cache_entry {
|
||||
struct list_head list;
|
||||
char *obj;
|
||||
char *method;
|
||||
struct blob_attr *attr;
|
||||
json_object *data;
|
||||
int timeout;
|
||||
unsigned hash;
|
||||
unsigned int consecutive_timeouts; // Tracks successive timeouts
|
||||
bool is_blacklisted; // Marks if the ubus is blacklisted
|
||||
bool is_executed; // Marks if the ubus is executed
|
||||
};
|
||||
|
||||
struct dm_ubus_hash_req {
|
||||
|
|
@ -32,107 +40,400 @@ struct dm_ubus_hash_req {
|
|||
struct blob_attr *attr;
|
||||
};
|
||||
|
||||
|
||||
struct ubus_struct {
|
||||
const char *ubus_method_name;
|
||||
bool ubus_method_exists;
|
||||
};
|
||||
|
||||
static struct ubus_context *ubus_ctx = NULL;
|
||||
static json_object *json_res = NULL;
|
||||
static struct ubus_context *g_dm_ubus_ctx = NULL;
|
||||
|
||||
static const struct dm_ubus_cache_entry * dm_ubus_cache_lookup(unsigned hash);
|
||||
static const char *dm_ubus_str_error[__UBUS_STATUS_LAST] = {
|
||||
[UBUS_STATUS_OK] = "Success",
|
||||
[UBUS_STATUS_INVALID_COMMAND] = "Invalid command",
|
||||
[UBUS_STATUS_INVALID_ARGUMENT] = "Invalid argument",
|
||||
[UBUS_STATUS_METHOD_NOT_FOUND] = "Method not found",
|
||||
[UBUS_STATUS_NOT_FOUND] = "Not found",
|
||||
[UBUS_STATUS_NO_DATA] = "No response",
|
||||
[UBUS_STATUS_PERMISSION_DENIED] = "Permission denied",
|
||||
[UBUS_STATUS_TIMEOUT] = "Request timed out",
|
||||
[UBUS_STATUS_NOT_SUPPORTED] = "Operation not supported",
|
||||
[UBUS_STATUS_UNKNOWN_ERROR] = "Unknown error",
|
||||
[UBUS_STATUS_CONNECTION_FAILED] = "Connection failed",
|
||||
[UBUS_STATUS_NO_MEMORY] = "Out of memory",
|
||||
[UBUS_STATUS_PARSE_ERROR] = "Parsing message data failed",
|
||||
[UBUS_STATUS_SYSTEM_ERROR] = "System error",
|
||||
};
|
||||
|
||||
static void prepare_blob_message(struct blob_buf *b, const struct ubus_arg u_args[], int u_args_size)
|
||||
/* Based on an efficient hash function published by D. J. Bernstein
|
||||
*/
|
||||
static unsigned int djbhash(unsigned hash, const char *data, unsigned len)
|
||||
{
|
||||
if (!b)
|
||||
return;
|
||||
unsigned i;
|
||||
|
||||
blob_buf_init(b, 0);
|
||||
for (int i = 0; i < u_args_size; i++) {
|
||||
if (u_args[i].type == Integer) {
|
||||
blobmsg_add_u32(b, u_args[i].key, DM_STRTOL(u_args[i].val));
|
||||
} else if (u_args[i].type == Boolean) {
|
||||
bool val = false;
|
||||
string_to_bool(u_args[i].val, &val);
|
||||
blobmsg_add_u8(b, u_args[i].key, val);
|
||||
} else if (u_args[i].type == Table) {
|
||||
json_object *jobj = json_tokener_parse(u_args[i].val);
|
||||
blobmsg_add_json_element(b, u_args[i].key, jobj);
|
||||
json_object_put(jobj);
|
||||
for (i = 0; i < len; i++)
|
||||
hash = ((hash << 5) + hash) + data[i];
|
||||
|
||||
return (hash & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
static unsigned dm_ubus_req_hash_from_blob(const struct dm_ubus_hash_req *req)
|
||||
{
|
||||
unsigned hash = 5381;
|
||||
|
||||
if (!req || !req->obj || !req->method)
|
||||
return hash;
|
||||
|
||||
hash = djbhash(hash, req->obj, DM_STRLEN(req->obj));
|
||||
hash = djbhash(hash, req->method, DM_STRLEN(req->method));
|
||||
|
||||
char *jmsg = req->attr ? blobmsg_format_json(req->attr, true) : NULL;
|
||||
if (!jmsg)
|
||||
return hash;
|
||||
|
||||
hash = djbhash(hash, jmsg, DM_STRLEN(jmsg));
|
||||
free(jmsg);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static struct dm_ubus_cache_entry *dm_ubus_cache_lookup(unsigned hash)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry_match = NULL;
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
|
||||
list_for_each_entry(entry, &dm_ubus_cache, list) {
|
||||
if (entry->hash == hash) {
|
||||
entry_match = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return entry_match;
|
||||
}
|
||||
|
||||
static struct dm_ubus_cache_entry *dm_ubus_cache_entry_new(unsigned hash, const char *obj, const char *method, int timeout, struct blob_attr *attr)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
|
||||
entry = (struct dm_ubus_cache_entry *)calloc(1, sizeof(struct dm_ubus_cache_entry));
|
||||
if (!entry) {
|
||||
BBF_ERR("Failed to allocate memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_add_tail(&entry->list, &dm_ubus_cache);
|
||||
|
||||
entry->hash = hash;
|
||||
entry->obj = strdup(obj);
|
||||
entry->method = strdup(method);
|
||||
entry->timeout = timeout;
|
||||
entry->consecutive_timeouts = 0;
|
||||
entry->is_blacklisted = false;
|
||||
|
||||
size_t blob_data_len = attr ? blob_raw_len(attr) : 0;
|
||||
|
||||
if (blob_data_len) {
|
||||
entry->attr = (struct blob_attr *)calloc(1, blob_data_len);
|
||||
if (entry->attr) {
|
||||
memcpy(entry->attr, attr, blob_data_len);
|
||||
} else {
|
||||
blobmsg_add_string(b, u_args[i].key, u_args[i].val);
|
||||
BBF_ERR("Failed to allocate memory");
|
||||
}
|
||||
} else {
|
||||
entry->attr = NULL;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void dm_ubus_cache_entry_free(void)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
|
||||
list_for_each_entry(entry, &dm_ubus_cache, list) {
|
||||
entry->is_executed = false;
|
||||
|
||||
if (entry->data) {
|
||||
json_object_put(entry->data);
|
||||
entry->data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg)
|
||||
static void dm_ubus_data_handler(struct ubus_request *req, int type, struct blob_attr *msg)
|
||||
{
|
||||
char *str = NULL;
|
||||
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
str = blobmsg_format_json_indent(msg, true, -1);
|
||||
if (!str) {
|
||||
json_res = NULL;
|
||||
return;
|
||||
}
|
||||
if (req && req->priv) {
|
||||
json_object **req_res = (json_object **)req->priv;
|
||||
|
||||
json_res = json_tokener_parse(str);
|
||||
free((char *)str); //MEM should be free and not dmfree
|
||||
char *str = blobmsg_format_json_indent(msg, true, -1);
|
||||
if (!str) {
|
||||
req_res = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
*req_res = json_tokener_parse(str);
|
||||
|
||||
free((char *)str);
|
||||
}
|
||||
}
|
||||
|
||||
static int __dm_ubus_call_internal(const char *obj, const char *method, int timeout, struct blob_attr *attr)
|
||||
static int dm_ubus_call_sync(struct ubus_context *ubus_ctx, const char *obj, const char *method, int timeout, struct blob_attr *attr, json_object **req_res)
|
||||
{
|
||||
uint32_t id = 0;
|
||||
|
||||
json_res = NULL;
|
||||
if (req_res) *req_res = NULL;
|
||||
|
||||
if (ubus_ctx == NULL) {
|
||||
BBF_ERR("UBUS context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBF_ERR("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
if (!obj || !method || !attr) {
|
||||
BBF_ERR("obj or method or attr should not be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ubus_invoke(ubus_ctx, id, method, attr, receive_call_result_data, NULL, timeout);
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBF_WARNING("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = ubus_invoke(ubus_ctx, id, method, attr, dm_ubus_data_handler, req_res ? (void *)req_res : NULL, timeout);
|
||||
|
||||
if (err != 0) {
|
||||
const char *err_msg = (err >= 0 && err < __UBUS_STATUS_LAST) ? dm_ubus_str_error[err] : "Unknown error";
|
||||
BBF_ERR("UBUS invoke failed [object: %s, method: %s, timeout: %d ms] with error (%s:%d)",
|
||||
obj, method, timeout, err_msg, err);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __dm_ubus_call(const char *obj, const char *method, struct blob_attr *attr)
|
||||
static void dm_ubus_data_handler_entry(struct ubus_request *req, int type, struct blob_attr *msg)
|
||||
{
|
||||
return __dm_ubus_call_internal(obj, method, UBUS_TIMEOUT, attr);
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
char *str = NULL;
|
||||
|
||||
if (!msg || !req || !req->priv)
|
||||
return;
|
||||
|
||||
entry = (struct dm_ubus_cache_entry *)req->priv;
|
||||
|
||||
str = blobmsg_format_json_indent(msg, true, -1);
|
||||
if (!str) {
|
||||
entry->data = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
entry->data = json_tokener_parse(str);
|
||||
|
||||
free((char *)str);
|
||||
}
|
||||
|
||||
static int __ubus_call_blocking(const char *obj, const char *method, struct blob_attr *attr)
|
||||
static int __dm_ubus_call_sync_entry(struct ubus_context *ubus_ctx, struct dm_ubus_cache_entry *entry, const char *obj, const char *method, int timeout, struct blob_attr *attr)
|
||||
{
|
||||
return __dm_ubus_call_internal(obj, method, UBUS_MAX_BLOCK_TIME, attr);
|
||||
uint32_t id = 0;
|
||||
|
||||
if (entry == NULL) {
|
||||
BBF_ERR("UBUS entry should not be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
entry->data = NULL;
|
||||
entry->is_executed = true;
|
||||
|
||||
if (ubus_ctx == NULL) {
|
||||
BBF_ERR("UBUS context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!obj || !method || !attr) {
|
||||
BBF_ERR("obj or method or attr should not be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBF_WARNING("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int err = ubus_invoke(ubus_ctx, id, method, attr, dm_ubus_data_handler_entry, (void *)entry, timeout);
|
||||
|
||||
if (err != 0) {
|
||||
const char *err_msg = (err >= 0 && err < __UBUS_STATUS_LAST) ? dm_ubus_str_error[err] : "Unknown error";
|
||||
BBF_WARNING("UBUS invoke failed [object: %s, method: %s, timeout: %d ms] with error (%s:%d)",
|
||||
obj, method, timeout, err_msg, err);
|
||||
|
||||
if (err == UBUS_STATUS_TIMEOUT) {
|
||||
entry->consecutive_timeouts++;
|
||||
if (entry->consecutive_timeouts >= UBUS_MAX_CONSECUTIVE_TIMEOUTS) {
|
||||
entry->is_blacklisted = true;
|
||||
BBF_ERR("UBUS [object: %s, method: %s] has been blacklisted due to repeated timeouts", obj, method);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
entry->consecutive_timeouts = 0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dm_ubus_call_sync_entry(struct ubus_context *ubus_ctx, const char *obj, const char *method, int timeout, struct blob_attr *attr, json_object **req_res)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (!obj || !method || !attr) {
|
||||
BBF_ERR("obj or method or attr should not be NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct dm_ubus_hash_req hash_req = {
|
||||
.obj = obj,
|
||||
.method = method,
|
||||
.attr = attr
|
||||
};
|
||||
|
||||
const unsigned hash = dm_ubus_req_hash_from_blob(&hash_req);
|
||||
struct dm_ubus_cache_entry *entry = dm_ubus_cache_lookup(hash);
|
||||
|
||||
if (entry && entry->is_blacklisted) {
|
||||
if (req_res) *req_res = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
if (entry->is_executed == false) {
|
||||
err = __dm_ubus_call_sync_entry(ubus_ctx, entry, obj, method, timeout, attr);
|
||||
}
|
||||
|
||||
if (req_res) *req_res = entry->data;
|
||||
} else {
|
||||
struct dm_ubus_cache_entry *new_entry = dm_ubus_cache_entry_new(hash, obj, method, timeout, attr);
|
||||
if (new_entry == NULL) {
|
||||
if (req_res) *req_res = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = __dm_ubus_call_sync_entry(ubus_ctx, new_entry, obj, method, timeout, attr);
|
||||
if (req_res) *req_res = new_entry->data;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __dmubus_call(const char *obj, const char *method, int timeout,
|
||||
struct ubus_arg u_args[], int u_args_size, bool save_data, json_object **req_res)
|
||||
{
|
||||
struct blob_buf bb = {0};
|
||||
int rc = 0;
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
for (int i = 0; i < u_args_size; i++) {
|
||||
if (u_args[i].type == Integer) {
|
||||
blobmsg_add_u32(&bb, u_args[i].key, DM_STRTOL(u_args[i].val));
|
||||
} else if (u_args[i].type == Boolean) {
|
||||
bool val = false;
|
||||
string_to_bool(u_args[i].val, &val);
|
||||
blobmsg_add_u8(&bb, u_args[i].key, val);
|
||||
} else if (u_args[i].type == Table) {
|
||||
json_object *jobj = json_tokener_parse(u_args[i].val);
|
||||
blobmsg_add_json_element(&bb, u_args[i].key, jobj);
|
||||
json_object_put(jobj);
|
||||
} else {
|
||||
blobmsg_add_string(&bb, u_args[i].key, u_args[i].val);
|
||||
}
|
||||
}
|
||||
|
||||
if (save_data)
|
||||
rc = dm_ubus_call_sync_entry(g_dm_ubus_ctx, obj, method, timeout, bb.head, req_res);
|
||||
else
|
||||
rc = dm_ubus_call_sync(g_dm_ubus_ctx, obj, method, timeout, bb.head, req_res);
|
||||
|
||||
blob_buf_free(&bb);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dmubus_call(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res)
|
||||
{
|
||||
return __dmubus_call(obj, method, UBUS_TIMEOUT, u_args, u_args_size, true, req_res);
|
||||
}
|
||||
|
||||
int dmubus_call_timeout(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, int timeout, json_object **req_res)
|
||||
{
|
||||
return __dmubus_call(obj, method, timeout, u_args, u_args_size, false, req_res);
|
||||
}
|
||||
|
||||
int dmubus_call_blocking(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res)
|
||||
{
|
||||
return __dmubus_call(obj, method, UBUS_MAX_BLOCK_TIME, u_args, u_args_size, false, req_res);
|
||||
}
|
||||
|
||||
int dmubus_call_set(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size)
|
||||
{
|
||||
struct blob_buf b;
|
||||
return __dmubus_call(obj, method, UBUS_TIMEOUT, u_args, u_args_size, false, NULL);
|
||||
}
|
||||
|
||||
memset(&b, 0, sizeof(struct blob_buf));
|
||||
prepare_blob_message(&b, u_args, u_args_size);
|
||||
static int __dmubus_call_blob(const char *obj, const char *method, int timeout,
|
||||
json_object *json_obj, bool save_data, json_object **resp)
|
||||
{
|
||||
struct blob_buf bb = {0};
|
||||
int rc = 0;
|
||||
|
||||
int rc = __dm_ubus_call(obj, method, b.head);
|
||||
if (resp) *resp = NULL;
|
||||
|
||||
if (json_res != NULL) {
|
||||
json_object_put(json_res);
|
||||
json_res = NULL;
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
if (json_obj != NULL) {
|
||||
if (!blobmsg_add_object(&bb, json_obj)) {
|
||||
blob_buf_free(&bb);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
blob_buf_free(&b);
|
||||
if (save_data)
|
||||
rc = dm_ubus_call_sync_entry(g_dm_ubus_ctx, obj, method, timeout, bb.head, resp);
|
||||
else
|
||||
rc = dm_ubus_call_sync(g_dm_ubus_ctx, obj, method, timeout, bb.head, resp);
|
||||
|
||||
blob_buf_free(&bb);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dmubus_listen_timeout(struct uloop_timeout *timeout)
|
||||
int dmubus_call_blob(const char *obj, const char *method, json_object *value, json_object **resp)
|
||||
{
|
||||
uloop_end();
|
||||
return __dmubus_call_blob(obj, method, UBUS_TIMEOUT, value, true, resp);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_blocking(const char *obj, const char *method, json_object *value, json_object **resp)
|
||||
{
|
||||
return __dmubus_call_blob(obj, method, UBUS_MAX_BLOCK_TIME, value, false, resp);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_set(const char *obj, const char *method, json_object *value)
|
||||
{
|
||||
return __dmubus_call_blob(obj, method, UBUS_TIMEOUT, value, false, NULL);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_msg(const char *obj, const char *method, struct blob_buf *data, json_object **resp)
|
||||
{
|
||||
return dm_ubus_call_sync(g_dm_ubus_ctx, obj, method, UBUS_TIMEOUT, data->head, resp);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_msg_timeout(const char *obj, const char *method, struct blob_buf *data, int timeout)
|
||||
{
|
||||
return dm_ubus_call_sync(g_dm_ubus_ctx, obj, method, timeout, data->head, NULL);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_msg_set(const char *obj, const char *method, struct blob_buf *data)
|
||||
{
|
||||
return dm_ubus_call_sync(g_dm_ubus_ctx, obj, method, UBUS_TIMEOUT, data->head, NULL);
|
||||
}
|
||||
|
||||
static void _bbfdm_task_callback(struct uloop_timeout *t)
|
||||
|
|
@ -238,6 +539,11 @@ int bbfdm_task_fork(bbfdm_task_callback_t taskcb, bbfdm_task_callback_t finishcb
|
|||
err_out:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void dmubus_listen_timeout(struct uloop_timeout *timeout)
|
||||
{
|
||||
uloop_end();
|
||||
}
|
||||
/*******************************************************************************
|
||||
**
|
||||
** dmubus_wait_for_event
|
||||
|
|
@ -300,231 +606,6 @@ end:
|
|||
return;
|
||||
}
|
||||
|
||||
static inline json_object *ubus_call_req(const char *obj, const char *method, struct blob_attr *attr)
|
||||
{
|
||||
__dm_ubus_call(obj, method, attr);
|
||||
return json_res;
|
||||
}
|
||||
|
||||
static int dmubus_call_blob_internal(const char *obj, const char *method, json_object *value, int timeout, json_object **resp)
|
||||
{
|
||||
uint32_t id;
|
||||
struct blob_buf blob;
|
||||
int rc = -1;
|
||||
|
||||
json_res = NULL;
|
||||
if (resp) *resp = NULL;
|
||||
|
||||
if (ubus_ctx == NULL) {
|
||||
BBF_ERR("UBUS context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(&blob, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&blob, 0);
|
||||
|
||||
if (value != NULL) {
|
||||
if (!blobmsg_add_object(&blob, value)) {
|
||||
blob_buf_free(&blob);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBF_ERR("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
blob_buf_free(&blob);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ubus_invoke(ubus_ctx, id, method, blob.head, receive_call_result_data, NULL, timeout);
|
||||
|
||||
if (resp) *resp = json_res;
|
||||
blob_buf_free(&blob);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dmubus_call_blob(const char *obj, const char *method, json_object *value, json_object **resp)
|
||||
{
|
||||
return dmubus_call_blob_internal(obj, method, value, UBUS_TIMEOUT, resp);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_blocking(const char *obj, const char *method, json_object *value, json_object **resp)
|
||||
{
|
||||
return dmubus_call_blob_internal(obj, method, value, UBUS_MAX_BLOCK_TIME, resp);
|
||||
}
|
||||
|
||||
int dmubus_call_blob_set(const char *obj, const char *method, json_object *value)
|
||||
{
|
||||
int rc = dmubus_call_blob_internal(obj, method, value, UBUS_TIMEOUT, NULL);
|
||||
|
||||
if (json_res != NULL) {
|
||||
json_object_put(json_res);
|
||||
json_res = NULL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dmubus_call_blob_msg_internal(const char *obj, const char *method, struct blob_buf *data, int timeout, json_object **resp)
|
||||
{
|
||||
uint32_t id = 0;
|
||||
int rc;
|
||||
|
||||
json_res = NULL;
|
||||
|
||||
if (resp)
|
||||
*resp = NULL;
|
||||
|
||||
if (ubus_ctx == NULL) {
|
||||
BBF_ERR("UBUS context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBF_ERR("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ubus_invoke(ubus_ctx, id, method, data->head, receive_call_result_data, NULL, timeout);
|
||||
|
||||
if (resp)
|
||||
*resp = json_res;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dmubus_call_blob_msg_set(const char *obj, const char *method, struct blob_buf *data)
|
||||
{
|
||||
int rc = dmubus_call_blob_msg_internal(obj, method, data, UBUS_TIMEOUT, NULL);
|
||||
|
||||
if (json_res != NULL) {
|
||||
json_object_put(json_res);
|
||||
json_res = NULL;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Based on an efficient hash function published by D. J. Bernstein
|
||||
*/
|
||||
static unsigned int djbhash(unsigned hash, const char *data, unsigned len)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
hash = ((hash << 5) + hash) + data[i];
|
||||
|
||||
return (hash & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
static unsigned dm_ubus_req_hash_from_blob(const struct dm_ubus_hash_req *req)
|
||||
{
|
||||
unsigned hash = 5381;
|
||||
if (!req) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
hash = djbhash(hash, req->obj, DM_STRLEN(req->obj));
|
||||
hash = djbhash(hash, req->method, DM_STRLEN(req->method));
|
||||
|
||||
char *jmsg = blobmsg_format_json(req->attr, true);
|
||||
if (!jmsg) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
hash = djbhash(hash, jmsg, DM_STRLEN(jmsg));
|
||||
free(jmsg);
|
||||
return hash;
|
||||
}
|
||||
|
||||
static const struct dm_ubus_cache_entry * dm_ubus_cache_lookup(unsigned hash)
|
||||
{
|
||||
const struct dm_ubus_cache_entry *entry = NULL;
|
||||
const struct dm_ubus_cache_entry *entry_match = NULL;
|
||||
|
||||
list_for_each_entry(entry, &dmubus_cache, list) {
|
||||
if (entry->hash == hash) {
|
||||
entry_match = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return entry_match;
|
||||
}
|
||||
|
||||
static void dm_ubus_cache_entry_new(unsigned hash, json_object *data)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
|
||||
entry = calloc(1, sizeof(struct dm_ubus_cache_entry));
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
list_add_tail(&entry->list, &dmubus_cache);
|
||||
entry->data = data;
|
||||
entry->hash = hash;
|
||||
}
|
||||
|
||||
static void dm_ubus_cache_entry_free(void)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL, *tmp = NULL;
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &dmubus_cache, list) {
|
||||
list_del(&entry->list);
|
||||
|
||||
if (entry->data) {
|
||||
json_object_put(entry->data);
|
||||
entry->data = NULL;
|
||||
}
|
||||
|
||||
FREE(entry);
|
||||
}
|
||||
}
|
||||
|
||||
int dmubus_call(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res)
|
||||
{
|
||||
struct blob_buf bmsg;
|
||||
|
||||
memset(&bmsg, 0, sizeof(struct blob_buf));
|
||||
prepare_blob_message(&bmsg, u_args, u_args_size);
|
||||
|
||||
const struct dm_ubus_hash_req hash_req = {
|
||||
.obj = obj,
|
||||
.method = method,
|
||||
.attr = bmsg.head
|
||||
};
|
||||
|
||||
const unsigned hash = dm_ubus_req_hash_from_blob(&hash_req);
|
||||
const struct dm_ubus_cache_entry *entry = dm_ubus_cache_lookup(hash);
|
||||
json_object *res = NULL;
|
||||
|
||||
if (entry) {
|
||||
res = entry->data;
|
||||
} else {
|
||||
res = ubus_call_req(obj, method, bmsg.head);
|
||||
dm_ubus_cache_entry_new(hash, res);
|
||||
}
|
||||
|
||||
blob_buf_free(&bmsg);
|
||||
*req_res = res;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dmubus_call_blocking(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res)
|
||||
{
|
||||
struct blob_buf bmsg;
|
||||
int rc;
|
||||
|
||||
memset(&bmsg, 0, sizeof(struct blob_buf));
|
||||
prepare_blob_message(&bmsg, u_args, u_args_size);
|
||||
|
||||
rc = __ubus_call_blocking(obj, method, bmsg.head);
|
||||
|
||||
blob_buf_free(&bmsg);
|
||||
*req_res = json_res;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void receive_list_result(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv)
|
||||
{
|
||||
struct blob_attr *cur = NULL;
|
||||
|
|
@ -555,7 +636,7 @@ bool dmubus_object_method_exists(const char *object)
|
|||
if (object == NULL)
|
||||
return false;
|
||||
|
||||
if (ubus_ctx == NULL) {
|
||||
if (g_dm_ubus_ctx == NULL) {
|
||||
BBF_ERR("UBUS context is null");
|
||||
return false;
|
||||
}
|
||||
|
|
@ -569,7 +650,7 @@ bool dmubus_object_method_exists(const char *object)
|
|||
*delimiter = '\0';
|
||||
}
|
||||
|
||||
if (ubus_lookup(ubus_ctx, ubus_object, receive_list_result, &ubus_s))
|
||||
if (ubus_lookup(g_dm_ubus_ctx, ubus_object, receive_list_result, &ubus_s))
|
||||
return false;
|
||||
|
||||
if (ubus_s.ubus_method_name && !ubus_s.ubus_method_exists)
|
||||
|
|
@ -578,9 +659,65 @@ bool dmubus_object_method_exists(const char *object)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void dmubus_schedule_blacklisted_ubus_recovery(void);
|
||||
|
||||
static void blacklisted_ubus_recovery_timer_cb(struct uloop_timeout *timeout __attribute__((unused)))
|
||||
{
|
||||
dmubus_schedule_blacklisted_ubus_recovery();
|
||||
}
|
||||
|
||||
static struct uloop_timeout blacklisted_ubus_recovery_timer = {
|
||||
.cb = blacklisted_ubus_recovery_timer_cb
|
||||
};
|
||||
|
||||
static void verify_ubus_method(struct dm_ubus_cache_entry *entry)
|
||||
{
|
||||
struct ubus_context *ubus_ctx = ubus_connect(NULL);
|
||||
|
||||
int err = dm_ubus_call_sync(ubus_ctx, entry->obj, entry->method, entry->timeout, entry->attr, NULL);
|
||||
|
||||
if (err == 0) {
|
||||
BBF_INFO("Recovered ubus obj |%s| method |%s|", entry->obj, entry->method);
|
||||
|
||||
entry->consecutive_timeouts = 0;
|
||||
entry->is_blacklisted = false;
|
||||
} else {
|
||||
BBF_INFO("ubus obj |%s| method |%s| still unreachable", entry->obj, entry->method);
|
||||
}
|
||||
|
||||
ubus_free(ubus_ctx);
|
||||
}
|
||||
|
||||
static void dmubus_schedule_blacklisted_ubus_recovery(void)
|
||||
{
|
||||
int next_check_time = 60000; // 1 min
|
||||
|
||||
if (g_dm_ubus_ctx != NULL) {
|
||||
BBF_INFO("A method is currently running. Rescheduling blacklisted ubus recovery in %d msecs", next_check_time);
|
||||
uloop_timeout_set(&blacklisted_ubus_recovery_timer, next_check_time);
|
||||
return;
|
||||
}
|
||||
|
||||
struct dm_ubus_cache_entry *entry = NULL;
|
||||
|
||||
list_for_each_entry(entry, &dm_ubus_cache, list) {
|
||||
if (entry->is_blacklisted) {
|
||||
verify_ubus_method(entry);
|
||||
}
|
||||
}
|
||||
|
||||
BBF_DEBUG("Pid %d, Next blacklisted ubus recovery scheduled in %d msecs", getpid(), next_check_time);
|
||||
uloop_timeout_set(&blacklisted_ubus_recovery_timer, next_check_time);
|
||||
}
|
||||
|
||||
static void dmubus_stop_blacklisted_ubus_recovery(void)
|
||||
{
|
||||
uloop_timeout_cancel(&blacklisted_ubus_recovery_timer);
|
||||
}
|
||||
|
||||
void dm_ubus_init(struct dmctx *bbf_ctx)
|
||||
{
|
||||
bbf_ctx->ubus_ctx = ubus_ctx = ubus_connect(NULL);
|
||||
bbf_ctx->ubus_ctx = g_dm_ubus_ctx = ubus_connect(NULL);
|
||||
}
|
||||
|
||||
void dm_ubus_free(struct dmctx *bbf_ctx)
|
||||
|
|
@ -589,6 +726,28 @@ void dm_ubus_free(struct dmctx *bbf_ctx)
|
|||
|
||||
if (bbf_ctx->ubus_ctx) {
|
||||
ubus_free(bbf_ctx->ubus_ctx);
|
||||
bbf_ctx->ubus_ctx = ubus_ctx = NULL;
|
||||
bbf_ctx->ubus_ctx = g_dm_ubus_ctx = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void dm_ubus_cache_init(void)
|
||||
{
|
||||
INIT_LIST_HEAD(&dm_ubus_cache);
|
||||
|
||||
dmubus_schedule_blacklisted_ubus_recovery();
|
||||
}
|
||||
|
||||
void dm_ubus_cache_free(void)
|
||||
{
|
||||
struct dm_ubus_cache_entry *entry = NULL, *tmp = NULL;
|
||||
|
||||
list_for_each_entry_safe(entry, tmp, &dm_ubus_cache, list) {
|
||||
list_del(&entry->list);
|
||||
FREE(entry->obj);
|
||||
FREE(entry->method);
|
||||
FREE(entry->attr);
|
||||
FREE(entry);
|
||||
}
|
||||
|
||||
dmubus_stop_blacklisted_ubus_recovery();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ void dmubus_wait_for_event(const char *event, int timeout, void *ev_data, CB_FUN
|
|||
struct dmubus_ev_subtask *subtask);
|
||||
|
||||
int dmubus_call(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res);
|
||||
int dmubus_call_timeout(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, int timeout, json_object **req_res);
|
||||
int dmubus_call_blocking(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size, json_object **req_res);
|
||||
int dmubus_call_set(const char *obj, const char *method, struct ubus_arg u_args[], int u_args_size);
|
||||
|
||||
|
|
@ -60,6 +61,8 @@ int dmubus_call_blob(const char *obj, const char *method, json_object *value, js
|
|||
int dmubus_call_blob_blocking(const char *obj, const char *method, json_object *value, json_object **resp);
|
||||
int dmubus_call_blob_set(const char *obj, const char *method, json_object *value);
|
||||
|
||||
int dmubus_call_blob_msg(const char *obj, const char *method, struct blob_buf *blob_msg, json_object **resp);
|
||||
int dmubus_call_blob_msg_timeout(const char *obj, const char *method, struct blob_buf *blob_msg, int timeout);
|
||||
int dmubus_call_blob_msg_set(const char *obj, const char *method, struct blob_buf *blob_msg);
|
||||
|
||||
void dm_ubus_init(struct dmctx *bbf_ctx);
|
||||
|
|
@ -67,4 +70,7 @@ void dm_ubus_free(struct dmctx *bbf_ctx);
|
|||
|
||||
bool dmubus_object_method_exists(const char *obj);
|
||||
|
||||
void dm_ubus_cache_init(void);
|
||||
void dm_ubus_cache_free(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -39,17 +39,32 @@ static void bbfdm_uci_init_ctx(struct uci_context **uci_ctx, const char *confdir
|
|||
|
||||
void dm_uci_init(struct dmctx *bbf_ctx)
|
||||
{
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/");
|
||||
|
||||
if (bbf_ctx->dm_type == BBFDM_CWMP) {
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.cwmp/");
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.cwmp/config/");
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.cwmp/dmmap/");
|
||||
|
||||
bbfdm_uci_init_ctx(&bbf_ctx->config_uci_ctx, config_dir, "/tmp/bbfdm/.cwmp/config/");
|
||||
bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.cwmp/dmmap/");
|
||||
} else if (bbf_ctx->dm_type == BBFDM_USP) {
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.usp/");
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.usp/config/");
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.usp/dmmap/");
|
||||
|
||||
bbfdm_uci_init_ctx(&bbf_ctx->config_uci_ctx, config_dir, "/tmp/bbfdm/.usp/config/");
|
||||
bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.usp/dmmap/");
|
||||
} else {
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.bbfdm/");
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.bbfdm/config/");
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.bbfdm/dmmap/");
|
||||
|
||||
bbfdm_uci_init_ctx(&bbf_ctx->config_uci_ctx, config_dir, "/tmp/bbfdm/.bbfdm/config/");
|
||||
bbfdm_uci_init_ctx(&bbf_ctx->dmmap_uci_ctx, bbfdm_dir, "/tmp/bbfdm/.bbfdm/dmmap/");
|
||||
}
|
||||
|
||||
bbfdm_ensure_folder_exists("/tmp/bbfdm/.varstate/");
|
||||
bbfdm_uci_init_ctx(&bbf_ctx->varstate_uci_ctx, varstate_dir, "/tmp/bbfdm/.varstate/");
|
||||
|
||||
uci_ctx = bbf_ctx->config_uci_ctx;
|
||||
|
|
@ -190,6 +205,17 @@ int dmuci_get_section_type(const char *package, const char *section, char **valu
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI SECTION EXIST *****/
|
||||
struct uci_section *dmuci_get_section(const char *package, const char *section)
|
||||
{
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (dmuci_lookup_ptr(uci_ctx, &ptr, package, section, NULL, NULL))
|
||||
return NULL;
|
||||
|
||||
return ptr.s;
|
||||
}
|
||||
|
||||
int dmuci_get_option_value_string(const char *package, const char *section, const char *option, char **value)
|
||||
{
|
||||
struct uci_ptr ptr = {0};
|
||||
|
|
@ -365,8 +391,10 @@ int dmuci_commit_package(char *package)
|
|||
if (uci_lookup_ptr(uci_ctx, &ptr, package, true) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_commit(uci_ctx, &ptr.p, false) != UCI_OK)
|
||||
if (uci_commit(uci_ctx, &ptr.p, false) != UCI_OK) {
|
||||
BBF_ERR("Failed to commit UCI package '%s'. confdir: '%s', savedir: '%s'.", package, uci_ctx->confdir, uci_ctx->savedir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -403,117 +431,7 @@ int dmuci_revert_package(char *package)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**** UCI SET *****/
|
||||
int dmuci_set_value(const char *package, const char *section, const char *option, const char *value)
|
||||
{
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (dmuci_lookup_ptr(uci_ctx, &ptr, package, section, option, value))
|
||||
return -1;
|
||||
|
||||
if (uci_set(uci_ctx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(uci_ctx, ptr.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI ADD LIST *****/
|
||||
int dmuci_add_list_value(const char *package, const char *section, const char *option, const char *value)
|
||||
{
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (dmuci_lookup_ptr(uci_ctx, &ptr, package, section, option, value))
|
||||
return -1;
|
||||
|
||||
if (uci_add_list(uci_ctx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(uci_ctx, ptr.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI DEL LIST *****/
|
||||
int dmuci_del_list_value(const char *package, const char *section, const char *option, const char *value)
|
||||
{
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (dmuci_lookup_ptr(uci_ctx, &ptr, package, section, option, value))
|
||||
return -1;
|
||||
|
||||
if (uci_del_list(uci_ctx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(uci_ctx, ptr.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/****** UCI ADD *******/
|
||||
int dmuci_add_section(const char *package, const char *stype, struct uci_section **s)
|
||||
{
|
||||
struct uci_ptr ptr = {0};
|
||||
char fname[128];
|
||||
|
||||
*s = NULL;
|
||||
|
||||
snprintf(fname, sizeof(fname), "%s/%s", uci_ctx->confdir, package);
|
||||
|
||||
if (create_empty_file(fname))
|
||||
return -1;
|
||||
|
||||
if (dmuci_lookup_ptr(uci_ctx, &ptr, package, NULL, NULL, NULL))
|
||||
return -1;
|
||||
|
||||
if (uci_add_section(uci_ctx, ptr.p, stype, s) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(uci_ctx, ptr.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI DELETE *****/
|
||||
int dmuci_delete(const char *package, const char *section, const char *option, const char *value)
|
||||
{
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (dmuci_lookup_ptr(uci_ctx, &ptr, package, section, option, NULL))
|
||||
return -1;
|
||||
|
||||
if (uci_delete(uci_ctx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(uci_ctx, ptr.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI RENAME SECTION *****/
|
||||
int dmuci_rename_section(const char *package, const char *section, const char *value)
|
||||
{
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (dmuci_lookup_ptr(uci_ctx, &ptr, package, section, NULL, value))
|
||||
return -1;
|
||||
|
||||
if (uci_rename(uci_ctx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(uci_ctx, ptr.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**** UCI LOOKUP by section pointer ****/
|
||||
static int dmuci_lookup_ptr_by_section(struct uci_context *ctx, struct uci_ptr *ptr, struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
|
|
@ -545,6 +463,7 @@ lookup:
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**** UCI GET by section pointer*****/
|
||||
int dmuci_get_value_by_section_string(struct uci_section *s, const char *option, char **value)
|
||||
|
|
@ -625,133 +544,6 @@ int dmuci_get_value_by_section_list(struct uci_section *s, const char *option, s
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**** UCI SET by section pointer ****/
|
||||
int dmuci_set_value_by_section(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
struct uci_context *curr_ctx = get_uci_context_by_section(s);
|
||||
struct uci_ptr up = {0};
|
||||
|
||||
if (dmuci_lookup_ptr_by_section(curr_ctx, &up, s, option, value) == -1)
|
||||
return -1;
|
||||
|
||||
if (uci_set(curr_ctx, &up) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(curr_ctx, up.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI DELETE by section pointer *****/
|
||||
int dmuci_delete_by_section(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
struct uci_context *curr_ctx = get_uci_context_by_section(s);
|
||||
struct uci_ptr up = {0};
|
||||
|
||||
curr_ctx->flags |= UCI_FLAG_EXPORT_NAME;
|
||||
|
||||
if (dmuci_lookup_ptr_by_section(curr_ctx, &up, s, option, value) == -1)
|
||||
return -1;
|
||||
|
||||
if (uci_delete(curr_ctx, &up) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(curr_ctx, up.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dmuci_delete_by_section_unnamed(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
struct uci_context *curr_ctx = get_uci_context_by_section(s);
|
||||
struct uci_ptr up = {0};
|
||||
|
||||
if (dmuci_lookup_ptr_by_section(curr_ctx, &up, s, option, value) == -1)
|
||||
return -1;
|
||||
|
||||
if (uci_delete(curr_ctx, &up) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(curr_ctx, up.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI ADD LIST by section pointer *****/
|
||||
int dmuci_add_list_value_by_section(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
struct uci_context *curr_ctx = get_uci_context_by_section(s);
|
||||
struct uci_ptr up = {0};
|
||||
|
||||
if (dmuci_lookup_ptr_by_section(curr_ctx, &up, s, option, value) == -1)
|
||||
return -1;
|
||||
|
||||
if (uci_add_list(curr_ctx, &up) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(curr_ctx, up.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI DEL LIST by section pointer *****/
|
||||
int dmuci_del_list_value_by_section(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
struct uci_context *curr_ctx = get_uci_context_by_section(s);
|
||||
struct uci_ptr up = {0};
|
||||
|
||||
if (dmuci_lookup_ptr_by_section(curr_ctx, &up, s, option, value) == -1)
|
||||
return -1;
|
||||
|
||||
if (uci_del_list(curr_ctx, &up) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(curr_ctx, up.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI RENAME SECTION by section pointer *****/
|
||||
int dmuci_rename_section_by_section(struct uci_section *s, const char *value)
|
||||
{
|
||||
struct uci_context *curr_ctx = get_uci_context_by_section(s);
|
||||
struct uci_ptr up = {0};
|
||||
|
||||
if (dmuci_lookup_ptr_by_section(curr_ctx, &up, s, NULL, value) == -1)
|
||||
return -1;
|
||||
|
||||
if (uci_rename(curr_ctx, &up) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(curr_ctx, up.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI REORDER SECTION by section pointer *****/
|
||||
int dmuci_reoder_section_by_section(struct uci_section *s, char *pos)
|
||||
{
|
||||
struct uci_context *curr_ctx = get_uci_context_by_section(s);
|
||||
struct uci_ptr up = {0};
|
||||
|
||||
if (dmuci_lookup_ptr_by_section(curr_ctx, &up, s, NULL, pos) == -1)
|
||||
return -1;
|
||||
|
||||
if (uci_reorder_section(curr_ctx, up.s, strtoul(up.value, NULL, 10)) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
if (uci_save(curr_ctx, up.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI WALK SECTIONS *****/
|
||||
struct uci_section *dmuci_walk_section (const char *package, const char *stype, const void *arg1, const void *arg2, int cmp , int (*filter)(struct uci_section *s, const void *value), struct uci_section *prev_section, int walk)
|
||||
{
|
||||
|
|
@ -928,3 +720,307 @@ int dmuci_set_section_name(const char *sec_name, char *str, size_t size)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum uci_oper_type {
|
||||
UCI_OP_SET,
|
||||
UCI_OP_ADD_LIST,
|
||||
UCI_OP_DEL_LIST,
|
||||
UCI_OP_ADD,
|
||||
UCI_OP_DEL,
|
||||
UCI_OP_RENAME,
|
||||
UCI_OP_REORDER,
|
||||
UCI_OP_ADD_NAMED,
|
||||
__UCI_OP_MAX
|
||||
};
|
||||
|
||||
typedef struct uci_op_data {
|
||||
//Input
|
||||
const char *package;
|
||||
const char *section;
|
||||
const char *option;
|
||||
const char *value;
|
||||
const char *sec_type;
|
||||
struct uci_context *ucictx;
|
||||
struct dmctx *dmctx;
|
||||
|
||||
//Output
|
||||
struct uci_section **s;
|
||||
} bbfdm_uci_op_data;
|
||||
|
||||
static int __uci_perform_op(int operation, bbfdm_uci_op_data *op_data)
|
||||
{
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
if (op_data == NULL)
|
||||
return -1;
|
||||
|
||||
if (dmuci_lookup_ptr(op_data->ucictx, &ptr, op_data->package, op_data->section, op_data->option, op_data->value))
|
||||
return -1;
|
||||
|
||||
switch (operation) {
|
||||
case UCI_OP_SET:
|
||||
if (uci_set(op_data->ucictx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
break;
|
||||
case UCI_OP_ADD_NAMED:
|
||||
if (uci_set(op_data->ucictx, &ptr) != UCI_OK || ptr.s == NULL)
|
||||
return -1;
|
||||
*op_data->s = ptr.s;
|
||||
break;
|
||||
case UCI_OP_ADD_LIST:
|
||||
if (uci_add_list(op_data->ucictx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
break;
|
||||
case UCI_OP_DEL_LIST:
|
||||
if (uci_del_list(op_data->ucictx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
break;
|
||||
case UCI_OP_ADD:
|
||||
if (uci_add_section(op_data->ucictx, ptr.p, op_data->sec_type, op_data->s) != UCI_OK)
|
||||
return -1;
|
||||
break;
|
||||
case UCI_OP_DEL:
|
||||
if (uci_delete(op_data->ucictx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
break;
|
||||
case UCI_OP_RENAME:
|
||||
if (uci_rename(op_data->ucictx, &ptr) != UCI_OK)
|
||||
return -1;
|
||||
break;
|
||||
case UCI_OP_REORDER:
|
||||
if (uci_reorder_section(op_data->ucictx, ptr.s, strtoul(ptr.value, NULL, 10)) != UCI_OK)
|
||||
return -1;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (uci_save(op_data->ucictx, ptr.p) != UCI_OK)
|
||||
return -1;
|
||||
|
||||
add_list_modified_uci(op_data->dmctx, op_data->ucictx->confdir, op_data->package);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**** UCI SET by section pointer ****/
|
||||
int dmuci_set_value_by_section(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = get_uci_context_by_section(s);
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = s ? section_config(s) : "";
|
||||
conf_data.section = s ? section_name(s) : "";
|
||||
conf_data.option = option;
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_SET, &conf_data);
|
||||
}
|
||||
|
||||
int dmuci_set_value(const char *package, const char *section, const char *option, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = uci_ctx;
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = package;
|
||||
conf_data.section = section;
|
||||
conf_data.option = option;
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_SET, &conf_data);
|
||||
}
|
||||
|
||||
/**** UCI ADD section *****/
|
||||
int dmuci_add_section(const char *package, const char *stype, struct uci_section **s)
|
||||
{
|
||||
char fname[128];
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
if (s == NULL)
|
||||
return -1;
|
||||
|
||||
snprintf(fname, sizeof(fname), "%s/%s", uci_ctx->confdir, package);
|
||||
|
||||
if (create_empty_file(fname))
|
||||
return -1;
|
||||
|
||||
*s = NULL;
|
||||
|
||||
conf_data.s = s;
|
||||
conf_data.ucictx = uci_ctx;
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = package;
|
||||
conf_data.sec_type = stype;
|
||||
|
||||
return __uci_perform_op(UCI_OP_ADD, &conf_data);
|
||||
}
|
||||
|
||||
int dmuci_add_named_section(const char *package, const char *stype, const char *name, struct uci_section **s)
|
||||
{
|
||||
char fname[128];
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
if (s == NULL)
|
||||
return -1;
|
||||
|
||||
snprintf(fname, sizeof(fname), "%s/%s", uci_ctx->confdir, package);
|
||||
|
||||
if (create_empty_file(fname))
|
||||
return -1;
|
||||
|
||||
*s = NULL;
|
||||
|
||||
conf_data.s = s;
|
||||
conf_data.ucictx = uci_ctx;
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = package;
|
||||
conf_data.section = name;
|
||||
conf_data.value = stype;
|
||||
|
||||
return __uci_perform_op(UCI_OP_ADD_NAMED, &conf_data);
|
||||
}
|
||||
|
||||
/**** UCI DELETE by section pointer *****/
|
||||
int dmuci_delete_by_section(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = get_uci_context_by_section(s);
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = s ? section_config(s) : "";
|
||||
conf_data.section = s ? section_name(s) : "";
|
||||
conf_data.option = option;
|
||||
conf_data.value = value;
|
||||
|
||||
conf_data.ucictx->flags |= UCI_FLAG_EXPORT_NAME;
|
||||
|
||||
return __uci_perform_op(UCI_OP_DEL, &conf_data);
|
||||
}
|
||||
|
||||
int dmuci_delete(const char *package, const char *section, const char *option, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = uci_ctx;
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = package;
|
||||
conf_data.section = section;
|
||||
conf_data.option = option;
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_DEL, &conf_data);
|
||||
}
|
||||
|
||||
int dmuci_delete_by_section_unnamed(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = get_uci_context_by_section(s);
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = s ? section_config(s) : "";
|
||||
conf_data.section = s ? section_name(s) : "";
|
||||
conf_data.option = option;
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_DEL, &conf_data);
|
||||
}
|
||||
|
||||
/**** UCI ADD LIST by section pointer *****/
|
||||
int dmuci_add_list_value_by_section(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = get_uci_context_by_section(s);
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = s ? section_config(s) : "";
|
||||
conf_data.section = s ? section_name(s) : "";
|
||||
conf_data.option = option;
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_ADD_LIST, &conf_data);
|
||||
}
|
||||
|
||||
int dmuci_add_list_value(const char *package, const char *section, const char *option, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = uci_ctx;
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = package;
|
||||
conf_data.section = section;
|
||||
conf_data.option = option;
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_ADD_LIST, &conf_data);
|
||||
}
|
||||
|
||||
/**** UCI DEL LIST by section pointer *****/
|
||||
int dmuci_del_list_value_by_section(struct uci_section *s, const char *option, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = get_uci_context_by_section(s);
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = s ? section_config(s) : "";
|
||||
conf_data.section = s ? section_name(s) : "";
|
||||
conf_data.option = option;
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_DEL_LIST, &conf_data);
|
||||
}
|
||||
|
||||
int dmuci_del_list_value(const char *package, const char *section, const char *option, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = uci_ctx;
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = package;
|
||||
conf_data.section = section;
|
||||
conf_data.option = option;
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_DEL_LIST, &conf_data);
|
||||
}
|
||||
|
||||
/**** UCI RENAME SECTION by section pointer *****/
|
||||
int dmuci_rename_section_by_section(struct uci_section *s, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = get_uci_context_by_section(s);
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = s ? section_config(s) : "";
|
||||
conf_data.section = s ? section_name(s) : "";
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_RENAME, &conf_data);
|
||||
}
|
||||
|
||||
int dmuci_rename_section(const char *package, const char *section, const char *value)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = uci_ctx;
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = package;
|
||||
conf_data.section = section;
|
||||
conf_data.value = value;
|
||||
|
||||
return __uci_perform_op(UCI_OP_RENAME, &conf_data);
|
||||
}
|
||||
|
||||
/**** UCI REORDER SECTION by section pointer *****/
|
||||
int dmuci_reoder_section_by_section(struct uci_section *s, char *pos)
|
||||
{
|
||||
bbfdm_uci_op_data conf_data = {0};
|
||||
|
||||
conf_data.ucictx = get_uci_context_by_section(s);
|
||||
conf_data.dmctx = get_bbfdm_global_dmctx();
|
||||
conf_data.package = s ? section_config(s) : "";
|
||||
conf_data.section = s ? section_name(s) : "";
|
||||
conf_data.value = pos;
|
||||
|
||||
return __uci_perform_op(UCI_OP_REORDER, &conf_data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -156,6 +156,15 @@ int dmuci_get_section_type_##UCI_PATH(const char *package, const char *section,c
|
|||
uci_ctx = save_uci_ctx; \
|
||||
return res; \
|
||||
}\
|
||||
struct uci_section *dmuci_get_section_##UCI_PATH(const char *package, const char *section) \
|
||||
{\
|
||||
struct uci_context *save_uci_ctx; \
|
||||
save_uci_ctx = uci_ctx; \
|
||||
uci_ctx = uci_ctx_##UCI_PATH; \
|
||||
struct uci_section *res = dmuci_get_section(package, section); \
|
||||
uci_ctx = save_uci_ctx; \
|
||||
return res; \
|
||||
}\
|
||||
int dmuci_get_option_value_string_##UCI_PATH(const char *package, const char *section, const char *option, char **value) \
|
||||
{\
|
||||
struct uci_context *save_uci_ctx; \
|
||||
|
|
@ -210,6 +219,15 @@ int dmuci_add_section_##UCI_PATH(const char *package, const char *stype, struct
|
|||
uci_ctx = save_uci_ctx; \
|
||||
return res; \
|
||||
}\
|
||||
int dmuci_add_named_section_##UCI_PATH(const char *package, const char *stype, const char *name, struct uci_section **s)\
|
||||
{\
|
||||
struct uci_context *save_uci_ctx; \
|
||||
save_uci_ctx = uci_ctx; \
|
||||
uci_ctx = uci_ctx_##UCI_PATH; \
|
||||
int res = dmuci_add_named_section(package, stype, name, s); \
|
||||
uci_ctx = save_uci_ctx; \
|
||||
return res; \
|
||||
}\
|
||||
int dmuci_delete_##UCI_PATH(const char *package, const char *section, const char *option, const char *value) \
|
||||
{\
|
||||
struct uci_context *save_uci_ctx; \
|
||||
|
|
@ -264,6 +282,15 @@ int dmuci_commit_##UCI_PATH(void) \
|
|||
uci_ctx = save_uci_ctx; \
|
||||
return res; \
|
||||
}\
|
||||
int dmuci_revert_package_##UCI_PATH(char *package) \
|
||||
{\
|
||||
struct uci_context *save_uci_ctx; \
|
||||
save_uci_ctx = uci_ctx; \
|
||||
uci_ctx = uci_ctx_##UCI_PATH; \
|
||||
int res = dmuci_revert_package(package); \
|
||||
uci_ctx = save_uci_ctx; \
|
||||
return res; \
|
||||
}\
|
||||
int dmuci_delete_by_section_unnamed_##UCI_PATH(struct uci_section *s, const char *option, const char *value)\
|
||||
{\
|
||||
struct uci_context *save_uci_ctx; \
|
||||
|
|
@ -294,6 +321,7 @@ int dmuci_set_value(const char *package, const char *section, const char *option
|
|||
int dmuci_add_list_value(const char *package, const char *section, const char *option, const char *value);
|
||||
int dmuci_del_list_value(const char *package, const char *section, const char *option, const char *value);
|
||||
int dmuci_add_section(const char *package, const char *stype, struct uci_section **s);
|
||||
int dmuci_add_named_section(const char *package, const char *stype, const char *name, struct uci_section **s);
|
||||
int dmuci_delete(const char *package, const char *section, const char *option, const char *value);
|
||||
int dmuci_rename_section(const char *package, const char *section, const char *value);
|
||||
int dmuci_get_value_by_section_string(struct uci_section *s, const char *option, char **value);
|
||||
|
|
@ -306,27 +334,34 @@ int dmuci_add_list_value_by_section(struct uci_section *s, const char *option, c
|
|||
int dmuci_del_list_value_by_section(struct uci_section *s, const char *option, const char *value);
|
||||
int dmuci_rename_section_by_section(struct uci_section *s, const char *value);
|
||||
int dmuci_reoder_section_by_section(struct uci_section *s, char *pos);
|
||||
struct uci_section *dmuci_get_section(const char *package, const char *section);
|
||||
struct uci_section *dmuci_walk_section(const char *package, const char *stype, const void *arg1, const void *arg2, int cmp , int (*filter)(struct uci_section *s, const void *value), struct uci_section *prev_section, int walk);
|
||||
struct uci_section *dmuci_walk_all_sections(const char *package, struct uci_section *prev_section, int walk);
|
||||
|
||||
int dmuci_get_option_value_string_bbfdm(const char *package, const char *section, const char *option, char **value);
|
||||
int dmuci_set_value_bbfdm(const char *package, const char *section, const char *option, const char *value);
|
||||
int dmuci_set_value_by_section_bbfdm(struct uci_section *s, const char *option, const char *value);
|
||||
int dmuci_add_list_value_bbfdm(const char *package, const char *section, const char *option, const char *value);
|
||||
int dmuci_add_section_bbfdm(const char *package, const char *stype, struct uci_section **s);
|
||||
int dmuci_add_named_section_bbfdm(const char *package, const char *stype, const char *name, struct uci_section **s);
|
||||
int dmuci_delete_bbfdm(const char *package, const char *section, const char *option, const char *value);
|
||||
int dmuci_delete_by_section_unnamed_bbfdm(struct uci_section *s, const char *option, const char *value);
|
||||
int dmuci_delete_by_section_bbfdm(struct uci_section *s, const char *option, const char *value);
|
||||
int dmuci_commit_package_bbfdm(char *package);
|
||||
int dmuci_commit_bbfdm(void);
|
||||
int dmuci_revert_package_bbfdm(char *package);
|
||||
struct uci_section *dmuci_get_section_bbfdm(const char *package, const char *section);
|
||||
struct uci_section *dmuci_walk_section_bbfdm(const char *package, const char *stype, const void *arg1, const void *arg2, int cmp , int (*filter)(struct uci_section *s, const void *value), struct uci_section *prev_section, int walk);
|
||||
|
||||
struct uci_section *dmuci_walk_section_varstate(const char *package, const char *stype, const void *arg1, const void *arg2, int cmp , int (*filter)(struct uci_section *s, const void *value), struct uci_section *prev_section, int walk);
|
||||
int dmuci_add_list_value_varstate(const char *package, const char *section, const char *option, const char *value);
|
||||
int dmuci_add_section_varstate(const char *package, const char *stype, struct uci_section **s);
|
||||
int dmuci_delete_by_section_varstate(struct uci_section *s, const char *option, const char *value);
|
||||
int dmuci_get_option_value_string_varstate(const char *package, const char *section, const char *option, char **value);
|
||||
int dmuci_set_value_varstate(const char *package, const char *section, const char *option, const char *value);
|
||||
int dmuci_set_value_by_section_varstate(struct uci_section *s, const char *option, const char *value);
|
||||
int dmuci_commit_package_varstate(char *package);
|
||||
struct uci_section *dmuci_get_section_varstate(const char *package, const char *section);
|
||||
struct uci_section *dmuci_walk_section_varstate(const char *package, const char *stype, const void *arg1, const void *arg2, int cmp , int (*filter)(struct uci_section *s, const void *value), struct uci_section *prev_section, int walk);
|
||||
|
||||
int db_get_value_string(const char *package, const char *section, const char *option, char **value);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,14 +62,9 @@ static void dotso_plugin_disable_requested_entries(DMOBJ *entryobj, DMOBJ *reque
|
|||
|
||||
int load_dotso_plugins(DMOBJ *entryobj, const char *plugin_path)
|
||||
{
|
||||
#ifndef BBF_SCHEMA_FULL_TREE
|
||||
void *handle = dlopen(plugin_path, RTLD_NOW|RTLD_LOCAL);
|
||||
#else
|
||||
void *handle = dlopen(plugin_path, RTLD_LAZY);
|
||||
#endif
|
||||
if (!handle) {
|
||||
char *err_msg = dlerror();
|
||||
TRACE_FILE("Failed to add DotSo plugin '%s', [%s]\n", plugin_path, err_msg);
|
||||
BBF_ERR("Failed to add DotSo plugin '%s', [%s]\n", plugin_path, err_msg);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -88,7 +83,6 @@ int load_dotso_plugins(DMOBJ *entryobj, const char *plugin_path)
|
|||
|
||||
DMOBJ *dm_entryobj = find_entry_obj(entryobj, dynamic_obj[i].path);
|
||||
if (!dm_entryobj) {
|
||||
TRACE_FILE("Failed to add DotSo plugin '%s' to main tree with parent DM index '%d' => '%s'", plugin_path, i, dynamic_obj[i].path);
|
||||
BBF_ERR("Failed to add DotSo plugin '%s' to main tree with parent DM index '%d' => '%s'", plugin_path, i, dynamic_obj[i].path);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -134,6 +128,9 @@ int load_dotso_plugins(DMOBJ *entryobj, const char *plugin_path)
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
if (dynamic_obj[i].init_module)
|
||||
dynamic_obj[i].init_module(NULL);
|
||||
}
|
||||
add_list_loaded_libraries(&loaded_library_list, handle);
|
||||
|
||||
|
|
|
|||
|
|
@ -654,12 +654,13 @@ static int delete_obj(char *refparam, struct dmctx *ctx, void *data, char *insta
|
|||
return 0;
|
||||
}
|
||||
|
||||
static char *handle_reference_value(struct dmctx *ctx, struct json_object *linker_jobj, const char *key_name, char *key_value)
|
||||
static char *handle_reference_value(struct dmctx *ctx, struct json_object *linker_jobj, char *key_value)
|
||||
{
|
||||
if (!ctx || !linker_jobj || !key_name || !key_value)
|
||||
return "";
|
||||
|
||||
char linker_path[256] = {0};
|
||||
char *pref = NULL;
|
||||
|
||||
if (!ctx || !linker_jobj || !key_value)
|
||||
return "";
|
||||
|
||||
char *linker_val = json_object_get_string(linker_jobj);
|
||||
if (!linker_val)
|
||||
|
|
@ -669,18 +670,13 @@ static char *handle_reference_value(struct dmctx *ctx, struct json_object *linke
|
|||
if (DM_STRLEN(linker_path) == 0)
|
||||
return "";
|
||||
|
||||
char *ext_ref = strstr(linker_path, "==");
|
||||
if (ext_ref == NULL) {
|
||||
char *pref = NULL;
|
||||
adm_entry_get_reference_param(ctx, linker_path, key_value, &pref);
|
||||
if (DM_STRLEN(pref) != 0)
|
||||
return pref;
|
||||
|
||||
adm_entry_get_reference_param(ctx, linker_path, key_value, &pref);
|
||||
return pref ? pref : dmstrdup("");
|
||||
} else {
|
||||
char buf_ref[256 + 32] = {0};
|
||||
pref = bbfdm_resolve_external_reference_via_json(ctx, linker_path, key_value);
|
||||
|
||||
replace_str(linker_path, key_name, key_value, buf_ref, sizeof(buf_ref));
|
||||
return dmstrdup(buf_ref);
|
||||
}
|
||||
return pref ? pref : "";
|
||||
}
|
||||
|
||||
static char *handle_reference_list_value(struct dmctx *ctx, struct json_object *linker_jobj, struct uci_list *list)
|
||||
|
|
@ -694,10 +690,10 @@ static char *handle_reference_list_value(struct dmctx *ctx, struct json_object *
|
|||
|
||||
list_ref[0] = 0;
|
||||
uci_foreach_element(list, e) {
|
||||
char *ref = handle_reference_value(ctx, linker_jobj, "@list", e->name);
|
||||
char *ref = handle_reference_value(ctx, linker_jobj, e->name);
|
||||
|
||||
if (DM_STRLEN(ref))
|
||||
pos += snprintf(&list_ref[pos], sizeof(list_ref) - pos, "%s;", ref);
|
||||
pos += snprintf(&list_ref[pos], sizeof(list_ref) - pos, "%s,", ref);
|
||||
}
|
||||
|
||||
if (pos)
|
||||
|
|
@ -783,7 +779,7 @@ static char *uci_get_value(json_object *mapping_obj, int json_version, char *ref
|
|||
dmuci_get_option_value_string(json_object_get_string(file), uci_type, opt_temp, &res);
|
||||
|
||||
if (linker_jobj)
|
||||
value = handle_reference_value(ctx, linker_jobj, "@key", res);
|
||||
value = handle_reference_value(ctx, linker_jobj, res);
|
||||
else
|
||||
value = res;
|
||||
} else {
|
||||
|
|
@ -807,7 +803,7 @@ static char *uci_get_value(json_object *mapping_obj, int json_version, char *ref
|
|||
dmuci_get_option_value_string(json_object_get_string(file), json_object_get_string(section_name), opt_temp, &res);
|
||||
|
||||
if (linker_jobj) {
|
||||
value = handle_reference_value(ctx, linker_jobj, "@key", res);
|
||||
value = handle_reference_value(ctx, linker_jobj, res);
|
||||
} else {
|
||||
value = res;
|
||||
}
|
||||
|
|
@ -947,7 +943,7 @@ static char *uci_v1_get_value(json_object *mapping_obj, char *refparam, struct d
|
|||
|
||||
dmuci_get_value_by_section_string(req_sec, key_value, &res);
|
||||
if (linker_jobj) {
|
||||
value = handle_reference_value(ctx, linker_jobj, "@key", res);
|
||||
value = handle_reference_value(ctx, linker_jobj, res);
|
||||
} else {
|
||||
value = res;
|
||||
}
|
||||
|
|
@ -978,7 +974,7 @@ static char *ubus_v1_get_value(json_object *mapping_obj, char *refparam, struct
|
|||
json_object *json_obj = get_requested_json_obj(((struct dm_data *)data)->json_object, instance, json_object_get_string(key), key_name, sizeof(key_name));
|
||||
res = dmjson_get_value(json_obj, 1, key_name);
|
||||
if (linker_jobj) {
|
||||
value = handle_reference_value(ctx, linker_jobj, "@key", res);
|
||||
value = handle_reference_value(ctx, linker_jobj, res);
|
||||
} else {
|
||||
value = res;
|
||||
}
|
||||
|
|
@ -1424,7 +1420,7 @@ static int uci_set_value(json_object *mapping_obj, int json_version, char *refpa
|
|||
struct json_object *list_name = NULL;
|
||||
struct json_object *linker_jobj = NULL;
|
||||
char *opt_temp = NULL;
|
||||
char buf_value[1024] = {0};
|
||||
char buf_value[4096] = {0};
|
||||
int res = 0;
|
||||
|
||||
json_object_object_get_ex(mapping_obj, "uci", &uci_obj);
|
||||
|
|
@ -1598,7 +1594,7 @@ static int uci_v1_set_value(json_object *mapping_obj, int json_version, char *re
|
|||
{
|
||||
struct json_object *data_s = NULL;
|
||||
struct json_object *key = NULL, *list = NULL, *linker_jobj = NULL;
|
||||
char buf_value[1024] = {0};
|
||||
char buf_value[4096] = {0};
|
||||
|
||||
json_object_object_get_ex(mapping_obj, "data", &data_s);
|
||||
json_object_object_get_ex(mapping_obj, "key", &key);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
bool bbfdm_folder_exists(const char *path)
|
||||
|
|
@ -25,6 +26,17 @@ bool bbfdm_folder_exists(const char *path)
|
|||
return stat(path, &buffer) == 0 && S_ISDIR(buffer.st_mode);
|
||||
}
|
||||
|
||||
bool bbfdm_ensure_folder_exists(const char *path)
|
||||
{
|
||||
if (bbfdm_folder_exists(path))
|
||||
return true;
|
||||
|
||||
if (mkdir(path, 0755) == 0 || errno == EEXIST)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bbfdm_file_exists(const char *path)
|
||||
{
|
||||
struct stat buffer;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,18 @@ extern "C" {
|
|||
*/
|
||||
bool bbfdm_folder_exists(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Ensure that a folder exists at the given path.
|
||||
*
|
||||
* This function checks whether a folder exists at the specified path. If it does not,
|
||||
* the function attempts to create it. If the folder already exists or is successfully
|
||||
* created, the function returns true.
|
||||
*
|
||||
* @param[in] path Path to the folder.
|
||||
* @return true if the folder exists or is successfully created, false otherwise.
|
||||
*/
|
||||
bool bbfdm_ensure_folder_exists(const char *path);
|
||||
|
||||
/**
|
||||
* @brief Check if a file exists at the given path.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ int bbfdm_ubus_invoke_sync(struct bbfdm_ctx *bbfdm_ctx, const char *obj, const c
|
|||
}
|
||||
|
||||
if (ubus_lookup_id(bbfdm_ctx->ubus_ctx, obj, &id)) {
|
||||
BBFDM_ERR("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
BBFDM_WARNING("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ int bbfdm_ubus_invoke_async(struct ubus_context *ubus_ctx, const char *obj, cons
|
|||
}
|
||||
|
||||
if (ubus_lookup_id(ubus_ctx, obj, &id)) {
|
||||
BBFDM_ERR("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
BBFDM_WARNING("Failed to lookup UBUS object ID for '%s' using method '%s'", obj, method);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,6 @@ IF(${BBFDMD_MAX_MSG_LEN})
|
|||
ADD_DEFINITIONS(-DBBFDM_MAX_MSG_LEN=${BBFDMD_MAX_MSG_LEN})
|
||||
ENDIF()
|
||||
|
||||
OPTION(BBF_SCHEMA_FULL_TREE "build with schema full tree" OFF)
|
||||
|
||||
IF(BBF_SCHEMA_FULL_TREE)
|
||||
add_compile_definitions(BBF_SCHEMA_FULL_TREE)
|
||||
ENDIF(BBF_SCHEMA_FULL_TREE)
|
||||
|
||||
FILE(GLOB BBF_UBUS_SOURCES *.c)
|
||||
|
||||
ADD_LIBRARY(bbfdm-ubus SHARED ${BBF_UBUS_SOURCES})
|
||||
|
|
|
|||
|
|
@ -35,6 +35,17 @@ static int bbfdm_add_object(bbfdm_data_t *data)
|
|||
|
||||
blobmsg_close_array(&data->bb, array);
|
||||
|
||||
array = blobmsg_open_array(&data->bb, "modified_uci");
|
||||
|
||||
if (data->bbf_ctx.modified_uci_head != NULL) {
|
||||
struct dm_modified_uci *m;
|
||||
list_for_each_entry(m, data->bbf_ctx.modified_uci_head, list) {
|
||||
bb_add_string(&data->bb, "", m->uci_file);
|
||||
}
|
||||
}
|
||||
|
||||
blobmsg_close_array(&data->bb, array);
|
||||
|
||||
return fault;
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +78,17 @@ static int bbfdm_del_object(bbfdm_data_t *data)
|
|||
|
||||
blobmsg_close_array(&data->bb, array);
|
||||
|
||||
array = blobmsg_open_array(&data->bb, "modified_uci");
|
||||
|
||||
if (data->bbf_ctx.modified_uci_head != NULL) {
|
||||
struct dm_modified_uci *m;
|
||||
list_for_each_entry(m, data->bbf_ctx.modified_uci_head, list) {
|
||||
bb_add_string(&data->bb, "", m->uci_file);
|
||||
}
|
||||
}
|
||||
|
||||
blobmsg_close_array(&data->bb, array);
|
||||
|
||||
return fault;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
enum {
|
||||
DM_ADD_PATH,
|
||||
DM_ADD_OBJ_PATH,
|
||||
DM_ADD_OPTIONAL,
|
||||
__DM_ADD_MAX
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/file.h>
|
||||
#include <libubox/blobmsg.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubus.h>
|
||||
|
|
@ -28,17 +31,23 @@
|
|||
#include "plugin.h"
|
||||
|
||||
#define BBFDM_DEFAULT_MICROSERVICE_MODULE_PATH "/usr/share/bbfdm/micro_services"
|
||||
#define BBFDM_SERVICE_CONFIG_PATH "/etc/bbfdm/services"
|
||||
|
||||
// Global variables
|
||||
static void *deamon_lib_handle = NULL;
|
||||
static uint8_t s_log_level = 0xff;
|
||||
|
||||
static void bbfdm_ctx_init(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
INIT_LIST_HEAD(&bbfdm_ctx->event_handlers);
|
||||
INIT_LIST_HEAD(&bbfdm_ctx->config.apply_handlers);
|
||||
INIT_LIST_HEAD(&bbfdm_ctx->changed_uci);
|
||||
}
|
||||
|
||||
static void bbfdm_ctx_cleanup(struct bbfdm_context *u)
|
||||
{
|
||||
bbf_global_clean(DEAMON_DM_ROOT_OBJ);
|
||||
|
||||
free_path_list(&u->linker_list);
|
||||
free_path_list(&u->obj_list);
|
||||
|
||||
/* DotSo Plugin */
|
||||
bbfdm_free_dotso_plugin(u, &deamon_lib_handle);
|
||||
|
||||
|
|
@ -75,42 +84,6 @@ static void fill_optional_data(bbfdm_data_t *data, struct blob_attr *msg)
|
|||
}
|
||||
}
|
||||
|
||||
static char *get_value_by_reference_path(struct dmctx *ctx, char *reference_path)
|
||||
{
|
||||
char path[MAX_DM_PATH] = {0};
|
||||
char key_name[256], key_value[256];
|
||||
char *reference_value = NULL;
|
||||
regmatch_t pmatch[2];
|
||||
|
||||
if (!ctx || !reference_path)
|
||||
return NULL;
|
||||
|
||||
if (!match(reference_path, "\\[(.*?)\\]", 2, pmatch))
|
||||
return NULL;
|
||||
|
||||
snprintf(path, pmatch[0].rm_so + 1, "%s", reference_path);
|
||||
int len = DM_STRLEN(path);
|
||||
if (!len)
|
||||
return NULL;
|
||||
|
||||
char *match_str = reference_path + pmatch[1].rm_so;
|
||||
if (DM_STRLEN(match_str) == 0)
|
||||
return NULL;
|
||||
|
||||
int n = sscanf(match_str, "%255[^=]==\"%255[^\"]\"", key_name, key_value);
|
||||
if (n != 2) {
|
||||
n = sscanf(match_str, "%255[^=]==%255[^]]", key_name, key_value);
|
||||
if (n != 2)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(path + len, sizeof(path) - len, "*.%s", key_name);
|
||||
|
||||
adm_entry_get_reference_param(ctx, path, key_value, &reference_value);
|
||||
|
||||
return reference_value;
|
||||
}
|
||||
|
||||
static void async_req_free(struct bbfdm_async_req *r)
|
||||
{
|
||||
free(r);
|
||||
|
|
@ -168,7 +141,7 @@ static int bbfdm_start_deferred(bbfdm_data_t *data, void (*EXEC_CB)(bbfdm_data_t
|
|||
BBF_ERR("fork error");
|
||||
goto err_out;
|
||||
} else if (child == 0) {
|
||||
u = container_of(data->ctx, struct bbfdm_context, ubus_ctx);
|
||||
u = container_of(data->obj, struct bbfdm_context, ubus_obj);
|
||||
if (u == NULL) {
|
||||
BBF_ERR("{fork} Failed to get the bbfdm context");
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
@ -176,7 +149,7 @@ static int bbfdm_start_deferred(bbfdm_data_t *data, void (*EXEC_CB)(bbfdm_data_t
|
|||
|
||||
/* free fd's and memory inherited from parent */
|
||||
uloop_done();
|
||||
ubus_shutdown(data->ctx);
|
||||
ubus_free(data->ctx);
|
||||
async_req_free(r);
|
||||
fclose(stdin);
|
||||
fclose(stdout);
|
||||
|
|
@ -255,7 +228,7 @@ static const struct blobmsg_policy dm_schema_policy[] = {
|
|||
[DM_SCHEMA_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE},
|
||||
};
|
||||
|
||||
static int bbfdm_schema_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)),
|
||||
static int bbfdm_schema_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method __attribute__((unused)),
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
|
|
@ -266,7 +239,7 @@ static int bbfdm_schema_handler(struct ubus_context *ctx, struct ubus_object *ob
|
|||
|
||||
memset(&data, 0, sizeof(bbfdm_data_t));
|
||||
|
||||
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
||||
u = container_of(obj, struct bbfdm_context, ubus_obj);
|
||||
if (u == NULL) {
|
||||
BBF_ERR("Failed to get the bbfdm context");
|
||||
return UBUS_STATUS_UNKNOWN_ERROR;
|
||||
|
|
@ -295,10 +268,6 @@ static int bbfdm_schema_handler(struct ubus_context *ctx, struct ubus_object *ob
|
|||
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(&data, BBF_SCHEMA);
|
||||
#else
|
||||
if (dm_type == BBFDM_CWMP) {
|
||||
char *service_name = strdup(u->config.service_name);
|
||||
data.bbf_ctx.in_value = (dm_type == BBFDM_CWMP) ? service_name : NULL;
|
||||
|
|
@ -307,7 +276,6 @@ static int bbfdm_schema_handler(struct ubus_context *ctx, struct ubus_object *ob
|
|||
} else {
|
||||
bbfdm_get(&data, BBF_SCHEMA);
|
||||
}
|
||||
#endif
|
||||
|
||||
free_path_list(&paths_list);
|
||||
return 0;
|
||||
|
|
@ -345,6 +313,8 @@ static int bbfdm_instances_handler(struct ubus_context *ctx, struct ubus_object
|
|||
|
||||
fill_optional_data(&data, tb[DM_INSTANCES_OPTIONAL]);
|
||||
|
||||
bbfdm_refresh_references(data.bbf_ctx.dm_type, obj->name);
|
||||
|
||||
bbfdm_get(&data, BBF_INSTANCES);
|
||||
|
||||
free_path_list(&paths_list);
|
||||
|
|
@ -387,6 +357,9 @@ int bbfdm_set_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
|||
|
||||
fill_optional_data(&data, tb[DM_SET_OPTIONAL]);
|
||||
|
||||
// Make sure to refresh references db before calling set method to ensure that all references are stored in the database
|
||||
bbfdm_refresh_references(data.bbf_ctx.dm_type, obj->name);
|
||||
|
||||
BBF_INFO("ubus method|%s|, name|%s|, path(%s)", method, obj->name, path);
|
||||
|
||||
blob_buf_init(&data.bb, 0);
|
||||
|
|
@ -394,13 +367,13 @@ int bbfdm_set_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
|||
fault = fill_pvlist_set(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);
|
||||
fill_err_code_array(&data, &data.bb, 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);
|
||||
fill_err_code_array(&data, &data.bb, USP_FAULT_INTERNAL_ERROR);
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
|
@ -415,6 +388,10 @@ int bbfdm_set_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
|||
|
||||
bbf_cleanup(&data.bbf_ctx);
|
||||
|
||||
if (!fault) {
|
||||
bbfdm_refresh_references(data.bbf_ctx.dm_type, obj->name);
|
||||
}
|
||||
|
||||
end:
|
||||
free_pv_list(&pv_list);
|
||||
|
||||
|
|
@ -431,7 +408,7 @@ static const struct blobmsg_policy dm_operate_policy[__DM_OPERATE_MAX] = {
|
|||
[DM_OPERATE_OPTIONAL] = { .name = "optional", .type = BLOBMSG_TYPE_TABLE },
|
||||
};
|
||||
|
||||
static int bbfdm_operate_handler(struct ubus_context *ctx, struct ubus_object *obj __attribute__((unused)),
|
||||
static int bbfdm_operate_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
struct ubus_request_data *req, const char *method __attribute__((unused)),
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
|
|
@ -454,6 +431,7 @@ static int bbfdm_operate_handler(struct ubus_context *ctx, struct ubus_object *o
|
|||
|
||||
data.ctx = ctx;
|
||||
data.req = req;
|
||||
data.obj = obj;
|
||||
data.bbf_ctx.in_param = path;
|
||||
data.bbf_ctx.linker = tb[DM_OPERATE_COMMAND_KEY] ? blobmsg_get_string(tb[DM_OPERATE_COMMAND_KEY]) : "";
|
||||
|
||||
|
|
@ -478,7 +456,6 @@ static int bbfdm_operate_handler(struct ubus_context *ctx, struct ubus_object *o
|
|||
|
||||
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 },
|
||||
};
|
||||
|
||||
|
|
@ -519,26 +496,6 @@ int bbfdm_add_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
|||
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(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) {
|
||||
bbf_entry_services(data.bbf_ctx.dm_type, (!fault) ? true : false, true);
|
||||
|
|
@ -546,6 +503,10 @@ end:
|
|||
|
||||
bbf_cleanup(&data.bbf_ctx);
|
||||
|
||||
if (!fault) {
|
||||
bbfdm_refresh_references(data.bbf_ctx.dm_type, obj->name);
|
||||
}
|
||||
|
||||
ubus_send_reply(ctx, req, data.bb.head);
|
||||
blob_buf_free(&data.bb);
|
||||
|
||||
|
|
@ -615,113 +576,30 @@ int bbfdm_del_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
|||
bbf_cleanup(&data.bbf_ctx);
|
||||
free_path_list(&paths_list);
|
||||
|
||||
if (!fault) {
|
||||
bbfdm_refresh_references(data.bbf_ctx.dm_type, obj->name);
|
||||
}
|
||||
|
||||
ubus_send_reply(ctx, req, data.bb.head);
|
||||
blob_buf_free(&data.bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bbfdm_ref_path_handler(struct ubus_context *ctx, struct ubus_object *obj,
|
||||
int bbfdm_refresh_references_db(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_GET_MAX];
|
||||
struct bbfdm_context *u;
|
||||
struct pvNode *node = NULL;
|
||||
struct blob_buf bb;
|
||||
bool reference_value_found = false;
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
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])
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BBFDM_INFO("ubus method|%s|, name|%s|", method, obj->name);
|
||||
|
||||
char *path = blobmsg_get_string(tb[DM_GET_PATH]);
|
||||
|
||||
if (!match_with_path_list(&u->obj_list, path))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
BBF_INFO("ubus method|%s|, name|%s|", method, obj->name);
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
list_for_each_entry(node, &u->linker_list, list) {
|
||||
if (strcmp(node->param, path) == 0) {
|
||||
bb_add_string(&bb, "data", node->val);
|
||||
reference_value_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int res = bbfdm_refresh_references(BBFDM_BOTH, obj->name);
|
||||
|
||||
if (!reference_value_found) {
|
||||
struct dmctx bbf_ctx = {0};
|
||||
|
||||
bbf_init(&bbf_ctx);
|
||||
char *reference_path = get_value_by_reference_path(&bbf_ctx, path);
|
||||
|
||||
add_pv_list(path, reference_path, NULL, &u->linker_list);
|
||||
bb_add_string(&bb, "data", reference_path ? reference_path : "");
|
||||
|
||||
bbf_cleanup(&bbf_ctx);
|
||||
}
|
||||
|
||||
ubus_send_reply(ctx, req, bb.head);
|
||||
blob_buf_free(&bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bbfdm_ref_value_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_GET_MAX];
|
||||
struct bbfdm_context *u;
|
||||
struct dmctx bbf_ctx = {0};
|
||||
char *reference_value = NULL;
|
||||
struct blob_buf bb;
|
||||
|
||||
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])
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
BBFDM_INFO("ubus method|%s|, name|%s|", method, obj->name);
|
||||
|
||||
char *reference_path = blobmsg_get_string(tb[DM_GET_PATH]);
|
||||
|
||||
if (!match_with_path_list(&u->obj_list, reference_path))
|
||||
return UBUS_STATUS_INVALID_ARGUMENT;
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
bbf_init(&bbf_ctx);
|
||||
|
||||
adm_entry_get_reference_value(&bbf_ctx, reference_path, &reference_value);
|
||||
|
||||
bb_add_string(&bb, "data", reference_value ? reference_value : "");
|
||||
|
||||
bbf_cleanup(&bbf_ctx);
|
||||
blobmsg_add_u8(&bb, "status", !res ? true : false);
|
||||
|
||||
ubus_send_reply(ctx, req, bb.head);
|
||||
blob_buf_free(&bb);
|
||||
|
|
@ -737,103 +615,147 @@ static struct ubus_method bbf_methods[] = {
|
|||
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("reference_path", bbfdm_ref_path_handler, dm_get_policy),
|
||||
UBUS_METHOD("reference_value", bbfdm_ref_value_handler, dm_get_policy),
|
||||
UBUS_METHOD_NOARG("refresh_references_db", bbfdm_refresh_references_db)
|
||||
};
|
||||
|
||||
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 int regiter_ubus_object(struct ubus_context *ctx)
|
||||
static int regiter_ubus_object(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
struct bbfdm_context *u;
|
||||
bbfdm_ctx->ubus_obj.name = bbfdm_ctx->config.out_name;
|
||||
bbfdm_ctx->ubus_obj.type = &bbf_type;
|
||||
bbfdm_ctx->ubus_obj.type->name = bbfdm_ctx->config.out_name;
|
||||
bbfdm_ctx->ubus_obj.methods = bbf_methods;
|
||||
bbfdm_ctx->ubus_obj.n_methods = ARRAY_SIZE(bbf_methods);
|
||||
|
||||
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
||||
if (u == NULL) {
|
||||
BBF_ERR("failed to get the bbfdm context");
|
||||
return ubus_add_object(bbfdm_ctx->ubus_ctx, &bbfdm_ctx->ubus_obj);
|
||||
}
|
||||
|
||||
static void free_apply_handlers(bbfdm_config_t *config)
|
||||
{
|
||||
struct apply_handler_node *node = NULL, *tmp = NULL;
|
||||
|
||||
if (config == NULL)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(node, tmp, &config->apply_handlers, list) {
|
||||
list_del(&node->list);
|
||||
BBFDM_FREE(node->file_path);
|
||||
BBFDM_FREE(node);
|
||||
}
|
||||
}
|
||||
|
||||
static void free_changed_uci(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
struct apply_handler_node *node = NULL, *tmp = NULL;
|
||||
|
||||
if (bbfdm_ctx == NULL)
|
||||
return;
|
||||
|
||||
memset(bbfdm_ctx->uci_change_proto, 0, sizeof(bbfdm_ctx->uci_change_proto));
|
||||
|
||||
list_for_each_entry_safe(node, tmp, &bbfdm_ctx->changed_uci, list) {
|
||||
list_del(&node->list);
|
||||
BBFDM_FREE(node->file_path);
|
||||
BBFDM_FREE(node);
|
||||
}
|
||||
}
|
||||
|
||||
static int load_apply_handlers_from_file(bbfdm_config_t *config)
|
||||
{
|
||||
char serv_config[MAX_DM_PATH] = {0};
|
||||
|
||||
if (config == NULL) {
|
||||
BBF_ERR("bbfdm_config is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
bbf_object.name = u->config.out_name;
|
||||
bbf_object.type->name = u->config.out_name;
|
||||
|
||||
return ubus_add_object(ctx, &bbf_object);
|
||||
}
|
||||
|
||||
static void send_linker_response_event(struct ubus_context *ctx, const char *reference_path, const char *reference_value)
|
||||
{
|
||||
struct blob_buf bb;
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
bb_add_string(&bb, reference_path, reference_value ? reference_value : "");
|
||||
|
||||
ubus_send_event(ctx, "bbfdm.linker.response", bb.head);
|
||||
blob_buf_free(&bb);
|
||||
}
|
||||
|
||||
static void bbfdm_linker_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
{
|
||||
if (!type || !msg)
|
||||
return;
|
||||
|
||||
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;
|
||||
snprintf(serv_config, sizeof(serv_config), "%s/%s.json", BBFDM_SERVICE_CONFIG_PATH, config->service_name);
|
||||
if (!bbfdm_file_exists(serv_config) || !bbfdm_is_regular_file(serv_config)) {
|
||||
BBF_ERR("Config file %s not exists for service %s", serv_config, config->service_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(type, "bbfdm.linker.cleanup") == 0) {
|
||||
//BBF_ERR("bbfdm.linker.cleanup");
|
||||
free_pv_list(&u->linker_list);
|
||||
} else if (strcmp(type, "bbfdm.linker.request") == 0) {
|
||||
//BBF_ERR("bbfdm.linker.request");
|
||||
|
||||
struct dmctx bbf_ctx = {0};
|
||||
struct blob_attr *tb[1] = {0};
|
||||
const struct blobmsg_policy p[1] = {
|
||||
{ "path", BLOBMSG_TYPE_STRING }
|
||||
};
|
||||
|
||||
blobmsg_parse(p, 1, tb, blobmsg_data(msg), blobmsg_len(msg));
|
||||
|
||||
char *reference_path = tb[0] ? blobmsg_get_string(tb[0]) : "";
|
||||
|
||||
if (DM_STRLEN(reference_path) == 0)
|
||||
return;
|
||||
|
||||
if (!match_with_path_list(&u->obj_list, reference_path))
|
||||
return;
|
||||
|
||||
if (present_in_pv_list(&u->linker_list, reference_path))
|
||||
return;
|
||||
|
||||
bbf_init(&bbf_ctx);
|
||||
|
||||
char *reference_value = get_value_by_reference_path(&bbf_ctx, reference_path);
|
||||
|
||||
add_pv_list(reference_path, reference_value, NULL, &u->linker_list);
|
||||
send_linker_response_event(ctx, reference_path, reference_value);
|
||||
|
||||
bbf_cleanup(&bbf_ctx);
|
||||
json_object *json_root = json_object_from_file(serv_config);
|
||||
if (!json_root) {
|
||||
BBF_ERR("Failed to read json file %s", serv_config);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void bbfdm_ctx_init(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
INIT_LIST_HEAD(&bbfdm_ctx->linker_list);
|
||||
INIT_LIST_HEAD(&bbfdm_ctx->obj_list);
|
||||
INIT_LIST_HEAD(&bbfdm_ctx->event_handlers);
|
||||
json_object *daemon_config = NULL;
|
||||
json_object_object_get_ex(json_root, "daemon", &daemon_config);
|
||||
if (!daemon_config) {
|
||||
BBFDM_ERR("Failed to find daemon object");
|
||||
json_object_put(json_root);
|
||||
return -1;
|
||||
}
|
||||
|
||||
json_object *apply_handler = NULL;
|
||||
json_object_object_get_ex(daemon_config, "apply_handler", &apply_handler);
|
||||
if (!apply_handler) {
|
||||
json_object_put(json_root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
char type[2][8] = { "dmmap", "uci" };
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
json_object *array = NULL;
|
||||
|
||||
if (!json_object_object_get_ex(apply_handler, type[i], &array) ||
|
||||
json_object_get_type(array) != json_type_array) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t count = json_object_array_length(array);
|
||||
for (size_t j = 0; j < count; j++) {
|
||||
json_object *hndl_obj = json_object_array_get_idx(array, j);
|
||||
json_object *files = NULL;
|
||||
|
||||
json_object_object_get_ex(hndl_obj, "file", &files);
|
||||
if (!files || json_object_get_type(files) != json_type_array) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t f_count = json_object_array_length(files);
|
||||
for (size_t k = 0; k < f_count; k++) {
|
||||
char path[MAX_DM_PATH] = {0};
|
||||
|
||||
json_object *f_inst = json_object_array_get_idx(files, k);
|
||||
snprintf(path, sizeof(path), "/etc/%s/%s",
|
||||
(strcmp(type[i], "uci") == 0) ? "config" : "bbfdm/dmmap", json_object_get_string(f_inst));
|
||||
|
||||
// check if already present
|
||||
bool exist = false;
|
||||
struct apply_handler_node *node = NULL;
|
||||
list_for_each_entry(node, &config->apply_handlers, list) {
|
||||
if (DM_STRCMP(node->file_path, path) == 0) {
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exist == true)
|
||||
continue;
|
||||
|
||||
node = (struct apply_handler_node *)calloc(1, sizeof(struct apply_handler_node));
|
||||
if (node == NULL) {
|
||||
BBFDM_ERR("Failed to allocate memory for apply handlers");
|
||||
free_apply_handlers(config);
|
||||
json_object_put(json_root);
|
||||
return -1;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&node->list);
|
||||
list_add_tail(&node->list, &config->apply_handlers);
|
||||
|
||||
node->file_path = strdup(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(json_root);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_micro_service_config(bbfdm_config_t *config)
|
||||
|
|
@ -845,6 +767,11 @@ static int load_micro_service_config(bbfdm_config_t *config)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (load_apply_handlers_from_file(config) != 0) {
|
||||
BBF_ERR("Failed to load handlers from service file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (INTERNAL_ROOT_TREE == NULL) {
|
||||
// This API will only be called with micro-services started with '-m' option
|
||||
|
||||
|
|
@ -895,20 +822,208 @@ static int load_micro_service_data_model(struct bbfdm_context *daemon_ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct ubus_event_handler bbfdm_linker_handler = { .cb = bbfdm_linker_cb };
|
||||
|
||||
int bbfdm_ubus_regiter_init(struct bbfdm_context *bbfdm_ctx)
|
||||
int bbfdm_print_data_model_schema(struct bbfdm_context *bbfdm_ctx, const enum bbfdm_type_enum type)
|
||||
{
|
||||
struct dmctx bbf_ctx = {
|
||||
.in_param = ROOT_NODE,
|
||||
.nextlevel = false,
|
||||
.iscommand = true,
|
||||
.isevent = true,
|
||||
.isinfo = true,
|
||||
.dm_type = type
|
||||
};
|
||||
int err = 0;
|
||||
|
||||
err = ubus_connect_ctx(&bbfdm_ctx->ubus_ctx, NULL);
|
||||
if (err != UBUS_STATUS_OK) {
|
||||
bbfdm_ctx_init(bbfdm_ctx);
|
||||
|
||||
err = load_micro_service_config(&bbfdm_ctx->config);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to load micro-service config\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = load_micro_service_data_model(bbfdm_ctx);
|
||||
if (err) {
|
||||
fprintf(stderr, "Failed to load micro-service data model\n");
|
||||
bbfdm_ctx_cleanup(bbfdm_ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
bbf_init(&bbf_ctx);
|
||||
|
||||
err = bbf_entry_method(&bbf_ctx, BBF_SCHEMA);
|
||||
if (!err) {
|
||||
struct blob_attr *cur = NULL;
|
||||
size_t rem = 0;
|
||||
|
||||
blobmsg_for_each_attr(cur, bbf_ctx.bb.head, rem) {
|
||||
struct blob_attr *tb[3] = {0};
|
||||
const struct blobmsg_policy p[3] = {
|
||||
{ "path", BLOBMSG_TYPE_STRING },
|
||||
{ "data", BLOBMSG_TYPE_STRING },
|
||||
{ "type", BLOBMSG_TYPE_STRING }
|
||||
};
|
||||
|
||||
blobmsg_parse(p, 3, 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]) : "";
|
||||
char *type = (tb[2]) ? blobmsg_get_string(tb[2]) : "";
|
||||
|
||||
printf("%s %s %s\n", name, type, strlen(data) ? data : "0");
|
||||
}
|
||||
} else {
|
||||
printf("ERROR: %d retrieving %s\n", err, ROOT_NODE);
|
||||
err = -1;
|
||||
}
|
||||
|
||||
bbf_cleanup(&bbf_ctx);
|
||||
|
||||
free_apply_handlers(&bbfdm_ctx->config);
|
||||
free_changed_uci(bbfdm_ctx);
|
||||
|
||||
bbfdm_ctx_cleanup(bbfdm_ctx);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void perform_uci_sync_op(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
DM_MAP_OBJ *dynamic_obj = INTERNAL_ROOT_TREE;
|
||||
|
||||
if (dynamic_obj == NULL || bbfdm_ctx == NULL)
|
||||
return;
|
||||
|
||||
for (int i = 0; dynamic_obj[i].path; i++) {
|
||||
if (dynamic_obj[i].uci_sync_handler) {
|
||||
dynamic_obj[i].uci_sync_handler(bbfdm_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
free_changed_uci(bbfdm_ctx);
|
||||
|
||||
if (bbfdm_refresh_references(BBFDM_BOTH, bbfdm_ctx->config.out_name)) {
|
||||
BBF_ERR("Failed to refresh instance data base");
|
||||
}
|
||||
}
|
||||
|
||||
static void bbfdm_apply_event_cb(struct ubus_context *ctx __attribute__((unused)),
|
||||
struct ubus_event_handler *ev,
|
||||
const char *type __attribute__((unused)),
|
||||
struct blob_attr *msg)
|
||||
{
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
struct bbfdm_context *bbfdm_ctx = container_of(ev, struct bbfdm_context, apply_event);
|
||||
if (bbfdm_ctx == NULL)
|
||||
return;
|
||||
|
||||
bbfdm_config_t *config = &bbfdm_ctx->config;
|
||||
|
||||
const struct blobmsg_policy p[2] = {
|
||||
{ "proto", BLOBMSG_TYPE_STRING },
|
||||
{ "uci_changed", BLOBMSG_TYPE_ARRAY }
|
||||
};
|
||||
|
||||
struct blob_attr *tb[2] = {NULL, NULL};
|
||||
blobmsg_parse(p, 2, tb, blob_data(msg), blob_len(msg));
|
||||
|
||||
if (!tb[0] || !tb[1])
|
||||
return;
|
||||
|
||||
const char *proto = blobmsg_get_string(tb[0]);
|
||||
struct blob_attr *attr = NULL;
|
||||
int rem = 0;
|
||||
|
||||
blobmsg_for_each_attr(attr, tb[1], rem) {
|
||||
char *conf_name = blobmsg_get_string(attr);
|
||||
|
||||
/* Now check if the config file is intended file */
|
||||
struct apply_handler_node *node = NULL;
|
||||
|
||||
list_for_each_entry(node, &config->apply_handlers, list) {
|
||||
if (DM_STRCMP(node->file_path, conf_name) != 0)
|
||||
continue;
|
||||
|
||||
bool exist = false;
|
||||
struct apply_handler_node *uci_node = NULL;
|
||||
list_for_each_entry(uci_node, &bbfdm_ctx->changed_uci, list) {
|
||||
if (DM_STRCMP(uci_node->file_path, conf_name) == 0) {
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exist == true)
|
||||
break;
|
||||
|
||||
uci_node = (struct apply_handler_node *)calloc(1, sizeof(struct apply_handler_node));
|
||||
if (uci_node == NULL) {
|
||||
BBFDM_ERR("Failed to allocate memory for changed uci list");
|
||||
break;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&uci_node->list);
|
||||
list_add_tail(&uci_node->list, &bbfdm_ctx->changed_uci);
|
||||
uci_node->file_path = strdup(conf_name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!list_empty(&bbfdm_ctx->changed_uci)) {
|
||||
BBF_INFO("Scheduling UCI sync operation for changes performed by %s", proto);
|
||||
if (DM_STRLEN(bbfdm_ctx->uci_change_proto) != 0) {
|
||||
BBFDM_ERR("!!! Overwritting proto from %s to %s for UCI sync", bbfdm_ctx->uci_change_proto, proto);
|
||||
}
|
||||
|
||||
snprintf(bbfdm_ctx->uci_change_proto, sizeof(bbfdm_ctx->uci_change_proto), "%s", proto);
|
||||
perform_uci_sync_op(bbfdm_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static int register_bbfdm_apply_event(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
if (bbfdm_ctx == NULL)
|
||||
return -1;
|
||||
|
||||
memset(&bbfdm_ctx->apply_event, 0, sizeof(struct ubus_event_handler));
|
||||
bbfdm_ctx->apply_event.cb = bbfdm_apply_event_cb;
|
||||
|
||||
ubus_register_event_handler(bbfdm_ctx->ubus_ctx, &bbfdm_ctx->apply_event, "bbfdm.apply");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bbfdm_ubus_init(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
bbfdm_ctx->ubus_ctx = ubus_connect(NULL);
|
||||
if (!bbfdm_ctx->ubus_ctx) {
|
||||
BBF_ERR("Failed to connect to ubus");
|
||||
return -5; // Error code -5 indicating that ubus_ctx is connected
|
||||
return -1;
|
||||
}
|
||||
|
||||
uloop_init();
|
||||
ubus_add_uloop(&bbfdm_ctx->ubus_ctx);
|
||||
ubus_add_uloop(bbfdm_ctx->ubus_ctx);
|
||||
bbfdm_ctx->internal_ubus_ctx = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bbfdm_ubus_register_init(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
// Set the logmask with default, if not already set by api
|
||||
if (s_log_level == 0xff) {
|
||||
BBF_INFO("Log level not set, setting default value %d", LOG_ERR);
|
||||
bbfdm_ubus_set_log_level(LOG_ERR);
|
||||
}
|
||||
|
||||
if (bbfdm_ctx->ubus_ctx == NULL) {
|
||||
err = bbfdm_ubus_init(bbfdm_ctx);
|
||||
if (err) {
|
||||
BBF_ERR("Failed to initialize ubus_ctx internally");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
bbfdm_ctx_init(bbfdm_ctx);
|
||||
|
||||
|
|
@ -924,25 +1039,51 @@ int bbfdm_ubus_regiter_init(struct bbfdm_context *bbfdm_ctx)
|
|||
return err;
|
||||
}
|
||||
|
||||
err = regiter_ubus_object(&bbfdm_ctx->ubus_ctx);
|
||||
err = regiter_ubus_object(bbfdm_ctx);
|
||||
if (err != UBUS_STATUS_OK)
|
||||
return -1;
|
||||
|
||||
err = register_events_to_ubus(&bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers);
|
||||
if (err != 0)
|
||||
return err;
|
||||
err = bbfdm_refresh_references(BBFDM_BOTH, bbfdm_ctx->config.out_name);
|
||||
if (err) {
|
||||
BBF_ERR("Failed to refresh instance data base");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ubus_register_event_handler(&bbfdm_ctx->ubus_ctx, &bbfdm_linker_handler, "bbfdm.linker.*");
|
||||
err = register_bbfdm_apply_event(bbfdm_ctx);
|
||||
if (err) {
|
||||
BBF_ERR("Failed to register bbfdm apply event");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return register_events_to_ubus(bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers);
|
||||
}
|
||||
|
||||
int bbfdm_ubus_register_free(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
if (bbfdm_ctx->ubus_ctx) {
|
||||
ubus_unregister_event_handler(bbfdm_ctx->ubus_ctx, &bbfdm_ctx->apply_event);
|
||||
free_ubus_event_handler(bbfdm_ctx->ubus_ctx, &bbfdm_ctx->event_handlers);
|
||||
free_apply_handlers(&bbfdm_ctx->config);
|
||||
free_changed_uci(bbfdm_ctx);
|
||||
bbfdm_ctx_cleanup(bbfdm_ctx);
|
||||
}
|
||||
|
||||
if (bbfdm_ctx->ubus_ctx && bbfdm_ctx->internal_ubus_ctx) {
|
||||
ubus_free(bbfdm_ctx->ubus_ctx);
|
||||
uloop_done();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bbfdm_ubus_regiter_init(struct bbfdm_context *bbfdm_ctx)
|
||||
{
|
||||
return bbfdm_ubus_register_init(bbfdm_ctx);
|
||||
}
|
||||
|
||||
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;
|
||||
return bbfdm_ubus_register_free(bbfdm_ctx);
|
||||
}
|
||||
|
||||
void bbfdm_ubus_set_service_name(struct bbfdm_context *bbfdm_ctx, const char *srv_name)
|
||||
|
|
@ -954,9 +1095,29 @@ void bbfdm_ubus_set_service_name(struct bbfdm_context *bbfdm_ctx, const char *sr
|
|||
void bbfdm_ubus_set_log_level(int log_level)
|
||||
{
|
||||
setlogmask(LOG_UPTO(log_level));
|
||||
s_log_level = log_level;
|
||||
}
|
||||
|
||||
uint8_t bbfdm_ubus_get_log_level(void)
|
||||
{
|
||||
return s_log_level;
|
||||
}
|
||||
|
||||
void bbfdm_ubus_load_data_model(DM_MAP_OBJ *DynamicObj)
|
||||
{
|
||||
INTERNAL_ROOT_TREE = DynamicObj;
|
||||
}
|
||||
|
||||
int bbfdm_refresh_references(unsigned int dm_type, const char *srv_obj_name)
|
||||
{
|
||||
struct dmctx bbf_ctx = {
|
||||
.in_param = ROOT_NODE,
|
||||
.dm_type = dm_type
|
||||
};
|
||||
|
||||
bbf_init(&bbf_ctx);
|
||||
int res = bbfdm_cmd_exec(&bbf_ctx, BBF_REFERENCES_DB);
|
||||
bbf_cleanup(&bbf_ctx);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,13 @@ struct bbfdm_async_req {
|
|||
void *result;
|
||||
};
|
||||
|
||||
struct apply_handler_node {
|
||||
char *file_path;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
typedef struct bbfdm_config {
|
||||
struct list_head apply_handlers;
|
||||
char service_name[32]; // Service name for micro-service identification
|
||||
char in_name[128]; // Service plugin path
|
||||
char in_plugin_dir[128]; // Service extra/internal plugin directory path
|
||||
|
|
@ -25,32 +31,40 @@ typedef struct bbfdm_config {
|
|||
|
||||
struct bbfdm_context {
|
||||
bbfdm_config_t config;
|
||||
struct ubus_context ubus_ctx;
|
||||
struct ubus_event_handler apply_event;
|
||||
struct ubus_context *ubus_ctx;
|
||||
struct ubus_object ubus_obj;
|
||||
struct list_head event_handlers;
|
||||
struct list_head linker_list;
|
||||
struct list_head obj_list;
|
||||
};
|
||||
|
||||
struct ev_handler_node {
|
||||
char *dm_path;
|
||||
char *ev_name;
|
||||
struct ubus_event_handler *ev_handler;
|
||||
struct list_head list;
|
||||
struct list_head changed_uci;
|
||||
bool internal_ubus_ctx;
|
||||
char uci_change_proto[10];
|
||||
};
|
||||
|
||||
typedef struct bbfdm_data {
|
||||
struct ubus_context *ctx;
|
||||
struct ubus_object *obj;
|
||||
struct ubus_request_data *req;
|
||||
struct list_head *plist;
|
||||
struct dmctx bbf_ctx;
|
||||
struct blob_buf bb;
|
||||
} bbfdm_data_t;
|
||||
|
||||
int bbfdm_ubus_register_init(struct bbfdm_context *bbfdm_ctx);
|
||||
int bbfdm_ubus_register_free(struct bbfdm_context *bbfdm_ctx);
|
||||
|
||||
__attribute__((deprecated("Use bbfdm_ubus_register_init() instead of bbfdm_ubus_regiter_init()")))
|
||||
int bbfdm_ubus_regiter_init(struct bbfdm_context *bbfdm_ctx);
|
||||
|
||||
__attribute__((deprecated("Use bbfdm_ubus_register_free() instead of bbfdm_ubus_regiter_free()")))
|
||||
int bbfdm_ubus_regiter_free(struct bbfdm_context *bbfdm_ctx);
|
||||
|
||||
int bbfdm_print_data_model_schema(struct bbfdm_context *bbfdm_ctx, const enum bbfdm_type_enum type);
|
||||
|
||||
void bbfdm_ubus_set_service_name(struct bbfdm_context *bbfdm_ctx, const char *srv_name);
|
||||
|
||||
uint8_t bbfdm_ubus_get_log_level(void);
|
||||
void bbfdm_ubus_set_log_level(int log_level);
|
||||
void bbfdm_ubus_load_data_model(DM_MAP_OBJ *DynamicObj);
|
||||
int bbfdm_refresh_references(unsigned int dm_type, const char *srv_obj_name);
|
||||
|
||||
#endif /* BBFDM_UBUS_H */
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ bool validate_msglen(bbfdm_data_t *data)
|
|||
BBF_ERR("Blob exceed max len(%d), data len(%zd)", DEF_IPC_DATA_LEN, data_len);
|
||||
blob_buf_free(&data->bbf_ctx.bb);
|
||||
blob_buf_init(&data->bbf_ctx.bb, 0);
|
||||
fill_err_code_table(data, FAULT_9002);
|
||||
fill_err_code_array(data, &data->bbf_ctx.bb, FAULT_9002);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,16 +19,28 @@ struct event_args {
|
|||
char method_name[256];
|
||||
};
|
||||
|
||||
static char *get_events_dm_path(struct list_head *ev_list, const char *event)
|
||||
struct dm_path_node {
|
||||
char *dm_path;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct ev_handler_node {
|
||||
char *ev_name;
|
||||
struct ubus_event_handler ev_handler;
|
||||
struct list_head dm_paths_list; // For dm path list
|
||||
struct list_head list; // For event list
|
||||
};
|
||||
|
||||
static struct ev_handler_node *get_event_node(struct list_head *ev_list, const char *event_name)
|
||||
{
|
||||
struct ev_handler_node *iter = NULL;
|
||||
|
||||
if (ev_list == NULL || event == NULL)
|
||||
if (!ev_list || !event_name)
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(iter, ev_list, list) {
|
||||
if (iter->ev_name && strcmp(iter->ev_name, event) == 0)
|
||||
return iter->dm_path;
|
||||
if (iter->ev_name && strcmp(iter->ev_name, event_name) == 0)
|
||||
return iter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
@ -56,98 +68,128 @@ void event_callback(const void *arg1, void *arg2)
|
|||
free(e_args);
|
||||
}
|
||||
|
||||
static void bbfdm_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
static void bbfdm_event_handler_cb(struct ubus_context *ctx __attribute__((unused)), struct ubus_event_handler *ev,
|
||||
const char *type __attribute__((unused)), struct blob_attr *msg)
|
||||
{
|
||||
(void)ev;
|
||||
struct bbfdm_context *u;
|
||||
struct ev_handler_node *ev_node = NULL;
|
||||
struct dm_path_node *dp_iter = NULL;
|
||||
|
||||
u = container_of(ctx, struct bbfdm_context, ubus_ctx);
|
||||
if (u == NULL) {
|
||||
BBF_ERR("Failed to get the bbfdm context");
|
||||
ev_node = container_of(ev, struct ev_handler_node, ev_handler);
|
||||
if (!ev_node) {
|
||||
BBF_ERR("Failed to get event node");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!msg || !type)
|
||||
if (!msg) {
|
||||
BBF_ERR("Failed to get message from event");
|
||||
return;
|
||||
}
|
||||
|
||||
char *event_dm_path = get_events_dm_path(&u->event_handlers, type);
|
||||
if (event_dm_path == NULL)
|
||||
return;
|
||||
list_for_each_entry(dp_iter, &ev_node->dm_paths_list, list) {
|
||||
char dm_path[MAX_DM_PATH];
|
||||
|
||||
char dm_path[MAX_DM_PATH];
|
||||
replace_str(dp_iter->dm_path, ".{i}.", ".*.", dm_path, sizeof(dm_path));
|
||||
if (strlen(dm_path) == 0)
|
||||
return;
|
||||
|
||||
replace_str(event_dm_path, ".{i}.", ".*.", dm_path, sizeof(dm_path));
|
||||
if (strlen(dm_path) == 0)
|
||||
return;
|
||||
char *str = blobmsg_format_json(msg, true);
|
||||
|
||||
char *str = blobmsg_format_json(msg, true);
|
||||
struct dmctx bbf_ctx = {
|
||||
.in_param = dm_path,
|
||||
.in_value = str,
|
||||
.nextlevel = false,
|
||||
.iscommand = false,
|
||||
.isevent = true,
|
||||
.isinfo = false,
|
||||
.dm_type = BBFDM_USP
|
||||
};
|
||||
|
||||
struct dmctx bbf_ctx = {
|
||||
.in_param = dm_path,
|
||||
.in_value = str,
|
||||
.nextlevel = false,
|
||||
.iscommand = false,
|
||||
.isevent = true,
|
||||
.isinfo = false,
|
||||
.dm_type = BBFDM_USP
|
||||
};
|
||||
bbf_init(&bbf_ctx);
|
||||
|
||||
bbf_init(&bbf_ctx);
|
||||
|
||||
int ret = bbfdm_cmd_exec(&bbf_ctx, BBF_EVENT);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
size_t blob_data_len = blob_raw_len(bbf_ctx.bb.head);
|
||||
|
||||
if (blob_data_len) {
|
||||
struct event_args *e_args = (struct event_args *)calloc(1, sizeof(struct event_args));
|
||||
if (!e_args)
|
||||
int ret = bbfdm_cmd_exec(&bbf_ctx, BBF_EVENT);
|
||||
if (ret)
|
||||
goto end;
|
||||
|
||||
snprintf(e_args->method_name, sizeof(e_args->method_name), "%s.event", BBFDM_DEFAULT_UBUS_OBJ);
|
||||
size_t blob_data_len = blob_raw_len(bbf_ctx.bb.head);
|
||||
|
||||
e_args->blob_data = (struct blob_attr *)calloc(1, blob_data_len);
|
||||
if (blob_data_len) {
|
||||
struct event_args *e_args = (struct event_args *)calloc(1, sizeof(struct event_args));
|
||||
if (!e_args)
|
||||
goto end;
|
||||
|
||||
memcpy(e_args->blob_data, bbf_ctx.bb.head, blob_data_len);
|
||||
snprintf(e_args->method_name, sizeof(e_args->method_name), "%s.event", BBFDM_DEFAULT_UBUS_OBJ);
|
||||
|
||||
bbfdm_task_schedule(event_callback, NULL, e_args, 6);
|
||||
e_args->blob_data = (struct blob_attr *)calloc(1, blob_data_len);
|
||||
|
||||
memcpy(e_args->blob_data, bbf_ctx.bb.head, blob_data_len);
|
||||
|
||||
bbfdm_task_schedule(event_callback, NULL, e_args, 6);
|
||||
}
|
||||
|
||||
end:
|
||||
bbf_cleanup(&bbf_ctx);
|
||||
FREE(str);
|
||||
}
|
||||
|
||||
end:
|
||||
bbf_cleanup(&bbf_ctx);
|
||||
FREE(str);
|
||||
}
|
||||
|
||||
static void add_ubus_event_handler(struct ubus_event_handler *ev, const char *ev_name, const char *dm_path, struct list_head *ev_list)
|
||||
static void add_dm_path(struct ev_handler_node *node, const char *dm_path)
|
||||
{
|
||||
if (ev == NULL || ev_list == NULL)
|
||||
struct dm_path_node *dp_iter = NULL;
|
||||
|
||||
if (!node || !dm_path)
|
||||
return;
|
||||
|
||||
struct ev_handler_node *node = NULL;
|
||||
// Prevent duplicate dm_paths
|
||||
list_for_each_entry(dp_iter, &node->dm_paths_list, list) {
|
||||
if (strcmp(dp_iter->dm_path, dm_path) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
node = (struct ev_handler_node *)calloc(1, sizeof(struct ev_handler_node));
|
||||
if (!node) {
|
||||
BBF_ERR("Out of memory!");
|
||||
struct dm_path_node *dp_node = (struct dm_path_node *)calloc(1, sizeof(struct dm_path_node));
|
||||
if (!dp_node) {
|
||||
BBF_ERR("Out of memory adding DM path!");
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&node->list);
|
||||
list_add_tail(&node->list, ev_list);
|
||||
INIT_LIST_HEAD(&dp_node->list);
|
||||
|
||||
node->ev_handler = ev;
|
||||
node->ev_name = ev_name ? strdup(ev_name) : NULL;
|
||||
node->dm_path = dm_path ? strdup(dm_path) : NULL;
|
||||
dp_node->dm_path = strdup(dm_path);
|
||||
|
||||
list_add_tail(&dp_node->list, &node->dm_paths_list);
|
||||
}
|
||||
|
||||
static void add_ubus_event_handler(struct ubus_context *ctx, const char *ev_name, const char *dm_path, struct list_head *ev_list)
|
||||
{
|
||||
struct ev_handler_node *node;
|
||||
|
||||
if (!ctx || !ev_name || !dm_path || !ev_list)
|
||||
return;
|
||||
|
||||
node = get_event_node(ev_list, ev_name);
|
||||
|
||||
if (!node) {
|
||||
// Create a new event node
|
||||
node = (struct ev_handler_node *)calloc(1, sizeof(struct ev_handler_node));
|
||||
if (!node) {
|
||||
BBF_ERR("Out of memory!");
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&node->list);
|
||||
INIT_LIST_HEAD(&node->dm_paths_list);
|
||||
|
||||
list_add_tail(&node->list, ev_list);
|
||||
|
||||
node->ev_name = strdup(ev_name);
|
||||
node->ev_handler.cb = bbfdm_event_handler_cb;
|
||||
ubus_register_event_handler(ctx, &node->ev_handler, ev_name);
|
||||
}
|
||||
|
||||
add_dm_path(node, dm_path);
|
||||
}
|
||||
|
||||
int register_events_to_ubus(struct ubus_context *ctx, struct list_head *ev_list)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
if (ctx == NULL || ev_list == NULL)
|
||||
return -1;
|
||||
|
||||
struct dmctx bbf_ctx = {
|
||||
.in_param = ROOT_NODE,
|
||||
.nextlevel = false,
|
||||
|
|
@ -157,6 +199,9 @@ int register_events_to_ubus(struct ubus_context *ctx, struct list_head *ev_list)
|
|||
.dm_type = BBFDM_USP
|
||||
};
|
||||
|
||||
if (ctx == NULL || ev_list == NULL)
|
||||
return -1;
|
||||
|
||||
bbf_init(&bbf_ctx);
|
||||
|
||||
if (0 == bbfdm_cmd_exec(&bbf_ctx, BBF_SCHEMA)) {
|
||||
|
|
@ -178,50 +223,37 @@ int register_events_to_ubus(struct ubus_context *ctx, struct list_head *ev_list)
|
|||
if (!param_name || !event_name || !strlen(event_name))
|
||||
continue;
|
||||
|
||||
struct ubus_event_handler *ev = (struct ubus_event_handler *)calloc(1, sizeof(struct ubus_event_handler));
|
||||
if (!ev) {
|
||||
BBF_ERR("Out of memory!");
|
||||
err = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
ev->cb = bbfdm_event_handler;
|
||||
|
||||
if (0 != ubus_register_event_handler(ctx, ev, event_name)) {
|
||||
BBF_ERR("Failed to register: %s", event_name);
|
||||
err = -1;
|
||||
goto end;
|
||||
}
|
||||
|
||||
add_ubus_event_handler(ev, event_name, param_name, ev_list);
|
||||
add_ubus_event_handler(ctx, event_name, param_name, ev_list);
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
bbf_cleanup(&bbf_ctx);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_ubus_event_handler(struct ubus_context *ctx, struct list_head *ev_list)
|
||||
{
|
||||
struct ev_handler_node *iter = NULL, *node = NULL;
|
||||
{
|
||||
struct ev_handler_node *iter = NULL, *tmp = NULL;
|
||||
struct dm_path_node *dp_iter = NULL, *dp_tmp = NULL;
|
||||
|
||||
if (ctx == NULL || ev_list == NULL)
|
||||
if (!ctx || !ev_list)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(iter, node, ev_list, list) {
|
||||
if (iter->ev_handler != NULL) {
|
||||
ubus_unregister_event_handler(ctx, iter->ev_handler);
|
||||
free(iter->ev_handler);
|
||||
}
|
||||
|
||||
if (iter->dm_path)
|
||||
free(iter->dm_path);
|
||||
list_for_each_entry_safe(iter, tmp, ev_list, list) {
|
||||
ubus_unregister_event_handler(ctx, &iter->ev_handler);
|
||||
|
||||
if (iter->ev_name)
|
||||
free(iter->ev_name);
|
||||
|
||||
list_for_each_entry_safe(dp_iter, dp_tmp, &iter->dm_paths_list, list) {
|
||||
if (dp_iter->dm_path)
|
||||
free(dp_iter->dm_path);
|
||||
|
||||
list_del(&dp_iter->list);
|
||||
free(dp_iter);
|
||||
}
|
||||
|
||||
list_del(&iter->list);
|
||||
free(iter);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,13 +167,13 @@ void fill_err_code_table(bbfdm_data_t *data, int fault)
|
|||
blobmsg_close_table(&data->bb, table);
|
||||
}
|
||||
|
||||
void fill_err_code_array(bbfdm_data_t *data, int fault)
|
||||
void fill_err_code_array(bbfdm_data_t *data, struct blob_buf *bb, int fault)
|
||||
{
|
||||
void *array = blobmsg_open_array(&data->bb, "results");
|
||||
void *table = blobmsg_open_table(&data->bb, NULL);
|
||||
bb_add_string(&data->bb, "path", data->bbf_ctx.in_param);
|
||||
blobmsg_add_u32(&data->bb, "fault", bbf_fault_map(&data->bbf_ctx, fault));
|
||||
bb_add_string(&data->bb, "fault_msg", data->bbf_ctx.fault_msg);
|
||||
blobmsg_close_table(&data->bb, table);
|
||||
blobmsg_close_array(&data->bb, array);
|
||||
void *array = blobmsg_open_array(bb, "results");
|
||||
void *table = blobmsg_open_table(bb, NULL);
|
||||
bb_add_string(bb, "path", data->bbf_ctx.in_param);
|
||||
blobmsg_add_u32(bb, "fault", bbf_fault_map(&data->bbf_ctx, fault));
|
||||
bb_add_string(bb, "fault_msg", data->bbf_ctx.fault_msg);
|
||||
blobmsg_close_table(bb, table);
|
||||
blobmsg_close_array(bb, array);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ void add_path_list(const char *param, struct list_head *plist);
|
|||
void free_path_list(struct list_head *plist);
|
||||
|
||||
void fill_err_code_table(bbfdm_data_t *data, int fault);
|
||||
void fill_err_code_array(bbfdm_data_t *data, int fault);
|
||||
void fill_err_code_array(bbfdm_data_t *data, struct blob_buf *bb, int fault);
|
||||
|
||||
void bb_add_string(struct blob_buf *bb, const char *name, const char *value);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,8 +29,10 @@ void bbfdm_operate_cmd(bbfdm_data_t *data, void *output)
|
|||
|
||||
fault = bbfdm_cmd_exec(&data->bbf_ctx, BBF_OPERATE);
|
||||
if (fault) {
|
||||
void *fault_table = blobmsg_open_table(&data->bbf_ctx.bb, NULL);
|
||||
blobmsg_add_u32(&data->bbf_ctx.bb, "fault", fault);
|
||||
bb_add_string(&data->bbf_ctx.bb, "fault_msg", data->bbf_ctx.fault_msg);
|
||||
blobmsg_close_table(&data->bbf_ctx.bb, fault_table);
|
||||
}
|
||||
|
||||
blobmsg_close_array(&data->bbf_ctx.bb, output_array);
|
||||
|
|
@ -44,5 +46,28 @@ void bbfdm_operate_cmd(bbfdm_data_t *data, void *output)
|
|||
ubus_send_reply(data->ctx, data->req, data->bbf_ctx.bb.head);
|
||||
}
|
||||
|
||||
/* Commit or Revert changes in uci files */
|
||||
if (data->bbf_ctx.modified_uci_head != NULL &&
|
||||
!list_empty(data->bbf_ctx.modified_uci_head)) {
|
||||
struct dm_modified_uci *m;
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
blob_buf_init(&bb, 0);
|
||||
void *array = blobmsg_open_array(&bb, "services");
|
||||
|
||||
list_for_each_entry(m, data->bbf_ctx.modified_uci_head, list) {
|
||||
blobmsg_add_string(&bb, NULL, m->uci_file);
|
||||
}
|
||||
|
||||
blobmsg_close_array(&bb, array);
|
||||
|
||||
blobmsg_add_string(&bb, "proto", (data->bbf_ctx.dm_type == BBFDM_USP) ? "usp" : "both");
|
||||
blobmsg_add_u8(&bb, "reload", (fault == 0) ? true : false);
|
||||
|
||||
dmubus_call_blob_msg_timeout("bbf.config", (fault == 0) ? "commit" : "revert", &bb, 10000);
|
||||
|
||||
blob_buf_free(&bb);
|
||||
}
|
||||
|
||||
bbf_cleanup(&data->bbf_ctx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,15 +66,6 @@ int bbfdm_load_internal_plugin(struct bbfdm_context *bbfdm_ctx, DM_MAP_OBJ *dyna
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (dynamic_obj[i].root_obj != NULL && bbfdm_ctx != NULL) {
|
||||
struct dm_obj_s *entryobj = dynamic_obj[i].root_obj;
|
||||
for (; (entryobj && entryobj->obj); entryobj++) {
|
||||
char path[MAX_DM_PATH] = {0};
|
||||
snprintf(path, sizeof(path), "%s%s.", node_obj, entryobj->obj);
|
||||
add_path_list(path, &bbfdm_ctx->obj_list);
|
||||
}
|
||||
}
|
||||
|
||||
node_obj[len-1] = 0;
|
||||
|
||||
dm_entryobj[i].obj = node_obj;
|
||||
|
|
@ -138,7 +129,12 @@ int bbfdm_free_dotso_plugin(struct bbfdm_context *bbfdm_ctx, void **lib_handle)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bbfdm_load_json_plugin(struct bbfdm_context *bbfdm_ctx, struct list_head *json_plugin, struct list_head *json_list,
|
||||
static int browse_obj(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bbfdm_load_json_plugin(struct bbfdm_context *bbfdm_ctx, struct list_head *json_plugin, struct list_head *json_list,
|
||||
struct list_head *json_memhead, const char *file_path, DMOBJ **main_entry)
|
||||
{
|
||||
DMOBJ *dm_entryobj = NULL;
|
||||
|
|
@ -196,10 +192,16 @@ static int bbfdm_load_json_plugin(struct bbfdm_context *bbfdm_ctx, struct list_h
|
|||
return -1;
|
||||
}
|
||||
|
||||
char path[MAX_DM_PATH] = {0};
|
||||
snprintf(path, sizeof(path), "%s%s.", obj_prefix, obj_name);
|
||||
add_path_list(path, &bbfdm_ctx->obj_list);
|
||||
|
||||
// Parent Path is multi-instance object
|
||||
bool multi_instances = false;
|
||||
if (node_obj[obj_prefix_len - 1] == '.' &&
|
||||
node_obj[obj_prefix_len] == '{' &&
|
||||
node_obj[obj_prefix_len + 1] == 'i' &&
|
||||
node_obj[obj_prefix_len + 2] == '}' &&
|
||||
node_obj[obj_prefix_len + 3] == '.') {
|
||||
multi_instances = true;
|
||||
}
|
||||
|
||||
// Remove '.' from object prefix
|
||||
if (obj_prefix[obj_prefix_len - 1] == '.')
|
||||
obj_prefix[obj_prefix_len - 1] = 0;
|
||||
|
|
@ -219,6 +221,7 @@ static int bbfdm_load_json_plugin(struct bbfdm_context *bbfdm_ctx, struct list_h
|
|||
dm_entryobj[idx].permission = &DMREAD;
|
||||
dm_entryobj[idx].nextobj = (DMOBJ *)dm_dynamic_calloc(json_memhead, 2, sizeof(DMOBJ));
|
||||
dm_entryobj[idx].leaf = NULL;
|
||||
dm_entryobj[idx].browseinstobj = multi_instances ? browse_obj : NULL;
|
||||
dm_entryobj[idx].bbfdm_type = BBFDM_BOTH;
|
||||
|
||||
parse_obj(node_obj, jobj, dm_entryobj[idx].nextobj, 0, json_plugin_version, json_list);
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ int bbfdm_load_external_plugin(struct bbfdm_context *bbfdm_ctx, void **lib_handl
|
|||
int bbfdm_load_dotso_plugin(struct bbfdm_context *bbfdm_ctx, void **lib_handle, const char *file_path, DMOBJ **main_entry);
|
||||
int bbfdm_free_dotso_plugin(struct bbfdm_context *bbfdm_ctx, void *lib_handle);
|
||||
|
||||
int bbfdm_load_json_plugin(struct bbfdm_context *bbfdm_ctx, struct list_head *json_plugin, struct list_head *json_list,
|
||||
struct list_head *json_memhead, const char *file_path, DMOBJ **main_entry);
|
||||
int bbfdm_free_json_plugin(void);
|
||||
|
||||
#endif /* PLUGIN_H */
|
||||
|
|
|
|||
|
|
@ -39,6 +39,17 @@ int bbfdm_set_value(bbfdm_data_t *data)
|
|||
|
||||
blobmsg_close_array(&data->bb, array);
|
||||
|
||||
array = blobmsg_open_array(&data->bb, "modified_uci");
|
||||
|
||||
if (data->bbf_ctx.modified_uci_head != NULL) {
|
||||
struct dm_modified_uci *m;
|
||||
list_for_each_entry(m, data->bbf_ctx.modified_uci_head, list) {
|
||||
bb_add_string(&data->bb, "", m->uci_file);
|
||||
}
|
||||
}
|
||||
|
||||
blobmsg_close_array(&data->bb, array);
|
||||
|
||||
return fault;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,33 +20,51 @@
|
|||
**************************************************************/
|
||||
static void _exec_reboot(const void *arg1, void *arg2)
|
||||
{
|
||||
char config_name[16] = {0};
|
||||
struct bbfdm_ctx d_ctx = {0};
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
snprintf(config_name, sizeof(config_name), "%s", "sysmngr");
|
||||
bbfdm_init_ctx(&d_ctx);
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
// Set last_reboot_cause to 'RemoteReboot' because the upcoming reboot will be initiated by USP Operate
|
||||
dmuci_set_value(config_name, "reboots", "last_reboot_cause", "RemoteReboot");
|
||||
dmuci_commit_package(config_name);
|
||||
|
||||
bbfdm_uci_set(&d_ctx, "sysmngr", "reboots", "last_reboot_cause", "RemoteReboot");
|
||||
bbfdm_uci_commit_package(&d_ctx, "sysmngr");
|
||||
sleep(3);
|
||||
dmubus_call_set("rpc-sys", "reboot", UBUS_ARGS{0}, 0);
|
||||
sleep(5); // Wait for reboot to happen
|
||||
|
||||
bbfdm_ubus_invoke_sync(&d_ctx, "rpc-sys", "reboot", bb.head, 5000, NULL, NULL);
|
||||
sleep(30); // Wait for reboot to happen
|
||||
|
||||
BBF_ERR("Reboot call failed with rpc-sys, trying again with system");
|
||||
dmubus_call_set("system", "reboot", UBUS_ARGS{0}, 0);
|
||||
sleep(5); // Wait for reboot
|
||||
bbfdm_ubus_invoke_sync(&d_ctx, "system", "reboot", bb.head, 5000, NULL, NULL);
|
||||
sleep(30); // Wait for reboot
|
||||
|
||||
BBF_ERR("Reboot call failed!!!");
|
||||
|
||||
// Set last_reboot_cause to empty because there is a problem in the system reboot
|
||||
dmuci_set_value(config_name, "reboots", "last_reboot_cause", "");
|
||||
dmuci_commit_package(config_name);
|
||||
bbfdm_uci_set(&d_ctx, "sysmngr", "reboots", "last_reboot_cause", "");
|
||||
bbfdm_uci_commit_package(&d_ctx, "sysmngr");
|
||||
bbfdm_free_ctx(&d_ctx);
|
||||
blob_buf_free(&bb);
|
||||
}
|
||||
|
||||
static void _exec_factoryreset(const void *arg1, void *arg2)
|
||||
{
|
||||
struct bbfdm_ctx d_ctx = {0};
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
bbfdm_init_ctx(&d_ctx);
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
sleep(2);
|
||||
dmubus_call_set("rpc-sys", "factory", UBUS_ARGS{0}, 0);
|
||||
bbfdm_ubus_invoke_sync(&d_ctx, "rpc-sys", "factory", bb.head, 5000, NULL, NULL);
|
||||
sleep(5); // Wait for reboot to happen
|
||||
|
||||
BBF_ERR("FactoryReset via rpc-sys failed, trying defaultreset");
|
||||
bbfdm_free_ctx(&d_ctx);
|
||||
blob_buf_free(&bb);
|
||||
|
||||
dmcmd_no_wait("/sbin/defaultreset", 0);
|
||||
sleep(5); // Wait for reboot to happen
|
||||
BBF_ERR("FactoryReset call failed!!!");
|
||||
|
|
@ -57,7 +75,7 @@ static void _exec_factoryreset(const void *arg1, void *arg2)
|
|||
**************************************************************/
|
||||
static int get_Device_RootDataModelVersion(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
|
||||
{
|
||||
*value = dmstrdup("2.18");
|
||||
*value = dmstrdup("2.19");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -78,13 +96,27 @@ static int operate_Device_FactoryReset(char *refparam, struct dmctx *ctx, void *
|
|||
return !res ? 0 : USP_FAULT_COMMAND_FAILURE;
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* Init & Clean Module
|
||||
**************************************************************/
|
||||
int init_core_module(void *data)
|
||||
{
|
||||
struct dmctx bbf_ctx = {0};
|
||||
|
||||
bbf_ctx_init(&bbf_ctx, NULL);
|
||||
dmmap_synchronizeSchedulesSchedule(&bbf_ctx);
|
||||
bbf_ctx_clean(&bbf_ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************
|
||||
* OBJ & LEAF DEFINITION
|
||||
***********************************************************************************************************************************/
|
||||
/* *** BBFDM *** */
|
||||
DM_MAP_OBJ tDynamicObj[] = {
|
||||
/* parentobj, nextobject, parameter */
|
||||
{"Device.", tDMRootObj, tDMRootParams},
|
||||
{"Device.", tDMRootObj, tDMRootParams, init_core_module, NULL},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -138,27 +138,24 @@ static char *get_status(char *start, char *period, char *day)
|
|||
**************************************************************/
|
||||
static int addSchedule(char *refparam, struct dmctx *ctx, void *data, char **instance)
|
||||
{
|
||||
struct uci_section *s = NULL, *dmmap_s = NULL;
|
||||
char s_name[16] = {0};
|
||||
int i;
|
||||
struct dm_data *curr_data = (struct dm_data *)ctx->addobj_instance;
|
||||
char sec_name[32] = {0};
|
||||
|
||||
snprintf(s_name, sizeof(s_name), "schedule_%s", *instance);
|
||||
snprintf(sec_name, sizeof(sec_name), "schedule_%s", *instance);
|
||||
|
||||
dmuci_add_section("schedules", "schedule", &s);
|
||||
dmuci_rename_section_by_section(s, s_name);
|
||||
// Create and Set default config option
|
||||
dmuci_add_section("schedules", "schedule", &curr_data->config_section);
|
||||
dmuci_rename_section_by_section(curr_data->config_section, sec_name);
|
||||
|
||||
dmuci_set_value_by_section(s, "enable", "0");
|
||||
|
||||
for (i = 0; allowed_days[i] != NULL; i++) {
|
||||
dmuci_add_list_value_by_section(s, "day", allowed_days[i]);
|
||||
}
|
||||
dmuci_set_value_by_section(curr_data->config_section, "enable", "0");
|
||||
dmuci_set_value_by_section(curr_data->config_section, "duration", "1");
|
||||
|
||||
dmuci_set_value_by_section(s, "duration", "1");
|
||||
for (int i = 0; allowed_days[i] != NULL; i++)
|
||||
dmuci_add_list_value_by_section(curr_data->config_section, "day", allowed_days[i]);
|
||||
|
||||
// dmmap is already created by the core, Set default dmmap option if needed
|
||||
dmuci_set_value_by_section(curr_data->dmmap_section, "Alias", sec_name);
|
||||
|
||||
dmuci_add_section_bbfdm("dmmap_schedules", "schedule", &dmmap_s);
|
||||
dmuci_set_value_by_section(dmmap_s, "section_name", s_name);
|
||||
dmuci_set_value_by_section(dmmap_s, "schedule_instance", *instance);
|
||||
dmuci_set_value_by_section(dmmap_s, "schedule_alias", s_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -173,22 +170,22 @@ static int delSchedule(char *refparam, struct dmctx *ctx, void *data, char *inst
|
|||
/*************************************************************
|
||||
* ENTRY METHODS
|
||||
*************************************************************/
|
||||
static int browseScheduleInstance(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
|
||||
void dmmap_synchronizeSchedulesSchedule(struct dmctx *dmctx)
|
||||
{
|
||||
struct dm_data *p = NULL;
|
||||
char *inst = NULL;
|
||||
LIST_HEAD(dup_list);
|
||||
struct uci_section *schedule_s = NULL;
|
||||
char object_name[16] = {0};
|
||||
char *instance = NULL;
|
||||
|
||||
synchronize_specific_config_sections_with_dmmap("schedules", "schedule", "dmmap_schedules", &dup_list);
|
||||
list_for_each_entry(p, &dup_list, list) {
|
||||
bbfdm_create_empty_file("/etc/bbfdm/dmmap/Schedules");
|
||||
|
||||
inst = handle_instance(dmctx, parent_node, p->dmmap_section, "schedule_instance", "schedule_alias");
|
||||
snprintf(object_name, sizeof(object_name), "%s", "Schedules");
|
||||
|
||||
if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)p, inst) == DM_STOP)
|
||||
break;
|
||||
// Device.Schedules.Schedule.{i}.
|
||||
uci_foreach_sections("schedules", "schedule", schedule_s) {
|
||||
create_dmmap_obj(dmctx, 0, "Schedules", "Schedule", schedule_s, &instance);
|
||||
}
|
||||
free_dmmap_config_dup_list(&dup_list);
|
||||
return 0;
|
||||
|
||||
dmuci_commit_package_bbfdm(object_name);
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
|
|
@ -221,8 +218,8 @@ static int set_schedules_enable(char *refparam, struct dmctx *ctx, void *data, c
|
|||
|
||||
static int get_schedule_number(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
|
||||
{
|
||||
unsigned int cnt = get_number_of_entries(ctx, data, instance, browseScheduleInstance);
|
||||
dmasprintf(value, "%u", cnt);
|
||||
int cnt = get_number_of_entries(ctx, data, instance, NULL);
|
||||
dmasprintf(value, "%d", cnt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -253,12 +250,13 @@ static int set_schedule_enable(char *refparam, struct dmctx *ctx, void *data, ch
|
|||
|
||||
static int get_schedule_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
|
||||
{
|
||||
return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "schedule_alias", instance, value);
|
||||
return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "Alias", instance, value);
|
||||
}
|
||||
|
||||
static int set_schedule_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
|
||||
{
|
||||
int ret = 0;
|
||||
char buffer[256] = {0};
|
||||
|
||||
switch (action) {
|
||||
case VALUECHECK:
|
||||
|
|
@ -281,8 +279,9 @@ static int set_schedule_alias(char *refparam, struct dmctx *ctx, void *data, cha
|
|||
break;
|
||||
case VALUESET:
|
||||
dmuci_rename_section_by_section(((struct dm_data *)data)->config_section, value);
|
||||
dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "section_name", value);
|
||||
dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "schedule_alias", value);
|
||||
snprintf(buffer, sizeof(buffer), "schedules.%s", value);
|
||||
dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "__section_name__", buffer);
|
||||
dmuci_set_value_by_section(((struct dm_data *)data)->dmmap_section, "Alias", value);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -432,7 +431,7 @@ static int get_schedule_status(char *refparam, struct dmctx *ctx, void *data, ch
|
|||
/* *** Device.Schedules. *** */
|
||||
DMOBJ tSchedulesObj[] = {
|
||||
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
|
||||
{"Schedule", &DMWRITE, addSchedule, delSchedule, NULL, browseScheduleInstance, NULL, NULL, NULL, tScheduleParams, NULL, BBFDM_BOTH, NULL},
|
||||
{"Schedule", &DMWRITE, addSchedule, delSchedule, NULL, generic_browse, NULL, NULL, NULL, tScheduleParams, NULL, BBFDM_BOTH, NULL},
|
||||
{0}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -16,5 +16,8 @@
|
|||
extern DMOBJ tSchedulesObj[];
|
||||
extern DMLEAF tSchedulesParams[];
|
||||
extern DMLEAF tScheduleParams[];
|
||||
|
||||
void dmmap_synchronizeSchedulesSchedule(struct dmctx *dmctx);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -76,32 +76,46 @@ static char *generate_serial_number(const char *text, int length)
|
|||
return hex;
|
||||
}
|
||||
|
||||
static int filter(const struct dirent *entry)
|
||||
{
|
||||
// Exclude hidden files and files not ending with ".0"
|
||||
return (entry->d_name[0] != '.' && strstr(entry->d_name, ".0") != NULL);
|
||||
}
|
||||
|
||||
static int compare(const struct dirent **a, const struct dirent **b)
|
||||
{
|
||||
// Sort alphabetically (case-insensitive)
|
||||
return strcasecmp((*a)->d_name, (*b)->d_name);
|
||||
}
|
||||
|
||||
static int fill_certificate_paths(const char *dir_path, int *cidx)
|
||||
{
|
||||
struct dirent *d_file = NULL;
|
||||
DIR *dir = NULL;
|
||||
char cert_path[CERT_PATH_LEN];
|
||||
struct dirent **namelist;
|
||||
|
||||
sysfs_foreach_file(dir_path, dir, d_file) {
|
||||
int num_files = scandir(dir_path, &namelist, filter, compare);
|
||||
|
||||
if (d_file->d_name[0] == '.' || !strstr(d_file->d_name, ".0"))
|
||||
for (int i = 0; i < num_files; i++) {
|
||||
char cert_path[CERT_PATH_LEN];
|
||||
|
||||
if (*cidx >= MAX_CERT) {
|
||||
FREE(namelist[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*cidx >= MAX_CERT)
|
||||
break;
|
||||
snprintf(cert_path, sizeof(cert_path), "%s/%s", dir_path, namelist[i]->d_name);
|
||||
|
||||
snprintf(cert_path, sizeof(cert_path), "%s/%s", dir_path, d_file->d_name);
|
||||
|
||||
if (!file_exists(cert_path) || !is_regular_file(cert_path))
|
||||
if (!file_exists(cert_path) || !is_regular_file(cert_path)) {
|
||||
FREE(namelist[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
DM_STRNCPY(certifcates_paths[*cidx], cert_path, CERT_PATH_LEN);
|
||||
(*cidx)++;
|
||||
|
||||
FREE(namelist[i]);
|
||||
}
|
||||
|
||||
if (dir)
|
||||
closedir (dir);
|
||||
|
||||
FREE(namelist);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -145,10 +159,9 @@ static int get_certificate_paths(void)
|
|||
static int browseSecurityCertificateInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
|
||||
{
|
||||
struct certificate_profile certificateprofile = {0};
|
||||
struct uci_section *dmmap_sec = NULL;
|
||||
struct dm_data curr_data = {0};
|
||||
char *inst = NULL;
|
||||
int i, status;
|
||||
int i, status, id = 0;
|
||||
|
||||
get_certificate_paths();
|
||||
|
||||
|
|
@ -167,16 +180,11 @@ static int browseSecurityCertificateInst(struct dmctx *dmctx, DMNODE *parent_nod
|
|||
continue;
|
||||
}
|
||||
|
||||
if ((dmmap_sec = get_dup_section_in_dmmap_opt("dmmap_security", "security_certificate", "path", certifcates_paths[i])) == NULL) {
|
||||
dmuci_add_section_bbfdm("dmmap_security", "security_certificate", &dmmap_sec);
|
||||
dmuci_set_value_by_section_bbfdm(dmmap_sec, "path", certifcates_paths[i]);
|
||||
}
|
||||
|
||||
init_certificate(certifcates_paths[i], cert, &certificateprofile);
|
||||
|
||||
curr_data.additional_data = (void *)&certificateprofile;
|
||||
|
||||
inst = handle_instance(dmctx, parent_node, dmmap_sec, "security_certificate_instance", "security_certificate_alias");
|
||||
inst = handle_instance_without_section(dmctx, parent_node, ++id);
|
||||
|
||||
status = DM_LINK_INST_OBJ(dmctx, parent_node, &curr_data, inst);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ static int setup_teardown(void **state)
|
|||
{
|
||||
dm_init_mem(&bbf_ctx);
|
||||
dm_uci_init(&bbf_ctx);
|
||||
dm_ubus_cache_init();
|
||||
dm_ubus_init(&bbf_ctx);
|
||||
bbf_ctx.dm_type = BBFDM_USP;
|
||||
return 0;
|
||||
|
|
@ -22,6 +23,7 @@ static int group_teardown(void **state)
|
|||
dm_uci_exit(&bbf_ctx);
|
||||
dm_clean_mem(&bbf_ctx);
|
||||
dm_ubus_free(&bbf_ctx);
|
||||
dm_ubus_cache_free();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,21 @@
|
|||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "GatewayInfo"
|
||||
"object": "PacketCaptureDiagnostics"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "SelfTestDiagnostics"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "Syslog"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "{BBF_VENDOR_PREFIX}OpenVPN",
|
||||
"proto": "usp"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "RootDataModelVersion"
|
||||
|
|
@ -28,14 +41,14 @@
|
|||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "Reboot()"
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "FactoryReset()"
|
||||
}
|
||||
},
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "FactoryReset()"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"loglevel": "3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"daemon": {
|
||||
"enable": "1",
|
||||
"service_name": "wifidmd",
|
||||
"unified_daemon": false,
|
||||
"unified_daemon": true,
|
||||
"services": [
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
|
|
|
|||
27
test/files/etc/config/netmode
Normal file
27
test/files/etc/config/netmode
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
config netmode 'global'
|
||||
option enabled '0'
|
||||
option mode 'routed-pppoe'
|
||||
|
||||
config supported_modes 'mode_1'
|
||||
option name 'routed-dhcp'
|
||||
option description 'WAN with DHCP proto (Layer 3)'
|
||||
|
||||
config supported_modes 'mode_2'
|
||||
option name 'routed-pppoe'
|
||||
option description 'WAN with PPPoE (Layer 3)'
|
||||
|
||||
config supported_args 'mode_2_supprted_args_1'
|
||||
option name 'username'
|
||||
option description 'PPPoE username'
|
||||
option required '1'
|
||||
option type 'string'
|
||||
option dm_parent 'mode_2'
|
||||
|
||||
config supported_args 'mode_2_supprted_args_2'
|
||||
option name 'password'
|
||||
option description 'PPPoE password'
|
||||
option required '1'
|
||||
option type 'string'
|
||||
option dm_parent 'mode_2'
|
||||
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
config globals 'globals'
|
||||
option enable '1'
|
||||
option enable '0'
|
||||
|
||||
config profile 'profile_1'
|
||||
option name 'kids'
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ config globals 'globals'
|
|||
config sampleset 'set_1'
|
||||
option fetch_samples '0'
|
||||
option enable '1'
|
||||
option sample_interval '20'
|
||||
option sample_interval '1800'
|
||||
option report_sample '7'
|
||||
|
||||
config parameter 'param_1_set_1'
|
||||
|
|
@ -47,7 +47,7 @@ config parameter 'param_4_set_1'
|
|||
config sampleset 'set_2'
|
||||
option fetch_samples '0'
|
||||
option enable '1'
|
||||
option sample_interval '10'
|
||||
option sample_interval '300'
|
||||
option report_sample '4'
|
||||
|
||||
config parameter 'param_1_set_2'
|
||||
|
|
|
|||
26
test/files/etc/config/schedules
Normal file
26
test/files/etc/config/schedules
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
config global 'global'
|
||||
option enable '1'
|
||||
|
||||
config schedule 'schedule_1'
|
||||
option enable '0'
|
||||
list day 'Monday'
|
||||
list day 'Tuesday'
|
||||
list day 'Wednesday'
|
||||
list day 'Thursday'
|
||||
list day 'Friday'
|
||||
list day 'Saturday'
|
||||
list day 'Sunday'
|
||||
option duration '1'
|
||||
|
||||
config schedule 'schedule_2'
|
||||
option enable '0'
|
||||
list day 'Monday'
|
||||
list day 'Tuesday'
|
||||
list day 'Wednesday'
|
||||
list day 'Thursday'
|
||||
list day 'Friday'
|
||||
list day 'Saturday'
|
||||
list day 'Sunday'
|
||||
option duration '1'
|
||||
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,116 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <libubox/blobmsg.h>
|
||||
|
||||
#include <libbbfdm-api/dmapi.h>
|
||||
#include <libbbfdm-ubus/bbfdm-ubus.h>
|
||||
#include <libbbfdm-api/dmentry.h>
|
||||
|
||||
#include "../../libbbfdm-ubus/plugin.h"
|
||||
#include "../../libbbfdm/device.h"
|
||||
|
||||
static int cli_exec_schema(struct dmctx *bbfdm_ctx, char *in_path)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
bbfdm_ctx->in_param = in_path;
|
||||
bbfdm_ctx->nextlevel = false;
|
||||
bbfdm_ctx->iscommand = true;
|
||||
bbfdm_ctx->isevent = true;
|
||||
bbfdm_ctx->isinfo = true;
|
||||
|
||||
err = bbf_entry_method(bbfdm_ctx, BBF_SCHEMA);
|
||||
if (!err) {
|
||||
struct blob_attr *cur = NULL;
|
||||
size_t rem = 0;
|
||||
|
||||
blobmsg_for_each_attr(cur, bbfdm_ctx->bb.head, rem) {
|
||||
struct blob_attr *tb[3] = {0};
|
||||
const struct blobmsg_policy p[3] = {
|
||||
{ "path", BLOBMSG_TYPE_STRING },
|
||||
{ "data", BLOBMSG_TYPE_STRING },
|
||||
{ "type", BLOBMSG_TYPE_STRING }
|
||||
};
|
||||
|
||||
blobmsg_parse(p, 3, 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]) : "";
|
||||
char *type = (tb[2]) ? blobmsg_get_string(tb[2]) : "";
|
||||
|
||||
printf("%s %s %s\n", name, type, strlen(data) ? data : "0"); // Added a data check to handle events with empty or missing data
|
||||
}
|
||||
} else {
|
||||
printf("ERROR: %d retrieving %s\n", err, bbfdm_ctx->in_param);
|
||||
err = -1;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
DMOBJ *CLI_DM_ROOT_OBJ = NULL;
|
||||
void *cli_lib_handle = NULL;
|
||||
struct dmctx bbfdm_ctx = {0};
|
||||
char *plugin_path = NULL, *plugin_dir = NULL, *dm_path = NULL;
|
||||
unsigned int proto = BBFDM_BOTH;
|
||||
int err = 0, ch;
|
||||
|
||||
memset(&bbfdm_ctx, 0, sizeof(struct dmctx));
|
||||
|
||||
while ((ch = getopt(argc, argv, "hc:u:l:p:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'c':
|
||||
bbfdm_ctx.dm_type = BBFDM_CWMP;
|
||||
dm_path = argv[optind - 1];
|
||||
break;
|
||||
case 'u':
|
||||
bbfdm_ctx.dm_type = BBFDM_USP;
|
||||
dm_path = argv[optind - 1];
|
||||
break;
|
||||
case 'l':
|
||||
plugin_path = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
plugin_dir = optarg;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin_path == NULL) {
|
||||
err = bbfdm_load_internal_plugin(NULL, tDynamicObj, &CLI_DM_ROOT_OBJ);
|
||||
} else {
|
||||
err = bbfdm_load_dotso_plugin(NULL, &cli_lib_handle, plugin_path, &CLI_DM_ROOT_OBJ);
|
||||
}
|
||||
|
||||
if (err || !CLI_DM_ROOT_OBJ) {
|
||||
printf("ERROR: Failed to load plugin\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dm_path) {
|
||||
printf("ERROR: Data Model path should be defined\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize global context
|
||||
bbf_global_init(CLI_DM_ROOT_OBJ, plugin_dir);
|
||||
|
||||
// Initialize the bbfdm context
|
||||
bbf_ctx_init(&bbfdm_ctx, CLI_DM_ROOT_OBJ);
|
||||
|
||||
err = cli_exec_schema(&bbfdm_ctx, dm_path);
|
||||
|
||||
// Clean up the context and global resources
|
||||
bbf_ctx_clean(&bbfdm_ctx);
|
||||
bbf_global_clean(CLI_DM_ROOT_OBJ);
|
||||
|
||||
// Free plugin handle
|
||||
bbfdm_free_dotso_plugin(NULL, &cli_lib_handle);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@ FROM ubuntu:22.04
|
|||
|
||||
LABEL maintainer="vivek.dutta@iopsys.eu"
|
||||
LABEL build="docker build -t bbfdm-tools ."
|
||||
LABEL run="docker run -it --rm -v ${PWD}/..:/builds/bbfdm bbfdm-tools"
|
||||
LABEL run="docker run -it --rm -v PWD/..:/builds/bbfdm bbfdm-tools"
|
||||
|
||||
RUN \
|
||||
apt-get update && \
|
||||
|
|
@ -13,6 +13,17 @@ RUN \
|
|||
libssl-dev \
|
||||
libcurl4-openssl-dev \
|
||||
pkg-config \
|
||||
libtool \
|
||||
libnl-3-dev \
|
||||
libnl-genl-3-dev \
|
||||
libnl-route-3-dev \
|
||||
autoconf \
|
||||
automake \
|
||||
autotools-dev \
|
||||
binutils \
|
||||
net-tools \
|
||||
curl \
|
||||
sudo \
|
||||
git
|
||||
|
||||
RUN mkdir /opt/dev
|
||||
|
|
@ -50,4 +61,30 @@ RUN \
|
|||
make -j2 && \
|
||||
make install
|
||||
|
||||
ENTRYPOINT ["/bin/bash"]
|
||||
# 1. Create new unprivileged user "dev"
|
||||
# 2. Install fixuid to accomodate for the host machine UID/GID
|
||||
ARG FIXUID_VERSION=0.5.1
|
||||
RUN useradd -m -s /bin/bash dev && \
|
||||
curl -fsSL "https://github.com/boxboat/fixuid/releases/download/v${FIXUID_VERSION}/fixuid-${FIXUID_VERSION}-linux-amd64.tar.gz" | tar -C /usr/local/bin -xzf -
|
||||
|
||||
# Copy fixuid configuration
|
||||
COPY docker/fixuid.yml /etc/fixuid/config.yml
|
||||
|
||||
# Copy git configuration to dev's home folder
|
||||
COPY --chown=dev:dev docker/gitconfig /home/dev/.gitconfig
|
||||
|
||||
RUN echo "dev ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/10-dev
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/fixuid", "-q"]
|
||||
CMD ["bash"]
|
||||
|
||||
RUN mkdir -p /builds/bbf/bbfdm /home/dev/.ssh \
|
||||
&& chown -R dev:dev /builds/bbf/bbfdm \
|
||||
&& chown -R dev:dev /home/dev \
|
||||
&& chown -R dev:dev /opt/dev \
|
||||
&& mkdir -p /usr/share/bbfdm \
|
||||
&& chown -R dev:dev /usr/share/bbfdm
|
||||
|
||||
USER dev:dev
|
||||
WORKDIR /builds/bbf/bbfdm
|
||||
VOLUME ["/builds/bbf/bbfdm"]
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ $ ./tools/validate_json_plugin.py test/files/etc/bbfdm/json/TEST.json
|
|||
$ ./tools/validate_json_plugin.py tools/datamodel.json
|
||||
```
|
||||
|
||||
More examples available in [this path](../test/files/usr/share/bbfdm/plugins).
|
||||
More examples available in [this path](../test/files/usr/share/bbfdm/micro_services/core).
|
||||
|
||||
### generate_dm.sh
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,16 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# Copyright (C) 2024 iopsys Software Solutions AB
|
||||
# Copyright (C) 2024-2025 iopsys Software Solutions AB
|
||||
# Author: Amin Ben Romdhane <amin.benromdhane@iopsys.eu>
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
import glob
|
||||
|
||||
# Constants
|
||||
BBF_ERROR_CODE = 0
|
||||
CURRENT_PATH = os.getcwd()
|
||||
BBF_MS_CORE_DIR = "/usr/share/bbfdm/micro_services/core/"
|
||||
BBF_MS_DIR = "/usr/share/bbfdm/micro_services/"
|
||||
|
||||
DM_JSON_FILE = os.path.join(CURRENT_PATH, "tools", "datamodel.json")
|
||||
|
||||
|
|
@ -35,25 +32,6 @@ Array_Types = {
|
|||
}
|
||||
|
||||
|
||||
def rename_file(old_path, new_path):
|
||||
try:
|
||||
os.rename(old_path, new_path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def move_file(source_path, destination_path):
|
||||
shutil.move(source_path, destination_path)
|
||||
|
||||
|
||||
def install_json_plugin(source_path, destination_path, vendor_extn):
|
||||
with open(source_path, 'r', encoding='UTF-8') as src, open(destination_path, 'w', encoding='UTF-8') as dest:
|
||||
data = src.read()
|
||||
data = data.replace("{BBF_VENDOR_PREFIX}", vendor_extn)
|
||||
|
||||
dest.write(data)
|
||||
|
||||
|
||||
def remove_file(file_path):
|
||||
try:
|
||||
os.remove(file_path)
|
||||
|
|
@ -73,13 +51,6 @@ def remove_folder(folder_path):
|
|||
shutil.rmtree(folder_path)
|
||||
|
||||
|
||||
def cd_dir(path):
|
||||
try:
|
||||
os.chdir(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def obj_has_child(value):
|
||||
if isinstance(value, dict):
|
||||
for _obj, val in value.items():
|
||||
|
|
@ -138,146 +109,40 @@ def is_proto_exist(value, proto):
|
|||
return proto in protocols
|
||||
|
||||
|
||||
def clear_list(input_list):
|
||||
input_list.clear()
|
||||
def build_command(plugin, proto):
|
||||
service_name = get_option_value(plugin, "service_name")
|
||||
unified = get_option_value(plugin, "unified_daemon", False)
|
||||
daemon_name = get_option_value(plugin, "daemon_name", "")
|
||||
schema_option = get_option_value(plugin, "schema_option", "d") # default stays 'd'
|
||||
|
||||
if not service_name:
|
||||
return None # skip this plugin
|
||||
|
||||
def generate_shared_library(dm_name, source_files, vendor_prefix,
|
||||
extra_dependencies, is_microservice=False):
|
||||
# Return if source_files (list) is empty
|
||||
if len(source_files) == 0:
|
||||
return
|
||||
|
||||
if is_microservice:
|
||||
outdir = BBF_MS_DIR
|
||||
# Start with base command
|
||||
if unified:
|
||||
base_cmd = f"{daemon_name}"
|
||||
else:
|
||||
outdir = BBF_MS_CORE_DIR
|
||||
base_cmd = f"dm-service -m {service_name}"
|
||||
|
||||
output_library = outdir + dm_name
|
||||
|
||||
# Set vendor prefix
|
||||
if vendor_prefix is not None:
|
||||
VENDOR_PREFIX = vendor_prefix
|
||||
else:
|
||||
VENDOR_PREFIX = "X_IOWRT_EU_"
|
||||
|
||||
# Ensure that the source files exist
|
||||
for source_file in source_files:
|
||||
if not os.path.exists(source_file):
|
||||
print(f" Error: Source file {source_file} does not exist.")
|
||||
return False
|
||||
|
||||
cmd = ['gcc', '-shared', '-o', output_library, '-fPIC',
|
||||
'-DBBF_VENDOR_PREFIX=\\"{}\\"'.format(VENDOR_PREFIX)]
|
||||
cmd = cmd + source_files + extra_dependencies
|
||||
# Compile the shared library
|
||||
try:
|
||||
cmdstr = ' '.join(str(e) for e in cmd)
|
||||
subprocess.run(cmdstr, shell=True, check=True)
|
||||
print(f" Shared library {output_library} successfully created.")
|
||||
return True
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f" Error during compilation: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def build_and_install_bbfdm(vendor_prefix):
|
||||
print("Compiling and installing bbfdmd in progress ...")
|
||||
|
||||
create_folder(os.path.join(CURRENT_PATH, "build"))
|
||||
cd_dir(os.path.join(CURRENT_PATH, "build"))
|
||||
|
||||
# Set vendor prefix
|
||||
if vendor_prefix is not None:
|
||||
VENDOR_PREFIX = vendor_prefix
|
||||
else:
|
||||
VENDOR_PREFIX = "X_IOWRT_EU_"
|
||||
|
||||
# Build and install bbfdm
|
||||
cmake_command = [
|
||||
"cmake",
|
||||
"../",
|
||||
"-DBBF_SCHEMA_FULL_TREE=ON",
|
||||
f"-DBBF_VENDOR_PREFIX={VENDOR_PREFIX}",
|
||||
"-DBBF_MAX_OBJECT_INSTANCES=255",
|
||||
"-DBBFDMD_MAX_MSG_LEN=1048576",
|
||||
"-DCMAKE_INSTALL_PREFIX=/"
|
||||
]
|
||||
make_command = ["make"]
|
||||
make_install_command = ["make", "install"]
|
||||
|
||||
try:
|
||||
subprocess.check_call(cmake_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
subprocess.check_call(make_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
subprocess.check_call(make_install_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error running commands: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
cd_dir(CURRENT_PATH)
|
||||
remove_folder(os.path.join(CURRENT_PATH, "build"))
|
||||
print('Compiling and installing bbfdmd done')
|
||||
|
||||
|
||||
def build_and_install_dmcli():
|
||||
print("Compiling and installing dm-cli in progress ...")
|
||||
|
||||
create_folder(os.path.join(CURRENT_PATH, "build"))
|
||||
cd_dir(os.path.join(CURRENT_PATH, "build"))
|
||||
|
||||
# GCC command to compile dm-cli
|
||||
gcc_command = [
|
||||
"gcc",
|
||||
"../test/tools/dm-cli.c",
|
||||
"-lbbfdm-api",
|
||||
"-lbbfdm-ubus",
|
||||
"-lubox",
|
||||
"-lblobmsg_json",
|
||||
"-lcore",
|
||||
"-ljson-c",
|
||||
"-lssl",
|
||||
"-lcrypto",
|
||||
"-o", "dm-cli"
|
||||
]
|
||||
|
||||
try:
|
||||
subprocess.check_call(gcc_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
subprocess.check_call(["mv", "dm-cli", "/usr/sbin/dm-cli"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error running commands: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
cd_dir(CURRENT_PATH)
|
||||
remove_folder(os.path.join(CURRENT_PATH, "build"))
|
||||
print('Compiling and installing dm-cli done')
|
||||
|
||||
|
||||
def fill_list_dm(proto, dm_list, dm_name=None):
|
||||
# Determine the base command depending on the presence of dm_name
|
||||
if dm_name:
|
||||
command = f"dm-cli -l {dm_name}"
|
||||
else:
|
||||
command = "dm-cli -p /usr/share/bbfdm/micro_services/core"
|
||||
|
||||
# Add the appropriate flag (-c or -u) based on the proto value
|
||||
# Append protocol-specific schema options
|
||||
if proto == "cwmp":
|
||||
command += " -c Device."
|
||||
base_cmd += f" -{schema_option}"
|
||||
elif proto == "usp":
|
||||
command += " -u Device."
|
||||
base_cmd += f" -{schema_option}{schema_option}"
|
||||
|
||||
return base_cmd
|
||||
|
||||
|
||||
def fill_list_dm(command, dm_list):
|
||||
try:
|
||||
# Run the command
|
||||
result = subprocess.run(command, shell=True, text=True, capture_output=True, check=True)
|
||||
|
||||
# Get the output from the result
|
||||
output = result.stdout
|
||||
|
||||
# Split the output into lines
|
||||
lines = output.strip().split('\n')
|
||||
|
||||
# Iterate through each line and parse the information
|
||||
for line in lines:
|
||||
parts = line.split()
|
||||
if len(parts) < 3:
|
||||
continue
|
||||
path, n_type, data = parts[0], parts[1], parts[2]
|
||||
permission = "readWrite" if data == "1" else "readOnly"
|
||||
p_type = n_type[4:]
|
||||
|
|
@ -289,8 +154,7 @@ def fill_list_dm(proto, dm_list, dm_name=None):
|
|||
dm_list.append(entry)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Handle subprocess errors here
|
||||
print(f"Error running command: {e}")
|
||||
print(f"Error running command '{command}': {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
|
@ -307,146 +171,30 @@ def remove_duplicate_elements(input_list):
|
|||
return result_list
|
||||
|
||||
|
||||
def fill_list_supported_dm():
|
||||
for proto, DB in [("usp", LIST_SUPPORTED_USP_DM), ("cwmp", LIST_SUPPORTED_CWMP_DM)]:
|
||||
fill_list_dm(proto, DB)
|
||||
DB.sort(key=lambda x: x['param'], reverse=False)
|
||||
DB[:] = remove_duplicate_elements(DB)
|
||||
|
||||
for file in os.listdir(BBF_MS_DIR):
|
||||
f = os.path.join(BBF_MS_DIR, file)
|
||||
|
||||
if os.path.isfile(f):
|
||||
for proto, DB in [("usp", LIST_SUPPORTED_USP_DM), ("cwmp", LIST_SUPPORTED_CWMP_DM)]:
|
||||
fill_list_dm(proto, DB, f)
|
||||
DB.sort(key=lambda x: x['param'], reverse=False)
|
||||
DB[:] = remove_duplicate_elements(DB)
|
||||
|
||||
|
||||
def clone_git_repository(repo, version=None):
|
||||
repo_path = '/tmp/repo/'+os.path.basename(repo).replace('.git', '')
|
||||
if os.path.exists(repo_path):
|
||||
print(f' {repo} already exists at {repo_path} !')
|
||||
return True
|
||||
try:
|
||||
cmd = ["git", "clone", repo, repo_path]
|
||||
if version is not None:
|
||||
cmd.extend(["-b", version])
|
||||
subprocess.run(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
|
||||
return True
|
||||
except (OSError, subprocess.SubprocessError):
|
||||
print(f' Failed to clone {repo} !!!!!')
|
||||
return False
|
||||
|
||||
|
||||
def get_repo_version_info(repo, version=None):
|
||||
if version is None:
|
||||
return repo
|
||||
return f'{repo}^{version}'
|
||||
|
||||
|
||||
def download_and_build_plugins(plugins, vendor_prefix):
|
||||
global BBF_ERROR_CODE
|
||||
def fill_list_supported_dm(plugins):
|
||||
|
||||
if plugins is None or not isinstance(plugins, list) or not plugins:
|
||||
print("No plugins provided.")
|
||||
return
|
||||
|
||||
print("Generating data models from defined plugins...")
|
||||
for proto, DB in [("usp", LIST_SUPPORTED_USP_DM), ("cwmp", LIST_SUPPORTED_CWMP_DM)]:
|
||||
for plugin in plugins:
|
||||
command = build_command(plugin, proto)
|
||||
if command:
|
||||
print(f"Running command for {proto}: {command}")
|
||||
fill_list_dm(command, DB)
|
||||
|
||||
remove_folder("/tmp/repo")
|
||||
|
||||
for plugin_index, plugin in enumerate(plugins):
|
||||
|
||||
repo = get_option_value(plugin, "repo")
|
||||
proto = get_option_value(plugin, "proto")
|
||||
dm_files = get_option_value(plugin, "dm_files")
|
||||
is_microservice = get_option_value(plugin, "is_microservice")
|
||||
extra_dependencies = get_option_value(plugin, "extra_dependencies", [])
|
||||
dm_desc_file = get_option_value(plugin, "dm_info_file", "")
|
||||
prefix = get_option_value(plugin, "vendor_prefix", None)
|
||||
repo_path = None
|
||||
name = os.path.basename(repo).replace('.git', '')
|
||||
|
||||
if not prefix:
|
||||
prefix = vendor_prefix
|
||||
|
||||
if repo is None or proto is None or dm_files is None or not isinstance(dm_files, list):
|
||||
BBF_ERROR_CODE += 1
|
||||
print(f"# Necessary input missing {BBF_ERROR_CODE}")
|
||||
continue
|
||||
|
||||
print(f' - Processing plugin: MS({is_microservice}) {plugin}')
|
||||
|
||||
if proto == "git":
|
||||
repo_path = "/tmp/repo/"+name
|
||||
version = get_option_value(plugin, "version")
|
||||
|
||||
if not clone_git_repository(repo, version):
|
||||
BBF_ERROR_CODE += 1
|
||||
print(f"# Failed to clone {repo} {BBF_ERROR_CODE}")
|
||||
continue
|
||||
print(f' Processing {get_repo_version_info(repo, version)}')
|
||||
elif proto == "local":
|
||||
repo_path = repo
|
||||
print(f' Processing {get_repo_version_info(repo, proto)}')
|
||||
if repo_path is None:
|
||||
BBF_ERROR_CODE += 1
|
||||
print(f"# Repository path not defined {BBF_ERROR_CODE}!!!")
|
||||
continue
|
||||
|
||||
create_folder("/tmp/repo/dm_info")
|
||||
if dm_desc_file.endswith('.json'):
|
||||
dest_file = "/tmp/repo/dm_info/" + os.path.basename(dm_desc_file).replace('.json', f"_{plugin_index}.json")
|
||||
rename_file(repo_path + "/" + dm_desc_file, dest_file)
|
||||
|
||||
LIST_FILES = []
|
||||
os.chdir(repo_path)
|
||||
for dm_file in dm_files:
|
||||
filename = dm_file
|
||||
if filename.endswith('*.c'):
|
||||
LIST_FILES.extend(glob.glob(filename))
|
||||
else:
|
||||
if os.path.isfile(filename):
|
||||
if filename.endswith('.c'):
|
||||
LIST_FILES.append(filename)
|
||||
elif filename.endswith('.json'):
|
||||
install_json_plugin(filename, "/usr/share/bbfdm/micro_services/core/"+f"{plugin_index}_{name}.json", prefix)
|
||||
else:
|
||||
BBF_ERROR_CODE += 1
|
||||
print(f"# Unknown file format {filename} {BBF_ERROR_CODE}")
|
||||
else:
|
||||
BBF_ERROR_CODE += 1
|
||||
print(f"# Error: File not accessible {filename} {BBF_ERROR_CODE}!!!!!!")
|
||||
|
||||
if len(LIST_FILES) > 0:
|
||||
if not generate_shared_library(f"{plugin_index}_{name}.so", LIST_FILES, prefix, extra_dependencies, is_microservice):
|
||||
BBF_ERROR_CODE += 1
|
||||
print(f"# Error: Failed to generate shared library for {plugin_index}_{name}, error {BBF_ERROR_CODE}")
|
||||
|
||||
clear_list(LIST_FILES)
|
||||
cd_dir(CURRENT_PATH)
|
||||
|
||||
print(f'Generating plugins completed, error {BBF_ERROR_CODE}')
|
||||
DB.sort(key=lambda x: x['param'], reverse=False)
|
||||
DB[:] = remove_duplicate_elements(DB)
|
||||
|
||||
|
||||
def generate_supported_dm(vendor_prefix=None, plugins=None):
|
||||
def generate_supported_dm(plugins=None):
|
||||
'''
|
||||
Generates supported data models and performs necessary actions.
|
||||
|
||||
Args:
|
||||
vendor_prefix (str, optional): Vendor prefix for shared libraries.
|
||||
plugins (list, optional): List of plugin configurations.
|
||||
'''
|
||||
|
||||
# Build && Install bbfdm
|
||||
build_and_install_bbfdm(vendor_prefix)
|
||||
|
||||
# Build && Install dm-cli
|
||||
build_and_install_dmcli()
|
||||
|
||||
# Download && Build Plugins Data Models
|
||||
download_and_build_plugins(plugins, vendor_prefix)
|
||||
|
||||
# Fill the list supported data model
|
||||
fill_list_supported_dm()
|
||||
fill_list_supported_dm(plugins)
|
||||
|
|
|
|||
28527
tools/datamodel.json
28527
tools/datamodel.json
File diff suppressed because one or more lines are too long
4
tools/docker/fixuid.yml
Normal file
4
tools/docker/fixuid.yml
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
user: dev
|
||||
group: dev
|
||||
paths:
|
||||
- /home/dev
|
||||
4
tools/docker/gitconfig
Normal file
4
tools/docker/gitconfig
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
[advice]
|
||||
detachedHead = false
|
||||
[credential]
|
||||
helper = store
|
||||
|
|
@ -24,7 +24,6 @@ def print_dm_usage():
|
|||
if len(sys.argv) < 2:
|
||||
print_dm_usage()
|
||||
|
||||
VENDOR_PREFIX = None
|
||||
PLUGINS = None
|
||||
OUTPUT = None
|
||||
DM_JSON_FILES = None
|
||||
|
|
@ -62,10 +61,6 @@ for option, value in json_data.items():
|
|||
bbf_xml.SOFTWARE_VERSION = value
|
||||
continue
|
||||
|
||||
elif option == "vendor_prefix":
|
||||
VENDOR_PREFIX = value
|
||||
continue
|
||||
|
||||
elif option == "dm_json_files":
|
||||
DM_JSON_FILES = value
|
||||
continue
|
||||
|
|
@ -82,43 +77,39 @@ for option, value in json_data.items():
|
|||
print_dm_usage()
|
||||
exit(1)
|
||||
|
||||
if OUTPUT is None:
|
||||
bbf.download_and_build_plugins(PLUGINS, VENDOR_PREFIX)
|
||||
else:
|
||||
bbf.generate_supported_dm(VENDOR_PREFIX, PLUGINS)
|
||||
bbf.generate_supported_dm(PLUGINS)
|
||||
|
||||
file_format = bbf.get_option_value(OUTPUT, "file_format", ['xml'])
|
||||
output_file_prefix = bbf.get_option_value(OUTPUT, "output_file_prefix", "datamodel")
|
||||
output_dir = bbf.get_option_value(OUTPUT, "output_dir", "./out")
|
||||
|
||||
bbf.create_folder(output_dir)
|
||||
file_format = bbf.get_option_value(OUTPUT, "file_format", ['xml'])
|
||||
output_file_prefix = bbf.get_option_value(OUTPUT, "output_file_prefix", "datamodel")
|
||||
output_dir = bbf.get_option_value(OUTPUT, "output_dir", "./out")
|
||||
|
||||
print("Dumping default DM_JSON_FILES")
|
||||
print(DM_JSON_FILES)
|
||||
DM_JSON_FILES.extend(glob.glob('/tmp/repo/dm_info/*.json'))
|
||||
print("Dumping all")
|
||||
print(DM_JSON_FILES)
|
||||
bbf.create_folder(output_dir)
|
||||
|
||||
print("Dumping default DM_JSON_FILES")
|
||||
print(DM_JSON_FILES)
|
||||
DM_JSON_FILES.extend(glob.glob('/tmp/desc_files/*.json'))
|
||||
print("Dumping all")
|
||||
print(DM_JSON_FILES)
|
||||
|
||||
if isinstance(file_format, list):
|
||||
for _format in file_format:
|
||||
|
||||
if _format == "xml":
|
||||
acs = bbf.get_option_value(OUTPUT, "acs", ['default'])
|
||||
if isinstance(acs, list):
|
||||
for acs_format in acs:
|
||||
|
||||
output_file_name = output_dir + '/' + output_file_prefix + '_' + acs_format + '.xml'
|
||||
if acs_format == "hdm":
|
||||
bbf_xml.generate_xml('HDM', DM_JSON_FILES, output_file_name)
|
||||
|
||||
if acs_format == "default":
|
||||
bbf_xml.generate_xml('default', DM_JSON_FILES, output_file_name)
|
||||
|
||||
if _format == "xls":
|
||||
output_file_name = output_dir + '/' + output_file_prefix + '.xls'
|
||||
bbf_excel.generate_excel(output_file_name)
|
||||
|
||||
print("Datamodel generation completed, aritifacts shall be available in out directory or as per input json configuration")
|
||||
if isinstance(file_format, list):
|
||||
for _format in file_format:
|
||||
|
||||
if _format == "xml":
|
||||
acs = bbf.get_option_value(OUTPUT, "acs", ['default'])
|
||||
if isinstance(acs, list):
|
||||
for acs_format in acs:
|
||||
|
||||
output_file_name = output_dir + '/' + output_file_prefix + '_' + acs_format + '.xml'
|
||||
if acs_format == "hdm":
|
||||
bbf_xml.generate_xml('HDM', DM_JSON_FILES, output_file_name)
|
||||
|
||||
if acs_format == "default":
|
||||
bbf_xml.generate_xml('default', DM_JSON_FILES, output_file_name)
|
||||
|
||||
if _format == "xls":
|
||||
output_file_name = output_dir + '/' + output_file_prefix + '.xls'
|
||||
bbf_excel.generate_excel(output_file_name)
|
||||
|
||||
print("Datamodel generation completed, aritifacts shall be available in out directory or as per input json configuration")
|
||||
|
||||
sys.exit(bbf.BBF_ERROR_CODE)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# Set variables
|
||||
CONTAINER_NAME="generate_dm_tools"
|
||||
IMAGE_NAME="dev.iopsys.eu:5050/bbf/bbfdm/tools:latest"
|
||||
IMAGE_NAME="dev.iopsys.eu:5050/bbf/bbfdm/tools:2.0"
|
||||
INPUT=""
|
||||
root="${PWD/tools}"
|
||||
|
||||
|
|
@ -23,9 +23,9 @@ usages()
|
|||
runner()
|
||||
{
|
||||
# Create and start the Docker container
|
||||
docker run --rm -it -v"${root}:/bbfdm" -w "/bbfdm" \
|
||||
docker run --rm -it -v ~/.ssh:/home/dev/.ssh -v"${root}:/builds/bbf/bbfdm" \
|
||||
--entrypoint=/bin/bash --name "$CONTAINER_NAME" "$IMAGE_NAME" \
|
||||
-c "./gitlab-ci/generate_supported_dm.sh /bbfdm/${1}"
|
||||
-c "./gitlab-ci/generate_supported_dm.sh ${1}"
|
||||
}
|
||||
|
||||
while getopts n:I:i:h opts
|
||||
|
|
|
|||
|
|
@ -196,13 +196,6 @@ if __name__ == '__main__':
|
|||
help= 'Includes OBJ/PARAM defined under remote repositories defined as bbf plugin'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-p', '--vendor-prefix',
|
||||
default = 'X_IOWRT_EU_',
|
||||
metavar = 'X_IOWRT_EU_',
|
||||
help = 'Generate data model tree using provided vendor prefix for vendor defined objects'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-o', '--output',
|
||||
default = "datamodel.xls",
|
||||
|
|
@ -225,7 +218,7 @@ if __name__ == '__main__':
|
|||
|
||||
plugins.append(r)
|
||||
|
||||
bbf.generate_supported_dm(args.vendor_prefix, plugins)
|
||||
bbf.generate_supported_dm(plugins)
|
||||
generate_excel(args.output)
|
||||
print(f'Datamodel generation completed, aritifacts available in {args.output}')
|
||||
sys.exit(bbf.BBF_ERROR_CODE)
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ def get_info_from_json(data, dm_json_files=None):
|
|||
string=string + "."
|
||||
|
||||
if len(string) != 0:
|
||||
string = string.replace("X_IOWRT_EU_", "{BBF_VENDOR_PREFIX}").replace("X_GENEXIS_EU_", "{BBF_VENDOR_PREFIX}")
|
||||
list_data.append(string)
|
||||
|
||||
if len(list_data) == 0:
|
||||
|
|
@ -103,6 +104,12 @@ def get_info_from_json(data, dm_json_files=None):
|
|||
|
||||
index = -1
|
||||
for key in ob.keys():
|
||||
|
||||
if key == "json_plugin_version":
|
||||
continue
|
||||
|
||||
key = key.replace("X_IOWRT_EU_", "{BBF_VENDOR_PREFIX}").replace("X_GENEXIS_EU_", "{BBF_VENDOR_PREFIX}")
|
||||
|
||||
if key in list_data:
|
||||
index = list_data.index(key)
|
||||
break
|
||||
|
|
@ -114,8 +121,8 @@ def get_info_from_json(data, dm_json_files=None):
|
|||
if i != (len(list_data) - 1) and list_data[i + 1] == list_data[i] + "{i}.":
|
||||
continue
|
||||
try:
|
||||
if str(list_data[i]).find("X_IOWRT_EU_") != -1:
|
||||
param = str(list_data[i]).replace("X_IOWRT_EU_", "{BBF_VENDOR_PREFIX}")
|
||||
if str(list_data[i]).find("X_IOWRT_EU_") != -1 or str(list_data[i]).find("X_GENEXIS_EU_") != -1:
|
||||
param = str(list_data[i]).replace("X_IOWRT_EU_", "{BBF_VENDOR_PREFIX}").replace("X_GENEXIS_EU_", "{BBF_VENDOR_PREFIX}")
|
||||
else:
|
||||
param = str(list_data[i])
|
||||
|
||||
|
|
@ -173,6 +180,8 @@ def generate_bbf_xml_file(output_file, dm_json_files=None):
|
|||
|
||||
ob_description = ET.SubElement(objec, "description")
|
||||
ob_description.text = desc.replace("<", "{").replace(">", "}") if desc is not None else ""
|
||||
if desc is None:
|
||||
print(f'#### Description should be added for {name} object ####')
|
||||
|
||||
DM_OBJ_COUNT += 1
|
||||
else:
|
||||
|
|
@ -185,7 +194,9 @@ def generate_bbf_xml_file(output_file, dm_json_files=None):
|
|||
|
||||
p_description = ET.SubElement(parameter, "description")
|
||||
p_description.text = desc.replace("<", "{").replace(">", "}") if desc is not None else ""
|
||||
|
||||
if desc is None:
|
||||
print(f'#### Description should be added for {name} parameter ####')
|
||||
|
||||
syntax = ET.SubElement(parameter, "syntax")
|
||||
|
||||
if list_ob is not None and len(list_ob) != 0:
|
||||
|
|
@ -390,13 +401,6 @@ if __name__ == '__main__':
|
|||
help= 'Includes OBJ/PARAM defined under remote repositories defined as bbf plugin'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-p', '--vendor-prefix',
|
||||
default = 'X_IOWRT_EU_',
|
||||
metavar = 'X_IOWRT_EU_',
|
||||
help = 'Generate data model tree using provided vendor prefix for vendor defined objects.'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'-d', '--device-protocol',
|
||||
default = 'DEVICE_PROTOCOL_DSLFTR069v1',
|
||||
|
|
@ -476,7 +480,7 @@ if __name__ == '__main__':
|
|||
|
||||
plugins.append(r)
|
||||
|
||||
bbf.generate_supported_dm(args.vendor_prefix, plugins)
|
||||
bbf.generate_supported_dm(plugins)
|
||||
generate_xml(args.format, args.dm_json_files, args.output)
|
||||
print(f'Datamodel generation completed, aritifacts available in {args.output}')
|
||||
sys.exit(bbf.BBF_ERROR_CODE)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,7 @@ OBJS = src/ubus/bbf_config.o src/ubus/utils.o
|
|||
|
||||
PROG_CFLAGS = $(CFLAGS) -Wall -Werror
|
||||
PROG_LDFLAGS = $(LDFLAGS)
|
||||
PROG_LIBS += -luci -lubus -lubox -lblobmsg_json
|
||||
PROG_LIBS += -luci -lubus -lubox -lblobmsg_json -ljson-c
|
||||
|
||||
INSTALL_DIR = /usr/sbin
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ List of utilities:
|
|||
|
||||
1. bbf.diag
|
||||
2. bbf.secure
|
||||
3. bbf.config
|
||||
3. bbf_configd
|
||||
|
||||
## bbf.diag utility
|
||||
|
||||
|
|
@ -67,82 +67,349 @@ But storing Secured parameters in plain text in uci/filesystem is bit of a secur
|
|||
|
||||
A how to use guide for bbf.secure available [here](https://dev.iopsys.eu/feed/iopsys/-/tree/devel/bbfdm?ref_type=heads#bbf_obfuscation_key)
|
||||
|
||||
## bbf.config utility
|
||||
## bbf_configd
|
||||
|
||||
The BBF Config daemon (`bbf_configd`) is a configuration management system designed for device management. It provides UBUS-based configuration commit and revert operations with service monitoring and reload capabilities.
|
||||
|
||||
OpenWRT way of reloading services with `ubus call uci commit '{"name":"<uci_name>"}` does not perfectly fits with datamodel requirements. It send a trigger via rpcd to procd by using this ubus call which returns instantly, internally procd reloads for all the services which has a reload dependency configured on that specific uci.
|
||||
Sometimes, there is a good amount of delay in trigger and actual service reload.
|
||||
bbf_configd solves that by adding an in-build reload monitoring functionality which get the list of impacted services and then monitor for PID change for the services with a timeout of 10 sec, with this we make sure Higher layer application(icwmp/obsupa) waits for the application before processing more command.
|
||||
|
||||
Bbf.config solves that by adding an in-build reload monitoring functionality which get the list of impacted services and then monitor for PID change for the services with a timeout of 10 sec, with this we make sure Higher layer application(icwmp/obsupa) waits for the application before processing more command.
|
||||
### Architecture
|
||||
|
||||
Currently have two variants of bbf.config, which can be enabled with below compile time configs
|
||||
- **Purpose**: Daemon implementing UBUS interface for configuration management
|
||||
- **Key Features**:
|
||||
- Configuration commit/revert operations
|
||||
- Service monitoring and validation
|
||||
- Event handling for configuration changes
|
||||
- Critical service detection and monitoring
|
||||
|
||||
1. CONFIG_BBF_CONFIGMNGR_SCRIPT_BACKEND => Simple rpcd script based backend
|
||||
2. CONFIG_BBF_CONFIGMNGR_C_BACKEND => C based application backend with PID monitoring (default)
|
||||
### UBUS Interface
|
||||
|
||||
### bbf.config Supported methods
|
||||
#### Methods
|
||||
|
||||
`bbf.config` provides several methods for managing and monitoring configuration changes in services. These methods can be accessed using the ubus command.
|
||||
##### `commit`
|
||||
|
||||
```bash
|
||||
$ ubus -v list bbf.config
|
||||
'bbf.config' @da2cc0d9
|
||||
"commit":{"services":"Array","proto":"String","reload":"Boolean"}
|
||||
"revert":{"services":"Array","proto":"String","reload":"Boolean"}
|
||||
"changes":{"services":"Array","proto":"String","reload":"Boolean"}
|
||||
"commit":{"services":"Array","proto":"String","reload":"Boolean"}
|
||||
- **Description**: Commits configuration changes and reloads affected services
|
||||
- **Parameters**:
|
||||
- `services` (array): List of specific services to commit
|
||||
- `proto` (string): Protocol type ("both", "cwmp", "usp")
|
||||
- `reload` (bool): Whether to reload services after commit
|
||||
- **Response**: Status message indicating success or failure
|
||||
|
||||
##### `revert`
|
||||
|
||||
"revert":{"services":"Array","proto":"String","reload":"Boolean"}
|
||||
- **Description**: Reverts uncommitted configuration changes
|
||||
- **Parameters**:
|
||||
- `services` (array): List of specific services to revert
|
||||
- `proto` (string): Protocol type
|
||||
- **Response**: Status message indicating success or failure
|
||||
|
||||
#### Events
|
||||
|
||||
##### `bbfdm.apply`
|
||||
- **Trigger**: Sent after configuration commits
|
||||
- **Payload**: Protocol information and list of changed UCI files
|
||||
- **Purpose**: Notifies components about applied configuration changes to perform synchronization if required
|
||||
|
||||
### Configuration Management
|
||||
|
||||
#### Protocol Support
|
||||
The daemon supports multiple protocols with dedicated configuration directories:
|
||||
|
||||
| Protocol | Config Directory | DMMAP Directory | Index |
|
||||
|----------|------------------|-----------------|-------|
|
||||
| both | `/tmp/bbfdm/.bbfdm/config/` | `/tmp/bbfdm/.bbfdm/dmmap/` | 0 |
|
||||
| cwmp | `/tmp/bbfdm/.cwmp/config/` | `/tmp/bbfdm/.cwmp/dmmap/` | 1 |
|
||||
| usp | `/tmp/bbfdm/.usp/config/` | `/tmp/bbfdm/.usp/dmmap/` | 2 |
|
||||
|
||||
#### UCI Integration
|
||||
- **Standard UCI Path**: `/etc/config/`
|
||||
- **DMMAP UCI Path**: `/etc/bbfdm/dmmap/`
|
||||
- **Operations**: Commit, revert, and list configurations
|
||||
- **Persistence**: Changes saved to protocol-specific directories
|
||||
|
||||
### Service Management
|
||||
|
||||
#### Service Monitoring
|
||||
- **Validation**: Checks if services are properly reloaded after configuration changes
|
||||
- **Criteria**:
|
||||
- Instance status changes (running/stopped)
|
||||
- Process ID (PID) changes
|
||||
- Instance count changes
|
||||
- **Timeout**: Configurable wait time for service reload validation
|
||||
|
||||
#### Critical Services
|
||||
- **Definition File**: `/etc/bbfdm/critical_services.json`
|
||||
- **Purpose**: Identifies services that require monitoring during configuration changes
|
||||
- **Effect**: Enables asynchronous request handling with validation
|
||||
|
||||
#### External Handlers
|
||||
- **Configuration Path**: `/etc/bbfdm/services/`
|
||||
- **Types**: DMMAP and UCI handlers
|
||||
- **Default Handler**: `/etc/bbfdm/bbf_default_reload.sh`
|
||||
- **Purpose**: Custom scripts for service-specific configuration handling
|
||||
|
||||
##### Example and Explanation
|
||||
|
||||
```json
|
||||
{
|
||||
"daemon": {
|
||||
"enable": "1",
|
||||
"service_name": "xxxx",
|
||||
"unified_daemon": true,
|
||||
"services": [
|
||||
{
|
||||
"parent_dm": "Device.",
|
||||
"object": "XXXX"
|
||||
}
|
||||
],
|
||||
"config": {
|
||||
"loglevel": "3"
|
||||
},
|
||||
"apply_handler": {
|
||||
"uci": [
|
||||
{
|
||||
"file": [
|
||||
"xxxx",
|
||||
"yyyy"
|
||||
],
|
||||
"external_handler": "/etc/xxxx/bbf_config_reload.sh"
|
||||
}
|
||||
],
|
||||
"dmmap": [
|
||||
{
|
||||
"file": [
|
||||
"zzzz"
|
||||
],
|
||||
"external_handler": "/etc/xxxx/bbf_dmmap_handler.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
#### bbf.config commit method:
|
||||
|
||||
This method commits configuration changes to the specified services based on the given `proto` option (protocol). It reloads services according to `reload` option but handles critical services differently.
|
||||
- If external component asks to commit UCI file 'xxxx' or 'yyyy' or both then this module will commit the changes from protocol based UCI save directory to the standard UCI and will execute the script provided in the `external_handler` field. If no external handler is provided then it will execute default reload handler script `/etc/bbfdm/bbf_default_reload.sh`.
|
||||
- If there is any change in the dmmap file `zzzz` and a commit request has been received then this module will commit the changes in standard dmmap file from the protocol based dmmap directory and if any external handler is given like in above example then it will execute that script. Services may use this handler script to perform any desired tasks on changes in the dmmap file.
|
||||
|
||||
The Critical services are defined in `/etc/bbfdm/critical_services.json` file.
|
||||
- If a service is critical, the process waits until the service's timeout expires or the service's PID changes and then it does reload the service.
|
||||
- Non-critical services are reloaded immediately.
|
||||
### Operational Flow
|
||||
|
||||
Critical Services File
|
||||
#### Commit Operation
|
||||
1. Parse incoming UBUS request parameters
|
||||
2. Load critical services configuration
|
||||
3. If monitoring required, capture current service states
|
||||
4. Process configuration changes:
|
||||
- Specific services: Process only requested services
|
||||
- All services: Process all modified configurations
|
||||
5. Execute external handlers for affected services if any otherwise default handler
|
||||
6. If monitoring enabled:
|
||||
- Defer response and validate service reloads
|
||||
- Send response after validation or timeout
|
||||
7. Send bbfdm.apply event with list of all modified UCI names
|
||||
8. Clean up allocated resources
|
||||
|
||||
The following file defines critical services for each protocol:
|
||||
#### Revert Operation
|
||||
1. Parse incoming UBUS request parameters
|
||||
2. Revert UCI changes for specified or all services
|
||||
4. Return immediate success response
|
||||
|
||||
#### Flow Diagram
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Start bbf_configd] --> B[Parse command line args]
|
||||
B --> C[Initialize ulog & uloop]
|
||||
C --> D[Connect to UBUS]
|
||||
D --> E[Load critical services JSON]
|
||||
E --> F[Load apply handlers from /etc/bbfdm/services]
|
||||
F --> G[Register UBUS object 'bbf.config']
|
||||
G --> H[Register event handler 'bbf.config.notify']
|
||||
H --> I[Enter uloop_run - Wait for requests]
|
||||
|
||||
I --> J{Incoming Request}
|
||||
|
||||
J -->|commit| K[bbf_config_commit_handler]
|
||||
J -->|revert| L[bbf_config_revert_handler]
|
||||
J -->|notify event| M[receive_notify_event]
|
||||
|
||||
%% Commit Handler Flow
|
||||
K --> K1[Parse blob message]
|
||||
K1 --> K2[Allocate async request structure]
|
||||
K2 --> K3[Determine protocol index from 'proto']
|
||||
K3 --> K4[Get monitor status for critical services]
|
||||
K4 --> K5{Monitor required?}
|
||||
|
||||
K5 -->|Yes| K6[Get current service info via UBUS]
|
||||
K5 -->|No| K7[Process services]
|
||||
|
||||
K6 --> K7
|
||||
K7 --> K8{Services specified?}
|
||||
|
||||
K8 -->|Yes| K9[commit specified configs]
|
||||
K8 -->|No| K10[commit all config changes]
|
||||
|
||||
K9 --> K11[Execute action scripts]
|
||||
K10 --> K11
|
||||
K11 --> K12{Monitor enabled?}
|
||||
|
||||
K12 -->|Yes| K13[Defer request & set timeout]
|
||||
K12 -->|No| K14[Send immediate reply]
|
||||
|
||||
K13 --> K15[validate_required_services]
|
||||
K15 --> K16{Services reloaded?}
|
||||
K16 -->|Yes| K17[Complete request, send reply]
|
||||
K16 -->|No| K18[Wait & retry]
|
||||
K18 --> K17
|
||||
|
||||
K14 --> K20[Send bbfdm.apply event]
|
||||
K17 --> K19[Sync with externally changed uci list]
|
||||
K19 --> K20
|
||||
K20 --> I
|
||||
|
||||
%% Revert Handler Flow
|
||||
L --> L1[Parse blob message]
|
||||
L1 --> L2[Determine protocol index]
|
||||
L2 --> L3{Services specified?}
|
||||
L3 -->|Yes| L4[revert specified_services]
|
||||
L3 -->|No| L5[revert all services]
|
||||
L4 --> L6[Send reply]
|
||||
L5 --> L6
|
||||
L6 --> I
|
||||
|
||||
%% Notify Event Flow
|
||||
M --> M1[Parse config name from event]
|
||||
M1 --> M2{Internal commit in progress?}
|
||||
M2 -->|Yes| M3[Add to global external changed list]
|
||||
M2 -->|No| M4[Send immediate bbfdm.apply event]
|
||||
M3 --> I
|
||||
M4 --> I
|
||||
|
||||
%% Service Processing Detail
|
||||
subgraph SP[Service Processing Details]
|
||||
SP1[commit services] --> SP2[For each service]
|
||||
SP2 --> SP3[Determine UCI path & save directory]
|
||||
SP3 --> SP4[Set UCI context directories]
|
||||
SP4 --> SP5[Lookup UCI pointer]
|
||||
SP5 -->|Commit| SP6[uci_commit]
|
||||
SP6 --> SP7[Add specific action to action list]
|
||||
SP7 --> SP8[Add to changed UCI list if not DMMAP]
|
||||
end
|
||||
|
||||
%% UBUS Service Info Gathering
|
||||
subgraph SG[Service Info Gathering]
|
||||
SG1[fill_service_info] --> SG2[Call service.list via UBUS]
|
||||
SG2 --> SG3[_get_service_list_cb or _get_specific_service_cb]
|
||||
SG3 --> SG4[Parse service instances & triggers]
|
||||
SG4 --> SG5[Build config package structure]
|
||||
end
|
||||
|
||||
K6 -.-> SG1
|
||||
K15 -.-> SG1
|
||||
K9 -.-> SP1
|
||||
K10 -.-> SP1
|
||||
|
||||
style K fill:#e1f5fe
|
||||
style L fill:#f3e5f5
|
||||
style M fill:#e8f5e8
|
||||
style K13 fill:#fff3e0
|
||||
style K20 fill:#fce4ec
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
#### Common Error Scenarios
|
||||
- **Memory Allocation Failures**: Proper cleanup and error responses
|
||||
- **UCI Operation Failures**: Logging and graceful continuation
|
||||
- **UBUS Communication Errors**: Timeout handling and retries
|
||||
- **Service Validation Timeouts**: Fallback completion after maximum wait time
|
||||
|
||||
### Configuration Files
|
||||
|
||||
#### Critical Services Definition (`critical_services.json`)
|
||||
```json
|
||||
{
|
||||
"cwmp": ["cwmp_service"],
|
||||
"usp": ["usp_service"]
|
||||
}
|
||||
```
|
||||
|
||||
##### Example
|
||||
```bash
|
||||
cat /etc/bbfdm/critical_services.json
|
||||
{
|
||||
"usp": [
|
||||
"firewall",
|
||||
"network",
|
||||
"dhcp",
|
||||
"wireless",
|
||||
"time"
|
||||
"/etc/config/mapcontroller",
|
||||
"/etc/config/wireless",
|
||||
"/etc/bbfdm/dmmap/WiFi",
|
||||
...
|
||||
...
|
||||
],
|
||||
"cwmp": [
|
||||
"firewall",
|
||||
"network",
|
||||
"dhcp",
|
||||
"stunc",
|
||||
"xmpp",
|
||||
"wireless",
|
||||
"time"
|
||||
"/etc/config/mapcontroller",
|
||||
"/etc/config/wireless",
|
||||
"/etc/bbfdm/dmmap/WiFi",
|
||||
...
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### bbf.config revert method:
|
||||
this method commits the changes in the required services based on proto option.
|
||||
|
||||
#### bbf.config changes method:
|
||||
|
||||
this method provides the list of certical services based on protocol (proto option) and provide the available config changes based on protocol.
|
||||
```bash
|
||||
ubus call bbf.config changes '{"proto":"usp"}'
|
||||
#### Service Configuration for apply handler (`services/*.json`)
|
||||
```json
|
||||
{
|
||||
"configs": [
|
||||
"users",
|
||||
"wireless"
|
||||
],
|
||||
"critical_services": [
|
||||
"firewall",
|
||||
"network",
|
||||
"dhcp",
|
||||
"wireless",
|
||||
"time"
|
||||
]
|
||||
"daemon": {
|
||||
"enable": "0 or 1",
|
||||
"service_name": "name_of_the_service",
|
||||
...
|
||||
...
|
||||
"apply_handler": {
|
||||
"uci": [
|
||||
{
|
||||
"file": ["config_name"],
|
||||
"external_handler": "/path/to/handler.sh"
|
||||
}
|
||||
],
|
||||
"dmmap": [
|
||||
{
|
||||
"file": ["dmmap_file"],
|
||||
"external_handler": "/path/to/dmmap_handler.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
#### UBUS Commands
|
||||
```bash
|
||||
# Commit all changes
|
||||
ubus call bbf.config commit '{"proto":"both", "reload":true}'
|
||||
|
||||
# Commit specific services
|
||||
ubus call bbf.config commit '{"services":["network","wireless"], "proto":"both"}'
|
||||
|
||||
# Revert all changes
|
||||
ubus call bbf.config revert '{"proto":"both"}'
|
||||
|
||||
# Revert specific services
|
||||
ubus call bbf.config revert '{"services":["network"], "proto":"both"}'
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
#### Libraries
|
||||
- `libubox`: UBUS and event loop functionality
|
||||
- `libuci`: UCI configuration management
|
||||
- `json-c`: JSON parsing for configuration files
|
||||
- `libc`: Standard C library functions
|
||||
|
||||
#### System Requirements
|
||||
- OpenWrt-based system
|
||||
- UBUS daemon running
|
||||
- UCI system configured
|
||||
- Proper directory structure for configuration files
|
||||
|
|
|
|||
|
|
@ -1,144 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
BBFDM_CONFIG_CONFDIR="/etc/config"
|
||||
BBFDM_DMMAP_CONFDIR="/etc/bbfdm/dmmap"
|
||||
BBFDM_CONFIG_SAVEDIR="/tmp/bbfdm/.bbfdm/config"
|
||||
BBFDM_DMMAP_SAVEDIR="/tmp/bbfdm/.bbfdm/dmmap"
|
||||
|
||||
LOGLEVEL="$(uci -q get bbfdm.bbfdmd.loglevel)"
|
||||
|
||||
log() {
|
||||
local level
|
||||
|
||||
level="${LOGLEVEL:-0}"
|
||||
if [ "${level}" -gt 2 ]; then
|
||||
echo "$@" | logger -t bbf.config -p info
|
||||
fi
|
||||
}
|
||||
|
||||
check_result() {
|
||||
local res="$1"
|
||||
local service="$2"
|
||||
local action="$3"
|
||||
|
||||
if [ "${res}" -ne 0 ]; then
|
||||
echo "{ \"error\": \"Failed to ${action} ${service} service\" }"
|
||||
exit "${res}"
|
||||
fi
|
||||
}
|
||||
|
||||
apply_config_changes() {
|
||||
local service="$1"
|
||||
local action="$3"
|
||||
local reload="$4"
|
||||
|
||||
# Check if either service or action is empty
|
||||
if [ -z "$service" ] || [ -z "$action" ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
log "Applying $action configuration for service: $service"
|
||||
|
||||
# Commit/Revert config changes
|
||||
log "Applying ${action} configuration for file: ${service}"
|
||||
uci -q -c "${BBFDM_CONFIG_CONFDIR}" -t "${BBFDM_CONFIG_SAVEDIR}" "${action}" "${service}"
|
||||
check_result "$?" "${service}" "${action}"
|
||||
|
||||
if [ "${reload}" == "1" ]; then
|
||||
# Reload service
|
||||
ubus -t 1 call uci "${action}" "{'config': '${service}'}"
|
||||
check_result "$?" "${service}" "${action}"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
list)
|
||||
echo '{ "commit": { "services": [], "proto": "str", "monitor": true, "reload": true }, "revert": { "services": [], "proto": "str", "monitor": true, "reload": true }, "changes": { "proto": "str" } }'
|
||||
;;
|
||||
call)
|
||||
# Read input JSON from standard input
|
||||
read -r input
|
||||
|
||||
# Parse input JSON
|
||||
json_load "${input}"
|
||||
|
||||
# Get the 'proto' value from the input JSON
|
||||
json_get_var proto proto
|
||||
|
||||
if [ "${proto}" == "cwmp" ]; then
|
||||
BBFDM_CONFIG_SAVEDIR="/tmp/bbfdm/.cwmp/config"
|
||||
BBFDM_DMMAP_SAVEDIR="/tmp/bbfdm/.cwmp/dmmap"
|
||||
elif [ "${proto}" == "usp" ]; then
|
||||
BBFDM_CONFIG_SAVEDIR="/tmp/bbfdm/.usp/config"
|
||||
BBFDM_DMMAP_SAVEDIR="/tmp/bbfdm/.usp/dmmap"
|
||||
fi
|
||||
|
||||
case "$2" in
|
||||
commit|revert)
|
||||
|
||||
# Get the 'reload' value from the input JSON
|
||||
json_get_var reload reload
|
||||
json_get_var monitor monitor
|
||||
|
||||
if [ -z "${reload}" ]; then
|
||||
reload=1
|
||||
else
|
||||
if [ "${reload}" != "0" ] && [ "${reload}" != "1" ]; then
|
||||
echo '{ "error": "Reload should be boolean type !!!" }'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if 'services' array is provided
|
||||
json_get_type type "services"
|
||||
if [ -z "${type}" ]; then
|
||||
# Iterate over all services and apply config changes
|
||||
for config in $(uci -q -c "${BBFDM_CONFIG_CONFDIR}" -t "${BBFDM_CONFIG_SAVEDIR}" changes | awk -F'.' '{print $1}' | sort | uniq); do
|
||||
apply_config_changes "${config}" "" "$2" "$reload"
|
||||
done
|
||||
else
|
||||
# Check if 'services' is array
|
||||
if [ "${type}" != "array" ]; then
|
||||
echo '{ "error": "Services argument should be array of strings !!!" }'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Iterate over each service and apply config changes
|
||||
json_for_each_item "apply_config_changes" "services" "$2" "$reload"
|
||||
fi
|
||||
|
||||
if [ "${reload}" == "1" ]; then
|
||||
# Commit/Revert bbfdm dmmap config changes
|
||||
if [ -d "${BBFDM_DMMAP_SAVEDIR}" ] && [ "$(ls -A "${BBFDM_DMMAP_SAVEDIR}" 2>/dev/null)" ]; then
|
||||
for file in "${BBFDM_DMMAP_SAVEDIR}"/*; do
|
||||
file_name=$(basename "${file}")
|
||||
log "Applying $2 configuration for file: $file_name"
|
||||
uci -q -c "${BBFDM_DMMAP_CONFDIR}" -t "${BBFDM_DMMAP_SAVEDIR}" "$2" "${file_name}"
|
||||
check_result "$?" "${file_name}" "$2"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${monitor}" -eq "1" ]; then
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
# Send 'bbf.config.change' event to run refresh instances
|
||||
ubus send bbf.config.change
|
||||
|
||||
echo '{ "status": "ok" }'
|
||||
;;
|
||||
changes)
|
||||
json_init
|
||||
json_add_array "configs"
|
||||
for config in $(uci -q -c "${BBFDM_CONFIG_CONFDIR}" -t "${BBFDM_CONFIG_SAVEDIR}" changes | awk -F'.' '{print $1}' | sort | uniq); do
|
||||
json_add_string "" "${config}"
|
||||
done
|
||||
json_close_array
|
||||
json_dump
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
|
@ -26,22 +26,84 @@ uci_get_bbf_dmmap() {
|
|||
}
|
||||
|
||||
get_ip_addr_used() {
|
||||
protocol=$1
|
||||
interface=$2
|
||||
TARGET="$1"
|
||||
PROTO="$2"
|
||||
IFACE="$3"
|
||||
|
||||
if [ "$protocol" = "IPv6" ]; then
|
||||
if [ -n "$interface" ]; then
|
||||
ip_addr_used=$(ifstatus "$interface" | jsonfilter -e '@["ipv6-address"][0].address')
|
||||
# Function to check if the input is an IPv6 address format
|
||||
is_ipv6_format() {
|
||||
echo "$1" | grep -q ":"
|
||||
}
|
||||
|
||||
# Function to resolve domain name to IP address based on the protocol version
|
||||
resolve_ip() {
|
||||
nslookup "$2" 2>/dev/null | awk -v proto="$1" '
|
||||
proto == "IPv6" && /^Address: .*:.*$/ { print $2; exit }
|
||||
proto == "IPv4" && /^Address: / && $2 !~ /:/ { print $2; exit }
|
||||
'
|
||||
}
|
||||
|
||||
# Function to extract host from a URL or return the input if it's not a URL
|
||||
extract_host() {
|
||||
input="$1"
|
||||
# Check if it's a URL (contains "://")
|
||||
if echo "$input" | grep -q '://'; then
|
||||
# Remove the scheme (http://, https://, ftp://, etc.)
|
||||
input=$(echo "$input" | sed -E 's|^[a-zA-Z]+://||')
|
||||
|
||||
# If input starts with [, it's an IPv6 address in the form of [fd7d::1]:port
|
||||
if echo "$input" | grep -q '^\[.*\]'; then
|
||||
echo "$input" | sed -E 's/^\[([0-9a-fA-F:]+)\].*/\1/'
|
||||
else
|
||||
# Strip everything after the first colon or slash (to handle domain:port or domain/path)
|
||||
echo "$input" | sed -E 's/[:/].*//'
|
||||
fi
|
||||
else
|
||||
ip_addr_used=$(ip -6 route | grep default | awk -F ' ' '{print $3}' | head -n 1)
|
||||
# Return the input as-is if it's not a URL
|
||||
echo "$input"
|
||||
fi
|
||||
else
|
||||
if [ -n "$interface" ]; then
|
||||
ip_addr_used=$(ifstatus "$interface" | jsonfilter -e '@["ipv4-address"][0].address')
|
||||
}
|
||||
|
||||
# Normalize input
|
||||
HOST=$(extract_host "${TARGET}")
|
||||
RESOLVED_IP=""
|
||||
|
||||
case "${PROTO}" in
|
||||
IPv4)
|
||||
IP_VERSION=4
|
||||
RESOLVED_IP=$(echo "$HOST" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' || resolve_ip "IPv4" "$HOST")
|
||||
ROUTE_CMD="ip route get"
|
||||
;;
|
||||
IPv6)
|
||||
IP_VERSION=6
|
||||
RESOLVED_IP=$(is_ipv6_format "$HOST" && echo "$HOST" || resolve_ip "IPv6" "$HOST")
|
||||
ROUTE_CMD="ip -6 route get"
|
||||
;;
|
||||
Any | *)
|
||||
RESOLVED_IP=$(echo "$HOST" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$' || resolve_ip "IPv4" "$HOST")
|
||||
if [ -n "${RESOLVED_IP}" ]; then
|
||||
IP_VERSION=4
|
||||
ROUTE_CMD="ip route get"
|
||||
else
|
||||
RESOLVED_IP=$(is_ipv6_format "$HOST" && echo "$HOST" || resolve_ip "IPv6" "$HOST")
|
||||
IP_VERSION=6
|
||||
ROUTE_CMD="ip -6 route get"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "${RESOLVED_IP}" ]; then
|
||||
if [ -n "${IFACE}" ]; then
|
||||
IFACE_DEV=$(ifstatus "${IFACE}" | jsonfilter -e @.l3_device)
|
||||
if [ "$IP_VERSION" -eq 6 ]; then
|
||||
SRC_IP=$(ip -6 addr show "${IFACE_DEV}" | awk '/inet6 / && $2 !~ /fe80/ { sub(/\/.*/, "", $2); print $2; exit }')
|
||||
else
|
||||
SRC_IP=$(ip addr show "${IFACE_DEV}" | awk '/inet / { sub(/\/.*/, "", $2); print $2; exit }')
|
||||
fi
|
||||
else
|
||||
ip_addr_used=$(ip route | grep default | awk -F ' ' '{print $9}')
|
||||
SRC_IP=$(${ROUTE_CMD} "${RESOLVED_IP}" 2>/dev/null | sed -n 's/.*src \([0-9a-fA-F:.]*\).*/\1/p' | head -n1)
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "${ip_addr_used}"
|
||||
|
||||
echo "${SRC_IP}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,22 @@
|
|||
#
|
||||
|
||||
# Send 'bbf.config.notify' event to notify about the 'config.change' from external configs
|
||||
ubus send bbf.config.notify
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
config="${1}"
|
||||
|
||||
if [ -z "${config}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
json_init
|
||||
json_add_string "config" "${config}"
|
||||
json_compact
|
||||
|
||||
json_data=$(json_dump)
|
||||
|
||||
ubus send bbf.config.notify "${json_data}"
|
||||
|
||||
json_cleanup
|
||||
|
||||
exit 0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Script: bbf_default_reload.sh
|
||||
# Description:
|
||||
# This script reloads UCI Configs based on input args.
|
||||
# Input args should be space separated uci file names
|
||||
#
|
||||
# Usage:
|
||||
# sh bbf_default_reload.sh network firewall
|
||||
#
|
||||
# Actions:
|
||||
# - performs "ubus call uci commit '{"config":"<uci name>"}' for
|
||||
# each uci file received in argument list
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
log() {
|
||||
echo "${@}"|logger -t bbf.config.default.reload -p info
|
||||
}
|
||||
|
||||
input="$@"
|
||||
|
||||
# Validate input
|
||||
if [ -z "$input" ]; then
|
||||
log "Error: No input provided"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for uci in ${input}; do
|
||||
log "Reloading ${uci} config"
|
||||
|
||||
json_init
|
||||
json_add_string "config" "${uci}"
|
||||
json_compact
|
||||
|
||||
json_data=$(json_dump)
|
||||
ubus -t 5 call uci commit "${json_data}"
|
||||
|
||||
json_cleanup
|
||||
done
|
||||
|
||||
exit 0
|
||||
|
|
@ -14,6 +14,8 @@
|
|||
#include <libubox/blobmsg_json.h>
|
||||
#include <libubox/uloop.h>
|
||||
#include <libubus.h>
|
||||
#include <json-c/json.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
|
|
@ -25,16 +27,10 @@
|
|||
#define DEFAULT_LOG_LEVEL LOG_INFO
|
||||
|
||||
#define BBF_CONFIG_DAEMON_NAME "bbf_configd"
|
||||
#define CONFIG_CONFDIR "/etc/config/"
|
||||
#define DMMAP_CONFDIR "/etc/bbfdm/dmmap/"
|
||||
#define CRITICAL_DEF_JSON "/etc/bbfdm/critical_services.json"
|
||||
#define BBFDM_MICROSERVICE_INPUT_PATH "/etc/bbfdm/services"
|
||||
|
||||
struct proto_args {
|
||||
const char *name;
|
||||
const char *config_savedir;
|
||||
const char *dmmap_savedir;
|
||||
unsigned char index;
|
||||
};
|
||||
static struct list_head g_external_changed_uci;
|
||||
|
||||
// Structure to represent an instance of a service
|
||||
struct instance {
|
||||
|
|
@ -57,14 +53,17 @@ struct config_package {
|
|||
};
|
||||
|
||||
struct bbf_config_async_req {
|
||||
int idx;
|
||||
struct ubus_context *ctx;
|
||||
struct ubus_request_data req;
|
||||
struct uloop_timeout timeout;
|
||||
struct blob_attr *services;
|
||||
struct config_package package[MAX_PACKAGE_NUM];
|
||||
struct list_head changed_uci_list;
|
||||
};
|
||||
|
||||
static struct blob_buf g_critical_bb;
|
||||
static struct list_head g_apply_handlers;
|
||||
|
||||
#ifdef BBF_CONFIG_DEBUG
|
||||
static void log_instance(struct instance *inst)
|
||||
|
|
@ -119,18 +118,6 @@ static void show_package_tree(struct config_package *packages)
|
|||
}
|
||||
#endif
|
||||
|
||||
static struct proto_args supported_protocols[] = {
|
||||
{
|
||||
"both", "/tmp/bbfdm/.bbfdm/config/", "/tmp/bbfdm/.bbfdm/dmmap/", 0
|
||||
},
|
||||
{
|
||||
"cwmp", "/tmp/bbfdm/.cwmp/config/", "/tmp/bbfdm/.cwmp/dmmap/", 1
|
||||
},
|
||||
{
|
||||
"usp", "/tmp/bbfdm/.usp/config/", "/tmp/bbfdm/.usp/dmmap/", 2
|
||||
},
|
||||
};
|
||||
|
||||
static bool g_internal_commit = false;
|
||||
|
||||
enum {
|
||||
|
|
@ -146,16 +133,6 @@ static const struct blobmsg_policy bbf_config_policy[] = {
|
|||
[SERVICES_RELOAD] = { .name = "reload", .type = BLOBMSG_TYPE_BOOL },
|
||||
};
|
||||
|
||||
static unsigned char get_idx_by_proto(const char *proto)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(supported_protocols); i++) {
|
||||
if (strcmp(supported_protocols[i].name, proto) == 0)
|
||||
return supported_protocols[i].index;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_config_idx(struct config_package *package, const char *config_name)
|
||||
{
|
||||
if (!config_name)
|
||||
|
|
@ -419,9 +396,13 @@ static bool validate_required_services(struct ubus_context *ctx, struct config_p
|
|||
// Iterate through each service attribute
|
||||
blobmsg_for_each_attr(service, services, rem) {
|
||||
char *config_name = blobmsg_get_string(service);
|
||||
char *p = strrchr(config_name, '/');
|
||||
if (p) {
|
||||
p = p + 1;
|
||||
}
|
||||
|
||||
// Find the index of the configuration package
|
||||
int idx = find_config_idx(package, config_name);
|
||||
int idx = find_config_idx(package, p ? p : config_name);
|
||||
if (idx < 0)
|
||||
continue;
|
||||
|
||||
|
|
@ -482,22 +463,48 @@ wait:
|
|||
return false;
|
||||
}
|
||||
|
||||
static void send_bbf_config_change_event()
|
||||
static void send_bbf_apply_event(int idx, struct list_head *changed_uci_list)
|
||||
{
|
||||
char protocol[16] = {0};
|
||||
|
||||
if (changed_uci_list == NULL || list_empty(changed_uci_list))
|
||||
return;
|
||||
|
||||
struct ubus_context *ctx;
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
ctx = ubus_connect(NULL);
|
||||
if (ctx == NULL) {
|
||||
ULOG_ERR("Can't create UBUS context for 'bbf.config.change' event");
|
||||
return;
|
||||
}
|
||||
|
||||
ULOG_INFO("Sending bbf.config.change event");
|
||||
struct modi_uci_node *node = NULL, *tmp = NULL;
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
ubus_send_event(ctx, "bbf.config.change", bb.head);
|
||||
|
||||
snprintf(protocol, sizeof(protocol), "%s", (idx == -1) ? "external" : get_proto_name_by_idx(idx));
|
||||
|
||||
blobmsg_add_string(&bb, "proto", protocol);
|
||||
void *array = blobmsg_open_array(&bb, "uci_changed");
|
||||
list_for_each_entry_safe(node, tmp, changed_uci_list, list) {
|
||||
if (node->uci == NULL) {
|
||||
list_del(&node->list);
|
||||
FREE(node);
|
||||
continue;
|
||||
}
|
||||
|
||||
blobmsg_add_string(&bb, NULL, node->uci);
|
||||
list_del(&node->list);
|
||||
FREE(node->uci);
|
||||
FREE(node);
|
||||
}
|
||||
blobmsg_close_array(&bb, array);
|
||||
|
||||
ctx = ubus_connect(NULL);
|
||||
if (ctx == NULL) {
|
||||
ULOG_ERR("Can't create UBUS context for 'bbfdm.apply' event");
|
||||
blob_buf_free(&bb);
|
||||
return;
|
||||
}
|
||||
|
||||
ULOG_INFO("Sending bbfdm.apply event");
|
||||
|
||||
ubus_send_event(ctx, "bbfdm.apply", bb.head);
|
||||
blob_buf_free(&bb);
|
||||
ubus_free(ctx);
|
||||
}
|
||||
|
|
@ -525,13 +532,28 @@ static void complete_deferred_request(struct bbf_config_async_req *async_req)
|
|||
// Complete the deferred request and send the response
|
||||
ubus_complete_deferred_request(async_req->ctx, &async_req->req, 0);
|
||||
|
||||
// If any uci is changed externally then add it in bbf.apply event
|
||||
struct modi_uci_node *node = NULL, *tmp = NULL;
|
||||
list_for_each_entry_safe(node, tmp, &g_external_changed_uci, list) {
|
||||
if (node->uci == NULL) {
|
||||
list_del(&node->list);
|
||||
FREE(node);
|
||||
continue;
|
||||
}
|
||||
|
||||
add_changed_uci_list(&async_req->changed_uci_list, node->uci);
|
||||
list_del(&node->list);
|
||||
FREE(node->uci);
|
||||
FREE(node);
|
||||
}
|
||||
|
||||
// Send 'bbf.apply' event
|
||||
send_bbf_apply_event(async_req->idx, &async_req->changed_uci_list);
|
||||
|
||||
// Free the allocated memory
|
||||
FREE(async_req->services);
|
||||
FREE(async_req);
|
||||
|
||||
// Send 'bbf.config.change' event to run refresh instances
|
||||
send_bbf_config_change_event();
|
||||
|
||||
// Set internal commit to false
|
||||
g_internal_commit = false;
|
||||
|
||||
|
|
@ -567,7 +589,7 @@ struct blob_attr *get_blob_attr_with_idx(int idx, struct blob_attr *msg)
|
|||
struct blob_attr *params = NULL;
|
||||
struct blob_attr *cur;
|
||||
int rem;
|
||||
const char *proto = supported_protocols[idx].name;
|
||||
const char *proto = get_proto_name_by_idx(idx);
|
||||
|
||||
blobmsg_for_each_attr(cur, msg, rem) {
|
||||
const char *name = blobmsg_name(cur);
|
||||
|
|
@ -588,7 +610,7 @@ static bool check_if_critical_service(int proto_idx, const char *sname)
|
|||
|
||||
services_ba = get_blob_attr_with_idx(proto_idx, g_critical_bb.head);
|
||||
if (services_ba == NULL) {
|
||||
ULOG_DEBUG("Critical service not defined for %s proto", supported_protocols[proto_idx].name);
|
||||
ULOG_DEBUG("Critical service not defined for %s proto", get_proto_name_by_idx(proto_idx));
|
||||
return is_critical;
|
||||
}
|
||||
|
||||
|
|
@ -600,7 +622,7 @@ static bool check_if_critical_service(int proto_idx, const char *sname)
|
|||
break;
|
||||
}
|
||||
}
|
||||
ULOG_DEBUG("Service %s, found %d, in %s critical list", sname, is_critical, supported_protocols[proto_idx].name);
|
||||
ULOG_DEBUG("Service %s, found %d, in %s critical list", sname, is_critical, get_proto_name_by_idx(proto_idx));
|
||||
return is_critical;
|
||||
}
|
||||
|
||||
|
|
@ -634,8 +656,11 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
|
|||
struct blob_attr *tb[__MAX];
|
||||
bool monitor = false, reload = true;
|
||||
unsigned char idx = 0;
|
||||
struct list_head action_list;
|
||||
struct list_head *changed_uci;
|
||||
|
||||
ULOG_INFO("Commit handler called");
|
||||
INIT_LIST_HEAD(&action_list);
|
||||
|
||||
if (blobmsg_parse(bbf_config_policy, __MAX, tb, blob_data(msg), blob_len(msg))) {
|
||||
send_reply(ctx, req, "error", "Failed to parse blob");
|
||||
|
|
@ -648,6 +673,9 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
|
|||
return -1;
|
||||
}
|
||||
|
||||
changed_uci = &async_req->changed_uci_list;
|
||||
INIT_LIST_HEAD(changed_uci);
|
||||
|
||||
// Set internal commit to true
|
||||
g_internal_commit = true;
|
||||
|
||||
|
|
@ -675,11 +703,6 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
|
|||
#endif
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
ULOG_INFO("Applying changes to dmmap UCI config");
|
||||
uci_apply_changes(DMMAP_CONFDIR, supported_protocols[idx].dmmap_savedir, true);
|
||||
}
|
||||
|
||||
struct blob_attr *services = tb[SERVICES_NAME];
|
||||
|
||||
size_t arr_len = (services) ? blobmsg_len(services) : 0;
|
||||
|
|
@ -700,14 +723,42 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
|
|||
}
|
||||
|
||||
ULOG_INFO("Committing changes for specified services and reloading");
|
||||
reload_specified_services(ctx, CONFIG_CONFDIR, supported_protocols[idx].config_savedir, async_req->services, true, reload);
|
||||
reload_specified_services(ctx, idx, async_req->services, true, reload, &action_list,
|
||||
&g_apply_handlers, changed_uci);
|
||||
} else {
|
||||
ULOG_INFO("Applying changes to dmmap UCI config");
|
||||
uci_apply_changes_dmmap(idx, true, &action_list, &g_apply_handlers);
|
||||
ULOG_INFO("Committing changes for all services and reloading");
|
||||
reload_all_services(ctx, CONFIG_CONFDIR, supported_protocols[idx].config_savedir, true, reload);
|
||||
reload_all_services(ctx, idx, true, reload, &action_list, &g_apply_handlers, changed_uci);
|
||||
}
|
||||
|
||||
struct action_node *node = NULL, *tmp = NULL;
|
||||
list_for_each_entry_safe(node, tmp, &action_list, list) {
|
||||
char cmd[4096] = {0};
|
||||
unsigned pos = 0;
|
||||
|
||||
ULOG_INFO("Reloading changes");
|
||||
|
||||
if (!file_exists(node->action)) {
|
||||
list_del(&node->list);
|
||||
FREE(node);
|
||||
continue;
|
||||
}
|
||||
|
||||
pos += snprintf(cmd, sizeof(cmd), "sh %s", node->action);
|
||||
|
||||
for (int i = 0; i < node->idx; i++) {
|
||||
pos += snprintf(&cmd[pos], sizeof(cmd) - pos, " %s", node->arg[i]);
|
||||
}
|
||||
|
||||
exec_apply_handler_script(cmd);
|
||||
list_del(&node->list);
|
||||
FREE(node);
|
||||
}
|
||||
|
||||
if (monitor) {
|
||||
ULOG_INFO("Deferring request and setting up async completion");
|
||||
async_req->idx = idx;
|
||||
ubus_defer_request(ctx, req, &async_req->req);
|
||||
async_req->timeout.cb = complete_request_callback;
|
||||
uloop_timeout_set(&async_req->timeout, 2000);
|
||||
|
|
@ -715,13 +766,13 @@ static int bbf_config_commit_handler(struct ubus_context *ctx, struct ubus_objec
|
|||
ULOG_INFO("Sending immediate success response");
|
||||
send_reply(ctx, req, "status", "ok");
|
||||
|
||||
// Send 'bbf.apply' event
|
||||
send_bbf_apply_event(idx, changed_uci);
|
||||
|
||||
// Free the allocated memory
|
||||
FREE(async_req->services);
|
||||
FREE(async_req);
|
||||
|
||||
// Send 'bbf.config.change' event to run refresh instances
|
||||
send_bbf_config_change_event();
|
||||
|
||||
// Set internal commit to false
|
||||
g_internal_commit = false;
|
||||
|
||||
|
|
@ -757,89 +808,78 @@ static int bbf_config_revert_handler(struct ubus_context *ctx, struct ubus_objec
|
|||
|
||||
if (arr_len) {
|
||||
ULOG_INFO("Reverting specified services");
|
||||
reload_specified_services(ctx, CONFIG_CONFDIR, supported_protocols[idx].config_savedir, services, false, false);
|
||||
reload_specified_services(ctx, idx, services, false, false, NULL, NULL, NULL);
|
||||
} else {
|
||||
ULOG_INFO("Reverting changes to dmmap UCI config");
|
||||
uci_apply_changes_dmmap(idx, false, NULL, NULL); // revert dmmap changes
|
||||
ULOG_INFO("Reverting all services");
|
||||
reload_all_services(ctx, CONFIG_CONFDIR, supported_protocols[idx].config_savedir, false, false);
|
||||
reload_all_services(ctx, idx, false, false, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
ULOG_INFO("Applying changes to revert all UCI dmmap configurations");
|
||||
uci_apply_changes(DMMAP_CONFDIR, supported_protocols[idx].dmmap_savedir, false);
|
||||
|
||||
ULOG_INFO("Sending success response");
|
||||
send_reply(ctx, req, "status", "ok");
|
||||
|
||||
// Send 'bbf.config.change' event to run refresh instances
|
||||
send_bbf_config_change_event();
|
||||
|
||||
ULOG_INFO("revert handler exit");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_critical_services(int proto_idx, struct blob_buf *bb)
|
||||
static void free_changed_uci_list(struct list_head *uci_list)
|
||||
{
|
||||
struct blob_attr *services_ba = NULL;
|
||||
struct modi_uci_node *node = NULL, *tmp = NULL;
|
||||
|
||||
services_ba = get_blob_attr_with_idx(proto_idx, g_critical_bb.head);
|
||||
if (services_ba != NULL) {
|
||||
blobmsg_add_field(bb, blobmsg_type(services_ba), "critical_services", blobmsg_data(services_ba), blobmsg_data_len(services_ba));
|
||||
if (uci_list == NULL)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(node, tmp, uci_list, list) {
|
||||
list_del(&node->list);
|
||||
FREE(node->uci);
|
||||
FREE(node);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bbf_config_changes_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[__MAX];
|
||||
struct blob_buf bb = {0};
|
||||
unsigned char idx = 0;
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
if (blobmsg_parse(bbf_config_policy, __MAX, tb, blob_data(msg), blob_len(msg))) {
|
||||
blobmsg_add_string(&bb, "error", "Failed to parse blob");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (tb[SERVICES_PROTO]) {
|
||||
char *proto = blobmsg_get_string(tb[SERVICES_PROTO]);
|
||||
idx = get_idx_by_proto(proto);
|
||||
}
|
||||
|
||||
void *array = blobmsg_open_array(&bb, "configs");
|
||||
uci_config_changes(CONFIG_CONFDIR, supported_protocols[idx].config_savedir, &bb);
|
||||
blobmsg_close_array(&bb, array);
|
||||
|
||||
update_critical_services(idx, &bb);
|
||||
|
||||
end:
|
||||
ubus_send_reply(ctx, req, bb.head);
|
||||
blob_buf_free(&bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void receive_notify_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
{
|
||||
// Skip sending 'bbf.config.change' event if triggered by an internal commit
|
||||
char file_path[1024] = {0};
|
||||
|
||||
struct blob_attr *tb[1] = {0};
|
||||
const struct blobmsg_policy p[1] = {
|
||||
{ "config", BLOBMSG_TYPE_STRING }
|
||||
};
|
||||
|
||||
blobmsg_parse(p, 1, tb, blob_data(msg), blob_len(msg));
|
||||
|
||||
if (!tb[0])
|
||||
return;
|
||||
|
||||
char *config = blobmsg_get_string(tb[0]);
|
||||
if (strlen(config) == 0)
|
||||
return;
|
||||
|
||||
snprintf(file_path, sizeof(file_path), "/etc/config/%s", config);
|
||||
|
||||
if (g_internal_commit) {
|
||||
ULOG_DEBUG("Event triggered by internal commit; skipping 'bbf.config.change' event transmission");
|
||||
ULOG_DEBUG("internal commit in progress, add uci in global list");
|
||||
add_changed_uci_list(&g_external_changed_uci, file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger 'bbf.config.change' event to refresh instances as required
|
||||
send_bbf_config_change_event();
|
||||
// Trigger 'bbfdm.apply' event
|
||||
struct list_head uci_list;
|
||||
|
||||
INIT_LIST_HEAD(&uci_list);
|
||||
add_changed_uci_list(&uci_list, file_path);
|
||||
|
||||
send_bbf_apply_event(-1, &uci_list);
|
||||
free_changed_uci_list(&uci_list);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct ubus_method bbf_config_methods[] = {
|
||||
UBUS_METHOD("commit", bbf_config_commit_handler, bbf_config_policy),
|
||||
UBUS_METHOD("revert", bbf_config_revert_handler, bbf_config_policy),
|
||||
UBUS_METHOD("changes", bbf_config_changes_handler, bbf_config_policy),
|
||||
};
|
||||
|
||||
static struct ubus_object_type bbf_config_object_type = UBUS_OBJECT_TYPE("bbf.config", bbf_config_methods);
|
||||
|
|
@ -868,6 +908,164 @@ static void load_critical_services()
|
|||
blobmsg_add_json_from_file(&g_critical_bb, CRITICAL_DEF_JSON);
|
||||
}
|
||||
|
||||
static int filter(const struct dirent *entry)
|
||||
{
|
||||
return entry->d_name[0] != '.';
|
||||
}
|
||||
|
||||
static int compare(const struct dirent **a, const struct dirent **b)
|
||||
{
|
||||
size_t len_a = strlen((*a)->d_name);
|
||||
size_t len_b = strlen((*b)->d_name);
|
||||
|
||||
if (len_a < len_b) // Sort by length (shorter first)
|
||||
return -1;
|
||||
|
||||
if (len_a > len_b)
|
||||
return 1;
|
||||
|
||||
return strcasecmp((*a)->d_name, (*b)->d_name); // If lengths are equal, sort alphabetically
|
||||
}
|
||||
|
||||
static void free_apply_handlers()
|
||||
{
|
||||
struct applier_node *node = NULL, *tmp = NULL;
|
||||
|
||||
list_for_each_entry_safe(node, tmp, &g_apply_handlers, list) {
|
||||
list_del(&node->list);
|
||||
FREE(node->file_path);
|
||||
FREE(node->action);
|
||||
FREE(node);
|
||||
}
|
||||
}
|
||||
|
||||
static void __load_handlers(const char *file)
|
||||
{
|
||||
if (file == NULL || strlen(file) == 0)
|
||||
return;
|
||||
|
||||
json_object *json_root = json_object_from_file(file);
|
||||
if (!json_root) {
|
||||
ULOG_INFO("Failed to read json file %s", file);
|
||||
return;
|
||||
}
|
||||
|
||||
json_object *daemon_config = NULL;
|
||||
json_object_object_get_ex(json_root, "daemon", &daemon_config);
|
||||
if (!daemon_config) {
|
||||
ULOG_INFO("Failed to find daemon object");
|
||||
json_object_put(json_root);
|
||||
return;
|
||||
}
|
||||
|
||||
json_object *apply_handler = NULL;
|
||||
json_object_object_get_ex(daemon_config, "apply_handler", &apply_handler);
|
||||
if (!apply_handler) {
|
||||
json_object_put(json_root);
|
||||
return;
|
||||
}
|
||||
|
||||
char type[2][8] = { "dmmap", "uci" };
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
json_object *array = NULL;
|
||||
|
||||
if (!json_object_object_get_ex(apply_handler, type[i], &array) ||
|
||||
json_object_get_type(array) != json_type_array) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t count = json_object_array_length(array);
|
||||
for (size_t j = 0; j < count; j++) {
|
||||
json_object *hndl_obj = json_object_array_get_idx(array, j);
|
||||
json_object *files = NULL, *handler = NULL;
|
||||
|
||||
json_object_object_get_ex(hndl_obj, "external_handler", &handler);
|
||||
if (!handler) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *action = json_object_get_string(handler);
|
||||
if (strlen(action) == 0 || !file_exists(action)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
json_object_object_get_ex(hndl_obj, "file", &files);
|
||||
if (!files || json_object_get_type(files) != json_type_array) {
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t f_count = json_object_array_length(files);
|
||||
for (size_t k = 0; k < f_count; k++) {
|
||||
char path[1024] = {0};
|
||||
|
||||
json_object *f_inst = json_object_array_get_idx(files, k);
|
||||
snprintf(path, sizeof(path), "/etc/%s/%s",
|
||||
(strcmp(type[i], "uci") == 0) ? "config" : "bbfdm/dmmap", json_object_get_string(f_inst));
|
||||
|
||||
|
||||
// check if already present
|
||||
bool exist = false;
|
||||
struct applier_node *node = NULL;
|
||||
list_for_each_entry(node, &g_apply_handlers, list) {
|
||||
if (strcmp(node->file_path, path) == 0 && strcmp(node->action, action) == 0) {
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exist == true)
|
||||
continue;
|
||||
|
||||
node = (struct applier_node *)calloc(1, sizeof(struct applier_node));
|
||||
if (node == NULL) {
|
||||
ULOG_INFO("Failed to allocate memory for apply handlers");
|
||||
json_object_put(json_root);
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&node->list);
|
||||
list_add_tail(&node->list, &g_apply_handlers);
|
||||
|
||||
node->file_path = strdup(path);
|
||||
node->action = strdup(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(json_root);
|
||||
return;
|
||||
}
|
||||
|
||||
static void load_apply_handlers()
|
||||
{
|
||||
struct dirent **namelist;
|
||||
|
||||
INIT_LIST_HEAD(&g_apply_handlers);
|
||||
|
||||
int num_files = scandir(BBFDM_MICROSERVICE_INPUT_PATH, &namelist, filter, compare);
|
||||
|
||||
for (int i = 0; i < num_files; i++) {
|
||||
char file_path[512] = {0};
|
||||
|
||||
snprintf(file_path, sizeof(file_path), "%s/%s", BBFDM_MICROSERVICE_INPUT_PATH, namelist[i]->d_name);
|
||||
|
||||
if (!file_exists(file_path) || !regular_file(file_path)) {
|
||||
free(namelist[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
__load_handlers(file_path);
|
||||
|
||||
free(namelist[i]);
|
||||
}
|
||||
|
||||
if (namelist)
|
||||
free(namelist);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct ubus_event_handler ev = {
|
||||
|
|
@ -907,6 +1105,9 @@ int main(int argc, char **argv)
|
|||
ubus_add_uloop(uctx);
|
||||
|
||||
load_critical_services();
|
||||
load_apply_handlers();
|
||||
|
||||
INIT_LIST_HEAD(&g_external_changed_uci);
|
||||
|
||||
if (ubus_add_object(uctx, &bbf_config_object)) {
|
||||
ULOG_ERR("Failed to add 'bbf.config' ubus object");
|
||||
|
|
@ -921,6 +1122,8 @@ int main(int argc, char **argv)
|
|||
uloop_run();
|
||||
|
||||
exit:
|
||||
free_apply_handlers();
|
||||
free_changed_uci_list(&g_external_changed_uci);
|
||||
blob_buf_free(&g_critical_bb);
|
||||
uloop_done();
|
||||
ubus_free(uctx);
|
||||
|
|
|
|||
|
|
@ -11,11 +11,198 @@
|
|||
#include <stdarg.h>
|
||||
#include <libubus.h>
|
||||
#include <uci.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define DEFAULT_UBUS_TIMEOUT 5000
|
||||
|
||||
struct proto_args {
|
||||
const char *name;
|
||||
const char *config_savedir;
|
||||
const char *dmmap_savedir;
|
||||
unsigned char index;
|
||||
};
|
||||
|
||||
static struct proto_args supported_protocols[] = {
|
||||
{
|
||||
"both", "/tmp/bbfdm/.bbfdm/config/", "/tmp/bbfdm/.bbfdm/dmmap/", 0
|
||||
},
|
||||
{
|
||||
"cwmp", "/tmp/bbfdm/.cwmp/config/", "/tmp/bbfdm/.cwmp/dmmap/", 1
|
||||
},
|
||||
{
|
||||
"usp", "/tmp/bbfdm/.usp/config/", "/tmp/bbfdm/.usp/dmmap/", 2
|
||||
},
|
||||
};
|
||||
|
||||
static void add_external_action_list(struct list_head *action_list, struct list_head *ext_handler, const char *file_path)
|
||||
{
|
||||
if (file_path == NULL || strlen(file_path) == 0 || action_list == NULL)
|
||||
return;
|
||||
|
||||
struct applier_node *app_node = NULL;
|
||||
bool ext_exist = false;
|
||||
|
||||
char *config = strrchr(file_path, '/');
|
||||
if (config) {
|
||||
config = config + 1;
|
||||
}
|
||||
|
||||
list_for_each_entry(app_node, ext_handler, list) {
|
||||
if (strcmp(app_node->file_path, file_path) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ext_exist = true;
|
||||
|
||||
bool node_exist = false;
|
||||
bool arg_exist = false;
|
||||
struct action_node *act_node = NULL;
|
||||
|
||||
list_for_each_entry(act_node, action_list, list) {
|
||||
if (strcmp(app_node->action, act_node->action) == 0) {
|
||||
node_exist = true;
|
||||
for (int i = 0; i < act_node->idx; i++) {
|
||||
if (strcmp(act_node->arg[i], config) == 0) {
|
||||
arg_exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node_exist == false) {
|
||||
act_node = (struct action_node *)calloc(1, sizeof(struct action_node));
|
||||
if (act_node == NULL) {
|
||||
ULOG_INFO("Failed to allocate memory for action list");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(act_node->action, sizeof(act_node->action), "%s", app_node->action);
|
||||
INIT_LIST_HEAD(&act_node->list);
|
||||
list_add_tail(&act_node->list, action_list);
|
||||
}
|
||||
|
||||
if (arg_exist == false && act_node->idx < ARG_COUNT) {
|
||||
snprintf(act_node->arg[act_node->idx], ARG_LEN, "%s", config);
|
||||
act_node->idx = act_node->idx + 1;
|
||||
ULOG_DEBUG("Added %s handler for %s config", act_node->action, config);
|
||||
}
|
||||
}
|
||||
|
||||
if (ext_exist == true || strncmp(file_path, DMMAP_CONFDIR, strlen(DMMAP_CONFDIR)) == 0) {
|
||||
/* external handler exist, so already added in list or
|
||||
* the file is a dmmap file so it has no default handler
|
||||
* to add in the action list */
|
||||
return;
|
||||
}
|
||||
|
||||
/* external handler not exist, add default handler */
|
||||
struct action_node *act_node = NULL;
|
||||
bool node_exist = false;
|
||||
bool arg_exist = false;
|
||||
|
||||
list_for_each_entry(act_node, action_list, list) {
|
||||
if (strcmp(app_node->action, DEFAULT_HANDLER_ACT) == 0) {
|
||||
node_exist = true;
|
||||
for (int i = 0; i < act_node->idx; i++) {
|
||||
if (strcmp(act_node->arg[i], config) == 0) {
|
||||
arg_exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (node_exist == false) {
|
||||
act_node = (struct action_node *)calloc(1, sizeof(struct action_node));
|
||||
if (act_node == NULL) {
|
||||
ULOG_INFO("Failed to allocate memory for action list");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(act_node->action, sizeof(act_node->action), "%s", DEFAULT_HANDLER_ACT);
|
||||
INIT_LIST_HEAD(&act_node->list);
|
||||
list_add_tail(&act_node->list, action_list);
|
||||
}
|
||||
|
||||
if (arg_exist == false && act_node->idx < ARG_COUNT) {
|
||||
snprintf(act_node->arg[act_node->idx], ARG_LEN, "%s", config);
|
||||
act_node->idx = act_node->idx + 1;
|
||||
ULOG_DEBUG("Added default handler for %s config", config);
|
||||
}
|
||||
}
|
||||
|
||||
void add_changed_uci_list(struct list_head *changed_uci, const char *file_path)
|
||||
{
|
||||
if (changed_uci == NULL || file_path == NULL || strlen(file_path) == 0)
|
||||
return;
|
||||
|
||||
struct modi_uci_node *node = NULL;
|
||||
bool exist = false;
|
||||
|
||||
list_for_each_entry(node, changed_uci, list) {
|
||||
if (!node->uci || strcmp(node->uci, file_path) != 0)
|
||||
continue;
|
||||
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (exist)
|
||||
return;
|
||||
|
||||
node = (struct modi_uci_node *)calloc(1, sizeof(struct modi_uci_node));
|
||||
if (!node) {
|
||||
ULOG_INFO("Failed to allocate memory for changed uci list");
|
||||
return;
|
||||
}
|
||||
|
||||
node->uci = strdup(file_path);
|
||||
INIT_LIST_HEAD(&node->list);
|
||||
list_add_tail(&node->list, changed_uci);
|
||||
}
|
||||
|
||||
unsigned char get_idx_by_proto(const char *proto)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(supported_protocols); i++) {
|
||||
if (strcmp(supported_protocols[i].name, proto) == 0)
|
||||
return supported_protocols[i].index;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *get_proto_conf_savedir_by_idx(int idx)
|
||||
{
|
||||
if (idx < ARRAY_SIZE(supported_protocols)) {
|
||||
return supported_protocols[idx].config_savedir;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
const char *get_proto_dmmap_savedir_by_idx(int idx)
|
||||
{
|
||||
if (idx < ARRAY_SIZE(supported_protocols)) {
|
||||
return supported_protocols[idx].dmmap_savedir;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
const char *get_proto_name_by_idx(int idx)
|
||||
{
|
||||
if (idx < ARRAY_SIZE(supported_protocols)) {
|
||||
return supported_protocols[idx].name;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void strncpyt(char *dst, const char *src, size_t n)
|
||||
{
|
||||
if (dst == NULL || src == NULL)
|
||||
|
|
@ -27,6 +214,16 @@ void strncpyt(char *dst, const char *src, size_t n)
|
|||
}
|
||||
}
|
||||
|
||||
bool file_exists(const char *path)
|
||||
{
|
||||
struct stat buffer;
|
||||
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
return stat(path, &buffer) == 0;
|
||||
}
|
||||
|
||||
int bbf_config_call(struct ubus_context *ctx, const char *object, const char *method, struct blob_buf *data, ubus_data_handler_t callback, void *arg)
|
||||
{
|
||||
int fault = 0;
|
||||
|
|
@ -52,32 +249,9 @@ int bbf_config_call(struct ubus_context *ctx, const char *object, const char *me
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void reload_service(struct ubus_context *ctx, const char *config_name, bool is_commit)
|
||||
{
|
||||
struct blob_buf bb = {0};
|
||||
|
||||
if (!ctx || !config_name) {
|
||||
ULOG_ERR("Failed to reload service: 'ctx' or 'config_name' is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&bb, 0, sizeof(struct blob_buf));
|
||||
|
||||
blob_buf_init(&bb, 0);
|
||||
|
||||
blobmsg_add_string(&bb, "config", config_name);
|
||||
|
||||
int result = bbf_config_call(ctx, "uci", (is_commit) ? "commit" : "revert", &bb, NULL, NULL);
|
||||
if (result != 0) {
|
||||
ULOG_ERR("Failed to %s configuration '%s'", (is_commit ? "commit" : "revert"), config_name);
|
||||
} else {
|
||||
ULOG_DEBUG("Successfully executed %s on configuration '%s'.", (is_commit ? "commit" : "revert"), config_name);
|
||||
}
|
||||
|
||||
blob_buf_free(&bb);
|
||||
}
|
||||
|
||||
void reload_specified_services(struct ubus_context *ctx, const char *conf_dir, const char *save_dir, struct blob_attr *services, bool is_commit, bool reload)
|
||||
void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_attr *services,
|
||||
bool is_commit, bool reload, struct list_head *action_list,
|
||||
struct list_head *handler_list, struct list_head *changed_uci)
|
||||
{
|
||||
struct uci_context *uci_ctx = NULL;
|
||||
struct blob_attr *service = NULL;
|
||||
|
|
@ -89,25 +263,45 @@ void reload_specified_services(struct ubus_context *ctx, const char *conf_dir, c
|
|||
return;
|
||||
}
|
||||
|
||||
if (conf_dir) {
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir);
|
||||
uci_set_confdir(uci_ctx, conf_dir);
|
||||
}
|
||||
|
||||
if (save_dir) {
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
}
|
||||
|
||||
ULOG_DEBUG("Processing services list...");
|
||||
blobmsg_for_each_attr(service, services, rem) {
|
||||
struct uci_ptr ptr = {0};
|
||||
char conf_dir[64] = {0};
|
||||
char save_dir[64] = {0};
|
||||
char package[64] = {0};
|
||||
bool is_dmmap = false;
|
||||
|
||||
char *config_name = blobmsg_get_string(service);
|
||||
if (strncmp(CONFIG_CONFDIR, config_name, strlen(CONFIG_CONFDIR)) == 0) {
|
||||
/* standard uci path received */
|
||||
snprintf(conf_dir, sizeof(conf_dir), "%s", CONFIG_CONFDIR);
|
||||
snprintf(save_dir, sizeof(save_dir), "%s", get_proto_conf_savedir_by_idx(idx));
|
||||
snprintf(package, sizeof(package), "%s", config_name + strlen(CONFIG_CONFDIR));
|
||||
} else if (strncmp(DMMAP_CONFDIR, config_name, strlen(DMMAP_CONFDIR)) == 0) {
|
||||
/* dmmap uci path received */
|
||||
snprintf(conf_dir, sizeof(conf_dir), "%s", DMMAP_CONFDIR);
|
||||
snprintf(save_dir, sizeof(save_dir), "%s", get_proto_dmmap_savedir_by_idx(idx));
|
||||
snprintf(package, sizeof(package), "%s", config_name + strlen(DMMAP_CONFDIR));
|
||||
is_dmmap = true;
|
||||
} else {
|
||||
/* no path default to standard uci */
|
||||
snprintf(conf_dir, sizeof(conf_dir), "%s", CONFIG_CONFDIR);
|
||||
snprintf(save_dir, sizeof(save_dir), "%s", get_proto_conf_savedir_by_idx(idx));
|
||||
snprintf(package, sizeof(package), "%s", config_name);
|
||||
}
|
||||
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir);
|
||||
uci_set_confdir(uci_ctx, conf_dir);
|
||||
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
|
||||
ULOG_DEBUG("Looking up UCI configuration for service '%s'", config_name);
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, config_name, true) != UCI_OK) {
|
||||
char file_path[1024] = {0};
|
||||
snprintf(file_path, sizeof(file_path), "%s%s", conf_dir, package);
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, package, true) != UCI_OK) {
|
||||
ULOG_ERR("Failed to lookup UCI pointer for service '%s'. Skipping", config_name);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -118,6 +312,10 @@ void reload_specified_services(struct ubus_context *ctx, const char *conf_dir, c
|
|||
ULOG_ERR("Failed to commit UCI changes for service '%s'", config_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_dmmap) {
|
||||
add_changed_uci_list(changed_uci, file_path);
|
||||
}
|
||||
} else {
|
||||
ULOG_DEBUG("Reverting UCI changes for service '%s'", config_name);
|
||||
if (uci_revert(uci_ctx, &ptr) != UCI_OK) {
|
||||
|
|
@ -126,9 +324,12 @@ void reload_specified_services(struct ubus_context *ctx, const char *conf_dir, c
|
|||
}
|
||||
}
|
||||
|
||||
if (reload) {
|
||||
ULOG_INFO("Reloading service '%s'", config_name);
|
||||
reload_service(ctx, config_name, is_commit);
|
||||
if (is_commit && is_dmmap) {
|
||||
add_external_action_list(action_list, handler_list, file_path);
|
||||
}
|
||||
|
||||
if (reload && !is_dmmap) {
|
||||
add_external_action_list(action_list, handler_list, file_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +337,9 @@ void reload_specified_services(struct ubus_context *ctx, const char *conf_dir, c
|
|||
uci_free_context(uci_ctx);
|
||||
}
|
||||
|
||||
void reload_all_services(struct ubus_context *ctx, const char *conf_dir, const char *save_dir, bool is_commit, bool reload)
|
||||
void reload_all_services(struct ubus_context *ctx, int idx, bool is_commit,
|
||||
bool reload, struct list_head *action_list,
|
||||
struct list_head *handler_list, struct list_head *changed_uci)
|
||||
{
|
||||
struct uci_context *uci_ctx = NULL;
|
||||
char **configs = NULL, **p = NULL;
|
||||
|
|
@ -147,15 +350,12 @@ void reload_all_services(struct ubus_context *ctx, const char *conf_dir, const c
|
|||
return;
|
||||
}
|
||||
|
||||
if (conf_dir) {
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir);
|
||||
uci_set_confdir(uci_ctx, conf_dir);
|
||||
}
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", CONFIG_CONFDIR);
|
||||
uci_set_confdir(uci_ctx, CONFIG_CONFDIR);
|
||||
|
||||
if (save_dir) {
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
}
|
||||
const char *save_dir = get_proto_conf_savedir_by_idx(idx);
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
|
||||
if (uci_list_configs(uci_ctx, &configs) != UCI_OK) {
|
||||
ULOG_ERR("Failed to list UCI configurations");
|
||||
|
|
@ -167,6 +367,8 @@ void reload_all_services(struct ubus_context *ctx, const char *conf_dir, const c
|
|||
struct uci_ptr ptr = {0};
|
||||
|
||||
ULOG_DEBUG("Looking up UCI configuration for '%s'", *p);
|
||||
char file_path[1024] = {0};
|
||||
snprintf(file_path, sizeof(file_path), "%s%s", CONFIG_CONFDIR, *p);
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK) {
|
||||
ULOG_ERR("Failed to lookup UCI pointer for config '%s'. Skipping", *p);
|
||||
|
|
@ -184,6 +386,8 @@ void reload_all_services(struct ubus_context *ctx, const char *conf_dir, const c
|
|||
ULOG_ERR("Failed to commit changes for config '%s'", *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
add_changed_uci_list(changed_uci, file_path);
|
||||
} else {
|
||||
ULOG_DEBUG("Reverting UCI changes for config '%s'", *p);
|
||||
if (uci_revert(uci_ctx, &ptr) != UCI_OK) {
|
||||
|
|
@ -193,8 +397,7 @@ void reload_all_services(struct ubus_context *ctx, const char *conf_dir, const c
|
|||
}
|
||||
|
||||
if (reload) {
|
||||
ULOG_INFO("Reloading service for config '%s'", *p);
|
||||
reload_service(ctx, *p, is_commit);
|
||||
add_external_action_list(action_list, handler_list, file_path);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -204,10 +407,19 @@ exit:
|
|||
uci_free_context(uci_ctx);
|
||||
}
|
||||
|
||||
void uci_apply_changes(const char *conf_dir, const char *save_dir, bool is_commit)
|
||||
void exec_apply_handler_script(const char *cmd)
|
||||
{
|
||||
FILE *pp = popen(cmd, "r"); // flawfinder: ignore
|
||||
if (pp) {
|
||||
pclose(pp);
|
||||
}
|
||||
}
|
||||
|
||||
void uci_apply_changes_dmmap(int idx, bool is_commit, struct list_head *action_list, struct list_head *ext_handler)
|
||||
{
|
||||
struct uci_context *uci_ctx = NULL;
|
||||
char **configs = NULL, **p = NULL;
|
||||
char save_dir[128] = {0};
|
||||
|
||||
uci_ctx = uci_alloc_context();
|
||||
if (!uci_ctx) {
|
||||
|
|
@ -215,15 +427,12 @@ void uci_apply_changes(const char *conf_dir, const char *save_dir, bool is_commi
|
|||
return;
|
||||
}
|
||||
|
||||
if (conf_dir) {
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir);
|
||||
uci_set_confdir(uci_ctx, conf_dir);
|
||||
}
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", DMMAP_CONFDIR);
|
||||
uci_set_confdir(uci_ctx, DMMAP_CONFDIR);
|
||||
snprintf(save_dir, sizeof(save_dir), "%s", get_proto_dmmap_savedir_by_idx(idx));
|
||||
|
||||
if (save_dir) {
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
}
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
|
||||
if (uci_list_configs(uci_ctx, &configs) != UCI_OK) {
|
||||
ULOG_ERR("Failed to list UCI configurations");
|
||||
|
|
@ -247,6 +456,10 @@ void uci_apply_changes(const char *conf_dir, const char *save_dir, bool is_commi
|
|||
ULOG_ERR("Failed to commit changes for config '%s'", *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
char file_path[1024] = {0};
|
||||
snprintf(file_path, sizeof(file_path), "%s%s", DMMAP_CONFDIR, *p);
|
||||
add_external_action_list(action_list, ext_handler, file_path);
|
||||
} else {
|
||||
ULOG_DEBUG("Reverting changes for config '%s'", *p);
|
||||
if (uci_revert(uci_ctx, &ptr) != UCI_OK) {
|
||||
|
|
@ -262,54 +475,12 @@ exit:
|
|||
uci_free_context(uci_ctx);
|
||||
}
|
||||
|
||||
void uci_config_changes(const char *conf_dir, const char *save_dir, struct blob_buf *bb)
|
||||
bool regular_file(const char *path)
|
||||
{
|
||||
struct uci_context *uci_ctx = NULL;
|
||||
char **configs = NULL, **p = NULL;
|
||||
struct stat buffer;
|
||||
|
||||
uci_ctx = uci_alloc_context();
|
||||
if (!uci_ctx) {
|
||||
ULOG_ERR("Failed to allocate UCI context");
|
||||
return;
|
||||
}
|
||||
if (!path)
|
||||
return false;
|
||||
|
||||
if (conf_dir) {
|
||||
ULOG_DEBUG("Setting UCI configuration directory to '%s'", conf_dir);
|
||||
uci_set_confdir(uci_ctx, conf_dir);
|
||||
}
|
||||
|
||||
if (save_dir) {
|
||||
ULOG_DEBUG("Setting UCI save directory to '%s'", save_dir);
|
||||
uci_set_savedir(uci_ctx, save_dir);
|
||||
}
|
||||
|
||||
if (uci_list_configs(uci_ctx, &configs) != UCI_OK) {
|
||||
ULOG_ERR("Failed to list UCI configurations");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ULOG_DEBUG("Identifying configurations with unsaved changes...");
|
||||
for (p = configs; p && *p; p++) {
|
||||
struct uci_ptr ptr = {0};
|
||||
|
||||
ULOG_DEBUG("Looking up UCI configuration for '%s'", *p);
|
||||
|
||||
if (uci_lookup_ptr(uci_ctx, &ptr, *p, true) != UCI_OK) {
|
||||
ULOG_ERR("Failed to lookup UCI pointer for config '%s'. Skipping.", *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (uci_list_empty(&ptr.p->saved_delta)) {
|
||||
ULOG_DEBUG("No unsaved changes in config '%s'. Skipping", *p);
|
||||
continue;
|
||||
}
|
||||
|
||||
ULOG_INFO("Unsaved changes detected in config '%s', adding to blob buffer", *p);
|
||||
blobmsg_add_string(bb, NULL, *p);
|
||||
}
|
||||
|
||||
FREE(configs);
|
||||
|
||||
exit:
|
||||
uci_free_context(uci_ctx);
|
||||
return stat(path, &buffer) == 0 && S_ISREG(buffer.st_mode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,16 +25,59 @@
|
|||
#define ULOG_DEBUG(fmt, ...) ulog(LOG_DEBUG, fmt, ## __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define CONFIG_CONFDIR "/etc/config/"
|
||||
#define DMMAP_CONFDIR "/etc/bbfdm/dmmap/"
|
||||
#define DEFAULT_HANDLER_ACT "/etc/bbfdm/bbf_default_reload.sh"
|
||||
#define ARG_LEN 64
|
||||
#define ARG_COUNT 40
|
||||
#define ACTION_LEN 512
|
||||
|
||||
enum wifi_config_flags_enum {
|
||||
WIRELESS_CONFIG = 1,
|
||||
MAPCONTROLLER_CONFIG = 1<<1,
|
||||
};
|
||||
|
||||
struct applier_node {
|
||||
char *file_path;
|
||||
char *action;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct action_node {
|
||||
struct list_head list;
|
||||
char action[ACTION_LEN];
|
||||
char arg[ARG_COUNT][ARG_LEN];
|
||||
int idx;
|
||||
};
|
||||
|
||||
struct modi_uci_node {
|
||||
char *uci;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
void strncpyt(char *dst, const char *src, size_t n);
|
||||
|
||||
int bbf_config_call(struct ubus_context *ctx, const char *object, const char *method, struct blob_buf *data, ubus_data_handler_t callback, void *arg);
|
||||
|
||||
void reload_specified_services(struct ubus_context *ctx, const char *conf_dir, const char *save_dir, struct blob_attr *services, bool is_commit, bool reload);
|
||||
void reload_specified_services(struct ubus_context *ctx, int idx, struct blob_attr *services,
|
||||
bool is_commit, bool reload, struct list_head *action_list,
|
||||
struct list_head *handler_list, struct list_head *changed_uci);
|
||||
|
||||
void reload_all_services(struct ubus_context *ctx, const char *conf_dir, const char *save_dir, bool is_commit, bool reload);
|
||||
void reload_all_services(struct ubus_context *ctx, int idx, bool is_commit,
|
||||
bool reload, struct list_head *action_list,
|
||||
struct list_head *handler_list, struct list_head *changed_uci);
|
||||
|
||||
void uci_apply_changes(const char *conf_dir, const char *save_dir, bool is_commit);
|
||||
void exec_apply_handler_script(const char *cmd);
|
||||
|
||||
void uci_config_changes(const char *conf_dir, const char *save_dir, struct blob_buf *bb);
|
||||
void uci_apply_changes_dmmap(int idx, bool is_commit, struct list_head *action_list,
|
||||
struct list_head *handler_list);
|
||||
|
||||
unsigned char get_idx_by_proto(const char *proto);
|
||||
const char *get_proto_conf_savedir_by_idx(int idx);
|
||||
const char *get_proto_dmmap_savedir_by_idx(int idx);
|
||||
const char *get_proto_name_by_idx(int idx);
|
||||
bool file_exists(const char *path);
|
||||
bool regular_file(const char *path);
|
||||
void add_changed_uci_list(struct list_head *changed_uci, const char *file_path);
|
||||
|
||||
#endif //__UTILS_H__
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue