vdr  1.7.27
plugin.c
Go to the documentation of this file.
00001 /*
00002  * plugin.c: The VDR plugin interface
00003  *
00004  * See the main source file 'vdr.c' for copyright information and
00005  * how to reach the author.
00006  *
00007  * $Id: plugin.c 2.3 2012/03/11 13:56:02 kls Exp $
00008  */
00009 
00010 #include "plugin.h"
00011 #include <ctype.h>
00012 #include <dirent.h>
00013 #include <dlfcn.h>
00014 #include <stdlib.h>
00015 #include <time.h>
00016 #include "config.h"
00017 #include "interface.h"
00018 #include "thread.h"
00019 
00020 #define LIBVDR_PREFIX  "libvdr-"
00021 #define SO_INDICATOR   ".so."
00022 
00023 #define MAXPLUGINARGS  1024
00024 #define HOUSEKEEPINGDELTA 10 // seconds
00025 
00026 // --- cPlugin ---------------------------------------------------------------
00027 
00028 char *cPlugin::configDirectory = NULL;
00029 
00030 cPlugin::cPlugin(void)
00031 {
00032   name = NULL;
00033   started = false;
00034 }
00035 
00036 cPlugin::~cPlugin()
00037 {
00038 }
00039 
00040 void cPlugin::SetName(const char *s)
00041 {
00042   name = s;
00043   I18nRegister(name);
00044 }
00045 
00046 const char *cPlugin::CommandLineHelp(void)
00047 {
00048   return NULL;
00049 }
00050 
00051 bool cPlugin::ProcessArgs(int argc, char *argv[])
00052 {
00053   return true;
00054 }
00055 
00056 bool cPlugin::Initialize(void)
00057 {
00058   return true;
00059 }
00060 
00061 bool cPlugin::Start(void)
00062 {
00063   return true;
00064 }
00065 
00066 void cPlugin::Stop(void)
00067 {
00068 }
00069 
00070 void cPlugin::Housekeeping(void)
00071 {
00072 }
00073 
00074 void cPlugin::MainThreadHook(void)
00075 {
00076 }
00077 
00078 cString cPlugin::Active(void)
00079 {
00080   return NULL;
00081 }
00082 
00083 time_t cPlugin::WakeupTime(void)
00084 {
00085   return 0;
00086 }
00087 
00088 const char *cPlugin::MainMenuEntry(void)
00089 {
00090   return NULL;
00091 }
00092 
00093 cOsdObject *cPlugin::MainMenuAction(void)
00094 {
00095   return NULL;
00096 }
00097 
00098 cMenuSetupPage *cPlugin::SetupMenu(void)
00099 {
00100   return NULL;
00101 }
00102 
00103 bool cPlugin::SetupParse(const char *Name, const char *Value)
00104 {
00105   return false;
00106 }
00107 
00108 void cPlugin::SetupStore(const char *Name, const char *Value)
00109 {
00110   Setup.Store(Name, Value, this->Name());
00111 }
00112 
00113 void cPlugin::SetupStore(const char *Name, int Value)
00114 {
00115   Setup.Store(Name, Value, this->Name());
00116 }
00117 
00118 bool cPlugin::Service(const char *Id, void *Data)
00119 {
00120   return false;
00121 }
00122 
00123 const char **cPlugin::SVDRPHelpPages(void)
00124 {
00125   return NULL;
00126 }
00127 
00128 cString cPlugin::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
00129 {
00130   return NULL;
00131 }
00132 
00133 void cPlugin::SetConfigDirectory(const char *Dir)
00134 {
00135   free(configDirectory);
00136   configDirectory = strdup(Dir);
00137 }
00138 
00139 const char *cPlugin::ConfigDirectory(const char *PluginName)
00140 {
00141   static cString buffer;
00142   if (!cThread::IsMainThread())
00143      esyslog("ERROR: plugin '%s' called cPlugin::ConfigDirectory(), which is not thread safe!", PluginName ? PluginName : "<no name given>");
00144   buffer = cString::sprintf("%s/plugins%s%s", configDirectory, PluginName ? "/" : "", PluginName ? PluginName : "");
00145   return MakeDirs(buffer, true) ? *buffer : NULL;
00146 }
00147 
00148 // --- cDll ------------------------------------------------------------------
00149 
00150 cDll::cDll(const char *FileName, const char *Args)
00151 {
00152   fileName = strdup(FileName);
00153   args = Args ? strdup(Args) : NULL;
00154   handle = NULL;
00155   plugin = NULL;
00156 }
00157 
00158 cDll::~cDll()
00159 {
00160   delete plugin;
00161   if (handle)
00162      dlclose(handle);
00163   free(args);
00164   free(fileName);
00165 }
00166 
00167 static char *SkipQuote(char *s)
00168 {
00169   char c = *s;
00170   memmove(s, s + 1, strlen(s));
00171   while (*s && *s != c) {
00172         if (*s == '\\')
00173            memmove(s, s + 1, strlen(s));
00174         if (*s)
00175            s++;
00176         }
00177   if (*s) {
00178      memmove(s, s + 1, strlen(s));
00179      return s;
00180      }
00181   esyslog("ERROR: missing closing %c", c);
00182   fprintf(stderr, "vdr: missing closing %c\n", c);
00183   return NULL;
00184 }
00185 
00186 bool cDll::Load(bool Log)
00187 {
00188   if (Log)
00189      isyslog("loading plugin: %s", fileName);
00190   if (handle) {
00191      esyslog("attempt to load plugin '%s' twice!", fileName);
00192      return false;
00193      }
00194   handle = dlopen(fileName, RTLD_NOW);
00195   const char *error = dlerror();
00196   if (!error) {
00197      void *(*creator)(void);
00198      creator = (void *(*)(void))dlsym(handle, "VDRPluginCreator");
00199      if (!(error = dlerror()))
00200         plugin = (cPlugin *)creator();
00201      }
00202   if (!error) {
00203      if (plugin && args) {
00204         int argc = 0;
00205         char *argv[MAXPLUGINARGS];
00206         char *p = skipspace(stripspace(args));
00207         char *q = NULL;
00208         bool done = false;
00209         while (!done) {
00210               if (!q)
00211                  q = p;
00212               switch (*p) {
00213                 case '\\': memmove(p, p + 1, strlen(p));
00214                            if (*p)
00215                               p++;
00216                            else {
00217                               esyslog("ERROR: missing character after \\");
00218                               fprintf(stderr, "vdr: missing character after \\\n");
00219                               return false;
00220                               }
00221                            break;
00222                 case '"':
00223                 case '\'': if ((p = SkipQuote(p)) == NULL)
00224                               return false;
00225                            break;
00226                 default: if (!*p || isspace(*p)) {
00227                             done = !*p;
00228                             *p = 0;
00229                             if (q) {
00230                                if (argc < MAXPLUGINARGS - 1)
00231                                   argv[argc++] = q;
00232                                else {
00233                                   esyslog("ERROR: plugin argument list too long");
00234                                   fprintf(stderr, "vdr: plugin argument list too long\n");
00235                                   return false;
00236                                   }
00237                                q = NULL;
00238                                }
00239                             }
00240                          if (!done)
00241                             p = *p ? p + 1 : skipspace(p + 1);
00242                 }
00243               }
00244         argv[argc] = NULL;
00245         if (argc)
00246            plugin->SetName(argv[0]);
00247         optind = 0; // to reset the getopt() data
00248         return !Log || !argc || plugin->ProcessArgs(argc, argv);
00249         }
00250      }
00251   else {
00252      esyslog("ERROR: %s", error);
00253      fprintf(stderr, "vdr: %s\n", error);
00254      }
00255   return !error && plugin;
00256 }
00257 
00258 // --- cPluginManager --------------------------------------------------------
00259 
00260 cPluginManager *cPluginManager::pluginManager = NULL;
00261 
00262 cPluginManager::cPluginManager(const char *Directory)
00263 {
00264   directory = NULL;
00265   lastHousekeeping = time(NULL);
00266   nextHousekeeping = -1;
00267   if (pluginManager) {
00268      fprintf(stderr, "vdr: attempt to create more than one plugin manager - exiting!\n");
00269      exit(2);
00270      }
00271   SetDirectory(Directory);
00272   pluginManager = this;
00273 }
00274 
00275 cPluginManager::~cPluginManager()
00276 {
00277   Shutdown();
00278   free(directory);
00279   if (pluginManager == this)
00280      pluginManager = NULL;
00281 }
00282 
00283 void cPluginManager::SetDirectory(const char *Directory)
00284 {
00285   free(directory);
00286   directory = Directory ? strdup(Directory) : NULL;
00287 }
00288 
00289 void cPluginManager::AddPlugin(const char *Args)
00290 {
00291   if (strcmp(Args, "*") == 0) {
00292      cReadDir d(directory);
00293      struct dirent *e;
00294      while ((e = d.Next()) != NULL) {
00295            if (strstr(e->d_name, LIBVDR_PREFIX) == e->d_name) {
00296               char *p = strstr(e->d_name, SO_INDICATOR);
00297               if (p) {
00298                  *p = 0;
00299                  p += strlen(SO_INDICATOR);
00300                  if (strcmp(p, APIVERSION) == 0) {
00301                     char *name = e->d_name + strlen(LIBVDR_PREFIX);
00302                     if (strcmp(name, "*") != 0) { // let's not get into a loop!
00303                        AddPlugin(e->d_name + strlen(LIBVDR_PREFIX));
00304                        }
00305                     }
00306                  }
00307               }
00308            }
00309      return;
00310      }
00311   char *s = strdup(skipspace(Args));
00312   char *p = strchr(s, ' ');
00313   if (p)
00314      *p = 0;
00315   struct stat st;
00316   if (stat (cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), &st) && errno == ENOENT) {
00317      esyslog("WARN: missing plugin '%s'", s);
00318      fprintf(stderr, "vdr: missing plugin '%s'\n", s);
00319      }
00320   else
00321   dlls.Add(new cDll(cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), Args));
00322   free(s);
00323 }
00324 
00325 bool cPluginManager::LoadPlugins(bool Log)
00326 {
00327   for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
00328       if (!dll->Load(Log))
00329          /*return false*/;
00330       }
00331   return true;
00332 }
00333 
00334 bool cPluginManager::InitializePlugins(void)
00335 {
00336   for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
00337       cPlugin *p = dll->Plugin();
00338       if (p) {
00339          isyslog("initializing plugin: %s (%s): %s", p->Name(), p->Version(), p->Description());
00340          if (!p->Initialize())
00341             return false;
00342          }
00343       }
00344   return true;
00345 }
00346 
00347 bool cPluginManager::StartPlugins(void)
00348 {
00349   for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
00350       cPlugin *p = dll->Plugin();
00351       if (p) {
00352          isyslog("starting plugin: %s", p->Name());
00353          if (!p->Start())
00354             return false;
00355          p->started = true;
00356          }
00357       }
00358   return true;
00359 }
00360 
00361 void cPluginManager::Housekeeping(void)
00362 {
00363   if (time(NULL) - lastHousekeeping > HOUSEKEEPINGDELTA) {
00364      if (++nextHousekeeping >= dlls.Count())
00365         nextHousekeeping = 0;
00366      cDll *dll = dlls.Get(nextHousekeeping);
00367      if (dll) {
00368         cPlugin *p = dll->Plugin();
00369         if (p) {
00370            p->Housekeeping();
00371            }
00372         }
00373      lastHousekeeping = time(NULL);
00374      }
00375 }
00376 
00377 void cPluginManager::MainThreadHook(void)
00378 {
00379   for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
00380       cPlugin *p = dll->Plugin();
00381       if (p)
00382          p->MainThreadHook();
00383       }
00384 }
00385 
00386 bool cPluginManager::Active(const char *Prompt)
00387 {
00388   if (pluginManager) {
00389      for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
00390          cPlugin *p = dll->Plugin();
00391          if (p) {
00392             cString s = p->Active();
00393             if (!isempty(*s)) {
00394                if (!Prompt || !Interface->Confirm(cString::sprintf("%s - %s", *s, Prompt)))
00395                   return true;
00396                }
00397             }
00398          }
00399      }
00400   return false;
00401 }
00402 
00403 cPlugin *cPluginManager::GetNextWakeupPlugin(void)
00404 {
00405   cPlugin *NextPlugin = NULL;
00406   if (pluginManager) {
00407      time_t Now = time(NULL);
00408      time_t Next = 0;
00409      for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
00410          cPlugin *p = dll->Plugin();
00411          if (p) {
00412             time_t t = p->WakeupTime();
00413             if (t > Now && (!Next || t < Next)) {
00414                Next = t;
00415                NextPlugin = p;
00416                }
00417             }
00418          }
00419      }
00420   return NextPlugin;
00421 }
00422 
00423 bool cPluginManager::HasPlugins(void)
00424 {
00425   return pluginManager && pluginManager->dlls.Count();
00426 }
00427 
00428 cPlugin *cPluginManager::GetPlugin(int Index)
00429 {
00430   cDll *dll = pluginManager ? pluginManager->dlls.Get(Index) : NULL;
00431   return dll ? dll->Plugin() : NULL;
00432 }
00433 
00434 cPlugin *cPluginManager::GetPlugin(const char *Name)
00435 {
00436   if (pluginManager && Name) {
00437      for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
00438          cPlugin *p = dll->Plugin();
00439          if (p && strcmp(p->Name(), Name) == 0)
00440             return p;
00441          }
00442      }
00443   return NULL;
00444 }
00445 
00446 cPlugin *cPluginManager::CallFirstService(const char *Id, void *Data)
00447 {
00448   if (pluginManager) {
00449      for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
00450          cPlugin *p = dll->Plugin();
00451          if (p && p->Service(Id, Data))
00452             return p;
00453          }
00454      }
00455   return NULL;
00456 }
00457 
00458 bool cPluginManager::CallAllServices(const char *Id, void *Data)
00459 {
00460   bool found=false;
00461   if (pluginManager) {
00462      for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
00463          cPlugin *p = dll->Plugin();
00464          if (p && p->Service(Id, Data))
00465             found = true;
00466          }
00467      }
00468   return found;
00469 }
00470 
00471 void cPluginManager::StopPlugins(void)
00472 {
00473   for (cDll *dll = dlls.Last(); dll; dll = dlls.Prev(dll)) {
00474       cPlugin *p = dll->Plugin();
00475       if (p && p->started) {
00476          isyslog("stopping plugin: %s", p->Name());
00477          p->Stop();
00478          p->started = false;
00479          }
00480       }
00481 }
00482 
00483 void cPluginManager::Shutdown(bool Log)
00484 {
00485   cDll *dll;
00486   while ((dll = dlls.Last()) != NULL) {
00487         cPlugin *p = dll->Plugin();
00488         if (p && Log)
00489            isyslog("deleting plugin: %s", p->Name());
00490         dlls.Del(dll);
00491         }
00492 }