d809a384a077d31e1c6bda30eb68f573ed825c2d
[obnox/wireshark/wip.git] / plugins.c
1 /* plugins.c
2  * plugin routines
3  *
4  * $Id: plugins.c,v 1.15 2000/05/05 09:32:10 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@zing.org>
8  * Copyright 1999 Gerald Combs
9  *
10  * 
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  * 
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include "plugins.h"
31
32 #ifdef HAVE_PLUGINS
33
34 #include <time.h>
35
36 #ifdef HAVE_DIRENT_H
37 #include <dirent.h>
38 #endif
39
40 #ifdef HAVE_DIRECT_H
41 #include <direct.h>
42 #endif
43
44 #include <string.h>
45 #include <stdlib.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48
49 #ifdef HAVE_FCNTL_H
50 #include <fcntl.h>
51 #endif
52
53 #ifdef HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
56
57 #include "globals.h"
58 #include "util.h"
59
60 #ifdef PLUGINS_NEED_ADDRESS_TABLE
61 #include "plugins/plugin_table.h"
62 plugin_address_table_t  patable;
63 #endif
64
65 /* linked list of all plugins */
66 plugin *plugin_list;
67 guint32 enabled_plugins_number;
68
69 #ifdef WIN32
70 static gchar std_plug_dir[] = "c:/program files/ethereal/plugins/0.8.7";
71 static gchar local_plug_dir[] = "c:/ethereal/plugins/0.8.7";
72 #else
73 static gchar std_plug_dir[] = "/usr/lib/ethereal/plugins/0.8.7";
74 static gchar local_plug_dir[] = "/usr/local/lib/ethereal/plugins/0.8.7";
75 #endif
76 static gchar *user_plug_dir = NULL;
77 static gchar *plugin_status_file = NULL;
78
79 /*
80  * add a new plugin to the list
81  * returns :
82  * - 0 : OK
83  * - ENOMEM : memory allocation problem
84  * - EEXIST : the same plugin (i.e. name/version) was already registered.
85  */
86 int
87 add_plugin(void *handle, gchar *name, gchar *version, gchar *protocol,
88            gchar *filter_string, dfilter *filter,
89            void (*dissector) (const u_char *,
90                               int,
91                               frame_data *,
92                               proto_tree *))
93 {
94     plugin *new_plug, *pt_plug;
95
96     pt_plug = plugin_list;
97     if (!pt_plug) /* the list is empty */
98     {
99         new_plug = (plugin *)g_malloc(sizeof(plugin));
100         if (new_plug == NULL) return ENOMEM;
101         plugin_list = new_plug;
102     }
103     else
104     {
105         while (1)
106         {
107             /* check if the same name/version is already registered */
108             if (!strcmp(pt_plug->name, name) &&
109                 !strcmp(pt_plug->version, version))
110             {
111                 return EEXIST;
112             }
113
114             /* we found the last plugin in the list */
115             if (pt_plug->next == NULL) break;
116
117             pt_plug = pt_plug->next;
118         }
119         new_plug = (plugin *)g_malloc(sizeof(plugin));
120         if (new_plug == NULL) return ENOMEM;
121         pt_plug->next = new_plug;
122     }
123
124     new_plug->handle = handle;
125     new_plug->name = name;
126     new_plug->version = version;
127     new_plug->enabled = FALSE;
128     new_plug->protocol = protocol;
129     new_plug->filter_string = g_strdup(filter_string);
130     new_plug->filter = filter;
131     new_plug->dissector = dissector;
132     new_plug->next = NULL;
133     return 0;
134 }
135
136 /*
137  * enable a plugin
138  * returns a pointer to the enabled plugin, or NULL if the plugin wasn't found
139  * in the list
140  */
141 void *
142 enable_plugin(const gchar *name, const gchar *version)
143 {
144     plugin *pt_plug;
145
146     pt_plug = plugin_list;
147     while (pt_plug)
148     {
149         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
150         {
151             pt_plug->enabled = TRUE;
152             enabled_plugins_number++;
153             return pt_plug;
154         }
155         pt_plug = pt_plug->next;
156     }
157     return NULL;
158 }
159
160 /*
161  * disable a plugin
162  * returns a pointer to the disabled plugin, or NULL if the plugin wasn't found
163  * in the list
164  */
165 void *
166 disable_plugin(const gchar *name, const gchar *version)
167 {
168     plugin *pt_plug;
169
170     pt_plug = plugin_list;
171     while (pt_plug)
172     {
173         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
174         {
175             pt_plug->enabled = FALSE;
176             enabled_plugins_number--;
177             return pt_plug;
178         }
179         pt_plug = pt_plug->next;
180     }
181     return NULL;
182 }
183
184 /*
185  * find a plugin using its name/version
186  */
187 void *
188 find_plugin(const gchar *name, const gchar *version)
189 {
190     plugin *pt_plug;
191
192     pt_plug = plugin_list;
193     while (pt_plug)
194     {
195         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
196         {
197             return pt_plug;
198         }
199         pt_plug = pt_plug->next;
200     }
201     return NULL;
202 }
203
204 /*
205  * check if a plugin is enabled
206  */
207 gboolean
208 is_enabled(const gchar *name, const gchar *version)
209 {
210     plugin *pt_plug;
211
212     pt_plug = plugin_list;
213     while (pt_plug)
214     {
215         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
216             return pt_plug->enabled;
217         pt_plug = pt_plug->next;
218     }
219     return FALSE;
220 }
221
222 /*
223  * replace the filter used by a plugin (filter string and dfilter)
224  */
225 void
226 plugin_replace_filter(const gchar *name, const gchar *version,
227         const gchar *filter_string, dfilter *filter)
228 {
229     plugin *pt_plug;
230
231     pt_plug = plugin_list;
232     while (pt_plug)
233     {
234         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
235         {
236             g_free(pt_plug->filter_string);
237             pt_plug->filter_string = g_strdup(filter_string);
238             dfilter_destroy(pt_plug->filter);
239             pt_plug->filter = filter;
240             return;
241         }
242         pt_plug = pt_plug->next;
243     }
244 }
245
246 /*
247  * save plugin status, returns 0 on success, -1 on failure:
248  * file format :
249  * for each plugin, two lines are saved :
250  * plugin_name plugin_version [0|1]    (0: disabled, 1: enabled)
251  * filter_string
252  *
253  * Ex :
254  * gryphon.so 0.8.0 1
255  * tcp.port == 7000
256  */
257 int
258 save_plugin_status()
259 {
260     gchar  *pf_path;
261     FILE   *statusfile;
262     plugin *pt_plug;
263
264     if (!plugin_status_file) {
265         plugin_status_file = (gchar *)g_malloc(strlen(get_home_dir()) + 26);
266         sprintf(plugin_status_file, "%s/%s/plugins.status", get_home_dir(), PF_DIR);
267     }
268     statusfile=fopen(plugin_status_file, "w");
269     if (!statusfile) {
270         pf_path = g_malloc(strlen(get_home_dir()) + strlen(PF_DIR) + 2);
271         sprintf(pf_path, "%s/%s", get_home_dir(), PF_DIR);
272         #ifdef WIN32
273         mkdir(pf_path);
274         #else
275         mkdir(pf_path, 0755);
276         #endif
277         g_free(pf_path);
278         statusfile=fopen(plugin_status_file, "w");
279         if (!statusfile) return -1;
280     }
281
282     pt_plug = plugin_list;
283     while (pt_plug)
284     {
285         fprintf(statusfile,"%s %s %s\n%s\n", pt_plug->name, pt_plug->version,
286                 (pt_plug->enabled ? "1" : "0"), pt_plug->filter_string);
287         pt_plug = pt_plug->next;
288     }
289     fclose(statusfile);
290     return 0;
291 }
292
293 /*
294  * Check if the status of this plugin has been saved.
295  * If necessary, enable the plugin, and change the filter.
296  */
297 static void
298 check_plugin_status(gchar *name, gchar *version, GModule *handle,
299                     gchar *filter_string, FILE *statusfile)
300 {
301     gchar   *ref_string;
302     guint16  ref_string_len;
303     gchar    line[512];
304     void   (*plugin_init)(void*);
305     dfilter *filter;
306
307     if (!statusfile) return;
308
309     ref_string = (gchar *)g_malloc(strlen(name) + strlen(version) + 2);
310     ref_string_len = sprintf(ref_string, "%s %s", name, version);
311
312     while (!feof(statusfile))
313     {
314         if (fgets(line, 512, statusfile) == NULL) return;
315         if (strncmp(line, ref_string, ref_string_len) != 0) { /* not the right plugin */
316             if (fgets(line, 512, statusfile) == NULL) return;
317         }
318         else { /* found the plugin */
319             if (line[ref_string_len+1] == '1') {
320                 enable_plugin(name, version);
321                 if (g_module_symbol(handle, "plugin_init", (gpointer*)&plugin_init) == TRUE) {
322 #ifdef PLUGINS_NEED_ADDRESS_TABLE
323                     plugin_init(&patable);
324 #else
325                     plugin_init(NULL);
326 #endif
327                 }
328 #ifdef PLUGINS_NEED_ADDRESS_TABLE
329                 else {
330                         return;
331                 }
332 #endif
333             }
334
335             if (fgets(line, 512, statusfile) == NULL) return;
336             if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0';
337             /* only compile the new filter if it is different from the default */
338             if (strcmp(line, filter_string) && dfilter_compile(line, &filter) == 0)
339                 plugin_replace_filter(name, version, line, filter);
340             return;
341         }
342     }
343     g_free(ref_string);
344 }
345
346 static void
347 plugins_scan_dir(const char *dirname)
348 {
349     DIR           *dir;             /* scanned directory */
350     struct dirent *file;            /* current file */
351     gchar          filename[512];   /* current file name */
352     GModule       *handle;          /* handle returned by dlopen */
353     gchar         *name;
354     gchar         *version;
355     gchar         *protocol;
356     gchar         *filter_string;
357     gchar         *dot;
358     dfilter       *filter = NULL;
359     void         (*dissector) (const u_char *, int, frame_data *, proto_tree *);
360     int            cr;
361     FILE          *statusfile;
362
363 #ifdef WIN32
364 #define LT_LIB_EXT ".dll"
365 #else
366 #define LT_LIB_EXT ".so"
367 #endif
368
369     if (!plugin_status_file)
370     {
371         plugin_status_file = (gchar *)g_malloc(strlen(get_home_dir()) + 26);
372         sprintf(plugin_status_file, "%s/%s/plugins.status", get_home_dir(), PF_DIR);
373     }
374     statusfile = fopen(plugin_status_file, "r");
375
376     if ((dir = opendir(dirname)) != NULL)
377     {
378         while ((file = readdir(dir)) != NULL)
379         {
380             /* don't try to open "." and ".." */
381             if (!(strcmp(file->d_name, "..") &&
382                   strcmp(file->d_name, "."))) continue;
383
384             /* skip anything but files with LT_LIB_EXT */
385             dot = strrchr(file->d_name, '.');
386             if (dot == NULL || strcmp(dot, LT_LIB_EXT) != 0) continue;
387
388             sprintf(filename, "%s/%s", dirname, file->d_name);
389             if ((handle = g_module_open(filename, 0)) == NULL) continue;
390             name = (gchar *)file->d_name;
391             if (g_module_symbol(handle, "version", (gpointer*)&version) == FALSE)
392             {
393                 g_module_close(handle);
394                 continue;
395             }
396             if (g_module_symbol(handle, "protocol", (gpointer*)&protocol) == FALSE)
397             {
398                 g_module_close(handle);
399                 continue;
400             }
401             if (g_module_symbol(handle, "filter_string", (gpointer*)&filter_string) == FALSE)
402             {
403                 g_module_close(handle);
404                 continue;
405             }
406             if (dfilter_compile(filter_string, &filter) != 0) {
407                 g_module_close(handle);
408                 continue;
409             }
410             if (g_module_symbol(handle, "dissector", (gpointer*)&dissector) == FALSE) {
411                 if (filter != NULL)
412                     dfilter_destroy(filter);
413                 g_module_close(handle);
414                 continue;
415             }
416
417             if ((cr = add_plugin(handle, g_strdup(file->d_name), version,
418                                  protocol, filter_string, filter, dissector)))
419             {
420                 if (cr == EEXIST)
421                     fprintf(stderr, "The plugin : %s, version %s\n"
422                             "was found in multiple directories\n", name, version);
423                 else
424                     fprintf(stderr, "Memory allocation problem\n"
425                             "when processing plugin %s, version %sn",
426                             name, version);
427                 if (filter != NULL)
428                     dfilter_destroy(filter);
429                 g_module_close(handle);
430                 continue;
431             }
432             if (statusfile) {
433                 check_plugin_status(file->d_name, version, handle,
434                                     filter_string, statusfile);
435                 rewind(statusfile);
436             }
437         }
438         closedir(dir);
439     }
440     if (statusfile) fclose(statusfile);
441 }
442
443 /*
444  * init plugins
445  */
446 void
447 init_plugins()
448 {
449     struct stat std_dir_stat, local_dir_stat, plugin_dir_stat;
450
451     if (plugin_list == NULL)      /* ensure init_plugins is only run once */
452     {
453         enabled_plugins_number = 0;
454
455 #ifdef PLUGINS_NEED_ADDRESS_TABLE
456         /* Intialize address table */
457         patable.p_check_col                     = check_col;
458         patable.p_col_add_fstr                  = col_add_fstr;
459         patable.p_col_append_fstr               = col_append_str;
460         patable.p_col_add_str                   = col_add_str;
461         patable.p_col_append_str                = col_append_str;
462
463         patable.p_dfilter_init                  = dfilter_init;
464         patable.p_dfilter_cleanup               = dfilter_cleanup;
465
466         patable.p_pi                            = &pi;
467
468         patable.p_proto_register_protocol       = proto_register_protocol;
469         patable.p_proto_register_field_array    = proto_register_field_array;
470         patable.p_proto_register_subtree_array  = proto_register_subtree_array;
471
472         patable.p_dissector_add                 = dissector_add;
473
474         patable.p_heur_dissector_add            = heur_dissector_add;
475
476         patable.p_proto_item_add_subtree        = proto_item_add_subtree;
477         patable.p_proto_tree_add_item           = proto_tree_add_item;
478         patable.p_proto_tree_add_item_hidden    = proto_tree_add_item_hidden;
479         patable.p_proto_tree_add_protocol_format = proto_tree_add_protocol_format;
480         patable.p_proto_tree_add_bytes_format   = proto_tree_add_bytes_format;
481         patable.p_proto_tree_add_time_format    = proto_tree_add_time_format;
482         patable.p_proto_tree_add_ipxnet_format  = proto_tree_add_ipxnet_format;
483         patable.p_proto_tree_add_ipv4_format    = proto_tree_add_ipv4_format;
484         patable.p_proto_tree_add_ipv6_format    = proto_tree_add_ipv6_format;
485         patable.p_proto_tree_add_ether_format   = proto_tree_add_ether_format;
486         patable.p_proto_tree_add_string_format  = proto_tree_add_string_format;
487         patable.p_proto_tree_add_boolean_format = proto_tree_add_boolean_format;
488         patable.p_proto_tree_add_uint_format    = proto_tree_add_uint_format;
489         patable.p_proto_tree_add_text           = proto_tree_add_text;
490         patable.p_proto_tree_add_notext         = proto_tree_add_notext;
491 #endif
492
493         plugins_scan_dir(std_plug_dir);
494         plugins_scan_dir(local_plug_dir);
495         if ((strcmp(std_plug_dir, PLUGIN_DIR) != 0) &&
496                 (strcmp(local_plug_dir, PLUGIN_DIR) != 0))
497         {
498             if (stat(PLUGIN_DIR, &plugin_dir_stat) == 0)
499             {
500                 /* check if PLUGIN_DIR is really different from std_dir and
501                  * local_dir if they exist ! */
502                 if (stat(std_plug_dir, &std_dir_stat) == 0)
503                 {
504                     if (stat(local_plug_dir, &local_dir_stat) == 0)
505                     {
506                         if ((plugin_dir_stat.st_dev != std_dir_stat.st_dev ||
507                                     plugin_dir_stat.st_ino != std_dir_stat.st_ino) &&
508                                 (plugin_dir_stat.st_dev != local_dir_stat.st_dev ||
509                                  plugin_dir_stat.st_ino != local_dir_stat.st_ino))
510                             plugins_scan_dir(PLUGIN_DIR);
511                     }
512                     else
513                     {
514                         if ((plugin_dir_stat.st_dev != std_dir_stat.st_dev ||
515                                     plugin_dir_stat.st_ino != std_dir_stat.st_ino))
516                             plugins_scan_dir(PLUGIN_DIR);
517                     }
518                 }
519                 else if (stat(local_plug_dir, &local_dir_stat) == 0)
520                 {
521                     if ((plugin_dir_stat.st_dev != local_dir_stat.st_dev ||
522                                 plugin_dir_stat.st_ino != local_dir_stat.st_ino))
523                         plugins_scan_dir(PLUGIN_DIR);
524                 }
525                 else plugins_scan_dir(PLUGIN_DIR);
526             }
527         }
528         if (!user_plug_dir)
529         {
530             user_plug_dir = (gchar *)g_malloc(strlen(get_home_dir()) + 19);
531             sprintf(user_plug_dir, "%s/%s/plugins", get_home_dir(), PF_DIR);
532         }
533         plugins_scan_dir(user_plug_dir);
534     }
535 }
536
537 #endif