2009-08-17 17:58:06 +04:00
/*
Copyright Red Hat , Inc . 2002 - 2004 , 2009
The Magma Cluster API Library is free software ; you can redistribute
it and / or modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either version
2.1 of the License , or ( at your option ) any later version .
The Magma Cluster API Library 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
*/
/** @file
* Plugin loading routines
*/
# include <dlfcn.h>
# include <stdlib.h>
# include <errno.h>
2009-08-12 21:20:50 +04:00
# include <stdio.h>
2009-08-17 17:58:06 +04:00
# include <sys/types.h>
# include <sys/stat.h>
# include <unistd.h>
2009-09-15 18:28:20 +04:00
# include <stdint.h>
2009-08-12 21:20:50 +04:00
# include <list.h>
2009-08-18 00:53:58 +04:00
# include <simpleconfig.h>
2009-08-12 21:20:50 +04:00
# include <server_plugin.h>
# include <malloc.h>
# include <string.h>
2009-09-02 02:54:17 +04:00
# include <dirent.h>
2009-08-12 21:20:50 +04:00
typedef struct _plugin_list {
list_head ( ) ;
2009-09-02 00:22:30 +04:00
const listener_plugin_t * listener ;
const backend_plugin_t * backend ;
plugin_type_t type ;
2009-08-12 21:20:50 +04:00
} plugin_list_t ;
static plugin_list_t * server_plugins = NULL ;
2009-09-02 02:54:17 +04:00
2009-08-17 17:58:06 +04:00
int
2009-09-02 00:22:30 +04:00
plugin_reg_backend ( const backend_plugin_t * plugin )
2009-08-12 21:20:50 +04:00
{
plugin_list_t * newplug ;
2009-09-02 02:54:17 +04:00
if ( plugin_find_backend ( plugin - > name ) ) {
errno = EEXIST ;
return - 1 ;
}
2009-08-12 21:20:50 +04:00
newplug = malloc ( sizeof ( * newplug ) ) ;
2009-08-17 17:58:06 +04:00
if ( ! newplug )
return - 1 ;
2009-08-12 21:20:50 +04:00
memset ( newplug , 0 , sizeof ( * newplug ) ) ;
2009-09-02 00:22:30 +04:00
newplug - > backend = plugin ;
newplug - > type = PLUGIN_BACKEND ;
list_insert ( & server_plugins , newplug ) ;
return 0 ;
}
int
plugin_reg_listener ( const listener_plugin_t * plugin )
{
plugin_list_t * newplug ;
2009-09-02 02:54:17 +04:00
if ( plugin_find_listener ( plugin - > name ) ) {
errno = EEXIST ;
return - 1 ;
}
2009-09-02 00:22:30 +04:00
newplug = malloc ( sizeof ( * newplug ) ) ;
if ( ! newplug )
return - 1 ;
memset ( newplug , 0 , sizeof ( * newplug ) ) ;
newplug - > listener = plugin ;
newplug - > type = PLUGIN_LISTENER ;
2009-08-12 21:20:50 +04:00
list_insert ( & server_plugins , newplug ) ;
2009-08-17 17:58:06 +04:00
return 0 ;
2009-08-12 21:20:50 +04:00
}
2009-09-02 00:22:30 +04:00
2009-08-12 21:20:50 +04:00
void
plugin_dump ( void )
{
plugin_list_t * p ;
int x ;
list_for ( & server_plugins , p , x ) {
2009-09-02 00:22:30 +04:00
if ( p - > type = = PLUGIN_BACKEND ) {
printf ( " backend: %s %s \n " ,
p - > backend - > name , p - > backend - > version ) ;
} else if ( p - > type = = PLUGIN_LISTENER ) {
printf ( " listener: %s %s \n " ,
p - > listener - > name , p - > listener - > version ) ;
}
2009-08-12 21:20:50 +04:00
}
}
2009-09-02 02:54:17 +04:00
2009-09-02 00:22:30 +04:00
const backend_plugin_t *
plugin_find_backend ( const char * name )
2009-08-12 21:20:50 +04:00
{
plugin_list_t * p ;
int x ;
list_for ( & server_plugins , p , x ) {
2009-09-02 00:22:30 +04:00
if ( p - > type ! = PLUGIN_BACKEND )
continue ;
if ( ! strcasecmp ( name , p - > backend - > name ) )
return p - > backend ;
2009-08-12 21:20:50 +04:00
}
return NULL ;
}
2009-09-02 00:22:30 +04:00
const listener_plugin_t *
plugin_find_listener ( const char * name )
2009-08-12 21:20:50 +04:00
{
2009-09-02 00:22:30 +04:00
plugin_list_t * p ;
int x ;
2009-08-12 21:20:50 +04:00
2009-09-02 00:22:30 +04:00
list_for ( & server_plugins , p , x ) {
if ( p - > type ! = PLUGIN_LISTENER )
continue ;
if ( ! strcasecmp ( name , p - > listener - > name ) )
return p - > listener ;
}
2009-08-12 21:20:50 +04:00
2009-09-02 00:22:30 +04:00
return NULL ;
2009-08-12 21:20:50 +04:00
}
2009-09-02 00:22:30 +04:00
2009-09-02 02:54:17 +04:00
static int
backend_plugin_load ( void * handle , const char * libpath )
{
const backend_plugin_t * plug = NULL ;
double ( * modversion ) ( void ) ;
backend_plugin_t * ( * modinfo ) ( void ) ;
modversion = dlsym ( handle , BACKEND_VER_STR ) ;
if ( ! modversion ) {
# ifdef DEBUG
printf ( " Failed to map %s \n " , BACKEND_VER_STR ) ;
# endif
errno = EINVAL ;
return - 1 ;
}
if ( modversion ( ) ! = PLUGIN_VERSION_BACKEND ) {
# ifdef DEBUG
printf ( " API version mismatch in %s: \n "
" %f expected; %f received. \n " , libpath ,
PLUGIN_VERSION_BACKEND , modversion ( ) ) ;
# endif
errno = EINVAL ;
return - 1 ;
}
modinfo = dlsym ( handle , BACKEND_INFO_STR ) ;
if ( ! modinfo ) {
# ifdef DEBUG
printf ( " Failed to map %s \n " , BACKEND_INFO_STR ) ;
# endif
errno = EINVAL ;
return - 1 ;
}
plug = modinfo ( ) ;
if ( plugin_reg_backend ( plug ) < 0 ) {
errno = EINVAL ;
return - 1 ;
}
# ifdef DEBUG
else {
printf ( " Registered backend plugin %s %s \n " ,
plug - > name , plug - > version ) ;
}
# endif
return 0 ;
}
static int
listener_plugin_load ( void * handle , const char * libpath )
{
const listener_plugin_t * plug = NULL ;
double ( * modversion ) ( void ) ;
listener_plugin_t * ( * modinfo ) ( void ) ;
modversion = dlsym ( handle , LISTENER_VER_STR ) ;
if ( ! modversion ) {
# ifdef DEBUG
printf ( " Failed to map %s \n " , LISTENER_VER_STR ) ;
# endif
errno = EINVAL ;
return - 1 ;
}
if ( modversion ( ) ! = PLUGIN_VERSION_LISTENER ) {
# ifdef DEBUG
printf ( " API version mismatch in %s: \n "
" %f expected; %f received. \n " , libpath ,
PLUGIN_VERSION_LISTENER , modversion ( ) ) ;
# endif
dlclose ( handle ) ;
errno = EINVAL ;
return - 1 ;
}
modinfo = dlsym ( handle , LISTENER_INFO_STR ) ;
if ( ! modinfo ) {
# ifdef DEBUG
printf ( " Failed to map %s \n " , LISTENER_INFO_STR ) ;
# endif
errno = EINVAL ;
return - 1 ;
}
plug = modinfo ( ) ;
if ( plugin_reg_listener ( plug ) < 0 ) {
errno = EINVAL ;
return - 1 ;
}
# ifdef DEBUG
else {
printf ( " Registered listener plugin %s %s \n " ,
plug - > name , plug - > version ) ;
}
# endif
return 0 ;
}
2009-08-17 17:58:06 +04:00
/**
* Load a cluster plugin . so file and map all the functions
2009-09-02 00:22:30 +04:00
* provided to entries in a backend_plugin_t structure .
2009-08-17 17:58:06 +04:00
*
* @ param libpath Path to file .
* @ return NULL on failure , or plugin - specific
2009-09-02 00:22:30 +04:00
* ( const ) backend_plugin_t * structure on
2009-08-17 17:58:06 +04:00
* success .
*/
int
plugin_load ( const char * libpath )
{
void * handle = NULL ;
struct stat sb ;
errno = 0 ;
2009-08-12 21:20:50 +04:00
2009-08-17 17:58:06 +04:00
if ( ! libpath ) {
errno = EINVAL ;
return - 1 ;
}
if ( stat ( libpath , & sb ) ! = 0 ) {
return - 1 ;
}
/*
If it ' s not owner - readable or it ' s a directory ,
ignore / fail . Thus , a user may change the permission of
a plugin " u-r " and this would then prevent magma apps
from loading it .
*/
if ( S_ISDIR ( sb . st_mode ) ) {
errno = EISDIR ;
return - 1 ;
}
if ( ! ( sb . st_mode & S_IRUSR ) ) {
# ifdef DEBUG
printf ( " Ignoring %s (User-readable bit not set) \n " ,
libpath ) ;
# endif
errno = EPERM ;
return - 1 ;
}
2009-09-02 02:54:17 +04:00
# ifdef DEBUG
printf ( " Loading plugin from %s \n " , libpath ) ;
# endif
2009-08-17 17:58:06 +04:00
handle = dlopen ( libpath , RTLD_LAZY ) ;
if ( ! handle ) {
errno = ELIBACC ;
return - 1 ;
}
2009-09-02 02:54:17 +04:00
if ( ! backend_plugin_load ( handle , libpath ) | |
! listener_plugin_load ( handle , libpath ) )
return 0 ;
dlclose ( handle ) ;
errno = EINVAL ;
return - 1 ;
}
/**
Free up a null - terminated array of strings
*/
static void
free_dirnames ( char * * dirnames )
{
int x = 0 ;
for ( ; dirnames [ x ] ; x + + )
free ( dirnames [ x ] ) ;
free ( dirnames ) ;
}
static int
_compare ( const void * a , const void * b )
{
return strcmp ( ( const char * ) a , ( const char * ) b ) ;
}
/**
Read all entries in a directory and return them in a NULL - terminated ,
sorted array .
*/
static int
read_dirnames_sorted ( const char * directory , char * * * dirnames )
{
DIR * dir ;
struct dirent * entry ;
char filename [ 1024 ] ;
int count = 0 , x = 0 ;
dir = opendir ( directory ) ;
if ( ! dir )
return - 1 ;
/* Count the number of plugins */
while ( ( entry = readdir ( dir ) ) ! = NULL )
+ + count ;
/* Malloc the entries */
* dirnames = malloc ( sizeof ( char * ) * ( count + 1 ) ) ;
if ( ! * dirnames ) {
2009-08-17 17:58:06 +04:00
# ifdef DEBUG
2009-09-02 02:54:17 +04:00
printf ( " %s: Failed to malloc %d bytes " ,
__FUNCTION__ , ( int ) ( sizeof ( char * ) * ( count + 1 ) ) ) ;
2009-08-17 17:58:06 +04:00
# endif
2009-09-02 02:54:17 +04:00
closedir ( dir ) ;
errno = ENOMEM ;
2009-08-17 17:58:06 +04:00
return - 1 ;
}
2009-09-02 02:54:17 +04:00
memset ( * dirnames , 0 , sizeof ( char * ) * ( count + 1 ) ) ;
rewinddir ( dir ) ;
2009-08-17 17:58:06 +04:00
2009-09-02 02:54:17 +04:00
/* Store the directory names. */
while ( ( entry = readdir ( dir ) ) ! = NULL ) {
snprintf ( filename , sizeof ( filename ) , " %s/%s " , directory ,
entry - > d_name ) ;
( * dirnames ) [ x ] = strdup ( filename ) ;
if ( ! ( * dirnames ) [ x ] ) {
2009-08-17 17:58:06 +04:00
# ifdef DEBUG
2009-09-02 02:54:17 +04:00
printf ( " Failed to duplicate %s \n " ,
filename ) ;
2009-08-17 17:58:06 +04:00
# endif
2009-09-02 02:54:17 +04:00
free_dirnames ( * dirnames ) ;
closedir ( dir ) ;
errno = ENOMEM ;
return - 1 ;
}
+ + x ;
2009-08-17 17:58:06 +04:00
}
2009-09-02 02:54:17 +04:00
closedir ( dir ) ;
/* Sort the directory names. */
qsort ( ( * dirnames ) , count , sizeof ( char * ) , _compare ) ;
return 0 ;
}
/**
*/
int
plugin_search ( const char * pathname )
{
int found = 0 ;
int fcount = 0 ;
char * * filenames ;
2009-08-17 17:58:06 +04:00
# ifdef DEBUG
2009-09-02 02:54:17 +04:00
printf ( " Trying plugins in %s \n " , pathname ) ;
2009-08-17 17:58:06 +04:00
# endif
2009-09-02 02:54:17 +04:00
if ( read_dirnames_sorted ( pathname , & filenames ) ! = 0 ) {
2009-08-17 17:58:06 +04:00
return - 1 ;
}
2009-09-02 02:54:17 +04:00
for ( fcount = 0 ; filenames [ fcount ] ; fcount + + ) {
if ( plugin_load ( filenames [ fcount ] ) = = 0 )
+ + found ;
}
free_dirnames ( filenames ) ;
if ( ! found ) {
# ifdef DEBUG
printf ( " No usable plugins found. \n " ) ;
# endif
errno = ELIBACC ;
2009-08-17 17:58:06 +04:00
return - 1 ;
}
2009-09-02 02:54:17 +04:00
return found ;
2009-08-17 17:58:06 +04:00
}
2009-09-02 02:54:17 +04:00