From Tom Brezinski:
[obnox/wireshark/wip.git] / tap-expert.c
1 /* tap-expert.c
2  * Copyright 2011 Martin Mathieson
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <stdio.h>
31
32 #ifdef HAVE_SYS_TYPES_H
33 #include <sys/types.h>
34 #endif
35
36 #include <string.h>
37 #include <epan/packet.h>
38 #include <epan/packet_info.h>
39 #include <epan/tap.h>
40 #include <epan/stat_cmd_args.h>
41 #include <epan/expert.h>
42
43 /* Tap data */
44 typedef enum severity_level_t {
45     chat_level=0,
46     note_level,
47     warn_level,
48     error_level,
49     max_level
50 } severity_level_t;
51
52 /* This variable stores the lowest level that will be displayed.
53    May be changed from the command line */
54 static severity_level_t lowest_report_level = chat_level;
55
56 typedef struct expert_entry
57 {
58     guint32      group;
59     const gchar  *protocol;
60     gchar        *summary;
61     int          frequency;
62 } expert_entry;
63
64
65 /* Overall struct for storing all data seen */
66 typedef struct expert_tapdata_t {
67     GArray         *ei_array[max_level];   /* expert info items */
68     GStringChunk*  text;    /* for efficient storage of summary strings */
69 } expert_tapdata_t;
70
71
72 /* Reset expert stats */
73 static void
74 expert_stat_reset(void *tapdata)
75 {
76     gint n;
77     expert_tapdata_t *etd = tapdata;
78
79     /* Free & reallocate chunk of strings */
80     g_string_chunk_free(etd->text);
81     etd->text = g_string_chunk_new(100);
82
83     /* Empty each of the arrays */
84     for (n=0; n < max_level; n++) {
85         g_array_set_size(etd->ei_array[n], 0);
86     }
87 }
88
89 /* Process stat struct for an expert frame */
90 static int
91 expert_stat_packet(void *tapdata, packet_info *pinfo _U_, epan_dissect_t *edt _U_,
92                    const void *pointer)
93 {
94     expert_info_t    *ei = (expert_info_t *)pointer;
95     expert_tapdata_t *data = tapdata;
96     severity_level_t severity_level;
97     expert_entry     tmp_entry;
98     expert_entry     *entry;
99     guint            n;
100
101     switch (ei->severity) {
102         case PI_CHAT:
103             severity_level = chat_level;
104             break;
105         case PI_NOTE:
106             severity_level = note_level;
107             break;
108         case PI_WARN:
109             severity_level = warn_level;
110             break;
111         case PI_ERROR:
112             severity_level = error_level;
113             break;
114         default:
115             g_assert_not_reached();
116             return 0;
117     }
118
119     /* Don't store details at a lesser severity than we are interested in */
120     if (severity_level < lowest_report_level) {
121         return 1;
122     }
123
124     /* If a duplicate just bump up frequency.
125        TODO: could make more efficient by avoiding linear search...*/
126     for (n=0; n < data->ei_array[severity_level]->len; n++) {
127         entry = &g_array_index(data->ei_array[severity_level], expert_entry, n);
128         if ((strcmp(ei->protocol, entry->protocol) == 0) &&
129             (strcmp(ei->summary, entry->summary) == 0)) {
130             entry->frequency++;
131             return 1;
132         }
133     }
134
135     /* Else Add new item to end of list for severity level */
136     g_array_append_val(data->ei_array[severity_level], tmp_entry);
137
138     /* Get pointer to newly-allocated item */
139     entry = &g_array_index(data->ei_array[severity_level], expert_entry,
140                            data->ei_array[severity_level]->len - 1); /* ugly */
141     /* Copy/Store protocol and summary strings efficiently using GStringChunk */
142     entry->protocol = g_string_chunk_insert_const(data->text, ei->protocol);
143     entry->summary = g_string_chunk_insert_const(data->text, ei->summary);
144     entry->group = ei->group;
145     entry->frequency = 1;
146
147     return 1;
148 }
149
150 /* Output for all of the items of one severity */
151 static void draw_items_for_severity(GArray *items, const gchar *label)
152 {
153     guint n;
154     expert_entry *ei;
155     int total = 0;
156
157     /* Don't print title if no items */
158     if (items->len == 0) {
159         return;
160     }
161
162     /* Add frequencies together to get total */
163     for (n=0; n < items->len; n++) {
164         ei = &g_array_index(items, expert_entry, n);
165         total += ei->frequency;
166     }
167
168     /* Title */
169     printf("\n%s (%u)\n", label, total);
170     printf("=============\n");
171
172     /* Column headings */
173     printf("   Frequency      Group           Protocol  Summary\n");
174
175     /* Items */
176     for (n=0; n < items->len; n++) {
177         ei = &g_array_index(items, expert_entry, n);
178         printf("%12u %10s %18s  %s\n",
179               ei->frequency,
180               val_to_str(ei->group, expert_group_vals, "Unknown"),
181               ei->protocol, ei->summary);
182     }
183 }
184
185 /* (Re)draw expert stats */
186 static void
187 expert_stat_draw(void *phs _U_)
188 {
189     /* Look up the statistics struct */
190     expert_tapdata_t *hs = (expert_tapdata_t *)phs;
191
192     draw_items_for_severity(hs->ei_array[error_level], "Errors");
193     draw_items_for_severity(hs->ei_array[warn_level],  "Warns");
194     draw_items_for_severity(hs->ei_array[note_level],  "Notes");
195     draw_items_for_severity(hs->ei_array[chat_level],  "Chats");
196 }
197
198 /* Create a new expert stats struct */
199 static void expert_stat_init(const char *optarg, void *userdata _U_)
200 {
201     const char        *args = NULL;
202     const char        *filter = NULL;
203     GString           *error_string;
204     expert_tapdata_t  *hs;
205     int n;
206
207     /* Check for args. */
208     if (strncmp(optarg, "expert", 6) == 0) {
209         /* Skip those characters */
210         args = optarg + 6;
211     }
212     else {
213         /* No args. Will show all reports, with no filter */
214         lowest_report_level = max_level;
215     }
216
217     /* First (optional) arg is Error|Warn|Note|Chat */
218     if (args != NULL) {
219         if (g_ascii_strncasecmp(args, ",error", 6) == 0) {
220             lowest_report_level = error_level;
221             args += 6;
222         }
223         else if (g_ascii_strncasecmp(args, ",warn", 5) == 0) {
224             lowest_report_level = warn_level;
225             args += 5;
226         } else if (g_ascii_strncasecmp(args, ",note", 5) == 0) {
227             lowest_report_level = note_level;
228             args += 5;
229         } else if (g_ascii_strncasecmp(args, ",chat", 5) == 0) {
230             lowest_report_level = chat_level;
231             args += 5;
232         }
233     }
234
235     /* Second (optional) arg is a filter string */
236     if (args != NULL) {
237         if (args[0] == ',') {
238             filter = args+1;
239         }
240     }
241
242
243     /* Create top-level struct */
244     hs = g_malloc(sizeof(expert_tapdata_t));
245     memset(hs, 0,  sizeof(expert_tapdata_t));
246
247     /* Allocate chunk of strings */
248     hs->text = g_string_chunk_new(100);
249
250     /* Allocate GArray for each severity level */
251     for (n=0; n < max_level; n++) {
252         hs->ei_array[n] = g_array_sized_new(FALSE, FALSE, sizeof(expert_info_t), 1000);
253     }
254
255     /**********************************************/
256     /* Register the tap listener                  */
257     /**********************************************/
258
259     error_string = register_tap_listener("expert", hs,
260                                          filter, 0,
261                                          expert_stat_reset,
262                                          expert_stat_packet,
263                                          expert_stat_draw);
264     if (error_string) {
265         printf("Expert tap error (%s)!\n", error_string->str);
266         g_string_free(error_string, TRUE);
267         g_free(hs);
268         exit(1);
269     }
270 }
271
272
273 /* Register this tap listener (need void on own so line register function found) */
274 void
275 register_tap_listener_expert_info(void)
276 {
277     register_stat_cmd_arg("expert", expert_stat_init, NULL);
278 }
279