]> git.sesse.net Git - vlc/blob - src/misc/modules.c
dd99683b55e6a2c52e080a00ba61690927ffb84e
[vlc] / src / misc / modules.c
1 /*****************************************************************************
2  * modules.c : Built-in and plugin modules management functions
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: modules.c,v 1.39 2001/07/16 22:00:45 gbazin Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Ethan C. Baldridge <BaldridgeE@cadmus.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24 #include "defs.h"
25
26 #include "config.h"
27
28 /* Some faulty libcs have a broken struct dirent when _FILE_OFFSET_BITS
29  * is set to 64. Don't try to be cleverer. */
30 #ifdef _FILE_OFFSET_BITS
31 #undef _FILE_OFFSET_BITS
32 #endif
33
34 #include <stdlib.h>                                      /* free(), strtol() */
35 #include <stdio.h>                                              /* sprintf() */
36 #include <string.h>                                              /* strdup() */
37
38 #if !defined( _MSC_VER )
39 #include <dirent.h>
40 #endif
41
42 #if defined(HAVE_DLFCN_H)                                /* Linux, BSD, Hurd */
43 #   include <dlfcn.h>                        /* dlopen(), dlsym(), dlclose() */
44 #   define HAVE_DYNAMIC_PLUGINS
45 #elif defined(HAVE_IMAGE_H)                                          /* BeOS */
46 #   include <image.h>
47 #   define HAVE_DYNAMIC_PLUGINS
48 #elif defined(WIN32) && defined( __MINGW32__ )
49 #   define HAVE_DYNAMIC_PLUGINS
50 #else
51 #   undef HAVE_DYNAMIC_PLUGINS
52 #endif
53
54 #ifdef SYS_BEOS
55 #   include "beos_specific.h"
56 #endif
57
58 #ifdef SYS_DARWIN
59 #   include "darwin_specific.h"
60 #endif
61
62 #include "common.h"
63 #include "threads.h"
64 #include "mtime.h"
65 #include "tests.h"
66 #include "netutils.h"
67 #include "modules.h"
68
69 #include "interface.h"
70 #include "intf_msg.h"
71 #include "intf_playlist.h"
72
73 #include "stream_control.h"
74 #include "input_ext-intf.h"
75 #include "input_ext-dec.h"
76 #include "input.h"
77 #include "input_netlist.h"
78 #include "mpeg_system.h"
79
80 #include "video.h"
81 #include "video_output.h"
82
83 #include "audio_output.h"
84
85 #ifdef HAVE_DYNAMIC_PLUGINS
86 #   include "modules_core.h"
87 #endif
88 #include "modules_builtin.h"
89 #include "modules_export.h"
90
91 #include "main.h"
92
93 /*****************************************************************************
94  * Local prototypes
95  *****************************************************************************/
96 #ifdef HAVE_DYNAMIC_PLUGINS
97 static int AllocatePluginModule ( char * );
98 #endif
99 #ifdef ALLOCATE_ALL_BUILTINS
100 static int AllocateBuiltinModule( int ( * ) ( module_t * ),
101                                   int ( * ) ( module_t * ),
102                                   int ( * ) ( module_t * ) );
103 #endif
104 static int DeleteModule ( module_t * );
105 static int LockModule   ( module_t * );
106 static int UnlockModule ( module_t * );
107 #ifdef HAVE_DYNAMIC_PLUGINS
108 static int HideModule   ( module_t * );
109 static int CallSymbol   ( module_t *, char * );
110 #endif
111
112 static module_symbols_t symbols;
113
114 /*****************************************************************************
115  * module_InitBank: create the module bank.
116  *****************************************************************************
117  * This function creates a module bank structure and fills it with the
118  * built-in modules, as well as all the plugin modules it can find.
119  *****************************************************************************/
120 void module_InitBank( void )
121 {
122 #ifdef HAVE_DYNAMIC_PLUGINS
123     static char * path[] = { ".", "plugins", PLUGIN_PATH, NULL, NULL };
124
125     char **         ppsz_path = path;
126     char *          psz_fullpath;
127     char *          psz_file;
128 #if defined( SYS_BEOS ) || defined( SYS_DARWIN )
129     char *          psz_vlcpath = system_GetProgramPath();
130     int             i_vlclen = strlen( psz_vlcpath );
131     boolean_t       b_notinroot;
132 #endif
133     DIR *           dir;
134     struct dirent * file;
135 #endif /* HAVE_DYNAMIC_PLUGINS */
136
137     p_module_bank->first = NULL;
138     vlc_mutex_init( &p_module_bank->lock );
139
140     /*
141      * Store the symbols to be exported
142      */
143     STORE_SYMBOLS( &symbols );
144 #ifdef TRACE
145     STORE_TRACE_SYMBOLS( &symbols );
146 #endif
147
148     /*
149      * Check all the built-in modules
150      */
151 #ifdef ALLOCATE_ALL_BUILTINS
152     intf_WarnMsg( 2, "module: checking built-in modules" );
153
154     ALLOCATE_ALL_BUILTINS();
155 #endif
156
157     /*
158      * Check all the plugin modules we can find
159      */
160 #ifdef HAVE_DYNAMIC_PLUGINS
161     intf_WarnMsg( 2, "module: checking plugin modules" );
162
163     for( ; *ppsz_path != NULL ; ppsz_path++ )
164     {
165         /* Store strlen(*ppsz_path) for later use. */
166         int i_dirlen = strlen( *ppsz_path );
167
168 #if defined( SYS_BEOS ) || defined( SYS_DARWIN )
169         b_notinroot = 0;
170         /* Under BeOS, we need to add beos_GetProgramPath() to access
171          * files under the current directory */
172         if( ( i_dirlen > 1 ) && strncmp( *ppsz_path, "/", 1 ) )
173         {
174             i_dirlen += i_vlclen + 2;
175             b_notinroot = 1;
176
177             psz_fullpath = malloc( i_dirlen );
178             if( psz_fullpath == NULL )
179             {
180                 continue;
181             }
182             sprintf( psz_fullpath, "%s/%s", psz_vlcpath, *ppsz_path );
183         }
184         else
185 #endif
186         {
187             psz_fullpath = *ppsz_path;
188         }
189
190         intf_WarnMsgImm( 1, "module: browsing `%s'", psz_fullpath );
191
192         if( (dir = opendir( psz_fullpath )) )
193         {
194             /* Parse the directory and try to load all files it contains. */
195             while( (file = readdir( dir )) )
196             {
197                 int i_filelen = strlen( file->d_name );
198
199                 /* We only load files ending with ".so" */
200                 if( i_filelen > 3
201                         && !strncmp( file->d_name + i_filelen - 3, ".so", 3 ) )
202                 {
203                     psz_file = malloc( i_dirlen + i_filelen + 2 );
204                     if( psz_file == NULL )
205                     {
206                         continue;
207                     }
208                     sprintf( psz_file, "%s/%s", psz_fullpath, file->d_name );
209
210                     /* We created a nice filename -- now we just try to load
211                      * it as a plugin module. */
212                     AllocatePluginModule( psz_file );
213
214                     /* We don't care if the allocation succeeded */
215                     free( psz_file );
216                 }
217             }
218
219             /* Close the directory if successfully opened */
220             closedir( dir );
221         }
222
223 #if defined( SYS_BEOS ) || defined( SYS_DARWIN )
224         if( b_notinroot )
225         {
226             free( psz_fullpath );
227         }
228 #endif
229     }
230 #endif /* HAVE_DYNAMIC_PLUGINS */
231
232     intf_WarnMsg( 3, "module: module bank initialized" );
233
234     return;
235 }
236
237 /*****************************************************************************
238  * module_EndBank: empty the module bank.
239  *****************************************************************************
240  * This function unloads all unused plugin modules and empties the module
241  * bank in case of success.
242  *****************************************************************************/
243 void module_EndBank( void )
244 {
245     module_t * p_next;
246
247     while( p_module_bank->first != NULL )
248     {
249         if( DeleteModule( p_module_bank->first ) )
250         {
251             /* Module deletion failed */
252             intf_ErrMsg( "module error: `%s' can't be removed. trying harder.",
253                          p_module_bank->first->psz_name );
254
255             /* We just free the module by hand. Niahahahahaha. */
256             p_next = p_module_bank->first->next;
257             free(p_module_bank->first);
258             p_module_bank->first = p_next;
259         }
260     }
261
262     /* Destroy the lock */
263     vlc_mutex_destroy( &p_module_bank->lock );
264
265     return;
266 }
267
268 /*****************************************************************************
269  * module_ResetBank: reset the module bank.
270  *****************************************************************************
271  * This function resets the module bank by unloading all unused plugin
272  * modules.
273  *****************************************************************************/
274 void module_ResetBank( void )
275 {
276     intf_ErrMsg( "FIXME: module_ResetBank unimplemented" );
277     return;
278 }
279
280 /*****************************************************************************
281  * module_ManageBank: manage the module bank.
282  *****************************************************************************
283  * This function parses the module bank and hides modules that have been
284  * unused for a while.
285  *****************************************************************************/
286 void module_ManageBank( void )
287 {
288 #ifdef HAVE_DYNAMIC_PLUGINS
289     module_t * p_module;
290
291     /* We take the global lock */
292     vlc_mutex_lock( &p_module_bank->lock );
293
294     /* Parse the module list to see if any modules need to be unloaded */
295     for( p_module = p_module_bank->first ;
296          p_module != NULL ;
297          p_module = p_module->next )
298     {
299         /* If the module is unused and if it is a plugin module... */
300         if( p_module->i_usage == 0 && !p_module->b_builtin )
301         {
302             if( p_module->i_unused_delay < MODULE_HIDE_DELAY )
303             {
304                 p_module->i_unused_delay++;
305             }
306             else
307             {
308                 intf_WarnMsg( 3, "module: hiding unused plugin module `%s'",
309                               p_module->psz_name );
310                 HideModule( p_module );
311
312                 /* Break here, so that we only hide one module at a time */
313                 break;
314             }
315         }
316     }
317
318     /* We release the global lock */
319     vlc_mutex_unlock( &p_module_bank->lock );
320 #endif /* HAVE_DYNAMIC_PLUGINS */
321
322     return;
323 }
324
325 /*****************************************************************************
326  * module_Need: return the best module function, given a capability list.
327  *****************************************************************************
328  * This function returns the module that best fits the asked capabilities.
329  *****************************************************************************/
330 module_t * module_Need( int i_capabilities, void *p_data )
331 {
332     module_t * p_module;
333     module_t * p_bestmodule = NULL;
334     int i_score, i_totalscore, i_bestscore = 0;
335     int i_index;
336
337     /* We take the global lock */
338     vlc_mutex_lock( &p_module_bank->lock );
339
340     /* Parse the module list for capabilities and probe each of them */
341     for( p_module = p_module_bank->first ;
342          p_module != NULL ;
343          p_module = p_module->next )
344     {
345         /* Test that this module can do everything we need */
346         if( ( p_module->i_capabilities & i_capabilities ) == i_capabilities )
347         {
348             i_totalscore = 0;
349
350             LockModule( p_module );
351
352             /* Parse all the requested capabilities and test them */
353             for( i_index = 0 ; (1 << i_index) <= i_capabilities ; i_index++ )
354             {
355                 if( ( (1 << i_index) & i_capabilities ) )
356                 {
357                     i_score = ( (function_list_t *)p_module->p_functions)
358                                                   [i_index].pf_probe( p_data );
359
360                     if( i_score )
361                     {
362                         i_totalscore += i_score;
363                     }
364                     else
365                     {
366                         break;
367                     }
368                 }
369             }
370
371             /* If the high score was broken, we have a new champion */
372             if( i_totalscore > i_bestscore )
373             {
374                 /* Keep the current module locked, but release the previous */
375                 if( p_bestmodule != NULL )
376                 {
377                     UnlockModule( p_bestmodule );
378                 }
379
380                 /* This is the new best module */
381                 i_bestscore = i_totalscore;
382                 p_bestmodule = p_module;
383             }
384             else
385             {
386                 /* This module wasn't interesting, unlock it and forget it */
387                 UnlockModule( p_module );
388             }
389         }
390     }
391
392     /* We can release the global lock, module refcount was incremented */
393     vlc_mutex_unlock( &p_module_bank->lock );
394
395     if( p_bestmodule != NULL )
396     {
397         intf_WarnMsg( 1, "module: locking module `%s'",
398                       p_bestmodule->psz_name );
399     }
400
401     /* Don't forget that the module is still locked if bestmodule != NULL */
402     return( p_bestmodule );
403 }
404
405 /*****************************************************************************
406  * module_Unneed: decrease the usage count of a module.
407  *****************************************************************************
408  * This function must be called by the thread that called module_Need, to
409  * decrease the reference count and allow for hiding of modules.
410  *****************************************************************************/
411 void module_Unneed( module_t * p_module )
412 {
413     /* We take the global lock */
414     vlc_mutex_lock( &p_module_bank->lock );
415
416     /* Just unlock the module - we can't do anything if it fails,
417      * so there is no need to check the return value. */
418     UnlockModule( p_module );
419
420     intf_WarnMsg( 1, "module: unlocking module `%s'", p_module->psz_name );
421
422     /* We release the global lock */
423     vlc_mutex_unlock( &p_module_bank->lock );
424
425     return;
426 }
427
428 /*****************************************************************************
429  * Following functions are local.
430  *****************************************************************************/
431
432 #ifdef HAVE_DYNAMIC_PLUGINS
433 /*****************************************************************************
434  * AllocatePluginModule: load a module into memory and initialize it.
435  *****************************************************************************
436  * This function loads a dynamically loadable module and allocates a structure
437  * for its information data. The module can then be handled by module_Need,
438  * module_Unneed and HideModule. It can be removed by DeleteModule.
439  *****************************************************************************/
440 static int AllocatePluginModule( char * psz_filename )
441 {
442     module_t * p_module, * p_othermodule;
443     module_handle_t handle;
444
445     /* Try to dynamically load the module. */
446     if( module_load( psz_filename, &handle ) )
447     {
448         /* The plugin module couldn't be opened */
449         intf_WarnMsgImm( 1, "module warning: cannot open %s (%s)",
450                          psz_filename, module_error() );
451         return( -1 );
452     }
453
454     /* Now that we have successfully loaded the module, we can
455      * allocate a structure for it */ 
456     p_module = malloc( sizeof( module_t ) );
457     if( p_module == NULL )
458     {
459         intf_ErrMsg( "module error: can't allocate p_module" );
460         module_unload( handle );
461         return( -1 );
462     }
463
464     /* We need to fill these since they may be needed by CallSymbol() */
465     p_module->is.plugin.psz_filename = psz_filename;
466     p_module->is.plugin.handle = handle;
467     p_module->p_symbols = &symbols;
468
469     /* Initialize the module : fill p_module->psz_name, etc. */
470     if( CallSymbol( p_module, "InitModule" ) != 0 )
471     {
472         /* We couldn't call InitModule() */
473         free( p_module );
474         module_unload( handle );
475         return( -1 );
476     }
477
478     /* Check that version numbers match */
479     if( strcmp( VERSION, p_module->psz_version ) )
480     {
481         free( p_module );
482         module_unload( handle );
483         return( -1 );
484     }
485
486     /* Check that we don't already have a module with this name */
487     for( p_othermodule = p_module_bank->first ;
488          p_othermodule != NULL ;
489          p_othermodule = p_othermodule->next )
490     {
491         if( !strcmp( p_othermodule->psz_name, p_module->psz_name ) )
492         {
493             free( p_module );
494             module_unload( handle );
495             return( -1 );
496         }
497     }
498
499     /* Activate the module : fill the capability structure, etc. */
500     if( CallSymbol( p_module, "ActivateModule" ) != 0 )
501     {
502         /* We couldn't call ActivateModule() */
503         free( p_module );
504         module_unload( handle );
505         return( -1 );
506     }
507
508     /* We strdup() these entries so that they are still valid when the
509      * module is unloaded. */
510     p_module->is.plugin.psz_filename =
511             strdup( p_module->is.plugin.psz_filename );
512     p_module->psz_name = strdup( p_module->psz_name );
513     p_module->psz_longname = strdup( p_module->psz_longname );
514     p_module->psz_version = strdup( p_module->psz_version );
515     if( p_module->is.plugin.psz_filename == NULL 
516             || p_module->psz_name == NULL
517             || p_module->psz_longname == NULL
518             || p_module->psz_version == NULL )
519     {
520         intf_ErrMsg( "module error: can't duplicate strings" );
521
522         if( p_module->is.plugin.psz_filename != NULL )
523         {
524             free( p_module->is.plugin.psz_filename );
525         }
526
527         if( p_module->psz_name != NULL )
528         {
529             free( p_module->psz_name );
530         }
531
532         if( p_module->psz_longname != NULL )
533         {
534             free( p_module->psz_longname );
535         }
536
537         if( p_module->psz_version != NULL )
538         {
539             free( p_module->psz_version );
540         }
541
542         free( p_module );
543         module_unload( handle );
544         return( -1 );
545     }
546
547     /* Everything worked fine ! The module is ready to be added to the list. */
548     p_module->i_usage = 0;
549     p_module->i_unused_delay = 0;
550
551     p_module->b_builtin = 0;
552
553     /* Link module into the linked list */
554     if( p_module_bank->first != NULL )
555     {
556         p_module_bank->first->prev = p_module;
557     }
558     p_module->next = p_module_bank->first;
559     p_module->prev = NULL;
560     p_module_bank->first = p_module;
561
562     /* Immediate message so that a slow module doesn't make the user wait */
563     intf_WarnMsgImm( 2, "module: new plugin module `%s', %s",
564                      p_module->psz_name, p_module->psz_longname );
565
566     return( 0 );
567 }
568 #endif /* HAVE_DYNAMIC_PLUGINS */
569
570 #ifdef ALLOCATE_ALL_BUILTINS
571 /*****************************************************************************
572  * AllocateBuiltinModule: initialize a built-in module.
573  *****************************************************************************
574  * This function registers a built-in module and allocates a structure
575  * for its information data. The module can then be handled by module_Need,
576  * module_Unneed and HideModule. It can be removed by DeleteModule.
577  *****************************************************************************/
578 static int AllocateBuiltinModule( int ( *pf_init ) ( module_t * ),
579                                   int ( *pf_activate ) ( module_t * ),
580                                   int ( *pf_deactivate ) ( module_t * ) )
581 {
582     module_t * p_module, * p_othermodule;
583
584     /* Now that we have successfully loaded the module, we can
585      * allocate a structure for it */ 
586     p_module = malloc( sizeof( module_t ) );
587     if( p_module == NULL )
588     {
589         intf_ErrMsg( "module error: can't allocate p_module" );
590         return( -1 );
591     }
592
593     /* Initialize the module : fill p_module->psz_name, etc. */
594     if( pf_init( p_module ) != 0 )
595     {
596         /* With a well-written module we shouldn't have to print an
597          * additional error message here, but just make sure. */
598         intf_ErrMsg( "module error: failed calling init in builtin module" );
599         free( p_module );
600         return( -1 );
601     }
602
603     /* Check that version numbers match */
604     if( strcmp( VERSION, p_module->psz_version ) )
605     {
606         free( p_module );
607         return( -1 );
608     }
609
610     /* Check that we don't already have a module with this name */
611     for( p_othermodule = p_module_bank->first ;
612          p_othermodule != NULL ;
613          p_othermodule = p_othermodule->next )
614     {
615         if( !strcmp( p_othermodule->psz_name, p_module->psz_name ) )
616         {
617             free( p_module );
618             return( -1 );
619         }
620     }
621
622     if( pf_activate( p_module ) != 0 )
623     {
624         /* With a well-written module we shouldn't have to print an
625          * additional error message here, but just make sure. */
626         intf_ErrMsg( "module error: failed calling activate "
627                      "in builtin module" );
628         free( p_module );
629         return( -1 );
630     }
631
632     /* We strdup() these entries so that they are still valid when the
633      * module is unloaded. */
634     p_module->psz_name = strdup( p_module->psz_name );
635     p_module->psz_longname = strdup( p_module->psz_longname );
636     p_module->psz_version = strdup( p_module->psz_version );
637     if( p_module->psz_name == NULL 
638             || p_module->psz_longname == NULL
639             || p_module->psz_version == NULL )
640     {
641         intf_ErrMsg( "module error: can't duplicate strings" );
642
643         if( p_module->psz_name != NULL )
644         {
645             free( p_module->psz_name );
646         }
647
648         if( p_module->psz_longname != NULL )
649         {
650             free( p_module->psz_longname );
651         }
652
653         if( p_module->psz_version != NULL )
654         {
655             free( p_module->psz_version );
656         }
657
658         free( p_module );
659         return( -1 );
660     }
661
662     /* Everything worked fine ! The module is ready to be added to the list. */
663     p_module->i_usage = 0;
664     p_module->i_unused_delay = 0;
665
666     p_module->b_builtin = 1;
667     p_module->is.builtin.pf_deactivate = pf_deactivate;
668
669     /* Link module into the linked list */
670     if( p_module_bank->first != NULL )
671     {
672         p_module_bank->first->prev = p_module;
673     }
674     p_module->next = p_module_bank->first;
675     p_module->prev = NULL;
676     p_module_bank->first = p_module;
677
678     /* Immediate message so that a slow module doesn't make the user wait */
679     intf_WarnMsgImm( 2, "module: new builtin module `%s', %s",
680                      p_module->psz_name, p_module->psz_longname );
681
682     return( 0 );
683 }
684 #endif /* ALLOCATE_ALL_BUILTINS */
685
686 /*****************************************************************************
687  * DeleteModule: delete a module and its structure.
688  *****************************************************************************
689  * This function can only be called if i_usage <= 0.
690  *****************************************************************************/
691 static int DeleteModule( module_t * p_module )
692 {
693     /* If the module is not in use but is still in memory, we first have
694      * to hide it and remove it from memory before we can free the
695      * data structure. */
696     if( p_module->b_builtin )
697     {
698         if( p_module->i_usage != 0 )
699         {
700             intf_ErrMsg( "module error: trying to free builtin module `%s' with"
701                          " usage %i", p_module->psz_name, p_module->i_usage );
702             return( -1 );
703         }
704         else
705         {
706             /* We deactivate the module now. */
707             p_module->is.builtin.pf_deactivate( p_module );
708         }
709     }
710 #ifdef HAVE_DYNAMIC_PLUGINS
711     else
712     {
713         if( p_module->i_usage >= 1 )
714         {
715             intf_ErrMsg( "module error: trying to free module `%s' which is"
716                          " still in use", p_module->psz_name );
717             return( -1 );
718         }
719
720         /* Two possibilities here: i_usage == -1 and the module is already
721          * unloaded, we can continue, or i_usage == 0, and we have to hide
722          * the module before going on. */
723         if( p_module->i_usage == 0 )
724         {
725             if( HideModule( p_module ) != 0 )
726             {
727                 return( -1 );
728             }
729         }
730     }
731 #endif
732
733     /* Unlink the module from the linked list. */
734     if( p_module == p_module_bank->first )
735     {
736         p_module_bank->first = p_module->next;
737     }
738
739     if( p_module->prev != NULL )
740     {
741         p_module->prev->next = p_module->next;
742     }
743
744     if( p_module->next != NULL )
745     {
746         p_module->next->prev = p_module->prev;
747     }
748
749     /* We free the structures that we strdup()ed in Allocate*Module(). */
750 #ifdef HAVE_DYNAMIC_PLUGINS
751     if( !p_module->b_builtin )
752     {
753         free( p_module->is.plugin.psz_filename );
754     }
755 #endif
756     free( p_module->psz_name );
757     free( p_module->psz_longname );
758     free( p_module->psz_version );
759
760     free( p_module );
761
762     return( 0 );
763 }
764
765 /*****************************************************************************
766  * LockModule: increase the usage count of a module and load it if needed.
767  *****************************************************************************
768  * This function has to be called before a thread starts using a module. If
769  * the module is already loaded, we just increase its usage count. If it isn't
770  * loaded, we have to dynamically open it and initialize it.
771  * If you successfully call LockModule() at any moment, be careful to call
772  * UnlockModule() when you don't need it anymore.
773  *****************************************************************************/
774 static int LockModule( module_t * p_module )
775 {
776     if( p_module->i_usage >= 0 )
777     {
778         /* This module is already loaded and activated, we can return */
779         p_module->i_usage++;
780         return( 0 );
781     }
782
783     if( p_module->b_builtin )
784     {
785         /* A built-in module should always have a refcount >= 0 ! */
786         intf_ErrMsg( "module error: built-in module `%s' has refcount %i",
787                      p_module->psz_name, p_module->i_usage );
788         return( -1 );
789     }
790
791 #ifdef HAVE_DYNAMIC_PLUGINS
792     if( p_module->i_usage != -1 )
793     {
794         /* This shouldn't happen. Ever. We have serious problems here. */
795         intf_ErrMsg( "module error: plugin module `%s' has refcount %i",
796                      p_module->psz_name, p_module->i_usage );
797         return( -1 );
798     }
799
800     /* i_usage == -1, which means that the module isn't in memory */
801     if( module_load( p_module->is.plugin.psz_filename,
802                      &p_module->is.plugin.handle ) )
803     {
804         /* The plugin module couldn't be opened */
805         intf_ErrMsg( "module error: cannot open %s (%s)",
806                      p_module->is.plugin.psz_filename, module_error() );
807         return( -1 );
808     }
809
810     /* FIXME: what to do if the guy modified the plugin while it was
811      * unloaded ? It makes XMMS crash nastily, perhaps we should try
812      * to be a bit more clever here. */
813
814     /* Activate the module : fill the capability structure, etc. */
815     if( CallSymbol( p_module, "ActivateModule" ) != 0 )
816     {
817         /* We couldn't call ActivateModule() -- looks nasty, but
818          * we can't do much about it. Just try to unload module. */
819         module_unload( p_module->is.plugin.handle );
820         p_module->i_usage = -1;
821         return( -1 );
822     }
823
824     /* Everything worked fine ! The module is ready to be used */
825     p_module->i_usage = 1;
826 #endif /* HAVE_DYNAMIC_PLUGINS */
827
828     return( 0 );
829 }
830
831 /*****************************************************************************
832  * UnlockModule: decrease the usage count of a module.
833  *****************************************************************************
834  * We decrease the usage count of a module so that we know when a module
835  * becomes unused and can be hidden.
836  *****************************************************************************/
837 static int UnlockModule( module_t * p_module )
838 {
839     if( p_module->i_usage <= 0 )
840     {
841         /* This shouldn't happen. Ever. We have serious problems here. */
842         intf_ErrMsg( "module error: trying to call module_Unneed() on `%s'"
843                      " which isn't even in use", p_module->psz_name );
844         return( -1 );
845     }
846
847     /* This module is still in use, we can return */
848     p_module->i_usage--;
849     p_module->i_unused_delay = 0;
850
851     return( 0 );
852 }
853
854 #ifdef HAVE_DYNAMIC_PLUGINS
855 /*****************************************************************************
856  * HideModule: remove a module from memory but keep its structure.
857  *****************************************************************************
858  * This function can only be called if i_usage == 0. It will make a call
859  * to the module's inner DeactivateModule() symbol, and then unload it
860  * from memory. A call to module_Need() will automagically load it again.
861  *****************************************************************************/
862 static int HideModule( module_t * p_module )
863 {
864     if( p_module->b_builtin )
865     {
866         /* A built-in module should never be hidden. */
867         intf_ErrMsg( "module error: trying to hide built-in module `%s'",
868                      p_module->psz_name );
869         return( -1 );
870     }
871
872     if( p_module->i_usage >= 1 )
873     {
874         intf_ErrMsg( "module error: trying to hide module `%s' which is still"
875                      " in use", p_module->psz_name );
876         return( -1 );
877     }
878
879     if( p_module->i_usage <= -1 )
880     {
881         intf_ErrMsg( "module error: trying to hide module `%s' which is already"
882                      " hidden", p_module->psz_name );
883         return( -1 );
884     }
885
886     /* Deactivate the module : free the capability structure, etc. */
887     if( CallSymbol( p_module, "DeactivateModule" ) != 0 )
888     {
889         /* We couldn't call DeactivateModule() -- looks nasty, but
890          * we can't do much about it. Just try to unload module anyway. */
891         module_unload( p_module->is.plugin.handle );
892         p_module->i_usage = -1;
893         return( -1 );
894     }
895
896     /* Everything worked fine, we can safely unload the module. */
897     module_unload( p_module->is.plugin.handle );
898     p_module->i_usage = -1;
899
900     return( 0 );
901 }
902
903 /*****************************************************************************
904  * CallSymbol: calls a module symbol.
905  *****************************************************************************
906  * This function calls a symbol given its name and a module structure. The
907  * symbol MUST refer to a function returning int and taking a module_t* as
908  * an argument.
909  *****************************************************************************/
910 static int CallSymbol( module_t * p_module, char * psz_name )
911 {
912     int (* pf_symbol) ( module_t * p_module );
913
914     /* Try to resolve the symbol */
915     pf_symbol = module_getsymbol( p_module->is.plugin.handle, psz_name );
916
917     if( pf_symbol == NULL )
918     {
919         /* We couldn't load the symbol */
920         intf_WarnMsg( 1, "module warning: "
921                          "cannot find symbol %s in module %s (%s)",
922                          psz_name, p_module->is.plugin.psz_filename,
923                          module_error() );
924         return( -1 );
925     }
926
927     /* We can now try to call the symbol */
928     if( pf_symbol( p_module ) != 0 )
929     {
930         /* With a well-written module we shouldn't have to print an
931          * additional error message here, but just make sure. */
932         intf_ErrMsg( "module error: failed calling symbol %s in module %s",
933                      psz_name, p_module->is.plugin.psz_filename );
934         return( -1 );
935     }
936
937     /* Everything worked fine, we can return */
938     return( 0 );
939 }
940 #endif /* HAVE_DYNAMIC_PLUGINS */
941