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