[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / network / WakeOnAccess.cpp
blob36ba50ef1248125925a521a15f070bc5b29021ac
1 /*
2 * Copyright (C) 2013-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "WakeOnAccess.h"
11 #include "DNSNameCache.h"
12 #include "ServiceBroker.h"
13 #include "application/ApplicationComponents.h"
14 #include "application/ApplicationPlayer.h"
15 #include "dialogs/GUIDialogKaiToast.h"
16 #include "dialogs/GUIDialogProgress.h"
17 #include "filesystem/SpecialProtocol.h"
18 #include "guilib/GUIComponent.h"
19 #include "guilib/GUIWindowManager.h"
20 #include "guilib/LocalizeStrings.h"
21 #include "messaging/ApplicationMessenger.h"
22 #include "network/Network.h"
23 #include "settings/AdvancedSettings.h"
24 #include "settings/MediaSourceSettings.h"
25 #include "settings/Settings.h"
26 #include "settings/SettingsComponent.h"
27 #include "settings/lib/Setting.h"
28 #include "utils/JobManager.h"
29 #include "utils/StringUtils.h"
30 #include "utils/URIUtils.h"
31 #include "utils/Variant.h"
32 #include "utils/XBMCTinyXML2.h"
33 #include "utils/XMLUtils.h"
34 #include "utils/XTimeUtils.h"
35 #include "utils/log.h"
37 #include <limits.h>
38 #include <mutex>
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
42 #include <sys/socket.h>
44 #ifdef HAS_UPNP
45 #include "network/upnp/UPnP.h"
46 #include <Platinum/Source/Platinum/Platinum.h>
47 #endif
49 #define DEFAULT_NETWORK_INIT_SEC (20) // wait 20 sec for network after startup or resume
50 #define DEFAULT_NETWORK_SETTLE_MS (500) // require 500ms of consistent network availability before trusting it
52 #define DEFAULT_TIMEOUT_SEC (5*60) // at least 5 minutes between each magic packets
53 #define DEFAULT_WAIT_FOR_ONLINE_SEC_1 (40) // wait at 40 seconds after sending magic packet
54 #define DEFAULT_WAIT_FOR_ONLINE_SEC_2 (40) // same for extended wait
55 #define DEFAULT_WAIT_FOR_SERVICES_SEC (5) // wait 5 seconds after host go online to launch file sharing daemons
57 using namespace std::chrono_literals;
59 static CDateTime upnpInitReady;
61 static int GetTotalSeconds(const CDateTimeSpan& ts)
63 int hours = ts.GetHours() + ts.GetDays() * 24;
64 int minutes = ts.GetMinutes() + hours * 60;
65 return ts.GetSeconds() + minutes * 60;
68 static unsigned long HostToIP(const std::string& host)
70 std::string ip;
71 CDNSNameCache::Lookup(host, ip);
72 return inet_addr(ip.c_str());
75 #define LOCALIZED(id) g_localizeStrings.Get(id)
77 static void ShowDiscoveryMessage(const char* function, const char* server_name, bool new_entry)
79 std::string message;
81 if (new_entry)
83 CLog::Log(LOGINFO, "{} - Create new entry for host '{}'", function, server_name);
84 message = StringUtils::Format(LOCALIZED(13035), server_name);
86 else
88 CLog::Log(LOGINFO, "{} - Update existing entry for host '{}'", function, server_name);
89 message = StringUtils::Format(LOCALIZED(13034), server_name);
91 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Info, LOCALIZED(13033), message, 4000, true, 3000);
94 struct UPnPServer
96 UPnPServer() : m_nextWake(CDateTime::GetCurrentDateTime()) {}
97 bool operator == (const UPnPServer& server) const { return server.m_uuid == m_uuid; }
98 bool operator != (const UPnPServer& server) const { return !(*this == server); }
99 bool operator == (const std::string& server_uuid) const { return server_uuid == m_uuid; }
100 bool operator != (const std::string& server_uuid) const { return !(*this == server_uuid); }
101 std::string m_name;
102 std::string m_uuid;
103 std::string m_mac;
104 CDateTime m_nextWake;
107 static UPnPServer* LookupUPnPServer(std::vector<UPnPServer>& list, const std::string& uuid)
109 auto serverIt = find(list.begin(), list.end(), uuid);
111 return serverIt != list.end() ? &(*serverIt) : nullptr;
114 static void AddOrUpdateUPnPServer(std::vector<UPnPServer>& list, const UPnPServer& server)
116 auto serverIt = find(list.begin(), list.end(), server);
118 bool addNewEntry = serverIt == list.end();
120 if (addNewEntry)
121 list.push_back(server); // add server
122 else
123 *serverIt = server; // update existing server
125 ShowDiscoveryMessage(__FUNCTION__, server.m_name.c_str(), addNewEntry);
128 static void AddMatchingUPnPServers(std::vector<UPnPServer>& list, const std::string& host, const std::string& mac, const CDateTimeSpan& wakeupDelay)
130 #ifdef HAS_UPNP
131 while (CDateTime::GetCurrentDateTime() < upnpInitReady)
132 KODI::TIME::Sleep(1s);
134 PLT_SyncMediaBrowser* browser = UPNP::CUPnP::GetInstance()->m_MediaBrowser;
136 if (browser)
138 UPnPServer server;
139 server.m_nextWake += wakeupDelay;
141 for (NPT_List<PLT_DeviceDataReference>::Iterator device = browser->GetMediaServers().GetFirstItem(); device; ++device)
143 if (host == (const char*) (*device)->GetURLBase().GetHost())
145 server.m_name = (*device)->GetFriendlyName();
146 server.m_uuid = (*device)->GetUUID();
147 server.m_mac = mac;
149 AddOrUpdateUPnPServer(list, server);
153 #endif
156 static std::string LookupUPnPHost(const std::string& uuid)
158 #ifdef HAS_UPNP
159 UPNP::CUPnP* upnp = UPNP::CUPnP::GetInstance();
161 if (!upnp->IsClientStarted())
163 upnp->StartClient();
165 upnpInitReady = CDateTime::GetCurrentDateTime() + CDateTimeSpan(0, 0, 0, 10);
168 PLT_SyncMediaBrowser* browser = upnp->m_MediaBrowser;
170 PLT_DeviceDataReference device;
172 if (browser && NPT_SUCCEEDED(browser->FindServer(uuid.c_str(), device)) && !device.IsNull())
173 return (const char*)device->GetURLBase().GetHost();
174 #endif
176 return "";
179 CWakeOnAccess::WakeUpEntry::WakeUpEntry(bool isAwake)
180 : timeout(0, 0, 0, DEFAULT_TIMEOUT_SEC),
181 wait_online1_sec(DEFAULT_WAIT_FOR_ONLINE_SEC_1),
182 wait_online2_sec(DEFAULT_WAIT_FOR_ONLINE_SEC_2),
183 wait_services_sec(DEFAULT_WAIT_FOR_SERVICES_SEC),
184 nextWake(CDateTime::GetCurrentDateTime())
186 if (isAwake)
187 nextWake += timeout;
190 //**
192 class CMACDiscoveryJob : public CJob
194 public:
195 explicit CMACDiscoveryJob(const std::string& host) : m_host(host) {}
197 bool DoWork() override;
199 const std::string& GetMAC() const { return m_macAddress; }
200 const std::string& GetHost() const { return m_host; }
202 private:
203 std::string m_macAddress;
204 std::string m_host;
207 bool CMACDiscoveryJob::DoWork()
209 unsigned long ipAddress = HostToIP(m_host);
211 if (ipAddress == INADDR_NONE)
213 CLog::Log(LOGERROR, "{} - can't determine ip of '{}'", __FUNCTION__, m_host);
214 return false;
217 const std::vector<CNetworkInterface*>& ifaces = CServiceBroker::GetNetwork().GetInterfaceList();
218 for (const auto& it : ifaces)
220 if (it->GetHostMacAddress(ipAddress, m_macAddress))
221 return true;
224 return false;
227 //**
229 class WaitCondition
231 public:
232 virtual ~WaitCondition() = default;
233 virtual bool SuccessWaiting () const { return false; }
238 class NestDetect
240 public:
241 NestDetect() : m_gui_thread(CServiceBroker::GetAppMessenger()->IsProcessThread())
243 if (m_gui_thread)
244 ++m_nest;
246 ~NestDetect()
248 if (m_gui_thread)
249 m_nest--;
251 static int Level()
253 return m_nest;
255 bool IsNested() const
257 return m_gui_thread && m_nest > 1;
260 private:
261 static int m_nest;
262 const bool m_gui_thread;
264 int NestDetect::m_nest = 0;
268 class ProgressDialogHelper
270 public:
271 explicit ProgressDialogHelper(const std::string& heading)
273 if (CServiceBroker::GetAppMessenger()->IsProcessThread())
275 CGUIComponent *gui = CServiceBroker::GetGUI();
276 if (gui)
277 m_dialog = gui->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
280 if (m_dialog)
282 m_dialog->SetHeading(CVariant{heading});
283 m_dialog->SetLine(0, CVariant{""});
284 m_dialog->SetLine(1, CVariant{""});
285 m_dialog->SetLine(2, CVariant{""});
288 ~ProgressDialogHelper ()
290 if (m_dialog)
291 m_dialog->Close();
294 bool HasDialog() const { return m_dialog != 0; }
296 enum wait_result { TimedOut, Canceled, Success };
298 wait_result ShowAndWait (const WaitCondition& waitObj, unsigned timeOutSec, const std::string& line1)
300 auto timeOutMs = std::chrono::milliseconds(timeOutSec * 1000);
302 if (m_dialog)
304 m_dialog->SetLine(0, CVariant{line1});
306 m_dialog->SetPercentage(1); // avoid flickering by starting at 1% ..
309 XbmcThreads::EndTime<> end_time(timeOutMs);
311 while (!end_time.IsTimePast())
313 if (waitObj.SuccessWaiting())
314 return Success;
316 if (m_dialog)
318 if (!m_dialog->IsActive())
319 m_dialog->Open();
321 if (m_dialog->IsCanceled())
322 return Canceled;
324 m_dialog->Progress();
326 auto ms_passed = timeOutMs - end_time.GetTimeLeft();
328 int percentage = (ms_passed.count() * 100) / timeOutMs.count();
329 m_dialog->SetPercentage(std::max(percentage, 1)); // avoid flickering , keep minimum 1%
332 KODI::TIME::Sleep(m_dialog ? 20ms : 200ms);
335 return TimedOut;
338 private:
339 CGUIDialogProgress* m_dialog = 0;
342 class NetworkStartWaiter : public WaitCondition
344 public:
345 NetworkStartWaiter (unsigned settle_time_ms, const std::string& host) : m_settle_time_ms (settle_time_ms), m_host(host)
348 bool SuccessWaiting () const override
350 unsigned long address = ntohl(HostToIP(m_host));
351 bool online = CServiceBroker::GetNetwork().HasInterfaceForIP(address);
353 if (!online) // setup endtime so we dont return true until network is consistently connected
354 m_end.Set(std::chrono::milliseconds(m_settle_time_ms));
356 return online && m_end.IsTimePast();
358 private:
359 mutable XbmcThreads::EndTime<> m_end;
360 unsigned m_settle_time_ms;
361 const std::string m_host;
364 class PingResponseWaiter : public WaitCondition, private IJobCallback
366 public:
367 PingResponseWaiter(bool async, const CWakeOnAccess::WakeUpEntry& server) : m_server(server)
369 if (async)
371 CJob* job = new CHostProberJob(server);
372 m_jobId = CServiceBroker::GetJobManager()->AddJob(job, this);
375 ~PingResponseWaiter() override { CServiceBroker::GetJobManager()->CancelJob(m_jobId); }
376 bool SuccessWaiting () const override
378 return m_jobId ? m_hostOnline : Ping(m_server);
381 void OnJobComplete(unsigned int jobID, bool success, CJob *job) override
383 m_hostOnline = success;
386 static bool Ping(const CWakeOnAccess::WakeUpEntry& server, unsigned timeOutMs = 2000)
388 if (server.upnpUuid.empty())
390 unsigned long dst_ip = HostToIP(server.host);
392 return CServiceBroker::GetNetwork().PingHost(dst_ip, server.ping_port, timeOutMs, server.ping_mode & 1);
394 else // upnp mode
396 std::string host = LookupUPnPHost(server.upnpUuid);
398 if (host.empty())
400 KODI::TIME::Sleep(std::chrono::milliseconds(timeOutMs));
402 host = LookupUPnPHost(server.upnpUuid);
405 return !host.empty();
409 private:
410 class CHostProberJob : public CJob
412 public:
413 explicit CHostProberJob(const CWakeOnAccess::WakeUpEntry& server) : m_server (server) {}
415 bool DoWork() override
417 while (!ShouldCancel(0,0))
419 if (PingResponseWaiter::Ping(m_server))
420 return true;
422 return false;
425 private:
426 const CWakeOnAccess::WakeUpEntry& m_server;
429 const CWakeOnAccess::WakeUpEntry& m_server;
430 unsigned int m_jobId = 0;
431 bool m_hostOnline = false;
436 CWakeOnAccess::CWakeOnAccess()
437 : m_netinit_sec(DEFAULT_NETWORK_INIT_SEC) // wait for network to connect
438 , m_netsettle_ms(DEFAULT_NETWORK_SETTLE_MS) // wait for network to settle
442 CWakeOnAccess &CWakeOnAccess::GetInstance()
444 static CWakeOnAccess sWakeOnAccess;
445 return sWakeOnAccess;
448 bool CWakeOnAccess::WakeUpHost(const CURL& url)
450 const std::string& hostName = url.GetHostName();
452 if (!hostName.empty())
453 return WakeUpHost(hostName, url.Get(), url.IsProtocol("upnp"));
455 return true;
458 bool CWakeOnAccess::WakeUpHost(const std::string& hostName, const std::string& customMessage)
460 return WakeUpHost(hostName, customMessage, false);
463 bool CWakeOnAccess::WakeUpHost(const std::string& hostName, const std::string& customMessage, bool upnpMode)
465 if (!IsEnabled())
466 return true; // bail if feature is turned off
468 WakeUpEntry server;
470 if (FindOrTouchHostEntry(hostName, upnpMode, server))
472 CLog::Log(LOGINFO, "WakeOnAccess [{}] triggered by accessing : {}", server.friendlyName,
473 customMessage);
475 NestDetect nesting ; // detect recursive calls on gui thread..
477 if (nesting.IsNested()) // we might get in trouble if it gets called back in loop
478 CLog::Log(LOGWARNING, "WakeOnAccess recursively called on gui-thread [{}]",
479 NestDetect::Level());
481 bool ret = WakeUpHost(server);
483 if (!ret) // extra log if we fail for some reason
484 CLog::Log(LOGWARNING, "WakeOnAccess failed to bring up [{}] - there may be trouble ahead !",
485 server.friendlyName);
487 TouchHostEntry(hostName, upnpMode);
489 return ret;
491 return true;
494 bool CWakeOnAccess::WakeUpHost(const WakeUpEntry& server)
496 std::string heading = StringUtils::Format(LOCALIZED(13027), server.friendlyName);
498 ProgressDialogHelper dlg (heading);
501 NetworkStartWaiter waitObj (m_netsettle_ms, server.host); // wait until network connected before sending wake-on-lan
503 if (dlg.ShowAndWait (waitObj, m_netinit_sec, LOCALIZED(13028)) != ProgressDialogHelper::Success)
505 if (CServiceBroker::GetNetwork().IsConnected() && HostToIP(server.host) == INADDR_NONE)
507 // network connected (at least one interface) but dns-lookup failed (host by name, not ip-address), so dont abort yet
508 CLog::Log(LOGWARNING, "WakeOnAccess timeout/cancel while waiting for network (proceeding anyway)");
510 else
512 CLog::Log(LOGINFO, "WakeOnAccess timeout/cancel while waiting for network");
513 return false; // timedout or canceled ; give up
518 if (PingResponseWaiter::Ping(server, 500)) // quick ping with short timeout to not block too long
520 CLog::Log(LOGINFO, "WakeOnAccess success exit, server already running");
521 return true;
524 if (!CServiceBroker::GetNetwork().WakeOnLan(server.mac.c_str()))
526 CLog::Log(LOGERROR,"WakeOnAccess failed to send. (Is it blocked by firewall?)");
528 const auto& components = CServiceBroker::GetAppComponents();
529 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
530 if (CServiceBroker::GetAppMessenger()->IsProcessThread() || !appPlayer->IsPlaying())
531 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, heading, LOCALIZED(13029));
532 return false;
536 PingResponseWaiter waitObj (dlg.HasDialog(), server); // wait for ping response ..
538 ProgressDialogHelper::wait_result
539 result = dlg.ShowAndWait (waitObj, server.wait_online1_sec, LOCALIZED(13030));
541 if (result == ProgressDialogHelper::TimedOut)
542 result = dlg.ShowAndWait (waitObj, server.wait_online2_sec, LOCALIZED(13031));
544 if (result != ProgressDialogHelper::Success)
546 CLog::Log(LOGINFO, "WakeOnAccess timeout/cancel while waiting for response");
547 return false; // timedout or canceled
551 // we have ping response ; just add extra wait-for-services before returning if requested
554 WaitCondition waitObj ; // wait uninterruptible fixed time for services ..
556 dlg.ShowAndWait (waitObj, server.wait_services_sec, LOCALIZED(13032));
558 CLog::Log(LOGINFO, "WakeOnAccess sequence completed, server started");
560 return true;
563 bool CWakeOnAccess::FindOrTouchHostEntry(const std::string& hostName, bool upnpMode, WakeUpEntry& result)
565 std::unique_lock<CCriticalSection> lock(m_entrylist_protect);
567 bool need_wakeup = false;
569 UPnPServer* upnp = upnpMode ? LookupUPnPServer(m_UPnPServers, hostName) : nullptr;
571 for (auto& server : m_entries)
573 if (upnp ? StringUtils::EqualsNoCase(upnp->m_mac, server.mac) : StringUtils::EqualsNoCase(hostName, server.host))
575 CDateTime now = CDateTime::GetCurrentDateTime();
577 if (now >= (upnp ? upnp->m_nextWake : server.nextWake))
579 result = server;
581 result.friendlyName = upnp ? upnp->m_name : server.host;
583 if (upnp)
584 result.upnpUuid = upnp->m_uuid;
586 need_wakeup = true;
588 else // 'touch' next wakeup time
590 server.nextWake = now + server.timeout;
592 if (upnp)
593 upnp->m_nextWake = server.nextWake;
596 break;
600 return need_wakeup;
603 void CWakeOnAccess::TouchHostEntry(const std::string& hostName, bool upnpMode)
605 std::unique_lock<CCriticalSection> lock(m_entrylist_protect);
607 UPnPServer* upnp = upnpMode ? LookupUPnPServer(m_UPnPServers, hostName) : nullptr;
609 for (auto& server : m_entries)
611 if (upnp ? StringUtils::EqualsNoCase(upnp->m_mac, server.mac) : StringUtils::EqualsNoCase(hostName, server.host))
613 server.nextWake = CDateTime::GetCurrentDateTime() + server.timeout;
615 if (upnp)
616 upnp->m_nextWake = server.nextWake;
618 return;
623 static void AddHost (const std::string& host, std::vector<std::string>& hosts)
625 for (const auto& it : hosts)
626 if (StringUtils::EqualsNoCase(host, it))
627 return; // already there ..
629 if (!host.empty())
630 hosts.push_back(host);
633 static void AddHostFromDatabase(const DatabaseSettings& setting, std::vector<std::string>& hosts)
635 if (StringUtils::EqualsNoCase(setting.type, "mysql"))
636 AddHost(setting.host, hosts);
639 void CWakeOnAccess::QueueMACDiscoveryForHost(const std::string& host)
641 if (IsEnabled())
643 if (URIUtils::IsHostOnLAN(host, LanCheckMode::ANY_PRIVATE_SUBNET))
644 CServiceBroker::GetJobManager()->AddJob(new CMACDiscoveryJob(host), this);
645 else
646 CLog::Log(LOGINFO, "{} - skip Mac discovery for non-local host '{}'", __FUNCTION__, host);
650 static void AddHostsFromMediaSource(const CMediaSource& source, std::vector<std::string>& hosts)
652 for (const auto& it : source.vecPaths)
654 CURL url(it);
656 std::string host_name = url.GetHostName();
658 if (url.IsProtocol("upnp"))
659 host_name = LookupUPnPHost(host_name);
661 AddHost(host_name, hosts);
665 static void AddHostsFromVecSource(const VECSOURCES& sources, std::vector<std::string>& hosts)
667 for (const auto& it : sources)
668 AddHostsFromMediaSource(it, hosts);
671 static void AddHostsFromVecSource(const VECSOURCES* sources, std::vector<std::string>& hosts)
673 if (sources)
674 AddHostsFromVecSource(*sources, hosts);
677 void CWakeOnAccess::QueueMACDiscoveryForAllRemotes()
679 std::vector<std::string> hosts;
681 // add media sources
682 CMediaSourceSettings& ms = CMediaSourceSettings::GetInstance();
684 AddHostsFromVecSource(ms.GetSources("video"), hosts);
685 AddHostsFromVecSource(ms.GetSources("music"), hosts);
686 AddHostsFromVecSource(ms.GetSources("files"), hosts);
687 AddHostsFromVecSource(ms.GetSources("pictures"), hosts);
688 AddHostsFromVecSource(ms.GetSources("programs"), hosts);
690 const std::shared_ptr<CAdvancedSettings> advancedSettings = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings();
692 // add mysql servers
693 AddHostFromDatabase(advancedSettings->m_databaseVideo, hosts);
694 AddHostFromDatabase(advancedSettings->m_databaseMusic, hosts);
695 AddHostFromDatabase(advancedSettings->m_databaseEpg, hosts);
696 AddHostFromDatabase(advancedSettings->m_databaseTV, hosts);
698 // add from path substitutions ..
699 for (const auto& pathPair : advancedSettings->m_pathSubstitutions)
701 CURL url(pathPair.second);
702 AddHost (url.GetHostName(), hosts);
705 for (const std::string& host : hosts)
706 QueueMACDiscoveryForHost(host);
709 void CWakeOnAccess::SaveMACDiscoveryResult(const std::string& host, const std::string& mac)
711 CLog::Log(LOGINFO, "{} - Mac discovered for host '{}' -> '{}'", __FUNCTION__, host, mac);
713 for (auto& i : m_entries)
715 if (StringUtils::EqualsNoCase(host, i.host))
717 i.mac = mac;
718 ShowDiscoveryMessage(__FUNCTION__, host.c_str(), false);
720 AddMatchingUPnPServers(m_UPnPServers, host, mac, i.timeout);
721 SaveToXML();
722 return;
726 // not found entry to update - create using default values
727 WakeUpEntry entry (true);
728 entry.host = host;
729 entry.mac = mac;
730 m_entries.push_back(entry);
731 ShowDiscoveryMessage(__FUNCTION__, host.c_str(), true);
733 AddMatchingUPnPServers(m_UPnPServers, host, mac, entry.timeout);
734 SaveToXML();
737 void CWakeOnAccess::OnJobComplete(unsigned int jobID, bool success, CJob *job)
739 CMACDiscoveryJob* discoverJob = static_cast<CMACDiscoveryJob*>(job);
741 const std::string& host = discoverJob->GetHost();
742 const std::string& mac = discoverJob->GetMAC();
744 if (success)
746 std::unique_lock<CCriticalSection> lock(m_entrylist_protect);
748 SaveMACDiscoveryResult(host, mac);
750 else
752 CLog::Log(LOGERROR, "{} - Mac discovery failed for host '{}'", __FUNCTION__, host);
754 if (IsEnabled())
756 const std::string& heading = LOCALIZED(13033);
757 std::string message = StringUtils::Format(LOCALIZED(13036), host);
758 CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, heading, message, 4000, true, 3000);
763 void CWakeOnAccess::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
765 if (setting == nullptr)
766 return;
768 const std::string& settingId = setting->GetId();
769 if (settingId == CSettings::SETTING_POWERMANAGEMENT_WAKEONACCESS)
771 bool enabled = std::static_pointer_cast<const CSettingBool>(setting)->GetValue();
773 SetEnabled(enabled);
775 if (enabled)
776 QueueMACDiscoveryForAllRemotes();
780 std::string CWakeOnAccess::GetSettingFile()
782 return CSpecialProtocol::TranslatePath("special://profile/wakeonlan.xml");
785 void CWakeOnAccess::OnSettingsLoaded()
787 std::unique_lock<CCriticalSection> lock(m_entrylist_protect);
789 LoadFromXML();
792 void CWakeOnAccess::SetEnabled(bool enabled)
794 m_enabled = enabled;
796 CLog::Log(LOGINFO, "WakeOnAccess - Enabled:{}", m_enabled ? "TRUE" : "FALSE");
799 void CWakeOnAccess::LoadFromXML()
801 bool enabled = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_POWERMANAGEMENT_WAKEONACCESS);
803 CXBMCTinyXML2 xmlDoc;
804 if (!xmlDoc.LoadFile(GetSettingFile()))
806 if (enabled)
807 CLog::LogF(LOGINFO, "unable to load:{}", GetSettingFile());
808 return;
811 auto* rootElement = xmlDoc.RootElement();
812 if (StringUtils::CompareNoCase(rootElement->Value(), "onaccesswakeup"))
814 CLog::LogF(LOGERROR, "XML file {} doesn't contain <onaccesswakeup>", GetSettingFile());
815 return;
818 m_entries.clear();
820 CLog::Log(LOGINFO, "WakeOnAccess - Load settings :");
822 SetEnabled(enabled);
824 int tmp;
825 if (XMLUtils::GetInt(rootElement, "netinittimeout", tmp, 0, 5 * 60))
826 m_netinit_sec = tmp;
827 CLog::Log(LOGINFO, " -Network init timeout : [{}] sec", m_netinit_sec);
829 if (XMLUtils::GetInt(rootElement, "netsettletime", tmp, 0, 5 * 1000))
830 m_netsettle_ms = tmp;
831 CLog::Log(LOGINFO, " -Network settle time : [{}] ms", m_netsettle_ms);
833 const auto* pWakeUp = rootElement->FirstChildElement("wakeup");
834 while (pWakeUp != nullptr)
836 WakeUpEntry entry;
838 std::string strtmp;
839 if (XMLUtils::GetString(pWakeUp, "host", strtmp))
840 entry.host = strtmp;
842 if (XMLUtils::GetString(pWakeUp, "mac", strtmp))
843 entry.mac = strtmp;
845 if (entry.host.empty())
846 CLog::LogF(LOGERROR, "Missing <host> tag or it's empty");
847 else if (entry.mac.empty())
848 CLog::Log(LOGERROR, "Missing <mac> tag or it's empty");
849 else
851 if (XMLUtils::GetInt(pWakeUp, "pingport", tmp, 0, USHRT_MAX))
852 entry.ping_port = (unsigned short) tmp;
854 if (XMLUtils::GetInt(pWakeUp, "pingmode", tmp, 0, USHRT_MAX))
855 entry.ping_mode = (unsigned short) tmp;
857 if (XMLUtils::GetInt(pWakeUp, "timeout", tmp, 10, 12 * 60 * 60))
858 entry.timeout.SetDateTimeSpan (0, 0, 0, tmp);
860 if (XMLUtils::GetInt(pWakeUp, "waitonline", tmp, 0, 10 * 60)) // max 10 minutes
861 entry.wait_online1_sec = tmp;
863 if (XMLUtils::GetInt(pWakeUp, "waitonline2", tmp, 0, 10 * 60)) // max 10 minutes
864 entry.wait_online2_sec = tmp;
866 if (XMLUtils::GetInt(pWakeUp, "waitservices", tmp, 0, 5 * 60)) // max 5 minutes
867 entry.wait_services_sec = tmp;
869 CLog::Log(LOGINFO, " Registering wakeup entry:");
870 CLog::Log(LOGINFO, " HostName : {}", entry.host);
871 CLog::Log(LOGINFO, " MacAddress : {}", entry.mac);
872 CLog::Log(LOGINFO, " PingPort : {}", entry.ping_port);
873 CLog::Log(LOGINFO, " PingMode : {}", entry.ping_mode);
874 CLog::Log(LOGINFO, " Timeout : {} (sec)", GetTotalSeconds(entry.timeout));
875 CLog::Log(LOGINFO, " WaitForOnline : {} (sec)", entry.wait_online1_sec);
876 CLog::Log(LOGINFO, " WaitForOnlineEx : {} (sec)", entry.wait_online2_sec);
877 CLog::Log(LOGINFO, " WaitForServices : {} (sec)", entry.wait_services_sec);
879 m_entries.push_back(entry);
882 pWakeUp = pWakeUp->NextSiblingElement("wakeup"); // get next one
885 // load upnp server map
886 m_UPnPServers.clear();
888 const auto* pUPnPNode = rootElement->FirstChildElement("upnp_map");
889 while (pUPnPNode != nullptr)
891 UPnPServer server;
893 XMLUtils::GetString(pUPnPNode, "name", server.m_name);
894 XMLUtils::GetString(pUPnPNode, "uuid", server.m_uuid);
895 XMLUtils::GetString(pUPnPNode, "mac", server.m_mac);
897 if (server.m_name.empty())
898 server.m_name = server.m_uuid;
900 if (server.m_uuid.empty() || server.m_mac.empty())
901 CLog::LogF(LOGERROR, "Missing or empty <upnp_map> entry");
902 else
904 CLog::Log(LOGINFO, " Registering upnp_map entry [{} : {}] -> [{}]", server.m_name,
905 server.m_uuid, server.m_mac);
907 m_UPnPServers.push_back(server);
910 pUPnPNode = pUPnPNode->NextSiblingElement("upnp_map"); // get next one
914 void CWakeOnAccess::SaveToXML()
916 CXBMCTinyXML2 xmlDoc;
917 auto xmlRootElement = xmlDoc.NewElement("onaccesswakeup");
918 if (xmlRootElement == nullptr)
919 return;
921 auto* root = xmlDoc.InsertEndChild(xmlRootElement);
922 if (root == nullptr)
923 return;
925 XMLUtils::SetInt(root, "netinittimeout", m_netinit_sec);
926 XMLUtils::SetInt(root, "netsettletime", m_netsettle_ms);
928 for (const auto& i : m_entries)
930 auto* xmlSetting = xmlDoc.NewElement("wakeup");
931 if (xmlSetting == nullptr)
932 continue;
933 auto* pWakeUpNode = root->InsertEndChild(xmlSetting);
934 if (pWakeUpNode != nullptr)
936 XMLUtils::SetString(pWakeUpNode, "host", i.host);
937 XMLUtils::SetString(pWakeUpNode, "mac", i.mac);
938 XMLUtils::SetInt(pWakeUpNode, "pingport", i.ping_port);
939 XMLUtils::SetInt(pWakeUpNode, "pingmode", i.ping_mode);
940 XMLUtils::SetInt(pWakeUpNode, "timeout", GetTotalSeconds(i.timeout));
941 XMLUtils::SetInt(pWakeUpNode, "waitonline", i.wait_online1_sec);
942 XMLUtils::SetInt(pWakeUpNode, "waitonline2", i.wait_online2_sec);
943 XMLUtils::SetInt(pWakeUpNode, "waitservices", i.wait_services_sec);
947 for (const auto& upnp : m_UPnPServers)
949 auto* xmlSetting = xmlDoc.NewElement("upnp_map");
950 if (xmlSetting == nullptr)
951 continue;
952 auto* pUPnPNode = root->InsertEndChild(xmlSetting);
953 if (pUPnPNode != nullptr)
955 XMLUtils::SetString(pUPnPNode, "name", upnp.m_name);
956 XMLUtils::SetString(pUPnPNode, "uuid", upnp.m_uuid);
957 XMLUtils::SetString(pUPnPNode, "mac", upnp.m_mac);
961 xmlDoc.SaveFile(GetSettingFile());