Initiating the client side of a peer-to-peer SVDRP connection is now done with the new SVDRP command CONN instead of using the UDP port with the server's address

This commit is contained in:
Klaus Schmidinger 2018-02-20 13:28:04 +01:00
parent a72806a0ba
commit 361d642660
3 changed files with 200 additions and 104 deletions

View File

@ -9162,7 +9162,7 @@ Video Disk Recorder Revision History
a subdirectory. a subdirectory.
- SVDRP peering can now be limited to the default SVDRP host (see MANUAL for details). - SVDRP peering can now be limited to the default SVDRP host (see MANUAL for details).
2018-02-15: Version 2.3.9 2018-02-20: Version 2.3.9
- Updated the Italian OSD texts (thanks to Diego Pierotto). - Updated the Italian OSD texts (thanks to Diego Pierotto).
- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg). - Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
@ -9279,3 +9279,7 @@ Video Disk Recorder Revision History
improved logging and debug output. improved logging and debug output.
- Fixed case inconsistency with SVDRPDefaultHost in config.c. - Fixed case inconsistency with SVDRPDefaultHost in config.c.
- Added a section about the '.sort' file to vdr.5. - Added a section about the '.sort' file to vdr.5.
- Initiating the client side of a peer-to-peer SVDRP connection is now done with the new
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.

279
svdrp.c
View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured * and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection. * graphical interface that sits on top of an SVDRP connection.
* *
* $Id: svdrp.c 4.26 2018/02/15 14:21:37 kls Exp $ * $Id: svdrp.c 4.27 2018/02/20 13:28:04 kls Exp $
*/ */
#include "svdrp.h" #include "svdrp.h"
@ -106,7 +106,7 @@ public:
void Close(void); void Close(void);
int Port(void) const { return port; } int Port(void) const { return port; }
int Socket(void) const { return sock; } int Socket(void) const { return sock; }
static bool SendDgram(const char *Dgram, int Port, const char *Address = NULL); static bool SendDgram(const char *Dgram, int Port);
int Accept(void); int Accept(void);
cString Discover(void); cString Discover(void);
const cIpAddress *LastIpAddress(void) const { return &lastIpAddress; } const cIpAddress *LastIpAddress(void) const { return &lastIpAddress; }
@ -210,13 +210,14 @@ bool cSocket::Connect(const char *Address)
LOG_ERROR; LOG_ERROR;
return false; return false;
} }
dbgsvdrp("> %s:%d server connection established\n", Address, port);
isyslog("SVDRP %s > %s:%d server connection established", Setup.SVDRPHostName, Address, port); isyslog("SVDRP %s > %s:%d server connection established", Setup.SVDRPHostName, Address, port);
return true; return true;
} }
return false; return false;
} }
bool cSocket::SendDgram(const char *Dgram, int Port, const char *Address) bool cSocket::SendDgram(const char *Dgram, int Port)
{ {
// Create a socket: // Create a socket:
int Socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); int Socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
@ -224,7 +225,6 @@ bool cSocket::SendDgram(const char *Dgram, int Port, const char *Address)
LOG_ERROR; LOG_ERROR;
return false; return false;
} }
if (!Address) {
// Enable broadcast: // Enable broadcast:
int One = 1; int One = 1;
if (setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &One, sizeof(One)) < 0) { if (setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &One, sizeof(One)) < 0) {
@ -232,12 +232,11 @@ bool cSocket::SendDgram(const char *Dgram, int Port, const char *Address)
close(Socket); close(Socket);
return false; return false;
} }
}
// Configure port and ip: // Configure port and ip:
sockaddr_in Addr; sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr)); memset(&Addr, 0, sizeof(Addr));
Addr.sin_family = AF_INET; Addr.sin_family = AF_INET;
Addr.sin_addr.s_addr = Address ? inet_addr(Address) : htonl(INADDR_BROADCAST); Addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
Addr.sin_port = htons(Port); Addr.sin_port = htons(Port);
// Send datagram: // Send datagram:
dbgsvdrp("> %s:%d %s\n", inet_ntoa(Addr.sin_addr), Port, Dgram); dbgsvdrp("> %s:%d %s\n", inet_ntoa(Addr.sin_addr), Port, Dgram);
@ -310,23 +309,25 @@ cString cSocket::Discover(void)
class cSVDRPClient { class cSVDRPClient {
private: private:
cIpAddress ipAddress; cIpAddress serverIpAddress;
cSocket socket; cSocket socket;
cString serverName; cString serverName;
int timeout; int timeout;
cTimeMs pingTime; cTimeMs pingTime;
cFile file; cFile file;
int fetchFlags; int fetchFlags;
bool connected;
void Close(void); void Close(void);
public: public:
cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout); cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout);
~cSVDRPClient(); ~cSVDRPClient();
const char *ServerName(void) const { return serverName; } const char *ServerName(void) const { return serverName; }
const char *Connection(void) const { return ipAddress.Connection(); } const char *Connection(void) const { return serverIpAddress.Connection(); }
bool HasAddress(const char *Address, int Port) const; bool HasAddress(const char *Address, int Port) const;
bool Send(const char *Command); bool Send(const char *Command);
bool Process(cStringList *Response = NULL); bool Process(cStringList *Response = NULL);
bool Execute(const char *Command, cStringList *Response = NULL); bool Execute(const char *Command, cStringList *Response = NULL);
bool Connected(void) const { return connected; }
void SetFetchFlag(eSvdrpFetchFlags Flag); void SetFetchFlag(eSvdrpFetchFlags Flag);
bool HasFetchFlag(eSvdrpFetchFlags Flag); bool HasFetchFlag(eSvdrpFetchFlags Flag);
}; };
@ -334,27 +335,28 @@ public:
static cPoller SVDRPClientPoller; static cPoller SVDRPClientPoller;
cSVDRPClient::cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout) cSVDRPClient::cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout)
:ipAddress(Address, Port) :serverIpAddress(Address, Port)
,socket(Port, true) ,socket(Port, true)
{ {
serverName = ServerName; serverName = ServerName;
timeout = Timeout * 1000 * 9 / 10; // ping after 90% of timeout timeout = Timeout * 1000 * 9 / 10; // ping after 90% of timeout
pingTime.Set(timeout); pingTime.Set(timeout);
fetchFlags = sffTimers; fetchFlags = sffNone;
connected = false;
if (socket.Connect(Address)) { if (socket.Connect(Address)) {
if (file.Open(socket.Socket())) { if (file.Open(socket.Socket())) {
SVDRPClientPoller.Add(file, false); SVDRPClientPoller.Add(file, false);
dsyslog("SVDRP %s > %s client created for '%s'", Setup.SVDRPHostName, ipAddress.Connection(), *serverName); dsyslog("SVDRP %s > %s client created for '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
return; return;
} }
} }
esyslog("SVDRP %s > %s ERROR: failed to create client for '%s'", Setup.SVDRPHostName, ipAddress.Connection(), *serverName); esyslog("SVDRP %s > %s ERROR: failed to create client for '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
} }
cSVDRPClient::~cSVDRPClient() cSVDRPClient::~cSVDRPClient()
{ {
Close(); Close();
dsyslog("SVDRP %s > %s client destroyed for '%s'", Setup.SVDRPHostName, ipAddress.Connection(), *serverName); dsyslog("SVDRP %s > %s client destroyed for '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
} }
void cSVDRPClient::Close(void) void cSVDRPClient::Close(void)
@ -371,13 +373,13 @@ void cSVDRPClient::Close(void)
bool cSVDRPClient::HasAddress(const char *Address, int Port) const bool cSVDRPClient::HasAddress(const char *Address, int Port) const
{ {
return strcmp(ipAddress.Address(), Address) == 0 && ipAddress.Port() == Port; return strcmp(serverIpAddress.Address(), Address) == 0 && serverIpAddress.Port() == Port;
} }
bool cSVDRPClient::Send(const char *Command) bool cSVDRPClient::Send(const char *Command)
{ {
pingTime.Set(timeout); pingTime.Set(timeout);
dbgsvdrp("> %s: %s\n", *serverName, Command); dbgsvdrp("> C %s: %s\n", *serverName, Command);
if (safe_write(file, Command, strlen(Command) + 1) < 0) { if (safe_write(file, Command, strlen(Command) + 1) < 0) {
LOG_ERROR; LOG_ERROR;
return false; return false;
@ -403,7 +405,7 @@ bool cSVDRPClient::Process(cStringList *Response)
input[--numChars] = 0; input[--numChars] = 0;
// make sure the string is terminated: // make sure the string is terminated:
input[numChars] = 0; input[numChars] = 0;
dbgsvdrp("< %s: %s\n", *serverName, input); dbgsvdrp("< C %s: %s\n", *serverName, input);
if (Response) if (Response)
Response->Append(strdup(input)); Response->Append(strdup(input));
else { else {
@ -414,12 +416,16 @@ bool cSVDRPClient::Process(cStringList *Response)
*t = 0; *t = 0;
if (strcmp(n, serverName) != 0) { if (strcmp(n, serverName) != 0) {
serverName = n; serverName = n;
dsyslog("SVDRP %s < %s remote server name is '%s'", Setup.SVDRPHostName, ipAddress.Connection(), *serverName); dsyslog("SVDRP %s < %s remote server name is '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
} }
Execute(cString::sprintf("CONN name:%s port:%d vdrversion:%d apiversion:%d timeout:%d", Setup.SVDRPHostName, SVDRPTcpPort, VDRVERSNUM, APIVERSNUM, Setup.SVDRPTimeout));
SetFetchFlag(sffTimers);
connected = true;
} }
} }
break; break;
case 221: dsyslog("SVDRP %s < %s remote server closed connection to '%s'", Setup.SVDRPHostName, ipAddress.Connection(), *serverName); case 221: dsyslog("SVDRP %s < %s remote server closed connection to '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
connected = false;
Close(); Close();
break; break;
} }
@ -430,7 +436,7 @@ bool cSVDRPClient::Process(cStringList *Response)
} }
else { else {
if (numChars >= int(sizeof(input))) { if (numChars >= int(sizeof(input))) {
esyslog("SVDRP %s < %s ERROR: out of memory", Setup.SVDRPHostName, ipAddress.Connection()); esyslog("SVDRP %s < %s ERROR: out of memory", Setup.SVDRPHostName, serverIpAddress.Connection());
Close(); Close();
break; break;
} }
@ -440,13 +446,13 @@ bool cSVDRPClient::Process(cStringList *Response)
Timeout.Set(SVDRPResonseTimeout); Timeout.Set(SVDRPResonseTimeout);
} }
else if (r <= 0) { else if (r <= 0) {
isyslog("SVDRP %s < %s lost connection to remote server '%s'", Setup.SVDRPHostName, ipAddress.Connection(), *serverName); isyslog("SVDRP %s < %s lost connection to remote server '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
Close(); Close();
return false; return false;
} }
} }
else if (Timeout.TimedOut()) { else if (Timeout.TimedOut()) {
esyslog("SVDRP %s < %s timeout while waiting for response from '%s'", Setup.SVDRPHostName, ipAddress.Connection(), *serverName); esyslog("SVDRP %s < %s timeout while waiting for response from '%s'", Setup.SVDRPHostName, serverIpAddress.Connection(), *serverName);
return false; return false;
} }
else if (!Response && numChars == 0) else if (!Response && numChars == 0)
@ -477,6 +483,70 @@ bool cSVDRPClient::HasFetchFlag(eSvdrpFetchFlags Flag)
return Result; return Result;
} }
// --- cSVDRPServerParams ----------------------------------------------------
class cSVDRPServerParams {
private:
cString name;
int port;
cString vdrversion;
cString apiversion;
int timeout;
cString host;
cString error;
public:
cSVDRPServerParams(const char *Params);
const char *Name(void) const { return name; }
const int Port(void) const { return port; }
const char *VdrVersion(void) const { return vdrversion; }
const char *ApiVersion(void) const { return apiversion; }
const int Timeout(void) const { return timeout; }
const char *Host(void) const { return host; }
bool Ok(void) const { return !*error; }
const char *Error(void) const { return error; }
};
cSVDRPServerParams::cSVDRPServerParams(const char *Params)
{
if (Params && *Params) {
name = strgetval(Params, "name", ':');
if (*name) {
cString p = strgetval(Params, "port", ':');
if (*p) {
port = atoi(p);
vdrversion = strgetval(Params, "vdrversion", ':');
if (*vdrversion) {
apiversion = strgetval(Params, "apiversion", ':');
if (*apiversion) {
cString t = strgetval(Params, "timeout", ':');
if (*t) {
timeout = atoi(t);
if (timeout > 10) { // don't let it get too small
host = strgetval(Params, "host", ':');
// no error if missing - this parameter is optional!
}
else
error = "invalid timeout";
}
else
error = "missing server timeout";
}
else
error = "missing server apiversion";
}
else
error = "missing server vdrversion";
}
else
error = "missing server port";
}
else
error = "missing server name";
}
else
error = "missing server parameters";
}
// --- cSVDRPClientHandler --------------------------------------------------- // --- cSVDRPClientHandler ---------------------------------------------------
class cSVDRPClientHandler : public cThread { class cSVDRPClientHandler : public cThread {
@ -487,16 +557,17 @@ private:
cVector<cSVDRPClient *> clientConnections; cVector<cSVDRPClient *> clientConnections;
void HandleClientConnection(void); void HandleClientConnection(void);
void ProcessConnections(void); void ProcessConnections(void);
cSVDRPClient *GetClientForServer(const char *ServerName);
protected: protected:
virtual void Action(void); virtual void Action(void);
public: public:
cSVDRPClientHandler(int TcpPort, int UdpPort); cSVDRPClientHandler(int TcpPort, int UdpPort);
virtual ~cSVDRPClientHandler(); virtual ~cSVDRPClientHandler();
void SendDiscover(const char *Address = NULL); void SendDiscover(void);
void AddClient(cSVDRPServerParams &ServerParams, const char *IpAddress);
bool Execute(const char *ServerName, const char *Command, cStringList *Response = NULL); bool Execute(const char *ServerName, const char *Command, cStringList *Response = NULL);
bool GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlags = sffNone); bool GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlags = sffNone);
bool TriggerFetchingTimers(const char *ServerName); bool TriggerFetchingTimers(const char *ServerName);
cSVDRPClient *GetClientForServer(const char *ServerName);
}; };
static cSVDRPClientHandler *SVDRPClientHandler = NULL; static cSVDRPClientHandler *SVDRPClientHandler = NULL;
@ -517,6 +588,7 @@ cSVDRPClientHandler::~cSVDRPClientHandler()
cSVDRPClient *cSVDRPClientHandler::GetClientForServer(const char *ServerName) cSVDRPClient *cSVDRPClientHandler::GetClientForServer(const char *ServerName)
{ {
cMutexLock MutexLock(&mutex);
for (int i = 0; i < clientConnections.Size(); i++) { for (int i = 0; i < clientConnections.Size(); i++) {
if (strcmp(clientConnections[i]->ServerName(), ServerName) == 0) if (strcmp(clientConnections[i]->ServerName(), ServerName) == 0)
return clientConnections[i]; return clientConnections[i];
@ -524,11 +596,11 @@ cSVDRPClient *cSVDRPClientHandler::GetClientForServer(const char *ServerName)
return NULL; return NULL;
} }
void cSVDRPClientHandler::SendDiscover(const char *Address) void cSVDRPClientHandler::SendDiscover(void)
{ {
cMutexLock MutexLock(&mutex); 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) : ""); 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(), Address); udpSocket.SendDgram(Dgram, udpSocket.Port());
} }
void cSVDRPClientHandler::ProcessConnections(void) void cSVDRPClientHandler::ProcessConnections(void)
@ -543,45 +615,30 @@ void cSVDRPClientHandler::ProcessConnections(void)
} }
} }
void cSVDRPClientHandler::AddClient(cSVDRPServerParams &ServerParams, const char *IpAddress)
{
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());
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
if (ServerParams.Host() && strcmp(ServerParams.Host(), Setup.SVDRPHostName) != 0)
return; // the remote VDR requests a specific host, but it's not us
clientConnections.Append(new cSVDRPClient(IpAddress, ServerParams.Port(), ServerParams.Name(), ServerParams.Timeout()));
}
void cSVDRPClientHandler::HandleClientConnection(void) void cSVDRPClientHandler::HandleClientConnection(void)
{ {
cString NewDiscover = udpSocket.Discover(); cString NewDiscover = udpSocket.Discover();
if (*NewDiscover) { if (*NewDiscover) {
cString p = strgetval(NewDiscover, "port", ':'); cSVDRPServerParams ServerParams(NewDiscover);
if (*p) { if (ServerParams.Ok())
int Port = atoi(p); AddClient(ServerParams, udpSocket.LastIpAddress()->Address());
for (int i = 0; i < clientConnections.Size(); i++) {
if (clientConnections[i]->HasAddress(udpSocket.LastIpAddress()->Address(), Port)) {
dsyslog("SVDRP %s < %s connection to '%s' confirmed", Setup.SVDRPHostName, clientConnections[i]->Connection(), clientConnections[i]->ServerName());
return;
}
}
cString ServerName = strgetval(NewDiscover, "name", ':');
if (*ServerName) {
if (Setup.SVDRPPeering == spmOnly && strcmp(ServerName, Setup.SVDRPDefaultHost) != 0)
return; // we only want to peer with the default host, but this isn't the default host
cString HostName = strgetval(NewDiscover, "host", ':');
if (*HostName && strcmp(HostName, Setup.SVDRPHostName) != 0)
return; // the remote VDR requests a specific host, but it's not us
cString t = strgetval(NewDiscover, "timeout", ':');
if (*t) {
int Timeout = atoi(t);
if (Timeout > 10) { // don't let it get too small
const char *Address = udpSocket.LastIpAddress()->Address();
clientConnections.Append(new cSVDRPClient(Address, Port, ServerName, Timeout));
SendDiscover(Address);
}
else else
esyslog("SVDRP %s < %s ERROR: invalid timeout (%d)", Setup.SVDRPHostName, udpSocket.LastIpAddress()->Connection(), Timeout); esyslog("SVDRP %s < %s ERROR: %s", Setup.SVDRPHostName, udpSocket.LastIpAddress()->Connection(), ServerParams.Error());
}
else
esyslog("SVDRP %s < %s ERROR: missing timeout", Setup.SVDRPHostName, udpSocket.LastIpAddress()->Connection());
}
else
esyslog("SVDRP %s < %s ERROR: missing server name", Setup.SVDRPHostName, udpSocket.LastIpAddress()->Connection());
}
else
esyslog("SVDRP %s < %s ERROR: missing port number", Setup.SVDRPHostName, udpSocket.LastIpAddress()->Connection());
} }
} }
@ -615,9 +672,11 @@ bool cSVDRPClientHandler::GetServerNames(cStringList *ServerNames, eSvdrpFetchFl
ServerNames->Clear(); ServerNames->Clear();
for (int i = 0; i < clientConnections.Size(); i++) { for (int i = 0; i < clientConnections.Size(); i++) {
cSVDRPClient *Client = clientConnections[i]; cSVDRPClient *Client = clientConnections[i];
if (Client->Connected()) {
if (FetchFlag == sffNone || Client->HasFetchFlag(FetchFlag)) if (FetchFlag == sffNone || Client->HasFetchFlag(FetchFlag))
ServerNames->Append(strdup(Client->ServerName())); ServerNames->Append(strdup(Client->ServerName()));
} }
}
return ServerNames->Size() > 0; return ServerNames->Size() > 0;
} }
@ -708,6 +767,10 @@ const char *HelpPages[] = {
" After a CLRE command, no further EPG processing is done for 10\n" " After a CLRE command, no further EPG processing is done for 10\n"
" seconds, so that data sent with subsequent PUTE commands doesn't\n" " seconds, so that data sent with subsequent PUTE commands doesn't\n"
" interfere with data from the broadcasters.", " interfere with data from the broadcasters.",
"CONN name:<name> port:<port> vdrversion:<vdrversion> apiversion:<apiversion> timeout:<timeout>\n"
" Used by peer-to-peer connections between VDRs to tell the other VDR\n"
" to establish a connection to this VDR. The name is the SVDRP host name\n"
" of this VDR, which may differ from its DNS name.",
"DELC <number>\n" "DELC <number>\n"
" Delete channel.", " Delete channel.",
"DELR <id>\n" "DELR <id>\n"
@ -929,7 +992,8 @@ static cString grabImageDir;
class cSVDRPServer { class cSVDRPServer {
private: private:
int socket; int socket;
cString connection; cIpAddress clientIpAddress;
cString clientName;
cFile file; cFile file;
cPUTEhandler *PUTEhandler; cPUTEhandler *PUTEhandler;
int numChars; int numChars;
@ -942,6 +1006,7 @@ private:
void PrintHelpTopics(const char **hp); void PrintHelpTopics(const char **hp);
void CmdCHAN(const char *Option); void CmdCHAN(const char *Option);
void CmdCLRE(const char *Option); void CmdCLRE(const char *Option);
void CmdCONN(const char *Option);
void CmdDELC(const char *Option); void CmdDELC(const char *Option);
void CmdDELR(const char *Option); void CmdDELR(const char *Option);
void CmdDELT(const char *Option); void CmdDELT(const char *Option);
@ -976,18 +1041,20 @@ private:
void CmdVOLU(const char *Option); void CmdVOLU(const char *Option);
void Execute(char *Cmd); void Execute(char *Cmd);
public: public:
cSVDRPServer(int Socket, const char *Connection); cSVDRPServer(int Socket, const cIpAddress *ClientIpAddress);
~cSVDRPServer(); ~cSVDRPServer();
const char *ClientName(void) const { return clientName; }
bool HasConnection(void) { return file.IsOpen(); } bool HasConnection(void) { return file.IsOpen(); }
bool Process(void); bool Process(void);
}; };
static cPoller SVDRPServerPoller; static cPoller SVDRPServerPoller;
cSVDRPServer::cSVDRPServer(int Socket, const char *Connection) cSVDRPServer::cSVDRPServer(int Socket, const cIpAddress *ClientIpAddress)
{ {
socket = Socket; socket = Socket;
connection = Connection; clientIpAddress = *ClientIpAddress;
clientName = clientIpAddress.Connection(); // will be set to actual name by a CONN command
PUTEhandler = NULL; PUTEhandler = NULL;
numChars = 0; numChars = 0;
length = BUFSIZ; length = BUFSIZ;
@ -998,14 +1065,14 @@ cSVDRPServer::cSVDRPServer(int Socket, const char *Connection)
Reply(220, "%s SVDRP VideoDiskRecorder %s; %s; %s", Setup.SVDRPHostName, VDRVERSION, *TimeToString(now), cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8"); Reply(220, "%s SVDRP VideoDiskRecorder %s; %s; %s", Setup.SVDRPHostName, VDRVERSION, *TimeToString(now), cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8");
SVDRPServerPoller.Add(file, false); SVDRPServerPoller.Add(file, false);
} }
dsyslog("SVDRP %s < %s server created", Setup.SVDRPHostName, *connection); dsyslog("SVDRP %s > %s server created", Setup.SVDRPHostName, *clientName);
} }
cSVDRPServer::~cSVDRPServer() cSVDRPServer::~cSVDRPServer()
{ {
Close(true); Close(true);
free(cmdLine); free(cmdLine);
dsyslog("SVDRP %s < %s server destroyed", Setup.SVDRPHostName, *connection); dsyslog("SVDRP %s < %s server destroyed", Setup.SVDRPHostName, *clientName);
} }
void cSVDRPServer::Close(bool SendReply, bool Timeout) void cSVDRPServer::Close(bool SendReply, bool Timeout)
@ -1014,7 +1081,7 @@ void cSVDRPServer::Close(bool SendReply, bool Timeout)
if (SendReply) { if (SendReply) {
Reply(221, "%s closing connection%s", Setup.SVDRPHostName, Timeout ? " (timeout)" : ""); Reply(221, "%s closing connection%s", Setup.SVDRPHostName, Timeout ? " (timeout)" : "");
} }
isyslog("SVDRP %s < %s connection closed", Setup.SVDRPHostName, *connection); isyslog("SVDRP %s < %s connection closed", Setup.SVDRPHostName, *clientName);
SVDRPServerPoller.Del(file, false); SVDRPServerPoller.Del(file, false);
file.Close(); file.Close();
DELETENULL(PUTEhandler); DELETENULL(PUTEhandler);
@ -1024,7 +1091,7 @@ void cSVDRPServer::Close(bool SendReply, bool Timeout)
bool cSVDRPServer::Send(const char *s) bool cSVDRPServer::Send(const char *s)
{ {
dbgsvdrp("> %s: %s", *connection, s); // terminating newline is already in the string! dbgsvdrp("> S %s: %s", *clientName, s); // terminating newline is already in the string!
if (safe_write(file, s, strlen(s)) < 0) { if (safe_write(file, s, strlen(s)) < 0) {
LOG_ERROR; LOG_ERROR;
Close(); Close();
@ -1056,14 +1123,14 @@ void cSVDRPServer::Reply(int Code, const char *fmt, ...)
} }
else { else {
Reply(451, "Bad format - looks like a programming error!"); Reply(451, "Bad format - looks like a programming error!");
esyslog("SVDRP %s < %s bad format!", Setup.SVDRPHostName, *connection); esyslog("SVDRP %s < %s bad format!", Setup.SVDRPHostName, *clientName);
} }
va_end(ap); va_end(ap);
free(buffer); free(buffer);
} }
else { else {
Reply(451, "Zero return code - looks like a programming error!"); Reply(451, "Zero return code - looks like a programming error!");
esyslog("SVDRP %s < %s zero return code!", Setup.SVDRPHostName, *connection); esyslog("SVDRP %s < %s zero return code!", Setup.SVDRPHostName, *clientName);
} }
} }
} }
@ -1219,6 +1286,27 @@ void cSVDRPServer::CmdCLRE(const char *Option)
} }
} }
void cSVDRPServer::CmdCONN(const char *Option)
{
if (*Option) {
if (SVDRPClientHandler) {
cSVDRPServerParams ServerParams(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());
}
else
Reply(501, "Error in server parameters: %s", ServerParams.Error());
}
else
Reply(451, "No SVDRP client handler");
}
else
Reply(501, "Missing server parameters");
}
void cSVDRPServer::CmdDELC(const char *Option) void cSVDRPServer::CmdDELC(const char *Option)
{ {
if (*Option) { if (*Option) {
@ -1248,7 +1336,7 @@ void cSVDRPServer::CmdDELC(const char *Option)
Channels->ReNumber(); Channels->ReNumber();
Channels->SetModifiedByUser(); Channels->SetModifiedByUser();
Channels->SetModified(); Channels->SetModified();
isyslog("SVDRP %s < %s deleted channel %s", Setup.SVDRPHostName, *connection, Option); isyslog("SVDRP %s < %s deleted channel %s", Setup.SVDRPHostName, *clientName, Option);
if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) { if (CurrentChannel && CurrentChannel->Number() != CurrentChannelNr) {
if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
Channels->SwitchTo(CurrentChannel->Number()); Channels->SwitchTo(CurrentChannel->Number());
@ -1296,7 +1384,7 @@ void cSVDRPServer::CmdDELR(const char *Option)
if (Recording->Delete()) { if (Recording->Delete()) {
Recordings->DelByName(Recording->FileName()); Recordings->DelByName(Recording->FileName());
Recordings->SetModified(); Recordings->SetModified();
isyslog("SVDRP %s < %s deleted recording %s", Setup.SVDRPHostName, *connection, Option); isyslog("SVDRP %s < %s deleted recording %s", Setup.SVDRPHostName, *clientName, Option);
Reply(250, "Recording \"%s\" deleted", Option); Reply(250, "Recording \"%s\" deleted", Option);
} }
else else
@ -1326,7 +1414,7 @@ void cSVDRPServer::CmdDELT(const char *Option)
} }
Timers->Del(Timer); Timers->Del(Timer);
Timers->SetModified(); Timers->SetModified();
isyslog("SVDRP %s < %s deleted timer %s", Setup.SVDRPHostName, *connection, *Timer->ToDescr()); isyslog("SVDRP %s < %s deleted timer %s", Setup.SVDRPHostName, *clientName, *Timer->ToDescr());
Reply(250, "Timer \"%s\" deleted", Option); Reply(250, "Timer \"%s\" deleted", Option);
} }
else else
@ -1472,7 +1560,7 @@ void cSVDRPServer::CmdGRAB(const char *Option)
int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE); int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
if (fd >= 0) { if (fd >= 0) {
if (safe_write(fd, Image, ImageSize) == ImageSize) { if (safe_write(fd, Image, ImageSize) == ImageSize) {
dsyslog("SVDRP %s < %s grabbed image to %s", Setup.SVDRPHostName, *connection, FileName); dsyslog("SVDRP %s < %s grabbed image to %s", Setup.SVDRPHostName, *clientName, FileName);
Reply(250, "Grabbed image %s", Option); Reply(250, "Grabbed image %s", Option);
} }
else { else {
@ -1826,7 +1914,7 @@ void cSVDRPServer::CmdLSTT(const char *Option)
void cSVDRPServer::CmdMESG(const char *Option) void cSVDRPServer::CmdMESG(const char *Option)
{ {
if (*Option) { if (*Option) {
isyslog("SVDRP %s < %s message '%s'", Setup.SVDRPHostName, *connection, Option); isyslog("SVDRP %s < %s message '%s'", Setup.SVDRPHostName, *clientName, Option);
Skins.QueueMessage(mtInfo, Option); Skins.QueueMessage(mtInfo, Option);
Reply(250, "Message queued"); Reply(250, "Message queued");
} }
@ -1851,7 +1939,7 @@ void cSVDRPServer::CmdMODC(const char *Option)
Channels->ReNumber(); Channels->ReNumber();
Channels->SetModifiedByUser(); Channels->SetModifiedByUser();
Channels->SetModified(); Channels->SetModified();
isyslog("SVDRP %s < %s modifed channel %d %s", Setup.SVDRPHostName, *connection, Channel->Number(), *Channel->ToText()); isyslog("SVDRP %s < %s modifed channel %d %s", Setup.SVDRPHostName, *clientName, Channel->Number(), *Channel->ToText());
Reply(250, "%d %s", Channel->Number(), *Channel->ToText()); Reply(250, "%d %s", Channel->Number(), *Channel->ToText());
} }
else else
@ -1891,7 +1979,7 @@ void cSVDRPServer::CmdMODT(const char *Option)
} }
*Timer = t; *Timer = t;
Timers->SetModified(); Timers->SetModified();
isyslog("SVDRP %s < %s modified timer %s (%s)", Setup.SVDRPHostName, *connection, *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "active" : "inactive"); isyslog("SVDRP %s < %s modified timer %s (%s)", Setup.SVDRPHostName, *clientName, *Timer->ToDescr(), Timer->HasFlags(tfActive) ? "active" : "inactive");
Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true)); Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true));
} }
else else
@ -1935,7 +2023,7 @@ void cSVDRPServer::CmdMOVC(const char *Option)
else else
cDevice::SetCurrentChannel(CurrentChannel->Number()); cDevice::SetCurrentChannel(CurrentChannel->Number());
} }
isyslog("SVDRP %s < %s moved channel %d to %d", Setup.SVDRPHostName, *connection, FromNumber, ToNumber); isyslog("SVDRP %s < %s moved channel %d to %d", Setup.SVDRPHostName, *clientName, FromNumber, ToNumber);
Reply(250,"Channel \"%d\" moved to \"%d\"", From, To); Reply(250,"Channel \"%d\" moved to \"%d\"", From, To);
} }
else else
@ -2015,7 +2103,7 @@ void cSVDRPServer::CmdNEWC(const char *Option)
Channels->ReNumber(); Channels->ReNumber();
Channels->SetModifiedByUser(); Channels->SetModifiedByUser();
Channels->SetModified(); Channels->SetModified();
isyslog("SVDRP %s < %s new channel %d %s", Setup.SVDRPHostName, *connection, channel->Number(), *channel->ToText()); isyslog("SVDRP %s < %s new channel %d %s", Setup.SVDRPHostName, *clientName, channel->Number(), *channel->ToText());
Reply(250, "%d %s", channel->Number(), *channel->ToText()); Reply(250, "%d %s", channel->Number(), *channel->ToText());
} }
else else
@ -2036,7 +2124,7 @@ void cSVDRPServer::CmdNEWT(const char *Option)
LOCK_TIMERS_WRITE; LOCK_TIMERS_WRITE;
Timer->ClrFlags(tfRecording); Timer->ClrFlags(tfRecording);
Timers->Add(Timer); Timers->Add(Timer);
isyslog("SVDRP %s < %s added timer %s", Setup.SVDRPHostName, *connection, *Timer->ToDescr()); isyslog("SVDRP %s < %s added timer %s", Setup.SVDRPHostName, *clientName, *Timer->ToDescr());
Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true)); Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true));
return; return;
} }
@ -2325,11 +2413,11 @@ void cSVDRPServer::CmdUPDT(const char *Option)
t->Parse(Option); t->Parse(Option);
delete Timer; delete Timer;
Timer = t; Timer = t;
isyslog("SVDRP %s < %s updated timer %s", Setup.SVDRPHostName, *connection, *Timer->ToDescr()); isyslog("SVDRP %s < %s updated timer %s", Setup.SVDRPHostName, *clientName, *Timer->ToDescr());
} }
else { else {
Timers->Add(Timer); Timers->Add(Timer);
isyslog("SVDRP %s < %s added timer %s", Setup.SVDRPHostName, *connection, *Timer->ToDescr()); isyslog("SVDRP %s < %s added timer %s", Setup.SVDRPHostName, *clientName, *Timer->ToDescr());
} }
Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true)); Reply(250, "%d %s", Timer->Id(), *Timer->ToText(true));
return; return;
@ -2395,6 +2483,7 @@ void cSVDRPServer::Execute(char *Cmd)
s = skipspace(s); s = skipspace(s);
if (CMD("CHAN")) CmdCHAN(s); if (CMD("CHAN")) CmdCHAN(s);
else if (CMD("CLRE")) CmdCLRE(s); else if (CMD("CLRE")) CmdCLRE(s);
else if (CMD("CONN")) CmdCONN(s);
else if (CMD("DELC")) CmdDELC(s); else if (CMD("DELC")) CmdDELC(s);
else if (CMD("DELR")) CmdDELR(s); else if (CMD("DELR")) CmdDELR(s);
else if (CMD("DELT")) CmdDELT(s); else if (CMD("DELT")) CmdDELT(s);
@ -2445,7 +2534,7 @@ bool cSVDRPServer::Process(void)
// make sure the string is terminated: // make sure the string is terminated:
cmdLine[numChars] = 0; cmdLine[numChars] = 0;
// showtime! // showtime!
dbgsvdrp("< %s: %s\n", *connection, cmdLine); dbgsvdrp("< S %s: %s\n", *clientName, cmdLine);
Execute(cmdLine); Execute(cmdLine);
numChars = 0; numChars = 0;
if (length > BUFSIZ) { if (length > BUFSIZ) {
@ -2474,7 +2563,7 @@ bool cSVDRPServer::Process(void)
cmdLine = NewBuffer; cmdLine = NewBuffer;
} }
else { else {
esyslog("SVDRP %s < %s ERROR: out of memory", Setup.SVDRPHostName, *connection); esyslog("SVDRP %s < %s ERROR: out of memory", Setup.SVDRPHostName, *clientName);
Close(); Close();
break; break;
} }
@ -2485,12 +2574,12 @@ bool cSVDRPServer::Process(void)
lastActivity = time(NULL); lastActivity = time(NULL);
} }
else if (r <= 0) { else if (r <= 0) {
isyslog("SVDRP %s < %s lost connection to client", Setup.SVDRPHostName, *connection); isyslog("SVDRP %s < %s lost connection to client", Setup.SVDRPHostName, *clientName);
Close(); Close();
} }
} }
if (Setup.SVDRPTimeout && time(NULL) - lastActivity > Setup.SVDRPTimeout) { if (Setup.SVDRPTimeout && time(NULL) - lastActivity > Setup.SVDRPTimeout) {
isyslog("SVDRP %s < %s timeout on connection", Setup.SVDRPHostName, *connection); isyslog("SVDRP %s < %s timeout on connection", Setup.SVDRPHostName, *clientName);
Close(true, true); Close(true, true);
} }
} }
@ -2524,6 +2613,7 @@ public:
cSVDRPServerHandler(int TcpPort); cSVDRPServerHandler(int TcpPort);
virtual ~cSVDRPServerHandler(); virtual ~cSVDRPServerHandler();
void WaitUntilReady(void); void WaitUntilReady(void);
cSVDRPServer *GetServerForClient(const char *ClientName);
}; };
static cSVDRPServerHandler *SVDRPServerHandler = NULL; static cSVDRPServerHandler *SVDRPServerHandler = NULL;
@ -2565,7 +2655,7 @@ void cSVDRPServerHandler::HandleServerConnection(void)
{ {
int NewSocket = tcpSocket.Accept(); int NewSocket = tcpSocket.Accept();
if (NewSocket >= 0) if (NewSocket >= 0)
serverConnections.Append(new cSVDRPServer(NewSocket, tcpSocket.LastIpAddress()->Connection())); serverConnections.Append(new cSVDRPServer(NewSocket, tcpSocket.LastIpAddress()));
} }
void cSVDRPServerHandler::Action(void) void cSVDRPServerHandler::Action(void)
@ -2584,6 +2674,16 @@ 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 --------------------------------------------------------- // --- SVDRP Handler ---------------------------------------------------------
static cMutex SVDRPHandlerMutex; static cMutex SVDRPHandlerMutex;
@ -2621,13 +2721,6 @@ void StopSVDRPClientHandler(void)
SVDRPClientHandler = NULL; SVDRPClientHandler = NULL;
} }
void SendSVDRPDiscover(const char *Address)
{
cMutexLock MutexLock(&SVDRPHandlerMutex);
if (SVDRPClientHandler)
SVDRPClientHandler->SendDiscover(Address);
}
bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag) bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag)
{ {
cMutexLock MutexLock(&SVDRPHandlerMutex); cMutexLock MutexLock(&SVDRPHandlerMutex);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: svdrp.h 4.7 2017/06/30 09:49:39 kls Exp $ * $Id: svdrp.h 4.8 2018/02/19 12:36:35 kls Exp $
*/ */
#ifndef __SVDRP_H #ifndef __SVDRP_H
@ -29,7 +29,6 @@ void StartSVDRPServerHandler(void);
void StartSVDRPClientHandler(void); void StartSVDRPClientHandler(void);
void StopSVDRPServerHandler(void); void StopSVDRPServerHandler(void);
void StopSVDRPClientHandler(void); void StopSVDRPClientHandler(void);
void SendSVDRPDiscover(const char *Address = NULL);
bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag = sffNone); bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag = sffNone);
///< Gets a list of all available VDRs this VDR is connected to via SVDRP, ///< Gets a list of all available VDRs this VDR is connected to via SVDRP,
///< and stores it in the given ServerNames list. The list is cleared ///< and stores it in the given ServerNames list. The list is cleared