mirror of
https://github.com/emqx/neuron.git
synced 2026-10-29 23:19:18 +08:00
Compare commits
53
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef6b3ffe03 | ||
|
|
674d803569 | ||
|
|
f69c07490e | ||
|
|
3c5aed4ffe | ||
|
|
e13927a44d | ||
|
|
3167ff773e | ||
|
|
57270893a1 | ||
|
|
3f8475a0db | ||
|
|
1c9abb0664 | ||
|
|
efaac88483 | ||
|
|
1baa36c82d | ||
|
|
7cfad3f3f9 | ||
|
|
f2c2a51f5c | ||
|
|
fe39b9fab2 | ||
|
|
515ee21cbf | ||
|
|
b4c5b75a03 | ||
|
|
33ec20709c | ||
|
|
782a3cd374 | ||
|
|
ab35b924ac | ||
|
|
bfefcea7ff | ||
|
|
4e9df9cc9f | ||
|
|
0e4bf245e1 | ||
|
|
9d9aa31238 | ||
|
|
423100ef8f | ||
|
|
fc6b787af1 | ||
|
|
0c1ab486c2 | ||
|
|
67d6bebc18 | ||
|
|
f6cb6bc01f | ||
|
|
83c96fe7f1 | ||
|
|
066d3fbbb2 | ||
|
|
11f1269b3b | ||
|
|
b400f170cd | ||
|
|
9c474ddf23 | ||
|
|
d43def9dab | ||
|
|
4d488bca7c | ||
|
|
709250b056 | ||
|
|
aa8c266d9a | ||
|
|
dfd691d923 | ||
|
|
33b795c6dc | ||
|
|
6a836b9fd5 | ||
|
|
42c087fb14 | ||
|
|
e0f8208b80 | ||
|
|
2778460252 | ||
|
|
26d8505bfd | ||
|
|
03ed43b514 | ||
|
|
c11e9076b6 | ||
|
|
6eb9a88f2f | ||
|
|
4f3cb646b7 | ||
|
|
6772c281fc | ||
|
|
9bd8d2d643 | ||
|
|
0ae8afdb73 | ||
|
|
dfb8dea029 | ||
|
|
3156933f2c |
+20
-3
@@ -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)
|
||||
|
||||
@@ -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
@@ -307,7 +307,7 @@ Neuron 将为 IIoT 平台提供一系列 API 服务,用于查询基本信息
|
||||
|
||||
**type** 必需
|
||||
|
||||
**plugin** 可选
|
||||
**plugin** 可选,支持逗号分隔多个插件名,如 `plugin=Modbus TCP,Modbus RTU`
|
||||
|
||||
**node** 可选
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, '.');
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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 };
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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(>ags[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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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 : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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("");
|
||||
}
|
||||
|
||||
@@ -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,7 +2,5 @@
|
||||
"ip": "0.0.0.0",
|
||||
"port": 7000,
|
||||
"disable_auth": 1,
|
||||
"syslog_host": "",
|
||||
"syslog_port": 541,
|
||||
"sub_filter_error": 0
|
||||
}
|
||||
|
||||
@@ -2,7 +2,5 @@
|
||||
"ip": "0.0.0.0",
|
||||
"port": 7000,
|
||||
"disable_auth": 0,
|
||||
"syslog_host": "",
|
||||
"syslog_port": 541,
|
||||
"sub_filter_error": 1
|
||||
}
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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="")
|
||||
|
||||
@@ -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']
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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");
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user