1 /* -*- mode: c; c-file-style: "gnu" -*-
2  * ccze-plugin.c -- Plugin interface for CCZE.
3  * Copyright (C) 2002, 2003 Gergely Nagy <algernon@bonehunter.rulez.org>
4  *
5  * This file is part of ccze.
6  *
7  * ccze is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * ccze is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15  * License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 
22 #include <sys/types.h>
23 #include <ccze.h>
24 #include <dirent.h>
25 #include <dlfcn.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "ccze-private.h"
32 
33 static ccze_plugin_t **plugins;
34 static size_t plugins_alloc, plugins_len;
35 static ccze_plugin_t **plugin_args;
36 static size_t plugin_args_alloc, plugin_args_len;
37 static char *plugin_running = NULL;
38 
39 static ccze_plugin_t *
_ccze_plugin_find(const char * name)40 _ccze_plugin_find (const char *name)
41 {
42   size_t i;
43 
44   for (i = 0; i < plugins_len; i++)
45     {
46       if (!strcmp (plugins[i]->name, name))
47 	return plugins[i];
48     }
49   return NULL;
50 }
51 
52 static int
_ccze_plugin_loaded(const char * name)53 _ccze_plugin_loaded (const char *name)
54 {
55   if (_ccze_plugin_find (name))
56     return 1;
57 
58   return 0;
59 }
60 
61 static int
_ccze_plugin_allow(const char * name)62 _ccze_plugin_allow (const char *name)
63 {
64   int i = 0;
65   int rval = 0;
66 
67   if (_ccze_plugin_loaded (name))
68     return 0;
69   if (ccze_config.pluginlist_len == 0)
70     return 1;
71 
72   while (i < ccze_config.pluginlist_len)
73     {
74       if (!strcmp (ccze_config.pluginlist[i], name))
75 	{
76 	  rval = 1;
77 	  break;
78 	}
79       i++;
80     }
81 
82   return rval;
83 }
84 
85 void
ccze_plugin_add(ccze_plugin_t * plugin)86 ccze_plugin_add (ccze_plugin_t *plugin)
87 {
88   if (!_ccze_plugin_allow (plugin->name))
89     return;
90 
91   plugins[plugins_len] = plugin;
92   plugins_len++;
93   if (plugins_len >= plugins_alloc)
94     {
95       plugins_alloc *= 2;
96       plugins = (ccze_plugin_t **)ccze_realloc
97 	(plugins, plugins_alloc * sizeof (ccze_plugin_t *));
98     }
99 }
100 
101 void
ccze_plugin_init(void)102 ccze_plugin_init (void)
103 {
104   plugins_alloc = 10;
105   plugins_len = 0;
106   plugins = (ccze_plugin_t **)ccze_calloc (plugins_alloc,
107 					   sizeof (ccze_plugin_t *));
108 }
109 
110 void
ccze_plugin_argv_init(void)111 ccze_plugin_argv_init (void)
112 {
113   plugin_args_alloc = 10;
114   plugin_args_len = 0;
115   plugin_args = (ccze_plugin_t **)ccze_calloc (plugin_args_alloc,
116 					       sizeof (ccze_plugin_t *));
117 }
118 
119 static void
_ccze_plugin_load(const char * name,const char * path,int recurse)120 _ccze_plugin_load (const char *name, const char *path, int recurse)
121 {
122   ccze_plugin_t *plugin;
123   char *tmp, *n;
124   void *dlhandle;
125   char **pluginlist = NULL;
126   int bailout = 0;
127 
128   if (_ccze_plugin_loaded (name))
129     return;
130 
131   dlhandle = dlopen (path, RTLD_LAZY);
132   if (dlerror () || !dlhandle)
133     return;
134 
135   asprintf (&tmp, "ccze_%s_info", name);
136   plugin = (ccze_plugin_t *)dlsym (dlhandle, tmp);
137   free (tmp);
138   if (dlerror () || !plugin)
139     {
140       if (!plugin)
141 	{
142 	  n = (char *)dlsym (dlhandle, "ccze_default_plugin");
143 	  asprintf (&tmp, "ccze_%s_info", n);
144 	  plugin = (ccze_plugin_t *)dlsym (dlhandle, tmp);
145 	  free (tmp);
146 	  if (!plugin)
147 	    bailout = 1;
148 	  else
149 	    plugin->name = strdup (name);
150 	}
151       else
152 	{
153 	  dlclose (dlhandle);
154 	  return;
155 	}
156     }
157   if (!bailout)
158     {
159       if (plugin->abi_version != CCZE_ABI_VERSION)
160 	{
161 	  dlclose (dlhandle);
162 	  return;
163 	}
164 
165       plugin->dlhandle = dlhandle;
166 
167       ccze_plugin_add (plugin);
168     }
169 
170   /* If there are more plugins defined in the file, load them all! */
171   if (recurse)
172     {
173       pluginlist = (char **)dlsym (dlhandle, "ccze_plugin_list");
174       if (pluginlist)
175 	{
176 	  int i = 0;
177 
178 	  while (pluginlist[i])
179 	    {
180 	      if (!_ccze_plugin_loaded (pluginlist[i]))
181 		_ccze_plugin_load (pluginlist[i], path, 0);
182 	      i++;
183 	    }
184 	}
185     }
186 }
187 
188 void
ccze_plugin_load(const char * name)189 ccze_plugin_load (const char *name)
190 {
191   char *home;
192   char *path;
193 
194   if ((home = getenv ("HOME")) != NULL)
195     {
196       asprintf (&path, "%s/.ccze/%s.so", home, name);
197       if (access (path, F_OK))
198 	{
199 	  free (path);
200 	  asprintf (&path, PKGLIBDIR "/%s.so", name);
201 	}
202     }
203   else
204     asprintf (&path, PKGLIBDIR "/%s.so", name);
205 
206   _ccze_plugin_load (name, path, 1);
207   free (path);
208 }
209 
210 static int
_ccze_plugin_select(const struct dirent * de)211 _ccze_plugin_select (const struct dirent *de)
212 {
213   if (strstr (de->d_name, ".so"))
214     return 1;
215   return 0;
216 }
217 
218 static void
_ccze_plugin_load_set(struct dirent *** namelist,int nn,const char * base)219 _ccze_plugin_load_set (struct dirent ***namelist, int nn, const char *base)
220 {
221   int m, n = nn;
222 
223   m = 0;
224   while (m < n)
225     {
226       char *tmp = strdup ((*namelist)[m]->d_name);
227       char *tmp2 = strstr (tmp, ".so");
228       char *path;
229       tmp2[0] = '\0';
230 
231       if (!_ccze_plugin_loaded (tmp))
232 	{
233 	  asprintf (&path, "%s/%s.so", base, tmp);
234 	  _ccze_plugin_load (tmp, path, 1);
235 	  free (path);
236 	}
237       free (tmp);
238       free ((*namelist)[m]);
239       m++;
240     }
241   free (*namelist);
242 }
243 
244 void
ccze_plugin_load_all(void)245 ccze_plugin_load_all (void)
246 {
247   struct dirent **namelist;
248   int n;
249   char *homeplugs, *home;
250 
251   if ((home = getenv ("HOME")) != NULL)
252     {
253       asprintf (&homeplugs, "%s/.ccze", home);
254       n = scandir (homeplugs, &namelist, _ccze_plugin_select, alphasort);
255       if (n != -1)
256 	_ccze_plugin_load_set (&namelist, n, homeplugs);
257       free (homeplugs);
258     }
259 
260   n = scandir (PKGLIBDIR, &namelist, _ccze_plugin_select, alphasort);
261   if (n != -1)
262     _ccze_plugin_load_set (&namelist, n, PKGLIBDIR);
263 }
264 
265 void
ccze_plugin_finalise(void)266 ccze_plugin_finalise (void)
267 {
268   plugins[plugins_len] = NULL;
269 }
270 
271 ccze_plugin_t **
ccze_plugins(void)272 ccze_plugins (void)
273 {
274   return plugins;
275 }
276 
277 void
ccze_plugin_setup(void)278 ccze_plugin_setup (void)
279 {
280   size_t i;
281 
282   for (i = 0; i < plugins_len; i++)
283     {
284       if (plugins[i])
285 	{
286 	  plugin_running = plugins[i]->name;
287 	  (*(plugins[i]->startup))();
288 	}
289     }
290   plugin_running = NULL;
291 }
292 
293 void
ccze_plugin_shutdown(void)294 ccze_plugin_shutdown (void)
295 {
296   size_t i;
297 
298   for (i = 0; i < plugins_len; i++)
299     {
300       if (plugins[i])
301 	{
302 	  plugin_running = plugins[i]->name;
303 	  (*(plugins[i]->shutdown)) ();
304 	  free (plugins[i]->argv);
305 	  if (plugins[i]->dlhandle)
306 	    dlclose (plugins[i]->dlhandle);
307 	}
308     }
309 
310   plugin_running = NULL;
311   free (plugins);
312 }
313 
314 void
ccze_plugin_run(ccze_plugin_t ** pluginset,char * subject,size_t subjlen,char ** rest,ccze_plugin_type_t type,int * handled,int * status)315 ccze_plugin_run (ccze_plugin_t **pluginset, char *subject, size_t subjlen,
316 		 char **rest, ccze_plugin_type_t type, int *handled,
317 		 int *status)
318 {
319   int i = 0;
320 
321   while (pluginset[i])
322     {
323       if (pluginset[i]->type == type ||
324 	  pluginset[i]->type == CCZE_PLUGIN_TYPE_ANY)
325 	{
326 	  plugin_running = pluginset[i]->name;
327 	  if ((*handled = (*(pluginset[i]->handler))
328 	       (subject, subjlen, rest)) != 0)
329 	    {
330 	      *status = *handled;
331 	      break;
332 	    }
333 	}
334       i++;
335     }
336   plugin_running = NULL;
337 }
338 
339 char **
ccze_plugin_argv_get(const char * name)340 ccze_plugin_argv_get (const char *name)
341 {
342   ccze_plugin_t *p = _ccze_plugin_find (name);
343 
344   if (!p)
345     return NULL;
346   return p->argv;
347 }
348 
349 int
ccze_plugin_argv_set(const char * name,const char * args)350 ccze_plugin_argv_set (const char *name, const char *args)
351 {
352   ccze_plugin_t *p = NULL;
353   size_t i, j = 1;
354   char *args_copy, *arg;
355 
356   if (!args || !name)
357     return -1;
358 
359   for (i = 0; i < plugin_args_len; i++)
360     {
361       if (!strcmp (plugin_args[i]->name, name))
362 	p = plugin_args[i];
363     }
364 
365   if (p)
366     {
367       free (p->argv);
368       p->argv = NULL;
369     }
370   else
371     {
372       p = (ccze_plugin_t *)ccze_malloc (sizeof (ccze_plugin_t));
373       p->name = strdup (name);
374       p->argv = NULL;
375       i = plugin_args_len++;
376       if (plugin_args_len >= plugin_args_alloc)
377 	{
378 	  plugin_args_alloc *= 2;
379 	  plugin_args = (ccze_plugin_t **)ccze_realloc
380 	    (plugin_args, plugin_args_alloc * sizeof (ccze_plugin_t *));
381 	}
382     }
383 
384   args_copy = strdup (args);
385   arg = strtok (args_copy, " \t\n");
386   do
387     {
388       p->argv = (char **)ccze_realloc (p->argv, (j + 1) * sizeof (char *));
389       p->argv[j++] = strdup (arg);
390     } while ((arg = strtok (NULL, " \t\n")) != NULL);
391   p->argv = (char **)ccze_realloc (p->argv, (j + 1) * sizeof (char *));
392   p->argv[j] = NULL;
393   p->argv[0] = strdup (name);
394   plugin_args[i] = p;
395   free (args_copy);
396 
397   return 1;
398 }
399 
400 void
ccze_plugin_argv_finalise(void)401 ccze_plugin_argv_finalise (void)
402 {
403   ccze_plugin_t *p;
404   size_t i;
405 
406   for (i = 0; i < plugin_args_len; i++)
407     {
408       p = _ccze_plugin_find (plugin_args[i]->name);
409       if (p)
410 	p->argv = plugin_args[i]->argv;
411       free (plugin_args[i]);
412     }
413   free (plugin_args);
414 }
415 
416 const char *
ccze_plugin_name_get(void)417 ccze_plugin_name_get (void)
418 {
419   return plugin_running;
420 }
421 
422 int
ccze_plugin_list_fancy(void)423 ccze_plugin_list_fancy (void)
424 {
425   size_t i;
426 
427   printf ("Available plugins:\n\n");
428   printf ("%-10s| %-8s| %s\n", "Name", "Type", "Description");
429   printf ("------------------------------------------------------------\n");
430 
431   for (i = 0; i < plugins_len; i++)
432     {
433       if (plugins[i])
434 	{
435 	  char *type = "Unknown";
436 
437 	  switch (plugins[i]->type)
438 	    {
439 	    case CCZE_PLUGIN_TYPE_FULL:
440 	      type = "Full";
441 	      break;
442 	    case CCZE_PLUGIN_TYPE_PARTIAL:
443 	      type = "Partial";
444 	      break;
445 	    case CCZE_PLUGIN_TYPE_ANY:
446 	      type = "Any";
447 	      break;
448 	    }
449 	  printf ("%-10s| %-8s| %s\n", plugins[i]->name, type,
450 		  (plugins[i]->desc) ? plugins[i]->desc : "Undocumented.");
451 	}
452     }
453   plugin_running = NULL;
454 
455   return 0;
456 }
457