diff --git a/language/English/strings.po b/language/English/strings.po index 4ebfbd941111e..9f6842c4f2d15 100755 --- a/language/English/strings.po +++ b/language/English/strings.po @@ -9295,7 +9295,19 @@ msgctxt "#19684" msgid "Adult" msgstr "" -#empty strings from id 19685 to 19999 +#. Title for shutdown confirmation dialog +#: xbmc/ApplicationMessenger.cpp +msgctxt "#19685" +msgid "Confirm shutdown" +msgstr "" + +#. Text for shutdown confirmation dialog +#: xbmc/ApplicationMessenger.cpp +msgctxt "#19686" +msgid "The PVR backend is busy. Shutdown anyway?" +msgstr "" + +#empty strings from id 19687 to 19999 #: system/settings/settings.xml msgctxt "#20000" diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 22144b7da6f76..f73193a625dd0 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -734,7 +734,9 @@ bool CApplication::Create() std::string executable = CUtil::ResolveExecutablePath(); CLog::Log(LOGNOTICE, "The executable running is: %s", executable.c_str()); - CLog::Log(LOGNOTICE, "Local hostname: %s", m_network->GetHostName().c_str()); + std::string hostname("[unknown]"); + m_network->GetHostName(hostname); + CLog::Log(LOGNOTICE, "Local hostname: %s", hostname.c_str()); std::string lowerAppName = CCompileInfo::GetAppName(); StringUtils::ToLower(lowerAppName); CLog::Log(LOGNOTICE, "Log File is located: %s%s.log", g_advancedSettings.m_logFolder.c_str(), lowerAppName.c_str()); @@ -2589,8 +2591,12 @@ bool CApplication::OnAction(const CAction &action) // built in functions : execute the built-in if (action.GetID() == ACTION_BUILT_IN_FUNCTION) { - CBuiltins::Execute(action.GetName()); - m_navigationTimer.StartZero(); + if (!CBuiltins::IsSystemPowerdownCommand(action.GetName()) || + g_PVRManager.CanSystemPowerdown()) + { + CBuiltins::Execute(action.GetName()); + m_navigationTimer.StartZero(); + } return true; } @@ -4723,7 +4729,7 @@ void CApplication::CheckShutdown() || m_musicInfoScanner->IsScanning() || m_videoInfoScanner->IsScanning() || g_windowManager.IsWindowActive(WINDOW_DIALOG_PROGRESS) // progress dialog is onscreen - || (CSettings::Get().GetBool("pvrmanager.enabled") && !g_PVRManager.IsIdle())) + || !g_PVRManager.CanSystemPowerdown(false)) { m_shutdownTimer.StartZero(); return; @@ -4999,7 +5005,11 @@ bool CApplication::ExecuteXBMCAction(std::string actionStr) // user has asked for something to be executed if (CBuiltins::HasCommand(actionStr)) - CBuiltins::Execute(actionStr); + { + if (!CBuiltins::IsSystemPowerdownCommand(actionStr) || + g_PVRManager.CanSystemPowerdown()) + CBuiltins::Execute(actionStr); + } else { // try translating the action from our ButtonTranslator diff --git a/xbmc/GUIInfoManager.cpp b/xbmc/GUIInfoManager.cpp index 2b7e0b8012a38..39c7b34722e18 100644 --- a/xbmc/GUIInfoManager.cpp +++ b/xbmc/GUIInfoManager.cpp @@ -1921,7 +1921,11 @@ std::string CGUIInfoManager::GetLabel(int info, int contextWindow, std::string * { std::string friendlyName = CSettings::Get().GetString("services.devicename"); if (StringUtils::EqualsNoCase(friendlyName, CCompileInfo::GetAppName())) - strLabel = StringUtils::Format("%s (%s)", friendlyName.c_str(), g_application.getNetwork().GetHostName().c_str()); + { + std::string hostname("[unknown]"); + g_application.getNetwork().GetHostName(hostname); + strLabel = StringUtils::Format("%s (%s)", friendlyName.c_str(), hostname.c_str()); + } else strLabel = friendlyName; } diff --git a/xbmc/URL.cpp b/xbmc/URL.cpp index ed4549dd4c8e4..6b13a57ed4ca4 100644 --- a/xbmc/URL.cpp +++ b/xbmc/URL.cpp @@ -19,6 +19,7 @@ */ #include "URL.h" +#include "Application.h" #include "utils/RegExp.h" #include "utils/log.h" #include "utils/URIUtils.h" @@ -29,6 +30,7 @@ #include "filesystem/StackDirectory.h" #include "addons/Addon.h" #include "utils/StringUtils.h" +#include "network/Network.h" #ifndef TARGET_POSIX #include #include @@ -679,14 +681,12 @@ std::string CURL::GetRedacted(const std::string& path) bool CURL::IsLocal() const { - return (IsLocalHost() || m_strProtocol.empty()); + return (m_strProtocol.empty() || IsLocalHost()); } bool CURL::IsLocalHost() const { - // localhost is case-insensitive - return (StringUtils::EqualsNoCase(m_strHostName, "localhost") || - m_strHostName == "127.0.0.1"); + return g_application.getNetwork().IsLocalHost(m_strHostName); } bool CURL::IsFileOnly(const std::string &url) diff --git a/xbmc/addons/include/xbmc_pvr_dll.h b/xbmc/addons/include/xbmc_pvr_dll.h index 50012b0497df9..6ef21f3b8ea59 100644 --- a/xbmc/addons/include/xbmc_pvr_dll.h +++ b/xbmc/addons/include/xbmc_pvr_dll.h @@ -594,6 +594,13 @@ extern "C" */ time_t GetBufferTimeEnd(); + /*! + * Get the hostname of the pvr backend server + * @return hostname as ip address or alias. If backend does not + * utilize a server, return empty string. + */ + const char* GetBackendHostname(); + /*! * Called by XBMC to assign the function pointers of this add-on to pClient. * @param pClient The struct to assign the function pointers to. @@ -674,6 +681,8 @@ extern "C" pClient->GetPlayingTime = GetPlayingTime; pClient->GetBufferTimeStart = GetBufferTimeStart; pClient->GetBufferTimeEnd = GetBufferTimeEnd; + + pClient->GetBackendHostname = GetBackendHostname; }; }; diff --git a/xbmc/addons/include/xbmc_pvr_types.h b/xbmc/addons/include/xbmc_pvr_types.h index b218943534bc6..8d78818e78388 100644 --- a/xbmc/addons/include/xbmc_pvr_types.h +++ b/xbmc/addons/include/xbmc_pvr_types.h @@ -75,10 +75,10 @@ struct DemuxPacket; #define PVR_STREAM_MAX_STREAMS 20 /* current PVR API version */ -#define XBMC_PVR_API_VERSION "1.9.2" +#define XBMC_PVR_API_VERSION "1.9.3" /* min. PVR API version */ -#define XBMC_PVR_MIN_API_VERSION "1.9.2" +#define XBMC_PVR_MIN_API_VERSION "1.9.3" #ifdef __cplusplus extern "C" { @@ -399,6 +399,7 @@ extern "C" { time_t (__cdecl* GetPlayingTime)(void); time_t (__cdecl* GetBufferTimeStart)(void); time_t (__cdecl* GetBufferTimeEnd)(void); + const char* (__cdecl* GetBackendHostname)(void); } PVRClient; #ifdef __cplusplus diff --git a/xbmc/interfaces/Builtins.cpp b/xbmc/interfaces/Builtins.cpp index d99f047b2c967..0c01c827939b3 100644 --- a/xbmc/interfaces/Builtins.cpp +++ b/xbmc/interfaces/Builtins.cpp @@ -96,6 +96,7 @@ #include #include "settings/AdvancedSettings.h" #include "settings/DisplaySettings.h" +#include "powermanagement/PowerManager.h" using namespace std; using namespace XFILE; @@ -244,6 +245,39 @@ bool CBuiltins::HasCommand(const std::string& execString) return false; } +bool CBuiltins::IsSystemPowerdownCommand(const std::string& execString) +{ + std::string execute; + vector params; + CUtil::SplitExecFunction(execString, execute, params); + StringUtils::ToLower(execute); + + // Check if action is resulting in system powerdown. + if (execute == "reboot" || + execute == "restart" || + execute == "reset" || + execute == "powerdown" || + execute == "hibernate" || + execute == "suspend" ) + { + return true; + } + else if (execute == "shutdown") + { + switch (CSettings::Get().GetInt("powermanagement.shutdownstate")) + { + case POWERSTATE_SHUTDOWN: + case POWERSTATE_SUSPEND: + case POWERSTATE_HIBERNATE: + return true; + + default: + return false; + } + } + return false; +} + void CBuiltins::GetHelp(std::string &help) { help.clear(); diff --git a/xbmc/interfaces/Builtins.h b/xbmc/interfaces/Builtins.h index e30385c68cf31..9488c02f6364a 100644 --- a/xbmc/interfaces/Builtins.h +++ b/xbmc/interfaces/Builtins.h @@ -26,6 +26,7 @@ class CBuiltins { public: static bool HasCommand(const std::string& execString); + static bool IsSystemPowerdownCommand(const std::string& execString); static void GetHelp(std::string &help); static int Execute(const std::string& execString); }; diff --git a/xbmc/network/Network.cpp b/xbmc/network/Network.cpp index d0c80c0f5e026..ed4c5d741c0c1 100644 --- a/xbmc/network/Network.cpp +++ b/xbmc/network/Network.cpp @@ -159,19 +159,49 @@ int CNetwork::ParseHex(char *str, unsigned char *addr) return len; } -std::string CNetwork::GetHostName(void) +bool CNetwork::GetHostName(std::string& hostname) { char hostName[128]; if (gethostname(hostName, sizeof(hostName))) - return "unknown"; + return false; - std::string hostStr; #ifdef TARGET_WINDOWS + std::string hostStr; g_charsetConverter.systemToUtf8(hostName, hostStr); + hostname = hostStr; #else - hostStr = hostName; + hostname = hostName; #endif - return hostStr; + return true; +} + +bool CNetwork::IsLocalHost(const std::string& hostname) +{ + if (hostname.empty()) + return false; + + if (StringUtils::StartsWith(hostname, "127.") + || (hostname == "::1") + || StringUtils::EqualsNoCase(hostname, "localhost")) + return true; + + std::string myhostname; + if (GetHostName(myhostname) + && StringUtils::EqualsNoCase(hostname, myhostname)) + return true; + + std::vector& ifaces = GetInterfaceList(); + std::vector::const_iterator iter = ifaces.begin(); + while (iter != ifaces.end()) + { + CNetworkInterface* iface = *iter; + if (iface && iface->GetCurrentIPAddress() == hostname) + return true; + + ++iter; + } + + return false; } CNetworkInterface* CNetwork::GetFirstConnectedInterface() diff --git a/xbmc/network/Network.h b/xbmc/network/Network.h index e2cd39b62a48c..2bd8369be73d0 100644 --- a/xbmc/network/Network.h +++ b/xbmc/network/Network.h @@ -110,7 +110,7 @@ class CNetwork virtual ~CNetwork(); // Return our hostname - virtual std::string GetHostName(void); + virtual bool GetHostName(std::string& hostname); // Return the list of interfaces virtual std::vector& GetInterfaceList(void) = 0; @@ -146,6 +146,9 @@ class CNetwork void StopServices(bool bWait); static int ParseHex(char *str, unsigned char *addr); + + // Return true if given name or ip address corresponds to localhost + bool IsLocalHost(const std::string& hostname); }; #ifdef HAS_LINUX_NETWORK diff --git a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp index 7991b990a1e12..98c4d77eed1b5 100644 --- a/xbmc/peripherals/devices/PeripheralCecAdapter.cpp +++ b/xbmc/peripherals/devices/PeripheralCecAdapter.cpp @@ -628,9 +628,9 @@ int CPeripheralCecAdapter::CecCommand(void *cbParam, const cec_command command) { adapter->m_bStarted = false; if (adapter->m_configuration.bPowerOffOnStandby == 1) - CApplicationMessenger::Get().Suspend(); + g_application.ExecuteXBMCAction("Suspend"); else if (adapter->m_configuration.bShutdownOnStandby == 1) - CApplicationMessenger::Get().Shutdown(); + g_application.ExecuteXBMCAction("Shutdown"); } break; case CEC_OPCODE_SET_MENU_LANGUAGE: diff --git a/xbmc/pvr/PVRManager.cpp b/xbmc/pvr/PVRManager.cpp index a80bc81a6f7a1..48beed5ea95f7 100644 --- a/xbmc/pvr/PVRManager.cpp +++ b/xbmc/pvr/PVRManager.cpp @@ -1414,6 +1414,26 @@ bool CPVRManager::IsIdle(void) const return true; } +bool CPVRManager::CanSystemPowerdown(bool bAskUser /*= true*/) const +{ + bool bReturn(true); + if (IsStarted()) + { + if (!m_addons->AllLocalBackendsIdle()) + { + if (bAskUser) + { + // Inform user about PVR being busy. Ask if user wants to powerdown anyway. + bool bCanceled = false; + bReturn = CGUIDialogYesNo::ShowAndGetInput(19685, 19686, 0, 0, -1, -1, bCanceled, 10000); + } + else + bReturn = false; // do not powerdown (busy, but no user interaction requested). + } + } + return bReturn; +} + void CPVRManager::ShowPlayerInfo(int iTimeout) { if (IsStarted() && m_guiInfo) diff --git a/xbmc/pvr/PVRManager.h b/xbmc/pvr/PVRManager.h index 205cc99970a82..72b17e7767809 100644 --- a/xbmc/pvr/PVRManager.h +++ b/xbmc/pvr/PVRManager.h @@ -371,6 +371,18 @@ namespace PVR */ bool IsIdle(void) const; + /*! + * @brief Check whether the system Kodi is running on can be powered down + * (shutdown/reboot/suspend/hibernate) without stopping any active + * recordings and/or without preventing the start of recordings + * sheduled for now + pvrpowermanagement.backendidletime. + * @param bAskUser True to informs user in case of potential + * data loss. User can decide to allow powerdown anyway. False to + * not to ask user and to not confirm power down. + * @return True if system can be safely powered down, false otherwise. + */ + bool CanSystemPowerdown(bool bAskUser = true) const; + /*! * @brief Set the current playing group, used to load the right channel. * @param group The new group. diff --git a/xbmc/pvr/addons/PVRClient.cpp b/xbmc/pvr/addons/PVRClient.cpp index e35db066e5c0d..6491b36c0ab36 100644 --- a/xbmc/pvr/addons/PVRClient.cpp +++ b/xbmc/pvr/addons/PVRClient.cpp @@ -132,6 +132,7 @@ void CPVRClient::ResetProperties(int iClientId /* = PVR_INVALID_CLIENT_ID */) m_strConnectionString = DEFAULT_INFO_STRING_VALUE; m_strFriendlyName = DEFAULT_INFO_STRING_VALUE; m_strBackendName = DEFAULT_INFO_STRING_VALUE; + m_strBackendHostname.clear(); m_bIsPlayingTV = false; m_bIsPlayingRecording = false; memset(&m_addonCapabilities, 0, sizeof(m_addonCapabilities)); @@ -353,7 +354,7 @@ bool CPVRClient::CheckAPIVersion(void) bool CPVRClient::GetAddonProperties(void) { - std::string strBackendName, strConnectionString, strFriendlyName, strBackendVersion; + std::string strBackendName, strConnectionString, strFriendlyName, strBackendVersion, strBackendHostname; PVR_ADDON_CAPABILITIES addonCapabilities; /* get the capabilities */ @@ -384,12 +385,17 @@ bool CPVRClient::GetAddonProperties(void) try { strBackendVersion = m_pStruct->GetBackendVersion(); } catch (std::exception &e) { LogException(e, "GetBackendVersion()"); return false; } + /* backend hostname */ + try { strBackendHostname = m_pStruct->GetBackendHostname(); } + catch (std::exception &e) { LogException(e, "GetBackendHostname()"); return false; } + /* update the members */ m_strBackendName = strBackendName; m_strConnectionString = strConnectionString; m_strFriendlyName = strFriendlyName; m_strBackendVersion = strBackendVersion; m_addonCapabilities = addonCapabilities; + m_strBackendHostname = strBackendHostname; return true; } @@ -410,6 +416,11 @@ const std::string& CPVRClient::GetBackendVersion(void) const return m_strBackendVersion; } +const std::string& CPVRClient::GetBackendHostname(void) const +{ + return m_strBackendHostname; +} + const std::string& CPVRClient::GetConnectionString(void) const { return m_strConnectionString; diff --git a/xbmc/pvr/addons/PVRClient.h b/xbmc/pvr/addons/PVRClient.h index 179d85aaa1c7e..77a3e5b3f2d44 100644 --- a/xbmc/pvr/addons/PVRClient.h +++ b/xbmc/pvr/addons/PVRClient.h @@ -128,6 +128,11 @@ namespace PVR */ const std::string& GetBackendVersion(void) const; + /*! + * @brief the ip address or alias of the pvr backend server + */ + const std::string& GetBackendHostname(void) const; + /*! * @return The connection string reported by the backend. */ @@ -625,6 +630,7 @@ namespace PVR PVR_ADDON_CAPABILITIES m_addonCapabilities; /*!< the cached add-on capabilities */ bool m_bGotAddonCapabilities; /*!< true if the add-on capabilities have already been fetched */ PVR_SIGNAL_STATUS m_qualityInfo; /*!< stream quality information */ + std::string m_strBackendHostname; /*!< the cached backend hostname */ /* stored strings to make sure const char* members in PVR_PROPERTIES stay valid */ std::string m_strUserPath; /*!< @brief translated path to the user profile */ diff --git a/xbmc/pvr/addons/PVRClients.cpp b/xbmc/pvr/addons/PVRClients.cpp index f8f933824bed4..d252e6d686b9d 100644 --- a/xbmc/pvr/addons/PVRClients.cpp +++ b/xbmc/pvr/addons/PVRClients.cpp @@ -34,6 +34,7 @@ #include "pvr/recordings/PVRRecordings.h" #include "pvr/timers/PVRTimers.h" #include "cores/IPlayer.h" +#include "network/Network.h" using namespace ADDON; using namespace PVR; @@ -1371,3 +1372,33 @@ time_t CPVRClients::GetBufferTimeEnd() const return time; } + +bool CPVRClients::NextEventWithinBackendIdleTime(const CPVRTimers& timers) +{ + // timers going off soon? + const CDateTime now(CDateTime::GetUTCDateTime()); + const CDateTimeSpan idle( + 0, 0, CSettings::Get().GetInt("pvrpowermanagement.backendidletime"), 0); + const CDateTime next(timers.GetNextEventTime()); + const CDateTimeSpan delta(next - now); + + return (delta <= idle); +} + +bool CPVRClients::AllLocalBackendsIdle() const +{ + PVR_CLIENTMAP clients; + GetConnectedClients(clients); + for (PVR_CLIENTMAP_CITR itr = clients.begin(); itr != clients.end(); itr++) + { + CPVRTimers timers; + PVR_ERROR ret = itr->second->GetTimers(&timers); + if (ret == PVR_ERROR_NOT_IMPLEMENTED || ret != PVR_ERROR_NO_ERROR) + continue; + + if (((timers.AmountActiveRecordings() > 0) || NextEventWithinBackendIdleTime(timers)) + && g_application.getNetwork().IsLocalHost(itr->second->GetBackendHostname())) + return false; + } + return true; +} diff --git a/xbmc/pvr/addons/PVRClients.h b/xbmc/pvr/addons/PVRClients.h index d10926ab5f6a7..08719e2a12b1c 100644 --- a/xbmc/pvr/addons/PVRClients.h +++ b/xbmc/pvr/addons/PVRClients.h @@ -552,6 +552,12 @@ namespace PVR bool GetPlayingClient(PVR_CLIENT &client) const; + /*! + * @brief Checks whether all local pvr backends (if any) are idle (no recording active, ...). + * @return True if all local backends are idle or no local backends are connected, false otherwise. + */ + bool AllLocalBackendsIdle() const; + time_t GetPlayingTime() const; time_t GetBufferTimeStart() const; time_t GetBufferTimeEnd() const; @@ -621,6 +627,8 @@ namespace PVR int GetClientId(const ADDON::AddonPtr client) const; + static bool NextEventWithinBackendIdleTime(const CPVRTimers& timers); + bool m_bChannelScanRunning; /*!< true when a channel scan is currently running, false otherwise */ bool m_bIsSwitchingChannels; /*!< true while switching channels */ int m_playingClientId; /*!< the ID of the client that is currently playing */ diff --git a/xbmc/windows/GUIWindowLoginScreen.cpp b/xbmc/windows/GUIWindowLoginScreen.cpp index 765f65d99a7ed..932a0501dfbbb 100644 --- a/xbmc/windows/GUIWindowLoginScreen.cpp +++ b/xbmc/windows/GUIWindowLoginScreen.cpp @@ -47,6 +47,7 @@ #include "guilib/LocalizeStrings.h" #include "addons/AddonManager.h" #include "view/ViewState.h" +#include "pvr/PVRManager.h" #define CONTROL_BIG_LIST 52 #define CONTROL_LABEL_HEADER 2 @@ -141,7 +142,8 @@ bool CGUIWindowLoginScreen::OnAction(const CAction &action) { std::string actionName = action.GetName(); StringUtils::ToLower(actionName); - if (actionName.find("shutdown") != std::string::npos) + if ((actionName.find("shutdown") != std::string::npos) && + PVR::g_PVRManager.CanSystemPowerdown()) CBuiltins::Execute(action.GetName()); return true; }