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