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