diff options
| -rw-r--r-- | HISTORY | 3 | ||||
| -rw-r--r-- | menu.c | 4 | ||||
| -rw-r--r-- | svdrp.c | 72 | ||||
| -rw-r--r-- | timers.c | 94 | ||||
| -rw-r--r-- | timers.h | 21 | ||||
| -rw-r--r-- | vdr.c | 21 | 
6 files changed, 87 insertions, 128 deletions
| @@ -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(). @@ -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();          }       } @@ -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<cSVDRPClient *> 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<cSVDRPServer *> 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; @@ -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) @@ -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); @@ -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 <getopt.h> @@ -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); | 
