diff --git a/.idea/.gitignore b/.idea/.gitignore index 73f69e09..b0811f16 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -6,3 +6,5 @@ /dataSources.local.xml # Editor-based HTTP Client requests /httpRequests/ +# GitHub Copilot persisted chat sessions +/copilot/chatSessions diff --git a/build b/build index 7730ef7f..a46c9d22 100644 --- a/build +++ b/build @@ -1 +1 @@ -89 \ No newline at end of file +91 \ No newline at end of file diff --git a/openapi/owgw.yaml b/openapi/owgw.yaml index 3aee8c96..e57fbc6f 100644 --- a/openapi/owgw.yaml +++ b/openapi/owgw.yaml @@ -551,6 +551,12 @@ components: lastModified: type: integer format: int64 + platform: + type: string + enum: + - AP + - SWITCH + default: AP DefaultConfigurationList: properties: @@ -1963,15 +1969,6 @@ paths: schema: type: string required: true - - in: query - name: deviceType - schema: - type: string - enum: - - AP - - SWITCH - required: false - default: AP requestBody: description: Information used to create the new device content: @@ -2018,15 +2015,6 @@ paths: schema: type: string required: true - - in: query - name: deviceType - schema: - type: string - enum: - - AP - - SWITCH - required: false - default: AP requestBody: description: Configuration details content: diff --git a/src/AP_WS_Connection.cpp b/src/AP_WS_Connection.cpp index c9459de1..bf1979ee 100644 --- a/src/AP_WS_Connection.cpp +++ b/src/AP_WS_Connection.cpp @@ -76,15 +76,11 @@ namespace OpenWifi { } AP_WS_Connection::~AP_WS_Connection() { -// poco_information(Logger_, fmt::format("DESTRUCTOR({}): 0 - Session={} Connection closed.", SerialNumber_, -// State_.sessionId)); -// std::lock_guard G(ConnectionMutex_); -// poco_information(Logger_, fmt::format("DESTRUCTOR({}): 1 - Session={} Connection closed.", SerialNumber_, -// State_.sessionId)); - EndConnection(false); - poco_debug(Logger_, fmt::format("TERMINATION({}): Session={}, Connection removed.", SerialNumber_, - State_.sessionId)); + std::lock_guard G(ConnectionMutex_); AP_WS_Server()->DecrementConnectionCount(); + EndConnection(); + poco_debug(Logger_, fmt::format("TERMINATION({}): Session={}, Connection removed.", SerialNumber_, + State_.sessionId)); } static void NotifyKafkaDisconnect(const std::string &SerialNumber, std::uint64_t uuid) { @@ -100,7 +96,7 @@ namespace OpenWifi { } } - void AP_WS_Connection::EndConnection(bool Clean) { + void AP_WS_Connection::EndConnection() { bool expectedValue=false; if (Dead_.compare_exchange_strong(expectedValue,true,std::memory_order_release,std::memory_order_relaxed)) { @@ -126,9 +122,7 @@ namespace OpenWifi { if(!SerialNumber_.empty()) { DeviceDisconnectionCleanup(SerialNumber_, uuid_); } - - if(Clean) - AP_WS_Server()->EndSession(State_.sessionId, SerialNumberInt_); + AP_WS_Server()->AddCleanupSession(State_.sessionId, SerialNumberInt_); } } diff --git a/src/AP_WS_Connection.h b/src/AP_WS_Connection.h index 5054b11d..4acd5657 100644 --- a/src/AP_WS_Connection.h +++ b/src/AP_WS_Connection.h @@ -30,7 +30,7 @@ namespace OpenWifi { Poco::Logger &L, std::pair, std::shared_ptr> R); ~AP_WS_Connection(); - void EndConnection(bool Clean = true); + void EndConnection(); void ProcessJSONRPCEvent(Poco::JSON::Object::Ptr &Doc); void ProcessJSONRPCResult(Poco::JSON::Object::Ptr Doc); void ProcessIncomingFrame(); @@ -108,7 +108,7 @@ namespace OpenWifi { void Start(); private: - std::recursive_mutex ConnectionMutex_; + mutable std::recursive_mutex ConnectionMutex_; std::mutex TelemetryMutex_; Poco::Logger &Logger_; std::shared_ptr Reactor_; diff --git a/src/AP_WS_Server.cpp b/src/AP_WS_Server.cpp index 0bbe3eaf..68f38a6d 100644 --- a/src/AP_WS_Server.cpp +++ b/src/AP_WS_Server.cpp @@ -200,9 +200,31 @@ namespace OpenWifi { Running_ = true; GarbageCollector_.setName("ws:garbage"); GarbageCollector_.start(*this); + + std::thread CleanupThread([this](){ CleanupSessions(); }); + CleanupThread.detach(); + return 0; } + void AP_WS_Server::CleanupSessions() { + + while(Running_) { + std::this_thread::sleep_for(std::chrono::seconds(10)); + + while(Running_ && !CleanupSessions_.empty()) { + std::pair Session; + { + std::lock_guard G(CleanupMutex_); + Session = CleanupSessions_.front(); + CleanupSessions_.pop_front(); + } + this->Logger().information(fmt::format("Cleaning up session: {} for device: {}", Session.first, Utils::IntToSerialNumber(Session.second))); + EndSession(Session.first, Session.second); + } + } + } + void AP_WS_Server::run() { uint64_t last_log = Utils::Now(), last_zombie_run = 0, @@ -236,29 +258,35 @@ namespace OpenWifi { waits = 0; auto hint = SerialNumbers_[hashIndex].begin(); while (hint != end(SerialNumbers_[hashIndex])) { - if (hint->second == nullptr) { poco_information( LocalLogger, fmt::format("Dead device found in hash index {}", hashIndex)); hint = SerialNumbers_[hashIndex].erase(hint); - continue; - } - auto Device = hint->second; - auto RightNow = Utils::Now(); - if (RightNow > Device->LastContact_ && - (RightNow - Device->LastContact_) > SessionTimeOut_) { - poco_information( - LocalLogger, - fmt::format("{}: Session seems idle. Controller disconnecting device.", - Device->SerialNumber_)); - hint = SerialNumbers_[hashIndex].erase(hint); - } else if (Device->State_.Connected) { - total_connected_time += - (RightNow - Device->State_.started); - ++hint; } else { - ++hint; + auto Device = hint->second; + auto RightNow = Utils::Now(); + if (Device->Dead_) { + AddCleanupSession(Device->State_.sessionId, Device->SerialNumberInt_); + ++hint; + // hint = SerialNumbers_[hashIndex].erase(hint); + } else if (RightNow > Device->LastContact_ && + (RightNow - Device->LastContact_) > SessionTimeOut_) { + poco_information( + LocalLogger, + fmt::format( + "{}: Session seems idle. Controller disconnecting device.", + Device->SerialNumber_)); + // hint = SerialNumbers_[hashIndex].erase(hint); + AddCleanupSession(Device->State_.sessionId, Device->SerialNumberInt_); + ++hint; + } else { + if (Device->State_.Connected) { + total_connected_time += + (RightNow - Device->State_.started); + } + ++hint; + } } } SerialNumbersMutex_[hashIndex].unlock(); @@ -272,8 +300,7 @@ namespace OpenWifi { } } - poco_information(LocalLogger, - fmt::format("Garbage collecting zombies... (step 2)")); + poco_information(LocalLogger, fmt::format("Garbage collecting zombies... (step 2)")); LeftOverSessions_ = 0; for (int i = 0; i < SessionHash::HashMax(); i++) { waits = 0; @@ -285,6 +312,10 @@ namespace OpenWifi { while (hint != end(Sessions_[i])) { if (hint->second == nullptr) { hint = Sessions_[i].erase(hint); + } else if (hint->second->Dead_) { + // hint = Sessions_[i].erase(hint); + AddCleanupSession(hint->second->State_.sessionId, hint->second->SerialNumberInt_); + ++hint; } else if (RightNow > hint->second->LastContact_ && (RightNow - hint->second->LastContact_) > SessionTimeOut_) { @@ -292,7 +323,9 @@ namespace OpenWifi { LocalLogger, fmt::format("{}: Session seems idle. Controller disconnecting device.", hint->second->SerialNumber_)); - hint = Sessions_[i].erase(hint); + AddCleanupSession(hint->second->State_.sessionId, hint->second->SerialNumberInt_); + ++hint; + // hint = Sessions_[i].erase(hint); } else { ++LeftOverSessions_; ++hint; @@ -309,10 +342,9 @@ namespace OpenWifi { } } - AverageDeviceConnectionTime_ = - NumberOfConnectedDevices_ > 0 - ? total_connected_time / NumberOfConnectedDevices_ - : 0; + AverageDeviceConnectionTime_ = NumberOfConnectedDevices_ > 0 + ? total_connected_time / NumberOfConnectedDevices_ + : 0; poco_information(LocalLogger, fmt::format("Garbage collecting zombies done...")); } catch (const Poco::Exception &E) { poco_error(LocalLogger, fmt::format("Poco::Exception: Garbage collecting zombies failed: {}", E.displayText())); @@ -323,12 +355,11 @@ namespace OpenWifi { } } + if(NumberOfConnectedDevices_) { if (last_garbage_run > 0) { AverageDeviceConnectionTime_ += (now - last_garbage_run); } - } else { - AverageDeviceConnectionTime_ = 0; } try { @@ -445,17 +476,21 @@ namespace OpenWifi { } void AP_WS_Server::StartSession(uint64_t session_id, uint64_t SerialNumber) { - auto deviceHash = MACHash::Hash(SerialNumber); auto sessionHash = SessionHash::Hash(session_id); - std::lock_guard SessionLock(SessionMutex_[sessionHash]); - auto SessionHint = Sessions_[sessionHash].find(session_id); - if (SessionHint != end(Sessions_[sessionHash])) { - std::lock_guard Lock(SerialNumbersMutex_[deviceHash]); - SerialNumbers_[deviceHash][SerialNumber] = SessionHint->second; + std::shared_ptr Connection; + { + std::lock_guard SessionLock(SessionMutex_[sessionHash]); + auto SessionHint = Sessions_[sessionHash].find(session_id); + if (SessionHint == end(Sessions_[sessionHash])) { + return; + } + Connection = SessionHint->second; Sessions_[sessionHash].erase(SessionHint); - } else { - poco_error(Logger(), fmt::format("StartSession: Could not find session '{}'", session_id)); } + + auto deviceHash = MACHash::Hash(SerialNumber); + std::lock_guard Lock(SerialNumbersMutex_[deviceHash]); + SerialNumbers_[deviceHash][SerialNumber] = Connection; } bool AP_WS_Server::EndSession(uint64_t session_id, uint64_t SerialNumber) { diff --git a/src/AP_WS_Server.h b/src/AP_WS_Server.h index a00587f4..a4669fba 100644 --- a/src/AP_WS_Server.h +++ b/src/AP_WS_Server.h @@ -203,6 +203,13 @@ namespace OpenWifi { --NumberOfConnectedDevices_; } + inline void AddCleanupSession(uint64_t session_id, uint64_t SerialNumber) { + std::lock_guard G(CleanupMutex_); + CleanupSessions_.emplace_back(session_id, SerialNumber); + } + + void CleanupSessions(); + private: std::array SessionMutex_; std::array>,SessionHashMax> Sessions_; @@ -222,6 +229,10 @@ namespace OpenWifi { bool SimulatorEnabled_ = false; bool AllowSerialNumberMismatch_ = true; + Poco::Thread CleanupThread_; + std::mutex CleanupMutex_; + std::deque> CleanupSessions_; + std::unique_ptr Reactor_pool_; std::atomic_bool Running_ = false; diff --git a/src/RESTAPI/RESTAPI_default_configuration.cpp b/src/RESTAPI/RESTAPI_default_configuration.cpp index 0edf9684..df3dcfe7 100644 --- a/src/RESTAPI/RESTAPI_default_configuration.cpp +++ b/src/RESTAPI/RESTAPI_default_configuration.cpp @@ -60,9 +60,13 @@ namespace OpenWifi { return BadRequest(RESTAPI::Errors::ModelIDListCannotBeEmpty); } - auto DeviceType = GetParameter("deviceType", "AP"); + DefConfig.Platform = DefConfig.Platform.empty() ? "AP" : DefConfig.Platform; + if(DefConfig.Platform != "AP" && DefConfig.Platform != "SWITCH") { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + std::string Error; - if (!ValidateUCentralConfiguration(ConfigurationValidator::GetType(DeviceType), + if (!ValidateUCentralConfiguration(ConfigurationValidator::GetType(DefConfig.Platform), DefConfig.Configuration, Error, GetBoolParameter("strict", false))) { return BadRequest(RESTAPI::Errors::ConfigBlockInvalid, Error); @@ -90,10 +94,20 @@ namespace OpenWifi { return NotFound(); } + if(Existing.Platform.empty()) { + Existing.Platform = "AP"; + } + + if(ParsedBody_->has("platform")) { + if(NewConfig.Platform.empty() || (NewConfig.Platform != "AP" && NewConfig.Platform != "SWITCH")) { + return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); + } + Existing.Platform = NewConfig.Platform; + } + if (!NewConfig.Configuration.empty()) { - auto DeviceType = GetParameter("deviceType", "AP"); std::string Error; - if (!ValidateUCentralConfiguration(ConfigurationValidator::GetType(DeviceType), + if (!ValidateUCentralConfiguration(ConfigurationValidator::GetType(Existing.Platform), NewConfig.Configuration, Error, GetBoolParameter("strict", false))) { return BadRequest(RESTAPI::Errors::ConfigBlockInvalid, Error); diff --git a/src/RESTObjects/RESTAPI_GWobjects.cpp b/src/RESTObjects/RESTAPI_GWobjects.cpp index 8a09d271..c796c4bb 100644 --- a/src/RESTObjects/RESTAPI_GWobjects.cpp +++ b/src/RESTObjects/RESTAPI_GWobjects.cpp @@ -184,15 +184,6 @@ namespace OpenWifi::GWObjects { return false; } - void DefaultConfiguration::to_json(Poco::JSON::Object &Obj) const { - EmbedDocument("configuration", Obj, Configuration); - field_to_json(Obj, "name", Name); - field_to_json(Obj, "modelIds", Models); - field_to_json(Obj, "description", Description); - field_to_json(Obj, "created", Created); - field_to_json(Obj, "lastModified", LastModified); - } - void DefaultFirmware::to_json(Poco::JSON::Object &Obj) const { field_to_json(Obj, "deviceType", deviceType); field_to_json(Obj, "description", Description); @@ -240,12 +231,25 @@ namespace OpenWifi::GWObjects { field_to_json(Obj, "deferred", deferred); } + void DefaultConfiguration::to_json(Poco::JSON::Object &Obj) const { + EmbedDocument("configuration", Obj, Configuration); + field_to_json(Obj, "name", Name); + field_to_json(Obj, "modelIds", Models); + field_to_json(Obj, "description", Description); + field_to_json(Obj, "created", Created); + field_to_json(Obj, "lastModified", LastModified); + field_to_json(Obj, "Platform", Platform); + } + bool DefaultConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) { try { - field_from_json(Obj, "name", Name); field_from_json(Obj, "configuration", Configuration); + field_from_json(Obj, "name", Name); field_from_json(Obj, "modelIds", Models); field_from_json(Obj, "description", Description); + field_from_json(Obj, "created", Created); + field_from_json(Obj, "lastModified", LastModified); + field_from_json(Obj, "Platform", Platform); return true; } catch (const Poco::Exception &E) { } diff --git a/src/RESTObjects/RESTAPI_GWobjects.h b/src/RESTObjects/RESTAPI_GWobjects.h index ccc7590e..f7a77275 100644 --- a/src/RESTObjects/RESTAPI_GWobjects.h +++ b/src/RESTObjects/RESTAPI_GWobjects.h @@ -186,6 +186,7 @@ namespace OpenWifi::GWObjects { std::string Description; uint64_t Created; uint64_t LastModified; + std::string Platform; void to_json(Poco::JSON::Object &Obj) const; bool from_json(const Poco::JSON::Object::Ptr &Obj); }; diff --git a/src/StorageService.h b/src/StorageService.h index 726790f2..d6fbaa89 100644 --- a/src/StorageService.h +++ b/src/StorageService.h @@ -200,6 +200,7 @@ namespace OpenWifi { bool GetDefaultConfigurations(uint64_t From, uint64_t HowMany, std::vector &Devices); bool FindDefaultConfigurationForModel(const std::string &Model, + const std::string &Platform, GWObjects::DefaultConfiguration &DefConfig); uint64_t GetDefaultConfigurationsCount(); bool DefaultConfigurationAlreadyExists(std::string &Name); diff --git a/src/storage/storage_defconfig.cpp b/src/storage/storage_defconfig.cpp index 36f8cc75..fc876a2d 100644 --- a/src/storage/storage_defconfig.cpp +++ b/src/storage/storage_defconfig.cpp @@ -19,18 +19,18 @@ namespace OpenWifi { "Models TEXT, " "Description TEXT, " "Created BIGINT , " - "LastModified BIGINT)"}; + "LastModified BIGINT, Platform TEXT )"}; const static std::string DB_DefConfig_SelectFields{"Name, " "Configuration, " "Models, " "Description, " "Created, " - "LastModified "}; + "LastModified, Platform "}; - const static std::string DB_DefConfig_InsertValues{"?,?,?,?,?,?"}; + const static std::string DB_DefConfig_InsertValues{"?,?,?,?,?,?,?"}; - typedef Poco::Tuple + typedef Poco::Tuple DefConfigRecordTuple; typedef std::vector DefConfigRecordList; @@ -41,6 +41,7 @@ namespace OpenWifi { T.Description = R.get<3>(); T.Created = R.get<4>(); T.LastModified = R.get<5>(); + T.Platform = R.get<6>(); } void Convert(const GWObjects::DefaultConfiguration &R, DefConfigRecordTuple &T) { @@ -50,6 +51,7 @@ namespace OpenWifi { T.set<3>(R.Description); T.set<4>(R.Created); T.set<5>(R.LastModified); + T.set<6>(R.Platform); } bool Storage::CreateDefaultConfiguration(std::string &Name, @@ -127,7 +129,7 @@ namespace OpenWifi { DefConfig.LastModified = Now; std::string St{"UPDATE DefaultConfigs SET Name=?, Configuration=?, Models=?, " - "Description=?, Created=? , LastModified=? WHERE Name=?"}; + "Description=?, Created=? , LastModified=? , Platform=? WHERE Name=?"}; DefConfigRecordTuple R; Convert(DefConfig, R); @@ -219,7 +221,7 @@ namespace OpenWifi { return false; } - bool Storage::FindDefaultConfigurationForModel(const std::string &Model, + bool Storage::FindDefaultConfigurationForModel(const std::string &Model, const std::string &Platform, GWObjects::DefaultConfiguration &DefConfig) { try { DefConfigRecordList Records; @@ -235,7 +237,7 @@ namespace OpenWifi { GWObjects::DefaultConfiguration Config; Convert(i, Config); for (const auto &j : Config.Models) { - if (j == "*" || j == Model) { + if ((j == "*" || j == Model) && (Poco::toUpper(Config.Platform) == Poco::toUpper(Platform))){ DefConfig = Config; return true; } diff --git a/src/storage/storage_device.cpp b/src/storage/storage_device.cpp index b2f746f7..8275a07b 100644 --- a/src/storage/storage_device.cpp +++ b/src/storage/storage_device.cpp @@ -578,11 +578,11 @@ namespace OpenWifi { } if (!Found && AP_WS_Server()->UseDefaults() && - FindDefaultConfigurationForModel(Caps.Compatible(), DefConfig)) { + FindDefaultConfigurationForModel(Caps.Compatible(), Caps.Platform(), DefConfig)) { Config::Config NewConfig(DefConfig.Configuration); NewConfig.SetUUID(Now); D.Configuration = NewConfig.get(); - } else if (!Found) { + } else if (!Found && Caps.Platform()=="AP") { Config::Config NewConfig; NewConfig.SetUUID(Now); D.Configuration = NewConfig.get(); diff --git a/src/storage/storage_tables.cpp b/src/storage/storage_tables.cpp index a54eaa92..08d1cbb2 100644 --- a/src/storage/storage_tables.cpp +++ b/src/storage/storage_tables.cpp @@ -275,9 +275,21 @@ namespace OpenWifi { "Models TEXT, " "Description TEXT, " "Created BIGINT , " - "LastModified BIGINT)", + "LastModified BIGINT, Platform TEXT)", Poco::Data::Keywords::now; } + + std::vector Script{ + "alter table DefaultConfigs add column Platform text" + }; + + for (const auto &i : Script) { + try { + Sess << i, Poco::Data::Keywords::now; + } catch (...) { + } + } + return 0; } catch (const Poco::Exception &E) { Logger().log(E);