2ff92b9b13f9e450071f1b5c66b9801f6b6adced
[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;
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         values_num = 0;
102         for (i = 0; i < T_MAX; i++)
103         {
104                 if (qtype_counts[i] != 0)
105                 {
106                         values[2 * values_num] = i;
107                         values[(2 * values_num) + 1] = qtype_counts[i];
108                         values_num++;
109                 }
110         }
111
112         if (swrite (pipe_fd, (const void *) &values_num, sizeof (values_num)) != 0)
113         {
114                 syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s",
115                                 strerror (errno));
116                 return (-1);
117         }
118
119         if (swrite (pipe_fd, (const void *) values, 2 * sizeof (int) * values_num) != 0)
120         {
121                 syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s",
122                                 strerror (errno));
123                 return (-1);
124         }
125
126         return (values_num);
127 }
128
129 static void named_child_loop (void)
130 {
131         pcap_t *pcap_obj;
132         char    pcap_error[PCAP_ERRBUF_SIZE];
133
134         struct pollfd poll_fds[2];
135         int status;
136
137         /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
138         pcap_obj = pcap_open_live (pcap_device,
139                         PCAP_SNAPLEN,
140                         0 /* Not promiscuous */,
141                         0 /* no read timeout */,
142                         pcap_error);
143         if (pcap_obj == NULL)
144         {
145                 syslog (LOG_ERR, "named plugin: Opening interface `%s' failed: %s",
146                                 (pcap_device != NULL) ? pcap_device : "any",
147                                 pcap_error);
148                 close (pipe_fd);
149                 return;
150         }
151
152         /* Set up pipe end */
153         poll_fds[0].fd = pipe_fd;
154         poll_fds[0].events = POLLOUT;
155
156         /* Set up pcap device */
157         poll_fds[1].fd = pcap_fileno (pcap_obj);
158         poll_fds[1].events = POLLIN | POLLPRI;
159
160         while (42)
161         {
162                 status = poll (poll_fds, 2, -1 /* wait forever for a change */);
163
164                 if (status < 0)
165                 {
166                         syslog (LOG_ERR, "named plugin: poll(2) failed: %s",
167                                         strerror (errno));
168                         break;
169                 }
170
171                 if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
172                 {
173                         syslog (LOG_NOTICE, "named plugin: Pipe closed. Exiting.");
174                         break;
175                 }
176                 else if (poll_fds[0].revents & POLLOUT)
177                 {
178                         if (named_child_send_data () < 0)
179                         {
180                                 break;
181                         }
182                 }
183
184                 if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
185                 {
186                         syslog (LOG_ERR, "named plugin: pcap-device closed. Exiting.");
187                         break;
188                 }
189                 else if (poll_fds[1].revents & (POLLIN | POLLPRI))
190                 {
191                         /* TODO: Read and analyse packet */
192                         status = pcap_dispatch (pcap_obj,
193                                         10 /* Only handle 10 packets at a time */,
194                                         handle_pcap /* callback */,
195                                         NULL /* Whatever this means.. */);
196                         if (status < 0)
197                         {
198                                 syslog (LOG_ERR, "named plugin: pcap_dispatch failed: %s",
199                                                 pcap_geterr (pcap_obj));
200                                 break;
201                         }
202                 }
203         } /* while (42) */
204
205         close (pipe_fd);
206         pcap_close (pcap_obj);
207 } /* static void named_child_loop (void) */
208
209 static void named_init (void)
210 {
211 #if HAVE_LIBPCAP
212         int pipe_fds[2];
213         pid_t pid_child;
214
215         if (pipe (pipe_fds) != 0)
216         {
217                 syslog (LOG_ERR, "named plugin: pipe(2) failed: %s",
218                                 strerror (errno));
219                 return;
220         }
221
222         /* Fork off child */
223         pid_child = fork ();
224         if (pid_child < 0)
225         {
226                 syslog (LOG_ERR, "named plugin: fork(2) failed: %s",
227                                 strerror (errno));
228                 close (pipe_fds[0]);
229                 close (pipe_fds[1]);
230                 return;
231         }
232         else if (pid_child != 0)
233         {
234                 /* parent: Close the writing end, keep the reading end. */
235                 pipe_fd = pipe_fds[0];
236                 close (pipe_fds[1]);
237         }
238         else
239         {
240                 /* child: Close the reading end, keep the writing end. */
241                 pipe_fd = pipe_fds[1];
242                 close (pipe_fds[0]);
243
244                 named_child_loop ();
245                 exit (0);
246         }
247
248         fcntl (pipe_fd, F_SETFL, O_NONBLOCK);
249 #endif
250 }
251
252 #if NAMED_HAVE_READ
253 static void named_read (void)
254 {
255         int values[2 * T_MAX];
256         int values_num;
257         int qtype;
258         int counter;
259         int i;
260
261         if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0)
262         {
263                 syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
264                                 strerror (errno));
265                 return;
266         }
267
268         assert ((values_num >= 0) && (values_num <= T_MAX));
269
270         if (sread (pipe_fd, (void *) values, 2 * sizeof (int) * values_num) != 0)
271         {
272                 syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
273                                 strerror (errno));
274                 return;
275         }
276
277         for (i = 0; i < values_num; i++)
278         {
279                 qtype = values[2 * i];
280                 counter = values[(2 * i) + 1];
281
282                 DBG ("qtype = %i; counter = %i;", qtype, counter);
283         }
284 }
285 #else /* if !NAMED_HAVE_READ */
286 # define named_read NULL
287 #endif
288
289 void module_register (void)
290 {
291         plugin_register (MODULE_NAME, named_init, named_read, NULL);
292         /* TODO */
293         cf_register (MODULE_NAME, named_config, config_keys, config_keys_num);
294 }
295
296 #undef MODULE_NAME