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@1024 | 23 | #include <glib/gmem.h>
|
nkeynes@1024 | 24 | #include <glib/gstrfuncs.h>
|
nkeynes@1024 | 25 | #include "plugin.h"
|
nkeynes@1041 | 26 | #include "lxpaths.h"
|
nkeynes@1024 | 27 |
|
nkeynes@1024 | 28 | #ifdef APPLE_BUILD
|
nkeynes@1024 | 29 | #define SOEXT ".dylib"
|
nkeynes@1024 | 30 | #else
|
nkeynes@1024 | 31 | #define SOEXT ".so"
|
nkeynes@1024 | 32 | #endif
|
nkeynes@1024 | 33 |
|
nkeynes@1027 | 34 | /** Dummy plugin used as a plugin directory marker */
|
nkeynes@1027 | 35 | #define DUMMY_PLUGIN ("lxdream_dummy" SOEXT)
|
nkeynes@1024 | 36 |
|
nkeynes@1027 | 37 | const char *plugin_type_string[] = { "undefined", "audio driver", "input driver" };
|
nkeynes@1027 | 38 |
|
nkeynes@1027 | 39 | int main(int argc, char *argv[]);
|
nkeynes@1027 | 40 | static const char *exec_path = NULL;
|
nkeynes@1027 | 41 |
|
nkeynes@1027 | 42 | /**
|
nkeynes@1027 | 43 | * Return the full path to the main binary
|
nkeynes@1027 | 44 | */
|
nkeynes@1027 | 45 | static const char *get_exec_path()
|
nkeynes@1027 | 46 | {
|
nkeynes@1027 | 47 | if( exec_path == NULL ) {
|
nkeynes@1027 | 48 | Dl_info dli;
|
nkeynes@1027 | 49 |
|
nkeynes@1027 | 50 | /* Use dladdr for this, since it should be available for any platform
|
nkeynes@1027 | 51 | * that we can support plugins on at all.
|
nkeynes@1027 | 52 | */
|
nkeynes@1027 | 53 | if( dladdr( main, &dli) ) {
|
nkeynes@1027 | 54 | gchar *path = g_strdup( dli.dli_fname );
|
nkeynes@1027 | 55 | char *i = strrchr( path, '/' );
|
nkeynes@1027 | 56 | if( i > path ) {
|
nkeynes@1027 | 57 | *i = '\0';
|
nkeynes@1027 | 58 | exec_path = path;
|
nkeynes@1027 | 59 | } else {
|
nkeynes@1027 | 60 | g_free(path);
|
nkeynes@1027 | 61 | }
|
nkeynes@1027 | 62 | }
|
nkeynes@1027 | 63 | }
|
nkeynes@1027 | 64 | return exec_path;
|
nkeynes@1027 | 65 | }
|
nkeynes@1027 | 66 |
|
nkeynes@1027 | 67 | static gboolean plugin_load( const gchar *plugin_path )
|
nkeynes@1024 | 68 | {
|
nkeynes@1024 | 69 | void *so = dlopen(plugin_path, RTLD_NOW|RTLD_LOCAL);
|
nkeynes@1024 | 70 | if( so == NULL ) {
|
nkeynes@1024 | 71 | WARN("Failed to load plugin '%s': %s", plugin_path, dlerror());
|
nkeynes@1024 | 72 | return FALSE;
|
nkeynes@1024 | 73 | }
|
nkeynes@1024 | 74 |
|
nkeynes@1024 | 75 | struct plugin_struct *plugin = (struct plugin_struct *)dlsym(so,"lxdream_plugin_entry");
|
nkeynes@1024 | 76 | if( plugin == NULL ) {
|
nkeynes@1024 | 77 | WARN("Failed to load plugin: '%s': Not an lxdream plugin", plugin_path);
|
nkeynes@1024 | 78 | dlclose(so);
|
nkeynes@1024 | 79 | return FALSE;
|
nkeynes@1024 | 80 | }
|
nkeynes@1024 | 81 |
|
nkeynes@1024 | 82 | if( strcmp(lxdream_short_version, plugin->version) != 0 ) {
|
nkeynes@1024 | 83 | WARN("Failed to load plugin: '%s': Incompatible version (%s)", plugin_path, plugin->version);
|
nkeynes@1024 | 84 | dlclose(so);
|
nkeynes@1024 | 85 | return FALSE;
|
nkeynes@1024 | 86 | }
|
nkeynes@1024 | 87 |
|
nkeynes@1027 | 88 | if( plugin->type == PLUGIN_NONE ) {
|
nkeynes@1027 | 89 | /* 'dummy' plugin - we don't actually want to load it */
|
nkeynes@1027 | 90 | dlclose(so);
|
nkeynes@1027 | 91 | return FALSE;
|
nkeynes@1027 | 92 | }
|
nkeynes@1027 | 93 |
|
nkeynes@1024 | 94 | if( plugin->type < PLUGIN_MIN_TYPE || plugin->type > PLUGIN_MAX_TYPE ) {
|
nkeynes@1024 | 95 | WARN("Failed to load plugin: '%s': Unrecognized plugin type (%d)", plugin_path, plugin->type );
|
nkeynes@1024 | 96 | dlclose(so);
|
nkeynes@1024 | 97 | return FALSE;
|
nkeynes@1024 | 98 | }
|
nkeynes@1024 | 99 |
|
nkeynes@1024 | 100 | if( plugin->register_plugin() == FALSE ) {
|
nkeynes@1024 | 101 | WARN("Failed to load plugin: '%s': Initialization failed", plugin_path);
|
nkeynes@1024 | 102 | dlclose(so);
|
nkeynes@1024 | 103 | return FALSE;
|
nkeynes@1024 | 104 | }
|
nkeynes@1027 | 105 | INFO("Loaded %s '%s'", plugin_type_string[plugin->type], plugin->name);
|
nkeynes@1024 | 106 | return TRUE;
|
nkeynes@1024 | 107 | }
|
nkeynes@1024 | 108 |
|
nkeynes@1027 | 109 | static gboolean has_plugins( const gchar *path )
|
nkeynes@1027 | 110 | {
|
nkeynes@1027 | 111 | struct stat st;
|
nkeynes@1027 | 112 |
|
nkeynes@1027 | 113 | gchar *dummy_name = g_strdup_printf( "%s/%s", path, DUMMY_PLUGIN );
|
nkeynes@1027 | 114 | if( stat( dummy_name, &st ) == 0 ) {
|
nkeynes@1027 | 115 | return TRUE;
|
nkeynes@1027 | 116 | } else {
|
nkeynes@1027 | 117 | return FALSE;
|
nkeynes@1027 | 118 | }
|
nkeynes@1027 | 119 | }
|
nkeynes@1027 | 120 |
|
nkeynes@1024 | 121 | /**
|
nkeynes@1024 | 122 | * Scan the plugin dir and load all valid plugins.
|
nkeynes@1024 | 123 | */
|
nkeynes@1027 | 124 | static int plugin_load_all( const gchar *plugin_dir )
|
nkeynes@1024 | 125 | {
|
nkeynes@1024 | 126 | int plugin_count;
|
nkeynes@1024 | 127 | struct dirent *ent;
|
nkeynes@1027 | 128 |
|
nkeynes@1024 | 129 | DIR *dir = opendir(plugin_dir);
|
nkeynes@1024 | 130 | if( dir == NULL ) {
|
nkeynes@1024 | 131 | WARN( "Unable to open plugin directory '%s'", plugin_dir );
|
nkeynes@1024 | 132 | return 0;
|
nkeynes@1024 | 133 | }
|
nkeynes@1024 | 134 |
|
nkeynes@1024 | 135 | while( (ent = readdir(dir)) != NULL ) {
|
nkeynes@1024 | 136 | const char *ext = strrchr(ent->d_name, '.');
|
nkeynes@1024 | 137 | if( ext != NULL && strcasecmp(SOEXT,ext) == 0 ) {
|
nkeynes@1024 | 138 | char *libname = g_strdup_printf( "%s/%s", plugin_dir,ent->d_name );
|
nkeynes@1024 | 139 | if( plugin_load( libname ) ) {
|
nkeynes@1024 | 140 | plugin_count++;
|
nkeynes@1024 | 141 | }
|
nkeynes@1024 | 142 | g_free(libname);
|
nkeynes@1024 | 143 | }
|
nkeynes@1024 | 144 | }
|
nkeynes@1024 | 145 | return plugin_count;
|
nkeynes@1024 | 146 | }
|
nkeynes@1027 | 147 |
|
nkeynes@1027 | 148 | int plugin_init()
|
nkeynes@1027 | 149 | {
|
nkeynes@1027 | 150 | const char *path = get_exec_path();
|
nkeynes@1027 | 151 | if( path == NULL || !has_plugins(path) ) {
|
nkeynes@1027 | 152 | path = get_plugin_path();
|
nkeynes@1027 | 153 | }
|
nkeynes@1027 | 154 |
|
nkeynes@1027 | 155 | INFO( "Plugin directory: %s", path );
|
nkeynes@1027 | 156 | return plugin_load_all( path );
|
nkeynes@1027 | 157 | }
|