Added a first rough draft of the new named plugin.
[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
27 #define MODULE_NAME "named"
28
29 #if HAVE_LIBPCAP
30 # define NAMED_HAVE_CONFIG 1
31 #else
32 # define NAMED_HAVE_CONFIG 0
33 #endif
34
35 #if HAVE_LIBPCAP
36 # include "dnstop.h"
37 # define NAMED_HAVE_READ 1
38 #else
39 # define NAMED_HAVE_READ 0
40 #endif
41
42 static char qtype_file = "named/qtype-%s.rrd";
43
44 static char *qtype_ds_def[] =
45 {
46         "DS:value:COUNTER:"COLLECTD_HEARTBEAT":0:U",
47         NULL
48 };
49 static int qtype_ds_num = 1;
50
51 #if NAMED_HAVE_CONFIG
52 #if HAVE_LIBPCAP
53 static char *config_keys[] =
54 {
55         "Interface",
56         NULL
57 };
58 static int config_keys_num = 1;
59 #endif /* HAVE_LIBPCAP */
60 #endif /* NAMED_HAVE_CONFIG */
61
62 #if HAVE_LIBPCAP
63 static char   *pcap_device = NULL;
64 static int     pipe_fd;
65 #endif
66
67 #if NAMED_HAVE_CONFIG
68 static int traffic_config (char *key, char *value)
69 {
70 #if HAVE_LIBPCAP
71         if (strcasecmp (key, "Interface") == 0)
72         {
73                 if (pcap_device != NULL)
74                         free (pcap_device);
75                 if ((pcap_device = strdup (value)) == NULL)
76                         return (1);
77         }
78         else
79         {
80                 return (-1);
81         }
82
83         return (0);
84 #endif /* HAVE_LIBPCAP */
85 }
86 #endif /* NAMED_HAVE_CONFIG */
87
88 static int named_child_send_data (void)
89 {
90         int values[2 * T_MAX];
91         int values_num;
92         int i;
93
94         values_num = 0;
95         for (i = 0; i < T_MAX; i++)
96         {
97                 if (qtype_counts[i] != 0)
98                 {
99                         values[2 * values_num] = i;
100                         values[(2 * values_num) + 1] = qtype_counts[i];
101                         values_num++;
102                 }
103         }
104
105         if (swrite (pipe_fd, (const void *) &values_num, sizeof (values_num)) != 0)
106         {
107                 syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s",
108                                 strerror (errno));
109                 return (-1);
110         }
111
112         if (swrite (pipe_fd, (const void *) values, 2 * sizeof (int) * values_num) != 0)
113         {
114                 syslog (LOG_ERR, "named plugin: Writing to pipe failed: %s",
115                                 strerror (errno));
116                 return (-1);
117         }
118
119         return (values_num);
120 }
121
122 static void named_child_loop (void)
123 {
124         pcap_t *pcap_obj;
125         char    pcap_error[PCAP_ERRBUF_SIZE];
126
127         struct pollfd poll_fds[2];
128         int status;
129
130         /* Passing `pcap_device == NULL' is okay and the same as passign "any" */
131         pcap_obj = pcap_open_live (pcap_device, /* Not promiscuous */ 0,
132                         /* no read timeout */ 0, pcap_error);
133         if (pcap_obj == NULL)
134         {
135                 syslog (LOG_ERR, "named plugin: Opening interface `%s' failed: %s",
136                                 (pcap_device != NULL) ? pcap_device : "any",
137                                 pcap_error);
138                 close (pipe_fd);
139                 return;
140         }
141
142         /* Set up pipe end */
143         poll_fds[0].fd = pipe_fd;
144         poll_fds[0].events = POLLOUT;
145
146         /* Set up pcap device */
147         poll_fds[1].fd = pcap_fileno (pcap_obj);
148         poll_fds[1].events = POLLIN | POLLPRI;
149
150         while (42)
151         {
152                 status = poll (poll_fds, 2, -1 /* wait forever for a change */);
153
154                 if (status < 0)
155                 {
156                         syslog (LOG_ERR, "named plugin: poll(2) failed: %s",
157                                         strerror (errno));
158                         break;
159                 }
160
161                 if (poll_fds[0].revents & (POLLERR | POLLHUP | POLLNVAL))
162                 {
163                         syslog (LOG_NOTICE, "named plugin: Pipe closed. Exiting.");
164                         break;
165                 }
166                 else if (poss_fds[0].revents & POLLOUT)
167                 {
168                         if (named_child_send_data () < 0)
169                         {
170                                 break;
171                         }
172                 }
173
174                 if (poll_fds[1].revents & (POLLERR | POLLHUP | POLLNVAL))
175                 {
176                         syslog (LOG_ERR, "named plugin: pcap-device closed. Exiting.");
177                         break;
178                 }
179                 else if (poll_fds[1].revents & (POLLIN | POLLPRI))
180                 {
181                         /* TODO: Read and analyse packet */
182                         status = pcap_dispatch (pcap_obj,
183                                         10 /* Only handle 10 packets at a time */,
184                                         handle_pcap /* callback */,
185                                         NULL /* Whatever this means.. */);
186                         if (status < 0)
187                         {
188                                 syslog (LOG_ERR, "named plugin: pcap_dispatch failed: %s",
189                                                 pcap_geterr (pcap_obj));
190                                 break;
191                         }
192                 }
193         } /* while (42) */
194
195         close (pipe_fd);
196         pcap_close (pcap_obj);
197 } /* static void named_child_loop (void) */
198
199 static void named_init (void)
200 {
201 #if HAVE_LIBPCAP
202         int pipe_fds[2];
203         pid_t pid_child;
204
205         if (pipe (pipe_fds) != 0)
206         {
207                 syslog (LOG_ERR, "named plugin: pipe(2) failed: %s",
208                                 strerror (errno));
209                 return;
210         }
211
212         /* Fork off child */
213         pid_child = fork ();
214         if (pid_child < 0)
215         {
216                 syslog (LOG_ERR, "named plugin: fork(2) failed: %s",
217                                 strerror (errno));
218                 close (pipe_fds[0]);
219                 close (pipe_fds[1]);
220                 pcap_close (pcap_obj);
221                 return;
222         }
223         else if (pid_child != 0)
224         {
225                 /* parent: Close the writing end, keep the reading end. */
226                 pipe_fd = pipe_fds[0];
227                 close (pipe_fds[1]);
228         }
229         else
230         {
231                 /* child: Close the reading end, keep the writing end. */
232                 pipe_fd = pipe_fds[1];
233                 close (pipe_fds[0]);
234
235                 named_child_loop ();
236                 exit (0);
237         }
238
239         fcntl (pipe_fd, F_SETFL, O_NONBLOCK);
240 #endif
241 }
242
243 #if NAMED_HAVE_READ
244 static void named_read (void)
245 {
246         int values[2 * T_MAX];
247         int values_num;
248         int qtype;
249         int counter;
250         int i;
251
252         if (sread (pipe_fd, (void *) &values_num, sizeof (values_num)) != 0)
253         {
254                 syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
255                                 strerror (errno));
256                 return;
257         }
258
259         assert ((values_num >= 0) && (values_num <= T_MAX));
260
261         if (sread (pipe_fd, (void *) values, 2 * sizeof (int) * values_num) != 0)
262         {
263                 syslog (LOG_ERR, "named plugin: Reading from the pipe failed: %s",
264                                 strerror (errno));
265                 return;
266         }
267
268         for (i = 0; i < values_num; i++)
269         {
270                 qtype = values[2 * i];
271                 counter = values[(2 * i) + 1];
272
273                 DBG ("qtype = %i; counter = %i;", qtype, counter);
274         }
275 }
276 #else /* if !NAMED_HAVE_READ */
277 # define named_read NULL
278 #endif
279
280 void module_register (void)
281 {
282         plugin_register (MODULE_NAME, named_init, named_read, NULL);
283         /* TODO */
284         cf_register (MODULE_NAME, named_config, config_keys, config_keys_num);
285 }
286
287 #undef MODULE_NAME