vdr  2.2.0
plugin.c
Go to the documentation of this file.
1 /*
2  * plugin.c: The VDR plugin interface
3  *
4  * See the main source file 'vdr.c' for copyright information and
5  * how to reach the author.
6  *
7  * $Id: plugin.c 3.0 2012/09/01 13:10:27 kls Exp $
8  */
9 
10 #include "plugin.h"
11 #include <ctype.h>
12 #include <dirent.h>
13 #include <dlfcn.h>
14 #include <stdlib.h>
15 #include <time.h>
16 #include "config.h"
17 #include "interface.h"
18 #include "thread.h"
19 
20 #define LIBVDR_PREFIX "libvdr-"
21 #define SO_INDICATOR ".so."
22 
23 #define MAXPLUGINARGS 1024
24 #define HOUSEKEEPINGDELTA 10 // seconds
25 
26 // --- cPlugin ---------------------------------------------------------------
27 
31 
33 {
34  name = NULL;
35  started = false;
36 }
37 
39 {
40 }
41 
42 void cPlugin::SetName(const char *s)
43 {
44  name = s;
46 }
47 
48 const char *cPlugin::CommandLineHelp(void)
49 {
50  return NULL;
51 }
52 
53 bool cPlugin::ProcessArgs(int argc, char *argv[])
54 {
55  return true;
56 }
57 
59 {
60  return true;
61 }
62 
63 bool cPlugin::Start(void)
64 {
65  return true;
66 }
67 
68 void cPlugin::Stop(void)
69 {
70 }
71 
73 {
74 }
75 
77 {
78 }
79 
81 {
82  return NULL;
83 }
84 
85 time_t cPlugin::WakeupTime(void)
86 {
87  return 0;
88 }
89 
90 const char *cPlugin::MainMenuEntry(void)
91 {
92  return NULL;
93 }
94 
96 {
97  return NULL;
98 }
99 
101 {
102  return NULL;
103 }
104 
105 bool cPlugin::SetupParse(const char *Name, const char *Value)
106 {
107  return false;
108 }
109 
110 void cPlugin::SetupStore(const char *Name, const char *Value)
111 {
112  Setup.Store(Name, Value, this->Name());
113 }
114 
115 void cPlugin::SetupStore(const char *Name, int Value)
116 {
117  Setup.Store(Name, Value, this->Name());
118 }
119 
120 bool cPlugin::Service(const char *Id, void *Data)
121 {
122  return false;
123 }
124 
125 const char **cPlugin::SVDRPHelpPages(void)
126 {
127  return NULL;
128 }
129 
130 cString cPlugin::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
131 {
132  return NULL;
133 }
134 
135 void cPlugin::SetConfigDirectory(const char *Dir)
136 {
137  configDirectory = Dir;
138 }
139 
140 const char *cPlugin::ConfigDirectory(const char *PluginName)
141 {
142  static cString buffer;
143  if (!cThread::IsMainThread())
144  esyslog("ERROR: plugin '%s' called cPlugin::ConfigDirectory(), which is not thread safe!", PluginName ? PluginName : "<no name given>");
145  buffer = cString::sprintf("%s/plugins%s%s", *configDirectory, PluginName ? "/" : "", PluginName ? PluginName : "");
146  return MakeDirs(buffer, true) ? *buffer : NULL;
147 }
148 
149 void cPlugin::SetCacheDirectory(const char *Dir)
150 {
151  cacheDirectory = Dir;
152 }
153 
154 const char *cPlugin::CacheDirectory(const char *PluginName)
155 {
156  static cString buffer;
157  if (!cThread::IsMainThread())
158  esyslog("ERROR: plugin '%s' called cPlugin::CacheDirectory(), which is not thread safe!", PluginName ? PluginName : "<no name given>");
159  buffer = cString::sprintf("%s/plugins%s%s", *cacheDirectory, PluginName ? "/" : "", PluginName ? PluginName : "");
160  return MakeDirs(buffer, true) ? *buffer : NULL;
161 }
162 
163 void cPlugin::SetResourceDirectory(const char *Dir)
164 {
165  resourceDirectory = Dir;
166 }
167 
168 const char *cPlugin::ResourceDirectory(const char *PluginName)
169 {
170  static cString buffer;
171  if (!cThread::IsMainThread())
172  esyslog("ERROR: plugin '%s' called cPlugin::ResourceDirectory(), which is not thread safe!", PluginName ? PluginName : "<no name given>");
173  buffer = cString::sprintf("%s/plugins%s%s", *resourceDirectory, PluginName ? "/" : "", PluginName ? PluginName : "");
174  return MakeDirs(buffer, true) ? *buffer : NULL;
175 }
176 
177 // --- cDll ------------------------------------------------------------------
178 
179 cDll::cDll(const char *FileName, const char *Args)
180 {
181  fileName = strdup(FileName);
182  args = Args ? strdup(Args) : NULL;
183  handle = NULL;
184  plugin = NULL;
185 }
186 
188 {
189  delete plugin;
190  if (handle)
191  dlclose(handle);
192  free(args);
193  free(fileName);
194 }
195 
196 static char *SkipQuote(char *s)
197 {
198  char c = *s;
199  memmove(s, s + 1, strlen(s));
200  while (*s && *s != c) {
201  if (*s == '\\')
202  memmove(s, s + 1, strlen(s));
203  if (*s)
204  s++;
205  }
206  if (*s) {
207  memmove(s, s + 1, strlen(s));
208  return s;
209  }
210  esyslog("ERROR: missing closing %c", c);
211  fprintf(stderr, "vdr: missing closing %c\n", c);
212  return NULL;
213 }
214 
215 bool cDll::Load(bool Log)
216 {
217  if (Log)
218  isyslog("loading plugin: %s", fileName);
219  if (handle) {
220  esyslog("attempt to load plugin '%s' twice!", fileName);
221  return false;
222  }
223  handle = dlopen(fileName, RTLD_NOW);
224  const char *error = dlerror();
225  if (!error) {
226  void *(*creator)(void);
227  creator = (void *(*)(void))dlsym(handle, "VDRPluginCreator");
228  if (!(error = dlerror()))
229  plugin = (cPlugin *)creator();
230  }
231  if (!error) {
232  if (plugin && args) {
233  int argc = 0;
234  char *argv[MAXPLUGINARGS];
235  char *p = skipspace(stripspace(args));
236  char *q = NULL;
237  bool done = false;
238  while (!done) {
239  if (!q)
240  q = p;
241  switch (*p) {
242  case '\\': memmove(p, p + 1, strlen(p));
243  if (*p)
244  p++;
245  else {
246  esyslog("ERROR: missing character after \\");
247  fprintf(stderr, "vdr: missing character after \\\n");
248  return false;
249  }
250  break;
251  case '"':
252  case '\'': if ((p = SkipQuote(p)) == NULL)
253  return false;
254  break;
255  default: if (!*p || isspace(*p)) {
256  done = !*p;
257  *p = 0;
258  if (q) {
259  if (argc < MAXPLUGINARGS - 1)
260  argv[argc++] = q;
261  else {
262  esyslog("ERROR: plugin argument list too long");
263  fprintf(stderr, "vdr: plugin argument list too long\n");
264  return false;
265  }
266  q = NULL;
267  }
268  }
269  if (!done)
270  p = *p ? p + 1 : skipspace(p + 1);
271  }
272  }
273  argv[argc] = NULL;
274  if (argc)
275  plugin->SetName(argv[0]);
276  optind = 0; // to reset the getopt() data
277  return !Log || !argc || plugin->ProcessArgs(argc, argv);
278  }
279  }
280  else {
281  esyslog("ERROR: %s", error);
282  fprintf(stderr, "vdr: %s\n", error);
283  }
284  return !error && plugin;
285 }
286 
287 // --- cPluginManager --------------------------------------------------------
288 
290 
291 cPluginManager::cPluginManager(const char *Directory)
292 {
293  directory = NULL;
294  lastHousekeeping = time(NULL);
295  nextHousekeeping = -1;
296  if (pluginManager) {
297  fprintf(stderr, "vdr: attempt to create more than one plugin manager - exiting!\n");
298  exit(2);
299  }
300  SetDirectory(Directory);
301  pluginManager = this;
302 }
303 
305 {
306  Shutdown();
307  free(directory);
308  if (pluginManager == this)
309  pluginManager = NULL;
310 }
311 
312 void cPluginManager::SetDirectory(const char *Directory)
313 {
314  free(directory);
315  directory = Directory ? strdup(Directory) : NULL;
316 }
317 
318 void cPluginManager::AddPlugin(const char *Args)
319 {
320  if (strcmp(Args, "*") == 0) {
321  cReadDir d(directory);
322  struct dirent *e;
323  while ((e = d.Next()) != NULL) {
324  if (strstr(e->d_name, LIBVDR_PREFIX) == e->d_name) {
325  char *p = strstr(e->d_name, SO_INDICATOR);
326  if (p) {
327  *p = 0;
328  p += strlen(SO_INDICATOR);
329  if (strcmp(p, APIVERSION) == 0) {
330  char *name = e->d_name + strlen(LIBVDR_PREFIX);
331  if (strcmp(name, "*") != 0) { // let's not get into a loop!
332  AddPlugin(e->d_name + strlen(LIBVDR_PREFIX));
333  }
334  }
335  }
336  }
337  }
338  return;
339  }
340  char *s = strdup(skipspace(Args));
341  char *p = strchr(s, ' ');
342  if (p)
343  *p = 0;
344  struct stat st;
345  if (stat (cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), &st) && errno == ENOENT) {
346  esyslog("WARN: missing plugin '%s'", s);
347  fprintf(stderr, "vdr: missing plugin '%s'\n", s);
348  }
349  else
350  dlls.Add(new cDll(cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), Args));
351  free(s);
352 }
353 
355 {
356  for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
357  if (!dll->Load(Log))
358  /*return false*/;
359  }
360  return true;
361 }
362 
364 {
365  for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
366  cPlugin *p = dll->Plugin();
367  if (p) {
368  isyslog("initializing plugin: %s (%s): %s", p->Name(), p->Version(), p->Description());
369  if (!p->Initialize())
370  return false;
371  }
372  }
373  return true;
374 }
375 
377 {
378  for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
379  cPlugin *p = dll->Plugin();
380  if (p) {
381  isyslog("starting plugin: %s", p->Name());
382  if (!p->Start())
383  return false;
384  p->started = true;
385  }
386  }
387  return true;
388 }
389 
391 {
392  if (time(NULL) - lastHousekeeping > HOUSEKEEPINGDELTA) {
393  if (++nextHousekeeping >= dlls.Count())
394  nextHousekeeping = 0;
395  cDll *dll = dlls.Get(nextHousekeeping);
396  if (dll) {
397  cPlugin *p = dll->Plugin();
398  if (p) {
399  p->Housekeeping();
400  }
401  }
402  lastHousekeeping = time(NULL);
403  }
404 }
405 
407 {
408  for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
409  cPlugin *p = dll->Plugin();
410  if (p)
411  p->MainThreadHook();
412  }
413 }
414 
415 bool cPluginManager::Active(const char *Prompt)
416 {
417  if (pluginManager) {
418  for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
419  cPlugin *p = dll->Plugin();
420  if (p) {
421  cString s = p->Active();
422  if (!isempty(*s)) {
423  if (!Prompt || !Interface->Confirm(cString::sprintf("%s - %s", *s, Prompt)))
424  return true;
425  }
426  }
427  }
428  }
429  return false;
430 }
431 
433 {
434  cPlugin *NextPlugin = NULL;
435  if (pluginManager) {
436  time_t Now = time(NULL);
437  time_t Next = 0;
438  for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
439  cPlugin *p = dll->Plugin();
440  if (p) {
441  time_t t = p->WakeupTime();
442  if (t > Now && (!Next || t < Next)) {
443  Next = t;
444  NextPlugin = p;
445  }
446  }
447  }
448  }
449  return NextPlugin;
450 }
451 
453 {
454  return pluginManager && pluginManager->dlls.Count();
455 }
456 
458 {
459  cDll *dll = pluginManager ? pluginManager->dlls.Get(Index) : NULL;
460  return dll ? dll->Plugin() : NULL;
461 }
462 
464 {
465  if (pluginManager && Name) {
466  for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
467  cPlugin *p = dll->Plugin();
468  if (p && strcmp(p->Name(), Name) == 0)
469  return p;
470  }
471  }
472  return NULL;
473 }
474 
475 cPlugin *cPluginManager::CallFirstService(const char *Id, void *Data)
476 {
477  if (pluginManager) {
478  for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
479  cPlugin *p = dll->Plugin();
480  if (p && p->Service(Id, Data))
481  return p;
482  }
483  }
484  return NULL;
485 }
486 
487 bool cPluginManager::CallAllServices(const char *Id, void *Data)
488 {
489  bool found=false;
490  if (pluginManager) {
491  for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
492  cPlugin *p = dll->Plugin();
493  if (p && p->Service(Id, Data))
494  found = true;
495  }
496  }
497  return found;
498 }
499 
501 {
502  for (cDll *dll = dlls.Last(); dll; dll = dlls.Prev(dll)) {
503  cPlugin *p = dll->Plugin();
504  if (p && p->started) {
505  isyslog("stopping plugin: %s", p->Name());
506  p->Stop();
507  p->started = false;
508  }
509  }
510 }
511 
513 {
514  cDll *dll;
515  while ((dll = dlls.Last()) != NULL) {
516  cPlugin *p = dll->Plugin();
517  if (p && Log)
518  isyslog("deleting plugin: %s", p->Name());
519  dlls.Del(dll);
520  }
521 }
struct dirent * Next(void)
Definition: tools.c:1466
const char * name
Definition: plugin.h:27
virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
Definition: plugin.c:130
static const char * ResourceDirectory(const char *PluginName=NULL)
Definition: plugin.c:168
bool isempty(const char *s)
Definition: tools.c:297
bool Confirm(const char *s, int Seconds=10, bool WaitForTimeout=false)
Definition: interface.c:67
bool LoadPlugins(bool Log=false)
Definition: plugin.c:354
const char * Name(void)
Definition: plugin.h:34
virtual cString Active(void)
Definition: plugin.c:80
void Shutdown(bool Log=false)
Definition: plugin.c:512
char * stripspace(char *s)
Definition: tools.c:201
virtual const char ** SVDRPHelpPages(void)
Definition: plugin.c:125
void Store(const char *Name, const char *Value, const char *Plugin=NULL, bool AllowMultiple=false)
Definition: config.c:518
virtual const char * Version(void)=0
#define SO_INDICATOR
Definition: plugin.c:21
bool MakeDirs(const char *FileName, bool IsDirectory)
Definition: tools.c:445
#define LIBVDR_PREFIX
Definition: plugin.c:20
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition: tools.c:1080
void SetupStore(const char *Name, const char *Value=NULL)
Definition: plugin.c:110
virtual cOsdObject * MainMenuAction(void)
Definition: plugin.c:95
#define APIVERSION
Definition: config.h:30
virtual bool ProcessArgs(int argc, char *argv[])
Definition: plugin.c:53
Definition: plugin.h:20
#define esyslog(a...)
Definition: tools.h:34
cPluginManager(const char *Directory)
Definition: plugin.c:291
cPlugin * Plugin(void)
Definition: plugin.h:78
bool Load(bool Log=false)
Definition: plugin.c:215
virtual ~cDll()
Definition: plugin.c:187
static tThreadId IsMainThread(void)
Definition: thread.h:129
bool started
Definition: plugin.h:28
static const char * ConfigDirectory(const char *PluginName=NULL)
Definition: plugin.c:140
static cString resourceDirectory
Definition: plugin.h:26
bool InitializePlugins(void)
Definition: plugin.c:363
virtual void Stop(void)
Definition: plugin.c:68
void MainThreadHook(void)
Definition: plugin.c:406
static const char * CacheDirectory(const char *PluginName=NULL)
Definition: plugin.c:154
static bool Active(const char *Prompt=NULL)
Definition: plugin.c:415
cPlugin(void)
Definition: plugin.c:32
virtual const char * Description(void)=0
virtual const char * CommandLineHelp(void)
Definition: plugin.c:48
Definition: plugin.h:68
static bool CallAllServices(const char *Id, void *Data=NULL)
Definition: plugin.c:487
virtual void MainThreadHook(void)
Definition: plugin.c:76
static cPlugin * CallFirstService(const char *Id, void *Data=NULL)
Definition: plugin.c:475
static char * SkipQuote(char *s)
Definition: plugin.c:196
static cString configDirectory
Definition: plugin.h:24
virtual bool SetupParse(const char *Name, const char *Value)
Definition: plugin.c:105
cListObject * Next(void) const
Definition: tools.h:468
void SetName(const char *s)
Definition: plugin.c:42
#define MAXPLUGINARGS
Definition: plugin.c:23
virtual cMenuSetupPage * SetupMenu(void)
Definition: plugin.c:100
cSetup Setup
Definition: config.c:373
void I18nRegister(const char *Plugin)
Registers the named plugin, so that it can use internationalized texts.
Definition: i18n.c:164
friend class cDll
Definition: plugin.h:21
virtual void Housekeeping(void)
Definition: plugin.c:72
static bool HasPlugins(void)
Definition: plugin.c:452
virtual bool Initialize(void)
Definition: plugin.c:58
static cPluginManager * pluginManager
Definition: plugin.h:85
void Housekeeping(void)
Definition: plugin.c:390
cListObject * Prev(void) const
Definition: tools.h:467
virtual const char * MainMenuEntry(void)
Definition: plugin.c:90
static void SetCacheDirectory(const char *Dir)
Definition: plugin.c:149
virtual ~cPluginManager()
Definition: plugin.c:304
static cPlugin * GetNextWakeupPlugin(void)
Definition: plugin.c:432
static cString cacheDirectory
Definition: plugin.h:25
void AddPlugin(const char *Args)
Definition: plugin.c:318
char * skipspace(const char *s)
Definition: tools.h:200
void SetDirectory(const char *Directory)
Definition: plugin.c:312
#define HOUSEKEEPINGDELTA
Definition: plugin.c:24
#define isyslog(a...)
Definition: tools.h:35
static cPlugin * GetPlugin(int Index)
Definition: plugin.c:457
virtual bool Start(void)
Definition: plugin.c:63
bool StartPlugins(void)
Definition: plugin.c:376
cInterface * Interface
Definition: interface.c:20
virtual time_t WakeupTime(void)
Definition: plugin.c:85
void StopPlugins(void)
Definition: plugin.c:500
static void SetResourceDirectory(const char *Dir)
Definition: plugin.c:163
cDll(const char *FileName, const char *Args)
Definition: plugin.c:179
Definition: tools.h:168
virtual ~cPlugin()
Definition: plugin.c:38
static void SetConfigDirectory(const char *Dir)
Definition: plugin.c:135
virtual bool Service(const char *Id, void *Data=NULL)
Definition: plugin.c:120