vdr 2.7.7
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.1 2025/02/12 22:22:20 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 = dlerror();
230 if (!error) {
231 typedef cPlugin *create_t(void);
232 create_t *create = (create_t *)dlsym(handle, "VDRPluginCreator");
233 error = dlerror();
234 if (!error && create) {
235 plugin = create();
236 destroy = (destroy_t *)dlsym(handle, "VDRPluginDestroyer");
237 error = dlerror();
238 if (error) {
239 error = NULL;
240 isyslog("plugin %s: missing symbol VDRPluginDestroyer(), please rebuild", fileName);
241 }
242 }
243 }
244 if (!error) {
245 if (plugin && args) {
246 int argc = 0;
247 char *argv[MAXPLUGINARGS];
248 char *p = skipspace(stripspace(args));
249 char *q = NULL;
250 bool done = false;
251 while (!done) {
252 if (!q)
253 q = p;
254 switch (*p) {
255 case '\\': memmove(p, p + 1, strlen(p));
256 if (*p)
257 p++;
258 else {
259 esyslog("ERROR: missing character after \\");
260 fprintf(stderr, "vdr: missing character after \\\n");
261 return false;
262 }
263 break;
264 case '"':
265 case '\'': if ((p = SkipQuote(p)) == NULL)
266 return false;
267 break;
268 default: if (!*p || isspace(*p)) {
269 done = !*p;
270 *p = 0;
271 if (q) {
272 if (argc < MAXPLUGINARGS - 1)
273 argv[argc++] = q;
274 else {
275 esyslog("ERROR: plugin argument list too long");
276 fprintf(stderr, "vdr: plugin argument list too long\n");
277 return false;
278 }
279 q = NULL;
280 }
281 }
282 if (!done)
283 p = *p ? p + 1 : skipspace(p + 1);
284 }
285 }
286 argv[argc] = NULL;
287 if (argc)
288 plugin->SetName(argv[0]);
289 optind = 0; // to reset the getopt() data
290 return !Log || !argc || plugin->ProcessArgs(argc, argv);
291 }
292 }
293 else {
294 esyslog("ERROR: %s", error);
295 fprintf(stderr, "vdr: %s\n", error);
296 }
297 return !error && plugin;
298}
299
300// --- cPluginManager --------------------------------------------------------
301
303
304cPluginManager::cPluginManager(const char *Directory)
305{
306 directory = NULL;
307 lastHousekeeping = time(NULL);
308 nextHousekeeping = -1;
309 if (pluginManager) {
310 fprintf(stderr, "vdr: attempt to create more than one plugin manager - exiting!\n");
311 exit(2);
312 }
313 SetDirectory(Directory);
314 pluginManager = this;
315}
316
318{
319 Shutdown();
320 free(directory);
321 if (pluginManager == this)
322 pluginManager = NULL;
323}
324
325void cPluginManager::SetDirectory(const char *Directory)
326{
327 free(directory);
328 directory = Directory ? strdup(Directory) : NULL;
329}
330
331void cPluginManager::AddPlugin(const char *Args)
332{
333 if (strcmp(Args, "*") == 0) {
335 for (int i = 0; i < Files.Size(); i++) {
336 char *FileName = Files.At(i);
337 if (strstr(FileName, LIBVDR_PREFIX) == FileName) {
338 char *p = strstr(FileName, SO_INDICATOR);
339 if (p) {
340 *p = 0;
341 p += strlen(SO_INDICATOR);
342 if (strcmp(p, APIVERSION) == 0) {
343 char *name = FileName + strlen(LIBVDR_PREFIX);
344 if (strcmp(name, "*") != 0) { // let's not get into a loop!
345 AddPlugin(FileName + strlen(LIBVDR_PREFIX));
346 }
347 }
348 }
349 }
350 }
351 return;
352 }
353 char *s = strdup(skipspace(Args));
354 char *p = strchr(s, ' ');
355 if (p)
356 *p = 0;
357 struct stat st;
358 if (stat (cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), &st) && errno == ENOENT) {
359 esyslog("WARN: missing plugin '%s'", s);
360 fprintf(stderr, "vdr: missing plugin '%s'\n", s);
361 }
362 else
363 dlls.Add(new cDll(cString::sprintf("%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, APIVERSION), Args));
364 free(s);
365}
366
368{
369 for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
370 if (!dll->Load(Log))
371 /*return false*/;
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("initializing plugin: %s (%s): %s", p->Name(), p->Version(), p->Description());
382 if (!p->Initialize())
383 return false;
384 }
385 }
386 return true;
387}
388
390{
391 for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
392 cPlugin *p = dll->Plugin();
393 if (p) {
394 isyslog("starting plugin: %s", p->Name());
395 if (!p->Start())
396 return false;
397 p->started = true;
398 }
399 }
400 return true;
401}
402
404{
405 if (time(NULL) - lastHousekeeping > HOUSEKEEPINGDELTA) {
406 if (++nextHousekeeping >= dlls.Count())
408 cDll *dll = dlls.Get(nextHousekeeping);
409 if (dll) {
410 cPlugin *p = dll->Plugin();
411 if (p) {
412 p->Housekeeping();
413 }
414 }
415 lastHousekeeping = time(NULL);
416 }
417}
418
420{
421 for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
422 cPlugin *p = dll->Plugin();
423 if (p)
424 p->MainThreadHook();
425 }
426}
427
428bool cPluginManager::Active(const char *Prompt)
429{
430 if (pluginManager) {
431 for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
432 cPlugin *p = dll->Plugin();
433 if (p) {
434 cString s = p->Active();
435 if (!isempty(*s)) {
436 if (!Prompt || !Interface->Confirm(cString::sprintf("%s - %s", *s, Prompt)))
437 return true;
438 }
439 }
440 }
441 }
442 return false;
443}
444
446{
447 cPlugin *NextPlugin = NULL;
448 if (pluginManager) {
449 time_t Now = time(NULL);
450 time_t Next = 0;
451 for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
452 cPlugin *p = dll->Plugin();
453 if (p) {
454 time_t t = p->WakeupTime();
455 if (t > Now && (!Next || t < Next)) {
456 Next = t;
457 NextPlugin = p;
458 }
459 }
460 }
461 }
462 return NextPlugin;
463}
464
466{
467 return pluginManager && pluginManager->dlls.Count();
468}
469
471{
472 cDll *dll = pluginManager ? pluginManager->dlls.Get(Index) : NULL;
473 return dll ? dll->Plugin() : NULL;
474}
475
477{
478 if (pluginManager && Name) {
479 for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
480 cPlugin *p = dll->Plugin();
481 if (p && strcmp(p->Name(), Name) == 0)
482 return p;
483 }
484 }
485 return NULL;
486}
487
488cPlugin *cPluginManager::CallFirstService(const char *Id, void *Data)
489{
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 return p;
495 }
496 }
497 return NULL;
498}
499
500bool cPluginManager::CallAllServices(const char *Id, void *Data)
501{
502 bool found=false;
503 if (pluginManager) {
504 for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
505 cPlugin *p = dll->Plugin();
506 if (p && p->Service(Id, Data))
507 found = true;
508 }
509 }
510 return found;
511}
512
514{
515 for (cDll *dll = dlls.Last(); dll; dll = dlls.Prev(dll)) {
516 cPlugin *p = dll->Plugin();
517 if (p && p->started) {
518 isyslog("stopping plugin: %s", p->Name());
519 p->Stop();
520 p->started = false;
521 }
522 }
523}
524
526{
527 cDll *dll;
528 while ((dll = dlls.Last()) != NULL) {
529 cPlugin *p = dll->Plugin();
530 if (p && Log)
531 isyslog("deleting plugin: %s", p->Name());
532 dlls.Del(dll);
533 }
534}
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
static cPluginManager * pluginManager
Definition plugin.h:92
void StopPlugins(void)
Definition plugin.c:513
static cPlugin * GetNextWakeupPlugin(void)
Definition plugin.c:445
void MainThreadHook(void)
Definition plugin.c:419
bool StartPlugins(void)
Definition plugin.c:389
cPluginManager(const char *Directory)
Definition plugin.c:304
void SetDirectory(const char *Directory)
Definition plugin.c:325
bool InitializePlugins(void)
Definition plugin.c:376
void AddPlugin(const char *Args)
Definition plugin.c:331
static bool Active(const char *Prompt=NULL)
Definition plugin.c:428
static bool HasPlugins(void)
Definition plugin.c:465
cDlls dlls
Definition plugin.h:96
int nextHousekeeping
Definition plugin.h:95
static bool CallAllServices(const char *Id, void *Data=NULL)
Definition plugin.c:500
bool LoadPlugins(bool Log=false)
Definition plugin.c:367
void Shutdown(bool Log=false)
Definition plugin.c:525
static cPlugin * CallFirstService(const char *Id, void *Data=NULL)
Definition plugin.c:488
char * directory
Definition plugin.h:93
virtual ~cPluginManager()
Definition plugin.c:317
void Housekeeping(void)
Definition plugin.c:403
time_t lastHousekeeping
Definition plugin.h:94
static cPlugin * GetPlugin(int Index)
Definition plugin.c:470
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
static cString sprintf(const char *fmt,...) __attribute__((format(printf
Definition tools.c:1195
static tThreadId IsMainThread(void)
Definition thread.h:131
int Size(void) const
Definition tools.h:754
T & At(int Index) const
Definition tools.h:731
cSetup Setup
Definition config.c:372
#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:211
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
bool isempty(const char *s)
Definition tools.c:357
bool MakeDirs(const char *FileName, bool IsDirectory)
Definition tools.c:507
char * stripspace(char *s)
Definition tools.c:227
char * skipspace(const char *s)
Definition tools.h:244
#define esyslog(a...)
Definition tools.h:35
#define isyslog(a...)
Definition tools.h:36