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