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