Compare commits

...

88 commits
7.4.0 ... devel

Author SHA1 Message Date
Amin Ben Romdhane
72c3307651 Store object name when parsing multi-instance object without section 2025-12-07 23:26:17 +01:00
Suvendhu Hansa
8f72146f0f Handling sections with missing instance in dmmap 2025-11-28 17:18:59 +05:30
Vivek Kumar Dutta
a0347e59b6
Schedules: Fix setting alias 2025-11-25 19:24:45 +05:30
Amin Ben Romdhane
880a7583ca libbbfdm-ubus: fix crash on large Get output 2025-11-24 14:01:18 +01:00
Vivek Dutta
adfdb54d62
Update ubus call logs 2025-10-30 10:45:49 +05:30
Amin Ben Romdhane
4855d91797
libbbfdm-ubus: free 'bbfdm_ctx' dynamic allocations only when 'ubus_ctx' is initialized 2025-10-28 17:45:23 +05:30
Xiaofeng Meng
1a86b8a443
fix bbfdm_resolve_external_reference_via_dmmap 2025-10-24 16:26:10 +05:30
Amin Ben Romdhane
293474bf1f Operate: commit or revert only if modified UCI list is not empty 2025-10-23 21:04:12 +02:00
Xiaofeng Meng
165deef400
support dmmap for bbfdm_get_reference_linker 2025-10-23 09:38:32 +05:30
Amin Ben Romdhane
21f4b19cda
bbfdm methods are not working for objects exposed from parent object different to 'Device.' 2025-10-22 18:45:19 +05:30
Amin Ben Romdhane
d45e57afd6 Tools: added decollector module 2025-10-22 12:44:36 +02:00
Amin Ben Romdhane
3e9faeff2b Delete the unnecessary sync timer 2025-10-20 13:23:20 +02:00
Amin Ben Romdhane
a332ebef29
libbbfdm-ubus: refactor ubus_ctx handling for greater flexibility 2025-10-17 22:04:52 +05:30
Amin Ben Romdhane
69134df069
Correct API name typo and deprecate old APIs 2025-10-15 16:14:01 +05:30
Amin Ben Romdhane
1596a6a8c1 libbbf-api: Add a new ubus API to get the response based on blob_msg input 2025-10-01 17:34:12 +02:00
Suvendhu Hansa
acb6b1cbf2 Added opconf and gnx-l2snooping dm in tools input 2025-09-25 17:39:09 +05:30
Suvendhu Hansa
ba2fc7dc21 execute sync handler on bbfdm.apply event 2025-09-17 19:52:11 +05:30
Vivek Kumar Dutta
252da0b834
Fix setting debug log level 2025-09-14 18:03:13 +05:30
Suvendhu Hansa
786863cf0e
Multiple updates
- Support to notify about external uci changes
- Call uci_to_dmmap sync function on uci changes notification
- Documentation update
2025-09-14 12:10:52 +05:30
Suvendhu Hansa
2cd0a1ef6b
Register external handler 2025-09-11 13:29:19 +05:30
Vivek Kumar Dutta
227d121ee5
Pipeline improvement 2025-09-10 15:52:01 +05:30
Suvendhu Hansa
19db3c2d17
dmctx aware bbfdm api 2025-09-05 18:49:59 +05:30
Suvendhu Hansa
2f3a0805b0
rename unnamed dmmap section 2025-09-04 18:56:38 +05:30
Vivek Kumar Dutta
ca8707b320
Fix pipeline 2025-09-02 13:09:30 +05:30
Vivek Kumar Dutta
ebe5ada3d0
dm-services: enable libeasy 2025-09-02 12:54:48 +05:30
Vivek Kumar Dutta
c9af4e50ce
Logging improvements 2025-08-27 14:28:28 +05:30
Vivek Kumar Dutta
2e6debc04b
Updated some logs 2025-08-26 18:33:15 +05:30
Suvendhu Hansa
09f95d7edd Fix instance number check 2025-08-22 22:02:27 +05:30
Suvendhu Hansa
97320a95ab Fix string handling 2025-08-22 21:59:42 +05:30
Vivek Kumar Dutta
42d1e14a91 ci: Fix compilation of libeasy 2025-08-22 21:57:29 +05:30
Xiaofeng Meng
5d7b4d759e
Fix generic error message for 9007 fault code 2025-08-22 15:06:08 +05:30
Amin Ben Romdhane
16ed3a79e3
Support multiple DM paths for the same UBUS event 2025-08-18 13:37:08 +05:30
Vivek Kumar Dutta
8c1517dab6
Fix branch name for micro-services 2025-08-06 14:55:59 +05:30
Vivek Kumar Dutta
51af53e2be
Fix CI for release branches 2025-08-06 13:59:59 +05:30
Vivek Kumar Dutta
47087c5941
Fix micro-service pipelines 2025-08-05 13:04:48 +05:30
Amin Ben Romdhane
d4bc6eea94 Redesign dmmap to be portable and structured 2025-08-04 14:04:52 +02:00
Amin Ben Romdhane
a4f6108138
Improve references database 2025-07-21 15:28:45 +05:30
Amin Ben Romdhane
7acb5cb2f4
Set dm_type for internal API calls 2025-07-18 12:24:07 +05:30
Amin Ben Romdhane
b6fdc87df8 Ensure UCI savedir paths are created before initialization 2025-07-14 00:34:31 +02:00
Amin Ben Romdhane
b9b9743cf0 bbfdm_tools: read the description from json plugin directly 2025-07-08 20:54:04 +02:00
Amin Ben Romdhane
549a296cbf
Set an empty value as a reference value when the path exists but its linker is empty 2025-06-30 14:37:59 +00:00
Amin Ben Romdhane
8d55820ed1 Refresh references db after calling delete method 2025-06-26 16:36:52 +02:00
Amin Ben Romdhane
928443c5c8 Make sure to refresh references db before calling set method to ensure that all references are stored in the database 2025-06-24 14:50:23 +02:00
Amin Ben Romdhane
aa74995f9b
Added a mechanism to recover blacklisted ubus methods 2025-06-20 18:35:05 +05:30
Amin Ben Romdhane
ae1c44524d bbfdmd: make ubus timeout configurable for every service using JSON register service 2025-06-16 18:53:16 +05:30
Amin Ben Romdhane
22cc348d27 Added a mechanism to recover blacklisted services 2025-06-16 12:22:42 +00:00
Suvendhu Hansa
4bfeedf7bc
Added wifidmd DM vendor extension description 2025-06-16 12:21:52 +00:00
Suvendhu Hansa
ad2602f114 Blacklist ubus object only in timed out 2025-06-13 12:42:10 +05:30
Amin Ben Romdhane
25e2d0bab6 bbf.config: handle wifi configs reload from external script 2025-06-11 14:08:44 +02:00
Amin Ben Romdhane
571a4335a9 Added support for ubus blob API with timeout 2025-06-11 14:03:38 +02:00
Amin Ben Romdhane
67c6e502a2 Pipeline: Fix parental-control compilation 2025-06-11 13:41:52 +02:00
Vivek Kumar Dutta
cb405b3b88 Added dependency based on directory 2025-05-27 19:55:08 +05:30
Amin Ben Romdhane
7cce094e03
ubus: Blacklist unsupported or slow methods after repeated failures 2025-05-27 19:54:41 +05:30
Vivek Dutta
dd0c6433aa
Revert "depcheck to check non-zero file" 2025-05-27 19:53:16 +05:30
Vivek Kumar Dutta
6cbfa27973
depcheck to check non-zero file 2025-05-27 14:26:24 +05:30
Amin Ben Romdhane
08cf6a9725 bbfdmd: Show only usp schema when registering the services 2025-05-22 10:55:57 +02:00
Amin Ben Romdhane
a20a15888b
Store data model schema during service registration 2025-05-21 15:50:18 +00:00
Amin Ben Romdhane
79a91515e5 Add detailed error logging for ubus_invoke failures 2025-05-20 11:42:02 +00:00
Amin Ben Romdhane
cbe727e54e
Enable all micro-services in Pipeline 2025-05-20 15:12:48 +05:30
Amin Ben Romdhane
d017aa025c
bbfdmd: Prevent repeated async calls to unstable services 2025-05-19 19:22:48 +05:30
Amin Ben Romdhane
cba4ccc25c Tools: added gateway-info module 2025-05-15 11:44:39 +02:00
Suvendhu Hansa
3f56ad4595 bbfdm api for reverting dmmap changes 2025-05-14 20:51:16 +05:30
Amin Ben Romdhane
7d89e82de3
Tools: added netmode module 2025-05-14 09:01:55 +00:00
Amin Ben Romdhane
a32a9a2bfc
Commit dmmap changes during reference DB refresh 2025-05-13 14:09:14 +00:00
Amin Ben Romdhane
ea3fa033e7 Revise get_ip_addr_used API to give the correct IP address used 2025-05-09 17:10:16 +02:00
Amin Ben Romdhane
c7e783aecd
Update pipeline to cover all micro-services 2025-05-08 17:34:23 +00:00
Amin Ben Romdhane
c4701f0968 Add support for data model schema 2025-05-06 15:24:40 +02:00
Amin Ben Romdhane
ccc15bd9e1
Fix tools to show available datamodel correctly 2025-05-01 17:24:36 +05:30
Vivek Kumar Dutta
2f5059d7de
Change build type to debug 2025-04-29 09:12:36 +05:30
Vivek Kumar Dutta
805bfc19d7
Updated tr181 xml to 2.19.1 2025-04-26 15:10:57 +05:30
Amin Ben Romdhane
4a3a7ca0b1 Reference DB: add file lock to prevent race condition when updating reference DB file
Added an exclusive file locking mechanism using `flock()` to ensure that
only one process at a time can update the '/var/state/bbfdm_reference_db' file.
This prevents data loss or corruption caused by concurrent access.

This change improves robustness when `bbfdm_refresh_references()` is called
by multiple processes in parallel.
2025-04-25 12:13:20 +02:00
Amin Ben Romdhane
ab5cde2c42 Introduce a new ubus API with timeout 2025-04-23 07:15:22 +00:00
Amin Ben Romdhane
4e32948acc
Reboot(): Increase sleep duration after reboot calls to ensure proper system state 2025-04-23 03:31:37 +00:00
Vivek Kumar Dutta
0448fbc7e9
Bump RootDataModelVersion to 2.19 2025-04-22 19:05:19 +05:30
Amin Ben Romdhane
56937cd498
Update reference db refresh 2025-04-15 15:20:55 +00:00
Vivek Kumar Dutta
cecb673d82
Updated tools to use tr181-2.19 2025-04-12 18:31:44 +05:30
Vivek Kumar Dutta
4ec1489532
update tools for qos 2025-04-12 18:15:49 +05:30
Amin Ben Romdhane
cb75a1e027
Register wifidmd as unified daemon 2025-04-12 14:27:24 +05:30
Amin Ben Romdhane
4d520ceb62 Make refresh references API visible from micro-services 2025-04-11 13:17:51 +02:00
Amin Ben Romdhane
45a6d7e0c7 Redesign the handling of references based on uci reference_translation 2025-04-08 09:29:09 +02:00
Amin Ben Romdhane
e3757b5f37
Remove bbfdm.linker request and response events 2025-03-20 11:56:23 +00:00
Amin Ben Romdhane
bd9f7db4fb Optimize Device.Security. Object 2025-03-20 01:45:12 +01:00
Amin Ben Romdhane
09882ba799 Update documentation 2025-03-18 12:56:15 +01:00
Amin Ben Romdhane
d8842b9968 Fix the protocol value for objects that have restrictions on the service protocol 2025-03-18 12:53:53 +01:00
Amin Ben Romdhane
948a3771c1 Operate: Fix JSON format output in case of output failure 2025-03-18 12:52:19 +01:00
Vivek Kumar Dutta
53fdfcfa28
Updated logs 2025-03-16 11:52:42 +05:30
Vivek Kumar Dutta
1fa64da324 Update buffer size of set to 4096 2025-03-13 13:24:10 +00:00
Amin Ben Romdhane
95848b941b bbfdmd: Increase ubus timeout '5' -> '10' 2025-03-13 11:38:34 +01:00
99 changed files with 60795 additions and 47835 deletions

View file

@ -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

View file

@ -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(

View file

@ -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 |
| ------------ | ---------------------------------------- | -------- |

View file

@ -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, &registered_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, &registered_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, &registered_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, &registered_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, &registered_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();

View file

@ -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;
}

View file

@ -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) {

View file

@ -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);

View file

@ -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 '[', its 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));
}
}

View file

@ -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 */

View file

@ -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;

View file

@ -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, &registered_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, &registered_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, &registered_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, &registered_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;
}

View file

@ -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 */

View file

@ -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)

View file

@ -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;

View file

@ -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"

View file

@ -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" }] }
```

View file

@ -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": {

View file

@ -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" }] }
```

View file

@ -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.

View file

@ -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-services 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-services 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
```

View 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.

View file

@ -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 |
| -------- | ---------------------------------- | ------------------------------------ | -------------------------------------- |

View 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 APIs 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)**

View file

@ -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
const char *srv_name
Pointer to the service name to set.
return
None
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
Inputs:
int log_level
Desired log level to set.
return
None
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
Inputs:
DM_MAP_OBJ *DynamicObj
Pointer to the internal data model.
return
None
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`, its 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. |

View file

@ -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

View 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"

View file

@ -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

View 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

View file

@ -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}

View file

@ -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

View file

@ -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

View file

@ -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/*

View file

@ -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
)
}

View file

@ -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 $?

View file

@ -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__);

View file

@ -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 {

View file

@ -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 *)&registered_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(&registered_db);
return (ctx->findparam == 0) ? err : 0;
}

View file

@ -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);

View file

@ -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 */

View file

@ -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

View file

@ -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);

View file

@ -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();
}

View file

@ -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

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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.
*

View file

@ -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;
}

View file

@ -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})

View file

@ -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;
}

View file

@ -3,7 +3,6 @@
enum {
DM_ADD_PATH,
DM_ADD_OBJ_PATH,
DM_ADD_OPTIONAL,
__DM_ADD_MAX
};

View file

@ -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;
}

View file

@ -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 */

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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,9 +192,15 @@ 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] == '.')
@ -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);

View file

@ -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 */

View file

@ -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;
}

View file

@ -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}
};

View file

@ -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");
dmuci_set_value_by_section(curr_data->config_section, "enable", "0");
dmuci_set_value_by_section(curr_data->config_section, "duration", "1");
for (i = 0; allowed_days[i] != NULL; i++) {
dmuci_add_list_value_by_section(s, "day", allowed_days[i]);
}
for (int i = 0; allowed_days[i] != NULL; i++)
dmuci_add_list_value_by_section(curr_data->config_section, "day", allowed_days[i]);
dmuci_set_value_by_section(s, "duration", "1");
// 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}
};

View file

@ -16,5 +16,8 @@
extern DMOBJ tSchedulesObj[];
extern DMLEAF tSchedulesParams[];
extern DMLEAF tScheduleParams[];
void dmmap_synchronizeSchedulesSchedule(struct dmctx *dmctx);
#endif

View file

@ -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);

View file

@ -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;
}

View file

@ -19,7 +19,20 @@
},
{
"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.",
@ -28,11 +41,11 @@
{
"parent_dm": "Device.",
"object": "Reboot()"
},
{
"parent_dm": "Device.",
"object": "FactoryReset()"
}
},
{
"parent_dm": "Device.",
"object": "FactoryReset()"
}
],
"config": {
"loglevel": "3"

View file

@ -2,7 +2,7 @@
"daemon": {
"enable": "1",
"service_name": "wifidmd",
"unified_daemon": false,
"unified_daemon": true,
"services": [
{
"parent_dm": "Device.",

View 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'

View file

@ -1,5 +1,5 @@
config globals 'globals'
option enable '1'
option enable '0'
config profile 'profile_1'
option name 'kids'

View file

@ -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'

View 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

View file

@ -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;
}

View file

@ -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"]

View file

@ -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

View file

@ -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)

File diff suppressed because one or more lines are too long

4
tools/docker/fixuid.yml Normal file
View file

@ -0,0 +1,4 @@
user: dev
group: dev
paths:
- /home/dev

4
tools/docker/gitconfig Normal file
View file

@ -0,0 +1,4 @@
[advice]
detachedHead = false
[credential]
helper = store

View file

@ -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")
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)
bbf.create_folder(output_dir)
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)
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 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:
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)
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 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)
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")
print("Datamodel generation completed, aritifacts shall be available in out directory or as per input json configuration")
sys.exit(bbf.BBF_ERROR_CODE)

View file

@ -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

View file

@ -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)

View file

@ -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,6 +194,8 @@ 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")
@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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}"
}

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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);
}

View file

@ -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__