6d23fada2fe6ade2d323c434764e89f2f1a16b40
[obnox/wireshark/wip.git] / plugins.c
1 /* plugins.c
2  * plugin routines
3  *
4  * $Id: plugins.c,v 1.6 2000/01/29 16:41:14 gram 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
61 /* linked list of all plugins */
62 plugin *plugin_list;
63
64 static gchar std_plug_dir[] = "/usr/lib/ethereal/plugins/0.8";
65 static gchar local_plug_dir[] = "/usr/local/lib/ethereal/plugins/0.8";
66 static gchar *user_plug_dir = NULL;
67 static gchar *plugin_status_file = NULL;
68
69 /*
70  * add a new plugin to the list
71  * returns :
72  * - 0 : OK
73  * - ENOMEM : memory allocation problem
74  * - EEXIST : the same plugin (i.e. name/version) was already registered.
75  */
76 int
77 add_plugin(void *handle, gchar *name, gchar *version, gchar *protocol,
78            gchar *filter_string, dfilter *filter,
79            void (*dissector) (const u_char *,
80                               int,
81                               frame_data *,
82                               proto_tree *))
83 {
84     plugin *new_plug, *pt_plug;
85
86     pt_plug = plugin_list;
87     if (!pt_plug) /* the list is empty */
88     {
89         new_plug = (plugin *)g_malloc(sizeof(plugin));
90         if (new_plug == NULL) return ENOMEM;
91         plugin_list = new_plug;
92     }
93     else
94     {
95         while (1)
96         {
97             /* check if the same name/version is already registered */
98             if (!strcmp(pt_plug->name, name) &&
99                 !strcmp(pt_plug->version, version))
100             {
101                 return EEXIST;
102             }
103
104             /* we found the last plugin in the list */
105             if (pt_plug->next == NULL) break;
106
107             pt_plug = pt_plug->next;
108         }
109         new_plug = (plugin *)g_malloc(sizeof(plugin));
110         if (new_plug == NULL) return ENOMEM;
111         pt_plug->next = new_plug;
112     }
113
114     new_plug->handle = handle;
115     new_plug->name = name;
116     new_plug->version = version;
117     new_plug->enabled = FALSE;
118     new_plug->protocol = protocol;
119     new_plug->filter_string = g_strdup(filter_string);
120     new_plug->filter = filter;
121     new_plug->dissector = dissector;
122     new_plug->next = NULL;
123     return 0;
124 }
125
126 /*
127  * enable a plugin
128  * returns a pointer to the enabled plugin, or NULL if the plugin wasn't found
129  * in the list
130  */
131 void *
132 enable_plugin(const gchar *name, const gchar *version)
133 {
134     plugin *pt_plug;
135
136     pt_plug = plugin_list;
137     while (pt_plug)
138     {
139         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
140         {
141             pt_plug->enabled = TRUE;
142             return pt_plug;
143         }
144         pt_plug = pt_plug->next;
145     }
146     return NULL;
147 }
148
149 /*
150  * disable a plugin
151  * returns a pointer to the disabled plugin, or NULL if the plugin wasn't found
152  * in the list
153  */
154 void *
155 disable_plugin(const gchar *name, const gchar *version)
156 {
157     plugin *pt_plug;
158
159     pt_plug = plugin_list;
160     while (pt_plug)
161     {
162         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
163         {
164             pt_plug->enabled = FALSE;
165             return pt_plug;
166         }
167         pt_plug = pt_plug->next;
168     }
169     return NULL;
170 }
171
172 /*
173  * find a plugin using its name/version
174  */
175 void *
176 find_plugin(const gchar *name, const gchar *version)
177 {
178     plugin *pt_plug;
179
180     pt_plug = plugin_list;
181     while (pt_plug)
182     {
183         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
184         {
185             return pt_plug;
186         }
187         pt_plug = pt_plug->next;
188     }
189     return NULL;
190 }
191
192 /*
193  * check if a plugin is enabled
194  */
195 gboolean
196 is_enabled(const gchar *name, const gchar *version)
197 {
198     plugin *pt_plug;
199
200     pt_plug = plugin_list;
201     while (pt_plug)
202     {
203         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
204             return pt_plug->enabled;
205         pt_plug = pt_plug->next;
206     }
207     return FALSE;
208 }
209
210 /*
211  * replace the filter used by a plugin (filter string and dfilter)
212  */
213 void
214 plugin_replace_filter(const gchar *name, const gchar *version,
215         const gchar *filter_string, dfilter *filter)
216 {
217     plugin *pt_plug;
218
219     pt_plug = plugin_list;
220     while (pt_plug)
221     {
222         if (!strcmp(pt_plug->name, name) && !strcmp(pt_plug->version, version))
223         {
224             g_free(pt_plug->filter_string);
225             pt_plug->filter_string = g_strdup(filter_string);
226             dfilter_destroy(pt_plug->filter);
227             pt_plug->filter = filter;
228             return;
229         }
230         pt_plug = pt_plug->next;
231     }
232 }
233
234 /*
235  * save plugin status, returns 0 on success, -1 on failure:
236  * file format :
237  * for each plugin, two lines are saved :
238  * plugin_name plugin_version [0|1]    (0: disabled, 1: enabled)
239  * filter_string
240  *
241  * Ex :
242  * gryphon.so 0.8.0 1
243  * tcp.port == 7000
244  */
245 int
246 save_plugin_status()
247 {
248     gchar  *pf_path;
249     FILE   *statusfile;
250     plugin *pt_plug;
251
252     if (!plugin_status_file) {
253         plugin_status_file = (gchar *)g_malloc(strlen(get_home_dir()) + 26);
254         sprintf(plugin_status_file, "%s/%s/plugins.status", get_home_dir(), PF_DIR);
255     }
256     statusfile=fopen(plugin_status_file, "w");
257     if (!statusfile) {
258         pf_path = g_malloc(strlen(get_home_dir()) + strlen(PF_DIR) + 2);
259         sprintf(pf_path, "%s/%s", get_home_dir(), PF_DIR);
260         #ifdef WIN32
261         mkdir(pf_path);
262         #else
263         mkdir(pf_path, 0755);
264         #endif
265         g_free(pf_path);
266         statusfile=fopen(plugin_status_file, "w");
267         if (!statusfile) return -1;
268     }
269
270     pt_plug = plugin_list;
271     while (pt_plug)
272     {
273         fprintf(statusfile,"%s %s %s\n%s\n", pt_plug->name, pt_plug->version,
274                 (pt_plug->enabled ? "1" : "0"), pt_plug->filter_string);
275         pt_plug = pt_plug->next;
276     }
277     fclose(statusfile);
278     return 0;
279 }
280
281 /*
282  * Check if the status of this plugin has been saved.
283  * If necessary, enable the plugin, and change the filter.
284  */
285 static void
286 check_plugin_status(gchar *name, gchar *version, GModule *handle,
287                     gchar *filter_string, FILE *statusfile)
288 {
289     gchar   *ref_string;
290     guint16  ref_string_len;
291     gchar    line[512];
292     void   (*proto_init)();
293     dfilter *filter;
294
295     if (!statusfile) return;
296
297     ref_string = (gchar *)g_malloc(strlen(name) + strlen(version) + 2);
298     ref_string_len = sprintf(ref_string, "%s %s", name, version);
299
300     while (!feof(statusfile))
301     {
302         if (fgets(line, 512, statusfile) == NULL) return;
303         if (strncmp(line, ref_string, ref_string_len) != 0) { /* not the right plugin */
304             if (fgets(line, 512, statusfile) == NULL) return;
305         }
306         else { /* found the plugin */
307             if (line[ref_string_len+1] == '1') {
308                 enable_plugin(name, version);
309                 if (g_module_symbol(handle, "proto_init", (gpointer*)&proto_init) == TRUE) {
310                     proto_init();
311                 }
312             }
313
314             if (fgets(line, 512, statusfile) == NULL) return;
315             if (line[strlen(line)-1] == '\n') line[strlen(line)-1] = '\0';
316             /* only compile the new filter if it is different from the default */
317             if (strcmp(line, filter_string) && dfilter_compile(line, &filter) == 0)
318                 plugin_replace_filter(name, version, line, filter);
319             return;
320         }
321     }
322     g_free(ref_string);
323 }
324
325 static void
326 plugins_scan_dir(const char *dirname)
327 {
328     DIR           *dir;             /* scanned directory */
329     struct dirent *file;            /* current file */
330     gchar          filename[512];   /* current file name */
331     GModule       *handle;          /* handle returned by dlopen */
332     gchar         *name;
333     gchar         *version;
334     gchar         *protocol;
335     gchar         *filter_string;
336     gchar         *dot;
337     dfilter       *filter = NULL;
338     void         (*dissector) (const u_char *, int, frame_data *, proto_tree *);
339     int            cr;
340     FILE          *statusfile;
341
342 #ifdef WIN32
343 #define LT_LIB_EXT ".dll"
344 #else
345 #define LT_LIB_EXT ".la"
346 #endif
347
348     if (!plugin_status_file)
349     {
350         plugin_status_file = (gchar *)g_malloc(strlen(get_home_dir()) + 26);
351         sprintf(plugin_status_file, "%s/%s/plugins.status", get_home_dir(), PF_DIR);
352     }
353     statusfile = fopen(plugin_status_file, "r");
354
355     if ((dir = opendir(dirname)) != NULL)
356     {
357         while ((file = readdir(dir)) != NULL)
358         {
359             /* don't try to open "." and ".." */
360             if (!(strcmp(file->d_name, "..") &&
361                   strcmp(file->d_name, "."))) continue;
362
363             /* skip anything but .la */
364             dot = strrchr(file->d_name, '.');
365             if (dot == NULL || ! strcmp(dot, LT_LIB_EXT)) continue;
366
367             sprintf(filename, "%s/%s", dirname, file->d_name);
368
369             if ((handle = g_module_open(filename, 0)) == NULL) continue;
370             name = (gchar *)file->d_name;
371             if (g_module_symbol(handle, "version", (gpointer*)&version) == FALSE)
372             {
373                 g_module_close(handle);
374                 continue;
375             }
376             if (g_module_symbol(handle, "protocol", (gpointer*)&protocol) == FALSE)
377             {
378                 g_module_close(handle);
379                 continue;
380             }
381             if (g_module_symbol(handle, "filter_string", (gpointer*)&filter_string) == FALSE)
382             {
383                 g_module_close(handle);
384                 continue;
385             }
386             if (dfilter_compile(filter_string, &filter) != 0) {
387                 g_module_close(handle);
388                 continue;
389             }
390             if (g_module_symbol(handle, "dissector", (gpointer*)&dissector) == FALSE) {
391                 if (filter != NULL)
392                     dfilter_destroy(filter);
393                 g_module_close(handle);
394                 continue;
395             }
396
397             if ((cr = add_plugin(handle, g_strdup(file->d_name), version,
398                                  protocol, filter_string, filter, dissector)))
399             {
400                 if (cr == EEXIST)
401                     fprintf(stderr, "The plugin : %s, version %s\n"
402                             "was found in multiple directories\n", name, version);
403                 else
404                     fprintf(stderr, "Memory allocation problem\n"
405                             "when processing plugin %s, version %sn",
406                             name, version);
407                 if (filter != NULL)
408                     dfilter_destroy(filter);
409                 g_module_close(handle);
410                 continue;
411             }
412             if (statusfile) {
413                 check_plugin_status(file->d_name, version, handle,
414                                     filter_string, statusfile);
415                 rewind(statusfile);
416             }
417         }
418         closedir(dir);
419     }
420     if (statusfile) fclose(statusfile);
421 }
422
423 /*
424  * init plugins
425  */
426 void
427 init_plugins()
428 {
429     if (plugin_list == NULL)      /* ensure init_plugins is only run once */
430     {
431         plugins_scan_dir(std_plug_dir);
432         plugins_scan_dir(local_plug_dir);
433         if ((strcmp(std_plug_dir, PLUGIN_DIR) != 0) &&
434             (strcmp(local_plug_dir, PLUGIN_DIR) != 0))
435         {
436           plugins_scan_dir(PLUGIN_DIR);
437         }
438         if (!user_plug_dir)
439         {
440             user_plug_dir = (gchar *)g_malloc(strlen(get_home_dir()) + 19);
441             sprintf(user_plug_dir, "%s/%s/plugins", get_home_dir(), PF_DIR);
442         }
443         plugins_scan_dir(user_plug_dir);
444     }
445 }
446
447 #endif