Add support for read configuration from file

This commit is contained in:
Victor Antonovich 2017-11-29 13:39:40 +03:00
parent 7fb5309551
commit 3ea208491a
12 changed files with 514 additions and 131 deletions

View File

@ -1,7 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?>
<cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1826992774">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1826992774" moduleId="org.eclipse.cdt.core.settings" name="Default">
@ -16,17 +14,28 @@
</extensions>
</storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration buildProperties="" id="cdt.managedbuild.toolchain.gnu.base.1826992774" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
<configuration buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.base.1826992774" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
<folderInfo id="cdt.managedbuild.toolchain.gnu.base.1826992774.1618506567" name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.base.1143041813" name="cdt.managedbuild.toolchain.gnu.base" superClass="cdt.managedbuild.toolchain.gnu.base">
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.781693864" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
<builder id="cdt.managedbuild.target.gnu.builder.base.751022712" managedBuildOn="false" name="Gnu Make Builder.Default" superClass="cdt.managedbuild.target.gnu.builder.base"/>
<builder id="cdt.managedbuild.target.gnu.builder.base.751022712" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
<tool id="cdt.managedbuild.tool.gnu.archiver.base.1388164087" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1875165406" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"/>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1454341581" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.1875165406" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.437218346" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1454341581" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.722267304" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</tool>
<tool id="cdt.managedbuild.tool.gnu.c.linker.base.2010478517" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.970100266" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"/>
<tool id="cdt.managedbuild.tool.gnu.assembler.base.27610334" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.970100266" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base">
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.177254843" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
</inputType>
</tool>
<tool id="cdt.managedbuild.tool.gnu.assembler.base.27610334" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.285918871" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
</tool>
</toolChain>
</folderInfo>
</configuration>
@ -37,8 +46,15 @@
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<project id="mbus.null.914501471" name="mbus"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope"/>
<storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1826992774;cdt.managedbuild.toolchain.gnu.base.1826992774.1618506567;cdt.managedbuild.tool.gnu.cpp.compiler.base.1875165406;cdt.managedbuild.tool.gnu.cpp.compiler.input.437218346">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1826992774;cdt.managedbuild.toolchain.gnu.base.1826992774.1618506567;cdt.managedbuild.tool.gnu.c.compiler.base.1454341581;cdt.managedbuild.tool.gnu.c.compiler.input.722267304">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
</cproject>

1
.gitignore vendored
View File

@ -11,3 +11,4 @@ Makefile
*.log
*.in~
systemd-units/mbusd@.service
*.conf

View File

@ -4,13 +4,17 @@ ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src doc
mbusddocdir = ${prefix}/doc/mbusd
mbusddocdir = ${docdir}
mbusddoc_DATA = \
README.md\
CHANGELOG.md\
LICENSE
EXTRA_DIST = $(mbusddoc_DATA)
mbusdconfdir = ${mbusddocdir}
mbusdconf_DATA = \
conf/mbusd.conf.example
EXTRA_DIST = $(mbusddoc_DATA) $(mbusdconf_DATA)
# Copy all the spec files. Of cource, only one is actually used.
dist-hook:
@ -34,4 +38,3 @@ $(systemdsystemunit_DATA):
distclean-local::
-$(RM) $(systemdsystemunit_DATA)
endif

View File

@ -158,9 +158,9 @@ am__uninstall_files_from_dir = { \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
am__installdirs = "$(DESTDIR)$(mbusddocdir)" \
"$(DESTDIR)$(systemdsystemunitdir)"
DATA = $(mbusddoc_DATA) $(systemdsystemunit_DATA)
am__installdirs = "$(DESTDIR)$(mbusdconfdir)" \
"$(DESTDIR)$(mbusddocdir)" "$(DESTDIR)$(systemdsystemunitdir)"
DATA = $(mbusdconf_DATA) $(mbusddoc_DATA) $(systemdsystemunit_DATA)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
am__recursive_targets = \
@ -367,13 +367,17 @@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src doc
mbusddocdir = ${prefix}/doc/mbusd
mbusddocdir = ${docdir}
mbusddoc_DATA = \
README.md\
CHANGELOG.md\
LICENSE
EXTRA_DIST = $(mbusddoc_DATA)
mbusdconfdir = ${mbusddocdir}
mbusdconf_DATA = \
conf/mbusd.conf.example
EXTRA_DIST = $(mbusddoc_DATA) $(mbusdconf_DATA)
# systemd
AM_DISTCHECK_CONFIGURE_FLAGS = \
@ -443,6 +447,27 @@ clean-libtool:
distclean-libtool:
-rm -f libtool config.lt
install-mbusdconfDATA: $(mbusdconf_DATA)
@$(NORMAL_INSTALL)
@list='$(mbusdconf_DATA)'; test -n "$(mbusdconfdir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(mbusdconfdir)'"; \
$(MKDIR_P) "$(DESTDIR)$(mbusdconfdir)" || exit 1; \
fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
done | $(am__base_list) | \
while read files; do \
echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(mbusdconfdir)'"; \
$(INSTALL_DATA) $$files "$(DESTDIR)$(mbusdconfdir)" || exit $$?; \
done
uninstall-mbusdconfDATA:
@$(NORMAL_UNINSTALL)
@list='$(mbusdconf_DATA)'; test -n "$(mbusdconfdir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(mbusdconfdir)'; $(am__uninstall_files_from_dir)
install-mbusddocDATA: $(mbusddoc_DATA)
@$(NORMAL_INSTALL)
@list='$(mbusddoc_DATA)'; test -n "$(mbusddocdir)" || list=; \
@ -788,7 +813,7 @@ check: check-recursive
all-am: Makefile $(DATA) config.h
installdirs: installdirs-recursive
installdirs-am:
for dir in "$(DESTDIR)$(mbusddocdir)" "$(DESTDIR)$(systemdsystemunitdir)"; do \
for dir in "$(DESTDIR)$(mbusdconfdir)" "$(DESTDIR)$(mbusddocdir)" "$(DESTDIR)$(systemdsystemunitdir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-recursive
@ -844,7 +869,8 @@ info: info-recursive
info-am:
install-data-am: install-mbusddocDATA install-systemdsystemunitDATA
install-data-am: install-mbusdconfDATA install-mbusddocDATA \
install-systemdsystemunitDATA
install-dvi: install-dvi-recursive
@ -890,7 +916,8 @@ ps: ps-recursive
ps-am:
uninstall-am: uninstall-mbusddocDATA uninstall-systemdsystemunitDATA
uninstall-am: uninstall-mbusdconfDATA uninstall-mbusddocDATA \
uninstall-systemdsystemunitDATA
.MAKE: $(am__recursive_targets) all install-am install-strip
@ -905,12 +932,13 @@ uninstall-am: uninstall-mbusddocDATA uninstall-systemdsystemunitDATA
install install-am install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-info install-info-am install-man \
install-mbusddocDATA install-pdf install-pdf-am install-ps \
install-ps-am install-strip install-systemdsystemunitDATA \
installcheck installcheck-am installdirs installdirs-am \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags tags-am uninstall uninstall-am uninstall-mbusddocDATA \
install-mbusdconfDATA install-mbusddocDATA install-pdf \
install-pdf-am install-ps install-ps-am install-strip \
install-systemdsystemunitDATA installcheck installcheck-am \
installdirs installdirs-am maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic \
mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am uninstall-mbusdconfDATA uninstall-mbusddocDATA \
uninstall-systemdsystemunitDATA
.PRECIOUS: Makefile

View File

@ -47,20 +47,15 @@ Usage:
-h Usage help.
-d Instruct mbusd not to fork itself (non-daemonize).
-t Enable RTS RS-485 data direction control (if not disabled while compile).
-y file
Enable RS-485 direction data direction control by writing '1' to file
for transmitter enable and '0' to file for transmitter disable
-Y file
Enable RS-485 direction data direction control by writing '0' to file
for transmitter enable and '1' to file for transmitter disable
-L logfile
Specifies log file name ('-' for logging to STDOUT only, default is /var/log/mbusd.log).
-v level
Specifies log verbosity level (0 for errors only, 1 for warnings
and 2 for also information messages.) If mbusd was compiled in debug mode,
valid log levels are up to 9, where log levels above 2 forces
logging of information about additional internal events.
-L logfile
Specifies log file name ('-' for logging to STDOUT only, default is /var/log/mbusd.log).
-c cfgfile
Read configuration from cfgfile.
-p device
Specifies serial port device name.
-s speed
@ -69,6 +64,13 @@ Usage:
Specifies serial port mode (like 8N1).
-P port
Specifies TCP port number (default 502).
-t Enable RTS RS-485 data direction control (if not disabled while compile).
-y file
Enable RS-485 direction data direction control by writing '1' to file
for transmitter enable and '0' to file for transmitter disable
-Y file
Enable RS-485 direction data direction control by writing '0' to file
for transmitter enable and '1' to file for transmitter disable
-C maxconn
Specifies maximum number of simultaneous TCP connections.
-N retries
@ -82,16 +84,28 @@ Usage:
Please note running **mbusd** on default Modbus TCP port (502) requires root privileges!
Configuration file:
-------------------
**mbusd** can read the configuration from a file specified by `-c` command line flag.
Please see [example configuration file](conf/mbusd.conf.example)
for complete list of available configuration options.
systemd:
---------------
**mbusd** has [systemd](https://wiki.archlinux.org/index.php/systemd) support. The build system detects whether the system has systemd after which `sudo make install` installs the `mbusd@.service` file on systems with systemd active.
**mbusd** has [systemd](https://wiki.archlinux.org/index.php/systemd) support.
The build system detects whether the system has systemd after which `sudo make install`
installs the `mbusd@.service` file on systems with systemd active.
The **mbusd** service can be started via:
# systemctl start mbusd@<serial port>.service
where `<serial port>` is serial port device name (like `ttyUSB0`).
**mbusd** started by systemd will read its configuration from file named `/etc/mbusd/mbusd-<serial port>.conf`.
This way it's possible to run multiple **mbusd** instances with different configurations.
To see the **mbusd** service status:
# systemctl status mbusd@<serial port>.service
@ -106,12 +120,6 @@ To start the **mbusd** service on system boot:
Please check systemd documentation for other usefull systemd [commands](https://wiki.archlinux.org/index.php/systemd)
The checked in `mbusd@.service` starts **mbusd** equivalent to the following command:
# $PREFIX/bin/mbusd -p /dev/<serial port> -s 9600 -m 8N1 -P 502 -d -v2
Feel free to modify the service file locally with your own desired configuration.
Reporting bugs:
---------------
@ -119,7 +127,8 @@ Please file [issue](https://github.com/3cky/mbusd/issues) with attached debug lo
# mbusd -L/tmp/mbusd.log -p /dev/ttyUSB0 -s 9600 -P 502 -d -v9
Unless you were prompted so or there is another pertinent reason (e.g. GitHub fails to accept the bug report), please do not send bug reports via personal email.
Unless you were prompted so or there is another pertinent reason (e.g. GitHub fails to accept the bug report),
please do not send bug reports via personal email.
Contributing:
-------------
@ -146,6 +155,9 @@ Andrew Denysenko (<nitr0@seti.kr.ua>):
James Jarvis (<jj@aprsworld.com>):
- file based RS-485 data direction control
Luuk Loeffen (<luukloeffen@hotmail.com>):
- systemd support
License:
--------

44
conf/mbusd.conf.example Normal file
View File

@ -0,0 +1,44 @@
#############################################
# #
# Sample configuration file for mbusd #
# #
#############################################
########## Serial port settings #############
# Serial port device name
device = /dev/ttyS0
# Serial port speed
speed = 9600
# Serial port mode
mode = 8n1
# RS-485 data direction control type (addc, rts, sysfs_0, sysfs_1)
trx_control = addc
# Sysfs file to use to control data direction
# trx_sysfile =
############# TCP port settings #############
# TCP server port number
port = 502
# Maximum number of simultaneous TCP connections
maxconn = 32
# Connection timeout value in seconds
timeout = 60
######### Request/response settings #########
# Maximum number of request retries
retries = 3
# Pause between requests in milliseconds
pause = 100
# Response wait time in milliseconds
wait = 500

View File

@ -5,21 +5,23 @@ mbusd \- MODBUS/TCP to MODBUS/RTU gateway.
.B mbusd
.RB [ -h ]
.RB [ -d ]
.RB [ -t ]
.RB [ -y
.IR file ]
.RB [ -Y
.IR file ]
.RB [ -v
.IR level ]
.RB [ -L
.IR logfile ]
.RB [ -v
.IR level ]
.RB [ -c
.IR cfgfile ]
.RB [ -p
.IR device ]
.RB [ -s
.IR speed ]
.RB [ -m
.IR mode ]
.RB [ -t ]
.RB [ -y
.IR file ]
.RB [ -Y
.IR file ]
.RB [ -P
.IR port ]
.RB [ -C
@ -39,22 +41,16 @@ mbusd \- MODBUS/TCP to MODBUS/RTU gateway.
Usage help.
.IP \fB-d\fR
Instruct \fImbusd\fR not to fork itself (non-daemonize).
.IP \fB-t\fR
Enable RTS RS-485 data direction control (if not disabled while compile).
.IP "\fB-y \fIfile\fR"
Enable RS-485 direction data direction control by writing '1' to file
for transmitter enable and '0' to file for transmitter disable
.IP "\fB-Y \fIfile\fR"
Enable RS-485 direction data direction control by writing '0' to file
for transmitter enable and '1' to file for transmitter disable
.IP "\fB-L \fIlogfile\fR"
Specifies log file name ('-' for logging to STDOUT only, default is /var/log/mbusd.log).
.IP "\fB-v \fIlevel\fR"
Specifies log verbosity level. 0 enables logging of errors only,
1 also enables warnings and 2 enables information messages.
If \fImbusd\fR was compiled in debug mode, valid log levels are up to 9,
where log levels above 2 forces logging of information about additional
internal events.
.IP "\fB-L \fIlogfile\fR"
Specifies log file name ('-' for logging to STDOUT only, default is /var/log/mbusd.log).
.IP "\fB-c \fIcfgfile\fR"
Read configuration from cfgfile.
.IP "\fB-p \fIdevice\fR"
Specifies serial port device name.
.IP "\fB-s \fIspeed\fR"
@ -63,6 +59,14 @@ Specifies serial port speed.
Specifies serial port mode (like 8N1).
.IP "\fB-P \fIport\fR"
Specifies TCP port number.
.IP \fB-t\fR
Enable RTS RS-485 data direction control (if not disabled while compile).
.IP "\fB-y \fIfile\fR"
Enable RS-485 direction data direction control by writing '1' to file
for transmitter enable and '0' to file for transmitter disable
.IP "\fB-Y \fIfile\fR"
Enable RS-485 direction data direction control by writing '0' to file
for transmitter enable and '1' to file for transmitter disable
.IP "\fB-C \fImaxconn\fR"
Specifies maximum number of simultaneous TCP connections.
.IP "\fB-N \fIretries\fR"

233
src/cfg.c
View File

@ -33,9 +33,23 @@
#include "cfg.h"
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define CFG_MAX_LINE_LENGTH 200
#define CFG_NAME_MATCH(n) strcmp(n, name) == 0
#define CFG_VALUE_MATCH(n) strcasecmp(n, value) == 0
/* Global configuration storage variable */
cfg_t cfg;
/* Configuration error message */
char cfg_err[INTBUFSIZE + 1];
#define CFG_ERR(s, v) snprintf(cfg_err, INTBUFSIZE, s, v)
/*
* Setting up config defaults
*/
@ -48,9 +62,10 @@ cfg_init(void)
#endif
strncpy(cfg.ttyport, DEFAULT_PORT, INTBUFSIZE);
cfg.ttyspeed = DEFAULT_SPEED;
cfg.ttymode = DEFAULT_MODE;
#ifdef TRXCTL
strncpy(cfg.ttymode, DEFAULT_MODE, INTBUFSIZE);
#ifdef TRXCTL
cfg.trxcntl = TRX_ADDC;
*cfg.trxcntl_file = '\0';
#endif
cfg.serverport = DEFAULT_SERVERPORT;
cfg.maxconn = DEFAULT_MAXCONN;
@ -60,3 +75,217 @@ cfg_init(void)
cfg.resppause = DV(3, cfg.ttyspeed);
cfg.conntimeout = DEFAULT_CONNTIMEOUT;
}
static char *
cfg_rtrim(char *s)
{
char *p = s + strlen(s);
while (p > s && isspace((unsigned char )(*--p)))
*p = '\0';
return s;
}
static char *
cfg_ltrim(const char *s)
{
while (*s && isspace((unsigned char )(*s)))
s++;
return (char *) s;
}
int
cfg_handle_param(char *name, char *value)
{
if (CFG_NAME_MATCH("device"))
{
strncpy(cfg.ttyport, value, INTBUFSIZE);
}
else if (CFG_NAME_MATCH("speed"))
{
cfg.ttyspeed = strtoul(value, NULL, 0);
}
else if (CFG_NAME_MATCH("mode"))
{
int mode_invalid;
if (strlen(value) != 3)
mode_invalid = 1;
else
{
char parity = toupper(value[1]);
mode_invalid = value[0] != '8' || (value[2] != '1' && value[2] != '2') ||
(parity != 'N' && parity != 'E' && parity != 'O');
}
if (mode_invalid)
{
CFG_ERR("invalid device mode: %s", value);
return 0;
}
strncpy(cfg.ttymode, value, INTBUFSIZE);
}
else if (CFG_NAME_MATCH("port"))
{
cfg.serverport = strtoul(value, NULL, 0);
}
else if (CFG_NAME_MATCH("maxconn"))
{
cfg.maxconn = strtoul(value, NULL, 0);
if (cfg.maxconn < 1 || cfg.maxconn > MAX_MAXCONN)
{
CFG_ERR("invalid maxconn value: %s", value);
return 0;
}
}
else if (CFG_NAME_MATCH("retries"))
{
cfg.maxtry = strtoul(value, NULL, 0);
if (cfg.maxtry > MAX_MAXTRY)
{
CFG_ERR("invalid retries value: %s", value);
return 0;
}
}
else if (CFG_NAME_MATCH("pause"))
{
cfg.rqstpause = strtoul(value, NULL, 0);
if (cfg.rqstpause < 1 || cfg.rqstpause > MAX_RQSTPAUSE)
{
CFG_ERR("invalid pause value: %s", value);
return 0;
}
}
else if (CFG_NAME_MATCH("wait"))
{
cfg.respwait = strtoul(value, NULL, 0);
if (cfg.respwait < 1 || cfg.respwait > MAX_RESPWAIT)
{
CFG_ERR("invalid wait value: %s", value);
return 0;
}
}
else if (CFG_NAME_MATCH("timeout"))
{
cfg.conntimeout = strtoul(value, NULL, 0);
if (cfg.conntimeout > MAX_CONNTIMEOUT)
return 0;
#ifdef TRXCTL
}
else if (CFG_NAME_MATCH("trx_control"))
{
if (CFG_VALUE_MATCH("addc"))
{
cfg.trxcntl = TRX_ADDC;
}
else if (CFG_VALUE_MATCH("rts"))
{
cfg.trxcntl = TRX_RTS;
}
else if (CFG_VALUE_MATCH("sysfs_0"))
{
cfg.trxcntl = TRX_SYSFS_0;
}
else if (CFG_VALUE_MATCH("sysfs_1"))
{
cfg.trxcntl = TRX_SYSFS_1;
}
else
{
/* Unknown TRX control mode */
CFG_ERR("unknown trx control mode: %s", value);
return 0;
}
}
else if (CFG_NAME_MATCH("trx_sysfile"))
{
strncpy(cfg.trxcntl_file, value, INTBUFSIZE);
#endif
#ifdef LOG
}
else if (CFG_NAME_MATCH("loglevel"))
{
cfg.dbglvl = (char)strtol(optarg, NULL, 0);
#endif
}
else {
/* Unknown parameter name */
CFG_ERR("unknown parameter: %s", name);
return 0;
}
return 1;
}
int
cfg_parse_file(void *file)
{
char *line;
char *start;
char *end;
char *name;
char *value;
int lineno = 0;
int error = 0;
*cfg_err = '\0';
line = (char *) malloc(CFG_MAX_LINE_LENGTH);
if (!line)
{
return -1;
}
while (fgets(line, CFG_MAX_LINE_LENGTH, file) != NULL)
{
lineno++;
start = cfg_ltrim(cfg_rtrim(line));
if (*start == '#')
{
/* skip comment */
continue;
}
else if (*start)
{
/* parse `name=value` pair */
for (end = start; *end && *end != '='; end++);
if (*end == '=')
{
*end = '\0';
name = cfg_rtrim(start);
value = cfg_ltrim(cfg_rtrim(end + 1));
/* handle name/value pair */
if (!cfg_handle_param(name, value))
{
error = lineno;
break;
}
}
else
{
/* no '=' found on config line */
error = lineno;
CFG_ERR("can't parse line: %s", start);
break;
}
}
}
free(line);
return error;
}
int
cfg_read_file(const char *filename)
{
FILE* file;
int error;
file = fopen(filename, "r");
if (!file)
return -1;
error = cfg_parse_file(file);
fclose(file);
return error;
}

View File

@ -53,7 +53,7 @@ typedef struct
/* tty speed */
int ttyspeed;
/* tty mode */
char *ttymode;
char ttymode[INTBUFSIZE + 1];
/* trx control type (0 - ADDC, 1 - by RTS, 2 - by sysfs GPIO with 1 activating transmit, 3 - by sysfs GPIO with 0 activating transmit) */
int trxcntl;
/* trx control sysfs file */
@ -76,6 +76,8 @@ typedef struct
/* Prototypes */
extern cfg_t cfg;
extern char cfg_err[];
void cfg_init(void);
int cfg_read_file(const char *filename);
#endif /* _CFG_H */

View File

@ -53,6 +53,31 @@
#define DEFAULT_RESPWAIT 500
#define DEFAULT_CONNTIMEOUT 60
/* Max simultaneous TCP connections to server */
#ifndef MAX_MAXCONN
# define MAX_MAXCONN 128
#endif
/* Max RTU device request retries */
#ifndef MAX_MAXTRY
# define MAX_MAXTRY 15
#endif
/* Max RTU device pause between requests, in msecs */
#ifndef MAX_RQSTPAUSE
# define MAX_RQSTPAUSE 10000
#endif
/* Max RTU device response wait, in msecs */
#ifndef MAX_RESPWAIT
# define MAX_RESPWAIT 10000
#endif
/* Max connection timeout, in secs */
#ifndef MAX_CONNTIMEOUT
# define MAX_CONNTIMEOUT 1000
#endif
#define CRCSIZE 2 /* size (in bytes) of CRC */
#define HDRSIZE 6 /* size (in bytes) of header */
#define BUFSIZE 256 /* size (in bytes) of MODBUS data */

View File

@ -100,62 +100,70 @@ void
usage(char *exename)
{
cfg_init();
printf("%s-%s Copyright (C) 2002-2003, 2011, 2013-2016 Victor Antonovich <v.antonovich@gmail.com>, "
printf("%s-%s Copyright (C) 2002-2003, 2011, 2013-2017 Victor Antonovich <v.antonovich@gmail.com>, "
"Andrew Denysenko <nitr0@seti.kr.ua>\n\n"
"Usage: %s [-h] [-d] "
#ifdef TRXCTL
"[-t] [-y sysfsfile] [-Y sysfsfile]\n"
#ifdef LOG
"[-L logfile] [-v level] "
#endif
"[-v level] [-L logfile] [-p device] [-s speed] [-m mode] [-P port]\n"
" [-C maxconn] [-N retries] [-R pause] [-W wait] [-T timeout]\n\n"
"[-c cfgfile] \n"
" [-p device] [-s speed] [-m mode]\n"
#ifdef TRXCTL
" [-t] [-y sysfsfile] [-Y sysfsfile]\n"
#endif
" [-P port] [-C maxconn] [-N retries] [-R pause] [-W wait] [-T timeout]\n\n"
"Options:\n"
" -h : this help\n"
" -d : don't daemonize\n"
#ifdef TRXCTL
" -t : enable RTS RS-485 data direction control using RTS\n"
" -y : enable RTS RS-485 data direction control using sysfs file, active transmit\n"
" -Y : enable RTS RS-485 data direction control using sysfs file, active receive\n"
#endif
#ifdef LOG
" -L logfile : set log file name (default %s%s, \n"
" '-' for logging to STDOUT only)\n"
#ifdef DEBUG
" -v level : set log level (0-9, default %d, 0 - errors only)\n"
#else
" -v level : set log level (0-2, default %d, 0 - errors only)\n"
#endif
" -L logfile : set log file name (default %s%s, \n"
" '-' for logging to STDOUT only)\n"
#endif
" -c cfgfile : read configuration from cfgfile\n"
" -p device : set serial port device name (default %s)\n"
" -s speed : set serial port speed (default %d)\n"
" -m mode : set serial port mode (default %s)\n"
" -P port : set TCP server port number (default %d)\n"
#ifdef TRXCTL
" -t : enable RTS RS-485 data direction control using RTS\n"
" -y : enable RTS RS-485 data direction control using sysfs file, active transmit\n"
" -Y : enable RTS RS-485 data direction control using sysfs file, active receive\n"
#endif
" -C maxconn : set maximum number of simultaneous TCP connections\n"
" (1-128, default %d)\n"
" (1-%d, default %d)\n"
" -N retries : set maximum number of request retries\n"
" (0-15, default %d, 0 - without retries)\n"
" (0-%d, default %d, 0 - without retries)\n"
" -R pause : set pause between requests in milliseconds\n"
" (1-10000, default %lu)\n"
" (1-%d, default %lu)\n"
" -W wait : set response wait time in milliseconds\n"
" (1-10000, default %lu)\n"
" (1-%d, default %lu)\n"
" -T timeout : set connection timeout value in seconds\n"
" (0-1000, default %d, 0 - no timeout)"
" (0-%d, default %d, 0 - no timeout)"
"\n", PACKAGE, VERSION, exename,
#ifdef LOG
cfg.dbglvl, LOGPATH, LOGNAME,
LOGPATH, LOGNAME, cfg.dbglvl,
#endif
cfg.ttyport, cfg.ttyspeed, cfg.ttymode, cfg.serverport, cfg.maxconn,
cfg.maxtry, cfg.rqstpause, cfg.respwait, cfg.conntimeout);
cfg.ttyport, cfg.ttyspeed, cfg.ttymode, cfg.serverport,
MAX_MAXCONN, cfg.maxconn, MAX_MAXTRY, cfg.maxtry,
MAX_RQSTPAUSE, cfg.rqstpause, MAX_RESPWAIT, cfg.respwait,
MAX_CONNTIMEOUT, cfg.conntimeout);
exit(0);
}
int
main(int argc, char *argv[])
{
int err = 0, rc;
int err = 0, rc, err_line;
char *exename;
char ttyparity;
sig_init();
cfg_init();
if ((exename = strrchr(argv[0], '/')) == NULL)
@ -172,7 +180,7 @@ main(int argc, char *argv[])
#ifdef LOG
"v:L:"
#endif
"p:s:m:P:C:N:R:W:T:")) != RC_ERR)
"p:s:m:P:C:N:R:W:T:c:")) != RC_ERR)
{
switch (rc)
{
@ -181,16 +189,28 @@ main(int argc, char *argv[])
case 'd':
isdaemon = FALSE;
break;
case 'c':
if ((err_line = cfg_read_file(optarg)) != 0)
{
if (err_line > 0)
printf("%s: can't read config file %s: error at line %d: %s\n",
exename, optarg, err_line, cfg_err);
else
printf("%s: can't read config file %s: %s\n",
exename, optarg, strerror(errno));
exit(-1);
}
break;
#ifdef TRXCTL
case 't':
cfg.trxcntl = TRX_RTS;
break;
case 'y':
cfg.trxcntl = TRX_SYSFS_1;
cfg.trxcntl = TRX_SYSFS_1;
strncpy(cfg.trxcntl_file, optarg, INTBUFSIZE);
break;
case 'Y':
cfg.trxcntl = TRX_SYSFS_0;
cfg.trxcntl = TRX_SYSFS_0;
strncpy(cfg.trxcntl_file, optarg, INTBUFSIZE);
break;
#endif
@ -222,8 +242,7 @@ main(int argc, char *argv[])
else
{ /* concatenate given log file name with default path */
strncpy(cfg.logname, LOGPATH, INTBUFSIZE);
strncat(cfg.logname, optarg,
INTBUFSIZE - strlen(cfg.logname));
strncat(cfg.logname, optarg, INTBUFSIZE - strlen(cfg.logname));
}
}
else strncpy(cfg.logname, optarg, INTBUFSIZE);
@ -234,8 +253,7 @@ main(int argc, char *argv[])
{ /* concatenate given port name with default
path to devices mountpoint */
strncpy(cfg.ttyport, "/dev/", INTBUFSIZE);
strncat(cfg.ttyport, optarg,
INTBUFSIZE - strlen(cfg.ttyport));
strncat(cfg.ttyport, optarg, INTBUFSIZE - strlen(cfg.ttyport));
}
else strncpy(cfg.ttyport, optarg, INTBUFSIZE);
break;
@ -243,79 +261,80 @@ main(int argc, char *argv[])
cfg.ttyspeed = strtoul(optarg, NULL, 0);
break;
case 'm':
cfg.ttymode = optarg;
/* tty mode sanity checks */
if (strlen(cfg.ttymode) != 3)
{
printf("%s: -m: invalid serial port mode ('%s')\n", exename, cfg.ttymode);
exit(-1);
}
if (cfg.ttymode[0] != '8')
{
printf("%s: -m: invalid serial port character size "
"(%c, must be 8)\n",
exename, cfg.ttymode[0]);
exit(-1);
}
ttyparity = toupper(cfg.ttymode[1]);
if (ttyparity != 'N' && ttyparity != 'E' && ttyparity != 'O')
{
printf("%s: -m: invalid serial port parity "
"(%c, must be N, E or O)\n", exename, ttyparity);
exit(-1);
}
if (cfg.ttymode[2] != '1' && cfg.ttymode[2] != '2')
{
printf("%s: -m: invalid serial port stop bits "
"(%c, must be 1 or 2)\n", exename, cfg.ttymode[2]);
exit(-1);
}
break;
strncpy(cfg.ttymode, optarg, INTBUFSIZE);
/* tty mode sanity checks */
if (strlen(cfg.ttymode) != 3)
{
printf("%s: -m: invalid serial port mode ('%s')\n",
exename, cfg.ttymode);
exit(-1);
}
if (cfg.ttymode[0] != '8')
{
printf("%s: -m: invalid serial port character size "
"(%c, must be 8)\n",
exename, cfg.ttymode[0]);
exit(-1);
}
ttyparity = toupper(cfg.ttymode[1]);
if (ttyparity != 'N' && ttyparity != 'E' && ttyparity != 'O')
{
printf("%s: -m: invalid serial port parity "
"(%c, must be N, E or O)\n", exename, ttyparity);
exit(-1);
}
if (cfg.ttymode[2] != '1' && cfg.ttymode[2] != '2')
{
printf("%s: -m: invalid serial port stop bits "
"(%c, must be 1 or 2)\n", exename, cfg.ttymode[2]);
exit(-1);
}
break;
case 'P':
cfg.serverport = strtoul(optarg, NULL, 0);
break;
case 'C':
cfg.maxconn = strtoul(optarg, NULL, 0);
if (cfg.maxconn < 1 || cfg.maxconn > 128)
if (cfg.maxconn < 1 || cfg.maxconn > MAX_MAXCONN)
{ /* report about invalid max conn number */
printf("%s: -C: invalid maxconn value"
" (%d, must be 1-128)\n", exename, cfg.maxconn);
" (%d, must be 1-%d)\n", exename, cfg.maxconn, MAX_MAXCONN);
exit(-1);
}
break;
case 'N':
cfg.maxtry = strtoul(optarg, NULL, 0);
if (cfg.maxtry > 15)
if (cfg.maxtry > MAX_MAXTRY)
{ /* report about invalid max try number */
printf("%s: -N: invalid maxtry value"
" (%d, must be 0-15)\n", exename, cfg.maxtry);
" (%d, must be 0-%d)\n", exename, cfg.maxtry, MAX_MAXTRY);
exit(-1);
}
break;
case 'R':
cfg.rqstpause = strtoul(optarg, NULL, 0);
if (cfg.rqstpause < 1 || cfg.rqstpause > 10000)
if (cfg.rqstpause < 1 || cfg.rqstpause > MAX_RQSTPAUSE)
{ /* report about invalid rqst pause value */
printf("%s: -R: invalid inter-request pause value"
" (%lu, must be 1-10000)\n", exename, cfg.rqstpause);
" (%lu, must be 1-%d)\n", exename, cfg.rqstpause, MAX_RQSTPAUSE);
exit(-1);
}
break;
case 'W':
cfg.respwait = strtoul(optarg, NULL, 0);
if (cfg.respwait < 1 || cfg.respwait > 10000)
if (cfg.respwait < 1 || cfg.respwait > MAX_RESPWAIT)
{ /* report about invalid resp wait value */
printf("%s: -W: invalid response wait time value"
" (%lu, must be 1-10000)\n", exename, cfg.respwait);
" (%lu, must be 1-%d)\n", exename, cfg.respwait, MAX_RESPWAIT);
exit(-1);
}
break;
case 'T':
cfg.conntimeout = strtoul(optarg, NULL, 0);
if (cfg.conntimeout > 1000)
if (cfg.conntimeout > MAX_CONNTIMEOUT)
{ /* report about invalid conn timeout value */
printf("%s: -T: invalid conn timeout value"
" (%d, must be 1-1000)\n", exename, cfg.conntimeout);
" (%d, must be 1-%d)\n", exename, cfg.conntimeout, MAX_CONNTIMEOUT);
exit(-1);
}
break;

View File

@ -3,7 +3,7 @@ Description=Modbus TCP to Modbus RTU (RS-232/485) gateway.
Requires=network.target
[Service]
ExecStart=@bindir@/mbusd -p /dev/%i -s 9600 -m 8N1 -P 502 -d -v2
ExecStart=@bindir@/mbusd -d -v2 -L - -c /etc/mbusd/mbusd-%i.conf -p /dev/%i
Restart=on-failure
RestartSec=1
StandardOutput=journal