nkeynes@1024 | 1 | /**
|
nkeynes@1024 | 2 | * $Id$
|
nkeynes@1024 | 3 | *
|
nkeynes@1024 | 4 | * Plugin loader code
|
nkeynes@1024 | 5 | *
|
nkeynes@1024 | 6 | * Copyright (c) 2009 Nathan Keynes.
|
nkeynes@1024 | 7 | *
|
nkeynes@1024 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@1024 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@1024 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@1024 | 11 | * (at your option) any later version.
|
nkeynes@1024 | 12 | *
|
nkeynes@1024 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@1024 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@1024 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@1024 | 16 | * GNU General Public License for more details.
|
nkeynes@1024 | 17 | */
|
nkeynes@1024 | 18 |
|
nkeynes@1027 | 19 | #include <sys/stat.h>
|
nkeynes@1024 | 20 | #include <dirent.h>
|
nkeynes@1024 | 21 | #include <dlfcn.h>
|
nkeynes@1024 | 22 | #include <string.h>
|
nkeynes@1296 | 23 | #include <glib.h>
|
nkeynes@1024 | 24 | #include "plugin.h"
|
nkeynes@1041 | 25 | #include "lxpaths.h"
|
nkeynes@1024 | 26 |
|
nkeynes@1024 | 27 | #ifdef APPLE_BUILD
|
nkeynes@1024 | 28 | #define SOEXT ".dylib"
|
nkeynes@1024 | 29 | #else
|
nkeynes@1024 | 30 | #define SOEXT ".so"
|
nkeynes@1024 | 31 | #endif
|
nkeynes@1024 | 32 |
|
nkeynes@1027 | 33 | /** Dummy plugin used as a plugin directory marker */
|
nkeynes@1027 | 34 | #define DUMMY_PLUGIN ("lxdream_dummy" SOEXT)
|
nkeynes@1024 | 35 |
|
nkeynes@1027 | 36 | const char *plugin_type_string[] = { "undefined", "audio driver", "input driver" };
|
nkeynes@1027 | 37 |
|
nkeynes@1027 | 38 | int main(int argc, char *argv[]);
|
nkeynes@1027 | 39 | static const char *exec_path = NULL;
|
nkeynes@1027 | 40 |
|
nkeynes@1027 | 41 | /**
|
nkeynes@1027 | 42 | * Return the full path to the main binary
|
nkeynes@1027 | 43 | */
|
nkeynes@1027 | 44 | static const char *get_exec_path()
|
nkeynes@1027 | 45 | {
|
nkeynes@1027 | 46 | if( exec_path == NULL ) {
|
nkeynes@1027 | 47 | Dl_info dli;
|
nkeynes@1027 | 48 |
|
nkeynes@1027 | 49 | /* Use dladdr for this, since it should be available for any platform
|
nkeynes@1027 | 50 | * that we can support plugins on at all.
|
nkeynes@1027 | 51 | */
|
nkeynes@1027 | 52 | if( dladdr( main, &dli) ) {
|
nkeynes@1027 | 53 | gchar *path = g_strdup( dli.dli_fname );
|
nkeynes@1027 | 54 | char *i = strrchr( path, '/' );
|
nkeynes@1027 | 55 | if( i > path ) {
|
nkeynes@1027 | 56 | *i = '\0';
|
nkeynes@1027 | 57 | exec_path = path;
|
nkeynes@1027 | 58 | } else {
|
nkeynes@1027 | 59 | g_free(path);
|
nkeynes@1027 | 60 | }
|
nkeynes@1027 | 61 | }
|
nkeynes@1027 | 62 | }
|
nkeynes@1027 | 63 | return exec_path;
|
nkeynes@1027 | 64 | }
|
nkeynes@1027 | 65 |
|
nkeynes@1027 | 66 | static gboolean plugin_load( const gchar *plugin_path )
|
nkeynes@1024 | 67 | {
|
nkeynes@1024 | 68 | void *so = dlopen(plugin_path, RTLD_NOW|RTLD_LOCAL);
|
nkeynes@1024 | 69 | if( so == NULL ) {
|
nkeynes@1024 | 70 | WARN("Failed to load plugin '%s': %s", plugin_path, dlerror());
|
nkeynes@1024 | 71 | return FALSE;
|
nkeynes@1024 | 72 | }
|
nkeynes@1024 | 73 |
|
nkeynes@1024 | 74 | struct plugin_struct *plugin = (struct plugin_struct *)dlsym(so,"lxdream_plugin_entry");
|
nkeynes@1024 | 75 | if( plugin == NULL ) {
|
nkeynes@1024 | 76 | WARN("Failed to load plugin: '%s': Not an lxdream plugin", plugin_path);
|
nkeynes@1024 | 77 | dlclose(so);
|
nkeynes@1024 | 78 | return FALSE;
|
nkeynes@1024 | 79 | }
|
nkeynes@1024 | 80 |
|
nkeynes@1024 | 81 | if( strcmp(lxdream_short_version, plugin->version) != 0 ) {
|
nkeynes@1024 | 82 | WARN("Failed to load plugin: '%s': Incompatible version (%s)", plugin_path, plugin->version);
|
nkeynes@1024 | 83 | dlclose(so);
|
nkeynes@1024 | 84 | return FALSE;
|
nkeynes@1024 | 85 | }
|
nkeynes@1024 | 86 |
|
nkeynes@1027 | 87 | if( plugin->type == PLUGIN_NONE ) {
|
nkeynes@1027 | 88 | /* 'dummy' plugin - we don't actually want to load it */
|
nkeynes@1027 | 89 | dlclose(so);
|
nkeynes@1027 | 90 | return FALSE;
|
nkeynes@1027 | 91 | }
|
nkeynes@1027 | 92 |
|
nkeynes@1024 | 93 | if( plugin->type < PLUGIN_MIN_TYPE || plugin->type > PLUGIN_MAX_TYPE ) {
|
nkeynes@1024 | 94 | WARN("Failed to load plugin: '%s': Unrecognized plugin type (%d)", plugin_path, plugin->type );
|
nkeynes@1024 | 95 | dlclose(so);
|
nkeynes@1024 | 96 | return FALSE;
|
nkeynes@1024 | 97 | }
|
nkeynes@1024 | 98 |
|
nkeynes@1024 | 99 | if( plugin->register_plugin() == FALSE ) {
|
nkeynes@1024 | 100 | WARN("Failed to load plugin: '%s': Initialization failed", plugin_path);
|
nkeynes@1024 | 101 | dlclose(so);
|
nkeynes@1024 | 102 | return FALSE;
|
nkeynes@1024 | 103 | }
|
nkeynes@1075 | 104 | DEBUG("Loaded %s '%s'", plugin_type_string[plugin->type], plugin->name);
|
nkeynes@1024 | 105 | return TRUE;
|
nkeynes@1024 | 106 | }
|
nkeynes@1024 | 107 |
|
nkeynes@1027 | 108 | static gboolean has_plugins( const gchar *path )
|
nkeynes@1027 | 109 | {
|
nkeynes@1027 | 110 | struct stat st;
|
nkeynes@1027 | 111 |
|
nkeynes@1027 | 112 | gchar *dummy_name = g_strdup_printf( "%s/%s", path, DUMMY_PLUGIN );
|
nkeynes@1027 | 113 | if( stat( dummy_name, &st ) == 0 ) {
|
nkeynes@1027 | 114 | return TRUE;
|
nkeynes@1027 | 115 | } else {
|
nkeynes@1027 | 116 | return FALSE;
|
nkeynes@1027 | 117 | }
|
nkeynes@1027 | 118 | }
|
nkeynes@1027 | 119 |
|
nkeynes@1024 | 120 | /**
|
nkeynes@1024 | 121 | * Scan the plugin dir and load all valid plugins.
|
nkeynes@1024 | 122 | */
|
nkeynes@1027 | 123 | static int plugin_load_all( const gchar *plugin_dir )
|
nkeynes@1024 | 124 | {
|
nkeynes@1071 | 125 | int plugin_count = 0;
|
nkeynes@1024 | 126 | struct dirent *ent;
|
nkeynes@1027 | 127 |
|
nkeynes@1024 | 128 | DIR *dir = opendir(plugin_dir);
|
nkeynes@1024 | 129 | if( dir == NULL ) {
|
nkeynes@1024 | 130 | WARN( "Unable to open plugin directory '%s'", plugin_dir );
|
nkeynes@1024 | 131 | return 0;
|
nkeynes@1024 | 132 | }
|
nkeynes@1024 | 133 |
|
nkeynes@1024 | 134 | while( (ent = readdir(dir)) != NULL ) {
|
nkeynes@1024 | 135 | const char *ext = strrchr(ent->d_name, '.');
|
nkeynes@1024 | 136 | if( ext != NULL && strcasecmp(SOEXT,ext) == 0 ) {
|
nkeynes@1024 | 137 | char *libname = g_strdup_printf( "%s/%s", plugin_dir,ent->d_name );
|
nkeynes@1024 | 138 | if( plugin_load( libname ) ) {
|
nkeynes@1024 | 139 | plugin_count++;
|
nkeynes@1024 | 140 | }
|
nkeynes@1024 | 141 | g_free(libname);
|
nkeynes@1024 | 142 | }
|
nkeynes@1024 | 143 | }
|
nkeynes@1024 | 144 | return plugin_count;
|
nkeynes@1024 | 145 | }
|
nkeynes@1027 | 146 |
|
nkeynes@1027 | 147 | int plugin_init()
|
nkeynes@1027 | 148 | {
|
nkeynes@1027 | 149 | const char *path = get_exec_path();
|
nkeynes@1027 | 150 | if( path == NULL || !has_plugins(path) ) {
|
nkeynes@1027 | 151 | path = get_plugin_path();
|
nkeynes@1027 | 152 | }
|
nkeynes@1027 | 153 |
|
nkeynes@1075 | 154 | DEBUG( "Plugin directory: %s", path );
|
nkeynes@1027 | 155 | return plugin_load_all( path );
|
nkeynes@1027 | 156 | }
|