move around - flatter.
[profile/ivi/evas.git] / src / lib / file / evas_module.c
1 #ifndef _GNU_SOURCE
2 # define _GNU_SOURCE
3 #endif
4
5 #include <dirent.h>     /* DIR, dirent */
6 #include <dlfcn.h>      /* dlopen,dlclose,etc */
7
8 #include <evas_common.h>
9 #include <evas_private.h>
10
11 Evas_List *evas_modules = NULL;
12 static Evas_List *evas_module_paths = NULL;
13
14 static void
15 _evas_module_path_append(Evas_Module_Type type, char *path, const char *subdir)
16 {
17    Evas_Module_Path *mp;
18    char *buf;
19
20    buf = evas_file_path_join(path, subdir);
21    if (!buf) return;
22    if (evas_file_path_exists(buf))
23      {
24         mp = malloc(sizeof(Evas_Module_Path));
25         mp->type = type;
26         mp->path = buf;
27         evas_module_paths = evas_list_append(evas_module_paths, mp);
28      }
29    else
30      free(buf);
31 }
32
33
34 /* this will alloc a list of paths to search for the modules */
35 /* by now these are:  */
36 /* 1. ~/.evas/modules/ */
37 /* 2. $(EVAS_MODULE_DIR)/evas/modules/ */
38 /* 3. dladdr/evas/modules/ */
39 /* 4. PREFIX/evas/modules/ */
40 void
41 evas_module_paths_init(void)
42 {
43
44    char *prefix;
45    char *path;
46    Evas_List *paths = NULL;
47
48    /* 1. ~/.evas/modules/ */
49    prefix = getenv("HOME");
50    if (prefix)
51      {
52         path = malloc(strlen(prefix) + 1 + strlen("/.evas/modules"));
53         if (path)
54           {
55              path[0] = 0;
56              strcpy(path, prefix);
57              strcat(path, "/.evas/modules");
58              if (evas_file_path_exists(path))
59                paths = evas_list_append(paths, path);
60              else
61                free(path);
62           }
63      }
64
65    /* 2. $(EVAS_MODULE_DIR)/evas/modules/ */
66    prefix = getenv("EVAS_MODULES_DIR");
67    if (prefix)
68      {
69         path = malloc(strlen(prefix) + 1 + strlen("/evas/modules"));
70         if (path)
71           {
72              path[0] = 0;
73              strcpy(path, prefix);
74              strcat(path, "/evas/modules");
75              if (evas_file_path_exists(path))
76                paths = evas_list_append(paths, path);
77              else
78                free(path);
79           }
80      }
81
82 #ifdef HAVE_DLADDR
83      {
84         Dl_info evas_dl;
85         /* 3. libevas.so/../evas/modules/ */
86         if (dladdr(evas_module_paths_init, &evas_dl))
87           {
88              int length;
89
90              if (strrchr(evas_dl.dli_fname, '/'))
91                {
92                   length = strlen(strrchr(evas_dl.dli_fname, '/'));
93                   path = malloc(strlen(evas_dl.dli_fname) - length +
94                                 strlen("/evas/modules") + 1);
95                   if (path)
96                     {
97                        strncpy(path, evas_dl.dli_fname,
98                                strlen(evas_dl.dli_fname) - length);
99                        path[strlen(evas_dl.dli_fname) - length] = 0;
100                        strcat(path, "/evas/modules");
101                        if (evas_file_path_exists(path))
102                          paths = evas_list_append(paths, path);
103                        else
104                          free(path);
105                     }
106                }
107           }
108      }
109 #else
110    /* 4. PREFIX/evas/modules/ */
111    prefix = PACKAGE_LIB_DIR;
112    path = malloc(strlen(prefix) + 1 + strlen("/evas/modules"));
113    if (path)
114      {
115         strcpy(path, prefix);
116         strcat(path, "/evas/modules");
117         if (evas_file_path_exists(path))
118           paths = evas_list_append(paths, path);
119         else
120           free(path);
121      }
122 #endif
123
124    /* append all the module types subdirs */
125    while (paths)
126      {
127         /* here must be added all the types of modules and their subdirectory */
128         /* do this on a separate function */
129         /* 1. engines */
130         _evas_module_path_append(EVAS_MODULE_TYPE_ENGINE, paths->data, "engines");
131         _evas_module_path_append(EVAS_MODULE_TYPE_IMAGE_LOADER, paths->data, "loaders");
132         _evas_module_path_append(EVAS_MODULE_TYPE_IMAGE_SAVER, paths->data, "savers");
133         _evas_module_path_append(EVAS_MODULE_TYPE_OBJECT, paths->data, "objects");
134         free(paths->data);
135         paths = evas_list_remove_list(paths, paths);
136      }
137 }
138
139 /* this will alloc an Evas_Module struct for each module
140  * it finds on the paths */
141 void
142 evas_module_init(void)
143 {
144    Evas_List *l;
145    int new_id_engine = 1;
146
147 /*    printf("[init modules]\n"); */
148    evas_module_paths_init();
149    for (l = evas_module_paths; l; l = l->next)
150      {
151         Evas_Module_Path *mp;
152         DIR *dir;
153         struct dirent *de;
154
155         mp = l->data;
156
157         if (!(dir = opendir(mp->path))) break;
158 /*      printf("[evas module] searching modules on %s\n", mp->path); */
159         while ((de = readdir(dir)))
160           {
161              char *buf;
162
163              if ((!strcmp(de->d_name, ".")) || (!strcmp(de->d_name, "..")))
164                continue;
165              buf = malloc(strlen(mp->path) + 1 + strlen(de->d_name) + 1);
166              sprintf(buf, "%s/%s", mp->path, de->d_name);
167              if (evas_file_path_is_dir(buf))
168                {
169                   Evas_Module *em;
170
171                   em = calloc(1, sizeof(Evas_Module));
172                   if (!em) continue;
173                   em->name = strdup(de->d_name);
174                   em->path = strdup(mp->path);
175                   em->type = mp->type;
176                   em->handle = NULL;
177                   em->data = NULL;
178                   em->loaded = 0;
179                   if (em->type == EVAS_MODULE_TYPE_ENGINE)
180                     {
181                        Evas_Module_Engine *eme;
182
183                        eme = malloc(sizeof(Evas_Module_Engine));
184                        if (eme)
185                          {
186                             eme->id = new_id_engine;
187                             em->data = eme;
188                             new_id_engine++;
189                          }
190                     }
191                   else if (em->type == EVAS_MODULE_TYPE_IMAGE_LOADER)
192                     {
193                     }
194                   else if (em->type == EVAS_MODULE_TYPE_IMAGE_SAVER)
195                     {
196                     }
197 /*                printf("[evas module] including module path %s/%s of type %d\n",em->path, em->name, em->type); */
198                   evas_modules = evas_list_append(evas_modules, em);
199                }
200              free(buf);
201           }
202         closedir(dir);
203      }
204 }
205
206 Evas_Module *
207 evas_module_find_type(Evas_Module_Type type, const char *name)
208 {
209    Evas_List *l;
210
211    for (l = evas_modules; l; l = l->next)
212      {
213         Evas_Module *em;
214
215         em = (Evas_Module*)l->data;
216         if ((type == em->type) && (!strcmp(name,em->name)))
217           {
218              if (evas_modules != l)
219                {
220                   evas_modules = evas_list_promote_list(evas_modules, l);
221                }
222              return em;
223           }
224      }
225    return NULL;
226 }
227
228 int
229 evas_module_load(Evas_Module *em)
230 {
231    char buf[4096];
232    void *handle;
233
234    if (em->loaded) return 1;
235
236 /*   printf("LOAD %s\n", em->name); */
237 #if defined(__CEGCC__) || defined(__MINGW32CE__)
238    switch (em->type) {
239      case EVAS_MODULE_TYPE_IMAGE_SAVER:
240         snprintf(buf, sizeof(buf), "%s/%s/%s/saver_%s.dll", em->path, em->name, MODULE_ARCH, em->name);
241         break;
242      case EVAS_MODULE_TYPE_IMAGE_LOADER:
243         snprintf(buf, sizeof(buf), "%s/%s/%s/loader_%s.dll", em->path, em->name, MODULE_ARCH, em->name);
244         break;
245      case EVAS_MODULE_TYPE_ENGINE:
246         snprintf(buf, sizeof(buf), "%s/%s/%s/engine_%s.dll", em->path, em->name, MODULE_ARCH, em->name);
247         break;
248      default:
249         snprintf(buf, sizeof(buf), "%s/%s/%s/object_%s.dll", em->path, em->name, MODULE_ARCH, em->name);
250    }
251 #elif _WIN32
252    snprintf(buf, sizeof(buf), "%s/%s/%s/module.dll", em->path, em->name, MODULE_ARCH);
253 #else
254    snprintf(buf, sizeof(buf), "%s/%s/%s/module.so", em->path, em->name, MODULE_ARCH);
255 #endif
256    if (!evas_file_path_exists(buf))
257      {
258         printf("[evas module] error loading the module %s. It doesnt exists\n", buf);
259         return 0;
260      }
261
262    handle = dlopen(buf, RTLD_LAZY);
263    if (handle)
264      {
265         em->handle = handle;
266         em->func.open = dlsym(em->handle, "module_open");
267         em->func.close = dlsym(em->handle, "module_close");
268         em->api = dlsym(em->handle, "evas_modapi");
269
270         if ((!em->func.open) || (!em->api) || (!em->func.close))
271           goto error_dl;
272         /* check the api */
273         if (em->api->version != EVAS_MODULE_API_VERSION)
274           {
275              printf("[evas module] error loading the modules %s. The version doesnt match\n", buf);
276              goto error_dl;
277           }
278         em->func.open(em);
279         em->loaded = 1;
280         return 1;
281      }
282    error_dl:
283      {
284         const char *err;
285
286         err = dlerror();
287         printf("[evas module] error loading the module %s. %s\n", buf, err);
288         if (handle)
289           dlclose(handle);
290         em->handle = NULL;
291         em->func.open = NULL;
292         em->func.close = NULL;
293         em->api = NULL;
294      }
295    return 0;
296 }
297
298 void
299 evas_module_unload(Evas_Module *em)
300 {
301    if (!em->loaded)
302      return;
303    if (em->handle)
304      {
305         em->func.close(em);
306         dlclose(em->handle);
307      }
308    em->handle = NULL;
309    em->func.open = NULL;
310    em->func.close = NULL;
311    em->api = NULL;
312    em->loaded = 0;
313 }
314
315 void
316 evas_module_ref(Evas_Module *em)
317 {
318    em->ref++;
319 /*   printf("M: %s ref++ = %i\n", em->name, em->ref); */
320 }
321
322 void
323 evas_module_unref(Evas_Module *em)
324 {
325    em->ref--;
326 /*   printf("M: %s ref-- = %i\n", em->name, em->ref); */
327 }
328
329 static int use_count = 0;
330
331 void
332 evas_module_use(Evas_Module *em)
333 {
334    em->last_used = use_count;
335 }
336
337 void
338 evas_module_clean(void)
339 {
340    static int call_count = 0;
341    int ago;
342    int noclean = -1;
343    Evas_List *l;
344    Evas_Module *em;
345
346    /* only clean modules every 256 calls */
347    call_count++;
348    if (call_count <= 256) return;
349    call_count = 0;
350
351    if (noclean == -1)
352      {
353         if (getenv("EVAS_NOCLEAN"))
354           {
355              noclean = 1;
356           }
357         noclean = 0;
358      }
359    if (noclean == 1) return;
360
361    /* disable module cleaning for now - may cause instability with some modules */
362    return;
363
364    /* incriment use counter = 28bits */
365    use_count++;
366    if (use_count > 0x0fffffff) use_count = 0;
367
368    /* printf("CLEAN!\n"); */
369    /* go through all modules */
370    for (l = evas_modules; l; l = l->next)
371      {
372         em = l->data;
373         /* printf("M %s %i %i\n", em->name, em->ref, em->loaded); */
374         /* if the module is refernced - skip */
375         if ((em->ref > 0) || (!em->loaded)) continue;
376         /* how many clean cycles ago was this module last used */
377         ago = use_count - em->last_used;
378         if (em->last_used > use_count) ago += 0x10000000;
379         /* if it was used last more than N clean cycles ago - unload */
380         if (ago > 5)
381           {
382             /* printf("  UNLOAD %s\n", em->name); */
383              evas_module_unload(em);
384           }
385      }
386 }
387
388 /* will dlclose all the modules loaded and free all the structs */
389 void
390 evas_module_shutdown(void)
391 {
392    Evas_Module *em;
393
394 /*    printf("[shutdown modules]\n"); */
395    while (evas_modules)
396      {
397         em = (Evas_Module *)evas_modules->data;
398         evas_module_unload(em);
399         if (em->name) free(em->name);
400         if (em->path) free(em->path);
401         if (em->type == EVAS_MODULE_TYPE_ENGINE)
402           {
403              if (em->data) free(em->data);
404           }
405         else if (em->type == EVAS_MODULE_TYPE_IMAGE_LOADER)
406           {
407           }
408         else if (em->type == EVAS_MODULE_TYPE_IMAGE_SAVER)
409           {
410           }
411         free(evas_modules->data);
412         evas_modules = evas_list_remove_list(evas_modules, evas_modules);
413      }
414    while (evas_module_paths)
415      {
416         Evas_Module_Path *mp;
417
418         mp = evas_module_paths->data;
419         evas_module_paths = evas_list_remove_list(evas_module_paths, evas_module_paths);
420         free(mp->path);
421         free(mp);
422      }
423 }
424
425 EAPI int
426 _evas_module_engine_inherit(Evas_Func *funcs, char *name)
427 {
428    Evas_Module *em;
429
430    em = evas_module_find_type(EVAS_MODULE_TYPE_ENGINE, name);
431    if (em)
432      {
433         if (evas_module_load(em))
434           {
435              /* FIXME: no way to unref */
436              evas_module_ref(em);
437              evas_module_use(em);
438              *funcs = *((Evas_Func *)(em->functions));
439              return 1;
440           }
441      }
442    return 0;
443 }