1 /*
2 * Copyright © 2005 Novell, Inc.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of
9 * Novell, Inc. not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior permission.
11 * Novell, Inc. makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
13 * implied warranty.
14 *
15 * NOVELL, INC. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL NOVELL, INC. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Author: David Reveman <davidr@novell.com>
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <dlfcn.h>
30 #include <dirent.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #include <compiz-core.h>
37
38 CompPlugin *plugins = 0;
39
40 static Bool
coreInit(CompPlugin * p)41 coreInit (CompPlugin *p)
42 {
43 return TRUE;
44 }
45
46 static void
coreFini(CompPlugin * p)47 coreFini (CompPlugin *p)
48 {
49 }
50
51 static CompMetadata *
coreGetMetadata(CompPlugin * plugin)52 coreGetMetadata (CompPlugin *plugin)
53 {
54 return &coreMetadata;
55 }
56
57 static CompOption *
coreGetObjectOptions(CompPlugin * plugin,CompObject * object,int * count)58 coreGetObjectOptions (CompPlugin *plugin,
59 CompObject *object,
60 int *count)
61 {
62 static GetPluginObjectOptionsProc dispTab[] = {
63 (GetPluginObjectOptionsProc) 0, /* GetCoreOptions */
64 (GetPluginObjectOptionsProc) getDisplayOptions,
65 (GetPluginObjectOptionsProc) getScreenOptions
66 };
67
68 *count = 0;
69 RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab),
70 NULL, (plugin, object, count));
71 }
72
73 static Bool
coreSetObjectOption(CompPlugin * plugin,CompObject * object,const char * name,CompOptionValue * value)74 coreSetObjectOption (CompPlugin *plugin,
75 CompObject *object,
76 const char *name,
77 CompOptionValue *value)
78 {
79 static SetPluginObjectOptionProc dispTab[] = {
80 (SetPluginObjectOptionProc) 0, /* SetCoreOption */
81 (SetPluginObjectOptionProc) setDisplayOption,
82 (SetPluginObjectOptionProc) setScreenOption
83 };
84
85 RETURN_DISPATCH (object, dispTab, ARRAY_SIZE (dispTab), FALSE,
86 (plugin, object, name, value));
87 }
88
89 static CompPluginVTable coreVTable = {
90 "core",
91 coreGetMetadata,
92 coreInit,
93 coreFini,
94 0, /* InitObject */
95 0, /* FiniObject */
96 coreGetObjectOptions,
97 coreSetObjectOption
98 };
99
100 static Bool
cloaderLoadPlugin(CompPlugin * p,const char * path,const char * name)101 cloaderLoadPlugin (CompPlugin *p,
102 const char *path,
103 const char *name)
104 {
105 if (path)
106 return FALSE;
107
108 if (strcmp (name, coreVTable.name))
109 return FALSE;
110
111 p->vTable = &coreVTable;
112 p->devPrivate.ptr = NULL;
113 p->devType = "cloader";
114
115 return TRUE;
116 }
117
118 static void
cloaderUnloadPlugin(CompPlugin * p)119 cloaderUnloadPlugin (CompPlugin *p)
120 {
121 }
122
123 static char **
cloaderListPlugins(const char * path,int * n)124 cloaderListPlugins (const char *path,
125 int *n)
126 {
127 char **list;
128
129 if (path)
130 return 0;
131
132 list = malloc (sizeof (char *));
133 if (!list)
134 return 0;
135
136 *list = strdup (coreVTable.name);
137 if (!*list)
138 {
139 free (list);
140 return 0;
141 }
142
143 *n = 1;
144
145 return list;
146 }
147
148 static Bool
dlloaderLoadPlugin(CompPlugin * p,const char * path,const char * name)149 dlloaderLoadPlugin (CompPlugin *p,
150 const char *path,
151 const char *name)
152 {
153 char *file;
154 void *dlhand;
155 struct stat fileInfo;
156 Bool loaded = FALSE;
157
158 if (cloaderLoadPlugin (p, path, name))
159 return TRUE;
160
161 file = malloc ((path ? strlen (path) : 0) + strlen (name) + 8);
162 if (!file)
163 return FALSE;
164
165 if (path)
166 sprintf (file, "%s/lib%s.so", path, name);
167 else
168 sprintf (file, "lib%s.so", name);
169
170 if (stat (file, &fileInfo) != 0)
171 {
172 /* file likely not present */
173 compLogMessage ("core", CompLogLevelDebug,
174 "Could not stat() file %s : %s",
175 file, strerror (errno));
176 free (file);
177 return FALSE;
178 }
179
180 dlhand = dlopen (file, RTLD_LAZY);
181 if (dlhand)
182 {
183 PluginGetInfoProc getInfo;
184 char *error;
185
186 dlerror ();
187
188 getInfo = (PluginGetInfoProc) dlsym (dlhand,
189 "getCompPluginInfo20070830");
190
191 error = dlerror ();
192 if (error)
193 {
194 compLogMessage ("core", CompLogLevelError, "dlsym: %s", error);
195
196 getInfo = 0;
197 }
198
199 if (getInfo)
200 {
201 p->vTable = (*getInfo) ();
202 if (!p->vTable)
203 {
204 compLogMessage ("core", CompLogLevelError,
205 "Couldn't get vtable from '%s' plugin",
206 file);
207 }
208 else
209 {
210 p->devPrivate.ptr = dlhand;
211 p->devType = "dlloader";
212 loaded = TRUE;
213 }
214 }
215 }
216 else
217 {
218 compLogMessage ("core", CompLogLevelError,
219 "Couldn't load plugin '%s' : %s", file, dlerror ());
220 }
221
222 free (file);
223
224 if (!loaded && dlhand)
225 dlclose (dlhand);
226
227 return loaded;
228 }
229
230 static void
dlloaderUnloadPlugin(CompPlugin * p)231 dlloaderUnloadPlugin (CompPlugin *p)
232 {
233 if (strcmp (p->devType, "dlloader") == 0)
234 dlclose (p->devPrivate.ptr);
235 else
236 cloaderUnloadPlugin (p);
237 }
238
239 static int
dlloaderFilter(const struct dirent * name)240 dlloaderFilter (const struct dirent *name)
241 {
242 int length = strlen (name->d_name);
243
244 if (length < 7)
245 return 0;
246
247 if (strncmp (name->d_name, "lib", 3) ||
248 strncmp (name->d_name + length - 3, ".so", 3))
249 return 0;
250
251 return 1;
252 }
253
254 static char **
dlloaderListPlugins(const char * path,int * n)255 dlloaderListPlugins (const char *path,
256 int *n)
257 {
258 struct dirent **nameList;
259 char **list, **cList;
260 char *name;
261 int length, nFile, i, j = 0;
262
263 cList = cloaderListPlugins (path, n);
264 if (cList)
265 j = *n;
266
267 if (!path)
268 path = ".";
269
270 nFile = scandir (path, &nameList, dlloaderFilter, alphasort);
271 if (!nFile)
272 return cList;
273
274 list = realloc (cList, (j + nFile) * sizeof (char *));
275 if (!list)
276 return cList;
277
278 for (i = 0; i < nFile; i++)
279 {
280 length = strlen (nameList[i]->d_name);
281
282 name = malloc ((length - 5) * sizeof (char));
283 if (name)
284 {
285 strncpy (name, nameList[i]->d_name + 3, length - 6);
286 name[length - 6] = '\0';
287
288 list[j++] = name;
289 }
290 }
291
292 if (j)
293 {
294 *n = j;
295
296 return list;
297 }
298
299 free (list);
300
301 return NULL;
302 }
303
304 LoadPluginProc loaderLoadPlugin = dlloaderLoadPlugin;
305 UnloadPluginProc loaderUnloadPlugin = dlloaderUnloadPlugin;
306 ListPluginsProc loaderListPlugins = dlloaderListPlugins;
307
308 typedef struct _InitObjectContext {
309 CompPlugin *plugin;
310 CompObject *object;
311 } InitObjectContext;
312
313 typedef struct _InitObjectTypeContext {
314 CompPlugin *plugin;
315 CompObjectType type;
316 } InitObjectTypeContext;
317
318 static CompBool
319 initObjectTree (CompObject *object,
320 void *closure);
321
322 static CompBool
323 finiObjectTree (CompObject *object,
324 void *closure);
325
326 static CompBool
initObjectsWithType(CompObjectType type,CompObject * parent,void * closure)327 initObjectsWithType (CompObjectType type,
328 CompObject *parent,
329 void *closure)
330 {
331 InitObjectTypeContext *pCtx = (InitObjectTypeContext *) closure;
332 InitObjectContext ctx;
333
334 pCtx->type = type;
335
336 ctx.plugin = pCtx->plugin;
337 ctx.object = NULL;
338
339 if (!compObjectForEach (parent, type, initObjectTree, (void *) &ctx))
340 {
341 compObjectForEach (parent, type, finiObjectTree, (void *) &ctx);
342
343 return FALSE;
344 }
345
346 return TRUE;
347 }
348
349 static CompBool
finiObjectsWithType(CompObjectType type,CompObject * parent,void * closure)350 finiObjectsWithType (CompObjectType type,
351 CompObject *parent,
352 void *closure)
353 {
354 InitObjectTypeContext *pCtx = (InitObjectTypeContext *) closure;
355 InitObjectContext ctx;
356
357 /* pCtx->type is set to the object type that failed to be initialized */
358 if (pCtx->type == type)
359 return FALSE;
360
361 ctx.plugin = pCtx->plugin;
362 ctx.object = NULL;
363
364 compObjectForEach (parent, type, finiObjectTree, (void *) &ctx);
365
366 return TRUE;
367 }
368
369 static CompBool
initObjectTree(CompObject * object,void * closure)370 initObjectTree (CompObject *object,
371 void *closure)
372 {
373 InitObjectContext *pCtx = (InitObjectContext *) closure;
374 CompPlugin *p = pCtx->plugin;
375 InitObjectTypeContext ctx;
376
377 pCtx->object = object;
378
379 if (p->vTable->initObject)
380 {
381 if (!(*p->vTable->initObject) (p, object))
382 {
383 compLogMessage (p->vTable->name, CompLogLevelError,
384 "InitObject failed");
385 return FALSE;
386 }
387 }
388
389 ctx.plugin = p;
390 ctx.type = 0;
391
392 /* initialize children */
393 if (!compObjectForEachType (object, initObjectsWithType, (void *) &ctx))
394 {
395 compObjectForEachType (object, finiObjectsWithType, (void *) &ctx);
396
397 if (p->vTable->initObject && p->vTable->finiObject)
398 (*p->vTable->finiObject) (p, object);
399
400 return FALSE;
401 }
402
403 if (!(*core.initPluginForObject) (p, object))
404 {
405 compObjectForEachType (object, finiObjectsWithType, (void *) &ctx);
406
407 if (p->vTable->initObject && p->vTable->finiObject)
408 (*p->vTable->finiObject) (p, object);
409
410 return FALSE;
411 }
412
413 return TRUE;
414 }
415
416 static CompBool
finiObjectTree(CompObject * object,void * closure)417 finiObjectTree (CompObject *object,
418 void *closure)
419 {
420 InitObjectContext *pCtx = (InitObjectContext *) closure;
421 CompPlugin *p = pCtx->plugin;
422 InitObjectTypeContext ctx;
423
424 /* pCtx->object is set to the object that failed to be initialized */
425 if (pCtx->object == object)
426 return FALSE;
427
428 ctx.plugin = p;
429 ctx.type = ~0;
430
431 compObjectForEachType (object, finiObjectsWithType, (void *) &ctx);
432
433 if (p->vTable->initObject && p->vTable->finiObject)
434 (*p->vTable->finiObject) (p, object);
435
436 (*core.finiPluginForObject) (p, object);
437
438 return TRUE;
439 }
440
441 static Bool
initPlugin(CompPlugin * p)442 initPlugin (CompPlugin *p)
443 {
444 InitObjectContext ctx;
445
446 if (!(*p->vTable->init) (p))
447 {
448 compLogMessage ("core", CompLogLevelError,
449 "InitPlugin '%s' failed", p->vTable->name);
450 return FALSE;
451 }
452
453 ctx.plugin = p;
454 ctx.object = NULL;
455
456 if (!initObjectTree (&core.base, (void *) &ctx))
457 {
458 (*p->vTable->fini) (p);
459 return FALSE;
460 }
461
462 return TRUE;
463 }
464
465 static void
finiPlugin(CompPlugin * p)466 finiPlugin (CompPlugin *p)
467 {
468 InitObjectContext ctx;
469
470 ctx.plugin = p;
471 ctx.object = NULL;
472
473 finiObjectTree (&core.base, (void *) &ctx);
474
475 (*p->vTable->fini) (p);
476 }
477
478 CompBool
objectInitPlugins(CompObject * o)479 objectInitPlugins (CompObject *o)
480 {
481 InitObjectContext ctx;
482 CompPlugin *p;
483 int i, j = 0;
484
485 ctx.object = NULL;
486
487 for (p = plugins; p; p = p->next)
488 j++;
489
490 while (j--)
491 {
492 i = 0;
493 for (p = plugins; i < j; p = p->next)
494 i++;
495
496 ctx.plugin = p;
497
498 if (!initObjectTree (o, (void *) &ctx))
499 {
500 for (p = p->next; p; p = p->next)
501 {
502 ctx.plugin = p;
503
504 finiObjectTree (o, (void *) &ctx);
505 }
506
507 return FALSE;
508 }
509 }
510
511 return TRUE;
512 }
513
514 void
objectFiniPlugins(CompObject * o)515 objectFiniPlugins (CompObject *o)
516 {
517 InitObjectContext ctx;
518 CompPlugin *p;
519
520 ctx.object = NULL;
521
522 for (p = plugins; p; p = p->next)
523 {
524 ctx.plugin = p;
525
526 finiObjectTree (o, (void *) &ctx);
527 }
528 }
529
530 CompPlugin *
findActivePlugin(const char * name)531 findActivePlugin (const char *name)
532 {
533 CompPlugin *p;
534
535 for (p = plugins; p; p = p->next)
536 {
537 if (strcmp (p->vTable->name, name) == 0)
538 return p;
539 }
540
541 return 0;
542 }
543
544 void
unloadPlugin(CompPlugin * p)545 unloadPlugin (CompPlugin *p)
546 {
547 (*loaderUnloadPlugin) (p);
548 free (p);
549 }
550
551 CompPlugin *
loadPlugin(const char * name)552 loadPlugin (const char *name)
553 {
554 CompPlugin *p;
555 char *home, *plugindir;
556 Bool status;
557
558 p = malloc (sizeof (CompPlugin));
559 if (!p)
560 return 0;
561
562 p->next = 0;
563 p->devPrivate.uval = 0;
564 p->devType = NULL;
565 p->vTable = 0;
566
567 home = getenv ("HOME");
568 if (home)
569 {
570 plugindir = malloc (strlen (home) + strlen (HOME_PLUGINDIR) + 3);
571 if (plugindir)
572 {
573 sprintf (plugindir, "%s/%s", home, HOME_PLUGINDIR);
574 status = (*loaderLoadPlugin) (p, plugindir, name);
575 free (plugindir);
576
577 if (status)
578 return p;
579 }
580 }
581
582 status = (*loaderLoadPlugin) (p, PLUGINDIR, name);
583 if (status)
584 return p;
585
586 status = (*loaderLoadPlugin) (p, NULL, name);
587 if (status)
588 return p;
589
590 compLogMessage ("core", CompLogLevelError,
591 "Couldn't load plugin '%s'", name);
592
593 free (p);
594
595 return 0;
596 }
597
598 Bool
pushPlugin(CompPlugin * p)599 pushPlugin (CompPlugin *p)
600 {
601 if (findActivePlugin (p->vTable->name))
602 {
603 compLogMessage ("core", CompLogLevelWarn,
604 "Plugin '%s' already active",
605 p->vTable->name);
606
607 return FALSE;
608 }
609
610 p->next = plugins;
611 plugins = p;
612
613 if (!initPlugin (p))
614 {
615 compLogMessage ("core", CompLogLevelError,
616 "Couldn't activate plugin '%s'", p->vTable->name);
617 plugins = p->next;
618
619 return FALSE;
620 }
621
622 return TRUE;
623 }
624
625 CompPlugin *
popPlugin(void)626 popPlugin (void)
627 {
628 CompPlugin *p = plugins;
629
630 if (!p)
631 return 0;
632
633 finiPlugin (p);
634
635 plugins = p->next;
636
637 return p;
638 }
639
640 CompPlugin *
getPlugins(void)641 getPlugins (void)
642 {
643 return plugins;
644 }
645
646 static Bool
stringExist(char ** list,int nList,char * s)647 stringExist (char **list,
648 int nList,
649 char *s)
650 {
651 int i;
652
653 for (i = 0; i < nList; i++)
654 if (strcmp (list[i], s) == 0)
655 return TRUE;
656
657 return FALSE;
658 }
659
660 char **
availablePlugins(int * n)661 availablePlugins (int *n)
662 {
663 char *home, *plugindir;
664 char **list, **currentList, **pluginList, **homeList = NULL;
665 int nCurrentList, nPluginList, nHomeList;
666 int count, i, j;
667
668 home = getenv ("HOME");
669 if (home)
670 {
671 plugindir = malloc (strlen (home) + strlen (HOME_PLUGINDIR) + 3);
672 if (plugindir)
673 {
674 sprintf (plugindir, "%s/%s", home, HOME_PLUGINDIR);
675 homeList = (*loaderListPlugins) (plugindir, &nHomeList);
676 free (plugindir);
677 }
678 }
679
680 pluginList = (*loaderListPlugins) (PLUGINDIR, &nPluginList);
681 currentList = (*loaderListPlugins) (NULL, &nCurrentList);
682
683 count = 0;
684 if (homeList)
685 count += nHomeList;
686 if (pluginList)
687 count += nPluginList;
688 if (currentList)
689 count += nCurrentList;
690
691 if (!count)
692 return NULL;
693
694 list = malloc (count * sizeof (char *));
695 if (!list)
696 return NULL;
697
698 j = 0;
699 if (homeList)
700 {
701 for (i = 0; i < nHomeList; i++)
702 if (!stringExist (list, j, homeList[i]))
703 list[j++] = homeList[i];
704
705 free (homeList);
706 }
707
708 if (pluginList)
709 {
710 for (i = 0; i < nPluginList; i++)
711 if (!stringExist (list, j, pluginList[i]))
712 list[j++] = pluginList[i];
713
714 free (pluginList);
715 }
716
717 if (currentList)
718 {
719 for (i = 0; i < nCurrentList; i++)
720 if (!stringExist (list, j, currentList[i]))
721 list[j++] = currentList[i];
722
723 free (currentList);
724 }
725
726 *n = j;
727
728 return list;
729 }
730
731 int
getPluginABI(const char * name)732 getPluginABI (const char *name)
733 {
734 CompPlugin *p = findActivePlugin (name);
735 CompOption *option;
736 int nOption;
737
738 if (!p || !p->vTable->getObjectOptions)
739 return 0;
740
741 /* MULTIDPYERROR: ABI options should be moved into core */
742 option = (*p->vTable->getObjectOptions) (p, &core.displays->base,
743 &nOption);
744
745 return getIntOptionNamed (option, nOption, "abi", 0);
746 }
747
748 Bool
checkPluginABI(const char * name,int abi)749 checkPluginABI (const char *name,
750 int abi)
751 {
752 int pluginABI;
753
754 pluginABI = getPluginABI (name);
755 if (!pluginABI)
756 {
757 compLogMessage ("core", CompLogLevelError,
758 "Plugin '%s' not loaded.\n", name);
759 return FALSE;
760 }
761 else if (pluginABI != abi)
762 {
763 compLogMessage ("core", CompLogLevelError,
764 "Plugin '%s' has ABI version '%d', expected "
765 "ABI version '%d'.\n",
766 name, pluginABI, abi);
767 return FALSE;
768 }
769
770 return TRUE;
771 }
772
773 Bool
getPluginDisplayIndex(CompDisplay * d,const char * name,int * index)774 getPluginDisplayIndex (CompDisplay *d,
775 const char *name,
776 int *index)
777 {
778 CompPlugin *p = findActivePlugin (name);
779 CompOption *option;
780 int nOption, value;
781
782 if (!p || !p->vTable->getObjectOptions)
783 return FALSE;
784
785 option = (*p->vTable->getObjectOptions) (p, &d->base, &nOption);
786
787 value = getIntOptionNamed (option, nOption, "index", -1);
788 if (value < 0)
789 return FALSE;
790
791 *index = value;
792
793 return TRUE;
794 }
795