deal properly with recursive module unloading
[profile/ivi/pulseaudio.git] / src / pulsecore / module.c
1 /* $Id$ */
2
3 /***
4   This file is part of PulseAudio.
5  
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2 of the License,
9   or (at your option) any later version.
10  
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <limits.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <assert.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <ctype.h>
33
34 #include <pulse/timeval.h>
35 #include <pulse/xmalloc.h>
36
37 #include <pulsecore/core-subscribe.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/core-util.h>
40
41 #include "module.h"
42
43 #define PA_SYMBOL_INIT "pa__init"
44 #define PA_SYMBOL_DONE "pa__done"
45
46 #define UNLOAD_POLL_TIME 2
47
48 /* lt_dlsym() violates ISO C, so confide the breakage into this function to
49  * avoid warnings. */
50 typedef void (*fnptr)(void);
51 static inline fnptr lt_dlsym_fn(lt_dlhandle handle, const char *symbol) {
52     return (fnptr) (long) lt_dlsym(handle, symbol);
53 }
54
55 static void timeout_callback(pa_mainloop_api *m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
56     pa_core *c = userdata;
57     struct timeval ntv;
58     assert(c && c->mainloop == m && c->module_auto_unload_event == e);
59
60     pa_module_unload_unused(c);
61
62     pa_gettimeofday(&ntv);
63     ntv.tv_sec += UNLOAD_POLL_TIME;
64     m->time_restart(e, &ntv);
65 }
66
67 static inline fnptr load_sym(lt_dlhandle handle, const char *module, const char *symbol) {
68     char *buffer, *ch;
69     size_t buflen;
70     fnptr res;
71
72     res = lt_dlsym_fn(handle, symbol);
73     if (res)
74         return res;
75
76     /* As the .la files might have been cleansed from the system, we should
77      * try with the ltdl prefix as well. */
78
79     buflen = strlen(symbol) + strlen(module) + strlen("_LTX_") + 1;
80     buffer = pa_xmalloc(buflen);
81     assert(buffer);
82
83     strcpy(buffer, module);
84
85     for (ch = buffer;*ch != '\0';ch++) {
86         if (!isalnum(*ch))
87             *ch = '_';
88     }
89
90     strcat(buffer, "_LTX_");
91     strcat(buffer, symbol);
92
93     res = lt_dlsym_fn(handle, buffer);
94
95     pa_xfree(buffer);
96
97     return res;
98 }
99
100 pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
101     pa_module *m = NULL;
102     int r;
103     
104     assert(c && name);
105
106     if (c->disallow_module_loading)
107         goto fail;
108     
109     m = pa_xmalloc(sizeof(pa_module));
110
111     m->name = pa_xstrdup(name);
112     m->argument = pa_xstrdup(argument);
113     
114     if (!(m->dl = lt_dlopenext(name))) {
115         pa_log(__FILE__": Failed to open module \"%s\": %s", name, lt_dlerror());
116         goto fail;
117     }
118
119     if (!(m->init = (int (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_INIT))) {
120         pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name);
121         goto fail;
122     }
123
124     if (!(m->done = (void (*)(pa_core *_c, pa_module*_m)) load_sym(m->dl, name, PA_SYMBOL_DONE))) {
125         pa_log(__FILE__": Failed to load module \"%s\": symbol \""PA_SYMBOL_DONE"\" not found.", name);
126         goto fail;
127     }
128     
129     m->userdata = NULL;
130     m->core = c;
131     m->n_used = -1;
132     m->auto_unload = 0;
133     m->unload_requested = 0;
134
135     assert(m->init);
136     if (m->init(c, m) < 0) {
137         pa_log_error(__FILE__": Failed to load  module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : "");
138         goto fail;
139     }
140
141     if (!c->modules)
142         c->modules = pa_idxset_new(NULL, NULL);
143
144     if (!c->module_auto_unload_event) {
145         struct timeval ntv;
146         pa_gettimeofday(&ntv);
147         ntv.tv_sec += UNLOAD_POLL_TIME;
148         c->module_auto_unload_event = c->mainloop->time_new(c->mainloop, &ntv, timeout_callback, c);
149     }
150     assert(c->module_auto_unload_event);
151     
152     assert(c->modules);
153     r = pa_idxset_put(c->modules, m, &m->index);
154     assert(r >= 0 && m->index != PA_IDXSET_INVALID);
155
156     pa_log_info(__FILE__": Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : ""); 
157
158     pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index);
159     
160     return m;
161     
162 fail:
163
164     if (m) {
165         pa_xfree(m->argument);
166         pa_xfree(m->name);
167         
168         if (m->dl)
169             lt_dlclose(m->dl);
170
171         pa_xfree(m);
172     }
173
174     return NULL;
175 }
176
177 static void pa_module_free(pa_module *m) {
178     assert(m && m->done && m->core);
179
180     if (m->core->disallow_module_loading)
181         return;
182
183     pa_log_info(__FILE__": Unloading \"%s\" (index: #%u).", m->name, m->index); 
184
185     m->done(m->core, m);
186
187     lt_dlclose(m->dl);
188     
189     pa_log_info(__FILE__": Unloaded \"%s\" (index: #%u).", m->name, m->index); 
190
191     pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index);
192     
193     pa_xfree(m->name);
194     pa_xfree(m->argument);
195     pa_xfree(m);
196 }
197
198 void pa_module_unload(pa_core *c, pa_module *m) {
199     assert(c && m);
200
201     assert(c->modules);
202     if (!(m = pa_idxset_remove_by_data(c->modules, m, NULL)))
203         return;
204
205     pa_module_free(m);
206 }
207
208 void pa_module_unload_by_index(pa_core *c, uint32_t idx) {
209     pa_module *m;
210     assert(c && idx != PA_IDXSET_INVALID);
211
212     assert(c->modules);
213     if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
214         return;
215
216     pa_module_free(m);
217 }
218
219 static void free_callback(void *p, PA_GCC_UNUSED void *userdata) {
220     pa_module *m = p;
221     assert(m);
222     pa_module_free(m);
223 }
224
225 void pa_module_unload_all(pa_core *c) {
226     assert(c);
227     pa_module *m;
228
229     if (!c->modules)
230         return;
231
232     while ((m = pa_idxset_first(c->modules, NULL)))
233         pa_module_unload(c, m);
234
235     pa_idxset_free(c->modules, free_callback, NULL);
236     c->modules = NULL;
237
238     if (c->module_auto_unload_event) {
239         c->mainloop->time_free(c->module_auto_unload_event);
240         c->module_auto_unload_event = NULL;
241     }
242
243     if (c->module_defer_unload_event) {
244         c->mainloop->defer_free(c->module_defer_unload_event);
245         c->module_defer_unload_event = NULL;
246     }
247 }
248
249 static int unused_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, void *userdata) {
250     pa_module *m = p;
251     time_t *now = userdata;
252     assert(p && del && now);
253     
254     if (m->n_used == 0 && m->auto_unload && m->last_used_time+m->core->module_idle_time <= *now) {
255         pa_module_free(m);
256         *del = 1;
257     }
258
259     return 0;
260 }
261
262 void pa_module_unload_unused(pa_core *c) {
263     time_t now;
264     assert(c);
265
266     if (!c->modules)
267         return;
268     
269     time(&now);
270     pa_idxset_foreach(c->modules, unused_callback, &now);
271 }
272
273 static int unload_callback(void *p, PA_GCC_UNUSED uint32_t idx, int *del, PA_GCC_UNUSED void *userdata) {
274     pa_module *m = p;
275     assert(m);
276
277     if (m->unload_requested) {
278         pa_module_free(m);
279         *del = 1;
280     }
281
282     return 0;
283 }
284
285 static void defer_cb(pa_mainloop_api*api, pa_defer_event *e, void *userdata) {
286     pa_core *core = userdata;
287     api->defer_enable(e, 0);
288
289     if (!core->modules)
290         return;
291
292     pa_idxset_foreach(core->modules, unload_callback, NULL);
293
294 }
295
296 void pa_module_unload_request(pa_module *m) {
297     assert(m);
298
299     m->unload_requested = 1;
300
301     if (!m->core->module_defer_unload_event)
302         m->core->module_defer_unload_event = m->core->mainloop->defer_new(m->core->mainloop, defer_cb, m->core);
303
304     m->core->mainloop->defer_enable(m->core->module_defer_unload_event, 1);
305 }
306
307 void pa_module_set_used(pa_module*m, int used) {
308     assert(m);
309
310     if (m->n_used != used)
311         pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index);
312     
313     if (m->n_used != used && used == 0)
314         time(&m->last_used_time);
315
316     m->n_used = used;
317 }
318
319 pa_modinfo *pa_module_get_info(pa_module *m) {
320     assert(m);
321
322     return pa_modinfo_get_by_handle(m->dl);
323 }