From 6600478675add5e26e8af97c8de8b972a0cc1230 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 25 Feb 2018 13:26:17 +0100 Subject: [PATCH] Moved handling remote timers into cSVDRPClientHandler::ProcessConnections() --- HISTORY | 3 +- menu.c | 4 +-- svdrp.c | 72 ++++++++++++++++++++++++------------------- timers.c | 94 +++++++++++++++++++------------------------------------- timers.h | 21 ++++--------- vdr.c | 21 +++---------- 6 files changed, 87 insertions(+), 128 deletions(-) diff --git a/HISTORY b/HISTORY index fec7d18e..2721c151 100644 --- a/HISTORY +++ b/HISTORY @@ -9162,7 +9162,7 @@ Video Disk Recorder Revision History a subdirectory. - SVDRP peering can now be limited to the default SVDRP host (see MANUAL for details). -2018-02-20: Version 2.3.9 +2018-02-25: Version 2.3.9 - Updated the Italian OSD texts (thanks to Diego Pierotto). - Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). @@ -9283,3 +9283,4 @@ Video Disk Recorder Revision History SVDRP command CONN instead of using the UDP port with the server's address. This change requires that all VDRs that shall take part in a peer-to-peer network need to be updated to this version. +- Moved handling remote timers into cSVDRPClientHandler::ProcessConnections(). diff --git a/menu.c b/menu.c index 1759670d..d1eccfca 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 4.61 2018/02/13 09:25:43 kls Exp $ + * $Id: menu.c 4.62 2018/02/25 13:07:09 kls Exp $ */ #include "menu.h" @@ -4184,7 +4184,7 @@ eOSState cMenuSetupMisc::ProcessKey(eKeys Key) else { LOCK_TIMERS_WRITE; Timers->SetExplicitModify(); - if (Timers->DelRemoteTimers()) + if (Timers->StoreRemoteTimers(NULL, NULL)) Timers->SetModified(); } } diff --git a/svdrp.c b/svdrp.c index b89bdf1d..48df03ca 100644 --- a/svdrp.c +++ b/svdrp.c @@ -10,7 +10,7 @@ * and interact with the Video Disk Recorder - or write a full featured * graphical interface that sits on top of an SVDRP connection. * - * $Id: svdrp.c 4.27 2018/02/20 13:28:04 kls Exp $ + * $Id: svdrp.c 4.28 2018/02/25 13:26:17 kls Exp $ */ #include "svdrp.h" @@ -317,6 +317,7 @@ private: cFile file; int fetchFlags; bool connected; + bool Send(const char *Command); void Close(void); public: cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout); @@ -324,12 +325,12 @@ public: const char *ServerName(void) const { return serverName; } const char *Connection(void) const { return serverIpAddress.Connection(); } bool HasAddress(const char *Address, int Port) const; - bool Send(const char *Command); bool Process(cStringList *Response = NULL); bool Execute(const char *Command, cStringList *Response = NULL); bool Connected(void) const { return connected; } void SetFetchFlag(eSvdrpFetchFlags Flag); bool HasFetchFlag(eSvdrpFetchFlags Flag); + bool GetRemoteTimers(cStringList &Response); }; static cPoller SVDRPClientPoller; @@ -365,9 +366,6 @@ void cSVDRPClient::Close(void) SVDRPClientPoller.Del(file, false); file.Close(); socket.Close(); - LOCK_TIMERS_WRITE; - if (Timers) - Timers->DelRemoteTimers(serverName); } } @@ -483,6 +481,14 @@ bool cSVDRPClient::HasFetchFlag(eSvdrpFetchFlags Flag) return Result; } +bool cSVDRPClient::GetRemoteTimers(cStringList &Response) +{ + if (HasFetchFlag(sffTimers)) + return Execute("LSTT ID", &Response); + return false; +} + + // --- cSVDRPServerParams ---------------------------------------------------- class cSVDRPServerParams { @@ -554,20 +560,21 @@ private: cMutex mutex; int tcpPort; cSocket udpSocket; + cStateKey timersStateKey; cVector clientConnections; + void SendDiscover(void); void HandleClientConnection(void); void ProcessConnections(void); + cSVDRPClient *GetClientForServer(const char *ServerName); protected: virtual void Action(void); public: cSVDRPClientHandler(int TcpPort, int UdpPort); virtual ~cSVDRPClientHandler(); - void SendDiscover(void); void AddClient(cSVDRPServerParams &ServerParams, const char *IpAddress); bool Execute(const char *ServerName, const char *Command, cStringList *Response = NULL); bool GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlags = sffNone); bool TriggerFetchingTimers(const char *ServerName); - cSVDRPClient *GetClientForServer(const char *ServerName); }; static cSVDRPClientHandler *SVDRPClientHandler = NULL; @@ -575,6 +582,7 @@ static cSVDRPClientHandler *SVDRPClientHandler = NULL; cSVDRPClientHandler::cSVDRPClientHandler(int TcpPort, int UdpPort) :cThread("SVDRP client handler", true) ,udpSocket(UdpPort, false) +,timersStateKey(true) { tcpPort = TcpPort; } @@ -588,7 +596,6 @@ cSVDRPClientHandler::~cSVDRPClientHandler() cSVDRPClient *cSVDRPClientHandler::GetClientForServer(const char *ServerName) { - cMutexLock MutexLock(&mutex); for (int i = 0; i < clientConnections.Size(); i++) { if (strcmp(clientConnections[i]->ServerName(), ServerName) == 0) return clientConnections[i]; @@ -598,17 +605,36 @@ cSVDRPClient *cSVDRPClientHandler::GetClientForServer(const char *ServerName) void cSVDRPClientHandler::SendDiscover(void) { - cMutexLock MutexLock(&mutex); cString Dgram = cString::sprintf("SVDRP:discover name:%s port:%d vdrversion:%d apiversion:%d timeout:%d%s", Setup.SVDRPHostName, tcpPort, VDRVERSNUM, APIVERSNUM, Setup.SVDRPTimeout, (Setup.SVDRPPeering == spmOnly && *Setup.SVDRPDefaultHost) ? *cString::sprintf(" host:%s", Setup.SVDRPDefaultHost) : ""); udpSocket.SendDgram(Dgram, udpSocket.Port()); } void cSVDRPClientHandler::ProcessConnections(void) { - cMutexLock MutexLock(&mutex); + cString PollTimersCmd; + if (cTimers::GetTimersRead(timersStateKey)) { + PollTimersCmd = cString::sprintf("POLL %s TIMERS", Setup.SVDRPHostName); + timersStateKey.Remove(); + } for (int i = 0; i < clientConnections.Size(); i++) { - if (!clientConnections[i]->Process()) { - delete clientConnections[i]; + cSVDRPClient *Client = clientConnections[i]; + if (Client->Process()) { + cStringList RemoteTimers; + if (Client->GetRemoteTimers(RemoteTimers)) { + cTimers *Timers = cTimers::GetTimersWrite(timersStateKey); + bool TimersModified = Timers->StoreRemoteTimers(Client->ServerName(), &RemoteTimers); + timersStateKey.Remove(TimersModified); + } + if (*PollTimersCmd) { + if (!Client->Execute(PollTimersCmd)) + esyslog("ERROR: can't send '%s' to '%s'", *PollTimersCmd, Client->ServerName()); + } + } + else { + cTimers *Timers = cTimers::GetTimersWrite(timersStateKey); + bool TimersModified = Timers->StoreRemoteTimers(Client->ServerName(), NULL); + timersStateKey.Remove(TimersModified); + delete Client; clientConnections.Remove(i); i--; } @@ -617,11 +643,10 @@ void cSVDRPClientHandler::ProcessConnections(void) void cSVDRPClientHandler::AddClient(cSVDRPServerParams &ServerParams, const char *IpAddress) { + cMutexLock MutexLock(&mutex); for (int i = 0; i < clientConnections.Size(); i++) { - if (clientConnections[i]->HasAddress(IpAddress, ServerParams.Port())) { - dsyslog("SVDRP %s < %s connection to '%s' already exists", Setup.SVDRPHostName, clientConnections[i]->Connection(), clientConnections[i]->ServerName()); + if (clientConnections[i]->HasAddress(IpAddress, ServerParams.Port())) return; - } } if (Setup.SVDRPPeering == spmOnly && strcmp(ServerParams.Name(), Setup.SVDRPDefaultHost) != 0) return; // we only want to peer with the default host, but this isn't the default host @@ -1294,8 +1319,7 @@ void cSVDRPServer::CmdCONN(const char *Option) if (ServerParams.Ok()) { clientName = ServerParams.Name(); Reply(250, "OK"); // must finish this transaction before creating the new client - if (!SVDRPClientHandler->GetClientForServer(ServerParams.Name())) - SVDRPClientHandler->AddClient(ServerParams, clientIpAddress.Address()); + SVDRPClientHandler->AddClient(ServerParams, clientIpAddress.Address()); } else Reply(501, "Error in server parameters: %s", ServerParams.Error()); @@ -2601,7 +2625,6 @@ void SetSVDRPGrabImageDir(const char *GrabImageDir) class cSVDRPServerHandler : public cThread { private: - cMutex mutex; bool ready; cSocket tcpSocket; cVector serverConnections; @@ -2613,7 +2636,6 @@ public: cSVDRPServerHandler(int TcpPort); virtual ~cSVDRPServerHandler(); void WaitUntilReady(void); - cSVDRPServer *GetServerForClient(const char *ClientName); }; static cSVDRPServerHandler *SVDRPServerHandler = NULL; @@ -2641,7 +2663,6 @@ void cSVDRPServerHandler::WaitUntilReady(void) void cSVDRPServerHandler::ProcessConnections(void) { - cMutexLock MutexLock(&mutex); for (int i = 0; i < serverConnections.Size(); i++) { if (!serverConnections[i]->Process()) { delete serverConnections[i]; @@ -2665,7 +2686,6 @@ void cSVDRPServerHandler::Action(void) ready = true; while (Running()) { SVDRPServerPoller.Poll(1000); - cMutexLock MutexLock(&mutex); HandleServerConnection(); ProcessConnections(); } @@ -2674,16 +2694,6 @@ void cSVDRPServerHandler::Action(void) } } -cSVDRPServer *cSVDRPServerHandler::GetServerForClient(const char *ClientName) -{ - cMutexLock MutexLock(&mutex); - for (int i = 0; i < serverConnections.Size(); i++) { - if (serverConnections[i]->ClientName() && strcmp(serverConnections[i]->ClientName(), ClientName) == 0) - return serverConnections[i]; - } - return NULL; -} - // --- SVDRP Handler --------------------------------------------------------- static cMutex SVDRPHandlerMutex; diff --git a/timers.c b/timers.c index 9d28f922..9e4c4856 100644 --- a/timers.c +++ b/timers.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.c 4.14 2017/11/12 13:01:22 kls Exp $ + * $Id: timers.c 4.15 2018/02/25 13:05:03 kls Exp $ */ #include "timers.h" @@ -898,78 +898,48 @@ bool cTimers::DeleteExpired(void) return TimersModified; } -bool cTimers::GetRemoteTimers(const char *ServerName) +bool cTimers::StoreRemoteTimers(const char *ServerName, const cStringList *RemoteTimers) { + //TODO handle only new/deleted/modified timers? bool Result = false; - if (ServerName) { - Result = DelRemoteTimers(ServerName); - cStringList Response; - if (ExecSVDRPCommand(ServerName, "LSTT ID", &Response)) { - for (int i = 0; i < Response.Size(); i++) { - const char *s = Response[i]; - int Code = SVDRPCode(s); - if (Code == 250) { - if (const char *v = SVDRPValue(s)) { - int Id = atoi(v); - while (*v && *v != ' ') - v++; // skip id - cTimer *Timer = new cTimer; - if (Timer->Parse(v)) { - Timer->SetRemote(ServerName); - Timer->SetId(Id); - Add(Timer); - Result = true; - } - else { - esyslog("ERROR: %s: error in timer settings: %s", ServerName, v); - delete Timer; - } - } - } - else if (Code != 550) - esyslog("ERROR: %s: %s", ServerName, s); - } - return Result; - } - } - else { - cStringList ServerNames; - if (GetSVDRPServerNames(&ServerNames, sffTimers)) { - for (int i = 0; i < ServerNames.Size(); i++) - Result |= GetRemoteTimers(ServerNames[i]); - } - } - return Result; -} - -bool cTimers::DelRemoteTimers(const char *ServerName) -{ - bool Deleted = false; + // Delete old remote timers: cTimer *Timer = First(); while (Timer) { cTimer *t = Next(Timer); if (Timer->Remote() && (!ServerName || strcmp(Timer->Remote(), ServerName) == 0)) { Del(Timer); - Deleted = true; + Result = true; } Timer = t; } - return Deleted; -} - -void cTimers::TriggerRemoteTimerPoll(const char *ServerName) -{ - if (ServerName) { - if (!ExecSVDRPCommand(ServerName, cString::sprintf("POLL %s TIMERS", Setup.SVDRPHostName))) - esyslog("ERROR: can't send 'POLL %s TIMERS' to '%s'", Setup.SVDRPHostName, ServerName); - } - else { - cStringList ServerNames; - if (GetSVDRPServerNames(&ServerNames)) { - for (int i = 0; i < ServerNames.Size(); i++) - TriggerRemoteTimerPoll(ServerNames[i]); - } + // Add new remote timers: + if (ServerName && RemoteTimers) { + for (int i = 0; i < RemoteTimers->Size(); i++) { + const char *s = (*RemoteTimers)[i]; + int Code = SVDRPCode(s); + if (Code == 250) { + if (const char *v = SVDRPValue(s)) { + int Id = atoi(v); + while (*v && *v != ' ') + v++; // skip id + cTimer *Timer = new cTimer; + if (Timer->Parse(v)) { + Timer->SetRemote(ServerName); + Timer->SetId(Id); + Add(Timer); + Result = true; + } + else { + esyslog("ERROR: %s: error in timer settings: %s", ServerName, v); + delete Timer; + } + } + } + else if (Code != 550) + esyslog("ERROR: %s: %s", ServerName, s); + } } + return Result; } static bool RemoteTimerError(const cTimer *Timer, cString *Msg) diff --git a/timers.h b/timers.h index 18ba30ec..3146fce5 100644 --- a/timers.h +++ b/timers.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: timers.h 4.9 2017/10/31 09:47:14 kls Exp $ + * $Id: timers.h 4.10 2018/02/25 12:54:55 kls Exp $ */ #ifndef __TIMERS_H @@ -185,21 +185,12 @@ public: void Add(cTimer *Timer, cTimer *After = NULL); void Ins(cTimer *Timer, cTimer *Before = NULL); void Del(cTimer *Timer, bool DeleteObject = true); - bool GetRemoteTimers(const char *ServerName = NULL); - ///< Gets the timers from the given remote machine and adds them to this - ///< list. If no ServerName is given, all timers from all known remote - ///< machines will be fetched. This function calls DelRemoteTimers() with - ///< the given ServerName first. + bool StoreRemoteTimers(const char *ServerName = NULL, const cStringList *RemoteTimers = NULL); + ///< Stores the given list of RemoteTimers, which come from the VDR ServerName, in + ///< this list. If no ServerName is given, all remote timers from all peer machines + ///< will be removed from this list. If no RemoteTimers are given, only the remote + ///< timers from ServerName will be removed from this list. ///< Returns true if any remote timers have been added or deleted - bool DelRemoteTimers(const char *ServerName = NULL); - ///< Deletes all timers of the given remote machine from this list (leaves - ///< them untouched on the remote machine). If no ServerName is given, the - ///< timers of all remote machines will be deleted from the list. - ///< Returns true if any remote timers have been deleted. - void TriggerRemoteTimerPoll(const char *ServerName = NULL); - ///< Sends an SVDRP POLL command to the given remote machine. - ///< If no ServerName is given, the POLL command will be sent to all - ///< known remote machines. }; bool HandleRemoteTimerModifications(cTimer *NewTimer, cTimer *OldTimer = NULL, cString *Msg = NULL); diff --git a/vdr.c b/vdr.c index 78afa872..1e012a2e 100644 --- a/vdr.c +++ b/vdr.c @@ -22,7 +22,7 @@ * * The project's page is at http://www.tvdr.de * - * $Id: vdr.c 4.21 2017/11/09 16:15:34 kls Exp $ + * $Id: vdr.c 4.22 2018/02/25 13:07:09 kls Exp $ */ #include @@ -1081,25 +1081,18 @@ int main(int argc, char *argv[]) { // Timers and Recordings: bool TimersModified = false; - bool TriggerRemoteTimerPoll = false; static cStateKey TimersStateKey(true); - if (cTimers::GetTimersRead(TimersStateKey)) { - TriggerRemoteTimerPoll = true; + if (cTimers::GetTimersRead(TimersStateKey)) TimersStateKey.Remove(); - } cTimers *Timers = cTimers::GetTimersWrite(TimersStateKey); - // Get remote timers: - TimersModified |= Timers->GetRemoteTimers(); // Assign events to timers: static cStateKey SchedulesStateKey; if (const cSchedules *Schedules = cSchedules::GetSchedulesRead(SchedulesStateKey)) TimersModified |= Timers->SetEvents(Schedules); // Must do all following calls with the exact same time! // Process ongoing recordings: - if (cRecordControls::Process(Timers, Now)) { + if (cRecordControls::Process(Timers, Now)) TimersModified = true; - TriggerRemoteTimerPoll = true; - } // Must keep the lock on the schedules until after processing the record // controls, in order to avoid short interrupts in case the current event // is replaced by a new one (which some broadcasters do, instead of just @@ -1113,7 +1106,6 @@ int main(int argc, char *argv[]) else LastTimerChannel = Timer->Channel()->Number(); TimersModified = true; - TriggerRemoteTimerPoll = true; } // Make sure timers "see" their channel early enough: static time_t LastTimerCheck = 0; @@ -1168,13 +1160,8 @@ int main(int argc, char *argv[]) LastTimerCheck = Now; } // Delete expired timers: - if (Timers->DeleteExpired()) { + if (Timers->DeleteExpired()) TimersModified = true; - TriggerRemoteTimerPoll = true; - } - // Trigger remote timer polls: - if (TriggerRemoteTimerPoll) - Timers->TriggerRemoteTimerPoll(); // Make sure there is enough free disk space for ongoing recordings: AssertFreeDiskSpace(Timers->GetMaxPriority()); TimersStateKey.Remove(TimersModified);