modules.c 29.7 KB
Newer Older
Sam Hocevar's avatar
 
Sam Hocevar committed
1
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
2
 * modules.c : Built-in and plugin modules management functions
Sam Hocevar's avatar
 
Sam Hocevar committed
3 4
 *****************************************************************************
 * Copyright (C) 2001 VideoLAN
Sam Hocevar's avatar
 
Sam Hocevar committed
5
 * $Id: modules.c,v 1.23 2001/04/15 04:19:58 sam Exp $
Sam Hocevar's avatar
 
Sam Hocevar committed
6 7
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
Sam Hocevar's avatar
 
Sam Hocevar committed
8
 *          Ethan C. Baldridge <BaldridgeE@cadmus.com>
Sam Hocevar's avatar
 
Sam Hocevar committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/
#include "defs.h"

#include "config.h"

Sam Hocevar's avatar
 
Sam Hocevar committed
28 29 30 31 32 33
/* Some faulty libcs have a broken struct dirent when _FILE_OFFSET_BITS
 * is set to 64. Don't try to be cleverer. */
#ifdef _FILE_OFFSET_BITS
#undef _FILE_OFFSET_BITS
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
34 35
#include <stdlib.h>                                      /* free(), strtol() */
#include <stdio.h>                                              /* sprintf() */
Sam Hocevar's avatar
 
Sam Hocevar committed
36 37
#include <string.h>                                              /* strdup() */
#include <dirent.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
38 39

#if defined(HAVE_DLFCN_H)                                /* Linux, BSD, Hurd */
Sam Hocevar's avatar
 
Sam Hocevar committed
40
#   include <dlfcn.h>                        /* dlopen(), dlsym(), dlclose() */
Sam Hocevar's avatar
 
Sam Hocevar committed
41
#   define HAVE_DYNAMIC_PLUGINS
Sam Hocevar's avatar
 
Sam Hocevar committed
42
#elif defined(HAVE_IMAGE_H)                                          /* BeOS */
Sam Hocevar's avatar
 
Sam Hocevar committed
43
#   include <image.h>
Sam Hocevar's avatar
 
Sam Hocevar committed
44
#   define HAVE_DYNAMIC_PLUGINS
Sam Hocevar's avatar
 
Sam Hocevar committed
45
#else
Sam Hocevar's avatar
 
Sam Hocevar committed
46
#   undef HAVE_DYNAMIC_PLUGINS
Sam Hocevar's avatar
 
Sam Hocevar committed
47 48 49
#endif

#ifdef SYS_BEOS
Sam Hocevar's avatar
 
Sam Hocevar committed
50
#   include "beos_specific.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
51 52
#endif

53
#ifdef SYS_DARWIN1_3
Sam Hocevar's avatar
 
Sam Hocevar committed
54
#   include "darwin_specific.h"
55 56
#endif

Sam Hocevar's avatar
 
Sam Hocevar committed
57 58 59
#include "common.h"
#include "threads.h"

Sam Hocevar's avatar
 
Sam Hocevar committed
60 61
#include "intf_msg.h"
#include "modules.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
62
#include "modules_builtin.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
63
#include "modules_core.h"
Sam Hocevar's avatar
 
Sam Hocevar committed
64 65

/* Local prototypes */
Sam Hocevar's avatar
 
Sam Hocevar committed
66 67 68 69 70 71 72 73 74 75 76 77 78 79
#ifdef HAVE_DYNAMIC_PLUGINS
static int AllocatePluginModule ( module_bank_t *, char * );
#endif
static int AllocateBuiltinModule( module_bank_t *,
                                  int ( * ) ( module_t * ),
                                  int ( * ) ( module_t * ),
                                  int ( * ) ( module_t * ) );
static int FreeModule   ( module_bank_t * p_bank, module_t * );
static int LockModule   ( module_t * );
static int UnlockModule ( module_t * );
#ifdef HAVE_DYNAMIC_PLUGINS
static int HideModule   ( module_t * );
static int CallSymbol   ( module_t *, char * );
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
80 81

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
82
 * module_CreateBank: create the module bank.
Sam Hocevar's avatar
 
Sam Hocevar committed
83
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
84
 * This function creates a module bank structure.
Sam Hocevar's avatar
 
Sam Hocevar committed
85
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
86
module_bank_t * module_CreateBank( void )
Sam Hocevar's avatar
 
Sam Hocevar committed
87 88 89 90 91 92 93 94
{
    module_bank_t * p_bank;

    p_bank = malloc( sizeof( module_bank_t ) );

    return( p_bank );
}

Sam Hocevar's avatar
 
Sam Hocevar committed
95 96 97 98
/*****************************************************************************
 * module_InitBank: create the module bank.
 *****************************************************************************
 * This function creates a module bank structure and fills it with the
Sam Hocevar's avatar
 
Sam Hocevar committed
99
 * built-in modules, as well as all the plugin modules it can find.
Sam Hocevar's avatar
 
Sam Hocevar committed
100 101 102
 *****************************************************************************/
void module_InitBank( module_bank_t * p_bank )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
103
#ifdef HAVE_DYNAMIC_PLUGINS
104
    static char * path[] = { ".", "lib", PLUGIN_PATH, NULL, NULL };
Sam Hocevar's avatar
 
Sam Hocevar committed
105 106

    char **         ppsz_path = path;
Sam Hocevar's avatar
 
Sam Hocevar committed
107
    char *          psz_fullpath;
Sam Hocevar's avatar
 
Sam Hocevar committed
108
    char *          psz_file;
Sam Hocevar's avatar
 
Sam Hocevar committed
109 110
#if defined( SYS_BEOS ) || defined( SYS_DARWIN1_3 )
    char *          psz_vlcpath = system_GetProgramPath();
Jean-Marc Dressler's avatar
 
Jean-Marc Dressler committed
111
    int             i_vlclen = strlen( psz_vlcpath );
Sam Hocevar's avatar
 
Sam Hocevar committed
112
    boolean_t       b_notinroot;
Sam Hocevar's avatar
 
Sam Hocevar committed
113 114 115
#endif
    DIR *           dir;
    struct dirent * file;
Sam Hocevar's avatar
 
Sam Hocevar committed
116
#endif /* HAVE_DYNAMIC_PLUGINS */
Sam Hocevar's avatar
 
Sam Hocevar committed
117 118 119 120

    p_bank->first = NULL;
    vlc_mutex_init( &p_bank->lock );

Sam Hocevar's avatar
 
Sam Hocevar committed
121
    intf_WarnMsg( 1, "module: module bank initialized" );
Sam Hocevar's avatar
 
Sam Hocevar committed
122

Sam Hocevar's avatar
 
Sam Hocevar committed
123 124 125 126 127 128 129
    intf_WarnMsg( 2, "module: checking built-in modules" );

    ALLOCATE_ALL_BUILTINS();

#ifdef HAVE_DYNAMIC_PLUGINS
    intf_WarnMsg( 2, "module: checking plugin modules" );

Sam Hocevar's avatar
 
Sam Hocevar committed
130 131
    for( ; *ppsz_path != NULL ; ppsz_path++ )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
132 133 134
        /* Store strlen(*ppsz_path) for later use. */
        int i_dirlen = strlen( *ppsz_path );

Sam Hocevar's avatar
 
Sam Hocevar committed
135
#if defined( SYS_BEOS ) || defined( SYS_DARWIN1_3 )
Sam Hocevar's avatar
 
Sam Hocevar committed
136 137 138 139
        b_notinroot = 0;
        /* Under BeOS, we need to add beos_GetProgramPath() to access
         * files under the current directory */
        if( ( i_dirlen > 1 ) && strncmp( *ppsz_path, "/", 1 ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
140
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
141 142
            i_dirlen += i_vlclen + 2;
            b_notinroot = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
143

Sam Hocevar's avatar
 
Sam Hocevar committed
144 145 146 147 148 149 150 151 152 153 154 155 156
            psz_fullpath = malloc( i_dirlen );
            if( psz_fullpath == NULL )
            {
                continue;
            }
            sprintf( psz_fullpath, "%s/%s", psz_vlcpath, *ppsz_path );
        }
        else
#endif
        {
            psz_fullpath = *ppsz_path;
        }

Sam Hocevar's avatar
 
Sam Hocevar committed
157
        intf_WarnMsgImm( 3, "module: browsing `%s'", psz_fullpath );
Sam Hocevar's avatar
 
Sam Hocevar committed
158 159 160

        if( (dir = opendir( psz_fullpath )) )
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
161 162 163
            /* Parse the directory and try to load all files it contains. */
            while( (file = readdir( dir )) )
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
164 165 166 167
                int i_filelen = strlen( file->d_name );

                /* We only load files ending with ".so" */
                if( i_filelen > 3
168
                        && !strncmp( file->d_name + i_filelen - 3, ".so", 3 ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
169
                {
Sam Hocevar's avatar
 
Sam Hocevar committed
170 171
                    psz_file = malloc( i_dirlen + i_filelen + 2 );
                    if( psz_file == NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
172
                    {
Sam Hocevar's avatar
 
Sam Hocevar committed
173
                        continue;
Sam Hocevar's avatar
 
Sam Hocevar committed
174
                    }
Sam Hocevar's avatar
 
Sam Hocevar committed
175 176
                    sprintf( psz_file, "%s/%s", psz_fullpath, file->d_name );

Sam Hocevar's avatar
 
Sam Hocevar committed
177
                    /* We created a nice filename -- now we just try to load
Sam Hocevar's avatar
 
Sam Hocevar committed
178 179
                     * it as a plugin module. */
                    AllocatePluginModule( p_bank, psz_file );
Sam Hocevar's avatar
 
Sam Hocevar committed
180 181 182 183 184

                    /* We don't care if the allocation succeeded */
                    free( psz_file );
                }
            }
Sam Hocevar's avatar
 
Sam Hocevar committed
185 186 187

            /* Close the directory if successfully opened */
            closedir( dir );
Sam Hocevar's avatar
 
Sam Hocevar committed
188
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
189

Sam Hocevar's avatar
 
Sam Hocevar committed
190
#if defined( SYS_BEOS ) || defined( SYS_DARWIN1_3 )
Sam Hocevar's avatar
 
Sam Hocevar committed
191 192 193 194 195
        if( b_notinroot )
        {
            free( psz_fullpath );
        }
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
196
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
197
#endif /* HAVE_DYNAMIC_PLUGINS */
Sam Hocevar's avatar
 
Sam Hocevar committed
198 199 200 201

    return;
}

Sam Hocevar's avatar
 
Sam Hocevar committed
202 203 204
/*****************************************************************************
 * module_DestroyBank: destroy the module bank.
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
205
 * This function unloads all unused plugin modules and removes the module
Sam Hocevar's avatar
 
Sam Hocevar committed
206 207
 * bank in case of success.
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
208
void module_DestroyBank( module_bank_t * p_bank )
Sam Hocevar's avatar
 
Sam Hocevar committed
209
{
Sam Hocevar's avatar
 
Sam Hocevar committed
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    module_t * p_next;

    while( p_bank->first != NULL )
    {
        if( FreeModule( p_bank, p_bank->first ) )
        {
            /* Module deletion failed */
            intf_ErrMsg( "module error: `%s' can't be removed. trying harder.",
                         p_bank->first->psz_name );

            /* We just free the module by hand. Niahahahahaha. */
            p_next = p_bank->first->next;
            free(p_bank->first);
            p_bank->first = p_next;
        }
    }

Henri Fallon's avatar
 
Henri Fallon committed
227
    /* Destroy the lock */
Henri Fallon's avatar
 
Henri Fallon committed
228 229
    vlc_mutex_destroy( &p_bank->lock );
    
Sam Hocevar's avatar
 
Sam Hocevar committed
230 231 232 233
    /* We can free the module bank */
    free( p_bank );

    return;
Sam Hocevar's avatar
 
Sam Hocevar committed
234 235 236 237 238
}

/*****************************************************************************
 * module_ResetBank: reset the module bank.
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
239
 * This function resets the module bank by unloading all unused plugin
Sam Hocevar's avatar
 
Sam Hocevar committed
240 241
 * modules.
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
242
void module_ResetBank( module_bank_t * p_bank )
Sam Hocevar's avatar
 
Sam Hocevar committed
243 244
{
    intf_ErrMsg( "FIXME: module_ResetBank unimplemented" );
Sam Hocevar's avatar
 
Sam Hocevar committed
245
    return;
Sam Hocevar's avatar
 
Sam Hocevar committed
246 247 248 249 250 251 252 253 254 255
}

/*****************************************************************************
 * module_ManageBank: manage the module bank.
 *****************************************************************************
 * This function parses the module bank and hides modules that have been
 * unused for a while.
 *****************************************************************************/
void module_ManageBank( module_bank_t * p_bank )
{
Sam Hocevar's avatar
 
Sam Hocevar committed
256
#ifdef HAVE_DYNAMIC_PLUGINS
Sam Hocevar's avatar
 
Sam Hocevar committed
257 258 259 260 261 262 263 264 265 266
    module_t * p_module;

    /* We take the global lock */
    vlc_mutex_lock( &p_bank->lock );

    /* Parse the module list to see if any modules need to be unloaded */
    for( p_module = p_bank->first ;
         p_module != NULL ;
         p_module = p_module->next )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
267
        /* If the module is unused and if it is a plugin module... */
Sam Hocevar's avatar
 
Sam Hocevar committed
268 269 270 271 272 273 274 275
        if( p_module->i_usage == 0 && !p_module->b_builtin )
        {
            if( p_module->i_unused_delay < MODULE_HIDE_DELAY )
            {
                p_module->i_unused_delay++;
            }
            else
            {
Sam Hocevar's avatar
 
Sam Hocevar committed
276
                intf_WarnMsg( 3, "module: hiding unused module `%s'",
277
                              p_module->psz_name );
Sam Hocevar's avatar
 
Sam Hocevar committed
278 279 280 281 282 283
                HideModule( p_module );
            }
        }
    }

    /* We release the global lock */
Sam Hocevar's avatar
 
Sam Hocevar committed
284
    vlc_mutex_unlock( &p_bank->lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
285
#endif /* HAVE_DYNAMIC_PLUGINS */
Sam Hocevar's avatar
 
Sam Hocevar committed
286 287 288 289 290

    return;
}

/*****************************************************************************
291
 * module_Need: return the best module function, given a capability list.
Sam Hocevar's avatar
 
Sam Hocevar committed
292
 *****************************************************************************
293
 * This function returns the module that best fits the asked capabilities.
Sam Hocevar's avatar
 
Sam Hocevar committed
294
 *****************************************************************************/
295 296
module_t * module_Need( module_bank_t *p_bank,
                        int i_capabilities, void *p_data )
Sam Hocevar's avatar
 
Sam Hocevar committed
297
{
298 299 300 301
    module_t * p_module;
    module_t * p_bestmodule = NULL;
    int i_score, i_totalscore, i_bestscore = 0;
    int i_index;
Sam Hocevar's avatar
 
Sam Hocevar committed
302

303 304
    /* We take the global lock */
    vlc_mutex_lock( &p_bank->lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
305

306 307 308 309
    /* Parse the module list for capabilities and probe each of them */
    for( p_module = p_bank->first ;
         p_module != NULL ;
         p_module = p_module->next )
Sam Hocevar's avatar
 
Sam Hocevar committed
310
    {
311 312 313 314
        /* Test that this module can do everything we need */
        if( ( p_module->i_capabilities & i_capabilities ) == i_capabilities )
        {
            i_totalscore = 0;
Sam Hocevar's avatar
 
Sam Hocevar committed
315

316
            LockModule( p_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
317

318 319 320 321 322 323
            /* Parse all the requested capabilities and test them */
            for( i_index = 0 ; (1 << i_index) <= i_capabilities ; i_index++ )
            {
                if( ( (1 << i_index) & i_capabilities ) )
                {
                    i_score = ( (function_list_t *)p_module->p_functions)
Sam Hocevar's avatar
 
Sam Hocevar committed
324
                                                  [i_index].pf_probe( p_data );
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355

                    if( i_score )
                    {
                        i_totalscore += i_score;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            /* If the high score was broken, we have a new champion */
            if( i_totalscore > i_bestscore )
            {
                /* Keep the current module locked, but release the previous */
                if( p_bestmodule != NULL )
                {
                    UnlockModule( p_bestmodule );
                }

                /* This is the new best module */
                i_bestscore = i_totalscore;
                p_bestmodule = p_module;
            }
            else
            {
                /* This module wasn't interesting, unlock it and forget it */
                UnlockModule( p_module );
            }
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
356 357
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
358
    /* We can release the global lock, module refcount was incremented */
359
    vlc_mutex_unlock( &p_bank->lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
360

Sam Hocevar's avatar
 
Sam Hocevar committed
361 362
    if( p_bestmodule != NULL )
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
363
        intf_WarnMsg( 3, "module: locking module `%s'",
Sam Hocevar's avatar
 
Sam Hocevar committed
364 365
                      p_bestmodule->psz_name );
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
366

367 368
    /* Don't forget that the module is still locked if bestmodule != NULL */
    return( p_bestmodule );
Sam Hocevar's avatar
 
Sam Hocevar committed
369 370 371 372 373
}

/*****************************************************************************
 * module_Unneed: decrease the usage count of a module.
 *****************************************************************************
374 375
 * This function must be called by the thread that called module_Need, to
 * decrease the reference count and allow for hiding of modules.
Sam Hocevar's avatar
 
Sam Hocevar committed
376
 *****************************************************************************/
377
void module_Unneed( module_bank_t * p_bank, module_t * p_module )
Sam Hocevar's avatar
 
Sam Hocevar committed
378
{
379 380
    /* We take the global lock */
    vlc_mutex_lock( &p_bank->lock );
Sam Hocevar's avatar
 
Sam Hocevar committed
381

382 383 384
    /* Just unlock the module - we can't do anything if it fails,
     * so there is no need to check the return value. */
    UnlockModule( p_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
385

Sam Hocevar's avatar
 
Sam Hocevar committed
386
    intf_WarnMsg( 3, "module: unlocking module `%s'", p_module->psz_name );
Sam Hocevar's avatar
 
Sam Hocevar committed
387

388 389 390 391
    /* We release the global lock */
    vlc_mutex_unlock( &p_bank->lock );

    return;
Sam Hocevar's avatar
 
Sam Hocevar committed
392 393 394 395 396 397
}

/*****************************************************************************
 * Following functions are local.
 *****************************************************************************/

Sam Hocevar's avatar
 
Sam Hocevar committed
398
#ifdef HAVE_DYNAMIC_PLUGINS
Sam Hocevar's avatar
 
Sam Hocevar committed
399
/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
400
 * AllocatePluginModule: load a module into memory and initialize it.
Sam Hocevar's avatar
 
Sam Hocevar committed
401 402 403 404 405
 *****************************************************************************
 * This function loads a dynamically loadable module and allocates a structure
 * for its information data. The module can then be handled by module_Need,
 * module_Unneed and HideModule. It can be removed by FreeModule.
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
406
static int AllocatePluginModule( module_bank_t * p_bank, char * psz_filename )
Sam Hocevar's avatar
 
Sam Hocevar committed
407
{
408
    module_t * p_module, * p_othermodule;
Sam Hocevar's avatar
 
Sam Hocevar committed
409 410 411
    module_handle_t handle;

    /* Try to dynamically load the module. */
Sam Hocevar's avatar
 
Sam Hocevar committed
412
    if( module_load( psz_filename, &handle ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
413
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
414 415
        /* The plugin module couldn't be opened */
        intf_WarnMsgImm( 3, "module warning: cannot open %s (%s)",
Sam Hocevar's avatar
 
Sam Hocevar committed
416
                         psz_filename, module_error() );
Sam Hocevar's avatar
 
Sam Hocevar committed
417 418 419 420 421 422 423 424 425 426 427 428 429 430
        return( -1 );
    }

    /* Now that we have successfully loaded the module, we can
     * allocate a structure for it */ 
    p_module = malloc( sizeof( module_t ) );
    if( p_module == NULL )
    {
        intf_ErrMsg( "module error: can't allocate p_module" );
        module_unload( handle );
        return( -1 );
    }

    /* We need to fill these since they may be needed by CallSymbol() */
Sam Hocevar's avatar
 
Sam Hocevar committed
431 432
    p_module->is.plugin.psz_filename = psz_filename;
    p_module->is.plugin.handle = handle;
Sam Hocevar's avatar
 
Sam Hocevar committed
433

434
    /* Initialize the module : fill p_module->psz_name, etc. */
Sam Hocevar's avatar
 
Sam Hocevar committed
435 436 437 438 439 440 441 442
    if( CallSymbol( p_module, "InitModule" ) != 0 )
    {
        /* We couldn't call InitModule() */
        free( p_module );
        module_unload( handle );
        return( -1 );
    }

443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
    /* Check that version numbers match */
    if( strcmp( VERSION, p_module->psz_version ) )
    {
        free( p_module );
        module_unload( handle );
        return( -1 );
    }

    /* Check that we don't already have a module with this name */
    for( p_othermodule = p_bank->first ;
         p_othermodule != NULL ;
         p_othermodule = p_othermodule->next )
    {
        if( !strcmp( p_othermodule->psz_name, p_module->psz_name ) )
        {
            free( p_module );
            module_unload( handle );
            return( -1 );
        }
    }

    /* Activate the module : fill the capability structure, etc. */
Sam Hocevar's avatar
 
Sam Hocevar committed
465 466 467 468 469 470 471 472 473 474
    if( CallSymbol( p_module, "ActivateModule" ) != 0 )
    {
        /* We couldn't call ActivateModule() */
        free( p_module );
        module_unload( handle );
        return( -1 );
    }

    /* We strdup() these entries so that they are still valid when the
     * module is unloaded. */
Sam Hocevar's avatar
 
Sam Hocevar committed
475 476
    p_module->is.plugin.psz_filename =
            strdup( p_module->is.plugin.psz_filename );
Sam Hocevar's avatar
 
Sam Hocevar committed
477 478 479
    p_module->psz_name = strdup( p_module->psz_name );
    p_module->psz_longname = strdup( p_module->psz_longname );
    p_module->psz_version = strdup( p_module->psz_version );
Sam Hocevar's avatar
 
Sam Hocevar committed
480
    if( p_module->is.plugin.psz_filename == NULL 
Sam Hocevar's avatar
 
Sam Hocevar committed
481 482 483 484 485
            || p_module->psz_name == NULL
            || p_module->psz_longname == NULL
            || p_module->psz_version == NULL )
    {
        intf_ErrMsg( "module error: can't duplicate strings" );
Sam Hocevar's avatar
 
Sam Hocevar committed
486
        free( p_module->is.plugin.psz_filename );
Sam Hocevar's avatar
 
Sam Hocevar committed
487 488 489 490 491 492 493 494 495 496 497 498 499 500
        free( p_module->psz_name );
        free( p_module->psz_longname );
        free( p_module->psz_version );
        free( p_module );
        module_unload( handle );
        return( -1 );
    }

    /* Everything worked fine ! The module is ready to be added to the list. */
    p_module->i_usage = 0;
    p_module->i_unused_delay = 0;

    p_module->b_builtin = 0;

Sam Hocevar's avatar
 
Sam Hocevar committed
501
    /* Link module into the linked list */
Sam Hocevar's avatar
 
Sam Hocevar committed
502 503 504 505 506
    if( p_bank->first != NULL )
    {
        p_bank->first->prev = p_module;
    }
    p_module->next = p_bank->first;
Sam Hocevar's avatar
 
Sam Hocevar committed
507
    p_module->prev = NULL;
Sam Hocevar's avatar
 
Sam Hocevar committed
508 509
    p_bank->first = p_module;

Sam Hocevar's avatar
 
Sam Hocevar committed
510
    /* Immediate message so that a slow module doesn't make the user wait */
Sam Hocevar's avatar
 
Sam Hocevar committed
511
    intf_WarnMsgImm( 2, "module: plugin module `%s', %s",
Sam Hocevar's avatar
 
Sam Hocevar committed
512
                     p_module->psz_name, p_module->psz_longname );
Sam Hocevar's avatar
 
Sam Hocevar committed
513

Sam Hocevar's avatar
 
Sam Hocevar committed
514 515
    return( 0 );
}
Sam Hocevar's avatar
 
Sam Hocevar committed
516
#endif /* HAVE_DYNAMIC_PLUGINS */
Sam Hocevar's avatar
 
Sam Hocevar committed
517 518

/*****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
519
 * AllocateBuiltinModule: initialize a built-in module.
Sam Hocevar's avatar
 
Sam Hocevar committed
520
 *****************************************************************************
Sam Hocevar's avatar
 
Sam Hocevar committed
521 522 523
 * This function registers a built-in module and allocates a structure
 * for its information data. The module can then be handled by module_Need,
 * module_Unneed and HideModule. It can be removed by FreeModule.
Sam Hocevar's avatar
 
Sam Hocevar committed
524
 *****************************************************************************/
Sam Hocevar's avatar
 
Sam Hocevar committed
525 526 527 528
static int AllocateBuiltinModule( module_bank_t * p_bank,
                                  int ( *pf_init ) ( module_t * ),
                                  int ( *pf_activate ) ( module_t * ),
                                  int ( *pf_deactivate ) ( module_t * ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
529
{
Sam Hocevar's avatar
 
Sam Hocevar committed
530 531 532 533 534 535
    module_t * p_module, * p_othermodule;

    /* Now that we have successfully loaded the module, we can
     * allocate a structure for it */ 
    p_module = malloc( sizeof( module_t ) );
    if( p_module == NULL )
Sam Hocevar's avatar
 
Sam Hocevar committed
536
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
537
        intf_ErrMsg( "module error: can't allocate p_module" );
Sam Hocevar's avatar
 
Sam Hocevar committed
538 539 540
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
541 542
    /* Initialize the module : fill p_module->psz_name, etc. */
    if( pf_init( p_module ) != 0 )
Sam Hocevar's avatar
 
Sam Hocevar committed
543
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
544 545 546 547
        /* With a well-written module we shouldn't have to print an
         * additional error message here, but just make sure. */
        intf_ErrMsg( "module error: failed calling init in builtin module" );
        free( p_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
548 549 550
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
551 552
    /* Check that version numbers match */
    if( strcmp( VERSION, p_module->psz_version ) )
Sam Hocevar's avatar
 
Sam Hocevar committed
553
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
554
        free( p_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
555 556 557
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
558 559 560 561
    /* Check that we don't already have a module with this name */
    for( p_othermodule = p_bank->first ;
         p_othermodule != NULL ;
         p_othermodule = p_othermodule->next )
Sam Hocevar's avatar
 
Sam Hocevar committed
562
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
563 564 565 566 567 568 569 570 571 572 573 574 575 576
        if( !strcmp( p_othermodule->psz_name, p_module->psz_name ) )
        {
            free( p_module );
            return( -1 );
        }
    }

    if( pf_activate( p_module ) != 0 )
    {
        /* With a well-written module we shouldn't have to print an
         * additional error message here, but just make sure. */
        intf_ErrMsg( "module error: failed calling activate "
                     "in builtin module" );
        free( p_module );
Sam Hocevar's avatar
 
Sam Hocevar committed
577 578 579
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
    /* We strdup() these entries so that they are still valid when the
     * module is unloaded. */
    p_module->psz_name = strdup( p_module->psz_name );
    p_module->psz_longname = strdup( p_module->psz_longname );
    p_module->psz_version = strdup( p_module->psz_version );
    if( p_module->psz_name == NULL 
            || p_module->psz_longname == NULL
            || p_module->psz_version == NULL )
    {
        intf_ErrMsg( "module error: can't duplicate strings" );
        free( p_module->psz_name );
        free( p_module->psz_longname );
        free( p_module->psz_version );
        free( p_module );
        return( -1 );
    }

    /* Everything worked fine ! The module is ready to be added to the list. */
    p_module->i_usage = 0;
    p_module->i_unused_delay = 0;

    p_module->b_builtin = 1;
    p_module->is.builtin.pf_deactivate = pf_deactivate;

    /* Link module into the linked list */
    if( p_bank->first != NULL )
    {
        p_bank->first->prev = p_module;
    }
    p_module->next = p_bank->first;
    p_module->prev = NULL;
    p_bank->first = p_module;

    /* Immediate message so that a slow module doesn't make the user wait */
    intf_WarnMsgImm( 2, "module: builtin module `%s', %s",
                     p_module->psz_name, p_module->psz_longname );
Sam Hocevar's avatar
 
Sam Hocevar committed
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633

    return( 0 );
}

/*****************************************************************************
 * FreeModule: delete a module and its structure.
 *****************************************************************************
 * This function can only be called if i_usage <= 0.
 *****************************************************************************/
static int FreeModule( module_bank_t * p_bank, module_t * p_module )
{
    /* If the module is not in use but is still in memory, we first have
     * to hide it and remove it from memory before we can free the
     * data structure. */
    if( p_module->b_builtin )
    {
        if( p_module->i_usage != 0 )
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
634
            intf_ErrMsg( "module error: trying to free builtin module `%s' with"
Sam Hocevar's avatar
 
Sam Hocevar committed
635 636 637
                         " usage %i", p_module->psz_name, p_module->i_usage );
            return( -1 );
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
638 639 640 641 642
        else
        {
            /* We deactivate the module now. */
            p_module->is.builtin.pf_deactivate( p_module );
        }
Sam Hocevar's avatar
 
Sam Hocevar committed
643
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
644
#ifdef HAVE_DYNAMIC_PLUGINS
Sam Hocevar's avatar
 
Sam Hocevar committed
645 646 647 648
    else
    {
        if( p_module->i_usage >= 1 )
        {
Sam Hocevar's avatar
 
Sam Hocevar committed
649
            intf_ErrMsg( "module error: trying to free module `%s' which is"
Sam Hocevar's avatar
 
Sam Hocevar committed
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
                         " still in use", p_module->psz_name );
            return( -1 );
        }

        /* Two possibilities here: i_usage == -1 and the module is already
         * unloaded, we can continue, or i_usage == 0, and we have to hide
         * the module before going on. */
        if( p_module->i_usage == 0 )
        {
            if( HideModule( p_module ) != 0 )
            {
                return( -1 );
            }
        }
    }
Sam Hocevar's avatar
 
Sam Hocevar committed
665
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683

    /* Unlink the module from the linked list. */
    if( p_module == p_bank->first )
    {
        p_bank->first = p_module->next;
    }

    if( p_module->prev != NULL )
    {
        p_module->prev->next = p_module->next;
    }

    if( p_module->next != NULL )
    {
        p_module->next->prev = p_module->prev;
    }

    /* We free the structures that we strdup()ed in Allocate*Module(). */
Sam Hocevar's avatar
 
Sam Hocevar committed
684 685 686 687 688 689
#ifdef HAVE_DYNAMIC_PLUGINS
    if( !p_module->b_builtin )
    {
        free( p_module->is.plugin.psz_filename );
    }
#endif
Sam Hocevar's avatar
 
Sam Hocevar committed
690 691 692 693 694 695 696 697 698
    free( p_module->psz_name );
    free( p_module->psz_longname );
    free( p_module->psz_version );

    free( p_module );

    return( 0 );
}

699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
/*****************************************************************************
 * LockModule: increase the usage count of a module and load it if needed.
 *****************************************************************************
 * This function has to be called before a thread starts using a module. If
 * the module is already loaded, we just increase its usage count. If it isn't
 * loaded, we have to dynamically open it and initialize it.
 * If you successfully call LockModule() at any moment, be careful to call
 * UnlockModule() when you don't need it anymore.
 *****************************************************************************/
static int LockModule( module_t * p_module )
{
    if( p_module->i_usage >= 0 )
    {
        /* This module is already loaded and activated, we can return */
        p_module->i_usage++;
        return( 0 );
    }

    if( p_module->b_builtin )
    {
        /* A built-in module should always have a refcount >= 0 ! */
        intf_ErrMsg( "module error: built-in module `%s' has refcount %i",
                     p_module->psz_name, p_module->i_usage );
        return( -1 );
    }

Sam Hocevar's avatar
 
Sam Hocevar committed
725
#ifdef HAVE_DYNAMIC_PLUGINS
726 727 728
    if( p_module->i_usage != -1 )
    {
        /* This shouldn't happen. Ever. We have serious problems here. */
Sam Hocevar's avatar
 
Sam Hocevar committed
729
        intf_ErrMsg( "module error: plugin module `%s' has refcount %i",
730 731 732 733 734
                     p_module->psz_name, p_module->i_usage );
        return( -1 );
    }

    /* i_usage == -1, which means that the module isn't in memory */
Sam Hocevar's avatar
 
Sam Hocevar committed
735 736
    if( module_load( p_module->is.plugin.psz_filename,
                     &p_module->is.plugin.handle ) )
737
    {
Sam Hocevar's avatar
 
Sam Hocevar committed
738
        /* The plugin module couldn't be opened */
739
        intf_ErrMsg( "module error: cannot open %s (%s)",
Sam Hocevar's avatar
 
Sam Hocevar committed
740
                     p_module->is.plugin.psz_filename, module_error() );
741 742 743 744 745 746 747 748 749 750 751 752
        return( -1 );
    }

    /* FIXME: what to do if the guy modified the plugin while it was
     * unloaded ? It makes XMMS crash nastily, perhaps we should try
     * to be a bit more clever here. */

    /* Activate the module : fill the capability structure, etc. */
    if( CallSymbol( p_module, "ActivateModule" ) != 0 )
    {
        /* We couldn't call ActivateModule() -- looks nasty, but
         * we can't do much about it. Just try to unload module. */
Sam Hocevar's avatar
 
Sam Hocevar committed
753
        module_unload( p_module->is.plugin.handle );
754 755 756 757 758 759
        p_module->i_usage = -1;
        return( -1 );
    }

    /* Everything worked fine ! The module is ready to be used */
    p_module->i_usage = 1;
Sam Hocevar's avatar
 
Sam Hocevar committed
760
#endif /* HAVE_DYNAMIC_PLUGINS */
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787

    return( 0 );
}

/*****************************************************************************
 * UnlockModule: decrease the usage count of a module.
 *****************************************************************************
 * We decrease the usage count of a module so that we know when a module
 * becomes unused and can be hidden.
 *****************************************************************************/
static int UnlockModule( module_t * p_module )
{
    if( p_module->i_usage <= 0 )
    {
        /* This shouldn't happen. Ever. We have serious problems here. */
        intf_ErrMsg( "module error: trying to call module_Unneed() on `%s'"
                     " which isn't even in use", p_module->psz_name );
        return( -1 );
    }

    /* This module is still in use, we can return */
    p_module->i_usage--;
    p_module->i_unused_delay = 0;

    return( 0 );
}

Sam Hocevar's avatar
 
Sam Hocevar committed
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
#ifdef HAVE_DYNAMIC_PLUGINS
/*****************************************************************************
 * HideModule: remove a module from memory but keep its structure.
 *****************************************************************************
 * This function can only be called if i_usage == 0. It will make a call
 * to the module's inner DeactivateModule() symbol, and then unload it
 * from memory. A call to module_Need() will automagically load it again.
 *****************************************************************************/
static int HideModule( module_t * p_module )
{
    if( p_module->b_builtin )
    {
        /* A built-in module should never be hidden. */
        intf_ErrMsg( "module error: trying to hide built-in module `%s'",
                     p_module->psz_name );
        return( -1 );
    }

    if( p_module->i_usage >= 1 )
    {
        intf_ErrMsg( "module error: trying to hide module `%s' which is still"
                     " in use", p_module->psz_name );
        return( -1 );
    }

    if( p_module->i_usage <= -1 )
    {
        intf_ErrMsg( "module error: trying to hide module `%s' which is already"
                     " hidden", p_module->psz_name );
        return( -1 );
    }

    /* Deactivate the module : free the capability structure, etc. */
    if( CallSymbol( p_module, "DeactivateModule" ) != 0 )
    {
        /* We couldn't call DeactivateModule() -- looks nasty, but
         * we can't do much about it. Just try to unload module anyway. */
        module_unload( p_module->is.plugin.handle );
        p_module->i_usage = -1;
        return( -1 );
    }

    /* Everything worked fine, we can safely unload the module. */
    module_unload( p_module->is.plugin.handle );
    p_module->i_usage = -1;

    return( 0 );
}

Sam Hocevar's avatar
 
Sam Hocevar committed
837 838 839 840 841 842 843 844 845 846 847 848 849
/*****************************************************************************
 * CallSymbol: calls a module symbol.
 *****************************************************************************
 * This function calls a symbol given its name and a module structure. The
 * symbol MUST refer to a function returning int and taking a module_t* as
 * an argument.
 *****************************************************************************/
static int CallSymbol( module_t * p_module, char * psz_name )
{
    typedef int ( symbol_t ) ( module_t * p_module );
    symbol_t * p_symbol;

    /* Try to resolve the symbol */
Sam Hocevar's avatar
 
Sam Hocevar committed
850
    p_symbol = module_getsymbol( p_module->is.plugin.handle, psz_name );
Sam Hocevar's avatar
 
Sam Hocevar committed
851 852 853 854

    if( !p_symbol )
    {
        /* We couldn't load the symbol */
Sam Hocevar's avatar
 
Sam Hocevar committed
855
        intf_WarnMsg( 3, "module warning: "
Sam Hocevar's avatar
 
Sam Hocevar committed
856
                         "cannot find symbol %s in module %s (%s)",
Sam Hocevar's avatar
 
Sam Hocevar committed
857 858
                         psz_name, p_module->is.plugin.psz_filename,
                         module_error() );
Sam Hocevar's avatar
 
Sam Hocevar committed
859 860 861 862 863 864 865 866
        return( -1 );
    }

    /* We can now try to call the symbol */
    if( p_symbol( p_module ) != 0 )
    {
        /* With a well-written module we shouldn't have to print an
         * additional error message here, but just make sure. */
Sam Hocevar's avatar
 
Sam Hocevar committed
867
        intf_ErrMsg( "module error: failed calling symbol %s in module %s",
Sam Hocevar's avatar
 
Sam Hocevar committed
868
                     psz_name, p_module->is.plugin.psz_filename );
Sam Hocevar's avatar
 
Sam Hocevar committed
869 870 871 872 873 874
        return( -1 );
    }

    /* Everything worked fine, we can return */
    return( 0 );
}
Sam Hocevar's avatar
 
Sam Hocevar committed
875
#endif /* HAVE_DYNAMIC_PLUGINS */
Sam Hocevar's avatar
 
Sam Hocevar committed
876