mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
New SVDRP command MESG; SVDRP can reuse port; SVDRP timeout
This commit is contained in:
parent
c464c4f9b9
commit
3586013b8f
7
HISTORY
7
HISTORY
@ -350,7 +350,7 @@ Video Disk Recorder Revision History
|
|||||||
- Encrypted channels can now be selected even without knowing the PNR (however, it
|
- Encrypted channels can now be selected even without knowing the PNR (however, it
|
||||||
is still necessary for the EPG info).
|
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
|
- 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
|
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
|
- When removing recordings empty directories are now removed from the video
|
||||||
directory.
|
directory.
|
||||||
- Added the "schnitt" tools from Matthias Schniedermeyer.
|
- 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").
|
||||||
|
4
MANUAL
4
MANUAL
@ -328,6 +328,10 @@ Video Disk Recorder User's Manual
|
|||||||
to keep the EPG up-to-date.
|
to keep the EPG up-to-date.
|
||||||
A value of '0' turns off scanning on a single card system.
|
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
|
* Executing system commands
|
||||||
|
|
||||||
The "Main" menu option "Commands" allows you to execute any system commands
|
The "Main" menu option "Commands" allows you to execute any system commands
|
||||||
|
5
config.c
5
config.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: 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"
|
#include "config.h"
|
||||||
@ -738,6 +738,7 @@ cSetup::cSetup(void)
|
|||||||
MarginStart = 2;
|
MarginStart = 2;
|
||||||
MarginStop = 10;
|
MarginStop = 10;
|
||||||
EPGScanTimeout = 5;
|
EPGScanTimeout = 5;
|
||||||
|
SVDRPTimeout = 300;
|
||||||
CurrentChannel = -1;
|
CurrentChannel = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,6 +759,7 @@ bool cSetup::Parse(char *s)
|
|||||||
else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value);
|
else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value);
|
else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = 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 if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
@ -811,6 +813,7 @@ bool cSetup::Save(const char *FileName)
|
|||||||
fprintf(f, "MarginStart = %d\n", MarginStart);
|
fprintf(f, "MarginStart = %d\n", MarginStart);
|
||||||
fprintf(f, "MarginStop = %d\n", MarginStop);
|
fprintf(f, "MarginStop = %d\n", MarginStop);
|
||||||
fprintf(f, "EPGScanTimeout = %d\n", EPGScanTimeout);
|
fprintf(f, "EPGScanTimeout = %d\n", EPGScanTimeout);
|
||||||
|
fprintf(f, "SVDRPTimeout = %d\n", SVDRPTimeout);
|
||||||
fprintf(f, "CurrentChannel = %d\n", CurrentChannel);
|
fprintf(f, "CurrentChannel = %d\n", CurrentChannel);
|
||||||
f.Close();
|
f.Close();
|
||||||
isyslog(LOG_INFO, "saved setup to %s", FileName);
|
isyslog(LOG_INFO, "saved setup to %s", FileName);
|
||||||
|
3
config.h
3
config.h
@ -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: 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
|
#ifndef __CONFIG_H
|
||||||
@ -269,6 +269,7 @@ public:
|
|||||||
int SetSystemTime;
|
int SetSystemTime;
|
||||||
int MarginStart, MarginStop;
|
int MarginStart, MarginStop;
|
||||||
int EPGScanTimeout;
|
int EPGScanTimeout;
|
||||||
|
int SVDRPTimeout;
|
||||||
int CurrentChannel;
|
int CurrentChannel;
|
||||||
cSetup(void);
|
cSetup(void);
|
||||||
bool Load(const char *FileName);
|
bool Load(const char *FileName);
|
||||||
|
7
i18n.c
7
i18n.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: 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 <mihasetina@softhome.net>
|
* Slovenian translations provided by Miha Setina <mihasetina@softhome.net>
|
||||||
* Italian translations provided by Alberto Carraro <bertocar@tin.it>
|
* Italian translations provided by Alberto Carraro <bertocar@tin.it>
|
||||||
@ -416,6 +416,11 @@ const tPhrase Phrases[] = {
|
|||||||
"Cas do EPG pregleda",
|
"Cas do EPG pregleda",
|
||||||
"Timeout EPG",
|
"Timeout EPG",
|
||||||
},
|
},
|
||||||
|
{ "SVDRPTimeout",
|
||||||
|
"SVDRP Timeout",
|
||||||
|
"", // TODO
|
||||||
|
"Timeout SVDRP",
|
||||||
|
},
|
||||||
// The days of the week:
|
// The days of the week:
|
||||||
{ "MTWTFSS",
|
{ "MTWTFSS",
|
||||||
"MDMDFSS",
|
"MDMDFSS",
|
||||||
|
16
interface.c
16
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 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"
|
#include "interface.h"
|
||||||
@ -70,8 +70,16 @@ unsigned int cInterface::GetCh(bool Wait, bool *Repeat, bool *Release)
|
|||||||
eKeys cInterface::GetKey(bool Wait)
|
eKeys cInterface::GetKey(bool Wait)
|
||||||
{
|
{
|
||||||
Flush();
|
Flush();
|
||||||
if (SVDRP)
|
if (SVDRP) {
|
||||||
SVDRP->Process();
|
SVDRP->Process();
|
||||||
|
if (!open) {
|
||||||
|
char *message = SVDRP->GetMessage();
|
||||||
|
if (message) {
|
||||||
|
Info(message);
|
||||||
|
delete message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
eKeys Key = keyFromWait;
|
eKeys Key = keyFromWait;
|
||||||
if (Key == kNone) {
|
if (Key == kNone) {
|
||||||
bool Repeat = false, Release = false;
|
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)
|
void cInterface::Info(const char *s)
|
||||||
{
|
{
|
||||||
Open();
|
Open();
|
||||||
isyslog(LOG_INFO, s);
|
isyslog(LOG_INFO, "info: %s", s);
|
||||||
Status(s, clrWhite, clrGreen);
|
Status(s, clrWhite, clrGreen);
|
||||||
Wait();
|
Wait();
|
||||||
Status(NULL);
|
Status(NULL);
|
||||||
@ -291,7 +299,7 @@ void cInterface::Info(const char *s)
|
|||||||
void cInterface::Error(const char *s)
|
void cInterface::Error(const char *s)
|
||||||
{
|
{
|
||||||
Open();
|
Open();
|
||||||
esyslog(LOG_ERR, s);
|
esyslog(LOG_ERR, "ERROR: %s", s);
|
||||||
Status(s, clrWhite, clrRed);
|
Status(s, clrWhite, clrRed);
|
||||||
Wait();
|
Wait();
|
||||||
Status(NULL);
|
Status(NULL);
|
||||||
|
3
menu.c
3
menu.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: 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"
|
#include "menu.h"
|
||||||
@ -1608,6 +1608,7 @@ void cMenuSetup::Set(void)
|
|||||||
Add(new cMenuEditIntItem( tr("MarginStart"), &data.MarginStart));
|
Add(new cMenuEditIntItem( tr("MarginStart"), &data.MarginStart));
|
||||||
Add(new cMenuEditIntItem( tr("MarginStop"), &data.MarginStop));
|
Add(new cMenuEditIntItem( tr("MarginStop"), &data.MarginStop));
|
||||||
Add(new cMenuEditIntItem( tr("EPGScanTimeout"), &data.EPGScanTimeout));
|
Add(new cMenuEditIntItem( tr("EPGScanTimeout"), &data.EPGScanTimeout));
|
||||||
|
Add(new cMenuEditIntItem( tr("SVDRPTimeout"), &data.SVDRPTimeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
eOSState cMenuSetup::ProcessKey(eKeys Key)
|
eOSState cMenuSetup::ProcessKey(eKeys Key)
|
||||||
|
54
svdrp.c
54
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 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
|
#define _GNU_SOURCE
|
||||||
@ -63,6 +63,10 @@ bool cSocket::Open(void)
|
|||||||
port = 0;
|
port = 0;
|
||||||
return false;
|
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;
|
struct sockaddr_in name;
|
||||||
name.sin_family = AF_INET;
|
name.sin_family = AF_INET;
|
||||||
name.sin_port = htons(port);
|
name.sin_port = htons(port);
|
||||||
@ -137,6 +141,12 @@ const char *HelpPages[] = {
|
|||||||
"LSTT [ <number> ]\n"
|
"LSTT [ <number> ]\n"
|
||||||
" List timers. Without option, all timers are listed. Otherwise\n"
|
" List timers. Without option, all timers are listed. Otherwise\n"
|
||||||
" only the given timer is listed.",
|
" only the given timer is listed.",
|
||||||
|
"MESG [ <message> ]\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 <number> <settings>\n"
|
"MODC <number> <settings>\n"
|
||||||
" Modify a channel. Settings must be in the same format as returned\n"
|
" Modify a channel. Settings must be in the same format as returned\n"
|
||||||
" by the LSTC command.",
|
" by the LSTC command.",
|
||||||
@ -224,22 +234,25 @@ const char *GetHelpPage(const char *Cmd)
|
|||||||
cSVDRP::cSVDRP(int Port)
|
cSVDRP::cSVDRP(int Port)
|
||||||
:socket(Port)
|
:socket(Port)
|
||||||
{
|
{
|
||||||
|
message = NULL;
|
||||||
|
lastActivity = 0;
|
||||||
isyslog(LOG_INFO, "SVDRP listening on port %d", Port);
|
isyslog(LOG_INFO, "SVDRP listening on port %d", Port);
|
||||||
}
|
}
|
||||||
|
|
||||||
cSVDRP::~cSVDRP()
|
cSVDRP::~cSVDRP()
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
|
delete message;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cSVDRP::Close(void)
|
void cSVDRP::Close(bool Timeout)
|
||||||
{
|
{
|
||||||
if (file.IsOpen()) {
|
if (file.IsOpen()) {
|
||||||
//TODO how can we get the *full* hostname?
|
//TODO how can we get the *full* hostname?
|
||||||
char buffer[MAXCMDBUFFER];
|
char buffer[MAXCMDBUFFER];
|
||||||
gethostname(buffer, sizeof(buffer));
|
gethostname(buffer, sizeof(buffer));
|
||||||
Reply(221, "%s closing connection", buffer);
|
Reply(221, "%s closing connection%s", buffer, Timeout ? " (timeout)" : "");
|
||||||
isyslog(LOG_INFO, "closing connection"); //TODO store IP#???
|
isyslog(LOG_INFO, "closing SVDRP connection"); //TODO store IP#???
|
||||||
file.Close();
|
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)
|
void cSVDRP::CmdMODC(const char *Option)
|
||||||
{
|
{
|
||||||
if (*Option) {
|
if (*Option) {
|
||||||
@ -820,6 +847,7 @@ void cSVDRP::Execute(char *Cmd)
|
|||||||
else if (CMD("HITK")) CmdHITK(s);
|
else if (CMD("HITK")) CmdHITK(s);
|
||||||
else if (CMD("LSTC")) CmdLSTC(s);
|
else if (CMD("LSTC")) CmdLSTC(s);
|
||||||
else if (CMD("LSTT")) CmdLSTT(s);
|
else if (CMD("LSTT")) CmdLSTT(s);
|
||||||
|
else if (CMD("MESG")) CmdMESG(s);
|
||||||
else if (CMD("MODC")) CmdMODC(s);
|
else if (CMD("MODC")) CmdMODC(s);
|
||||||
else if (CMD("MODT")) CmdMODT(s);
|
else if (CMD("MODT")) CmdMODT(s);
|
||||||
else if (CMD("MOVC")) CmdMOVC(s);
|
else if (CMD("MOVC")) CmdMOVC(s);
|
||||||
@ -839,7 +867,8 @@ void cSVDRP::Execute(char *Cmd)
|
|||||||
|
|
||||||
void cSVDRP::Process(void)
|
void cSVDRP::Process(void)
|
||||||
{
|
{
|
||||||
bool SendGreeting = !file.IsOpen();
|
bool NewConnection = !file.IsOpen();
|
||||||
|
bool SendGreeting = NewConnection;
|
||||||
|
|
||||||
if (file.IsOpen() || file.Open(socket.Accept())) {
|
if (file.IsOpen() || file.Open(socket.Accept())) {
|
||||||
char buffer[MAXCMDBUFFER];
|
char buffer[MAXCMDBUFFER];
|
||||||
@ -849,6 +878,8 @@ void cSVDRP::Process(void)
|
|||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
Reply(220, "%s SVDRP VideoDiskRecorder %s; %s", buffer, VDRVERSION, ctime(&now));
|
Reply(220, "%s SVDRP VideoDiskRecorder %s; %s", buffer, VDRVERSION, ctime(&now));
|
||||||
}
|
}
|
||||||
|
if (NewConnection)
|
||||||
|
lastActivity = time(NULL);
|
||||||
int rbytes = file.ReadString(buffer, sizeof(buffer) - 1);
|
int rbytes = file.ReadString(buffer, sizeof(buffer) - 1);
|
||||||
if (rbytes > 0) {
|
if (rbytes > 0) {
|
||||||
//XXX overflow check???
|
//XXX overflow check???
|
||||||
@ -859,11 +890,22 @@ void cSVDRP::Process(void)
|
|||||||
buffer[rbytes] = 0;
|
buffer[rbytes] = 0;
|
||||||
// showtime!
|
// showtime!
|
||||||
Execute(buffer);
|
Execute(buffer);
|
||||||
|
lastActivity = time(NULL);
|
||||||
}
|
}
|
||||||
else if (rbytes < 0)
|
else if (rbytes < 0)
|
||||||
Close();
|
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???
|
//TODO more than one connection???
|
||||||
|
8
svdrp.h
8
svdrp.h
@ -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 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
|
#ifndef __SVDRP_H
|
||||||
@ -31,7 +31,9 @@ private:
|
|||||||
cSocket socket;
|
cSocket socket;
|
||||||
cFile file;
|
cFile file;
|
||||||
CRect ovlClipRects[MAXCLIPRECTS];
|
CRect ovlClipRects[MAXCLIPRECTS];
|
||||||
void Close(void);
|
char *message;
|
||||||
|
time_t lastActivity;
|
||||||
|
void Close(bool Timeout = false);
|
||||||
bool Send(const char *s, int length = -1);
|
bool Send(const char *s, int length = -1);
|
||||||
void Reply(int Code, const char *fmt, ...);
|
void Reply(int Code, const char *fmt, ...);
|
||||||
void CmdCHAN(const char *Option);
|
void CmdCHAN(const char *Option);
|
||||||
@ -42,6 +44,7 @@ private:
|
|||||||
void CmdHITK(const char *Option);
|
void CmdHITK(const char *Option);
|
||||||
void CmdLSTC(const char *Option);
|
void CmdLSTC(const char *Option);
|
||||||
void CmdLSTT(const char *Option);
|
void CmdLSTT(const char *Option);
|
||||||
|
void CmdMESG(const char *Option);
|
||||||
void CmdMODC(const char *Option);
|
void CmdMODC(const char *Option);
|
||||||
void CmdMODT(const char *Option);
|
void CmdMODT(const char *Option);
|
||||||
void CmdMOVC(const char *Option);
|
void CmdMOVC(const char *Option);
|
||||||
@ -59,6 +62,7 @@ public:
|
|||||||
cSVDRP(int Port);
|
cSVDRP(int Port);
|
||||||
~cSVDRP();
|
~cSVDRP();
|
||||||
void Process(void);
|
void Process(void);
|
||||||
|
char *GetMessage(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //__SVDRP_H
|
#endif //__SVDRP_H
|
||||||
|
57
svdrpsend.pl
Executable file
57
svdrpsend.pl
Executable file
@ -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 (<SOCK>) {
|
||||||
|
print STDOUT $_;
|
||||||
|
last if substr($_, 3, 1) ne "-";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub Error
|
||||||
|
{
|
||||||
|
print STDERR "@_\n";
|
||||||
|
close(SOCK);
|
||||||
|
exit 0;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user