diff --git a/src/common/Defines.h b/src/common/Defines.h index d7bcf42b..5e5dd31f 100644 --- a/src/common/Defines.h +++ b/src/common/Defines.h @@ -134,6 +134,8 @@ const uint32_t REST_API_DEFAULT_PORT = 9990; const uint8_t BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U }; enum HOST_STATE { + FNE_STATE = 240U, + HOST_STATE_LOCKOUT = 250U, HOST_STATE_ERROR = 254U, HOST_STATE_QUIT = 255U, diff --git a/src/common/network/json/json.h b/src/common/network/json/json.h index 80d7ff76..36bb48d3 100644 --- a/src/common/network/json/json.h +++ b/src/common/network/json/json.h @@ -11,7 +11,7 @@ // Licensed under the BSD-2-Clause License (https://opensource.org/licenses/BSD-2-Clause) // /* - * Copyright (C) 2023 by Bryan Biedenkapp N2PLL + * Copyright (C) 2023,2024 by Bryan Biedenkapp N2PLL * Copyright 2009-2010 Cybozu Labs, Inc. * Copyright 2011-2014 Kazuho Oku * All rights reserved. @@ -57,6 +57,8 @@ #include #include +#include + // for isnan/isinf #if __cplusplus >= 201103L #include @@ -88,19 +90,6 @@ extern "C" { #endif #endif -// experimental support for int64_t (see README.mkdn for detail) -#ifdef PICOJSON_USE_INT64 -#define __STDC_FORMAT_MACROS -#include -#if __cplusplus >= 201103L -#include -#else -extern "C" { -#include -} -#endif -#endif - // to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0 #ifndef PICOJSON_USE_LOCALE #define PICOJSON_USE_LOCALE 1 @@ -136,10 +125,8 @@ namespace json null_type, boolean_type, number_type, -#ifdef PICOJSON_USE_INT64 - int64_type, -#endif int32_type, + uint64_type, uint32_type, uint16_type, uint8_type, @@ -165,10 +152,8 @@ namespace json union _storage { bool boolean_; double number_; -#ifdef PICOJSON_USE_INT64 - int64_t int64_; -#endif int int32_; + uint64_t uint64_; uint32_t uint32_; uint16_t uint16_; uint8_t uint8_; @@ -186,9 +171,6 @@ namespace json value(); value(int type, bool); explicit value(bool b); -#ifdef PICOJSON_USE_INT64 - explicit value(int64_t i); -#endif explicit value(double n); explicit value(const std::string &s); explicit value(const array &a); @@ -260,10 +242,8 @@ namespace json INIT(boolean_, false); INIT(number_, 0.0); -#ifdef PICOJSON_USE_INT64 - INIT(int64_, 0); -#endif INIT(int32_, 0); + INIT(uint64_, 0); INIT(uint32_, 0); INIT(uint16_, 0); INIT(uint8_, 0); @@ -281,12 +261,6 @@ namespace json u_.boolean_ = b; } -#ifdef PICOJSON_USE_INT64 - inline value::value(int64_t i) : type_(int64_type), u_() { - u_.int64_ = i; - } -#endif - inline value::value(double n) : type_(number_type), u_() { if ( #ifdef _MSC_VER @@ -409,10 +383,8 @@ namespace json IS(null, null) IS(bool, boolean) -#ifdef PICOJSON_USE_INT64 - IS(int64_t, int64) -#endif IS_NUMBER(int, int32) + IS_NUMBER(uint64_t, uint64) IS_NUMBER(uint32_t, uint32) IS_NUMBER(uint16_t, uint16) IS_NUMBER(uint8_t, uint8) @@ -424,11 +396,7 @@ namespace json #undef IS_NUMBER template <> inline bool value::is() const { - return type_ == number_type -#ifdef PICOJSON_USE_INT64 - || type_ == int64_type -#endif - ; + return type_ == number_type; } #define GET(ctype, var) \ @@ -445,17 +413,13 @@ namespace json GET(std::string, *u_.string_) GET(array, *u_.array_) GET(object, *u_.object_) -#ifdef PICOJSON_USE_INT64 - GET(double, - (type_ == int64_type && (const_cast(this)->type_ = number_type, (const_cast(this)->u_.number_ = u_.int64_)), - u_.number_)) - GET(int64_t, u_.int64_) -#else GET(double, u_.number_) -#endif GET(int, (type_ == number_type && (const_cast(this)->type_ = int32_type, (const_cast(this)->u_.int32_ = u_.number_)), u_.int32_)) + GET(uint64_t, + (type_ == number_type && (const_cast(this)->type_ = uint64_type, (const_cast(this)->u_.uint64_ = u_.number_)), + u_.uint64_)) GET(uint32_t, (type_ == number_type && (const_cast(this)->type_ = uint32_type, (const_cast(this)->u_.uint32_ = u_.number_)), u_.uint32_)) @@ -484,13 +448,11 @@ namespace json SET(double, number, u_.number_ = _val;) SET(int8_t, int32, u_.int32_ = _val;) SET(int, int32, u_.int32_ = _val;) + SET(uint64_t, uint64, u_.uint64_ = _val;) SET(uint32_t, uint32, u_.uint32_ = _val;) SET(uint16_t, uint16, u_.uint16_ = _val;) SET(uint8_t, uint8, u_.uint8_ = _val;) SET(float, float, u_.float_ = _val;) -#ifdef PICOJSON_USE_INT64 - SET(int64_t, int64, u_.int64_ = _val;) -#endif #undef SET #if PICOJSON_USE_RVALUE_REFERENCE @@ -515,18 +477,16 @@ namespace json return u_.boolean_; case number_type: return u_.number_ != 0; -#ifdef PICOJSON_USE_INT64 - case int64_type: - return u_.int64_ != 0; -#endif case int32_type: return u_.int32_ != 0; + case uint64_type: + return u_.uint64_ != 0; case uint32_type: return u_.uint32_ != 0; case uint16_type: - return u_.uint32_ != 0; + return u_.uint16_ != 0; case uint8_type: - return u_.uint32_ != 0; + return u_.uint8_ != 0; case float_type: return u_.float_ != 0; case string_type: @@ -579,13 +539,6 @@ namespace json return "null"; case boolean_type: return u_.boolean_ ? "true" : "false"; -#ifdef PICOJSON_USE_INT64 - case int64_type: { - char buf[sizeof("-9223372036854775808")]; - SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); - return buf; - } -#endif case number_type: { char buf[256]; double tmp; @@ -608,6 +561,11 @@ namespace json SNPRINTF(buf, sizeof(buf), "%d", u_.int32_); return buf; } + case uint64_type: { + char buf[256]; + SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.uint64_); + return buf; + } case uint32_type: { char buf[256]; SNPRINTF(buf, sizeof(buf), "%u", u_.uint32_); @@ -650,14 +608,12 @@ namespace json return "null"; case boolean_type: return "boolean"; -#ifdef PICOJSON_USE_INT64 - case int64_type: - return "int64"; -#endif case number_type: return "number"; case int32_type: return "int32"; + case uint64_type: + return "uint64"; case uint32_type: return "uint32"; case uint16_type: @@ -1113,18 +1069,6 @@ namespace json return false; } -#ifdef PICOJSON_USE_INT64 - { - errno = 0; - intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); - if (errno == 0 && std::numeric_limits::min() <= ival && ival <= std::numeric_limits::max() && - endp == num_str.c_str() + num_str.size()) { - ctx.set_int64(ival); - return true; - } - } -#endif - f = strtod(num_str.c_str(), &endp); if (endp == num_str.c_str() + num_str.size()) { ctx.set_number(f); @@ -1154,12 +1098,6 @@ namespace json return false; } -#ifdef PICOJSON_USE_INT64 - bool set_int64(int64_t) { - return false; - } -#endif - bool set_number(double) { return false; } @@ -1212,13 +1150,6 @@ namespace json return true; } -#ifdef PICOJSON_USE_INT64 - bool set_int64(int64_t i) { - *out_ = value(i); - return true; - } -#endif - bool set_number(double f) { *out_ = value(f); return true; @@ -1300,12 +1231,6 @@ namespace json return true; } -#ifdef PICOJSON_USE_INT64 - bool set_int64(int64_t) { - return true; - } -#endif - bool set_number(double) { return true; } diff --git a/src/fne/HostFNE.cpp b/src/fne/HostFNE.cpp index 663c73a8..3021448a 100644 --- a/src/fne/HostFNE.cpp +++ b/src/fne/HostFNE.cpp @@ -429,6 +429,10 @@ bool HostFNE::createMasterNetwork() m_network->setLookups(m_ridLookup, m_tidLookup); + if (m_RESTAPI != nullptr) { + m_RESTAPI->setNetwork(m_network); + } + bool ret = m_network->open(); if (!ret) { delete m_network; diff --git a/src/fne/network/FNENetwork.cpp b/src/fne/network/FNENetwork.cpp index 3f02dfad..a01093f8 100644 --- a/src/fne/network/FNENetwork.cpp +++ b/src/fne/network/FNENetwork.cpp @@ -485,7 +485,9 @@ void FNENetwork::clock(uint32_t ms) // validate peer (simple validation really) if (connection.connected() && connection.address() == ip) { uint32_t pingsRx = connection.pingsReceived(); - connection.pingsReceived(pingsRx++); + pingsRx++; + + connection.pingsReceived(pingsRx); connection.lastPing(now); connection.pktLastSeq(connection.pktLastSeq() + 1); @@ -493,7 +495,7 @@ void FNENetwork::clock(uint32_t ms) writePeerCommand(peerId, { NET_FUNC_PONG, NET_SUBFUNC_NOP }); if (m_debug) { - LogDebug(LOG_NET, "PEER %u ping received and answered", peerId); + LogDebug(LOG_NET, "PEER %u ping received and answered, pingsReceived = %u", peerId, connection.pingsReceived()); } } else { diff --git a/src/fne/network/FNENetwork.h b/src/fne/network/FNENetwork.h index 056f9a37..ce8e1192 100644 --- a/src/fne/network/FNENetwork.h +++ b/src/fne/network/FNENetwork.h @@ -42,6 +42,7 @@ // --------------------------------------------------------------------------- class HOST_SW_API HostFNE; +class HOST_SW_API RESTAPI; namespace network { namespace fne { class HOST_SW_API TagDMRData; } } namespace network { namespace fne { class HOST_SW_API TagP25Data; } } namespace network { namespace fne { class HOST_SW_API TagNXDNData; } } @@ -212,6 +213,7 @@ namespace network friend class fne::TagNXDNData; fne::TagNXDNData* m_tagNXDN; + friend class ::RESTAPI; HostFNE* m_host; std::string m_address; diff --git a/src/fne/network/RESTAPI.cpp b/src/fne/network/RESTAPI.cpp index e3da6fb8..39e47916 100644 --- a/src/fne/network/RESTAPI.cpp +++ b/src/fne/network/RESTAPI.cpp @@ -154,6 +154,7 @@ RESTAPI::RESTAPI(const std::string& address, uint16_t port, const std::string& p m_passwordHash(nullptr), m_debug(debug), m_host(host), + m_network(nullptr), m_ridLookup(nullptr), m_tidLookup(nullptr), m_authTokens() @@ -204,6 +205,15 @@ void RESTAPI::setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRu m_tidLookup = tidLookup; } +/// +/// Sets the instance of the FNE network. +/// +/// FNE Network Instance +void RESTAPI::setNetwork(network::FNENetwork* network) +{ + m_network = network; +} + /// /// Opens connection to the network. /// @@ -245,6 +255,8 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(PUT_AUTHENTICATE).put(REST_API_BIND(RESTAPI::restAPI_PutAuth, this)); m_dispatcher.match(GET_VERSION).get(REST_API_BIND(RESTAPI::restAPI_GetVersion, this)); + m_dispatcher.match(GET_STATUS).get(REST_API_BIND(RESTAPI::restAPI_GetStatus, this)); + m_dispatcher.match(GET_PEERLIST).get(REST_API_BIND(RESTAPI::restAPI_GetPeerList, this)); } /// @@ -394,3 +406,86 @@ void RESTAPI::restAPI_GetVersion(const HTTPPayload& request, HTTPPayload& reply, reply.payload(response); } + +/// +/// +/// +/// +/// +/// +void RESTAPI::restAPI_GetStatus(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +{ + if (!validateAuth(request, reply)) { + return; + } + + json::object response = json::object(); + setResponseDefaultStatus(response); + + yaml::Node systemConf = m_host->m_conf["system"]; + yaml::Node masterConf = m_host->m_conf["master"]; + { + uint8_t state = FNE_STATE; + response["state"].set(state); + response["dmrEnabled"].set(m_host->m_dmrEnabled); + response["p25Enabled"].set(m_host->m_p25Enabled); + response["nxdnEnabled"].set(m_host->m_nxdnEnabled); + + uint32_t peerId = masterConf["peerId"].as(); + response["peerId"].set(peerId); + } + + reply.payload(response); +} + +/// +/// +/// +/// +/// +/// +void RESTAPI::restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +{ + if (!validateAuth(request, reply)) { + return; + } + + json::object response = json::object(); + setResponseDefaultStatus(response); + + json::array peers = json::array(); + if (m_network != nullptr) { + if (m_network->m_peers.size() > 0) { + for (auto entry : m_network->m_peers) { + uint32_t peerId = entry.first; + network::FNEPeerConnection peer = entry.second; + + json::object peerObj = json::object(); + peerObj["peerId"].set(peerId); + + std::string address = peer.address(); + peerObj["address"].set(address); + uint16_t port = peer.port(); + peerObj["port"].set(port); + bool connected = peer.connected(); + peerObj["connected"].set(connected); + uint32_t connectionState = (uint32_t)peer.connectionState(); + peerObj["connectionState"].set(connectionState); + uint32_t pingsReceived = peer.pingsReceived(); + peerObj["pingsReceived"].set(pingsReceived); + uint64_t lastPing = peer.lastPing(); + peerObj["lastPing"].set(lastPing); + + json::object peerConfig = peer.config(); + if (peerConfig["rcon"].is()) + peerConfig.erase("rcon"); + peerObj["config"].set(peerConfig); + + peers.push_back(json::value(peerObj)); + } + } + } + + response["peers"].set(peers); + reply.payload(response); +} diff --git a/src/fne/network/RESTAPI.h b/src/fne/network/RESTAPI.h index 3bedfad2..30f490a8 100644 --- a/src/fne/network/RESTAPI.h +++ b/src/fne/network/RESTAPI.h @@ -44,6 +44,7 @@ // --------------------------------------------------------------------------- class HOST_SW_API HostFNE; +namespace network { class HOST_SW_API FNENetwork; } // --------------------------------------------------------------------------- // Class Declaration @@ -59,6 +60,8 @@ public: /// Sets the instances of the Radio ID and Talkgroup ID lookup tables. void setLookups(::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupRulesLookup* tidLookup); + /// Sets the instance of the FNE network. + void setNetwork(::network::FNENetwork* network); /// Opens connection to the network. bool open(); @@ -79,6 +82,7 @@ private: bool m_debug; HostFNE* m_host; + network::FNENetwork *m_network; ::lookups::RadioIdLookup* m_ridLookup; ::lookups::TalkgroupRulesLookup* m_tidLookup; @@ -102,6 +106,10 @@ private: /// void restAPI_GetVersion(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /// + void restAPI_GetStatus(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); + /// + void restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match); }; #endif // __REST_API_H__ diff --git a/src/fne/network/RESTDefines.h b/src/fne/network/RESTDefines.h index def01b94..c27e935d 100644 --- a/src/fne/network/RESTDefines.h +++ b/src/fne/network/RESTDefines.h @@ -23,19 +23,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#if !defined(__REST_DEFINES_H__) -#define __REST_DEFINES_H__ +#if !defined(__FNE_REST_DEFINES_H__) +#define __FNE_REST_DEFINES_H__ #include "fne/Defines.h" +#include "host/network/RESTDefines.h" // --------------------------------------------------------------------------- // Constants // --------------------------------------------------------------------------- -#define DVM_REST_RAND_MAX 0xfffffffffffffffe +#define GET_PEERLIST "/peerlist" -#define PUT_AUTHENTICATE "/auth" - -#define GET_VERSION "/version" - -#endif // __REST_API_H__ +#endif // __FNE_REST_DEFINES_H__ diff --git a/src/host/network/RESTDefines.h b/src/host/network/RESTDefines.h index 5bbb6e14..d02deb73 100644 --- a/src/host/network/RESTDefines.h +++ b/src/host/network/RESTDefines.h @@ -103,4 +103,4 @@ #define GET_NXDN_DUMP_RCCH GET_NXDN_DUMP_RCCH_BASE"(\\d+)" #define GET_NXDN_CC_DEDICATED "/nxdn/cc-enable" -#endif // __REST_API_H__ +#endif // __REST_DEFINES_H__ diff --git a/src/remote/RESTClientMain.cpp b/src/remote/RESTClientMain.cpp index 12fd4326..701d26f5 100644 --- a/src/remote/RESTClientMain.cpp +++ b/src/remote/RESTClientMain.cpp @@ -25,6 +25,7 @@ */ #include "remote/RESTClient.h" #include "host/network/RESTDefines.h" +#include "fne/network/RESTDefines.h" #include "common/Thread.h" #include "common/Log.h" #include "common/Utils.h" @@ -53,6 +54,7 @@ #define RCD_GET_VERSION "version" #define RCD_GET_STATUS "status" #define RCD_GET_VOICE_CH "voice-ch" +#define RCD_GET_PEERLIST "peerlist" #define RCD_MODE "mdm-mode" #define RCD_MODE_OPT_IDLE "idle" @@ -187,6 +189,7 @@ void usage(const char* message, const char* arg) reply += " version Display current version of host\r\n"; reply += " status Display current settings and operation mode\r\n"; reply += " voice-ch Retrieves the list of configured voice channels\r\n"; + reply += " peerlist Retrieves the list of connected peers (FNE only)\r\n"; reply += "\r\n"; reply += " mdm-mode Set current mode of host (idle, lockout, dmr, p25, nxdn)\r\n"; reply += " mdm-kill Causes the host to quit\r\n"; @@ -429,6 +432,9 @@ int main(int argc, char** argv) else if (rcom == RCD_GET_VOICE_CH) { retCode = client->send(HTTP_GET, GET_VOICE_CH, json::object(), response); } + else if (rcom == RCD_GET_PEERLIST) { + retCode = client->send(HTTP_GET, GET_PEERLIST, json::object(), response); + } else if (rcom == RCD_MODE && argCnt >= 1U) { std::string mode = getArgString(args, 0U);