8815a8bb6f5636af142bf83b871bdd15c3d1ba4f
[statsd-tg.git] / src / statsd-tg.c
1 /**
2  * collectd-td - collectd traffic generator
3  * Copyright (C) 2013       Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian Forster <ff at octo.it>
20  **/
21
22 #if HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <time.h>
31 #include <signal.h>
32 #include <errno.h>
33 #include <assert.h>
34
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netdb.h>
38
39 #if !__GNUC__
40 # define __attribute__(x) /**/
41 #endif
42
43 #define DEF_NODE "localhost"
44 #define DEF_SERVICE "8125"
45
46 #define DEF_NUM_COUNTERS  1000
47 #define DEF_NUM_TIMERS    1000
48 #define DEF_NUM_GAUGES     100
49 #define DEF_NUM_SETS       100
50 #define DEF_SET_SIZE       128
51
52 static int conf_num_counters = DEF_NUM_COUNTERS;
53 static int conf_num_timers   = DEF_NUM_TIMERS;
54 static int conf_num_gauges   = DEF_NUM_GAUGES;
55 static int conf_num_sets     = DEF_NUM_SETS;
56 static int conf_set_size     = DEF_SET_SIZE;
57 static const char *conf_node = DEF_NODE;
58 static const char *conf_service = DEF_SERVICE;
59
60 static int sock = -1;
61
62 static struct sigaction sigint_action;
63 static struct sigaction sigterm_action;
64
65 static _Bool loop = 1;
66
67 __attribute__((noreturn))
68 static void exit_usage (int exit_status) /* {{{ */
69 {
70   fprintf ((exit_status == EXIT_FAILURE) ? stderr : stdout,
71       PACKAGE_NAME" -- statsd traffic generator\n"
72       "\n"
73       "  Usage: statsd-ng [OPTION]\n"
74       "\n"
75       "  Valid options:\n"
76       "    -c <number>    Number of counters to emulate. (Default: %i)\n"
77       "    -t <number>    Number of timers to emulate. (Default: %i)\n"
78       "    -g <number>    Number of gauges to emulate. (Default: %i)\n"
79       "    -s <number>    Number of sets to emulate. (Default: %i)\n"
80       "    -S <size>      Number of elements in each set. (Default: %i)\n"
81       "    -d <dest>      Destination address of the network packets.\n"
82       "                   (Default: "DEF_NODE")\n"
83       "    -D <port>      Destination port of the network packets.\n"
84       "                   (Default: "DEF_SERVICE")\n"
85       "    -h             Print usage information (this output).\n"
86       "\n"
87       "Copyright (C) 2013  Florian Forster\n"
88       "Licensed under the GNU General Public License, version 2 (GPLv2)\n",
89       DEF_NUM_COUNTERS, DEF_NUM_TIMERS, DEF_NUM_GAUGES,
90       DEF_NUM_SETS, DEF_SET_SIZE);
91   exit (exit_status);
92 } /* }}} void exit_usage */
93
94 static void signal_handler (int signal __attribute__((unused))) /* {{{ */
95 {
96   loop = 0;
97 } /* }}} void signal_handler */
98
99 static int sock_open (void) /* {{{ */
100 {
101   struct addrinfo ai_hints;
102   struct addrinfo *ai_list = NULL;
103   struct addrinfo *ai_ptr;
104
105   int status;
106
107   memset (&ai_hints, 0, sizeof (ai_hints));
108 #ifdef AI_ADDRCONFIG
109   ai_hints.ai_flags = AI_ADDRCONFIG;
110 #endif
111   ai_hints.ai_family = AF_UNSPEC;
112   ai_hints.ai_socktype = SOCK_DGRAM;
113
114   status = getaddrinfo (conf_node, conf_service, &ai_hints, &ai_list);
115   if (status != 0)
116   {
117     fprintf (stderr, "getaddrinfo failed: %s\n", gai_strerror (status));
118     exit (EXIT_FAILURE);
119   }
120
121   for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
122   {
123     int fd;
124
125     fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
126     if (fd < 0)
127     {
128       continue;
129     }
130
131     status = connect (fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen);
132     if (status != 0)
133     {
134       close (fd);
135       continue;
136     }
137
138     sock = fd;
139     break;
140   }
141
142   freeaddrinfo (ai_list);
143
144   if (sock < 0)
145   {
146     fprintf (stderr, "Opening network socket failed.\n");
147     exit (EXIT_FAILURE);
148   }
149
150   return (0);
151 } /* }}} int sock_open */
152
153 static int send_random_event (void) /* {{{ */
154 {
155   long conf_num_total = conf_num_counters + conf_num_timers
156       + conf_num_gauges + conf_num_sets;
157   /* Not completely fair, but good enough for our use-case. */
158   long rnd = lrand48 () % conf_num_total;
159
160   long value = lrand48 ();
161   char *type;
162
163   char buffer[1024];
164   int buffer_size;
165   ssize_t status;
166
167   if (rnd < conf_num_counters)
168   {
169     /* counter */
170     type = "c";
171     value = (value % 8) + 1;
172   }
173   else if (rnd < (conf_num_counters + conf_num_timers))
174   {
175     /* timer */
176     type = "ms";
177     value = (value % 1024) + 1;
178   }
179   else if (rnd < (conf_num_counters + conf_num_timers + conf_num_gauges))
180   {
181     /* gauge */
182     type = "g";
183     value = (value % 128) - 64;
184   }
185   else
186   {
187     /* set */
188     type = "s";
189     value %= conf_set_size;
190   }
191
192   buffer_size = snprintf (buffer, sizeof (buffer), "%06li:%li|%s",
193                           rnd, value, type);
194   assert (buffer_size > 0);
195   if (((size_t) buffer_size) >= sizeof (buffer))
196     return (-1);
197   assert (buffer[buffer_size] == 0);
198
199   status = send (sock, buffer, (size_t) buffer_size, /* flags = */ 0);
200   if (status < 0)
201   {
202     fprintf (stderr, "send failed: %s", strerror (errno));
203     return (-1);
204   }
205
206   return (0);
207 } /* }}} int send_random_event */
208
209 static int get_integer_opt (const char *str, int *ret_value) /* {{{ */
210 {
211   char *endptr;
212   int tmp;
213
214   errno = 0;
215   endptr = NULL;
216   tmp = (int) strtol (str, &endptr, /* base = */ 0);
217   if (errno != 0)
218   {
219     fprintf (stderr, "Unable to parse option as a number: \"%s\": %s\n",
220         str, strerror (errno));
221     exit (EXIT_FAILURE);
222   }
223   else if (endptr == str)
224   {
225     fprintf (stderr, "Unable to parse option as a number: \"%s\"\n", str);
226     exit (EXIT_FAILURE);
227   }
228   else if (*endptr != 0)
229   {
230     fprintf (stderr, "Garbage after end of value: \"%s\"\n", str);
231     exit (EXIT_FAILURE);
232   }
233
234   *ret_value = tmp;
235   return (0);
236 } /* }}} int get_integer_opt */
237
238 static int read_options (int argc, char **argv) /* {{{ */
239 {
240   int opt;
241
242   while ((opt = getopt (argc, argv, "c:t:g:s:S:d:D:h")) != -1)
243   {
244     switch (opt)
245     {
246       case 'c':
247         get_integer_opt (optarg, &conf_num_counters);
248         break;
249
250       case 't':
251         get_integer_opt (optarg, &conf_num_timers);
252         break;
253
254       case 'g':
255         get_integer_opt (optarg, &conf_num_gauges);
256         break;
257
258       case 's':
259         get_integer_opt (optarg, &conf_num_sets);
260         break;
261
262       case 'S':
263         get_integer_opt (optarg, &conf_set_size);
264         break;
265
266       case 'd':
267         conf_node = optarg;
268         break;
269
270       case 'D':
271         conf_service = optarg;
272         break;
273
274       case 'h':
275         exit_usage (EXIT_SUCCESS);
276
277       default:
278         exit_usage (EXIT_FAILURE);
279     } /* switch (opt) */
280   } /* while (getopt) */
281
282   return (0);
283 } /* }}} int read_options */
284
285 int main (int argc, char **argv) /* {{{ */
286 {
287   read_options (argc, argv);
288
289   sigint_action.sa_handler = signal_handler;
290   sigaction (SIGINT, &sigint_action, /* old = */ NULL);
291
292   sigterm_action.sa_handler = signal_handler;
293   sigaction (SIGTERM, &sigterm_action, /* old = */ NULL);
294
295   sock_open ();
296
297   while (loop)
298   {
299     send_random_event ();
300   }
301
302   close (sock);
303   sock = -1;
304
305   exit (EXIT_SUCCESS);
306   return (0);
307 } /* }}} int main */
308
309 /* vim: set sw=2 sts=2 et fdm=marker : */