/* This file is part of libextractor. Copyright (C) 2002, 2003, 2004, 2005, 2006, 2009, 2012 Vidyut Samanta and Christian Grothoff libextractor is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. libextractor is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libextractor; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file main/extractor_plugins.c * @brief code to load plugins * @author Christian Grothoff */ #include "extractor_plugins.h" #include "extractor_plugpath.h" #include "extractor_ipc.h" #include "extractor_logging.h" /** * Try to resolve a plugin function. * * @param lib_handle library to search for the symbol * @param prefix prefix to add * @param sym_name base name for the symbol * @param options set to special options requested by the plugin * @return NULL on error, otherwise pointer to the symbol */ static void * get_symbol_with_prefix (void *lib_handle, const char *template, const char *prefix, const char **options) { char *name; void *symbol; const char *sym_name; char *sym; char *dot; const char *(*opt_fun)(void); if (NULL != options) *options = NULL; if (NULL == (sym_name = strrchr (prefix, '_'))) return NULL; sym_name++; if (NULL == (sym = strdup (sym_name))) { LOG_STRERROR ("strdup"); return NULL; } if (NULL != (dot = strchr (sym, '.'))) *dot = '\0'; if (NULL == (name = malloc (strlen (sym) + strlen (template) + 1))) { free (sym); return NULL; } sprintf (name, template, sym); /* try without '_' first */ symbol = lt_dlsym (lib_handle, name + 1); if (NULL == symbol) { /* now try with the '_' */ char *first_error = strdup (lt_dlerror ()); symbol = lt_dlsym (lib_handle, name); if (NULL == symbol) { LOG ("Resolving symbol `%s' failed, " "so I tried `%s', but that failed also. Errors are: " "`%s' and `%s'.\n", name + 1, name, first_error == NULL ? "out of memory" : first_error, lt_dlerror ()); } if (NULL != first_error) free (first_error); } if ( (NULL != symbol) && (NULL != options) ) { /* get special options */ sprintf (name, "_EXTRACTOR_%s_options", sym); /* try without '_' first */ opt_fun = lt_dlsym (lib_handle, name + 1); if (NULL == opt_fun) opt_fun = lt_dlsym (lib_handle, name); if (NULL != opt_fun) *options = opt_fun (); } free (sym); free (name); return symbol; } /** * Load a plugin. * * @param plugin plugin to load * @return 0 on success, -1 on error */ int EXTRACTOR_plugin_load_ (struct EXTRACTOR_PluginList *plugin) { #if WINDOWS wchar_t wlibname[4097]; char llibname[4097]; #endif lt_dladvise advise; if (EXTRACTOR_OPTION_DISABLED == plugin->flags) return -1; if (NULL == plugin->libname) plugin->libname = EXTRACTOR_find_plugin_ (plugin->short_libname); if (NULL == plugin->libname) { LOG ("Failed to find plugin `%s'\n", plugin->short_libname); plugin->flags = EXTRACTOR_OPTION_DISABLED; return -1; } lt_dladvise_init (&advise); lt_dladvise_ext (&advise); lt_dladvise_local (&advise); #if WINDOWS wlibname[0] = L'\0'; llibname[0] = '\0'; if ( (MultiByteToWideChar (CP_UTF8, 0, plugin->libname, -1, wlibname, sizeof (wlibname)) <= 0) || (WideCharToMultiByte (CP_ACP, 0, wlibname, -1, llibname, sizeof (llibname), NULL, NULL) < 0) ) { LOG ("Loading `%s' plugin failed: %s\n", plugin->short_libname, "can't convert plugin name to local encoding"); free (plugin->libname); plugin->libname = NULL; plugin->flags = EXTRACTOR_OPTION_DISABLED; return -1; } plugin->libraryHandle = lt_dlopenadvise (llibname, advise); #else plugin->libraryHandle = lt_dlopenadvise (plugin->libname, advise); #endif lt_dladvise_destroy (&advise); if (NULL == plugin->libraryHandle) { LOG ("Loading `%s' plugin failed (using name `%s'): %s\n", plugin->short_libname, plugin->libname, lt_dlerror ()); free (plugin->libname); plugin->libname = NULL; plugin->flags = EXTRACTOR_OPTION_DISABLED; return -1; } plugin->extract_method = get_symbol_with_prefix (plugin->libraryHandle, "_EXTRACTOR_%s_extract_method", plugin->libname, &plugin->specials); if (NULL == plugin->extract_method) { LOG ("Resolving `extract' method of plugin `%s' failed: %s\n", plugin->short_libname, lt_dlerror ()); lt_dlclose (plugin->libraryHandle); free (plugin->libname); plugin->libname = NULL; plugin->flags = EXTRACTOR_OPTION_DISABLED; return -1; } return 0; } /** * Add a library for keyword extraction. * * @param prev the previous list of libraries, may be NULL * @param library the name of the library * @param options options to pass to the plugin * @param flags options to use * @return the new list of libraries, equal to prev iff an error occurred */ struct EXTRACTOR_PluginList * EXTRACTOR_plugin_add (struct EXTRACTOR_PluginList *prev, const char *library, const char *options, enum EXTRACTOR_Options flags) { struct EXTRACTOR_PluginList *plugin; struct EXTRACTOR_PluginList *pos; char *libname; for (pos = prev; NULL != pos; pos = pos->next) if (0 == strcmp (pos->short_libname, library)) return prev; /* no change, library already loaded */ if (NULL == (libname = EXTRACTOR_find_plugin_ (library))) { LOG ("Could not load plugin `%s'\n", library); return prev; } if (NULL == (plugin = malloc (sizeof (struct EXTRACTOR_PluginList)))) return prev; memset (plugin, 0, sizeof (struct EXTRACTOR_PluginList)); plugin->next = prev; if (NULL == (plugin->short_libname = strdup (library))) { free (plugin); return NULL; } plugin->libname = libname; plugin->flags = flags; if (NULL != options) plugin->plugin_options = strdup (options); else plugin->plugin_options = NULL; plugin->seek_request = -1; return plugin; } /** * Load multiple libraries as specified by the user. * * @param config a string given by the user that defines which * libraries should be loaded. Has the format * "[[-]LIBRARYNAME[(options)][:[-]LIBRARYNAME[(options)]]]*". * For example, 'mp3:ogg.so' loads the * mp3 and the ogg library. The '-' before the LIBRARYNAME * indicates that the library should be removed from * the library list. * @param prev the previous list of libraries, may be NULL * @param flags options to use * @return the new list of libraries, equal to prev iff an error occurred * or if config was empty (or NULL). */ struct EXTRACTOR_PluginList * EXTRACTOR_plugin_add_config (struct EXTRACTOR_PluginList *prev, const char *config, enum EXTRACTOR_Options flags) { char *cpy; size_t pos; size_t last; ssize_t lastconf; size_t len; if (NULL == config) return prev; if (NULL == (cpy = strdup (config))) return prev; len = strlen (config); pos = 0; last = 0; lastconf = 0; while (pos < len) { while ( (':' != cpy[pos]) && ('\0' != cpy[pos]) && ('(' != cpy[pos]) ) pos++; switch (cpy[pos]) { case '(': cpy[pos++] = '\0'; /* replace '(' by termination */ lastconf = pos; /* start config from here, after (. */ while ( ('\0' != cpy[pos]) && (')' != cpy[pos])) pos++; /* config until ) or EOS. */ if (')' == cpy[pos]) { cpy[pos++] = '\0'; /* write end of config here. */ while ( (':' != cpy[pos]) && ('\0' != cpy[pos]) ) pos++; /* forward until real end of string found. */ cpy[pos++] = '\0'; } else { cpy[pos++] = '\0'; /* end of string. */ } break; case ':': case '\0': lastconf = -1; /* NULL config when no (). */ cpy[pos++] = '\0'; /* replace ':' by termination */ break; default: ABORT (); } if ('-' == cpy[last]) { last++; prev = EXTRACTOR_plugin_remove (prev, &cpy[last]); } else { prev = EXTRACTOR_plugin_add (prev, &cpy[last], (-1 != lastconf) ? &cpy[lastconf] : NULL, flags); } last = pos; } free (cpy); return prev; } /** * Remove a plugin from a list. * * @param prev the current list of plugins * @param library the name of the plugin to remove * @return the reduced list, unchanged if the plugin was not loaded */ struct EXTRACTOR_PluginList * EXTRACTOR_plugin_remove (struct EXTRACTOR_PluginList *prev, const char *library) { struct EXTRACTOR_PluginList *pos; struct EXTRACTOR_PluginList *first; pos = prev; first = prev; while ( (NULL != pos) && (0 != strcmp (pos->short_libname, library)) ) { prev = pos; pos = pos->next; } if (NULL == pos) { LOG ("Unloading plugin `%s' failed!\n", library); return first; } /* found, close library */ if (first == pos) first = pos->next; else prev->next = pos->next; if (NULL != pos->channel) EXTRACTOR_IPC_channel_destroy_ (pos->channel); if ( (NULL != pos->shm) && (0 == EXTRACTOR_IPC_shared_memory_change_rc_ (pos->shm, -1)) ) EXTRACTOR_IPC_shared_memory_destroy_ (pos->shm); if (NULL != pos->short_libname) free (pos->short_libname); if (NULL != pos->libname) free (pos->libname); free (pos->plugin_options); if (NULL != pos->libraryHandle) lt_dlclose (pos->libraryHandle); free (pos); return first; } /** * Remove all plugins from the given list (destroys the list). * * @param plugin the list of plugins */ void EXTRACTOR_plugin_remove_all (struct EXTRACTOR_PluginList *plugins) { while (NULL != plugins) plugins = EXTRACTOR_plugin_remove (plugins, plugins->short_libname); } /* end of extractor_plugins.c */