From 4a1f226bfbdf99d350beebb92e4f7efad7d5b98d Mon Sep 17 00:00:00 2001 From: Vitaly Zhuravlev Date: Thu, 1 Jun 2017 14:53:37 +0300 Subject: [PATCH] v0.6 (#14) * make libmodbus as nested submodule * encapsulated TCP: use enc:// before IP. for Modbus TCP is used as default if no protocol is defined. Also tcp:// might be used * implemented multiport semaphores (using hash function with scaling to number of nsems) * added item short alias: modbus_read --- .gitmodules | 4 ++ Makefile.am | 2 +- configure.ac | 25 ++--------- libmodbus | 1 + src/Makefile.am | 7 +-- src/modbus.c | 110 +++++++++++++++++++++++++++++++++--------------- 6 files changed, 90 insertions(+), 59 deletions(-) create mode 100644 .gitmodules create mode 160000 libmodbus diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2c06365 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "libmodbus"] + path = libmodbus +url=https://github.com/v-zhuravlev/libmodbus + branch = v3.1.4 \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index 140eef1..c4f559b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = src +SUBDIRS = libmodbus src EXTRA_DIST = \ include ACLOCAL_AMFLAGS = -I m4 diff --git a/configure.ac b/configure.ac index 6500fb8..9c6220c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,28 +1,11 @@ -AC_INIT([libzbxmodbus], [0.5]) +AC_INIT([libzbxmodbus], [0.6]) AM_INIT_AUTOMAKE([foreign -Wall -Werror]) AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_SUBDIRS([libmodbus]) AC_CONFIG_SRCDIR([src/modbus.c]) m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) -# Checks for libraries. -have_modbus=no -AC_SEARCH_LIBS([modbus_get_float], [modbus], [have_modbus=yes]) -if test "x${have_modbus}" = xno; then -AC_MSG_ERROR([ ------------------------------------------- -The modbus library and header file -required to build zbxmodbus. Stopping... -Check 'config.log' for more information. -------------------------------------------]) -fi -PKG_CHECK_MODULES([LIBMODBUS], [libmodbus >= 3.1.1], [], -[AC_MSG_ERROR([ - ------------------------------------------ - libmodbus 3.1.1 or higher is required. - Versions below that can cause serious performance issues with pollers - because of bugged timeout handling. Please download and build latest - libmodbus from http://libmodbus.org/ - ------------------------------------------]) -]) + + #Zabbix 2.4 / Zabbix 3.0 / 3.2 chooser AC_ARG_ENABLE([zabbix-2], diff --git a/libmodbus b/libmodbus new file mode 160000 index 0000000..681b431 --- /dev/null +++ b/libmodbus @@ -0,0 +1 @@ +Subproject commit 681b43177225799e84c5b0be4f0719aa3b3eab52 diff --git a/src/Makefile.am b/src/Makefile.am index 8c98890..e44d186 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,7 +1,8 @@ lib_LTLIBRARIES = libzbxmodbus.la + libzbxmodbus_la_SOURCES = \ modbus.c -libzbxmodbus_la_CFLAGS = $(LIBMODBUS_CFLAGS) +libzbxmodbus_la_CFLAGS = -I$(top_srcdir)/libmodbus/src if ZABBIX_2 libzbxmodbus_la_CFLAGS += -I../include/zabbix-2.4 @@ -12,8 +13,8 @@ endif if ZABBIX_3_2 libzbxmodbus_la_CFLAGS += -I../include/zabbix-3.2 endif - -libzbxmodbus_la_LDFLAGS = $(LIBMODBUS_LIBS) \ +libzbxmodbus_la_LIBADD = $(top_srcdir)/libmodbus/src/libmodbus.la +libzbxmodbus_la_LDFLAGS = \ -module \ -shared \ -avoid-version diff --git a/src/modbus.c b/src/modbus.c index 9328262..cd6f9de 100644 --- a/src/modbus.c +++ b/src/modbus.c @@ -45,15 +45,18 @@ #define MODBUS_PDU_ADDRESS_0 0 #define MODBUS_PROTOCOL_ADDRESS_1 1 -#define LOCK_SERIAL_PORT sem_lock(MODBUS_SEM_ID) -#define UNLOCK_SERIAL_PORT sem_unlock(MODBUS_SEM_ID) +#define NSEMS 128 + +#define LOCK_PORT(x) sem_lock(x) +#define UNLOCK_PORT(x) sem_unlock(x) + //semaphore constants #define MODBUS_SEM_KEY "." int MODBUS_SEM_ID = -1; #define ZBX_MUTEX int #define ZBX_MUTEX_NULL -1 -#define ZBX_MUTEX_ERROR 1 +#define ZBX_MUTEX_ERROR -1 #define ZBX_MUTEX_OK 1 #define ZBX_MUTEX_NAME int #define MAX_RETRIES 10 @@ -68,7 +71,7 @@ union semun { static int item_timeout = 0; static ZBX_MUTEX serial_port_access = ZBX_MUTEX_NULL; int zbx_modbus_read_registers(AGENT_REQUEST *request, AGENT_RESULT *result); -void create_modbus_context(char *con_string, modbus_t **ctx_out, int *lock_required_out); +void create_modbus_context(char *con_string, modbus_t **ctx_out, int *lock_required_out, short *lock_key); int param_is_empty(char *param_to_check); int validate_datatype_param (char *datatype_param); int initsem(); @@ -80,6 +83,7 @@ static ZBX_METRIC keys[] = /* KEY FLAG FUNCTION TEST PARAMETERS */ { {"modbus_read_registers", CF_HAVEPARAMS, zbx_modbus_read_registers, NULL}, + {"modbus_read", CF_HAVEPARAMS, zbx_modbus_read_registers, NULL}, {NULL} }; @@ -126,7 +130,17 @@ ZBX_METRIC *zbx_module_item_list() return keys; } +//generate hash for nsem pseudo unique number. +unsigned long hash(unsigned char *str) +{ + unsigned long hash = 5381; + int c; + while (c = *str++) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + + return hash; +} /****************************************************************************** * * @@ -183,14 +197,13 @@ int zbx_modbus_read_registers(AGENT_REQUEST *request, AGENT_RESULT *result) return SYSINFO_RET_FAIL; } - + modbus_t *ctx; int lock_required; - - - + short lock_key = 0; - create_modbus_context(param1,&ctx,&lock_required); + + create_modbus_context(param1,&ctx,&lock_required, &lock_key); if (ctx == NULL) { SET_MSG_RESULT(result, strdup("Unable to create the libmodbus context")); modbus_free(ctx); @@ -280,13 +293,7 @@ int zbx_modbus_read_registers(AGENT_REQUEST *request, AGENT_RESULT *result) } } - /* 3.0.3 - struct timeval response_timeout ; - response_timeout.tv_sec = 0; - response_timeout.tv_usec = 0; - modbus_set_response_timeout(ctx, &response_timeout); - */ modbus_set_response_timeout(ctx, item_timeout, 0); //read part @@ -296,12 +303,14 @@ int zbx_modbus_read_registers(AGENT_REQUEST *request, AGENT_RESULT *result) int regs_to_read = 1; if (datatype == MODBUS_FLOAT || datatype == MODBUS_LONG) { regs_to_read=2;} - if (lock_required == 1 ) LOCK_SERIAL_PORT; + + + if (lock_required == 1 ) LOCK_PORT(lock_key); if (modbus_connect(ctx) == -1) { SET_MSG_RESULT(result, strdup(modbus_strerror(errno))); modbus_free(ctx); - if (lock_required == 1 ) UNLOCK_SERIAL_PORT; + if (lock_required == 1 ) UNLOCK_PORT(lock_key); return SYSINFO_RET_FAIL; } @@ -323,14 +332,14 @@ int zbx_modbus_read_registers(AGENT_REQUEST *request, AGENT_RESULT *result) SET_MSG_RESULT(result, strdup("Check function (1,2,3,4) used")); //close connection modbus_close(ctx); - if (lock_required == 1 ) UNLOCK_SERIAL_PORT; + if (lock_required == 1 ) UNLOCK_PORT(lock_key); modbus_free(ctx); return SYSINFO_RET_FAIL; break; } //close connection modbus_close(ctx); - if (lock_required == 1 ) UNLOCK_SERIAL_PORT; + if (lock_required == 1 ) UNLOCK_PORT(lock_key); modbus_free(ctx); if (rc == -1) { @@ -407,7 +416,7 @@ int zbx_modbus_read_registers(AGENT_REQUEST *request, AGENT_RESULT *result) int zbx_module_init() { if (ZBX_MUTEX_ERROR == (MODBUS_SEM_ID = initsem())) { - //"unable to create semaphore set for serial port")); + printf("libzbxmodbus: unable to create semaphores. Please check maximum allowed nsem, it must be no less than %d. See limits in /proc/sys/kernel/sem\n", NSEMS); return ZBX_MODULE_FAIL; } @@ -442,12 +451,13 @@ int validate_datatype_param (char *datatype_param) {//checks that datatype provi return (datatype_param[1] == '\0') ? 1: 0; } -void create_modbus_context(char *con_string, modbus_t **ctx_out, int *lock_required_out) { +void create_modbus_context(char *con_string, modbus_t **ctx_out, int *lock_required_out, short *lock_key) { char first_char = con_string[0]; if (first_char == '/') {//then its rtu(serial con) - // -- next code is to parse first arg and find all required to connect to rtu successfully + *lock_required_out = 1; + // -- next code is to parse first arg and find all required to connect to rtu successfully char rtu_port[100]; int rtu_speed = 9600; char rtu_parity = 'N'; @@ -455,20 +465,51 @@ void create_modbus_context(char *con_string, modbus_t **ctx_out, int *lock_requi int rtu_stop_bit = 1; sscanf(con_string,"%s %d %c %d %d",rtu_port,&rtu_speed,&rtu_parity,&rtu_bits,&rtu_stop_bit); - *lock_required_out = 1; + *lock_key = hash(rtu_port) % 128; *ctx_out = modbus_new_rtu(rtu_port, rtu_speed, rtu_parity, rtu_bits, rtu_stop_bit); + } - else {//try modbus_tcp - *lock_required_out = 0; - *ctx_out = modbus_new_tcp(con_string, MODBUS_TCP_DEFAULT_PORT ); + else {//its TCP (encapsulated or Modbus TCP) + + char host[100]; + int port = MODBUS_TCP_DEFAULT_PORT; + + if (strstr(con_string, "enc://") != NULL) { + + *lock_required_out = 1; + memmove(con_string, con_string+6, strlen(con_string)); + sscanf(con_string, "%99[^:]:%99d[^\n]", host, &port); + *lock_key = hash(host) % NSEMS; + *ctx_out = modbus_new_rtutcp(host, port); + + } else if (strstr(con_string, "tcp://") != NULL) { + + *lock_required_out = 0; + memmove(con_string, con_string+6, strlen(con_string)); + sscanf(con_string, "%99[^:]:%99d[^\n]", host, &port); + *lock_key = hash(host) % NSEMS; + *ctx_out = modbus_new_tcp(host, port); + + } + else {//try Modbus TCP + + *lock_required_out = 0; + sscanf(con_string, "%99[^:]:%99d[^\n]", host, &port); + *lock_key = hash(host) % NSEMS; + *ctx_out = modbus_new_tcp(host, port); + + } } return; } + + + int initsem() /* sem_key from ftok() */ { - int nsems = 1; + int nsems = NSEMS; key_t sem_key; int semid; int i; @@ -496,7 +537,7 @@ int initsem() /* sem_key from ftok() */ int e = errno; semctl(semid, 0, IPC_RMID); /* clean up */ errno = e; - return -1; /* error, check errno */ + return ZBX_MUTEX_ERROR; /* error, check errno */ } } } else if (errno == EEXIST) { /* someone else got it first */ @@ -516,7 +557,7 @@ int initsem() /* sem_key from ftok() */ } if (!ready) { errno = ETIME; - return -1; + return ZBX_MUTEX_ERROR; } } else { return semid; /* error, check errno */ @@ -525,10 +566,10 @@ int initsem() /* sem_key from ftok() */ } -void sem_lock () { +void sem_lock (int sem_num) { struct sembuf sb; - sb.sem_num = 0; + sb.sem_num = sem_num; sb.sem_op = -1; /* set to allocate resource */ sb.sem_flg = SEM_UNDO; @@ -539,10 +580,10 @@ void sem_lock () { } -void sem_unlock () { +void sem_unlock (int sem_num) { struct sembuf sb; - sb.sem_num = 0; + sb.sem_num = sem_num; sb.sem_op = 1; /* free resource */ sb.sem_flg = SEM_UNDO; @@ -557,5 +598,6 @@ void sem_uninit (int semid) { if (semctl(semid, 0, IPC_RMID) == -1) { //zabbix_log(LOG_LEVEL_ERROR, "Failed to destroy semaphore set for semid: %d",MODBUS_SEM_ID); + printf("libzbxmodbus: failed to destroy semaphore set for semid: %d",MODBUS_SEM_ID); } -} \ No newline at end of file +}