collectd-tg: Add option parsing and usage information.
[collectd.git] / src / collectd-tg.c
1 /**
2  * collectd-td - collectd traffic generator
3  * Copyright (C) 2010  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; 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  *   Florian Forster <ff at octo.it>
20  **/
21
22 #if HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #ifndef _ISOC99_SOURCE
27 # define _ISOC99_SOURCE
28 #endif
29
30 #ifndef _POSIX_C_SOURCE
31 # define _POSIX_C_SOURCE 200809L
32 #endif
33
34 #ifndef _XOPEN_SOURCE
35 # define _XOPEN_SOURCE 700
36 #endif
37
38 #if !__GNUC__
39 # define __attribute__(x) /**/
40 #endif
41
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <time.h>
47 #include <signal.h>
48
49 #include "utils_heap.h"
50
51 #include "libcollectdclient/collectd/client.h"
52 #include "libcollectdclient/collectd/network.h"
53 #include "libcollectdclient/collectd/network_buffer.h"
54
55 #define DEF_NUM_HOSTS 1000
56 #define DEF_NUM_PLUGINS 20
57 #define DEF_NUM_VALUES 100000
58
59 static int conf_num_hosts = DEF_NUM_HOSTS;
60 static int conf_num_plugins = DEF_NUM_PLUGINS;
61 static int conf_num_values = DEF_NUM_VALUES;
62 static const char *conf_destination = NET_DEFAULT_V6_ADDR;
63 static const char *conf_service = NET_DEFAULT_PORT;
64
65 static lcc_network_t *net;
66
67 static c_heap_t *values_heap = NULL;
68
69 static struct sigaction sigint_action;
70 static struct sigaction sigterm_action;
71
72 static _Bool loop = 1;
73
74 __attribute__((noreturn))
75 static void exit_usage (int exit_status) /* {{{ */
76 {
77   fprintf ((exit_status == EXIT_FAILURE) ? stderr : stdout,
78       "collectd-tg -- collectd traffic generator\n"
79       "\n"
80       "  Usage: collectd-ng [OPTION]\n"
81       "\n"
82       "  Valid options:\n"
83       "    -n <number>    Number of value lists. (Default: %i)\n"
84       "    -H <number>    Number of hosts to emulate. (Default: %i)\n"
85       "    -p <number>    Number of plugins to emulate. (Default: %i)\n"
86       "    -d <dest>      Destination address of the network packets.\n"
87       "                   (Default: %s)\n"
88       "    -D <port>      Destination port of the network packets.\n"
89       "                   (Default: %s)\n"
90       "    -h             Print usage information (this output).\n"
91       "\n"
92       "Copyright (C) 2010  Florian Forster\n"
93       "Licensed under the GNU General Public License, version 2 (GPLv2)\n",
94       DEF_NUM_VALUES, DEF_NUM_HOSTS, DEF_NUM_PLUGINS,
95       NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT);
96   exit (exit_status);
97 } /* }}} void exit_usage */
98
99 static void signal_handler (int signal) /* {{{ */
100 {
101   loop = 0;
102 } /* }}} void signal_handler */
103
104 static int compare_time (const void *v0, const void *v1) /* {{{ */
105 {
106   const lcc_value_list_t *vl0 = v0;
107   const lcc_value_list_t *vl1 = v1;
108
109   if (vl0->time < vl1->time)
110     return (-1);
111   else if (vl0->time > vl1->time)
112     return (1);
113   else
114     return (0);
115 } /* }}} int compare_time */
116
117 static int get_boundet_random (int min, int max) /* {{{ */
118 {
119   int range;
120
121   if (min >= max)
122     return (-1);
123   if (min == (max - 1))
124     return (min);
125
126   range = max - min;
127
128   return (min + ((int) (((double) range) * ((double) random ()) / (((double) RAND_MAX) + 1.0))));
129 } /* }}} int get_boundet_random */
130
131 #if 0
132 static int dump_network_buffer (void) /* {{{ */
133 {
134   char buffer[LCC_NETWORK_BUFFER_SIZE_DEFAULT];
135   size_t buffer_size;
136   int status;
137   size_t offset;
138
139   memset (buffer, 0, sizeof (buffer));
140   buffer_size = sizeof (buffer);
141
142   status = lcc_network_buffer_get (nb, buffer, &buffer_size);
143   if (status != 0)
144   {
145     fprintf (stderr, "lcc_network_buffer_get failed with status %i.\n",
146         status);
147     return (status);
148   }
149
150   if (buffer_size > sizeof (buffer))
151     buffer_size = sizeof (buffer);
152
153   for (offset = 0; offset < buffer_size; offset += 16)
154   {
155     size_t i;
156
157     for (i = 0; (i < 16) && ((offset + i) < buffer_size); i++)
158     {
159       uint8_t v = (uint8_t) buffer[offset + i];
160       printf ("%02"PRIx8" ", v);
161     }
162     for (; i < 16; i++)
163       printf ("   ");
164     printf ("   ");
165     for (i = 0; (i < 16) && ((offset + i) < buffer_size); i++)
166     {
167       uint8_t v = (uint8_t) buffer[offset + i];
168       if ((v >= 32) && (v < 128))
169         printf ("%c", (int) buffer[offset + i]);
170       else
171         printf (".");
172     }
173     printf ("\n");
174   }
175
176   return (0);
177 } /* }}} int dump_network_buffer */
178 #endif
179
180 static lcc_value_list_t *create_value_list (void) /* {{{ */
181 {
182   lcc_value_list_t *vl;
183   int host_num;
184
185   vl = malloc (sizeof (*vl));
186   if (vl == NULL)
187   {
188     fprintf (stderr, "malloc failed.\n");
189     return (NULL);
190   }
191   memset (vl, 0, sizeof (*vl));
192
193   vl->values = calloc (/* nmemb = */ 1, sizeof (*vl->values));
194   if (vl->values == NULL)
195   {
196     fprintf (stderr, "calloc failed.\n");
197     free (vl);
198     return (NULL);
199   }
200
201   vl->values_types = calloc (/* nmemb = */ 1, sizeof (*vl->values_types));
202   if (vl->values_types == NULL)
203   {
204     fprintf (stderr, "calloc failed.\n");
205     free (vl->values);
206     free (vl);
207     return (NULL);
208   }
209
210   vl->values_len = 1;
211
212   host_num = get_boundet_random (0, conf_num_hosts);
213
214   vl->interval = 10;
215   vl->time = time (NULL) - (host_num % vl->interval);
216
217   if (get_boundet_random (0, 2) == 0)
218     vl->values_types[0] = LCC_TYPE_GAUGE;
219   else
220     vl->values_types[0] = LCC_TYPE_DERIVE;
221
222   snprintf (vl->identifier.host, sizeof (vl->identifier.host),
223       "host%04i", host_num);
224   snprintf (vl->identifier.plugin, sizeof (vl->identifier.plugin),
225       "plugin%03i", get_boundet_random (0, conf_num_plugins));
226   strncpy (vl->identifier.type,
227       (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive",
228       sizeof (vl->identifier.type));
229   snprintf (vl->identifier.type_instance, sizeof (vl->identifier.type_instance),
230       "ti%li", random ());
231
232   return (vl);
233 } /* }}} int create_value_list */
234
235 static void destroy_value_list (lcc_value_list_t *vl) /* {{{ */
236 {
237   if (vl == NULL)
238     return;
239
240   free (vl->values);
241   free (vl->values_types);
242   free (vl);
243 } /* }}} void destroy_value_list */
244
245 static int send_value (lcc_value_list_t *vl) /* {{{ */
246 {
247   int status;
248
249   if (vl->values_types[0] == LCC_TYPE_GAUGE)
250     vl->values[0].gauge = 100.0 * ((gauge_t) random ()) / (((gauge_t) RAND_MAX) + 1.0);
251   else
252     vl->values[0].derive += get_boundet_random (0, 100);
253
254   status = lcc_network_values_send (net, vl);
255   if (status != 0)
256     fprintf (stderr, "lcc_network_values_send failed with status %i.\n", status);
257
258   vl->time += vl->interval;
259
260   return (0);
261 } /* }}} int send_value */
262
263 static int read_options (int argc, char **argv) /* {{{ */
264 {
265   int opt;
266
267   while ((opt = getopt (argc, argv, "n:H:p:d:D:h")) != -1)
268   {
269     switch (opt)
270     {
271       case 'n':
272         {
273           int tmp = atoi (optarg);
274           if (tmp < 1)
275           {
276             fprintf (stderr, "Unable to parse option as a number: \"%s\"\n",
277                 optarg);
278             exit (EXIT_FAILURE);
279           }
280           conf_num_values = tmp;
281         }
282         break;
283
284       case 'H':
285         {
286           int tmp = atoi (optarg);
287           if (tmp < 1)
288           {
289             fprintf (stderr, "Unable to parse option as a number: \"%s\"\n",
290                 optarg);
291             exit (EXIT_FAILURE);
292           }
293           conf_num_hosts = tmp;
294         }
295         break;
296
297       case 'p':
298         {
299           int tmp = atoi (optarg);
300           if (tmp < 1)
301           {
302             fprintf (stderr, "Unable to parse option as a number: \"%s\"\n",
303                 optarg);
304             exit (EXIT_FAILURE);
305           }
306           conf_num_plugins = tmp;
307         }
308         break;
309
310       case 'd':
311         conf_destination = optarg;
312         break;
313
314       case 'D':
315         conf_service = optarg;
316         break;
317
318       case 'h':
319         exit_usage (EXIT_SUCCESS);
320
321       default:
322         exit_usage (EXIT_FAILURE);
323     } /* switch (opt) */
324   } /* while (getopt) */
325
326   return (0);
327 } /* }}} int read_options */
328
329 int main (int argc, char **argv) /* {{{ */
330 {
331   int i;
332   time_t last_time;
333   int values_sent = 0;
334
335   read_options (argc, argv);
336
337   sigint_action.sa_handler = signal_handler;
338   sigaction (SIGINT, &sigint_action, /* old = */ NULL);
339
340   sigterm_action.sa_handler = signal_handler;
341   sigaction (SIGTERM, &sigterm_action, /* old = */ NULL);
342
343
344   values_heap = c_heap_create (compare_time);
345   if (values_heap == NULL)
346   {
347     fprintf (stderr, "c_heap_create failed.\n");
348     exit (EXIT_FAILURE);
349   }
350
351   net = lcc_network_create ();
352   if (net == NULL)
353   {
354     fprintf (stderr, "lcc_network_create failed.\n");
355     exit (EXIT_FAILURE);
356   }
357   else
358   {
359     lcc_server_t *srv;
360     
361     srv = lcc_server_create (net, conf_destination, conf_service);
362     if (srv == NULL)
363     {
364       fprintf (stderr, "lcc_server_create failed.\n");
365       exit (EXIT_FAILURE);
366     }
367
368     lcc_server_set_ttl (srv, 42);
369 #if 0
370     lcc_server_set_security_level (srv, ENCRYPT,
371         "admin", "password1");
372 #endif
373   }
374
375   fprintf (stdout, "Creating %i values ... ", conf_num_values);
376   fflush (stdout);
377   for (i = 0; i < conf_num_values; i++)
378   {
379     lcc_value_list_t *vl;
380
381     vl = create_value_list ();
382     if (vl == NULL)
383     {
384       fprintf (stderr, "create_value_list failed.\n");
385       exit (EXIT_FAILURE);
386     }
387
388     c_heap_insert (values_heap, vl);
389   }
390   fprintf (stdout, "done\n");
391
392   last_time = 0;
393   while (loop)
394   {
395     lcc_value_list_t *vl = c_heap_get_root (values_heap);
396
397     if (vl == NULL)
398       break;
399
400     if (vl->time != last_time)
401     {
402       printf ("%i values have been sent.\n", values_sent);
403
404       /* Check if we need to sleep */
405       time_t now = time (NULL);
406
407       while (now < vl->time)
408       {
409         /* 1 / 100 second */
410         struct timespec ts = { 0, 10000000 };
411         nanosleep (&ts, /* remaining = */ NULL);
412         now = time (NULL);
413       }
414       last_time = vl->time;
415     }
416
417     send_value (vl);
418     values_sent++;
419
420     c_heap_insert (values_heap, vl);
421   }
422
423   fprintf (stdout, "Shutting down.\n");
424   fflush (stdout);
425
426   while (42)
427   {
428     lcc_value_list_t *vl = c_heap_get_root (values_heap);
429     if (vl == NULL)
430       break;
431     destroy_value_list (vl);
432   }
433   c_heap_destroy (values_heap);
434
435   lcc_network_destroy (net);
436   exit (EXIT_SUCCESS);
437   return (0);
438 } /* }}} int main */
439
440 /* vim: set sw=2 sts=2 et fdm=marker : */