vdr
1.7.27
|
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 }