summaryrefslogtreecommitdiff
path: root/plugin.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <kls (at) cadsoft (dot) de>2002-05-09 18:00:00 +0200
committerKlaus Schmidinger <kls (at) cadsoft (dot) de>2002-05-09 18:00:00 +0200
commitae8a947367b4be57c9b0ca7bbf0032de0e2018d3 (patch)
treef9a5b6c7d320222b841a54c86590eb012ab3a107 /plugin.c
parentc9a5d8ea5328e4a8bcb0c3423b825c02cb0c3b27 (diff)
downloadvdr-patch-lnbsharing-ae8a947367b4be57c9b0ca7bbf0032de0e2018d3.tar.gz
vdr-patch-lnbsharing-ae8a947367b4be57c9b0ca7bbf0032de0e2018d3.tar.bz2
Version 1.1.0vdr-1.1.0
- Begin of the 1.1 development branch. THIS IS NOT A STABLE VERSION! The current stable version for every day use is still the 1.0 branch. - First step towards a universal plugin interface. See the file PLUGINS.html for a detailed description. The man page vdr(1) describes the new options '-L' and '-P' used to load plugins. This first step implements the complete "outer" shell for plugins. The "inner" access to VDR data structures will follow. - The VDR version number is now displayed in the title line of the "Setup" menu.
Diffstat (limited to 'plugin.c')
-rw-r--r--plugin.c319
1 files changed, 319 insertions, 0 deletions
diff --git a/plugin.c b/plugin.c
new file mode 100644
index 0000000..ecae317
--- /dev/null
+++ b/plugin.c
@@ -0,0 +1,319 @@
+/*
+ * plugin.c: The VDR plugin interface
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: plugin.c 1.1 2002/05/09 16:26:56 kls Exp $
+ */
+
+#include "plugin.h"
+#include <ctype.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include "config.h"
+
+#define LIBVDR_PREFIX "libvdr-"
+#define SO_INDICATOR ".so."
+
+#define MAXPLUGINARGS 1024
+
+// --- cPlugin ---------------------------------------------------------------
+
+cPlugin::cPlugin(void)
+{
+ name = NULL;
+}
+
+cPlugin::~cPlugin()
+{
+ I18nRegister(NULL, Name());
+}
+
+void cPlugin::SetName(const char *s)
+{
+ name = s;
+}
+
+const char *cPlugin::CommandLineHelp(void)
+{
+ return NULL;
+}
+
+bool cPlugin::ProcessArgs(int argc, char *argv[])
+{
+ return true;
+}
+
+void cPlugin::Start(void)
+{
+}
+
+const char *cPlugin::MainMenuEntry(void)
+{
+ return NULL;
+}
+
+cOsdMenu *cPlugin::MainMenuAction(void)
+{
+ return NULL;
+}
+
+cMenuSetupPage *cPlugin::SetupMenu(void)
+{
+ return NULL;
+}
+
+bool cPlugin::SetupParse(const char *Name, const char *Value)
+{
+ return false;
+}
+
+void cPlugin::SetupStore(const char *Name, const char *Value)
+{
+ Setup.Store(Name, Value, this->Name());
+}
+
+void cPlugin::SetupStore(const char *Name, int Value)
+{
+ Setup.Store(Name, Value, this->Name());
+}
+
+void cPlugin::RegisterI18n(const tI18nPhrase * const Phrases)
+{
+ I18nRegister(Phrases, Name());
+}
+
+// --- cDll ------------------------------------------------------------------
+
+cDll::cDll(const char *FileName, const char *Args)
+{
+ fileName = strdup(FileName);
+ args = Args ? strdup(Args) : NULL;
+ handle = NULL;
+ plugin = NULL;
+}
+
+cDll::~cDll()
+{
+ delete plugin;
+ if (handle)
+ dlclose(handle);
+ delete args;
+ delete fileName;
+}
+
+static char *SkipQuote(char *s)
+{
+ char c = *s;
+ strcpy(s, s + 1);
+ while (*s && *s != c) {
+ if (*s == '\\')
+ strcpy(s, s + 1);
+ if (*s)
+ s++;
+ }
+ if (*s) {
+ strcpy(s, s + 1);
+ return s;
+ }
+ esyslog(LOG_ERR, "ERROR: missing closing %c", c);
+ fprintf(stderr, "vdr: missing closing %c\n", c);
+ return NULL;
+}
+
+bool cDll::Load(bool Log)
+{
+ if (Log)
+ isyslog(LOG_INFO, "loading plugin: %s", fileName);
+ if (handle) {
+ esyslog(LOG_ERR, "attempt to load plugin '%s' twice!", fileName);
+ return false;
+ }
+ handle = dlopen(fileName, RTLD_NOW);
+ const char *error = dlerror();
+ if (!error) {
+ void *(*creator)(void);
+ (void *)creator = dlsym(handle, "VDRPluginCreator");
+ if (!(error = dlerror()))
+ plugin = (cPlugin *)creator();
+ }
+ if (!error) {
+ if (plugin && args) {
+ int argc = 0;
+ char *argv[MAXPLUGINARGS];
+ char *p = args;
+ char *q = NULL;
+ bool done = false;
+ while (!done) {
+ if (!q)
+ q = p;
+ switch (*p) {
+ case '\\': strcpy(p, p + 1);
+ if (*p)
+ p++;
+ else {
+ esyslog(LOG_ERR, "ERROR: missing character after \\");
+ fprintf(stderr, "vdr: missing character after \\\n");
+ return false;
+ }
+ break;
+ case '"':
+ case '\'': if ((p = SkipQuote(p)) == NULL)
+ return false;
+ break;
+ default: if (!*p || isspace(*p)) {
+ done = !*p;
+ *p = 0;
+ if (q) {
+ if (argc < MAXPLUGINARGS - 1)
+ argv[argc++] = q;
+ else {
+ esyslog(LOG_ERR, "ERROR: plugin argument list too long");
+ fprintf(stderr, "vdr: plugin argument list too long\n");
+ return false;
+ }
+ q = NULL;
+ }
+ }
+ if (!done)
+ p++;
+ }
+ }
+ argv[argc] = NULL;
+ if (argc)
+ plugin->SetName(argv[0]);
+ optind = 0; // to reset the getopt() data
+ return !argc || plugin->ProcessArgs(argc, argv);
+ }
+ }
+ else {
+ esyslog(LOG_ERR, "ERROR: %s", error);
+ fprintf(stderr, "vdr: %s\n", error);
+ }
+ return !error && plugin;
+}
+
+// --- cPluginManager --------------------------------------------------------
+
+cPluginManager *cPluginManager::pluginManager = NULL;
+
+cPluginManager::cPluginManager(const char *Directory)
+{
+ directory = NULL;
+ if (pluginManager) {
+ fprintf(stderr, "vdr: attempt to create more than one plugin manager - exiting!\n");
+ exit(2);
+ }
+ SetDirectory(Directory);
+ pluginManager = this;
+}
+
+cPluginManager::~cPluginManager()
+{
+ Shutdown();
+ delete directory;
+ if (pluginManager == this)
+ pluginManager = NULL;
+}
+
+void cPluginManager::SetDirectory(const char *Directory)
+{
+ delete directory;
+ directory = Directory ? strdup(Directory) : NULL;
+}
+
+void cPluginManager::AddPlugin(const char *Args)
+{
+ if (strcmp(Args, "*") == 0) {
+ DIR *d = opendir(directory);
+ if (d) {
+ struct dirent *e;
+ while ((e = readdir(d)) != NULL) {
+ if (strstr(e->d_name, LIBVDR_PREFIX) == e->d_name) {
+ char *p = strstr(e->d_name, SO_INDICATOR);
+ if (p) {
+ *p = 0;
+ p += strlen(SO_INDICATOR);
+ if (strcmp(p, VDRVERSION) == 0) {
+ char *name = e->d_name + strlen(LIBVDR_PREFIX);
+ if (strcmp(name, "*") != 0) { // let's not get into a loop!
+ AddPlugin(e->d_name + strlen(LIBVDR_PREFIX));
+ }
+ }
+ }
+ }
+ }
+ closedir(d);
+ }
+ return;
+ }
+ char *s = strdup(Args);
+ char *p = strchr(s, ' ');
+ if (p)
+ *p = 0;
+ char *buffer = NULL;
+ asprintf(&buffer, "%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, VDRVERSION);
+ dlls.Add(new cDll(buffer, Args));
+ delete buffer;
+ delete s;
+}
+
+bool cPluginManager::LoadPlugins(bool Log)
+{
+ for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
+ if (!dll->Load(Log))
+ return false;
+ }
+ return true;
+}
+
+void cPluginManager::StartPlugins(void)
+{
+ for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
+ cPlugin *p = dll->Plugin();
+ if (p) {
+ int Language = Setup.OSDLanguage;
+ Setup.OSDLanguage = 0; // the i18n texts are only available _after_ Start()
+ isyslog(LOG_INFO, "starting plugin: %s (%s): %s", p->Name(), p->Version(), p->Description());
+ Setup.OSDLanguage = Language;
+ dll->Plugin()->Start();
+ }
+ }
+}
+
+bool cPluginManager::HasPlugins(void)
+{
+ return pluginManager && pluginManager->dlls.Count();
+}
+
+cPlugin *cPluginManager::GetPlugin(int Index)
+{
+ cDll *dll = pluginManager ? pluginManager->dlls.Get(Index) : NULL;
+ return dll ? dll->Plugin() : NULL;
+}
+
+cPlugin *cPluginManager::GetPlugin(const char *Name)
+{
+ if (pluginManager) {
+ for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
+ cPlugin *p = dll->Plugin();
+ if (p && strcmp(p->Name(), Name) == 0)
+ return p;
+ }
+ }
+ return NULL;
+}
+
+void cPluginManager::Shutdown(bool Log)
+{
+ cDll *dll;
+ while ((dll = dlls.Last()) != NULL) {
+ if (Log) {
+ cPlugin *p = dll->Plugin();
+ if (p)
+ isyslog(LOG_INFO, "stopping plugin: %s", p->Name());
+ }
+ dlls.Del(dll);
+ }
+}