Merge branch '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 #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 = malloc (sizeof (*vl));
160   if (vl == NULL)
161   {
162     fprintf (stderr, "malloc failed.\n");
163     return (NULL);
164   }
165   memset (vl, 0, sizeof (*vl));
166
167   vl->values = calloc (/* nmemb = */ 1, sizeof (*vl->values));
168   if (vl->values == NULL)
169   {
170     fprintf (stderr, "calloc failed.\n");
171     free (vl);
172     return (NULL);
173   }
174
175   vl->values_types = calloc (/* nmemb = */ 1, sizeof (*vl->values_types));
176   if (vl->values_types == NULL)
177   {
178     fprintf (stderr, "calloc failed.\n");
179     free (vl->values);
180     free (vl);
181     return (NULL);
182   }
183
184   vl->values_len = 1;
185
186   host_num = get_boundet_random (0, conf_num_hosts);
187
188   vl->interval = conf_interval;
189   vl->time = 1.0 + dtime ()
190     + (host_num % (1 + (int) vl->interval));
191
192   if (get_boundet_random (0, 2) == 0)
193     vl->values_types[0] = LCC_TYPE_GAUGE;
194   else
195     vl->values_types[0] = LCC_TYPE_DERIVE;
196
197   snprintf (vl->identifier.host, sizeof (vl->identifier.host),
198       "host%04i", host_num);
199   snprintf (vl->identifier.plugin, sizeof (vl->identifier.plugin),
200       "plugin%03i", get_boundet_random (0, conf_num_plugins));
201   strncpy (vl->identifier.type,
202       (vl->values_types[0] == LCC_TYPE_GAUGE) ? "gauge" : "derive",
203       sizeof (vl->identifier.type));
204   vl->identifier.type[sizeof (vl->identifier.type) - 1] = 0;
205   snprintf (vl->identifier.type_instance, sizeof (vl->identifier.type_instance),
206       "ti%li", random ());
207
208   return (vl);
209 } /* }}} int create_value_list */
210
211 static void destroy_value_list (lcc_value_list_t *vl) /* {{{ */
212 {
213   if (vl == NULL)
214     return;
215
216   free (vl->values);
217   free (vl->values_types);
218   free (vl);
219 } /* }}} void destroy_value_list */
220
221 static int send_value (lcc_value_list_t *vl) /* {{{ */
222 {
223   int status;
224
225   if (vl->values_types[0] == LCC_TYPE_GAUGE)
226     vl->values[0].gauge = 100.0 * ((gauge_t) random ()) / (((gauge_t) RAND_MAX) + 1.0);
227   else
228     vl->values[0].derive += (derive_t) get_boundet_random (0, 100);
229
230   status = lcc_network_values_send (net, vl);
231   if (status != 0)
232     fprintf (stderr, "lcc_network_values_send failed with status %i.\n", status);
233
234   vl->time += vl->interval;
235
236   return (0);
237 } /* }}} int send_value */
238
239 static int get_integer_opt (const char *str, int *ret_value) /* {{{ */
240 {
241   char *endptr;
242   int tmp;
243
244   errno = 0;
245   endptr = NULL;
246   tmp = (int) strtol (str, &endptr, /* base = */ 0);
247   if (errno != 0)
248   {
249     fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
250         str, strerror (errno));
251     exit (EXIT_FAILURE);
252   }
253   else if (endptr == str)
254   {
255     fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
256     exit (EXIT_FAILURE);
257   }
258   else if (*endptr != 0)
259   {
260     fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
261     exit (EXIT_FAILURE);
262   }
263
264   *ret_value = tmp;
265   return (0);
266 } /* }}} int get_integer_opt */
267
268 static int get_double_opt (const char *str, double *ret_value) /* {{{ */
269 {
270   char *endptr;
271   double tmp;
272
273   errno = 0;
274   endptr = NULL;
275   tmp = strtod (str, &endptr);
276   if (errno != 0)
277   {
278     fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
279         str, strerror (errno));
280     exit (EXIT_FAILURE);
281   }
282   else if (endptr == str)
283   {
284     fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
285     exit (EXIT_FAILURE);
286   }
287   else if (*endptr != 0)
288   {
289     fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
290     exit (EXIT_FAILURE);
291   }
292
293   *ret_value = tmp;
294   return (0);
295 } /* }}} int get_double_opt */
296
297 static int read_options (int argc, char **argv) /* {{{ */
298 {
299   int opt;
300
301   while ((opt = getopt (argc, argv, "n:H:p:i:d:D:h")) != -1)
302   {
303     switch (opt)
304     {
305       case 'n':
306         get_integer_opt (optarg, &conf_num_values);
307         break;
308
309       case 'H':
310         get_integer_opt (optarg, &conf_num_hosts);
311         break;
312
313       case 'p':
314         get_integer_opt (optarg, &conf_num_plugins);
315         break;
316
317       case 'i':
318         get_double_opt (optarg, &conf_interval);
319         break;
320
321       case 'd':
322         conf_destination = optarg;
323         break;
324
325       case 'D':
326         conf_service = optarg;
327         break;
328
329       case 'h':
330         exit_usage (EXIT_SUCCESS);
331
332       default:
333         exit_usage (EXIT_FAILURE);
334     } /* switch (opt) */
335   } /* while (getopt) */
336
337   return (0);
338 } /* }}} int read_options */
339
340 int main (int argc, char **argv) /* {{{ */
341 {
342   int i;
343   double last_time;
344   int values_sent = 0;
345
346   read_options (argc, argv);
347
348   sigint_action.sa_handler = signal_handler;
349   sigaction (SIGINT, &sigint_action, /* old = */ NULL);
350
351   sigterm_action.sa_handler = signal_handler;
352   sigaction (SIGTERM, &sigterm_action, /* old = */ NULL);
353
354
355   values_heap = c_heap_create (compare_time);
356   if (values_heap == NULL)
357   {
358     fprintf (stderr, "c_heap_create failed.\n");
359     exit (EXIT_FAILURE);
360   }
361
362   net = lcc_network_create ();
363   if (net == NULL)
364   {
365     fprintf (stderr, "lcc_network_create failed.\n");
366     exit (EXIT_FAILURE);
367   }
368   else
369   {
370     lcc_server_t *srv;
371     
372     srv = lcc_server_create (net, conf_destination, conf_service);
373     if (srv == NULL)
374     {
375       fprintf (stderr, "lcc_server_create failed.\n");
376       exit (EXIT_FAILURE);
377     }
378
379     lcc_server_set_ttl (srv, 42);
380 #if 0
381     lcc_server_set_security_level (srv, ENCRYPT,
382         "admin", "password1");
383 #endif
384   }
385
386   fprintf (stdout, "Creating %i values ... ", conf_num_values);
387   fflush (stdout);
388   for (i = 0; i < conf_num_values; i++)
389   {
390     lcc_value_list_t *vl;
391
392     vl = create_value_list ();
393     if (vl == NULL)
394     {
395       fprintf (stderr, "create_value_list failed.\n");
396       exit (EXIT_FAILURE);
397     }
398
399     c_heap_insert (values_heap, vl);
400   }
401   fprintf (stdout, "done\n");
402
403   last_time = 0;
404   while (loop)
405   {
406     lcc_value_list_t *vl = c_heap_get_root (values_heap);
407
408     if (vl == NULL)
409       break;
410
411     if (vl->time != last_time)
412     {
413       printf ("%i values have been sent.\n", values_sent);
414
415       /* Check if we need to sleep */
416       double now = dtime ();
417
418       while (now < vl->time)
419       {
420         /* 1 / 100 second */
421         struct timespec ts = { 0, 10000000 };
422
423         ts.tv_sec = (time_t) now;
424         ts.tv_nsec = (long) ((now - ((double) ts.tv_sec)) * 1e9);
425
426         nanosleep (&ts, /* remaining = */ NULL);
427         now = dtime ();
428
429         if (!loop)
430           break;
431       }
432       last_time = vl->time;
433     }
434
435     send_value (vl);
436     values_sent++;
437
438     c_heap_insert (values_heap, vl);
439   }
440
441   fprintf (stdout, "Shutting down.\n");
442   fflush (stdout);
443
444   while (42)
445   {
446     lcc_value_list_t *vl = c_heap_get_root (values_heap);
447     if (vl == NULL)
448       break;
449     destroy_value_list (vl);
450   }
451   c_heap_destroy (values_heap);
452
453   lcc_network_destroy (net);
454   exit (EXIT_SUCCESS);
455   return (0);
456 } /* }}} int main */
457
458 /* vim: set sw=2 sts=2 et fdm=marker : */