WIP: fsctl_pipe_wait
[metze/wireshark/wip.git] / wsutil / plugins.c
1 /* plugins.c
2  * plugin routines
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #ifdef HAVE_PLUGINS
28
29 #include <time.h>
30
31 #ifdef HAVE_DIRENT_H
32 #include <dirent.h>
33 #endif
34
35 #ifdef HAVE_DIRECT_H
36 #include <direct.h>
37 #endif
38
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <errno.h>
43
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47
48 #include <glib.h>
49 #include <gmodule.h>
50
51 #include <wsutil/filesystem.h>
52 #include <wsutil/privileges.h>
53 #include <wsutil/file_util.h>
54 #include <wsutil/report_err.h>
55
56 #include <wsutil/plugins.h>
57
58 /* linked list of all plugins */
59 typedef struct _plugin {
60     GModule        *handle;       /* handle returned by g_module_open */
61     gchar          *name;         /* plugin name */
62     gchar          *version;      /* plugin version */
63     guint32         types;        /* bitmask of plugin types this plugin supports */
64     struct _plugin *next;         /* forward link */
65 } plugin;
66
67 static plugin *plugin_list = NULL;
68
69 /*
70  * Add a new plugin type.
71  * Takes a callback routine as an argument; it is called for each plugin
72  * we find, and handed a handle for the plugin, the name of the plugin,
73  * and the version string for the plugin.  The plugin returns TRUE if
74  * it's a plugin for that type and FALSE if not.
75  */
76 typedef struct {
77     const char *type;
78     plugin_callback callback;
79     guint type_val;
80 } plugin_type;
81
82 static GSList *plugin_types = NULL;
83
84 void
85 add_plugin_type(const char *type, plugin_callback callback)
86 {
87     plugin_type *new_type;
88     static guint type_val;
89
90     if (type_val >= 32) {
91         /*
92          * There's a bitmask of types that a plugin provides, and it's
93          * 32 bits, so we don't support types > 31.
94          */
95         report_failure("At most 32 plugin types can be supported, so the plugin type '%s' won't be supported.",
96                        type);
97         return;
98     }        
99     new_type = (plugin_type *)g_malloc(sizeof (plugin_type));
100     new_type->type = type;
101     new_type->callback = callback;
102     new_type->type_val = type_val;
103     plugin_types = g_slist_append(plugin_types, new_type);
104     type_val++;
105 }
106
107 /*
108  * add a new plugin to the list
109  * returns :
110  * - 0 : OK
111  * - ENOMEM : memory allocation problem
112  * - EEXIST : the same plugin (i.e. name/version) was already registered.
113  */
114 static int
115 add_plugin(plugin *new_plug)
116 {
117     plugin *pt_plug;
118
119     pt_plug = plugin_list;
120     if (!pt_plug) /* the list is empty */
121     {
122         plugin_list = new_plug;
123     }
124     else
125     {
126         while (1)
127         {
128             /* check if the same name/version is already registered */
129             if (strcmp(pt_plug->name, new_plug->name) == 0 &&
130                 strcmp(pt_plug->version, new_plug->version) == 0)
131             {
132                 return EEXIST;
133             }
134
135             /* we found the last plugin in the list */
136             if (pt_plug->next == NULL)
137                 break;
138
139             pt_plug = pt_plug->next;
140         }
141         pt_plug->next = new_plug;
142     }
143
144     return 0;
145 }
146
147 static void
148 call_plugin_callback(gpointer data, gpointer user_data)
149 {
150     plugin_type *type = (plugin_type *)data;
151     plugin *new_plug = (plugin *)user_data;
152
153     if ((*type->callback)(new_plug->handle)) {
154         /* The plugin supports this type */
155         new_plug->types |= 1 << type->type_val;
156     }
157 }
158
159 static void
160 plugins_scan_dir(const char *dirname)
161 {
162 #define FILENAME_LEN        1024
163     WS_DIR        *dir;             /* scanned directory */
164     WS_DIRENT     *file;            /* current file */
165     const char    *name;
166     gchar          filename[FILENAME_LEN];   /* current file name */
167     GModule       *handle;          /* handle returned by g_module_open */
168     gpointer       gp;
169     plugin        *new_plug;
170     gchar         *dot;
171     int            cr;
172
173     if ((dir = ws_dir_open(dirname, 0, NULL)) != NULL)
174     {
175         while ((file = ws_dir_read_name(dir)) != NULL)
176         {
177             name = ws_dir_get_name(file);
178
179             /*
180              * GLib 2.x defines G_MODULE_SUFFIX as the extension used on
181              * this platform for loadable modules.
182              */
183             /* skip anything but files with G_MODULE_SUFFIX */
184             dot = strrchr(name, '.');
185             if (dot == NULL || strcmp(dot+1, G_MODULE_SUFFIX) != 0)
186                 continue;
187
188             g_snprintf(filename, FILENAME_LEN, "%s" G_DIR_SEPARATOR_S "%s",
189                        dirname, name);
190             if ((handle = g_module_open(filename, (GModuleFlags)0)) == NULL)
191             {
192                 report_failure("Couldn't load module %s: %s", filename,
193                                g_module_error());
194                 continue;
195             }
196
197             if (!g_module_symbol(handle, "version", &gp))
198             {
199                 report_failure("The plugin %s has no version symbol", name);
200                 g_module_close(handle);
201                 continue;
202             }
203
204             new_plug = (plugin *)g_malloc(sizeof(plugin));
205             new_plug->handle = handle;
206             new_plug->name = g_strdup(name);
207             new_plug->version = (char *)gp;
208             new_plug->types = 0;
209             new_plug->next = NULL;
210
211             /*
212              * Hand the plugin to each of the plugin type callbacks.
213              */
214             g_slist_foreach(plugin_types, call_plugin_callback, new_plug);
215
216             /*
217              * Does this dissector do anything useful?
218              */
219             if (new_plug->types == 0)
220             {
221                 /*
222                  * No.
223                  */
224                 report_failure("The plugin '%s' has no registration routines",
225                                name);
226                 g_module_close(handle);
227                 g_free(new_plug->name);
228                 g_free(new_plug);
229                 continue;
230             }
231
232             /*
233              * OK, attempt to add it to the list of plugins.
234              */
235             if ((cr = add_plugin(new_plug)))
236             {
237                 if (cr == EEXIST)
238                     fprintf(stderr, "The plugin %s, version %s\n"
239                             "was found in multiple directories\n",
240                             new_plug->name, new_plug->version);
241                 else
242                     fprintf(stderr, "Memory allocation problem\n"
243                             "when processing plugin %s, version %s\n",
244                             new_plug->name, new_plug->version);
245                 g_module_close(handle);
246                 g_free(new_plug->name);
247                 g_free(new_plug);
248                 continue;
249             }
250
251         }
252         ws_dir_close(dir);
253     }
254 }
255
256
257 /*
258  * Scan for plugins.
259  */
260 void
261 scan_plugins(void)
262 {
263     const char *plugin_dir;
264     const char *name;
265     char *plugin_dir_path;
266     char *plugins_pers_dir;
267     WS_DIR *dir;                /* scanned directory */
268     WS_DIRENT *file;                /* current file */
269
270     if (plugin_list == NULL)      /* ensure scan_plugins is only run once */
271     {
272         /*
273          * Scan the global plugin directory.
274          * If we're running from a build directory, scan the subdirectories
275          * of that directory, as the global plugin directory is the
276          * "plugins" directory of the source tree, and the subdirectories
277          * are the source directories for the plugins, with the plugins
278          * built in those subdirectories.
279          */
280         plugin_dir = get_plugin_dir();
281         if (running_in_build_directory())
282         {
283             if ((dir = ws_dir_open(plugin_dir, 0, NULL)) != NULL)
284             {
285                 while ((file = ws_dir_read_name(dir)) != NULL)
286                 {
287                     name = ws_dir_get_name(file);
288                     if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
289                         continue;        /* skip "." and ".." */
290                     /*
291                      * Get the full path of a ".libs" subdirectory of that
292                      * directory.
293                      */
294                     plugin_dir_path = g_strdup_printf(
295                         "%s" G_DIR_SEPARATOR_S "%s" G_DIR_SEPARATOR_S ".libs",
296                         plugin_dir, name);
297                     if (test_for_directory(plugin_dir_path) != EISDIR) {
298                         /*
299                          * Either it doesn't refer to a directory or it
300                          * refers to something that doesn't exist.
301                          *
302                          * Assume that means that the plugins are in
303                          * the subdirectory of the plugin directory, not
304                          * a ".libs" subdirectory of that subdirectory.
305                          */
306                         g_free(plugin_dir_path);
307                         plugin_dir_path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s",
308                             plugin_dir, name);
309                     }
310                     plugins_scan_dir(plugin_dir_path);
311                     g_free(plugin_dir_path);
312                 }
313                 ws_dir_close(dir);
314             }
315         }
316         else
317             plugins_scan_dir(plugin_dir);
318
319         /*
320          * If the program wasn't started with special privileges,
321          * scan the users plugin directory.  (Even if we relinquish
322          * them, plugins aren't safe unless we've *permanently*
323          * relinquished them, and we can't do that in Wireshark as,
324          * if we need privileges to start capturing, we'd need to
325          * reclaim them before each time we start capturing.)
326          */
327         if (!started_with_special_privs())
328         {
329             plugins_pers_dir = get_plugins_pers_dir();
330             plugins_scan_dir(plugins_pers_dir);
331             g_free(plugins_pers_dir);
332         }
333     }
334 }
335
336 /*
337  * Iterate over all plugins, calling a callback with information about
338  * the plugin.
339  */
340 typedef struct {
341     plugin  *pt_plug;
342     GString *types;
343     const char *sep;
344 } type_callback_info;
345
346 static void
347 add_plugin_type_description(gpointer data, gpointer user_data)
348 {
349     plugin_type *type = (plugin_type *)data;
350     type_callback_info *info = (type_callback_info *)user_data;
351
352     /*
353      * If the plugin handles this type, add the type to the list of types.
354      */
355     if (info->pt_plug->types & (1 << type->type_val)) {
356         g_string_append_printf(info->types, "%s%s", info->sep, type->type);
357         info->sep = ", ";
358     }
359 }
360
361 WS_DLL_PUBLIC void
362 plugins_get_descriptions(plugin_description_callback callback, void *user_data)
363 {
364     type_callback_info info;
365
366     info.types = NULL; /* FUCK LLVM UP THE ASS WITH A RED HOT POKER */
367     for (info.pt_plug = plugin_list; info.pt_plug != NULL;
368          info.pt_plug = info.pt_plug->next)
369     {
370         info.sep = "";
371         info.types = g_string_new(""); 
372
373         /*
374          * Build a list of all the plugin types.
375          */
376         g_slist_foreach(plugin_types, add_plugin_type_description, &info);
377
378         /*
379          * And hand the information to the callback.
380          */
381         callback(info.pt_plug->name, info.pt_plug->version, info.types->str,
382                  g_module_name(info.pt_plug->handle), user_data);
383
384         g_string_free(info.types, TRUE);
385     }
386 }
387
388 static void
389 print_plugin_description(const char *name, const char *version,
390                          const char *description, const char *filename,
391                          void *user_data _U_)
392 {
393     printf("%s\t%s\t%s\t%s\n", name, version, description, filename);
394 }
395
396 void
397 plugins_dump_all(void)
398 {
399     plugins_get_descriptions(print_plugin_description, NULL);
400 }
401
402 #endif /* HAVE_PLUGINS */
403
404 /*
405  * Editor modelines
406  *
407  * Local Variables:
408  * c-basic-offset: 4
409  * tab-width: 8
410  * indent-tabs-mode: nil
411  * End:
412  *
413  * ex: set shiftwidth=4 tabstop=8 expandtab:
414  * :indentSize=4:tabSize=8:noTabs=true:
415  */