#include #include #include #include #include "fileutil.h" #include "strutil.h" #include "mqttcfg.h" #include "debug.h" #define _DEFAULT_CFG_FILE_NAME "mqttcl.cfg.json" #define _CFG_KEY_NAME_BROKER_ADDR "brokerAddr" #define _CFG_KEY_NAME_BROKER_PORT "brokerPort" #define _CFG_KEY_NAME_DEFAULT_QOS "defaultQos" #define _CFG_KEY_NAME_DEFAULT_RETAIN "defaultRetain" #define _CFG_KEY_NAME_DEVICE_PREFIX "devicePrefix" #define _CFG_KEY_NAME_DEVICE_ID "deviceID" #define _CFG_KEY_NAME_TLS_MODE "tlsMode" #define _CFG_KEY_NAME_TLS_CA_CRT_FILE "tlsCaCrtFile" #define _CFG_KEY_NAME_TLS_CL_CRT_FILE "tlsClCrtFile" #define _CFG_KEY_NAME_TLS_CL_KEY_FILE "tlsClKeyFile" #define _CFG_KEY_NAME_TLS_PSK "tlsPsk" #define _CFG_KEY_NAME_LAST_WILL_MESSAGE "lastWillMessage" #define _CFG_KEY_NAME_LAST_WILL_TOPIC "lastWillTopic" #define _CFG_KEY_NAME_LAST_WILL_QOS "lastWillQos" #define _CFG_KEY_NAME_LAST_WILL_RETAIN "lastWillRetain" #define _CFG_KEY_NAME_LAST_WILL_ON_EXIT "lastWillOnExit" #define _CFG_KEY_NAME_LAST_WILL_ON_EXIT_MSG "lastWillOnExitMsg" #define _CFG_KEY_NAME_CONNECT_MESSAGE "connectMessage" #define _CFG_KEY_NAME_CONNECT_TOPIC "connectTopic" #define _CFG_KEY_NAME_CONNECT_QOS "connectQos" #define _CFG_KEY_NAME_CONNECT_RETAIN "connectRetain" #define _CFG_KEY_NAME_MAX_KEEP_ALIVE "maxKeepAlive" #define _CFG_KEY_NAME_PREFIX_STRING "topicPrefix" ///////////////////////////////////////////////////////////////////////////// CMqttClConfig::CMqttClConfig(const char *pszShmUuid) : m_strShmID(formatString("SHM-%s", strucase(pszShmUuid).c_str())), m_nBrokerPort(0), m_nDefaultQOS(0), m_bDefaultRetain(MQTTCL_DEFAULT_RETAIN), m_nLastWillQos(m_nDefaultQOS), m_bLastWillRetain(MQTTCL_DEFAULT_RETAIN), m_bHasLastWill(false), m_bLastWillOnExit(false), m_nConnectQos(m_nDefaultQOS), m_bConnectRetain(MQTTCL_DEFAULT_RETAIN), m_bHasConnect(false), m_bHasPrefix(false), m_nTlsMode(0), m_nMaxKeepAlive(MQTTCL_DEFAULT_MAX_KEEP_ALIVE_TIME) { } CMqttClConfig::~CMqttClConfig(void) { } ///////////////////////////////////////////////////////////////////////////// // LoadCfg bool CMqttClConfig::LoadCfg(const char *pszCfgFilePath, CLogfile &rlf) { char szCfgFilePath[PATH_MAX]; std::string strErr; if(!pszCfgFilePath) { // use default config file path pszCfgFilePath = ::BuildCanonicalFilePath(NULL, _DEFAULT_CFG_FILE_NAME, szCfgFilePath, sizeof(szCfgFilePath)); } ///////////////////////////////////////////////////////////////////////// // load and parse config file json_t *pjtCfg; json_error_t err; if(!(pjtCfg = ::json_load_file(pszCfgFilePath, JSON_REJECT_DUPLICATES, &err))) { rlf.Error("CMqttClConfig::LoadCfg: %s!\n", err.text); return false; } CJson_t jtCfg(pjtCfg, true); ///////////////////////////////////////////////////////////////////////// // tlsMode if(!GetIntValue(jtCfg, _CFG_KEY_NAME_TLS_MODE, m_nTlsMode, strErr)) { m_nTlsMode = MQTTCL_TLS_MODE_OFF; rlf.Warning("CMqttClConfig::LoadCfg: %s! TLS will not be used!\n", strErr.c_str()); } else if(m_nTlsMode < MQTTCL_TLS_MODE_OFF) { rlf.Warning("CMqttClConfig::LoadCfg: Invalid TLS mode: %d! TLS will be disabled!\n", m_nTlsMode); m_nTlsMode = MQTTCL_TLS_MODE_OFF; } else if(m_nTlsMode > MQTTCL_TLS_MODE_PSK) { rlf.Warning("CMqttClConfig::LoadCfg: Invalid TLS mode: %d! TLS mode will be set to PSK!\n", m_nTlsMode); m_nTlsMode = MQTTCL_TLS_MODE_PSK; } ///////////////////////////////////////////////////////////////////////// // brokerAddr if(!GetStringValue(jtCfg, _CFG_KEY_NAME_BROKER_ADDR, m_strBrokerAddr, strErr)) { rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str()); return false; } ///////////////////////////////////////////////////////////////////////// // brokerPort if(!GetIntValue(jtCfg, _CFG_KEY_NAME_BROKER_PORT, m_nBrokerPort, strErr)) { m_nBrokerPort = (m_nTlsMode > MQTTCL_TLS_MODE_OFF) ? 8883 : 1883; rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default broker port %d!\n", strErr.c_str(), m_nBrokerPort); } else if(m_nBrokerPort < 0 || m_nBrokerPort > 0xffff) { rlf.Error("CMqttClConfig::LoadCfg: Invalid broker port number: %d!\n", m_nBrokerPort); return false; } ///////////////////////////////////////////////////////////////////////// // defaultQos if(!GetIntValue(jtCfg, _CFG_KEY_NAME_DEFAULT_QOS, m_nDefaultQOS, strErr)) { m_nDefaultQOS = MQTTCL_DEFAULT_QOS; rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default QOS: %d!\n", strErr.c_str(), m_nDefaultQOS); } else if(m_nDefaultQOS < MQTTCL_MIN_QOS) { rlf.Warning("CMqttClConfig::LoadCfg: Invalid QOS: %d - using %d!\n", m_nDefaultQOS, MQTTCL_MIN_QOS); m_nDefaultQOS = MQTTCL_MIN_QOS; } else if(m_nDefaultQOS > MQTTCL_MAX_QOS) { rlf.Warning("CMqttClConfig::LoadCfg: Invalid QOS: %d - using %d!\n", m_nDefaultQOS, MQTTCL_MAX_QOS); m_nDefaultQOS = MQTTCL_MAX_QOS; } ///////////////////////////////////////////////////////////////////////// // defaultRetain if(!GetBoolValue(jtCfg, _CFG_KEY_NAME_DEFAULT_RETAIN, m_bDefaultRetain, strErr)) { m_bDefaultRetain = MQTTCL_DEFAULT_RETAIN; rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default retain \"%s\"!\n", strErr.c_str(), m_bDefaultRetain ? "true" : "false"); } ///////////////////////////////////////////////////////////////////////// // lastWillMessage if(!GetStringValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_MESSAGE, m_strLastWillMessage, strErr)) { rlf.Info("CMqttClConfig::LoadCfg: %s! No Last Will Message provided.\n", strErr.c_str()); } if((m_bHasLastWill = !m_strLastWillMessage.empty())) { ///////////////////////////////////////////////////////////////////// // lastWillTopic if(!GetStringValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_TOPIC, m_strLastWillTopic, strErr)) { m_strLastWillTopic = MQTTCL_DEFAULT_LAST_WILL_CONNECT_TOPIC; rlf.Info("CMqttClConfig::LoadCfg: %s! Setting Last Will Topic to default: \"%s\".\n", strErr.c_str()), m_strLastWillTopic.c_str(); } ///////////////////////////////////////////////////////////////////// // lastWillQos if(!GetIntValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_QOS, m_nLastWillQos, strErr)) { m_nLastWillQos = m_nDefaultQOS; rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Last Will Qos: %d!\n", strErr.c_str(), m_nLastWillQos); } ///////////////////////////////////////////////////////////////////// // lastWillRetain if(!GetBoolValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_RETAIN, m_bLastWillRetain, strErr)) { m_bLastWillRetain = MQTTCL_DEFAULT_RETAIN; rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Last Will retain: %s!\n", strErr.c_str(), m_bLastWillRetain ? "true" : "false"); } ///////////////////////////////////////////////////////////////////// // lastWillOnExit if(!GetBoolValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_ON_EXIT, m_bLastWillOnExit, strErr)) { m_bLastWillOnExit = false; rlf.Warning("CMqttClConfig::LoadCfg: %s! No Last Will on Exit.\n", strErr.c_str()); } if(m_bLastWillOnExit) { ///////////////////////////////////////////////////////////////// // lastWillOnExitMsg if(!GetStringValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_ON_EXIT_MSG, m_strLastWillOnExitMsg, strErr)) { m_strLastWillOnExitMsg = m_strLastWillMessage; rlf.Info("CMqttClConfig::LoadCfg: %s! Setting Last Will Exit Message to default: \"%s\".\n", strErr.c_str()), m_strLastWillOnExitMsg.c_str(); } } } ///////////////////////////////////////////////////////////////////////// // connectMessage if(!GetStringValue(jtCfg, _CFG_KEY_NAME_CONNECT_MESSAGE, m_strConnectMessage, strErr)) { rlf.Info("CMqttClConfig::LoadCfg: %s! No Connect Message provided.\n", strErr.c_str()); } if((m_bHasConnect = !m_strConnectMessage.empty())) { ///////////////////////////////////////////////////////////////////// // connectTopic if(!GetStringValue(jtCfg, _CFG_KEY_NAME_CONNECT_TOPIC, m_strConnectTopic, strErr)) { m_strConnectTopic = m_strLastWillTopic; rlf.Info("CMqttClConfig::LoadCfg: %s! Setting Connect Topic to default: \"%s\".\n", strErr.c_str()), m_strLastWillTopic.c_str(); } ///////////////////////////////////////////////////////////////////// // connectQos if(!GetIntValue(jtCfg, _CFG_KEY_NAME_CONNECT_QOS, m_nConnectQos, strErr)) { m_nConnectQos = m_nLastWillQos; rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Connect Qos: %d!\n", strErr.c_str(), m_nConnectQos); } ///////////////////////////////////////////////////////////////////// // connectRetain if(!GetBoolValue(jtCfg, _CFG_KEY_NAME_CONNECT_RETAIN, m_bConnectRetain, strErr)) { m_bConnectRetain = m_bLastWillRetain; rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Last Will retain: %s!\n", strErr.c_str(), m_bConnectRetain ? "true" : "false"); } } ///////////////////////////////////////////////////////////////////////// // maxKeepAlive if(!GetIntValue(jtCfg, _CFG_KEY_NAME_MAX_KEEP_ALIVE, m_nMaxKeepAlive, strErr)) { m_nMaxKeepAlive = MQTTCL_DEFAULT_MAX_KEEP_ALIVE_TIME; rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Keep-alive time: %d!\n", strErr.c_str(), m_nMaxKeepAlive); } ///////////////////////////////////////////////////////////////////////// // devicePrefix if(!GetStringValue(jtCfg, _CFG_KEY_NAME_DEVICE_PREFIX, m_strDevicePrefix, strErr)) { m_strDevicePrefix = MQTTCL_DEVICE_PREFIX; rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default device prefix \"%s\"!\n", strErr.c_str(), m_strDevicePrefix.c_str()); } ///////////////////////////////////////////////////////////////////////// // deviceID if(GetStringValue(jtCfg, _CFG_KEY_NAME_DEVICE_ID, m_strDeviceID, strErr)) { rlf.Info("CMqttClConfig::LoadCfg: Using configured device ID: \"%s\"!\n", m_strDeviceID.c_str()); } else { m_strDeviceID = CreateDeviceID(m_strDevicePrefix.c_str()); } ///////////////////////////////////////////////////////////////////////// // topicPrefix string if(!GetStringValue(jtCfg, _CFG_KEY_NAME_PREFIX_STRING, m_strPrefix, strErr)) { m_strPrefix = formatString("%s/%s", GetDeviceID(), GetShmID()); } ///////////////////////////////////////////////////////////////////////// // m_nTlsMode == (MQTTCL_TLS_MODE_CRT || MQTTCL_TLS_MODE_PSK) if(m_nTlsMode == MQTTCL_TLS_MODE_CRT) { ///////////////////////////////////////////////////////////////////// // tlsCaCrtFile if(!GetStringValue(jtCfg, _CFG_KEY_NAME_TLS_CA_CRT_FILE, m_strTlsCaCrtFile, strErr)) { rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str()); return false; } ///////////////////////////////////////////////////////////////////// // tlsClCrtFile if(!GetStringValue(jtCfg, _CFG_KEY_NAME_TLS_CL_CRT_FILE, m_strTlsClCrtFile, strErr)) { rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str()); return false; } ///////////////////////////////////////////////////////////////////// // tlsClKeyFile if(!GetStringValue(jtCfg, _CFG_KEY_NAME_TLS_CL_KEY_FILE, m_strTlsClKeyFile, strErr)) { rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str()); return false; } } else if(m_nTlsMode == MQTTCL_TLS_MODE_PSK) { ///////////////////////////////////////////////////////////////////// // tlsPsk if(!GetStringValue(jtCfg, _CFG_KEY_NAME_TLS_PSK, m_strTlsPSK, strErr)) { rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str()); return false; } } return true; } ///////////////////////////////////////////////////////////////////////////// bool CMqttClConfig::GetValue(CJson_t &rjtParent, const char *pszKey, CJson_t &rjtVal, std::string &strErr) { if(!rjtParent.GetValue(pszKey, rjtVal)) { strErr = formatString("Key \"%s\" not found", pszKey); return false; } return true; } ///////////////////////////////////////////////////////////////////////////// bool CMqttClConfig::GetBoolValue(CJson_t &rjtParent, const char *pszKey, bool &rbVal, std::string &strErr) { CJson_t jtVal; if(GetValue(rjtParent, pszKey, jtVal, strErr)) { int nType; switch((nType = jtVal.Type())) { case JSON_TRUE: case JSON_FALSE: rbVal = (nType == JSON_TRUE); return true; default: strErr = formatString("\"%s\" (type=%d) is not a boolean value", pszKey, nType); return false; } } return false; } ///////////////////////////////////////////////////////////////////////////// bool CMqttClConfig::GetIntValue(CJson_t &rjtParent, const char *pszKey, int &rnVal, std::string &strErr) { CJson_t jtVal; if(GetValue(rjtParent, pszKey, jtVal, strErr)) { if(json_is_integer(jtVal.operator const json_t*())) { rnVal = (int)::json_integer_value(jtVal); return true; } strErr = formatString("\"%s\" (type=%d) is not an integer value", pszKey, jtVal.Type()); } return false; } ///////////////////////////////////////////////////////////////////////////// bool CMqttClConfig::GetStringValue(CJson_t &rjtParent, const char *pszKey, std::string &rstrVal, std::string &strErr) { CJson_t jtVal; if(GetValue(rjtParent, pszKey, jtVal, strErr)) { if(json_is_string(jtVal.operator const json_t*())) { rstrVal = ::json_string_value(jtVal); return !rstrVal.empty(); } strErr = formatString("\"%s\" (type=%d) is not a string value", pszKey, jtVal.Type()); } rstrVal.clear(); return false; } ///////////////////////////////////////////////////////////////////////////// sa_family_t CMqttClConfig::GetDevIdInterfaceName(char *pszItfName, size_t nCChItfName, const char *pszRequested) { sa_family_t nFamily = 0; struct ifaddrs *ifaddr, *ifa; memset(pszItfName, 0, nCChItfName); if(getifaddrs(&ifaddr) == 0) { for(ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { if(!ifa->ifa_addr || (ifa->ifa_flags & IFF_LOOPBACK)) continue; if( ifa->ifa_addr->sa_family == AF_INET || ifa->ifa_addr->sa_family == AF_INET6) { if(!strcmp(ifa->ifa_name, pszRequested)) { strncpy(pszItfName, ifa->ifa_name, nCChItfName - 1); nFamily = ifa->ifa_addr->sa_family; break; } else if(!nFamily) { strncpy(pszItfName, ifa->ifa_name, nCChItfName - 1); nFamily = ifa->ifa_addr->sa_family; } } } freeifaddrs(ifaddr); } return nFamily; } const char* CMqttClConfig::GetMacAddress(std::string &s) { int fd; struct ifreq ifr; s.clear(); if((ifr.ifr_addr.sa_family = CMqttClConfig::GetDevIdInterfaceName(ifr.ifr_name, sizeof(ifr.ifr_name), "eth0"))) { if((fd = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) >= 0) { if( (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) && (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER)) { const char *m = (const char*)ifr.ifr_hwaddr.sa_data; s = formatString("%0.2hhX:%0.2hhX:%0.2hhX:%0.2hhX:%0.2hhX:%0.2hhX" , m[0], m[1], m[2], m[3], m[4], m[5]); } close(fd); } } return s.c_str(); } std::string CMqttClConfig::CreateDeviceID(const char *pszDevicePrefix) { std::string m, s; s = formatString("%s-%s", pszDevicePrefix, CMqttClConfig::GetMacAddress(m)); return s; }