Compare commits

...
53 Commits
Author SHA1 Message Date
fengzeroz ef6b3ffe03 Merge pull request #2943 from fengzeroz/main
errorcode: app not running
2026-09-21 09:47:06 +08:00
fengzero 674d803569 errorcode: app not running 2026-09-20 10:07:10 +00:00
fengzeroz f69c07490e Merge pull request #2941 from fengzeroz/main
tags symbol check
2026-09-07 15:33:51 +08:00
fengzero 3c5aed4ffe tags symbol check 2026-09-07 07:29:46 +00:00
fengzeroz e13927a44d Merge pull request #2939 from fengzeroz/main
http check body length
2026-09-07 14:15:54 +08:00
fengzero 3167ff773e http check body length 2026-09-07 05:56:40 +00:00
fengzeroz 57270893a1 Merge pull request #2938 from ZhouBox/main
refactor: replace u_int16_t with uint16_t
2026-08-31 17:46:19 +08:00
ZhouBox 3f8475a0db refactor: replace u_int16_t with uint16_t 2026-08-31 17:32:59 +08:00
fengzeroz 1c9abb0664 Merge pull request #2937 from ZhouBox/main
fix: include sys/queue.h only on Darwin
2026-08-31 17:13:02 +08:00
ZhouBox efaac88483 fix: include sys/queue.h only on Darwin 2026-08-31 16:57:20 +08:00
fengzeroz 1baa36c82d Merge pull request #2936 from hxy7yx/main-1
make datalayers optional & remove syslog
2026-08-27 17:17:29 +08:00
hxy7yx 7cfad3f3f9 feat: remove remote syslog support 2026-08-26 00:01:04 -07:00
hxy7yx f2c2a51f5c build: make datalayers optional 2026-08-24 02:19:16 -07:00
fengzeroz fe39b9fab2 Merge pull request #2934 from ZhouBox/main
restful: support querying nodes by multiple plugins
2026-08-21 09:57:04 +08:00
xiang.zhou 515ee21cbf restful: support querying nodes by multiple plugins 2026-08-17 08:56:02 +08:00
fengzeroz b4c5b75a03 Merge pull request #2931 from fengzeroz/main
load node after load single node
2026-08-12 17:05:27 +08:00
fengzero 33ec20709c load node after load single node 2026-08-12 09:03:56 +00:00
fengzeroz 782a3cd374 Merge pull request #2929 from fengzeroz/main
filter license server store
2026-08-12 15:49:56 +08:00
fengzero ab35b924ac filter license server store 2026-08-12 07:37:39 +00:00
fengzeroz bfefcea7ff Merge pull request #2926 from fengzeroz/main
schema support int/bool/float
2026-08-12 10:58:56 +08:00
fengzero 4e9df9cc9f store single node 2026-08-12 02:36:40 +00:00
fengzero 0e4bf245e1 schema support int/bool/float 2026-08-11 06:46:25 +00:00
fengzeroz 9d9aa31238 Merge pull request #2922 from fengzeroz/main
filter license server
2026-08-06 17:21:52 +08:00
fengzero 423100ef8f filter license server 2026-08-06 09:20:47 +00:00
fengzeroz fc6b787af1 Merge pull request #2920 from fengzeroz/main
return all plugin
2026-08-06 15:06:12 +08:00
fengzero 0c1ab486c2 return all plugin 2026-08-06 07:02:07 +00:00
fengzeroz 67d6bebc18 Merge pull request #2917 from fengzeroz/main
ekuiper: filters no tags write
2026-07-22 15:21:09 +08:00
fengzeroz f6cb6bc01f Merge pull request #2915 from hxy7yx/main-1
feat:preserve tag order after renaming
2026-07-22 15:20:56 +08:00
fengzero 83c96fe7f1 ekuiper: filters no tags write 2026-07-22 06:42:01 +00:00
hxy7yx 066d3fbbb2 feat: add offline CLI to set node state STOPPED 2026-07-19 23:47:29 -07:00
hxy7yx 11f1269b3b feat:preserve tag order after renaming 2026-07-19 19:45:34 -07:00
fengzeroz b400f170cd Merge pull request #2912 from fengzeroz/main
bool: parse option
2026-07-17 17:30:03 +08:00
fengzero 9c474ddf23 bool: parse option 2026-07-17 09:28:11 +00:00
fengzeroz d43def9dab Merge pull request #2908 from fengzeroz/main
#2095 fix allock fail, memory leak
2026-07-16 17:17:08 +08:00
fengzero 4d488bca7c fix allock fail, memory leak 2026-07-16 08:38:29 +00:00
fengzeroz 709250b056 Merge pull request #2907 from fengzeroz/main
#2904 fix fd leak & check socket
2026-07-16 16:37:38 +08:00
fengzero aa8c266d9a fix fd leak & check socket 2026-07-16 08:21:36 +00:00
fengzeroz dfd691d923 Merge pull request #2906 from fengzeroz/main
rm system api
2026-07-16 16:17:20 +08:00
fengzero 33b795c6dc rm system api 2026-07-16 08:03:51 +00:00
fengzeroz 6a836b9fd5 Merge pull request #2901 from hxy7yx/main-1
fix(modbus): handle malloc/pthread_create errors in set_skip_timer
2026-07-13 17:09:43 +08:00
fengzeroz 42c087fb14 Merge pull request #2900 from fengzeroz/main
tpy: parse varconfig & symbols
2026-07-13 17:06:19 +08:00
hxy7yx e0f8208b80 fix(modbus): handle malloc/pthread_create errors in set_skip_timer 2026-07-13 01:51:50 -07:00
fengzero 2778460252 tpy: parse varconfig & symbols 2026-07-13 08:37:08 +00:00
fengzeroz 26d8505bfd Merge pull request #2894 from fengzeroz/main
check array length
2026-07-02 17:27:52 +08:00
fengzero 03ed43b514 check array length 2026-07-02 09:09:51 +00:00
fengzeroz c11e9076b6 Merge pull request #2890 from fengzeroz/main
mqtt check item case
2026-07-02 17:00:29 +08:00
fengzero 6eb9a88f2f mqtt check item case 2026-07-02 08:12:26 +00:00
fengzeroz 4f3cb646b7 Merge pull request #2886 from fengzeroz/main
cid: check depth
2026-07-02 16:10:45 +08:00
fengzero 6772c281fc cid: check depth 2026-07-02 07:54:14 +00:00
fengzeroz 9bd8d2d643 Merge pull request #2882 from fengzeroz/main
fix array string check null
2026-07-02 15:49:41 +08:00
fengzero 0ae8afdb73 fix array string check null 2026-07-02 06:47:33 +00:00
fengzeroz dfb8dea029 Merge pull request #2878 from fengzeroz/main
limit path & fix memory check
2026-07-02 14:36:48 +08:00
fengzero 3156933f2c limit path & fix memory check 2026-07-02 03:03:39 +00:00
77 changed files with 44708 additions and 640 deletions
+20 -3
View File
@@ -9,6 +9,11 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Datalayers is no longer part of the default NeuronEX package. Keep the
# implementation available for standalone/legacy builds, but avoid pulling in
# Apache Arrow unless it is explicitly requested.
option(ENABLE_DATALAYERS "Build the Datalayers northbound plugin" OFF)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Debug")
endif()
@@ -80,6 +85,9 @@ set(PERSIST_SOURCES
src/persist/json/persist_json_plugin.c)
aux_source_directory(src/parser NEURON_SRC_PARSE)
aux_source_directory(src/otel NEURON_SRC_OTEL)
if(NOT ENABLE_DATALAYERS)
list(FILTER NEURON_SRC_PARSE EXCLUDE REGEX "neu_json_datalayers\\.c$")
endif()
set(NEURON_BASE_SOURCES
src/base/tag.c
src/base/neu_plugin_common.c
@@ -103,6 +111,7 @@ set(NEURON_BASE_SOURCES
src/utils/cid.c
src/utils/ede.c
src/utils/tpy.c
src/utils/neu_path.c
${PERSIST_SOURCES})
if (SMART_LINK)
@@ -173,13 +182,15 @@ set(NEURON_SOURCES
plugins/restful/cid_handle.c
plugins/restful/ede_handle.c
plugins/restful/tpy_handle.c
plugins/restful/system_handle.c
plugins/restful/datalayers_handle.c
plugins/restful/rest.c
plugins/restful/user.c
plugins/restful/simulator_handle.c
simulator/modbus_tcp_simulator.c)
if(ENABLE_DATALAYERS)
list(APPEND NEURON_SOURCES plugins/restful/datalayers_handle.c)
endif()
set(CMAKE_BUILD_RPATH ./)
add_executable(neuron)
target_sources(neuron PRIVATE ${NEURON_SOURCES})
@@ -187,6 +198,10 @@ target_include_directories(neuron PRIVATE include/neuron src plugins)
target_link_libraries(neuron dl neuron-base sqlite3 -lm xml2)
target_link_options(neuron PRIVATE "LINKER:--dynamic-list-data")
if(ENABLE_DATALAYERS)
target_compile_definitions(neuron PRIVATE NEU_ENABLE_DATALAYERS)
endif()
#copy file for run
file(COPY ${CMAKE_SOURCE_DIR}/zlog.conf DESTINATION ${CMAKE_BINARY_DIR}/config)
file(COPY ${CMAKE_SOURCE_DIR}/dev.conf DESTINATION ${CMAKE_BINARY_DIR}/config)
@@ -202,7 +217,9 @@ add_subdirectory(plugins/mqtt)
add_subdirectory(plugins/ekuiper)
add_subdirectory(plugins/file)
add_subdirectory(plugins/monitor)
add_subdirectory(plugins/datalayers)
if(ENABLE_DATALAYERS)
add_subdirectory(plugins/datalayers)
endif()
add_subdirectory(plugins/kafka)
add_subdirectory(simulator)
-2
View File
@@ -6,8 +6,6 @@ file perms = 666
stdout_format = "%d:%ms [%V] [%c] %f:%L %m%n"
file_format = "%d:%ms [%V] %f:%L %m%n"
syslog = "<P>%d(%Y-%m-%dT%T%z) %H neuron[%p]: level=%v msg="[%c] %m" file="%f:%L"%n"
[rules]
*.* "./logs/%c.log", 50MB * 2 ~ "./logs/%c.#2r.log"; file_format
+1 -1
View File
@@ -307,7 +307,7 @@ Neuron 将为 IIoT 平台提供一系列 API 服务,用于查询基本信息
**type** 必需
**plugin** 可选
**plugin** 可选,支持逗号分隔多个插件名,如 `plugin=Modbus TCP,Modbus RTU`
**node** 可选
+1 -1
View File
@@ -299,7 +299,7 @@ Neuron provide a series of API services for IIoT platform, to query the basic in
**type** required
**plugin** optional
**plugin** optional, support multiple plugin names separated by comma, e.g. `plugin=Modbus TCP,Modbus RTU`
**node** optional
+5
View File
@@ -40,6 +40,7 @@
#define NEU_DEFAULT_GROUP_INTERVAL 100
#define NEU_NODE_NAME_LEN 128
#define NEU_PLUGIN_NAME_LEN 32
#define NEU_PLUGIN_FILTER_MAX 16
#define NEU_NODE_TAGS_LEN 256
#define NEU_PLUGIN_LIBRARY_LEN 64
#define NEU_PLUGIN_DESCRIPTION_LEN 512
@@ -56,6 +57,10 @@
#define NEU_MSG_MAX_SIZE 2048
#define NEU_PATH_LEN 128
#define NEU_HTTP_BODY_MAX_SIZE (1 * 1024 * 1024)
#define NEU_HTTP_BODY_MAX_SIZE_LARGE (4 * 1024 * 1024)
#define NEU_HTTP_BODY_TOO_BIG (-2)
#define NEU_TAG_META_LENGTH 20
#define NEU_TAG_META_SIZE 32
#define NEU_TAG_FORMAT_LENGTH 16
+2
View File
@@ -158,6 +158,8 @@ typedef enum {
NEU_ERR_PLUGIN_NOT_SUPPORT_FDOWN_DATA = 3028,
NEU_ERR_PLUGIN_NOT_SUPPORT_IMPORT_TAGS = 3029,
NEU_ERR_PLUGIN_APP_NOT_RUNNING = 3030,
NEU_ERR_MQTT_FAILURE = 4000,
NEU_ERR_MQTT_NO_CERTFILESET = 4001,
NEU_ERR_MQTT_CERTFILE_LOAD_FAILURE = 4002,
+2 -1
View File
@@ -556,7 +556,8 @@ typedef struct neu_req_del_node {
typedef struct neu_req_get_node {
neu_node_type_e type;
char plugin[NEU_PLUGIN_NAME_LEN];
int n_plugin;
char plugin[NEU_PLUGIN_FILTER_MAX][NEU_PLUGIN_NAME_LEN];
char node[NEU_NODE_NAME_LEN];
struct {
+1
View File
@@ -38,6 +38,7 @@ extern "C" {
#include "utils/zlog.h"
#include "utils/neu_jwt.h"
#include "utils/neu_path.h"
#include "utils/time.h"
#include "utils/utarray.h"
#include "utils/utextend.h"
+4
View File
@@ -35,6 +35,10 @@ extern "C" {
// buffer overflow.
ssize_t neu_url_decode(const char *s, size_t len, char *buf, size_t size);
// Get a NUL terminated copy of the request body, which the caller must free.
// Returns 0 on success, NEU_HTTP_BODY_TOO_BIG if the body exceeds
// NEU_HTTP_BODY_MAX_SIZE_LARGE, and -1 if the body is empty or on allocation
// failure. On failure `*data` is NULL and `*data_size` is zero.
int neu_http_get_body(nng_aio *aio, void **data, size_t *data_size);
// Find query parameter value of the given name.
+20 -15
View File
@@ -25,24 +25,27 @@
#include <nng/nng.h>
#include <nng/supplemental/http/http.h>
#include "define.h"
#include "utils/neu_jwt.h"
#include "json/neu_json_error.h"
#define NEU_PROCESS_HTTP_REQUEST(aio, req_type, decode_fun, func) \
{ \
char * req_data = NULL; \
size_t req_data_size = 0; \
req_type *req = NULL; \
\
if (neu_http_get_body((aio), (void **) &req_data, &req_data_size) == \
0 && \
decode_fun(req_data, &req) == 0) { \
{ func }; \
decode_fun##_free(req); \
} else { \
neu_http_bad_request(aio, "{\"error\": 1002}"); \
} \
free(req_data); \
#define NEU_PROCESS_HTTP_REQUEST(aio, req_type, decode_fun, func) \
{ \
char * req_data = NULL; \
size_t req_data_size = 0; \
req_type *req = NULL; \
int body_rv = \
neu_http_get_body((aio), (void **) &req_data, &req_data_size); \
\
if (0 == body_rv && decode_fun(req_data, &req) == 0) { \
{ func }; \
decode_fun##_free(req); \
} else if (NEU_HTTP_BODY_TOO_BIG == body_rv) { \
neu_http_bad_request(aio, "{\"error\": 1017}"); \
} else { \
neu_http_bad_request(aio, "{\"error\": 1002}"); \
} \
free(req_data); \
}
#define NEU_PROCESS_HTTP_REQUEST_VALIDATE_JWT(aio, req_type, decode_fun, func) \
@@ -119,6 +122,8 @@ struct neu_http_handler {
char *path;
char *dst_url;
} value;
size_t max_body;
};
int neu_http_add_handler(nng_http_server * server,
+50
View File
@@ -0,0 +1,50 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2024 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#ifndef NEURON_UTILS_PATH_H
#define NEURON_UTILS_PATH_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
// Reject an untrusted path component (e.g. a node/plugin/library name that is
// used to build a filesystem path). Returns true only when `name` is a single,
// safe path segment: non-empty, contains no '/' and is not "." or "..".
bool neu_path_is_valid_component(const char *name);
// Confine an untrusted `path` to the trusted base directory `base`.
//
// The result is the canonical absolute path when it resolves inside `base`
// (either a relative path joined under `base`, or an absolute path that already
// lies within `base`). Returns a newly heap-allocated string the caller must
// free(), or NULL if the path escapes `base`, is invalid, or cannot be
// resolved. A not-yet-existing final component (e.g. a file about to be
// created) is handled by resolving its parent directory.
//
// If `base` is NULL or empty, the current working directory is used.
char *neu_path_confine(const char *base, const char *path);
#ifdef __cplusplus
}
#endif
#endif
+1
View File
@@ -30,6 +30,7 @@ extern "C" {
typedef struct {
int64_t igroup;
int64_t ioffset;
int64_t bitsize;
char name[NEU_TAG_NAME_LEN];
char type[NEU_TAG_NAME_LEN];
} tpy_var_t;
+1 -3
View File
@@ -2,10 +2,8 @@
"ip": "127.0.0.1",
"port": 7000,
"disable_auth": 0,
"syslog_host": "",
"syslog_port": 541,
"sub_filter_error": 0,
"modbus_simulator": {
"port": 502
}
}
}
+1 -1
View File
@@ -391,7 +391,7 @@ int handle_trans_data(neu_plugin_t * plugin,
if (plugin->common.link_state != NEU_NODE_LINK_STATE_CONNECTED) {
pthread_rwlock_unlock(&plugin->plugin_mutex);
return NEU_ERR_PLUGIN_NOT_RUNNING;
return NEU_ERR_PLUGIN_APP_NOT_RUNNING;
}
const route_entry_t *route = route_tbl_get(
+3
View File
@@ -30,6 +30,7 @@
#include "read_write.h"
#define EKUIPER_PLUGIN_URL "tcp://127.0.0.1:7081"
#define EKUIPER_PLUGIN_RECV_MAX_SIZE (4 * 1024 * 1024)
const neu_plugin_module_t neu_plugin_module;
@@ -149,6 +150,8 @@ static inline int start(neu_plugin_t *plugin, const char *url)
nng_pipe_notify(plugin->sock, NNG_PIPE_EV_REM_POST, pipe_rm_cb, plugin);
nng_socket_set_int(plugin->sock, NNG_OPT_SENDBUF, 2048);
nng_socket_set_int(plugin->sock, NNG_OPT_RECVBUF, 2048);
nng_socket_set_size(plugin->sock, NNG_OPT_RECVMAXSZ,
EKUIPER_PLUGIN_RECV_MAX_SIZE);
if ((rv = nng_listen(plugin->sock, url, NULL, 0)) != 0) {
plog_error(plugin, "nng_listen: %s", nng_strerror(rv));
+6 -7
View File
@@ -240,13 +240,12 @@ void recv_data_callback(void *arg)
req->single.tag, req->single.node);
}
} else {
rv = send_write_tags_req(plugin, &req->plural, json_str, trace_flag,
trace_id, span_id);
if (rv == 0) {
if (req->plural.n_tag == 0) {
plog_warn(plugin, "no tags to write on node %s",
req->plural.node);
} else {
if (req->plural.n_tag == 0) {
plog_warn(plugin, "no tags to write on node %s", req->plural.node);
} else {
rv = send_write_tags_req(plugin, &req->plural, json_str, trace_flag,
trace_id, span_id);
if (rv == 0) {
plog_notice(plugin, "write %d tags on node %s",
req->plural.n_tag, req->plural.node);
}
+12 -6
View File
@@ -181,7 +181,9 @@ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
{
neu_dvalue_t dvalue = { 0 };
FILE *fp = fopen(tag->address, "r");
char *safe_path = neu_path_confine(NULL, tag->address);
FILE *fp = safe_path ? fopen(safe_path, "r") : NULL;
free(safe_path);
if (fp == NULL) {
dvalue.type = NEU_TYPE_ERROR;
if (errno == ENOENT) {
@@ -190,14 +192,16 @@ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
dvalue.value.i32 = NEU_ERR_FILE_OPEN_FAILURE;
}
} else {
char *buf = calloc(1, 1024);
int ret = fread(buf, 1, 1024, fp);
if (ret < 0) {
char * buf = calloc(1, 1024);
size_t ret = fread(buf, 1, 1024, fp);
if (ret == 0 && ferror(fp)) {
dvalue.type = NEU_TYPE_ERROR;
dvalue.value.i32 = NEU_ERR_FILE_READ_FAILURE;
} else {
size_t n = ret < NEU_VALUE_SIZE - 1 ? ret : NEU_VALUE_SIZE - 1;
dvalue.type = NEU_TYPE_STRING;
strncpy(dvalue.value.str, buf, NEU_VALUE_SIZE - 1);
strncpy(dvalue.value.str, buf, n);
dvalue.value.str[n] = '\0';
}
fclose(fp);
@@ -219,7 +223,9 @@ static int driver_group_timer(neu_plugin_t *plugin, neu_plugin_group_t *group)
static int driver_write(neu_plugin_t *plugin, void *req, neu_datatag_t *tag,
neu_value_u value)
{
FILE *fp = fopen(tag->address, "w");
char *safe_path = neu_path_confine(NULL, tag->address);
FILE *fp = safe_path ? fopen(safe_path, "w") : NULL;
free(safe_path);
if (fp == NULL) {
if (errno == ENOENT) {
plugin->common.adapter_callbacks->driver.write_response(
+2 -2
View File
@@ -142,7 +142,7 @@ int handle_trans_data(neu_plugin_t *plugin, neu_reqresp_trans_data_t *data)
int rv = 0;
if (NULL == plugin->rk) {
return NEU_ERR_PLUGIN_NOT_RUNNING;
return NEU_ERR_PLUGIN_APP_NOT_RUNNING;
}
const kafka_route_entry_t *route =
@@ -176,7 +176,7 @@ int handle_trans_data(neu_plugin_t *plugin, neu_reqresp_trans_data_t *data)
}
free(json_str);
return rv == 0 ? NEU_ERR_SUCCESS : NEU_ERR_PLUGIN_NOT_RUNNING;
return rv == 0 ? NEU_ERR_SUCCESS : NEU_ERR_PLUGIN_APP_NOT_RUNNING;
}
int handle_subscribe_group(neu_plugin_t *plugin, neu_req_subscribe_t *sub)
+3 -3
View File
@@ -350,7 +350,7 @@ static int kafka_plugin_config(neu_plugin_t *plugin, const char *setting)
if (NULL == plugin->rk) {
plog_error(plugin, "create kafka producer fail");
kafka_config_fini(&config);
return NEU_ERR_PLUGIN_NOT_RUNNING;
return NEU_ERR_PLUGIN_APP_NOT_RUNNING;
}
if (0 != start_poll_timer(plugin)) {
@@ -375,7 +375,7 @@ static int kafka_plugin_start(neu_plugin_t *plugin)
{
if (NULL == plugin->rk) {
plog_error(plugin, "kafka producer is NULL");
return NEU_ERR_PLUGIN_NOT_RUNNING;
return NEU_ERR_PLUGIN_APP_NOT_RUNNING;
}
if (0 != start_poll_timer(plugin)) {
@@ -445,7 +445,7 @@ static int kafka_plugin_request(neu_plugin_t *plugin, neu_reqresp_head_t *head,
break;
}
default:
error = NEU_ERR_PLUGIN_NOT_RUNNING;
error = NEU_ERR_PLUGIN_APP_NOT_RUNNING;
break;
}
+11 -1
View File
@@ -332,13 +332,23 @@ void set_skip_timer(uint8_t slave_id, uint32_t degrade_time)
{
degrade_timer_data_t *data =
(degrade_timer_data_t *) malloc(sizeof(degrade_timer_data_t));
if (data == NULL) {
skip[slave_id] = false;
return;
}
data->slave_id = slave_id;
data->degrade_time = degrade_time;
failed_cycles[slave_id] = 0;
pthread_t timer_thread;
pthread_create(&timer_thread, NULL, degrade_timer, data);
int ret = pthread_create(&timer_thread, NULL, degrade_timer, data);
if (ret != 0) {
free(data);
skip[slave_id] = false;
return;
}
pthread_detach(timer_thread);
}
+20 -6
View File
@@ -41,9 +41,11 @@ static char *generate_heartbeat_json(neu_plugin_t *plugin, UT_array *states)
char * json_str = NULL;
json.n_state = utarray_len(states);
json.states = calloc(json.n_state, sizeof(neu_json_node_state_t));
if (NULL == json.states) {
return NULL;
if (json.n_state > 0) {
json.states = calloc(json.n_state, sizeof(neu_json_node_state_t));
if (NULL == json.states) {
return NULL;
}
}
utarray_foreach(states, neu_nodes_state_t *, state)
@@ -210,7 +212,11 @@ static char *generate_event_json(neu_plugin_t *plugin, neu_reqresp_type_e event,
json_req.add_gtag.n_group = add_gtag->n_group;
json_req.add_gtag.groups =
calloc(add_gtag->n_group, sizeof(*json_req.add_gtag.groups));
for (u_int16_t i = 0; i < add_gtag->n_group; i++) {
if (NULL == json_req.add_gtag.groups) {
plog_error(plugin, "calloc fail");
break;
}
for (uint16_t i = 0; i < add_gtag->n_group; i++) {
json_req.add_gtag.groups[i].group =
strdup(add_gtag->groups[i].group);
json_req.add_gtag.groups[i].n_tag = add_gtag->groups[i].n_tag;
@@ -218,7 +224,11 @@ static char *generate_event_json(neu_plugin_t *plugin, neu_reqresp_type_e event,
json_req.add_gtag.groups[i].tags =
calloc(add_gtag->groups[i].n_tag,
sizeof(*json_req.add_gtag.groups->tags));
for (u_int16_t j = 0; j < add_gtag->groups[i].n_tag; j++) {
if (NULL == json_req.add_gtag.groups[i].tags) {
plog_error(plugin, "calloc fail");
break;
}
for (uint16_t j = 0; j < add_gtag->groups[i].n_tag; j++) {
json_req.add_gtag.groups[i].tags[j].type =
add_gtag->groups[i].tags[j].type;
json_req.add_gtag.groups[i].tags[j].name =
@@ -239,7 +249,11 @@ static char *generate_event_json(neu_plugin_t *plugin, neu_reqresp_type_e event,
neu_json_encode_by_fn(&json_req, neu_json_encode_add_gtags_req,
&json_str);
free(json_req.add_tags.tags);
for (uint16_t i = 0; i < add_gtag->n_group; i++) {
free(json_req.add_gtag.groups[i].group);
free(json_req.add_gtag.groups[i].tags);
}
free(json_req.add_gtag.groups);
break;
}
case NEU_REQ_ADD_PLUGIN_EVENT: {
+59 -6
View File
@@ -379,7 +379,8 @@ static int json_value_to_tag_value(union neu_json_value *req,
value->value.strs.strs =
calloc(req->val_array_str.length, sizeof(char *));
for (size_t i = 0; i < req->val_array_str.length; i++) {
value->value.strs.strs[i] = strdup(req->val_array_str.p_strs[i]);
const char *s = req->val_array_str.p_strs[i];
value->value.strs.strs[i] = strdup(s ? s : "");
}
break;
case NEU_JSON_OBJECT:
@@ -582,7 +583,9 @@ void handle_write_req(neu_mqtt_qos_e qos, const char *topic,
}
for (size_t i = 0; i < wr->n_tags; i++) {
if (wr->tags[i]->name == NULL || wr->tags[i]->value == NULL) {
if (wr->tags[i]->name == NULL ||
wr->tags[i]->item_case != MODEL__DATA_ITEM__ITEM_VALUE ||
wr->tags[i]->value == NULL) {
plog_error(plugin, "write request tag missing name/value");
model__write_request__free_unpacked(wr, NULL);
return;
@@ -604,7 +607,12 @@ void handle_write_req(neu_mqtt_qos_e qos, const char *topic,
}
}
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
if (NULL == mqtt) {
plog_error(plugin, "calloc mqtt fail");
model__write_request__free_unpacked(wr, NULL);
return;
}
mqtt->uuid = strdup(wr->uuid);
mqtt->payload = NULL;
mqtt->traceparent = NULL;
@@ -620,6 +628,14 @@ void handle_write_req(neu_mqtt_qos_e qos, const char *topic,
cmd.n_tag = wr->n_tags;
if (cmd.n_tag > 0) {
cmd.tags = calloc(cmd.n_tag, sizeof(neu_resp_tag_value_t));
if (NULL == cmd.tags) {
plog_error(plugin, "calloc cmd.tags fail");
free(cmd.driver);
free(cmd.group);
neu_json_decode_mqtt_req_free(mqtt);
model__write_request__free_unpacked(wr, NULL);
return;
}
}
for (int i = 0; i < cmd.n_tag; i++) {
@@ -930,7 +946,12 @@ void handle_driver_action_req(neu_mqtt_qos_e qos, const char *topic,
return;
}
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
if (NULL == mqtt) {
plog_error(plugin, "calloc mqtt fail");
model__driver_action_request__free_unpacked(dar, NULL);
return;
}
mqtt->uuid = strdup(dar->uuid);
mqtt->payload = NULL;
mqtt->traceparent = NULL;
@@ -1004,7 +1025,12 @@ void handle_read_req(neu_mqtt_qos_e qos, const char *topic,
neu_reqresp_head_t header = { 0 };
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
mqtt = calloc(1, sizeof(neu_json_mqtt_t));
if (NULL == mqtt) {
plog_error(plugin, "calloc mqtt fail");
model__read_request__free_unpacked(read_req, NULL);
return;
}
mqtt->uuid = strdup(read_req->uuid);
mqtt->payload = NULL;
mqtt->traceparent = NULL;
@@ -1021,6 +1047,14 @@ void handle_read_req(neu_mqtt_qos_e qos, const char *topic,
cmd.n_tag = read_req->n_tags;
if (cmd.n_tag > 0) {
cmd.tags = calloc(cmd.n_tag, sizeof(char *));
if (NULL == cmd.tags) {
plog_error(plugin, "calloc cmd.tags fail");
free(cmd.driver);
free(cmd.group);
neu_json_decode_mqtt_req_free(mqtt);
model__read_request__free_unpacked(read_req, NULL);
return;
}
}
for (int i = 0; i < cmd.n_tag; i++) {
cmd.tags[i] = strdup(read_req->tags[i]);
@@ -1029,6 +1063,7 @@ void handle_read_req(neu_mqtt_qos_e qos, const char *topic,
if (0 != neu_plugin_op(plugin, header, &cmd)) {
neu_req_read_group_fini(&cmd);
plog_error(plugin, "neu_plugin_op(NEU_REQ_READ_GROUP) fail");
model__read_request__free_unpacked(read_req, NULL);
return;
}
@@ -1713,12 +1748,14 @@ int handle_nodes_state(neu_plugin_t *plugin, neu_reqresp_nodes_state_t *states)
goto end;
}
bool alloc_fail = false;
utarray_foreach(states->states, neu_nodes_state_t *, state)
{
Model__NodeState *ns = calloc(1, sizeof(Model__NodeState));
if (NULL == ns) {
plog_error(plugin, "malloc fail");
goto end;
alloc_fail = true;
break;
}
model__node_state__init(ns);
ns->node = state->node;
@@ -1728,9 +1765,25 @@ int handle_nodes_state(neu_plugin_t *plugin, neu_reqresp_nodes_state_t *states)
node_states[utarray_eltidx(states->states, state)] = ns;
}
if (alloc_fail) {
for (size_t i = 0; i < nsr.n_nodes; i++) {
free(node_states[i]);
}
free(node_states);
goto end;
}
nsr.nodes = node_states;
size = model__node_state_report__get_packed_size(&nsr);
json_str = malloc(size);
if (NULL == json_str) {
plog_error(plugin, "malloc fail");
for (size_t i = 0; i < nsr.n_nodes; i++) {
free(node_states[i]);
}
free(node_states);
goto end;
}
model__node_state_report__pack(&nsr, (uint8_t *) json_str);
for (size_t i = 0; i < nsr.n_nodes; i++) {
+28 -3
View File
@@ -49,10 +49,31 @@ static int mqtt_schema_parse(json_t *root, mqtt_schema_vt_t **vts,
str_val[strlen(str_val) - 1] == '}') {
return -1;
} else {
vt->vt = MQTT_SCHEMA_UD;
vt->vt = MQTT_SCHEMA_UD;
vt->jtype = NEU_JSON_STR;
strncpy(vt->ud, str_val, sizeof(vt->ud) - 1);
}
}
} else if (json_is_integer(value) || json_is_real(value) ||
json_is_boolean(value)) {
*vts_len += 1;
*vts = realloc(*vts, *vts_len * sizeof(mqtt_schema_vt_t));
mqtt_schema_vt_t *vt = &(*vts)[*vts_len - 1];
memset(vt, 0, sizeof(mqtt_schema_vt_t));
strncpy(vt->name, key, sizeof(vt->name) - 1);
vt->vt = MQTT_SCHEMA_UD;
if (json_is_integer(value)) {
vt->jtype = NEU_JSON_INT;
vt->jvalue.val_int = json_integer_value(value);
} else if (json_is_real(value)) {
vt->jtype = NEU_JSON_DOUBLE;
vt->jvalue.val_double = json_real_value(value);
} else {
vt->jtype = NEU_JSON_BOOL;
vt->jvalue.val_bool = json_boolean_value(value);
}
} else if (json_is_object(value)) {
if (deep >= 3) {
return -1;
@@ -280,8 +301,12 @@ static void *schema_encode(char *driver, char *group,
break;
}
case MQTT_SCHEMA_UD:
elem.t = NEU_JSON_STR;
elem.v.val_str = vts[i].ud;
elem.t = vts[i].jtype;
if (vts[i].jtype == NEU_JSON_STR) {
elem.v.val_str = vts[i].ud;
} else {
elem.v = vts[i].jvalue;
}
break;
case MQTT_SCHEMA_OBJECT: {
void *sub_root = schema_encode(driver, group, tags, vts[i].sub_vts,
+3
View File
@@ -53,6 +53,9 @@ typedef struct mqtt_schema_vt {
char ud[128];
neu_json_type_e jtype;
neu_json_value_u jvalue;
mqtt_schema_vt_t *sub_vts;
size_t n_sub_vts;
} mqtt_schema_vt_t;
+45 -14
View File
@@ -64,12 +64,10 @@ static bool tags_check_symbol(const char *tags)
{
for (size_t i = 0; i < strlen(tags); i++) {
char c = tags[i];
if (c == '!' || c == '@' || c == '#' || c == '$' || c == '%' ||
c == '^' || c == '&' || c == '*' || c == '(' || c == ')' ||
c == '-' || c == '+' || c == '=' || c == '[' || c == ']' ||
c == '{' || c == '}' || c == ';' || c == ':' || c == '\'' ||
c == '"' || c == '<' || c == '>' || c == '?' || c == '\\' ||
c == '|' || c == '~' || c == '`' || c == '\t' || c == '\n') {
if (c == '^' || c == '=' || c == '[' || c == ']' || c == '{' ||
c == '}' || c == ';' || c == ':' || c == '\'' || c == '"' ||
c == '<' || c == '>' || c == '?' || c == '\\' || c == '|' ||
c == '~' || c == '`' || c == '\t' || c == '\n') {
return false;
}
}
@@ -203,10 +201,10 @@ void handle_del_adapter(nng_aio *aio)
void handle_get_adapter(nng_aio *aio)
{
int ret = 0;
char plugin_name[NEU_PLUGIN_NAME_LEN] = { 0 };
char node_name[NEU_NODE_NAME_LEN] = { 0 };
char group_name[NEU_GROUP_NAME_LEN] = { 0 };
int ret = 0;
char plugin_name[NEU_PLUGIN_FILTER_MAX * NEU_PLUGIN_NAME_LEN] = { 0 };
char node_name[NEU_NODE_NAME_LEN] = { 0 };
char group_name[NEU_GROUP_NAME_LEN] = { 0 };
neu_plugin_t * plugin = neu_rest_get_plugin();
neu_node_type_e node_type = { 0 };
@@ -226,9 +224,40 @@ void handle_get_adapter(nng_aio *aio)
return;
}
if (neu_http_get_param_str(aio, "plugin", plugin_name,
sizeof(plugin_name)) > 0) {
strncpy(cmd.plugin, plugin_name, NEU_PLUGIN_NAME_LEN - 1);
ssize_t plugin_len =
neu_http_get_param_str(aio, "plugin", plugin_name, sizeof(plugin_name));
if (plugin_len > 0) {
if (plugin_len >= (ssize_t) sizeof(plugin_name)) {
NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, {
neu_http_response(aio, error_code.error, result_error);
})
return;
}
char *save = NULL;
char *p = strtok_r(plugin_name, ",", &save);
while (p != NULL) {
while (' ' == *p || '\t' == *p) {
p++;
}
size_t len = strlen(p);
while (len > 0 && (' ' == p[len - 1] || '\t' == p[len - 1])) {
p[--len] = '\0';
}
if (len == 0) {
p = strtok_r(NULL, ",", &save);
continue;
}
if (cmd.n_plugin >= NEU_PLUGIN_FILTER_MAX) {
NEU_JSON_RESPONSE_ERROR(NEU_ERR_PARAM_IS_WRONG, {
neu_http_response(aio, error_code.error, result_error);
})
return;
}
strncpy(cmd.plugin[cmd.n_plugin], p, NEU_PLUGIN_NAME_LEN - 1);
cmd.n_plugin++;
p = strtok_r(NULL, ",", &save);
}
}
if (neu_http_get_param_str(aio, "node", node_name, sizeof(node_name)) > 0) {
@@ -706,7 +735,9 @@ void handle_put_node_tag(nng_aio *aio)
header.ctx = aio;
header.type = NEU_REQ_UPDATE_NODE_TAG;
strncpy(cmd.node, req->name, NEU_NODE_NAME_LEN - 1);
strncpy(cmd.tags, req->tags, NEU_NODE_TAGS_LEN - 1);
if (req->tags != NULL) {
strncpy(cmd.tags, req->tags, NEU_NODE_TAGS_LEN - 1);
}
int ret = neu_plugin_op(plugin, header, &cmd);
if (ret != 0) {
NEU_JSON_RESPONSE_ERROR(NEU_ERR_IS_BUSY, {
+9 -4
View File
@@ -613,11 +613,16 @@ static int unsub_app(context_t *ctx, neu_resp_subscribe_info_t *info);
static inline bool is_default_app_node(const char *name, const char *plugin)
{
return (0 == strcmp("DataStorage", name) &&
0 == strcmp("Datalayers", plugin)) ||
(0 == strcmp("DataProcessing", name) &&
0 == strcmp("eKuiper", plugin)) ||
bool is_default = (0 == strcmp("DataProcessing", name) &&
0 == strcmp("eKuiper", plugin)) ||
(0 == strcmp("monitor", name) && 0 == strcmp("Monitor", plugin));
#ifdef NEU_ENABLE_DATALAYERS
is_default = is_default ||
(0 == strcmp("DataStorage", name) && 0 == strcmp("Datalayers", plugin));
#endif
return is_default;
}
static void get_apps_context_next(context_t *ctx, neu_reqresp_type_e type,
+17 -11
View File
@@ -27,7 +27,9 @@
#include "adapter_handle.h"
#include "cert_handle.h"
#include "cid_handle.h"
#ifdef NEU_ENABLE_DATALAYERS
#include "datalayers_handle.h"
#endif
#include "datatag_handle.h"
#include "ede_handle.h"
#include "global_config_handle.h"
@@ -39,7 +41,6 @@
#include "plugin_handle.h"
#include "rw_handle.h"
#include "scan_handle.h"
#include "system_handle.h"
#include "tpy_handle.h"
#include "utils/http.h"
#include "version_handle.h"
@@ -150,12 +151,10 @@ static struct neu_http_handler cors_handler[] = {
{
.url = "/api/v2/ede",
},
{
.url = "/api/v2/system/ctl",
},
{
.url = "/api/v2/users",
},
#ifdef NEU_ENABLE_DATALAYERS
{
.url = "/api/v2/datalayers/group",
},
@@ -165,6 +164,7 @@ static struct neu_http_handler cors_handler[] = {
{
.url = "/api/v2/datalayers/tag",
},
#endif
{
.url = "/api/v2/export/db",
},
@@ -269,25 +269,28 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/tags",
.value.handler = handle_add_tags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_POST,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/gtags",
.value.handler = handle_add_gtags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_POST,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/tags/import",
.value.handler = handle_import_tags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_PUT,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/tags",
.value.handler = handle_update_tags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
@@ -408,12 +411,14 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/write/tags",
.value.handler = handle_write_tags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_POST,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/write/gtags",
.value.handler = handle_write_gtags,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_POST,
@@ -450,6 +455,7 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/subscribes",
.value.handler = handle_grp_subscribes,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
@@ -516,12 +522,14 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/global/config",
.value.handler = handle_put_global_config,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_PUT,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/global/drivers",
.value.handler = handle_put_drivers,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
@@ -540,6 +548,7 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/global/apps",
.value.handler = handle_put_apps,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
@@ -571,12 +580,6 @@ static struct neu_http_handler rest_handlers[] = {
.url = "/api/v2/otel",
.value.handler = handle_otel_get,
},
{
.method = NEU_HTTP_METHOD_POST,
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/system/ctl",
.value.handler = handle_system_ctl,
},
{
.method = NEU_HTTP_METHOD_GET,
.type = NEU_HTTP_HANDLER_FUNCTION,
@@ -601,6 +604,7 @@ static struct neu_http_handler rest_handlers[] = {
.url = "/api/v2/users",
.value.handler = handle_delete_user,
},
#ifdef NEU_ENABLE_DATALAYERS
{
.method = NEU_HTTP_METHOD_GET,
.type = NEU_HTTP_HANDLER_FUNCTION,
@@ -619,6 +623,7 @@ static struct neu_http_handler rest_handlers[] = {
.url = "/api/v2/datalayers/tag",
.value.handler = handle_datalayers_get_tag,
},
#endif
{
.method = NEU_HTTP_METHOD_GET,
.type = NEU_HTTP_HANDLER_FUNCTION,
@@ -750,6 +755,7 @@ static struct neu_http_handler rest_handlers[] = {
.type = NEU_HTTP_HANDLER_FUNCTION,
.url = "/api/v2/simulator/config",
.value.handler = handle_simulator_set_config,
.max_body = NEU_HTTP_BODY_MAX_SIZE_LARGE,
},
{
.method = NEU_HTTP_METHOD_GET,
+4
View File
@@ -27,7 +27,9 @@
#include "adapter_handle.h"
#include "argparse.h"
#include "cert_handle.h"
#ifdef NEU_ENABLE_DATALAYERS
#include "datalayers_handle.h"
#endif
#include "datatag_handle.h"
#include "define.h"
#include "global_config_handle.h"
@@ -300,6 +302,7 @@ static int dashb_plugin_request(neu_plugin_t * plugin,
neu_otel_scope_set_status_code2(scope, NEU_OTEL_STATUS_OK, 0);
}
break;
#ifdef NEU_ENABLE_DATALAYERS
case NEU_RESP_GET_DATALAYERS_GROUPS:
handle_datalayers_get_groups_resp(
header->ctx, (neu_resp_get_subscribe_group_t *) data);
@@ -321,6 +324,7 @@ static int dashb_plugin_request(neu_plugin_t * plugin,
neu_otel_scope_set_status_code2(scope, NEU_OTEL_STATUS_OK, 0);
}
break;
#endif
case NEU_RESP_GET_DRIVER_SUBSCRIBE_GROUP:
handle_grp_get_subscribes_resp(header->ctx,
(neu_resp_get_subscribe_group_t *) data);
+9 -5
View File
@@ -365,8 +365,8 @@ void handle_write(nng_aio *aio)
cmd.value.value.strs.strs =
calloc(req->value.val_array_str.length, sizeof(char *));
for (size_t i = 0; i < req->value.val_array_str.length; i++) {
cmd.value.value.strs.strs[i] =
strdup(req->value.val_array_str.p_strs[i]);
const char *s = req->value.val_array_str.p_strs[i];
cmd.value.value.strs.strs[i] = strdup(s ? s : "");
}
break;
case NEU_JSON_OBJECT:
@@ -588,8 +588,10 @@ void handle_write_tags(nng_aio *aio)
sizeof(char *));
for (size_t k = 0;
k < req->tags[i].value.val_array_str.length; k++) {
const char *s =
req->tags[i].value.val_array_str.p_strs[k];
cmd.tags[i].value.value.strs.strs[k] =
strdup(req->tags[i].value.val_array_str.p_strs[k]);
strdup(s ? s : "");
}
break;
case NEU_JSON_OBJECT:
@@ -732,8 +734,10 @@ static void trans(neu_json_write_gtags_req_t *req, neu_req_write_gtags_t *cmd)
for (size_t j = 0;
j < req->groups[i].tags[k].value.val_array_str.length;
j++) {
cmd->groups[i].tags[k].value.value.strs.strs[j] = strdup(
req->groups[i].tags[k].value.val_array_str.p_strs[j]);
const char *s =
req->groups[i].tags[k].value.val_array_str.p_strs[j];
cmd->groups[i].tags[k].value.value.strs.strs[j] =
strdup(s ? s : "");
}
break;
-32
View File
@@ -1,32 +0,0 @@
#include <dirent.h>
#include <errno.h>
#include <getopt.h>
#include <jwt.h>
#include <libgen.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "json/neu_json_fn.h"
#include "handle.h"
#include "utils/http.h"
#include "utils/neu_jwt.h"
#include "parser/neu_json_system.h"
#include "system_handle.h"
void handle_system_ctl(nng_aio *aio)
{
NEU_PROCESS_HTTP_REQUEST_VALIDATE_JWT(
aio, neu_json_system_ctl_req_t, neu_json_decode_system_ctl_req, {
if (req->action == 1) {
neu_http_ok(aio, "{\"error\": 0 }");
exit(-2);
}
})
}
-27
View File
@@ -1,27 +0,0 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2022 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#ifndef _NEU_SYSTEM_HANDLE_H_
#define _NEU_SYSTEM_HANDLE_H_
#include <nng/nng.h>
void handle_system_ctl(nng_aio *aio);
#endif
+144 -71
View File
@@ -17,6 +17,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
@@ -31,12 +32,15 @@
#include <zlog.h>
#include "argparse.h"
#include "define.h"
#include "persist/persist.h"
#include "utils/log.h"
#include "version.h"
#include "json/json.h"
#include "json/neu_json_param.h"
#include <sqlite3.h>
#define OPTIONAL_ARGUMENT_IS_PRESENT \
((optarg == NULL && optind < argc && argv[optind][0] != '-') \
? (bool) (optarg = argv[optind++]) \
@@ -65,6 +69,9 @@ const char *usage_text =
" -d, --daemon run as daemon process\n"
" -h, --help show this help message\n"
" stop stop running neuron\n"
" node stop --node <NAME>\n"
" set a south/north node state to STOPPED in the DB\n"
" (offline; restart neuron manually afterwards)\n"
" --log log to the stdout\n"
" --log_level <LEVEL> default log level(DEBUG,NOTICE)\n"
" --reset-password reset dashboard to use default password\n"
@@ -78,8 +85,6 @@ const char *usage_text =
" --config_file <PATH> startup parameter configuration file\n"
" --config_dir <DIR> directory from which neuron reads configuration\n"
" --plugin_dir <DIR> directory from which neuron loads plugin lib files\n"
" --syslog_host <HOST> syslog server host to which neuron will send logs\n"
" --syslog_port <PORT> syslog server port (default 541 if not provided)\n"
" --sub_filter_error The subscribe attribute only detects the last read value and does not report any error tags\n"
"\n";
// clang-format on
@@ -112,6 +117,120 @@ static inline int reset_password()
return rv;
}
static inline bool file_exists(const char *const path)
{
struct stat buf = { 0 };
return -1 != stat(path, &buf);
}
#define NEURON_DB_FILE "persistence/sqlite.db"
int neu_cli_stop_node_offline(const char *node_name)
{
if (NULL == node_name || '\0' == node_name[0] ||
strlen(node_name) >= NEU_NODE_NAME_LEN) {
fprintf(stderr, "node stop requires --node with 1-%d bytes\n",
NEU_NODE_NAME_LEN - 1);
return 1;
}
for (const unsigned char *p = (const unsigned char *) node_name; *p; ++p) {
if (iscntrl(*p)) {
fprintf(stderr, "node name must not contain control characters\n");
return 1;
}
}
if (!file_exists(NEURON_DB_FILE)) {
fprintf(stderr, "database `%s` not exists\n", NEURON_DB_FILE);
return 1;
}
if (0 != access(NEURON_DB_FILE, R_OK | W_OK)) {
fprintf(stderr,
"no read/write permission on `%s` (errno=%d: %s).\n"
"If Neuron was started with sudo, run this command with the "
"same user, e.g. `sudo ./neuron node stop --node ...`\n",
NEURON_DB_FILE, errno, strerror(errno));
return 1;
}
sqlite3 *db = NULL;
int rc =
sqlite3_open_v2(NEURON_DB_FILE, &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, NULL);
if (SQLITE_OK != rc) {
fprintf(stderr, "failed to open `%s`: %s\n", NEURON_DB_FILE,
db ? sqlite3_errmsg(db) : sqlite3_errstr(rc));
if (db) {
sqlite3_close(db);
}
return 1;
}
sqlite3_busy_timeout(db, 3000);
sqlite3_stmt *stmt = NULL;
const char * select_sql = "SELECT type FROM nodes WHERE name=? LIMIT 1";
rc = sqlite3_prepare_v2(db, select_sql, -1, &stmt, NULL);
if (SQLITE_OK != rc) {
fprintf(stderr, "prepare failed: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
sqlite3_bind_text(stmt, 1, node_name, -1, SQLITE_TRANSIENT);
rc = sqlite3_step(stmt);
if (SQLITE_ROW != rc) {
fprintf(stderr, "node `%s` not found\n", node_name);
sqlite3_finalize(stmt);
sqlite3_close(db);
return 1;
}
int type = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
if (NEU_NA_TYPE_DRIVER != type && NEU_NA_TYPE_APP != type) {
fprintf(stderr, "node `%s` is not a south/north node\n", node_name);
sqlite3_close(db);
return 1;
}
const char *update_sql =
"UPDATE nodes SET state=? WHERE name=? AND type IN (?,?)";
rc = sqlite3_prepare_v2(db, update_sql, -1, &stmt, NULL);
if (SQLITE_OK != rc) {
fprintf(stderr, "prepare update failed: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
sqlite3_bind_int(stmt, 1, NEU_NODE_RUNNING_STATE_STOPPED);
sqlite3_bind_text(stmt, 2, node_name, -1, SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, 3, NEU_NA_TYPE_DRIVER);
sqlite3_bind_int(stmt, 4, NEU_NA_TYPE_APP);
rc = sqlite3_step(stmt);
if (SQLITE_DONE != rc) {
fprintf(stderr,
"failed to update node `%s`: %s\n"
"If Neuron is still running, stop it first (`sudo ./neuron "
"stop`), then retry.\n",
node_name, sqlite3_errmsg(db));
sqlite3_finalize(stmt);
sqlite3_close(db);
return 1;
}
if (0 == sqlite3_changes(db)) {
fprintf(stderr, "node `%s` was not updated\n", node_name);
sqlite3_finalize(stmt);
sqlite3_close(db);
return 1;
}
sqlite3_finalize(stmt);
sqlite3_close(db);
printf("node `%s` state set to STOPPED (uid=%u); restart neuron manually\n",
node_name, (unsigned) getuid());
return 0;
}
static inline size_t parse_restart_policy(const char *s, size_t *out)
{
if (0 == strcmp(s, "always")) {
@@ -135,17 +254,14 @@ static inline size_t parse_restart_policy(const char *s, size_t *out)
return 0;
}
static inline bool file_exists(const char *const path)
{
struct stat buf = { 0 };
return -1 != stat(path, &buf);
}
static inline int load_spec_arg(int argc, char *argv[], neu_cli_args_t *args)
{
int ret = 0;
if (argc > 1 && strcmp(argv[1], "stop") == 0) {
args->stop = true;
} else if (argc > 2 && strcmp(argv[1], "node") == 0 &&
strcmp(argv[2], "stop") == 0) {
args->node_stop = true;
}
return ret;
}
@@ -242,24 +358,6 @@ static inline int load_env(neu_cli_args_t *args, char **log_level_out,
*plugin_dir_out = strdup(plugin_dir);
}
char *syslog_host = getenv(NEU_ENV_SYSLOG_HOST);
if (NULL != syslog_host) {
if (NULL != args->syslog_host) {
free(args->syslog_host);
}
args->syslog_host = strdup(syslog_host);
}
char *syslog_port = getenv(NEU_ENV_SYSLOG_PORT);
if (NULL != syslog_port) {
int port = atoi(syslog_port);
if (port < 1 || 65535 < port) {
printf("neuron %s setting invalid!\n", NEU_ENV_SYSLOG_PORT);
ret = -1;
break;
}
args->syslog_port = port;
}
} while (0);
return ret;
@@ -301,8 +399,6 @@ static inline int load_config_file(int argc, char *argv[],
{ .name = "disable_auth", .t = NEU_JSON_INT },
{ .name = "ip", .t = NEU_JSON_STR },
{ .name = "port", .t = NEU_JSON_INT },
{ .name = "syslog_host", .t = NEU_JSON_STR },
{ .name = "syslog_port", .t = NEU_JSON_INT },
{ .name = "sub_filter_error", .t = NEU_JSON_INT },
};
@@ -366,27 +462,9 @@ static inline int load_config_file(int argc, char *argv[],
break;
}
if (elems[3].v.val_str != NULL) {
if (strlen(elems[3].v.val_str) > 0) {
args->syslog_host = strdup(elems[3].v.val_str);
}
} else {
printf("config file %s syslog_host setting error!\n",
config_file);
break;
}
if (0 < elems[4].v.val_int && elems[4].v.val_int < 65536) {
args->syslog_port = elems[4].v.val_int;
} else {
printf("config file %s syslog_port setting invalid!\n",
config_file);
break;
}
if (elems[5].v.val_int == 1) {
if (elems[3].v.val_int == 1) {
args->sub_filter_err = true;
} else if (elems[5].v.val_int == 0) {
} else if (elems[3].v.val_int == 0) {
args->sub_filter_err = false;
} else {
printf("config file %s sub_filter_error setting error!\n",
@@ -419,10 +497,6 @@ static inline int load_config_file(int argc, char *argv[],
free(elems[1].v.val_str);
}
if (elems[3].v.val_str != NULL) {
free(elems[3].v.val_str);
}
return ret;
}
@@ -447,9 +521,8 @@ void neu_cli_args_init(neu_cli_args_t *args, int argc, char *argv[])
{ "config_dir", required_argument, NULL, 'c' },
{ "plugin_dir", required_argument, NULL, 'p' },
{ "stop", no_argument, NULL, 's' },
{ "syslog_host", required_argument, NULL, 'S' },
{ "syslog_port", required_argument, NULL, 'P' },
{ "sub_filter_error", no_argument, NULL, 'f' },
{ "node", required_argument, NULL, 'n' },
{ NULL, 0, NULL, 0 },
};
@@ -527,25 +600,13 @@ void neu_cli_args_init(neu_cli_args_t *args, int argc, char *argv[])
}
plugin_dir = strdup(optarg);
break;
case 'P': {
int syslog_port = atoi(optarg);
if (syslog_port < 1 || 65535 < syslog_port) {
fprintf(stderr, "%s: option '--syslog_port' invalid : `%s`\n",
argv[0], optarg);
goto quit;
}
args->syslog_port = syslog_port;
break;
}
case 'S':
if (args->syslog_host) {
free(args->syslog_host);
}
args->syslog_host = strdup(optarg);
break;
case 'f':
args->sub_filter_err = true;
break;
case 'n':
free(args->node_name);
args->node_name = strdup(optarg);
break;
case '?':
default:
usage();
@@ -596,6 +657,18 @@ void neu_cli_args_init(neu_cli_args_t *args, int argc, char *argv[])
goto quit;
}
if (args->node_stop) {
if (args->node_name == NULL || args->node_name[0] == '\0') {
fprintf(stderr, "node stop requires --node <NAME>\n");
ret = 1;
goto quit;
}
} else if (args->node_name != NULL) {
fprintf(stderr, "--node requires `node stop`\n");
ret = 1;
goto quit;
}
if (log_level != NULL) {
if (strcmp(log_level, "DEBUG") == 0) {
default_log_level = ZLOG_LEVEL_DEBUG;
@@ -619,7 +692,7 @@ void neu_cli_args_fini(neu_cli_args_t *args)
free(args->log_init_file);
free(args->config_dir);
free(args->plugin_dir);
free(args->node_name);
free(args->ip);
free(args->syslog_host);
}
}
+16 -16
View File
@@ -33,8 +33,6 @@
#define NEU_ENV_DISABLE_AUTH "NEURON_DISABLE_AUTH"
#define NEU_ENV_CONFIG_DIR "NEURON_CONFIG_DIR"
#define NEU_ENV_PLUGIN_DIR "NEURON_PLUGIN_DIR"
#define NEU_ENV_SYSLOG_HOST "NEURON_SYSLOG_HOST"
#define NEU_ENV_SYSLOG_PORT "NEURON_SYSLOG_PORT"
#define NEU_ENV_SUB_FILTER_ERROR "NEURON_SUB_FILTER_ERROR"
#define NEURON_CONFIG_FNAME "./config/neuron.json"
@@ -44,7 +42,6 @@ extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
extern const char *g_config_dir;
@@ -53,25 +50,28 @@ extern const char *g_plugin_dir;
/** Neuron command line arguments.
*/
typedef struct {
bool daemonized; // flag indicating whether to run as daemon process
bool dev_log; // logging for development
size_t restart; // restart policy
bool disable_auth;
char * log_init_file;
char * config_dir;
char * plugin_dir;
bool stop;
char * ip;
int port;
char * syslog_host;
uint16_t syslog_port;
bool sub_filter_err;
bool daemonized; // flag indicating whether to run as daemon process
bool dev_log; // logging for development
size_t restart; // restart policy
bool disable_auth;
char * log_init_file;
char * config_dir;
char * plugin_dir;
bool stop;
bool node_stop;
char * node_name;
char * ip;
int port;
bool sub_filter_err;
} neu_cli_args_t;
/** Parse command line arguments.
*/
void neu_cli_args_init(neu_cli_args_t *args, int argc, char *argv[]);
/** Offline: set node state to STOPPED in sqlite.db. */
int neu_cli_stop_node_offline(const char *node_name);
/** Clean up the command line arguments.
*/
void neu_cli_args_fini(neu_cli_args_t *args);
+33 -3
View File
@@ -29,6 +29,7 @@ typedef struct tag_elem {
char *name;
neu_datatag_t *tag;
uint64_t order;
UT_hash_handle hh;
} tag_elem_t;
@@ -37,20 +38,34 @@ struct neu_group {
char *name;
tag_elem_t *tags;
uint64_t next_tag_order;
uint32_t interval;
int64_t timestamp;
pthread_mutex_t mtx;
};
static int tag_order_cmp(tag_elem_t *left, tag_elem_t *right);
static UT_array *to_array(tag_elem_t *tags);
static void update_timestamp(neu_group_t *group);
static int tag_order_cmp(tag_elem_t *left, tag_elem_t *right)
{
return (left->order > right->order) - (left->order < right->order);
}
neu_group_t *neu_group_new(const char *name, uint32_t interval)
{
neu_group_t *group = calloc(1, sizeof(neu_group_t));
if (NULL == group) {
return NULL;
}
group->name = strdup(name);
group->name = strdup(name);
if (NULL == group->name) {
free(group);
return NULL;
}
group->interval = interval;
pthread_mutex_init(&group->mtx, NULL);
@@ -142,10 +157,24 @@ int neu_group_add_tag(neu_group_t *group, const neu_datatag_t *tag)
return NEU_ERR_TAG_NAME_CONFLICT;
}
el = calloc(1, sizeof(tag_elem_t));
el = calloc(1, sizeof(tag_elem_t));
if (NULL == el) {
pthread_mutex_unlock(&group->mtx);
return NEU_ERR_EINTERNAL;
}
el->name = strdup(tag->name);
el->tag = neu_tag_dup(tag);
if (NULL == el->name || NULL == el->tag) {
free(el->name);
if (el->tag) {
neu_tag_free(el->tag);
}
free(el);
pthread_mutex_unlock(&group->mtx);
return NEU_ERR_EINTERNAL;
}
el->order = group->next_tag_order++;
HASH_ADD_STR(group->tags, name, el);
update_timestamp(group);
pthread_mutex_unlock(&group->mtx);
@@ -212,7 +241,8 @@ int neu_group_rename_tag(neu_group_t *group, const char *old_name,
el->name = new_el_name;
free(el->tag->name);
el->tag->name = new_tag_name;
HASH_ADD_STR(group->tags, name, el);
HASH_ADD_KEYPTR_INORDER(hh, group->tags, el->name, strlen(el->name), el,
tag_order_cmp);
update_timestamp(group);
ret = NEU_ERR_SUCCESS;
+4 -3
View File
@@ -76,7 +76,7 @@ static void find_os_info()
}
buf[strcspn(buf, "\n")] = 0;
strncpy(g_metrics_.machine, buf, sizeof(g_metrics_.machine));
g_metrics_.kernel[sizeof(g_metrics_.machine) - 1] = 0;
g_metrics_.machine[sizeof(g_metrics_.machine) - 1] = 0;
pclose(f);
@@ -87,6 +87,7 @@ static void find_os_info()
strncpy(g_metrics_.clib, "glibc", sizeof(g_metrics_.clib));
strncpy(g_metrics_.clib_version, gnu_get_libc_version(),
sizeof(g_metrics_.clib_version));
g_metrics_.clib_version[sizeof(g_metrics_.clib_version) - 1] = 0;
#endif
}
@@ -96,7 +97,7 @@ static size_t parse_memory_fields(int col)
char buf[64] = {};
size_t val = 0;
sprintf(buf, "free -b | awk 'NR==2 {print $%i}'", col);
snprintf(buf, sizeof(buf), "free -b | awk 'NR==2 {print $%i}'", col);
f = popen(buf, "r");
if (NULL == f) {
@@ -131,7 +132,7 @@ static inline size_t neuron_memory_used()
size_t val = 0;
pid_t pid = getpid();
sprintf(buf, "ps -o rss= %ld", (long) pid);
snprintf(buf, sizeof(buf), "ps -o rss= %ld", (long) pid);
f = popen(buf, "r");
if (NULL == f) {
+7 -3
View File
@@ -37,9 +37,9 @@ static void tag_array_copy(void *_dst, const void *_src)
dst->decimal = src->decimal;
dst->bias = src->bias;
dst->option = src->option;
dst->address = strdup(src->address);
dst->name = strdup(src->name);
dst->description = strdup(src->description);
dst->address = strdup(src->address ? src->address : "");
dst->name = strdup(src->name ? src->name : "");
dst->description = strdup(src->description ? src->description : "");
if (src->unit == NULL) {
dst->unit = strdup("");
} else {
@@ -107,6 +107,9 @@ UT_icd *neu_tag_get_icd()
neu_datatag_t *neu_tag_dup(const neu_datatag_t *tag)
{
neu_datatag_t *new = calloc(1, sizeof(*new));
if (NULL == new) {
return NULL;
}
tag_array_copy(new, tag);
return new;
}
@@ -311,6 +314,7 @@ int neu_datatag_parse_addr_option(const neu_datatag_t * datatag,
break;
}
case NEU_TYPE_BOOL:
case NEU_TYPE_BIT: {
char *op = find_last_character(datatag->address, '.');
+3
View File
@@ -1408,6 +1408,9 @@ int neu_conn_stream_tcp_server_consume(neu_conn_t *conn, int fd, void *context,
int neu_conn_wait_msg(neu_conn_t *conn, void *context, uint16_t n_byte,
neu_conn_process_msg fn)
{
if (n_byte > conn->buf_size) {
n_byte = conn->buf_size;
}
ssize_t ret = neu_conn_recv(conn, conn->buf, n_byte);
neu_protocol_unpack_buf_t pbuf = { 0 };
conn->offset = 0;
+2 -1
View File
@@ -206,13 +206,14 @@ int neu_conn_eth_check_interface(const char *interface)
int fd = socket(PF_PACKET, SOCK_RAW, ETH_P_IPV6);
int ret = -1;
if (fd <= 0) {
if (fd < 0) {
return -1;
}
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", interface);
ret = ioctl(fd, SIOCGIFHWADDR, &ifr);
if (ret != 0) {
close(fd);
return -1;
}
+34 -6
View File
@@ -31,6 +31,7 @@
#include "utils/base64.h"
#include "utils/http.h"
#include "utils/log.h"
#include "utils/neu_path.h"
#include "utils/time.h"
#include "adapter.h"
@@ -303,6 +304,21 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
char schema[64] = { 0 };
char buffer[65] = { 0 };
// The library name is used to build a path under the plugin dir; reject
// any name with a path separator or '..' so it cannot escape that dir.
if (!neu_path_is_valid_component(cmd->library)) {
nlog_warn("reject library name with path separator: %s",
cmd->library);
free(cmd->so_file);
free(cmd->schema_file);
header->type = NEU_RESP_ERROR;
e.error = NEU_ERR_LIBRARY_NAME_NOT_CONFORM;
snprintf(header->receiver, sizeof(header->receiver), "%s",
header->sender);
reply(manager, header, &e);
break;
}
if (sscanf(cmd->library, "libplugin-%64s.so", buffer) != 1) {
nlog_warn("library %s no conform", cmd->library);
free(cmd->so_file);
@@ -479,8 +495,11 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
neu_req_del_plugin_t *cmd = (neu_req_del_plugin_t *) &header[1];
neu_resp_error_t e = { 0 };
char plugins[1][NEU_PLUGIN_NAME_LEN] = { 0 };
strncpy(plugins[0], cmd->plugin, NEU_PLUGIN_NAME_LEN - 1);
UT_array *nodes = neu_manager_get_nodes(
manager, NEU_NA_TYPE_DRIVER | NEU_NA_TYPE_APP, cmd->plugin, "",
manager, NEU_NA_TYPE_DRIVER | NEU_NA_TYPE_APP, plugins, 1, "",
false, false, 0, false, 0, "");
if (nodes != NULL) {
@@ -674,8 +693,11 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
break;
}
char plugins[1][NEU_PLUGIN_NAME_LEN] = { 0 };
strncpy(plugins[0], module_name, NEU_PLUGIN_NAME_LEN - 1);
UT_array *nodes = neu_manager_get_nodes(
manager, NEU_NA_TYPE_DRIVER | NEU_NA_TYPE_APP, module_name, "",
manager, NEU_NA_TYPE_DRIVER | NEU_NA_TYPE_APP, plugins, 1, "",
false, false, 0, false, 0, "");
if (nodes != NULL) {
@@ -941,9 +963,9 @@ static int manager_loop(enum neu_event_io_type type, int fd, void *usr_data)
case NEU_REQ_GET_NODE: {
neu_req_get_node_t *cmd = (neu_req_get_node_t *) &header[1];
UT_array * nodes = neu_manager_get_nodes(
manager, cmd->type, cmd->plugin, cmd->node, cmd->query.s_delay,
cmd->query.q_state, cmd->query.state, cmd->query.q_link,
cmd->query.link, cmd->query.q_group_name);
manager, cmd->type, cmd->plugin, cmd->n_plugin, cmd->node,
cmd->query.s_delay, cmd->query.q_state, cmd->query.state,
cmd->query.q_link, cmd->query.link, cmd->query.q_group_name);
neu_resp_get_node_t resp = { .nodes = nodes };
header->type = NEU_RESP_GET_NODE;
@@ -1850,6 +1872,11 @@ static void start_static_adapter(neu_manager_t *manager, const char *name)
static void start_single_adapter(neu_manager_t *manager, const char *name,
const char *plugin_name, bool display)
{
if (neu_node_manager_is_exist(manager->node_manager, name)) {
nlog_warn("adapter %s already exist", name);
return;
}
neu_adapter_t * adapter = NULL;
neu_plugin_instance_t instance = { 0 };
neu_adapter_info_t adapter_info = {
@@ -1867,7 +1894,8 @@ static void start_single_adapter(neu_manager_t *manager, const char *name,
adapter = neu_adapter_create(&adapter_info, true);
neu_node_manager_add_single(manager->node_manager, adapter, display);
if (display) {
if (strncmp(adapter_info.module->module_name, "neuron-license-server",
24) != 0) {
manager_storage_add_node(manager, name, "");
}
+5 -5
View File
@@ -113,15 +113,15 @@ int neu_manager_del_node(neu_manager_t *manager, const char *node_name)
neu_node_manager_del(manager->node_manager, node_name);
return NEU_ERR_SUCCESS;
}
UT_array *neu_manager_get_nodes(neu_manager_t *manager, int type,
const char *plugin, const char *node,
const char (*plugins)[NEU_PLUGIN_NAME_LEN],
int n_plugins, const char *node,
bool sort_delay, bool q_state, int state,
bool q_link, int link, const char *q_group_name)
{
return neu_node_manager_filter(manager->node_manager, type, plugin, node,
sort_delay, q_state, state, q_link, link,
q_group_name);
return neu_node_manager_filter(manager->node_manager, type, plugins,
n_plugins, node, sort_delay, q_state, state,
q_link, link, q_group_name);
}
int neu_manager_update_node_name(neu_manager_t *manager, const char *node,
+2 -1
View File
@@ -59,7 +59,8 @@ int neu_manager_add_node(neu_manager_t *manager, const char *node_name,
bool load);
int neu_manager_del_node(neu_manager_t *manager, const char *node_name);
UT_array *neu_manager_get_nodes(neu_manager_t *manager, int type,
const char *plugin, const char *node,
const char (*plugins)[NEU_PLUGIN_NAME_LEN],
int n_plugins, const char *node,
bool sort_delay, bool q_state, int state,
bool q_link, int link,
const char *q_group_name);
+26 -4
View File
@@ -272,7 +272,8 @@ static bool find_tag_in_tags(const char *tags, const char *tag)
}
UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type,
const char *plugin, const char *node,
const char (*plugins)[NEU_PLUGIN_NAME_LEN],
int n_plugins, const char *node,
bool sort_delay, bool q_state, int state,
bool q_link, int link,
const char *q_group_name)
@@ -287,9 +288,18 @@ UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type,
{
if (!el->is_static && el->display) {
if (el->adapter->module->type & type) {
if (strlen(plugin) > 0 &&
strcmp(el->adapter->module->module_name, plugin) != 0) {
continue;
if (n_plugins > 0) {
bool matched = false;
for (int i = 0; i < n_plugins; i++) {
if (strcmp(el->adapter->module->module_name,
plugins[i]) == 0) {
matched = true;
break;
}
}
if (!matched) {
continue;
}
}
if (strlen(node) > 0) {
char *name_find = strstr(el->adapter->name, node);
@@ -459,6 +469,18 @@ bool neu_node_manager_is_driver(neu_node_manager_t *mgr, const char *name)
return false;
}
bool neu_node_manager_is_exist(neu_node_manager_t *mgr, const char *name)
{
node_entity_t *node = NULL;
HASH_FIND_STR(mgr->nodes, name, node);
if (node != NULL) {
return true;
}
return false;
}
UT_array *neu_node_manager_get_addrs(neu_node_manager_t *mgr, int type)
{
UT_icd icd = { sizeof(struct sockaddr_un), NULL, NULL, NULL };
+3 -1
View File
@@ -52,7 +52,8 @@ uint16_t neu_node_manager_size(neu_node_manager_t *mgr);
// neu_resp_node_info array
UT_array *neu_node_manager_get(neu_node_manager_t *mgr, int type);
UT_array *neu_node_manager_filter(neu_node_manager_t *mgr, int type,
const char *plugin, const char *node,
const char (*plugins)[NEU_PLUGIN_NAME_LEN],
int n_plugins, const char *node,
bool sort_delay, bool q_state, int state,
bool q_link, int link,
const char *q_group_name);
@@ -63,6 +64,7 @@ UT_array * neu_node_manager_get_adapter(neu_node_manager_t *mgr, int type);
neu_adapter_t *neu_node_manager_find(neu_node_manager_t *mgr, const char *name);
bool neu_node_manager_is_single(neu_node_manager_t *mgr, const char *name);
bool neu_node_manager_is_driver(neu_node_manager_t *mgr, const char *name);
bool neu_node_manager_is_exist(neu_node_manager_t *mgr, const char *name);
// addr array
UT_array *neu_node_manager_get_addrs(neu_node_manager_t *mgr, int type);
+2 -1
View File
@@ -284,9 +284,10 @@ UT_array *neu_plugin_manager_get(neu_plugin_manager_t *mgr)
utarray_new(plugins, &icd);
HASH_ITER(hh, mgr->plugins, el, tmp)
{
if (el->single) {
if (strcmp(el->name, "neuron-license-server") == 0) {
continue;
}
neu_resp_plugin_info_t info = {
.kind = el->kind,
.type = el->type,
+1 -1
View File
@@ -22,13 +22,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include "event/event.h"
#ifdef NEU_PLATFORM_DARWIN
#include <sys/event.h>
#include <sys/queue.h>
struct neu_event_timer {
int id;
+9 -73
View File
@@ -17,12 +17,8 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -47,11 +43,6 @@ static bool sig_trigger = false;
int64_t global_timestamp = 0;
struct {
struct sockaddr_in addr;
int fd;
} g_remote_syslog_ctx;
static void sig_handler(int sig)
{
nlog_warn("recv sig: %d", sig);
@@ -69,63 +60,6 @@ static void sig_handler(int sig)
exit(-1);
}
static inline char syslog_priority(const char *level)
{
switch (level[0]) {
case 'D': // DEBUG
return '7';
case 'I': // INFO
return '6';
case 'N': // NOTICE
return '5';
case 'W': // WARN
return '4';
case 'E': // ERROR
return '3';
case 'F': // FATAL
return '2';
default: // UNKNOWN
return '1';
}
}
static int remote_syslog(zlog_msg_t *msg)
{
// fix priority
msg->buf[1] = syslog_priority(msg->path);
sendto(g_remote_syslog_ctx.fd, msg->buf, msg->len, 0,
(const struct sockaddr *) &g_remote_syslog_ctx.addr,
sizeof(g_remote_syslog_ctx.addr));
return 0;
}
static int config_remote_syslog(const char *host, uint16_t port)
{
g_remote_syslog_ctx.fd = socket(AF_INET, SOCK_DGRAM, 0);
if (g_remote_syslog_ctx.fd < 0) {
return -1;
}
g_remote_syslog_ctx.addr.sin_family = AF_INET;
g_remote_syslog_ctx.addr.sin_port = htons(port);
g_remote_syslog_ctx.addr.sin_addr.s_addr = inet_addr(host);
if (0 == inet_pton(AF_INET, host, &g_remote_syslog_ctx.addr.sin_addr)) {
// not an valid ip address, try resolve as host name
struct hostent *he = gethostbyname(host);
if (NULL == he) {
return -1;
}
memcpy(&g_remote_syslog_ctx.addr.sin_addr, he->h_addr_list[0],
he->h_length);
}
zlog_set_record("remote_syslog", remote_syslog);
return 0;
}
static int neuron_run(const neu_cli_args_t *args)
{
struct rlimit rl = { 0 };
@@ -173,6 +107,12 @@ int main(int argc, char *argv[])
global_timestamp = neu_time_ms();
neu_cli_args_init(&args, argc, argv);
if (args.node_stop) {
rv = neu_cli_stop_node_offline(args.node_name);
neu_cli_args_fini(&args);
return rv;
}
disable_jwt = args.disable_auth;
sub_filter_err = args.sub_filter_err;
snprintf(host_port, sizeof(host_port), "http://%s:%d", args.ip, args.port);
@@ -184,14 +124,10 @@ int main(int argc, char *argv[])
zlog_init(args.log_init_file);
if (args.syslog_host && strlen(args.syslog_host) > 0 &&
0 != config_remote_syslog(args.syslog_host, args.syslog_port)) {
nlog_fatal("neuron setup remote syslog fail, exit.");
goto main_end;
}
neuron = zlog_get_category("neuron");
zlog_level_switch(neuron, default_log_level);
if (neuron) {
zlog_level_switch(neuron, default_log_level);
}
if (neu_modbus_simulator_init(args.config_dir) != 0) {
nlog_warn("modbus simulator init failed");
+15
View File
@@ -785,7 +785,15 @@ void neu_otel_scope_set_parent_span_id2(neu_otel_scope_ctx ctx,
{
trace_scope_t *scope = (trace_scope_t *) ctx;
if (len < 0) {
len = 0;
} else if (len > 8) {
len = 8; // parent_span_id buffer is 8 bytes
}
uint8_t *p_sp_id = calloc(1, 8);
if (NULL == p_sp_id) {
return;
}
memcpy(p_sp_id, parent_span_id, len);
scope->span->parent_span_id.len = len;
scope->span->parent_span_id.data = p_sp_id;
@@ -1036,6 +1044,13 @@ void neu_otel_split_traceparent(const char *in, char *trace_id, char *span_id,
const size_t trace_id_cap = 64;
const size_t span_id_cap = 32;
if (NULL == copy) {
trace_id[0] = '\0';
span_id[0] = '\0';
*flags = 0;
return;
}
token = strtok_r(copy, delimiter, &saveptr);
token = strtok_r(NULL, delimiter, &saveptr);
+26
View File
@@ -600,6 +600,12 @@ static int decode_write_tags_req_json(void * json_obj,
}
req->tags = calloc(req->n_tag, sizeof(neu_json_write_tags_elem_t));
if (NULL == req->tags) {
req->n_tag = 0;
free(req->node);
free(req->group);
return -1;
}
for (int i = 0; i < req->n_tag; i++) {
neu_json_elem_t v_elems[] = {
{
@@ -1315,6 +1321,11 @@ static int decode_write_gtags_req_json(void * json_obj,
}
req->groups = calloc(req->n_group, sizeof(neu_json_write_gtags_elem_t));
if (NULL == req->groups) {
req->n_group = 0;
free(req->node);
return -1;
}
for (int i = 0; i < req->n_group; i++) {
neu_json_elem_t g_elems[] = {
{
@@ -1329,6 +1340,13 @@ static int decode_write_gtags_req_json(void * json_obj,
ret = neu_json_decode_array_by_json(
json_obj, "groups", i, NEU_JSON_ELEM_SIZE(g_elems), g_elems);
if (ret != 0) {
for (int x = i - 1; x >= 0; x--) {
free(req->groups[x].tags);
}
free(req->groups);
return -1;
}
req->groups[i].group = g_elems[0].v.val_str;
req->groups[i].n_tag = json_array_size(g_elems[1].v.val_object);
@@ -1338,6 +1356,14 @@ static int decode_write_gtags_req_json(void * json_obj,
req->groups[i].tags =
calloc(req->groups[i].n_tag, sizeof(neu_json_write_tags_elem_t));
if (NULL == req->groups[i].tags) {
req->groups[i].n_tag = 0;
for (int x = i - 1; x >= 0; x--) {
free(req->groups[x].tags);
}
free(req->groups);
return -1;
}
for (int k = 0; k < req->groups[i].n_tag; k++) {
neu_json_elem_t v_elems[] = {
-75
View File
@@ -1,75 +0,0 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2022 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
/*
* DO NOT EDIT THIS FILE MANUALLY!
* It was automatically generated by `json-autotype`.
*/
#include <stdlib.h>
#include <string.h>
#include "json/json.h"
#include "neu_json_system.h"
void neu_json_decode_system_ctl_req_free(neu_json_system_ctl_req_t *req)
{
if (req != NULL) {
free(req);
}
}
int neu_json_decode_system_ctl_req(char * buf,
neu_json_system_ctl_req_t **result)
{
int ret = 0;
void *json_obj = neu_json_decode_new(buf);
neu_json_system_ctl_req_t *req =
calloc(1, sizeof(neu_json_system_ctl_req_t));
if (req == NULL) {
return -1;
}
neu_json_elem_t req_elems[] = {
{
.name = "action",
.t = NEU_JSON_INT,
},
};
ret = neu_json_decode_by_json(json_obj, NEU_JSON_ELEM_SIZE(req_elems),
req_elems);
if (ret != 0) {
free(req);
if (json_obj != NULL) {
neu_json_decode_free(json_obj);
}
return -1;
}
req->action = req_elems[0].v.val_int;
*result = req;
if (json_obj != NULL) {
neu_json_decode_free(json_obj);
}
return ret;
}
-43
View File
@@ -1,43 +0,0 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2022 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
/*
* DO NOT EDIT THIS FILE MANUALLY!
* It was automatically generated by `json-autotype`.
*/
#ifndef _NEU_JSON_API_NEU_JSON_SYSTEM_H_
#define _NEU_JSON_API_NEU_JSON_SYSTEM_H_
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
int action;
} neu_json_system_ctl_req_t;
void neu_json_decode_system_ctl_req_free(neu_json_system_ctl_req_t *req);
int neu_json_decode_system_ctl_req(char * buf,
neu_json_system_ctl_req_t **result);
#ifdef __cplusplus
}
#endif
#endif
+6 -2
View File
@@ -259,7 +259,7 @@ int neu_json_decode_tag_array_json(void *json_obj, neu_json_tag_array_t *arr)
return 0;
decode_fail:
while (--i > 0) {
while (i-- > 0) {
neu_json_decode_tag_fini(&tags[i]);
}
free(tags);
@@ -595,7 +595,7 @@ int neu_json_decode_gtag_array_json(void *json_obj, neu_json_gtag_array_t *arr)
return 0;
decode_fail:
while (--i > 0) {
while (i-- > 0) {
neu_json_decode_gtag_fini(&gtags[i]);
}
free(gtags);
@@ -814,6 +814,10 @@ int neu_json_decode_del_tags_req(char *buf, neu_json_del_tags_req_t **result)
}
req->tags = calloc(req->n_tags, sizeof(neu_json_del_tags_req_name_t));
if (NULL == req->tags) {
req->n_tags = 0;
goto decode_fail;
}
neu_json_del_tags_req_name_t *p_tag = req->tags;
for (int i = 0; i < req->n_tags; i++) {
neu_json_elem_t id_elems[] = { {
+59 -25
View File
@@ -536,12 +536,15 @@ int neu_sqlite_persister_load_nodes(neu_persister_t *self,
int step = sqlite3_step(stmt);
while (SQLITE_ROW == step) {
neu_persist_node_info_t info = {};
char *name = strdup((char *) sqlite3_column_text(stmt, 0));
const char *name_txt = (const char *) sqlite3_column_text(stmt, 0);
char * name = name_txt ? strdup(name_txt) : NULL;
if (NULL == name) {
break;
}
char *plugin_name = strdup((char *) sqlite3_column_text(stmt, 3));
const char *plugin_name_txt =
(const char *) sqlite3_column_text(stmt, 3);
char *plugin_name = plugin_name_txt ? strdup(plugin_name_txt) : NULL;
if (NULL == plugin_name) {
free(name);
break;
@@ -971,12 +974,16 @@ int neu_sqlite_persister_load_subscriptions(neu_persister_t *self,
int step = sqlite3_step(stmt);
while (SQLITE_ROW == step) {
char *driver_name = strdup((char *) sqlite3_column_text(stmt, 0));
const char *driver_name_txt =
(const char *) sqlite3_column_text(stmt, 0);
char *driver_name = driver_name_txt ? strdup(driver_name_txt) : NULL;
if (NULL == driver_name) {
break;
}
char *group_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *group_name_txt =
(const char *) sqlite3_column_text(stmt, 1);
char *group_name = group_name_txt ? strdup(group_name_txt) : NULL;
if (NULL == group_name) {
free(driver_name);
break;
@@ -1091,7 +1098,8 @@ static int collect_group_info(sqlite3_stmt *stmt, UT_array **group_infos)
int step = sqlite3_step(stmt);
while (SQLITE_ROW == step) {
neu_persist_group_info_t info = {};
char *name = strdup((char *) sqlite3_column_text(stmt, 0));
const char *name_txt = (const char *) sqlite3_column_text(stmt, 0);
char * name = name_txt ? strdup(name_txt) : NULL;
if (NULL == name) {
break;
}
@@ -1207,7 +1215,8 @@ int neu_sqlite_persister_load_node_setting(neu_persister_t * self,
goto end;
}
char *s = strdup((char *) sqlite3_column_text(stmt, 0));
const char *setting_txt = (const char *) sqlite3_column_text(stmt, 0);
char * s = setting_txt ? strdup(setting_txt) : NULL;
if (NULL == s) {
nlog_error("strdup fail");
rv = NEU_ERR_EINTERNAL;
@@ -1240,14 +1249,17 @@ static int collect_user_info(sqlite3_stmt *stmt, UT_array **user_infos)
int step = sqlite3_step(stmt);
while (SQLITE_ROW == step) {
neu_persist_user_info_t info = {};
char *name = strdup((char *) sqlite3_column_text(stmt, 0));
const char *name_txt = (const char *) sqlite3_column_text(stmt, 0);
char * name = name_txt ? strdup(name_txt) : NULL;
if (NULL == name) {
break;
}
info.name = name;
info.hash = strdup((char *) sqlite3_column_text(stmt, 1));
info.name = name;
const char *hash_txt = (const char *) sqlite3_column_text(stmt, 1);
info.hash = hash_txt ? strdup(hash_txt) : NULL;
if (NULL == info.hash) {
free(name);
break;
}
utarray_push_back(*user_infos, &info);
@@ -1342,7 +1354,8 @@ int neu_sqlite_persister_load_user(neu_persister_t *self, const char *user_name,
goto error;
}
user->hash = strdup((char *) sqlite3_column_text(stmt, 0));
const char *hash_txt = (const char *) sqlite3_column_text(stmt, 0);
user->hash = hash_txt ? strdup(hash_txt) : NULL;
if (NULL == user->hash) {
nlog_error("strdup fail");
goto error;
@@ -1481,8 +1494,10 @@ int neu_sqlite_persister_load_server_cert(
// Read all fields
cert_info->id = sqlite3_column_int(stmt, 0);
cert_info->app_name = strdup((char *) sqlite3_column_text(stmt, 1));
cert_info->common_name = strdup((char *) sqlite3_column_text(stmt, 2));
char *app_name_db = (char *) sqlite3_column_text(stmt, 1);
cert_info->app_name = app_name_db ? strdup(app_name_db) : NULL;
char *common_name = (char *) sqlite3_column_text(stmt, 2);
cert_info->common_name = common_name ? strdup(common_name) : NULL;
char *subject = (char *) sqlite3_column_text(stmt, 3);
cert_info->subject = subject ? strdup(subject) : NULL;
@@ -1543,13 +1558,16 @@ static int collect_client_cert_info(sqlite3_stmt *stmt, UT_array **cert_infos)
info.id = sqlite3_column_int(stmt, 0);
char *app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *app_name_col = (const char *) sqlite3_column_text(stmt, 1);
char * app_name = app_name_col ? strdup(app_name_col) : NULL;
if (NULL == app_name) {
break;
}
info.app_name = app_name;
char *common_name = strdup((char *) sqlite3_column_text(stmt, 2));
const char *common_name_col =
(const char *) sqlite3_column_text(stmt, 2);
char *common_name = common_name_col ? strdup(common_name_col) : NULL;
if (NULL == common_name) {
free(app_name);
break;
@@ -1838,7 +1856,8 @@ int neu_sqlite_persister_load_security_policy(
info->id = sqlite3_column_int(stmt, 0);
char *app_name_col = strdup((char *) sqlite3_column_text(stmt, 1));
const char *app_name_txt = (const char *) sqlite3_column_text(stmt, 1);
char * app_name_col = app_name_txt ? strdup(app_name_txt) : NULL;
if (NULL == app_name_col) {
free(info);
sqlite3_finalize(stmt);
@@ -1846,7 +1865,9 @@ int neu_sqlite_persister_load_security_policy(
}
info->app_name = app_name_col;
char *policy_name = strdup((char *) sqlite3_column_text(stmt, 2));
const char *policy_name_txt =
(const char *) sqlite3_column_text(stmt, 2);
char *policy_name = policy_name_txt ? strdup(policy_name_txt) : NULL;
if (NULL == policy_name) {
free(info->app_name);
free(info);
@@ -1899,13 +1920,16 @@ static int collect_security_policy_info(sqlite3_stmt *stmt,
info.id = sqlite3_column_int(stmt, 0);
char *app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *app_name_txt = (const char *) sqlite3_column_text(stmt, 1);
char * app_name = app_name_txt ? strdup(app_name_txt) : NULL;
if (NULL == app_name) {
break;
}
info.app_name = app_name;
char *policy_name = strdup((char *) sqlite3_column_text(stmt, 2));
const char *policy_name_txt =
(const char *) sqlite3_column_text(stmt, 2);
char *policy_name = policy_name_txt ? strdup(policy_name_txt) : NULL;
if (NULL == policy_name) {
free(app_name);
break;
@@ -2027,7 +2051,8 @@ int neu_sqlite_persister_load_auth_setting(
auth_info->id = sqlite3_column_int(stmt, 0);
auth_info->enabled = sqlite3_column_int(stmt, 2);
auth_info->app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *auth_app_name = (const char *) sqlite3_column_text(stmt, 1);
auth_info->app_name = auth_app_name ? strdup(auth_app_name) : NULL;
if (NULL == auth_info->app_name) {
nlog_error("strdup fail");
goto error;
@@ -2064,20 +2089,25 @@ static int collect_auth_user_info(sqlite3_stmt *stmt, UT_array **user_infos)
info.id = sqlite3_column_int(stmt, 0);
char *app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *app_name_txt = (const char *) sqlite3_column_text(stmt, 1);
char * app_name = app_name_txt ? strdup(app_name_txt) : NULL;
if (NULL == app_name) {
break;
}
info.app_name = app_name;
char *username = strdup((char *) sqlite3_column_text(stmt, 2));
const char *username_txt = (const char *) sqlite3_column_text(stmt, 2);
char * username = username_txt ? strdup(username_txt) : NULL;
if (NULL == username) {
free(app_name);
break;
}
info.username = username;
char *password_hash = strdup((char *) sqlite3_column_text(stmt, 3));
const char *password_hash_txt =
(const char *) sqlite3_column_text(stmt, 3);
char *password_hash =
password_hash_txt ? strdup(password_hash_txt) : NULL;
if (NULL == password_hash) {
free(app_name);
free(username);
@@ -2170,19 +2200,23 @@ int neu_sqlite_persister_load_auth_user(
user_info->id = sqlite3_column_int(stmt, 0);
user_info->app_name = strdup((char *) sqlite3_column_text(stmt, 1));
const char *ui_app_name = (const char *) sqlite3_column_text(stmt, 1);
user_info->app_name = ui_app_name ? strdup(ui_app_name) : NULL;
if (NULL == user_info->app_name) {
nlog_error("strdup fail");
goto error;
}
user_info->username = strdup((char *) sqlite3_column_text(stmt, 2));
const char *ui_username = (const char *) sqlite3_column_text(stmt, 2);
user_info->username = ui_username ? strdup(ui_username) : NULL;
if (NULL == user_info->username) {
nlog_error("strdup fail");
goto error;
}
user_info->password_hash = strdup((char *) sqlite3_column_text(stmt, 3));
const char *ui_password_hash = (const char *) sqlite3_column_text(stmt, 3);
user_info->password_hash =
ui_password_hash ? strdup(ui_password_hash) : NULL;
if (NULL == user_info->password_hash) {
nlog_error("strdup fail");
goto error;
+6
View File
@@ -18,6 +18,7 @@
**/
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -57,6 +58,11 @@ int Base64Decode(const char *b64message, unsigned char **buffer, int *length)
{ // Decodes a base64 encoded string
BIO *bio, *b64;
// BIO_read() takes an int length; reject oversized input so neither the
// read length nor the derived decodeLen can overflow/truncate to int.
if (strlen(b64message) > INT_MAX) {
return -1;
}
int decodeLen = calcDecodeLength(b64message);
*buffer = (unsigned char *) malloc(decodeLen + 1);
if (NULL == *buffer) {
+23 -6
View File
@@ -24,6 +24,7 @@
#include "define.h"
#include "utils/cid.h"
#include "utils/log.h"
#include "utils/neu_path.h"
#include "json/json.h"
static int parse_ied(xmlNode *xml_ied, cid_ied_t *ied);
@@ -40,7 +41,14 @@ static void fill_doi_ctls(cid_t *cid);
int neu_cid_parse(const char *path, cid_t *cid)
{
memset(cid, 0, sizeof(cid_t));
xmlDoc *doc = xmlReadFile(path, NULL, 0);
char *safe_path = neu_path_confine(NULL, path);
if (safe_path == NULL) {
nlog_warn("reject icd file path outside working dir: %s", path);
return -1;
}
// XML_PARSE_NONET blocks network access for external entities/DTDs (XXE).
xmlDoc *doc = xmlReadFile(safe_path, NULL, XML_PARSE_NONET);
free(safe_path);
if (doc == NULL) {
nlog_warn("Failed to read icd file %s", path);
return -1;
@@ -275,10 +283,13 @@ typedef struct {
static int find_da_basic_type(cid_template_t *template, const char *datype_id,
const char *seg_name, da_basic_type_t *da_types,
int max)
int max, int depth)
{
char name[NEU_CID_LEN64] = { 0 };
int index = 0;
if (depth > 32) {
return 0;
}
for (int i = 0; i < template->n_datypes; i++) {
if (strcmp(template->datypes[i].id, datype_id) == 0) {
for (int j = 0; j < template->datypes[i].n_bdas; j++) {
@@ -295,7 +306,7 @@ static int find_da_basic_type(cid_template_t *template, const char *datype_id,
if (template->datypes[i].bdas[j].btype == Struct) {
index += find_da_basic_type(
template, template->datypes[i].bdas[j].ref_type, name,
da_types + index, max - index);
da_types + index, max - index, depth + 1);
} else {
snprintf(da_types[index].all_name,
sizeof(da_types[index].all_name), "%s", name);
@@ -342,7 +353,7 @@ static int find_basic_type(cid_template_t *template, const char *type_id,
da_basic_type_t da_types[32] = { 0 };
int n_da_types = find_da_basic_type(
template, dotype->das[i].ref_type, dotype->das[i].name, da_types,
(int) (sizeof(da_types) / sizeof(da_types[0])));
(int) (sizeof(da_types) / sizeof(da_types[0])), 0);
for (int j = 0; j < n_da_types; j++) {
if (strlen(da_name) > 0) {
if (strcmp(da_types[j].all_name, da_name) == 0) {
@@ -471,7 +482,7 @@ static void update_doi(const char *ref_type, cid_doi_t *dois, int n_dois,
da_basic_type_t da_types[32] = { 0 };
int n_da_types = find_da_basic_type(
template, tm_do->das[k].ref_type, tm_do->das[k].name, da_types,
(int) (sizeof(da_types) / sizeof(da_types[0])));
(int) (sizeof(da_types) / sizeof(da_types[0])), 0);
if (da->fc == SP || da->fc == SG) {
for (int j = 0; j < n_da_types; j++) {
@@ -1059,8 +1070,14 @@ static int parse_template(xmlNode *xml_template, cid_template_t *template)
}
if (count != NULL) {
int cnt = atoi(count);
if (cnt < 0) {
cnt = 0;
} else if (cnt > 255) {
cnt = 255; // array_size is uint8_t
}
tm_da->is_array = true;
tm_da->array_size = atoi(count);
tm_da->array_size = (uint8_t) cnt;
} else {
tm_da->is_array = false;
tm_da->array_size = 0;
+14 -1
View File
@@ -24,6 +24,7 @@
#include <string.h>
#include "utils/ede.h"
#include "utils/neu_path.h"
typedef struct {
int object_type;
@@ -242,7 +243,12 @@ int neu_ede_parse_file(const char *file_path, neu_ede_result_t *result)
result->entries = NULL;
result->count = 0;
fp = fopen(file_path, "r");
char *safe_path = neu_path_confine(NULL, file_path);
if (safe_path == NULL) {
return -1;
}
fp = fopen(safe_path, "r");
free(safe_path);
if (fp == NULL) {
return -1;
}
@@ -359,6 +365,13 @@ int neu_ede_parse_file_to_tags(const char *file_path, neu_datatag_t **tags,
return -1;
}
if (result.count == 0) {
neu_ede_result_uninit(&result);
*tags = NULL;
*count = 0;
return 0;
}
out = calloc(result.count, sizeof(neu_datatag_t));
if (out == NULL) {
neu_ede_result_uninit(&result);
+14
View File
@@ -67,6 +67,9 @@ ssize_t neu_url_decode(const char *s, size_t len, char *buf, size_t size)
size_t i = 0, j = 0;
int n = 0;
unsigned int c;
if (size == 0) {
return -1;
}
while (i < len && j < size) {
c = s[i++];
if ('+' == c) {
@@ -97,8 +100,18 @@ int neu_http_get_body(nng_aio *aio, void **data, size_t *data_size)
nng_http_req_get_data(req, data, data_size);
if (*data_size == 0) {
return -1;
} else if (*data_size > NEU_HTTP_BODY_MAX_SIZE_LARGE) {
nlog_error("<%p> request body too big: %zu", aio, *data_size);
*data = NULL;
*data_size = 0;
return NEU_HTTP_BODY_TOO_BIG;
} else {
char *buf = calloc(*data_size + 1, sizeof(char));
if (NULL == buf) {
*data = NULL;
*data_size = 0;
return -1;
}
memcpy(buf, *data, *data_size);
*data = buf;
return 0;
@@ -257,6 +270,7 @@ int neu_http_response(nng_aio *aio, neu_err_code_e code, char *content)
case NEU_ERR_GROUP_ALREADY_SUBSCRIBED:
case NEU_ERR_PLUGIN_PROTOCOL_DECODE_FAILURE:
case NEU_ERR_PLUGIN_NOT_RUNNING:
case NEU_ERR_PLUGIN_APP_NOT_RUNNING:
case NEU_ERR_PLUGIN_TAG_NOT_READY:
case NEU_ERR_PLUGIN_PACKET_OUT_OF_ORDER:
case NEU_ERR_MQTT_FAILURE:
+7
View File
@@ -24,6 +24,7 @@
#include <nng/nng.h>
#include <nng/supplemental/http/http.h>
#include "define.h"
#include "utils/http_handler.h"
#include "utils/log.h"
@@ -53,6 +54,12 @@ int neu_http_add_handler(nng_http_server * server,
return -1;
}
if (NEU_HTTP_HANDLER_FUNCTION == http_handler->type) {
size_t max_body = http_handler->max_body > 0 ? http_handler->max_body
: NEU_HTTP_BODY_MAX_SIZE;
nng_http_handler_collect_body(handler, true, max_body);
}
switch (http_handler->method) {
case NEU_HTTP_METHOD_GET:
ret = nng_http_handler_set_method(handler, "GET");
+10 -3
View File
@@ -18,6 +18,7 @@
**/
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@@ -548,6 +549,13 @@ static int decode_object(json_t *root, neu_json_elem_t *ele)
}
}
if (json_is_array(ob) && json_array_size(ob) > UINT16_MAX) {
ele->ok = false;
zlog_error(neuron, "json decode: array too long (%zu elements)",
json_array_size(ob));
return -1;
}
ele->ok = true;
switch (ele->t) {
@@ -601,9 +609,8 @@ static int decode_object(json_t *root, neu_json_elem_t *ele)
for (int i = 0; i < ele->v.val_array_str.length; i++) {
json_t * value = json_array_get(ob, i);
const char *str_val = json_string_value(value);
if (str_val != NULL) {
ele->v.val_array_str.p_strs[i] = strdup(str_val);
}
ele->v.val_array_str.p_strs[i] =
strdup(str_val != NULL ? str_val : "");
}
}
+122
View File
@@ -0,0 +1,122 @@
/**
* NEURON IIoT System for Industry 4.0
* Copyright (C) 2020-2024 EMQ Technologies Co., Ltd All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**/
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "utils/neu_path.h"
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
bool neu_path_is_valid_component(const char *name)
{
if (name == NULL || name[0] == '\0') {
return false;
}
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
return false;
}
// A safe component must not contain a path separator.
if (strchr(name, '/') != NULL) {
return false;
}
return true;
}
// Return true if `resolved` is `base` or lies below `base/`.
static bool is_within(const char *base, const char *resolved)
{
size_t bl = strlen(base);
if (strncmp(resolved, base, bl) != 0) {
return false;
}
// Exact match, or the next char is a separator (a real sub-path).
return resolved[bl] == '\0' || resolved[bl] == '/';
}
char *neu_path_confine(const char *base, const char *path)
{
if (path == NULL || path[0] == '\0') {
return NULL;
}
char base_real[PATH_MAX];
if (base == NULL || base[0] == '\0') {
base = ".";
}
if (realpath(base, base_real) == NULL) {
return NULL;
}
// Build an absolute candidate: absolute paths are taken as-is (and must
// still resolve inside base), relative paths are joined under base.
char candidate[PATH_MAX];
if (path[0] == '/') {
if (strlen(path) >= sizeof(candidate)) {
return NULL;
}
strcpy(candidate, path);
} else {
if ((size_t) snprintf(candidate, sizeof(candidate), "%s/%s", base_real,
path) >= sizeof(candidate)) {
return NULL;
}
}
char resolved[PATH_MAX];
if (realpath(candidate, resolved) == NULL) {
// The final component may not exist yet (e.g. a file about to be
// created). Resolve the parent directory and re-append the basename.
char tmp[PATH_MAX];
strncpy(tmp, candidate, sizeof(tmp));
tmp[sizeof(tmp) - 1] = '\0';
char *slash = strrchr(tmp, '/');
if (slash == NULL) {
return NULL;
}
*slash = '\0';
const char *bname = slash + 1;
if (!neu_path_is_valid_component(bname)) {
return NULL;
}
char parent_real[PATH_MAX];
if (realpath(tmp[0] != '\0' ? tmp : "/", parent_real) == NULL) {
return NULL;
}
if (!is_within(base_real, parent_real)) {
return NULL;
}
if ((size_t) snprintf(resolved, sizeof(resolved), "%s/%s", parent_real,
bname) >= sizeof(resolved)) {
return NULL;
}
return strdup(resolved);
}
if (!is_within(base_real, resolved)) {
return NULL;
}
return strdup(resolved);
}
+341 -77
View File
@@ -20,14 +20,24 @@
#include <libxml/tree.h>
#include "utils/log.h"
#include "utils/neu_path.h"
#include "utils/tpy.h"
static int parse_vars(xmlNode *xml_ied, tpy_t *tpy);
static int parse_var_entries(xmlNode *section, const char *entry_tag,
const char *default_group, xmlNode *datatypes,
tpy_t *tpy);
int neu_tpy_parse(const char *path, tpy_t *tpy)
{
memset(tpy, 0, sizeof(tpy_t));
xmlDoc *doc = xmlReadFile(path, NULL, 0);
char *safe_path = neu_path_confine(NULL, path);
if (safe_path == NULL) {
nlog_warn("reject tpy file path outside working dir: %s", path);
return -1;
}
// XML_PARSE_NONET blocks network access for external entities/DTDs (XXE).
xmlDoc *doc = xmlReadFile(safe_path, NULL, XML_PARSE_NONET);
free(safe_path);
if (doc == NULL) {
nlog_warn("Failed to read tpy file %s", path);
return -1;
@@ -40,19 +50,31 @@ int neu_tpy_parse(const char *path, tpy_t *tpy)
return -1;
}
xmlNode *find = root->children;
bool vars_parsed = false;
xmlNode *datatypes = NULL;
while (find != NULL) {
for (xmlNode *find = root->children; find != NULL; find = find->next) {
if (find->type == XML_ELEMENT_NODE &&
strcmp((const char *) find->name, "VarConfigs") == 0) {
int ret = parse_vars(find, tpy);
if (ret == 0) {
vars_parsed = true;
}
strcmp((const char *) find->name, "DataTypes") == 0) {
datatypes = find;
break;
}
}
find = find->next;
for (xmlNode *find = root->children; find != NULL; find = find->next) {
if (find->type == XML_ELEMENT_NODE &&
strcmp((const char *) find->name, "VarConfigs") == 0 &&
parse_var_entries(find, "VarConfig", "", datatypes, tpy) == 0) {
vars_parsed = true;
}
}
for (xmlNode *find = root->children; find != NULL; find = find->next) {
if (find->type == XML_ELEMENT_NODE &&
strcmp((const char *) find->name, "Symbols") == 0 &&
parse_var_entries(find, "Symbol", "Symbols", datatypes, tpy) == 0) {
vars_parsed = true;
}
}
if (vars_parsed) {
@@ -60,7 +82,7 @@ int neu_tpy_parse(const char *path, tpy_t *tpy)
return 0;
} else {
neu_tpy_free(tpy);
nlog_warn("Failed to parse VarConfigs");
nlog_warn("Failed to parse VarConfigs/Symbols");
xmlFreeDoc(doc);
return -1;
}
@@ -79,17 +101,283 @@ void neu_tpy_free(tpy_t *tpy)
}
}
static int parse_vars(xmlNode *xml_ied, tpy_t *tpy)
static bool tpy_has_var(tpy_t *tpy, const char *name)
{
xmlNode *var = xml_ied->children;
for (int i = 0; i < tpy->n_statics; i++) {
for (int j = 0; j < tpy->statics[i].n_vars; j++) {
if (strcmp(tpy->statics[i].vars[j].name, name) == 0) {
return true;
}
}
}
return false;
}
static void tpy_add_var(tpy_t *tpy, const char *group, tpy_var_t *tpy_var)
{
for (int i = 0; i < tpy->n_statics; i++) {
if (strcmp(tpy->statics[i].name, group) == 0) {
tpy->statics[i].n_vars += 1;
tpy->statics[i].vars =
realloc(tpy->statics[i].vars,
tpy->statics[i].n_vars * sizeof(tpy_var_t));
tpy->statics[i].vars[tpy->statics[i].n_vars - 1] = *tpy_var;
return;
}
}
tpy->n_statics += 1;
tpy->statics = realloc(tpy->statics, tpy->n_statics * sizeof(tpy_static_t));
tpy->statics[tpy->n_statics - 1].n_vars = 1;
strncpy(tpy->statics[tpy->n_statics - 1].name, group,
sizeof(tpy->statics[tpy->n_statics - 1].name) - 1);
tpy->statics[tpy->n_statics - 1].vars = malloc(sizeof(tpy_var_t));
tpy->statics[tpy->n_statics - 1].vars[0] = *tpy_var;
}
static bool to_neu_type(const char *type, neu_type_e *out)
{
if (strcmp(type, "BOOL") == 0) {
*out = NEU_TYPE_BOOL;
} else if (strcmp(type, "WORD") == 0) {
*out = NEU_TYPE_UINT16;
} else if (strcmp(type, "INT") == 0) {
*out = NEU_TYPE_INT16;
} else if (strcmp(type, "USINT") == 0) {
*out = NEU_TYPE_UINT8;
} else if (strcmp(type, "BYTE") == 0) {
*out = NEU_TYPE_UINT8;
} else if (strcmp(type, "SINT") == 0) {
*out = NEU_TYPE_INT8;
} else if (strcmp(type, "UINT") == 0) {
*out = NEU_TYPE_UINT16;
} else if (strcmp(type, "UDINT") == 0) {
*out = NEU_TYPE_UINT32;
} else if (strcmp(type, "DINT") == 0) {
*out = NEU_TYPE_INT32;
} else if (strcmp(type, "DWORD") == 0) {
*out = NEU_TYPE_UINT32;
} else if (strcmp(type, "REAL") == 0) {
*out = NEU_TYPE_FLOAT;
} else if (strcmp(type, "LREAL") == 0) {
*out = NEU_TYPE_DOUBLE;
} else if (strcmp(type, "BIT") == 0) {
*out = NEU_TYPE_BOOL;
} else if (strcmp(type, "LINT") == 0) {
*out = NEU_TYPE_INT64;
} else if (strcmp(type, "ULINT") == 0) {
*out = NEU_TYPE_UINT64;
} else if (strcmp(type, "LWORD") == 0) {
*out = NEU_TYPE_UINT64;
} else if (strcmp(type, "TIME") == 0) {
*out = NEU_TYPE_UINT32;
} else if (strcmp(type, "DATE") == 0) {
*out = NEU_TYPE_UINT32;
} else if (strncmp(type, "STRING", strlen("STRING")) == 0) {
*out = NEU_TYPE_STRING;
} else {
return false;
}
return true;
}
static xmlNode *find_datatype(xmlNode *datatypes, const char *type_name)
{
if (datatypes == NULL) {
return NULL;
}
for (xmlNode *dt = datatypes->children; dt != NULL; dt = dt->next) {
if (dt->type != XML_ELEMENT_NODE ||
strcmp((const char *) dt->name, "DataType") != 0) {
continue;
}
for (xmlNode *f = dt->children; f != NULL; f = f->next) {
if (f->type != XML_ELEMENT_NODE ||
strcmp((const char *) f->name, "Name") != 0) {
continue;
}
char *content = (char *) xmlNodeGetContent(f);
bool match = content != NULL && strcmp(content, type_name) == 0;
if (content != NULL) {
xmlFree(content);
}
if (match) {
return dt;
}
break;
}
}
return NULL;
}
#define TPY_MAX_EXPAND_DEPTH 16
static void expand_var(tpy_t *tpy, xmlNode *datatypes, const char *group,
const char *name, int64_t igroup, int64_t ioffset,
const char *type_name, int64_t bitsize, int depth)
{
if (depth > TPY_MAX_EXPAND_DEPTH) {
nlog_warn("tpy: type nesting too deep, skip point: %s", name);
return;
}
neu_type_e neu_type;
if (to_neu_type(type_name, &neu_type)) {
if (strlen(name) >= NEU_TAG_NAME_LEN) {
nlog_warn("tpy: point name too long, skip: %s", name);
return;
}
if (tpy_has_var(tpy, name)) {
return;
}
tpy_var_t tpy_var = { 0 };
tpy_var.igroup = igroup;
tpy_var.ioffset = ioffset;
tpy_var.bitsize = bitsize;
strncpy(tpy_var.name, name, sizeof(tpy_var.name) - 1);
strncpy(tpy_var.type, type_name, sizeof(tpy_var.type) - 1);
tpy_add_var(tpy, group, &tpy_var);
return;
}
xmlNode *dt = find_datatype(datatypes, type_name);
if (dt == NULL) {
nlog_warn("tpy: unknown type '%s' for %s, skip", type_name, name);
return;
}
char elem_type[NEU_TAG_NAME_LEN] = { 0 };
bool has_array = false;
bool has_subitem = false;
bool elem_is_pointer = false;
for (xmlNode *f = dt->children; f != NULL; f = f->next) {
if (f->type != XML_ELEMENT_NODE) {
continue;
}
if (strcmp((const char *) f->name, "Type") == 0) {
char *content = (char *) xmlNodeGetContent(f);
if (content != NULL) {
strncpy(elem_type, content, sizeof(elem_type) - 1);
xmlFree(content);
}
char *pointer = (char *) xmlGetProp(f, (const xmlChar *) "Pointer");
if (pointer != NULL) {
elem_is_pointer = true;
xmlFree(pointer);
}
} else if (strcmp((const char *) f->name, "ArrayInfo") == 0) {
has_array = true;
} else if (strcmp((const char *) f->name, "SubItem") == 0) {
has_subitem = true;
}
}
if (has_array) {
nlog_warn("tpy: array type '%s' not supported for %s, skip", type_name,
name);
return;
}
if (elem_is_pointer) {
nlog_debug("tpy: skip pointer-typed point %s (-> '%s')", name,
type_name);
return;
}
if (has_subitem) {
for (xmlNode *si = dt->children; si != NULL; si = si->next) {
if (si->type != XML_ELEMENT_NODE ||
strcmp((const char *) si->name, "SubItem") != 0) {
continue;
}
char sub_name[NEU_TAG_NAME_LEN] = { 0 };
char sub_type[NEU_TAG_NAME_LEN] = { 0 };
int64_t sub_bitoffs = 0;
int64_t sub_bitsize = 0;
bool sub_is_pointer = false;
for (xmlNode *f = si->children; f != NULL; f = f->next) {
if (f->type != XML_ELEMENT_NODE) {
continue;
}
char *content = (char *) xmlNodeGetContent(f);
if (content == NULL) {
continue;
}
if (strcmp((const char *) f->name, "Name") == 0) {
strncpy(sub_name, content, sizeof(sub_name) - 1);
} else if (strcmp((const char *) f->name, "Type") == 0) {
strncpy(sub_type, content, sizeof(sub_type) - 1);
char *pointer =
(char *) xmlGetProp(f, (const xmlChar *) "Pointer");
if (pointer != NULL) {
sub_is_pointer = true;
xmlFree(pointer);
}
} else if (strcmp((const char *) f->name, "BitOffs") == 0) {
sub_bitoffs = atoll(content);
} else if (strcmp((const char *) f->name, "BitSize") == 0) {
sub_bitsize = atoll(content);
}
xmlFree(content);
}
if (strlen(sub_name) == 0 || strlen(sub_type) == 0) {
continue;
}
if (sub_is_pointer) {
nlog_debug("tpy: skip pointer field %s.%s (-> '%s')", name,
sub_name, sub_type);
continue;
}
char full_name[NEU_TAG_NAME_LEN] = { 0 };
int n =
snprintf(full_name, sizeof(full_name), "%s.%s", name, sub_name);
if (n < 0 || (size_t) n >= sizeof(full_name)) {
nlog_warn("tpy: point name too long, skip: %s.%s", name,
sub_name);
continue;
}
expand_var(tpy, datatypes, group, full_name, igroup,
ioffset + sub_bitoffs / 8, sub_type, sub_bitsize,
depth + 1);
}
return;
}
if (strlen(elem_type) > 0 && strcmp(elem_type, type_name) != 0) {
expand_var(tpy, datatypes, group, name, igroup, ioffset, elem_type,
bitsize, depth + 1);
return;
}
nlog_warn("tpy: cannot expand type '%s' for %s, skip", type_name, name);
}
static int parse_var_entries(xmlNode *section, const char *entry_tag,
const char *default_group, xmlNode *datatypes,
tpy_t *tpy)
{
xmlNode *var = section->children;
while (var != NULL) {
if (var->type == XML_ELEMENT_NODE &&
strcmp((const char *) var->name, "VarConfig") == 0) {
strcmp((const char *) var->name, entry_tag) == 0) {
xmlNode * attr = var->children;
tpy_var_t tpy_var = { 0 };
char static_name[NEU_GROUP_NAME_LEN] = { 0 };
char igroup_str[32] = { 0 };
char ioffset_str[32] = { 0 };
char bitsize_str[32] = { 0 };
bool has_igroup = false;
bool has_ioffset = false;
bool is_pointer = false;
while (attr != NULL) {
if (attr->type == XML_ELEMENT_NODE) {
@@ -101,7 +389,9 @@ static int parse_vars(xmlNode *xml_ied, tpy_t *tpy)
attr, (const xmlChar *) "TaskPrio");
snprintf(static_name, sizeof(static_name), "%s_%s",
name_static, name_task);
name_static != NULL ? name_static
: default_group,
name_task != NULL ? name_task : "");
strncpy(tpy_var.name, content,
sizeof(tpy_var.name) - 1);
xmlFree(name_static);
@@ -109,14 +399,26 @@ static int parse_vars(xmlNode *xml_ied, tpy_t *tpy)
} else if (strcmp((const char *) attr->name, "Type") == 0) {
strncpy(tpy_var.type, content,
sizeof(tpy_var.type) - 1);
char *pointer = (char *) xmlGetProp(
attr, (const xmlChar *) "Pointer");
if (pointer != NULL) {
is_pointer = true;
xmlFree(pointer);
}
} else if (strcmp((const char *) attr->name, "IGroup") ==
0) {
strncpy(igroup_str, content, sizeof(igroup_str) - 1);
tpy_var.igroup = atoll(igroup_str);
has_igroup = true;
} else if (strcmp((const char *) attr->name, "IOffset") ==
0) {
strncpy(ioffset_str, content, sizeof(ioffset_str) - 1);
tpy_var.ioffset = atoll(ioffset_str);
has_ioffset = true;
} else if (strcmp((const char *) attr->name, "BitSize") ==
0) {
strncpy(bitsize_str, content, sizeof(bitsize_str) - 1);
tpy_var.bitsize = atoll(bitsize_str);
}
if (content != NULL) {
@@ -126,32 +428,14 @@ static int parse_vars(xmlNode *xml_ied, tpy_t *tpy)
attr = attr->next;
}
if (strlen(tpy_var.name) > 0 && strlen(tpy_var.type) > 0) {
int found = 0;
for (int i = 0; i < tpy->n_statics; i++) {
if (strcmp(tpy->statics[i].name, static_name) == 0) {
found = 1;
tpy->statics[i].n_vars += 1;
tpy->statics[i].vars =
realloc(tpy->statics[i].vars,
tpy->statics[i].n_vars * sizeof(tpy_var_t));
tpy->statics[i].vars[tpy->statics[i].n_vars - 1] =
tpy_var;
break;
}
}
if (!found) {
tpy->n_statics += 1;
tpy->statics = realloc(
tpy->statics, tpy->n_statics * sizeof(tpy_static_t));
tpy->statics[tpy->n_statics - 1].n_vars = 1;
strncpy(tpy->statics[tpy->n_statics - 1].name, static_name,
sizeof(tpy->statics[tpy->n_statics - 1].name) - 1);
tpy->statics[tpy->n_statics - 1].vars =
malloc(sizeof(tpy_var_t));
tpy->statics[tpy->n_statics - 1].vars[0] = tpy_var;
}
if (is_pointer) {
nlog_debug("tpy: skip pointer-typed point %s (-> '%s')",
tpy_var.name, tpy_var.type);
} else if (strlen(tpy_var.name) > 0 && strlen(tpy_var.type) > 0 &&
has_igroup && has_ioffset) {
expand_var(tpy, datatypes, static_name, tpy_var.name,
tpy_var.igroup, tpy_var.ioffset, tpy_var.type,
tpy_var.bitsize, 0);
}
}
@@ -161,37 +445,6 @@ static int parse_vars(xmlNode *xml_ied, tpy_t *tpy)
return 0;
}
static neu_type_e to_neu_type(char *type)
{
if (strcmp(type, "BOOL") == 0) {
return NEU_TYPE_BOOL;
} else if (strcmp(type, "WORD") == 0) {
return NEU_TYPE_UINT16;
} else if (strcmp(type, "INT") == 0) {
return NEU_TYPE_INT16;
} else if (strcmp(type, "USINT") == 0) {
return NEU_TYPE_UINT8;
} else if (strcmp(type, "BYTE") == 0) {
return NEU_TYPE_UINT8;
} else if (strcmp(type, "SINT") == 0) {
return NEU_TYPE_INT8;
} else if (strcmp(type, "UINT") == 0) {
return NEU_TYPE_UINT16;
} else if (strcmp(type, "UDINT") == 0) {
return NEU_TYPE_UINT32;
} else if (strcmp(type, "DINT") == 0) {
return NEU_TYPE_INT32;
} else if (strcmp(type, "DWORD") == 0) {
return NEU_TYPE_UINT32;
} else if (strcmp(type, "REAL") == 0) {
return NEU_TYPE_FLOAT;
} else if (strcmp(type, "LREAL") == 0) {
return NEU_TYPE_DOUBLE;
} else {
return NEU_TYPE_UINT8;
}
}
void neu_tpy_to_msg(char *driver, tpy_t *tpy, neu_req_add_gtag_t *cmd)
{
snprintf(cmd->driver, sizeof(cmd->driver), "%s", driver);
@@ -208,13 +461,24 @@ void neu_tpy_to_msg(char *driver, tpy_t *tpy, neu_req_add_gtag_t *cmd)
for (int k = 0; k < cmd->groups[i].n_tag; k++) {
char address[128] = { 0 };
cmd->groups[i].tags[k].name = strdup(tpy->statics[i].vars[k].name);
cmd->groups[i].tags[k].type =
to_neu_type(tpy->statics[i].vars[k].type);
to_neu_type(tpy->statics[i].vars[k].type,
&cmd->groups[i].tags[k].type);
cmd->groups[i].tags[k].attribute =
NEU_ATTRIBUTE_READ | NEU_ATTRIBUTE_WRITE;
snprintf(address, sizeof(address) - 1, "%" PRIi64 ",%" PRIi64,
tpy->statics[i].vars[k].igroup,
tpy->statics[i].vars[k].ioffset);
if (cmd->groups[i].tags[k].type == NEU_TYPE_STRING) {
int64_t str_len = tpy->statics[i].vars[k].bitsize / 8;
if (str_len >= NEU_VALUE_SIZE) {
str_len = NEU_VALUE_SIZE - 1;
}
snprintf(address, sizeof(address) - 1,
"%" PRIi64 ",%" PRIi64 ".%" PRIi64,
tpy->statics[i].vars[k].igroup,
tpy->statics[i].vars[k].ioffset, str_len);
} else {
snprintf(address, sizeof(address) - 1, "%" PRIi64 ",%" PRIi64,
tpy->statics[i].vars[k].igroup,
tpy->statics[i].vars[k].ioffset);
}
cmd->groups[i].tags[k].address = strdup(address);
cmd->groups[i].tags[k].description = strdup("");
}
+19
View File
@@ -1,5 +1,7 @@
import copy
import requests
import neuron.api as api
import neuron.error as error
import neuron.config as config
@@ -649,6 +651,23 @@ class TestHttp:
assert 1 == len(response.json()["groups"])
@description(given="running neuron", when="post an over sized body", then="should be rejected before parsing")
def test_body_too_big(self):
# far above the 1MB cap of an ordinary endpoint, but below the 4MB
# cap of the bulk endpoints
payload = '{"name": "' + 'a' * (2 * 1024 * 1024) + '"}'
headers = {"Authorization": config.default_jwt,
"Content-Type": "application/json"}
response = requests.post(
url=config.BASE_URL + '/api/v2/node', headers=headers, data=payload)
assert 400 == response.status_code
assert error.NEU_ERR_BODY_TOO_BIG == response.json()['error']
# neuron is still alive and serving after the rejection
response = api.get_version()
assert 200 == response.status_code
@description(given="running neuron", when="test jwt error", then="failed")
def test_jwt_err(self):
response = api.change_password(new_password='123456', jwt='invalid')
-2
View File
@@ -2,7 +2,5 @@
"ip": "0.0.0.0",
"port": 7000,
"disable_auth": 1,
"syslog_host": "",
"syslog_port": 541,
"sub_filter_error": 0
}
-2
View File
@@ -2,7 +2,5 @@
"ip": "0.0.0.0",
"port": 7000,
"disable_auth": 0,
"syslog_host": "",
"syslog_port": 541,
"sub_filter_error": 1
}
+7 -5
View File
@@ -94,11 +94,13 @@ def del_node(node, jwt=config.default_jwt):
return requests.delete(url=config.BASE_URL + '/api/v2/node', headers={"Authorization": jwt}, json={"name": node})
def get_nodes(type, jwt=config.default_jwt, group=""):
if group == "":
return requests.get(url=config.BASE_URL + '/api/v2/node', headers={"Authorization": jwt}, params={"type": type})
else:
return requests.get(url=config.BASE_URL + '/api/v2/node?group=' + group, headers={"Authorization": jwt}, params={"type": type})
def get_nodes(type, jwt=config.default_jwt, group="", plugins=None):
params = {"type": type}
if plugins:
params["plugin"] = ",".join(plugins)
if group != "":
params["group"] = group
return requests.get(url=config.BASE_URL + '/api/v2/node', headers={"Authorization": jwt}, params=params)
def get_nodes_by_tags(type, tags, jwt=config.default_jwt):
+2 -2
View File
@@ -16,8 +16,8 @@ NEU_ERR_DUPLICATE_PASSWORD = 1013
NEU_ERR_COMMAND_EXECUTION_FAILED = 1014
NEU_ERR_IP_ADDRESS_INVALID = 1015
NEU_ERR_IP_ADDRESS_IN_USE = 1016
NEU_ERR_INVALID_USER = 1017
NEU_ERR_INVALID_PASSWORD = 1018
NEU_ERR_BODY_TOO_BIG = 1017
NEU_ERR_INVALID_CID = 1018
NEU_ERR_NODE_EXIST = 2002
NEU_ERR_NODE_NOT_EXIST = 2003
+27
View File
@@ -59,6 +59,33 @@ class TestNode:
assert 200 == response.status_code
assert "mqtt" == response.json()['nodes'][0]["name"]
@description(given="a driver node", when="get driver node by single plugin", then="get success")
def test_get_driver_by_single_plugin(self):
response = api.get_nodes(type=NEU_NODE_DRIVER, plugins=[PLUGIN_MODBUS_TCP])
assert 200 == response.status_code
assert 1 == len(response.json()['nodes'])
assert "modbus-tcp" == response.json()['nodes'][0]["name"]
@description(given="driver and app nodes", when="get driver nodes by multiple plugins", then="get success")
def test_get_driver_by_multiple_plugins(self):
response = api.get_nodes(type=NEU_NODE_DRIVER, plugins=[PLUGIN_MODBUS_TCP, PLUGIN_MODBUS_RTU])
assert 200 == response.status_code
assert 1 == len(response.json()['nodes'])
assert "modbus-tcp" == response.json()['nodes'][0]["name"]
@description(given="driver node and non-existent plugin", when="get driver node by plugins with unmatched", then="get success")
def test_get_driver_by_plugin_with_unmatched(self):
response = api.get_nodes(type=NEU_NODE_DRIVER, plugins=[PLUGIN_MODBUS_TCP, "non-existent-plugin"])
assert 200 == response.status_code
assert 1 == len(response.json()['nodes'])
assert "modbus-tcp" == response.json()['nodes'][0]["name"]
@description(given="driver node", when="get driver node by plugins no match", then="get empty")
def test_get_driver_by_plugin_no_match(self):
response = api.get_nodes(type=NEU_NODE_DRIVER, plugins=[PLUGIN_MODBUS_RTU])
assert 200 == response.status_code
assert [] == response.json()['nodes']
@description(given="existent driver node", when="update the node name to empty string", then="update failed")
def test_update_node_name_to_empty(self):
response = api.update_node(node="modbus-tcp", new_name="")
+2 -2
View File
@@ -35,7 +35,7 @@ class TestNodeTag:
@description(given="existent driver node", when="add tags with invalid symbol", then="add failed")
def test_add_tags_with_invalid_symbol(self):
response = api.add_node_with_tags(node="modbus-tcp-2", plugin=PLUGIN_MODBUS_TCP, tags="tag1,tag@2")
response = api.add_node_with_tags(node="modbus-tcp-2", plugin=PLUGIN_MODBUS_TCP, tags="tag1,tag?2")
assert 400 == response.status_code
assert NEU_ERR_NODE_TAGS_INVALID == response.json()['error']
@@ -44,7 +44,7 @@ class TestNodeTag:
response = api.add_node(node="modbus-tcp-2", plugin=PLUGIN_MODBUS_TCP)
assert 200 == response.status_code
assert NEU_ERR_SUCCESS == response.json()['error']
response = api.update_node_tags(node="modbus-tcp-2", tags="tag#3,tag4")
response = api.update_node_tags(node="modbus-tcp-2", tags="tag?3,tag4")
assert 400 == response.status_code
assert NEU_ERR_NODE_TAGS_INVALID == response.json()['error']
+42 -1
View File
@@ -5,6 +5,7 @@ import neuron.process as process
from neuron.error import *
import shutil
import os
import subprocess
import time
hold_bit = [{"name": "hold_bit", "address": "1!400001.15",
@@ -507,4 +508,44 @@ class TestPersist:
p.stop()
os.remove(dst_file)
os.rename(dst_backup, dst_file)
os.rename(dst_backup, dst_file)
class TestOfflineNodeStopCli:
def test_offline_node_stop_persists_stopped(self):
process.remove_persistence()
p = process.NeuronProcess()
p.start()
api.add_node_check(node='cli-offline-stop', plugin=PLUGIN_MODBUS_TCP)
response = api.modbus_tcp_node_setting(node='cli-offline-stop',
port=60502)
assert 200 == response.status_code
p.stop()
result = subprocess.run(
['./neuron', 'node', 'stop', '--node', 'cli-offline-stop'],
cwd='build', text=True, capture_output=True, check=False)
assert result.returncode == 0, result.stderr
assert 'STOPPED' in result.stdout
p.start()
try:
time.sleep(1)
response = api.get_nodes_state(node='cli-offline-stop')
assert 200 == response.status_code
assert NEU_NODE_STATE_STOPPED == response.json()['running']
finally:
p.stop()
def test_offline_node_stop_rejects_missing_node(self):
process.remove_persistence()
p = process.NeuronProcess()
p.start()
p.stop()
result = subprocess.run(
['./neuron', 'node', 'stop', '--node', 'missing-node'],
cwd='build', text=True, capture_output=True, check=False)
assert result.returncode != 0
assert 'not found' in result.stderr
+23 -8
View File
@@ -826,24 +826,39 @@ class TestTag:
assert NEU_ERR_TAG_DECIMAL_INVALID == response.json()['error']
assert 1 == response.json()['index']
@description(given="an existing tag", when="renaming to a new name", then="successfully renamed")
@description(given="three existing tags", when="renaming the middle tag", then="successfully renamed without changing tag order")
def test_rename_tag_success(self):
tag = [{"name": "rename_test_tag", "address": "1!400001",
"attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16}]
api.add_tags(node="modbus-tcp", group="group1", tags=tag)
tags = [
{"name": "rename_first_tag", "address": "1!400001",
"attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16},
{"name": "rename_middle_tag", "address": "1!400002",
"attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16},
{"name": "rename_last_tag", "address": "1!400003",
"attribute": NEU_TAG_ATTRIBUTE_RW, "type": NEU_TYPE_INT16},
]
api.add_tags(node="modbus-tcp", group="group1", tags=tags)
response = api.rename_tag(node="modbus-tcp", group="group1",
old_name="rename_test_tag", new_name="renamed_tag")
old_name="rename_middle_tag",
new_name="renamed_middle_tag")
assert 200 == response.status_code
assert NEU_ERR_SUCCESS == response.json()['error']
response = api.get_tags(node="modbus-tcp", group="group1")
assert 200 == response.status_code
tag_names = [t['name'] for t in response.json()['tags']]
assert "renamed_tag" in tag_names
assert "rename_test_tag" not in tag_names
renamed_tags = [
name for name in tag_names
if name in {"rename_first_tag", "renamed_middle_tag",
"rename_last_tag"}
]
assert ["rename_first_tag", "renamed_middle_tag",
"rename_last_tag"] == renamed_tags
assert "rename_middle_tag" not in tag_names
api.del_tags(node="modbus-tcp", group="group1", tags=["renamed_tag"])
api.del_tags(node="modbus-tcp", group="group1",
tags=["rename_first_tag", "renamed_middle_tag",
"rename_last_tag"])
@description(given="an existing tag", when="renaming to a name that already exists", then="fail with conflict error")
def test_rename_tag_conflict(self):
+43029
View File
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -93,14 +93,12 @@ target_include_directories(mqtt_schema_test PRIVATE
)
target_link_libraries(mqtt_schema_test neuron-base gtest_main gtest)
file(COPY ${CMAKE_SOURCE_DIR}/tests/ut/EDE_test.csv DESTINATION ${UT_DIRECTORY}/config)
add_executable(ede_test ede_test.cc ${CMAKE_SOURCE_DIR}/src/utils/ede.c)
target_include_directories(ede_test PRIVATE
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/include
)
target_compile_definitions(ede_test PRIVATE
EDE_TEST_FILE_PATH="${CMAKE_SOURCE_DIR}/tests/EDE_test.csv"
)
target_link_libraries(ede_test neuron-base gtest_main gtest)
include(GoogleTest)
+238
View File
@@ -0,0 +1,238 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--This file is created by the TCatPlcCtrl automatically. Manually changes will be overwritten!-->
<PlcProjectInfo xmlns:p="http://www.beckhoff.com/2002/01/TcPlcProjectDesc">
<ProjectInfo>
<Path>D:\Samples\VarTypes\VarTypes.pro</Path>
<IsLibrary>false</IsLibrary>
<ChangeDate>2026-07-13T10:00:00</ChangeDate>
</ProjectInfo>
<RoutingInfo>
<AdsInfo>
<NetId>5.1.2.3.1.1</NetId>
<Port>801</Port>
<TargetName>
<![CDATA[Zielsystem: VarTypesSample (5.1.2.3.1.1), Laufzeit: 1]]>
</TargetName>
</AdsInfo>
</RoutingInfo>
<CompilerInfo>
<CpuFamily>x86</CpuFamily>
<CompilerVersion>3.1.4024.20</CompilerVersion>
<Alignment>Byte</Alignment>
<PackSize>1</PackSize>
<TwinCATVersion>3.1.0</TwinCATVersion>
<TCatPlcCtrlVersion>3.1.4024</TCatPlcCtrlVersion>
</CompilerInfo>
<VarConfigs>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.bMotorRunning</Name>
<Type>BOOL</Type>
<Comment>
<![CDATA[BOOL type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">0</IOffset>
<BitSize>1</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.byStatus</Name>
<Type>BYTE</Type>
<Comment>
<![CDATA[BYTE type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">1</IOffset>
<BitSize>8</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.wAlarmCode</Name>
<Type>WORD</Type>
<Comment>
<![CDATA[WORD type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">2</IOffset>
<BitSize>16</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.siTemperature</Name>
<Type>SINT</Type>
<Comment>
<![CDATA[SINT type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">4</IOffset>
<BitSize>8</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.usiLevel</Name>
<Type>USINT</Type>
<Comment>
<![CDATA[USINT type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">5</IOffset>
<BitSize>8</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.iCounter</Name>
<Type>INT</Type>
<Comment>
<![CDATA[INT type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">6</IOffset>
<BitSize>16</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.uiSpeed</Name>
<Type>UINT</Type>
<Comment>
<![CDATA[UINT type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">8</IOffset>
<BitSize>16</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.diPosition</Name>
<Type>DINT</Type>
<Comment>
<![CDATA[DINT type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">10</IOffset>
<BitSize>32</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.udiRuntime</Name>
<Type>UDINT</Type>
<Comment>
<![CDATA[UDINT type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">14</IOffset>
<BitSize>32</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="1">GVL.dwFlags</Name>
<Type>DWORD</Type>
<Comment>
<![CDATA[DWORD type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">18</IOffset>
<BitSize>32</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Output" TaskPrio="1">GVL.rTemperatureSetpoint</Name>
<Type>REAL</Type>
<Comment>
<![CDATA[REAL type]]>
</Comment>
<IGroup>61488</IGroup>
<IOffset VarConfig="Fixed">0</IOffset>
<BitSize>32</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Output" TaskPrio="1">GVL.lrFlowTotal</Name>
<Type>LREAL</Type>
<Comment>
<![CDATA[LREAL type]]>
</Comment>
<IGroup>61488</IGroup>
<IOffset VarConfig="Fixed">4</IOffset>
<BitSize>64</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="2">GVL.tCycleTime</Name>
<Type>TIME</Type>
<Comment>
<![CDATA[TIME type, mapped to raw UINT32 milliseconds]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">22</IOffset>
<BitSize>32</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="2">GVL.dStartDate</Name>
<Type>DATE</Type>
<Comment>
<![CDATA[DATE type, mapped to raw UINT32 seconds since epoch]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">50</IOffset>
<BitSize>32</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="2">GVL.todShiftStart</Name>
<Type>TOD</Type>
<Comment>
<![CDATA[unrecognized type, exercises to_neu_type() default fallback]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">54</IOffset>
<BitSize>32</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="3">GVL.bBitFlag</Name>
<Type>BIT</Type>
<Comment>
<![CDATA[BIT type, bit-addressed %IX area]]>
</Comment>
<IGroup>61473</IGroup>
<IOffset VarConfig="Fixed">0</IOffset>
<BitSize>1</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="3">GVL.liTotalCounter</Name>
<Type>LINT</Type>
<Comment>
<![CDATA[LINT type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">26</IOffset>
<BitSize>64</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="3">GVL.uliTotalRuntime</Name>
<Type>ULINT</Type>
<Comment>
<![CDATA[ULINT type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">34</IOffset>
<BitSize>64</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="3">GVL.lwFlags64</Name>
<Type>LWORD</Type>
<Comment>
<![CDATA[LWORD type]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">42</IOffset>
<BitSize>64</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="3">GVL.szCommand</Name>
<Type Decoration="40000051">STRING(80)</Type>
<Comment>
<![CDATA[default-length STRING, BitSize includes the null terminator byte]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">50</IOffset>
<BitSize>648</BitSize>
</VarConfig>
<VarConfig>
<Name Static="Input" TaskPrio="3">GVL.szShortCode</Name>
<Type Decoration="40000010">STRING(15)</Type>
<Comment>
<![CDATA[custom-length STRING]]>
</Comment>
<IGroup>61472</IGroup>
<IOffset VarConfig="Fixed">131</IOffset>
<BitSize>128</BitSize>
</VarConfig>
</VarConfigs>
</PlcProjectInfo>
+1 -23
View File
@@ -14,6 +14,7 @@
zlog_category_t *neuron = NULL;
static constexpr size_t kExpectedEdeTagCount = 1499;
#define EDE_TEST_FILE_PATH "./config/EDE_test.csv"
static std::vector<std::string> split_semicolon(const std::string &line)
{
@@ -267,29 +268,6 @@ TEST(EdeTest, EdeToMsg)
free(cmd.groups);
}
TEST(EdeTest, CommandableToRw)
{
const char * tmp_file = "/tmp/ede_commandable_test.csv";
std::ofstream ofs(tmp_file);
ofs << "PROJECT_NAME;X\n";
ofs << "# keyname;device obj.-instance;object-name;object-type;"
"object-instance;description;present-value-default;"
"min-present-value;max-present-value;commandable\n";
ofs << "k1;2;obj;1;10;desc;;;;Y\n";
ofs << "k2;2;obj;1;11;desc;;;;N\n";
ofs.close();
neu_ede_result_t result = { 0 };
ASSERT_EQ(neu_ede_parse_file(tmp_file, &result), 0);
ASSERT_EQ(result.count, 2U);
EXPECT_EQ(result.entries[0].attribute,
(neu_attribute_e)(NEU_ATTRIBUTE_READ | NEU_ATTRIBUTE_WRITE));
EXPECT_EQ(result.entries[1].attribute, NEU_ATTRIBUTE_READ);
neu_ede_result_uninit(&result);
std::remove(tmp_file);
}
int main(int argc, char **argv)
{
zlog_init("./config/dev.conf");
+1 -15
View File
@@ -1,10 +1,7 @@
#include <gtest/gtest.h>
#include "json/json.h"
#include "utils/log.h"
#include "parser/neu_json_system.h"
#include "json/json.h"
zlog_category_t *neuron = NULL;
TEST(JsonTest, DecodeField)
@@ -331,17 +328,6 @@ TEST(JsonTest, EncodeArray)
neu_json_decode_free(ob);
}
TEST(JsonTest, System_ctl)
{
char * buf = (char *) "{\"action\": 1}";
char * buf1 = (char *) "{\"action1\": 1}";
neu_json_system_ctl_req_t *req = NULL;
EXPECT_EQ(-1, neu_json_decode_system_ctl_req(buf1, &req));
EXPECT_EQ(0, neu_json_decode_system_ctl_req(buf, &req));
EXPECT_EQ(1, req->action);
free(req);
}
int main(int argc, char **argv)
{
zlog_init("./config/dev.conf");
+42
View File
@@ -89,6 +89,48 @@ TEST(validate_deep_success, schema)
free(vts);
}
TEST(validate_constants, schema)
{
const char *schema =
"{\"string\":\"value\",\"integer\":1234,\"float\":11.44,"
"\"boolean\":true,\"object\":{\"integer\":-42,\"float\":0.5,"
"\"boolean\":false}}";
mqtt_schema_vt_t *vts = NULL;
size_t n_vts;
int ret = mqtt_schema_validate(schema, &vts, &n_vts);
ASSERT_EQ(ret, 0);
ASSERT_EQ(n_vts, 5);
EXPECT_EQ(vts[0].jtype, NEU_JSON_STR);
EXPECT_STREQ(vts[0].ud, "value");
EXPECT_EQ(vts[1].jtype, NEU_JSON_INT);
EXPECT_EQ(vts[1].jvalue.val_int, 1234);
EXPECT_EQ(vts[2].jtype, NEU_JSON_DOUBLE);
EXPECT_EQ(vts[2].jvalue.val_double, 11.44);
EXPECT_EQ(vts[3].jtype, NEU_JSON_BOOL);
EXPECT_TRUE(vts[3].jvalue.val_bool);
ASSERT_EQ(vts[4].vt, MQTT_SCHEMA_OBJECT);
ASSERT_EQ(vts[4].n_sub_vts, 3);
EXPECT_EQ(vts[4].sub_vts[0].jvalue.val_int, -42);
EXPECT_EQ(vts[4].sub_vts[1].jvalue.val_double, 0.5);
EXPECT_FALSE(vts[4].sub_vts[2].jvalue.val_bool);
char * result = NULL;
neu_json_read_resp_t tags = { 0 };
ret = mqtt_schema_encode((char *) "driver", (char *) "group", &tags, vts,
n_vts, NULL, 0, &result);
ASSERT_EQ(ret, 0);
EXPECT_STREQ(result,
"{\"string\": \"value\", \"integer\": 1234, \"float\": "
"11.44, \"boolean\": true, \"object\": {\"integer\": -42, "
"\"float\": 0.5, \"boolean\": false}}");
free(result);
free(vts[4].sub_vts);
free(vts);
}
TEST(validate_failed, schema)
{
const char *schema =
-2
View File
@@ -5,8 +5,6 @@ file perms = 666
[formats]
simple = "%d:%ms [%V] %f:%L %m%n"
syslog = "<P>%d(%Y-%m-%dT%T%z) %H neuron[%p]: level=%v msg="[%c] %m" file="%f:%L"%n"
[rules]
*.* "./logs/%c.log", 50MB * 2 ~ "./logs/%c.#2r.log"; simple