nkeynes@1024: /** nkeynes@1024: * $Id$ nkeynes@1024: * nkeynes@1024: * Plugin loader code nkeynes@1024: * nkeynes@1024: * Copyright (c) 2009 Nathan Keynes. nkeynes@1024: * nkeynes@1024: * This program is free software; you can redistribute it and/or modify nkeynes@1024: * it under the terms of the GNU General Public License as published by nkeynes@1024: * the Free Software Foundation; either version 2 of the License, or nkeynes@1024: * (at your option) any later version. nkeynes@1024: * nkeynes@1024: * This program is distributed in the hope that it will be useful, nkeynes@1024: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@1024: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@1024: * GNU General Public License for more details. nkeynes@1024: */ nkeynes@1024: nkeynes@1027: #include nkeynes@1024: #include nkeynes@1024: #include nkeynes@1024: #include nkeynes@1024: #include nkeynes@1024: #include nkeynes@1024: #include "plugin.h" nkeynes@1041: #include "lxpaths.h" nkeynes@1024: nkeynes@1024: #ifdef APPLE_BUILD nkeynes@1024: #define SOEXT ".dylib" nkeynes@1024: #else nkeynes@1024: #define SOEXT ".so" nkeynes@1024: #endif nkeynes@1024: nkeynes@1027: /** Dummy plugin used as a plugin directory marker */ nkeynes@1027: #define DUMMY_PLUGIN ("lxdream_dummy" SOEXT) nkeynes@1024: nkeynes@1027: const char *plugin_type_string[] = { "undefined", "audio driver", "input driver" }; nkeynes@1027: nkeynes@1027: int main(int argc, char *argv[]); nkeynes@1027: static const char *exec_path = NULL; nkeynes@1027: nkeynes@1027: /** nkeynes@1027: * Return the full path to the main binary nkeynes@1027: */ nkeynes@1027: static const char *get_exec_path() nkeynes@1027: { nkeynes@1027: if( exec_path == NULL ) { nkeynes@1027: Dl_info dli; nkeynes@1027: nkeynes@1027: /* Use dladdr for this, since it should be available for any platform nkeynes@1027: * that we can support plugins on at all. nkeynes@1027: */ nkeynes@1027: if( dladdr( main, &dli) ) { nkeynes@1027: gchar *path = g_strdup( dli.dli_fname ); nkeynes@1027: char *i = strrchr( path, '/' ); nkeynes@1027: if( i > path ) { nkeynes@1027: *i = '\0'; nkeynes@1027: exec_path = path; nkeynes@1027: } else { nkeynes@1027: g_free(path); nkeynes@1027: } nkeynes@1027: } nkeynes@1027: } nkeynes@1027: return exec_path; nkeynes@1027: } nkeynes@1027: nkeynes@1027: static gboolean plugin_load( const gchar *plugin_path ) nkeynes@1024: { nkeynes@1024: void *so = dlopen(plugin_path, RTLD_NOW|RTLD_LOCAL); nkeynes@1024: if( so == NULL ) { nkeynes@1024: WARN("Failed to load plugin '%s': %s", plugin_path, dlerror()); nkeynes@1024: return FALSE; nkeynes@1024: } nkeynes@1024: nkeynes@1024: struct plugin_struct *plugin = (struct plugin_struct *)dlsym(so,"lxdream_plugin_entry"); nkeynes@1024: if( plugin == NULL ) { nkeynes@1024: WARN("Failed to load plugin: '%s': Not an lxdream plugin", plugin_path); nkeynes@1024: dlclose(so); nkeynes@1024: return FALSE; nkeynes@1024: } nkeynes@1024: nkeynes@1024: if( strcmp(lxdream_short_version, plugin->version) != 0 ) { nkeynes@1024: WARN("Failed to load plugin: '%s': Incompatible version (%s)", plugin_path, plugin->version); nkeynes@1024: dlclose(so); nkeynes@1024: return FALSE; nkeynes@1024: } nkeynes@1024: nkeynes@1027: if( plugin->type == PLUGIN_NONE ) { nkeynes@1027: /* 'dummy' plugin - we don't actually want to load it */ nkeynes@1027: dlclose(so); nkeynes@1027: return FALSE; nkeynes@1027: } nkeynes@1027: nkeynes@1024: if( plugin->type < PLUGIN_MIN_TYPE || plugin->type > PLUGIN_MAX_TYPE ) { nkeynes@1024: WARN("Failed to load plugin: '%s': Unrecognized plugin type (%d)", plugin_path, plugin->type ); nkeynes@1024: dlclose(so); nkeynes@1024: return FALSE; nkeynes@1024: } nkeynes@1024: nkeynes@1024: if( plugin->register_plugin() == FALSE ) { nkeynes@1024: WARN("Failed to load plugin: '%s': Initialization failed", plugin_path); nkeynes@1024: dlclose(so); nkeynes@1024: return FALSE; nkeynes@1024: } nkeynes@1075: DEBUG("Loaded %s '%s'", plugin_type_string[plugin->type], plugin->name); nkeynes@1024: return TRUE; nkeynes@1024: } nkeynes@1024: nkeynes@1027: static gboolean has_plugins( const gchar *path ) nkeynes@1027: { nkeynes@1027: struct stat st; nkeynes@1027: nkeynes@1027: gchar *dummy_name = g_strdup_printf( "%s/%s", path, DUMMY_PLUGIN ); nkeynes@1027: if( stat( dummy_name, &st ) == 0 ) { nkeynes@1027: return TRUE; nkeynes@1027: } else { nkeynes@1027: return FALSE; nkeynes@1027: } nkeynes@1027: } nkeynes@1027: nkeynes@1024: /** nkeynes@1024: * Scan the plugin dir and load all valid plugins. nkeynes@1024: */ nkeynes@1027: static int plugin_load_all( const gchar *plugin_dir ) nkeynes@1024: { nkeynes@1071: int plugin_count = 0; nkeynes@1024: struct dirent *ent; nkeynes@1027: nkeynes@1024: DIR *dir = opendir(plugin_dir); nkeynes@1024: if( dir == NULL ) { nkeynes@1024: WARN( "Unable to open plugin directory '%s'", plugin_dir ); nkeynes@1024: return 0; nkeynes@1024: } nkeynes@1024: nkeynes@1024: while( (ent = readdir(dir)) != NULL ) { nkeynes@1024: const char *ext = strrchr(ent->d_name, '.'); nkeynes@1024: if( ext != NULL && strcasecmp(SOEXT,ext) == 0 ) { nkeynes@1024: char *libname = g_strdup_printf( "%s/%s", plugin_dir,ent->d_name ); nkeynes@1024: if( plugin_load( libname ) ) { nkeynes@1024: plugin_count++; nkeynes@1024: } nkeynes@1024: g_free(libname); nkeynes@1024: } nkeynes@1024: } nkeynes@1024: return plugin_count; nkeynes@1024: } nkeynes@1027: nkeynes@1027: int plugin_init() nkeynes@1027: { nkeynes@1027: const char *path = get_exec_path(); nkeynes@1027: if( path == NULL || !has_plugins(path) ) { nkeynes@1027: path = get_plugin_path(); nkeynes@1027: } nkeynes@1027: nkeynes@1075: DEBUG( "Plugin directory: %s", path ); nkeynes@1027: return plugin_load_all( path ); nkeynes@1027: }