/* * plugin-load.cc * Copyright 2007-2013 William Pitcock and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions, and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions, and the following disclaimer in the documentation * provided with the distribution. * * This software is provided "as is" and without any warranty, express or * implied. In no event shall the authors be liable for any damages arising from * the use of this software. */ #include "plugins-internal.h" #include #include #include #include #include #include #include #include "audstrings.h" #include "internal.h" #include "plugin.h" #include "runtime.h" static const char * plugin_dir_list[] = { "Transport", "Container", "Input", "Output", "Effect", "General", "Visualization" }; struct LoadedModule { Plugin * header; GModule * module; }; static Index loaded_modules; bool plugin_check_flags (int version) { switch (aud_get_mainloop_type ()) { case MainloopType::GLib: version &= ~_AUD_PLUGIN_GLIB_ONLY; break; case MainloopType::Qt: version &= ~_AUD_PLUGIN_QT_ONLY; break; } return ! (version & 0xffff0000); } Plugin * plugin_load (const char * filename) { AUDINFO ("Loading plugin: %s.\n", filename); GModule * module = g_module_open (filename, G_MODULE_BIND_LOCAL); if (! module) { AUDERR ("%s could not be loaded: %s\n", filename, g_module_error ()); return nullptr; } Plugin * header; if (! g_module_symbol (module, "aud_plugin_instance", (void * *) & header)) header = nullptr; if (! header || header->magic != _AUD_PLUGIN_MAGIC) { AUDERR ("%s is not a valid Audacious plugin.\n", filename); g_module_close (module); return nullptr; } /* flags are stored in high 16 bits of version field */ if ((header->version & 0xffff) < _AUD_PLUGIN_VERSION_MIN || (header->version & 0xffff) > _AUD_PLUGIN_VERSION) { AUDERR ("%s is not compatible with this version of Audacious.\n", filename); g_module_close (module); return nullptr; } if (plugin_check_flags (header->version) && (header->type == PluginType::Transport || header->type == PluginType::Playlist || header->type == PluginType::Input || header->type == PluginType::Effect)) { if (! header->init ()) { AUDERR ("%s failed to initialize.\n", filename); g_module_close (module); return nullptr; } } loaded_modules.append (header, module); return header; } static void plugin_unload (LoadedModule & loaded) { if (plugin_check_flags (loaded.header->version) && (loaded.header->type == PluginType::Transport || loaded.header->type == PluginType::Playlist || loaded.header->type == PluginType::Input || loaded.header->type == PluginType::Effect)) { loaded.header->cleanup (); } #ifndef VALGRIND_FRIENDLY g_module_close (loaded.module); #endif } /******************************************************************/ static bool scan_plugin_func (const char * path, const char * basename, void * data) { if (! str_has_suffix_nocase (basename, PLUGIN_SUFFIX)) return false; GStatBuf st; if (g_stat (path, & st) < 0) { AUDERR ("Unable to stat %s: %s\n", path, strerror (errno)); return false; } if (S_ISREG (st.st_mode)) plugin_register (path, st.st_mtime); return false; } static void scan_plugins (const char * path) { dir_foreach (path, scan_plugin_func, nullptr); } void plugin_system_init () { assert (g_module_supported ()); plugin_registry_load (); const char * path = aud_get_path (AudPath::PluginDir); for (const char * dir : plugin_dir_list) scan_plugins (filename_build ({path, dir})); plugin_registry_prune (); } void plugin_system_cleanup () { plugin_registry_save (); for (LoadedModule & loaded : loaded_modules) plugin_unload (loaded); loaded_modules.clear (); }