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