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.
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"
40 #include <arpa/inet.h>
41 #include <netinet/in.h>
42 #include <sys/socket.h>
45 #include "network/upnp/UPnP.h"
46 #include <Platinum/Source/Platinum/Platinum.h>
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
)
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
)
83 CLog::Log(LOGINFO
, "{} - Create new entry for host '{}'", function
, server_name
);
84 message
= StringUtils::Format(LOCALIZED(13035), server_name
);
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);
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
); }
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();
121 list
.push_back(server
); // add server
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
)
131 while (CDateTime::GetCurrentDateTime() < upnpInitReady
)
132 KODI::TIME::Sleep(1s
);
134 PLT_SyncMediaBrowser
* browser
= UPNP::CUPnP::GetInstance()->m_MediaBrowser
;
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();
149 AddOrUpdateUPnPServer(list
, server
);
156 static std::string
LookupUPnPHost(const std::string
& uuid
)
159 UPNP::CUPnP
* upnp
= UPNP::CUPnP::GetInstance();
161 if (!upnp
->IsClientStarted())
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();
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())
192 class CMACDiscoveryJob
: public CJob
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
; }
203 std::string m_macAddress
;
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
);
217 const std::vector
<CNetworkInterface
*>& ifaces
= CServiceBroker::GetNetwork().GetInterfaceList();
218 for (const auto& it
: ifaces
)
220 if (it
->GetHostMacAddress(ipAddress
, m_macAddress
))
232 virtual ~WaitCondition() = default;
233 virtual bool SuccessWaiting () const { return false; }
241 NestDetect() : m_gui_thread(CServiceBroker::GetAppMessenger()->IsProcessThread())
255 bool IsNested() const
257 return m_gui_thread
&& m_nest
> 1;
262 const bool m_gui_thread
;
264 int NestDetect::m_nest
= 0;
268 class ProgressDialogHelper
271 explicit ProgressDialogHelper(const std::string
& heading
)
273 if (CServiceBroker::GetAppMessenger()->IsProcessThread())
275 CGUIComponent
*gui
= CServiceBroker::GetGUI();
277 m_dialog
= gui
->GetWindowManager().GetWindow
<CGUIDialogProgress
>(WINDOW_DIALOG_PROGRESS
);
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 ()
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);
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())
318 if (!m_dialog
->IsActive())
321 if (m_dialog
->IsCanceled())
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
);
339 CGUIDialogProgress
* m_dialog
= 0;
342 class NetworkStartWaiter
: public WaitCondition
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();
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
367 PingResponseWaiter(bool async
, const CWakeOnAccess::WakeUpEntry
& server
) : m_server(server
)
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);
396 std::string host
= LookupUPnPHost(server
.upnpUuid
);
400 KODI::TIME::Sleep(std::chrono::milliseconds(timeOutMs
));
402 host
= LookupUPnPHost(server
.upnpUuid
);
405 return !host
.empty();
410 class CHostProberJob
: public CJob
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
))
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"));
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
)
466 return true; // bail if feature is turned off
470 if (FindOrTouchHostEntry(hostName
, upnpMode
, server
))
472 CLog::Log(LOGINFO
, "WakeOnAccess [{}] triggered by accessing : {}", server
.friendlyName
,
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
);
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)");
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");
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));
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");
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
))
581 result
.friendlyName
= upnp
? upnp
->m_name
: server
.host
;
584 result
.upnpUuid
= upnp
->m_uuid
;
588 else // 'touch' next wakeup time
590 server
.nextWake
= now
+ server
.timeout
;
593 upnp
->m_nextWake
= server
.nextWake
;
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
;
616 upnp
->m_nextWake
= server
.nextWake
;
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 ..
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
)
643 if (URIUtils::IsHostOnLAN(host
, LanCheckMode::ANY_PRIVATE_SUBNET
))
644 CServiceBroker::GetJobManager()->AddJob(new CMACDiscoveryJob(host
), this);
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
)
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
)
674 AddHostsFromVecSource(*sources
, hosts
);
677 void CWakeOnAccess::QueueMACDiscoveryForAllRemotes()
679 std::vector
<std::string
> hosts
;
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();
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
))
718 ShowDiscoveryMessage(__FUNCTION__
, host
.c_str(), false);
720 AddMatchingUPnPServers(m_UPnPServers
, host
, mac
, i
.timeout
);
726 // not found entry to update - create using default values
727 WakeUpEntry
entry (true);
730 m_entries
.push_back(entry
);
731 ShowDiscoveryMessage(__FUNCTION__
, host
.c_str(), true);
733 AddMatchingUPnPServers(m_UPnPServers
, host
, mac
, entry
.timeout
);
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();
746 std::unique_lock
<CCriticalSection
> lock(m_entrylist_protect
);
748 SaveMACDiscoveryResult(host
, mac
);
752 CLog::Log(LOGERROR
, "{} - Mac discovery failed for host '{}'", __FUNCTION__
, host
);
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)
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();
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
);
792 void CWakeOnAccess::SetEnabled(bool 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()))
807 CLog::LogF(LOGINFO
, "unable to load:{}", GetSettingFile());
811 auto* rootElement
= xmlDoc
.RootElement();
812 if (StringUtils::CompareNoCase(rootElement
->Value(), "onaccesswakeup"))
814 CLog::LogF(LOGERROR
, "XML file {} doesn't contain <onaccesswakeup>", GetSettingFile());
820 CLog::Log(LOGINFO
, "WakeOnAccess - Load settings :");
825 if (XMLUtils::GetInt(rootElement
, "netinittimeout", tmp
, 0, 5 * 60))
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)
839 if (XMLUtils::GetString(pWakeUp
, "host", strtmp
))
842 if (XMLUtils::GetString(pWakeUp
, "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");
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)
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");
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)
921 auto* root
= xmlDoc
.InsertEndChild(xmlRootElement
);
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)
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)
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());