summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--FORMATS24
-rw-r--r--HISTORY3
-rw-r--r--MANUAL24
-rw-r--r--config.c63
-rw-r--r--config.h20
-rw-r--r--i18n.c5
-rw-r--r--menu.c75
-rw-r--r--osd.h3
-rw-r--r--tools.c14
-rw-r--r--tools.h3
-rw-r--r--vdr.c3
11 files changed, 217 insertions, 20 deletions
diff --git a/FORMATS b/FORMATS
index a86cfd2c..3eee494f 100644
--- a/FORMATS
+++ b/FORMATS
@@ -66,3 +66,27 @@ Video Disk Recorder File Formats
See the MANUAL file for a description of the available options.
+* commands.conf
+
+ This file contains the definitions of commands that can be executed from
+ the "Main" menus "Commands" option.
+
+ Each line contains one command definition in the following format:
+
+ title : command
+
+ where 'title' is the string the will be displayed in the "Commands" menu,
+ and 'command' is the actual command string that will be executed when this
+ option is selected. The delimiting ':' may be surrounded by any number of
+ white space characters.
+
+ In order to avoid error messages to stderr, every command should have
+ stderr redirected to stdout. Everything the command prints to stdout will
+ be displayed in a result window, with 'title' as its title.
+
+ Examples:
+
+ Check for new mail: /usr/local/bin/checkmail 2>&1
+ CPU status : /usr/loval/bin/cpustatus 2>&1
+ Disk space : df -h | grep '/video' | awk '{ print 100 - $5 "% free"; }'
+
diff --git a/HISTORY b/HISTORY
index bfca2d2c..bf328597 100644
--- a/HISTORY
+++ b/HISTORY
@@ -278,3 +278,6 @@ Video Disk Recorder Revision History
- Fixed a timing problem with OSD refresh and SVDRP.
- Avoiding multiple definitions of the same timer in the "Schedule" menu (this
could happen when pressing the "Red" button while editing the timer).
+- There can now be a configuration file named 'commands.conf' that defines
+ commands that can be executed through the "Main" menu's "Commands" option
+ (see FORMATS for details on how to define these commands).
diff --git a/MANUAL b/MANUAL
index b3f1cc72..3f0b1f7b 100644
--- a/MANUAL
+++ b/MANUAL
@@ -273,3 +273,27 @@ Video Disk Recorder User's Manual
MarginStart = 2 Defines how many minutes before the official start time
MarginStop = 10 of a broadcast VDR shall start recording, and how long
after the official end time it shall stop recording.
+
+* Executing system commands
+
+ The "Main" menu option "Commands" allows you to execute any system commands
+ defined in the configuration file 'commands.conf' (see FORMATS for details).
+ The "Commands" option will only be present in the "Main" menu if a valid
+ 'commands.conf' file containing at least one command definition has been
+ found at program start.
+
+ This feature can be used to do virtually anything, like checking for new
+ mail, displaying the CPU temperature - you name it! All you need to do is
+ enter the necessary command definition into 'commands.conf' and implement
+ the actual command that will be called. Such a command can typically be a
+ shell script or a Perl program. Anything that command writes to stdout will
+ be displayed on a result screen after executing the command. In order to
+ avoid error messages going to stderr, command definitions should redirect
+ stderr to stdout (see FORMATS).
+
+ WARNING: THE COMMANDS DEFINED IN 'commands.conf' WILL BE EXECUTED UNDER THE
+ ======= SAME USER ID THAT VDR IS RUNNING WITH. BE VERY CAREFUL WHEN
+ DEFINING THESE COMMANDS AND MAKE SURE THEY DON'T HARM YOUR SYSTEM,
+ ESPECIALLY IF YOU ARE RUNNING VDR UNDER A HIGH PRIVILEGED USER ID
+ (LIKE 'root').
+
diff --git a/config.c b/config.c
index 0da6ebf5..d4f8b383 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.31 2000/11/11 09:56:01 kls Exp $
+ * $Id: config.c 1.32 2000/11/11 15:41:07 kls Exp $
*/
#include "config.h"
@@ -426,7 +426,7 @@ bool cTimer::Parse(const char *s)
delete summary;
summary = NULL;
//XXX Apparently sscanf() doesn't work correctly if the last %a argument
- //XXX results in an empty string (this firt occured when the EIT gathering
+ //XXX results in an empty string (this first occured when the EIT gathering
//XXX was put into a separate thread - don't know why this happens...
//XXX As a cure we copy the original string and add a blank.
//XXX If anybody can shed some light on why sscanf() failes here, I'd love
@@ -539,10 +539,69 @@ cTimer *cTimer::GetMatch(void)
return t0;
}
+// --- cCommand -------------------------------------------------------------
+
+char *cCommand::result = NULL;
+
+cCommand::cCommand(void)
+{
+ title = command = NULL;
+}
+
+cCommand::~cCommand()
+{
+ delete title;
+ delete command;
+}
+
+bool cCommand::Parse(const char *s)
+{
+ const char *p = strchr(s, ':');
+ if (p) {
+ int l = p - s;
+ if (l > 0) {
+ title = new char[l + 1];
+ strn0cpy(title, s, l + 1);
+ if (!isempty(title)) {
+ command = stripspace(strdup(skipspace(p + 1)));
+ return !isempty(command);
+ }
+ }
+ }
+ return false;
+}
+
+const char *cCommand::Execute(void)
+{
+ dsyslog(LOG_INFO, "executing command '%s'", command);
+ delete result;
+ result = NULL;
+ FILE *p = popen(command, "r");
+ if (p) {
+ int l = 0;
+ int c;
+ while ((c = fgetc(p)) != EOF) {
+ if (l % 20 == 0)
+ result = (char *)realloc(result, l + 21);
+ result[l++] = c;
+ }
+ if (result)
+ result[l] = 0;
+ pclose(p);
+ }
+ else
+ esyslog(LOG_ERR, "ERROR: can't open pipe for command '%s'", command);
+ return result;
+}
+
// -- cKeys ------------------------------------------------------------------
cKeys Keys;
+// -- cCommands --------------------------------------------------------------
+
+cCommands Commands;
+
// -- cChannels --------------------------------------------------------------
int CurrentGroup = -1;
diff --git a/config.h b/config.h
index 679e88ed..c6f6ba99 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.31 2000/11/11 10:39:00 kls Exp $
+ * $Id: config.h 1.32 2000/11/11 14:39:40 kls Exp $
*/
#ifndef __CONFIG_H
@@ -115,7 +115,7 @@ public:
char *summary;
cTimer(bool Instant = false);
cTimer(const cEventInfo *EventInfo);
- ~cTimer();
+ virtual ~cTimer();
cTimer& operator= (const cTimer &Timer);
const char *ToText(void);
bool Parse(const char *s);
@@ -132,6 +132,19 @@ public:
static const char *PrintDay(int d);
};
+class cCommand : public cListObject {
+private:
+ char *title;
+ char *command;
+ static char *result;
+public:
+ cCommand(void);
+ virtual ~cCommand();
+ bool Parse(const char *s);
+ const char *Title(void) { return title; }
+ const char *Execute(void);
+ };
+
template<class T> class cConfig : public cList<T> {
private:
char *fileName;
@@ -217,11 +230,14 @@ public:
cTimer *GetTimer(cTimer *Timer);
};
+class cCommands : public cConfig<cCommand> {};
+
extern int CurrentGroup;
extern cChannels Channels;
extern cTimers Timers;
extern cKeys Keys;
+extern cCommands Commands;
class cSetup {
private:
diff --git a/i18n.c b/i18n.c
index 06b1241d..94009cb1 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.1 2000/11/11 10:39:27 kls Exp $
+ * $Id: i18n.c 1.2 2000/11/11 16:20:47 kls Exp $
*/
/*
@@ -70,6 +70,9 @@ const tPhrase Phrases[] = {
{ "Setup",
"Einstellungen",
},
+ { "Commands",
+ "Befehle",
+ },
{ "Edit Channel",
"Kanal Editieren",
},
diff --git a/menu.c b/menu.c
index 0096a8a9..c9959a4f 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.45 2000/11/11 12:55:10 kls Exp $
+ * $Id: menu.c 1.46 2000/11/11 15:22:56 kls Exp $
*/
#include "menu.h"
@@ -850,26 +850,30 @@ eOSState cMenuTextItem::ProcessKey(eKeys Key)
return osContinue;
}
-// --- cMenuSummary ----------------------------------------------------------
+// --- cMenuText -------------------------------------------------------------
-class cMenuSummary : public cOsdMenu {
+class cMenuText : public cOsdMenu {
public:
- cMenuSummary(const char *Text);
+ cMenuText(const char *Title, const char *Text);
virtual eOSState ProcessKey(eKeys Key);
};
-cMenuSummary::cMenuSummary(const char *Text)
-:cOsdMenu(tr("Summary"))
+cMenuText::cMenuText(const char *Title, const char *Text)
+:cOsdMenu(Title)
{
Add(new cMenuTextItem(Text, 1, 2, MenuColumns - 2, MAXOSDITEMS));
}
-eOSState cMenuSummary::ProcessKey(eKeys Key)
+eOSState cMenuText::ProcessKey(eKeys Key)
{
eOSState state = cOsdMenu::ProcessKey(Key);
- if (state == osUnknown)
- state = osContinue;
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk: return osBack;
+ default: state = osContinue;
+ }
+ }
return state;
}
@@ -1058,7 +1062,7 @@ eOSState cMenuTimers::Summary(void)
return osContinue;
cTimer *ti = Timers.Get(Current());
if (ti && ti->summary && *ti->summary)
- return AddSubMenu(new cMenuSummary(ti->summary));
+ return AddSubMenu(new cMenuText(tr("Summary"), ti->summary));
return Edit(); // convenience for people not using the Summary feature ;-)
}
@@ -1442,7 +1446,7 @@ eOSState cMenuRecordings::Summary(void)
return osContinue;
cMenuRecordingItem *ri = (cMenuRecordingItem *)Get(Current());
if (ri && ri->recording->Summary() && *ri->recording->Summary())
- return AddSubMenu(new cMenuSummary(ri->recording->Summary()));
+ return AddSubMenu(new cMenuText(tr("Summary"), ri->recording->Summary()));
return osContinue;
}
@@ -1524,6 +1528,52 @@ eOSState cMenuSetup::ProcessKey(eKeys Key)
return state;
}
+// --- cMenuCommands ---------------------------------------------------------
+
+class cMenuCommands : public cOsdMenu {
+private:
+ eOSState Execute(void);
+public:
+ cMenuCommands(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuCommands::cMenuCommands(void)
+:cOsdMenu(tr("Commands"))
+{
+ int i = 0;
+ cCommand *command;
+
+ while ((command = Commands.Get(i)) != NULL) {
+ Add(new cOsdItem(command->Title()));
+ i++;
+ }
+}
+
+eOSState cMenuCommands::Execute(void)
+{
+ cCommand *command = Commands.Get(Current());
+ if (command) {
+ const char *Result = command->Execute();
+ if (Result)
+ return AddSubMenu(new cMenuText(command->Title(), Result));
+ }
+ return osContinue;
+}
+
+eOSState cMenuCommands::ProcessKey(eKeys Key)
+{
+ eOSState state = cOsdMenu::ProcessKey(Key);
+
+ if (state == osUnknown) {
+ switch (Key) {
+ case kOk: return Execute();
+ default: break;
+ }
+ }
+ return state;
+}
+
// --- cMenuMain -------------------------------------------------------------
#define STOP_RECORDING tr("Stop recording ")
@@ -1536,6 +1586,8 @@ cMenuMain::cMenuMain(bool Replaying)
Add(new cOsdItem(tr("Timers"), osTimers));
Add(new cOsdItem(tr("Recordings"), osRecordings));
Add(new cOsdItem(tr("Setup"), osSetup));
+ if (Commands.Count())
+ Add(new cOsdItem(tr("Commands"), osCommands));
if (Replaying)
Add(new cOsdItem(tr("Stop replaying"), osStopReplay));
const char *s = NULL;
@@ -1560,6 +1612,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
case osTimers: return AddSubMenu(new cMenuTimers);
case osRecordings: return AddSubMenu(new cMenuRecordings);
case osSetup: return AddSubMenu(new cMenuSetup);
+ case osCommands: return AddSubMenu(new cMenuCommands);
case osStopRecord: if (Interface->Confirm(tr("Stop Recording?"))) {
cOsdItem *item = Get(Current());
if (item) {
diff --git a/osd.h b/osd.h
index 74794d49..af189584 100644
--- a/osd.h
+++ b/osd.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: osd.h 1.15 2000/11/10 15:28:28 kls Exp $
+ * $Id: osd.h 1.16 2000/11/11 14:49:29 kls Exp $
*/
#ifndef __OSD_H
@@ -24,6 +24,7 @@ enum eOSState { osUnknown,
osTimers,
osRecordings,
osSetup,
+ osCommands,
osRecord,
osReplay,
osStopRecord,
diff --git a/tools.c b/tools.c
index 4acea0b3..6d86f162 100644
--- a/tools.c
+++ b/tools.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.c 1.22 2000/10/29 11:21:55 kls Exp $
+ * $Id: tools.c 1.23 2000/11/11 15:17:12 kls Exp $
*/
#define _GNU_SOURCE
@@ -92,6 +92,18 @@ char *skipspace(const char *s)
return (char *)s;
}
+char *stripspace(char *s)
+{
+ if (s && *s) {
+ for (char *p = s + strlen(s) - 1; p >= s; p--) {
+ if (!isspace(*p))
+ break;
+ *p = 0;
+ }
+ }
+ return s;
+}
+
bool isempty(const char *s)
{
return !(s && *skipspace(s));
diff --git a/tools.h b/tools.h
index b5bdd278..0794a1a6 100644
--- a/tools.h
+++ b/tools.h
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: tools.h 1.18 2000/10/29 11:19:20 kls Exp $
+ * $Id: tools.h 1.19 2000/11/11 15:14:40 kls Exp $
*/
#ifndef __TOOLS_H
@@ -41,6 +41,7 @@ char *readline(FILE *f);
char *strn0cpy(char *dest, const char *src, size_t n);
char *strreplace(char *s, char c1, char c2);
char *skipspace(const char *s);
+char *stripspace(char *s);
bool isempty(const char *s);
int time_ms(void);
void delay_ms(int ms);
diff --git a/vdr.c b/vdr.c
index e39db311..060a9d00 100644
--- a/vdr.c
+++ b/vdr.c
@@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
- * $Id: vdr.c 1.44 2000/11/10 16:13:27 kls Exp $
+ * $Id: vdr.c 1.45 2000/11/11 14:40:11 kls Exp $
*/
#include <getopt.h>
@@ -165,6 +165,7 @@ int main(int argc, char *argv[])
Setup.Load(AddDirectory(ConfigDirectory, "setup.conf"));
Channels.Load(AddDirectory(ConfigDirectory, "channels.conf"));
Timers.Load(AddDirectory(ConfigDirectory, "timers.conf"));
+ Commands.Load(AddDirectory(ConfigDirectory, "commands.conf"));
#ifdef REMOTE_LIRC
Keys.SetDummyValues();
#else