module.c revision 2f6e87e64736666857c1bbe2cb0692c1f4e56508
1/*
2 * module.c, dynamic module interface
3 *
4 * Copyright (c) 2009-2010 Wind River Systems, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include <string.h>
20#include <stdlib.h>
21#include <pthread.h>
22
23#include <module.h>
24
25#define LOG_TAG "module"
26#include <log.h>
27
28static struct module *g_module_head;
29static char *g_module_err;
30
31static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
32
33#define for_each_module(__module, __head)               \
34    for ((__module) = (__head); (__module) != NULL;     \
35         (__module) = (__module)->next)
36
37#define find_last_module(__head)                \
38    ({                                          \
39        struct module *m;                       \
40                                                \
41        for_each_module(m, (__head)) {          \
42            if (!m->next)                       \
43                break;                          \
44        }                                       \
45        m;                                      \
46    })
47
48
49static struct module *module_find_with_name(struct module *head,
50                                            const char *filename)
51{
52    struct module *module;
53
54    for_each_module(module, head) {
55        if (!strcmp(module->name, filename))
56            return module;
57    }
58
59    return NULL;
60}
61
62static struct module *module_find_with_handle(struct module *head,
63                                              const void *handle)
64{
65    struct module *module;
66
67    for_each_module(module, head) {
68        if (module->handle == handle)
69            return module;
70    }
71
72    return NULL;
73}
74
75static struct module *module_add_list(struct module *head,
76                                      struct module *add)
77{
78    struct module *last;
79
80    last = find_last_module(head);
81    if (last)
82        last->next = add;
83    else
84        head = add;
85
86    return head;
87}
88
89static struct module *module_del_list(struct module *head,
90                                      struct module *del)
91{
92    struct module *prev = NULL;
93
94    for_each_module(prev, head) {
95        if (prev->next == del)
96            break;
97    }
98
99    if (!prev)
100        head = del->next;
101    else
102        prev->next = del->next;
103
104    return head;
105}
106
107static inline void module_set_error(const char *dlerr)
108{
109    if (g_module_err)
110        free(g_module_err);
111
112    if (dlerr)
113        g_module_err = strdup(dlerr);
114    else
115        g_module_err = NULL;
116}
117
118const char *module_error(void)
119{
120    return g_module_err;
121}
122
123struct module *module_open(const char *file, int flag)
124{
125    struct module *new, *existing;
126    void *handle;
127    const char *dlerr;
128    int init_ret = 0;
129
130    pthread_mutex_lock(&g_lock);
131
132    existing = module_find_with_name(g_module_head, file);
133    if (existing) {
134        LOGE("found opened module %s\n", existing->name);
135        existing->ref_count++;
136        pthread_mutex_unlock(&g_lock);
137        return existing;
138    }
139
140    new = malloc(sizeof(*new));
141    if (!new) {
142        pthread_mutex_unlock(&g_lock);
143        return NULL;
144    }
145
146    new->ref_count = 1;
147    new->priv = NULL;
148    new->next = NULL;
149
150    dlerror();
151    new->handle = dlopen(file, flag);
152    dlerr = dlerror();
153    if (dlerr) {
154        LOGE("dlopen failed (%s)\n", dlerr);
155        module_set_error(dlerr);
156        pthread_mutex_unlock(&g_lock);
157        goto free_new;
158    }
159
160    existing = module_find_with_handle(g_module_head, new->handle);
161    if (existing) {
162        LOGE("found opened module %s\n", existing->name);
163        existing->ref_count++;
164
165        free(new);
166        pthread_mutex_unlock(&g_lock);
167        return existing;
168    }
169
170    dlerror();
171    new->init = dlsym(new->handle, "module_init");
172    dlerr = dlerror();
173    if (!dlerr) {
174        LOGE("module %s has init(), call the symbol\n", new->name);
175        init_ret = new->init(new);
176    }
177
178    if (init_ret) {
179        LOGE("module %s init() failed (%d)\n", new->name, init_ret);
180        pthread_mutex_unlock(&g_lock);
181        goto free_handle;
182    }
183
184    dlerror();
185    new->exit = dlsym(new->handle, "module_exit");
186    dlerr = dlerror();
187    if (dlerr)
188        new->exit = NULL;
189
190    new->name = strdup(file);
191    if (!new->name) {
192        if (new->exit)
193            new->exit(new);
194        goto free_handle;
195    }
196
197    g_module_head = module_add_list(g_module_head, new);
198
199    pthread_mutex_unlock(&g_lock);
200    return new;
201
202free_handle:
203    dlclose(new->handle);
204
205free_new:
206    free(new);
207
208    pthread_mutex_unlock(&g_lock);
209    return NULL;
210}
211
212int module_close(struct module *module)
213{
214    const char *dlerr;
215    int ret = 0;
216
217    if (!module || !module->handle)
218        return 0;
219
220    pthread_mutex_lock(&g_lock);
221
222    module->ref_count--;
223    ret = module->ref_count;
224
225    LOGV("module %s decrease refcont (%d)\n", module->name, module->ref_count);
226
227    if (!module->ref_count) {
228        if (module->exit)
229            module->exit(module);
230
231        dlerror();
232        dlclose(module->handle);
233        dlerr = dlerror();
234        if (dlerr) {
235            module_set_error(dlerr);
236            ret = -1;
237        }
238
239        g_module_head = module_del_list(g_module_head, module);
240
241        LOGV("module %s closed\n", module->name);
242
243        free(module->name);
244        free(module);
245    }
246
247    pthread_mutex_unlock(&g_lock);
248    return ret;
249}
250
251void *module_symbol(struct module *module, const char *string)
252{
253    void *symbol;
254    const char *dlerr;
255
256    if (!module || !module->handle || !string)
257        return NULL;
258
259    pthread_mutex_lock(&g_lock);
260
261    dlerror();
262    symbol = dlsym(module->handle, string);
263    dlerr = dlerror();
264    if (dlerr) {
265        LOGE("not founded symbol %s in module %s (%s)\n",
266             string, module->name, dlerr);
267        module_set_error(dlerr);
268        symbol = NULL;
269    }
270    else
271        LOGV("found symbol %s in module %s\n", string, module->name);
272
273    pthread_mutex_unlock(&g_lock);
274    return symbol;
275}
276