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