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.
- 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 Finnish OSD texts (thanks to Rolf Ahrenberg).
@ -9279,3 +9279,7 @@ Video Disk Recorder Revision History
improved logging and debug output.
- Fixed case inconsistency with SVDRPDefaultHost in config.c.
- 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.

295
svdrp.c
View File

@ -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.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"
@ -106,7 +106,7 @@ public:
void Close(void);
int Port(void) const { return port; }
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);
cString Discover(void);
const cIpAddress *LastIpAddress(void) const { return &lastIpAddress; }
@ -210,13 +210,14 @@ bool cSocket::Connect(const char *Address)
LOG_ERROR;
return false;
}
dbgsvdrp("> %s:%d server connection established\n", Address, port);
isyslog("SVDRP %s > %s:%d server connection established", Setup.SVDRPHostName, Address, port);
return true;
}
return false;
}
bool cSocket::SendDgram(const char *Dgram, int Port, const char *Address)
bool cSocket::SendDgram(const char *Dgram, int Port)
{
// Create a socket:
int Socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
@ -224,20 +225,18 @@ bool cSocket::SendDgram(const char *Dgram, int Port, const char *Address)
LOG_ERROR;
return false;
}
if (!Address) {
// Enable broadcast:
int One = 1;
if (setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &One, sizeof(One)) < 0) {
LOG_ERROR;
close(Socket);
return false;
}
// Enable broadcast:
int One = 1;
if (setsockopt(Socket, SOL_SOCKET, SO_BROADCAST, &One, sizeof(One)) < 0) {
LOG_ERROR;
close(Socket);
return false;
}
// Configure port and ip:
sockaddr_in Addr;
memset(&Addr, 0, sizeof(Addr));
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);
// Send datagram:
dbgsvdrp("> %s:%d %s\n", inet_ntoa(Addr.sin_addr), Port, Dgram);
@ -310,23 +309,25 @@ cString cSocket::Discover(void)
class cSVDRPClient {
private:
cIpAddress ipAddress;
cIpAddress serverIpAddress;
cSocket socket;
cString serverName;
int timeout;
cTimeMs pingTime;
cFile file;
int fetchFlags;
bool connected;
void Close(void);
public:
cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout);
~cSVDRPClient();
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 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);
};
@ -334,27 +335,28 @@ public:
static cPoller SVDRPClientPoller;
cSVDRPClient::cSVDRPClient(const char *Address, int Port, const char *ServerName, int Timeout)
:ipAddress(Address, Port)
:serverIpAddress(Address, Port)
,socket(Port, true)
{
serverName = ServerName;
timeout = Timeout * 1000 * 9 / 10; // ping after 90% of timeout
pingTime.Set(timeout);
fetchFlags = sffTimers;
fetchFlags = sffNone;
connected = false;
if (socket.Connect(Address)) {
if (file.Open(socket.Socket())) {
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;
}
}
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()
{
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)
@ -371,13 +373,13 @@ void cSVDRPClient::Close(void)
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)
{
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) {
LOG_ERROR;
return false;
@ -403,7 +405,7 @@ bool cSVDRPClient::Process(cStringList *Response)
input[--numChars] = 0;
// make sure the string is terminated:
input[numChars] = 0;
dbgsvdrp("< %s: %s\n", *serverName, input);
dbgsvdrp("< C %s: %s\n", *serverName, input);
if (Response)
Response->Append(strdup(input));
else {
@ -414,12 +416,16 @@ bool cSVDRPClient::Process(cStringList *Response)
*t = 0;
if (strcmp(n, serverName) != 0) {
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;
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();
break;
}
@ -430,7 +436,7 @@ bool cSVDRPClient::Process(cStringList *Response)
}
else {
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();
break;
}
@ -440,13 +446,13 @@ bool cSVDRPClient::Process(cStringList *Response)
Timeout.Set(SVDRPResonseTimeout);
}
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();
return false;
}
}
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;
}
else if (!Response && numChars == 0)
@ -477,6 +483,70 @@ bool cSVDRPClient::HasFetchFlag(eSvdrpFetchFlags Flag)
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 ---------------------------------------------------
class cSVDRPClientHandler : public cThread {
@ -487,16 +557,17 @@ private:
cVector<cSVDRPClient *> clientConnections;
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(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 GetServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlags = sffNone);
bool TriggerFetchingTimers(const char *ServerName);
cSVDRPClient *GetClientForServer(const char *ServerName);
};
static cSVDRPClientHandler *SVDRPClientHandler = NULL;
@ -517,6 +588,7 @@ 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];
@ -524,11 +596,11 @@ cSVDRPClient *cSVDRPClientHandler::GetClientForServer(const char *ServerName)
return NULL;
}
void cSVDRPClientHandler::SendDiscover(const char *Address)
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(), Address);
udpSocket.SendDgram(Dgram, udpSocket.Port());
}
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)
{
cString NewDiscover = udpSocket.Discover();
if (*NewDiscover) {
cString p = strgetval(NewDiscover, "port", ':');
if (*p) {
int Port = atoi(p);
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
esyslog("SVDRP %s < %s ERROR: invalid timeout (%d)", Setup.SVDRPHostName, udpSocket.LastIpAddress()->Connection(), Timeout);
}
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());
}
cSVDRPServerParams ServerParams(NewDiscover);
if (ServerParams.Ok())
AddClient(ServerParams, udpSocket.LastIpAddress()->Address());
else
esyslog("SVDRP %s < %s ERROR: missing port number", Setup.SVDRPHostName, udpSocket.LastIpAddress()->Connection());
esyslog("SVDRP %s < %s ERROR: %s", Setup.SVDRPHostName, udpSocket.LastIpAddress()->Connection(), ServerParams.Error());
}
}
@ -615,8 +672,10 @@ bool cSVDRPClientHandler::GetServerNames(cStringList *ServerNames, eSvdrpFetchFl
ServerNames->Clear();
for (int i = 0; i < clientConnections.Size(); i++) {
cSVDRPClient *Client = clientConnections[i];
if (FetchFlag == sffNone || Client->HasFetchFlag(FetchFlag))
ServerNames->Append(strdup(Client->ServerName()));
if (Client->Connected()) {
if (FetchFlag == sffNone || Client->HasFetchFlag(FetchFlag))
ServerNames->Append(strdup(Client->ServerName()));
}
}
return ServerNames->Size() > 0;
}
@ -708,6 +767,10 @@ const char *HelpPages[] = {
" 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"
" 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"
" Delete channel.",
"DELR <id>\n"
@ -929,7 +992,8 @@ static cString grabImageDir;
class cSVDRPServer {
private:
int socket;
cString connection;
cIpAddress clientIpAddress;
cString clientName;
cFile file;
cPUTEhandler *PUTEhandler;
int numChars;
@ -942,6 +1006,7 @@ private:
void PrintHelpTopics(const char **hp);
void CmdCHAN(const char *Option);
void CmdCLRE(const char *Option);
void CmdCONN(const char *Option);
void CmdDELC(const char *Option);
void CmdDELR(const char *Option);
void CmdDELT(const char *Option);
@ -976,18 +1041,20 @@ private:
void CmdVOLU(const char *Option);
void Execute(char *Cmd);
public:
cSVDRPServer(int Socket, const char *Connection);
cSVDRPServer(int Socket, const cIpAddress *ClientIpAddress);
~cSVDRPServer();
const char *ClientName(void) const { return clientName; }
bool HasConnection(void) { return file.IsOpen(); }
bool Process(void);
};
static cPoller SVDRPServerPoller;
cSVDRPServer::cSVDRPServer(int Socket, const char *Connection)
cSVDRPServer::cSVDRPServer(int Socket, const cIpAddress *ClientIpAddress)
{
socket = Socket;
connection = Connection;
clientIpAddress = *ClientIpAddress;
clientName = clientIpAddress.Connection(); // will be set to actual name by a CONN command
PUTEhandler = NULL;
numChars = 0;
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");
SVDRPServerPoller.Add(file, false);
}
dsyslog("SVDRP %s < %s server created", Setup.SVDRPHostName, *connection);
dsyslog("SVDRP %s > %s server created", Setup.SVDRPHostName, *clientName);
}
cSVDRPServer::~cSVDRPServer()
{
Close(true);
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)
@ -1014,7 +1081,7 @@ void cSVDRPServer::Close(bool SendReply, bool Timeout)
if (SendReply) {
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);
file.Close();
DELETENULL(PUTEhandler);
@ -1024,7 +1091,7 @@ void cSVDRPServer::Close(bool SendReply, bool Timeout)
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) {
LOG_ERROR;
Close();
@ -1056,14 +1123,14 @@ void cSVDRPServer::Reply(int Code, const char *fmt, ...)
}
else {
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);
free(buffer);
}
else {
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)
{
if (*Option) {
@ -1248,7 +1336,7 @@ void cSVDRPServer::CmdDELC(const char *Option)
Channels->ReNumber();
Channels->SetModifiedByUser();
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 (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring())
Channels->SwitchTo(CurrentChannel->Number());
@ -1296,7 +1384,7 @@ void cSVDRPServer::CmdDELR(const char *Option)
if (Recording->Delete()) {
Recordings->DelByName(Recording->FileName());
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);
}
else
@ -1326,7 +1414,7 @@ void cSVDRPServer::CmdDELT(const char *Option)
}
Timers->Del(Timer);
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);
}
else
@ -1472,7 +1560,7 @@ void cSVDRPServer::CmdGRAB(const char *Option)
int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
if (fd >= 0) {
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);
}
else {
@ -1826,7 +1914,7 @@ void cSVDRPServer::CmdLSTT(const char *Option)
void cSVDRPServer::CmdMESG(const char *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);
Reply(250, "Message queued");
}
@ -1851,7 +1939,7 @@ void cSVDRPServer::CmdMODC(const char *Option)
Channels->ReNumber();
Channels->SetModifiedByUser();
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());
}
else
@ -1891,7 +1979,7 @@ void cSVDRPServer::CmdMODT(const char *Option)
}
*Timer = t;
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));
}
else
@ -1935,7 +2023,7 @@ void cSVDRPServer::CmdMOVC(const char *Option)
else
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);
}
else
@ -2015,7 +2103,7 @@ void cSVDRPServer::CmdNEWC(const char *Option)
Channels->ReNumber();
Channels->SetModifiedByUser();
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());
}
else
@ -2036,7 +2124,7 @@ void cSVDRPServer::CmdNEWT(const char *Option)
LOCK_TIMERS_WRITE;
Timer->ClrFlags(tfRecording);
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));
return;
}
@ -2325,11 +2413,11 @@ void cSVDRPServer::CmdUPDT(const char *Option)
t->Parse(Option);
delete Timer;
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 {
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));
return;
@ -2395,6 +2483,7 @@ void cSVDRPServer::Execute(char *Cmd)
s = skipspace(s);
if (CMD("CHAN")) CmdCHAN(s);
else if (CMD("CLRE")) CmdCLRE(s);
else if (CMD("CONN")) CmdCONN(s);
else if (CMD("DELC")) CmdDELC(s);
else if (CMD("DELR")) CmdDELR(s);
else if (CMD("DELT")) CmdDELT(s);
@ -2445,7 +2534,7 @@ bool cSVDRPServer::Process(void)
// make sure the string is terminated:
cmdLine[numChars] = 0;
// showtime!
dbgsvdrp("< %s: %s\n", *connection, cmdLine);
dbgsvdrp("< S %s: %s\n", *clientName, cmdLine);
Execute(cmdLine);
numChars = 0;
if (length > BUFSIZ) {
@ -2474,7 +2563,7 @@ bool cSVDRPServer::Process(void)
cmdLine = NewBuffer;
}
else {
esyslog("SVDRP %s < %s ERROR: out of memory", Setup.SVDRPHostName, *connection);
esyslog("SVDRP %s < %s ERROR: out of memory", Setup.SVDRPHostName, *clientName);
Close();
break;
}
@ -2485,12 +2574,12 @@ bool cSVDRPServer::Process(void)
lastActivity = time(NULL);
}
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();
}
}
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);
}
}
@ -2524,6 +2613,7 @@ public:
cSVDRPServerHandler(int TcpPort);
virtual ~cSVDRPServerHandler();
void WaitUntilReady(void);
cSVDRPServer *GetServerForClient(const char *ClientName);
};
static cSVDRPServerHandler *SVDRPServerHandler = NULL;
@ -2565,7 +2655,7 @@ void cSVDRPServerHandler::HandleServerConnection(void)
{
int NewSocket = tcpSocket.Accept();
if (NewSocket >= 0)
serverConnections.Append(new cSVDRPServer(NewSocket, tcpSocket.LastIpAddress()->Connection()));
serverConnections.Append(new cSVDRPServer(NewSocket, tcpSocket.LastIpAddress()));
}
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 ---------------------------------------------------------
static cMutex SVDRPHandlerMutex;
@ -2621,13 +2721,6 @@ void StopSVDRPClientHandler(void)
SVDRPClientHandler = NULL;
}
void SendSVDRPDiscover(const char *Address)
{
cMutexLock MutexLock(&SVDRPHandlerMutex);
if (SVDRPClientHandler)
SVDRPClientHandler->SendDiscover(Address);
}
bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag)
{
cMutexLock MutexLock(&SVDRPHandlerMutex);

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* 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
@ -29,7 +29,6 @@ void StartSVDRPServerHandler(void);
void StartSVDRPClientHandler(void);
void StopSVDRPServerHandler(void);
void StopSVDRPClientHandler(void);
void SendSVDRPDiscover(const char *Address = NULL);
bool GetSVDRPServerNames(cStringList *ServerNames, eSvdrpFetchFlags FetchFlag = sffNone);
///< 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