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