From 2175c993ef81b78527c85649f5bdc3585d2f41b2 Mon Sep 17 00:00:00 2001 From: Marcelo Aquino Date: Fri, 23 Mar 2018 07:00:34 -0300 Subject: [PATCH] Linux: Use config file for gateway settings (#1061) - The following settings can be use on the config file: - verbose=[debug,info,notice,warn,err] - Logging verbosity. - log_file[0|1] - Enable logging to a file. - log_filepath=(FILE) - Log file path. - log_pipe=[0|1] - Enable logging to a named pipe(aka fifo). Use this option to view your gateway's log messages from the log_pipe_file (defined below). To do so, run the following command on another terminal: - $ cat "log_pipe_file" - log_pipe_file=(FILE) - syslog=[0|1] - Enable logging to syslog. - eeprom_file=[/etc/mysensors.eeprom] - eeprom_size=[1024] - Change some mysgw parameters: - Added: - -q, --quiet: for quiet mode, disable log messages written to the terminal. - Removed: - -d, --debug: removed, log messages are now enabled by default. - Replaced: - -b, --background: replaced by --daemon - isatty() is no longer used, log messages by default are printed to stderr unless the gateway is started with --quiet (#1022) - MY_LINUX_CONFIG_FILE: no longer holds the path to the eeprom file, but to the configuration file --- MyConfig.h | 2 +- configure | 2 +- drivers/Linux/SoftEeprom.cpp | 106 ++++--- drivers/Linux/SoftEeprom.h | 17 +- drivers/Linux/config.c | 249 +++++++++++++++ drivers/Linux/config.h | 45 +++ drivers/Linux/log.c | 287 +++++++++++++----- drivers/Linux/log.h | 39 ++- examples_linux/mysgw.cpp | 4 +- hal/architecture/Linux/MyHwLinuxGeneric.cpp | 9 +- hal/architecture/Linux/MyHwLinuxGeneric.h | 2 +- hal/architecture/Linux/MyMainLinuxGeneric.cpp | 91 +++--- initscripts/mysgw.systemd | 2 +- initscripts/mysgw.sysvinit | 2 +- 14 files changed, 681 insertions(+), 176 deletions(-) create mode 100644 drivers/Linux/config.c create mode 100644 drivers/Linux/config.h diff --git a/MyConfig.h b/MyConfig.h index 387d0c69..ce73deae 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -1873,7 +1873,7 @@ * @note For now the configuration file is only used to store the emulated eeprom state. */ #ifndef MY_LINUX_CONFIG_FILE -#define MY_LINUX_CONFIG_FILE "/etc/mysensors.dat" +#define MY_LINUX_CONFIG_FILE "/etc/mysensors.conf" #endif /** @}*/ // End of LinuxSettingGrpPub group /** @}*/ // End of PlatformSettingGrpPub group diff --git a/configure b/configure index 77df1433..c03f3b2e 100755 --- a/configure +++ b/configure @@ -37,7 +37,7 @@ Installation options: MySensors options: --my-debug=[enable|disable] Enables or disables MySensors core debugging. [enable] - --my-config-file= Config file path. [/etc/mysensors.dat] + --my-config-file= Config file path. [/etc/mysensors.conf] --my-gateway=[none|ethernet|serial|mqtt] Set the protocol used to communicate with the controller. [ethernet] --my-node-id= Disable gateway feature and run as a node with the specified id. diff --git a/drivers/Linux/SoftEeprom.cpp b/drivers/Linux/SoftEeprom.cpp index 4c04f5dd..ac005539 100644 --- a/drivers/Linux/SoftEeprom.cpp +++ b/drivers/Linux/SoftEeprom.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2017 Sensnology AB + * Copyright (C) 2013-2018 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -26,46 +26,8 @@ #include "log.h" #include "SoftEeprom.h" -SoftEeprom::SoftEeprom(const char *fileName, size_t length) +SoftEeprom::SoftEeprom() : _length(0), _fileName(NULL), _values(NULL) { - struct stat fileInfo; - - _fileName = strdup(fileName); - if (_fileName == NULL) { - logError("Error: %s\n", strerror(errno)); - exit(1); - } - - _length = length; - _values = new uint8_t[_length]; - for (size_t i = 0; i < _length; ++i) { - _values[i] = 0xFF; - } - - if (stat(_fileName, &fileInfo) != 0) { - //File does not exist. Create it. - logInfo("Config file %s does not exist, creating new config file.\n", _fileName); - std::ofstream myFile(_fileName, std::ios::out | std::ios::binary); - if (!myFile) { - logError("Unable to create config file %s.\n", _fileName); - exit(1); - } - myFile.write((const char*)_values, _length); - myFile.close(); - } else if (fileInfo.st_size < 0 || (size_t)fileInfo.st_size != _length) { - logError("Config file %s is not the correct size of %zu. Please remove the file and a new one will be created.\n", - _fileName, _length); - exit(1); - } else { - //Read config into local memory. - std::ifstream myFile(_fileName, std::ios::in | std::ios::binary); - if (!myFile) { - logError("Unable to open config to file %s for reading.\n", _fileName); - exit(1); - } - myFile.read((char*)_values, _length); - myFile.close(); - } } SoftEeprom::SoftEeprom(const SoftEeprom& other) @@ -81,8 +43,63 @@ SoftEeprom::SoftEeprom(const SoftEeprom& other) SoftEeprom::~SoftEeprom() { - delete[] _values; - free(_fileName); + destroy(); +} + +int SoftEeprom::init(const char *fileName, size_t length) +{ + struct stat fileInfo; + + destroy(); + + _fileName = strdup(fileName); + if (_fileName == NULL) { + logError("Error: %s\n", strerror(errno)); + return -1; + } + + _length = length; + _values = new uint8_t[_length]; + for (size_t i = 0; i < _length; ++i) { + _values[i] = 0xFF; + } + + if (stat(_fileName, &fileInfo) != 0) { + //File does not exist. Create it. + logInfo("EEPROM file %s does not exist, creating new file.\n", _fileName); + std::ofstream myFile(_fileName, std::ios::out | std::ios::binary); + if (!myFile) { + logError("Unable to create config file %s.\n", _fileName); + return -1; + } + myFile.write((const char*)_values, _length); + myFile.close(); + } else if (fileInfo.st_size < 0 || (size_t)fileInfo.st_size != _length) { + logError("EEPROM file %s is not the correct size of %zu. Please remove the file and a new one will be created.\n", + _fileName, _length); + return -1; + } else { + //Read config into local memory. + std::ifstream myFile(_fileName, std::ios::in | std::ios::binary); + if (!myFile) { + logError("Unable to open EEPROM file %s for reading.\n", _fileName); + return -1; + } + myFile.read((char*)_values, _length); + myFile.close(); + } + + return 0; +} + +void SoftEeprom::destroy() +{ + if (_values) { + delete[] _values; + } + if (_fileName) { + free(_fileName); + } } void SoftEeprom::readBlock(void* buf, void* addr, size_t length) @@ -90,7 +107,7 @@ void SoftEeprom::readBlock(void* buf, void* addr, size_t length) static bool config_to_mem = false; unsigned long int offs = reinterpret_cast(addr); - if (!config_to_mem) { + if (!config_to_mem && length) { //Read config into local memory. std::ifstream myFile(_fileName, std::ios::in | std::ios::binary); if (!myFile) { @@ -144,6 +161,9 @@ void SoftEeprom::writeByte(int addr, uint8_t value) SoftEeprom& SoftEeprom::operator=(const SoftEeprom& other) { if (this != &other) { + delete[] _values; + free(_fileName); + _fileName = strdup(other._fileName); _length = other._length; diff --git a/drivers/Linux/SoftEeprom.h b/drivers/Linux/SoftEeprom.h index 6b243183..806bb0a7 100644 --- a/drivers/Linux/SoftEeprom.h +++ b/drivers/Linux/SoftEeprom.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2017 Sensnology AB + * Copyright (C) 2013-2018 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -37,7 +37,7 @@ public: /** * @brief SoftEeprom constructor. */ - SoftEeprom(const char *fileName, size_t length); + SoftEeprom(); /** * @brief SoftEeprom copy constructor. */ @@ -46,6 +46,19 @@ public: * @brief SoftEeprom destructor. */ ~SoftEeprom(); + /** + * @brief Initializes the eeprom class. + * + * @param fileName filepath where the data is saved. + * @param length eeprom size in bytes. + * @return 0 if SUCCESS or -1 if FAILURE. + */ + int init(const char *fileName, size_t length); + /** + * @brief Clear all allocated memory variables. + * + */ + void destroy(); /** * @brief Read a block of bytes from eeprom. * diff --git a/drivers/Linux/config.c b/drivers/Linux/config.c new file mode 100644 index 00000000..db0b7dd2 --- /dev/null +++ b/drivers/Linux/config.c @@ -0,0 +1,249 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2018 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * Based on mosquitto project, Copyright (c) 2012 Roger Light + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include "log.h" + +static int _config_create(const char *config_file); +static int _config_parse_int(char *token, const char *name, int *value); +static int _config_parse_string(char *token, const char *name, char **value); + +int config_parse(const char *config_file) +{ + FILE *fptr; + char buf[1024]; + struct stat fileInfo; + + if (stat(config_file, &fileInfo) != 0) { + //File does not exist. Create it. + logInfo("Config file %s does not exist, creating new file.\n", config_file); + _config_create(config_file); + } + + fptr = fopen(config_file, "rt"); + if (!fptr) { + logError("Error opening config file \"%s\".\n", config_file); + return -1; + } + + conf.verbose = 4; + conf.log_pipe = 0; + conf.log_pipe_file = NULL; + conf.syslog = 0; + conf.eeprom_file = NULL; + conf.eeprom_size = 1024; + + while (fgets(buf, 1024, fptr)) { + if (buf[0] != '#' && buf[0] != 10 && buf[0] != 13) { + while (buf[strlen(buf)-1] == 10 || buf[strlen(buf)-1] == 13) { + buf[strlen(buf)-1] = 0; + } + + if (!strncmp(buf, "verbose=", 8)) { + char *verbose = NULL; + if (_config_parse_string(&(buf[8]), "verbose", &verbose)) { + fclose(fptr); + return -1; + } else { + if (!strncmp(verbose, "err", 3)) { + conf.verbose = 3; + } else if (!strncmp(verbose, "warn", 4)) { + conf.verbose = 4; + } else if (!strncmp(verbose, "notice", 6)) { + conf.verbose = 5; + } else if (!strncmp(verbose, "info", 4)) { + conf.verbose = 6; + } else if (!strncmp(verbose, "debug", 5)) { + conf.verbose = 7; + } else { + logError("Invalid value for verbose in configuration.\n"); + fclose(fptr); + free(verbose); + return -1; + } + free(verbose); + } + } else if (!strncmp(buf, "log_file=", 9)) { + if (_config_parse_int(&(buf[9]), "log_file", &conf.log_file)) { + fclose(fptr); + return -1; + } else { + if (conf.log_file != 0 && conf.log_file != 1) { + logError("log_file must be 1 or 0 in configuration.\n"); + fclose(fptr); + return -1; + } + } + } else if (!strncmp(buf, "log_filepath=", 13)) { + if (_config_parse_string(&(buf[13]), "log_filepath", &conf.log_filepath)) { + fclose(fptr); + return -1; + } + } else if (!strncmp(buf, "log_pipe=", 9)) { + if (_config_parse_int(&(buf[9]), "log_pipe", &conf.log_pipe)) { + fclose(fptr); + return -1; + } else { + if (conf.log_pipe != 0 && conf.log_pipe != 1) { + logError("log_pipe must be 1 or 0 in configuration.\n"); + fclose(fptr); + return -1; + } + } + } else if (!strncmp(buf, "log_pipe_file=", 14)) { + if (_config_parse_string(&(buf[14]), "log_pipe_file", &conf.log_pipe_file)) { + fclose(fptr); + return -1; + } + } else if (!strncmp(buf, "syslog=", 7)) { + if (_config_parse_int(&(buf[7]), "syslog", &conf.syslog)) { + fclose(fptr); + return -1; + } else { + if (conf.syslog != 0 && conf.syslog != 1) { + logError("syslog must be 1 or 0 in configuration.\n"); + fclose(fptr); + return -1; + } + } + } else if (!strncmp(buf, "eeprom_file=", 12)) { + if (_config_parse_string(&(buf[12]), "eeprom_file", &conf.eeprom_file)) { + fclose(fptr); + return -1; + } + } else if (!strncmp(buf, "eeprom_size=", 12)) { + if (_config_parse_int(&(buf[12]), "eeprom_size", &conf.eeprom_size)) { + fclose(fptr); + return -1; + } else { + if (conf.eeprom_size <= 0) { + logError("eeprom_size value must be greater than 0 in configuration.\n"); + fclose(fptr); + return -1; + } + } + } else { + logWarning("Unknown config option \"%s\".\n", buf); + } + } + } + fclose(fptr); + + if (!conf.eeprom_file) { + logError("No eeprom_file found in configuration.\n"); + return -1; + } + + if (conf.log_file && !conf.log_filepath) { + logError("log_filepath must be set if you enable log_file in configuration.\n"); + return -1; + } + + if (conf.log_pipe && !conf.log_pipe_file) { + logError("log_pipe_file must be set if you enable log_pipe in configuration.\n"); + return -1; + } + + return 0; +} + +void config_cleanup(void) +{ + if (conf.eeprom_file) { + free(conf.eeprom_file); + } + if (conf.log_pipe_file) { + free(conf.log_pipe_file); + } +} + +int _config_create(const char *config_file) +{ + FILE *myFile; + int ret; + + const char default_conf[] = "# Logging verbosity: debug,info,notice,warn,err\n" \ + "verbose=debug\n" \ + "# Enable logging to a file.\n" \ + "log_file=0\n" \ + "# Log file path.\n" \ + "log_filepath=/tmp/mysgw.log\n" \ + "# Enable logging to a named pipe.\n" \ + "# Use this option to view your gateway's log messages\n" \ + "# from the log_pipe_file defined bellow.\n" \ + "# To do so, run the following command on another terminal:\n" \ + "# cat \"log_pipe_file\"\n" \ + "log_pipe=0\n" \ + "log_pipe_file=/tmp/mysgw.pipe\n" \ + "# Enable logging to syslog.\n" \ + "syslog=0\n" \ + "eeprom_file=/etc/mysensors.eeprom\n" \ + "eeprom_size=1024\n"; + + myFile = fopen(config_file, "w"); + if (!myFile) { + logError("Unable to create config file %s.\n", config_file); + return -1; + } + ret = fputs(default_conf, myFile); + fclose(myFile); + + return (ret > 0); +} + +int _config_parse_int(char *token, const char *name, int *value) +{ + if (token) { + *value = atoi(token); + } else { + logError("Empty %s value in configuration.\n", name); + return 1; + } + + return 0; +} + +int _config_parse_string(char *token, const char *name, char **value) +{ + if (token) { + if (*value) { + logError("Duplicate %s value in configuration.\n", name); + return 1; + } + while (token[0] == ' ' || token[0] == '\t') { + token++; + } + *value = strdup(token); + if (!*value) { + logError("Out of memory.\n"); + return 1; + } + } else { + logError("Empty %s value in configuration.\n", name); + return 1; + } + return 0; +} diff --git a/drivers/Linux/config.h b/drivers/Linux/config.h new file mode 100644 index 00000000..5c5dbc56 --- /dev/null +++ b/drivers/Linux/config.h @@ -0,0 +1,45 @@ +/* + * The MySensors Arduino library handles the wireless radio link and protocol + * between your home built sensors/actuators and HA controller of choice. + * The sensors forms a self healing radio network with optional repeaters. Each + * repeater and gateway builds a routing tables in EEPROM which keeps track of the + * network topology allowing messages to be routed to nodes. + * + * Created by Henrik Ekblad + * Copyright (C) 2013-2018 Sensnology AB + * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors + * + * Documentation: http://www.mysensors.org + * Support Forum: http://forum.mysensors.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct config { + int verbose; + int log_file; + char *log_filepath; + int log_pipe; + char *log_pipe_file; + int syslog; + char *eeprom_file; + int eeprom_size; +} conf; + +int config_parse(const char *config_file); +void config_cleanup(void); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/drivers/Linux/log.c b/drivers/Linux/log.c index 8d33c6fe..e7bd8178 100644 --- a/drivers/Linux/log.c +++ b/drivers/Linux/log.c @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2017 Sensnology AB + * Copyright (C) 2013-2018 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -19,49 +19,201 @@ #include "log.h" #include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include -// Default values -static const int log_opts = LOG_CONS | LOG_PERROR; // print syslog to stderror -static const int log_facility = LOG_USER; +static const char *_log_level_colors[] = { + "\x1b[1;5;91m", "\x1b[1;91m", "\x1b[91m", "\x1b[31m", "\x1b[33m", "\x1b[34m", "\x1b[32m", "\x1b[36m" +}; +static const char *_log_level_names[] = { + "EMERGENCY", "ALERT", "CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG" +}; +static uint8_t _log_quiet = 0; +static uint8_t _log_level = LOG_DEBUG; +static uint8_t _log_syslog = 0; -static uint8_t log_open = 0; +static uint8_t _log_pipe = 0; +static char *_log_pipe_file = NULL; +static int _log_pipe_fd = -1; -void logOpen(int options, int facility) +static FILE *_log_file_fp = NULL; + +void logSetQuiet(uint8_t enable) { - openlog(NULL, options, facility); - log_open = 1; + _log_quiet = enable ? 1 : 0; } -void vlogInfo(const char *fmt, va_list args) +void logSetLevel(int level) { - if (!log_open) { - logOpen(log_opts, log_facility); + if (level < LOG_EMERG || level > LOG_DEBUG) { + return; + } + + _log_level = level; +} + +void logSetSyslog(int options, int facility) +{ + openlog(NULL, options, facility); + _log_syslog = 1; +} + +int logSetPipe(char *pipe_file) +{ + if (pipe_file == NULL) { + return -1; + } + + _log_pipe_file = strdup(pipe_file); + if (_log_pipe_file == NULL) { + return -1; + } + + int ret = mkfifo(_log_pipe_file, 0666); + if (ret == 0) { + _log_pipe = 1; + } + + return ret; +} + +int logSetFile(char *file) +{ + if (file == NULL) { + return -1; + } + + _log_file_fp = fopen(file, "a"); + if (_log_file_fp == NULL) { + return errno; + } + + return 0; +} + +void logClose(void) +{ + if (_log_syslog) { + closelog(); + _log_syslog = 0; + } + + if (_log_pipe) { + if (_log_pipe_fd > 0) { + close(_log_pipe_fd); + } + /* remove the FIFO */ + unlink(_log_pipe_file); + _log_pipe = 0; + } + if (_log_pipe_file != NULL) { + free(_log_pipe_file); + _log_pipe_file = NULL; + } + + if (_log_file_fp != NULL) { + fclose(_log_file_fp); + _log_file_fp = NULL; + } +} + +void vlog(int level, const char *fmt, va_list args) +{ + if (_log_level < level) { + return; + } + + if (!_log_quiet || _log_file_fp != NULL) { + /* Get current time */ + time_t t = time(NULL); + struct tm *lt = localtime(&t); + + char buf[16]; + buf[strftime(buf, sizeof(buf), "%b %d %H:%M:%S", lt)] = '\0'; + +#ifdef LOG_DISABLE_COLOR + (void)_log_level_colors; + if (!_log_quiet) { + fprintf(stderr, "%s %-5s ", buf, _log_level_names[level]); + vfprintf(stderr, fmt, args); + } + if (_log_file_fp != NULL) { + vfprintf(_log_file_fp, "%s %-5s ", buf, _log_level_names[level]); + vfprintf(_log_file_fp, fmt, args); + } +#else + if (!_log_quiet) { + fprintf(stderr, "%s %s%-5s\x1b[0m ", buf, _log_level_colors[level], _log_level_names[level]); + vfprintf(stderr, fmt, args); + } + if (_log_file_fp != NULL) { + fprintf(_log_file_fp, "%s %s%-5s\x1b[0m ", buf, _log_level_colors[level], _log_level_names[level]); + vfprintf(_log_file_fp, fmt, args); + } +#endif + } + + if (_log_syslog) { + vsyslog(level, fmt, args); + } + + if (_log_pipe) { + if (_log_pipe_fd < 0) { + _log_pipe_fd = open(_log_pipe_file, O_WRONLY | O_NONBLOCK); + } + if (_log_pipe_fd > 0) { + if (vdprintf(_log_pipe_fd, fmt, args) < 0) { + close(_log_pipe_fd); + _log_pipe_fd = -1; + } + } + } - vsyslog(LOG_INFO, fmt, args); } void #ifdef __GNUC__ __attribute__((format(printf, 1, 2))) #endif -logInfo(const char *fmt, ...) +logEmergency(const char *fmt, ...) { va_list args; va_start(args, fmt); - vlogInfo(fmt, args); + vlog(LOG_EMERG, fmt, args); va_end(args); } -void vlogError(const char *fmt, va_list args) +void +#ifdef __GNUC__ +__attribute__((format(printf, 1, 2))) +#endif +logAlert(const char *fmt, ...) { - if (!log_open) { - logOpen(log_opts, log_facility); - } - vsyslog(LOG_ERR, fmt, args); + va_list args; + + va_start(args, fmt); + vlog(LOG_ALERT, fmt, args); + va_end(args); +} + +void +#ifdef __GNUC__ +__attribute__((format(printf, 1, 2))) +#endif +logCritical(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vlog(LOG_CRIT, fmt, args); + va_end(args); } void @@ -73,60 +225,10 @@ logError(const char *fmt, ...) va_list args; va_start(args, fmt); - vlogError(fmt, args); + vlog(LOG_ERR, fmt, args); va_end(args); } -void vlogNotice(const char *fmt, va_list args) -{ - if (!log_open) { - logOpen(log_opts, log_facility); - } - vsyslog(LOG_NOTICE, fmt, args); -} - -void -#ifdef __GNUC__ -__attribute__((format(printf, 1, 2))) -#endif -logNotice(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - vlogNotice(fmt, args); - va_end(args); -} - -void vlogDebug(const char *fmt, va_list args) -{ - if (!log_open) { - logOpen(log_opts, log_facility); - } - vsyslog(LOG_DEBUG, fmt, args); -} - -void -#ifdef __GNUC__ -__attribute__((format(printf, 1, 2))) -#endif -logDebug(const char *fmt, ...) -{ - va_list args; - - va_start(args, fmt); - vlogDebug(fmt, args); - va_end(args); -} - -void vlogWarning(const char *fmt, va_list args) -{ - if (!log_open) { - logOpen(log_opts, log_facility); - } - vsyslog(LOG_WARNING, fmt, args); -} - void #ifdef __GNUC__ __attribute__((format(printf, 1, 2))) @@ -136,6 +238,45 @@ logWarning(const char *fmt, ...) va_list args; va_start(args, fmt); - vlogWarning(fmt, args); + vlog(LOG_WARNING, fmt, args); + va_end(args); +} + +void +#ifdef __GNUC__ +__attribute__((format(printf, 1, 2))) +#endif +logNotice(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vlog(LOG_NOTICE, fmt, args); + va_end(args); +} + +void +#ifdef __GNUC__ +__attribute__((format(printf, 1, 2))) +#endif +logInfo(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vlog(LOG_INFO, fmt, args); + va_end(args); +} + +void +#ifdef __GNUC__ +__attribute__((format(printf, 1, 2))) +#endif +logDebug(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vlog(LOG_DEBUG, fmt, args); va_end(args); } diff --git a/drivers/Linux/log.h b/drivers/Linux/log.h index b4975808..0cecfc42 100644 --- a/drivers/Linux/log.h +++ b/drivers/Linux/log.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2017 Sensnology AB + * Copyright (C) 2013-2018 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -21,27 +21,36 @@ #define LOG_H #include +#include +#include +#include #ifdef __cplusplus extern "C" { #endif -extern void logOpen(int options, int facility); +#define vlogError(...) vlog(LOG_ERR, __VA_ARGS__) +#define vlogWarning(...) vlog(LOG_WARNING, __VA_ARGS__) +#define vlogNotice(...) vlog(LOG_NOTICE, __VA_ARGS__) +#define vlogInfo(...) vlog(LOG_INFO, __VA_ARGS__) +#define vlogDebug(...) vlog(LOG_DEBUG, __VA_ARGS__) -extern void vlogInfo(const char *fmt, va_list args); -extern void logInfo(const char *fmt, ...) __attribute__((format(printf,1,2))); +void logSetQuiet(uint8_t enable); +void logSetLevel(int level); +void logSetSyslog(int options, int facility); +int logSetPipe(char *pipe_file); +int logSetFile(char *file); +void logClose(void); -extern void vlogError(const char *fmt, va_list args); -extern void logError(const char *fmt, ...) __attribute__((format(printf,1,2))); - -extern void vlogNotice(const char *fmt, va_list args); -extern void logNotice(const char *fmt, ...) __attribute__((format(printf,1,2))); - -extern void vlogDebug(const char *fmt, va_list args); -extern void logDebug(const char *fmt, ...) __attribute__((format(printf,1,2))); - -extern void vlogWarning(const char *fmt, va_list args); -extern void logWarning(const char *fmt, ...) __attribute__((format(printf,1,2))); +void vlog(int level, const char *fmt, va_list args); +void logEmergency(const char *fmt, ...) __attribute__((format(printf,1,2))); +void logAlert(const char *fmt, ...) __attribute__((format(printf,1,2))); +void logCritical(const char *fmt, ...) __attribute__((format(printf,1,2))); +void logError(const char *fmt, ...) __attribute__((format(printf,1,2))); +void logWarning(const char *fmt, ...) __attribute__((format(printf,1,2))); +void logNotice(const char *fmt, ...) __attribute__((format(printf,1,2))); +void logInfo(const char *fmt, ...) __attribute__((format(printf,1,2))); +void logDebug(const char *fmt, ...) __attribute__((format(printf,1,2))); #ifdef __cplusplus } diff --git a/examples_linux/mysgw.cpp b/examples_linux/mysgw.cpp index 28024f15..65541a6d 100644 --- a/examples_linux/mysgw.cpp +++ b/examples_linux/mysgw.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2017 Sensnology AB + * Copyright (C) 2013-2018 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -24,7 +24,7 @@ // For more options run ./configure --help // Config file -//#define MY_LINUX_CONFIG_FILE "/etc/mysensors.dat" +//#define MY_LINUX_CONFIG_FILE "/etc/mysensors.conf" // How many clients should be able to connect to this gateway (default 1) #define MY_GATEWAY_MAX_CLIENTS 10 diff --git a/hal/architecture/Linux/MyHwLinuxGeneric.cpp b/hal/architecture/Linux/MyHwLinuxGeneric.cpp index 18d09060..8be3de8d 100644 --- a/hal/architecture/Linux/MyHwLinuxGeneric.cpp +++ b/hal/architecture/Linux/MyHwLinuxGeneric.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2017 Sensnology AB + * Copyright (C) 2013-2018 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org @@ -27,8 +27,9 @@ #include #include "SoftEeprom.h" #include "log.h" +#include "config.h" -static SoftEeprom eeprom = SoftEeprom(MY_LINUX_CONFIG_FILE, 1024); // ATMega328 has 1024 bytes +static SoftEeprom eeprom; static FILE *randomFp = NULL; bool hwInit(void) @@ -43,6 +44,10 @@ bool hwInit(void) #endif #endif + if (eeprom.init(conf.eeprom_file, conf.eeprom_size) != 0) { + exit(1); + } + return true; } diff --git a/hal/architecture/Linux/MyHwLinuxGeneric.h b/hal/architecture/Linux/MyHwLinuxGeneric.h index 40d7950b..220a7208 100644 --- a/hal/architecture/Linux/MyHwLinuxGeneric.h +++ b/hal/architecture/Linux/MyHwLinuxGeneric.h @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2017 Sensnology AB + * Copyright (C) 2013-2018 Sensnology AB * Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors * * Documentation: http://www.mysensors.org diff --git a/hal/architecture/Linux/MyMainLinuxGeneric.cpp b/hal/architecture/Linux/MyMainLinuxGeneric.cpp index 6faf52ee..16709783 100644 --- a/hal/architecture/Linux/MyMainLinuxGeneric.cpp +++ b/hal/architecture/Linux/MyMainLinuxGeneric.cpp @@ -6,7 +6,7 @@ * network topology allowing messages to be routed to nodes. * * Created by Henrik Ekblad - * Copyright (C) 2013-2017 Sensnology AB + * Copyright (C) 2013-2018 Sensnology AB * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors * * Documentation: http://www.mysensors.org @@ -28,6 +28,7 @@ #include #include #include "log.h" +#include "config.h" #include "MySensorsCore.h" void handle_sigint(int sig) @@ -48,7 +49,7 @@ void handle_sigint(int sig) MY_SERIALDEVICE.end(); #endif - closelog(); + logClose(); exit(EXIT_SUCCESS); } @@ -104,17 +105,18 @@ void print_usage() { printf("Usage: mysgw [options]\n\n" \ "Options:\n" \ + " -c, --config-file Config file. [" MY_LINUX_CONFIG_FILE "]\n" \ " -h, --help Display a short summary of all program options.\n" \ - " -d, --debug Enable debug.\n" \ - " -b, --background Run as a background process.\n" - " --gen-soft-hmac-key Generate and print a soft hmac key.\n" - " --gen-soft-serial-key Generate and print a soft serial key.\n" - " --gen-aes-key Generate and print an aes encryption key.\n" - " --print-soft-hmac-key Print the soft hmac key from the config file.\n" - " --print-soft-serial-key Print the soft serial key from the config file.\n" - " --print-aes-key Print the aes encryption key from the config file.\n" - " --set-soft-hmac-key Write a soft hmac key to the config file.\n" - " --set-soft-serial-key Write a soft serial key to the config file.\n" + " -q, --quiet Quiet mode, disable log messages written to the terminal.\n" \ + " --daemon Run as a daemon.\n" \ + " --gen-soft-hmac-key Generate and print a soft hmac key.\n" \ + " --gen-soft-serial-key Generate and print a soft serial key.\n" \ + " --gen-aes-key Generate and print an aes encryption key.\n" \ + " --print-soft-hmac-key Print the soft hmac key from the config file.\n" \ + " --print-soft-serial-key Print the soft serial key from the config file.\n" \ + " --print-aes-key Print the aes encryption key from the config file.\n" \ + " --set-soft-hmac-key Write a soft hmac key to the config file.\n" \ + " --set-soft-serial-key Write a soft serial key to the config file.\n" \ " --set-aes-key Write an aes encryption key to the config file.\n"); } @@ -338,19 +340,21 @@ void set_aes_key(char *key_str) int main(int argc, char *argv[]) { - int opt, log_opts, debug = 0, foreground = 1; - char *key = NULL; + int opt, daemon = 0, quiet = 0; + char *key = NULL, *config_file = NULL; /* register the signal handler */ signal(SIGINT, handle_sigint); signal(SIGTERM, handle_sigint); + signal(SIGPIPE, handle_sigint); hwRandomNumberInit(); static struct option long_options[] = { + {"config-file", required_argument, 0, 'c'}, + {"daemon", no_argument, 0, 'J'}, {"help", no_argument, 0, 'h'}, - {"debug", no_argument, 0, 'd'}, - {"background", no_argument, 0, 'b'}, + {"quiet", no_argument, 0, 'q'}, {"gen-soft-hmac-key", no_argument, 0, 'A'}, {"gen-soft-serial-key", no_argument, 0, 'B'}, {"gen-aes-key", no_argument, 0, 'C'}, @@ -364,16 +368,16 @@ int main(int argc, char *argv[]) }; int long_index = 0; - while ((opt = getopt_long(argc, argv,"hdbABCDEFGHI", long_options, &long_index )) != -1) { + while ((opt = getopt_long(argc, argv,"chqABCDEFGHIJ", long_options, &long_index )) != -1) { switch (opt) { + case 'c': + config_file = strdup(optarg); + break; case 'h': print_usage(); exit(EXIT_SUCCESS); - case 'd': - debug = 1; - break; - case 'b': - foreground = 0; + case 'q': + quiet = 1; break; case 'A': generate_soft_sign_hmac_key(); @@ -405,27 +409,46 @@ int main(int argc, char *argv[]) key = strdup(optarg); set_aes_key(key); exit(EXIT_SUCCESS); + case 'J': + daemon = 1; + break; default: print_usage(); exit(EXIT_SUCCESS); } } - log_opts = LOG_CONS; - if (foreground && isatty(STDIN_FILENO)) { - // Also print syslog to stderror - log_opts |= LOG_PERROR; - } - if (!debug) { - // Ignore debug type messages - setlogmask(LOG_UPTO (LOG_INFO)); - } - logOpen(log_opts, LOG_USER); - - if (!foreground && !debug) { + if (daemon) { if (daemonize() != 0) { exit(EXIT_FAILURE); } + quiet = 1; + } + + if (config_parse(config_file?config_file:MY_LINUX_CONFIG_FILE) != 0) { + exit(EXIT_FAILURE); + } + if (config_file) { + free(config_file); + } + + logSetQuiet(quiet); + logSetLevel(conf.verbose); + + if (conf.log_file) { + if (logSetFile(conf.log_filepath) != 0) { + logError("Failed to open log file.\n"); + } + } + + if (conf.log_pipe) { + if (logSetPipe(conf.log_pipe_file) != 0) { + logError("Failed to open log pipe.\n"); + } + } + + if (conf.syslog) { + logSetSyslog(LOG_CONS, LOG_USER); } logInfo("Starting gateway...\n"); diff --git a/initscripts/mysgw.systemd b/initscripts/mysgw.systemd index 41e7279e..d983ab27 100644 --- a/initscripts/mysgw.systemd +++ b/initscripts/mysgw.systemd @@ -3,7 +3,7 @@ Description=MySensors Gateway daemon Requires=network.target [Service] -ExecStart=%gateway_dir%/mysgw +ExecStart=%gateway_dir%/mysgw -q [Install] WantedBy=multi-user.target diff --git a/initscripts/mysgw.sysvinit b/initscripts/mysgw.sysvinit index a511f056..c24fde90 100644 --- a/initscripts/mysgw.sysvinit +++ b/initscripts/mysgw.sysvinit @@ -17,7 +17,7 @@ PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin DESC="MySensors Gateway" NAME=mysgw DAEMON=%gateway_dir%/$NAME -DAEMON_ARGS="-b" +DAEMON_ARGS="--daemon -q" PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME