get rid of argp dependency

This commit is contained in:
John
2023-10-01 14:09:38 +02:00
parent 1b9723c96f
commit 22b6f02752
20 changed files with 740 additions and 304 deletions
-12
View File
@@ -74,18 +74,6 @@ check_function_exists(ppoll HAVE_PPOLL)
check_include_file(linux/serial.h HAVE_LINUX_SERIAL -DHAVE_LINUX_SERIAL=1)
check_include_file(dev/usb/uftdiio.h HAVE_FREEBSD_UFTDI -DHAVE_FREEBSD_UFTDI=1)
check_function_exists(argp_parse HAVE_ARGP_H)
if(NOT HAVE_ARGP_H)
if(NOT ARGP_HINTS)
set(ARGP_HINTS ~/argp-standalone/src)
endif(NOT ARGP_HINTS)
find_library(LIB_ARGP NAMES argp argp-standalone HINTS ARGP_HINTS)
if (NOT LIB_ARGP)
message(FATAL_ERROR "argp library not available")
endif(NOT LIB_ARGP)
set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES} ${LIB_ARGP}")
endif(NOT HAVE_ARGP_H)
option(coverage "enable code coverage tracking." OFF)
if(NOT coverage STREQUAL OFF)
add_definitions(-g -O0 --coverage -Wall)
+1 -1
View File
@@ -61,7 +61,7 @@ Building ebusd from the source requires the following packages and/or features:
* g++ with C++11 support (>=4.8.1)
* make
* kernel with pselect or ppoll support
* glibc with argp support or argp-standalone
* glibc with getopt_long support
* libmosquitto-dev for MQTT support
* libssl-dev for SSL support
-3
View File
@@ -40,9 +40,6 @@
/* Defined if syslog.h is available. */
#cmakedefine HAVE_SYSLOG_H
/* Defined if argp.h is available. */
#cmakedefine HAVE_ARGP_H
/* The name of package. */
#cmakedefine PACKAGE "${PACKAGE_NAME}"
-8
View File
@@ -48,14 +48,6 @@ if test "x$with_contrib" != "xno"; then
fi
AC_ARG_WITH(ebusfeed, AS_HELP_STRING([--with-ebusfeed], [enable inclusion of ebusfeed tool]), [with_ebusfeed=yes], [])
AM_CONDITIONAL([WITH_EBUSFEED], [test "x$with_ebusfeed" == "xyes"])
AC_ARG_WITH(argp-lib, AS_HELP_STRING([--with-argp-lib=PATH], [path to argp libraries]), [LDFLAGS+="-L$with_argp_lib"])
AC_ARG_WITH(argp-include, AS_HELP_STRING([--with-argp-include=PATH], [path to argp includes]), [CXXFLAGS+="-I$with_argp_include"])
AC_CHECK_FUNC([argp_parse], [have_argp=yes], AC_CHECK_LIB([argp], [argp_parse], [have_argp=yes; LIBS="-largp $LIBS"], [have_argp=no]))
if test "x$have_argp" = "xyes"; then
AC_CHECK_HEADER([argp.h], AC_DEFINE([HAVE_ARGP_H], [1], [Defined if argp.h is available.]), AC_MSG_ERROR([argp.h not found]))
else
AC_MSG_ERROR([argp library not found, specify argp-standalone location in --with-argp-lib= and --with-argp-include= options.])
fi
AC_ARG_WITH(mqtt, AS_HELP_STRING([--without-mqtt], [disable support for MQTT handling]), [], [with_mqtt=yes])
if test "x$with_mqtt" != "xno"; then
AC_CHECK_LIB([mosquitto], [mosquitto_lib_init],
+1 -1
View File
@@ -8,7 +8,7 @@ url="https://github.com/john30/ebusd"
# Upstream only supports these archs.
arch="x86 x86_64 aarch64 armhf armv7"
license="GPL-3.0-only"
makedepends="argp-standalone cmake mosquitto-dev openssl-dev"
makedepends="cmake mosquitto-dev openssl-dev"
source="$pkgname-$pkgver.tar.gz::https://github.com/john30/${pkgname}/archive/refs/tags/${pkgver}.tar.gz"
build() {
+1 -1
View File
@@ -33,7 +33,7 @@ include_directories(../lib/ebus)
include_directories(../lib/utils)
add_executable(ebusd ${ebusd_SOURCES})
target_link_libraries(ebusd utils ebus pthread rt ${LIB_ARGP} ${ebusd_LIBS})
target_link_libraries(ebusd utils ebus pthread rt ${ebusd_LIBS})
install(TARGETS ebusd EXPORT ebusd DESTINATION usr/bin)
+9 -9
View File
@@ -30,11 +30,11 @@
namespace ebusd {
/** the final @a argp_child structure. */
static const struct argp_child g_last_argp_child = {nullptr, 0, nullptr, 0};
/** the final @a argParseChildOpt structure. */
static const argParseChildOpt g_last_arg_child = {nullptr, nullptr};
/** the list of @a argp_child structures. */
static struct argp_child g_argp_children[
/** the list of @a argParseChildOpt structures. */
static argParseChildOpt g_arg_children[
1
#ifdef HAVE_MQTT
+1
@@ -44,17 +44,17 @@ static struct argp_child g_argp_children[
#endif
];
const struct argp_child* datahandler_getargs() {
const argParseChildOpt* datahandler_getargs() {
size_t count = 0;
#ifdef HAVE_MQTT
g_argp_children[count++] = *mqtthandler_getargs();
g_arg_children[count++] = *mqtthandler_getargs();
#endif
#ifdef HAVE_KNX
g_argp_children[count++] = *knxhandler_getargs();
g_arg_children[count++] = *knxhandler_getargs();
#endif
if (count > 0) {
g_argp_children[count] = g_last_argp_child;
return g_argp_children;
g_arg_children[count] = g_last_arg_child;
return g_arg_children;
}
return nullptr;
}
+4 -4
View File
@@ -19,12 +19,12 @@
#ifndef EBUSD_DATAHANDLER_H_
#define EBUSD_DATAHANDLER_H_
#include <argp.h>
#include <map>
#include <list>
#include <string>
#include "ebusd/bushandler.h"
#include "lib/ebus/message.h"
#include "lib/utils/arg.h"
namespace ebusd {
@@ -49,10 +49,10 @@ enum scanStatus_t {
/**
* Helper function for getting the argp definition for all known @a DataHandler instances.
* @return a pointer to the argp_child structure, or nullptr.
* Helper function for getting the arg definition for all known @a DataHandler instances.
* @return a pointer to the child argument options, or nullptr.
*/
const struct argp_child* datahandler_getargs();
const argParseChildOpt* datahandler_getargs();
/**
* Registration function that is called once during initialization.
+14 -17
View File
@@ -52,9 +52,9 @@ using std::dec;
#define O_VAR (O_INT-1)
/** the definition of the KNX arguments. */
static const struct argp_option g_knx_argp_options[] = {
static const argDef g_knx_argDefs[] = {
{nullptr, 0, nullptr, 0, "KNX options:", 1 },
{"knxurl", O_URL, "URL", OPTION_ARG_OPTIONAL, "URL to open (i.e. \"[multicast][@interface]\" for KNXnet/IP"
{"knxurl", O_URL, "URL", af_optional, "URL to open (i.e. \"[multicast][@interface]\" for KNXnet/IP"
#ifdef HAVE_KNXD
" or \"ip:host[:port]\" / \"local:/socketpath\" for knxd"
#endif
@@ -79,11 +79,11 @@ static vector<string>* g_integrationVars = nullptr; //!< the integration settin
/**
* The KNX argument parsing function.
* @param key the key from @a g_knx_argp_options.
* @param key the key from @a g_knx_arg_options.
* @param arg the option argument, or nullptr.
* @param state the parsing state.
*/
static error_t knx_parse_opt(int key, char *arg, struct argp_state *state) {
static int knx_parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
result_t result;
unsigned int value;
switch (key) {
@@ -94,12 +94,12 @@ static error_t knx_parse_opt(int key, char *arg, struct argp_state *state) {
case O_AGR: // --knxrage=5
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid knxrage value");
argParseError(parseOpt, "invalid knxrage value");
return EINVAL;
}
value = parseInt(arg, 10, 0, 99999999, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid knxrage");
argParseError(parseOpt, "invalid knxrage");
return EINVAL;
}
g_maxReadAge = value;
@@ -107,12 +107,12 @@ static error_t knx_parse_opt(int key, char *arg, struct argp_state *state) {
case O_AGW: // --knxwage=5
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid knxwage value");
argParseError(parseOpt, "invalid knxwage value");
return EINVAL;
}
value = parseInt(arg, 10, 0, 99999999, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid knxwage");
argParseError(parseOpt, "invalid knxwage");
return EINVAL;
}
g_maxWriteAge = value;
@@ -120,7 +120,7 @@ static error_t knx_parse_opt(int key, char *arg, struct argp_state *state) {
case O_INT: // --knxint=/etc/ebusd/knx.cfg
if (arg == nullptr || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid knxint file");
argParseError(parseOpt, "invalid knxint file");
return EINVAL;
}
g_integrationFile = arg;
@@ -129,7 +129,7 @@ static error_t knx_parse_opt(int key, char *arg, struct argp_state *state) {
case O_VAR: // --knxvar=NAME=VALUE[,NAME=VALUE]*
{
if (arg == nullptr || arg[0] == 0 || !strchr(arg, '=')) {
argp_error(state, "invalid knxvar");
argParseError(parseOpt, "invalid knxvar");
return EINVAL;
}
if (!g_integrationVars) {
@@ -143,18 +143,15 @@ static error_t knx_parse_opt(int key, char *arg, struct argp_state *state) {
}
default:
return ARGP_ERR_UNKNOWN;
return ESRCH;
}
return 0;
}
static const struct argp g_knx_argp = { g_knx_argp_options, knx_parse_opt, nullptr, nullptr, nullptr, nullptr,
nullptr };
static const struct argp_child g_knx_argp_child = {&g_knx_argp, 0, "", 1};
static const argParseChildOpt g_knx_arg_child = { g_knx_argDefs, knx_parse_opt };
const struct argp_child* knxhandler_getargs() {
return &g_knx_argp_child;
const argParseChildOpt* knxhandler_getargs() {
return &g_knx_arg_child;
}
bool knxhandler_register(UserInfo* userInfo, BusHandler* busHandler, MessageMap* messages,
+4 -3
View File
@@ -29,6 +29,7 @@
#include "lib/ebus/message.h"
#include "lib/ebus/stringhelper.h"
#include "lib/knx/knx.h"
#include "lib/utils/arg.h"
namespace ebusd {
@@ -41,10 +42,10 @@ using std::string;
using std::vector;
/**
* Helper function for getting the argp definition for KNX.
* @return a pointer to the argp_child structure.
* Helper function for getting the arg definition for KNX.
* @return a pointer to the child argument options, or nullptr.
*/
const struct argp_child* knxhandler_getargs();
const argParseChildOpt* knxhandler_getargs();
/**
* Registration function that is called once during initialization.
+81 -76
View File
@@ -23,7 +23,6 @@
#include "ebusd/main.h"
#include <dirent.h>
#include <sys/stat.h>
#include <argp.h>
#include <csignal>
#include <iostream>
#include <algorithm>
@@ -32,17 +31,11 @@
#include <vector>
#include "ebusd/mainloop.h"
#include "ebusd/network.h"
#include "lib/utils/arg.h"
#include "lib/utils/log.h"
#include "lib/utils/httpclient.h"
#include "ebusd/scan.h"
/** the version string of the program. */
const char *argp_program_version = "" PACKAGE_STRING "." REVISION "";
/** the report bugs to address of the program. */
const char *argp_program_bug_address = "" PACKAGE_BUGREPORT "";
namespace ebusd {
using std::dec;
@@ -146,10 +139,6 @@ static string s_configPath = CONFIG_PATH;
/** whether scanConfig or configPath were set by arguments. */
static bool s_scanConfigOrPathSet = false;
/** the documentation of the program. */
static const char argpdoc[] =
"A daemon for communication with eBUS heating systems.";
#define O_INISND -2
#define O_DEVLAT (O_INISND-1)
#define O_SCNRET (O_DEVLAT-1)
@@ -187,7 +176,7 @@ static const char argpdoc[] =
#define O_DMPFLU (O_DMPSIZ-1)
/** the definition of the known program arguments. */
static const struct argp_option argpoptions[] = {
static const argDef argDefs[] = {
{nullptr, 0, nullptr, 0, "Device options:", 1 },
{"device", 'd', "DEV", 0, "Use DEV as eBUS device ("
"prefix \"ens:\" for enhanced high speed device or "
@@ -203,7 +192,7 @@ static const struct argp_option argpoptions[] = {
{nullptr, 0, nullptr, 0, "Message configuration options:", 2 },
{"configpath", 'c', "PATH", 0, "Read CSV config files from PATH (local folder or HTTPS URL) ["
CONFIG_PATH "]", 0 },
{"scanconfig", 's', "ADDR", OPTION_ARG_OPTIONAL, "Pick CSV config files matching initial scan ADDR: "
{"scanconfig", 's', "ADDR", af_optional, "Pick CSV config files matching initial scan ADDR: "
"empty for broadcast ident message (default when configpath is not given), "
"\"none\" for no initial scan message, "
"\"full\" for full scan, "
@@ -215,11 +204,11 @@ static const struct argp_option argpoptions[] = {
{"configlang", O_CFGLNG, "LANG", 0,
"Prefer LANG in multilingual configuration files [system default language]", 0 },
{"checkconfig", O_CHKCFG, nullptr, 0, "Check config files, then stop", 0 },
{"dumpconfig", O_DMPCFG, "FORMAT", OPTION_ARG_OPTIONAL,
{"dumpconfig", O_DMPCFG, "FORMAT", af_optional,
"Check and dump config files in FORMAT (\"json\" or \"csv\"), then stop", 0 },
{"dumpconfigto", O_DMPCTO, "FILE", 0, "Dump config files to FILE", 0 },
{"pollinterval", O_POLINT, "SEC", 0, "Poll for data every SEC seconds (0=disable) [5]", 0 },
{"inject", 'i', "stop", OPTION_ARG_OPTIONAL, "Inject remaining arguments as already seen messages (e.g. "
{"inject", 'i', "stop", af_optional, "Inject remaining arguments as already seen messages (e.g. "
"\"FF08070400/0AB5454850303003277201\"), optionally stop afterwards", 0 },
#ifdef HAVE_SSL
{"cafile", O_CAFILE, "FILE", 0, "Use CA FILE for checking certificates (uses defaults,"
@@ -261,7 +250,7 @@ static const struct argp_option argpoptions[] = {
" [notice]", 0 },
{nullptr, 0, nullptr, 0, "Raw logging options:", 6 },
{"lograwdata", O_RAW, "bytes", OPTION_ARG_OPTIONAL,
{"lograwdata", O_RAW, "bytes", af_optional,
"Log messages or all received/sent bytes on the bus", 0 },
{"lograwdatafile", O_RAWFIL, "FILE", 0, "Write raw log to FILE [" PACKAGE_LOGFILE "]", 0 },
{"lograwdatasize", O_RAWSIZ, "SIZE", 0, "Make raw log file no larger than SIZE kB [100]", 0 },
@@ -277,12 +266,12 @@ static const struct argp_option argpoptions[] = {
/**
* The program argument parsing function.
* @param key the key from @a argpoptions.
* @param key the key from @a argDefs.
* @param arg the option argument, or nullptr.
* @param state the parsing state.
* @param parseOpt the parse options.
*/
error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct options *opt = (struct options*)state->input;
static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
struct options *opt = (struct options*)parseOpt->userArg;
result_t result = RESULT_OK;
unsigned int value;
@@ -290,7 +279,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
// Device options:
case 'd': // --device=/dev/ttyUSB0
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid device");
argParseError(parseOpt, "invalid device");
return EINVAL;
}
opt->device = arg;
@@ -307,7 +296,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_DEVLAT: // --latency=10
value = parseInt(arg, 10, 0, 200000, &result); // backwards compatible (micros)
if (result != RESULT_OK || (value <= 1000 && value > 200)) { // backwards compatible (micros)
argp_error(state, "invalid latency");
argParseError(parseOpt, "invalid latency");
return EINVAL;
}
opt->extraLatency = value > 1000 ? value/1000 : value; // backwards compatible (micros)
@@ -316,7 +305,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
// Message configuration options:
case 'c': // --configpath=https://cfg.ebusd.eu/
if (arg == nullptr || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid configpath");
argParseError(parseOpt, "invalid configpath");
return EINVAL;
}
s_configPath = arg;
@@ -336,7 +325,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
} else {
auto address = (symbol_t)parseInt(arg, 16, 0x00, 0xff, &result);
if (result != RESULT_OK || !isValidAddress(address)) {
argp_error(state, "invalid initial scan address");
argParseError(parseOpt, "invalid initial scan address");
return EINVAL;
}
if (isMaster(address)) {
@@ -353,7 +342,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_SCNRET: // --scanretries=10
value = parseInt(arg, 10, 0, 100, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid scanretries");
argParseError(parseOpt, "invalid scanretries");
return EINVAL;
}
opt->scanRetries = value;
@@ -371,14 +360,14 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
} else if (strcmp("json", arg) == 0) {
opt->dumpConfig = OF_DEFINITION | OF_NAMES | OF_UNITS | OF_COMMENTS | OF_VALUENAME | OF_ALL_ATTRS | OF_JSON;
} else {
argp_error(state, "invalid dumpconfig");
argParseError(parseOpt, "invalid dumpconfig");
return EINVAL;
}
opt->checkConfig = true;
break;
case O_DMPCTO: // --dumpconfigto=FILE
if (!arg || arg[0] == 0) {
argp_error(state, "invalid dumpconfigto");
argParseError(parseOpt, "invalid dumpconfigto");
return EINVAL;
}
opt->dumpConfigTo = arg;
@@ -386,7 +375,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_POLINT: // --pollinterval=5
value = parseInt(arg, 10, 0, 3600, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid pollinterval");
argParseError(parseOpt, "invalid pollinterval");
return EINVAL;
}
opt->pollInterval = value;
@@ -407,7 +396,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
{
auto address = (symbol_t)parseInt(arg, 16, 0, 0xff, &result);
if (result != RESULT_OK || !isMaster(address)) {
argp_error(state, "invalid address");
argParseError(parseOpt, "invalid address");
return EINVAL;
}
opt->address = address;
@@ -419,7 +408,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_ACQTIM: // --acquiretimeout=10
value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros)
if (result != RESULT_OK || (value <= 1000 && value > 100)) { // backwards compatible (micros)
argp_error(state, "invalid acquiretimeout");
argParseError(parseOpt, "invalid acquiretimeout");
return EINVAL;
}
opt->acquireTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros)
@@ -427,7 +416,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_ACQRET: // --acquireretries=3
value = parseInt(arg, 10, 0, 10, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid acquireretries");
argParseError(parseOpt, "invalid acquireretries");
return EINVAL;
}
opt->acquireRetries = value;
@@ -435,7 +424,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_SNDRET: // --sendretries=2
value = parseInt(arg, 10, 0, 10, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid sendretries");
argParseError(parseOpt, "invalid sendretries");
return EINVAL;
}
opt->sendRetries = value;
@@ -443,7 +432,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_RCVTIM: // --receivetimeout=25
value = parseInt(arg, 10, 1, 100000, &result); // backwards compatible (micros)
if (result != RESULT_OK || (value <= 1000 && value > 100)) { // backwards compatible (micros)
argp_error(state, "invalid receivetimeout");
argParseError(parseOpt, "invalid receivetimeout");
return EINVAL;
}
opt->receiveTimeout = value > 1000 ? value/1000 : value; // backwards compatible (micros)
@@ -451,7 +440,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_MASCNT: // --numbermasters=0
value = parseInt(arg, 10, 0, 25, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid numbermasters");
argParseError(parseOpt, "invalid numbermasters");
return EINVAL;
}
opt->masterCount = value;
@@ -463,14 +452,14 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
// Daemon options:
case O_ACLDEF: // --accesslevel=*
if (arg == nullptr) {
argp_error(state, "invalid accesslevel");
argParseError(parseOpt, "invalid accesslevel");
return EINVAL;
}
opt->accessLevel = arg;
break;
case O_ACLFIL: // --aclfile=/etc/ebusd/acl
if (arg == nullptr || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid aclfile");
argParseError(parseOpt, "invalid aclfile");
return EINVAL;
}
opt->aclFile = arg;
@@ -486,7 +475,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
break;
case O_PIDFIL: // --pidfile=/var/run/ebusd.pid
if (arg == nullptr || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid pidfile");
argParseError(parseOpt, "invalid pidfile");
return EINVAL;
}
opt->pidFile = arg;
@@ -494,7 +483,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case 'p': // --port=8888
value = parseInt(arg, 10, 1, 65535, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid port");
argParseError(parseOpt, "invalid port");
return EINVAL;
}
opt->port = (uint16_t)value;
@@ -505,21 +494,21 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_HTTPPT: // --httpport=0
value = parseInt(arg, 10, 1, 65535, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid httpport");
argParseError(parseOpt, "invalid httpport");
return EINVAL;
}
opt->httpPort = (uint16_t)value;
break;
case O_HTMLPA: // --htmlpath=/var/ebusd/html
if (arg == nullptr || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid htmlpath");
argParseError(parseOpt, "invalid htmlpath");
return EINVAL;
}
opt->htmlPath = arg;
break;
case O_UPDCHK: // --updatecheck=on
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid updatecheck");
argParseError(parseOpt, "invalid updatecheck");
return EINVAL;
}
if (strcmp("on", arg) == 0) {
@@ -527,7 +516,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
} else if (strcmp("off", arg) == 0) {
opt->updateCheck = false;
} else {
argp_error(state, "invalid updatecheck");
argParseError(parseOpt, "invalid updatecheck");
return EINVAL;
}
break;
@@ -535,7 +524,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
// Log options:
case 'l': // --logfile=/var/log/ebusd.log
if (arg == nullptr || strcmp("/", arg) == 0) {
argp_error(state, "invalid logfile");
argParseError(parseOpt, "invalid logfile");
return EINVAL;
}
opt->logFile = arg;
@@ -546,23 +535,23 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
if (pos == nullptr) {
pos = strchr(arg, ' ');
if (pos == nullptr) {
argp_error(state, "invalid log");
argParseError(parseOpt, "invalid log");
return EINVAL;
}
}
*pos = 0;
int facilities = parseLogFacilities(arg);
if (facilities == -1) {
argp_error(state, "invalid log: areas");
argParseError(parseOpt, "invalid log: areas");
return EINVAL;
}
LogLevel level = parseLogLevel(pos + 1);
if (level == ll_COUNT) {
argp_error(state, "invalid log: level");
argParseError(parseOpt, "invalid log: level");
return EINVAL;
}
if (opt->logAreas != -1 || opt->logLevel != ll_COUNT) {
argp_error(state, "invalid log (combined with logareas or loglevel)");
argParseError(parseOpt, "invalid log (combined with logareas or loglevel)");
return EINVAL;
}
setFacilitiesLogLevel(facilities, level);
@@ -573,11 +562,11 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
{
int facilities = parseLogFacilities(arg);
if (facilities == -1) {
argp_error(state, "invalid logareas");
argParseError(parseOpt, "invalid logareas");
return EINVAL;
}
if (opt->multiLog) {
argp_error(state, "invalid logareas (combined with log)");
argParseError(parseOpt, "invalid logareas (combined with log)");
return EINVAL;
}
opt->logAreas = facilities;
@@ -587,11 +576,11 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
{
LogLevel logLevel = parseLogLevel(arg);
if (logLevel == ll_COUNT) {
argp_error(state, "invalid loglevel");
argParseError(parseOpt, "invalid loglevel");
return EINVAL;
}
if (opt->multiLog) {
argp_error(state, "invalid loglevel (combined with log)");
argParseError(parseOpt, "invalid loglevel (combined with log)");
return EINVAL;
}
opt->logLevel = logLevel;
@@ -604,7 +593,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
break;
case O_RAWFIL: // --lograwdatafile=/var/log/ebusd.log
if (arg == nullptr || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid lograwdatafile");
argParseError(parseOpt, "invalid lograwdatafile");
return EINVAL;
}
opt->logRawFile = arg;
@@ -612,7 +601,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_RAWSIZ: // --lograwdatasize=100
value = parseInt(arg, 10, 1, 1000000, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid lograwdatasize");
argParseError(parseOpt, "invalid lograwdatasize");
return EINVAL;
}
opt->logRawSize = value;
@@ -625,7 +614,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
break;
case O_DMPFIL: // --dumpfile=/tmp/ebusd_dump.bin
if (arg == nullptr || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid dumpfile");
argParseError(parseOpt, "invalid dumpfile");
return EINVAL;
}
opt->dumpFile = arg;
@@ -633,7 +622,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case O_DMPSIZ: // --dumpsize=100
value = parseInt(arg, 10, 1, 1000000, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid dumpsize");
argParseError(parseOpt, "invalid dumpsize");
return EINVAL;
}
opt->dumpSize = value;
@@ -642,33 +631,27 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
opt->dumpFlush = true;
break;
case ARGP_KEY_ARG:
if (opt->injectMessages || (opt->checkConfig && opt->scanConfig)) {
return ARGP_ERR_UNKNOWN;
}
argp_error(state, "invalid arguments starting with \"%s\"", arg);
return EINVAL;
default:
return ARGP_ERR_UNKNOWN;
return ESRCH;
}
// check for invalid arg combinations
if (opt->readOnly && (opt->answer || opt->generateSyn || opt->initialSend
|| (opt->scanConfig && opt->initialScan != ESC))) {
argp_error(state, "cannot combine readonly with answer/generatesyn/initsend/scanconfig");
argParseError(parseOpt, "cannot combine readonly with answer/generatesyn/initsend/scanconfig");
return EINVAL;
}
if (opt->scanConfig && opt->pollInterval == 0) {
argp_error(state, "scanconfig without polling may lead to invalid files included for certain products!");
argParseError(parseOpt, "scanconfig without polling may lead to invalid files included for certain products!");
return EINVAL;
}
if (opt->injectMessages && (opt->checkConfig || opt->dumpConfig)) {
argp_error(state, "cannot combine inject with checkconfig/dumpconfig");
argParseError(parseOpt, "cannot combine inject with checkconfig/dumpconfig");
return EINVAL;
}
return 0;
}
void shutdown(bool error = false);
void daemonize() {
@@ -823,8 +806,17 @@ void signalHandler(int sig) {
* @return the exit code.
*/
int main(int argc, char* argv[], char* envp[]) {
struct argp aargp = { argpoptions, parse_opt, nullptr, argpdoc, datahandler_getargs(), nullptr, nullptr };
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
const argParseOpt parseOpt = {
argDefs,
parse_opt,
0,
"" PACKAGE_NAME,
"[INJECT...]",
"A daemon for communication with eBUS heating systems.",
"Report bugs to " PACKAGE_BUGREPORT " .",
datahandler_getargs(),
&s_opt
};
char envname[32] = "--"; // needs to cover at least max length of any option name plus "--"
char* envopt = envname+2;
@@ -849,7 +841,7 @@ int main(int argc, char* argv[], char* envp[]) {
// ignore those defined in Dockerfile, EBUSD_OPTS, those with final args, and interactive ones
continue;
}
char* envargv[] = {envname, pos+1};
char* envargv[] = {argv[0], envname, pos+1};
int cnt = pos[1] ? 2 : 1;
if (pos[1] && strlen(*env) < sizeof(envname)-3
&& (strcmp(envopt, "scanconfig") == 0 || strcmp(envopt, "lograwdata") == 0)) {
@@ -858,8 +850,7 @@ int main(int argc, char* argv[], char* envp[]) {
strcat(envopt, pos);
}
int idx = -1;
s_opt.injectMessages = true; // for skipping unknown values via ARGP_ERR_UNKNOWN
error_t err = argp_parse(&aargp, cnt, envargv, ARGP_PARSE_ARGV0|ARGP_SILENT|ARGP_IN_ORDER, &idx, &s_opt);
int err = argParse(&parseOpt, 1+cnt, envargv, &idx);
if (err != 0 && idx == -1) { // ignore args for non-arg boolean options
if (err == ESRCH) { // special value to abort immediately
logWrite(lf_main, ll_error, "invalid argument in env: %s", *env); // force logging on exit
@@ -867,13 +858,27 @@ int main(int argc, char* argv[], char* envp[]) {
}
logWrite(lf_main, ll_error, "invalid/unknown argument in env (ignored): %s", *env); // force logging
}
s_opt.injectMessages = false; // restore (was not parsed from cmdline args yet)
}
int arg_index = -1;
if (argp_parse(&aargp, argc, argv, ARGP_IN_ORDER, &arg_index, &s_opt) != 0) {
logWrite(lf_main, ll_error, "invalid arguments"); // force logging on exit
return EINVAL;
switch (argParse(&parseOpt, argc, argv, &arg_index)) {
case 0: // OK
break;
case '?': // help printed
return 0;
case 'V':
printf("" PACKAGE_STRING "." REVISION "\n");
return 0;
default:
logWrite(lf_main, ll_error, "invalid arguments"); // force logging on exit
return EINVAL;
}
if (arg_index >= 0) {
if (!s_opt.injectMessages && !(s_opt.checkConfig && s_opt.scanConfig)) {
fprintf(stderr, "invalid arguments starting with \"%s\"", argv[arg_index]);
return EINVAL;
}
}
if (s_opt.logAreas != -1 || s_opt.logLevel != ll_COUNT) {
+28 -28
View File
@@ -56,7 +56,7 @@ using std::dec;
#define O_VERB (O_INSE+1)
/** the definition of the MQTT arguments. */
static const struct argp_option g_mqtt_argp_options[] = {
static const argDef g_mqtt_argDefs[] = {
{nullptr, 0, nullptr, 0, "MQTT options:", 1 },
{"mqtthost", O_HOST, "HOST", 0, "Connect to MQTT broker on HOST [localhost]", 0 },
{"mqttport", O_PORT, "PORT", 0, "Connect to MQTT broker on PORT (usually 1883), 0 to disable [0]", 0 },
@@ -72,7 +72,7 @@ static const struct argp_option g_mqtt_argp_options[] = {
{"mqttqos", O_PQOS, "QOS", 0, "Set the QoS value for all topics (0-2) [0]", 0 },
{"mqttint", O_INTF, "FILE", 0, "Read MQTT integration settings from FILE (no default)", 0 },
{"mqttvar", O_IVAR, "NAME=VALUE[,...]", 0, "Add variable(s) to the read MQTT integration settings", 0 },
{"mqttjson", O_JSON, "short", OPTION_ARG_OPTIONAL,
{"mqttjson", O_JSON, "short", af_optional,
"Publish in JSON format instead of strings, optionally in short (value directly below field key)", 0 },
{"mqttverbose", O_VERB, nullptr, 0, "Publish all available attributes", 0 },
#if (LIBMOSQUITTO_VERSION_NUMBER >= 1003001)
@@ -144,17 +144,17 @@ void splitFields(const string& str, vector<string>* row);
/**
* The MQTT argument parsing function.
* @param key the key from @a g_mqtt_argp_options.
* @param key the key from @a g_mqtt_argDefs.
* @param arg the option argument, or nullptr.
* @param state the parsing state.
*/
static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
static int mqtt_parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
result_t result = RESULT_OK;
unsigned int value;
switch (key) {
case O_HOST: // --mqtthost=localhost
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid mqtthost");
argParseError(parseOpt, "invalid mqtthost");
return EINVAL;
}
g_host = arg;
@@ -163,7 +163,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_PORT: // --mqttport=1883
value = parseInt(arg, 10, 1, 65535, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid mqttport");
argParseError(parseOpt, "invalid mqttport");
return EINVAL;
}
g_port = (uint16_t)value;
@@ -171,7 +171,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_CLID: // --mqttclientid=clientid
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid mqttclientid");
argParseError(parseOpt, "invalid mqttclientid");
return EINVAL;
}
g_clientId = arg;
@@ -179,7 +179,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_USER: // --mqttuser=username
if (arg == nullptr) {
argp_error(state, "invalid mqttuser");
argParseError(parseOpt, "invalid mqttuser");
return EINVAL;
}
g_username = arg;
@@ -187,7 +187,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_PASS: // --mqttpass=password
if (arg == nullptr) {
argp_error(state, "invalid mqttpass");
argParseError(parseOpt, "invalid mqttpass");
return EINVAL;
}
g_password = replaceSecret(arg);
@@ -196,21 +196,21 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_TOPI: // --mqtttopic=ebusd
{
if (arg == nullptr || arg[0] == 0 || arg[0] == '/' || strchr(arg, '+') || arg[strlen(arg)-1] == '/') {
argp_error(state, "invalid mqtttopic");
argParseError(parseOpt, "invalid mqtttopic");
return EINVAL;
}
char *pos = strchr(arg, '#');
if (pos && (pos == arg || pos[1])) { // allow # only at very last position (to indicate not using any default)
argp_error(state, "invalid mqtttopic");
argParseError(parseOpt, "invalid mqtttopic");
return EINVAL;
}
if (g_topic) {
argp_error(state, "duplicate mqtttopic");
argParseError(parseOpt, "duplicate mqtttopic");
return EINVAL;
}
StringReplacer replacer;
if (!replacer.parse(arg, true)) {
argp_error(state, "malformed mqtttopic");
argParseError(parseOpt, "malformed mqtttopic");
return ESRCH; // abort in any case due to the above potentially being destructive
}
g_topic = arg;
@@ -219,7 +219,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_GTOP: // --mqttglobal=global/
if (arg == nullptr || strchr(arg, '+') || strchr(arg, '#')) {
argp_error(state, "invalid mqttglobal");
argParseError(parseOpt, "invalid mqttglobal");
return EINVAL;
}
g_globalTopic = arg;
@@ -232,7 +232,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_PQOS: // --mqttqos=0
value = parseInt(arg, 10, 0, 2, &result);
if (result != RESULT_OK) {
argp_error(state, "invalid mqttqos value");
argParseError(parseOpt, "invalid mqttqos value");
return EINVAL;
}
g_qos = static_cast<signed>(value);
@@ -240,7 +240,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_INTF: // --mqttint=/etc/ebusd/mqttint.cfg
if (arg == nullptr || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid mqttint file");
argParseError(parseOpt, "invalid mqttint file");
return EINVAL;
}
g_integrationFile = arg;
@@ -248,7 +248,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_IVAR: // --mqttvar=NAME=VALUE[,NAME=VALUE]*
if (arg == nullptr || arg[0] == 0 || !strchr(arg, '=')) {
argp_error(state, "invalid mqttvar");
argParseError(parseOpt, "invalid mqttvar");
return EINVAL;
}
if (!g_integrationVars) {
@@ -277,7 +277,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
#if (LIBMOSQUITTO_VERSION_NUMBER >= 1004001)
case O_VERS: // --mqttversion=3.1.1
if (arg == nullptr || arg[0] == 0 || (strcmp(arg, "3.1") != 0 && strcmp(arg, "3.1.1") != 0)) {
argp_error(state, "invalid mqttversion");
argParseError(parseOpt, "invalid mqttversion");
return EINVAL;
}
g_version = strcmp(arg, "3.1.1") == 0 ? MQTT_PROTOCOL_V311 : MQTT_PROTOCOL_V31;
@@ -295,7 +295,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
#if (LIBMOSQUITTO_MAJOR >= 1)
case O_CAFI: // --mqttca=file or --mqttca=dir/
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid mqttca");
argParseError(parseOpt, "invalid mqttca");
return EINVAL;
}
if (arg[strlen(arg)-1] == '/') {
@@ -309,7 +309,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_CERT: // --mqttcert=CERTFILE
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid mqttcert");
argParseError(parseOpt, "invalid mqttcert");
return EINVAL;
}
g_certfile = arg;
@@ -317,7 +317,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_KEYF: // --mqttkey=KEYFILE
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid mqttkey");
argParseError(parseOpt, "invalid mqttkey");
return EINVAL;
}
g_keyfile = arg;
@@ -325,7 +325,7 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
case O_KEPA: // --mqttkeypass=PASSWORD
if (arg == nullptr) {
argp_error(state, "invalid mqttkeypass");
argParseError(parseOpt, "invalid mqttkeypass");
return EINVAL;
}
g_keypass = replaceSecret(arg);
@@ -336,18 +336,18 @@ static error_t mqtt_parse_opt(int key, char *arg, struct argp_state *state) {
#endif
default:
return ARGP_ERR_UNKNOWN;
return EINVAL;
}
return 0;
}
static const struct argp g_mqtt_argp = { g_mqtt_argp_options, mqtt_parse_opt, nullptr, nullptr, nullptr, nullptr,
nullptr };
static const struct argp_child g_mqtt_argp_child = {&g_mqtt_argp, 0, "", 1};
static const argParseChildOpt g_mqtt_arg_child = {
g_mqtt_argDefs, mqtt_parse_opt
};
const struct argp_child* mqtthandler_getargs() {
return &g_mqtt_argp_child;
const argParseChildOpt* mqtthandler_getargs() {
return &g_mqtt_arg_child;
}
bool check(int code, const char* method) {
+4 -3
View File
@@ -29,6 +29,7 @@
#include "ebusd/bushandler.h"
#include "lib/ebus/message.h"
#include "lib/ebus/stringhelper.h"
#include "lib/utils/arg.h"
namespace ebusd {
@@ -42,10 +43,10 @@ using std::string;
using std::vector;
/**
* Helper function for getting the argp definition for MQTT.
* @return a pointer to the argp_child structure.
* Helper function for getting the arg definition for MQTT.
* @return a pointer to the child argument options, or nullptr.
*/
const struct argp_child* mqtthandler_getargs();
const argParseChildOpt* mqtthandler_getargs();
/**
* Registration function that is called once during initialization.
+1
View File
@@ -1,6 +1,7 @@
add_definitions(-Wconversion)
set(libutils_a_SOURCES
arg.h arg.cpp
log.h log.cpp
tcpsocket.h tcpsocket.cpp
thread.h thread.cpp
+346
View File
@@ -0,0 +1,346 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2023 John Baier <ebusd@ebusd.eu>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lib/utils/arg.h"
#include <getopt.h>
#include <memory.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
namespace ebusd {
#define isAlpha(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
void calcCounts(const argDef *argDefs, int &count, int &shortCharsCount, int &shortOptsCount) {
for (const argDef *arg = argDefs; arg && arg->help; arg++) {
if (!arg->name) {
continue;
}
count++;
if (!isAlpha(arg->key)) {
continue;
}
shortCharsCount++;
shortOptsCount++;
if (arg->valueName) {
shortOptsCount++;
if (arg->flags & af_optional) {
shortOptsCount++;
}
}
}
}
void buildOpts(const argDef *argDefs, int &count, int &shortCharsCount, int &shortOptsCount, struct option *longOpts, char *shortChars, int *shortIndexes, char *shortOpts, int argDefIdx) {
struct option *opt = longOpts+count;
for (const argDef *arg = argDefs; arg && arg->help; arg++, argDefIdx++) {
if (!arg->name) {
continue;
}
opt->name = arg->name;
opt->has_arg = arg->valueName ? ((arg->flags & af_optional) ? optional_argument : required_argument) : no_argument;
opt->flag = nullptr;
opt->val = argDefIdx;
if (isAlpha(arg->key)) {
shortChars[shortCharsCount] = (char)arg->key;
shortIndexes[shortCharsCount++] = count;
shortOpts[shortOptsCount++] = (char)arg->key;
if (arg->valueName) {
shortOpts[shortOptsCount++] = ':';
if (arg->flags & af_optional) {
shortOpts[shortOptsCount++] = ':';
}
}
}
opt++;
count++;
}
}
static const argDef endArgDef = {nullptr, 0, nullptr, 0, nullptr, 0 };
static const argDef helpArgDef = {"help", '?', nullptr, 0, "Give this help list", 0 };
static const argDef helpArgDefs[] = {
helpArgDef,
endArgDef
};
static const argDef versionArgDef = {"version", 'V', nullptr, 0, "Print program version", 0 };
static const argDef versionArgDefs[] = {
versionArgDef,
endArgDef
};
int argParse(const argParseOpt *parseOpt, int argc, char **argv, int *argIndex) {
int count = 0, shortCharsCount = 0, shortOptsCount = 0;
if (!(parseOpt->flags & af_noHelp)) {
calcCounts(helpArgDefs, count, shortCharsCount, shortOptsCount);
}
if (!(parseOpt->flags & af_noVersion)) {
calcCounts(versionArgDefs, count, shortCharsCount, shortOptsCount);
}
calcCounts(parseOpt->argDefs, count, shortCharsCount, shortOptsCount);
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
calcCounts(child->argDefs, count, shortCharsCount, shortOptsCount);
}
struct option *longOpts = (struct option*)calloc(count+1, sizeof(struct option)); // room for EOF
char *shortChars = (char*)calloc(shortCharsCount+1, sizeof(char)); // room for \0
int *shortIndexes = (int*)calloc(shortCharsCount, sizeof(int));
char *shortOpts = (char*)calloc(2+shortOptsCount+1, sizeof(char)); // room for +, :, and \0
count = 0;
shortCharsCount = 0;
shortOptsCount = 0;
shortOpts[shortOptsCount++] = '+'; // posix mode to stop at first non-option
shortOpts[shortOptsCount++] = ':'; // return ':' for missing option
if (!(parseOpt->flags & af_noHelp)) {
buildOpts(helpArgDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars, shortIndexes, shortOpts, 0xff00);
}
if (!(parseOpt->flags & af_noVersion)) {
buildOpts(versionArgDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars, shortIndexes, shortOpts, 0xff01);
}
buildOpts(parseOpt->argDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars, shortIndexes, shortOpts, 0);
int children = 0;
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
buildOpts(child->argDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars, shortIndexes, shortOpts, 0x100*(++children));
}
optind = 1; // setting to 0 does not work
int c = 0, longIdx = -1, ret = 0;
while ((c = getopt_long(argc, argv, shortOpts, longOpts, &longIdx)) != -1) {
if (c == '?') {
// unknown option or help
if (optopt != '?') {
ret = '!';
fprintf(stderr, "invalid argument %s\n", argv[optind - 1]);
} else {
ret = c;
}
break;
}
if (c == ':') {
// missing option
fprintf(stderr, "missing argument to %s\n", argv[optind - 1]);
ret = c;
break;
}
if (isAlpha(c)) {
// short name
int idx = (int)(strchr(shortChars, c) - shortChars);
if (idx >= 0 && idx < shortCharsCount) {
longIdx = shortIndexes[idx];
} else {
longIdx = -1;
}
} else if (c >= 0 && longIdx < 0) {
longIdx = c;
}
if (longIdx < 0 || longIdx >= count) {
ret = '!'; // error
break;
}
int val = longOpts[longIdx].val;
if (val == 0xff00) { // help
ret = '?';
break;
}
if (val == 0xff01) { // version
ret = 'V';
break;
}
const argDef *argDefs;
parse_function_t parser;
if (val & 0xff00) {
const argParseChildOpt *child = parseOpt->childOpts + ((val>>8)-1);
argDefs = child->argDefs;
parser = child->parser;
} else {
argDefs = parseOpt->argDefs;
parser = parseOpt->parser;
}
const argDef *arg = argDefs + (val & 0xff);
c = parser(arg->key, optarg, parseOpt);
if (c != 0) {
ret = c;
break;
}
}
if (ret == '?') {
argHelp(parseOpt);
} else if (argIndex && optind < argc) {
*argIndex = optind;
}
free(longOpts);
free(shortChars);
free(shortOpts);
return ret;
}
#define MIN_INDENT 18
#define MAX_INDENT 29
#define MAX_BREAK 79
void wrap(const char* str, size_t pos, size_t indent) {
const char* end = strchr(str, 0);
const char* eol = strchr(str, '\n');
char buf[MAX_BREAK + 1];
bool first = true;
while (*str && str < end) {
if (!first) {
if (indent) {
printf("%*c", (int)indent, ' ');
}
pos = indent;
}
// start from max position backwards to find a break char
size_t cnt = MAX_BREAK - pos;
if (eol && eol < str) {
eol = strchr(str, '\n');
}
if (eol && eol < str + cnt) {
// EOL is before latest possible break
cnt = eol - str;
} else if (end < str + cnt) {
cnt = end - str;
}
for (; cnt > 0; cnt--) {
char ch = str[cnt];
if (ch == ' ' || ch == '\n' || ch == 0) {
// break found
buf[0] = 0;
strncat(buf, str, cnt);
printf("%s\n", buf);
str += cnt;
if (*str) {
str++;
}
break; // restart
}
}
if (cnt == 0 && *str) {
// final
printf("%s\n", str);
break;
}
first = false;
}
}
size_t calcIndent(const argDef *argDefs) {
size_t indent = 0;
for (const argDef *arg = argDefs; arg && arg->help; arg++) {
if (!arg->name) {
continue;
}
// e.g. " -d, --device=DEV Use DEV..."
size_t length = 2 + 3 + 3 + strlen(arg->name) + 2;
if (arg->valueName) {
length += 1 + strlen(arg->valueName);
if (arg->flags & af_optional) {
length += 2;
}
}
if (length > indent) {
indent = length;
if (indent > MAX_INDENT) {
return indent;
}
}
}
return indent;
}
void printArgs(const argDef *argDefs, size_t indent) {
for (const argDef *arg = argDefs; arg && arg->help; arg++) {
if (!arg->name) {
if (*arg->help) {
printf("\n %s\n", arg->help);
} else {
printf("\n");
}
continue;
}
printf(" ");
if (isAlpha(arg->key) || arg->key == '?') {
printf("-%c,", arg->key);
} else {
printf(" ");
}
printf(" --%s", arg->name);
size_t taken = 2 + 3 + 3 + strlen(arg->name);
if (arg->valueName) {
taken += 1 + strlen(arg->valueName);
if (arg->flags & af_optional) {
printf("[=%s]", arg->valueName);
taken += 2;
} else {
printf("=%s", arg->valueName);
}
}
if (taken > indent) {
printf(" ");
wrap(arg->help, taken+1, indent);
} else {
printf("%*c", (int)(indent - taken), ' ');
wrap(arg->help, indent, indent);
}
}
}
void argHelp(const argParseOpt *parseOpt) {
size_t indent = calcIndent(parseOpt->argDefs);
if (indent < MAX_INDENT) {
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
size_t childIndent = calcIndent(child->argDefs);
if (childIndent > indent) {
indent = childIndent;
if (indent > MAX_INDENT) {
break;
}
}
}
}
if (indent > MAX_INDENT) {
indent = MAX_INDENT;
} else if (indent < MIN_INDENT) {
indent = MIN_INDENT;
}
printf("Usage: %s [OPTION...] %s\n",
parseOpt->name,
parseOpt->positional ? parseOpt->positional : ""
);
wrap(parseOpt->help, 0, 0);
printArgs(parseOpt->argDefs, indent);
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
printArgs(child->argDefs, indent);
}
if (!(parseOpt->flags & (af_noHelp|af_noVersion))) {
printf("\n");
if (!(parseOpt->flags & af_noHelp)) {
printArgs(helpArgDefs, indent);
}
if (!(parseOpt->flags & af_noVersion)) {
printArgs(versionArgDefs, indent);
}
}
if (parseOpt->suffix) {
printf("\n");
wrap(parseOpt->suffix, 0, 0);
}
fflush(stdout);
}
} // namespace ebusd
+97
View File
@@ -0,0 +1,97 @@
/*
* ebusd - daemon for communication with eBUS heating systems.
* Copyright (C) 2023 John Baier <ebusd@ebusd.eu>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIB_UTILS_ARGS_H_
#define LIB_UTILS_ARGS_H_
namespace ebusd {
/** \file lib/utils/args.h */
/** the available arg flags. */
enum ArgFlag {
af_optional = 1<<0, //!< optional argument value
af_noHelp = 1<<1, //!< do not include -?/--help option
af_noVersion = 1<<2, //!< do not include -V/--version option
};
/** Definition of a single argument. */
typedef struct argDef {
const char* name; //!< the (long) name of the argument, or nullptr for a group header
int key; //!< the argument key, also used as short name if alphabetic or the question mark
const char* valueName; //!< the optional argument value name
int flags; //!< flags for the argument, bit combination of @a ArgFlag
const char* help; //!< help text (mandatory)
int unused; //!< currently unused (kept for compatibility to argp)
} argDef;
struct argParseOpt;
/**
* Function to be called for each argument.
* @param key the argument key as defined.
* @param arg the argument value, or nullptr.
* @param parseOpt ppointer to the @a argParseOpt structure.
* @return 0 on success, non-zero otherwise.
*/
typedef int (*parse_function_t)(int key, char *arg, const struct argParseOpt *parseOpt);
/** Options for child definitions. */
typedef struct argParseChildOpt {
const argDef *argDefs; //!< pointer to the argument defintions (last one needs to have nullptr help as end sign)
parse_function_t parser; //!< parse function to use
} argParseChildOpt;
/** Options to pass to @a argParse(). */
typedef struct argParseOpt {
const argDef *argDefs; //!< pointer to the argument defintions (last one needs to have nullptr help as end sign)
parse_function_t parser; //!< parse function to use
int flags; //!< flags for the parser, bit combination of @a ArgFlag
const char* name; //!< name of the program parsed
const char* positional; //!< help text for optional positional argument
const char* help; //!< help text for the program (second line of help output)
const char* suffix; //!< optional help suffix text
const argParseChildOpt *childOpts; //!< optional child definitions
void* userArg; //!< optional user argument
} argParseOpt;
/**
* Parse the arguments given in @a argv.
* @param parseOpt pointer to the @a argParseOpt structure.
* @param argc the argument count (including the full program name in index 0).
* @param argv the argument values (including the full program name in index 0).
* @param argIndex optional pointer for storing the index to the first non-argument found in argv.
* @return 0 on success, '!' for an invalid argument value, ':' for a missing argument value,
* '?' when "-?" was given, or the result of the parse function if non-zero.
*/
int argParse(const argParseOpt *parseOpt, int argc, char **argv, int *argIndex);
/**
* Print the help text.
* @param parseOpt ppointer to the @a argParseOpt structure.
*/
void argHelp(const argParseOpt *parseOpt);
/**
* Convenience macro to print an error message to stderr.
*/
#define argParseError(argParseOpt, message) fprintf(stderr, "%s\n", message);
} // namespace ebusd
#endif // LIB_UTILS_ARGS_H_
+5 -3
View File
@@ -1,3 +1,5 @@
add_definitions(-Wno-unused-parameter)
set(ebusctl_SOURCES ebusctl.cpp)
set(ebuspicloader_SOURCES ebuspicloader.cpp intelhex/intelhexclass.cpp)
@@ -7,13 +9,13 @@ include_directories(intelhex)
add_executable(ebusctl ${ebusctl_SOURCES})
add_executable(ebuspicloader ${ebuspicloader_SOURCES})
target_link_libraries(ebusctl utils ebus ${LIB_ARGP} ${ebusctl_LIBS})
target_link_libraries(ebuspicloader utils ${LIB_ARGP} ${ebuspicloader_LIBS})
target_link_libraries(ebusctl utils ebus ${ebusctl_LIBS})
target_link_libraries(ebuspicloader utils ${ebuspicloader_LIBS})
if(WITH_EBUSFEED)
set(ebusfeed_SOURCES ebusfeed.cpp)
add_executable(ebusfeed ${ebusfeed_SOURCES})
target_link_libraries(ebusfeed ebus ${LIB_ARGP} ${ebusfeed_LIBS})
target_link_libraries(ebusfeed utils ebus ${ebusfeed_LIBS})
endif(WITH_EBUSFEED)
install(TARGETS ebusctl ebuspicloader EXPORT ebusd DESTINATION usr/bin)
+35 -34
View File
@@ -20,7 +20,6 @@
# include <config.h>
#endif
#include <argp.h>
#include <string.h>
#ifdef HAVE_PPOLL
# include <poll.h>
@@ -30,6 +29,7 @@
#include <cstdlib>
#include <sstream>
#include <string>
#include "lib/utils/arg.h"
#include "lib/utils/tcpsocket.h"
namespace ebusd {
@@ -62,24 +62,8 @@ static struct options opt = {
0 // argCount
};
/** the version string of the program. */
const char *argp_program_version = "ebusctl of """ PACKAGE_STRING "";
/** the report bugs to address of the program. */
const char *argp_program_bug_address = "" PACKAGE_BUGREPORT "";
/** the documentation of the program. */
static const char argpdoc[] =
"Client for acessing " PACKAGE " via TCP.\n"
"\v"
"If given, send COMMAND together with CMDOPT options to " PACKAGE ".\n"
"Use 'help' as COMMAND for help on available " PACKAGE " commands.";
/** the description of the accepted arguments. */
static char argpargsdoc[] = "\nCOMMAND [CMDOPT...]";
/** the definition of the known program arguments. */
static const struct argp_option argpoptions[] = {
static const argDef argDefs[] = {
{nullptr, 0, nullptr, 0, "Options:", 1 },
{"server", 's', "HOST", 0, "Connect to " PACKAGE " on HOST (name or IP) [localhost]", 0 },
{"port", 'p', "PORT", 0, "Connect to " PACKAGE " on PORT [8888]", 0 },
@@ -92,19 +76,19 @@ static const struct argp_option argpoptions[] = {
/**
* The program argument parsing function.
* @param key the key from @a argpoptions.
* @param key the key from @a argDefs.
* @param arg the option argument, or nullptr.
* @param state the parsing state.
* @param parseOpt the parse options.
*/
error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct options *opt = (struct options*)state->input;
static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
struct options *opt = (struct options*)parseOpt->userArg;
char* strEnd = nullptr;
unsigned int value;
switch (key) {
// Device settings:
case 's': // --server=localhost
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid server");
argParseError(parseOpt, "invalid server");
return EINVAL;
}
opt->server = arg;
@@ -112,7 +96,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case 'p': // --port=8888
value = strtoul(arg, &strEnd, 10);
if (strEnd == nullptr || strEnd == arg || *strEnd != 0 || value < 1 || value > 65535) {
argp_error(state, "invalid port");
argParseError(parseOpt, "invalid port");
return EINVAL;
}
opt->port = (uint16_t)value;
@@ -120,7 +104,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case 't': // --timeout=10
value = strtoul(arg, &strEnd, 10);
if (strEnd == nullptr || strEnd == arg || *strEnd != 0 || value > 3600) {
argp_error(state, "invalid timeout");
argParseError(parseOpt, "invalid timeout");
return EINVAL;
}
opt->timeout = (uint16_t)value;
@@ -128,12 +112,8 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case 'e': // --error
opt->errorResponse = true;
break;
case ARGP_KEY_ARGS:
opt->args = state->argv + state->next;
opt->argCount = state->argc - state->next;
break;
default:
return ARGP_ERR_UNKNOWN;
return ESRCH;
}
return 0;
}
@@ -362,11 +342,32 @@ bool connect(const char* host, uint16_t port, uint16_t timeout, char* const *arg
* @return the exit code.
*/
int main(int argc, char* argv[]) {
struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, nullptr, nullptr, nullptr };
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, nullptr, &opt) != 0) {
return EINVAL;
argParseOpt parseOpt = {
argDefs,
parse_opt,
af_noVersion,
"ebusctl",
"[COMMAND [CMDOPT...]]",
"Client for accessing " PACKAGE " via TCP.",
"If given, send COMMAND together with CMDOPT options to " PACKAGE ".\n"
"Use 'help' as COMMAND for help on available " PACKAGE " commands.",
nullptr,
&opt
};
int arg_index = -1;
switch (argParse(&parseOpt, argc, argv, &arg_index)) {
case 0: // OK
break;
case '?': // help printed
return 0;
default:
return EINVAL;
}
if (arg_index >= 0) {
opt.args = argv + arg_index;
opt.argCount = argc - arg_index;
}
bool success = connect(opt.server, opt.port, opt.timeout, opt.args, opt.argCount);
exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
+47 -47
View File
@@ -20,7 +20,6 @@
# include <config.h>
#endif
#include <argp.h>
#include <unistd.h>
#include <string.h>
#include <iostream>
@@ -29,6 +28,7 @@
#include <iomanip>
#include "lib/ebus/device.h"
#include "lib/ebus/result.h"
#include "lib/utils/arg.h"
namespace ebusd {
@@ -58,31 +58,8 @@ static struct options opt = {
"/tmp/ebus_dump.bin", // dumpFile
};
/** the version string of the program. */
const char *argp_program_version = "ebusfeed of """ PACKAGE_STRING "";
/** the report bugs to address of the program. */
const char *argp_program_bug_address = "" PACKAGE_BUGREPORT "";
/** the documentation of the program. */
static const char argpdoc[] =
"Feed data from an " PACKAGE " DUMPFILE to a serial device.\n"
"\v"
"With no DUMPFILE, /tmp/ebus_dump.bin is used.\n"
"\n"
"Example for setting up two pseudo terminals with 'socat':\n"
" 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n"
" 2. create symbol links to appropriate devices, e.g.\n"
" 'ln -s /dev/pts/2 /dev/ttyUSB60'\n"
" 'ln -s /dev/pts/3 /dev/ttyUSB20'\n"
" 3. start " PACKAGE ": '" PACKAGE " -f -d /dev/ttyUSB20 --nodevicecheck'\n"
" 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'\n";
/** the description of the accepted arguments. */
static char argpargsdoc[] = "[DUMPFILE]";
/** the definition of the known program arguments. */
static const struct argp_option argpoptions[] = {
static const ebusd::argDef argDefs[] = {
{"device", 'd', "DEV", 0, "Write to DEV (serial device) [/dev/ttyUSB60]", 0 },
{"time", 't', "USEC", 0, "Delay each byte by USEC us [10000]", 0 },
@@ -91,18 +68,18 @@ static const struct argp_option argpoptions[] = {
/**
* The program argument parsing function.
* @param key the key from @a argpoptions.
* @param key the key from @a argDefs.
* @param arg the option argument, or nullptr.
* @param state the parsing state.
* @param parseOpt the parse options.
*/
error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct options *opt = (struct options*)state->input;
static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) {
struct options *opt = (struct options*)parseOpt->userArg;
char* strEnd = nullptr;
switch (key) {
// Device settings:
case 'd': // --device=/dev/ttyUSB60
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid device");
argParseError(parseOpt, "invalid device");
return EINVAL;
}
opt->device = arg;
@@ -110,23 +87,12 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
case 't': // --time=10000
opt->time = (unsigned int)strtoul(arg, &strEnd, 10);
if (strEnd == nullptr || strEnd == arg || *strEnd != 0 || opt->time < 1000 || opt->time > 100000000) {
argp_error(state, "invalid time");
argParseError(parseOpt, "invalid time");
return EINVAL;
}
break;
case ARGP_KEY_ARG:
if (state->arg_num == 0) {
if (arg == nullptr || arg[0] == 0 || strcmp("/", arg) == 0) {
argp_error(state, "invalid dumpfile");
return EINVAL;
}
opt->dumpFile = arg;
} else {
return ARGP_ERR_UNKNOWN;
}
break;
default:
return ARGP_ERR_UNKNOWN;
return ESRCH;
}
return 0;
}
@@ -139,11 +105,45 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
* @return the exit code.
*/
int main(int argc, char* argv[]) {
struct argp argp = { argpoptions, parse_opt, argpargsdoc, argpdoc, nullptr, nullptr, nullptr };
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, nullptr, &opt) != 0) {
return EINVAL;
ebusd::argParseOpt parseOpt = {
argDefs,
parse_opt,
"ebusfeed",
"[DUMPFILE]",
"Feed data from an " PACKAGE " DUMPFILE to a serial device.",
"With no DUMPFILE, /tmp/ebus_dump.bin is used.\n"
"\n"
"Example for setting up two pseudo terminals with 'socat':\n"
" 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n"
" 2. create symbol links to appropriate devices, e.g.\n"
" 'ln -s /dev/pts/2 /dev/ttyUSB60'\n"
" 'ln -s /dev/pts/3 /dev/ttyUSB20'\n"
" 3. start " PACKAGE ": '" PACKAGE " -f -d /dev/ttyUSB20 --nodevicecheck'\n"
" 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'",
&opt
};
int arg_index = -1;
switch (ebusd::argParse(&parseOpt, argc, argv, &arg_index)) {
case 0: // OK
break;
case '?': // help printed
return 0;
default:
return EINVAL;
}
if (arg_index >= 0) {
if (argv[arg_index][0] == 0 || strcmp("/", argv[arg_index]) == 0) {
argParseError(parseOpt, "invalid dumpfile");
return EINVAL;
}
if (arg_index != argc -1) {
// more than one arg
argParseError(parseOpt, "multiple dumpfile");
return EINVAL;
}
opt.dumpFile = argv[arg_index];
}
Device* device = Device::create(opt.device, false, false, false);
if (device == nullptr) {
cout << "unable to create device " << opt.device << endl;
+62 -54
View File
@@ -29,7 +29,6 @@
#include <time.h>
#include <termios.h>
#include <unistd.h>
#include <argp.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
@@ -37,29 +36,13 @@
#include <string>
#include <cstring>
#include "intelhex/intelhexclass.h"
#include "lib/utils/arg.h"
#include "lib/utils/tcpsocket.h"
using ebusd::socketConnect;
/** the version string of the program. */
const char *argp_program_version = "eBUS adapter PIC firmware loader";
/** the documentation of the program. */
static const char argpdoc[] =
"A tool for loading firmware to the eBUS adapter PIC and configure adjustable settings."
"\vPORT is either the serial port to use (e.g. "
#ifdef __CYGWIN__
"/dev/ttyS0 for COM1 on Windows"
#else
"/dev/ttyUSB0"
#endif
") that also supports a trailing wildcard '*' for testing"
" multiple ports, or a network port as \"ip:port\" for use with e.g. socat or ebusd-esp in PIC pass-through mode.";
static const char argpargsdoc[] = "PORT";
/** the definition of the known program arguments. */
static const struct argp_option argpoptions[] = {
static const ebusd::argDef argDefs[] = {
{nullptr, 0, nullptr, 0, "IP options:", 1 },
{"dhcp", 'd', nullptr, 0, "set dynamic IP address via DHCP (default)", 0 },
{"ip", 'i', "IP", 0, "set fix IP address (e.g. 192.168.0.10)", 0 },
@@ -82,7 +65,7 @@ static const struct argp_option argpoptions[] = {
{nullptr, 0, nullptr, 0, "Tool options:", 9 },
{"verbose", 'v', nullptr, 0, "enable verbose output", 0 },
{"slow", 's', nullptr, 0, "low speed mode for transfer (115kBd instead of 921kBd)", 0 },
{nullptr, 0, nullptr, 0, nullptr, 0 },
{nullptr, 0, nullptr, 0, nullptr, 0 },
};
static bool verbose = false;
@@ -136,7 +119,13 @@ bool parseShort(const char *arg, uint16_t minValue, uint16_t maxValue, uint16_t
return true;
}
error_t parse_opt(int key, char *arg, struct argp_state *state) {
/**
* The program argument parsing function.
* @param key the key from @a argDefs.
* @param arg the option argument, or nullptr.
* @param parseOpt the parse options.
*/
static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) {
char *ip = nullptr, *part = nullptr;
int pos = 0, sum = 0;
struct stat st;
@@ -147,22 +136,22 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
break;
case 'd': // --dhcp
if (setIp || setMask || setGateway) {
argp_error(state, "either DHCP or IP address is needed");
argParseError(parseOpt, "either DHCP or IP address is needed");
return EINVAL;
}
setDhcp = true;
break;
case 'i': // --ip=192.168.0.10
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid IP address");
argParseError(parseOpt, "invalid IP address");
return EINVAL;
}
if (setDhcp) {
argp_error(state, "either DHCP or IP address is needed");
argParseError(parseOpt, "either DHCP or IP address is needed");
return EINVAL;
}
if (setIp) {
argp_error(state, "IP address was specified twice");
argParseError(parseOpt, "IP address was specified twice");
return EINVAL;
}
ip = strdup(arg);
@@ -177,41 +166,41 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
}
free(ip);
if (pos != 4 || part || sum == 0) {
argp_error(state, "invalid IP address");
argParseError(parseOpt, "invalid IP address");
return EINVAL;
}
setIp = true;
break;
case 'm': // --mask=24
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid IP mask");
argParseError(parseOpt, "invalid IP mask");
return EINVAL;
}
if (setDhcp) {
argp_error(state, "either DHCP or IP address is needed");
argParseError(parseOpt, "either DHCP or IP address is needed");
return EINVAL;
}
if (setMask) {
argp_error(state, "mask was specified twice");
argParseError(parseOpt, "mask was specified twice");
return EINVAL;
}
if (!parseByte(arg, 1, 0x1e, &setMaskLen)) {
argp_error(state, "invalid IP mask");
argParseError(parseOpt, "invalid IP mask");
return EINVAL;
}
setMask = true;
break;
case 'g': // --gateway=192.168.0.11
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid gateway");
argParseError(parseOpt, "invalid gateway");
return EINVAL;
}
if (setDhcp) {
argp_error(state, "either DHCP or IP address is needed");
argParseError(parseOpt, "either DHCP or IP address is needed");
return EINVAL;
}
if (!setIp || !setMask) {
argp_error(state, "IP and mask need to be specified before gateway");
argParseError(parseOpt, "IP and mask need to be specified before gateway");
return EINVAL;
}
ip = strdup(arg);
@@ -228,7 +217,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
uint8_t maskRemain = setMaskLen-pos*8;
uint8_t mask = maskRemain >= 8 ? 255 : maskRemain == 0 ? 0 : (255^((1 << (8 - maskRemain)) - 1));
if ((address & mask) != (setIpAddress[pos] & mask)) {
argp_error(state, "invalid gateway (different network)");
argParseError(parseOpt, "invalid gateway (different network)");
free(ip);
return EINVAL;
}
@@ -237,15 +226,15 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
}
free(ip);
if (pos != 4 || part || sum == 0 || setGatewayBits == 0) {
argp_error(state, "invalid gateway");
argParseError(parseOpt, "invalid gateway");
return EINVAL;
}
if (setGatewayBits == hostBits) {
argp_error(state, "invalid gateway (same as address)");
argParseError(parseOpt, "invalid gateway (same as address)");
return EINVAL;
}
if (!setGatewayBits || setGatewayBits == ((1 << (32 - setMaskLen)) - 1)) {
argp_error(state, "invalid gateway (net or broadcast address)");
argParseError(parseOpt, "invalid gateway (net or broadcast address)");
return EINVAL;
}
if (setGatewayBits == 1) { // default
@@ -260,7 +249,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
}
if (!(setGatewayBits >> 5)) {
if (!(setGatewayBits & 0x1f)) {
argp_error(state, "invalid gateway (net address)");
argParseError(parseOpt, "invalid gateway (net address)");
return EINVAL;
}
// fine: host part above max gateway adjustable bits is the same and remainder non-zero
@@ -274,7 +263,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
setGateway = true;
break;
}
argp_error(state, "invalid gateway (out of possible range of first/last 31 hosts in subnet)");
argParseError(parseOpt, "invalid gateway (out of possible range of first/last 31 hosts in subnet)");
return EINVAL;
case 'M': // --macip
setMacFromIp = true;
@@ -286,11 +275,11 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
break;
case 'a': // --arbdel=1000
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid arbitration delay");
argParseError(parseOpt, "invalid arbitration delay");
return EINVAL;
}
if (!parseShort(arg, 0, 620, &setArbitrationDelayMicros)) {
argp_error(state, "invalid arbitration delay");
argParseError(parseOpt, "invalid arbitration delay");
return EINVAL;
}
setArbitrationDelay = true;
@@ -305,7 +294,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
break;
case -3: // --variant=U|W|E|F|N|u|w|e|f|n
if (arg == nullptr || arg[0] == 0) {
argp_error(state, "invalid variant");
argParseError(parseOpt, "invalid variant");
return EINVAL;
}
if (arg[0] == 'u' || arg[0] == 'U') {
@@ -319,7 +308,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
} else if (arg[0] == 'n' || arg[0] == 'N') {
setVariantValue = 0;
} else {
argp_error(state, "invalid variant");
argParseError(parseOpt, "invalid variant");
return EINVAL;
}
setVariantForced = arg[0]<'a';
@@ -327,7 +316,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
break;
case 'f': // --flash=firmware.hex
if (arg == nullptr || arg[0] == 0 || stat(arg, &st) != 0 || !S_ISREG(st.st_mode)) {
argp_error(state, "invalid flash file");
argParseError(parseOpt, "invalid flash file");
return EINVAL;
}
flashFile = arg;
@@ -339,7 +328,7 @@ error_t parse_opt(int key, char *arg, struct argp_state *state) {
lowSpeed = true;
break;
default:
return ARGP_ERR_UNKNOWN;
return ESRCH;
}
return 0;
}
@@ -1094,7 +1083,7 @@ int readSettings(int fd, uint8_t* currentData = nullptr) {
} else {
std::cout << "off" << std::endl;
}
std::cout << "Variant: "; // since firmware 20221206
std::cout << "Variant: "; // since firmware 20221206
switch (configData[5]&0x03) {
case 3:
std::cout << "USB/RPI (high-speed)";
@@ -1156,7 +1145,7 @@ bool writeSettings(int fd, uint8_t* currentData = nullptr) {
if (setVariant) {
configData[5] = (configData[5]&0x38) | (setVariantForced?0:0x04) | (setVariantValue&0x03);
if (setVariantValue==0) {
configData[1] = (configData[1]&~0x1f); // set mask=0 to disable Ethernet
configData[1] = (configData[1]&~0x1f); // set mask=0 to disable Ethernet
}
}
if (writeConfig(fd, 0x0000, 8, configData) != 0) {
@@ -1170,13 +1159,32 @@ bool writeSettings(int fd, uint8_t* currentData = nullptr) {
int run(int fd);
int main(int argc, char* argv[]) {
struct argp aargp = { argpoptions, parse_opt, argpargsdoc, argpdoc, nullptr, nullptr, nullptr };
ebusd::argParseOpt parseOpt = {
argDefs,
parse_opt,
ebusd::af_noVersion,
"ebuspicloader",
"[PORT]",
"A tool for loading firmware to the eBUS adapter PIC and configure adjustable settings.",
"PORT is either the serial port to use (e.g. "
#ifdef __CYGWIN__
"/dev/ttyS0 for COM1 on Windows"
#else
"/dev/ttyUSB0"
#endif
") that also supports a trailing wildcard '*' for testing"
" multiple ports, or a network port as \"ip:port\" for use with e.g. socat or ebusd-esp in PIC pass-through mode.",
nullptr,
nullptr
};
int arg_index = -1;
setenv("ARGP_HELP_FMT", "no-dup-args-note", 0);
if (argp_parse(&aargp, argc, argv, ARGP_IN_ORDER, &arg_index, nullptr) != 0) {
std::cerr << "invalid arguments" << std::endl;
exit(EXIT_FAILURE);
switch (ebusd::argParse(&parseOpt, argc, argv, &arg_index)) {
case 0: // OK
break;
case '?': // help printed
return 0;
default:
return EINVAL;
}
if (setIp != setMask || (setMacFromIp && !setIp)) {
@@ -1188,7 +1196,7 @@ int main(int argc, char* argv[]) {
printFileChecksum();
exit(EXIT_SUCCESS);
} else {
argp_help(&aargp, stderr, ARGP_HELP_STD_ERR, const_cast<char*>("ebuspicloader"));
ebusd::argHelp(&parseOpt);
exit(EXIT_FAILURE);
}
}