From f2f3f34859dc0a257b2a17e98cb3f56ba2f3f857 Mon Sep 17 00:00:00 2001 From: Vivek Kumar Dutta Date: Mon, 15 May 2023 15:56:18 +0530 Subject: [PATCH] obuspc: USP based controller --- obuspc/Makefile | 72 ++++ obuspc/files/etc/config/obuspc | 19 + obuspc/files/etc/init.d/obuspc | 349 ++++++++++++++++++ .../etc/uci-defaults/99-fix-agent-endpoint | 31 ++ 4 files changed, 471 insertions(+) create mode 100644 obuspc/Makefile create mode 100644 obuspc/files/etc/config/obuspc create mode 100755 obuspc/files/etc/init.d/obuspc create mode 100644 obuspc/files/etc/uci-defaults/99-fix-agent-endpoint diff --git a/obuspc/Makefile b/obuspc/Makefile new file mode 100644 index 000000000..35134f394 --- /dev/null +++ b/obuspc/Makefile @@ -0,0 +1,72 @@ +# +# Copyright (C) 2023 IOPSYS Software Solutions AB +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=obuspc +PKG_VERSION:=1.0.1.1 + +LOCAL_DEV:=0 +ifneq ($(LOCAL_DEV),1) +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://dev.iopsys.eu/bbf/obuspa-test-controller.git +PKG_SOURCE_VERSION:=f1f721bc1a4feaf63c7f7837eb7b0c86111e2f71 +PKG_MAINTAINER:=Vivek Dutta +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz +PKG_MIRROR_HASH:=skip +endif + +PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION) + +PKG_FIXUP:=autoreconf + +PKG_LICENSE:=BSD-3-Clause +PKG_LICENSE_FILES:=LICENSE + +include $(INCLUDE_DIR)/package.mk + +define Package/obuspc + SECTION:=utils + CATEGORY:=Utilities + SUBMENU:=TRx69 + TITLE:=USP Controller + DEPENDS:=+libopenssl +libuci +libblobmsg-json +libcurl +libsqlite3 +libubox +libubus +libmosquitto-ssl +libwebsockets-openssl +endef + +define Package/obuspc/description + OB-USP-Controller is a local usp controller based on User Services Platform (USP). +endef + +TARGET_CFLAGS += \ + -D_GNU_SOURCE \ + -Wall \ + -Werror + +CONFIGURE_ARGS += \ + --localstatedir="/tmp/" \ + --enable-stomp \ + --enable-coap \ + --enable-mqtt \ + --enable-websockets + +ifeq ($(LOCAL_DEV),1) +define Build/Prepare + $(CP) -rf ~/git/obuspa-test-controller/* $(PKG_BUILD_DIR)/ + $(Build/Patch) +endef +endif + +define Package/obuspc/install + $(INSTALL_DIR) $(1)/usr/sbin + $(INSTALL_DIR) $(1)/etc/init.d + $(INSTALL_DIR) $(1)/etc/config + $(INSTALL_DIR) $(1)/etc/bbfdm/json + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_BIN) $(PKG_BUILD_DIR)/obuspc $(1)/usr/sbin/ + $(INSTALL_BIN) ./files/etc/init.d/obuspc $(1)/etc/init.d/ + $(INSTALL_BIN) ./files/etc/uci-defaults/99-fix-agent-endpoint $(1)/etc/uci-defaults/ + $(INSTALL_DATA) ./files/etc/config/obuspc $(1)/etc/config/ +endef + +$(eval $(call BuildPackage,obuspc)) diff --git a/obuspc/files/etc/config/obuspc b/obuspc/files/etc/config/obuspc new file mode 100644 index 000000000..cdcbd0e72 --- /dev/null +++ b/obuspc/files/etc/config/obuspc @@ -0,0 +1,19 @@ +config obuspc 'global' + option enabled '1' + option debug '1' + option log_level '1' + option prototrace '1' + +config mqtt 'mqtt' + option BrokerAddress '127.0.0.1' + option BrokerPort '1883' + option TransportProtocol 'TCP/IP' + +config controller 'controller' + option EndpointID 'proto::interop-usp-controller' + option Protocol 'MQTT' + option ResponseTopicConfigured '/usp/controller' + option mqtt 'mqtt' + +config agent 'agent' + option Topic '/usp/endpoint' diff --git a/obuspc/files/etc/init.d/obuspc b/obuspc/files/etc/init.d/obuspc new file mode 100755 index 000000000..755f17468 --- /dev/null +++ b/obuspc/files/etc/init.d/obuspc @@ -0,0 +1,349 @@ +#!/bin/sh /etc/rc.common + +START=99 +STOP=01 +USE_PROCD=1 + +PROG=/usr/sbin/obuspc +CONFIGURATION=obuspc + +RESET_FILE="/tmp/usp/obuspc_param_reset.txt" +SQL_DB_FILE="/tmp/usp/uspc.db" + +BASEPATH="" +INSTANCE_COUNT=0 + +. /usr/share/libubox/jshn.sh + +global_init() +{ + BASEPATH="" + INSTANCE_COUNT=0 +} + +log() +{ + echo "$*"|logger -t obuspc.init -p debug +} + +db_set_reset_file() +{ + local param value + + param="${1}" + shift + value="$*" + + if [ -n "${param}" ] && [ -n "${value}" ]; then + echo "${param} \"${value}\"">>${RESET_FILE} + else + echo >>${RESET_FILE} + fi +} + +db_set() +{ + db_set_reset_file "$@" +} + +# if db present then check if it matches with existing instances +# fallback to max instance present + 1 +# In case of no db get the count +get_base_path() +{ + local refpath value path count + + refpath="${1}" + value="${2}" + path="" + count=0 + + if [ -z "${path}" ]; then + INSTANCE_COUNT=$(( INSTANCE_COUNT + 1 )) + path="${refpath}${INSTANCE_COUNT}" + fi + BASEPATH="${path}" +} + +get_refrence_path() +{ + local dmref value path + + dmref="${1}" + value="${2}" + path="" + + path=$(grep "${dmref}\d.Alias " ${RESET_FILE}|grep -w "${value}") + path=${path%.*} + echo "${path}" +} + +validate_global_section() +{ + uci_validate_section ${CONFIGURATION} obuspc "${1}" \ + 'enabled:bool:1' \ + 'debug:bool:0' \ + 'prototrace:bool:0' \ + 'log_level:uinteger' \ + 'log_dest:string' \ + 'db_file:string' +} + +validate_mqtt_client_section() +{ + uci_validate_section ${CONFIGURATION} mqtt "${1}" \ + 'Enable:bool:1' \ + 'BrokerAddress:string' \ + 'BrokerPort:port:1883' \ + 'Username:string' \ + 'Password:string' \ + 'ProtocolVersion:or("3.1", "3.1.1","5.0"):5.0' \ + 'TransportProtocol:or("TCP/IP","TLS"):TCP/IP' \ + 'ClientID:string' +} + +validate_controller_section() +{ + uci_validate_section ${CONFIGURATION} mtp "${1}" \ + 'EndpointID:string' \ + 'Protocol:or("MQTT", "WebSocket")' \ + 'ResponseTopicConfigured:string' \ + 'Destination:string' \ + 'Port:port' \ + 'Path:string' \ + 'mqtt:string' \ + 'stomp:string' \ + 'Reference:string' \ + 'EnableEncryption:bool:0' +} + +configure_controller() { + local EndpointID Protocol ResponseTopicConfigured + local Destination Path Port EnableEncryption Reference + local stomp mqtt dm_ref sec + + sec="${1}" + validate_controller_section "${1}" || { + log "Validation of mtp section failed" + return 1; + } + + if [ -z "${EndpointID}" ]; then + log "EndpointID not defined for controller" + return 1; + fi + + db_set Device.LocalAgent.EndpointID "${EndpointID}" + + sec="${sec/mtp_/cpe-}" + get_base_path "Device.LocalAgent.MTP." "${sec}" + if [ -z "${BASEPATH}" ]; then + log "Failed to get path [$BASEPATH]" + return 1; + fi + + if [ -z "${Protocol}" ]; then + log "Protocol not defined for the mtp[${1}] section" + return 1; + fi + + dm_ref="" + if [ -z "${Reference}" ]; then + if [ "${Protocol}" = "STOMP" ]; then + stomp="${stomp/stomp_/cpe-}" + dm_ref=$(get_refrence_path "Device.STOMP.Connection." "${stomp}") + elif [ "${Protocol}" = "MQTT" ]; then + mqtt="${mqtt/mqtt_/cpe-}" + dm_ref=$(get_refrence_path "Device.MQTT.Client." "${mqtt}") + fi + else + dm_ref="${Reference}" + fi + + db_set "${BASEPATH}.Alias" "${sec}" + db_set "${BASEPATH}.Enable" "1" + db_set "${BASEPATH}.Protocol" "${Protocol}" + if [ "${Protocol}" = "MQTT" ]; then + db_set "${BASEPATH}.MQTT.Reference" "${dm_ref}" + db_set "${BASEPATH}.MQTT.ResponseTopicConfigured" "${ResponseTopicConfigured}" + elif [ "${Protocol}" = "STOMP" ]; then + db_set "${BASEPATH}.STOMP.Reference" "${dm_ref}" + db_set "${BASEPATH}.STOMP.Destination" "${Destination}" + elif [ "${Protocol}" = "CoAP" ]; then + db_set "${BASEPATH}.CoAP.Path" "${Path}" + db_set "${BASEPATH}.CoAP.Port" "${Port}" + elif [ "${Protocol}" = "WebSocket" ]; then + db_set "${BASEPATH}.WebSocket.Path" "${Path}" + db_set "${BASEPATH}.WebSocket.Port" "${Port}" + db_set "${BASEPATH}.WebSocket.EnableEncryption" "${EnableEncryption}" + fi + db_set +} + +validate_agent_section() +{ + uci_validate_section ${CONFIGURATION} agent "${1}" \ + 'name:string' \ + 'EndpointID:string' \ + 'Topic:string' +} + +configure_agent() { + local EndpointID Topic name + local stomp mqtt dm_ref sec + + sec="${1}" + validate_agent_section "${1}" || { + log "Validation of agent section failed" + return 1; + } + + if [ -z "${EndpointID}" ]; then + log "EndpointID not defined for agent" + return 1; + fi + + if [ -z "${Topic}" ]; then + log "Topic not defined for agent" + return 1; + fi + + + sec="${sec/mtp_/cpe-}" + get_base_path "Device.LocalAgent.Controller." "${sec}" + if [ -z "${BASEPATH}" ]; then + log "Failed to get path [$BASEPATH]" + return 1; + fi + + db_set "${BASEPATH}.Alias" "${sec}" + db_set "${BASEPATH}.Enable" "1" + db_set "${BASEPATH}.EndpointID" "${EndpointID}" + db_set "${BASEPATH}.MTP.1.Enable" "1" + db_set "${BASEPATH}.MTP.1.Protocol" "MQTT" + db_set "${BASEPATH}.MTP.1.MQTT.Reference" "Device.MQTT.Client.1" + db_set "${BASEPATH}.MTP.1.MQTT.Topic" "${Topic}" + db_set +} + +configure_mqtt_client() { + local BrokerAddress BrokerPort Enable Username Password ProtocolVersion + local TransportProtocol ClientID + local sec + + sec="${1}" + validate_mqtt_client_section "${1}" || { + log "Validation of mqtt section failed" + return 1; + } + + sec="${sec/mqtt_/cpe-}" + get_base_path "Device.MQTT.Client." "${sec}" + if [ -z "${BASEPATH}" ]; then + log "Failed to get path [$BASEPATH]" + return 1; + fi + + db_set "${BASEPATH}.Alias" "${sec}" + db_set "${BASEPATH}.Enable" "${Enable}" + db_set "${BASEPATH}.BrokerAddress" "${BrokerAddress}" + db_set "${BASEPATH}.BrokerPort" "${BrokerPort}" + db_set "${BASEPATH}.Username" "${Username}" + db_set "${BASEPATH}.Password" "${Password}" + db_set "${BASEPATH}.ProtocolVersion" "${ProtocolVersion}" + db_set "${BASEPATH}.TransportProtocol" "${TransportProtocol}" + db_set "${BASEPATH}.ClientID" "${ClientID}" + + db_set +} + +configure_obuspc() { + local enabled trust_cert ifname interface debug prototrace log_level db_file log_dest + local client_cert + + validate_global_section "global" + + if [ "${debug}" -ne "0" ]; then + # Forward stdout of the command to logd + procd_set_param stdout 1 + # Same for stderr + procd_set_param stderr 1 + fi + + procd_append_param command -u + if [ "${prototrace}" -eq 1 ]; then + procd_append_param command -p + fi + + if [ -n "${log_level}" ]; then + procd_append_param command -v "${log_level}" + fi + + if [ -n "${log_dest}" ]; then + procd_append_param command -l "${log_dest}" + fi + + if [ -f "${RESET_FILE}" ]; then + procd_append_param command -r ${RESET_FILE} + fi +} + +# Create factory reset file +db_init() +{ + # Load configuration + config_load $CONFIGURATION + config_get SQL_DB_FILE global db_file "/tmp/usp/uspc.db" + + # Remove DB and generate from uci + [ -f "${SQL_DB_FILE}" ] && rm -f "${SQL_DB_FILE}" + + # Remove reset file if present + [ -f "${RESET_FILE}" ] && rm -f ${RESET_FILE} + + config_load $CONFIGURATION + global_init + config_foreach configure_mqtt_client mqtt + global_init + config_foreach configure_controller controller + global_init + config_foreach configure_agent agent + + return 0; +} + +register_service() +{ + procd_open_instance ${CONFIGURATION} + procd_set_param command ${PROG} + + configure_obuspc + procd_set_param respawn \ + "${respawn_threshold:-5}" \ + "${respawn_timeout:-10}" "${respawn_retry:-3}" + + procd_close_instance +} + +start_service() { + local enabled + + config_load ${CONFIGURATION} + config_get_bool enabled global enabled 0 + + if [ "${enabled}" -eq 0 ]; then + return 0; + fi + + mkdir -p /tmp/usp/ + db_init + register_service +} + +stop_service() { + ${PROG} -c stop >/dev/null 2>&1 +} + +service_triggers() { + procd_add_reload_trigger "obuspc" +} diff --git a/obuspc/files/etc/uci-defaults/99-fix-agent-endpoint b/obuspc/files/etc/uci-defaults/99-fix-agent-endpoint new file mode 100644 index 000000000..c4ff888e1 --- /dev/null +++ b/obuspc/files/etc/uci-defaults/99-fix-agent-endpoint @@ -0,0 +1,31 @@ +#!/bin/sh + +. /lib/functions.sh + +get_oui_from_db() +{ + db -q get device.deviceinfo.ManufacturerOUI +} + +get_serial_from_db() +{ + db -q get device.deviceinfo.SerialNumber +} + +fix_agent_endpoint() +{ + local AgentEndpointID serial oui user pass + + # Get endpoint id from obuspa config first + config_load obuspa + config_get AgentEndpointID localagent EndpointID "" + if [ -z "${AgentEndpointID}" ]; then + serial=$(get_serial_from_db) + oui=$(get_oui_from_db) + AgentEndpointID="os::${oui}-${serial//+/%2B}" + fi + + uci -q set obuspc.agent.EndpointID="${AgentEndpointID}" +} + +fix_agent_endpoint