├── .gitignore ├── Makefile ├── README.md └── src └── write_graphite.c /.gitignore: -------------------------------------------------------------------------------- 1 | .libs 2 | build 3 | work 4 | *.la 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | COLLECTD_PREFIX?=/usr/local 2 | COLLECTD_VERSION=5.0.0 3 | 4 | COLLECTD_SRC=work/collectd-$(COLLECTD_VERSION) 5 | 6 | LIBTOOL=$(COLLECTD_SRC)/libtool 7 | FETCH=fetch 8 | 9 | CFLAGS?=-DNDEBUG -O3 10 | 11 | all: .INIT write_graphite.la 12 | 13 | install: all 14 | $(LIBTOOL) --mode=install /usr/bin/install -c write_graphite.la \ 15 | $(COLLECTD_PREFIX)/lib/collectd 16 | $(LIBTOOL) --finish \ 17 | $(COLLECTD_PREFIX)/lib/collectd 18 | 19 | clean: 20 | rm -rf .libs 21 | rm -rf build 22 | rm -f write_graphite.la 23 | 24 | distclean: clean 25 | rm -rf work 26 | 27 | .INIT: 28 | mkdir -p build 29 | mkdir -p work 30 | ( if [ ! -d $(COLLECTD_SRC)/src ] ; then \ 31 | if which fetch ; then \ 32 | DOWNLOAD_TOOL=`which fetch` ; \ 33 | elif which wget ; then \ 34 | DOWNLOAD_TOOL=`which wget` ; \ 35 | fi ; \ 36 | cd work ; \ 37 | $${DOWNLOAD_TOOL} http://collectd.org/files/collectd-$(COLLECTD_VERSION).tar.gz ; \ 38 | tar zxvf collectd-$(COLLECTD_VERSION).tar.gz ; \ 39 | cd collectd-$(COLLECTD_VERSION) ; \ 40 | if [ ! -f libtool ] ; then \ 41 | ./configure ; \ 42 | fi ; \ 43 | fi ) 44 | 45 | write_graphite.la: build/write_graphite.lo 46 | $(LIBTOOL) --tag=CC --mode=link gcc -Wall -Werror $(CFLAGS) -module \ 47 | -avoid-version -o $@ -rpath $(COLLECTD_PREFIX)/lib/collectd \ 48 | -lpthread build/write_graphite.lo 49 | 50 | build/write_graphite.lo: src/write_graphite.c 51 | $(LIBTOOL) --mode=compile gcc -DHAVE_CONFIG_H -I src \ 52 | -I $(COLLECTD_SRC)/src -Wall -Werror $(CFLAGS) -MD -MP -c \ 53 | -o $@ src/write_graphite.c 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | write\_graphite 2 | ============== 3 | 4 | An output plugin for [collectd](http://collectd.org). 5 | 6 | *NOTE* This work has been merged into [collectd](http://collectd.org/) as of version 5.1. I strongly suggest using the native client, as octo has done great things with it and I no longer plan on maintaining this code. Thanks! 7 | 8 | Description 9 | ----------- 10 | 11 | The write\_graphite plugin sends data to [Carbon](http://graphite.wikidot.com/carbon), the [Graphite](http://graphite.wikidot.com) backend. Data is sent in 4K blocks over TCP to Carbon. I could possibly have named this plugin write\_carbon. 12 | 13 | 14 | Installation 15 | ------------ 16 | 17 | First, modify the variables at the top of the Makefile to fit your system. (I use FreeBSD.) Then continue making the project as usual. During the initial make, collectd will be downloaded and configured to provide the neccesary libtool script. 18 | 19 | $ git clone git@github.com:jssjr/collectd-write_graphite.git 20 | $ cd collectd-write_graphite 21 | $ make 22 | $ sudo make install 23 | 24 | 25 | Configuration 26 | ------------- 27 | 28 | Enable the plugin in collectd.conf by adding: 29 | 30 | LoadPlugin write_graphite 31 | 32 | Configure the plugin to match your carbon configuration. 33 | 34 | 35 | 36 | Host "localhost" 37 | Port "2003" 38 | Prefix "collectd." 39 | 40 | 41 | 42 | Restart collectd to load the new plugin. 43 | 44 | ### Available Carbon Configuration Directives 45 | 46 | * Host *required* 47 | 48 | The hostname of the Carbon collection agent. 49 | 50 | * Port *required* 51 | 52 | The port used by the Carbon collect agent. 53 | 54 | * Prefix 55 | 56 | The prefix string prepended to the hostname that is sent to Carbon. Use dots (.) to create folders. A good choise might be "collectd." or "servers." 57 | 58 | * Postfix 59 | 60 | The postfix string appended to the hostname sent to Carbon. 61 | 62 | * DotCharacter 63 | 64 | The character used to replace dots (.) in a hostname or datasource name. Defaults to an underscore. 65 | -------------------------------------------------------------------------------- /src/write_graphite.c: -------------------------------------------------------------------------------- 1 | /** 2 | * collectd - src/write_graphite.c 3 | * Copyright (C) 2012 Pierre-Yves Ritschard 4 | * Copyright (C) 2011 Scott Sanders 5 | * Copyright (C) 2009 Paul Sadauskas 6 | * Copyright (C) 2009 Doug MacEachern 7 | * Copyright (C) 2007-2012 Florian octo Forster 8 | * 9 | * This program is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by the 11 | * Free Software Foundation; only version 2 of the License is applicable. 12 | * 13 | * This program is distributed in the hope that it will be useful, but 14 | * WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License along 19 | * with this program; if not, write to the Free Software Foundation, Inc., 20 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 | * 22 | * Authors: 23 | * Florian octo Forster 24 | * Doug MacEachern 25 | * Paul Sadauskas 26 | * Scott Sanders 27 | * Pierre-Yves Ritschard 28 | * 29 | * Based on the write_http plugin. 30 | **/ 31 | 32 | /* write_graphite plugin configuation example 33 | * 34 | * 35 | * 36 | * Host "localhost" 37 | * Port "2003" 38 | * Prefix "collectd" 39 | * 40 | * 41 | */ 42 | 43 | #include "collectd.h" 44 | #include "common.h" 45 | #include "plugin.h" 46 | #include "configfile.h" 47 | 48 | #include "utils_cache.h" 49 | #include "utils_parse_option.h" 50 | 51 | /* Folks without pthread will need to disable this plugin. */ 52 | #include 53 | 54 | #include 55 | #include 56 | 57 | #ifndef WG_DEFAULT_NODE 58 | # define WG_DEFAULT_NODE "localhost" 59 | #endif 60 | 61 | #ifndef WG_DEFAULT_SERVICE 62 | # define WG_DEFAULT_SERVICE "2003" 63 | #endif 64 | 65 | #ifndef WG_DEFAULT_ESCAPE 66 | # define WG_DEFAULT_ESCAPE '_' 67 | #endif 68 | 69 | /* Ethernet - (IPv6 + TCP) = 1500 - (40 + 32) = 1428 */ 70 | #ifndef WG_SEND_BUF_SIZE 71 | # define WG_SEND_BUF_SIZE 1428 72 | #endif 73 | 74 | /* 75 | * Private variables 76 | */ 77 | struct wg_callback 78 | { 79 | int sock_fd; 80 | 81 | char *node; 82 | char *service; 83 | char *prefix; 84 | char *postfix; 85 | char escape_char; 86 | 87 | _Bool store_rates; 88 | _Bool separate_instances; 89 | _Bool always_append_ds; 90 | 91 | char send_buf[WG_SEND_BUF_SIZE]; 92 | size_t send_buf_free; 93 | size_t send_buf_fill; 94 | cdtime_t send_buf_init_time; 95 | 96 | pthread_mutex_t send_lock; 97 | }; 98 | 99 | 100 | /* 101 | * Functions 102 | */ 103 | static void wg_reset_buffer (struct wg_callback *cb) 104 | { 105 | memset (cb->send_buf, 0, sizeof (cb->send_buf)); 106 | cb->send_buf_free = sizeof (cb->send_buf); 107 | cb->send_buf_fill = 0; 108 | cb->send_buf_init_time = cdtime (); 109 | } 110 | 111 | static int wg_send_buffer (struct wg_callback *cb) 112 | { 113 | ssize_t status = 0; 114 | 115 | status = swrite (cb->sock_fd, cb->send_buf, strlen (cb->send_buf)); 116 | if (status < 0) 117 | { 118 | char errbuf[1024]; 119 | ERROR ("write_graphite plugin: send failed with status %zi (%s)", 120 | status, sstrerror (errno, errbuf, sizeof (errbuf))); 121 | 122 | 123 | close (cb->sock_fd); 124 | cb->sock_fd = -1; 125 | 126 | return (-1); 127 | } 128 | 129 | return (0); 130 | } 131 | 132 | /* NOTE: You must hold cb->send_lock when calling this function! */ 133 | static int wg_flush_nolock (cdtime_t timeout, struct wg_callback *cb) 134 | { 135 | int status; 136 | 137 | DEBUG ("write_graphite plugin: wg_flush_nolock: timeout = %.3f; " 138 | "send_buf_fill = %zu;", 139 | (double)timeout, 140 | cb->send_buf_fill); 141 | 142 | /* timeout == 0 => flush unconditionally */ 143 | if (timeout > 0) 144 | { 145 | cdtime_t now; 146 | 147 | now = cdtime (); 148 | if ((cb->send_buf_init_time + timeout) > now) 149 | return (0); 150 | } 151 | 152 | if (cb->send_buf_fill <= 0) 153 | { 154 | cb->send_buf_init_time = cdtime (); 155 | return (0); 156 | } 157 | 158 | status = wg_send_buffer (cb); 159 | wg_reset_buffer (cb); 160 | 161 | return (status); 162 | } 163 | 164 | static int wg_callback_init (struct wg_callback *cb) 165 | { 166 | struct addrinfo ai_hints; 167 | struct addrinfo *ai_list; 168 | struct addrinfo *ai_ptr; 169 | int status; 170 | 171 | const char *node = cb->node ? cb->node : WG_DEFAULT_NODE; 172 | const char *service = cb->service ? cb->service : WG_DEFAULT_SERVICE; 173 | 174 | if (cb->sock_fd > 0) 175 | return (0); 176 | 177 | memset (&ai_hints, 0, sizeof (ai_hints)); 178 | #ifdef AI_ADDRCONFIG 179 | ai_hints.ai_flags |= AI_ADDRCONFIG; 180 | #endif 181 | ai_hints.ai_family = AF_UNSPEC; 182 | ai_hints.ai_socktype = SOCK_STREAM; 183 | 184 | ai_list = NULL; 185 | 186 | status = getaddrinfo (node, service, &ai_hints, &ai_list); 187 | if (status != 0) 188 | { 189 | ERROR ("write_graphite plugin: getaddrinfo (%s, %s) failed: %s", 190 | node, service, gai_strerror (status)); 191 | return (-1); 192 | } 193 | 194 | assert (ai_list != NULL); 195 | for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) 196 | { 197 | cb->sock_fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, 198 | ai_ptr->ai_protocol); 199 | if (cb->sock_fd < 0) 200 | continue; 201 | 202 | status = connect (cb->sock_fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen); 203 | if (status != 0) 204 | { 205 | close (cb->sock_fd); 206 | cb->sock_fd = -1; 207 | continue; 208 | } 209 | 210 | break; 211 | } 212 | 213 | freeaddrinfo (ai_list); 214 | 215 | if (cb->sock_fd < 0) 216 | { 217 | char errbuf[1024]; 218 | ERROR ("write_graphite plugin: Connecting to %s:%s failed. " 219 | "The last error was: %s", node, service, 220 | sstrerror (errno, errbuf, sizeof (errbuf))); 221 | close (cb->sock_fd); 222 | return (-1); 223 | } 224 | 225 | wg_reset_buffer (cb); 226 | 227 | return (0); 228 | } 229 | 230 | static void wg_callback_free (void *data) 231 | { 232 | struct wg_callback *cb; 233 | 234 | if (data == NULL) 235 | return; 236 | 237 | cb = data; 238 | 239 | pthread_mutex_lock (&cb->send_lock); 240 | 241 | wg_flush_nolock (/* timeout = */ 0, cb); 242 | 243 | close(cb->sock_fd); 244 | cb->sock_fd = -1; 245 | 246 | sfree(cb->node); 247 | sfree(cb->service); 248 | sfree(cb->prefix); 249 | sfree(cb->postfix); 250 | 251 | pthread_mutex_destroy (&cb->send_lock); 252 | 253 | sfree(cb); 254 | } 255 | 256 | static int wg_flush (cdtime_t timeout, 257 | const char *identifier __attribute__((unused)), 258 | user_data_t *user_data) 259 | { 260 | struct wg_callback *cb; 261 | int status; 262 | 263 | if (user_data == NULL) 264 | return (-EINVAL); 265 | 266 | cb = user_data->data; 267 | 268 | pthread_mutex_lock (&cb->send_lock); 269 | 270 | if (cb->sock_fd < 0) 271 | { 272 | status = wg_callback_init (cb); 273 | if (status != 0) 274 | { 275 | ERROR ("write_graphite plugin: wg_callback_init failed."); 276 | pthread_mutex_unlock (&cb->send_lock); 277 | return (-1); 278 | } 279 | } 280 | 281 | status = wg_flush_nolock (timeout, cb); 282 | pthread_mutex_unlock (&cb->send_lock); 283 | 284 | return (status); 285 | } 286 | 287 | static int wg_format_values (char *ret, size_t ret_len, 288 | int ds_num, const data_set_t *ds, const value_list_t *vl, 289 | _Bool store_rates) 290 | { 291 | size_t offset = 0; 292 | int status; 293 | gauge_t *rates = NULL; 294 | 295 | assert (0 == strcmp (ds->type, vl->type)); 296 | 297 | memset (ret, 0, ret_len); 298 | 299 | #define BUFFER_ADD(...) do { \ 300 | status = ssnprintf (ret + offset, ret_len - offset, \ 301 | __VA_ARGS__); \ 302 | if (status < 1) \ 303 | { \ 304 | sfree (rates); \ 305 | return (-1); \ 306 | } \ 307 | else if (((size_t) status) >= (ret_len - offset)) \ 308 | { \ 309 | sfree (rates); \ 310 | return (-1); \ 311 | } \ 312 | else \ 313 | offset += ((size_t) status); \ 314 | } while (0) 315 | 316 | if (ds->ds[ds_num].type == DS_TYPE_GAUGE) 317 | BUFFER_ADD ("%f", vl->values[ds_num].gauge); 318 | else if (store_rates) 319 | { 320 | if (rates == NULL) 321 | rates = uc_get_rate (ds, vl); 322 | if (rates == NULL) 323 | { 324 | WARNING ("format_values: " 325 | "uc_get_rate failed."); 326 | return (-1); 327 | } 328 | BUFFER_ADD ("%g", rates[ds_num]); 329 | } 330 | else if (ds->ds[ds_num].type == DS_TYPE_COUNTER) 331 | BUFFER_ADD ("%llu", vl->values[ds_num].counter); 332 | else if (ds->ds[ds_num].type == DS_TYPE_DERIVE) 333 | BUFFER_ADD ("%"PRIi64, vl->values[ds_num].derive); 334 | else if (ds->ds[ds_num].type == DS_TYPE_ABSOLUTE) 335 | BUFFER_ADD ("%"PRIu64, vl->values[ds_num].absolute); 336 | else 337 | { 338 | ERROR ("format_values plugin: Unknown data source type: %i", 339 | ds->ds[ds_num].type); 340 | sfree (rates); 341 | return (-1); 342 | } 343 | 344 | #undef BUFFER_ADD 345 | 346 | sfree (rates); 347 | return (0); 348 | } 349 | 350 | static void wg_copy_escape_part (char *dst, const char *src, size_t dst_len, 351 | char escape_char) 352 | { 353 | size_t i; 354 | 355 | memset (dst, 0, dst_len); 356 | 357 | if (src == NULL) 358 | return; 359 | 360 | for (i = 0; i < dst_len; i++) 361 | { 362 | if (src[i] == 0) 363 | { 364 | dst[i] = 0; 365 | break; 366 | } 367 | 368 | if ((src[i] == '.') 369 | || isspace ((int) src[i]) 370 | || iscntrl ((int) src[i])) 371 | dst[i] = escape_char; 372 | else 373 | dst[i] = src[i]; 374 | } 375 | } 376 | 377 | static int wg_format_name (char *ret, int ret_len, 378 | const value_list_t *vl, 379 | const struct wg_callback *cb, 380 | const char *ds_name) 381 | { 382 | char n_host[DATA_MAX_NAME_LEN]; 383 | char n_plugin[DATA_MAX_NAME_LEN]; 384 | char n_plugin_instance[DATA_MAX_NAME_LEN]; 385 | char n_type[DATA_MAX_NAME_LEN]; 386 | char n_type_instance[DATA_MAX_NAME_LEN]; 387 | 388 | char *prefix; 389 | char *postfix; 390 | 391 | char tmp_plugin[2 * DATA_MAX_NAME_LEN + 1]; 392 | char tmp_type[2 * DATA_MAX_NAME_LEN + 1]; 393 | 394 | prefix = cb->prefix; 395 | if (prefix == NULL) 396 | prefix = ""; 397 | 398 | postfix = cb->postfix; 399 | if (postfix == NULL) 400 | postfix = ""; 401 | 402 | wg_copy_escape_part (n_host, vl->host, 403 | sizeof (n_host), cb->escape_char); 404 | wg_copy_escape_part (n_plugin, vl->plugin, 405 | sizeof (n_plugin), cb->escape_char); 406 | wg_copy_escape_part (n_plugin_instance, vl->plugin_instance, 407 | sizeof (n_plugin_instance), cb->escape_char); 408 | wg_copy_escape_part (n_type, vl->type, 409 | sizeof (n_type), cb->escape_char); 410 | wg_copy_escape_part (n_type_instance, vl->type_instance, 411 | sizeof (n_type_instance), cb->escape_char); 412 | 413 | if (n_plugin_instance[0] != '\0') 414 | ssnprintf (tmp_plugin, sizeof (tmp_plugin), "%s%c%s", 415 | n_plugin, 416 | cb->separate_instances ? '.' : '-', 417 | n_plugin_instance); 418 | else 419 | sstrncpy (tmp_plugin, n_plugin, sizeof (tmp_plugin)); 420 | 421 | if (n_type_instance[0] != '\0') 422 | ssnprintf (tmp_type, sizeof (tmp_type), "%s%c%s", 423 | n_type, 424 | cb->separate_instances ? '.' : '-', 425 | n_type_instance); 426 | else 427 | sstrncpy (tmp_type, n_type, sizeof (tmp_type)); 428 | 429 | if (ds_name != NULL) 430 | ssnprintf (ret, ret_len, "%s%s%s.%s.%s.%s", 431 | prefix, n_host, postfix, tmp_plugin, tmp_type, ds_name); 432 | else 433 | ssnprintf (ret, ret_len, "%s%s%s.%s.%s", 434 | prefix, n_host, postfix, tmp_plugin, tmp_type); 435 | 436 | return (0); 437 | } 438 | 439 | static int wg_send_message (const char* key, const char* value, 440 | cdtime_t time, struct wg_callback *cb) 441 | { 442 | int status; 443 | size_t message_len; 444 | char message[1024]; 445 | 446 | message_len = (size_t) ssnprintf (message, sizeof (message), 447 | "%s %s %u\r\n", 448 | key, 449 | value, 450 | (unsigned int) CDTIME_T_TO_TIME_T (time)); 451 | if (message_len >= sizeof (message)) { 452 | ERROR ("write_graphite plugin: message buffer too small: " 453 | "Need %zu bytes.", message_len + 1); 454 | return (-1); 455 | } 456 | 457 | pthread_mutex_lock (&cb->send_lock); 458 | 459 | if (cb->sock_fd < 0) 460 | { 461 | status = wg_callback_init (cb); 462 | if (status != 0) 463 | { 464 | ERROR ("write_graphite plugin: wg_callback_init failed."); 465 | pthread_mutex_unlock (&cb->send_lock); 466 | return (-1); 467 | } 468 | } 469 | 470 | if (message_len >= cb->send_buf_free) 471 | { 472 | status = wg_flush_nolock (/* timeout = */ 0, cb); 473 | if (status != 0) 474 | { 475 | pthread_mutex_unlock (&cb->send_lock); 476 | return (status); 477 | } 478 | } 479 | 480 | /* Assert that we have enough space for this message. */ 481 | assert (message_len < cb->send_buf_free); 482 | 483 | /* `message_len + 1' because `message_len' does not include the 484 | * trailing null byte. Neither does `send_buffer_fill'. */ 485 | memcpy (cb->send_buf + cb->send_buf_fill, 486 | message, message_len + 1); 487 | cb->send_buf_fill += message_len; 488 | cb->send_buf_free -= message_len; 489 | 490 | DEBUG ("write_graphite plugin: [%s]:%s buf %zu/%zu (%.1f %%) \"%s\"", 491 | cb->node, 492 | cb->service, 493 | cb->send_buf_fill, sizeof (cb->send_buf), 494 | 100.0 * ((double) cb->send_buf_fill) / ((double) sizeof (cb->send_buf)), 495 | message); 496 | 497 | pthread_mutex_unlock (&cb->send_lock); 498 | 499 | return (0); 500 | } 501 | 502 | static int wg_write_messages (const data_set_t *ds, const value_list_t *vl, 503 | struct wg_callback *cb) 504 | { 505 | char key[10*DATA_MAX_NAME_LEN]; 506 | char values[512]; 507 | 508 | int status, i; 509 | 510 | if (0 != strcmp (ds->type, vl->type)) 511 | { 512 | ERROR ("write_graphite plugin: DS type does not match " 513 | "value list type"); 514 | return -1; 515 | } 516 | 517 | for (i = 0; i < ds->ds_num; i++) 518 | { 519 | const char *ds_name = NULL; 520 | 521 | if (cb->always_append_ds || (ds->ds_num > 1)) 522 | ds_name = ds->ds[i].name; 523 | 524 | /* Copy the identifier to `key' and escape it. */ 525 | status = wg_format_name (key, sizeof (key), vl, cb, ds_name); 526 | if (status != 0) 527 | { 528 | ERROR ("write_graphite plugin: error with format_name"); 529 | return (status); 530 | } 531 | 532 | escape_string (key, sizeof (key)); 533 | /* Convert the values to an ASCII representation and put that into 534 | * `values'. */ 535 | status = wg_format_values (values, sizeof (values), i, ds, vl, 536 | cb->store_rates); 537 | if (status != 0) 538 | { 539 | ERROR ("write_graphite plugin: error with " 540 | "wg_format_values"); 541 | return (status); 542 | } 543 | 544 | /* Send the message to graphite */ 545 | status = wg_send_message (key, values, vl->time, cb); 546 | if (status != 0) 547 | { 548 | ERROR ("write_graphite plugin: error with " 549 | "wg_send_message"); 550 | return (status); 551 | } 552 | } 553 | 554 | return (0); 555 | } 556 | 557 | static int wg_write (const data_set_t *ds, const value_list_t *vl, 558 | user_data_t *user_data) 559 | { 560 | struct wg_callback *cb; 561 | int status; 562 | 563 | if (user_data == NULL) 564 | return (EINVAL); 565 | 566 | cb = user_data->data; 567 | 568 | status = wg_write_messages (ds, vl, cb); 569 | 570 | return (status); 571 | } 572 | 573 | static int config_set_char (char *dest, 574 | oconfig_item_t *ci) 575 | { 576 | char buffer[4]; 577 | int status; 578 | 579 | memset (buffer, 0, sizeof (buffer)); 580 | 581 | status = cf_util_get_string_buffer (ci, buffer, sizeof (buffer)); 582 | if (status != 0) 583 | return (status); 584 | 585 | if (buffer[0] == 0) 586 | { 587 | ERROR ("write_graphite plugin: Cannot use an empty string for the " 588 | "\"EscapeCharacter\" option."); 589 | return (-1); 590 | } 591 | 592 | if (buffer[1] != 0) 593 | { 594 | WARNING ("write_graphite plugin: Only the first character of the " 595 | "\"EscapeCharacter\" option ('%c') will be used.", 596 | (int) buffer[0]); 597 | } 598 | 599 | *dest = buffer[0]; 600 | 601 | return (0); 602 | } 603 | 604 | static int wg_config_carbon (oconfig_item_t *ci) 605 | { 606 | struct wg_callback *cb; 607 | user_data_t user_data; 608 | char callback_name[DATA_MAX_NAME_LEN]; 609 | int i; 610 | 611 | cb = malloc (sizeof (*cb)); 612 | if (cb == NULL) 613 | { 614 | ERROR ("write_graphite plugin: malloc failed."); 615 | return (-1); 616 | } 617 | memset (cb, 0, sizeof (*cb)); 618 | cb->sock_fd = -1; 619 | cb->node = NULL; 620 | cb->service = NULL; 621 | cb->prefix = NULL; 622 | cb->postfix = NULL; 623 | cb->escape_char = WG_DEFAULT_ESCAPE; 624 | cb->store_rates = 1; 625 | 626 | pthread_mutex_init (&cb->send_lock, /* attr = */ NULL); 627 | 628 | for (i = 0; i < ci->children_num; i++) 629 | { 630 | oconfig_item_t *child = ci->children + i; 631 | 632 | if (strcasecmp ("Host", child->key) == 0) 633 | cf_util_get_string (child, &cb->node); 634 | else if (strcasecmp ("Port", child->key) == 0) 635 | cf_util_get_service (child, &cb->service); 636 | else if (strcasecmp ("Prefix", child->key) == 0) 637 | cf_util_get_string (child, &cb->prefix); 638 | else if (strcasecmp ("Postfix", child->key) == 0) 639 | cf_util_get_string (child, &cb->postfix); 640 | else if (strcasecmp ("StoreRates", child->key) == 0) 641 | cf_util_get_boolean (child, &cb->store_rates); 642 | else if (strcasecmp ("SeparateInstances", child->key) == 0) 643 | cf_util_get_boolean (child, &cb->separate_instances); 644 | else if (strcasecmp ("AlwaysAppendDS", child->key) == 0) 645 | cf_util_get_boolean (child, &cb->always_append_ds); 646 | else if (strcasecmp ("EscapeCharacter", child->key) == 0) 647 | config_set_char (&cb->escape_char, child); 648 | else 649 | { 650 | ERROR ("write_graphite plugin: Invalid configuration " 651 | "option: %s.", child->key); 652 | } 653 | } 654 | 655 | ssnprintf (callback_name, sizeof (callback_name), "write_graphite/%s/%s", 656 | cb->node != NULL ? cb->node : WG_DEFAULT_NODE, 657 | cb->service != NULL ? cb->service : WG_DEFAULT_SERVICE); 658 | 659 | memset (&user_data, 0, sizeof (user_data)); 660 | user_data.data = cb; 661 | user_data.free_func = wg_callback_free; 662 | plugin_register_write (callback_name, wg_write, &user_data); 663 | 664 | user_data.free_func = NULL; 665 | plugin_register_flush (callback_name, wg_flush, &user_data); 666 | 667 | return (0); 668 | } 669 | 670 | static int wg_config (oconfig_item_t *ci) 671 | { 672 | int i; 673 | 674 | for (i = 0; i < ci->children_num; i++) 675 | { 676 | oconfig_item_t *child = ci->children + i; 677 | 678 | if (strcasecmp ("Carbon", child->key) == 0) 679 | wg_config_carbon (child); 680 | else 681 | { 682 | ERROR ("write_graphite plugin: Invalid configuration " 683 | "option: %s.", child->key); 684 | } 685 | } 686 | 687 | return (0); 688 | } 689 | 690 | void module_register (void) 691 | { 692 | plugin_register_complex_config ("write_graphite", wg_config); 693 | } 694 | 695 | /* vim: set sw=4 ts=4 sts=4 tw=78 et : */ 696 | --------------------------------------------------------------------------------