named plugin: Many many more fixes to get the plugin working.
[collectd.git] / src / named.c
1 /**
2  * collectd - src/named.c
3  * Copyright (C) 2006  Florian octo Forster
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; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
27 #include "utils_debug.h"
28
29 #if HAVE_SYS_POLL_H
30 # include <sys/poll.h>
31 #endif
32
33 #define MODULE_NAME "named"
34
35 #if HAVE_LIBPCAP
36 # define NAMED_HAVE_CONFIG 1
37 #else
38 # define NAMED_HAVE_CONFIG 0
39 #endif
40
41 #if HAVE_LIBPCAP
42 # include "dnstop.h"
43 # define NAMED_HAVE_READ 1
44 #else
45 # define NAMED_HAVE_READ 0
46 #endif
47
48 static char *qtype_file = "named/qtype-%s.rrd";
49
50 static char *qtype_ds_def[] =
51 {
52         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:U",
53         NULL
54 };
55 static int qtype_ds_num = 1;
56
57 #if NAMED_HAVE_CONFIG
58 #if HAVE_LIBPCAP
59 static char *config_keys[] =
60 {
61         "Interface",
62         NULL
63 };
64 static int config_keys_num = 1;
65 #endif /* HAVE_LIBPCAP */
66 #endif /* NAMED_HAVE_CONFIG */
67
68 #if HAVE_LIBPCAP
69 #define PCAP_SNAPLEN 1460
70 static char   *pcap_device = NULL;
71 static int     pipe_fd = -1;
72 #endif
73
74 #if NAMED_HAVE_CONFIG
75 static int named_config (char *key, char *value)
76 {
77 #if HAVE_LIBPCAP
78         if (strcasecmp (key, "Interface") == 0)
79         {
80                 if (pcap_device != NULL)
81                         free (pcap_device);
82                 if ((pcap_device = strdup (value)) == NULL)
83                         return (1);
84         }
85         else
86         {
87                 return (-1);
88         }
89
90         return (0);
91 #endif /* HAVE_LIBPCAP */
92 }
93 #endif /* NAMED_HAVE_CONFIG */
94
95 static int named_child_send_data (void)
96 {
97         int values[2 * T_MAX];
98         int values_num;
99         int i;
100
101         if (pipe_fd < 0)
102                 return (-1);
103
104         values_num = 0;
105         for (i = 0; i < T_MAX; i++)
106         {
107                 if (qtype_counts[i] != 0)
108                 {
109                         values[2 * values_num] = i;
110                         values[(2 * values_num) + 1] = qtype_counts[i];
111                         values_num++;
112                 }
113         }
114
115         DBG ("swrite (pipe_fd = %i, values_num = %i)", pipe_fd, values_num);
116         if (swrite (pipe_fd, (const void *) &values_num, sizeof (values_num)) != 0)
117         {
118                 DBG ("Writing to pipe failed: %s", strerror (errno));
119                 syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s",
120                                 strerror (errno));
121                 return (-1);
122         }
123
124         if (values_num == 0)
125                 return (0);
126
127         DBG ("swrite (pipe_fd = %i, values = %p, size = %i)", pipe_fd, (void *) values, (int) (sizeof (int) * values_num));
128         if (swrite (pipe_fd, (const void *) values, 2 * sizeof (int) * values_num) != 0)
129         {
130                 DBG ("Writing to pipe failed: %s", strerror (errno));
131                 syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s",
132                                 strerror (errno));
133                 return (-1);
134         }
135
136         return (values_num);
137 }
138
139 static void named_child_loop (void)
140 {
141         pcap_t *pcap_obj;
142         char    pcap_error[PCAP_ERRBUF_SIZE];
143         struct  bpf_program fp;
144
145         struct pollfd poll_fds[2];
146         int status;
147
148         /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
149         DBG ("Creating PCAP object..");
150         pcap_obj = pcap_open_live (pcap_device,
151                         PCAP_SNAPLEN,
152                         0 /* Not promiscuous */,
153                         0 /* no read timeout */,
154                         pcap_error);
155         if (pcap_obj == NULL)
156         {
157                 syslog (LOG_ERR, "named plugin: Opening interface `%s' failed: %s",
158                                 (pcap_device != NULL) ? pcap_device : "any",
159                                 pcap_error);
160                 close (pipe_fd);
161                 pipe_fd = -1;
162                 return;
163         }
164         pcap = pcap_obj; /* FIXME: This is used by `handle_pcap' */
165
166         memset (&fp, 0, sizeof (fp));
167         if (pcap_compile (pcap_obj, &fp, "udp dst port 53", 1, 0) < 0)
168         {
169                 DBG ("pcap_compile failed");
170                 syslog (LOG_ERR, "named plugin: pcap_compile failed");
171                 close (pipe_fd);
172                 pipe_fd = -1;
173                 return;
174         }
175         if (pcap_setfilter (pcap_obj, &fp) < 0)
176         {
177                 DBG ("pcap_setfilter failed");
178                 syslog (LOG_ERR, "named plugin: pcap_setfilter failed");
179                 close (pipe_fd);
180                 pipe_fd = -1;
181                 return;
182         }
183
184         DBG ("PCAP object created.");
185
186         /* Set up pipe end */
187         poll_fds[0].fd = pipe_fd;
188         poll_fds[0].events = POLLOUT;
189
190         /* Set up pcap device */
191         poll_fds[1].fd = pcap_fileno (pcap_obj);
192         poll_fds[1].events = POLLIN | POLLPRI;
193
194         while (42)
195         {
196                 DBG ("poll (...)");
197                 status = poll (poll_fds, 2, -1 /* wait forever for a change */);
198
199                 if (status < 0)
200                 {
201                         syslog (LOG_ERR, "named plugin: poll(2) failed: %s",
202                                         strerror (errno));
203                         break;
204                 }
205
206                 if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
207                 {
208                         DBG ("Pipe closed. Exiting.");
209                         syslog (LOG_NOTICE, "named plugin: Pipe closed. Exiting.");
210                         break;
211                 }
212                 else if (poll_fds[0].revents & POLLOUT)
213                 {
214                         DBG ("Calling `named_child_send_data'");
215                         if (named_child_send_data () < 0)
216                         {
217                                 break;
218                         }
219                 }
220
221                 if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
222                 {
223                         DBG ("pcap-device closed. Exiting.");
224                         syslog (LOG_ERR, "named plugin: pcap-device closed. Exiting.");
225                         break;
226                 }
227                 else if (poll_fds[1].revents & (POLLIN | POLLPRI))
228                 {
229                         /* TODO: Read and analyse packet */
230                         status = pcap_dispatch (pcap_obj,
231                                         1 /* Only handle 10 packets at a time */,
232                                         handle_pcap /* callback */,
233                                         NULL /* Whatever this means.. */);
234                         if (status < 0)
235                         {
236                                 DBG ("pcap_dispatch failed: %s", pcap_geterr (pcap_obj));
237                                 syslog (LOG_ERR, "named plugin: pcap_dispatch failed: %s",
238                                                 pcap_geterr (pcap_obj));
239                                 break;
240                         }
241                 }
242         } /* while (42) */
243
244         DBG ("child is exiting");
245
246         close (pipe_fd);
247         pipe_fd = -1;
248         pcap_close (pcap_obj);
249 } /* static void named_child_loop (void) */
250
251 static void named_init (void)
252 {
253 #if HAVE_LIBPCAP
254         int pipe_fds[2];
255         pid_t pid_child;
256
257         if (pipe (pipe_fds) != 0)
258         {
259                 syslog (LOG_ERR, "named plugin: pipe(2) failed: %s",
260                                 strerror (errno));
261                 return;
262         }
263
264         /* Fork off child */
265         pid_child = fork ();
266         if (pid_child < 0)
267         {
268                 syslog (LOG_ERR, "named plugin: fork(2) failed: %s",
269                                 strerror (errno));
270                 close (pipe_fds[0]);
271                 close (pipe_fds[1]);
272                 return;
273         }
274         else if (pid_child != 0)
275         {
276                 /* parent: Close the writing end, keep the reading end. */
277                 pipe_fd = pipe_fds[0];
278                 close (pipe_fds[1]);
279         }
280         else
281         {
282                 /* child: Close the reading end, keep the writing end. */
283                 pipe_fd = pipe_fds[1];
284                 close (pipe_fds[0]);
285
286                 named_child_loop ();
287                 exit (0);
288         }
289
290         /* fcntl (pipe_fd, F_SETFL, O_NONBLOCK); */
291 #endif
292 }
293
294 static void qtype_write (char *host, char *inst, char *val)
295 {
296         char file[512];
297         int status;
298
299         status = snprintf (file, 512, qtype_file, inst);
300         if (status < 1)
301                 return;
302         else if (status >= 512)
303                 return;
304
305         rrd_update_file (host, file, val, qtype_ds_def, qtype_ds_num);
306 }
307
308 static void qtype_submit (int qtype_int, unsigned int counter)
309 {
310         char *qtype_char;
311         char  buffer[32];
312         int   status;
313
314         qtype_char = qtype_str (qtype_int);
315
316         status = snprintf (buffer, 32, "N:%i", counter);
317         if ((status < 1) || (status >= 32))
318                 return;
319
320         plugin_submit ("named_qtype", qtype_char, buffer);
321 }
322
323 #if NAMED_HAVE_READ
324 static void named_read (void)
325 {
326         int values[2 * T_MAX];
327         int values_num;
328         int qtype;
329         int counter;
330         int i;
331
332         if (pipe_fd < 0)
333                 return;
334
335         DBG ("Reading from pipe_fd = %i..", pipe_fd);
336         if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0)
337         {
338                 syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
339                                 strerror (errno));
340                 pipe_fd = -1;
341                 return;
342         }
343
344         assert ((values_num >= 0) && (values_num <= T_MAX));
345
346         if (values_num == 0)
347         {
348                 DBG ("No values available; returning");
349                 return;
350         }
351
352         DBG ("Reading %i qtype/values from pipe_fd = %i..", values_num, pipe_fd);
353         if (sread (pipe_fd, (void *) values, 2 * sizeof (int) * values_num) != 0)
354         {
355                 syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
356                                 strerror (errno));
357                 pipe_fd = -1;
358                 return;
359         }
360
361         for (i = 0; i < values_num; i++)
362         {
363                 qtype = values[2 * i];
364                 counter = values[(2 * i) + 1];
365
366                 DBG ("qtype = %i; counter = %i;", qtype, counter);
367                 qtype_submit (qtype, counter);
368         }
369 }
370 #else /* if !NAMED_HAVE_READ */
371 # define named_read NULL
372 #endif
373
374 void module_register (void)
375 {
376         plugin_register (MODULE_NAME, named_init, named_read, NULL);
377         plugin_register ("named_qtype", NULL, NULL, qtype_write);
378         /* TODO */
379         cf_register (MODULE_NAME, named_config, config_keys, config_keys_num);
380 }
381
382 #undef MODULE_NAME