mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
The SVDRP port now accepts multiple concurrent connections
This commit is contained in:
parent
b6af7a9cf9
commit
2b9e988dd5
14
HISTORY
14
HISTORY
@ -8596,7 +8596,7 @@ Video Disk Recorder Revision History
|
|||||||
- Bumped all version numbers to 2.2.0.
|
- Bumped all version numbers to 2.2.0.
|
||||||
- Official release.
|
- Official release.
|
||||||
|
|
||||||
2015-04-19: Version 2.3.1
|
2015-04-29: Version 2.3.1
|
||||||
|
|
||||||
- The new function cOsd::MaxPixmapSize() can be called to determine the maximum size
|
- The new function cOsd::MaxPixmapSize() can be called to determine the maximum size
|
||||||
a cPixmap may have on the current OSD. The 'osddemo' example has been modified
|
a cPixmap may have on the current OSD. The 'osddemo' example has been modified
|
||||||
@ -8645,3 +8645,15 @@ Video Disk Recorder Revision History
|
|||||||
//#define DEPRECATED_GETBITMAP
|
//#define DEPRECATED_GETBITMAP
|
||||||
in osd.h as a quick workaround. In the long run the plugin will need to be adapted.
|
in osd.h as a quick workaround. In the long run the plugin will need to be adapted.
|
||||||
- The -u option now also accepts a numerical user id (suggested by Derek Kelly).
|
- The -u option now also accepts a numerical user id (suggested by Derek Kelly).
|
||||||
|
- The SVDRP port now accepts multiple concurrent connections. You can now keep an
|
||||||
|
SVDRP connection open as long as you wish, without preventing others from
|
||||||
|
connecting. Note, though, that SVDRP connections still get closed automatically
|
||||||
|
if there has been no activity for 300 seconds (configurable via
|
||||||
|
"Setup/Miscellaneous/SVDRP timeout (s)").
|
||||||
|
- The SVDRP log messages have been unified and now always contain the IP and port
|
||||||
|
number of the remote host.
|
||||||
|
- SVDRP connections are now handled in a separate thread, which makes them more
|
||||||
|
responsive. Note that there is only one thread that handles all concurrent SVDRP
|
||||||
|
connections. That way each SVDRP command is guaranteed to be processed separately,
|
||||||
|
without interfering with any other SVDRP commands that might be issued at the same
|
||||||
|
time.
|
||||||
|
12
interface.c
12
interface.c
@ -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: interface.c 3.1 2015/01/11 13:37:47 kls Exp $
|
* $Id: interface.c 4.1 2015/04/28 11:16:06 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
@ -19,27 +19,19 @@
|
|||||||
|
|
||||||
cInterface *Interface = NULL;
|
cInterface *Interface = NULL;
|
||||||
|
|
||||||
cInterface::cInterface(int SVDRPport)
|
cInterface::cInterface(void)
|
||||||
{
|
{
|
||||||
interrupted = false;
|
interrupted = false;
|
||||||
SVDRP = NULL;
|
|
||||||
if (SVDRPport)
|
|
||||||
SVDRP = new cSVDRP(SVDRPport);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cInterface::~cInterface()
|
cInterface::~cInterface()
|
||||||
{
|
{
|
||||||
delete SVDRP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eKeys cInterface::GetKey(bool Wait)
|
eKeys cInterface::GetKey(bool Wait)
|
||||||
{
|
{
|
||||||
if (!cRemote::HasKeys())
|
if (!cRemote::HasKeys())
|
||||||
Skins.Flush();
|
Skins.Flush();
|
||||||
if (SVDRP) {
|
|
||||||
if (SVDRP->Process())
|
|
||||||
Wait = false;
|
|
||||||
}
|
|
||||||
if (!cRemote::IsLearning())
|
if (!cRemote::IsLearning())
|
||||||
return cRemote::Get(Wait ? 1000 : 10);
|
return cRemote::Get(Wait ? 1000 : 10);
|
||||||
else
|
else
|
||||||
|
@ -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: interface.h 1.31 2004/05/01 11:11:13 kls Exp $
|
* $Id: interface.h 4.1 2015/04/28 11:15:11 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __INTERFACE_H
|
#ifndef __INTERFACE_H
|
||||||
@ -13,17 +13,14 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
#include "skins.h"
|
#include "skins.h"
|
||||||
#include "svdrp.h"
|
|
||||||
|
|
||||||
class cInterface {
|
class cInterface {
|
||||||
private:
|
private:
|
||||||
bool interrupted;
|
bool interrupted;
|
||||||
cSVDRP *SVDRP;
|
|
||||||
bool QueryKeys(cRemote *Remote, cSkinDisplayMenu *DisplayMenu);
|
bool QueryKeys(cRemote *Remote, cSkinDisplayMenu *DisplayMenu);
|
||||||
public:
|
public:
|
||||||
cInterface(int SVDRPport = 0);
|
cInterface(void);
|
||||||
~cInterface();
|
~cInterface();
|
||||||
bool HasSVDRPConnection(void) { return SVDRP && SVDRP->HasConnection(); }
|
|
||||||
void Interrupt(void) { interrupted = true; }
|
void Interrupt(void) { interrupted = true; }
|
||||||
eKeys GetKey(bool Wait = true);
|
eKeys GetKey(bool Wait = true);
|
||||||
eKeys Wait(int Seconds = 0, bool KeepChar = false);
|
eKeys Wait(int Seconds = 0, bool KeepChar = false);
|
||||||
|
234
svdrp.c
234
svdrp.c
@ -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 3.6 2015/01/12 11:16:27 kls Exp $
|
* $Id: svdrp.c 4.1 2015/04/29 13:10:01 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "svdrp.h"
|
#include "svdrp.h"
|
||||||
@ -33,14 +33,32 @@
|
|||||||
#include "keys.h"
|
#include "keys.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
|
#include "recording.h"
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
#include "skins.h"
|
#include "skins.h"
|
||||||
|
#include "thread.h"
|
||||||
#include "timers.h"
|
#include "timers.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "videodir.h"
|
#include "videodir.h"
|
||||||
|
|
||||||
// --- cSocket ---------------------------------------------------------------
|
// --- cSocket ---------------------------------------------------------------
|
||||||
|
|
||||||
|
class cSocket {
|
||||||
|
private:
|
||||||
|
int port;
|
||||||
|
int sock;
|
||||||
|
int queue;
|
||||||
|
cString lastAcceptedConnection;
|
||||||
|
public:
|
||||||
|
cSocket(int Port, int Queue = 1);
|
||||||
|
~cSocket();
|
||||||
|
bool Open(void);
|
||||||
|
void Close(void);
|
||||||
|
int Socket(void) const { return sock; }
|
||||||
|
int Accept(void);
|
||||||
|
const char *LastAcceptedConnection(void) const { return lastAcceptedConnection; }
|
||||||
|
};
|
||||||
|
|
||||||
cSocket::cSocket(int Port, int Queue)
|
cSocket::cSocket(int Port, int Queue)
|
||||||
{
|
{
|
||||||
port = Port;
|
port = Port;
|
||||||
@ -100,6 +118,7 @@ bool cSocket::Open(void)
|
|||||||
LOG_ERROR;
|
LOG_ERROR;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
isyslog("SVDRP listening on port %d/tcp", port);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -110,7 +129,7 @@ int cSocket::Accept(void)
|
|||||||
struct sockaddr_in clientname;
|
struct sockaddr_in clientname;
|
||||||
uint size = sizeof(clientname);
|
uint size = sizeof(clientname);
|
||||||
int newsock = accept(sock, (struct sockaddr *)&clientname, &size);
|
int newsock = accept(sock, (struct sockaddr *)&clientname, &size);
|
||||||
if (newsock > 0) {
|
if (newsock >= 0) {
|
||||||
bool accepted = SVDRPhosts.Acceptable(clientname.sin_addr.s_addr);
|
bool accepted = SVDRPhosts.Acceptable(clientname.sin_addr.s_addr);
|
||||||
if (!accepted) {
|
if (!accepted) {
|
||||||
const char *s = "Access denied!\n";
|
const char *s = "Access denied!\n";
|
||||||
@ -119,7 +138,8 @@ int cSocket::Accept(void)
|
|||||||
close(newsock);
|
close(newsock);
|
||||||
newsock = -1;
|
newsock = -1;
|
||||||
}
|
}
|
||||||
isyslog("connect from %s, port %hu - %s", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port), accepted ? "accepted" : "DENIED");
|
lastAcceptedConnection = cString::sprintf("%s:%hu", inet_ntoa(clientname.sin_addr), ntohs(clientname.sin_port));
|
||||||
|
isyslog("SVDRP %s connection %s", *lastAcceptedConnection, accepted ? "accepted" : "DENIED");
|
||||||
}
|
}
|
||||||
else if (errno != EINTR && errno != EAGAIN)
|
else if (errno != EINTR && errno != EAGAIN)
|
||||||
LOG_ERROR;
|
LOG_ERROR;
|
||||||
@ -130,6 +150,19 @@ int cSocket::Accept(void)
|
|||||||
|
|
||||||
// --- cPUTEhandler ----------------------------------------------------------
|
// --- cPUTEhandler ----------------------------------------------------------
|
||||||
|
|
||||||
|
class cPUTEhandler {
|
||||||
|
private:
|
||||||
|
FILE *f;
|
||||||
|
int status;
|
||||||
|
const char *message;
|
||||||
|
public:
|
||||||
|
cPUTEhandler(void);
|
||||||
|
~cPUTEhandler();
|
||||||
|
bool Process(const char *s);
|
||||||
|
int Status(void) { return status; }
|
||||||
|
const char *Message(void) { return message; }
|
||||||
|
};
|
||||||
|
|
||||||
cPUTEhandler::cPUTEhandler(void)
|
cPUTEhandler::cPUTEhandler(void)
|
||||||
{
|
{
|
||||||
if ((f = tmpfile()) != NULL) {
|
if ((f = tmpfile()) != NULL) {
|
||||||
@ -385,17 +418,77 @@ const char *GetHelpPage(const char *Cmd, const char **p)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *cSVDRP::grabImageDir = NULL;
|
static cString grabImageDir;
|
||||||
|
|
||||||
cSVDRP::cSVDRP(int Port)
|
class cSVDRP {
|
||||||
:socket(Port)
|
private:
|
||||||
|
int socket;
|
||||||
|
cString connection;
|
||||||
|
cFile file;
|
||||||
|
cRecordings recordings;
|
||||||
|
cPUTEhandler *PUTEhandler;
|
||||||
|
int numChars;
|
||||||
|
int length;
|
||||||
|
char *cmdLine;
|
||||||
|
time_t lastActivity;
|
||||||
|
void Close(bool SendReply = false, bool Timeout = false);
|
||||||
|
bool Send(const char *s, int length = -1);
|
||||||
|
void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||||
|
void PrintHelpTopics(const char **hp);
|
||||||
|
void CmdCHAN(const char *Option);
|
||||||
|
void CmdCLRE(const char *Option);
|
||||||
|
void CmdDELC(const char *Option);
|
||||||
|
void CmdDELR(const char *Option);
|
||||||
|
void CmdDELT(const char *Option);
|
||||||
|
void CmdEDIT(const char *Option);
|
||||||
|
void CmdGRAB(const char *Option);
|
||||||
|
void CmdHELP(const char *Option);
|
||||||
|
void CmdHITK(const char *Option);
|
||||||
|
void CmdLSTC(const char *Option);
|
||||||
|
void CmdLSTE(const char *Option);
|
||||||
|
void CmdLSTR(const char *Option);
|
||||||
|
void CmdLSTT(const char *Option);
|
||||||
|
void CmdMESG(const char *Option);
|
||||||
|
void CmdMODC(const char *Option);
|
||||||
|
void CmdMODT(const char *Option);
|
||||||
|
void CmdMOVC(const char *Option);
|
||||||
|
void CmdMOVR(const char *Option);
|
||||||
|
void CmdNEWC(const char *Option);
|
||||||
|
void CmdNEWT(const char *Option);
|
||||||
|
void CmdNEXT(const char *Option);
|
||||||
|
void CmdPLAY(const char *Option);
|
||||||
|
void CmdPLUG(const char *Option);
|
||||||
|
void CmdPUTE(const char *Option);
|
||||||
|
void CmdREMO(const char *Option);
|
||||||
|
void CmdSCAN(const char *Option);
|
||||||
|
void CmdSTAT(const char *Option);
|
||||||
|
void CmdUPDT(const char *Option);
|
||||||
|
void CmdUPDR(const char *Option);
|
||||||
|
void CmdVOLU(const char *Option);
|
||||||
|
void Execute(char *Cmd);
|
||||||
|
public:
|
||||||
|
cSVDRP(int Socket, const char *Connection);
|
||||||
|
~cSVDRP();
|
||||||
|
bool HasConnection(void) { return file.IsOpen(); }
|
||||||
|
bool Process(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
cSVDRP::cSVDRP(int Socket, const char *Connection)
|
||||||
{
|
{
|
||||||
|
socket = Socket;
|
||||||
|
connection = Connection;
|
||||||
PUTEhandler = NULL;
|
PUTEhandler = NULL;
|
||||||
numChars = 0;
|
numChars = 0;
|
||||||
length = BUFSIZ;
|
length = BUFSIZ;
|
||||||
cmdLine = MALLOC(char, length);
|
cmdLine = MALLOC(char, length);
|
||||||
lastActivity = 0;
|
lastActivity = time(NULL);
|
||||||
isyslog("SVDRP listening on port %d", Port);
|
if (file.Open(socket)) {
|
||||||
|
//TODO how can we get the *full* hostname?
|
||||||
|
char buffer[BUFSIZ];
|
||||||
|
gethostname(buffer, sizeof(buffer));
|
||||||
|
time_t now = time(NULL);
|
||||||
|
Reply(220, "%s SVDRP VideoDiskRecorder %s; %s; %s", buffer, VDRVERSION, *TimeToString(now), cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cSVDRP::~cSVDRP()
|
cSVDRP::~cSVDRP()
|
||||||
@ -413,10 +506,11 @@ void cSVDRP::Close(bool SendReply, bool Timeout)
|
|||||||
gethostname(buffer, sizeof(buffer));
|
gethostname(buffer, sizeof(buffer));
|
||||||
Reply(221, "%s closing connection%s", buffer, Timeout ? " (timeout)" : "");
|
Reply(221, "%s closing connection%s", buffer, Timeout ? " (timeout)" : "");
|
||||||
}
|
}
|
||||||
isyslog("closing SVDRP connection"); //TODO store IP#???
|
isyslog("SVDRP %s connection closed", *connection);
|
||||||
file.Close();
|
file.Close();
|
||||||
DELETENULL(PUTEhandler);
|
DELETENULL(PUTEhandler);
|
||||||
}
|
}
|
||||||
|
close(socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cSVDRP::Send(const char *s, int length)
|
bool cSVDRP::Send(const char *s, int length)
|
||||||
@ -454,7 +548,7 @@ void cSVDRP::Reply(int Code, const char *fmt, ...)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Reply(451, "Zero return code - looks like a programming error!");
|
Reply(451, "Zero return code - looks like a programming error!");
|
||||||
esyslog("SVDRP: zero return code!");
|
esyslog("SVDRP %s zero return code!", *connection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -641,7 +735,7 @@ void cSVDRP::CmdDELC(const char *Option)
|
|||||||
Channels.Del(channel);
|
Channels.Del(channel);
|
||||||
Channels.ReNumber();
|
Channels.ReNumber();
|
||||||
Channels.SetModified(true);
|
Channels.SetModified(true);
|
||||||
isyslog("channel %s deleted", Option);
|
isyslog("SVDRP %s channel %s deleted", *connection, 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());
|
||||||
@ -714,7 +808,7 @@ void cSVDRP::CmdDELT(const char *Option)
|
|||||||
cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
|
cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
|
||||||
if (timer) {
|
if (timer) {
|
||||||
if (!timer->Recording()) {
|
if (!timer->Recording()) {
|
||||||
isyslog("deleting timer %s", *timer->ToDescr());
|
isyslog("SVDRP %s deleting timer %s", *connection, *timer->ToDescr());
|
||||||
Timers.Del(timer);
|
Timers.Del(timer);
|
||||||
Timers.SetModified();
|
Timers.SetModified();
|
||||||
Reply(250, "Timer \"%s\" deleted", Option);
|
Reply(250, "Timer \"%s\" deleted", Option);
|
||||||
@ -831,7 +925,7 @@ void cSVDRP::CmdGRAB(const char *Option)
|
|||||||
// canonicalize the file name:
|
// canonicalize the file name:
|
||||||
char RealFileName[PATH_MAX];
|
char RealFileName[PATH_MAX];
|
||||||
if (FileName) {
|
if (FileName) {
|
||||||
if (grabImageDir) {
|
if (*grabImageDir) {
|
||||||
cString s(FileName);
|
cString s(FileName);
|
||||||
FileName = s;
|
FileName = s;
|
||||||
const char *slash = strrchr(FileName, '/');
|
const char *slash = strrchr(FileName, '/');
|
||||||
@ -868,7 +962,7 @@ void cSVDRP::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("grabbed image to %s", FileName);
|
dsyslog("SVDRP %s grabbed image to %s", *connection, FileName);
|
||||||
Reply(250, "Grabbed image %s", Option);
|
Reply(250, "Grabbed image %s", Option);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1195,7 +1289,7 @@ void cSVDRP::CmdLSTT(const char *Option)
|
|||||||
void cSVDRP::CmdMESG(const char *Option)
|
void cSVDRP::CmdMESG(const char *Option)
|
||||||
{
|
{
|
||||||
if (*Option) {
|
if (*Option) {
|
||||||
isyslog("SVDRP message: '%s'", Option);
|
isyslog("SVDRP %s message '%s'", *connection, Option);
|
||||||
Skins.QueueMessage(mtInfo, Option);
|
Skins.QueueMessage(mtInfo, Option);
|
||||||
Reply(250, "Message queued");
|
Reply(250, "Message queued");
|
||||||
}
|
}
|
||||||
@ -1219,7 +1313,7 @@ void cSVDRP::CmdMODC(const char *Option)
|
|||||||
*channel = ch;
|
*channel = ch;
|
||||||
Channels.ReNumber();
|
Channels.ReNumber();
|
||||||
Channels.SetModified(true);
|
Channels.SetModified(true);
|
||||||
isyslog("modifed channel %d %s", channel->Number(), *channel->ToText());
|
isyslog("SVDRP %s modifed channel %d %s", *connection, channel->Number(), *channel->ToText());
|
||||||
Reply(250, "%d %s", channel->Number(), *channel->ToText());
|
Reply(250, "%d %s", channel->Number(), *channel->ToText());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1262,7 +1356,7 @@ void cSVDRP::CmdMODT(const char *Option)
|
|||||||
}
|
}
|
||||||
*timer = t;
|
*timer = t;
|
||||||
Timers.SetModified();
|
Timers.SetModified();
|
||||||
isyslog("timer %s modified (%s)", *timer->ToDescr(), timer->HasFlags(tfActive) ? "active" : "inactive");
|
isyslog("SVDRP %s timer %s modified (%s)", *connection, *timer->ToDescr(), timer->HasFlags(tfActive) ? "active" : "inactive");
|
||||||
Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
|
Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1306,7 +1400,7 @@ void cSVDRP::CmdMOVC(const char *Option)
|
|||||||
else
|
else
|
||||||
cDevice::SetCurrentChannel(CurrentChannel);
|
cDevice::SetCurrentChannel(CurrentChannel);
|
||||||
}
|
}
|
||||||
isyslog("channel %d moved to %d", FromNumber, ToNumber);
|
isyslog("SVDRP %s channel %d moved to %d", *connection, FromNumber, ToNumber);
|
||||||
Reply(250,"Channel \"%d\" moved to \"%d\"", From, To);
|
Reply(250,"Channel \"%d\" moved to \"%d\"", From, To);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1382,7 +1476,7 @@ void cSVDRP::CmdNEWC(const char *Option)
|
|||||||
Channels.Add(channel);
|
Channels.Add(channel);
|
||||||
Channels.ReNumber();
|
Channels.ReNumber();
|
||||||
Channels.SetModified(true);
|
Channels.SetModified(true);
|
||||||
isyslog("new channel %d %s", channel->Number(), *channel->ToText());
|
isyslog("SVDRP %s new channel %d %s", *connection, channel->Number(), *channel->ToText());
|
||||||
Reply(250, "%d %s", channel->Number(), *channel->ToText());
|
Reply(250, "%d %s", channel->Number(), *channel->ToText());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1402,7 +1496,7 @@ void cSVDRP::CmdNEWT(const char *Option)
|
|||||||
if (timer->Parse(Option)) {
|
if (timer->Parse(Option)) {
|
||||||
Timers.Add(timer);
|
Timers.Add(timer);
|
||||||
Timers.SetModified();
|
Timers.SetModified();
|
||||||
isyslog("timer %s added", *timer->ToDescr());
|
isyslog("SVDRP %s timer %s added", *connection, *timer->ToDescr());
|
||||||
Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
|
Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1622,11 +1716,11 @@ void cSVDRP::CmdUPDT(const char *Option)
|
|||||||
t->Parse(Option);
|
t->Parse(Option);
|
||||||
delete timer;
|
delete timer;
|
||||||
timer = t;
|
timer = t;
|
||||||
isyslog("timer %s updated", *timer->ToDescr());
|
isyslog("SVDRP %s timer %s updated", *connection, *timer->ToDescr());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Timers.Add(timer);
|
Timers.Add(timer);
|
||||||
isyslog("timer %s added", *timer->ToDescr());
|
isyslog("SVDRP %s timer %s added", *connection, *timer->ToDescr());
|
||||||
}
|
}
|
||||||
Timers.SetModified();
|
Timers.SetModified();
|
||||||
Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
|
Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
|
||||||
@ -1729,19 +1823,7 @@ void cSVDRP::Execute(char *Cmd)
|
|||||||
|
|
||||||
bool cSVDRP::Process(void)
|
bool cSVDRP::Process(void)
|
||||||
{
|
{
|
||||||
bool NewConnection = !file.IsOpen();
|
if (file.IsOpen()) {
|
||||||
bool SendGreeting = NewConnection;
|
|
||||||
|
|
||||||
if (file.IsOpen() || file.Open(socket.Accept())) {
|
|
||||||
if (SendGreeting) {
|
|
||||||
//TODO how can we get the *full* hostname?
|
|
||||||
char buffer[BUFSIZ];
|
|
||||||
gethostname(buffer, sizeof(buffer));
|
|
||||||
time_t now = time(NULL);
|
|
||||||
Reply(220, "%s SVDRP VideoDiskRecorder %s; %s; %s", buffer, VDRVERSION, *TimeToString(now), cCharSetConv::SystemCharacterTable() ? cCharSetConv::SystemCharacterTable() : "UTF-8");
|
|
||||||
}
|
|
||||||
if (NewConnection)
|
|
||||||
lastActivity = time(NULL);
|
|
||||||
while (file.Ready(false)) {
|
while (file.Ready(false)) {
|
||||||
unsigned char c;
|
unsigned char c;
|
||||||
int r = safe_read(file, &c, 1);
|
int r = safe_read(file, &c, 1);
|
||||||
@ -1781,7 +1863,7 @@ bool cSVDRP::Process(void)
|
|||||||
cmdLine = NewBuffer;
|
cmdLine = NewBuffer;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
esyslog("ERROR: out of memory");
|
esyslog("SVDRP %s ERROR: out of memory", *connection);
|
||||||
Close();
|
Close();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1792,23 +1874,87 @@ bool cSVDRP::Process(void)
|
|||||||
lastActivity = time(NULL);
|
lastActivity = time(NULL);
|
||||||
}
|
}
|
||||||
else if (r <= 0) {
|
else if (r <= 0) {
|
||||||
isyslog("lost connection to SVDRP client");
|
isyslog("SVDRP %s lost connection to client", *connection);
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Setup.SVDRPTimeout && time(NULL) - lastActivity > Setup.SVDRPTimeout) {
|
if (Setup.SVDRPTimeout && time(NULL) - lastActivity > Setup.SVDRPTimeout) {
|
||||||
isyslog("timeout on SVDRP connection");
|
isyslog("SVDRP %s timeout on connection", *connection);
|
||||||
Close(true, true);
|
Close(true, true);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return file.IsOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cSVDRP::SetGrabImageDir(const char *GrabImageDir)
|
void SetSVDRPGrabImageDir(const char *GrabImageDir)
|
||||||
{
|
{
|
||||||
free(grabImageDir);
|
grabImageDir = GrabImageDir;
|
||||||
grabImageDir = GrabImageDir ? strdup(GrabImageDir) : NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO more than one connection???
|
// --- cSVDRPHandler ---------------------------------------------------------
|
||||||
|
|
||||||
|
class cSVDRPHandler : public cThread {
|
||||||
|
private:
|
||||||
|
cSocket socket;
|
||||||
|
cVector<cSVDRP *> connections;
|
||||||
|
void ProcessConnections(void);
|
||||||
|
protected:
|
||||||
|
virtual void Action(void);
|
||||||
|
public:
|
||||||
|
cSVDRPHandler(int Port);
|
||||||
|
virtual ~cSVDRPHandler();
|
||||||
|
};
|
||||||
|
|
||||||
|
cSVDRPHandler::cSVDRPHandler(int Port)
|
||||||
|
:cThread("SVDRP handler", true)
|
||||||
|
,socket(Port)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cSVDRPHandler::~cSVDRPHandler()
|
||||||
|
{
|
||||||
|
Cancel(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSVDRPHandler::ProcessConnections(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < connections.Size(); i++) {
|
||||||
|
if (connections[i]) {
|
||||||
|
if (!connections[i]->Process()) {
|
||||||
|
delete connections[i];
|
||||||
|
connections.Remove(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSVDRPHandler::Action(void)
|
||||||
|
{
|
||||||
|
if (socket.Open()) {
|
||||||
|
while (Running()) {
|
||||||
|
cFile::AnyFileReady(socket.Socket(), 1000);
|
||||||
|
int NewSocket = socket.Accept();
|
||||||
|
if (NewSocket >= 0)
|
||||||
|
connections.Append(new cSVDRP(NewSocket, socket.LastAcceptedConnection()));
|
||||||
|
ProcessConnections();
|
||||||
|
}
|
||||||
|
socket.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static cSVDRPHandler *SVDRPHandler = NULL;
|
||||||
|
|
||||||
|
void StartSVDRPHandler(int Port)
|
||||||
|
{
|
||||||
|
if (Port && !SVDRPHandler) {
|
||||||
|
SVDRPHandler = new cSVDRPHandler(Port);
|
||||||
|
SVDRPHandler->Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StopSVDRPHandler(void)
|
||||||
|
{
|
||||||
|
delete SVDRPHandler;
|
||||||
|
SVDRPHandler = NULL;
|
||||||
|
}
|
||||||
|
87
svdrp.h
87
svdrp.h
@ -4,93 +4,14 @@
|
|||||||
* 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 3.2 2013/10/21 07:42:03 kls Exp $
|
* $Id: svdrp.h 4.1 2015/04/29 13:10:06 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __SVDRP_H
|
#ifndef __SVDRP_H
|
||||||
#define __SVDRP_H
|
#define __SVDRP_H
|
||||||
|
|
||||||
#include "recording.h"
|
void SetSVDRPGrabImageDir(const char *GrabImageDir);
|
||||||
#include "tools.h"
|
void StartSVDRPHandler(int Port);
|
||||||
|
void StopSVDRPHandler(void);
|
||||||
class cSocket {
|
|
||||||
private:
|
|
||||||
int port;
|
|
||||||
int sock;
|
|
||||||
int queue;
|
|
||||||
void Close(void);
|
|
||||||
public:
|
|
||||||
cSocket(int Port, int Queue = 1);
|
|
||||||
~cSocket();
|
|
||||||
bool Open(void);
|
|
||||||
int Accept(void);
|
|
||||||
};
|
|
||||||
|
|
||||||
class cPUTEhandler {
|
|
||||||
private:
|
|
||||||
FILE *f;
|
|
||||||
int status;
|
|
||||||
const char *message;
|
|
||||||
public:
|
|
||||||
cPUTEhandler(void);
|
|
||||||
~cPUTEhandler();
|
|
||||||
bool Process(const char *s);
|
|
||||||
int Status(void) { return status; }
|
|
||||||
const char *Message(void) { return message; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class cSVDRP {
|
|
||||||
private:
|
|
||||||
cSocket socket;
|
|
||||||
cFile file;
|
|
||||||
cRecordings recordings;
|
|
||||||
cPUTEhandler *PUTEhandler;
|
|
||||||
int numChars;
|
|
||||||
int length;
|
|
||||||
char *cmdLine;
|
|
||||||
time_t lastActivity;
|
|
||||||
static char *grabImageDir;
|
|
||||||
void Close(bool SendReply = false, bool Timeout = false);
|
|
||||||
bool Send(const char *s, int length = -1);
|
|
||||||
void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
|
||||||
void PrintHelpTopics(const char **hp);
|
|
||||||
void CmdCHAN(const char *Option);
|
|
||||||
void CmdCLRE(const char *Option);
|
|
||||||
void CmdDELC(const char *Option);
|
|
||||||
void CmdDELR(const char *Option);
|
|
||||||
void CmdDELT(const char *Option);
|
|
||||||
void CmdEDIT(const char *Option);
|
|
||||||
void CmdGRAB(const char *Option);
|
|
||||||
void CmdHELP(const char *Option);
|
|
||||||
void CmdHITK(const char *Option);
|
|
||||||
void CmdLSTC(const char *Option);
|
|
||||||
void CmdLSTE(const char *Option);
|
|
||||||
void CmdLSTR(const char *Option);
|
|
||||||
void CmdLSTT(const char *Option);
|
|
||||||
void CmdMESG(const char *Option);
|
|
||||||
void CmdMODC(const char *Option);
|
|
||||||
void CmdMODT(const char *Option);
|
|
||||||
void CmdMOVC(const char *Option);
|
|
||||||
void CmdMOVR(const char *Option);
|
|
||||||
void CmdNEWC(const char *Option);
|
|
||||||
void CmdNEWT(const char *Option);
|
|
||||||
void CmdNEXT(const char *Option);
|
|
||||||
void CmdPLAY(const char *Option);
|
|
||||||
void CmdPLUG(const char *Option);
|
|
||||||
void CmdPUTE(const char *Option);
|
|
||||||
void CmdREMO(const char *Option);
|
|
||||||
void CmdSCAN(const char *Option);
|
|
||||||
void CmdSTAT(const char *Option);
|
|
||||||
void CmdUPDT(const char *Option);
|
|
||||||
void CmdUPDR(const char *Option);
|
|
||||||
void CmdVOLU(const char *Option);
|
|
||||||
void Execute(char *Cmd);
|
|
||||||
public:
|
|
||||||
cSVDRP(int Port);
|
|
||||||
~cSVDRP();
|
|
||||||
bool HasConnection(void) { return file.IsOpen(); }
|
|
||||||
bool Process(void);
|
|
||||||
static void SetGrabImageDir(const char *GrabImageDir);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif //__SVDRP_H
|
#endif //__SVDRP_H
|
||||||
|
14
vdr.c
14
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.tvdr.de
|
* The project's page is at http://www.tvdr.de
|
||||||
*
|
*
|
||||||
* $Id: vdr.c 4.2 2015/04/19 12:38:12 kls Exp $
|
* $Id: vdr.c 4.3 2015/04/29 09:18:54 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -65,6 +65,7 @@
|
|||||||
#include "sourceparams.h"
|
#include "sourceparams.h"
|
||||||
#include "sources.h"
|
#include "sources.h"
|
||||||
#include "status.h"
|
#include "status.h"
|
||||||
|
#include "svdrp.h"
|
||||||
#include "themes.h"
|
#include "themes.h"
|
||||||
#include "timers.h"
|
#include "timers.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
@ -375,7 +376,7 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
case 'g' | 0x100:
|
case 'g' | 0x100:
|
||||||
return GenerateIndex(optarg) ? 0 : 2;
|
return GenerateIndex(optarg) ? 0 : 2;
|
||||||
case 'g': cSVDRP::SetGrabImageDir(*optarg != '-' ? optarg : NULL);
|
case 'g': SetSVDRPGrabImageDir(*optarg != '-' ? optarg : NULL);
|
||||||
break;
|
break;
|
||||||
case 'h': DisplayHelp = true;
|
case 'h': DisplayHelp = true;
|
||||||
break;
|
break;
|
||||||
@ -831,7 +832,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
// User interface:
|
// User interface:
|
||||||
|
|
||||||
Interface = new cInterface(SVDRPport);
|
Interface = new cInterface;
|
||||||
|
|
||||||
// Default skins:
|
// Default skins:
|
||||||
|
|
||||||
@ -913,6 +914,10 @@ int main(int argc, char *argv[])
|
|||||||
sd_notify(0, "READY=1\nSTATUS=Ready");
|
sd_notify(0, "READY=1\nSTATUS=Ready");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// SVDRP:
|
||||||
|
|
||||||
|
StartSVDRPHandler(SVDRPport);
|
||||||
|
|
||||||
// Main program loop:
|
// Main program loop:
|
||||||
|
|
||||||
#define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL)
|
#define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL)
|
||||||
@ -1418,7 +1423,7 @@ int main(int argc, char *argv[])
|
|||||||
// Keep the recordings handler alive:
|
// Keep the recordings handler alive:
|
||||||
RecordingsHandler.Active();
|
RecordingsHandler.Active();
|
||||||
|
|
||||||
if ((Now - LastInteract) > ACTIVITYTIMEOUT && !cRecordControls::Active() && !RecordingsHandler.Active() && !Interface->HasSVDRPConnection() && (Now - cRemote::LastActivity()) > ACTIVITYTIMEOUT) {
|
if ((Now - LastInteract) > ACTIVITYTIMEOUT && !cRecordControls::Active() && !RecordingsHandler.Active() && (Now - cRemote::LastActivity()) > ACTIVITYTIMEOUT) {
|
||||||
// Handle housekeeping tasks
|
// Handle housekeeping tasks
|
||||||
|
|
||||||
// Shutdown:
|
// Shutdown:
|
||||||
@ -1466,6 +1471,7 @@ Exit:
|
|||||||
signal(SIGPIPE, SIG_DFL);
|
signal(SIGPIPE, SIG_DFL);
|
||||||
signal(SIGALRM, SIG_DFL);
|
signal(SIGALRM, SIG_DFL);
|
||||||
|
|
||||||
|
StopSVDRPHandler();
|
||||||
PluginManager.StopPlugins();
|
PluginManager.StopPlugins();
|
||||||
cRecordControls::Shutdown();
|
cRecordControls::Shutdown();
|
||||||
RecordingsHandler.DelAll();
|
RecordingsHandler.DelAll();
|
||||||
|
Loading…
Reference in New Issue
Block a user