diff --git a/dmcli/Config.in b/dmcli/Config.in new file mode 100644 index 000000000..965dc3cbf --- /dev/null +++ b/dmcli/Config.in @@ -0,0 +1,9 @@ +if PACKAGE_dmcli + +config DMCLI_REMOTE_CONNECTION + bool "Add dmcli remote controller configuration" + default n + help + This adds a usp controller configuration for dmcli remote connection from different machine/laptop/server. + +endif diff --git a/dmcli/Makefile b/dmcli/Makefile new file mode 100644 index 000000000..e66c2fcca --- /dev/null +++ b/dmcli/Makefile @@ -0,0 +1,75 @@ +# +# Copyright (c) 2021 Genexis Netherlands 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 Netherlands B.V. is +# prohibited. The prohibition includes every form of reproduction and +# distribution. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=dmcli +PKG_LICENSE:=PROPRIETARY GENEXIS +PKG_VERSION:=1.9.4 + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://dev.iopsys.eu/gnx/dmcli.git +PKG_SOURCE_VERSION:=6171e208611ba4ea1abdab2b70a8fa30f55476ca +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.xz +PKG_MIRROR_HASH:=skip + +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/dmcli + SECTION:=tools + CATEGORY:=Genexis + TITLE:=DMCLI (datamodel-based CLI) + URL:=http://genexis.eu + DEPENDS:=+usp-js +DMCLI_REMOTE_CONNECTION:mosquitto-auth-plugin +shadow-utils +@BUSYBOX_CONFIG_ADDUSER +endef + +define Package/dmcli/description + CLI to view and configure datamodels of CPE +endef + +define Package/dmcli/conffiles +/etc/dmcli/dmcli.conf +endef + +define Package/dmcli/config + source "$(SOURCE)/Config.in" +endef + +define Package/dmcli/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_BUILD_DIR)/dmcli $(1)/usr/bin/ + + $(INSTALL_DIR) $(1)/usr/lib/dmcli + $(CP) $(PKG_BUILD_DIR)/common $(1)/usr/lib/dmcli/ + mv $(1)/usr/lib/dmcli/common/os_qjs.js $(1)/usr/lib/dmcli/common/os.js + rm $(1)/usr/lib/dmcli/common/os_node.js + $(CP) $(PKG_BUILD_DIR)/core $(1)/usr/lib/dmcli/ + $(CP) $(PKG_BUILD_DIR)/cli $(1)/usr/lib/dmcli/ + $(CP) $(PKG_BUILD_DIR)/data $(1)/usr/lib/dmcli/ + $(CP) $(PKG_BUILD_DIR)/plugins $(1)/usr/lib/dmcli/ + + $(INSTALL_DIR) $(1)/etc/uci-defaults + $(INSTALL_DATA) ./files/etc/uci-defaults/36-dmcli $(1)/etc/uci-defaults/ +ifeq ($(CONFIG_DMCLI_REMOTE_CONNECTION),y) + $(INSTALL_DATA) ./files/etc/uci-defaults/36-dmcli-remote $(1)/etc/uci-defaults/ +else + $(INSTALL_DATA) ./files/etc/uci-defaults/36-dmcli-remote-remove $(1)/etc/uci-defaults/ +endif + + $(INSTALL_DIR) $(1)/etc/dmcli + $(CP) ./files/etc/dmcli/dmcli.acl $(1)/etc/dmcli/ + $(CP) ./files/etc/dmcli/dmcli.conf $(1)/etc/dmcli/ + + $(INSTALL_DIR) $(1)/etc/users/roles/ + $(INSTALL_DATA) ./files/etc/users/roles/operator.json $(1)/etc/users/roles/ +endef + +$(eval $(call BuildPackage,dmcli)) diff --git a/dmcli/files/etc/dmcli/dmcli.acl b/dmcli/files/etc/dmcli/dmcli.acl new file mode 100644 index 000000000..4f6fdfe9c --- /dev/null +++ b/dmcli/files/etc/dmcli/dmcli.acl @@ -0,0 +1,4 @@ +user operator +topic read /usp/operator/controller/reply-to +topic read /usp/operator/controller +topic write /usp/operator/endpoint diff --git a/dmcli/files/etc/dmcli/dmcli.conf b/dmcli/files/etc/dmcli/dmcli.conf new file mode 100644 index 000000000..5a9227573 --- /dev/null +++ b/dmcli/files/etc/dmcli/dmcli.conf @@ -0,0 +1,45 @@ +{ + "Settings": { + "USP": { + "ActiveConnectionProfile": "local", + "ConnectionProfile": [ + { + "Name": "local", + "Host": "127.0.0.1", + "Port": 9002, + "Username": "operator", + "Protocol": "ws", + "FromId": "oui:000F94:device-controller-operator", + "PublishEndpoint": "/usp/operator/endpoint", + "SubscribeEndpoint": "/usp/operator/controller" + } + ], + "Session": { + "AutoStart": false + }, + "Notification": { + "LogTo": "console", + "Format": "brief", + "LogFile": "usp-notification.log" + } + }, + "CLI": { + "Home": "/", + "Color": "true", + "Mode": "Command", + "ShowCommandTime": false, + "SortDMTree": false + }, + "Prompt": { + "Auto": true, + "Color": "default", + "SelectedBackgroundColor": "yellow", + "PageSize": "3", + "AutoPromptOnEmptyCommand": false, + "AutoPromptInstanceNumbers": false + }, + "Log": { + "Level": "Error" + } + } +} diff --git a/dmcli/files/etc/uci-defaults/36-dmcli b/dmcli/files/etc/uci-defaults/36-dmcli new file mode 100644 index 000000000..f12779795 --- /dev/null +++ b/dmcli/files/etc/uci-defaults/36-dmcli @@ -0,0 +1,120 @@ +#!/bin/sh + +. /lib/functions.sh +. /lib/functions/iopsys-environment.sh +. /usr/share/libubox/jshn.sh + +DMCLI_CONF="/etc/dmcli/dmcli.conf" +CONTROLLER_ID='oui:000F94:device-controller-operator' +DMCLI_RESP_TOPIC="/usp/operator/endpoint" +DMCLI_CTRL_TOPIC="/usp/operator/controller" +DMCLI_PORT="9002" + +grep -q "^operator:" /etc/passwd || { + adduser -g 'Operator' -D -H -s /usr/bin/dmcli --home '/usr/lib/dmcli' 'operator' + hash="" + if type get_operator_password_hash > /dev/null 2>&1; then + hash=$(get_operator_password_hash) + fi + if [ -z "$hash" ]; then + hash='$6$zP4Wk/VQJOLwwofC$teuhnYFQBcA8YUZo/Q0quDMi4SsOHmfBcyvt5VNchPnzgwF1nfNNliC3yBVW22NwmwttPEWeBEBfnMTBB0rYs/' + fi + echo "operator:${hash}" | chpasswd -e +} + +grep -q "^/usr/bin/dmcli$" /etc/shells || { + echo '/usr/bin/dmcli' >> /etc/shells +} + +uci -q del_list sshd.@sshd[0].AllowUsers='operator' +uci -q add_list sshd.@sshd[0].AllowUsers='operator' + +uci -q delete users.operator +uci -q set users.operator=user +uci -q set users.operator.enabled=1 +uci -q set users.operator.shell='dmcli' +uci -q set users.operator.member_roles='operator' + +if [ -f "/etc/config/mosquitto" ]; then + uci_add mosquitto listener dmcli_local + uci_set mosquitto dmcli_local enabled 1 + uci_set mosquitto dmcli_local port "${DMCLI_PORT}" + uci_set mosquitto dmcli_local protocol 'websockets' + uci_set mosquitto dmcli_local acl_file '/etc/dmcli/dmcli.acl' + uci_set mosquitto dmcli_local no_remote_access '1' + uci_set mosquitto dmcli_local allow_anonymous '1' +fi + +if [ -f "/etc/config/obuspa" ]; then + uci_add obuspa mqtt mqtt_operator + uci_set obuspa mqtt_operator BrokerAddress '127.0.0.1' + uci_set obuspa mqtt_operator BrokerPort '1883' + uci_set obuspa mqtt_operator TransportProtocol 'TCP/IP' + + uci_add obuspa mtp mtp_operator + uci_set obuspa mtp_operator Protocol 'MQTT' + uci_set obuspa mtp_operator ResponseTopicConfigured "${DMCLI_RESP_TOPIC}" + uci_set obuspa mtp_operator mqtt 'mqtt_operator' + + uci_add obuspa controller controller_operator + uci_set obuspa controller_operator EndpointID "${CONTROLLER_ID}" + uci_set obuspa controller_operator Protocol 'MQTT' + uci_set obuspa controller_operator Topic "${DMCLI_CTRL_TOPIC}" + uci_set obuspa controller_operator mqtt 'mqtt_operator' + uci_set obuspa controller_operator assigned_role_name 'operator' +fi + +_get_endpoint_id() { + local id serial oui + + id="$(uci -q get obuspa.localagent.EndpointID)" + if [ -n "${id}" ]; then + echo "${id}" + return 0 + fi + + serial="$(db -q get device.deviceinfo.SerialNumber)" + oui="$(db -q get device.deviceinfo.ManufacturerOUI)" + + echo "os::${oui}-${serial//+/%2B}" +} + +update_dmcli_conf() { + local endpointid confTmpFile + local port fromid publish subscribe toid + + if [ -f "${DMCLI_CONF}" ]; then + endpointid="$(_get_endpoint_id)" + json_load_file "${DMCLI_CONF}" || return + json_select "Settings" || return + json_select "USP" || return + json_select "ConnectionProfile" || return + json_select "1" || return + json_get_var port "Port" + json_get_var fromid "FromId" + json_get_var publish "PublishEndpoint" + json_get_var subscribe "SubscribeEndpoint" + json_get_var toid "ToId" + + json_add_int "Port" "${DMCLI_PORT}" + json_add_string "FromId" "${CONTROLLER_ID}" + json_add_string "PublishEndpoint" "${DMCLI_RESP_TOPIC}" + json_add_string "SubscribeEndpoint" "${DMCLI_CTRL_TOPIC}" + json_add_string "ToId" "${endpointid}" + json_select .. + json_select .. + json_select .. + json_select .. + + if [ "${port}" != "${DMCLI_PORT}" ] || [ "${fromid}" != "${CONTROLLER_ID}" ] || \ + [ "${publish}" != "${DMCLI_RESP_TOPIC}" ] || [ "${subscribe}" != "${DMCLI_CTRL_TOPIC}" ] || \ + [ "${toid}" != "${endpointid}" ]; then + confTmpFile="$(mktemp -u -p "$(dirname "$DMCLI_CONF")" "$(basename "$DMCLI_CONF").XXXXXXX")" + json_pretty + json_dump > "${confTmpFile}" || return + mv -f "${confTmpFile}" "${DMCLI_CONF}" || return + fi + fi +} + +update_dmcli_conf || exit diff --git a/dmcli/files/etc/uci-defaults/36-dmcli-remote b/dmcli/files/etc/uci-defaults/36-dmcli-remote new file mode 100644 index 000000000..42dd38b9e --- /dev/null +++ b/dmcli/files/etc/uci-defaults/36-dmcli-remote @@ -0,0 +1,14 @@ +#!/bin/sh + +. /lib/functions.sh + +if [ -f "/etc/config/mosquitto" ]; then + uci_add mosquitto listener dmcli + uci_set mosquitto dmcli enabled 1 + uci_set mosquitto dmcli port '9003' + uci_set mosquitto dmcli protocol 'websockets' + uci_set mosquitto dmcli auth_plugin '/usr/lib/mosquitto_auth_plugin.so' + uci_set mosquitto dmcli acl_file '/etc/dmcli/dmcli.acl' +fi + +exit 0 diff --git a/dmcli/files/etc/uci-defaults/36-dmcli-remote-remove b/dmcli/files/etc/uci-defaults/36-dmcli-remote-remove new file mode 100644 index 000000000..ebb994d2d --- /dev/null +++ b/dmcli/files/etc/uci-defaults/36-dmcli-remote-remove @@ -0,0 +1,9 @@ +#!/bin/sh + +. /lib/functions.sh + +if [ -f "/etc/config/mosquitto" ]; then + uci_remove mosquitto dmcli +fi + +exit 0 diff --git a/dmcli/files/etc/users/roles/operator.json b/dmcli/files/etc/users/roles/operator.json new file mode 100644 index 000000000..4a8faf9ec --- /dev/null +++ b/dmcli/files/etc/users/roles/operator.json @@ -0,0 +1,14 @@ +{ + "tr181": { + "name": "operator", + "instance": 6, + "permission": [ + { + "object": "Device.", + "perm": [ + "PERMIT_ALL" + ] + } + ] + } +} diff --git a/dmcli/src/Makefile b/dmcli/src/Makefile new file mode 100644 index 000000000..2cbf9c6b6 --- /dev/null +++ b/dmcli/src/Makefile @@ -0,0 +1,7 @@ +all: dmcli + +dmcli: main.c + $(CC) $(CFLAGS) -Wall -Werror -o $@ $^ + +clean: + rm -f dmcli diff --git a/dmcli/src/main.c b/dmcli/src/main.c new file mode 100644 index 000000000..1cdb44257 --- /dev/null +++ b/dmcli/src/main.c @@ -0,0 +1,32 @@ +/* +* Copyright (c) 2021 Genexis Netherlands 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 Netherlands B.V. is +* prohibited. The prohibition includes every form of reproduction and +* distribution. +*/ + +#include +#include +#include + +/* C Wrapper for operator to login to the CLI via ssh: the shell in + * the passwd file cannot be a script that requires an interpreter. */ +int main(int argc, char *argv[]) +{ + char *cmd[3 + (argc > 1 ? argc - 1 : 0)]; + + cmd[0] = "/usr/bin/qjs"; + cmd[1] = "/usr/lib/dmcli/cli/main.js"; + cmd[2] = NULL; + + if (argc > 1) { + memcpy(&cmd[2], &argv[1], (argc - 1) * sizeof(char *)); + cmd[2 + argc - 1] = NULL; + } + + execv(cmd[0], cmd); + fprintf(stderr, "%s: command not found\n", cmd[0]); + return 127; +}