diff --git a/HISTORY b/HISTORY index 0ec90804..d0149ebb 100644 --- a/HISTORY +++ b/HISTORY @@ -350,7 +350,7 @@ Video Disk Recorder Revision History - Encrypted channels can now be selected even without knowing the PNR (however, it is still necessary for the EPG info). -2001-02-11: Version 0.71 +2001-02-18: Version 0.71 - Fixed 'Transfer Mode' in cases where a non-primary interface was switched to a channel that only the primary interface can receive (which could happen in @@ -392,3 +392,8 @@ Video Disk Recorder Revision History - When removing recordings empty directories are now removed from the video directory. - Added the "schnitt" tools from Matthias Schniedermeyer. +- New SVDRP command MESG to display a short message on the OSD. +- The Perl script 'svdrpsend.pl' can be used to send SVDRP commands to VDR. +- SVDRP can now immediately reuse the same port if VDR is restarted. +- SVDRP now has a timeout after which the connection is automatically closed + (default is 300 seconds, can be changed in "Setup"). diff --git a/MANUAL b/MANUAL index 986da1dd..3593be9a 100644 --- a/MANUAL +++ b/MANUAL @@ -328,6 +328,10 @@ Video Disk Recorder User's Manual to keep the EPG up-to-date. A value of '0' turns off scanning on a single card system. + SVDRPTimeout = 300 The time (in seconds) of inactivity on an open SVDRP + connection after which the connection is automatically + closed. Default is 300, a value of 0 means no timeout. + * Executing system commands The "Main" menu option "Commands" allows you to execute any system commands diff --git a/config.c b/config.c index 3d3017c8..21d8c581 100644 --- a/config.c +++ b/config.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.41 2001/02/11 11:22:48 kls Exp $ + * $Id: config.c 1.42 2001/02/18 13:11:59 kls Exp $ */ #include "config.h" @@ -738,6 +738,7 @@ cSetup::cSetup(void) MarginStart = 2; MarginStop = 10; EPGScanTimeout = 5; + SVDRPTimeout = 300; CurrentChannel = -1; } @@ -758,6 +759,7 @@ bool cSetup::Parse(char *s) else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value); else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value); else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value); + else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value); else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value); else return false; @@ -811,6 +813,7 @@ bool cSetup::Save(const char *FileName) fprintf(f, "MarginStart = %d\n", MarginStart); fprintf(f, "MarginStop = %d\n", MarginStop); fprintf(f, "EPGScanTimeout = %d\n", EPGScanTimeout); + fprintf(f, "SVDRPTimeout = %d\n", SVDRPTimeout); fprintf(f, "CurrentChannel = %d\n", CurrentChannel); f.Close(); isyslog(LOG_INFO, "saved setup to %s", FileName); diff --git a/config.h b/config.h index 8f44e56e..963bcae3 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.40 2001/02/03 15:55:45 kls Exp $ + * $Id: config.h 1.41 2001/02/18 13:12:06 kls Exp $ */ #ifndef __CONFIG_H @@ -269,6 +269,7 @@ public: int SetSystemTime; int MarginStart, MarginStop; int EPGScanTimeout; + int SVDRPTimeout; int CurrentChannel; cSetup(void); bool Load(const char *FileName); diff --git a/i18n.c b/i18n.c index 749f7804..89915264 100644 --- a/i18n.c +++ b/i18n.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: i18n.c 1.12 2001/02/13 22:17:27 kls Exp $ + * $Id: i18n.c 1.13 2001/02/18 13:14:00 kls Exp $ * * Slovenian translations provided by Miha Setina * Italian translations provided by Alberto Carraro @@ -416,6 +416,11 @@ const tPhrase Phrases[] = { "Cas do EPG pregleda", "Timeout EPG", }, + { "SVDRPTimeout", + "SVDRP Timeout", + "", // TODO + "Timeout SVDRP", + }, // The days of the week: { "MTWTFSS", "MDMDFSS", diff --git a/interface.c b/interface.c index 2346f6ca..55cdb0b9 100644 --- a/interface.c +++ b/interface.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.c 1.34 2001/02/02 14:49:39 kls Exp $ + * $Id: interface.c 1.35 2001/02/18 10:46:13 kls Exp $ */ #include "interface.h" @@ -70,8 +70,16 @@ unsigned int cInterface::GetCh(bool Wait, bool *Repeat, bool *Release) eKeys cInterface::GetKey(bool Wait) { Flush(); - if (SVDRP) + if (SVDRP) { SVDRP->Process(); + if (!open) { + char *message = SVDRP->GetMessage(); + if (message) { + Info(message); + delete message; + } + } + } eKeys Key = keyFromWait; if (Key == kNone) { bool Repeat = false, Release = false; @@ -281,7 +289,7 @@ void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor) void cInterface::Info(const char *s) { Open(); - isyslog(LOG_INFO, s); + isyslog(LOG_INFO, "info: %s", s); Status(s, clrWhite, clrGreen); Wait(); Status(NULL); @@ -291,7 +299,7 @@ void cInterface::Info(const char *s) void cInterface::Error(const char *s) { Open(); - esyslog(LOG_ERR, s); + esyslog(LOG_ERR, "ERROR: %s", s); Status(s, clrWhite, clrRed); Wait(); Status(NULL); diff --git a/menu.c b/menu.c index 92c8091d..37c1a16c 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.65 2001/02/11 11:01:47 kls Exp $ + * $Id: menu.c 1.66 2001/02/18 13:12:32 kls Exp $ */ #include "menu.h" @@ -1608,6 +1608,7 @@ void cMenuSetup::Set(void) Add(new cMenuEditIntItem( tr("MarginStart"), &data.MarginStart)); Add(new cMenuEditIntItem( tr("MarginStop"), &data.MarginStop)); Add(new cMenuEditIntItem( tr("EPGScanTimeout"), &data.EPGScanTimeout)); + Add(new cMenuEditIntItem( tr("SVDRPTimeout"), &data.SVDRPTimeout)); } eOSState cMenuSetup::ProcessKey(eKeys Key) diff --git a/svdrp.c b/svdrp.c index 0ce30dba..ffdac28b 100644 --- a/svdrp.c +++ b/svdrp.c @@ -10,7 +10,7 @@ * and interact with the Video Disk Recorder - or write a full featured * graphical interface that sits on top of an SVDRP connection. * - * $Id: svdrp.c 1.13 2000/12/03 15:34:35 kls Exp $ + * $Id: svdrp.c 1.14 2001/02/18 14:18:13 kls Exp $ */ #define _GNU_SOURCE @@ -63,6 +63,10 @@ bool cSocket::Open(void) port = 0; return false; } + // allow it to always reuse the same port: + int ReUseAddr = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ReUseAddr, sizeof(ReUseAddr)); + // struct sockaddr_in name; name.sin_family = AF_INET; name.sin_port = htons(port); @@ -137,6 +141,12 @@ const char *HelpPages[] = { "LSTT [ ]\n" " List timers. Without option, all timers are listed. Otherwise\n" " only the given timer is listed.", + "MESG [ ]\n" + " Displays the given message on the OSD. If message is omitted, the\n" + " currently pending message (if any) will be returned. The message\n" + " will be displayed for a few seconds as soon as the OSD has become\n" + " idle. If a new MESG command is entered while the previous message\n" + " has not yet been displayed, the old message will be overwritten.", "MODC \n" " Modify a channel. Settings must be in the same format as returned\n" " by the LSTC command.", @@ -224,22 +234,25 @@ const char *GetHelpPage(const char *Cmd) cSVDRP::cSVDRP(int Port) :socket(Port) { + message = NULL; + lastActivity = 0; isyslog(LOG_INFO, "SVDRP listening on port %d", Port); } cSVDRP::~cSVDRP() { Close(); + delete message; } -void cSVDRP::Close(void) +void cSVDRP::Close(bool Timeout) { if (file.IsOpen()) { //TODO how can we get the *full* hostname? char buffer[MAXCMDBUFFER]; gethostname(buffer, sizeof(buffer)); - Reply(221, "%s closing connection", buffer); - isyslog(LOG_INFO, "closing connection"); //TODO store IP#??? + Reply(221, "%s closing connection%s", buffer, Timeout ? " (timeout)" : ""); + isyslog(LOG_INFO, "closing SVDRP connection"); //TODO store IP#??? file.Close(); } } @@ -557,6 +570,20 @@ void cSVDRP::CmdLSTT(const char *Option) } } +void cSVDRP::CmdMESG(const char *Option) +{ + if (*Option) { + delete message; + message = strdup(Option); + isyslog(LOG_INFO, "SVDRP message: '%s'", message); + Reply(250, "Message stored"); + } + else if (message) + Reply(250, "%s", message); + else + Reply(550, "No pending message"); +} + void cSVDRP::CmdMODC(const char *Option) { if (*Option) { @@ -820,6 +847,7 @@ void cSVDRP::Execute(char *Cmd) else if (CMD("HITK")) CmdHITK(s); else if (CMD("LSTC")) CmdLSTC(s); else if (CMD("LSTT")) CmdLSTT(s); + else if (CMD("MESG")) CmdMESG(s); else if (CMD("MODC")) CmdMODC(s); else if (CMD("MODT")) CmdMODT(s); else if (CMD("MOVC")) CmdMOVC(s); @@ -839,7 +867,8 @@ void cSVDRP::Execute(char *Cmd) void cSVDRP::Process(void) { - bool SendGreeting = !file.IsOpen(); + bool NewConnection = !file.IsOpen(); + bool SendGreeting = NewConnection; if (file.IsOpen() || file.Open(socket.Accept())) { char buffer[MAXCMDBUFFER]; @@ -849,6 +878,8 @@ void cSVDRP::Process(void) time_t now = time(NULL); Reply(220, "%s SVDRP VideoDiskRecorder %s; %s", buffer, VDRVERSION, ctime(&now)); } + if (NewConnection) + lastActivity = time(NULL); int rbytes = file.ReadString(buffer, sizeof(buffer) - 1); if (rbytes > 0) { //XXX overflow check??? @@ -859,11 +890,22 @@ void cSVDRP::Process(void) buffer[rbytes] = 0; // showtime! Execute(buffer); + lastActivity = time(NULL); } else if (rbytes < 0) Close(); + else if (Setup.SVDRPTimeout && time(NULL) - lastActivity > Setup.SVDRPTimeout) { + isyslog(LOG_INFO, "timeout on SVDRP connection"); + Close(true); + } } } -//TODO timeout??? +char *cSVDRP::GetMessage(void) +{ + char *s = message; + message = NULL; + return s; +} + //TODO more than one connection??? diff --git a/svdrp.h b/svdrp.h index 12eb11e3..62c1e753 100644 --- a/svdrp.h +++ b/svdrp.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: svdrp.h 1.6 2000/09/17 13:22:04 kls Exp $ + * $Id: svdrp.h 1.7 2001/02/18 13:36:47 kls Exp $ */ #ifndef __SVDRP_H @@ -31,7 +31,9 @@ private: cSocket socket; cFile file; CRect ovlClipRects[MAXCLIPRECTS]; - void Close(void); + char *message; + time_t lastActivity; + void Close(bool Timeout = false); bool Send(const char *s, int length = -1); void Reply(int Code, const char *fmt, ...); void CmdCHAN(const char *Option); @@ -42,6 +44,7 @@ private: void CmdHITK(const char *Option); void CmdLSTC(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); @@ -59,6 +62,7 @@ public: cSVDRP(int Port); ~cSVDRP(); void Process(void); + char *GetMessage(void); }; #endif //__SVDRP_H diff --git a/svdrpsend.pl b/svdrpsend.pl new file mode 100755 index 00000000..5efd504b --- /dev/null +++ b/svdrpsend.pl @@ -0,0 +1,57 @@ +#!/usr/bin/perl + +use Socket; +use Getopt::Std; + +$Usage = qq{ +Usage: $0 options command... + +Options: -d hostname destination hostname (default: localhost) + -p port SVDRP port number (default: 2001) +}; + +die $Usage if (!$ARGV[0] || !getopts("d:p:")); + +$Dest = $opt_d || "localhost"; +$Port = $opt_p || 2001; +$Cmd = "@ARGV" || Error("missing command"); + +$Timeout = 10; # max. seconds to wait for response + +$SIG{ALRM} = sub { Error("timeout"); }; +alarm($Timeout); + +$iaddr = inet_aton($Dest) || Error("no host: $Dest"); +$paddr = sockaddr_in($Port, $iaddr); + +$proto = getprotobyname('tcp'); +socket(SOCK, PF_INET, SOCK_STREAM, $proto) || Error("socket: $!"); +connect(SOCK, $paddr) || Error("connect: $!"); +select(SOCK); $| = 1; +Receive(); +Send($Cmd); +Send("quit"); +close(SOCK) || Error("close: $!"); + +sub Send +{ + my $cmd = shift || Error("no command to send"); + print SOCK "$cmd\r\n"; + Receive(); +} + +sub Receive +{ + while () { + print STDOUT $_; + last if substr($_, 3, 1) ne "-"; + } +} + +sub Error +{ + print STDERR "@_\n"; + close(SOCK); + exit 0; +} +