Merge branch 'collectd-5.4' into collectd-5.5
[collectd.git] / src / collectd-tg.c
1 /**
2  * collectd-tg - src/collectd-tg.c
3  * Copyright (C) 2010-2012  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian Forster <octo at collectd.org>
25  **/
26
27 #if HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #if !__GNUC__
32 # define __attribute__(x) /**/
33 #endif
34
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <time.h>
40 #include <signal.h>
41 #include <errno.h>
42
43 #include "utils_heap.h"
44
45 #include "libcollectdclient/collectd/client.h"
46 #include "libcollectdclient/collectd/network.h"
47 #include "libcollectdclient/collectd/network_buffer.h"
48
49 #define DEF_NUM_HOSTS    1000
50 #define DEF_NUM_PLUGINS    20
51 #define DEF_NUM_VALUES 100000
52 #define DEF_INTERVAL       10.0
53
54 static int conf_num_hosts = DEF_NUM_HOSTS;
55 static int conf_num_plugins = DEF_NUM_PLUGINS;
56 static int conf_num_values = DEF_NUM_VALUES;
57 static double conf_interval = DEF_INTERVAL;
58 static const char *conf_destination = NET_DEFAULT_V6_ADDR;
59 static const char *conf_service = NET_DEFAULT_PORT;
60
61 static lcc_network_t *net;
62
63 static c_heap_t *values_heap = NULL;
64
65 static struct sigaction sigint_action;
66 static struct sigaction sigterm_action;
67
68 static _Bool loop = 1;
69
70 __attribute__((noreturn))
71 static void exit_usage (int exit_status) /* {{{ */
72 {
73   fprintf ((exit_status == EXIT_FAILURE) ? stderr : stdout,
74       "collectd-tg -- collectd traffic generator\n"
75       "\n"
76       "  Usage: collectd-ng [OPTION]\n"
77       "\n"
78       "  Valid options:\n"
79       "    -n <number>    Number of value lists. (Default: %i)\n"
80       "    -H <number>    Number of hosts to emulate. (Default: %i)\n"
81       "    -p <number>    Number of plugins to emulate. (Default: %i)\n"
82       "    -i <seconds>   Interval of each value in seconds. (Default: %.3f)\n"
83       "    -d <dest>      Destination address of the network packets.\n"
84       "                   (Default: %s)\n"
85       "    -D <port>      Destination port of the network packets.\n"
86       "                   (Default: %s)\n"
87       "    -h             Print usage information (this output).\n"
88       "\n"
89       "Copyright (C) 2010-2012  Florian Forster\n"
90       "Licensed under the MIT license.\n",
91       DEF_NUM_VALUES, DEF_NUM_HOSTS, DEF_NUM_PLUGINS,
92       DEF_INTERVAL,
93       NET_DEFAULT_V6_ADDR, NET_DEFAULT_PORT);
94   exit (exit_status);
95 } /* }}} void exit_usage */
96
97 static void signal_handler (int signal) /* {{{ */
98 {
99   loop = 0;
100 } /* }}} void signal_handler */
101
102 static int compare_time (const void *v0, const void *v1) /* {{{ */
103 {
104   const lcc_value_list_t *vl0 = v0;
105   const lcc_value_list_t *vl1 = v1;
106
107   if (vl0->time < vl1->time)
108     return (-1);
109   else if (vl0->time > vl1->time)
110     return (1);
111   else
112     return (0);
113 } /* }}} int compare_time */
114
115 static int get_boundet_random (int min, int max) /* {{{ */
116 {
117   int range;
118
119   if (min >= max)
120     return (-1);
121   if (min == (max - 1))
122     return (min);
123
124   range = max - min;
125
126   return (min + ((int) (((double) range) * ((double) random ()) / (((double) RAND_MAX) + 1.0))));
127 } /* }}} int get_boundet_random */
128
129 static lcc_value_list_t *create_value_list (void) /* {{{ */
130 {
131   lcc_value_list_t *vl;
132   int host_num;
133
134   vl = malloc (sizeof (*vl));
135   if (vl == NULL)
136   {
137     fprintf (stderr, "malloc failed.\n");
138     return (NULL);
139   }
140   memset (vl, 0, sizeof (*vl));
141
142   vl->values = calloc (/* nmemb = */ 1, sizeof (*vl->values));
143   if (vl->values == NULL)
144   {
145     fprintf (stderr, "calloc failed.\n");
146     free (vl);
147     return (NULL);
148   }
149
150   vl->values_types = calloc (/* nmemb = */ 1, sizeof (*vl->values_types));
151   if (vl->values_types == NULL)
152   {
153     fprintf (stderr, "calloc failed.\n");
154     free (vl->values);
155     free (vl);
156     return (NULL);
157   }
158
159   vl->values_len = 1;
160
161   host_num = get_boundet_random (0, conf_num_hosts);
162
163   vl->interval = conf_interval;
164   vl->time = 1.0 + time (NULL)
165     + (host_num % (1 + (int) vl->interval));
166
167   if (get_boundet_random (0, 2) == 0)
168     vl->values_types[0] = LCC_TYPE_GAUGE;
169   else
170     vl->values_types[0] = LCC_TYPE_DERIVE;
171
172   snprintf (vl->identifier.host, sizeof (vl->identifier.host),
173       "host%04i", host_num);
174   snprintf (vl->identifier.plugin, sizeof (vl->identifier.plugin),
175       "plugin%03i", get_boundet_random (0, conf_num_plugins));
176   strncpy (vl->identifier.type,
177       (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive",
178       sizeof (vl->identifier.type));
179   vl->identifier.type[sizeof (vl->identifier.type) - 1] = 0;
180   snprintf (vl->identifier.type_instance, sizeof (vl->identifier.type_instance),
181       "ti%li", random ());
182
183   return (vl);
184 } /* }}} int create_value_list */
185
186 static void destroy_value_list (lcc_value_list_t *vl) /* {{{ */
187 {
188   if (vl == NULL)
189     return;
190
191   free (vl->values);
192   free (vl->values_types);
193   free (vl);
194 } /* }}} void destroy_value_list */
195
196 static int send_value (lcc_value_list_t *vl) /* {{{ */
197 {
198   int status;
199
200   if (vl->values_types[0] == LCC_TYPE_GAUGE)
201     vl->values[0].gauge = 100.0 * ((gauge_t) random ()) / (((gauge_t) RAND_MAX) + 1.0);
202   else
203     vl->values[0].derive += get_boundet_random (0, 100);
204
205   status = lcc_network_values_send (net, vl);
206   if (status != 0)
207     fprintf (stderr, "lcc_network_values_send failed with status %i.\n", status);
208
209   vl->time += vl->interval;
210
211   return (0);
212 } /* }}} int send_value */
213
214 static int get_integer_opt (const char *str, int *ret_value) /* {{{ */
215 {
216   char *endptr;
217   int tmp;
218
219   errno = 0;
220   endptr = NULL;
221   tmp = (int) strtol (str, &endptr, /* base = */ 0);
222   if (errno != 0)
223   {
224     fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
225         str, strerror (errno));
226     exit (EXIT_FAILURE);
227   }
228   else if (endptr == str)
229   {
230     fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
231     exit (EXIT_FAILURE);
232   }
233   else if (*endptr != 0)
234   {
235     fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
236     exit (EXIT_FAILURE);
237   }
238
239   *ret_value = tmp;
240   return (0);
241 } /* }}} int get_integer_opt */
242
243 static int get_double_opt (const char *str, double *ret_value) /* {{{ */
244 {
245   char *endptr;
246   double tmp;
247
248   errno = 0;
249   endptr = NULL;
250   tmp = strtod (str, &endptr);
251   if (errno != 0)
252   {
253     fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
254         str, strerror (errno));
255     exit (EXIT_FAILURE);
256   }
257   else if (endptr == str)
258   {
259     fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
260     exit (EXIT_FAILURE);
261   }
262   else if (*endptr != 0)
263   {
264     fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
265     exit (EXIT_FAILURE);
266   }
267
268   *ret_value = tmp;
269   return (0);
270 } /* }}} int get_double_opt */
271
272 static int read_options (int argc, char **argv) /* {{{ */
273 {
274   int opt;
275
276   while ((opt = getopt (argc, argv, "n:H:p:i:d:D:h")) != -1)
277   {
278     switch (opt)
279     {
280       case 'n':
281         get_integer_opt (optarg, &conf_num_values);
282         break;
283
284       case 'H':
285         get_integer_opt (optarg, &conf_num_hosts);
286         break;
287
288       case 'p':
289         get_integer_opt (optarg, &conf_num_plugins);
290         break;
291
292       case 'i':
293         get_double_opt (optarg, &conf_interval);
294         break;
295
296       case 'd':
297         conf_destination = optarg;
298         break;
299
300       case 'D':
301         conf_service = optarg;
302         break;
303
304       case 'h':
305         exit_usage (EXIT_SUCCESS);
306
307       default:
308         exit_usage (EXIT_FAILURE);
309     } /* switch (opt) */
310   } /* while (getopt) */
311
312   return (0);
313 } /* }}} int read_options */
314
315 int main (int argc, char **argv) /* {{{ */
316 {
317   int i;
318   time_t last_time;
319   int values_sent = 0;
320
321   read_options (argc, argv);
322
323   sigint_action.sa_handler = signal_handler;
324   sigaction (SIGINT, &sigint_action, /* old = */ NULL);
325
326   sigterm_action.sa_handler = signal_handler;
327   sigaction (SIGTERM, &sigterm_action, /* old = */ NULL);
328
329
330   values_heap = c_heap_create (compare_time);
331   if (values_heap == NULL)
332   {
333     fprintf (stderr, "c_heap_create failed.\n");
334     exit (EXIT_FAILURE);
335   }
336
337   net = lcc_network_create ();
338   if (net == NULL)
339   {
340     fprintf (stderr, "lcc_network_create failed.\n");
341     exit (EXIT_FAILURE);
342   }
343   else
344   {
345     lcc_server_t *srv;
346     
347     srv = lcc_server_create (net, conf_destination, conf_service);
348     if (srv == NULL)
349     {
350       fprintf (stderr, "lcc_server_create failed.\n");
351       exit (EXIT_FAILURE);
352     }
353
354     lcc_server_set_ttl (srv, 42);
355 #if 0
356     lcc_server_set_security_level (srv, ENCRYPT,
357         "admin", "password1");
358 #endif
359   }
360
361   fprintf (stdout, "Creating %i values ... ", conf_num_values);
362   fflush (stdout);
363   for (i = 0; i < conf_num_values; i++)
364   {
365     lcc_value_list_t *vl;
366
367     vl = create_value_list ();
368     if (vl == NULL)
369     {
370       fprintf (stderr, "create_value_list failed.\n");
371       exit (EXIT_FAILURE);
372     }
373
374     c_heap_insert (values_heap, vl);
375   }
376   fprintf (stdout, "done\n");
377
378   last_time = 0;
379   while (loop)
380   {
381     lcc_value_list_t *vl = c_heap_get_root (values_heap);
382
383     if (vl == NULL)
384       break;
385
386     if (vl->time != last_time)
387     {
388       printf ("%i values have been sent.\n", values_sent);
389
390       /* Check if we need to sleep */
391       time_t now = time (NULL);
392
393       while (now < vl->time)
394       {
395         /* 1 / 100 second */
396         struct timespec ts = { 0, 10000000 };
397         nanosleep (&ts, /* remaining = */ NULL);
398         now = time (NULL);
399
400         if (!loop)
401           break;
402       }
403       last_time = vl->time;
404     }
405
406     send_value (vl);
407     values_sent++;
408
409     c_heap_insert (values_heap, vl);
410   }
411
412   fprintf (stdout, "Shutting down.\n");
413   fflush (stdout);
414
415   while (42)
416   {
417     lcc_value_list_t *vl = c_heap_get_root (values_heap);
418     if (vl == NULL)
419       break;
420     destroy_value_list (vl);
421   }
422   c_heap_destroy (values_heap);
423
424   lcc_network_destroy (net);
425   exit (EXIT_SUCCESS);
426   return (0);
427 } /* }}} int main */
428
429 /* vim: set sw=2 sts=2 et fdm=marker : */