mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2025-12-25 19:44:30 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4828d4c1a | ||
|
|
f480b001c7 | ||
|
|
df45b0a720 | ||
|
|
24646d3367 | ||
|
|
53e743d0b2 | ||
|
|
952bcc924a | ||
|
|
c541463faf | ||
|
|
506da7e733 | ||
|
|
6662cee5c9 | ||
|
|
5f8c187880 |
@@ -11,7 +11,7 @@ USE_LOCAL:=0
|
||||
ifneq ($(USE_LOCAL),1)
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/bbf/bbfdm.git
|
||||
PKG_SOURCE_VERSION:=549a296cbfe0f6f6aaf22c0371651668abd8b10f
|
||||
PKG_SOURCE_VERSION:=1615b42e405faceceac825f9c0387a58b90785ae
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
PKG_MIRROR_HASH:=skip
|
||||
endif
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=60
|
||||
START=97
|
||||
STOP=05
|
||||
|
||||
USE_PROCD=1
|
||||
PROG=/usr/sbin/dm-service
|
||||
DM_AGENT_PROG=/usr/sbin/dm-agent
|
||||
|
||||
BBFDM_MICROSERVICE_DIR="/etc/bbfdm/services"
|
||||
|
||||
@@ -24,7 +25,8 @@ validate_bbfdm_micro_service_section()
|
||||
_add_microservice()
|
||||
{
|
||||
local name path loglevel
|
||||
local enable enable_core unified_daemon
|
||||
local enable enable_core unified_daemon dm_framework
|
||||
local daemon_prog
|
||||
|
||||
# Check enable from micro-service
|
||||
path="${1}"
|
||||
@@ -47,14 +49,25 @@ _add_microservice()
|
||||
return 0
|
||||
fi
|
||||
|
||||
json_get_var dm_framework dm-framework 0
|
||||
if [ "${dm_framework}" -eq "1" ] || [ "${dm_framework}" = "true" ]; then
|
||||
daemon_prog="${DM_AGENT_PROG}"
|
||||
else
|
||||
daemon_prog="${PROG}"
|
||||
fi
|
||||
|
||||
json_select config
|
||||
json_get_var loglevel loglevel 4
|
||||
|
||||
procd_open_instance "${name}"
|
||||
|
||||
procd_set_param command ${PROG}
|
||||
procd_append_param command -m "${name}"
|
||||
procd_append_param command -l "${loglevel}"
|
||||
procd_set_param command ${daemon_prog}
|
||||
|
||||
# Only add parameters for dm-service, not for dm-agent
|
||||
if [ "${daemon_prog}" = "${PROG}" ]; then
|
||||
procd_append_param command -m "${name}"
|
||||
procd_append_param command -l "${loglevel}"
|
||||
fi
|
||||
|
||||
if [ "${enable_core}" -eq "1" ]; then
|
||||
procd_set_param limits core="unlimited"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
if PACKAGE_bridgemngr
|
||||
|
||||
config BRIDGEMNGR_BRIDGE_VLAN
|
||||
bool "Use bridge-vlan backend"
|
||||
help
|
||||
Set this option to use bridge-vlan as backend for VLAN objects.
|
||||
|
||||
config BRIDGEMNGR_BRIDGE_VENDOR_EXT
|
||||
bool "Use bridge BBF vendor extensions"
|
||||
default y
|
||||
help
|
||||
Set this option to use bridge BBF vendor extensions.
|
||||
|
||||
config BRIDGEMNGR_BRIDGE_VENDOR_PREFIX
|
||||
string "Package specific datamodel Vendor Prefix for TR181 extensions"
|
||||
default ""
|
||||
endif
|
||||
@@ -1,76 +0,0 @@
|
||||
#
|
||||
# Copyright (C) 2020-2024 iopsys
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=bridgemngr
|
||||
PKG_VERSION:=1.0.17
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
||||
LOCAL_DEV:=0
|
||||
ifneq ($(LOCAL_DEV),1)
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_URL:=https://dev.iopsys.eu/network/bridgemngr
|
||||
PKG_SOURCE_VERSION:=36e6e8319a95dad3bccfe9f2d8a298b39c6ce86b
|
||||
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz
|
||||
PKG_MIRROR_HASH:=skip
|
||||
endif
|
||||
|
||||
PKG_LICENSE:=GPL-2.0-only
|
||||
PKG_LICENSE_FILES:=LICENSE
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include ../bbfdm/bbfdm.mk
|
||||
|
||||
define Package/bridgemngr
|
||||
CATEGORY:=Utilities
|
||||
TITLE:=Bridge Manager
|
||||
DEPENDS:=+libuci +libubox +libubus +libblobmsg-json
|
||||
DEPENDS+=+libbbfdm-api +libbbfdm-ubus +dm-service
|
||||
endef
|
||||
|
||||
define Package/bridgemngr/description
|
||||
Package to add Device.Bridging. data model support.
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/config
|
||||
source "$(SOURCE)/Config.in"
|
||||
endef
|
||||
|
||||
MAKE_PATH:=src
|
||||
|
||||
ifeq ($(CONFIG_BRIDGEMNGR_BRIDGE_VENDOR_PREFIX),"")
|
||||
VENDOR_PREFIX = $(CONFIG_BBF_VENDOR_PREFIX)
|
||||
else
|
||||
VENDOR_PREFIX = $(CONFIG_BRIDGEMNGR_BRIDGE_VENDOR_PREFIX)
|
||||
endif
|
||||
|
||||
TARGET_CFLAGS += -DBBF_VENDOR_PREFIX=\\\"$(VENDOR_PREFIX)\\\"
|
||||
|
||||
ifeq ($(CONFIG_BRIDGEMNGR_BRIDGE_VLAN),y)
|
||||
TARGET_CFLAGS += -DBRIDGE_VLAN_BACKEND
|
||||
endif
|
||||
|
||||
define Package/bridgemngr/install
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
|
||||
$(BBFDM_REGISTER_SERVICES) ./bbfdm_service.json $(1) $(PKG_NAME)
|
||||
$(BBFDM_INSTALL_MS_DM) $(PKG_BUILD_DIR)/src/libbridgemngr.so $(1) $(PKG_NAME)
|
||||
ifeq ($(CONFIG_BRIDGEMNGR_BRIDGE_VENDOR_EXT), y)
|
||||
$(BBFDM_INSTALL_MS_PLUGIN) $(PKG_BUILD_DIR)/src/libbridgeext.so $(1) $(PKG_NAME) 10
|
||||
$(BBFDM_INSTALL_MS_PLUGIN) -v ${VENDOR_PREFIX} ./files/VLAN_Filtering_Extension.json $(1) $(PKG_NAME) 11
|
||||
endif
|
||||
|
||||
$(INSTALL_BIN) ./files/etc/init.d/bridging $(1)/etc/init.d/
|
||||
$(INSTALL_DATA) ./files/etc/config/bridging $(1)/etc/config/
|
||||
endef
|
||||
|
||||
ifeq ($(LOCAL_DEV),1)
|
||||
define Build/Prepare
|
||||
$(CP) ~/git/bridgemngr/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
endif
|
||||
|
||||
$(eval $(call BuildPackage,bridgemngr))
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"json_plugin_version": 2,
|
||||
"Device.Bridging.Bridge.{i}.": {
|
||||
"type": "object",
|
||||
"protocols": [
|
||||
"cwmp",
|
||||
"usp"
|
||||
],
|
||||
"access": true,
|
||||
"array": true,
|
||||
"{BBF_VENDOR_PREFIX}VLANFiltering": {
|
||||
"type": "boolean",
|
||||
"read": true,
|
||||
"write": true,
|
||||
"protocols": [
|
||||
"cwmp",
|
||||
"usp"
|
||||
],
|
||||
"datatype": "boolean",
|
||||
"description": "Enable or disable VLAN Filtering on this bridge.",
|
||||
"mapping": [
|
||||
{
|
||||
"data": "@Parent",
|
||||
"type": "uci_sec",
|
||||
"key": "vlan_filtering"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
dm-framework/datamodels/._Makefile
Executable file
BIN
dm-framework/datamodels/._Makefile
Executable file
Binary file not shown.
76
dm-framework/datamodels/Makefile
Normal file
76
dm-framework/datamodels/Makefile
Normal file
@@ -0,0 +1,76 @@
|
||||
#
|
||||
# Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
# This Software and its content are protected by the Dutch Copyright Act
|
||||
# ('Auteurswet'). All and any copying and distribution of the software
|
||||
# and its content without authorization by Genexis B.V. is
|
||||
# prohibited. The prohibition includes every form of reproduction and
|
||||
# distribution.
|
||||
#
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=datamodels
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=utils
|
||||
CATEGORY:=Genexis
|
||||
TITLE:=GeneOS Datamodel
|
||||
URL:=http://www.genexis.eu
|
||||
PKG_LICENSE:=GENEXIS
|
||||
PKG_LICENSE_URL:=
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
This package contains GeneOS datamodel.
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) -rf ./src/* $(PKG_BUILD_DIR)/
|
||||
cd $(PKG_BUILD_DIR); \
|
||||
npm install better-sqlite3 quickjs && \
|
||||
node ./scripts/json2code.js && \
|
||||
node ./scripts/qjs-handlers-validate.js
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += $(FPIC) -I$(PKG_BUILD_DIR)
|
||||
|
||||
define Build/Compile
|
||||
$(MAKE) -C $(PKG_BUILD_DIR)\
|
||||
PROJECT_ROOT="$(PKG_BUILD_DIR)" \
|
||||
CROSS_COMPILE="$(TARGET_CROSS)" \
|
||||
ARCH="$(LINUX_KARCH)" \
|
||||
EXTRA_CFLAGS="$(TARGET_CFLAGS)" \
|
||||
all
|
||||
endef
|
||||
|
||||
define Build/InstallDev
|
||||
$(INSTALL_DIR) $(1)/usr/include
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
|
||||
$(CP) $(PKG_BUILD_DIR)/dm_types.h $(1)/usr/include/
|
||||
$(CP) $(PKG_BUILD_DIR)/dm_node.h $(1)/usr/include/
|
||||
$(CP) $(PKG_BUILD_DIR)/dm.h $(1)/usr/include/
|
||||
$(CP) $(PKG_BUILD_DIR)/libdm.so $(1)/usr/lib/
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_DIR) $(1)/etc/bbfdm
|
||||
$(INSTALL_DIR) $(1)/usr/lib/dmf_handlers
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/default.db $(1)/etc/bbfdm/default_dm.db
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/libdm.so $(1)/usr/lib/
|
||||
# Copy only .js handler files recursively, preserving folder structure (skip hidden files/folders)
|
||||
( cd $(PKG_BUILD_DIR)/dm-files; \
|
||||
find . -type d -not -path './.*' -exec $(INSTALL_DIR) $(1)/usr/lib/dmf_handlers/{} \; ; \
|
||||
find . -type f -name '*.js' -not -path './.*' -exec $(INSTALL_BIN) {} $(1)/usr/lib/dmf_handlers/{} \; )
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
BIN
dm-framework/datamodels/src/._qjs-handlers-validate.js
Executable file
BIN
dm-framework/datamodels/src/._qjs-handlers-validate.js
Executable file
Binary file not shown.
41
dm-framework/datamodels/src/Makefile
Normal file
41
dm-framework/datamodels/src/Makefile
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
# This Software and its content are protected by the Dutch Copyright Act
|
||||
# ('Auteurswet'). All and any copying and distribution of the software
|
||||
# and its content without authorization by Genexis B.V. is
|
||||
# prohibited. The prohibition includes every form of reproduction and
|
||||
# distribution.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
PROG = libdm.so
|
||||
|
||||
SRCS = dm_node.c
|
||||
|
||||
# the next files are generated
|
||||
SRCS += dm.c
|
||||
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
DEPS = $(SRCS:.c=.d)
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
STRIP = $(CROSS_COMPILE)strip
|
||||
CFLAGS = -I$(STAGING_DIR)/usr/include $(EXTRA_CFLAGS)
|
||||
CFLAGS += -MMD -MP
|
||||
|
||||
LDFLAGS = -shared
|
||||
CFLAGS += -Wall -Werror -fpic
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(OBJS)
|
||||
$(CC) $^ $(LDFLAGS) -o $@
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $^ -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(PROG) *.o core $(DEPS) dm.c dm.h
|
||||
|
||||
-include $(DEPS)
|
||||
BIN
dm-framework/datamodels/src/dm-files/Bridge/._VLANBridge.json
Executable file
BIN
dm-framework/datamodels/src/dm-files/Bridge/._VLANBridge.json
Executable file
Binary file not shown.
443
dm-framework/datamodels/src/dm-files/Bridge/VLANBridge.json
Normal file
443
dm-framework/datamodels/src/dm-files/Bridge/VLANBridge.json
Normal file
@@ -0,0 +1,443 @@
|
||||
[
|
||||
{
|
||||
"object": "Device.Bridging.",
|
||||
"access": "readOnly",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "MaxBridgeEntries",
|
||||
"access": "readOnly",
|
||||
"dataType": "unsignedInt",
|
||||
"const" : "4094"
|
||||
},
|
||||
{
|
||||
"name": "MaxDBridgeEntries",
|
||||
"access": "readOnly",
|
||||
"dataType": "unsignedInt",
|
||||
"const" : "4094"
|
||||
},
|
||||
{
|
||||
"name": "MaxQBridgeEntries",
|
||||
"access": "readOnly",
|
||||
"dataType": "unsignedInt",
|
||||
"const" : "4094"
|
||||
},
|
||||
{
|
||||
"name": "MaxVLANEntries",
|
||||
"access": "readOnly",
|
||||
"dataType": "unsignedInt",
|
||||
"const" : "4094"
|
||||
},
|
||||
{
|
||||
"name": "BridgeNumberOfEntries",
|
||||
"access": "readOnly",
|
||||
"dataType": "unsignedInt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object": "Device.Bridging.Bridge.{i}.",
|
||||
"uniqueKeys": "Name,Alias",
|
||||
"access": "readWrite",
|
||||
"uci": "network.device",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Enable",
|
||||
"access": "readWrite",
|
||||
"dataType": "boolean",
|
||||
"uci": "enabled",
|
||||
"uci-default": "true"
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"access": "readOnly",
|
||||
"dataType": "string(:64)",
|
||||
"set_on_create": "bridge_",
|
||||
"db": true
|
||||
},
|
||||
{
|
||||
"name": "Alias",
|
||||
"access": "readWrite",
|
||||
"dataType": "string(:64)"
|
||||
},
|
||||
{
|
||||
"name": "Status",
|
||||
"access": "readOnly",
|
||||
"dataType": "enum",
|
||||
"enum": [
|
||||
"Disabled",
|
||||
"Enabled",
|
||||
"Error"
|
||||
],
|
||||
"default": "Disabled"
|
||||
},
|
||||
{
|
||||
"name": "Standard",
|
||||
"access": "readWrite",
|
||||
"dataType": "enum",
|
||||
"enum": [
|
||||
"802.1D-2004",
|
||||
"802.1Q-2005",
|
||||
"802.1Q-2011"
|
||||
],
|
||||
"default": "802.1Q-2011"
|
||||
},
|
||||
{
|
||||
"name": "PortNumberOfEntries",
|
||||
"access": "readOnly",
|
||||
"dataType": "unsignedInt"
|
||||
},
|
||||
{
|
||||
"name": "VLANNumberOfEntries",
|
||||
"access": "readOnly",
|
||||
"dataType": "unsignedInt"
|
||||
},
|
||||
{
|
||||
"name": "VLANPortNumberOfEntries",
|
||||
"access": "readOnly",
|
||||
"dataType": "unsignedInt"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object": "Device.Bridging.Bridge.{i}.STP.",
|
||||
"access": "readOnly",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Enable",
|
||||
"access": "readWrite",
|
||||
"dataType": "boolean",
|
||||
"uci": "stp"
|
||||
},
|
||||
{
|
||||
"name": "Status",
|
||||
"access": "readOnly",
|
||||
"dataType": "enum",
|
||||
"enum": [
|
||||
"Disabled",
|
||||
"Enabled",
|
||||
"Error_Misconfigured",
|
||||
"Error"
|
||||
],
|
||||
"default": "Disabled"
|
||||
},
|
||||
{
|
||||
"name": "Protocol",
|
||||
"access": "readWrite",
|
||||
"dataType": "enum",
|
||||
"enum": [
|
||||
"STP",
|
||||
"RSTP"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "BridgePriority",
|
||||
"access": "readWrite",
|
||||
"dataType": "unsignedInt(0:61440)",
|
||||
"default": "32768"
|
||||
},
|
||||
{
|
||||
"name": "HelloTime",
|
||||
"access": "readWrite",
|
||||
"dataType": "unsignedInt(100:1000)",
|
||||
"default": "200"
|
||||
},
|
||||
{
|
||||
"name": "MaxAge",
|
||||
"access": "readWrite",
|
||||
"dataType": "unsignedInt(600:4000)",
|
||||
"default": "2000"
|
||||
},
|
||||
{
|
||||
"name": "ForwardingDelay",
|
||||
"access": "readWrite",
|
||||
"dataType": "unsignedInt(4:30)",
|
||||
"default": "15"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object": "Device.Bridging.Bridge.{i}.Port.{i}.",
|
||||
"uniqueKeys": "Alias,Name",
|
||||
"access": "readWrite",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Enable",
|
||||
"access": "readWrite",
|
||||
"dataType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "Status",
|
||||
"access": "readOnly",
|
||||
"dataType": "enum",
|
||||
"enum": [
|
||||
"Up",
|
||||
"Down",
|
||||
"Unknown",
|
||||
"Dormant",
|
||||
"NotPresent",
|
||||
"LowerLayerDown",
|
||||
"Error"
|
||||
],
|
||||
"default": "Down"
|
||||
},
|
||||
{
|
||||
"name": "Alias",
|
||||
"access": "readWrite",
|
||||
"dataType": "string(:64)"
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"access": "readOnly",
|
||||
"dataType": "string(:64)",
|
||||
"set_on_create": "port_",
|
||||
"db": "true",
|
||||
"flags": [
|
||||
"linker"
|
||||
],
|
||||
"js-value": "ifname"
|
||||
},
|
||||
{
|
||||
"name": "LastChange",
|
||||
"access": "readOnly",
|
||||
"dataType": "unsignedInt",
|
||||
"const": "0"
|
||||
},
|
||||
{
|
||||
"name": "LowerLayers",
|
||||
"access": "readWrite",
|
||||
"dataType": "pathRef[]",
|
||||
"pathRef": [
|
||||
"Device.Bridging.Bridge.{i}.Port."
|
||||
],
|
||||
"js-value": "ssidPath"
|
||||
},
|
||||
{
|
||||
"name": "ManagementPort",
|
||||
"access": "readWrite",
|
||||
"dataType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "PriorityRegeneration",
|
||||
"access": "readWrite",
|
||||
"dataType": "unsignedInt(0:7)[]",
|
||||
"default": "0,1,2,3,4,5,6,7"
|
||||
},
|
||||
{
|
||||
"name": "{BBF_VENDOR_PREFIX}EgressPriorityRegeneration",
|
||||
"access": "readWrite",
|
||||
"dataType": "unsignedInt(0:7)[]"
|
||||
},
|
||||
{
|
||||
"name": "Type",
|
||||
"access": "readWrite",
|
||||
"dataType": "enum",
|
||||
"enum": [
|
||||
"ProviderNetworkPort",
|
||||
"CustomerNetworkPort",
|
||||
"CustomerEdgePort",
|
||||
"CustomerVLANPort",
|
||||
"VLANUnawarePort"
|
||||
],
|
||||
"default": "CustomerVLANPort"
|
||||
},
|
||||
{
|
||||
"name": "PVID",
|
||||
"access": "readWrite",
|
||||
"dataType": "int(1:4094)",
|
||||
"default": "1"
|
||||
},
|
||||
{
|
||||
"name": "TPID",
|
||||
"access": "readWrite",
|
||||
"dataType": "unsignedInt",
|
||||
"default": "33024"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object": "Device.Bridging.Bridge.{i}.Port.{i}.Stats.",
|
||||
"access": "readOnly",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "BytesSent",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "BytesReceived",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "PacketsSent",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "PacketsReceived",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "ErrorsSent",
|
||||
"dataType": "StatsCounter32"
|
||||
},
|
||||
{
|
||||
"name": "ErrorsReceived",
|
||||
"dataType": "StatsCounter32"
|
||||
},
|
||||
{
|
||||
"name": "UnicastPacketsSent",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "DiscardPacketsSent",
|
||||
"dataType": "StatsCounter32"
|
||||
},
|
||||
{
|
||||
"name": "DiscardPacketsReceived",
|
||||
"dataType": "StatsCounter32"
|
||||
},
|
||||
{
|
||||
"name": "MulticastPacketsSent",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "UnicastPacketsReceived",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "MulticastPacketsReceived",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "BroadcastPacketsSent",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "BroadcastPacketsReceived",
|
||||
"dataType": "unsignedLong"
|
||||
},
|
||||
{
|
||||
"name": "UnknownProtoPacketsReceived",
|
||||
"dataType": "StatsCounter32"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object": "Device.Bridging.Bridge.{i}.VLAN.{i}.",
|
||||
"uniqueKeys": "Alias,VLANID",
|
||||
"access": "readWrite",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Enable",
|
||||
"access": "readWrite",
|
||||
"dataType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "Name",
|
||||
"access": "readWrite",
|
||||
"dataType": "string(:64)",
|
||||
"set_on_create": "vlan_"
|
||||
},
|
||||
{
|
||||
"name": "Alias",
|
||||
"access": "readWrite",
|
||||
"dataType": "string(:64)"
|
||||
},
|
||||
{
|
||||
"name": "VLANID",
|
||||
"access": "readWrite",
|
||||
"dataType": "int(0:4094)"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object": "Device.Bridging.Bridge.{i}.VLANPort.{i}.",
|
||||
"uniqueKeys": "Alias,VLAN",
|
||||
"access": "readWrite",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Enable",
|
||||
"access": "readWrite",
|
||||
"dataType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "Alias",
|
||||
"access": "readWrite",
|
||||
"dataType": "string(:64)"
|
||||
},
|
||||
{
|
||||
"name": "VLAN",
|
||||
"access": "readWrite",
|
||||
"dataType": "pathRef",
|
||||
"pathRef": [
|
||||
"Device.Bridging.Bridge.{i}.VLAN."
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Port",
|
||||
"access": "readWrite",
|
||||
"dataType": "pathRef",
|
||||
"pathRef": [
|
||||
"Device.Bridging.Bridge.{i}.Port."
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Untagged",
|
||||
"access": "readWrite",
|
||||
"dataType": "boolean"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"object": "Device.Bridging.ProviderBridge.{i}.",
|
||||
"uniqueKeys": "Alias",
|
||||
"access": "readWrite",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Enable",
|
||||
"access": "readWrite",
|
||||
"dataType": "boolean"
|
||||
},
|
||||
{
|
||||
"name": "Status",
|
||||
"access": "readOnly",
|
||||
"dataType": "enum",
|
||||
"enum": [
|
||||
"Disabled",
|
||||
"Enabled",
|
||||
"Error_Misconfigured",
|
||||
"Error"
|
||||
],
|
||||
"default": "Disabled"
|
||||
},
|
||||
{
|
||||
"name": "Alias",
|
||||
"access": "readWrite",
|
||||
"dataType": "string(:64)"
|
||||
},
|
||||
{
|
||||
"name": "Type",
|
||||
"access": "readWrite",
|
||||
"dataType": "enum",
|
||||
"enum": [
|
||||
"S-VLAN",
|
||||
"PE"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SVLANcomponent",
|
||||
"access": "readWrite",
|
||||
"dataType": "pathRef",
|
||||
"pathRef": [
|
||||
"Device.Bridging.Bridge."
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "CVLANcomponents",
|
||||
"access": "readWrite",
|
||||
"dataType": "pathRef[]",
|
||||
"pathRef": [
|
||||
"Device.Bridging.Bridge."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
166
dm-framework/datamodels/src/dm-files/Bridge/bridge-apply.js
Normal file
166
dm-framework/datamodels/src/dm-files/Bridge/bridge-apply.js
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
getUciOption, getUciByType, setUci, addUci, delUci
|
||||
} from '../uci.js';
|
||||
import * as dm from '../dm_consts.js';
|
||||
import { getBridgeDeviceType } from './common.js';
|
||||
|
||||
function clearUnusedDevice(oldPorts, newPorts, devices) {
|
||||
oldPorts?.forEach(port => {
|
||||
if (port.includes('.') && !newPorts?.includes(port)) {
|
||||
const dev = devices?.find(x => x.name === port);
|
||||
if (dev?.['.name']) delUci('network', dev['.name']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function applyBridge(bri, ports, VLANs, VLANPorts) {
|
||||
const ifnames = [];
|
||||
const devices = getUciByType('network', 'device')?.filter(x => x.type !== undefined);
|
||||
|
||||
const portsVal = getUciOption('network', bri._key, 'ports');
|
||||
if (portsVal) delUci('network', bri._key, null, 'ports');
|
||||
|
||||
// get ports ethernet ifnames
|
||||
for (const port of ports || []) {
|
||||
if (port.ManagementPort || !port.LowerLayers.includes('Ethernet.Interface') || !port.Enable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ifname = _dm_linker_value(port.LowerLayers);
|
||||
if (!ifname) {
|
||||
_log_error(`ifname not found for port: ${port.LowerLayers}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// check vlan
|
||||
const portPath = `Device.Bridging.Bridge.${bri['.index']}.Port.${port['.index']}`;
|
||||
const vp = VLANPorts?.find(x => x.Port === portPath);
|
||||
if (!vp?.VLAN) {
|
||||
ifnames.push(ifname);
|
||||
continue;
|
||||
}
|
||||
|
||||
// get index of the vlan
|
||||
const [, indices] = _dm_node(vp.VLAN);
|
||||
const vlanIdx = indices[indices.length - 1];
|
||||
const vlan = VLANs?.find(x => x['.index'] === vlanIdx);
|
||||
if (!vlan || vlan.VLANID <= 0) {
|
||||
ifnames.push(ifname);
|
||||
continue;
|
||||
}
|
||||
|
||||
const eth = ifname;
|
||||
ifname = `${ifname}.${vlan.VLANID}`;
|
||||
|
||||
const dev = devices?.find(x => x.name === ifname);
|
||||
let devName;
|
||||
if (dev) {
|
||||
devName = dev['.name'];
|
||||
} else {
|
||||
devName = `br_${bri['.index']}_port_${vp['.index']}`;
|
||||
addUci('network', 'device', devName, {
|
||||
ifname: eth,
|
||||
name: ifname,
|
||||
vid: vlan.VLANID,
|
||||
});
|
||||
}
|
||||
|
||||
const uciConfigs = {};
|
||||
|
||||
// Handle Type parameter - determine device type based on port Type or default behavior
|
||||
let deviceType = '';
|
||||
if (port.Type) {
|
||||
deviceType = getBridgeDeviceType(port.Type);
|
||||
if (deviceType) uciConfigs.type = deviceType;
|
||||
} else if (!vp.Untagged) {
|
||||
uciConfigs.type = '8021q';
|
||||
deviceType = '8021q';
|
||||
}
|
||||
|
||||
// Handle TPID parameter
|
||||
if (port.TPID) {
|
||||
// If TPID is explicitly set, use it and derive device type if needed
|
||||
uciConfigs.tpid = port.TPID;
|
||||
// Set device type based on TPID if not already set
|
||||
if (!deviceType) {
|
||||
if (port.TPID === '33024') {
|
||||
uciConfigs.type = '8021q';
|
||||
} else if (port.TPID === '34984') {
|
||||
uciConfigs.type = '8021ad';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uciConfigs.disabled = vlan.Enable && vp.Enable ? '0' : '1';
|
||||
uciConfigs.ingress_qos_mapping = port.PriorityRegeneration !== '0,1,2,3,4,5,6,7'
|
||||
? port.PriorityRegeneration.split(',').map((p, i) => `${i}:${p}`)
|
||||
: '';
|
||||
|
||||
uciConfigs.egress_qos_mapping = port.X_IOPSYS_EU_EgressPriorityRegeneration !== ''
|
||||
? port.X_IOPSYS_EU_EgressPriorityRegeneration.split(',').map((p, i) => `${i}:${p}`)
|
||||
: '';
|
||||
|
||||
setUci('network', devName, uciConfigs);
|
||||
ifnames.push(ifname);
|
||||
}
|
||||
|
||||
clearUnusedDevice(portsVal, ifnames, devices);
|
||||
|
||||
if (ifnames.length > 0) {
|
||||
setUci('network', bri._key, { ports: ifnames });
|
||||
}
|
||||
}
|
||||
|
||||
export function applyDeviceBridgingBridgePort(ports, bri) {
|
||||
const vlans = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_VLAN, bri['.index']);
|
||||
const vlanPorts = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_VLANPORT, bri['.index']);
|
||||
applyBridge(bri, ports, vlans, vlanPorts);
|
||||
}
|
||||
|
||||
export function applyDeviceBridgingBridgeVLAN(vlans, bri) {
|
||||
const ports = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_PORT, bri['.index']);
|
||||
const vlanPorts = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_VLANPORT, bri['.index']);
|
||||
applyBridge(bri, ports, vlans, vlanPorts);
|
||||
}
|
||||
|
||||
export function applyDeviceBridgingBridgeVLANPort(vlanPorts, bri) {
|
||||
const ports = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_PORT, bri['.index']);
|
||||
const vlans = _dm_get(dm.DM_DEVICE_BRIDGING_BRIDGE_VLAN, bri['.index']);
|
||||
applyBridge(bri, ports, vlans, vlanPorts);
|
||||
}
|
||||
|
||||
export function initDeviceBridgingBridge(bri) {
|
||||
setUci('network', bri._key, {
|
||||
type: 'bridge',
|
||||
name: bri.Name,
|
||||
enabled: '0',
|
||||
});
|
||||
// create empty interface for the bridge
|
||||
addUci('network', 'interface', `itf_${bri._key}`, {
|
||||
device: bri.Name,
|
||||
bridge_empty: '1',
|
||||
});
|
||||
}
|
||||
|
||||
export const filterDeviceBridgingBridge = uci => uci.type === 'bridge';
|
||||
|
||||
export function deinitDeviceBridgingBridge(uci) {
|
||||
const ports = getUciOption('network', uci, 'ports');
|
||||
ports?.forEach(port => {
|
||||
if (port.includes('.')) {
|
||||
const dev = getUciByType('network', 'device', { match: { name: port } });
|
||||
if (dev) delUci('network', dev[0]['.name']);
|
||||
}
|
||||
});
|
||||
}
|
||||
125
dm-framework/datamodels/src/dm-files/Bridge/bridge-import.js
Normal file
125
dm-framework/datamodels/src/dm-files/Bridge/bridge-import.js
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
import { getUciByType } from '../uci.js';
|
||||
import { getBridgePortType, getTPIDFromDeviceType } from './common.js';
|
||||
|
||||
function importBridge(dev, devices, bridges) {
|
||||
const briPorts = [];
|
||||
const briVLAN = [];
|
||||
const briVLANPort = [];
|
||||
|
||||
// create the management port first
|
||||
briPorts.push({
|
||||
Alias: `cpe-${dev.name}`,
|
||||
Enable: 1,
|
||||
Name: dev.name,
|
||||
ManagementPort: 1,
|
||||
PVID: 1,
|
||||
TPID: 37120,
|
||||
Type: 'CustomerVLANPort',
|
||||
});
|
||||
|
||||
bridges.push({
|
||||
Name: dev.name,
|
||||
Alias: `cpe-${dev.name}`,
|
||||
Enable: 1,
|
||||
'Port.': briPorts,
|
||||
'VLAN.': briVLAN,
|
||||
'VLANPort.': briVLANPort,
|
||||
_key: dev['.name'],
|
||||
});
|
||||
|
||||
const ethPorts = devices.filter(x => x.ifname?.startsWith('eth'));
|
||||
|
||||
for (const portName of (dev.ports || [])) {
|
||||
let portIndex = ethPorts.findIndex(x => x.ifname === portName);
|
||||
if (portIndex >= 0) {
|
||||
// Regular ethernet port
|
||||
const ethDevice = ethPorts[portIndex];
|
||||
const portType = getBridgePortType(ethDevice.type) || 'CustomerVLANPort';
|
||||
const tpid = getTPIDFromDeviceType(ethDevice.type, ethDevice.tpid);
|
||||
|
||||
briPorts.push({
|
||||
Enable: 1,
|
||||
Name: ethDevice['.name'],
|
||||
Alias: `cpe-${ethDevice['.name']}`,
|
||||
TPID: tpid,
|
||||
PVID: 1,
|
||||
Type: portType,
|
||||
LowerLayers: `Device.Ethernet.Interface.${portIndex + 1}`,
|
||||
_key: ethDevice['.name'],
|
||||
});
|
||||
} else {
|
||||
// vlan device
|
||||
const device = devices.find(x => x.name === portName);
|
||||
if (!device) {
|
||||
_log_error('device not found', portName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (device.type === '8021q' || device.type === 'untagged' || device.type === '8021ad' || device.type === 'transparent') {
|
||||
let vlanIndex = briVLAN.findIndex(x => x.VLANID === device.vid);
|
||||
if (vlanIndex < 0) {
|
||||
briVLAN.push({ Enable: 1, VLANID: device.vid });
|
||||
vlanIndex = briVLAN.length;
|
||||
} else {
|
||||
vlanIndex += 1;
|
||||
}
|
||||
|
||||
// Get the base ethernet device to determine the correct port index
|
||||
const baseEthDevice = ethPorts.find(x => device.ifname === x.ifname);
|
||||
const basePortIndex = baseEthDevice ? ethPorts.indexOf(baseEthDevice) : 0;
|
||||
|
||||
const portType = getBridgePortType(device.type) || 'CustomerVLANPort';
|
||||
const tpid = getTPIDFromDeviceType(device.type, device.tpid);
|
||||
|
||||
briPorts.push({
|
||||
Enable: 1,
|
||||
Name: device['.name'],
|
||||
Alias: `cpe-${device['.name']}`,
|
||||
TPID: tpid,
|
||||
PVID: device.vid,
|
||||
Type: portType,
|
||||
LowerLayers: `Device.Ethernet.Interface.${basePortIndex + 1}`,
|
||||
_key: device['.name'],
|
||||
});
|
||||
|
||||
briVLANPort.push({
|
||||
Enable: 1,
|
||||
VLAN: `Device.Bridging.Bridge.${bridges.length}.VLAN.${vlanIndex}`,
|
||||
Port: `Device.Bridging.Bridge.${bridges.length}.Port.${briPorts.length}`,
|
||||
Untagged: device.type === 'untagged' ? 1 : 0,
|
||||
_key: device['.name'],
|
||||
});
|
||||
} else {
|
||||
_log_error('unknown device type:', device.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (briPorts.length > 1) {
|
||||
const indexes = Array.from({ length: briPorts.length - 1 }, (v, i) => i + 2);
|
||||
briPorts[0].LowerLayers = indexes.map(i => `Device.Bridging.Bridge.${bridges.length}.Port.${i}`).join(',');
|
||||
}
|
||||
}
|
||||
|
||||
export function importDeviceBridgingBridge() {
|
||||
const bridges = [];
|
||||
const devices = getUciByType('network', 'device');
|
||||
devices?.forEach(dev => {
|
||||
if (dev.type === 'bridge') {
|
||||
importBridge(dev, devices, bridges);
|
||||
}
|
||||
});
|
||||
|
||||
return bridges;
|
||||
}
|
||||
133
dm-framework/datamodels/src/dm-files/Bridge/bridge.js
Normal file
133
dm-framework/datamodels/src/dm-files/Bridge/bridge.js
Normal file
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
import * as std from 'std';
|
||||
import { isTrue } from '../utils.js';
|
||||
import { getUciByType } from '../uci.js';
|
||||
|
||||
function setMgmtPortLowerLayers(bri) {
|
||||
if (!bri) return 0;
|
||||
|
||||
const portPath = `Device.Bridging.Bridge.${bri['.index']}.Port.`;
|
||||
const mgmtPort = _dm_instances(portPath, '(ManagementPort="true" OR ManagementPort=1)');
|
||||
if (mgmtPort.length !== 1) return 0;
|
||||
|
||||
const nonMgmtPort = _dm_instances(portPath, '(ManagementPort="false" OR ManagementPort=0)');
|
||||
_dm_update(`${mgmtPort[0]}.LowerLayers`, nonMgmtPort.join(','));
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function changedDeviceBridgingBridgePort(bri) {
|
||||
return setMgmtPortLowerLayers(bri);
|
||||
}
|
||||
|
||||
export function changedDeviceBridgingBridgePortManagementPort(bri) {
|
||||
return setMgmtPortLowerLayers(bri);
|
||||
}
|
||||
|
||||
export function getDeviceBridgingBridgeStatus(bri) {
|
||||
const enable = _dm_get(`Device.Bridging.Bridge.${bri['.index']}.Enable`);
|
||||
return enable ? 'Enabled' : 'Disabled';
|
||||
}
|
||||
|
||||
export function getDeviceBridgingBridgeSTPStatus(bri) {
|
||||
const stpState = std.loadFile(`/sys/class/net/${bri.Name}/bridge/stp_state`)?.trim();
|
||||
return stpState === '1' ? 'Enabled' : 'Disabled';
|
||||
}
|
||||
|
||||
export function getDeviceBridgingBridgePortStatus(bri, port) {
|
||||
if (!port['.db']) return 'Up';
|
||||
|
||||
const enable = _dm_get(`Device.Bridging.Bridge.${bri['.index']}.Port.${port['.index']}.Enable`);
|
||||
return enable ? 'Up' : 'Down';
|
||||
}
|
||||
|
||||
export function infoDeviceBridgingBridgePort(path, port) {
|
||||
const mgmtPort = _dm_get(`${path}.ManagementPort`);
|
||||
if (typeof mgmtPort === 'undefined' || mgmtPort) return;
|
||||
|
||||
const lower = _dm_get(`${path}.LowerLayers`);
|
||||
if (lower) {
|
||||
port.ifname = _dm_linker_value(lower);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to read network statistics
|
||||
function getNetworkStat(port, statName) {
|
||||
return std.loadFile(`/sys/class/net/${port.ifname}/statistics/${statName}`)?.trim();
|
||||
}
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsBytesSent = (bri, port) =>
|
||||
getNetworkStat(port, 'tx_bytes');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsBytesReceived = (bri, port) =>
|
||||
getNetworkStat(port, 'rx_bytes');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsPacketsSent = (bri, port) =>
|
||||
getNetworkStat(port, 'tx_packets');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsPacketsReceived = (bri, port) =>
|
||||
getNetworkStat(port, 'rx_packets');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsErrorsSent = (bri, port) =>
|
||||
getNetworkStat(port, 'tx_errors');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsErrorsReceived = (bri, port) =>
|
||||
getNetworkStat(port, 'rx_errors');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsDiscardPacketsSent = (bri, port) =>
|
||||
getNetworkStat(port, 'tx_dropped');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsDiscardPacketsReceived = (bri, port) =>
|
||||
getNetworkStat(port, 'rx_dropped');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsMulticastPacketsReceived = (bri, port) =>
|
||||
getNetworkStat(port, 'multicast');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsUnicastPacketsSent = (bri, port) =>
|
||||
getNetworkStat(port, 'tx_unicast_packets');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsUnicastPacketsReceived = (bri, port) =>
|
||||
getNetworkStat(port, 'rx_unicast_packets');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsMulticastPacketsSent = (bri, port) =>
|
||||
getNetworkStat(port, 'tx_multicast_packets');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsBroadcastPacketsSent = (bri, port) =>
|
||||
getNetworkStat(port, 'tx_broadcast_packets');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsBroadcastPacketsReceived = (bri, port) =>
|
||||
getNetworkStat(port, 'rx_broadcast_packets');
|
||||
|
||||
export const getDeviceBridgingBridgePortStatsUnknownProtoPacketsReceived = (bri, port) =>
|
||||
getNetworkStat(port, 'rx_unknown_packets');
|
||||
|
||||
export function getDeviceBridgingBridgePort(bri) {
|
||||
const networkName = bri.Name.startsWith('br-') ? bri.Name.slice(3) : bri.Name;
|
||||
|
||||
const wifiIfaces = getUciByType('wireless', 'wifi-iface', { match: { multi_ap: '2' } });
|
||||
wifiIfaces?.forEach(x => {
|
||||
const ssid = getUciByType('dmmap_wireless', 'ssid',
|
||||
{ match: { device: x.device, ssid: x.ssid}, confdir: '/etc/bbfdm/dmmap'});
|
||||
if (Array.isArray(ssid) && ssid.length > 0) {
|
||||
x.ssidPath = _dm_linker_path("Device.WiFi.SSID.", "Name", ssid[0].name) ?? '';
|
||||
}
|
||||
});
|
||||
|
||||
return wifiIfaces?.filter(x => x.network === networkName);
|
||||
}
|
||||
|
||||
export function setDeviceBridgingBridgePortManagementPort(val, bri, port) {
|
||||
if (isTrue(val)) {
|
||||
_db_set(`Device.Bridging.Bridge.${bri['.index']}.Port.${port['.index']}.Name`, bri.Name);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
61
dm-framework/datamodels/src/dm-files/Bridge/common.js
Executable file
61
dm-framework/datamodels/src/dm-files/Bridge/common.js
Executable file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
export const bridgePortTypeMap = [
|
||||
{ portType: 'CustomerNetworkPort', devType: '8021ad' },
|
||||
{ portType: 'CustomerVLANPort', devType: '8021q' },
|
||||
{ portType: 'CustomerVLANPort', devType: 'untagged' },
|
||||
{ portType: 'CustomerVLANPort', devType: '' },
|
||||
{ portType: 'CustomerVLANPort', devType: undefined },
|
||||
{ portType: 'VLANUnawarePort', devType: 'transparent' }
|
||||
];
|
||||
|
||||
export function getBridgePortType(devType) {
|
||||
const mapping = bridgePortTypeMap.find(map => map.devType === devType);
|
||||
return mapping ? mapping.portType : null;
|
||||
}
|
||||
|
||||
export function getBridgeDeviceType(portType) {
|
||||
const mapping = bridgePortTypeMap.find(map => map.portType === portType);
|
||||
return mapping ? mapping.devType : null;
|
||||
}
|
||||
|
||||
export function getDefaultTPID(deviceType) {
|
||||
switch (deviceType) {
|
||||
case '8021q':
|
||||
return '33024';
|
||||
case '8021ad':
|
||||
return '34984';
|
||||
default:
|
||||
return '37120';
|
||||
}
|
||||
}
|
||||
|
||||
export function getTPIDFromDeviceType(deviceType, explicitTPID) {
|
||||
// If explicit TPID is set, use it
|
||||
if (explicitTPID && explicitTPID !== '') {
|
||||
return parseInt(explicitTPID, 10);
|
||||
}
|
||||
|
||||
// Default TPID based on device type
|
||||
switch (deviceType) {
|
||||
case '8021q':
|
||||
return 33024;
|
||||
case '8021ad':
|
||||
return 34984;
|
||||
case 'untagged':
|
||||
case 'transparent':
|
||||
case '':
|
||||
case undefined:
|
||||
default:
|
||||
return 37120;
|
||||
}
|
||||
}
|
||||
126
dm-framework/datamodels/src/dm-files/uci.js
Executable file
126
dm-framework/datamodels/src/dm-files/uci.js
Executable file
@@ -0,0 +1,126 @@
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
/*
|
||||
* Wrapper around the native QuickJS C binding `_uci_call` which speaks to
|
||||
* libuci directly (see qjs_uci_api.c). The exported helpers mimic the public
|
||||
* API of the original uci.js module so that existing call-sites can switch to
|
||||
* this implementation by simply importing uci2.js.
|
||||
*/
|
||||
|
||||
export function uciBool(val) {
|
||||
// by default enable is true if it is not defined
|
||||
return (val === undefined || val === '1' || val === 'true' || val === 'enable' || val === 'yes');
|
||||
}
|
||||
|
||||
function callUci(method, args) {
|
||||
const [ret, res] = _uci_call(method, args);
|
||||
if (ret !== 0) {
|
||||
// Returning undefined on error keeps behaviour consistent with the
|
||||
// original helpers which silently return undefined.
|
||||
return [ret, undefined];
|
||||
}
|
||||
return [ret, res];
|
||||
}
|
||||
|
||||
export function getUci(args) {
|
||||
const [, res] = callUci('get', args);
|
||||
if (res) {
|
||||
if (res.values) {
|
||||
if (!args.section) {
|
||||
return Object.values(res.values);
|
||||
}
|
||||
return res.values;
|
||||
}
|
||||
if (res.value !== undefined) {
|
||||
return res.value;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function getUciOption(config, section, option, extraArgs) {
|
||||
let args = { config, section, option };
|
||||
if (extraArgs) {
|
||||
args = { ...args, ...extraArgs };
|
||||
}
|
||||
return getUci(args);
|
||||
}
|
||||
|
||||
export function getUciByType(config, type, extraArgs) {
|
||||
let args = { config, type };
|
||||
if (extraArgs) {
|
||||
args = { ...args, ...extraArgs };
|
||||
}
|
||||
return getUci(args);
|
||||
}
|
||||
|
||||
export function getUciSection(config, section, extraArgs) {
|
||||
let args = { config, section };
|
||||
if (extraArgs) {
|
||||
args = { ...args, ...extraArgs };
|
||||
}
|
||||
return getUci(args);
|
||||
}
|
||||
|
||||
export function setUci(cfg, section, values, type, match, extraArgs) {
|
||||
let args = { config: cfg, section };
|
||||
if (type) args.type = type;
|
||||
if (values) args.values = values;
|
||||
if (match) args.match = match;
|
||||
if (extraArgs) args = { ...args, ...extraArgs };
|
||||
|
||||
const [ret] = callUci('set', args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function addUci(cfg, type, name, values, extraArgs) {
|
||||
let args = { config: cfg, type };
|
||||
if (name) args.name = name;
|
||||
if (values) args.values = values;
|
||||
if (extraArgs) args = { ...args, ...extraArgs };
|
||||
|
||||
const [, res] = callUci('add', args);
|
||||
return res || undefined;
|
||||
}
|
||||
|
||||
export function delUci(cfg, section, type, option, options, match, extraArgs) {
|
||||
let args = { config: cfg };
|
||||
if (section) args.section = section;
|
||||
if (type) args.type = type;
|
||||
if (option) args.option = option;
|
||||
if (options) args.options = options;
|
||||
if (match) args.match = match;
|
||||
if (extraArgs) args = { ...args, ...extraArgs };
|
||||
|
||||
const [, res] = callUci('delete', args);
|
||||
return res || undefined;
|
||||
}
|
||||
|
||||
export function delUciOption(config, section, option, match, extraArgs) {
|
||||
let args = { config, section, option };
|
||||
if (match) args.match = match;
|
||||
if (extraArgs) args = { ...args, ...extraArgs };
|
||||
const [, res] = callUci('delete', args);
|
||||
return res || undefined;
|
||||
}
|
||||
|
||||
export function uciChanges(cfg, extraArgs) {
|
||||
let args = { config: cfg };
|
||||
if (extraArgs) args = { ...args, ...extraArgs };
|
||||
const [, res] = callUci('changes', args);
|
||||
return res && res.changes ? res.changes : undefined;
|
||||
}
|
||||
|
||||
export function commitUci(cfg, extraArgs) {
|
||||
let args = { config: cfg };
|
||||
if (extraArgs) args = { ...args, ...extraArgs };
|
||||
const [ret] = callUci('commit', args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function revertUci(cfg, extraArgs) {
|
||||
let args = { config: cfg };
|
||||
if (extraArgs) args = { ...args, ...extraArgs };
|
||||
const [ret] = callUci('revert', args);
|
||||
return ret;
|
||||
}
|
||||
268
dm-framework/datamodels/src/dm-files/utils.js
Normal file
268
dm-framework/datamodels/src/dm-files/utils.js
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
/* eslint-disable no-undef */
|
||||
/* eslint-disable no-bitwise */
|
||||
import * as os from 'os';
|
||||
import * as std from 'std';
|
||||
|
||||
export function macAddressToBase64(macAddress) {
|
||||
// Split the MAC address into an array of bytes using the colon separator
|
||||
const bytes = macAddress.split(':');
|
||||
|
||||
// Convert the bytes from hexadecimal to decimal
|
||||
const decimalBytes = bytes.map((byte) => parseInt(byte, 16));
|
||||
|
||||
// Convert the decimal bytes into an array of 8-bit binary strings
|
||||
const binaryBytes = decimalBytes.map((byte) => byte.toString(2).padStart(8, '0'));
|
||||
|
||||
// Concatenate the binary strings into a single string
|
||||
const binaryString = binaryBytes.join('');
|
||||
|
||||
// Split the binary string into groups of 6 bits
|
||||
const base64Chars = [];
|
||||
for (let i = 0; i < binaryString.length; i += 6) {
|
||||
base64Chars.push(binaryString.slice(i, i + 6));
|
||||
}
|
||||
|
||||
// Convert each group of 6 bits to a decimal number
|
||||
const decimalBase64 = base64Chars.map((char) => parseInt(char, 2));
|
||||
|
||||
// Create the base64 character set
|
||||
const base64CharacterSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||
|
||||
// Map the decimal numbers to their corresponding base64 characters
|
||||
const base64String = decimalBase64.map((num) => base64CharacterSet.charAt(num)).join('');
|
||||
|
||||
return base64String;
|
||||
}
|
||||
|
||||
export function tr181Path(path, uciPath, keyName, keyVal) {
|
||||
if (!keyVal) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const [uciConfig, uciType] = uciPath.split('.');
|
||||
const args = { config: uciConfig, type: uciType };
|
||||
|
||||
const [, res] = _ubus_call('uci', 'get', args);
|
||||
if (!res || !res.values) {
|
||||
_log_error('tr181Path: invalid result');
|
||||
return '';
|
||||
}
|
||||
|
||||
let insts = Object.values(res.values);
|
||||
if (uciConfig === 'network' && uciType === 'interface') {
|
||||
insts = insts.filter((x) => x.device !== 'lo' && !x.device?.startsWith('@') && x.proto !== 'dhcpv6');
|
||||
}
|
||||
|
||||
const index = insts.findIndex((x) => x[keyName] === keyVal);
|
||||
if (index < 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (path.startsWith('Device.')) {
|
||||
return `${path}${index + 1}`;
|
||||
}
|
||||
return `Device.${path}${index + 1}`;
|
||||
}
|
||||
|
||||
export function tr181IPInterface(uci) {
|
||||
return tr181Path('IP.Interface.', 'network.interface', '.name', uci);
|
||||
}
|
||||
|
||||
export function prefixLengthToSubnetMask(prefixLength) {
|
||||
if (!prefixLength) {
|
||||
return '';
|
||||
}
|
||||
const mask = 0xFFFFFFFF << (32 - prefixLength);
|
||||
const subnetMask = [
|
||||
(mask >>> 24) & 0xFF,
|
||||
(mask >>> 16) & 0xFF,
|
||||
(mask >>> 8) & 0xFF,
|
||||
mask & 0xFF,
|
||||
].join('.');
|
||||
|
||||
return subnetMask;
|
||||
}
|
||||
|
||||
export function fileExists(path) {
|
||||
let exists = false;
|
||||
if (path !== '') {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const [obj, err] = os.stat(path);
|
||||
exists = (err === 0);
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
|
||||
export function waitUntilFileExists(path, timeoutMs = 10000) {
|
||||
const startTime = Date.now();
|
||||
while (!fileExists(path) && (Date.now() - startTime < timeoutMs)) {
|
||||
os.sleep(100);
|
||||
}
|
||||
|
||||
return fileExists(path);
|
||||
}
|
||||
|
||||
export function runCommand(command) {
|
||||
const fp = std.popen(command, 'r');
|
||||
if (fp) {
|
||||
const result = fp.readAsString();
|
||||
if (fp.close() === 0)
|
||||
return result;
|
||||
else
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function fileExistsWithRegex(directory, regex) {
|
||||
const [files, err] = os.readdir(directory);
|
||||
|
||||
if (err) {
|
||||
_log_warn(`fileExistsWithRegex(): Could not read directory: ${directory}`);
|
||||
}
|
||||
|
||||
for (let i = 0; i < files.length; i += 1) {
|
||||
if (regex.test(files[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isIPv4Address(addr) {
|
||||
return addr?.includes('.');
|
||||
}
|
||||
|
||||
export function isIPv6Address(addr) {
|
||||
return addr?.includes(':');
|
||||
}
|
||||
|
||||
// find the pathname in LowerLayers
|
||||
export function findPathInLowerlayer(path, inst, instKey) {
|
||||
const lowerlayer = _dm_get(`${path}.LowerLayers`);
|
||||
if (lowerlayer === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lowerlayer.includes(instKey)) {
|
||||
if (lowerlayer.includes(inst)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
const layers = lowerlayer.split(',');
|
||||
if (layers.find((x) => findPathInLowerlayer(x, inst, instKey))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function hex2a(hex) {
|
||||
let i = 0;
|
||||
let str = '';
|
||||
for (i = 0; i < hex.length; i += 2) {
|
||||
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export function capitalizeFirstLetter(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
||||
export function lowercaseFirstLetter(string) {
|
||||
return string.charAt(0).toLowerCase() + string.slice(1);
|
||||
}
|
||||
|
||||
export function getIfnameOperState(ifname) {
|
||||
if (!ifname) {
|
||||
return 'Down';
|
||||
}
|
||||
|
||||
const res = std.loadFile(`/sys/class/net/${ifname}/operstate`);
|
||||
if (res) {
|
||||
return capitalizeFirstLetter(res.trim());
|
||||
}
|
||||
|
||||
return 'Down';
|
||||
}
|
||||
|
||||
export function getIfnameState(ifname, name) {
|
||||
if (!ifname) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const res = std.loadFile(`/sys/class/net/${ifname}/${name}`);
|
||||
return res?.trim();
|
||||
}
|
||||
|
||||
export function strToHex(str) {
|
||||
if (!str) {
|
||||
return '';
|
||||
}
|
||||
let hex = '';
|
||||
for (let i = 0; i < str.length; i += 1) {
|
||||
hex += str.charCodeAt(i).toString(16);
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
// transform the object of following object:
|
||||
// {
|
||||
// 'SSIDtoVIDMapping.1.SSID': 'abc',
|
||||
// 'SSIDtoVIDMapping.1.VID': 100,
|
||||
// 'SSIDtoVIDMapping.2.SSID': 'xyz',
|
||||
// 'SSIDtoVIDMapping.2.VID': 200,
|
||||
// Enable: 'true'
|
||||
// }
|
||||
// into:
|
||||
// {
|
||||
// SSIDtoVIDMapping: [ { SSID: 'abc', VID: 100 }, { SSID: 'xyz', VID: 200 } ],
|
||||
// Enable: 'true'
|
||||
// }
|
||||
export function transformInputObject(obj) {
|
||||
const result = {};
|
||||
|
||||
Object.entries(obj).forEach(([key, value]) => {
|
||||
const splitKey = key.split('.');
|
||||
|
||||
if (splitKey.length < 3) {
|
||||
result[key] = value; // add invalid keys directly to the result
|
||||
return;
|
||||
}
|
||||
|
||||
const mainKey = splitKey[0];
|
||||
const index = parseInt(splitKey[1], 10) - 1;
|
||||
const prop = splitKey[2];
|
||||
|
||||
if (!result[mainKey]) {
|
||||
result[mainKey] = [];
|
||||
}
|
||||
|
||||
if (!result[mainKey][index]) {
|
||||
result[mainKey][index] = {};
|
||||
}
|
||||
|
||||
result[mainKey][index][prop] = value;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isTrue(val) {
|
||||
return val === 'true' || val === '1' || val === true;
|
||||
}
|
||||
1507
dm-framework/datamodels/src/dm_node.c
Normal file
1507
dm-framework/datamodels/src/dm_node.c
Normal file
File diff suppressed because it is too large
Load Diff
290
dm-framework/datamodels/src/dm_node.h
Normal file
290
dm-framework/datamodels/src/dm_node.h
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DM_NODE_H
|
||||
#define DM_NODE_H
|
||||
|
||||
#include "dm_types.h"
|
||||
|
||||
enum NODE_FLAG {
|
||||
FLAG_NONE = 0x0,
|
||||
FLAG_COUNTER = 0x40,
|
||||
FLAG_HAS_MIN = 0x80,
|
||||
FLAG_HAS_MAX = 0x100,
|
||||
FLAG_HAS_ORDER = 0x400,
|
||||
FLAG_WRITABLE = 0x800,
|
||||
FLAG_CONFIDENTIAL = 0x1000,
|
||||
FLAG_CWMP_ONLY = 0x4000,
|
||||
FLAG_USP_ONLY = 0x8000,
|
||||
FLAG_INTERNAL= 0x10000,
|
||||
};
|
||||
|
||||
enum DM_UCI_MAP_TYPE {
|
||||
DM_UCI_MAP_TYPE_NONE = 0,
|
||||
DM_UCI_MAP_TYPE_SIMPLE = 0x01, // simple value 2 value
|
||||
DM_UCI_MAP_TYPE_DISABLE = 0x02, // uci disable bool type
|
||||
DM_UCI_MAP_TYPE_TABLE = 0x04, // a JSON object is used for mapping, {uci: dm}
|
||||
DM_UCI_MAP_TYPE_INTERFACE = 0x08, // ip interface
|
||||
DM_UCI_MAP_TYPE_JS = 0x10, // js code
|
||||
};
|
||||
|
||||
struct dm_uci_map {
|
||||
unsigned int type;
|
||||
const char *map;
|
||||
const char *key;
|
||||
};
|
||||
|
||||
struct dm_node_info {
|
||||
enum DM_NODE_TYPE type;
|
||||
dm_node_id_t node_id;
|
||||
const char *const name;
|
||||
const struct dm_node_info *const parent;
|
||||
const char *const table_name; // if NULL the value is not stored in the database
|
||||
const char *pathname;
|
||||
enum NODE_FLAG flag;
|
||||
dm_node_id_t depends_node_id;
|
||||
};
|
||||
|
||||
struct dm_parameter {
|
||||
struct dm_node_info node;
|
||||
enum DM_DATA_TYPE data_type;
|
||||
|
||||
long min;
|
||||
long max;
|
||||
int list;
|
||||
|
||||
union {
|
||||
// for enum data type
|
||||
const char **enum_strings;
|
||||
|
||||
// for counter data type
|
||||
dm_node_id_t counter_object;
|
||||
|
||||
// for path data type
|
||||
const dm_node_id_t *paths;
|
||||
} data;
|
||||
const char *set_on_create;
|
||||
const char *js_val;
|
||||
const char *const_val;
|
||||
const char *default_val;
|
||||
const char *default_uci_val;
|
||||
struct dm_uci_map map;
|
||||
};
|
||||
|
||||
struct command_arg {
|
||||
const char *name;
|
||||
enum DM_DATA_TYPE type;
|
||||
int min;
|
||||
int max;
|
||||
int list;
|
||||
const char **enum_values;
|
||||
int mandatory;
|
||||
};
|
||||
|
||||
struct dm_command {
|
||||
struct dm_node_info node;
|
||||
int async;
|
||||
const struct command_arg *inputs;
|
||||
int inputs_cnt;
|
||||
const struct command_arg *outputs;
|
||||
int outputs_cnt;
|
||||
};
|
||||
|
||||
struct event_arg {
|
||||
const char *name;
|
||||
enum DM_DATA_TYPE type;
|
||||
};
|
||||
|
||||
struct dm_event {
|
||||
struct dm_node_info node;
|
||||
const struct event_arg *args;
|
||||
int args_cnt;
|
||||
const char *ubus_event;
|
||||
};
|
||||
|
||||
struct dm_object {
|
||||
struct dm_node_info node;
|
||||
int param_num;
|
||||
const struct dm_node_info *const *const param_list;
|
||||
int command_num;
|
||||
const struct dm_node_info *const *const command_list;
|
||||
int object_num;
|
||||
const struct dm_node_info *const *const event_list;
|
||||
int event_num;
|
||||
const struct dm_node_info *const *const object_list;
|
||||
int paths_refs_num;
|
||||
const struct dm_node_info *const *const paths_refs_list;
|
||||
const char *key_param_names;
|
||||
struct dm_uci_map map;
|
||||
const char *js_val;
|
||||
int fixed_objects;
|
||||
};
|
||||
|
||||
const struct dm_node_info *dm_node_get_root(void);
|
||||
const struct dm_node_info *dm_node_get_info(dm_node_id_t id);
|
||||
|
||||
/**
|
||||
* This function will look up a parameter by ID
|
||||
* @pre None
|
||||
* @post valid dm_parameter pointer returned or NULL if error
|
||||
* @param id The actual id for which we want to retrieve a
|
||||
* pointer to the dm_parameter struct
|
||||
* @return NULL in case id is out of range or if id does not point
|
||||
* to a parameter node, a pointer to the dm_parameter struct otherwise
|
||||
*/
|
||||
const struct dm_parameter *dm_node_get_parameter(dm_node_id_t id);
|
||||
|
||||
const struct dm_command *dm_node_get_command(dm_node_id_t id);
|
||||
const struct dm_event *dm_node_get_event(dm_node_id_t id);
|
||||
/**
|
||||
* This function will look up a command by ID
|
||||
* @pre None
|
||||
* @post valid dm_command pointer returned or NULL if error
|
||||
* @param id The actual id for which we want to retrieve a
|
||||
* pointer to the dm_parameter struct
|
||||
* @return NULL in case id is out of range or if id does not point
|
||||
* to a command node, a pointer to the dm_command struct otherwise
|
||||
*/
|
||||
const struct dm_command *dm_node_get_command(dm_node_id_t id);
|
||||
/**
|
||||
* This function will look up an object by ID
|
||||
* @pre None
|
||||
* @post valid dm_object pointer returned or NULL if error
|
||||
* @param id The actual id for which we want to retrieve a
|
||||
* pointer to the dm_object struct
|
||||
* @return NULL in case id is out of range or if id does not point
|
||||
* to an object node, a pointer to the dm_object struct otherwise
|
||||
* Note that if the id points to an object list a pointer to the
|
||||
* first object is returned
|
||||
*/
|
||||
const struct dm_object *dm_node_get_object(dm_node_id_t id);
|
||||
|
||||
int dm_node_is_valid(dm_node_id_t id);
|
||||
int dm_node_is_parameter(dm_node_id_t id);
|
||||
int dm_node_is_command(dm_node_id_t id);
|
||||
int dm_node_is_event(dm_node_id_t id);
|
||||
int dm_node_is_writable(dm_node_id_t id);
|
||||
int dm_node_is_object(dm_node_id_t id);
|
||||
int dm_node_is_objectlist(dm_node_id_t id);
|
||||
int dm_node_is_counter(dm_node_id_t id);
|
||||
int dm_node_is_text_type(dm_node_id_t id);
|
||||
int dm_node_is_bool_type(dm_node_id_t id);
|
||||
int dm_node_is_ul_type(dm_node_id_t id);
|
||||
int dm_node_is_unsigned_type(dm_node_id_t id);
|
||||
int dm_node_is_confidential(dm_node_id_t id);
|
||||
int dm_node_is_cwmp_only(dm_node_id_t id);
|
||||
int dm_node_is_usp_only(dm_node_id_t id);
|
||||
int dm_node_is_internal(dm_node_id_t id);
|
||||
dm_node_id_t dm_node_counter_id(dm_node_id_t id);
|
||||
int dm_node_has_db(dm_node_id_t id);
|
||||
|
||||
const char *dm_node_object_keys(dm_node_id_t id);
|
||||
int dm_node_is_fixed_objects(dm_node_id_t id);
|
||||
int dm_node_max_data_size(dm_node_id_t id);
|
||||
int dm_node_param_mem_size(dm_node_id_t node_id);
|
||||
const char *get_param_xsd_type(enum DM_DATA_TYPE type);
|
||||
const char *dm_node_name(dm_node_id_t id);
|
||||
dm_node_id_t dm_node_id_parent(dm_node_id_t id);
|
||||
int dm_node_parent(const dm_node_t *node, dm_node_t *parent);
|
||||
// get first multi-instance parent
|
||||
int dm_node_i_parent(const dm_node_t *node, dm_node_t *parent);
|
||||
dm_node_id_t dm_node_i_parent_id(const dm_node_id_t id);
|
||||
int dm_node_index_cnt(dm_node_id_t id);
|
||||
enum DM_DATA_TYPE dm_node_data_type(dm_node_id_t id);
|
||||
dm_node_id_t dm_node_get_apply_depends(dm_node_id_t id);
|
||||
dm_node_id_t dm_node_get_extends(dm_node_id_t id);
|
||||
/*
|
||||
Get the full name of a node.
|
||||
@param node[in] Node ID whose name we want to retrieve
|
||||
@param name[out] Location where the node name is written
|
||||
@param name_len[in] Amount of space available in name
|
||||
@return 0 in case of success, -1 in case of failure
|
||||
*/
|
||||
int dm_node2name(const dm_node_t *node, char *name, int name_len);
|
||||
int dm_node2name_with_index(const dm_node_t *node, char *name, int name_len, const char *index_replacement);
|
||||
int dm_name2node(const struct dm_node_info *parent, const char *name, dm_node_t *node);
|
||||
int dm_path2node(const char *path, dm_node_t *node);
|
||||
|
||||
int dm_node_verify_param_data(dm_node_id_t id, const char *data);
|
||||
|
||||
// is parameter ancestor is the ancestor or parent of parameter id
|
||||
int dm_node_is_ancestor(dm_node_id_t id, dm_node_id_t ancestor);
|
||||
// is parameter data type a list (comman separated)
|
||||
int dm_node_is_param_list(dm_node_id_t id);
|
||||
dm_index_t dm_node_last_index(const dm_node_t *node);
|
||||
|
||||
int dm_node_is_index_complete(const dm_node_t *node);
|
||||
|
||||
/** Compare if two nodes are identical.
|
||||
Two nodes are considered identical if their id, their index arrays and the cnt is the same
|
||||
@param node1[in] first node for the comparison
|
||||
@param node2[in] second node for the comparison
|
||||
@return 1 if identical, 0 if not identical
|
||||
*/
|
||||
int dm_node_equal(const dm_node_t *node1, const dm_node_t *node2);
|
||||
int dm_node_has_path(dm_node_id_t node_id, dm_node_id_t path);
|
||||
|
||||
// return 0-(max-1) for valid string, -1 for unknown enum string
|
||||
int dm_node_get_enum_index(dm_node_id_t id, const char *enum_str);
|
||||
// return the enum string for valid index: 0-(max-1), otherwise return NULL
|
||||
const char *dm_node_get_enum_str(dm_node_id_t id, int index);
|
||||
|
||||
/** Remove path name from the path names separated with comma
|
||||
@param paths path names separated with with comma
|
||||
@param node node of path name that is to be removed
|
||||
@return 0 if successful, -1 for failure
|
||||
*/
|
||||
int tr181_paths_remove(dm_path_t paths, const dm_node_t *node);
|
||||
|
||||
/** Append one path name to the path names separated with comma
|
||||
@param paths path names separated with with comma
|
||||
@param node node of path name that is to be appended
|
||||
@return 0 if successful, -1 for failure
|
||||
*/
|
||||
int tr181_paths_add(dm_path_t paths, const dm_node_t *node);
|
||||
|
||||
// Find the "Order" parameter node from all its child nodes
|
||||
int dm_node_find_order_param(const dm_node_t *obj_node, dm_node_t *order_node);
|
||||
|
||||
/** Return the database table for a node
|
||||
@param node The node for which the database table is returned
|
||||
@return pointer to the name of the database table (NULL if the node is not in the database
|
||||
*/
|
||||
const char *dm_node_get_table_name(const struct dm_node_info *node);
|
||||
// Get child node id by its name
|
||||
dm_node_id_t dm_node_get_child_id(dm_node_id_t id, const char *name);
|
||||
// get child node by name
|
||||
int dm_node_get_child(const dm_node_t *node, const char *name, dm_node_t *child);
|
||||
const char *dm_node_str(const dm_node_t *node);
|
||||
const char *dm_node_id_str(const dm_node_id_t id);
|
||||
|
||||
// compare if the nodes are compatible, this is if node1 and node2 have the same id and
|
||||
// all indexes of node1 are a subset of node2 or vice versa
|
||||
int dm_node_compatible(const dm_node_t *node1, const dm_node_t *node2);
|
||||
|
||||
// get the string xsd type of the data type
|
||||
const char *dm_node_get_param_xsd_type(dm_node_id_t id);
|
||||
|
||||
/** Get the output argument type of the command node
|
||||
@param [in] id node id
|
||||
@param [in] arg_name argument name of the command
|
||||
@return pointer of const string if successful, NULL for failure
|
||||
*/
|
||||
const struct command_arg *dm_node_get_command_output_arg(dm_node_id_t id, const char *arg_name);
|
||||
|
||||
// return 1 if verified, otherwise 0.
|
||||
int dm_node_verify_command_input(dm_node_id_t id, const char *input_name, const char *input_value);
|
||||
|
||||
// Compare the command argument pathname with index (index part will be skipped for comparision)
|
||||
// ex, "result.{i}.abc" == "result.100.abc"
|
||||
// return 0 if equal, otherwise 1
|
||||
int dm_node_compare_command_arg_name(const char* str1, const char* str2);
|
||||
#endif
|
||||
97
dm-framework/datamodels/src/dm_types.h
Normal file
97
dm-framework/datamodels/src/dm_types.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DM_TYPES_H
|
||||
#define DM_TYPES_H
|
||||
|
||||
#define MAX_DM_NODE_DEPTH 8
|
||||
#define INVALID_DM_INDEX ((dm_index_t)0)
|
||||
#define INVALID_DM_NODE_ID ((dm_node_id_t)-1)
|
||||
|
||||
typedef unsigned int dm_node_id_t;
|
||||
typedef unsigned int dm_index_t;
|
||||
|
||||
typedef dm_index_t dm_index_path_t[MAX_DM_NODE_DEPTH];
|
||||
typedef struct
|
||||
{
|
||||
dm_index_path_t index;
|
||||
} node_index_path_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
dm_node_id_t id;
|
||||
dm_index_path_t index;
|
||||
int cnt;
|
||||
} dm_node_t;
|
||||
|
||||
#define dm_init_node(id) \
|
||||
{ \
|
||||
id, {0}, 0 \
|
||||
}
|
||||
|
||||
typedef unsigned int dm_uint_t;
|
||||
typedef int dm_int_t;
|
||||
typedef int dm_bool_t;
|
||||
typedef char dm_enum_t[128];
|
||||
typedef char dm_ip_t[64];
|
||||
|
||||
// IPv4 or IPv6 routing prefix in Classless Inter-Domain Routing (CIDR) notation [RFC4632].
|
||||
// This is specified as an IP address followed by an appended "/n" suffix,
|
||||
// where n (the prefix size) is an integer in the range 0-32 (for IPv4) or 0-128 (for IPv6)
|
||||
// that indicates the number of (leftmost) '1' bits of the routing prefix.
|
||||
// If the IP address part is unspecified or inapplicable, it MUST be an empty string unless
|
||||
// otherwise specified by the parameter definition. In this case the IP prefix will be of the form "/n".
|
||||
// IPv4 example: 192.168.1.0/24
|
||||
// IPv6 example: 2001:edff:fe6a:f76::/64
|
||||
typedef char dm_ip_prefix_t[64];
|
||||
|
||||
typedef char dm_mac_t[20]; // 18 would suffice but there can be word access when getting a value; hence rounded up to 20 */
|
||||
typedef char dm_date_time_t[64];
|
||||
typedef char dm_url_t[260];
|
||||
typedef unsigned long dm_ulong_t;
|
||||
typedef unsigned long long dm_ulonglong_t;
|
||||
typedef char dm_path_t[1024];
|
||||
typedef char dm_domain_t[256];
|
||||
|
||||
#define dm_true 1
|
||||
#define dm_false 0
|
||||
|
||||
enum DM_DATA_TYPE {
|
||||
DM_DATA_INT = 0,
|
||||
DM_DATA_LONG,
|
||||
DM_DATA_UINT,
|
||||
DM_DATA_ULONG,
|
||||
DM_DATA_BOOLEAN,
|
||||
DM_DATA_STRING,
|
||||
DM_DATA_HEXBINARY,
|
||||
DM_DATA_BASE64,
|
||||
DM_DATA_IP,
|
||||
DM_DATA_IPV4,
|
||||
DM_DATA_IPV6,
|
||||
DM_DATA_IP_PREFIX,
|
||||
DM_DATA_IPV6_PREFIX,
|
||||
DM_DATA_MAC,
|
||||
DM_DATA_DATETIME,
|
||||
DM_DATA_ENUM,
|
||||
DM_DATA_URL,
|
||||
DM_PATH_NAME,
|
||||
DM_DATA_UNKNOWN
|
||||
};
|
||||
|
||||
enum DM_NODE_TYPE {
|
||||
DM_NODE_PARAMETER = 0,
|
||||
DM_NODE_OBJECT,
|
||||
DM_NODE_OBJECT_LIST,
|
||||
DM_NODE_COMMAND,
|
||||
DM_NODE_EVENT
|
||||
};
|
||||
|
||||
#endif
|
||||
BIN
dm-framework/datamodels/src/scripts/._json2code.js
Executable file
BIN
dm-framework/datamodels/src/scripts/._json2code.js
Executable file
Binary file not shown.
BIN
dm-framework/datamodels/src/scripts/._qjs-handlers-validate.js
Executable file
BIN
dm-framework/datamodels/src/scripts/._qjs-handlers-validate.js
Executable file
Binary file not shown.
1219
dm-framework/datamodels/src/scripts/json2code.js
Executable file
1219
dm-framework/datamodels/src/scripts/json2code.js
Executable file
File diff suppressed because it is too large
Load Diff
508
dm-framework/datamodels/src/scripts/makeDM.js
Normal file
508
dm-framework/datamodels/src/scripts/makeDM.js
Normal file
@@ -0,0 +1,508 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const xml2js = require('xml2js');
|
||||
const util = require('util');
|
||||
|
||||
const readFile = util.promisify(fs.readFile);
|
||||
const writeFile = util.promisify(fs.writeFile);
|
||||
|
||||
const parser = new xml2js.Parser();
|
||||
const parseXML = util.promisify(parser.parseString);
|
||||
|
||||
const cwmpTr181XmlFile = 'tr-181-2-19-1-cwmp-full.xml';
|
||||
const uspTr181XmlFile = 'tr-181-2-19-1-usp-full.xml';
|
||||
const uspTr181VendorExtXmlFile = 'tr-181-vendor-extensions-usp.xml';
|
||||
const Tr104USPXmlFile = 'tr-104-2-0-2-usp-full.xml';
|
||||
const Tr104CWMPXmlFile = 'tr-104-2-0-2-cwmp-full.xml';
|
||||
|
||||
let cwmpModel;
|
||||
let uspModel;
|
||||
let uspVendorExtModel;
|
||||
|
||||
let tr181 = true;
|
||||
|
||||
async function saveFile(file, obj) {
|
||||
await writeFile(file, JSON.stringify(obj, null, 4));
|
||||
console.log('saved file:', file);
|
||||
}
|
||||
|
||||
function getRange(attr) {
|
||||
if (typeof attr[0] !== 'object') {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (attr[0].range) {
|
||||
const range = attr[0].range[0].$;
|
||||
return `(${range.minInclusive ?? ''}:${range.maxInclusive ?? ''})`;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function objParent(obj) {
|
||||
let parent;
|
||||
if (obj.endsWith('}.')) {
|
||||
parent = obj.slice(0, -5);
|
||||
} else {
|
||||
parent = obj.slice(0, -1);
|
||||
}
|
||||
return parent.substring(0, parent.lastIndexOf('.'));
|
||||
}
|
||||
|
||||
function parsePathRef(obj, ref) {
|
||||
let path = obj.$.name;
|
||||
|
||||
ref = ref.trim().replace(/\n$/, '');
|
||||
if (ref.startsWith('.')) {
|
||||
if (path.startsWith('Device.Services.VoiceService.') && !path.endsWith('()')) {
|
||||
return `Device.Services.VoiceService.{i}${ref}`;
|
||||
}
|
||||
return `Device${ref}`;
|
||||
}
|
||||
if (ref.startsWith('#')) {
|
||||
while (ref.startsWith('#')) {
|
||||
path = objParent(path);
|
||||
ref = ref.slice(1);
|
||||
}
|
||||
return path + ref;
|
||||
}
|
||||
if (ref.startsWith('Device.')) {
|
||||
return ref;
|
||||
}
|
||||
return path + ref;
|
||||
}
|
||||
|
||||
function getParamType(obj, res, syntaxType) {
|
||||
const intTypes = [
|
||||
'int',
|
||||
'long',
|
||||
'unsignedInt',
|
||||
'unsignedLong',
|
||||
];
|
||||
|
||||
const intType = intTypes.find((x) => Object.keys(syntaxType).includes(x));
|
||||
if (intType) {
|
||||
res.dataType = `${intType}${getRange(syntaxType[intType])}`;
|
||||
} else if (syntaxType.boolean) {
|
||||
res.dataType = 'boolean';
|
||||
} else if (syntaxType.dateTime) {
|
||||
res.dataType = 'dateTime';
|
||||
} else if (syntaxType.string) {
|
||||
const attr = syntaxType.string[0];
|
||||
if (typeof attr === 'object') {
|
||||
if (attr.enumeration) {
|
||||
res.dataType = 'enum';
|
||||
res.enum = attr.enumeration.map((x) => x.$.value);
|
||||
} else if (attr.pathRef) {
|
||||
res.dataType = 'pathRef';
|
||||
if (attr.pathRef[0].$.targetParent) {
|
||||
res.pathRef = attr.pathRef[0].$.targetParent.split(' ').filter((x) => x).map((x) => parsePathRef(obj, x));
|
||||
}
|
||||
} else if (attr.enumerationRef) {
|
||||
res.dataType = 'enum';
|
||||
res.enumerationRef = parsePathRef(obj, attr.enumerationRef[0].$.targetParam);
|
||||
} else if (attr.size) {
|
||||
res.dataType = `string(${attr.size[0].$?.minLength ?? ''}:${attr.size[0].$?.maxLength ?? ''})`;
|
||||
} else if (attr.pattern) {
|
||||
// handle it as enum
|
||||
res.dataType = 'enum';
|
||||
res.enum = attr.pattern.map((x) => x.$.value);
|
||||
} else {
|
||||
assert(false, `unknown string type: ${JSON.stringify(syntaxType, null, 2)}`);
|
||||
}
|
||||
} else {
|
||||
res.dataType = 'string';
|
||||
}
|
||||
} else if (syntaxType.dataType) {
|
||||
res.dataType = syntaxType.dataType[0].$.ref;
|
||||
if (res.dataType === 'Alias') {
|
||||
res.dataType = 'string(:64)';
|
||||
} else if (res.dataType === 'DiagnosticsState') {
|
||||
res.dataType = 'enum';
|
||||
res.enum = ['None', 'Requested', 'Canceled', 'Complete', 'Error'];
|
||||
} else if (res.dataType === 'StatsCounter64') {
|
||||
res.dataType = 'unsignedLong';
|
||||
}
|
||||
} else if (syntaxType.hexBinary) {
|
||||
res.dataType = 'hexBinary';
|
||||
const { size } = syntaxType.hexBinary[0];
|
||||
if (size) {
|
||||
res.dataType += `(${size[0].$?.minLength ?? ''}:${size[0].$?.maxLength ?? ''})`;
|
||||
}
|
||||
} else if (syntaxType.base64) {
|
||||
res.dataType = 'base64';
|
||||
const { size } = syntaxType.base64[0];
|
||||
if (size) {
|
||||
res.dataType += `(${size[0].$?.minLength ?? ''}:${size[0].$?.maxLength ?? ''})`;
|
||||
}
|
||||
} else if (syntaxType.decimal) {
|
||||
res.dataType = 'decimal';
|
||||
const { size } = syntaxType.decimal[0];
|
||||
if (size) {
|
||||
res.dataType += `(${size[0].$?.minLength ?? ''}:${size[0].$?.maxLength ?? ''})`;
|
||||
}
|
||||
} else {
|
||||
console.log(`unknown datatype:\n ${JSON.stringify(syntaxType, null, 4)}`);
|
||||
res.dataType = 'unknown';
|
||||
}
|
||||
|
||||
if (syntaxType.list) {
|
||||
res.dataType += '[]';
|
||||
}
|
||||
}
|
||||
|
||||
function getParamObj(obj, param, proto) {
|
||||
const res = {
|
||||
name: param.$.name,
|
||||
};
|
||||
|
||||
if (proto) {
|
||||
res.proto = proto;
|
||||
}
|
||||
|
||||
res.access = param.$.access;
|
||||
const syntaxType = param.syntax[0];
|
||||
|
||||
getParamType(obj, res, syntaxType);
|
||||
|
||||
if (syntaxType.$?.hidden) {
|
||||
res.hidden = true;
|
||||
}
|
||||
|
||||
if (syntaxType.default && syntaxType.default[0].$?.value) {
|
||||
const def = syntaxType.default[0].$?.value;
|
||||
if (def !== 'false' && def !== '') {
|
||||
res.default = syntaxType.default[0].$?.value;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
let allObjects = [];
|
||||
|
||||
function generateCWMPObjects(objs) {
|
||||
objs.forEach((obj) => {
|
||||
const o = allObjects.find((x) => x.object === obj.$.name);
|
||||
if (o) {
|
||||
const params = obj.parameter.map((param) => getParamObj(obj, param, 'cwmp'));
|
||||
o.parameters = o.parameters.concat(params);
|
||||
} else {
|
||||
allObjects.push({
|
||||
object: obj.$.name,
|
||||
proto: 'cwmp',
|
||||
fixedObject: obj.$['dmr:fixedObject'],
|
||||
uniqueKeys: obj.uniqueKey?.map((x) => x.parameter[0].$.ref).join(','),
|
||||
// numEntriesParameter: obj.$.numEntriesParameter,
|
||||
access: obj.$.access,
|
||||
parameters: obj.parameter?.map((param) => (
|
||||
getParamObj(obj, param, 'cwmp'))) ?? [],
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getCommandInput(cmdInfo) {
|
||||
if (!cmdInfo.input) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const objParams = [];
|
||||
if (cmdInfo.input[0].object) {
|
||||
cmdInfo.input[0].object.forEach((obj) => {
|
||||
obj.parameter.forEach((p) => {
|
||||
const inputParams = {
|
||||
parameter: obj.$.name + p.$.name,
|
||||
mandatory: p.$.mandatory === 'true',
|
||||
};
|
||||
getParamType(cmdInfo, inputParams, p.syntax[0]);
|
||||
objParams.push(inputParams);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const params = cmdInfo.input[0].parameter?.map((p) => {
|
||||
const inputParams = {
|
||||
parameter: p.$.name,
|
||||
mandatory: p.$.mandatory === 'true',
|
||||
};
|
||||
getParamType(cmdInfo, inputParams, p.syntax[0]);
|
||||
return inputParams;
|
||||
});
|
||||
|
||||
return objParams.concat(params ?? []);
|
||||
}
|
||||
|
||||
function getCommandOutput(cmdInfo) {
|
||||
if (!cmdInfo.output) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const outParams = cmdInfo.output[0].parameter?.map((p) => {
|
||||
const outputs = {
|
||||
parameter: p.$.name,
|
||||
};
|
||||
getParamType(cmdInfo, outputs, p.syntax[0]);
|
||||
return outputs;
|
||||
}) ?? [];
|
||||
|
||||
const outObjs = cmdInfo.output[0].object?.map((obj) => {
|
||||
const outputs = {
|
||||
object: obj.$.name,
|
||||
};
|
||||
outputs.parameters = obj.parameter?.map((p) => {
|
||||
const outs = {
|
||||
parameter: p.$.name,
|
||||
};
|
||||
getParamType(cmdInfo, outs, p.syntax[0]);
|
||||
return outs;
|
||||
});
|
||||
return outputs;
|
||||
}) ?? [];
|
||||
|
||||
return outParams.concat(outObjs);
|
||||
}
|
||||
|
||||
function generateUSPObjects(objs) {
|
||||
objs.forEach((obj) => {
|
||||
const o = allObjects.find((x) => x.object === obj.$.name);
|
||||
if (o) {
|
||||
delete o.proto;
|
||||
obj.parameter?.forEach((p) => {
|
||||
const param = o.parameters.find((x) => x.name === p.$.name);
|
||||
if (param) {
|
||||
delete param.proto;
|
||||
} else {
|
||||
o.parameters.push(getParamObj(obj, p, 'usp'));
|
||||
}
|
||||
});
|
||||
|
||||
if (obj.command) {
|
||||
const cmds = obj.command.map((cmd) => ({
|
||||
name: cmd.$.name,
|
||||
async: !!cmd.$.async,
|
||||
input: getCommandInput(cmd),
|
||||
output: getCommandOutput(cmd),
|
||||
}));
|
||||
|
||||
if (o.commands) {
|
||||
o.commands = o.commands.concat(cmds);
|
||||
} else {
|
||||
o.commands = cmds;
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.event) {
|
||||
const events = obj.event.map((ev) => ({
|
||||
name: ev.$.name,
|
||||
parameter: ev.parameter?.map((p) => p.$.name),
|
||||
}));
|
||||
|
||||
if (o.events) {
|
||||
o.events = o.events.concat(events);
|
||||
} else {
|
||||
o.events = events;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const cwmpObj = cwmpModel.object.find((x) => x.$.name === obj.$.name);
|
||||
const newObj = {
|
||||
object: obj.$.name,
|
||||
proto: cwmpObj ? undefined : 'usp',
|
||||
uniqueKeys: obj.uniqueKey?.map((x) => x.parameter[0].$.ref).join(','),
|
||||
// numEntriesParameter: obj.$.numEntriesParameter,
|
||||
access: obj.$.access,
|
||||
fixedObject: obj.$['dmr:fixedObject'],
|
||||
parameters: obj.parameter?.map((param) => (
|
||||
getParamObj(obj, param, (cwmpObj && cwmpObj.parameter?.find((x) => x.$.name === param.$.name)) ? undefined : 'usp'))) ?? [],
|
||||
};
|
||||
|
||||
if (obj.command) {
|
||||
newObj.commands = obj.command.map((cmd) => ({
|
||||
name: cmd.$.name,
|
||||
async: !!cmd.$.async,
|
||||
input: getCommandInput(cmd),
|
||||
output: getCommandOutput(cmd),
|
||||
}));
|
||||
}
|
||||
|
||||
if (obj.event) {
|
||||
newObj.events = obj.event.map((ev) => ({
|
||||
name: ev.$.name,
|
||||
parameter: ev.parameter?.map((p) => p.$.name),
|
||||
}));
|
||||
}
|
||||
allObjects.push(newObj);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function mergeProfileObjs(obj1, obj2) {
|
||||
if (!obj1) {
|
||||
return obj2;
|
||||
}
|
||||
|
||||
if (!obj2) {
|
||||
return obj1;
|
||||
}
|
||||
|
||||
obj2.forEach((obj) => {
|
||||
const o = obj1.find((x) => x.object === obj.object);
|
||||
if (o) {
|
||||
if (o.parameter) {
|
||||
o.parameter = o.parameter.concat(obj.parameter ?? []);
|
||||
} else {
|
||||
o.parameter = obj.parameter;
|
||||
}
|
||||
|
||||
if (o.command) {
|
||||
o.command = o.command.concat(obj.command ?? []);
|
||||
} else {
|
||||
o.command = obj.command;
|
||||
}
|
||||
|
||||
if (o.event) {
|
||||
o.event = o.event.concat(obj.event ?? []);
|
||||
} else {
|
||||
o.event = obj.event;
|
||||
}
|
||||
} else {
|
||||
obj1.push(obj);
|
||||
}
|
||||
});
|
||||
|
||||
return obj1;
|
||||
}
|
||||
|
||||
function parseProfileObjects(model, profileName) {
|
||||
const profile = model.profile.find((x) => x.$.name === profileName);
|
||||
if (!profile) {
|
||||
return [];
|
||||
}
|
||||
// assert(profile, `profile not found ${profileName}`);
|
||||
|
||||
const objs = profile.object?.map((o) => ({
|
||||
object: o.$.ref,
|
||||
parameter: o.parameter?.map((p) => p.$.ref),
|
||||
command: o.command?.map((c) => c.$.ref),
|
||||
event: o.event?.map((e) => e.$.ref),
|
||||
}));
|
||||
|
||||
const exts = profile.$.extends ?? profile.$.base;
|
||||
if (exts) {
|
||||
let res = objs;
|
||||
exts.split(' ').forEach((ext) => {
|
||||
const extObjs = parseProfileObjects(model, ext);
|
||||
res = mergeProfileObjs(res, extObjs);
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
return objs;
|
||||
}
|
||||
|
||||
async function getProfileObjects(model, profileName) {
|
||||
const profileObjs = parseProfileObjects(model, profileName);
|
||||
const objs = [];
|
||||
|
||||
profileObjs.forEach((obj) => {
|
||||
const targetObj = model.object.find((o) => o.$.name === obj.object);
|
||||
assert(targetObj, `object not found ${obj.object}`);
|
||||
const keys = targetObj.uniqueKey?.map((x) => x.parameter[0].$.ref);
|
||||
targetObj.parameter = targetObj.parameter?.filter((x) => obj.parameter?.includes(x.$.name) || (keys?.includes(x.$.name)));
|
||||
targetObj.command = targetObj.command?.filter((x) => obj.command?.includes(x.$.name));
|
||||
objs.push(targetObj);
|
||||
});
|
||||
|
||||
return objs;
|
||||
}
|
||||
|
||||
async function loadXMLModel(file) {
|
||||
const xmlData = await readFile(file, 'utf8');
|
||||
const jsonData = await parseXML(xmlData);
|
||||
const [model] = jsonData['dm:document'].model;
|
||||
return model;
|
||||
}
|
||||
|
||||
function printUsage() {
|
||||
console.log('Usage:\nnode makeDM.js <tr181|tr104> [profile]');
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
if (process.argv.length < 3 || (process.argv[2] !== 'tr181' && process.argv[2] !== 'tr104')) {
|
||||
printUsage();
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
if (process.argv[2] === 'tr104') {
|
||||
tr181 = false;
|
||||
}
|
||||
|
||||
if (tr181 && fs.existsSync(uspTr181VendorExtXmlFile)) {
|
||||
uspVendorExtModel = await loadXMLModel(uspTr181VendorExtXmlFile);
|
||||
} else if (tr181) {
|
||||
console.warn(`Optional vendor extension file '${uspTr181VendorExtXmlFile}' not found, skipping.`);
|
||||
}
|
||||
if (tr181) {
|
||||
cwmpModel = await loadXMLModel(cwmpTr181XmlFile);
|
||||
uspModel = await loadXMLModel(uspTr181XmlFile);
|
||||
} else {
|
||||
cwmpModel = await loadXMLModel(Tr104CWMPXmlFile);
|
||||
uspModel = await loadXMLModel(Tr104USPXmlFile);
|
||||
cwmpModel.object.forEach((obj) => {
|
||||
obj.$.name = `Device.Services.${obj.$.name}`;
|
||||
});
|
||||
cwmpModel.profile.forEach((prof) => {
|
||||
prof.object?.forEach((obj) => {
|
||||
obj.$.ref = `Device.Services.${obj.$.ref}`;
|
||||
});
|
||||
});
|
||||
uspModel.object.forEach((obj) => {
|
||||
obj.$.name = `Device.Services.${obj.$.name}`;
|
||||
});
|
||||
uspModel.profile.forEach((prof) => {
|
||||
prof.object?.forEach((obj) => {
|
||||
obj.$.ref = `Device.Services.${obj.$.ref}`;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (process.argv.length === 3) {
|
||||
generateCWMPObjects(cwmpModel.object);
|
||||
generateUSPObjects(uspModel.object);
|
||||
if (tr181 && uspVendorExtModel) {
|
||||
generateUSPObjects(uspVendorExtModel.object);
|
||||
}
|
||||
const fileName = `${tr181 ? 'tr181' : 'tr104'}-full-objects.json`;
|
||||
await saveFile(fileName, allObjects);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
for (const arg of process.argv.slice(3)) {
|
||||
// profile
|
||||
const profile = arg;
|
||||
const cwmpObjects = await getProfileObjects(cwmpModel, profile);
|
||||
const uspObjects = await getProfileObjects(uspModel, profile);
|
||||
generateCWMPObjects(cwmpObjects);
|
||||
generateUSPObjects(uspObjects);
|
||||
await saveFile(`${profile}.json`, allObjects);
|
||||
allObjects = [];
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error while reading file: ${error}`);
|
||||
console.log(error.stack);
|
||||
}
|
||||
})();
|
||||
88
dm-framework/datamodels/src/scripts/qjs-handlers-validate.js
Normal file
88
dm-framework/datamodels/src/scripts/qjs-handlers-validate.js
Normal file
@@ -0,0 +1,88 @@
|
||||
// This script is used to load and validate the js handlers code in dm-file.
|
||||
(function () {
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { spawnSync } = require('child_process');
|
||||
|
||||
// Root directory (dm-files) relative to this script
|
||||
const dmFilesRoot = path.resolve(__dirname, '../dm-files');
|
||||
|
||||
/**
|
||||
* Recursively walk a directory and collect all *.js files that do not start with a dot.
|
||||
* @param {string} dir - directory to walk
|
||||
* @param {string[]} out - accumulator for file paths
|
||||
*/
|
||||
function collectJsFiles(dir, out) {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
// Skip hidden files/directories (starting with ".")
|
||||
if (entry.name.startsWith('.')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
collectJsFiles(fullPath, out);
|
||||
} else if (entry.isFile() && fullPath.endsWith('.js')) {
|
||||
out.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a single JavaScript file with QuickJS (qjs).
|
||||
* Exits the process with -1 on failure.
|
||||
* @param {string} filePath - absolute path of the JS file
|
||||
*/
|
||||
function validateWithQjs(filePath) {
|
||||
// Extract directory and filename for proper working directory
|
||||
const fileDir = path.dirname(filePath);
|
||||
const fileName = path.basename(filePath);
|
||||
|
||||
// Capture stdout/stderr so we can print them on failure
|
||||
// Set the working directory to the file's directory
|
||||
const result = spawnSync('qjs', [fileName], {
|
||||
encoding: 'utf8',
|
||||
cwd: fileDir
|
||||
});
|
||||
|
||||
if (result.status === 0) {
|
||||
return; // Validated successfully
|
||||
}
|
||||
|
||||
// Show QuickJS output so user sees error details
|
||||
console.error(`\n===== QuickJS validation failed: ${filePath} =====`);
|
||||
if (result.stdout) {
|
||||
console.error(result.stdout.trim());
|
||||
}
|
||||
if (result.stderr) {
|
||||
console.error(result.stderr.trim());
|
||||
}
|
||||
console.error('===============================================');
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (!fs.existsSync(dmFilesRoot)) {
|
||||
console.error(`dm-files directory not found at: ${dmFilesRoot}`);
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
const jsFiles = [];
|
||||
collectJsFiles(dmFilesRoot, jsFiles);
|
||||
|
||||
if (jsFiles.length === 0) {
|
||||
console.log('No JavaScript files found to validate.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Validating ${jsFiles.length} JavaScript file(s) with QuickJS...`);
|
||||
jsFiles.forEach(validateWithQjs);
|
||||
console.log('All files validated successfully.');
|
||||
}
|
||||
|
||||
// Execute when run directly (not required when imported)
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
})();
|
||||
128822
dm-framework/datamodels/src/scripts/tr-181-2-19-1-cwmp-full.xml
Executable file
128822
dm-framework/datamodels/src/scripts/tr-181-2-19-1-cwmp-full.xml
Executable file
File diff suppressed because it is too large
Load Diff
138669
dm-framework/datamodels/src/scripts/tr-181-2-19-1-usp-full.xml
Executable file
138669
dm-framework/datamodels/src/scripts/tr-181-2-19-1-usp-full.xml
Executable file
File diff suppressed because it is too large
Load Diff
57941
dm-framework/datamodels/src/scripts/tr181-full-objects.json
Normal file
57941
dm-framework/datamodels/src/scripts/tr181-full-objects.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
dm-framework/dm-agent/._Makefile
Executable file
BIN
dm-framework/dm-agent/._Makefile
Executable file
Binary file not shown.
BIN
dm-framework/dm-agent/._bbfdm_service.json
Executable file
BIN
dm-framework/dm-agent/._bbfdm_service.json
Executable file
Binary file not shown.
64
dm-framework/dm-agent/Makefile
Normal file
64
dm-framework/dm-agent/Makefile
Normal file
@@ -0,0 +1,64 @@
|
||||
#
|
||||
# Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
# This Software and its content are protected by the Dutch Copyright Act
|
||||
# ('Auteurswet'). All and any copying and distribution of the software
|
||||
# and its content without authorization by Genexis B.V. is
|
||||
# prohibited. The prohibition includes every form of reproduction and
|
||||
# distribution.
|
||||
#
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=bridgemngr
|
||||
PKG_VERSION:=1.0.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||
PLATFORM_CONFIG:=$(TOPDIR)/.config
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
include ../../bbfdm/bbfdm.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
DEPENDS:=+dm-api +datamodels +libubox +libubus +ubus
|
||||
CATEGORY:=Genexis
|
||||
TITLE:=GeneOS agent
|
||||
URL:=http://www.genexis.eu
|
||||
PKG_LICENSE:=GENEXIS
|
||||
PKG_LICENSE_URL:=
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
This package contains GeneOS agent.
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
mkdir -p $(PKG_BUILD_DIR)
|
||||
$(CP) -rf ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += $(FPIC) -I$(PKG_BUILD_DIR)
|
||||
|
||||
define Build/Compile
|
||||
$(MAKE) -C $(PKG_BUILD_DIR)\
|
||||
PROJECT_ROOT="$(PKG_BUILD_DIR)" \
|
||||
CROSS_COMPILE="$(TARGET_CROSS)" \
|
||||
ARCH="$(LINUX_KARCH)" \
|
||||
EXTRA_CFLAGS="$(TARGET_CFLAGS)" \
|
||||
all
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/sbin
|
||||
$(INSTALL_DIR) $(1)/etc/init.d
|
||||
$(INSTALL_DIR) $(1)/etc/config
|
||||
$(INSTALL_BIN) ./files/etc/init.d/bridging $(1)/etc/init.d/
|
||||
$(INSTALL_DATA) ./files/etc/config/bridging $(1)/etc/config/
|
||||
$(BBFDM_REGISTER_SERVICES) ./bbfdm_service.json $(1) $(PKG_NAME)
|
||||
$(INSTALL_DIR) $(1)/lib/upgrade/keep.d
|
||||
# $(INSTALL_BIN) ./files/etc/init.d/dm-agent $(1)/etc/init.d/dm-agent
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/dm-agent $(1)/usr/sbin
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
@@ -2,6 +2,7 @@
|
||||
"daemon": {
|
||||
"enable": "1",
|
||||
"service_name": "bridgemngr",
|
||||
"dm-framework": true,
|
||||
"unified_daemon": false,
|
||||
"services": [
|
||||
{
|
||||
@@ -77,6 +77,7 @@ handle_ebtables_rule() {
|
||||
}
|
||||
|
||||
start_service() {
|
||||
ubus -t 30 wait_for network.device uci
|
||||
config_load bridging
|
||||
config_foreach handle_ebtables_chain chain
|
||||
config_foreach handle_ebtables_rule rule
|
||||
BIN
dm-framework/dm-agent/src/._dm_agent.c
Executable file
BIN
dm-framework/dm-agent/src/._dm_agent.c
Executable file
Binary file not shown.
37
dm-framework/dm-agent/src/Makefile
Normal file
37
dm-framework/dm-agent/src/Makefile
Normal file
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
# This Software and its content are protected by the Dutch Copyright Act
|
||||
# ('Auteurswet'). All and any copying and distribution of the software
|
||||
# and its content without authorization by Genexis B.V. is
|
||||
# prohibited. The prohibition includes every form of reproduction and
|
||||
# distribution.
|
||||
#
|
||||
#
|
||||
|
||||
PROG = dm-agent
|
||||
|
||||
SRCS = dm_agent.c
|
||||
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
DEPS = $(SRCS:.c=.d)
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
STRIP = $(CROSS_COMPILE)strip
|
||||
|
||||
CFLAGS = -Wall -Werror $(EXTRA_CFLAGS)
|
||||
CFLAGS += -MMD -MP -std=gnu99
|
||||
|
||||
LDFLAGS += -ldm -ldmapi -lubus -luci -lubox -ljson-c -lblobmsg_json
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(OBJS)
|
||||
$(CC) $^ $(LDFLAGS) -o $@
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) $^ -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(PROG) *.o core $(DEPS)
|
||||
|
||||
-include $(DEPS)
|
||||
1683
dm-framework/dm-agent/src/dm_agent.c
Normal file
1683
dm-framework/dm-agent/src/dm_agent.c
Normal file
File diff suppressed because it is too large
Load Diff
75
dm-framework/dm-api/Makefile
Normal file
75
dm-framework/dm-api/Makefile
Normal file
@@ -0,0 +1,75 @@
|
||||
#
|
||||
# Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
# This Software and its content are protected by the Dutch Copyright Act
|
||||
# ('Auteurswet'). All and any copying and distribution of the software
|
||||
# and its content without authorization by Genexis B.V. is
|
||||
# prohibited. The prohibition includes every form of reproduction and
|
||||
# distribution.
|
||||
#
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=dm-api
|
||||
PKG_VERSION:=1.0
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)
|
||||
PLATFORM_CONFIG:=$(TOPDIR)/.config
|
||||
AUTO_CONF_H:=$(PKG_BUILD_DIR)/autoconf.h
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
CATEGORY:=Genexis
|
||||
TITLE:=dm-api
|
||||
PKG_BUILD_DEPENDS:=datamodels
|
||||
DEPENDS:=+libsqlite3 \
|
||||
+libjson-c +libstdcpp +quickjs \
|
||||
+libubus +libubox +libuci
|
||||
|
||||
# Depedencies for RG products
|
||||
URL:=http://www.genexis.eu
|
||||
PKG_LICENSE:=GENEXIS
|
||||
PKG_LICENSE_URL:=
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
This package contains api for the dm-framework
|
||||
endef
|
||||
|
||||
define Build/Prepare
|
||||
$(CP) -rf ./src/* $(PKG_BUILD_DIR)/
|
||||
endef
|
||||
|
||||
TARGET_CFLAGS += $(FPIC) -I$(PKG_BUILD_DIR)
|
||||
|
||||
define Build/Compile
|
||||
$(MAKE) -C $(PKG_BUILD_DIR)\
|
||||
PROJECT_ROOT="$(PKG_BUILD_DIR)" \
|
||||
CROSS_COMPILE="$(TARGET_CROSS)" \
|
||||
ARCH="$(LINUX_KARCH)" \
|
||||
EXTRA_CFLAGS="$(TARGET_CFLAGS)" \
|
||||
all
|
||||
endef
|
||||
|
||||
define Build/InstallDev
|
||||
$(INSTALL_DIR) $(1)/usr/include
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
|
||||
$(CP) $(PKG_BUILD_DIR)/core/dm_api.h $(1)/usr/include/
|
||||
$(CP) $(PKG_BUILD_DIR)/core/dm_linker.h $(1)/usr/include/
|
||||
$(CP) $(PKG_BUILD_DIR)/core/dbmgr.h $(1)/usr/include/
|
||||
$(CP) $(PKG_BUILD_DIR)/include/dm_log.h $(1)/usr/include/
|
||||
$(CP) $(PKG_BUILD_DIR)/utils/dm_list.h $(1)/usr/include/
|
||||
$(CP) $(PKG_BUILD_DIR)/libdmapi.so $(1)/usr/lib/
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib
|
||||
$(INSTALL_DIR) $(1)/sbin/
|
||||
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/libdmapi.so $(1)/usr/lib/
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
64
dm-framework/dm-api/src/Makefile
Normal file
64
dm-framework/dm-api/src/Makefile
Normal file
@@ -0,0 +1,64 @@
|
||||
#
|
||||
# Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
# This Software and its content are protected by the Dutch Copyright Act
|
||||
# ('Auteurswet'). All and any copying and distribution of the software
|
||||
# and its content without authorization by Genexis B.V. is
|
||||
# prohibited. The prohibition includes every form of reproduction and
|
||||
# distribution.
|
||||
#
|
||||
#
|
||||
|
||||
PROG = libdmapi.so
|
||||
|
||||
SRCS = \
|
||||
core/dm_api.c \
|
||||
core/dm_linker.c \
|
||||
core/dbmgr.c \
|
||||
core/inode_buf.c \
|
||||
core/dm_apply.c \
|
||||
core/dm_import.c \
|
||||
core/db_upgrade.c \
|
||||
utils/dm_list.c \
|
||||
utils/dm_log.c \
|
||||
utils/ubus_client.c \
|
||||
utils/utils.c \
|
||||
quickjs/qjs.c \
|
||||
quickjs/qjs_log.c \
|
||||
quickjs/qjs_dm_api.c \
|
||||
quickjs/qjs_uci_api.c \
|
||||
quickjs/qjs_ubus_api.c
|
||||
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
DEPS = $(SRCS:.c=.d)
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
STRIP = $(CROSS_COMPILE)strip
|
||||
CFLAGS += \
|
||||
-I core \
|
||||
-I $(STAGING_DIR)/usr/include $(EXTRA_CFLAGS) \
|
||||
-I handlers/common \
|
||||
-I handlers/tr181 \
|
||||
-I include \
|
||||
-I utils \
|
||||
-I quickjs
|
||||
|
||||
CFLAGS += -MMD -MP -std=gnu99
|
||||
|
||||
LDFLAGS = -shared
|
||||
CFLAGS += -Wall -Werror -fpic
|
||||
|
||||
LDFLAGS += -lquickjs -lsqlite3 -latomic
|
||||
# END for src from mgmt-agent
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(OBJS)
|
||||
$(CC) $^ $(LDFLAGS) -o $@
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) $^ -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(PROG) *.o core $(DEPS)
|
||||
|
||||
-include $(DEPS)
|
||||
BIN
dm-framework/dm-api/src/core/._dm_api.c
Executable file
BIN
dm-framework/dm-api/src/core/._dm_api.c
Executable file
Binary file not shown.
BIN
dm-framework/dm-api/src/core/._dm_api.h
Executable file
BIN
dm-framework/dm-api/src/core/._dm_api.h
Executable file
Binary file not shown.
BIN
dm-framework/dm-api/src/core/._dm_import.c
Executable file
BIN
dm-framework/dm-api/src/core/._dm_import.c
Executable file
Binary file not shown.
BIN
dm-framework/dm-api/src/core/._dm_linker.c
Executable file
BIN
dm-framework/dm-api/src/core/._dm_linker.c
Executable file
Binary file not shown.
BIN
dm-framework/dm-api/src/core/._dm_linker.h
Executable file
BIN
dm-framework/dm-api/src/core/._dm_linker.h
Executable file
Binary file not shown.
2053
dm-framework/dm-api/src/core/db_upgrade.c
Normal file
2053
dm-framework/dm-api/src/core/db_upgrade.c
Normal file
File diff suppressed because it is too large
Load Diff
17
dm-framework/dm-api/src/core/db_upgrade.h
Normal file
17
dm-framework/dm-api/src/core/db_upgrade.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DB_UPGRADE_H
|
||||
#define DB_UPGRADE_H
|
||||
|
||||
int get_db_user_version(const char *db_path, int *user_version);
|
||||
int upgrade_db(const char *curr_db, const char *new_db, int new_version);
|
||||
#endif
|
||||
684
dm-framework/dm-api/src/core/dbmgr.c
Normal file
684
dm-framework/dm-api/src/core/dbmgr.c
Normal file
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dbmgr.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dm.h"
|
||||
#include "dm_log.h"
|
||||
#include "dm_node.h"
|
||||
#include "sqlite3.h"
|
||||
|
||||
#define MAX_SQL_STATEMENT_LEN 4096
|
||||
static sqlite3 *transaction_db = NULL;
|
||||
static char sql_statement[MAX_SQL_STATEMENT_LEN];
|
||||
|
||||
// sqlite busy handler: the SQLITE_BUSY could happen when one process trys to access db
|
||||
// (read out of session in our case) while another process is in the process of commiting
|
||||
// a transaction. the busy time is normally short within 100 ms.
|
||||
static int db_busy_handler(void *context, int count)
|
||||
{
|
||||
dmlog_debug("db_busy_handler %s, %d", sql_statement, count);
|
||||
// non-zero means retry..
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char *_get_node_rel_path_name(const struct dm_node_info *head,
|
||||
const struct dm_node_info *node)
|
||||
{
|
||||
static char _path_name[512] = {'\0'};
|
||||
|
||||
if (node != NULL && node != head) {
|
||||
_get_node_rel_path_name(head, node->parent);
|
||||
}
|
||||
|
||||
if ((node == head) || (node == NULL)) /* end of recursion make sure pathname is the empty string */
|
||||
{
|
||||
memset(_path_name, 0, sizeof(_path_name));
|
||||
} else {
|
||||
strcat(_path_name, node->name);
|
||||
strcat(_path_name, "_");
|
||||
}
|
||||
|
||||
return _path_name;
|
||||
}
|
||||
|
||||
static const char *get_node_rel_path_name(const struct dm_node_info *head,
|
||||
const struct dm_node_info *node)
|
||||
{
|
||||
char *name = _get_node_rel_path_name(head, node);
|
||||
int len = strlen(name);
|
||||
// remove last '_'
|
||||
if (len > 0) {
|
||||
name[len - 1] = '\0';
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
static const char *get_table_field_name(const struct dm_node_info *node)
|
||||
{
|
||||
const struct dm_node_info *p = node;
|
||||
|
||||
while (p->parent != dm_node_get_root()) {
|
||||
if (p->type == DM_NODE_OBJECT_LIST) {
|
||||
break;
|
||||
}
|
||||
p = p->parent;
|
||||
}
|
||||
|
||||
return get_node_rel_path_name(p, node);
|
||||
}
|
||||
|
||||
static const char *get_int_str(int i)
|
||||
{
|
||||
static char digit[16];
|
||||
|
||||
sprintf(digit, "%d", i);
|
||||
return digit;
|
||||
}
|
||||
|
||||
static void get_db_index_condition(const struct dm_node_info *info, char *buf, const dm_index_t indexes[], int *cnt)
|
||||
{
|
||||
if (info == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (info != dm_node_get_root()) {
|
||||
get_db_index_condition(info->parent, buf, indexes, cnt);
|
||||
}
|
||||
|
||||
if (info->type == DM_NODE_OBJECT_LIST && indexes[*cnt] > 0) {
|
||||
if (*cnt > 0) {
|
||||
strcat(buf, " AND ");
|
||||
}
|
||||
strcat(buf, "\"IndexOf");
|
||||
strcat(buf, info->name);
|
||||
strcat(buf, "\"");
|
||||
|
||||
strcat(buf, "=");
|
||||
strcat(buf, get_int_str(indexes[*cnt]));
|
||||
(*cnt)++;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_db_index_field(const struct dm_node_info *info, char *buf, int *cnt)
|
||||
{
|
||||
if (info == NULL)
|
||||
return;
|
||||
|
||||
if (info != dm_node_get_root())
|
||||
get_db_index_field(info->parent, buf, cnt);
|
||||
|
||||
if (info->type == DM_NODE_OBJECT_LIST) {
|
||||
if (*cnt > 0) {
|
||||
strcat(buf, ",");
|
||||
}
|
||||
strcat(buf, "\"IndexOf");
|
||||
strcat(buf, info->name);
|
||||
strcat(buf, "\"");
|
||||
(*cnt)++;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *get_query_sql(const dm_node_t *node, int max_in_list)
|
||||
{
|
||||
const struct dm_node_info *info = dm_node_get_info(node->id);
|
||||
|
||||
if (info == NULL) {
|
||||
dmlog_error("get_query_sql, node id not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(sql_statement, "select ");
|
||||
if (dm_node_is_parameter(node->id)) {
|
||||
if (max_in_list)
|
||||
strcat(sql_statement, "MAX(");
|
||||
strcat(sql_statement, "\"");
|
||||
strcat(sql_statement, get_table_field_name(info));
|
||||
strcat(sql_statement, "\"");
|
||||
if (max_in_list)
|
||||
strcat(sql_statement, ")");
|
||||
} else if (dm_node_is_objectlist(node->id)) {
|
||||
const char *node_name = dm_node_name(node->id);
|
||||
if (node_name == NULL)
|
||||
return NULL;
|
||||
|
||||
strcat(sql_statement, "\"IndexOf");
|
||||
strcat(sql_statement, node_name);
|
||||
strcat(sql_statement, "\"");
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcat(sql_statement, " FROM ");
|
||||
|
||||
strcat(sql_statement, dm_node_get_table_name(info));
|
||||
|
||||
if (node->cnt > 0) {
|
||||
strcat(sql_statement, " WHERE ");
|
||||
int cnt = 0;
|
||||
|
||||
if (max_in_list) {
|
||||
// start from parant of the object.
|
||||
info = dm_node_get_info(dm_node_id_parent(dm_node_id_parent(node->id)));
|
||||
}
|
||||
get_db_index_condition(info, sql_statement, node->index, &cnt);
|
||||
}
|
||||
|
||||
strcat(sql_statement, ";");
|
||||
return sql_statement;
|
||||
}
|
||||
|
||||
static const char *get_query_all_indexes_sql(const dm_node_t *node, const char *keys, int sort)
|
||||
{
|
||||
const struct dm_node_info *info = dm_node_get_info(node->id);
|
||||
|
||||
if (info == NULL) {
|
||||
dmlog_error("get_query_all_indexes_sql, node id not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (dm_node_is_counter(node->id)) {
|
||||
dm_node_id_t counter = dm_node_counter_id(node->id);
|
||||
|
||||
if (counter == INVALID_DM_NODE_ID) {
|
||||
dmlog_error("invalid counter id %d", counter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
info = dm_node_get_info(counter);
|
||||
if (info == NULL) {
|
||||
dmlog_error("info is NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(sql_statement, "select COUNT(\"IndexOf");
|
||||
strcat(sql_statement, info->name);
|
||||
strcat(sql_statement, "\")");
|
||||
} else {
|
||||
strcpy(sql_statement, "select");
|
||||
strcat(sql_statement, " IndexOf");
|
||||
strcat(sql_statement, info->name);
|
||||
|
||||
int index_cnt = dm_node_index_cnt(node->id);
|
||||
if (index_cnt > 1 && node->cnt < index_cnt - 1) {
|
||||
const struct dm_node_info *pinfo = dm_node_get_info(dm_node_i_parent_id(node->id));
|
||||
if (pinfo) {
|
||||
strcat(sql_statement, ",IndexOf");
|
||||
strcat(sql_statement, pinfo->name);
|
||||
} else {
|
||||
dmlog_error("get_query_all_indexes_sql, invalid node: %s", dm_node_str(node));
|
||||
}
|
||||
}
|
||||
struct dm_object *obj = (struct dm_object *)info;
|
||||
if (obj->key_param_names != NULL) {
|
||||
strcat(sql_statement, ",_key");
|
||||
char *tmp = strdup(obj->key_param_names);
|
||||
char *key = strtok(tmp, ",");
|
||||
while (key != NULL) {
|
||||
strcat(sql_statement, ",\"");
|
||||
strcat(sql_statement, key);
|
||||
strcat(sql_statement, "\"");
|
||||
key = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
strcat(sql_statement, " FROM ");
|
||||
strcat(sql_statement, dm_node_get_table_name(info));
|
||||
|
||||
if (dm_node_index_cnt(info->parent->node_id) > 0 && node->cnt > 0) {
|
||||
strcat(sql_statement, " WHERE ");
|
||||
int cnt = 0;
|
||||
|
||||
get_db_index_condition(info->parent, sql_statement, node->index, &cnt);
|
||||
if (keys != NULL && keys[0] != '\0') {
|
||||
strcat(sql_statement, " AND ");
|
||||
}
|
||||
} else if (keys != NULL && keys[0] != '\0') {
|
||||
strcat(sql_statement, " WHERE ");
|
||||
}
|
||||
|
||||
if (keys != NULL && keys[0] != '\0') {
|
||||
strcat(sql_statement, keys);
|
||||
}
|
||||
|
||||
if (sort && info->flag & FLAG_HAS_ORDER)
|
||||
strcat(sql_statement, " ORDER BY \"Order\"");
|
||||
|
||||
strcat(sql_statement, ";");
|
||||
return sql_statement;
|
||||
}
|
||||
|
||||
// The SQL standard specifies that single-quotes in strings are escaped
|
||||
// by putting two single quotes in a row.
|
||||
static void strcat_str_data(char *dest, const char *data)
|
||||
{
|
||||
int len = strlen(dest);
|
||||
|
||||
for (; *data != '\0'; data++, len++) {
|
||||
if (*data == '\'') {
|
||||
dest[len] = '\'';
|
||||
len++;
|
||||
dest[len] = '\'';
|
||||
} else {
|
||||
dest[len] = *data;
|
||||
}
|
||||
}
|
||||
|
||||
dest[len] = '\0';
|
||||
}
|
||||
|
||||
static const char *get_update_sql(const dm_node_t *node, const char *data)
|
||||
{
|
||||
const struct dm_node_info *info = dm_node_get_info(node->id);
|
||||
|
||||
if (info == NULL) {
|
||||
dmlog_error("get_update_sql, node id not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(sql_statement, "UPDATE ");
|
||||
strcat(sql_statement, dm_node_get_table_name(info));
|
||||
strcat(sql_statement, " SET \"");
|
||||
strcat(sql_statement, get_table_field_name(info));
|
||||
strcat(sql_statement, "\" = ");
|
||||
|
||||
strcat(sql_statement, "\'");
|
||||
if (strchr(data, '\'') != NULL)
|
||||
// containing single quotes, needs special handling.
|
||||
strcat_str_data(sql_statement, (const char *)data);
|
||||
else
|
||||
strcat(sql_statement, (const char *)data);
|
||||
|
||||
strcat(sql_statement, "\'");
|
||||
|
||||
if (node->cnt > 0) {
|
||||
strcat(sql_statement, " WHERE ");
|
||||
int cnt = 0;
|
||||
|
||||
get_db_index_condition(info, sql_statement, node->index, &cnt);
|
||||
}
|
||||
|
||||
strcat(sql_statement, ";");
|
||||
|
||||
return sql_statement;
|
||||
}
|
||||
|
||||
static int db_next_index_callback(void *context, int argc, char *argv[], char *cols[])
|
||||
{
|
||||
(void)argc;
|
||||
(void)cols;
|
||||
|
||||
int *next_index = (int *)context;
|
||||
|
||||
if (argv[0]) {
|
||||
*next_index = atoi(argv[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exec_sql(const char *sql, sqlite_callback callback, void *context)
|
||||
{
|
||||
if (transaction_db == NULL) {
|
||||
dmlog_error("db is not opened");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *err_msg = NULL;
|
||||
int ret = sqlite3_exec(transaction_db, sql, callback, context, &err_msg);
|
||||
if (ret != SQLITE_OK) {
|
||||
dmlog_error("SQL command(%s) error: %s", sql, err_msg);
|
||||
sqlite3_free(err_msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// sql in transaction
|
||||
static int exec_trans_sql(const char *sql, sqlite_callback callback, void *context)
|
||||
{
|
||||
return exec_sql(sql, callback, context);
|
||||
}
|
||||
|
||||
static int get_object_next_index(const dm_node_t *node)
|
||||
{
|
||||
const struct dm_node_info *info = dm_node_get_info(node->id);
|
||||
if (info == NULL)
|
||||
return -1;
|
||||
|
||||
const struct dm_node_info *p = info->parent;
|
||||
|
||||
while (p != dm_node_get_root() && p->parent != dm_node_get_root()) {
|
||||
if (p->type == DM_NODE_OBJECT_LIST) {
|
||||
break;
|
||||
}
|
||||
p = p->parent;
|
||||
}
|
||||
|
||||
strcpy(sql_statement, "select ");
|
||||
strcat(sql_statement, "\"NextIndexOf");
|
||||
strcat(sql_statement, get_node_rel_path_name(p, info));
|
||||
|
||||
strcat(sql_statement, "\" FROM ");
|
||||
|
||||
if (p->parent == dm_node_get_root()) {
|
||||
strcat(sql_statement, dm_node_get_root()->name);
|
||||
strcat(sql_statement, "_");
|
||||
strcat(sql_statement, p->name);
|
||||
} else {
|
||||
const char *tb = dm_node_get_table_name(p);
|
||||
if (tb == NULL) {
|
||||
dmlog_error("no db table for node %s", dm_node_str(node));
|
||||
return -1;
|
||||
}
|
||||
strcat(sql_statement, tb);
|
||||
|
||||
if (dm_node_index_cnt(node->id) > 1) {
|
||||
strcat(sql_statement, " WHERE ");
|
||||
int cnt = 0;
|
||||
|
||||
get_db_index_condition(info->parent, sql_statement, node->index, &cnt);
|
||||
}
|
||||
}
|
||||
|
||||
strcat(sql_statement, ";");
|
||||
|
||||
int next_index = -1;
|
||||
|
||||
exec_sql(sql_statement, db_next_index_callback, &next_index);
|
||||
|
||||
return next_index;
|
||||
}
|
||||
|
||||
static int update_object_next_index(const dm_node_t *node, int next_index)
|
||||
{
|
||||
const struct dm_node_info *info = dm_node_get_info(node->id);
|
||||
if (info == NULL)
|
||||
return -1;
|
||||
|
||||
const struct dm_node_info *p = info->parent;
|
||||
|
||||
while (p != dm_node_get_root() && p->parent != dm_node_get_root()) {
|
||||
if (p->type == DM_NODE_OBJECT_LIST) {
|
||||
break;
|
||||
}
|
||||
p = p->parent;
|
||||
}
|
||||
|
||||
strcpy(sql_statement, "UPDATE ");
|
||||
|
||||
if (p->parent == dm_node_get_root()) {
|
||||
strcat(sql_statement, dm_node_get_root()->name);
|
||||
strcat(sql_statement, "_");
|
||||
strcat(sql_statement, p->name);
|
||||
} else {
|
||||
strcat(sql_statement, dm_node_get_table_name(p));
|
||||
}
|
||||
|
||||
strcat(sql_statement, " SET \"NextIndexOf");
|
||||
strcat(sql_statement, get_node_rel_path_name(p, info));
|
||||
|
||||
strcat(sql_statement, "\" = ");
|
||||
strcat(sql_statement, get_int_str(next_index));
|
||||
|
||||
if (dm_node_index_cnt(node->id) > 1) {
|
||||
strcat(sql_statement, " WHERE ");
|
||||
int cnt = 0;
|
||||
|
||||
get_db_index_condition(info->parent, sql_statement, node->index, &cnt);
|
||||
}
|
||||
|
||||
strcat(sql_statement, ";");
|
||||
|
||||
return exec_trans_sql(sql_statement, NULL, NULL);
|
||||
}
|
||||
|
||||
static const char *get_insert_sql(const dm_node_t *node, int new_index)
|
||||
{
|
||||
int i;
|
||||
int cnt = 0;
|
||||
|
||||
const struct dm_node_info *info = dm_node_get_info(node->id);
|
||||
|
||||
if (info == NULL) {
|
||||
dmlog_error("get_insert_sql, node id not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(sql_statement, "insert INTO ");
|
||||
strcat(sql_statement, dm_node_get_table_name(info));
|
||||
strcat(sql_statement, " (");
|
||||
|
||||
get_db_index_field(info, sql_statement, &cnt);
|
||||
strcat(sql_statement, ") VALUES(");
|
||||
|
||||
cnt = dm_node_index_cnt(node->id);
|
||||
for (i = 0; i < cnt - 1; i++) {
|
||||
strcat(sql_statement, get_int_str(node->index[i]));
|
||||
strcat(sql_statement, ",");
|
||||
}
|
||||
|
||||
strcat(sql_statement, get_int_str(new_index));
|
||||
|
||||
strcat(sql_statement, ");");
|
||||
|
||||
return sql_statement;
|
||||
}
|
||||
|
||||
static const char *get_delete_sql_condition(const dm_node_t *node)
|
||||
{
|
||||
static char condition[256];
|
||||
const struct dm_node_info *info = dm_node_get_info(node->id);
|
||||
|
||||
if (info == NULL) {
|
||||
dmlog_error("get_delete_sql_condition, node id not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (node->cnt > 0) {
|
||||
strcpy(condition, " WHERE ");
|
||||
int cnt = 0;
|
||||
|
||||
get_db_index_condition(info, condition, node->index, &cnt);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
int dbmgr_init(const char *db_file)
|
||||
{
|
||||
int ret = sqlite3_open_v2(db_file, &transaction_db, SQLITE_OPEN_READWRITE, NULL);
|
||||
if (SQLITE_OK != ret || transaction_db == NULL) {
|
||||
dmlog_error("sqlite3_open_v2 failed");
|
||||
return -1;
|
||||
}
|
||||
sqlite3_busy_timeout(transaction_db, SQLITE_BUSY_TIMEOUT);
|
||||
sqlite3_busy_handler(transaction_db, db_busy_handler, NULL);
|
||||
|
||||
dmlog_info("dbmgr_init OK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbmgr_finalize(void)
|
||||
{
|
||||
if (transaction_db) {
|
||||
sqlite3_close(transaction_db);
|
||||
transaction_db = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbmgr_tranx_begin(void)
|
||||
{
|
||||
dmlog_debug("dbmgr_tranx_begin");
|
||||
return exec_trans_sql("BEGIN;", NULL, NULL);
|
||||
}
|
||||
|
||||
int dbmgr_tranx_revert(void)
|
||||
{
|
||||
dmlog_debug("dbmgr_tranx_revert");
|
||||
return exec_trans_sql("ROLLBACK;", NULL, NULL);
|
||||
}
|
||||
|
||||
int dbmgr_tranx_commit(void)
|
||||
{
|
||||
dmlog_debug("dbmgr_tranx_commit");
|
||||
int ret = exec_trans_sql("COMMIT;", NULL, NULL);
|
||||
|
||||
if (ret != 0) {
|
||||
dmlog_error("dbmgr_tranx_commit, sql COMMIT; failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int db_get_data_callback(void *context, int cnt, char *data[], char *names[])
|
||||
{
|
||||
if (cnt <= 0 || data[0] == NULL) {
|
||||
dmlog_debug("db_get_data_callback, no data");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **val = context;
|
||||
*val = strdup(data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _dbmgr_get(const dm_node_t *node, char **value, int get_max_in_list)
|
||||
{
|
||||
const char *sql = get_query_sql(node, get_max_in_list);
|
||||
|
||||
if (sql == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return exec_sql(sql, db_get_data_callback, value);
|
||||
}
|
||||
|
||||
int dbmgr_get(const dm_node_t *node, char **value)
|
||||
{
|
||||
int retval = _dbmgr_get(node, value, 0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int dbmgr_get_child(const dm_node_t *node, const char *child_name, char **value)
|
||||
{
|
||||
dm_node_t child_node = *node;
|
||||
child_node.id = dm_node_get_child_id(node->id, child_name);
|
||||
return dbmgr_get(&child_node, value);
|
||||
}
|
||||
|
||||
int dbmgr_get_max(const dm_node_t *node, unsigned int *max)
|
||||
{
|
||||
char *value = NULL;
|
||||
if (_dbmgr_get(node, &value, 1) < 0 || value == NULL) {
|
||||
return -1;
|
||||
}
|
||||
*max = atoi(value);
|
||||
free(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbmgr_set(const dm_node_t *node, const char *data)
|
||||
{
|
||||
const char *sql = get_update_sql(node, data);
|
||||
int ret = exec_trans_sql(sql, NULL, NULL);
|
||||
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbmgr_set_uint(const dm_node_t *node, unsigned int data)
|
||||
{
|
||||
char *str;
|
||||
asprintf(&str, "%u", data);
|
||||
|
||||
int ret = dbmgr_set(node, str);
|
||||
free(str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dbmgr_get_next_free_index(const dm_node_t *node)
|
||||
{
|
||||
return get_object_next_index(node);
|
||||
}
|
||||
|
||||
int dbmgr_add(const dm_node_t *node)
|
||||
{
|
||||
int next_index = get_object_next_index(node);
|
||||
|
||||
if (next_index <= 0) {
|
||||
dmlog_error("dbmgr_add Error: next_index is %d", next_index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *sql = get_insert_sql(node, next_index);
|
||||
|
||||
int ret = exec_trans_sql(sql, NULL, NULL);
|
||||
|
||||
if (ret != 0)
|
||||
return -1;
|
||||
|
||||
if (update_object_next_index(node, next_index + 1) != 0) {
|
||||
return 0; // 0 means invalid index
|
||||
}
|
||||
|
||||
return next_index;
|
||||
}
|
||||
|
||||
static int delete_db_instances(const struct dm_node_info *info, const char *conditions)
|
||||
{
|
||||
if (info == NULL) {
|
||||
dmlog_info("delete_db_instances node not found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *table_name = dm_node_get_table_name(info);
|
||||
if (info->type == DM_NODE_OBJECT_LIST && table_name) {
|
||||
strcpy(sql_statement, "DELETE FROM ");
|
||||
strcat(sql_statement, table_name);
|
||||
if (conditions != NULL && *conditions != '\0') {
|
||||
strcat(sql_statement, conditions);
|
||||
}
|
||||
strcat(sql_statement, ";");
|
||||
|
||||
return exec_trans_sql(sql_statement, NULL, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dbmgr_del(const dm_node_t *node)
|
||||
{
|
||||
return delete_db_instances(dm_node_get_info(node->id), get_delete_sql_condition(node));
|
||||
}
|
||||
|
||||
int dbmgr_query_indexes(const dm_node_t *node, const char *keys, sqlite_callback cb, void *context, int sort)
|
||||
{
|
||||
const char *sql = get_query_all_indexes_sql(node, keys, sort);
|
||||
return exec_sql(sql, cb, context);
|
||||
}
|
||||
39
dm-framework/dm-api/src/core/dbmgr.h
Normal file
39
dm-framework/dm-api/src/core/dbmgr.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DBMGR_H
|
||||
#define DBMGR_H
|
||||
|
||||
#include "dm_types.h"
|
||||
|
||||
typedef int (*sqlite_callback)(void *, int, char *[], char *[]);
|
||||
|
||||
int dbmgr_init(const char *db_file);
|
||||
int dbmgr_finalize(void);
|
||||
|
||||
int dbmgr_tranx_begin(void);
|
||||
int dbmgr_tranx_revert(void);
|
||||
int dbmgr_tranx_commit(void);
|
||||
|
||||
int dbmgr_get(const dm_node_t *node, char **value);
|
||||
int dbmgr_get_child(const dm_node_t *node, const char *child_name, char **value);
|
||||
int dbmgr_get_max(const dm_node_t *node, unsigned int *max);
|
||||
|
||||
int dbmgr_set(const dm_node_t *node, const char *data);
|
||||
int dbmgr_set_uint(const dm_node_t *node, unsigned int data);
|
||||
|
||||
int dbmgr_add(const dm_node_t *node);
|
||||
int dbmgr_del(const dm_node_t *node);
|
||||
int dbmgr_query_indexes(const dm_node_t *node, const char *keys, sqlite_callback cb, void *context,
|
||||
int sort);
|
||||
int dbmgr_get_next_free_index(const dm_node_t *node);
|
||||
|
||||
#endif /* DBMGR_H */
|
||||
1607
dm-framework/dm-api/src/core/dm_api.c
Normal file
1607
dm-framework/dm-api/src/core/dm_api.c
Normal file
File diff suppressed because it is too large
Load Diff
265
dm-framework/dm-api/src/core/dm_api.h
Normal file
265
dm-framework/dm-api/src/core/dm_api.h
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef DM_API_H
|
||||
#define DM_API_H
|
||||
|
||||
#include <json-c/json.h>
|
||||
#include "dm_types.h"
|
||||
#include "dm_node.h"
|
||||
|
||||
struct dm_uci_context {
|
||||
int id;
|
||||
const char *savedir;
|
||||
};
|
||||
|
||||
/** Initialize dmapi.
|
||||
* @return 0 in case of success, or < 0 in case of error
|
||||
*/
|
||||
int dmapi_init(const char *service_name);
|
||||
|
||||
// Transaction action when session is end
|
||||
enum TRANX_ACTION {
|
||||
TRANX_NO_ACTION = 0,
|
||||
TRANX_ROLLBACK,
|
||||
TRANX_COMMIT,
|
||||
TRANX_COMMIT_AND_APPLY
|
||||
};
|
||||
|
||||
/** Start a session.
|
||||
* Only one session can be started and active
|
||||
* While starting the session the lock is taken to avoid race conditions
|
||||
* @pre dmapi_init should be called successfully
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int dmapi_session_start();
|
||||
|
||||
/** End a session.
|
||||
* Only one session can be started and active
|
||||
* @pre dmapi_session_start should be called successfully
|
||||
* @param action[in] one of the actions defined in TRANX_ACTION
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int dmapi_session_end(int action);
|
||||
|
||||
/** Apply changes in a session.
|
||||
* @pre dmapi_session_start should be called successfully
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int dmapi_session_apply(void);
|
||||
|
||||
/** Commits a transaction in a session.
|
||||
* @pre dmapi_session_start should be called successfully
|
||||
* and have modified parameters or objects by calling
|
||||
* dmapi_xxx_set/add/del
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int dmapi_session_commit(void);
|
||||
|
||||
/** Revert changes made in current session.
|
||||
* @pre dmapi_session_start should be called successfully
|
||||
* and have modified parameters or objects by calling
|
||||
* dmapi_xxx_set/add/del
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int dmapi_session_revert(void);
|
||||
|
||||
/** Check if session in the dmapi context is valid and active.
|
||||
* @pre dmapi_init should be called successfully
|
||||
* @return 1 if active, or 0 when invalid or not started
|
||||
*/
|
||||
int dmapi_in_session(void);
|
||||
|
||||
/** Delete a dmapi communication context.
|
||||
* @pre dmapi_init should be called successfully
|
||||
* @return none
|
||||
*/
|
||||
void dmapi_quit(void);
|
||||
|
||||
/** Enable or disable feature of "session on the fly"
|
||||
* With this feature enabled, session will be started automatically when set/add/del APIs are called,
|
||||
* so you don't have to call dmapi_session_start explicitly before modifying data models.
|
||||
* but you still need to end the session explicitly by calling dmapi_session_end.
|
||||
* @param enable: 0: disable, 1: enable
|
||||
* @pre dmapi_init should be called successfully
|
||||
* @return none
|
||||
*/
|
||||
void dmapi_set_session_on_fly(int enable);
|
||||
|
||||
/** Set UCI savedir
|
||||
* @param savedir: UCI savedir
|
||||
* @pre dmapi_init should be called successfully
|
||||
* @return none
|
||||
*/
|
||||
void dmapi_set_uci_savedir(const char *savedir);
|
||||
|
||||
/** Enable or disable feature of "Auto reference deletion" when deleting objects
|
||||
* this feature should be not enabled by cwmp client.
|
||||
* @param enable: 0: disable, 1: enable
|
||||
* @pre dmapi_init should be called successfully
|
||||
* @return none
|
||||
*/
|
||||
void dmapi_set_dm_auto_del(int enable);
|
||||
|
||||
/** Get parameter value of specific parameter node.
|
||||
*
|
||||
* @pre dmapi_init should be called successfully
|
||||
*
|
||||
* @param[in] node - pointer to parameter node
|
||||
* @param[out] value - pointer to pointer to save the result, the value must be freed after use.
|
||||
*
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int dmapi_param_get(const dm_node_t *node, char **value);
|
||||
|
||||
/** Set parameter value of specific parameter node.
|
||||
*
|
||||
* @pre dmapi_session_start should be called successfully
|
||||
*
|
||||
* @param[in] node - pointer to parameter node
|
||||
* @param[in] param_data - pointer to string parameter value to set
|
||||
*
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int dmapi_param_set(const dm_node_t *node, const char *param_data);
|
||||
|
||||
/** Add specific object node.
|
||||
*
|
||||
* @pre dmapi_session_start should be called successfully
|
||||
*
|
||||
* @param[in] node - pointer to object node, the index of the node will be updated upon success
|
||||
*
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int dmapi_object_add(dm_node_t *node);
|
||||
|
||||
/** Delete specific object node.
|
||||
*
|
||||
* @pre dmapi_session_start should be called successfully
|
||||
*
|
||||
* @param[in] node - pointer to object node
|
||||
*
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int dmapi_object_del(const dm_node_t *node);
|
||||
|
||||
/** Operate on the command node.
|
||||
*
|
||||
* @param[in] node - pointer to multi-object node as input
|
||||
* @param[in] args - arguments in json format.
|
||||
* @param[out] json_input - input of the command as json string format.
|
||||
* @param[out] output - output of the command as json_object.
|
||||
*
|
||||
* @return 0 in case of success, -1 in case of error
|
||||
*/
|
||||
int dmapi_operate(const dm_node_t *node, const char *json_input, struct json_object **json_output);
|
||||
|
||||
/** Check if an object instance exist
|
||||
* @param[in] node - node as input
|
||||
* @param[in] db_only - if the node should be in db or not
|
||||
* @return 1 if exist, otherwise return 0
|
||||
*/
|
||||
int dmapi_node_exist(const dm_node_t *node, int db_only);
|
||||
|
||||
typedef void *dm_nodelist_h;
|
||||
#define DM_INVALID_NODELIST ((dm_nodelist_h)NULL)
|
||||
|
||||
// macro to iterate the node list
|
||||
#define nodelist_for_each_node(node, list) \
|
||||
for ((node) = dm_nodelist_first((list)); (node) != NULL; (node) = dm_nodelist_next((list)))
|
||||
|
||||
/** Get all instances of a multi-object node, node must be a multi-object type.
|
||||
* @param node[in] pointer to multi-object node as input
|
||||
* @return handle of instances list in case of success, NULL value in case of error
|
||||
*/
|
||||
dm_nodelist_h dm_nodelist_get(const dm_node_t *node);
|
||||
|
||||
// for only db
|
||||
dm_nodelist_h dm_nodelist_get_db(const dm_node_t *node);
|
||||
|
||||
/** Get all instances of a multi-object node, node must be a multi-object type.
|
||||
* @param node[in] pointer to multi-object node as input
|
||||
* @param keys[in] sql condition
|
||||
* @param only_db[in] true if only get db instance.
|
||||
* @return handle of instances list in case of success, NULL value in case of error
|
||||
*/
|
||||
dm_nodelist_h dm_nodelist_find(const dm_node_t *node,
|
||||
const char *keys, int only_db);
|
||||
|
||||
/** Wrapper interface for dm_nodelist_find to find only one node as expected.
|
||||
* If more than one are found, the first one will be the result
|
||||
* @param node[in] pointer to multi-object node as input
|
||||
* @param keys[in] sql condition
|
||||
* @param only_db[in] true if only get db instance.
|
||||
* @param result[out] as output
|
||||
* @return 0 if not found, or a nonzero value when found
|
||||
*/
|
||||
int dm_nodelist_find_first(const dm_node_t *node,
|
||||
const char *keys, dm_node_t *result, int only_db);
|
||||
|
||||
/** Free a node list.
|
||||
* @pre dm_nodelist_get or dm_nodelist_find should be called successfully
|
||||
* @param list[in] handler of node list as input
|
||||
* @return none
|
||||
*/
|
||||
void dm_nodelist_free(dm_nodelist_h list);
|
||||
|
||||
/** Get first instance of node instance list.
|
||||
* @pre dm_nodelist_get or dm_nodelist_find should be called successfully
|
||||
* @param list[in] handler of node list as input
|
||||
* @return node pointer on success, NULL when node does not exist
|
||||
*/
|
||||
const dm_node_t *dm_nodelist_first(dm_nodelist_h list);
|
||||
|
||||
/** Get next instance of node instance list.
|
||||
* @pre dm_nodelist_get or dm_nodelist_find should be called successfully
|
||||
* @param list[in] handler of node list as input
|
||||
* @return node pointer on success, NULL when node does not exist
|
||||
*/
|
||||
const dm_node_t *dm_nodelist_next(dm_nodelist_h list);
|
||||
|
||||
/** Get count of instance in the list.
|
||||
* @pre dm_nodelist_get or dm_nodelist_find should be called successfully
|
||||
* @param[in] list handler of node list as input
|
||||
* @return number of instances
|
||||
*/
|
||||
int dm_nodelist_cnt(dm_nodelist_h list);
|
||||
|
||||
/** Get each node by index, scope is from [0 - (max_cnt-1)].
|
||||
* @pre dm_nodelist_get or dm_nodelist_find should be called successfully
|
||||
* @param list_h[in] handler of node list as input
|
||||
* @param i[in] index value as input
|
||||
* @return node pointer
|
||||
*/
|
||||
const dm_node_t *dm_nodelist_node(dm_nodelist_h list_h, int i);
|
||||
|
||||
/** Get node index by sequence number, scope is from [0 - (max_cnt-1)].
|
||||
* @pre dm_nodelist_get or dm_nodelist_find should be called successfully
|
||||
* @param list_h[in] handler of node list as input
|
||||
* @param i[in] index value as input
|
||||
* @return index of node
|
||||
*/
|
||||
dm_index_t dm_nodelist_index(dm_nodelist_h list_h, int i);
|
||||
const char *dm_nodelist_key(dm_nodelist_h list_h, int i, const char *key);
|
||||
void dmapi_dump_node_buffer(const dm_node_t *node);
|
||||
|
||||
int dmapi_handle_ubus_event(dm_node_id_t id, const char *json_event_str, struct json_object **res);
|
||||
|
||||
|
||||
#endif /* for dmapi_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
725
dm-framework/dm-api/src/core/dm_apply.c
Normal file
725
dm-framework/dm-api/src/core/dm_apply.c
Normal file
@@ -0,0 +1,725 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "dm_apply.h"
|
||||
|
||||
#include <quickjs/quickjs-libc.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbmgr.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_list.h"
|
||||
#include "dm_log.h"
|
||||
#include "dm_node.h"
|
||||
#include "qjs_uci_api.h"
|
||||
#include "qjs.h"
|
||||
#include "ubus_client.h"
|
||||
|
||||
typedef struct {
|
||||
const char* key;
|
||||
int order;
|
||||
} key_order_pair;
|
||||
|
||||
// datamodel change list during the session
|
||||
static dm_list_h changed_node_list = NULL;
|
||||
static dm_list_h apply_uci_list = NULL;
|
||||
|
||||
void add_apply_package(char *package)
|
||||
{
|
||||
if (!package) {
|
||||
return;
|
||||
}
|
||||
if (apply_uci_list == NULL) {
|
||||
apply_uci_list = dm_list_create(NULL);
|
||||
}
|
||||
|
||||
// add the uci package name to the apply list
|
||||
if (!dm_list_contains(apply_uci_list, (const void*)package)) {
|
||||
dmlog_debug("add uci package to apply: %s", package);
|
||||
dm_list_append(apply_uci_list, package);
|
||||
} else {
|
||||
free(package);
|
||||
}
|
||||
}
|
||||
|
||||
void del_apply_package(char *package)
|
||||
{
|
||||
if (!package || !apply_uci_list) {
|
||||
return;
|
||||
}
|
||||
|
||||
dmlog_debug("remove uci package to apply: %s", package);
|
||||
dm_list_remove(apply_uci_list, package);
|
||||
}
|
||||
|
||||
void dm_apply_reset_changes(void)
|
||||
{
|
||||
if (changed_node_list) {
|
||||
dm_list_free(changed_node_list);
|
||||
changed_node_list = NULL;
|
||||
}
|
||||
|
||||
if (apply_uci_list) {
|
||||
dm_list_free(apply_uci_list);
|
||||
apply_uci_list = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int node_change_cmp(const void *change1, const void *change2)
|
||||
{
|
||||
struct node_change *n1 = (struct node_change *)change1;
|
||||
struct node_change *n2 = (struct node_change *)change2;
|
||||
return !(dm_node_compatible(&n1->node, &n2->node) && (n1->redirected == n2->redirected));
|
||||
}
|
||||
|
||||
static dm_node_id_t find_parent_with_apply_handler(const struct dm_node_info *node_info)
|
||||
{
|
||||
if (node_info == NULL) {
|
||||
return INVALID_DM_NODE_ID;
|
||||
}
|
||||
if (qjs_has_apply_handler(node_info->node_id)) {
|
||||
return node_info->node_id;
|
||||
}
|
||||
|
||||
return find_parent_with_apply_handler(node_info->parent);
|
||||
}
|
||||
|
||||
int dm_appy_add_change(enum DATAMODEL_ACTION action, const dm_node_t *node)
|
||||
{
|
||||
dmlog_debug("add change %s", dm_node_str(node));
|
||||
if (changed_node_list == NULL) {
|
||||
changed_node_list = dm_list_create(node_change_cmp);
|
||||
}
|
||||
|
||||
struct node_change *pchange = calloc(1, sizeof(struct node_change));
|
||||
pchange->action = action;
|
||||
pchange->node = *node;
|
||||
const struct dm_node_info * info = dm_node_get_info(node->id);
|
||||
const char *uci_map;
|
||||
const struct dm_parameter *param = NULL;
|
||||
|
||||
// special handling for "Order" parameter
|
||||
if (info->type == DM_NODE_PARAMETER && info->flag & FLAG_HAS_ORDER) {
|
||||
// Order parameter,
|
||||
param = (const struct dm_parameter *)info;
|
||||
const struct dm_object *pobj = dm_node_get_object(dm_node_i_parent_id(node->id));
|
||||
if (param->map.map == NULL && pobj->map.map) {
|
||||
pchange->node.cnt--;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->type == DM_NODE_PARAMETER) {
|
||||
param = (const struct dm_parameter *)info;
|
||||
uci_map = param->map.map;
|
||||
} else {
|
||||
const struct dm_object *obj = (const struct dm_object *)info;
|
||||
uci_map = obj->map.map;
|
||||
}
|
||||
|
||||
if (uci_map == NULL && !qjs_has_apply_handler(node->id)) {
|
||||
// look for parent handler
|
||||
const dm_node_id_t pid = dm_node_i_parent_id(node->id);
|
||||
if (pid != INVALID_DM_NODE_ID) {
|
||||
const struct dm_object *pobj = dm_node_get_object(pid);
|
||||
if (pobj && pobj->map.map) {
|
||||
if (param && param->data_type == DM_PATH_NAME && param->data.paths &&
|
||||
dm_node_get_info(param->data.paths[0])->depends_node_id == pid) {
|
||||
goto end;
|
||||
}
|
||||
dmlog_debug("skip apply for %s", dm_node_str(node));
|
||||
free(pchange);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
dm_node_id_t id = find_parent_with_apply_handler(info->parent);
|
||||
if (id == INVALID_DM_NODE_ID) {
|
||||
dmlog_debug("ignored node change for apply %s", dm_node_str(node));
|
||||
free(pchange);
|
||||
return 0;
|
||||
}
|
||||
pchange->node.id = id;
|
||||
pchange->node.cnt = dm_node_index_cnt(id);
|
||||
}
|
||||
|
||||
dm_node_id_t depend = dm_node_get_apply_depends(pchange->node.id);
|
||||
if (depend != INVALID_DM_NODE_ID) {
|
||||
pchange->node.id = depend;
|
||||
pchange->redirected = 1;
|
||||
}
|
||||
|
||||
end:
|
||||
if (dm_list_contains(changed_node_list, (const void*)pchange)) {
|
||||
free(pchange);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dm_node_is_objectlist(pchange->node.id)) {
|
||||
pchange->node.cnt = dm_node_index_cnt(pchange->node.id) - 1;
|
||||
}
|
||||
|
||||
dm_list_append(changed_node_list, pchange);
|
||||
dmlog_debug("added node change %s", dm_node_str(&pchange->node));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *get_package_name(const char *uci_path)
|
||||
{
|
||||
char *dot_position = strchr(uci_path, '.');
|
||||
if (dot_position == NULL) {
|
||||
dmlog_error("missing dot in the uci_path: %s", uci_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int length = dot_position - uci_path;
|
||||
char *result = (char *)malloc(length + 1); // +1 for the null-terminator
|
||||
strncpy(result, uci_path, length);
|
||||
result[length] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
static char *get_package_type(const char *uci_path)
|
||||
{
|
||||
char *dot_position = strchr(uci_path, '.');
|
||||
|
||||
if (dot_position == NULL) {
|
||||
dmlog_error("missing dot in the uci_path: %s", uci_path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t length = strlen(dot_position + 1) + 1;
|
||||
char *substring = malloc(length);
|
||||
|
||||
strncpy(substring, dot_position + 1, length);
|
||||
substring[length - 1] = '\0';
|
||||
return substring;
|
||||
}
|
||||
|
||||
static dm_nodelist_h find_refer_instances(const dm_node_t *node, const dm_node_t *ref_node)
|
||||
{
|
||||
dm_path_t path;
|
||||
dm_node2name(ref_node, path, sizeof(dm_path_t));
|
||||
const struct dm_object *obj = dm_node_get_object(node->id);
|
||||
// find the parameter that refers to the "ref_node"
|
||||
for (int i = 0; i < obj->param_num; i++) {
|
||||
const struct dm_parameter *param = (const struct dm_parameter*)obj->param_list[i];
|
||||
if (param->data_type == DM_PATH_NAME && param->data.paths[0] == ref_node->id) {
|
||||
const struct dm_node_info *param_info = dm_node_get_info(param->node.node_id);
|
||||
char *search = NULL;
|
||||
asprintf(&search, "%s='%s'", param_info->name, path);
|
||||
dmlog_debug("search key: %s", search);
|
||||
dm_nodelist_h res = dm_nodelist_find(node, search, 1);
|
||||
free(search);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return DM_INVALID_NODELIST;
|
||||
}
|
||||
|
||||
static void apply_param_uci_map(const dm_node_t *node, const dm_node_t *parent, const struct dm_uci_map *map)
|
||||
{
|
||||
dmlog_debug("apply_param_uci_map: %s", dm_node_str(node));
|
||||
char *val = NULL;
|
||||
if (dbmgr_get(node, &val) < 0 || val == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *uci_path = NULL;
|
||||
char *uci_pkg = NULL;
|
||||
if (map->map) {
|
||||
if (parent && strchr(map->map, '.') == NULL) {
|
||||
char *uci_key = NULL;
|
||||
dm_node_t key_node;
|
||||
const struct dm_object *obj = dm_node_get_object(parent->id);
|
||||
if (dm_node_get_child(parent, "_key", &key_node) < 0) {
|
||||
dmlog_error("failed to get _key %s", dm_node_str(parent));
|
||||
goto end;
|
||||
}
|
||||
if (dbmgr_get(&key_node, &uci_key) < 0 || uci_key == NULL) {
|
||||
dmlog_error("failed to call dbmgr_get %s", dm_node_str(&key_node));
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (obj->map.map == NULL) {
|
||||
dmlog_error("missing uci map %s", dm_node_str(parent));
|
||||
free(uci_key);
|
||||
goto end;
|
||||
}
|
||||
|
||||
uci_pkg = get_package_name(obj->map.map);
|
||||
asprintf(&uci_path, "%s.%s.%s", uci_pkg, uci_key, map->map);
|
||||
free(uci_key);
|
||||
} else {
|
||||
uci_path = strdup(map->map);
|
||||
uci_pkg = get_package_name(map->map);
|
||||
}
|
||||
|
||||
if (map->type == DM_UCI_MAP_TYPE_SIMPLE || map->type == DM_UCI_MAP_TYPE_INTERFACE) {
|
||||
if (dm_node_data_type(node->id) == DM_PATH_NAME) {
|
||||
// the the uci to the section name of the corresponding pathname
|
||||
if (val[0] == '\0') {
|
||||
dm_uci_set(uci_path, val);
|
||||
} else {
|
||||
char *key = NULL;
|
||||
dm_node_t ref_node;
|
||||
dm_path2node(val, &ref_node);
|
||||
dbmgr_get_child(&ref_node, "_key", &key);
|
||||
if (key != NULL) {
|
||||
dm_uci_set(uci_path, key);
|
||||
free(key);
|
||||
} else {
|
||||
dmlog_error("failed to get key of node %s", val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (dm_node_is_bool_type(node->id)) {
|
||||
if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0)
|
||||
dm_uci_set(uci_path, "1");
|
||||
else
|
||||
dm_uci_set(uci_path, "0");
|
||||
} else {
|
||||
dm_uci_set(uci_path, val);
|
||||
}
|
||||
}
|
||||
} else if (map->type == DM_UCI_MAP_TYPE_DISABLE) {
|
||||
if (strcmp(val, "1") == 0 || strcmp(val, "true") == 0)
|
||||
dm_uci_set(uci_path, "0");
|
||||
else
|
||||
dm_uci_set(uci_path, "1");
|
||||
} else {
|
||||
dmlog_debug("not support apply uci type: %s", dm_node_str(node));
|
||||
}
|
||||
free(uci_path);
|
||||
}
|
||||
|
||||
qjs_call_apply_param_handler(node, val);
|
||||
|
||||
add_apply_package(uci_pkg);
|
||||
|
||||
end:
|
||||
free(val);
|
||||
return;
|
||||
}
|
||||
|
||||
struct dm_inst_key {
|
||||
const char *key;
|
||||
dm_node_t node;
|
||||
};
|
||||
|
||||
static int dm_inst_list_cmp(const void *item1, const void *item2)
|
||||
{
|
||||
struct dm_inst_key *inst1 = (struct dm_inst_key *)item1;
|
||||
struct dm_inst_key *inst2 = (struct dm_inst_key *)item2;
|
||||
if (strcmp((const char*)inst1->key, (const char*)inst2->key) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void free_dm_inst_list(dm_list_h list)
|
||||
{
|
||||
int cnt = dm_list_cnt(list);
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
struct dm_inst_key *inst = dm_list_get(list, i);
|
||||
free((char *)inst->key);
|
||||
}
|
||||
|
||||
dm_list_free(list);
|
||||
}
|
||||
|
||||
static dm_list_h get_obj_key_list(dm_nodelist_h list)
|
||||
{
|
||||
const dm_node_t *n = NULL;
|
||||
dm_list_h key_list = dm_list_create(dm_inst_list_cmp);
|
||||
nodelist_for_each_node(n, list)
|
||||
{
|
||||
char *key = NULL;
|
||||
if (dbmgr_get_child(n, "_key", &key) == 0 && key != NULL) {
|
||||
struct dm_inst_key *ins = malloc(sizeof(struct dm_inst_key));
|
||||
ins->key = key;
|
||||
ins->node = *n;
|
||||
dm_list_append(key_list, ins);
|
||||
} else {
|
||||
dmlog_error("failed to get key for node: %s", dm_node_str(n));
|
||||
}
|
||||
}
|
||||
|
||||
return key_list;
|
||||
}
|
||||
|
||||
static int apply_obj_uci_map(const dm_node_t *node, const struct dm_uci_map *map)
|
||||
{
|
||||
int ret = 0;
|
||||
char *pkg_name = get_package_name(map->map);
|
||||
char *type = get_package_type(map->map);
|
||||
dm_nodelist_h list = dm_nodelist_get_db(node);
|
||||
dm_list_h keys = get_obj_key_list(list);
|
||||
dm_list_h sect_list = dm_list_create(NULL);
|
||||
|
||||
char *uci_key = NULL;
|
||||
if (map->key != NULL && dm_node_index_cnt(node->id) > 1) {
|
||||
dm_node_t parent;
|
||||
dm_node_i_parent(node, &parent);
|
||||
dbmgr_get_child(&parent, "_key", &uci_key);
|
||||
}
|
||||
|
||||
json_object *json_list = NULL;
|
||||
if (dm_uci_get_section_list(pkg_name, type, NULL, 0, &json_list) == 0 && json_list != NULL) {
|
||||
json_object_object_foreach(json_list, key, val) {
|
||||
if (!qjs_uci_filter(node, val)) {
|
||||
continue;
|
||||
}
|
||||
const char *key_val = NULL;
|
||||
if (map->key != NULL) {
|
||||
json_object *val_obj;
|
||||
if (json_object_object_get_ex(val, map->key, &val_obj)) {
|
||||
key_val = json_object_get_string(val_obj);
|
||||
}
|
||||
}
|
||||
if (uci_key && strcmp(uci_key, key_val) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
struct dm_inst_key inst;
|
||||
inst.key = key;
|
||||
if (!dm_list_contains(keys, &inst)) {
|
||||
// special handling for user: set "deleted" instead of deleting
|
||||
if (strcmp(pkg_name, "users") == 0 && strcmp(type, "user") == 0) {
|
||||
char *tmp;
|
||||
asprintf(&tmp, "%s.%s.deleted", pkg_name, key);
|
||||
dm_uci_set(tmp, "1");
|
||||
free(tmp);
|
||||
} else {
|
||||
dmlog_debug("uci-map: del uci: %s.%s", pkg_name, key);
|
||||
if (qjs_call_uci_deinit_handler(node, key) == 0) {
|
||||
dm_uci_del(pkg_name, key);
|
||||
} else {
|
||||
dmlog_debug("uci-map: skipped deleting %s", key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dm_list_append(sect_list, strdup(key));
|
||||
}
|
||||
}
|
||||
json_object_put(json_list);
|
||||
}
|
||||
|
||||
int key_cnt = dm_list_cnt(keys);
|
||||
for (int i = 0; i < key_cnt; i++) {
|
||||
struct dm_inst_key *inst = dm_list_get(keys, i);
|
||||
if (!dm_list_contains(sect_list, (const void*)inst->key)) {
|
||||
// add section for the new instance
|
||||
dmlog_debug("uci-map: add uci: %s.%s", pkg_name, type);
|
||||
name_val_t opt_val;
|
||||
int opt_cnt = 0;
|
||||
if (uci_key != NULL && map->key) {
|
||||
opt_val.name = map->key;
|
||||
opt_val.value = uci_key;
|
||||
opt_cnt = 1;
|
||||
}
|
||||
|
||||
if (dm_uci_add(pkg_name, type, inst->key, &opt_val, opt_cnt) < 0) {
|
||||
dmlog_error("failed to add new uci section");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
qjs_call_uci_init_handler(&inst->node);
|
||||
}
|
||||
}
|
||||
|
||||
if (pkg_name) {
|
||||
add_apply_package(pkg_name);
|
||||
pkg_name = NULL;
|
||||
}
|
||||
|
||||
free(type);
|
||||
if (pkg_name) {
|
||||
free(pkg_name);
|
||||
}
|
||||
|
||||
if (uci_key) {
|
||||
free(uci_key);
|
||||
}
|
||||
|
||||
dm_nodelist_free(list);
|
||||
free_dm_inst_list(keys);
|
||||
dm_list_free(sect_list);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int compare_order(const void* a, const void* b) {
|
||||
key_order_pair* pair_a = (key_order_pair*)a;
|
||||
key_order_pair* pair_b = (key_order_pair*)b;
|
||||
return pair_a->order - pair_b->order;
|
||||
}
|
||||
|
||||
static void reorder_uci_sections(const char *pkg, key_order_pair *pairs, int cnt)
|
||||
{
|
||||
qsort(pairs, cnt, sizeof(key_order_pair), compare_order);
|
||||
struct blob_buf b;
|
||||
memset(&b, 0, sizeof(b));
|
||||
blob_buf_init(&b, 0);
|
||||
blobmsg_add_string(&b, "config", pkg);
|
||||
void *a = blobmsg_open_array(&b, "sections");
|
||||
for (int i = 0; i < cnt; ++i) {
|
||||
blobmsg_add_string(&b, NULL, pairs[i].key);
|
||||
}
|
||||
blobmsg_close_array(&b, a);
|
||||
ubus_client_call("uci", "order", b.head, NULL, NULL);
|
||||
blob_buf_free(&b);
|
||||
|
||||
add_apply_package((char*)pkg);
|
||||
}
|
||||
|
||||
// reorder the uci sections according to the "Order" value
|
||||
static void apply_order(const dm_node_t *node, const char *uci_map)
|
||||
{
|
||||
dmlog_info("apply_order, %s, %s", dm_node_str(node), uci_map);
|
||||
dm_nodelist_h list = dm_nodelist_get_db(node);
|
||||
int cnt = dm_nodelist_cnt(list);
|
||||
if (cnt <= 1) {
|
||||
dm_nodelist_free(list);
|
||||
return;
|
||||
}
|
||||
|
||||
key_order_pair* pairs = calloc(cnt, sizeof(key_order_pair));
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
char* order_str = NULL;
|
||||
dbmgr_get_child(dm_nodelist_node(list, i), "Order", &order_str);
|
||||
if (order_str == NULL) {
|
||||
dmlog_error("apply_order, failed to get Order");
|
||||
goto exit;
|
||||
}
|
||||
pairs[i].order = atoi(order_str);
|
||||
free(order_str);
|
||||
pairs[i].key = dm_nodelist_key(list, i, "_key");
|
||||
if (pairs[i].key == NULL) {
|
||||
dmlog_error("apply_order, unexpected empty key value");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
char *pkg = get_package_name(uci_map);
|
||||
reorder_uci_sections(pkg, pairs, cnt);
|
||||
|
||||
exit:
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
if (pairs[i].key) {
|
||||
JS_FreeCString(qjs_ctx(), pairs[i].key);
|
||||
}
|
||||
}
|
||||
free(pairs);
|
||||
dm_nodelist_free(list);
|
||||
}
|
||||
|
||||
static void apply_extended_obj(const dm_node_t *node, const char *path, dm_node_id_t id)
|
||||
{
|
||||
dmlog_debug("apply_extended_obj: %s, %s", dm_node_str(node), path);
|
||||
dm_node_t ref_node;
|
||||
if (path == NULL) {
|
||||
return;
|
||||
}
|
||||
if (path[0] == '\0') {
|
||||
// call the deinit handler for the extended object
|
||||
dm_node_t n = {id};
|
||||
char *uci_key = NULL;
|
||||
dbmgr_get_child(node, "_key", &uci_key);
|
||||
if (uci_key) {
|
||||
qjs_call_uci_deinit_handler(&n, uci_key);
|
||||
free(uci_key);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dm_path2node(path, &ref_node) != 0) {
|
||||
dmlog_error("apply_order, invalid path: %s", path);
|
||||
return;
|
||||
}
|
||||
|
||||
const struct dm_object *obj = dm_node_get_object(ref_node.id);
|
||||
for (int i = 0; i < obj->param_num; i++) {
|
||||
const struct dm_parameter *param = (const struct dm_parameter *)obj->param_list[i];
|
||||
if (param->node.table_name == NULL) {
|
||||
continue;
|
||||
}
|
||||
if (param->map.map == NULL && !qjs_has_apply_handler(node->id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dm_node_t n = ref_node;
|
||||
n.id = obj->param_list[i]->node_id;
|
||||
apply_param_uci_map(&n, node, ¶m->map);
|
||||
}
|
||||
}
|
||||
|
||||
void dm_apply_node(struct node_change *change)
|
||||
{
|
||||
const dm_node_t *node = &change->node;
|
||||
dm_node_t *pparent = NULL;
|
||||
const struct dm_object *parent_obj = NULL;
|
||||
dm_node_t parent_node;
|
||||
if (dm_node_i_parent(node, &parent_node) == 0) {
|
||||
pparent = &parent_node;
|
||||
parent_obj = dm_node_get_object(parent_node.id);
|
||||
}
|
||||
|
||||
if (parent_obj && parent_obj->map.map && parent_obj->node.depends_node_id != INVALID_DM_NODE_ID) {
|
||||
// apply to the referenced object that it "extends"
|
||||
dm_node_t refer_node = parent_node;
|
||||
refer_node.id = parent_obj->node.depends_node_id;
|
||||
refer_node.cnt--;
|
||||
dm_nodelist_h res = find_refer_instances(&refer_node, &parent_node);
|
||||
const dm_node_t *pnode = NULL;
|
||||
nodelist_for_each_node(pnode, res) {
|
||||
if (qjs_has_apply_handler(node->id)) {
|
||||
qjs_call_apply_obj_handler(node, pnode);
|
||||
}
|
||||
else if (dm_node_is_parameter(node->id)) {
|
||||
const struct dm_parameter *param = dm_node_get_parameter(node->id);
|
||||
apply_param_uci_map(node, pnode, ¶m->map);
|
||||
}
|
||||
}
|
||||
dm_nodelist_free(res);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dm_node_is_parameter(node->id)) {
|
||||
const struct dm_parameter *param = dm_node_get_parameter(node->id);
|
||||
if (param->data_type == DM_PATH_NAME && param->data.paths) {
|
||||
if (pparent && dm_node_get_info(param->data.paths[0])->depends_node_id == pparent->id) {
|
||||
char *val = NULL;
|
||||
dbmgr_get(node, &val);
|
||||
apply_extended_obj(pparent, val, param->data.paths[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (param->map.map != NULL || qjs_has_apply_handler(node->id)) {
|
||||
apply_param_uci_map(node, pparent, ¶m->map);
|
||||
if (param->node.flag & FLAG_HAS_ORDER) {
|
||||
const struct dm_object *pobj = dm_node_get_object(dm_node_i_parent_id(node->id));
|
||||
if (pobj && pobj->map.map) {
|
||||
dm_node_t pnode;
|
||||
dm_node_i_parent(node, &pnode);
|
||||
apply_order(&pnode, pobj->map.map);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (dm_node_is_objectlist(node->id)) {
|
||||
const struct dm_object *obj = dm_node_get_object(node->id);
|
||||
if (obj->map.map != NULL && obj->map.type == DM_UCI_MAP_TYPE_SIMPLE ) {
|
||||
if (obj->node.depends_node_id == INVALID_DM_NODE_ID) {
|
||||
apply_obj_uci_map(node, &obj->map);
|
||||
}
|
||||
} else if (qjs_has_apply_handler(node->id)) {
|
||||
qjs_call_apply_obj_handler(node, node);
|
||||
}
|
||||
} else if (qjs_has_apply_handler(node->id)) {
|
||||
qjs_call_apply_obj_handler(node, node);
|
||||
} else {
|
||||
dmlog_error("dm_apply_node, node is not handled: %s", dm_node_str(node));
|
||||
}
|
||||
}
|
||||
|
||||
static int commit_changes()
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (changed_node_list == NULL)
|
||||
return 0;
|
||||
|
||||
int i;
|
||||
int cnt = dm_list_cnt(changed_node_list);
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
struct node_change *change = (struct node_change *)dm_list_get(changed_node_list, i);
|
||||
dmlog_debug("apply node: %s", dm_node_str(&change->node));
|
||||
if (change->redirected) {
|
||||
qjs_call_apply_obj_handler(&change->node, &change->node);
|
||||
} else {
|
||||
dm_apply_node(change);
|
||||
}
|
||||
dmlog_debug("end of apply node: %s", dm_node_str(&change->node));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// reset parameters that are confidential in db.
|
||||
static void reset_confidentials()
|
||||
{
|
||||
if (changed_node_list == NULL)
|
||||
return;
|
||||
|
||||
int i;
|
||||
// dbmgr_tranx_begin();
|
||||
int cnt = dm_list_cnt(changed_node_list);
|
||||
for (i = 0; i < cnt; i++) {
|
||||
struct node_change *change = (struct node_change *)dm_list_get(changed_node_list, i);
|
||||
if (dm_node_is_confidential(change->node.id)) {
|
||||
if (dbmgr_set(&change->node, "") < 0) {
|
||||
dmlog_error("failed to reset confidentail parameter: %s", dm_node_str(&change->node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dbmgr_tranx_commit();
|
||||
}
|
||||
|
||||
int dm_apply_reset(void)
|
||||
{
|
||||
dm_apply_reset_changes();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// static int reload_service(const char *svc)
|
||||
// {
|
||||
// struct blob_buf bb = {};
|
||||
// if (svc == NULL) {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// blob_buf_init(&bb, 0);
|
||||
// blobmsg_add_string(&bb, "config", svc);
|
||||
// int ret = ubus_client_call("uci", "commit", bb.head, NULL, NULL);
|
||||
// if (ret != 0) {
|
||||
// dmlog_error("failed to reload service %s", svc);
|
||||
// } else {
|
||||
// dmlog_info("reloaded service %s", svc);
|
||||
// }
|
||||
// blob_buf_free(&bb);
|
||||
// return ret;
|
||||
// }
|
||||
|
||||
int dm_apply_do_apply()
|
||||
{
|
||||
// int i;
|
||||
|
||||
commit_changes();
|
||||
|
||||
// the commit will be done in the bbf_config daemon
|
||||
// int cnt = dm_list_cnt(apply_uci_list);
|
||||
// for (i = 0; i < cnt; i++) {
|
||||
// char *pkg_name = dm_list_get(apply_uci_list, i);
|
||||
// dm_uci_commit(pkg_name);
|
||||
// }
|
||||
|
||||
// cnt = dm_list_cnt(apply_uci_list);
|
||||
// for (i = 0; i < cnt; i++) {
|
||||
// char *pkg_name = dm_list_get(apply_uci_list, i);
|
||||
// reload_service(pkg_name);
|
||||
// }
|
||||
|
||||
reset_confidentials();
|
||||
dm_apply_reset();
|
||||
return 0;
|
||||
}
|
||||
37
dm-framework/dm-api/src/core/dm_apply.h
Normal file
37
dm-framework/dm-api/src/core/dm_apply.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DM_APPLY_H
|
||||
#define DM_APPLY_H
|
||||
|
||||
#include <quickjs/quickjs.h>
|
||||
|
||||
#include "dm_node.h"
|
||||
|
||||
enum DATAMODEL_ACTION {
|
||||
DATA_MODEL_ADD,
|
||||
DATA_MODEL_SET,
|
||||
DATA_MODEL_DELETE
|
||||
};
|
||||
|
||||
struct node_change {
|
||||
dm_node_t node;
|
||||
enum DATAMODEL_ACTION action;
|
||||
int redirected;
|
||||
};
|
||||
|
||||
int dm_appy_add_change(enum DATAMODEL_ACTION action, const dm_node_t *node);
|
||||
void dm_apply_reset_changes(void);
|
||||
int dm_apply_reset(void);
|
||||
int dm_apply_do_apply();
|
||||
void dm_apply_node(struct node_change *change);
|
||||
void add_apply_package(char *package);
|
||||
#endif /* DM_APPLY_H */
|
||||
373
dm-framework/dm-api/src/core/dm_import.c
Normal file
373
dm-framework/dm-api/src/core/dm_import.c
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "qjs_uci_api.h"
|
||||
#include "dbmgr.h"
|
||||
#include "dm.h"
|
||||
#include "dm_log.h"
|
||||
#include "dm_node.h"
|
||||
#include "ubus_client.h"
|
||||
#include "dm_api.h"
|
||||
#include "qjs.h"
|
||||
#include "dm_list.h"
|
||||
|
||||
static char* get_tr181_pathname(const char *pn, const char *uci_path, const char *uci_val)
|
||||
{
|
||||
json_object *values;
|
||||
if (uci_val == NULL || uci_val[0] == '\0') {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *path = strdup(uci_path);
|
||||
char *config = strtok(path, ".");
|
||||
char *type = strtok(NULL, ".");
|
||||
if (dm_uci_get_section_list(config, type, NULL, 0, &values) < 0 || values == NULL) {
|
||||
dmlog_error("get_tr181_pathname, dm_uci_get_section_list failed");
|
||||
free(path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(path);
|
||||
|
||||
int is_network_interface = 0;
|
||||
dm_list_h itf_dev_list = NULL;
|
||||
// special handling for network interface
|
||||
if (strcmp(uci_path, "network.interface") == 0) {
|
||||
is_network_interface = 1;
|
||||
itf_dev_list = dm_list_create(NULL);
|
||||
}
|
||||
int index = 1;
|
||||
// Iterate over all keys in the 'values' object
|
||||
json_object_object_foreach(values, key, value) {
|
||||
(void)key;
|
||||
struct json_object *name_value = NULL;
|
||||
struct json_object *anonymous = NULL;
|
||||
if (json_object_object_get_ex(value, ".anonymous", &anonymous)
|
||||
&& json_object_get_boolean(anonymous)) {
|
||||
json_object_object_get_ex(value, "name", &name_value);
|
||||
} else {
|
||||
json_object_object_get_ex(value, ".name", &name_value);
|
||||
}
|
||||
if (is_network_interface) {
|
||||
// special handling for network interface: skip the loopback and dhcpv6 interfaces.
|
||||
struct json_object *dev_value;
|
||||
if (json_object_object_get_ex(value, "device", &dev_value)) {
|
||||
const char *dev_name = json_object_get_string(dev_value);
|
||||
if (strcmp(dev_name, "lo") == 0) {
|
||||
continue;
|
||||
}
|
||||
if (dm_list_contains(itf_dev_list, (const void*)dev_name)) {
|
||||
continue;
|
||||
}
|
||||
dm_list_append(itf_dev_list, strdup(dev_name));
|
||||
}
|
||||
}
|
||||
if (strcmp(json_object_get_string(name_value), uci_val) == 0) {
|
||||
// Found the object with the given name, return its index
|
||||
char *ret_val = NULL;
|
||||
asprintf(&ret_val, "%s%d", pn, index);
|
||||
json_object_put(values);
|
||||
return ret_val;
|
||||
} else {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
if (is_network_interface) {
|
||||
dm_list_free(itf_dev_list);
|
||||
}
|
||||
json_object_put(values);
|
||||
return NULL;
|
||||
}
|
||||
static int import_pathname(const dm_node_t *node, char *uci_value)
|
||||
{
|
||||
const struct dm_parameter * param = dm_node_get_parameter(node->id);
|
||||
if (param->data.paths) {
|
||||
dm_node_t ref_node = *node;
|
||||
ref_node.id = param->data.paths[0];
|
||||
const struct dm_object *obj = dm_node_get_object(ref_node.id);
|
||||
if (obj && obj->map.map) {
|
||||
dm_path_t pathname;
|
||||
ref_node.cnt = dm_node_index_cnt(ref_node.id) - 1;
|
||||
if (dm_node2name(&ref_node, pathname, sizeof(pathname)) < 0) {
|
||||
dmlog_error("import_pathname, invalid node");
|
||||
return -1;
|
||||
}
|
||||
// trim the last {i}.
|
||||
int len = strlen(pathname);
|
||||
pathname[len - 4] = '\0';
|
||||
|
||||
char *pn = get_tr181_pathname(pathname, obj->map.map, uci_value);
|
||||
if (pn) {
|
||||
int ret = dmapi_param_set(node, pn);
|
||||
free(pn);
|
||||
return ret;
|
||||
} else {
|
||||
dmlog_error("import_pathname, get_tr181_pathname failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int importParam(const dm_node_t *node, const struct dm_uci_map *map)
|
||||
{
|
||||
char * val = NULL;
|
||||
if (map->type == DM_UCI_MAP_TYPE_JS) {
|
||||
JSValue result = qjs_eval_buf(map->map, strlen(map->map), "eval-import-js", JS_EVAL_TYPE_GLOBAL);
|
||||
if (!JS_IsUndefined(result) && !JS_IsException(result)) {
|
||||
const char *result_cstr = JS_ToCString(qjs_ctx(), result);
|
||||
dmapi_param_set(node, result_cstr);
|
||||
JS_FreeCString(qjs_ctx(), result_cstr);
|
||||
JS_FreeValue(qjs_ctx(), result);
|
||||
} else {
|
||||
dmlog_error("failed to import js: %s", map->map);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
if (node->cnt == 0 || strchr(map->map, '.') != NULL) {
|
||||
// uci option with full path
|
||||
dm_uci_get(map->map, &val);
|
||||
} else {
|
||||
// uci option from the js object.
|
||||
char *buf;
|
||||
asprintf(&buf, "_arg['%s']", map->map);
|
||||
JSValue result = qjs_eval_buf(buf, strlen(buf), "eval-uci-js", JS_EVAL_TYPE_GLOBAL);
|
||||
free(buf);
|
||||
if (!JS_IsException(result) && !JS_IsUndefined(result)) {
|
||||
const char *result_cstr = JS_ToCString(qjs_ctx(), result);
|
||||
val = strdup(result_cstr);
|
||||
JS_FreeCString(qjs_ctx(), result_cstr);
|
||||
}
|
||||
JS_FreeValue(qjs_ctx(), result);
|
||||
}
|
||||
}
|
||||
|
||||
const struct dm_parameter * param = dm_node_get_parameter(node->id);
|
||||
if (val == NULL) {
|
||||
// uci option not present, use the default value defined in JSON if exist.
|
||||
if (param->default_uci_val) {
|
||||
dmapi_param_set(node, param->default_uci_val);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (map->type == DM_UCI_MAP_TYPE_DISABLE) {
|
||||
if (*val == '0') {
|
||||
dmapi_param_set(node, "true");
|
||||
} else {
|
||||
dmapi_param_set(node, "false");
|
||||
}
|
||||
} else if (param->data_type == DM_DATA_BOOLEAN) {
|
||||
if (*val == '1' || !strcmp(val, "true") || !strcmp(val, "yes")) {
|
||||
dmapi_param_set(node, "true");
|
||||
} else {
|
||||
dmapi_param_set(node, "false");
|
||||
}
|
||||
} else if (map->type == DM_UCI_MAP_TYPE_INTERFACE) {
|
||||
char *pn = get_tr181_pathname("Device.IP.Interface.", "network.interface", val);
|
||||
if (pn) {
|
||||
dmapi_param_set(node, pn);
|
||||
free(pn);
|
||||
} else {
|
||||
dmlog_error("import_pathname for network interface failed %s, %s", val, dm_node_str(node));
|
||||
}
|
||||
} else {
|
||||
if (dm_node_data_type(node->id) == DM_PATH_NAME) {
|
||||
import_pathname(node, val);
|
||||
} else {
|
||||
if (param->data_type == DM_DATA_ENUM && param->data.enum_strings) {
|
||||
// convert the uci case to the tr181 case, ex 'udp' => 'UDP', 'up' => 'Up'
|
||||
int i = 0;
|
||||
while (param->data.enum_strings[i]) {
|
||||
if (strcasecmp(param->data.enum_strings[i], val) == 0) {
|
||||
dmapi_param_set(node, param->data.enum_strings[i]);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!param->data.enum_strings[i]) {
|
||||
dmlog_error("uci value %s not found in the enum, %s", val, dm_node_str(node));
|
||||
}
|
||||
} else {
|
||||
dmapi_param_set(node, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int import_dm_obj(const dm_node_t *node, JSValue js_values);
|
||||
|
||||
static int handle_obj_uci_map(const dm_node_t *node, const struct dm_uci_map *map)
|
||||
{
|
||||
int ret;
|
||||
char *path = strdup(map->map);
|
||||
char *config = strtok(path, ".");
|
||||
char *type = strtok(NULL, ".");
|
||||
json_object *values_obj;
|
||||
|
||||
if (map->key != NULL && dm_node_index_cnt(node->id) > 1) {
|
||||
dm_node_t parent;
|
||||
dm_node_i_parent(node, &parent);
|
||||
const struct dm_object *obj = dm_node_get_object(parent.id);
|
||||
if (obj->map.map == NULL || obj->map.type != DM_UCI_MAP_TYPE_SIMPLE) {
|
||||
dmlog_error("handle_obj_uci_map missing parent map uci: %s", dm_node_str(node));
|
||||
return -1;
|
||||
}
|
||||
char *key_val = NULL;
|
||||
if (dbmgr_get_child(&parent, "_key", &key_val) < 0 || key_val == NULL) {
|
||||
dmlog_error("handle_obj_uci_map dbmgr_get_child failed: %s", dm_node_str(node));
|
||||
return -1;
|
||||
}
|
||||
name_val_t match = {map->key, key_val};
|
||||
ret = dm_uci_get_section_list(config, type, &match, 1, &values_obj);
|
||||
free(key_val);
|
||||
} else {
|
||||
ret = dm_uci_get_section_list(config, type, NULL, 0, &values_obj);
|
||||
}
|
||||
|
||||
free(path);
|
||||
|
||||
if (ret != 0 || values_obj == NULL) {
|
||||
dmlog_warn("dm_uci_get_section_list failed, uci: %s", map->map);
|
||||
// uci could be missing, skip the error
|
||||
return 0;
|
||||
}
|
||||
|
||||
int obj_index = 0;
|
||||
json_object_object_foreach(values_obj, key, val) {
|
||||
(void)key;
|
||||
if (!qjs_uci_filter(node, val)) {
|
||||
continue;
|
||||
}
|
||||
dm_node_t new_inst = *node;
|
||||
if (dmapi_object_add(&new_inst) < 0) {
|
||||
dmlog_error("failed to add new instance");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
json_object *name_obj;
|
||||
if (json_object_object_get_ex(val, ".name", &name_obj)) {
|
||||
dm_node_t key_node = new_inst;
|
||||
key_node.id = dm_node_get_child_id(new_inst.id, "_key");
|
||||
if (dmapi_param_set(&key_node, json_object_get_string(name_obj)) < 0) {
|
||||
dmlog_error("failed to set param value");
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
dmlog_error("failed to get uci name");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
JSValue js_val = json_object_to_jsvalue(val);
|
||||
JS_SetPropertyStr(qjs_ctx(), qjs_global(), "_arg", js_val);
|
||||
JS_SetPropertyStr(qjs_ctx(), js_val, ".index", JS_NewInt32(qjs_ctx(), obj_index));
|
||||
obj_index++;
|
||||
ret = import_dm_obj(&new_inst, js_val);
|
||||
if (ret < 0) {
|
||||
dmlog_error("failed to import node %s", dm_node_str(&new_inst));
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
json_object_put(values_obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int update_dm_value(JSContext *ctx, const char *key, JSValue value);
|
||||
|
||||
static int import_dm_obj(const dm_node_t *node, JSValue js_values) {
|
||||
if (qjs_has_import_handler(node->id)) {
|
||||
int ret = 0;
|
||||
dm_path_t node_path;
|
||||
dm_node2name(node, node_path, sizeof(dm_path_t));
|
||||
if (dm_node_is_object(node->id)) {
|
||||
strcat(node_path, ".");
|
||||
}
|
||||
|
||||
JSValue input_val = js_values;
|
||||
JSValue js_val = qjs_call_import_handler(node, input_val);
|
||||
if (!JS_IsException(js_val) && !JS_IsUndefined(js_val)) {
|
||||
int len = strlen(node_path);
|
||||
if (node_path[len-2] == '}') {
|
||||
node_path[len-4] = '\0';
|
||||
}
|
||||
const char *imported_val = get_js_value_str(js_val);
|
||||
free_js_cstr(imported_val);
|
||||
ret = update_dm_value(qjs_ctx(), node_path, js_val);
|
||||
if (ret < 0) {
|
||||
dmlog_error("update_dm_value failed");
|
||||
}
|
||||
}
|
||||
JS_FreeValue(qjs_ctx(), js_val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dm_node_is_parameter(node->id)) {
|
||||
if (dm_node_is_confidential(node->id) || !dm_node_has_db(node->id)) {
|
||||
return 0;
|
||||
}
|
||||
const struct dm_parameter * param = dm_node_get_parameter(node->id);
|
||||
if (param->map.map == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return importParam(node, ¶m->map);
|
||||
}
|
||||
|
||||
if (dm_node_is_objectlist(node->id) && !dm_node_is_index_complete(node)) {
|
||||
const struct dm_object *obj = dm_node_get_object(node->id);
|
||||
if (obj->map.map != NULL && obj->map.type == DM_UCI_MAP_TYPE_SIMPLE) {
|
||||
return handle_obj_uci_map(node, &obj->map);
|
||||
} else {
|
||||
dmlog_debug("ignored import for object: %s", dm_node_str(node));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dm_node_is_object(node->id) || dm_node_is_objectlist(node->id)) {
|
||||
const struct dm_object * obj = dm_node_get_object(node->id);
|
||||
for (int i = 0; i < obj->param_num; i++) {
|
||||
dm_node_t param_node = *node;
|
||||
param_node.id = obj->param_list[i]->node_id;
|
||||
if (import_dm_obj(¶m_node, js_values) < 0) {
|
||||
dmlog_error("import param failed: %s", dm_node_str(¶m_node));
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < obj->object_num; i++) {
|
||||
dm_node_t node_obj = *node;
|
||||
node_obj.id = obj->object_list[i]->node_id;
|
||||
if (import_dm_obj(&node_obj, js_values) < 0) {
|
||||
dmlog_debug("import object failed: %s", dm_node_str(&node_obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int importDM()
|
||||
{
|
||||
dm_node_t root_node = dm_init_node(DM_DEVICE);
|
||||
return import_dm_obj(&root_node, JS_UNDEFINED);
|
||||
}
|
||||
624
dm-framework/dm-api/src/core/dm_linker.c
Executable file
624
dm-framework/dm-api/src/core/dm_linker.c
Executable file
@@ -0,0 +1,624 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/stat.h>
|
||||
#include <uci.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dm_api.h"
|
||||
#include "dm_node.h"
|
||||
#include "dm_log.h"
|
||||
#include "dm.h"
|
||||
#include "dbmgr.h"
|
||||
|
||||
#define LOCK_FILE "/var/lock/bbfdm_reference_db.lock"
|
||||
#define UCI_PACKAGE "bbfdm_reference_db"
|
||||
|
||||
extern const dm_node_id_t dm_linker_nodes[];
|
||||
|
||||
// Forward declarations
|
||||
static char *calculate_hash(const char *input);
|
||||
static int process_linker_node(struct uci_context *ctx, struct uci_package *pkg,
|
||||
const char *service_hash, const dm_node_t *node);
|
||||
static int clear_service_entries(struct uci_context *ctx, struct uci_package *pkg,
|
||||
const char *service_hash);
|
||||
static int ensure_uci_sections(struct uci_context *ctx, struct uci_package *pkg);
|
||||
|
||||
// FNV-1 32-bit parameters
|
||||
#define FNV_OFFSET_BASIS 0x811C9DC5U
|
||||
#define FNV_PRIME 0x1000193U
|
||||
|
||||
/*
|
||||
* Calculate 8-character uppercase hex hash using the FNV-1 algorithm.
|
||||
* Returns malloc-ed string (must be freed by caller) or NULL on error.
|
||||
*/
|
||||
static char *calculate_hash(const char *input) {
|
||||
if (!input) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t hash = FNV_OFFSET_BASIS;
|
||||
const unsigned char *ptr = (const unsigned char *)input;
|
||||
|
||||
while (*ptr) {
|
||||
hash *= FNV_PRIME;
|
||||
hash ^= (uint32_t)(*ptr);
|
||||
ptr++;
|
||||
}
|
||||
|
||||
char *result = malloc(9); // 8 chars + null terminator
|
||||
if (!result) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(result, 9, "%08X", hash);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a node ID is in the linker nodes array
|
||||
*/
|
||||
int is_linker_node(dm_node_id_t node_id) {
|
||||
|
||||
for (int i = 0; dm_linker_nodes[i] != INVALID_DM_NODE_ID; i++) {
|
||||
if (dm_linker_nodes[i] == node_id) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int object_has_linker_param(dm_node_id_t object_id) {
|
||||
const struct dm_object *obj = dm_node_get_object(object_id);
|
||||
if (!obj) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < obj->param_num; i++) {
|
||||
const struct dm_node_info *info = obj->param_list[i];
|
||||
if (is_linker_node(info->node_id)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single linker parameter node
|
||||
*/
|
||||
static int process_linker_node(struct uci_context *ctx, struct uci_package *pkg,
|
||||
const char *service_hash, const dm_node_t *node) {
|
||||
const struct dm_parameter *param = dm_node_get_parameter(node->id);
|
||||
if (!param) return -1;
|
||||
|
||||
// Check if this parameter is a linker node
|
||||
if (!is_linker_node(node->id)) {
|
||||
dmlog_error("Not a linker parameter %s", dm_node_str(node));
|
||||
return 0; // Not a linker parameter
|
||||
}
|
||||
|
||||
// Get the parent object path
|
||||
dm_node_t parent_node;
|
||||
if (dm_node_i_parent(node, &parent_node) != 0) {
|
||||
dmlog_error("Failed to get parent node for %s", dm_node_str(node));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get parent path
|
||||
dm_path_t parent_path;
|
||||
if (dm_node2name(&parent_node, parent_path, sizeof(parent_path)) != 0) {
|
||||
dmlog_error("Failed to get parent path for %s", dm_node_str(&parent_node));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get current path (same as parent for leaf parameters)
|
||||
dm_path_t current_path;
|
||||
if (dm_node2name(&parent_node, current_path, sizeof(current_path)) != 0) {
|
||||
dmlog_error("Failed to get current path for %s", dm_node_str(&parent_node));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get parameter value
|
||||
char *key_value = NULL;
|
||||
if (dbmgr_get(node, &key_value) != 0 || !key_value) {
|
||||
dmlog_debug("No value found for linker parameter %s", dm_node_str(node));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Compose linker string directly from parent_path, keeping instance numbers intact
|
||||
const char *key_name = param->node.name;
|
||||
size_t len_linker = strlen(parent_path) + strlen(key_name) + strlen(key_value) + 6; // extra for [==].
|
||||
char *linker_string = malloc(len_linker);
|
||||
if (!linker_string) {
|
||||
dmlog_error("Failed to allocate memory for linker string");
|
||||
free(key_value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dm_node_t parent_node_with_index = parent_node;
|
||||
parent_node_with_index.cnt = 0;
|
||||
dm_path_t parent_path_with_index;
|
||||
if (dm_node2name_with_index(&parent_node_with_index, parent_path_with_index, sizeof(parent_path_with_index), "*") != 0) {
|
||||
dmlog_error("Failed to get parent path for %s", dm_node_str(&parent_node));
|
||||
return -1;
|
||||
}
|
||||
|
||||
parent_path_with_index[strlen(parent_path_with_index) - 2] = '\0';
|
||||
snprintf(linker_string, len_linker, "%s[%s==%s].", parent_path_with_index, key_name, key_value);
|
||||
|
||||
// Calculate hashes
|
||||
char *hash_path = calculate_hash(linker_string);
|
||||
char *hash_value = calculate_hash(current_path);
|
||||
|
||||
if (!hash_path || !hash_value) {
|
||||
dmlog_error("Failed to calculate hashes for %s", linker_string);
|
||||
free(linker_string);
|
||||
free(key_value);
|
||||
free(hash_path);
|
||||
free(hash_value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Update UCI sections
|
||||
struct uci_section *ref_path_section = uci_lookup_section(ctx, pkg, "reference_path");
|
||||
struct uci_section *ref_value_section = uci_lookup_section(ctx, pkg, "reference_value");
|
||||
struct uci_section *service_section = uci_lookup_section(ctx, pkg, service_hash);
|
||||
|
||||
if (!ref_path_section || !ref_value_section || !service_section) {
|
||||
dmlog_error("Failed to find required UCI sections");
|
||||
free(linker_string);
|
||||
free(key_value);
|
||||
free(hash_path);
|
||||
free(hash_value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set reference_path option
|
||||
struct uci_ptr ptr;
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = pkg->e.name;
|
||||
ptr.section = "reference_path";
|
||||
ptr.option = hash_path;
|
||||
ptr.value = current_path;
|
||||
uci_set(ctx, &ptr);
|
||||
|
||||
// Set reference_value option
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = pkg->e.name;
|
||||
ptr.section = "reference_value";
|
||||
ptr.option = hash_value;
|
||||
ptr.value = (strlen(key_value) > 0) ? key_value : "#";
|
||||
uci_set(ctx, &ptr);
|
||||
|
||||
// Add to service section lists
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = pkg->e.name;
|
||||
ptr.section = service_hash;
|
||||
ptr.option = "reference_path";
|
||||
ptr.value = hash_path;
|
||||
uci_add_list(ctx, &ptr);
|
||||
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = pkg->e.name;
|
||||
ptr.section = service_hash;
|
||||
ptr.option = "reference_value";
|
||||
ptr.value = hash_value;
|
||||
uci_add_list(ctx, &ptr);
|
||||
|
||||
// dmlog_debug("Processed linker: %s -> %s (path_hash=%s, value_hash=%s)",
|
||||
// linker_string, current_path, hash_path, hash_value);
|
||||
|
||||
// Cleanup
|
||||
free(linker_string);
|
||||
free(key_value);
|
||||
free(hash_path);
|
||||
free(hash_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all linker nodes by iterating through the linker nodes array
|
||||
*/
|
||||
static int process_all_linker_nodes(struct uci_context *ctx, struct uci_package *pkg,
|
||||
const char *service_hash) {
|
||||
// Iterate through all linker nodes
|
||||
for (int i = 0; dm_linker_nodes[i] != INVALID_DM_NODE_ID; i++) {
|
||||
dm_node_id_t linker_id = dm_linker_nodes[i];
|
||||
|
||||
// Check if this is a parameter node
|
||||
if (!dm_node_is_parameter(linker_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all instances of multi-instance objects that contain this linker parameter
|
||||
const struct dm_parameter *param = dm_node_get_parameter(linker_id);
|
||||
if (!param || !param->node.parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the object that contains this parameter
|
||||
dm_node_id_t parent_id = param->node.parent->node_id;
|
||||
|
||||
// Only handle multi-instance objects; skip single-instance objects
|
||||
if (dm_node_is_objectlist(parent_id)) {
|
||||
dm_node_t parent_node = {0};
|
||||
parent_node.id = parent_id;
|
||||
|
||||
// For multi-index nodes, bypass inode buffer and go directly to DB
|
||||
dm_nodelist_h list = dm_nodelist_find(&parent_node, NULL, 1);
|
||||
if (list != DM_INVALID_NODELIST) {
|
||||
const dm_node_t *instance_node;
|
||||
nodelist_for_each_node(instance_node, list) {
|
||||
// dmlog_debug("processing linker node %s", dm_node_str(instance_node));
|
||||
// Create the parameter node for this instance
|
||||
dm_node_t param_node = *instance_node;
|
||||
param_node.id = linker_id;
|
||||
|
||||
// Process this linker parameter instance
|
||||
process_linker_node(ctx, pkg, service_hash, ¶m_node);
|
||||
}
|
||||
dm_nodelist_free(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear existing entries for this service
|
||||
*/
|
||||
static int clear_service_entries(struct uci_context *ctx, struct uci_package *pkg,
|
||||
const char *service_hash) {
|
||||
struct uci_section *service_section = uci_lookup_section(ctx, pkg, service_hash);
|
||||
if (!service_section) {
|
||||
return 0; // Section doesn't exist yet
|
||||
}
|
||||
|
||||
// Clear the lists
|
||||
struct uci_ptr ptr;
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = pkg->e.name;
|
||||
ptr.section = service_hash;
|
||||
ptr.option = "reference_path";
|
||||
uci_delete(ctx, &ptr);
|
||||
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = pkg->e.name;
|
||||
ptr.section = service_hash;
|
||||
ptr.option = "reference_value";
|
||||
uci_delete(ctx, &ptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure required UCI sections exist
|
||||
*/
|
||||
static int ensure_uci_sections(struct uci_context *ctx, struct uci_package *pkg) {
|
||||
struct uci_ptr ptr;
|
||||
|
||||
// Ensure reference_path section exists
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = pkg->e.name;
|
||||
ptr.section = "reference_path";
|
||||
ptr.value = "reference_path";
|
||||
if (uci_set(ctx, &ptr) != UCI_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Ensure reference_value section exists
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = pkg->e.name;
|
||||
ptr.section = "reference_value";
|
||||
ptr.value = "reference_value";
|
||||
if (uci_set(ctx, &ptr) != UCI_OK) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main API function to refresh linker nodes
|
||||
*/
|
||||
int dm_refresh_linker_nodes(const char *service_name) {
|
||||
dmlog_debug("dm_refresh_linker_nodes start");
|
||||
if (!service_name) {
|
||||
dmlog_error("Service name is required");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 1: Open and lock the reference database
|
||||
int lock_fd = open(LOCK_FILE, O_CREAT | O_RDWR, 0644);
|
||||
if (lock_fd < 0) {
|
||||
dmlog_error("Failed to open lock file: %s", LOCK_FILE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (flock(lock_fd, LOCK_EX) != 0) {
|
||||
dmlog_error("Failed to acquire exclusive lock");
|
||||
close(lock_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 2: Calculate service hash
|
||||
char *service_hash = calculate_hash(service_name);
|
||||
if (!service_hash) {
|
||||
dmlog_error("Failed to calculate service hash");
|
||||
close(lock_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dmlog_debug("service_hash: %s", service_hash);
|
||||
|
||||
// Step 3: Initialize UCI context and point to /var/state for runtime reference DB
|
||||
struct uci_context *ctx = uci_alloc_context();
|
||||
if (!ctx) {
|
||||
dmlog_error("Failed to allocate UCI context");
|
||||
free(service_hash);
|
||||
close(lock_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Use /var/state as configuration directory
|
||||
uci_set_confdir(ctx, "/var/state");
|
||||
|
||||
// Load or create the UCI package
|
||||
struct uci_package *pkg = NULL;
|
||||
if (uci_load(ctx, UCI_PACKAGE, &pkg) != UCI_OK) {
|
||||
// Package doesn't exist, create it
|
||||
struct uci_ptr ptr;
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = UCI_PACKAGE;
|
||||
if (uci_set(ctx, &ptr) != UCI_OK) {
|
||||
dmlog_error("Failed to create UCI package: %s", UCI_PACKAGE);
|
||||
uci_free_context(ctx);
|
||||
free(service_hash);
|
||||
close(lock_fd);
|
||||
return -1;
|
||||
}
|
||||
pkg = ptr.p;
|
||||
}
|
||||
|
||||
// Step 4: Ensure required sections exist
|
||||
if (ensure_uci_sections(ctx, pkg) != 0) {
|
||||
dmlog_error("Failed to ensure UCI sections");
|
||||
uci_free_context(ctx);
|
||||
free(service_hash);
|
||||
close(lock_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create service section
|
||||
struct uci_ptr ptr;
|
||||
memset(&ptr, 0, sizeof(ptr));
|
||||
ptr.package = pkg->e.name;
|
||||
ptr.section = service_hash;
|
||||
ptr.value = "service";
|
||||
uci_set(ctx, &ptr);
|
||||
|
||||
// Step 5: Clear existing entries for this service
|
||||
clear_service_entries(ctx, pkg, service_hash);
|
||||
|
||||
// Step 6: Process all linker nodes
|
||||
if (process_all_linker_nodes(ctx, pkg, service_hash) != 0) {
|
||||
dmlog_error("Failed to process linker nodes");
|
||||
uci_free_context(ctx);
|
||||
free(service_hash);
|
||||
close(lock_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Step 7: Commit changes
|
||||
if (uci_commit(ctx, &pkg, false) != UCI_OK) {
|
||||
dmlog_error("Failed to commit UCI changes");
|
||||
uci_free_context(ctx);
|
||||
free(service_hash);
|
||||
close(lock_fd);
|
||||
return -1;
|
||||
}
|
||||
// Cleanup
|
||||
uci_free_context(ctx);
|
||||
free(service_hash);
|
||||
|
||||
// Step 8: Release lock
|
||||
flock(lock_fd, LOCK_UN);
|
||||
close(lock_fd);
|
||||
|
||||
dmlog_info("Successfully refreshed linker nodes for service: %s", service_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve the linker parameter value for a given object instance path.
|
||||
* The object_path should include the instance number, for example
|
||||
* "Device.IP.Interface.1"
|
||||
* On success, *value will point to a malloc()'d string that must be freed
|
||||
* by the caller. The function returns 0 on success or -1 on error.
|
||||
*/
|
||||
int dm_resolve_linker(const char *object_path, char **value) {
|
||||
if (!object_path || !value) {
|
||||
dmlog_error("Invalid arguments to dm_resolve_linker");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*value = NULL;
|
||||
|
||||
/* Calculate the hash used as option name in the reference_value section */
|
||||
char *hash_str = calculate_hash(object_path);
|
||||
if (!hash_str) {
|
||||
dmlog_error("Failed to calculate hash for %s", object_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct uci_context *ctx = uci_alloc_context();
|
||||
if (!ctx) {
|
||||
dmlog_error("Failed to allocate UCI context");
|
||||
free(hash_str);
|
||||
return -1;
|
||||
}
|
||||
/* reference DB lives in /var/state */
|
||||
uci_set_confdir(ctx, "/var/state");
|
||||
|
||||
struct uci_package *pkg = NULL;
|
||||
if (uci_load(ctx, UCI_PACKAGE, &pkg) != UCI_OK || !pkg) {
|
||||
dmlog_error("Failed to load UCI package %s", UCI_PACKAGE);
|
||||
uci_free_context(ctx);
|
||||
free(hash_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct uci_section *ref_value_section = uci_lookup_section(ctx, pkg, "reference_value");
|
||||
if (!ref_value_section) {
|
||||
dmlog_error("reference_value section not found in %s", UCI_PACKAGE);
|
||||
uci_free_context(ctx);
|
||||
free(hash_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *str_val = uci_lookup_option_string(ctx, ref_value_section, hash_str);
|
||||
if (!str_val) {
|
||||
dmlog_debug("No linker value found for %s (hash=%s)", object_path, hash_str);
|
||||
uci_free_context(ctx);
|
||||
free(hash_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Allocate and copy result for caller */
|
||||
if (strcmp(str_val, "#") == 0) {
|
||||
/* '#' is used to represent an empty string */
|
||||
*value = strdup("");
|
||||
} else {
|
||||
*value = strdup(str_val);
|
||||
}
|
||||
|
||||
if (!*value) {
|
||||
uci_free_context(ctx);
|
||||
free(hash_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uci_free_context(ctx);
|
||||
free(hash_str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the object path for a given linker value.
|
||||
*
|
||||
* This helper reconstructs the deterministic linker string of the form
|
||||
* "<base_path>[<key_name>==<key_value>]."
|
||||
* that was generated by dm_refresh_linker_nodes() and uses its hash to
|
||||
* look up the real object path from the reference database.
|
||||
*
|
||||
* If no matching entry exists in the database the function will fall
|
||||
* back to returning the linker string itself, exactly mimicking the
|
||||
* behaviour of legacy _bbfdm_get_references().
|
||||
*
|
||||
* The caller is responsible for freeing the returned string.
|
||||
*
|
||||
* @param base_path Base object path ending with a dot (e.g. "Device.WiFi.SSID.")
|
||||
* @param key_name Parameter name that acts as linker key (e.g. "Name")
|
||||
* @param key_value Desired value of the linker key
|
||||
* @param object_path [out] malloc-allocated string holding either the
|
||||
* resolved object path (e.g. "Device.WiFi.SSID.4")
|
||||
* (NULL when no match is found)
|
||||
* @return 0 when resolved, -1 if no match/error
|
||||
*/
|
||||
int dm_resolve_linker_path(const char *base_path,
|
||||
const char *key_name,
|
||||
const char *key_value,
|
||||
char **object_path) {
|
||||
if (!base_path || !key_name || !key_value || !object_path) {
|
||||
dmlog_error("Invalid arguments to dm_resolve_linker_path");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strlen(base_path) == 0 || strlen(key_name) == 0 || strlen(key_value) == 0) {
|
||||
dmlog_error("base_path, key_name and key_value must not be empty");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*object_path = NULL;
|
||||
|
||||
/* Compose the linker string that was used as basis for the reference hash */
|
||||
size_t linker_len = strlen(base_path) + strlen(key_name) + strlen(key_value) + 6; /* "[==]." */
|
||||
char *linker_string = malloc(linker_len);
|
||||
if (!linker_string) {
|
||||
return -1;
|
||||
}
|
||||
snprintf(linker_string, linker_len, "%s[%s==%s].", base_path, key_name, key_value);
|
||||
|
||||
/* Calculate hash for use as option name inside reference_path section */
|
||||
char *hash_path = calculate_hash(linker_string);
|
||||
if (!hash_path) {
|
||||
free(linker_string);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Open UCI context pointing to runtime reference DB (/var/state) */
|
||||
struct uci_context *ctx = uci_alloc_context();
|
||||
if (!ctx) {
|
||||
dmlog_error("Failed to allocate UCI context");
|
||||
free(linker_string);
|
||||
free(hash_path);
|
||||
return -1;
|
||||
}
|
||||
uci_set_confdir(ctx, "/var/state");
|
||||
|
||||
struct uci_package *pkg = NULL;
|
||||
if (uci_load(ctx, UCI_PACKAGE, &pkg) != UCI_OK || !pkg) {
|
||||
dmlog_error("Failed to load UCI package %s", UCI_PACKAGE);
|
||||
uci_free_context(ctx);
|
||||
free(linker_string);
|
||||
free(hash_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct uci_section *ref_path_section = uci_lookup_section(ctx, pkg, "reference_path");
|
||||
if (!ref_path_section) {
|
||||
dmlog_error("reference_path section not found in %s", UCI_PACKAGE);
|
||||
uci_free_context(ctx);
|
||||
free(linker_string);
|
||||
free(hash_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *uci_path = uci_lookup_option_string(ctx, ref_path_section, hash_path);
|
||||
|
||||
int ret = -1; /* default: not resolved */
|
||||
|
||||
if (uci_path) {
|
||||
/* Matching object path found */
|
||||
*object_path = strdup(uci_path);
|
||||
if (*object_path)
|
||||
ret = 0;
|
||||
} else {
|
||||
/* No match. Leave *object_path NULL and return error */
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
uci_free_context(ctx);
|
||||
free(linker_string);
|
||||
free(hash_path);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
89
dm-framework/dm-api/src/core/dm_linker.h
Executable file
89
dm-framework/dm-api/src/core/dm_linker.h
Executable file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DM_LINKER_H
|
||||
#define DM_LINKER_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Refresh linker nodes for a given service
|
||||
*
|
||||
* This function walks through the entire data model tree and updates the
|
||||
* UCI reference database with linker parameter information for the specified
|
||||
* service. It creates a deterministic mapping between object paths and their
|
||||
* linker parameter values.
|
||||
*
|
||||
* The function:
|
||||
* 1. Takes an exclusive lock on the reference database
|
||||
* 2. Calculates a service hash from the service name
|
||||
* 3. Prepares the UCI package with required sections
|
||||
* 4. Walks through all multi-instance objects looking for linker parameters
|
||||
* 5. Builds linker strings and generates hashes
|
||||
* 6. Updates the UCI reference database
|
||||
* 7. Commits changes and releases the lock
|
||||
*
|
||||
* @param service_name The UBus object name of the service (e.g., "bbfdm.core")
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int dm_refresh_linker_nodes(const char *service_name);
|
||||
|
||||
/**
|
||||
* Check if a node ID is a linker node
|
||||
*
|
||||
* This function checks if the given node ID is in the linker nodes array.
|
||||
*
|
||||
* @param node_id The node ID to check
|
||||
* @return 1 if the node is a linker node, 0 otherwise
|
||||
*/
|
||||
int is_linker_node(dm_node_id_t node_id);
|
||||
|
||||
/**
|
||||
* Check if an object has a linker parameter
|
||||
*
|
||||
* This function checks if the given object has any parameters that are linker nodes.
|
||||
*
|
||||
* @param object_id The object ID to check
|
||||
* @return 1 if the object has a linker parameter, 0 otherwise
|
||||
*/
|
||||
int object_has_linker_param(dm_node_id_t object_id);
|
||||
|
||||
/**
|
||||
* Resolve the linker parameter value for a given object instance path
|
||||
*
|
||||
* This helper looks up the reference database that was populated by
|
||||
* dm_refresh_linker_nodes() and returns the value of the linker
|
||||
* parameter belonging to the supplied object path.
|
||||
*
|
||||
* The caller is responsible for freeing the returned string.
|
||||
*
|
||||
* @param object_path Full object path including instance number
|
||||
* (e.g. "Device.IP.Interface.1")
|
||||
* @param value[out] Pointer that will receive the malloc-allocated value
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
int dm_resolve_linker(const char *object_path, char **value);
|
||||
|
||||
/*
|
||||
* Resolve object path from a linker value.
|
||||
* Returns 0 and sets *object_path on success.
|
||||
* Returns -1 and leaves *object_path NULL if no matching object path exists
|
||||
* or on error.
|
||||
*/
|
||||
int dm_resolve_linker_path(const char *base_path, const char *key_name, const char *key_value, char **object_path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* DM_LINKER_H */
|
||||
478
dm-framework/dm-api/src/core/inode_buf.c
Normal file
478
dm-framework/dm-api/src/core/inode_buf.c
Normal file
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "inode_buf.h"
|
||||
#include "dm_log.h"
|
||||
#include "qjs.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define GROWTH_FACTOR 2
|
||||
#define DB_INFO_TIMEOUT 2000
|
||||
|
||||
extern void dmapi_refresh_instance_buf(const dm_node_t *node);
|
||||
|
||||
static struct inode_entry *inode_g = NULL;
|
||||
|
||||
dm_inst_array_t* dm_inst_array_create(size_t initial_capacity) {
|
||||
dm_inst_array_t *array = calloc(1, sizeof(dm_inst_array_t));
|
||||
if (initial_capacity <= 0) {
|
||||
initial_capacity = 1;
|
||||
}
|
||||
array->data = calloc(initial_capacity, sizeof(inst_entry_t));
|
||||
array->size = 0;
|
||||
array->capacity = initial_capacity;
|
||||
return array;
|
||||
}
|
||||
|
||||
static void get_all_i_node(dm_node_id_t id, dm_node_id_t *nodes_id, int *cnt)
|
||||
{
|
||||
if (!dm_node_is_objectlist(id)) {
|
||||
id = dm_node_i_parent_id(id);
|
||||
if (id == INVALID_DM_NODE_ID) {
|
||||
*cnt = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nodes_id[0] = id;
|
||||
*cnt = 1;
|
||||
dm_node_id_t p = dm_node_i_parent_id(id);
|
||||
while (p != INVALID_DM_NODE_ID) {
|
||||
nodes_id[*cnt] = p;
|
||||
(*cnt)++;
|
||||
if (*cnt >= MAX_DM_NODE_DEPTH) {
|
||||
dmlog_info("too long inode");
|
||||
// panic
|
||||
break;
|
||||
}
|
||||
p = dm_node_i_parent_id(p);
|
||||
}
|
||||
}
|
||||
|
||||
static struct inode_entry *find_inode_entry(struct inode_entry *head, const dm_node_id_t key)
|
||||
{
|
||||
struct inode_entry *current = head;
|
||||
while (current != NULL) {
|
||||
if (current->key == key) {
|
||||
return current;
|
||||
}
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inst_entry_t* dm_inst_array_find(dm_inst_array_t *array, unsigned int index) {
|
||||
if (array == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
for (size_t i = 0; i < array->size; i++) {
|
||||
if (array->data[i].index == index) {
|
||||
return &array->data[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inst_entry_t *find_inst(struct inode_entry *entry, unsigned int index, int *db)
|
||||
{
|
||||
inst_entry_t *inst = dm_inst_array_find(entry->insts, index);
|
||||
if (inst == NULL) {
|
||||
inst = dm_inst_array_find(entry->dyn_insts, index);
|
||||
if (db && inst) {
|
||||
*db = 0;
|
||||
}
|
||||
} else if (db) {
|
||||
*db = 1;
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
static void free_inode(struct inode_entry *inode);
|
||||
|
||||
void dm_inst_array_free(dm_inst_array_t *array) {
|
||||
if (array == NULL) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < array->size; i++) {
|
||||
JS_FreeValue(qjs_ctx(), array->data[i].value);
|
||||
free_inode(array->data[i].inode);
|
||||
}
|
||||
|
||||
if (array->data){
|
||||
free(array->data);
|
||||
}
|
||||
|
||||
free(array);
|
||||
}
|
||||
static void free_insts(struct inode_entry *entry)
|
||||
{
|
||||
dm_inst_array_free(entry->insts);
|
||||
dm_inst_array_free(entry->dyn_insts);
|
||||
entry->insts = NULL;
|
||||
entry->dyn_insts = NULL;
|
||||
}
|
||||
|
||||
static void free_inode(struct inode_entry *inode)
|
||||
{
|
||||
if (inode == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct inode_entry *current = inode;
|
||||
struct inode_entry *next;
|
||||
while (current != NULL) {
|
||||
next = current->next;
|
||||
free_insts(current);
|
||||
free(current);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
int inode_buf_get_js_values(const dm_node_t *node, JSValue values[])
|
||||
{
|
||||
int cnt = 0;
|
||||
dm_node_id_t nodes_id[MAX_DM_NODE_DEPTH];
|
||||
get_all_i_node(node->id, nodes_id, &cnt);
|
||||
|
||||
struct inode_entry **inode = &inode_g;
|
||||
|
||||
dm_node_t curr_node;
|
||||
curr_node.cnt = 0;
|
||||
|
||||
int index_i = 0;
|
||||
for (int i = cnt - 1; i >= 0; i--) {
|
||||
if (index_i >= node->cnt) {
|
||||
// not complete index: A.1.B.1.C.{i}.
|
||||
return index_i;
|
||||
}
|
||||
|
||||
curr_node.id = nodes_id[i];
|
||||
struct inode_entry *entry = find_inode_entry(*inode, nodes_id[i]);
|
||||
if (entry == NULL) {
|
||||
dmapi_refresh_instance_buf(&curr_node);
|
||||
entry = find_inode_entry(*inode, nodes_id[i]);
|
||||
}
|
||||
|
||||
if (entry == NULL) {
|
||||
dmlog_error("inode_buf_get_js_values, inode_entry %s not found, %s", dm_node_str(&curr_node), dm_node_str(node));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int db = 0;
|
||||
inst_entry_t *inst = find_inst(entry, node->index[index_i], &db);
|
||||
if (inst == NULL) {
|
||||
dmlog_error("inode_buf_get_js_values, nst_entry not found %s, %d", dm_node_str(node), node->index[index_i]);
|
||||
return -1;
|
||||
}
|
||||
values[index_i++] = inst->value;
|
||||
inode = &inst->inode;
|
||||
curr_node.index[curr_node.cnt] = inst->index;
|
||||
curr_node.cnt++;
|
||||
|
||||
if (db && qjs_has_info_handler(curr_node.id)) {
|
||||
if (inst->value_time == 0) {
|
||||
qjs_call_info_handler(&curr_node, inst->value);
|
||||
inst->value_time = get_uptime_msecs();
|
||||
} else {
|
||||
unsigned int now = get_uptime_msecs();
|
||||
if ((now - inst->value_time) >= DB_INFO_TIMEOUT) {
|
||||
qjs_call_info_handler(&curr_node, inst->value);
|
||||
inst->value_time = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
||||
|
||||
struct inode_entry *inode_buf_find(const dm_node_t *node, int *retry)
|
||||
{
|
||||
int cnt = 0;
|
||||
dm_node_id_t nodes_id[MAX_DM_NODE_DEPTH];
|
||||
get_all_i_node(node->id, nodes_id, &cnt);
|
||||
|
||||
struct inode_entry *inode = inode_g;
|
||||
|
||||
int index_i = 0;
|
||||
for (int i = cnt - 1; i >= 0; i--) {
|
||||
struct inode_entry *entry = find_inode_entry(inode, nodes_id[i]);
|
||||
if (entry == NULL) {
|
||||
if (retry) {
|
||||
*retry = 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (entry->key == node->id) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
if (index_i >= node->cnt) {
|
||||
if (retry) {
|
||||
*retry = 0;
|
||||
}
|
||||
dmlog_error("inode_buf_find, invalid node(missing index) %s", dm_node_str(node));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inst_entry_t *inst = find_inst(entry, node->index[index_i], NULL);
|
||||
if (inst == NULL) {
|
||||
dmlog_error("inode_buf_find, instance not found %s", dm_node_str(node));
|
||||
if (retry) {
|
||||
*retry = 0;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
inode = inst->inode;
|
||||
index_i++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void create_inode_inst(struct inode_entry **inode, const dm_node_id_t key, dm_inst_array_t *insts, int dynamic)
|
||||
{
|
||||
struct inode_entry *entry = calloc(1, sizeof(struct inode_entry));
|
||||
entry->key = key;
|
||||
if (dynamic) {
|
||||
entry->dyn_insts = insts;
|
||||
entry->dyn_time = get_uptime_msecs();
|
||||
} else {
|
||||
entry->insts = insts;
|
||||
}
|
||||
|
||||
entry->next = *inode;
|
||||
*inode = entry;
|
||||
}
|
||||
|
||||
static void resize_if_needed(dm_inst_array_t *array) {
|
||||
if (array->size >= array->capacity) {
|
||||
size_t new_cap = array->capacity * GROWTH_FACTOR;
|
||||
inst_entry_t *new_data = realloc(array->data, new_cap * sizeof(inst_entry_t));
|
||||
if (new_data == NULL) {
|
||||
/* keep original buffer; log and return to avoid losing pointer */
|
||||
dmlog_error("resize_if_needed: realloc failed (cap=%zu)", new_cap);
|
||||
return;
|
||||
}
|
||||
array->data = new_data;
|
||||
array->capacity = new_cap;
|
||||
}
|
||||
}
|
||||
|
||||
void dm_inst_array_append(dm_inst_array_t *array, inst_entry_t *value) {
|
||||
resize_if_needed(array);
|
||||
array->data[array->size++] = *value;
|
||||
}
|
||||
|
||||
int dm_inst_array_remove(dm_inst_array_t *array, unsigned int index) {
|
||||
if (!array) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < array->size; i++) {
|
||||
if (array->data[i].index == index) {
|
||||
JS_FreeValue(qjs_ctx(), array->data[i].value);
|
||||
free_inode(array->data[i].inode);
|
||||
for (size_t j = i; j < array->size - 1; j++) {
|
||||
array->data[j] = array->data[j + 1];
|
||||
}
|
||||
array->size--;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inode_buf_append_db_inst(const dm_node_t *node, inst_entry_t *inst)
|
||||
{
|
||||
dm_inst_array_t * arr = dm_inst_array_create(1);
|
||||
dm_inst_array_append(arr, inst);
|
||||
return inode_buf_add_inst(node, arr, 0, 1);
|
||||
}
|
||||
|
||||
int inode_buf_del_db_inst(const dm_node_t *node)
|
||||
{
|
||||
int retry;
|
||||
struct inode_entry *entry = inode_buf_find(node, &retry);
|
||||
if (!entry) {
|
||||
if (retry == 0) {
|
||||
return -1;
|
||||
}
|
||||
dm_node_t n = *node;
|
||||
n.cnt = node->cnt - 1;
|
||||
dmapi_refresh_instance_buf(&n);
|
||||
entry = inode_buf_find(node, &retry);
|
||||
}
|
||||
|
||||
if (!entry) {
|
||||
dmlog_error("inode_buf_del_db_inst, node not found: %s", dm_node_str(node));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dm_inst_array_remove(entry->insts, dm_node_last_index(node))) {
|
||||
// dmlog_debug("removed instance from buffer: %s", dm_node_str(node));
|
||||
return 0;
|
||||
}
|
||||
|
||||
dmlog_error("inode_buf_del_db_inst, instance not found: %s", dm_node_str(node));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void update_instances(struct inode_entry *inode, dm_inst_array_t *insts, int dynamic)
|
||||
{
|
||||
if (dynamic) {
|
||||
dm_inst_array_free(inode->dyn_insts);
|
||||
inode->dyn_insts = insts;
|
||||
inode->dyn_time = get_uptime_msecs();
|
||||
} else {
|
||||
dm_inst_array_free(inode->insts);
|
||||
inode->insts = insts;
|
||||
}
|
||||
}
|
||||
|
||||
int inode_buf_add_inst(const dm_node_t *node, dm_inst_array_t *insts, int dynamic, int append)
|
||||
{
|
||||
dm_node_id_t nodes_id[MAX_DM_NODE_DEPTH];
|
||||
int cnt;
|
||||
get_all_i_node(node->id, nodes_id, &cnt);
|
||||
struct inode_entry **inode = &inode_g;
|
||||
int index_i = 0;
|
||||
|
||||
dm_node_t curr_node;
|
||||
curr_node.cnt = 0;
|
||||
// dmlog_debug("inode_buf_add_inst %s, inst_cnt: %d, dynamic: %d", dm_node_str(node), insts->size, dynamic);
|
||||
for (int i = cnt - 1; i >= 0; i--) {
|
||||
curr_node.id = nodes_id[i];
|
||||
struct inode_entry *entry = find_inode_entry(*inode, nodes_id[i]);
|
||||
if (entry == NULL && i != 0) {
|
||||
dmapi_refresh_instance_buf(&curr_node);
|
||||
entry = find_inode_entry(*inode, nodes_id[i]);
|
||||
}
|
||||
|
||||
if (entry == NULL) {
|
||||
if (i == 0) {
|
||||
// create the instance
|
||||
if (!append) {
|
||||
create_inode_inst(inode, nodes_id[i], insts, dynamic);
|
||||
} else {
|
||||
// dmlog_debug("first time to add instance, refresh all");
|
||||
dm_inst_array_free(insts);
|
||||
dmapi_refresh_instance_buf(node);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
dmlog_error("inode buffer not found: %s", dm_node_str(&curr_node));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (i == 0) {
|
||||
if (!append) {
|
||||
update_instances(entry, insts, dynamic);
|
||||
} else {
|
||||
// currently only append one instance.
|
||||
dm_inst_array_append(entry->insts, &insts->data[0]);
|
||||
if (insts->data) {
|
||||
free(insts->data);
|
||||
}
|
||||
free(insts);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
inst_entry_t *inst = find_inst(entry, node->index[index_i], NULL);
|
||||
if (inst == NULL) {
|
||||
dmlog_error("inode_buf_add_inst inst_entry not found");
|
||||
return -1;
|
||||
}
|
||||
index_i++;
|
||||
inode = &inst->inode;
|
||||
curr_node.index[curr_node.cnt] = inst->index;
|
||||
curr_node.cnt++;
|
||||
}
|
||||
|
||||
dmlog_error("failed to add instance buffer for %s", dm_node_str(node));
|
||||
return -1;
|
||||
}
|
||||
|
||||
int inode_buf_update_param_value(const dm_node_t *node, const char *value)
|
||||
{
|
||||
JSValue args[MAX_DM_NODE_DEPTH];
|
||||
int ret = inode_buf_get_js_values(node, args);
|
||||
if (ret != node->cnt) {
|
||||
dmlog_error("inode_buf_update_param_value failed, node:%s, ret: %d", dm_node_str(node), ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const struct dm_node_info *info = dm_node_get_info(node->id);
|
||||
JS_SetPropertyStr(qjs_ctx(), args[ret - 1], info->name, JS_NewString(qjs_ctx(), value));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void inode_buf_free_all()
|
||||
{
|
||||
free_inode(inode_g);
|
||||
inode_g = NULL;
|
||||
}
|
||||
|
||||
static void print_inst_array(dm_inst_array_t *arr, int ident)
|
||||
{
|
||||
if (arr == NULL) {
|
||||
dmlog_info("no instances");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < arr->size; i++) {
|
||||
const char *str = get_js_value_str(arr->data[i].value);
|
||||
dmlog_info("idx:%d", arr->data[i].index);
|
||||
dmlog_info("val: \n%s", str);
|
||||
free_js_cstr(str);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_inode_insts(struct inode_entry *inode, int level)
|
||||
{
|
||||
dmlog_info("db instances:");
|
||||
print_inst_array(inode->insts, level);
|
||||
dmlog_info("dynamic instances (created time:%d)", inode->dyn_time);
|
||||
print_inst_array(inode->dyn_insts, level);
|
||||
}
|
||||
|
||||
void inode_buf_dump(const dm_node_t *node)
|
||||
{
|
||||
dmlog_info("node buffer: %s", dm_node_str(node));
|
||||
struct inode_entry *entry = inode_buf_find(node, NULL);
|
||||
if (!entry) {
|
||||
dmlog_info("node buffer not found: %s", dm_node_str(node));
|
||||
return;
|
||||
}
|
||||
|
||||
if (dm_node_is_index_complete(node)) {
|
||||
int db;
|
||||
inst_entry_t *inst = find_inst(entry, node->index[node->cnt-1], &db);
|
||||
if (!inst) {
|
||||
dmlog_info("node buffer (inst) not found: %s", dm_node_str(node));
|
||||
return;
|
||||
} else {
|
||||
dmlog_info("instance buffer (db:%d):", db);
|
||||
dmlog_info("index: %d", inst->index);
|
||||
const char *str = get_js_value_str(inst->value);
|
||||
dmlog_info("value: %s", str);
|
||||
free_js_cstr(str);
|
||||
}
|
||||
} else {
|
||||
dmlog_info("inode buffer:");
|
||||
print_inode_insts(entry, 0);
|
||||
}
|
||||
}
|
||||
99
dm-framework/dm-api/src/core/inode_buf.h
Normal file
99
dm-framework/dm-api/src/core/inode_buf.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef I_NODE_BUF_H
|
||||
#define I_NODE_BUF_H
|
||||
|
||||
#include <time.h>
|
||||
#include <quickjs/quickjs.h>
|
||||
|
||||
#include "dm_node.h"
|
||||
|
||||
/*
|
||||
Buffer for Multi-object instance.
|
||||
An example of buffer is as following:
|
||||
|
||||
inode_entry * head -+
|
||||
|
|
||||
v
|
||||
inode_entry [Device.IP.Interface.{i}.] -> [Device.Ethernet.Link.{i}.]
|
||||
| |
|
||||
v v
|
||||
inst_entry [instance 1] -> [instance 2] ...
|
||||
|
|
||||
v
|
||||
inode_entry [Device.IP.Interface.{i}.IPv4Address.{i}.] -> [Device.IP.Interface.{i}.IPv46ddress.{i}.]
|
||||
|
|
||||
v
|
||||
inst_entry [instance 1] -> [instance 2]
|
||||
*/
|
||||
|
||||
struct inode_entry;
|
||||
|
||||
typedef struct {
|
||||
unsigned int index;
|
||||
JSValue value; // key value for the instance
|
||||
struct inode_entry *inode; // head of child inode buf (in recursive)
|
||||
unsigned int value_time;
|
||||
} inst_entry_t;
|
||||
|
||||
typedef struct {
|
||||
inst_entry_t *data;
|
||||
size_t size;
|
||||
size_t capacity;
|
||||
} dm_inst_array_t;
|
||||
|
||||
dm_inst_array_t* dm_inst_array_create(size_t initial_capacity);
|
||||
void dm_inst_array_append(dm_inst_array_t *array, inst_entry_t *value);
|
||||
|
||||
|
||||
struct inode_entry {
|
||||
dm_node_id_t key;
|
||||
dm_inst_array_t *insts;
|
||||
dm_inst_array_t *dyn_insts;
|
||||
struct inode_entry *next;
|
||||
unsigned int dyn_time;
|
||||
};
|
||||
|
||||
/** Buffer new db instances for the multi-instance object.
|
||||
* @param[in] node node of instance
|
||||
* @param[in] insts instance array
|
||||
* @param[in] cnt length of instance array
|
||||
* @param[in] dynamic if the instances are dynamic or in db
|
||||
* @return 0 in case of success, or a nonzero value in case of error
|
||||
*/
|
||||
int inode_buf_add_inst(const dm_node_t *node, dm_inst_array_t *insts, int dynamic, int append);
|
||||
|
||||
/** Get instance values for the node
|
||||
* @param[in] node node of instance
|
||||
* @param[in] values values to store the result
|
||||
* @return number of values found in case of success, or -1 in case of error
|
||||
*/
|
||||
int inode_buf_get_js_values(const dm_node_t *node, JSValue values[]);
|
||||
|
||||
/** find inode buffer according to node
|
||||
* @param[in] node node of instance
|
||||
* @param[in] retry can be retried or not
|
||||
* @return pointer of inode_entry if found, otherwise NULL
|
||||
*/
|
||||
struct inode_entry *inode_buf_find(const dm_node_t *node, int *retry);
|
||||
|
||||
/** Free all the buffer
|
||||
* @return none
|
||||
*/
|
||||
void inode_buf_free_all(void);
|
||||
|
||||
int inode_buf_append_db_inst(const dm_node_t *node, inst_entry_t *inst);
|
||||
int inode_buf_del_db_inst(const dm_node_t *node);
|
||||
int inode_buf_update_param_value(const dm_node_t *node, const char *value);
|
||||
|
||||
void inode_buf_dump(const dm_node_t *node);
|
||||
#endif
|
||||
20
dm-framework/dm-api/src/include/dm_log.h
Normal file
20
dm-framework/dm-api/src/include/dm_log.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DMLOG_H
|
||||
#define DMLOG_H
|
||||
void dmlog_init(const char *name, int log_level);
|
||||
void dmlog_info(const char *fmt, ...);
|
||||
void dmlog_warn(const char *fmt, ...);
|
||||
void dmlog_error(const char *fmt, ...);
|
||||
void dmlog_debug(const char *fmt, ...);
|
||||
void dmlog_close();
|
||||
#endif /* DMLOG_H */
|
||||
50
dm-framework/dm-api/src/include/qjs_uci_api.h
Executable file
50
dm-framework/dm-api/src/include/qjs_uci_api.h
Executable file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* QuickJS UCI helper – public API
|
||||
*
|
||||
* Provides initialisation helpers for the shared libuci context used by
|
||||
* qjs_uci_api.c as well as the QuickJS registration entry point.
|
||||
*/
|
||||
|
||||
#ifndef QJS_UCI_API_H
|
||||
#define QJS_UCI_API_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialise the shared libuci context.
|
||||
* Safe to call multiple times – subsequent calls are no-ops.
|
||||
* Returns 0 on success, -1 on failure.
|
||||
*/
|
||||
int qjs_uci_global_init(void);
|
||||
|
||||
/* Set the directory where libuci stores pending changes (may be NULL). */
|
||||
void qjs_uci_global_set_savedir(const char *save_dir);
|
||||
|
||||
/* Free the shared libuci context at shutdown (optional). */
|
||||
void qjs_uci_global_cleanup(void);
|
||||
|
||||
/* Register the _uci_call() C binding with QuickJS. */
|
||||
int qjs_uci_api_init(void);
|
||||
|
||||
#include <json-c/json.h>
|
||||
|
||||
/* Simple name/value helper used by dm_uci_* helpers */
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *value;
|
||||
} name_val_t;
|
||||
|
||||
int dm_uci_get(const char *uci_path, char **value);
|
||||
int dm_uci_set(const char *uci_path, const char *value);
|
||||
int dm_uci_add(const char *config, const char *type, const char *section_name, name_val_t *opt_values, int value_cnt);
|
||||
int dm_uci_del(const char *config, const char *section);
|
||||
int dm_uci_get_section_list(const char *config, const char *type, name_val_t *match, int match_cnt, struct json_object **res);
|
||||
int dm_uci_commit(const char *config);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* QJS_UCI_API_H */
|
||||
BIN
dm-framework/dm-api/src/quickjs/._qjs.c
Executable file
BIN
dm-framework/dm-api/src/quickjs/._qjs.c
Executable file
Binary file not shown.
BIN
dm-framework/dm-api/src/quickjs/._qjs_dm_api.c
Executable file
BIN
dm-framework/dm-api/src/quickjs/._qjs_dm_api.c
Executable file
Binary file not shown.
1145
dm-framework/dm-api/src/quickjs/qjs.c
Normal file
1145
dm-framework/dm-api/src/quickjs/qjs.c
Normal file
File diff suppressed because it is too large
Load Diff
55
dm-framework/dm-api/src/quickjs/qjs.h
Normal file
55
dm-framework/dm-api/src/quickjs/qjs.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QJS_H
|
||||
#define QJS_H
|
||||
|
||||
#include <uci.h>
|
||||
#include <json-c/json.h>
|
||||
#include <quickjs/quickjs.h>
|
||||
|
||||
#include "dm_node.h"
|
||||
|
||||
int qjs_init(void);
|
||||
|
||||
void qjs_register_c_api(const char *name, JSCFunction func, int param_len);
|
||||
int qjs_has_get_handler(dm_node_id_t id);
|
||||
int qjs_has_set_handler(dm_node_id_t id);
|
||||
int qjs_call_set_handler(const dm_node_t *node, const char *value);
|
||||
int qjs_has_import_handler(dm_node_id_t id);
|
||||
int qjs_has_apply_handler(dm_node_id_t id);
|
||||
int qjs_has_changed_handler(dm_node_id_t id);
|
||||
int qjs_has_info_handler(dm_node_id_t id);
|
||||
int qjs_has_key_handler(dm_node_id_t id);
|
||||
|
||||
int qjs_call_get_handler(const dm_node_t *node, char **res);
|
||||
int qjs_call_apply_param_handler(const dm_node_t *node, const char *value);
|
||||
int qjs_call_apply_obj_handler(const dm_node_t *node, const dm_node_t *ext_node);
|
||||
JSValue qjs_call_import_handler(const dm_node_t *node, JSValue values);
|
||||
int qjs_call_operate_handler(const dm_node_t *node, const char *json_input, json_object **json_output);
|
||||
JSValue qjs_call_get_instance_handler(const dm_node_t *node, int start_index);
|
||||
int qjs_call_changed_handler(const dm_node_t *node);
|
||||
int qjs_call_info_handler(const dm_node_t *node, JSValue obj);
|
||||
int qjs_call_key_handler(const dm_node_t *node, char **res);
|
||||
JSContext *qjs_ctx();
|
||||
JSValue qjs_global();
|
||||
int qjs_array_len(JSValue arr);
|
||||
const char *get_js_value_str(JSValue value);
|
||||
const char *qjs_get_property_str(JSValue obj, const char *prop_name);
|
||||
void free_js_cstr(const char *str);
|
||||
void qjs_log_exception();
|
||||
JSValue qjs_eval_buf(const void *buf, int buf_len, const char *filename, int eval_flags);
|
||||
JSValue json_object_to_jsvalue(json_object *json_obj);
|
||||
int qjs_uci_filter(const dm_node_t *node, json_object *obj);
|
||||
int qjs_call_event_handler(dm_node_id_t node_id, const char *ubus_json_msg, struct json_object **res);
|
||||
int qjs_call_uci_init_handler(const dm_node_t *node);
|
||||
int qjs_call_uci_deinit_handler(const dm_node_t *node, const char *uci_section);
|
||||
#endif
|
||||
18
dm-framework/dm-api/src/quickjs/qjs_api.h
Normal file
18
dm-framework/dm-api/src/quickjs/qjs_api.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QJS_API_H
|
||||
#define QJS_API_H
|
||||
|
||||
int qjs_dm_api_init(void);
|
||||
int qjs_ubus_api_init(void);
|
||||
int qjs_log_api_init(void);
|
||||
#endif
|
||||
580
dm-framework/dm-api/src/quickjs/qjs_dm_api.c
Normal file
580
dm-framework/dm-api/src/quickjs/qjs_dm_api.c
Normal file
@@ -0,0 +1,580 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <quickjs/quickjs-libc.h>
|
||||
#include <quickjs/quickjs.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dbmgr.h"
|
||||
#include "dm_api.h"
|
||||
#include "dm_apply.h"
|
||||
#include "dm_log.h"
|
||||
#include "dm_node.h"
|
||||
#include "dm_types.h"
|
||||
#include "qjs.h"
|
||||
#include "qjs_api.h"
|
||||
#include "inode_buf.h"
|
||||
#include "dm_linker.h"
|
||||
|
||||
JSValue get_object(const dm_node_t *node, int only_db)
|
||||
{
|
||||
int i;
|
||||
if (dm_node_is_parameter(node->id)) {
|
||||
char *value = NULL;
|
||||
if (only_db) {
|
||||
if (dbmgr_get(node, &value) < 0 || value == NULL) {
|
||||
dmlog_error("dbmgr_get failed to get param: %s", dm_node_str(node));
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
} else {
|
||||
if (dmapi_param_get(node, &value) < 0 || value == NULL) {
|
||||
dmlog_error("dmapi_param_get failed to get param: %s", dm_node_str(node));
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue param;
|
||||
enum DM_DATA_TYPE type = dm_node_data_type(node->id);
|
||||
if (dm_node_is_param_list(node->id)) {
|
||||
param = JS_NewString(qjs_ctx(), value);
|
||||
} else if (type == DM_DATA_BOOLEAN) {
|
||||
if (strcmp(value, "1") == 0 || strcmp(value, "true") == 0) {
|
||||
param = JS_NewBool(qjs_ctx(), 1);
|
||||
} else {
|
||||
param = JS_NewBool(qjs_ctx(), 0);
|
||||
}
|
||||
} else if (type == DM_DATA_INT) {
|
||||
param = JS_NewInt32(qjs_ctx(), atoi(value));
|
||||
} else if (type == DM_DATA_UINT || type == DM_DATA_ULONG) {
|
||||
param = JS_NewUint32(qjs_ctx(), strtoul (value, NULL, 0));
|
||||
} else {
|
||||
param = JS_NewString(qjs_ctx(), value);
|
||||
}
|
||||
|
||||
free(value);
|
||||
return param;
|
||||
} else if (dm_node_is_object(node->id) || (dm_node_is_objectlist(node->id) && dm_node_is_index_complete(node))) {
|
||||
const struct dm_object *obj = dm_node_get_object(node->id);
|
||||
JSValue obj_val = JS_NewObject(qjs_ctx());
|
||||
|
||||
for (i = 0; i < obj->param_num; i++) {
|
||||
dm_node_t param_node = *node;
|
||||
param_node.id = obj->param_list[i]->node_id;
|
||||
if (only_db && !dm_node_has_db(param_node.id)) {
|
||||
continue;
|
||||
}
|
||||
JSValue param_val = get_object(¶m_node, only_db);
|
||||
if (JS_IsException(param_val)) {
|
||||
JS_FreeValue(qjs_ctx(), obj_val);
|
||||
return JS_UNDEFINED;
|
||||
} else {
|
||||
JS_SetPropertyStr(qjs_ctx(), obj_val, obj->param_list[i]->name, param_val);
|
||||
}
|
||||
}
|
||||
|
||||
if (dm_node_is_objectlist(node->id)) {
|
||||
JS_SetPropertyStr(qjs_ctx(), obj_val, ".index", JS_NewUint32(qjs_ctx(), node->index[node->cnt - 1]));
|
||||
}
|
||||
|
||||
for (i = 0; i < obj->object_num; i++) {
|
||||
dm_node_t obj_node = *node;
|
||||
obj_node.id = obj->object_list[i]->node_id;
|
||||
if (dm_node_is_objectlist(obj_node.id)
|
||||
&& only_db && !dm_node_has_db(obj_node.id)) {
|
||||
continue;
|
||||
}
|
||||
JSValue child_obj = get_object(&obj_node, only_db);
|
||||
if (JS_IsUndefined(child_obj)) {
|
||||
JS_FreeValue(qjs_ctx(), obj_val);
|
||||
return JS_UNDEFINED;
|
||||
} else {
|
||||
JS_SetPropertyStr(qjs_ctx(), obj_val, obj->object_list[i]->name, child_obj);
|
||||
}
|
||||
}
|
||||
|
||||
return obj_val;
|
||||
} else if (dm_node_is_objectlist(node->id)) {
|
||||
dm_nodelist_h list = dm_nodelist_find(node, NULL, only_db);
|
||||
JSValue arr = JS_NewArray(qjs_ctx());
|
||||
// JS_SetPropertyStr(qjs_ctx(), res, node->name, arr);
|
||||
for (i = 0; i < dm_nodelist_cnt(list); i++) {
|
||||
const dm_node_t *n = dm_nodelist_node(list, i);
|
||||
JSValue ins_val = get_object(n, only_db);
|
||||
if (JS_IsUndefined(ins_val)) {
|
||||
JS_FreeValue(qjs_ctx(), arr);
|
||||
/* free node list before returning to avoid leak */
|
||||
dm_nodelist_free(list);
|
||||
return JS_UNDEFINED;
|
||||
} else {
|
||||
// add index into the objecct
|
||||
// JS_SetPropertyStr(qjs_ctx(), ins_val, "index", JS_NewInt32(qjs_ctx(), n->index[n->cnt - 1]));
|
||||
JS_DefinePropertyValueUint32(qjs_ctx(), arr, i, ins_val, JS_PROP_C_W_E);
|
||||
}
|
||||
}
|
||||
dm_nodelist_free(list);
|
||||
return arr;
|
||||
} else {
|
||||
dmlog_error("get_object, unknown node: %s", dm_node_str(node));
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue _dm_get(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int only_db = 1;
|
||||
dm_node_t node;
|
||||
if (argc < 1) {
|
||||
dmlog_info("_dm_get, missing argument");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
if (JS_IsString(argv[0])) {
|
||||
const char *path = JS_ToCString(ctx, argv[0]);
|
||||
if (dm_path2node(path, &node) < 0) {
|
||||
dmlog_error("_dm_get, invalid pathname: %s", path);
|
||||
JS_FreeCString(ctx, path);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JS_FreeCString(ctx, path);
|
||||
|
||||
if (argc >= 2) {
|
||||
only_db = JS_ToBool(ctx, argv[1]);
|
||||
}
|
||||
} else if (JS_IsNumber(argv[0])) {
|
||||
JS_ToUint32(ctx, &node.id, argv[0]);
|
||||
if (argc >= 2) {
|
||||
if (JS_IsNumber(argv[1])) {
|
||||
node.cnt = 1;
|
||||
JS_ToUint32(ctx, &node.index[0], argv[1]);
|
||||
} else if (JS_IsArray(ctx, argv[1])) {
|
||||
JSValue length = JS_GetPropertyStr(ctx, argv[1], "length");
|
||||
JS_ToInt32(ctx, &node.cnt , length);
|
||||
JS_FreeValue(ctx, length);
|
||||
for (unsigned int i = 0; i < node.cnt; i++) {
|
||||
JSValue index_val = JS_GetPropertyUint32(ctx, argv[1], i);
|
||||
JS_ToUint32(ctx, &node.index[i], index_val);
|
||||
JS_FreeValue(ctx, index_val);
|
||||
}
|
||||
} else {
|
||||
dmlog_error("_dm_get, invalid index");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
} else {
|
||||
node.cnt = 0;
|
||||
}
|
||||
} else {
|
||||
dmlog_error("_dm_get, invalid parameter");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue val = get_object(&node, only_db);
|
||||
if (JS_IsException(val)) {
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
static JSValue _set_param_value(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, int only_db)
|
||||
{
|
||||
int ret = -1;
|
||||
dm_node_t node;
|
||||
if (argc < 2) {
|
||||
dmlog_info("_dm_set, missing argument");
|
||||
return JS_NewInt32(ctx, ret);
|
||||
}
|
||||
|
||||
const char *value = NULL;
|
||||
|
||||
if (JS_IsString(argv[0])) {
|
||||
const char *path = JS_ToCString(ctx, argv[0]);
|
||||
if (dm_path2node(path, &node) < 0) {
|
||||
dmlog_error("_dm_set, invalid pathname: %s", path);
|
||||
JS_FreeCString(ctx, path);
|
||||
return JS_NewInt32(ctx, ret);
|
||||
}
|
||||
JS_FreeCString(ctx, path);
|
||||
value = JS_ToCString(ctx, argv[1]);
|
||||
} else if (JS_IsNumber(argv[0])) {
|
||||
JS_ToUint32(ctx, &node.id, argv[0]);
|
||||
if (dm_node_index_cnt(node.id) > 0) {
|
||||
if (JS_IsNumber(argv[1])) {
|
||||
node.cnt = 1;
|
||||
JS_ToUint32(ctx, &node.index[0], argv[1]);
|
||||
} else if (JS_IsArray(ctx, argv[1])) {
|
||||
JSValue length = JS_GetPropertyStr(ctx, argv[1], "length");
|
||||
JS_ToInt32(ctx, &node.cnt , length);
|
||||
JS_FreeValue(ctx, length);
|
||||
for (unsigned int i = 0; i < node.cnt; i++) {
|
||||
JSValue index_val = JS_GetPropertyUint32(ctx, argv[1], i);
|
||||
JS_ToUint32(ctx, &node.index[i], index_val);
|
||||
JS_FreeValue(ctx, index_val);
|
||||
}
|
||||
} else {
|
||||
dmlog_error("_dm_get, invalid index");
|
||||
return JS_NewInt32(ctx, ret);
|
||||
}
|
||||
value = JS_ToCString(ctx, argv[2]);
|
||||
} else {
|
||||
node.cnt = 0;
|
||||
value = JS_ToCString(ctx, argv[1]);
|
||||
}
|
||||
} else {
|
||||
dmlog_error("_dm_get, invalid parameter");
|
||||
return JS_NewInt32(ctx, ret);
|
||||
}
|
||||
|
||||
if (value != NULL) {
|
||||
if (only_db) {
|
||||
// dbmgr_tranx_begin();
|
||||
ret = dbmgr_set(&node, value);
|
||||
if (dm_node_index_cnt(node.id) > 0) {
|
||||
ret |= inode_buf_update_param_value(&node, value);
|
||||
}
|
||||
// dbmgr_tranx_commit();
|
||||
} else {
|
||||
dmapi_param_set(&node, value);
|
||||
}
|
||||
JS_FreeCString(ctx, value);
|
||||
} else {
|
||||
dmlog_error("_dm_set, missing value");
|
||||
return JS_NewInt32(ctx, -1);
|
||||
}
|
||||
|
||||
return JS_NewInt32(ctx, ret);
|
||||
}
|
||||
|
||||
static JSValue _db_set(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return _set_param_value(ctx, this_val, argc, argv, 1);
|
||||
}
|
||||
|
||||
static JSValue _dm_set(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return _set_param_value(ctx, this_val, argc, argv, 0);
|
||||
}
|
||||
|
||||
static JSValue _dm_node_id(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
dm_node_t node;
|
||||
if (argc < 1 || !JS_IsString(argv[0])) {
|
||||
dmlog_info("_dm_node_id, invalid argument");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
const char *path = JS_ToCString(ctx, argv[0]);
|
||||
if (dm_path2node(path, &node) < 0) {
|
||||
dmlog_info("_dm_node_id, invalid pathname: %s", path);
|
||||
return JS_NewInt32(ctx, -1);
|
||||
}
|
||||
JS_FreeCString(ctx, path);
|
||||
return JS_NewUint32(ctx, node.id);
|
||||
}
|
||||
|
||||
static JSValue _dm_node(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
dm_node_t node;
|
||||
if (argc < 1 || !JS_IsString(argv[0])) {
|
||||
dmlog_info("_dm_node, invalid argument");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JSValue arr = JS_NewArray(qjs_ctx());
|
||||
|
||||
const char *path = JS_ToCString(ctx, argv[0]);
|
||||
if (dm_path2node(path, &node) < 0) {
|
||||
dmlog_info("_dm_node, invalid pathname: %s", path);
|
||||
JS_DefinePropertyValueUint32(qjs_ctx(), arr, 0, JS_NewInt32(ctx, -1), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueUint32(qjs_ctx(), arr, 1, JS_NewArray(qjs_ctx()), JS_PROP_C_W_E);
|
||||
} else {
|
||||
JS_DefinePropertyValueUint32(qjs_ctx(), arr, 0, JS_NewUint32(ctx, node.id), JS_PROP_C_W_E);
|
||||
JSValue index_arr = JS_NewArray(qjs_ctx());
|
||||
for (int i = 0; i < node.cnt; i++) {
|
||||
JS_DefinePropertyValueUint32(qjs_ctx(), index_arr, i, JS_NewUint32(ctx, node.index[i]), JS_PROP_C_W_E);
|
||||
}
|
||||
|
||||
JS_DefinePropertyValueUint32(qjs_ctx(), arr, 1, index_arr, JS_PROP_C_W_E);
|
||||
}
|
||||
|
||||
JS_FreeCString(ctx, path);
|
||||
return arr;
|
||||
}
|
||||
|
||||
static JSValue _dm_instances(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int only_db = 1;
|
||||
dm_node_t node;
|
||||
const char *keys = NULL;
|
||||
if (argc < 1) {
|
||||
dmlog_info("_dm_node, invalid argument");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue arr = JS_NewArray(qjs_ctx());
|
||||
const char *path = JS_ToCString(ctx, argv[0]);
|
||||
if (dm_path2node(path, &node) < 0) {
|
||||
dmlog_info("_dm_node, invalid pathname: %s", path);
|
||||
JS_FreeCString(ctx, path);
|
||||
return arr;
|
||||
}
|
||||
JS_FreeCString(ctx, path);
|
||||
|
||||
if (argc >= 2 && JS_IsString(argv[1])) {
|
||||
keys = JS_ToCString(ctx, argv[1]);
|
||||
}
|
||||
|
||||
if (argc >= 3) {
|
||||
only_db = JS_ToBool(ctx, argv[2]);
|
||||
}
|
||||
|
||||
dm_nodelist_h list = dm_nodelist_find(&node, keys, only_db);
|
||||
if (keys != NULL) {
|
||||
JS_FreeCString(ctx, keys);
|
||||
}
|
||||
|
||||
const dm_node_t *pnode = NULL;
|
||||
int i = 0;
|
||||
nodelist_for_each_node(pnode, list)
|
||||
{
|
||||
dm_path_t path;
|
||||
dm_node2name(pnode, path, sizeof(path));
|
||||
JS_DefinePropertyValueUint32(qjs_ctx(), arr, i++, JS_NewString(ctx, path), JS_PROP_C_W_E);
|
||||
}
|
||||
|
||||
dm_nodelist_free(list);
|
||||
return arr;
|
||||
}
|
||||
|
||||
int update_dm_value(JSContext *ctx, const char *key, JSValue value)
|
||||
{
|
||||
if (JS_IsString(value) || JS_IsNumber(value) || JS_IsBool(value)) {
|
||||
dm_node_t node;
|
||||
if (dm_path2node(key, &node) < 0) {
|
||||
dmlog_error("update_dm_value, invalid pathname: %s", key);
|
||||
return -1;
|
||||
}
|
||||
if (!dm_node_is_parameter(node.id) || !dm_node_is_index_complete(&node)) {
|
||||
dmlog_error("update_dm_value, unexpected parameter: %s", key);
|
||||
return -1;
|
||||
}
|
||||
const char *value_str = JS_ToCString(ctx, value);
|
||||
if (dm_node_is_bool_type(node.id)) {
|
||||
if (*value_str == '1' || !strcmp(value_str, "true")) {
|
||||
dmapi_param_set(&node, "true");
|
||||
} else {
|
||||
dmapi_param_set(&node, "false");
|
||||
}
|
||||
} else {
|
||||
dmapi_param_set(&node, value_str);
|
||||
}
|
||||
JS_FreeCString(ctx, value_str);
|
||||
} else if (JS_IsArray(ctx, value)) {
|
||||
dm_node_t node;
|
||||
if (dm_path2node(key, &node) < 0) {
|
||||
dmlog_error("update_dm_value, invalid pathname: %s", key);
|
||||
return -1;
|
||||
}
|
||||
// multi-instance node is expected.
|
||||
if (!dm_node_is_objectlist(node.id)) {
|
||||
dmlog_error("update_dm_value, unexpected multi-instance object: %s", key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t len;
|
||||
JSValue length = JS_GetPropertyStr(ctx, value, "length");
|
||||
JS_ToUint32(ctx, &len, length);
|
||||
JS_FreeValue(ctx, length);
|
||||
|
||||
for (unsigned int i = 0; i < len; i++) {
|
||||
if (dmapi_object_add(&node) < 0) {
|
||||
dmlog_error("update_dm_value, failed to add instance %s", key);
|
||||
return -1;
|
||||
}
|
||||
|
||||
dm_path_t path;
|
||||
dm_node2name(&node, path, sizeof(path));
|
||||
strlcat(path, ".", sizeof(path));
|
||||
// new path as the new key
|
||||
JSValue inst_val = JS_GetPropertyUint32(ctx, value, i);
|
||||
if (update_dm_value(ctx, path, inst_val) < 0) {
|
||||
dmlog_error("update_dm_value, failed to update instance %s", path);
|
||||
JS_FreeValue(ctx, inst_val);
|
||||
return -1;
|
||||
}
|
||||
JS_FreeValue(ctx, inst_val);
|
||||
}
|
||||
} else if (JS_IsObject(value)) {
|
||||
uint32_t len, i;
|
||||
JSPropertyEnum *tab;
|
||||
if (JS_GetOwnPropertyNames(ctx, &tab, &len, value,
|
||||
JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) {
|
||||
dmlog_error("_dm_update, JS_GetOwnPropertyNames failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
JSValue child_val = JS_GetProperty(ctx, value, tab[i].atom);
|
||||
const char *child_key = JS_AtomToCString(ctx, tab[i].atom);
|
||||
char *new_key = NULL;
|
||||
asprintf(&new_key, "%s%s", key, child_key);
|
||||
update_dm_value(ctx, new_key, child_val);
|
||||
free(new_key);
|
||||
/* release converted atom string */
|
||||
JS_FreeCString(ctx, child_key);
|
||||
JS_FreeValue(ctx, child_val);
|
||||
}
|
||||
js_free(ctx, tab);
|
||||
} else if (JS_IsUndefined(value)) {
|
||||
dmlog_debug("update_dm_value, skip update value for %s", key);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static JSValue _dm_update(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
dmlog_info("_dm_update, invalid argument");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
const char *path = JS_ToCString(ctx, argv[0]);
|
||||
int ret = update_dm_value(ctx, path, argv[1]);
|
||||
/* release path string allocated by QuickJS */
|
||||
JS_FreeCString(ctx, path);
|
||||
return JS_NewInt32(ctx, ret);
|
||||
}
|
||||
|
||||
static JSValue _dm_apply(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
dm_node_t node;
|
||||
if (argc < 1) {
|
||||
dmlog_info("_dm_apply, missing argument");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
if (JS_IsString(argv[0])) {
|
||||
const char *path = JS_ToCString(ctx, argv[0]);
|
||||
if (dm_path2node(path, &node) < 0) {
|
||||
dmlog_error("_dm_apply, invalid pathname: %s", path);
|
||||
JS_FreeCString(ctx, path);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
JS_FreeCString(ctx, path);
|
||||
} else if (JS_IsNumber(argv[0])) {
|
||||
JS_ToUint32(ctx, &node.id, argv[0]);
|
||||
if (argc >= 2) {
|
||||
if (JS_IsNumber(argv[1])) {
|
||||
node.cnt = 1;
|
||||
JS_ToUint32(ctx, &node.index[0], argv[1]);
|
||||
} else if (JS_IsArray(ctx, argv[1])) {
|
||||
JSValue length = JS_GetPropertyStr(ctx, argv[1], "length");
|
||||
JS_ToInt32(ctx, &node.cnt , length);
|
||||
JS_FreeValue(ctx, length);
|
||||
for (unsigned int i = 0; i < node.cnt; i++) {
|
||||
JSValue index_val = JS_GetPropertyUint32(ctx, argv[1], i);
|
||||
JS_ToUint32(ctx, &node.index[i], index_val);
|
||||
JS_FreeValue(ctx, index_val);
|
||||
}
|
||||
} else {
|
||||
dmlog_error("_dm_apply, invalid index");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
} else {
|
||||
node.cnt = 0;
|
||||
}
|
||||
} else {
|
||||
dmlog_error("_dm_apply, invalid parameter");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
struct node_change change;
|
||||
change.node = node;
|
||||
change.redirected = 0;
|
||||
change.action = DATA_MODEL_SET;
|
||||
dm_apply_node(&change);
|
||||
return JS_NewInt32(ctx, 0);
|
||||
}
|
||||
|
||||
static JSValue _dm_resolve(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
if (argc < 1 || !JS_IsString(argv[0])) {
|
||||
dmlog_info("_dm_resolve, invalid argument");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
const char *path = JS_ToCString(ctx, argv[0]);
|
||||
char *val = NULL;
|
||||
if (dm_resolve_linker(path, &val) != 0 || val == NULL) {
|
||||
JS_FreeCString(ctx, path);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue js_val = JS_NewString(ctx, val);
|
||||
free(val);
|
||||
JS_FreeCString(ctx, path);
|
||||
return js_val;
|
||||
}
|
||||
|
||||
static JSValue _dm_resolve_path(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
if (argc < 3 || !JS_IsString(argv[0]) || !JS_IsString(argv[1]) || !JS_IsString(argv[2])) {
|
||||
dmlog_info("_dm_resolve_path, invalid argument");
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
const char *base_path = JS_ToCString(ctx, argv[0]);
|
||||
const char *key_name = JS_ToCString(ctx, argv[1]);
|
||||
const char *key_value = JS_ToCString(ctx, argv[2]);
|
||||
|
||||
char *obj_path = NULL;
|
||||
if (dm_resolve_linker_path(base_path, key_name, key_value, &obj_path) != 0 || obj_path == NULL) {
|
||||
JS_FreeCString(ctx, base_path);
|
||||
JS_FreeCString(ctx, key_name);
|
||||
JS_FreeCString(ctx, key_value);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue js_val = JS_NewString(ctx, obj_path);
|
||||
|
||||
free(obj_path);
|
||||
JS_FreeCString(ctx, base_path);
|
||||
JS_FreeCString(ctx, key_name);
|
||||
JS_FreeCString(ctx, key_value);
|
||||
|
||||
return js_val;
|
||||
}
|
||||
|
||||
int qjs_dm_api_init()
|
||||
{
|
||||
qjs_register_c_api("_dm_get", _dm_get, 1);
|
||||
qjs_register_c_api("_dm_set", _dm_set, 2);
|
||||
qjs_register_c_api("_db_set", _db_set, 2);
|
||||
qjs_register_c_api("_dm_node_id", _dm_node_id, 1);
|
||||
qjs_register_c_api("_dm_node", _dm_node, 1);
|
||||
qjs_register_c_api("_dm_instances", _dm_instances, 1);
|
||||
qjs_register_c_api("_dm_update", _dm_update, 1);
|
||||
qjs_register_c_api("_dm_apply", _dm_apply, 1);
|
||||
qjs_register_c_api("_dm_linker_value", _dm_resolve, 1);
|
||||
qjs_register_c_api("_dm_linker_path", _dm_resolve_path, 3);
|
||||
return 0;
|
||||
}
|
||||
70
dm-framework/dm-api/src/quickjs/qjs_log.c
Normal file
70
dm-framework/dm-api/src/quickjs/qjs_log.c
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <quickjs/quickjs.h>
|
||||
|
||||
#include "dm_log.h"
|
||||
#include "qjs.h"
|
||||
#include "qjs_api.h"
|
||||
|
||||
enum LOG_TYPE {
|
||||
QJS_LOG_INFO,
|
||||
QJS_LOG_ERROR,
|
||||
QJS_LOG_DEBUG
|
||||
};
|
||||
|
||||
static JSValue _log(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv, enum LOG_TYPE type)
|
||||
{
|
||||
// keep it simple at the moment: syslog for each parameter independently.
|
||||
for (int i = 0; i < argc; i++) {
|
||||
JSValue json = JS_JSONStringify(ctx, argv[i], JS_UNDEFINED, JS_UNDEFINED);
|
||||
const char *msg = JS_ToCString(ctx, json);
|
||||
if (type == QJS_LOG_INFO) {
|
||||
dmlog_info(msg);
|
||||
} else if (type == QJS_LOG_ERROR) {
|
||||
dmlog_error(msg);
|
||||
} else {
|
||||
dmlog_debug(msg);
|
||||
}
|
||||
|
||||
JS_FreeCString(ctx, msg);
|
||||
JS_FreeValue(ctx, json);
|
||||
}
|
||||
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue _log_info(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return _log(ctx, this_val, argc, argv, QJS_LOG_INFO);
|
||||
}
|
||||
|
||||
static JSValue _log_error(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return _log(ctx, this_val, argc, argv, QJS_LOG_ERROR);
|
||||
}
|
||||
|
||||
static JSValue _log_debug(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
return _log(ctx, this_val, argc, argv, QJS_LOG_DEBUG);
|
||||
}
|
||||
|
||||
int qjs_log_api_init()
|
||||
{
|
||||
qjs_register_c_api("_log_info", _log_info, 1);
|
||||
qjs_register_c_api("_log_error", _log_error, 1);
|
||||
qjs_register_c_api("_log_debug", _log_debug, 1);
|
||||
return 0;
|
||||
}
|
||||
267
dm-framework/dm-api/src/quickjs/qjs_ubus_api.c
Normal file
267
dm-framework/dm-api/src/quickjs/qjs_ubus_api.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <json-c/json.h>
|
||||
#include <quickjs/quickjs-libc.h>
|
||||
#include <quickjs/quickjs.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "dm_log.h"
|
||||
#include "qjs.h"
|
||||
#include "qjs_api.h"
|
||||
#include "ubus_client.h"
|
||||
#include "dm_apply.h"
|
||||
|
||||
struct ubus_event_data {
|
||||
struct uloop_timeout tm;
|
||||
struct ubus_event_handler ev;
|
||||
struct blob_attr *msg;
|
||||
int res;
|
||||
};
|
||||
|
||||
struct invoke_cb_arg {
|
||||
JSContext *ctx;
|
||||
JSValue res;
|
||||
};
|
||||
|
||||
static void ubus_invoke_cb(struct ubus_request *req, int type, struct blob_attr *msg)
|
||||
{
|
||||
struct invoke_cb_arg *arg = (struct invoke_cb_arg *)req->priv;
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
char *json_str = blobmsg_format_json(msg, true);
|
||||
if (json_str == NULL) {
|
||||
dmlog_error("blobmsg_format_json failed");
|
||||
return;
|
||||
}
|
||||
// dmlog_debug("ubus_invoke_cb: %s", json_str);
|
||||
arg->res = JS_ParseJSON(arg->ctx, json_str, strlen(json_str), "ubus_invoke_cb");
|
||||
if (JS_IsException(arg->res)) {
|
||||
dmlog_error("JS_ParseJSON failed %s", json_str);
|
||||
}
|
||||
free(json_str);
|
||||
}
|
||||
|
||||
static JSValue _ubus_call(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
int ret = -1;
|
||||
const char *path = JS_ToCString(ctx, argv[0]);
|
||||
const char *method = JS_ToCString(ctx, argv[1]);
|
||||
struct invoke_cb_arg arg;
|
||||
arg.ctx = ctx;
|
||||
arg.res = JS_UNDEFINED;
|
||||
|
||||
// dmlog_debug("ubus %s: %s, argc: %d", path, method, argc);
|
||||
|
||||
if (argc > 2) {
|
||||
struct blob_buf bb = {};
|
||||
if (!JS_IsObject(argv[2])) {
|
||||
dmlog_error("_ubus_call, object is expected as ubus arguments");
|
||||
goto end;
|
||||
}
|
||||
JSValue json = JS_JSONStringify(ctx, argv[2], JS_UNDEFINED, JS_UNDEFINED);
|
||||
if (JS_IsException(json)) {
|
||||
dmlog_error("JS_ParseJSON failed");
|
||||
goto end;
|
||||
}
|
||||
blob_buf_init(&bb, 0);
|
||||
const char *str = JS_ToCString(ctx, json);
|
||||
if (!blobmsg_add_json_from_string(&bb, str)) {
|
||||
dmlog_error("blobmsg_add_json_from_string failed");
|
||||
JS_FreeCString(ctx, str);
|
||||
JS_FreeValue(ctx, json);
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = ubus_client_call(path, method, bb.head, ubus_invoke_cb, &arg);
|
||||
blob_buf_free(&bb);
|
||||
|
||||
if (strcmp(path, "uci") == 0 &&
|
||||
(strcmp(method, "set") == 0 || strcmp(method, "add") == 0 || strcmp(method, "delete") == 0)) {
|
||||
dmlog_debug("ubus uci %s: args: %s", method, str);
|
||||
if (ret == 0) {
|
||||
JSValue val = JS_GetPropertyStr(ctx, argv[2], "config");
|
||||
if (JS_IsString(val)) {
|
||||
const char *config = JS_ToCString(ctx, val);
|
||||
add_apply_package(strdup(config));
|
||||
JS_FreeCString(ctx, config);
|
||||
}
|
||||
JS_FreeValue(ctx, val);
|
||||
}
|
||||
}
|
||||
|
||||
JS_FreeCString(ctx, str);
|
||||
JS_FreeValue(ctx, json);
|
||||
} else {
|
||||
ret = ubus_client_call(path, method, NULL, ubus_invoke_cb, &arg);
|
||||
}
|
||||
|
||||
if (ret != 0 || JS_IsException(arg.res)) {
|
||||
ret = -1;
|
||||
}
|
||||
end:
|
||||
JS_FreeCString(ctx, path);
|
||||
JS_FreeCString(ctx, method);
|
||||
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_SetPropertyUint32(ctx, arr, 0, JS_NewInt32(ctx, ret));
|
||||
JS_SetPropertyUint32(ctx, arr, 1, arg.res);
|
||||
return arr;
|
||||
}
|
||||
|
||||
static void ubus_listen_timeout(struct uloop_timeout *timeout)
|
||||
{
|
||||
uloop_end();
|
||||
}
|
||||
|
||||
static bool compare_blob_msg(struct blob_attr *src_attr, struct blob_attr *dst_attr)
|
||||
{
|
||||
if (!src_attr || !dst_attr)
|
||||
return false;
|
||||
|
||||
int src_type = blob_id(src_attr);
|
||||
int dst_type = blob_id(dst_attr);
|
||||
if (src_type != dst_type)
|
||||
return false;
|
||||
|
||||
void *src_val = blobmsg_data(src_attr);
|
||||
void *dst_val = blobmsg_data(dst_attr);
|
||||
|
||||
switch (src_type) {
|
||||
case BLOBMSG_TYPE_STRING:
|
||||
if (src_val == NULL && dst_val == NULL)
|
||||
return true;
|
||||
|
||||
if (src_val && dst_val && strcmp((char *)src_val, (char*)dst_val) == 0)
|
||||
return true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool validate_blob_message(struct blob_attr *src, struct blob_attr *dst)
|
||||
{
|
||||
if (!src || !dst)
|
||||
return false;
|
||||
|
||||
size_t src_len = (size_t)blobmsg_data_len(src);
|
||||
size_t dst_len = (size_t)blobmsg_data_len(dst);
|
||||
|
||||
if (dst_len < src_len)
|
||||
return false;
|
||||
|
||||
bool res = true;
|
||||
struct blob_attr *src_attr, *dst_attr;
|
||||
|
||||
__blob_for_each_attr(src_attr, blobmsg_data(src), src_len) {
|
||||
bool matched = false;
|
||||
__blob_for_each_attr(dst_attr, blobmsg_data(dst), dst_len) {
|
||||
if (strcmp(blobmsg_name(src_attr), blobmsg_name(dst_attr)) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
matched = compare_blob_msg(src_attr, dst_attr);
|
||||
break;
|
||||
}
|
||||
if (matched == false) {
|
||||
res = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void ubus_receive_event(struct ubus_context *ctx, struct ubus_event_handler *ev,
|
||||
const char *type, struct blob_attr *msg)
|
||||
{
|
||||
struct ubus_event_data *data;
|
||||
if (!msg || !ev)
|
||||
return;
|
||||
|
||||
data = container_of(ev, struct ubus_event_data, ev);
|
||||
// skip the check if the content of event to check (data->msg) is empty.
|
||||
if (data->msg == NULL || validate_blob_message(data->msg, msg) == true) {
|
||||
data->res = 0;
|
||||
uloop_end();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static JSValue _ubus_wait_event(JSContext *ctx, JSValueConst this_val,
|
||||
int argc, JSValueConst *argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
dmlog_info("_ubus_wait_event, missing argument");
|
||||
return JS_NewInt32(ctx, -1);
|
||||
}
|
||||
|
||||
unsigned int timeout = 100;
|
||||
struct blob_attr *msg = NULL;
|
||||
JS_ToUint32(ctx, &timeout, argv[1]);
|
||||
struct blob_buf b;
|
||||
memset(&b, 0, sizeof(b));
|
||||
struct ubus_context *ubus_ctx = ubus_connect(NULL);
|
||||
if (!ubus_ctx) {
|
||||
return JS_NewInt32(ctx, -1);
|
||||
}
|
||||
|
||||
const char *event = JS_ToCString(ctx, argv[0]);
|
||||
blob_buf_init(&b, 0);
|
||||
if (argc > 2) {
|
||||
JSValue json_val = JS_JSONStringify(ctx, argv[2], JS_UNDEFINED, JS_UNDEFINED);
|
||||
const char *json_str = JS_ToCString(ctx, json_val);
|
||||
blobmsg_add_json_from_string(&b, json_str);
|
||||
JS_FreeCString(ctx, json_str);
|
||||
msg = b.head;
|
||||
}
|
||||
|
||||
struct ubus_event_data data = {
|
||||
.tm.cb = ubus_listen_timeout,
|
||||
.ev.cb = ubus_receive_event,
|
||||
.msg = msg,
|
||||
.res = -1,
|
||||
};
|
||||
|
||||
uloop_init();
|
||||
ubus_add_uloop(ubus_ctx);
|
||||
|
||||
if (ubus_register_event_handler(ubus_ctx, &data.ev, event) == 0) {
|
||||
uloop_timeout_set(&data.tm, timeout * 1000);
|
||||
uloop_run();
|
||||
uloop_done();
|
||||
ubus_unregister_event_handler(ubus_ctx, &data.ev);
|
||||
} else {
|
||||
dmlog_error("_ubus_wait_event, failed to register event");
|
||||
}
|
||||
|
||||
JS_FreeCString(ctx, event);
|
||||
blob_buf_free(&b);
|
||||
ubus_free(ubus_ctx);
|
||||
return JS_NewInt32(ctx, data.res);
|
||||
}
|
||||
|
||||
int qjs_ubus_api_init()
|
||||
{
|
||||
ubus_client_init();
|
||||
qjs_register_c_api("_ubus_call", _ubus_call, 2);
|
||||
qjs_register_c_api("_ubus_wait_event", _ubus_wait_event, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
1191
dm-framework/dm-api/src/quickjs/qjs_uci_api.c
Executable file
1191
dm-framework/dm-api/src/quickjs/qjs_uci_api.c
Executable file
File diff suppressed because it is too large
Load Diff
157
dm-framework/dm-api/src/utils/dm_list.c
Normal file
157
dm-framework/dm-api/src/utils/dm_list.c
Normal file
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dm_list.h"
|
||||
#include "dm_log.h"
|
||||
|
||||
#define DEFAULT_LIST_BUF_SIZE 128
|
||||
#define MAX_LIST_BUF_SIZE 1024 * 256
|
||||
|
||||
struct dm_list {
|
||||
unsigned int cnt;
|
||||
unsigned int buf_size;
|
||||
void **mem_ptr;
|
||||
list_item_cmp cmp;
|
||||
};
|
||||
|
||||
|
||||
// default compare for string.
|
||||
static int list_str_cmp(const void *item1, const void *item2)
|
||||
{
|
||||
if (strcmp((const char*)item1, (const char*)item2) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
dm_list_h dm_list_create(list_item_cmp cmp_cb)
|
||||
{
|
||||
struct dm_list *list = calloc(1, sizeof(struct dm_list));
|
||||
|
||||
if (list == NULL)
|
||||
return NULL;
|
||||
|
||||
if (cmp_cb == NULL) {
|
||||
list->cmp = list_str_cmp;
|
||||
} else {
|
||||
list->cmp = cmp_cb;
|
||||
}
|
||||
|
||||
return (dm_list_h)list;
|
||||
}
|
||||
|
||||
int dm_list_append(dm_list_h list, void *item)
|
||||
{
|
||||
if (list == NULL)
|
||||
return -1;
|
||||
|
||||
if (list->buf_size == 0) {
|
||||
list->buf_size = DEFAULT_LIST_BUF_SIZE;
|
||||
list->mem_ptr = (void **)calloc(DEFAULT_LIST_BUF_SIZE, sizeof(void *));
|
||||
if (list->mem_ptr == NULL)
|
||||
return -1;
|
||||
} else if (list->buf_size <= list->cnt) {
|
||||
// Check if the memory required to allocate is not out of bounds.
|
||||
if (list->buf_size > MAX_LIST_BUF_SIZE / 2)
|
||||
return -1;
|
||||
else {
|
||||
// double buffer
|
||||
void *new_mem = realloc((void *)list->mem_ptr, 2 * (list->buf_size) * sizeof(void *));
|
||||
|
||||
if (new_mem == NULL)
|
||||
return -1;
|
||||
list->buf_size *= 2;
|
||||
list->mem_ptr = (void **)new_mem;
|
||||
}
|
||||
}
|
||||
|
||||
list->mem_ptr[list->cnt] = item;
|
||||
list->cnt++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_list_cnt(dm_list_h list)
|
||||
{
|
||||
if (list == NULL)
|
||||
return 0;
|
||||
|
||||
return list->cnt;
|
||||
}
|
||||
|
||||
void *dm_list_get(dm_list_h list, int i)
|
||||
{
|
||||
if (list == NULL || i >= list->cnt)
|
||||
return NULL;
|
||||
|
||||
return list->mem_ptr[i];
|
||||
}
|
||||
|
||||
int dm_list_contains(dm_list_h list, const void *item)
|
||||
{
|
||||
int cnt = dm_list_cnt(list);
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
void *val = dm_list_get(list, i);
|
||||
if (val && list->cmp(val, item) == 0) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dm_list_remove(dm_list_h list, void *item)
|
||||
{
|
||||
int cnt = dm_list_cnt(list);
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
void *val = dm_list_get(list, i);
|
||||
if (val && list->cmp(val, item) == 0) {
|
||||
free(val);
|
||||
list->mem_ptr[i] = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void dm_list_free(dm_list_h list)
|
||||
{
|
||||
if (list == NULL)
|
||||
return;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < list->cnt; i++)
|
||||
if (list->mem_ptr[i])
|
||||
free(list->mem_ptr[i]);
|
||||
|
||||
if (list->mem_ptr)
|
||||
free(list->mem_ptr);
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
void dm_list_dump(dm_list_h list)
|
||||
{
|
||||
int cnt = dm_list_cnt(list);
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
char *val = dm_list_get(list, i);
|
||||
if (val) {
|
||||
dmlog_debug("dm-list val: %s", val);
|
||||
}
|
||||
}
|
||||
}
|
||||
77
dm-framework/dm-api/src/utils/dm_list.h
Normal file
77
dm-framework/dm-api/src/utils/dm_list.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DM_LIST_H
|
||||
#define DM_LIST_H
|
||||
|
||||
struct dm_list;
|
||||
typedef struct dm_list *dm_list_h;
|
||||
|
||||
// return 0 if equal, otherwise 1
|
||||
typedef int (*list_item_cmp)(const void *item1, const void *item2);
|
||||
|
||||
/**
|
||||
* Create a list
|
||||
* @param cmp_cb item compare callback (optional if dm_list_contains will not be called)
|
||||
* @return a valid handle if successful, otherwise return NULL
|
||||
*/
|
||||
dm_list_h dm_list_create(list_item_cmp cmp_cb);
|
||||
|
||||
/**
|
||||
* Append an item to the list
|
||||
* @param list handle create by dm_list_create
|
||||
* @item pointer of item, the memory must be allocated by user using malloc.
|
||||
* memory of item will be not free'd until dm_list_free or dm_list_del(not implemented yet) is called.
|
||||
* @return 0 if successful, otherwise return -1
|
||||
*/
|
||||
int dm_list_append(dm_list_h list, void *item);
|
||||
|
||||
/**
|
||||
* Get count of items in the list
|
||||
* @param list handle create by dm_list_create
|
||||
* @return number of items
|
||||
*/
|
||||
int dm_list_cnt(dm_list_h list);
|
||||
|
||||
/**
|
||||
* Get item in the list by index
|
||||
* @param list handle create by dm_list_create
|
||||
* @param i, index of item to get, valid scope is [0, cnt-1]
|
||||
* @return pointer of item if index is valid, otherwise return NULL
|
||||
*/
|
||||
void *dm_list_get(dm_list_h list, int i);
|
||||
|
||||
/**
|
||||
* check if a item is already in the list
|
||||
* @param list handle create by dm_list_create
|
||||
* @param item, item to check
|
||||
* @return 1 if exist, otherwise return 0
|
||||
*/
|
||||
int dm_list_contains(dm_list_h list, const void *item);
|
||||
|
||||
/**
|
||||
* remove a item from the list
|
||||
* @param list handle create by dm_list_create
|
||||
* @param item, item to remove
|
||||
* @return 0 if successful, otherwise return -1
|
||||
*/
|
||||
int dm_list_remove(dm_list_h list, void *item);
|
||||
|
||||
/**
|
||||
* Free the list, including all items in the list
|
||||
* @param list handle create by dm_list_create
|
||||
* @return none
|
||||
*/
|
||||
void dm_list_free(dm_list_h list);
|
||||
|
||||
// only works when the value is string type
|
||||
void dm_list_dump(dm_list_h list);
|
||||
#endif
|
||||
54
dm-framework/dm-api/src/utils/dm_log.c
Normal file
54
dm-framework/dm-api/src/utils/dm_log.c
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <dm_log.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
|
||||
void dmlog_init(const char *name, int log_level)
|
||||
{
|
||||
// todo : implement the filter level
|
||||
openlog(name, LOG_NOWAIT | LOG_ODELAY, LOG_LOCAL0);
|
||||
}
|
||||
|
||||
void dmlog_info(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsyslog(LOG_INFO, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
void dmlog_warn(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsyslog(LOG_WARNING, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
void dmlog_error(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsyslog(LOG_ERR, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
void dmlog_debug(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsyslog(LOG_DEBUG, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
void dmlog_close()
|
||||
{
|
||||
closelog();
|
||||
}
|
||||
45
dm-framework/dm-api/src/utils/ubus_client.c
Normal file
45
dm-framework/dm-api/src/utils/ubus_client.c
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ubus_client.h"
|
||||
#include "dm_log.h"
|
||||
|
||||
#define DEFAULT_UBUS_TIMEOUT 10000 // in ms
|
||||
|
||||
static struct ubus_context *ubus_ctx_g = NULL;
|
||||
|
||||
void ubus_client_init()
|
||||
{
|
||||
ubus_ctx_g = ubus_connect(NULL);
|
||||
}
|
||||
|
||||
int ubus_client_call(const char *path, const char *method, struct blob_attr *msg,
|
||||
ubus_data_handler_t cb, void *priv)
|
||||
{
|
||||
uint32_t id;
|
||||
if (ubus_lookup_id(ubus_ctx_g, path, &id)) {
|
||||
// dmlog_error("ubus_lookup_id failed, object:%s", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = ubus_invoke(ubus_ctx_g, id, method, msg, cb, priv, DEFAULT_UBUS_TIMEOUT);
|
||||
if (ret != 0) {
|
||||
dmlog_error("ubus_invoke failed: object:%s, method:%s, %d", path, method, ret);
|
||||
if (msg) {
|
||||
char *json_str = blobmsg_format_json(msg, true);
|
||||
dmlog_error("ubus_invoke mesg: %s", json_str);
|
||||
free(json_str);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
21
dm-framework/dm-api/src/utils/ubus_client.h
Normal file
21
dm-framework/dm-api/src/utils/ubus_client.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef UBUS_CLIENT_H
|
||||
#define UBUS_CLIENT_H
|
||||
#include <libubox/blobmsg_json.h>
|
||||
#include <libubus.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
void ubus_client_init();
|
||||
int ubus_client_call(const char *path, const char *method, struct blob_attr *msg,
|
||||
ubus_data_handler_t cb, void *priv);
|
||||
#endif
|
||||
84
dm-framework/dm-api/src/utils/utils.c
Normal file
84
dm-framework/dm-api/src/utils/utils.c
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/sendfile.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "dm_log.h"
|
||||
|
||||
int copy_file(const char *source, const char *dest)
|
||||
{
|
||||
int source_fd = open(source, O_RDONLY, 0);
|
||||
|
||||
if (source_fd < 0) {
|
||||
dmlog_error("%s, failed to open %s", __FUNCTION__, source);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct stat stat_source;
|
||||
|
||||
if (fstat(source_fd, &stat_source) < 0) {
|
||||
close(source_fd);
|
||||
dmlog_error("%s, failed to fstat %s", __FUNCTION__, source);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int dest_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
|
||||
if (dest_fd < 0) {
|
||||
close(source_fd);
|
||||
dmlog_error("%s, failed to open %s", __FUNCTION__, dest);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t ret = sendfile(dest_fd, source_fd, 0, stat_source.st_size);
|
||||
|
||||
if (ret != stat_source.st_size) {
|
||||
close(source_fd);
|
||||
close(dest_fd);
|
||||
remove(dest);
|
||||
dmlog_error("%s, failed to sendfile from %s to %s", __FUNCTION__, source, dest);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 != fsync(dest_fd)) {
|
||||
dmlog_error("%s, fsync fails", __FUNCTION__);
|
||||
close(source_fd);
|
||||
close(dest_fd);
|
||||
remove(dest);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sync();
|
||||
|
||||
close(source_fd);
|
||||
close(dest_fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int get_uptime_msecs(void)
|
||||
{
|
||||
time_t t;
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
t = ((time_t)(ts.tv_sec * 1000) + (time_t)(ts.tv_nsec / 1000000));
|
||||
return (uint32_t)t;
|
||||
}
|
||||
17
dm-framework/dm-api/src/utils/utils.h
Normal file
17
dm-framework/dm-api/src/utils/utils.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Genexis B.V. All rights reserved.
|
||||
*
|
||||
* This Software and its content are protected by the Dutch Copyright Act
|
||||
* ('Auteurswet'). All and any copying and distribution of the software
|
||||
* and its content without authorization by Genexis B.V. is
|
||||
* prohibited. The prohibition includes every form of reproduction and
|
||||
* distribution.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
int copy_file(const char *source, const char *dest);
|
||||
unsigned int get_uptime_msecs(void);
|
||||
#endif
|
||||
@@ -15,6 +15,8 @@ PKG_BUILD_PARALLEL:=1
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
TARGET_CFLAGS += -fPIC
|
||||
|
||||
define Package/quickjs
|
||||
SECTION:=lang
|
||||
CATEGORY:=Languages
|
||||
|
||||
Reference in New Issue
Block a user