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