various code cleanup:
[obnox/wireshark/wip.git] / epan / plugins.c
1 /* plugins.c
2  * plugin routines
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1999 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include "plugins.h"
30
31 #ifdef HAVE_PLUGINS
32
33 #include <time.h>
34
35 #ifdef HAVE_DIRENT_H
36 #include <dirent.h>
37 #endif
38
39 #ifdef HAVE_DIRECT_H
40 #include <direct.h>
41 #endif
42
43 #include <string.h>
44 #include <stdlib.h>
45 #include <errno.h>
46
47 #ifdef HAVE_SYS_STAT_H
48 #include <sys/stat.h>
49 #endif
50
51 #ifdef HAVE_UNISTD_H
52 #include <unistd.h>
53 #endif
54
55 #include "filesystem.h"
56 #include "report_err.h"
57
58 /* linked list of all plugins */
59 plugin *plugin_list;
60
61 #define PLUGINS_DIR_NAME        "plugins"
62
63 /*
64  * add a new plugin to the list
65  * returns :
66  * - 0 : OK
67  * - ENOMEM : memory allocation problem
68  * - EEXIST : the same plugin (i.e. name/version) was already registered.
69  */
70 static int
71 add_plugin(void *handle, gchar *name, gchar *version,
72            void (*register_protoinfo)(void), void (*reg_handoff)(void),
73            void (*register_tap_listener)(void))
74 {
75     plugin *new_plug, *pt_plug;
76
77     pt_plug = plugin_list;
78     if (!pt_plug) /* the list is empty */
79     {
80         new_plug = (plugin *)g_malloc(sizeof(plugin));
81         if (new_plug == NULL) return ENOMEM;
82         plugin_list = new_plug;
83     }
84     else
85     {
86         while (1)
87         {
88             /* check if the same name/version is already registered */
89             if (!strcmp(pt_plug->name, name) &&
90                 !strcmp(pt_plug->version, version))
91             {
92                 return EEXIST;
93             }
94
95             /* we found the last plugin in the list */
96             if (pt_plug->next == NULL) break;
97
98             pt_plug = pt_plug->next;
99         }
100         new_plug = (plugin *)g_malloc(sizeof(plugin));
101         if (new_plug == NULL) return ENOMEM;
102         pt_plug->next = new_plug;
103     }
104
105     new_plug->handle = handle;
106     new_plug->name = name;
107     new_plug->version = version;
108     new_plug->register_protoinfo = register_protoinfo;
109     new_plug->reg_handoff = reg_handoff;
110     new_plug->register_tap_listener = register_tap_listener;
111     new_plug->next = NULL;
112     return 0;
113 }
114
115 /*
116  * XXX - when we remove support for old-style plugins (which we should
117  * probably do eventually, as all plugins should be written as new-style
118  * ones), we may want to have "init_plugins()" merely save a pointer
119  * to the plugin's "init" routine, just as we save a pointer to its
120  * "reg_handoff" routine, and have a "register_all_plugins()" routine
121  * to go through the list of plugins and call all of them.
122  *
123  * Then we'd have "epan_init()", or perhaps even something higher up
124  * in the call tree, call "init_plugins()", and have "proto_init()"
125  * call "register_all_plugins()" right after calling "register_all_protocols()";
126  * this might be a bit cleaner.
127  */
128 static void
129 plugins_scan_dir(const char *dirname)
130 {
131 #define FILENAME_LEN    1024
132 #if GLIB_MAJOR_VERSION < 2
133     gchar         *hack_path;       /* pathname used to construct lt_lib_ext */
134     gchar         *lt_lib_ext;      /* extension for loadable modules */
135     DIR           *dir;             /* scanned directory */
136     struct dirent *file;            /* current file */
137     gchar         *name;
138 #else /* GLIB 2 */
139     GDir          *dir;             /* scanned directory */
140     GError        **dummy;
141     const gchar   *name;
142 #endif
143     gchar          filename[FILENAME_LEN];   /* current file name */
144     GModule       *handle;          /* handle returned by dlopen */
145     gchar         *version;
146     gpointer       gp;
147     void         (*register_protoinfo)(void);
148     void         (*reg_handoff)(void);
149     void         (*register_tap_listener)(void);
150     gchar         *dot;
151     int            cr;
152
153 #if GLIB_MAJOR_VERSION < 2
154     /*
155      * We find the extension used on this platform for loadable modules
156      * by the sneaky hack of calling "g_module_build_path" to build
157      * the pathname for a module with an empty directory name and
158      * empty module name, and then search for the last "." and use
159      * everything from the last "." on.
160      */
161     hack_path = g_module_build_path("", "");
162     lt_lib_ext = strrchr(hack_path, '.');
163     if (lt_lib_ext == NULL)
164     {
165         /*
166          * Does this mean there *is* no extension?  Assume so.
167          *
168          * XXX - the code below assumes that all loadable modules have
169          * an extension....
170          */
171         lt_lib_ext = "";
172     }
173
174     if ((dir = opendir(dirname)) != NULL)
175     {
176         while ((file = readdir(dir)) != NULL)
177         {
178             /* don't try to open "." and ".." */
179             if (!(strcmp(file->d_name, "..") &&
180                   strcmp(file->d_name, "."))) continue;
181
182             /* skip anything but files with lt_lib_ext */
183             dot = strrchr(file->d_name, '.');
184             if (dot == NULL || strcmp(dot, lt_lib_ext) != 0) continue;
185
186             g_snprintf(filename, FILENAME_LEN, "%s" G_DIR_SEPARATOR_S "%s",
187                 dirname, file->d_name);
188             name = (gchar *)file->d_name;
189 #else /* GLIB 2 */
190     /*
191      * GLib 2.x defines G_MODULE_SUFFIX as the extension used on this
192      * platform for loadable modules.
193      */
194     dummy = g_malloc(sizeof(GError *));
195     *dummy = NULL;
196     if ((dir = g_dir_open(dirname, 0, dummy)) != NULL)
197     {
198         while ((name = g_dir_read_name(dir)) != NULL)
199         {
200             /* skip anything but files with G_MODULE_SUFFIX */
201             dot = strrchr(name, '.');
202             if (dot == NULL || strcmp(dot+1, G_MODULE_SUFFIX) != 0) continue;
203
204             g_snprintf(filename, FILENAME_LEN, "%s" G_DIR_SEPARATOR_S "%s",
205                 dirname, name);
206 #endif
207             if ((handle = g_module_open(filename, 0)) == NULL)
208             {
209                 report_failure("Couldn't load module %s: %s", filename,
210                           g_module_error());
211                 continue;
212             }
213             if (!g_module_symbol(handle, "version", &gp))
214             {
215                 report_failure("The plugin %s has no version symbol", name);
216                 g_module_close(handle);
217                 continue;
218             }
219             version = gp;
220             
221             /*
222              * Do we have a register routine?
223              */
224             if (g_module_symbol(handle, "plugin_register", &gp))
225             {
226                 /*
227                  * Yes - this plugin includes one or more dissectors.
228                  */
229                 register_protoinfo = gp;
230             }
231             else
232             {
233                 /*
234                  * No - no dissectors.
235                  */
236                 register_protoinfo = NULL;
237             }
238
239             /*
240              * Do we have a reg_handoff routine?
241              */
242             if (g_module_symbol(handle, "plugin_reg_handoff", &gp))
243             {
244                 /*
245                  * Yes.
246                  */
247                 reg_handoff = gp;
248             }
249             else
250             {
251                 /*
252                  * No - that's OK even if we have dissectors, as long
253                  * as the plugin registers by name *and* there's
254                  * a caller looking for that name.
255                  */
256                 reg_handoff = NULL;
257             }
258
259             /*
260              * Do we have a register_tap_listener routine?
261              */
262             if (g_module_symbol(handle, "plugin_register_tap_listener", &gp))
263             {
264                 /*
265                  * Yes - this plugin includes one or more taps.
266                  */
267                 register_tap_listener = gp;
268             }
269             else
270             {
271                 /*
272                  * No - no taps here.
273                  */
274                 register_tap_listener = NULL;
275             }
276
277             /*
278              * Do we have an old-style init routine?
279              */
280             if (g_module_symbol(handle, "plugin_init", &gp))
281             {
282                 /*
283                  * Yes - do we also have a register routine or a
284                  * register_tap_listener routine?  If so, this is a bogus
285                  * hybrid of an old-style and new-style plugin.
286                  */
287                 if (register_protoinfo != NULL || register_tap_listener != NULL)
288                 {
289                     report_failure("The plugin %s has an old plugin init routine\nand a new register or register_tap_listener routine.",
290                         name);
291                     g_module_close(handle);
292                     continue;
293                 }
294
295                 /*
296                  * It's just an unsupported old-style plugin;
297                  */
298                 report_failure("The plugin %s has an old plugin init routine. Support has been dropped.\n Information on how to update your plugin is available at \nhttp://anonsvn.ethereal.com/ethereal/trunk/doc/README.plugins",
299                     name);
300                 g_module_close(handle);
301                 continue;
302             }
303
304             /*
305              * Does this dissector do anything useful?
306              */
307             if (register_protoinfo == NULL &&
308                 register_tap_listener == NULL)
309             {
310                 /*
311                  * No.
312                  */
313                 report_failure("The plugin %s has neither a register routine, or a register_tap_listener routine",
314                     name);
315                 g_module_close(handle);
316                 continue;
317             }
318
319             /*
320              * OK, attempt to add it to the list of plugins.
321              */
322             if ((cr = add_plugin(handle, g_strdup(name), version,
323                                  register_protoinfo, reg_handoff,
324                                  register_tap_listener)))
325             {
326                 if (cr == EEXIST)
327                     fprintf(stderr, "The plugin %s, version %s\n"
328                             "was found in multiple directories\n", name, version);
329                 else
330                     fprintf(stderr, "Memory allocation problem\n"
331                             "when processing plugin %s, version %s\n",
332                             name, version);
333                 g_module_close(handle);
334                 continue;
335             }
336
337             /*
338              * Call its register routine if it has one.
339              * XXX - just save this and call it with the built-in
340              * dissector register routines?
341              */
342             if (register_protoinfo != NULL)
343                     register_protoinfo();
344
345         }
346 #if GLIB_MAJOR_VERSION < 2
347         closedir(dir);
348     }
349     g_free(hack_path);
350 #else /* GLIB 2 */
351         g_dir_close(dir);
352     }
353     g_clear_error(dummy);
354     g_free(dummy);
355 #endif
356 }
357
358
359 /* get the global plugin dir */
360 /* Return value is malloced so the caller should g_free() it. */
361 char *get_plugins_global_dir(const char *plugin_dir)
362 {
363 #ifdef _WIN32
364         char *install_plugin_dir;
365
366         /*
367          * On Windows, the data file directory is the installation
368          * directory; the plugins are stored under it.
369          *
370          * Assume we're running the installed version of Ethereal;
371          * on Windows, the data file directory is the directory
372          * in which the Ethereal binary resides.
373          */
374         install_plugin_dir = g_strdup_printf("%s\\plugins\\%s", get_datafile_dir(), VERSION);
375
376         /*
377          * Make sure that pathname refers to a directory.
378          */
379         if (test_for_directory(install_plugin_dir) != EISDIR) {
380                 /*
381                  * Either it doesn't refer to a directory or it
382                  * refers to something that doesn't exist.
383                  *
384                  * Assume that means we're running, for example,
385                  * a version of Ethereal we've built in a source
386                  * directory, and fall back on the default
387                  * installation directory, so you can put the plugins
388                  * somewhere so they can be used with this version
389                  * of Ethereal.
390                  *
391                  * XXX - should we, instead, have the Windows build
392                  * procedure create a subdirectory of the "plugins"
393                  * source directory, and copy the plugin DLLs there,
394                  * so that you use the plugins from the build tree?
395                  */
396                 g_free(install_plugin_dir);
397                 install_plugin_dir =
398                     g_strdup("C:\\Program Files\\Ethereal\\plugins\\" VERSION);
399         }
400
401         return install_plugin_dir;
402 #else
403         /*
404          * Scan the plugin directory.
405          */
406         return strdup(plugin_dir);
407 #endif
408 }
409
410
411 /* get the personal plugin dir */
412 /* Return value is malloced so the caller should g_free() it. */
413 char *get_plugins_pers_dir(void)
414 {
415     return get_persconffile_path(PLUGINS_DIR_NAME, FALSE);
416 }
417
418 /*
419  * init plugins
420  */
421 void
422 init_plugins(const char *plugin_dir)
423 {
424     char *datafile_dir;
425
426     if (plugin_list == NULL)      /* ensure init_plugins is only run once */
427     {
428         /*
429          * Scan the global plugin directory.
430          */
431         datafile_dir = get_plugins_global_dir(plugin_dir);
432         plugins_scan_dir(datafile_dir);
433         g_free(datafile_dir);
434
435         /*
436          * Scan the users plugin directory.
437          */
438         datafile_dir = get_plugins_pers_dir();
439         plugins_scan_dir(datafile_dir);
440         g_free(datafile_dir);
441     }
442 }
443
444 void
445 register_all_plugin_handoffs(void)
446 {
447     plugin *pt_plug;
448
449     /*
450      * For all plugins with register-handoff routines, call the routines.
451      * This is called from "proto_init()"; it must be called after
452      * "register_all_protocols()" and "init_plugins()" are called,
453      * in case one plugin registers itself either with a built-in
454      * dissector or with another plugin; we must first register all
455      * dissectors, whether built-in or plugin, so their dissector tables
456      * are initialized, and only then register all handoffs.
457      */
458     for (pt_plug = plugin_list; pt_plug != NULL; pt_plug = pt_plug->next)
459     {
460         if (pt_plug->reg_handoff)
461             (pt_plug->reg_handoff)();
462     }
463 }
464
465 void
466 register_all_plugin_tap_listeners(void)
467 {
468     plugin *pt_plug;
469
470     /*
471      * For all plugins with register-tap-listener routines, call the
472      * routines.
473      */
474     for (pt_plug = plugin_list; pt_plug != NULL; pt_plug = pt_plug->next)
475     {
476         if (pt_plug->register_tap_listener)
477             (pt_plug->register_tap_listener)();
478     }
479 }
480 #endif