コード例 #1
0
ファイル: tcpclient.c プロジェクト: JeremyGrosser/statsrelay
static void tcpclient_write_event(struct ev_loop *loop, struct ev_io *watcher, int events) {
	tcpclient_t *client = (tcpclient_t *)watcher->data;
	buffer_t *sendq;

	if (!(events & EV_WRITE)) {
		return;
	}

	sendq = &client->send_queue;
	ssize_t buf_len = buffer_datacount(sendq);
	if (buf_len > 0) {
		ssize_t send_len = send(client->sd, sendq->head, buf_len, 0);
		stats_debug_log("tcpclient: sent %zd of %zd bytes to backend client %s via fd %d",
				send_len, buf_len, client->name, client->sd);
		if (send_len < 0) {
			stats_error_log("tcpclient[%s]: Error from send: %s", client->name, strerror(errno));
			ev_io_stop(client->loop, &client->write_watcher.watcher);
			ev_io_stop(client->loop, &client->read_watcher.watcher);
			client->last_error = time(NULL);
			tcpclient_set_state(client, STATE_BACKOFF);
			close(client->sd);
			client->callback_error(client, EVENT_ERROR, client->callback_context, NULL, 0);
			return;
		} else {
			client->callback_sent(client, EVENT_SENT, client->callback_context, sendq->head, (size_t) send_len);
			if (buffer_consume(sendq, send_len) != 0) {
				stats_error_log("tcpclient[%s]: Unable to consume send queue", client->name);
				return;
			}
			size_t qsize = buffer_datacount(&client->send_queue);
			if (client->failing && qsize < client->config->max_send_queue) {
				stats_log("tcpclient[%s]: client recovered from full queue, send queue is now %zd bytes",
					  client->name,
					  qsize);
				client->failing = 0;
			}
			if (qsize == 0) {
				ev_io_stop(client->loop, &client->write_watcher.watcher);
				client->write_watcher.started = false;
			}
		}
	} else {
		// No data left in the client's buffer, stop waiting
		// for write events.
		ev_io_stop(client->loop, &client->write_watcher.watcher);
		client->write_watcher.started = false;
	}
}
コード例 #2
0
ファイル: tcpclient.c プロジェクト: JeremyGrosser/statsrelay
static void tcpclient_read_event(struct ev_loop *loop, struct ev_io *watcher, int events) {
	tcpclient_t *client = (tcpclient_t *)watcher->data;
	ssize_t len;
	char *buf;

	if (!(events & EV_READ)) {
		return;
	}

	buf = malloc(TCPCLIENT_RECV_BUFFER);
	if (buf == NULL) {
		stats_error_log("tcpclient[%s]: Unable to allocate memory for receive buffer", client->name);
		return;
	}
	len = recv(client->sd, buf, TCPCLIENT_RECV_BUFFER, 0);
	if (len < 0) {
		stats_error_log("tcpclient[%s]: Error from recv: %s", client->name, strerror(errno));
		if (client->read_watcher.started) {
			ev_io_stop(client->loop, &client->read_watcher.watcher);
			client->read_watcher.started = false;
		}
		if (client->write_watcher.started) {
			ev_io_stop(client->loop, &client->write_watcher.watcher);
			client->write_watcher.started = false;
		}
		close(client->sd);
		free(buf);
		tcpclient_set_state(client, STATE_BACKOFF);
		client->last_error = time(NULL);
		client->callback_error(client, EVENT_ERROR, client->callback_context, NULL, 0);
		return;
	}

	if (len == 0) {
		stats_error_log("tcpclient[%s]: Server closed connection", client->name);
		ev_io_stop(client->loop, &client->read_watcher.watcher);
		ev_io_stop(client->loop, &client->write_watcher.watcher);
		close(client->sd);
		free(buf);
		tcpclient_set_state(client, STATE_INIT);
		client->last_error = time(NULL);
		client->callback_error(client, EVENT_ERROR, client->callback_context, NULL, 0);
		return;
	}
	client->callback_recv(client, EVENT_RECV, client->callback_context, buf, len);

}
コード例 #3
0
ファイル: json_config.c プロジェクト: theatrus/statsrelay
static int get_int_orelse(json_t* json, const char* key, int def) {
    json_t* v = json_object_get(json, key);
    if (v == NULL)
	return def;
    if (!json_is_number(v)) {
	stats_error_log("Expected an integer value for '%s' - using default of '%d'", key, def);
    }
    return (int)json_integer_value(v);
}
コード例 #4
0
ファイル: tcpclient.c プロジェクト: JeremyGrosser/statsrelay
int tcpclient_sendall(tcpclient_t *client, const char *buf, size_t len) {
	buffer_t *sendq = &client->send_queue;

	if (client->addr == NULL) {
		stats_error_log("tcpclient[%s]: Cannot send before connect!", client->name);
		return 1;
	} else {
		// Does nothing if we're already connected, triggers a
		// reconnect if backoff has expired.
		tcpclient_connect(client);
	}

	if (buffer_datacount(&client->send_queue) >= client->config->max_send_queue) {
		if (client->failing == 0) {
			stats_error_log("tcpclient[%s]: send queue for %s client is full (at %zd bytes, max is %" PRIu64 " bytes), dropping data",
					client->name,
					tcpclient_state_name[client->state],
					buffer_datacount(&client->send_queue),
					client->config->max_send_queue);
			client->failing = 1;
		}
		return 2;
	}
	if (buffer_spacecount(sendq) < len) {
		if (buffer_realign(sendq) != 0) {
			stats_error_log("tcpclient[%s]: Unable to realign send queue", client->name);
			return 3;
		}
	}
	while (buffer_spacecount(sendq) < len) {
		if (buffer_expand(sendq) != 0) {
			stats_error_log("tcpclient[%s]: Unable to allocate additional memory for send queue, dropping data", client->name);
			return 4;
		}
	}
	memcpy(buffer_tail(sendq), buf, len);
	buffer_produced(sendq, len);

	if (client->state == STATE_CONNECTED) {
		client->write_watcher.started = true;
		ev_io_start(client->loop, &client->write_watcher.watcher);
	}
	return 0;
}
コード例 #5
0
static bool set_boolean(const char *strval, bool *bool_val) {
	if (strcmp(strval, "true") == 0) {
		*bool_val = true;
	} else if (strcmp(strval, "false") == 0) {
		*bool_val = false;
	} else {
		stats_error_log("unexpected value \"%s\" for boolean field, "
				"must be true/false", strval);
		return false;
	}
	return true;
}
コード例 #6
0
ファイル: tcpclient.c プロジェクト: JeremyGrosser/statsrelay
static void tcpclient_connect_timeout(struct ev_loop *loop, struct ev_timer *watcher, int events) {
	tcpclient_t *client = (tcpclient_t *)watcher->data;
	if (client->connect_watcher.started) {
		ev_io_stop(loop, &client->connect_watcher.watcher);
		client->connect_watcher.started = false;
	}

	close(client->sd);
	stats_error_log("tcpclient[%s]: Connection timeout", client->name);
	client->last_error = time(NULL);
	tcpclient_set_state(client, STATE_BACKOFF);
	client->callback_error(client, EVENT_ERROR, client->callback_context, NULL, 0);
}
コード例 #7
0
ファイル: json_config.c プロジェクト: theatrus/statsrelay
static void parse_server_list(const json_t* jshards, list_t ring) {
    if (jshards == NULL) {
	stats_error_log("no servers specified for routing");
	return;
    }

    const json_t* jserver = NULL;
    size_t index;
    json_array_foreach(jshards, index, jserver) {
	statsrelay_list_expand(ring);
	char* serverline = strdup(json_string_value(jserver));
	stats_log("adding server %s", serverline);
	ring->data[ring->size - 1] = serverline;
    }
コード例 #8
0
ファイル: json_config.c プロジェクト: theatrus/statsrelay
static char* get_string(const json_t* json, const char* key) {
    json_t* j = json_object_get(json, key);
    if (key == NULL)
	return NULL;
    if (!json_is_string(j)) {
	stats_error_log("Expected a string value for '%s' - ignoring config value", key);
	return NULL;
    }
    const char* str = json_string_value(j);
    if (str == NULL)
	return NULL;
    else
	return strdup(str);
}
コード例 #9
0
ファイル: tcpclient.c プロジェクト: JeremyGrosser/statsrelay
static void tcpclient_connected(struct ev_loop *loop, struct ev_io *watcher, int events) {
	tcpclient_t *client = (tcpclient_t *)watcher->data;
	int err;
	socklen_t len = sizeof(err);

	// Cancel timeout timer
	ev_timer_stop(loop, &client->timeout_watcher);
	ev_io_stop(loop, &client->connect_watcher.watcher);

	if (getsockopt(client->sd, SOL_SOCKET, SO_ERROR, &err, &len) != 0) {
		stats_error_log("tcpclient[%s]: Unable to get socket error state: %s", client->name, strerror(errno));
		return;
	}

	if ((events & EV_ERROR) || err) {
		stats_error_log("tcpclient[%s]: Connect failed: %s", client->name, strerror(err));
		close(client->sd);
		client->last_error = time(NULL);
		tcpclient_set_state(client, STATE_BACKOFF);
		return;
	}

	tcpclient_set_state(client, STATE_CONNECTED);

	// Setup events for recv
	client->read_watcher.started = true;
	client->read_watcher.watcher.data = client;
	ev_io_init(&client->read_watcher.watcher, tcpclient_read_event, client->sd, EV_READ);
	ev_io_start(client->loop, &client->read_watcher.watcher);

	client->write_watcher.started = true;
	client->write_watcher.watcher.data = client;
	ev_io_init(&client->write_watcher.watcher, tcpclient_write_event, client->sd, EV_WRITE);
	ev_io_start(client->loop, &client->write_watcher.watcher);

	client->callback_connect(client, EVENT_CONNECTED, client->callback_context, NULL, 0);
}
コード例 #10
0
ファイル: json_config.c プロジェクト: theatrus/statsrelay
static void json_error_log(const char* msg, json_error_t* error) {
    stats_error_log("JSON error %s: %s (%s) at line %d", msg, error->text, error->source, error->line);
}
コード例 #11
0
struct config* parse_config(FILE *input) {
	struct config *config = malloc(sizeof(struct config));
	if (config == NULL) {
		stats_error_log("malloc() error");
		return NULL;
	}

	init_proto_config(&config->carbon_config);
	if (config->carbon_config.ring == NULL) {
		stats_error_log("failed to allocate ring");
		free(config);
		return NULL;
	}
	config->carbon_config.bind = strdup("127.0.0.1:2003");

	init_proto_config(&config->statsd_config);
	config->statsd_config.bind = strdup("127.0.0.1:8125");

	yaml_parser_t parser;
	yaml_event_t event;

	if (!yaml_parser_initialize(&parser)) {
		stats_log("failed to initialize yaml parser");
		goto parse_err;
	}
	yaml_parser_set_input_file(&parser, input);

	struct proto_config *protoc = NULL;
	char *strval;
	long numval;
	int shard_count = -1;
	int map_nesting = 0;
	bool in_document = false;
	bool keep_going = true;
	bool is_key = false;
	bool update_bind = false;
	bool update_send_queue = false;
	bool update_validate = false;
	bool update_tcp_cork = false;
	bool always_resolve_dns = false;
	bool expect_shard_map = false;
	while (keep_going) {
		if (!yaml_parser_parse(&parser, &event)) {
			goto parse_err;
		}

		switch(event.type) {
		case YAML_NO_EVENT:
		case YAML_STREAM_START_EVENT:
			break;  // nothing to do
		case YAML_STREAM_END_EVENT:
			keep_going = false;
			break;
		case YAML_DOCUMENT_START_EVENT:
			if (in_document) {
				stats_error_log("config should not have nested documents");
				goto parse_err;
			}
			in_document = true;
			break;
		case YAML_DOCUMENT_END_EVENT:
			in_document = false;
			break;
		case YAML_SEQUENCE_START_EVENT:
		case YAML_SEQUENCE_END_EVENT:
			stats_error_log("unexpectedly got sequence");
			goto parse_err;
			break;
		case YAML_MAPPING_START_EVENT:
			is_key = true;
			map_nesting++;
			break;
		case YAML_MAPPING_END_EVENT:
			map_nesting--;
			break;
		case YAML_ALIAS_EVENT:
			stats_error_log("don't know how to handle yaml aliases");
			goto parse_err;
			break;
		case YAML_SCALAR_EVENT:
			strval = (char *) event.data.scalar.value;
			switch (map_nesting) {
			case 0:
				stats_error_log("unexpectedly got scalar outside of a map");
				goto parse_err;
				break;
			case 1:
				if (strcmp(strval, "carbon") == 0) {
					protoc = &config->carbon_config;
					config->carbon_config.initialized = true;
				} else if (strcmp(strval, "statsd") == 0) {
					protoc = &config->statsd_config;
					config->statsd_config.initialized = true;
				} else {
					stats_error_log("unexpectedly got map value: \"%s\"", strval);
					goto parse_err;
				}
				break;
			case 2:
				if (is_key) {
					if (strcmp(strval, "bind") == 0) {
						update_bind = true;
					} else if (strcmp(strval, "max_send_queue") == 0) {
						update_send_queue = true;
					} else if (strcmp(strval, "shard_map") == 0) {
						shard_count = -1;
						expect_shard_map = true;
					} else if (strcmp(strval, "validate") == 0) {
						update_validate = true;
					} else if (strcmp(strval, "tcp_cork") == 0) {
						update_tcp_cork = true;
					} else if (strcmp(strval, "always_resolve_dns") == 0) {
						always_resolve_dns = true;
					}
				} else {
					if (update_bind) {
						free(protoc->bind);
						protoc->bind = strdup(strval);
						update_bind = false;
					} else if (update_send_queue) {
						if (!convert_number(strval, &numval)) {
							stats_error_log("max_send_queue was not a number: %s", strval);
						}
						protoc->max_send_queue = numval;
						update_send_queue = false;
					} else if (update_validate) {
						if (!set_boolean(strval, &protoc->enable_validation)) {
							goto parse_err;
						}
						update_validate = false;
					} else if (update_tcp_cork) {
						if (!set_boolean(strval, &protoc->enable_tcp_cork)) {
							goto parse_err;
						}
						update_tcp_cork = false;
					} else if (always_resolve_dns) {
						if (!set_boolean(strval, &protoc->always_resolve_dns)) {
							goto parse_err;
						}
					}
				}
				break;
			case 3:
				if (!expect_shard_map) {
					stats_error_log("was not expecting shard map");
					goto parse_err;
				} else if (is_key) {
					if (!convert_number(strval, &numval)) {
						stats_error_log("shard key was not a number: \"%s\"", strval);
						goto parse_err;

					}
					shard_count++;
					if (numval != shard_count) {
						stats_error_log("expected to see shard key %d, instead saw %d",
							  shard_count, numval);
						goto parse_err;
					}
				} else {
					if (statsrelay_list_expand(protoc->ring) == NULL) {
						stats_error_log("unable to expand list");
						goto parse_err;
					}
					if ((protoc->ring->data[protoc->ring->size - 1]  = strdup(strval)) == NULL) {
						stats_error_log("failed to copy string");
						goto parse_err;
					}
				}
			}
			is_key = !is_key;
			break;
		default:
			stats_error_log("unhandled yaml event");
			goto parse_err;
		}
		yaml_event_delete(&event);
	}

	yaml_parser_delete(&parser);
	return config;

parse_err:
	destroy_config(config);
	yaml_event_delete(&event);
	yaml_parser_delete(&parser);
	return NULL;
}
コード例 #12
0
ファイル: tcpclient.c プロジェクト: JeremyGrosser/statsrelay
int tcpclient_init(tcpclient_t *client,
		   struct ev_loop *loop,
		   void *callback_context,
		   struct proto_config *config,
		   char *host,
		   char *port,
		   char *protocol) {
	size_t len;

	client->state = STATE_INIT;
	client->loop = loop;
	client->sd = -1;
	client->addr = NULL;
	client->last_error = 0;
	client->failing = 0;
	client->config = config;
	client->socktype = SOCK_DGRAM;

	if(host == NULL) {
		stats_error_log("tcpclient_init: host is NULL\n");
		return 1;
	}
	len = strlen(host);
	if((client->host = calloc(1, len + 1)) == NULL) {
		stats_error_log("tcpclient[%s]: unable to allocate memory for host\n", client->host);
		return 1;
	}
	strncpy(client->host, host, len);

	if(port == NULL) {
		stats_error_log("tcpclient_init: port is NULL\n");
		free(client->host);
		return 1;
	}
	len = strlen(port);
	if((client->port = calloc(1, len + 1)) == NULL) {
		stats_error_log("tcpclient[%s]: unable to allocate memory for port\n", client->host);
		free(client->host);
		return 1;
	}
	strncpy(client->port, port, len);

	if(protocol == NULL) {
		// Default to TCP if protocol is not set
		if((client->protocol = calloc(1, 4)) == NULL) {
			stats_error_log("tcpclient[%s]: unable to allocate memory for protocol\n", client->host);
			free(client->host);
			free(client->port);
			return 1;
		}
		strncpy(client->protocol, "tcp", 3);
	}else{
		len = strlen(protocol);
		if((client->protocol = calloc(1, len + 1)) == NULL) {
			stats_error_log("tcpclient[%s]: unable to allocate memory for protocol\n", client->host);
			free(client->host);
			free(client->port);
			return 1;
		}
		strncpy(client->protocol, protocol, len);
	}

	strncpy(client->name, "UNRESOLVED", TCPCLIENT_NAME_LEN);

	client->callback_connect = &tcpclient_default_callback;
	client->callback_sent = &tcpclient_default_callback;
	client->callback_recv = &tcpclient_default_callback;
	client->callback_error = &tcpclient_default_callback;
	client->callback_context = callback_context;
	buffer_init(&client->send_queue);
	buffer_newsize(&client->send_queue, DEFAULT_BUFFER_SIZE);
	ev_timer_init(&client->timeout_watcher,
		      tcpclient_connect_timeout,
		      TCPCLIENT_CONNECT_TIMEOUT,
		      0);

	client->connect_watcher.started = false;
	client->read_watcher.started = false;
	client->write_watcher.started = false;
	return 0;
}
コード例 #13
0
ファイル: tcpclient.c プロジェクト: JeremyGrosser/statsrelay
int tcpclient_connect(tcpclient_t *client) {
	struct addrinfo hints;
	struct addrinfo *addr;
	int sd;

	if (client->state == STATE_CONNECTED || client->state == STATE_CONNECTING) {
		// Already connected, do nothing
		return 1;
	}

	if (client->state == STATE_BACKOFF) {
		// If backoff timer has expired, change to STATE_INIT and call recursively
		if ((time(NULL) - client->last_error) > TCPCLIENT_RETRY_TIMEOUT) {
			tcpclient_set_state(client, STATE_INIT);
			return tcpclient_connect(client);
		} else {
			return 2;
		}
	}

	if (client->state == STATE_INIT) {
		// Resolve address, create socket, set nonblocking, setup callbacks, fire connect
		if (client->config->always_resolve_dns == true && client->addr != NULL) {
			freeaddrinfo(client->addr);
			client->addr = NULL;
		}

		if (client->addr == NULL) {
			// We only know about tcp and udp, so if we get something unexpected just
			// default to tcp
			if (strncmp(client->protocol, "udp", 3) == 0) {
				client->socktype = SOCK_DGRAM;
			} else {
				client->socktype = SOCK_STREAM;
			}
			memset(&hints, 0, sizeof(hints));
			hints.ai_family = AF_UNSPEC;
			hints.ai_socktype = client->socktype;
			hints.ai_flags = AI_PASSIVE;

			if (getaddrinfo(client->host, client->port, &hints, &addr) != 0) {
				stats_error_log("tcpclient: Error resolving backend address %s: %s", client->host, gai_strerror(errno));
				client->last_error = time(NULL);
				tcpclient_set_state(client, STATE_BACKOFF);
				client->callback_error(client, EVENT_ERROR, client->callback_context, NULL, 0);
				return 3;
			}
			client->addr = addr;
			snprintf(client->name, TCPCLIENT_NAME_LEN, "%s/%s/%s", client->host, client->port, client->protocol);
		} else {
			addr = client->addr;
		}

		if ((sd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0) {
			stats_error_log("tcpclient[%s]: Unable to create socket: %s", client->name, strerror(errno));
			client->last_error = time(NULL);
			tcpclient_set_state(client, STATE_BACKOFF);
			client->callback_error(client, EVENT_ERROR, client->callback_context, NULL, 0);
			return 4;
		}
#ifdef TCP_CORK
		if (client->config->enable_tcp_cork &&
		    addr->ai_family == AF_INET &&
		    addr->ai_socktype == SOCK_STREAM &&
		    addr->ai_protocol == IPPROTO_TCP) {
			int state = 1;
			if (setsockopt(sd, IPPROTO_TCP, TCP_CORK, &state, sizeof(state))) {
				stats_error_log("failed to set TCP_CORK");
			}
		}
#endif
		client->sd = sd;

		if (fcntl(sd, F_SETFL, (fcntl(sd, F_GETFL) | O_NONBLOCK)) != 0) {
			stats_error_log("tcpclient[%s]: Unable to set socket to non-blocking: %s", client->name, strerror(errno));
			client->last_error = time(NULL);
			tcpclient_set_state(client, STATE_BACKOFF);
			close(sd);
			client->callback_error(client, EVENT_ERROR, client->callback_context, NULL, 0);
			return 5;
		}

		client->connect_watcher.started = true;
		client->connect_watcher.watcher.data = client;
		client->timeout_watcher.data = client;
		ev_io_init(&client->connect_watcher.watcher, tcpclient_connected, sd, EV_WRITE);
		ev_io_start(client->loop, &client->connect_watcher.watcher);
		ev_timer_set(&client->timeout_watcher, TCPCLIENT_CONNECT_TIMEOUT, 0);
		ev_timer_start(client->loop, &client->timeout_watcher);

		if (connect(sd, addr->ai_addr, addr->ai_addrlen) != 0 && errno != EINPROGRESS) {
			stats_error_log("tcpclient[%s]: Unable to connect: %s", client->name, strerror(errno));
			client->last_error = time(NULL);
			tcpclient_set_state(client, STATE_BACKOFF);
			ev_timer_stop(client->loop, &client->timeout_watcher);
			ev_io_stop(client->loop, &client->connect_watcher.watcher);
			close(sd);
			client->callback_error(client, EVENT_ERROR, client->callback_context, NULL, 0);
			return 6;
		}

		tcpclient_set_state(client, STATE_CONNECTING);
		return 0;
	}

	stats_error_log("tcpclient[%s]: Connect with unknown state %i", client->name, client->state);
	return 7;
}