From 3ea208491a0a16f0a7924c479fda5dc7b2c819bf Mon Sep 17 00:00:00 2001 From: Victor Antonovich Date: Wed, 29 Nov 2017 13:39:40 +0300 Subject: [PATCH] Add support for read configuration from file --- .cproject | 36 +++-- .gitignore | 1 + Makefile.am | 9 +- Makefile.in | 56 ++++++-- README.md | 46 ++++--- conf/mbusd.conf.example | 44 ++++++ doc/mbusd.8.in | 38 +++--- src/cfg.c | 233 +++++++++++++++++++++++++++++++- src/cfg.h | 4 +- src/conn.h | 25 ++++ src/main.c | 151 ++++++++++++--------- systemd-units/mbusd@.service.in | 2 +- 12 files changed, 514 insertions(+), 131 deletions(-) create mode 100644 conf/mbusd.conf.example diff --git a/.cproject b/.cproject index 3474600..4391916 100644 --- a/.cproject +++ b/.cproject @@ -1,7 +1,5 @@ - - - + @@ -16,17 +14,28 @@ - + - + - - + + + + + + - - + + + + + + + + + @@ -37,8 +46,15 @@ + + + + + + + + - diff --git a/.gitignore b/.gitignore index 5c17d91..6f2c83c 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ Makefile *.log *.in~ systemd-units/mbusd@.service +*.conf diff --git a/Makefile.am b/Makefile.am index 228b31c..d31df40 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 - diff --git a/Makefile.in b/Makefile.in index 50af1c2..e29c0e5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/README.md b/README.md index 3c6a501..558034d 100644 --- a/README.md +++ b/README.md @@ -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@.service where `` is serial port device name (like `ttyUSB0`). +**mbusd** started by systemd will read its configuration from file named `/etc/mbusd/mbusd-.conf`. +This way it's possible to run multiple **mbusd** instances with different configurations. + To see the **mbusd** service status: # systemctl status mbusd@.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/ -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 (): James Jarvis (): - file based RS-485 data direction control +Luuk Loeffen (): + - systemd support + License: -------- diff --git a/conf/mbusd.conf.example b/conf/mbusd.conf.example new file mode 100644 index 0000000..5e5db92 --- /dev/null +++ b/conf/mbusd.conf.example @@ -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 diff --git a/doc/mbusd.8.in b/doc/mbusd.8.in index 08d1ff5..e841a96 100644 --- a/doc/mbusd.8.in +++ b/doc/mbusd.8.in @@ -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" diff --git a/src/cfg.c b/src/cfg.c index ade4cda..a3a98b6 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -33,9 +33,23 @@ #include "cfg.h" +#include +#include +#include + +#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; +} diff --git a/src/cfg.h b/src/cfg.h index 2416d2c..9b60fa1 100644 --- a/src/cfg.h +++ b/src/cfg.h @@ -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 */ diff --git a/src/conn.h b/src/conn.h index 58b1f5a..2a5d343 100644 --- a/src/conn.h +++ b/src/conn.h @@ -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 */ diff --git a/src/main.c b/src/main.c index d788180..43abfa1 100644 --- a/src/main.c +++ b/src/main.c @@ -100,62 +100,70 @@ void usage(char *exename) { cfg_init(); - printf("%s-%s Copyright (C) 2002-2003, 2011, 2013-2016 Victor Antonovich , " + printf("%s-%s Copyright (C) 2002-2003, 2011, 2013-2017 Victor Antonovich , " "Andrew Denysenko \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; diff --git a/systemd-units/mbusd@.service.in b/systemd-units/mbusd@.service.in index 2fb6b7b..39856aa 100644 --- a/systemd-units/mbusd@.service.in +++ b/systemd-units/mbusd@.service.in @@ -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