Updated configuration directives.
[collectd.git] / src / snort.c
1 /**
2  * collectd - src/snort.c
3  * Copyright (C) 2013 Kris Nielander
4  * 
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Kris Nielander <nielander@fox-it.com>
20  *
21  * This plugin is based on the snmp plugin by Florian octo Forster.
22  *
23  **/
24
25 #include "collectd.h"
26 #include "plugin.h" /* plugin_register_*, plugin_dispatch_values */
27 #include "common.h" /* auxiliary functions */
28 #include <sys/mman.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 struct metric_definition_s {
35     char *name;
36     char *type;
37     int data_source_type;
38     int index;
39     struct metric_definition_s *next;
40 };
41 typedef struct metric_definition_s metric_definition_t;
42
43 struct instance_definition_s {
44     char *name;
45     char *path;
46     metric_definition_t **metric_list;
47     int metric_list_len;
48     cdtime_t last;
49     cdtime_t interval;
50     struct instance_definition_s *next;
51 };
52 typedef struct instance_definition_s instance_definition_t;
53
54 /* Private */
55 static metric_definition_t *metric_head = NULL;
56
57 static int snort_read_submit(instance_definition_t *id, metric_definition_t *md,
58     const char *buf){
59
60     /* Registration variables */
61     value_t value;
62     value_list_t vl = VALUE_LIST_INIT;
63
64     DEBUG("snort plugin: plugin_instance=%s type=%s value=%s", id->name, 
65         md->type, buf);
66
67     if (buf == NULL)
68         return (-1);
69
70     /* Parse value */
71     parse_value(buf, &value, md->data_source_type);
72
73     /* Register */
74     vl.values_len = 1;
75     vl.values = &value;
76
77     sstrncpy(vl.host, hostname_g, sizeof (vl.host));
78     sstrncpy(vl.plugin, "snort", sizeof(vl.plugin));
79     sstrncpy(vl.plugin_instance, id->name, sizeof(vl.plugin_instance));
80     sstrncpy(vl.type, md->type, sizeof(vl.type));
81
82     vl.time = id->last;
83     vl.interval = id->interval;
84
85     DEBUG("snort plugin: -> plugin_dispatch_values (&vl);");
86     plugin_dispatch_values(&vl);
87
88     return (0);
89 }
90
91 static int snort_read(user_data_t *ud){
92     instance_definition_t *id;
93     metric_definition_t *md;
94     
95     int i;
96     int fd;
97     int count;
98
99     char **metrics;
100     char **metrics_t;
101
102     struct stat sb;
103     char *buf, *buf_t;
104
105     /* mmap, char pointers */
106     char *p_start;
107     char *p_end;
108
109     id = ud->data;
110     DEBUG("snort plugin: snort_read (instance = %s)", id->name);
111
112     fd = open(id->path, O_RDONLY);
113     if (fd == -1){
114         ERROR("snort plugin: Unable to open `%s'.", id->path);
115         return (-1);
116     }
117
118     if ((fstat(fd, &sb) != 0) || (!S_ISREG(sb.st_mode))){
119         ERROR("snort plugin: `%s' is not a file.", id->path);
120         return (-1);
121     }
122
123     if (sb.st_size == 0){
124         ERROR("snort plugin: `%s' is empty.", id->path);
125         return (-1);
126     }
127
128     p_start = mmap(/* addr = */ NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 
129         /* offset = */ 0);
130     if (p_start == MAP_FAILED){
131         ERROR("snort plugin: mmap error");
132         return (-1);
133     }
134
135     /* Set the start value count. */
136     count = 1; 
137
138     /* Set the pointer to the last line of the file and count the fields. 
139      (Skip the last two characters of the buffer: `\n' and `\0') */    
140     for (p_end = (p_start + sb.st_size) - 2; p_end > p_start; --p_end){
141         if (*p_end == ','){
142             ++count;
143         } else if (*p_end == '\n'){
144             ++p_end;
145             break;
146         }
147     }
148     
149     if (count == 1){
150         ERROR("snort plugin: last line of `%s' does not contain enough values.", id->path);
151         return (-1);
152     }
153
154     if (*p_end == '#'){
155         ERROR("snort plugin: last line of `%s' is a comment.", id->path);
156         return (-1);
157     }
158
159     /* Copy the line to the buffer */
160     buf_t = buf = strdup(p_end);
161
162     /* Done with mmap and file pointer */
163     close(fd);
164     munmap(p_start, sb.st_size);
165
166     /* Create a list of all values */
167     metrics = (char **)calloc(count, sizeof(char *));
168     if (metrics == NULL){
169         return (-1);
170     }
171
172     for (metrics_t = metrics; (*metrics_t = strsep(&buf_t, ",")) != NULL;)
173         if (**metrics_t != '\0')
174             if (++metrics_t >= &metrics[count])
175                 break;
176
177     /* Set last time */
178     id->last = TIME_T_TO_CDTIME_T(strtol(*metrics, NULL, 0));
179
180     /* Register values */
181     for (i = 0; i < id->metric_list_len; ++i){
182         md = id->metric_list[i];
183         snort_read_submit(id, md, metrics[md->index]);
184     }
185
186     /* Free up resources */
187     free(metrics);
188     free(buf);
189     return (0);
190 }
191
192 static void snort_metric_definition_destroy(void *arg){
193     metric_definition_t *md;
194
195     md = arg;
196     if (md == NULL)
197         return;
198
199     if (md->name != NULL)
200         DEBUG("snort plugin: Destroying metric definition `%s'.", md->name);
201
202     sfree(md->name);
203     sfree(md->type);
204     sfree(md);
205 }
206
207 static int snort_config_add_metric_index(metric_definition_t *md, oconfig_item_t *ci){
208     if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER)){
209         WARNING("snort plugin: `Index' needs exactly one integer argument.");
210         return (-1);
211     }
212
213     md->index = (int)ci->values[0].value.number;
214     if (md->index <= 0){
215         WARNING("snort plugin: `Index' must be higher than 0.");
216         return (-1);
217     }
218
219     return (0);
220 }
221
222 /* Parse metric  */
223 static int snort_config_add_metric(oconfig_item_t *ci){
224     metric_definition_t *md;
225     const data_set_t *ds;
226     int status = 0;
227     int i;
228
229     if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)){
230         WARNING("snort plugin: The `Metric' config option needs exactly one string argument.");
231         return (-1);
232     }
233
234     md = (metric_definition_t *)malloc(sizeof(*md));
235     if (md == NULL)
236         return (-1);
237     memset(md, 0, sizeof(*md));
238
239     md->name = strdup(ci->values[0].value.string);
240     if (md->name == NULL){
241         free(md);
242         return (-1);
243     }
244
245     for (i = 0; i < ci->children_num; ++i){
246         oconfig_item_t *option = ci->children + i;
247         status = 0;
248
249         if (strcasecmp("Type", option->key) == 0)
250             status = cf_util_get_string(option, &md->type);
251         else if (strcasecmp("Index", option->key) == 0)
252             status = snort_config_add_metric_index(md, option);
253         else {
254             WARNING("snort plugin: Option `%s' not allowed here.", option->key);
255             status = -1;
256         }
257
258         if (status != 0)
259             break;
260     }
261
262     if (status != 0){
263         snort_metric_definition_destroy(md);
264         return (-1);
265     }
266
267     /* Verify all necessary options have been set. */
268     if (md->type == NULL){
269         WARNING("snort plugin: Option `Type' must be set.");
270         status = -1;
271     } else if (md->index == 0){
272         WARNING("snort plugin: Option `Index' must be set.");
273         status = -1;
274     }
275
276     if (status != 0){
277         snort_metric_definition_destroy(md);
278         return (-1);
279     }
280     
281     /* Retrieve the data source type from the types db. */
282     ds = plugin_get_ds(md->type);
283     if (ds == NULL){
284         WARNING("snort plugin: `Type' must be defined in `types.db'.");
285         snort_metric_definition_destroy(md);
286         return (-1);
287     } else {
288         md->data_source_type = ds->ds->type;
289     }
290
291     DEBUG("snort plugin: md = { name = %s, type = %s, data_source_type = %d, index = %d }",
292         md->name, md->type, md->data_source_type, md->index);
293
294     if (metric_head == NULL)
295         metric_head = md;
296     else {
297         metric_definition_t *last;
298         last = metric_head;
299         while (last->next != NULL)
300             last = last->next;
301         last->next = md;
302     }
303
304     return (0);
305 }
306
307 static void snort_instance_definition_destroy(void *arg){
308     instance_definition_t *id;
309
310     id = arg;
311     if (id == NULL)
312         return;
313
314     if (id->name != NULL)
315         DEBUG("snort plugin: Destroying instance definition `%s'.", id->name);
316
317     sfree(id->name);
318     sfree(id->path);
319     sfree(id->metric_list);
320     sfree(id);
321 }
322
323 static int snort_config_add_instance_collect(instance_definition_t *id, oconfig_item_t *ci){
324     metric_definition_t *metric;
325     int i;
326
327     if (ci->values_num < 1){
328         WARNING("snort plugin: The `Collect' config option needs at least one argument.");
329         return (-1);
330     }
331
332     /* Verify string arguments */
333     for (i = 0; i < ci->values_num; ++i)
334         if (ci->values[i].type != OCONFIG_TYPE_STRING){
335             WARNING("snort plugin: All arguments to `Collect' must be strings.");
336             return (-1);
337         }
338
339     id->metric_list = (metric_definition_t **)malloc(sizeof(metric_definition_t *) * ci->values_num);
340     if (id->metric_list == NULL)
341         return (-1);
342
343     for (i = 0; i < ci->values_num; ++i){
344         for (metric = metric_head; metric != NULL; metric = metric->next)
345             if (strcasecmp(ci->values[i].value.string, metric->name) == 0)
346                 break;
347
348         if (metric == NULL){
349             WARNING("snort plugin: `Collect' argument not found `%s'.", ci->values[i].value.string);
350             return (-1);
351         }
352
353         DEBUG("snort plugin: id { name=%s md->name=%s }", id->name, metric->name);
354
355         id->metric_list[i] = metric;
356         id->metric_list_len++;
357     }
358
359     return (0);
360 }
361
362 /* Parse instance  */
363 static int snort_config_add_instance(oconfig_item_t *ci){
364
365     instance_definition_t* id;
366     int status = 0;
367     int i;
368
369     /* Registration variables */
370     char cb_name[DATA_MAX_NAME_LEN];
371     user_data_t cb_data;
372     struct timespec cb_interval;
373
374     if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)){
375         WARNING("snort plugin: The `Instance' config option needs exactly one string argument.");
376         return (-1);
377     }
378
379     id = (instance_definition_t *)malloc(sizeof(*id));
380     if (id == NULL)
381         return (-1);
382     memset(id, 0, sizeof(*id));
383
384     id->name = strdup(ci->values[0].value.string);
385     if (id->name == NULL){
386         free(id);
387         return (-1);
388     }
389
390     /* Use default interval. */
391     id->interval = plugin_get_interval();
392
393     for (i = 0; i < ci->children_num; ++i){
394         oconfig_item_t *option = ci->children + i;
395         status = 0;
396
397         if (strcasecmp("Path", option->key) == 0)
398             status = cf_util_get_string(option, &id->path);
399         else if (strcasecmp("Collect", option->key) == 0)
400             status = snort_config_add_instance_collect(id, option);
401         else if (strcasecmp("Interval", option->key) == 0)
402             cf_util_get_cdtime(option, &id->interval);
403         else {
404             WARNING("snort plugin: Option `%s' not allowed here.", option->key);
405             status = -1;
406         }
407
408         if (status != 0)
409             break;
410     }
411
412     if (status != 0){
413         snort_instance_definition_destroy(id);
414         return (-1);
415     }
416
417     /* Verify all necessary options have been set. */
418     if (id->path == NULL){
419         WARNING("snort plugin: Option `Path' must be set.");
420         status = -1;
421     } else if (id->metric_list == NULL){
422         WARNING("snort plugin: Option `Collect' must be set.");
423         status = -1;
424    }
425
426     if (status != 0){
427         snort_instance_definition_destroy(id);
428         return (-1);
429     }
430
431     DEBUG("snort plugin: id = { name = %s, path = %s }", id->name, id->path);
432
433     ssnprintf (cb_name, sizeof (cb_name), "snort-%s", id->name);
434     memset(&cb_data, 0, sizeof(cb_data));
435     cb_data.data = id;
436     cb_data.free_func = snort_instance_definition_destroy;
437     CDTIME_T_TO_TIMESPEC(id->interval, &cb_interval);
438     status = plugin_register_complex_read(NULL, cb_name, snort_read, &cb_interval, &cb_data);
439
440     if (status != 0){
441         ERROR("snort plugin: Registering complex read function failed.");
442         snort_instance_definition_destroy(id);
443         return (-1);
444     }
445
446     return (0);
447 }
448
449 /* Parse blocks */
450 static int snort_config(oconfig_item_t *ci){
451     int i;
452     for (i = 0; i < ci->children_num; ++i){
453         oconfig_item_t *child = ci->children + i;
454         if (strcasecmp("Metric", child->key) == 0)
455             snort_config_add_metric(child);
456         else if (strcasecmp("Instance", child->key) == 0)
457             snort_config_add_instance(child);
458         else
459             WARNING("snort plugin: Ignore unknown config option `%s'.", child->key);
460     }
461
462     return (0);
463 } /* int snort_config */
464
465 static int snort_shutdown(void){
466     metric_definition_t *metric_this;
467     metric_definition_t *metric_next;
468
469     metric_this = metric_head;
470     metric_head = NULL;
471
472     while (metric_this != NULL){
473         metric_next = metric_this->next;
474         snort_metric_definition_destroy(metric_this);
475         metric_this = metric_next;
476     }
477
478     return (0);
479 }
480
481 void module_register(void){
482     plugin_register_complex_config("snort", snort_config);
483     plugin_register_shutdown("snort", snort_shutdown);
484 }
485