mqttcfg.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. #include <stdio.h>
  2. #include <limits.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include "fileutil.h"
  6. #include "strutil.h"
  7. #include "mqttcfg.h"
  8. #include "debug.h"
  9. #define _DEFAULT_CFG_FILE_NAME "mqttcl.cfg.json"
  10. #define _CFG_KEY_NAME_BROKER_ADDR "brokerAddr"
  11. #define _CFG_KEY_NAME_BROKER_PORT "brokerPort"
  12. #define _CFG_KEY_NAME_DEFAULT_QOS "defaultQos"
  13. #define _CFG_KEY_NAME_DEFAULT_RETAIN "defaultRetain"
  14. #define _CFG_KEY_NAME_DEVICE_PREFIX "devicePrefix"
  15. #define _CFG_KEY_NAME_DEVICE_ID "deviceID"
  16. #define _CFG_KEY_NAME_TLS_MODE "tlsMode"
  17. #define _CFG_KEY_NAME_TLS_CA_CRT_FILE "tlsCaCrtFile"
  18. #define _CFG_KEY_NAME_TLS_CL_CRT_FILE "tlsClCrtFile"
  19. #define _CFG_KEY_NAME_TLS_CL_KEY_FILE "tlsClKeyFile"
  20. #define _CFG_KEY_NAME_TLS_PSK "tlsPsk"
  21. #define _CFG_KEY_NAME_LAST_WILL_MESSAGE "lastWillMessage"
  22. #define _CFG_KEY_NAME_LAST_WILL_TOPIC "lastWillTopic"
  23. #define _CFG_KEY_NAME_LAST_WILL_QOS "lastWillQos"
  24. #define _CFG_KEY_NAME_LAST_WILL_RETAIN "lastWillRetain"
  25. #define _CFG_KEY_NAME_LAST_WILL_ON_EXIT "lastWillOnExit"
  26. #define _CFG_KEY_NAME_LAST_WILL_ON_EXIT_MSG "lastWillOnExitMsg"
  27. #define _CFG_KEY_NAME_CONNECT_MESSAGE "connectMessage"
  28. #define _CFG_KEY_NAME_CONNECT_TOPIC "connectTopic"
  29. #define _CFG_KEY_NAME_CONNECT_QOS "connectQos"
  30. #define _CFG_KEY_NAME_CONNECT_RETAIN "connectRetain"
  31. #define _CFG_KEY_NAME_MAX_KEEP_ALIVE "maxKeepAlive"
  32. #define _CFG_KEY_NAME_PREFIX_STRING "topicPrefix"
  33. /////////////////////////////////////////////////////////////////////////////
  34. CMqttClConfig::CMqttClConfig(const char *pszShmUuid) : m_strShmID(formatString("SHM-%s", strucase(pszShmUuid).c_str())),
  35. m_nBrokerPort(0),
  36. m_nDefaultQOS(0),
  37. m_bDefaultRetain(MQTTCL_DEFAULT_RETAIN),
  38. m_nLastWillQos(m_nDefaultQOS),
  39. m_bLastWillRetain(MQTTCL_DEFAULT_RETAIN),
  40. m_bHasLastWill(false),
  41. m_bLastWillOnExit(false),
  42. m_nConnectQos(m_nDefaultQOS),
  43. m_bConnectRetain(MQTTCL_DEFAULT_RETAIN),
  44. m_bHasConnect(false),
  45. m_bHasPrefix(false),
  46. m_nTlsMode(0),
  47. m_nMaxKeepAlive(MQTTCL_DEFAULT_MAX_KEEP_ALIVE_TIME)
  48. {
  49. }
  50. CMqttClConfig::~CMqttClConfig(void)
  51. {
  52. }
  53. /////////////////////////////////////////////////////////////////////////////
  54. // LoadCfg
  55. bool CMqttClConfig::LoadCfg(const char *pszCfgFilePath, CLogfile &rlf)
  56. {
  57. char szCfgFilePath[PATH_MAX];
  58. std::string strErr;
  59. if(!pszCfgFilePath)
  60. { // use default config file path
  61. pszCfgFilePath = ::BuildCanonicalFilePath(NULL, _DEFAULT_CFG_FILE_NAME, szCfgFilePath, sizeof(szCfgFilePath));
  62. }
  63. /////////////////////////////////////////////////////////////////////////
  64. // load and parse config file
  65. json_t *pjtCfg;
  66. json_error_t err;
  67. if(!(pjtCfg = ::json_load_file(pszCfgFilePath, JSON_REJECT_DUPLICATES, &err)))
  68. {
  69. rlf.Error("CMqttClConfig::LoadCfg: %s!\n", err.text);
  70. return false;
  71. }
  72. CJson_t jtCfg(pjtCfg, true);
  73. /////////////////////////////////////////////////////////////////////////
  74. // tlsMode
  75. if(!GetIntValue(jtCfg, _CFG_KEY_NAME_TLS_MODE, m_nTlsMode, strErr))
  76. {
  77. m_nTlsMode = MQTTCL_TLS_MODE_OFF;
  78. rlf.Warning("CMqttClConfig::LoadCfg: %s! TLS will not be used!\n", strErr.c_str());
  79. }
  80. else if(m_nTlsMode < MQTTCL_TLS_MODE_OFF)
  81. {
  82. rlf.Warning("CMqttClConfig::LoadCfg: Invalid TLS mode: %d! TLS will be disabled!\n", m_nTlsMode);
  83. m_nTlsMode = MQTTCL_TLS_MODE_OFF;
  84. }
  85. else if(m_nTlsMode > MQTTCL_TLS_MODE_PSK)
  86. {
  87. rlf.Warning("CMqttClConfig::LoadCfg: Invalid TLS mode: %d! TLS mode will be set to PSK!\n", m_nTlsMode);
  88. m_nTlsMode = MQTTCL_TLS_MODE_PSK;
  89. }
  90. /////////////////////////////////////////////////////////////////////////
  91. // brokerAddr
  92. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_BROKER_ADDR, m_strBrokerAddr, strErr))
  93. {
  94. rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str());
  95. return false;
  96. }
  97. /////////////////////////////////////////////////////////////////////////
  98. // brokerPort
  99. if(!GetIntValue(jtCfg, _CFG_KEY_NAME_BROKER_PORT, m_nBrokerPort, strErr))
  100. {
  101. m_nBrokerPort = (m_nTlsMode > MQTTCL_TLS_MODE_OFF) ? 8883 : 1883;
  102. rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default broker port %d!\n", strErr.c_str(), m_nBrokerPort);
  103. }
  104. else if(m_nBrokerPort < 0 || m_nBrokerPort > 0xffff)
  105. {
  106. rlf.Error("CMqttClConfig::LoadCfg: Invalid broker port number: %d!\n", m_nBrokerPort);
  107. return false;
  108. }
  109. /////////////////////////////////////////////////////////////////////////
  110. // defaultQos
  111. if(!GetIntValue(jtCfg, _CFG_KEY_NAME_DEFAULT_QOS, m_nDefaultQOS, strErr))
  112. {
  113. m_nDefaultQOS = MQTTCL_DEFAULT_QOS;
  114. rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default QOS: %d!\n", strErr.c_str(), m_nDefaultQOS);
  115. }
  116. else if(m_nDefaultQOS < MQTTCL_MIN_QOS)
  117. {
  118. rlf.Warning("CMqttClConfig::LoadCfg: Invalid QOS: %d - using %d!\n", m_nDefaultQOS, MQTTCL_MIN_QOS);
  119. m_nDefaultQOS = MQTTCL_MIN_QOS;
  120. }
  121. else if(m_nDefaultQOS > MQTTCL_MAX_QOS)
  122. {
  123. rlf.Warning("CMqttClConfig::LoadCfg: Invalid QOS: %d - using %d!\n", m_nDefaultQOS, MQTTCL_MAX_QOS);
  124. m_nDefaultQOS = MQTTCL_MAX_QOS;
  125. }
  126. /////////////////////////////////////////////////////////////////////////
  127. // defaultRetain
  128. if(!GetBoolValue(jtCfg, _CFG_KEY_NAME_DEFAULT_RETAIN, m_bDefaultRetain, strErr))
  129. {
  130. m_bDefaultRetain = MQTTCL_DEFAULT_RETAIN;
  131. rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default retain \"%s\"!\n", strErr.c_str(), m_bDefaultRetain ? "true" : "false");
  132. }
  133. /////////////////////////////////////////////////////////////////////////
  134. // lastWillMessage
  135. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_MESSAGE, m_strLastWillMessage, strErr))
  136. {
  137. rlf.Info("CMqttClConfig::LoadCfg: %s! No Last Will Message provided.\n", strErr.c_str());
  138. }
  139. if((m_bHasLastWill = !m_strLastWillMessage.empty()))
  140. {
  141. /////////////////////////////////////////////////////////////////////
  142. // lastWillTopic
  143. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_TOPIC, m_strLastWillTopic, strErr))
  144. {
  145. m_strLastWillTopic = MQTTCL_DEFAULT_LAST_WILL_CONNECT_TOPIC;
  146. rlf.Info("CMqttClConfig::LoadCfg: %s! Setting Last Will Topic to default: \"%s\".\n", strErr.c_str()), m_strLastWillTopic.c_str();
  147. }
  148. /////////////////////////////////////////////////////////////////////
  149. // lastWillQos
  150. if(!GetIntValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_QOS, m_nLastWillQos, strErr))
  151. {
  152. m_nLastWillQos = m_nDefaultQOS;
  153. rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Last Will Qos: %d!\n", strErr.c_str(), m_nLastWillQos);
  154. }
  155. /////////////////////////////////////////////////////////////////////
  156. // lastWillRetain
  157. if(!GetBoolValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_RETAIN, m_bLastWillRetain, strErr))
  158. {
  159. m_bLastWillRetain = MQTTCL_DEFAULT_RETAIN;
  160. rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Last Will retain: %s!\n", strErr.c_str(), m_bLastWillRetain ? "true" : "false");
  161. }
  162. /////////////////////////////////////////////////////////////////////
  163. // lastWillOnExit
  164. if(!GetBoolValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_ON_EXIT, m_bLastWillOnExit, strErr))
  165. {
  166. m_bLastWillOnExit = false;
  167. rlf.Warning("CMqttClConfig::LoadCfg: %s! No Last Will on Exit.\n", strErr.c_str());
  168. }
  169. if(m_bLastWillOnExit)
  170. {
  171. /////////////////////////////////////////////////////////////////
  172. // lastWillOnExitMsg
  173. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_LAST_WILL_ON_EXIT_MSG, m_strLastWillOnExitMsg, strErr))
  174. {
  175. m_strLastWillOnExitMsg = m_strLastWillMessage;
  176. rlf.Info("CMqttClConfig::LoadCfg: %s! Setting Last Will Exit Message to default: \"%s\".\n", strErr.c_str()), m_strLastWillOnExitMsg.c_str();
  177. }
  178. }
  179. }
  180. /////////////////////////////////////////////////////////////////////////
  181. // connectMessage
  182. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_CONNECT_MESSAGE, m_strConnectMessage, strErr))
  183. {
  184. rlf.Info("CMqttClConfig::LoadCfg: %s! No Connect Message provided.\n", strErr.c_str());
  185. }
  186. if((m_bHasConnect = !m_strConnectMessage.empty()))
  187. {
  188. /////////////////////////////////////////////////////////////////////
  189. // connectTopic
  190. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_CONNECT_TOPIC, m_strConnectTopic, strErr))
  191. {
  192. m_strConnectTopic = m_strLastWillTopic;
  193. rlf.Info("CMqttClConfig::LoadCfg: %s! Setting Connect Topic to default: \"%s\".\n", strErr.c_str()), m_strLastWillTopic.c_str();
  194. }
  195. /////////////////////////////////////////////////////////////////////
  196. // connectQos
  197. if(!GetIntValue(jtCfg, _CFG_KEY_NAME_CONNECT_QOS, m_nConnectQos, strErr))
  198. {
  199. m_nConnectQos = m_nLastWillQos;
  200. rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Connect Qos: %d!\n", strErr.c_str(), m_nConnectQos);
  201. }
  202. /////////////////////////////////////////////////////////////////////
  203. // connectRetain
  204. if(!GetBoolValue(jtCfg, _CFG_KEY_NAME_CONNECT_RETAIN, m_bConnectRetain, strErr))
  205. {
  206. m_bConnectRetain = m_bLastWillRetain;
  207. rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Last Will retain: %s!\n", strErr.c_str(), m_bConnectRetain ? "true" : "false");
  208. }
  209. }
  210. /////////////////////////////////////////////////////////////////////////
  211. // maxKeepAlive
  212. if(!GetIntValue(jtCfg, _CFG_KEY_NAME_MAX_KEEP_ALIVE, m_nMaxKeepAlive, strErr))
  213. {
  214. m_nMaxKeepAlive = MQTTCL_DEFAULT_MAX_KEEP_ALIVE_TIME;
  215. rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default Keep-alive time: %d!\n", strErr.c_str(), m_nMaxKeepAlive);
  216. }
  217. /////////////////////////////////////////////////////////////////////////
  218. // devicePrefix
  219. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_DEVICE_PREFIX, m_strDevicePrefix, strErr))
  220. {
  221. m_strDevicePrefix = MQTTCL_DEVICE_PREFIX;
  222. rlf.Warning("CMqttClConfig::LoadCfg: %s! Using default device prefix \"%s\"!\n", strErr.c_str(), m_strDevicePrefix.c_str());
  223. }
  224. /////////////////////////////////////////////////////////////////////////
  225. // deviceID
  226. if(GetStringValue(jtCfg, _CFG_KEY_NAME_DEVICE_ID, m_strDeviceID, strErr))
  227. {
  228. rlf.Info("CMqttClConfig::LoadCfg: Using configured device ID: \"%s\"!\n", m_strDeviceID.c_str());
  229. }
  230. else
  231. {
  232. m_strDeviceID = CreateDeviceID(m_strDevicePrefix.c_str());
  233. }
  234. /////////////////////////////////////////////////////////////////////////
  235. // topicPrefix string
  236. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_PREFIX_STRING, m_strPrefix, strErr))
  237. {
  238. m_strPrefix = formatString("%s/%s", GetDeviceID(), GetShmID());
  239. }
  240. /////////////////////////////////////////////////////////////////////////
  241. // m_nTlsMode == (MQTTCL_TLS_MODE_CRT || MQTTCL_TLS_MODE_PSK)
  242. if(m_nTlsMode == MQTTCL_TLS_MODE_CRT)
  243. {
  244. /////////////////////////////////////////////////////////////////////
  245. // tlsCaCrtFile
  246. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_TLS_CA_CRT_FILE, m_strTlsCaCrtFile, strErr))
  247. {
  248. rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str());
  249. return false;
  250. }
  251. /////////////////////////////////////////////////////////////////////
  252. // tlsClCrtFile
  253. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_TLS_CL_CRT_FILE, m_strTlsClCrtFile, strErr))
  254. {
  255. rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str());
  256. return false;
  257. }
  258. /////////////////////////////////////////////////////////////////////
  259. // tlsClKeyFile
  260. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_TLS_CL_KEY_FILE, m_strTlsClKeyFile, strErr))
  261. {
  262. rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str());
  263. return false;
  264. }
  265. }
  266. else if(m_nTlsMode == MQTTCL_TLS_MODE_PSK)
  267. {
  268. /////////////////////////////////////////////////////////////////////
  269. // tlsPsk
  270. if(!GetStringValue(jtCfg, _CFG_KEY_NAME_TLS_PSK, m_strTlsPSK, strErr))
  271. {
  272. rlf.Error("CMqttClConfig::LoadCfg: %s!\n", strErr.c_str());
  273. return false;
  274. }
  275. }
  276. return true;
  277. }
  278. /////////////////////////////////////////////////////////////////////////////
  279. bool CMqttClConfig::GetValue(CJson_t &rjtParent, const char *pszKey, CJson_t &rjtVal, std::string &strErr)
  280. {
  281. if(!rjtParent.GetValue(pszKey, rjtVal))
  282. {
  283. strErr = formatString("Key \"%s\" not found", pszKey);
  284. return false;
  285. }
  286. return true;
  287. }
  288. /////////////////////////////////////////////////////////////////////////////
  289. bool CMqttClConfig::GetBoolValue(CJson_t &rjtParent, const char *pszKey, bool &rbVal, std::string &strErr)
  290. {
  291. CJson_t jtVal;
  292. if(GetValue(rjtParent, pszKey, jtVal, strErr))
  293. {
  294. int nType;
  295. switch((nType = jtVal.Type()))
  296. {
  297. case JSON_TRUE:
  298. case JSON_FALSE:
  299. rbVal = (nType == JSON_TRUE);
  300. return true;
  301. default:
  302. strErr = formatString("\"%s\" (type=%d) is not a boolean value", pszKey, nType);
  303. return false;
  304. }
  305. }
  306. return false;
  307. }
  308. /////////////////////////////////////////////////////////////////////////////
  309. bool CMqttClConfig::GetIntValue(CJson_t &rjtParent, const char *pszKey, int &rnVal, std::string &strErr)
  310. {
  311. CJson_t jtVal;
  312. if(GetValue(rjtParent, pszKey, jtVal, strErr))
  313. {
  314. if(json_is_integer(jtVal.operator const json_t*()))
  315. {
  316. rnVal = (int)::json_integer_value(jtVal);
  317. return true;
  318. }
  319. strErr = formatString("\"%s\" (type=%d) is not an integer value", pszKey, jtVal.Type());
  320. }
  321. return false;
  322. }
  323. /////////////////////////////////////////////////////////////////////////////
  324. bool CMqttClConfig::GetStringValue(CJson_t &rjtParent, const char *pszKey, std::string &rstrVal, std::string &strErr)
  325. {
  326. CJson_t jtVal;
  327. if(GetValue(rjtParent, pszKey, jtVal, strErr))
  328. {
  329. if(json_is_string(jtVal.operator const json_t*()))
  330. {
  331. rstrVal = ::json_string_value(jtVal);
  332. return !rstrVal.empty();
  333. }
  334. strErr = formatString("\"%s\" (type=%d) is not a string value", pszKey, jtVal.Type());
  335. }
  336. rstrVal.clear();
  337. return false;
  338. }
  339. /////////////////////////////////////////////////////////////////////////////
  340. sa_family_t CMqttClConfig::GetDevIdInterfaceName(char *pszItfName, size_t nCChItfName, const char *pszRequested)
  341. {
  342. sa_family_t nFamily = 0;
  343. struct ifaddrs *ifaddr, *ifa;
  344. memset(pszItfName, 0, nCChItfName);
  345. if(getifaddrs(&ifaddr) == 0)
  346. {
  347. for(ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
  348. {
  349. if(!ifa->ifa_addr || (ifa->ifa_flags & IFF_LOOPBACK))
  350. continue;
  351. if( ifa->ifa_addr->sa_family == AF_INET ||
  352. ifa->ifa_addr->sa_family == AF_INET6)
  353. {
  354. if(!strcmp(ifa->ifa_name, pszRequested))
  355. {
  356. strncpy(pszItfName, ifa->ifa_name, nCChItfName - 1);
  357. nFamily = ifa->ifa_addr->sa_family;
  358. break;
  359. }
  360. else if(!nFamily)
  361. {
  362. strncpy(pszItfName, ifa->ifa_name, nCChItfName - 1);
  363. nFamily = ifa->ifa_addr->sa_family;
  364. }
  365. }
  366. }
  367. freeifaddrs(ifaddr);
  368. }
  369. return nFamily;
  370. }
  371. const char* CMqttClConfig::GetMacAddress(std::string &s)
  372. {
  373. int fd;
  374. struct ifreq ifr;
  375. s.clear();
  376. if((ifr.ifr_addr.sa_family = CMqttClConfig::GetDevIdInterfaceName(ifr.ifr_name, sizeof(ifr.ifr_name), "eth0")))
  377. {
  378. if((fd = socket(ifr.ifr_addr.sa_family, SOCK_DGRAM, 0)) >= 0)
  379. {
  380. if( (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) &&
  381. (ifr.ifr_hwaddr.sa_family == ARPHRD_ETHER))
  382. {
  383. const char *m = (const char*)ifr.ifr_hwaddr.sa_data;
  384. 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]);
  385. }
  386. close(fd);
  387. }
  388. }
  389. return s.c_str();
  390. }
  391. std::string CMqttClConfig::CreateDeviceID(const char *pszDevicePrefix)
  392. {
  393. std::string m, s;
  394. s = formatString("%s-%s", pszDevicePrefix, CMqttClConfig::GetMacAddress(m));
  395. return s;
  396. }