static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */ { amqp_exchange_declare_ok_t *ed_ret; if (conf->exchange_type == NULL) return (0); ed_ret = amqp_exchange_declare (conf->connection, /* channel = */ CAMQP_CHANNEL, /* exchange = */ amqp_cstring_bytes (conf->exchange), /* type = */ amqp_cstring_bytes (conf->exchange_type), /* passive = */ 0, /* durable = */ 0, /* auto_delete = */ 1, /* arguments = */ AMQP_EMPTY_TABLE); if ((ed_ret == NULL) && camqp_is_error (conf)) { char errbuf[1024]; ERROR ("amqp plugin: amqp_exchange_declare failed: %s", camqp_strerror (conf, errbuf, sizeof (errbuf))); camqp_close_connection (conf); return (-1); } INFO ("amqp plugin: Successfully created exchange \"%s\" " "with type \"%s\".", conf->exchange, conf->exchange_type); return (0); } /* }}} int camqp_create_exchange */
static int camqp_read_header(camqp_config_t *conf) /* {{{ */ { int status; amqp_frame_t frame; amqp_basic_properties_t *properties; char *content_type; status = amqp_simple_wait_frame(conf->connection, &frame); if (status < 0) { status = (-1) * status; ERROR("amqp plugin: amqp_simple_wait_frame failed: %s", STRERROR(status)); camqp_close_connection(conf); return status; } if (frame.frame_type != AMQP_FRAME_HEADER) { NOTICE("amqp plugin: Unexpected frame type: %#" PRIx8, frame.frame_type); return -1; } properties = frame.payload.properties.decoded; content_type = camqp_bytes_cstring(&properties->content_type); if (content_type == NULL) { ERROR("amqp plugin: Unable to determine content type."); return -1; } status = camqp_read_body(conf, (size_t)frame.payload.properties.body_size, content_type); sfree(content_type); return status; } /* }}} int camqp_read_header */
static void *camqp_subscribe_thread (void *user_data) /* {{{ */ { camqp_config_t *conf = user_data; int status; cdtime_t interval = plugin_get_interval (); while (subscriber_threads_running) { amqp_frame_t frame; status = camqp_connect (conf); if (status != 0) { struct timespec ts_interval; ERROR ("amqp plugin: camqp_connect failed. " "Will sleep for %.3f seconds.", CDTIME_T_TO_DOUBLE (interval)); CDTIME_T_TO_TIMESPEC (interval, &ts_interval); nanosleep (&ts_interval, /* remaining = */ NULL); continue; } status = amqp_simple_wait_frame (conf->connection, &frame); if (status < 0) { struct timespec ts_interval; ERROR ("amqp plugin: amqp_simple_wait_frame failed. " "Will sleep for %.3f seconds.", CDTIME_T_TO_DOUBLE (interval)); camqp_close_connection (conf); CDTIME_T_TO_TIMESPEC (interval, &ts_interval); nanosleep (&ts_interval, /* remaining = */ NULL); continue; } if (frame.frame_type != AMQP_FRAME_METHOD) { DEBUG ("amqp plugin: Unexpected frame type: %#"PRIx8, frame.frame_type); continue; } if (frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) { DEBUG ("amqp plugin: Unexpected method id: %#"PRIx32, frame.payload.method.id); continue; } camqp_read_header (conf); amqp_maybe_release_buffers (conf->connection); } /* while (subscriber_threads_running) */ camqp_config_free (conf); pthread_exit (NULL); return (NULL); } /* }}} void *camqp_subscribe_thread */
/* * Subscribing code */ static int camqp_read_body(camqp_config_t *conf, /* {{{ */ size_t body_size, const char *content_type) { char body[body_size + 1]; char *body_ptr; size_t received; amqp_frame_t frame; int status; memset(body, 0, sizeof(body)); body_ptr = &body[0]; received = 0; while (received < body_size) { status = amqp_simple_wait_frame(conf->connection, &frame); if (status < 0) { status = (-1) * status; ERROR("amqp plugin: amqp_simple_wait_frame failed: %s", STRERROR(status)); camqp_close_connection(conf); return status; } if (frame.frame_type != AMQP_FRAME_BODY) { NOTICE("amqp plugin: Unexpected frame type: %#" PRIx8, frame.frame_type); return -1; } if ((body_size - received) < frame.payload.body_fragment.len) { WARNING("amqp plugin: Body is larger than indicated by header."); return -1; } memcpy(body_ptr, frame.payload.body_fragment.bytes, frame.payload.body_fragment.len); body_ptr += frame.payload.body_fragment.len; received += frame.payload.body_fragment.len; } /* while (received < body_size) */ if (strcasecmp("text/collectd", content_type) == 0) { status = cmd_handle_putval(stderr, body); if (status != 0) ERROR("amqp plugin: cmd_handle_putval failed with status %i.", status); return status; } else if (strcasecmp("application/json", content_type) == 0) { ERROR("amqp plugin: camqp_read_body: Parsing JSON data has not " "been implemented yet. FIXME!"); return 0; } else { ERROR("amqp plugin: camqp_read_body: Unknown content type \"%s\".", content_type); return EINVAL; } /* not reached */ return 0; } /* }}} int camqp_read_body */
static void *camqp_subscribe_thread (void *user_data) /* {{{ */ { camqp_config_t *conf = user_data; int status; while (subscriber_threads_running) { amqp_frame_t frame; status = camqp_connect (conf); if (status != 0) { ERROR ("amqp plugin: camqp_connect failed. " "Will sleep for %i seconds.", interval_g); sleep (interval_g); continue; } status = amqp_simple_wait_frame (conf->connection, &frame); if (status < 0) { ERROR ("amqp plugin: amqp_simple_wait_frame failed. " "Will sleep for %i seconds.", interval_g); camqp_close_connection (conf); sleep (interval_g); continue; } if (frame.frame_type != AMQP_FRAME_METHOD) { DEBUG ("amqp plugin: Unexpected frame type: %#"PRIx8, frame.frame_type); continue; } if (frame.payload.method.id != AMQP_BASIC_DELIVER_METHOD) { DEBUG ("amqp plugin: Unexpected method id: %#"PRIx32, frame.payload.method.id); continue; } status = camqp_read_header (conf); amqp_maybe_release_buffers (conf->connection); } /* while (subscriber_threads_running) */ camqp_config_free (conf); pthread_exit (NULL); } /* }}} void *camqp_subscribe_thread */
/* XXX: You must hold "conf->lock" when calling this function! */ static int camqp_write_locked (camqp_config_t *conf, /* {{{ */ const char *buffer, const char *routing_key) { amqp_basic_properties_t props; int status; status = camqp_connect (conf); if (status != 0) return (status); memset (&props, 0, sizeof (props)); props._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_APP_ID_FLAG; if (conf->format == CAMQP_FORMAT_COMMAND) props.content_type = amqp_cstring_bytes("text/collectd"); else if (conf->format == CAMQP_FORMAT_JSON) props.content_type = amqp_cstring_bytes("application/json"); else if (conf->format == CAMQP_FORMAT_GRAPHITE) props.content_type = amqp_cstring_bytes("text/graphite"); else assert (23 == 42); props.delivery_mode = conf->delivery_mode; props.app_id = amqp_cstring_bytes("collectd"); status = amqp_basic_publish(conf->connection, /* channel = */ 1, amqp_cstring_bytes(CONF(conf, exchange)), amqp_cstring_bytes (routing_key), /* mandatory = */ 0, /* immediate = */ 0, &props, amqp_cstring_bytes(buffer)); if (status != 0) { ERROR ("amqp plugin: amqp_basic_publish failed with status %i.", status); camqp_close_connection (conf); } return (status); } /* }}} int camqp_write_locked */
static int camqp_create_exchange (camqp_config_t *conf) /* {{{ */ { amqp_exchange_declare_ok_t *ed_ret; amqp_table_t argument_table; struct amqp_table_entry_t_ argument_table_entries[1]; if (conf->exchange_type == NULL) return (0); /* Valid arguments: "auto_delete", "internal" */ argument_table.num_entries = STATIC_ARRAY_SIZE (argument_table_entries); argument_table.entries = argument_table_entries; argument_table_entries[0].key = amqp_cstring_bytes ("auto_delete"); argument_table_entries[0].value.kind = AMQP_FIELD_KIND_BOOLEAN; argument_table_entries[0].value.value.boolean = 1; ed_ret = amqp_exchange_declare (conf->connection, /* channel = */ CAMQP_CHANNEL, /* exchange = */ amqp_cstring_bytes (conf->exchange), /* type = */ amqp_cstring_bytes (conf->exchange_type), /* passive = */ 0, /* durable = */ 0, #if defined(AMQP_VERSION) && AMQP_VERSION >= 0x00060000 /* auto delete = */ 0, /* internal = */ 0, #endif /* arguments = */ argument_table); if ((ed_ret == NULL) && camqp_is_error (conf)) { char errbuf[1024]; ERROR ("amqp plugin: amqp_exchange_declare failed: %s", camqp_strerror (conf, errbuf, sizeof (errbuf))); camqp_close_connection (conf); return (-1); } INFO ("amqp plugin: Successfully created exchange \"%s\" " "with type \"%s\".", conf->exchange, conf->exchange_type); return (0); } /* }}} int camqp_create_exchange */
static void camqp_config_free (void *ptr) /* {{{ */ { camqp_config_t *conf = ptr; if (conf == NULL) return; camqp_close_connection (conf); sfree (conf->name); sfree (conf->host); sfree (conf->vhost); sfree (conf->user); sfree (conf->password); sfree (conf->exchange); sfree (conf->exchange_type); sfree (conf->queue); sfree (conf->routing_key); sfree (conf); } /* }}} void camqp_config_free */
static int camqp_setup_queue (camqp_config_t *conf) /* {{{ */ { amqp_queue_declare_ok_t *qd_ret; amqp_basic_consume_ok_t *cm_ret; qd_ret = amqp_queue_declare (conf->connection, /* channel = */ CAMQP_CHANNEL, /* queue = */ (conf->queue != NULL) ? amqp_cstring_bytes (conf->queue) : AMQP_EMPTY_BYTES, /* passive = */ 0, /* durable = */ 0, /* exclusive = */ 0, /* auto_delete = */ 1, /* arguments = */ AMQP_EMPTY_TABLE); if (qd_ret == NULL) { ERROR ("amqp plugin: amqp_queue_declare failed."); camqp_close_connection (conf); return (-1); } if (conf->queue == NULL) { conf->queue = camqp_bytes_cstring (&qd_ret->queue); if (conf->queue == NULL) { ERROR ("amqp plugin: camqp_bytes_cstring failed."); camqp_close_connection (conf); return (-1); } INFO ("amqp plugin: Created queue \"%s\".", conf->queue); } DEBUG ("amqp plugin: Successfully created queue \"%s\".", conf->queue); /* bind to an exchange */ if (conf->exchange != NULL) { amqp_queue_bind_ok_t *qb_ret; assert (conf->queue != NULL); qb_ret = amqp_queue_bind (conf->connection, /* channel = */ CAMQP_CHANNEL, /* queue = */ amqp_cstring_bytes (conf->queue), /* exchange = */ amqp_cstring_bytes (conf->exchange), /* routing_key = */ (conf->routing_key != NULL) ? amqp_cstring_bytes (conf->routing_key) : AMQP_EMPTY_BYTES, /* arguments = */ AMQP_EMPTY_TABLE); if ((qb_ret == NULL) && camqp_is_error (conf)) { char errbuf[1024]; ERROR ("amqp plugin: amqp_queue_bind failed: %s", camqp_strerror (conf, errbuf, sizeof (errbuf))); camqp_close_connection (conf); return (-1); } DEBUG ("amqp plugin: Successfully bound queue \"%s\" to exchange \"%s\".", conf->queue, conf->exchange); } /* if (conf->exchange != NULL) */ cm_ret = amqp_basic_consume (conf->connection, /* channel = */ CAMQP_CHANNEL, /* queue = */ amqp_cstring_bytes (conf->queue), /* consumer_tag = */ AMQP_EMPTY_BYTES, /* no_local = */ 0, /* no_ack = */ 1, /* exclusive = */ 0, /* arguments = */ AMQP_EMPTY_TABLE ); if ((cm_ret == NULL) && camqp_is_error (conf)) { char errbuf[1024]; ERROR ("amqp plugin: amqp_basic_consume failed: %s", camqp_strerror (conf, errbuf, sizeof (errbuf))); camqp_close_connection (conf); return (-1); } return (0); } /* }}} int camqp_setup_queue */
/* XXX: You must hold "conf->lock" when calling this function! */ static int camqp_write_locked (camqp_config_t *conf, /* {{{ */ const char *buffer, const char *routing_key) { int status; status = camqp_connect (conf); if (status != 0) return (status); amqp_basic_properties_t props = { ._flags = AMQP_BASIC_CONTENT_TYPE_FLAG | AMQP_BASIC_DELIVERY_MODE_FLAG | AMQP_BASIC_APP_ID_FLAG, .delivery_mode = conf->delivery_mode, .app_id = amqp_cstring_bytes("collectd") }; if (conf->format == CAMQP_FORMAT_COMMAND) props.content_type = amqp_cstring_bytes("text/collectd"); else if (conf->format == CAMQP_FORMAT_JSON) props.content_type = amqp_cstring_bytes("application/json"); else if (conf->format == CAMQP_FORMAT_GRAPHITE) props.content_type = amqp_cstring_bytes("text/graphite"); else assert (23 == 42); status = amqp_basic_publish(conf->connection, /* channel = */ 1, amqp_cstring_bytes(CONF(conf, exchange)), amqp_cstring_bytes (routing_key), /* mandatory = */ 0, /* immediate = */ 0, &props, amqp_cstring_bytes(buffer)); if (status != 0) { ERROR ("amqp plugin: amqp_basic_publish failed with status %i.", status); camqp_close_connection (conf); } return (status); } /* }}} int camqp_write_locked */ static int camqp_write (const data_set_t *ds, const value_list_t *vl, /* {{{ */ user_data_t *user_data) { camqp_config_t *conf = user_data->data; char routing_key[6 * DATA_MAX_NAME_LEN]; char buffer[8192]; int status; if ((ds == NULL) || (vl == NULL) || (conf == NULL)) return (EINVAL); if (conf->routing_key != NULL) { sstrncpy (routing_key, conf->routing_key, sizeof (routing_key)); } else { ssnprintf (routing_key, sizeof (routing_key), "collectd/%s/%s/%s/%s/%s", vl->host, vl->plugin, vl->plugin_instance, vl->type, vl->type_instance); /* Switch slashes (the only character forbidden by collectd) and dots * (the separation character used by AMQP). */ for (size_t i = 0; routing_key[i] != 0; i++) { if (routing_key[i] == '.') routing_key[i] = '/'; else if (routing_key[i] == '/') routing_key[i] = '.'; } } if (conf->format == CAMQP_FORMAT_COMMAND) { status = create_putval (buffer, sizeof (buffer), ds, vl); if (status != 0) { ERROR ("amqp plugin: create_putval failed with status %i.", status); return (status); } } else if (conf->format == CAMQP_FORMAT_JSON) { size_t bfree = sizeof (buffer); size_t bfill = 0; format_json_initialize (buffer, &bfill, &bfree); format_json_value_list (buffer, &bfill, &bfree, ds, vl, conf->store_rates); format_json_finalize (buffer, &bfill, &bfree); } else if (conf->format == CAMQP_FORMAT_GRAPHITE) { status = format_graphite (buffer, sizeof (buffer), ds, vl, conf->prefix, conf->postfix, conf->escape_char, conf->graphite_flags); if (status != 0) { ERROR ("amqp plugin: format_graphite failed with status %i.", status); return (status); } } else { ERROR ("amqp plugin: Invalid format (%i).", conf->format); return (-1); } pthread_mutex_lock (&conf->lock); status = camqp_write_locked (conf, buffer, routing_key); pthread_mutex_unlock (&conf->lock); return (status); } /* }}} int camqp_write */