Beispiel #1
0
static int
execute_commands(sdb_client_t *client, sdb_llist_t *commands)
{
	sdb_llist_iter_t *iter;
	int status = 0;

	iter = sdb_llist_get_iter(commands);
	if (! iter) {
		sdb_log(SDB_LOG_ERR, "Failed to iterate commands");
		return 1;
	}

	while (sdb_llist_iter_has_next(iter)) {
		sdb_object_t *obj = sdb_llist_iter_get_next(iter);

		if (sdb_client_send(client, SDB_CONNECTION_QUERY,
					(uint32_t)strlen(obj->name), obj->name) <= 0) {
			sdb_log(SDB_LOG_ERR, "Failed to send command '%s' to server",
					obj->name);
			status = 1;
			break;
		}

		/* Wait for server replies. We might get any number of log messages
		 * but eventually see the reply to the query, which is either DATA or
		 * ERROR. */
		while (42) {
			status = sdb_command_print_reply(client);
			if (status < 0) {
				sdb_log(SDB_LOG_ERR, "Failed to read reply from server");
				break;
			}

			if ((status == SDB_CONNECTION_DATA)
					|| (status == SDB_CONNECTION_ERROR))
				break;
			if (status == SDB_CONNECTION_OK) {
				/* pre 0.4 versions used OK instead of DATA */
				sdb_log(SDB_LOG_WARNING, "Received unexpected OK status from "
						"server in response to a QUERY (expected DATA); "
						"assuming we're talking to an old server");
				break;
			}
		}

		if ((status != SDB_CONNECTION_OK) && (status != SDB_CONNECTION_DATA))
			break; /* error */
	}

	sdb_llist_iter_destroy(iter);
	return status;
} /* execute_commands */
Beispiel #2
0
static int
mock_collect(sdb_object_t *user_data)
{
	size_t i;
	int check;

	if (SDB_OBJ_WRAPPER(user_data)->data != MAGIC_DATA) {
		sdb_log(SDB_LOG_ERR, "mock::plugin: Invalid user data %p "
				"passed to collect", SDB_OBJ_WRAPPER(user_data)->data);
		exit(1);
	}

	for (i = 0; i < SDB_STATIC_ARRAY_LEN(hostnames); ++i) {
		if ((check = sdb_plugin_store_host(hostnames[i], sdb_gettime()))) {
			sdb_log(SDB_LOG_ERR, "mock::plugin: Failed to store host: "
					"status %d", check);
			exit(1);
		}
	}
	for (i = 0; i < SDB_STATIC_ARRAY_LEN(metrics); ++i) {
		if ((check = sdb_plugin_store_metric(metrics[i].hostname,
						metrics[i].metric, &metrics[i].store,
						sdb_gettime()))) {
			sdb_log(SDB_LOG_ERR, "mock::plugin: Failed to store metric: "
					"status %d", check);
			exit(1);
		}
	}
	for (i = 0; i < SDB_STATIC_ARRAY_LEN(services); ++i) {
		if ((check = sdb_plugin_store_service(services[i].hostname,
						services[i].service, sdb_gettime()))) {
			sdb_log(SDB_LOG_ERR, "mock::plugin: Failed to store service: "
					"status %d", check);
			exit(1);
		}
	}
	for (i = 0; i < SDB_STATIC_ARRAY_LEN(attributes); ++i) {
		sdb_data_t datum = { SDB_TYPE_STRING, { .string = NULL } };
		datum.data.string = strdup(attributes[i].value);

		if ((check = sdb_plugin_store_attribute(attributes[i].hostname,
						attributes[i].name, &datum, sdb_gettime()))) {
			sdb_log(SDB_LOG_ERR, "mock::plugin: Failed to store attribute: "
					"status %d", check);
			exit(1);
		}

		free(datum.data.string);
	}
	return 0;
} /* mock_collect */
Beispiel #3
0
int
sdb_unixsock_client_connect(sdb_unixsock_client_t *client)
{
    struct sockaddr_un sa;
    int fd;

    if ((! client) || (! client->path))
        return -1;

    memset(&sa, 0, sizeof(sa));

    if (client->fh)
        fclose(client->fh);

    fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
    if (fd < 0) {
        char errbuf[1024];
        sdb_log(SDB_LOG_ERR, "unixsock: Failed to open socket: %s",
                sdb_strerror(errno, errbuf, sizeof(errbuf)));
        return -1;
    }

    sa.sun_family = AF_UNIX;
    strncpy(sa.sun_path, client->path, sizeof(sa.sun_path));
    sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';

    if (connect(fd, (struct sockaddr *)&sa, sizeof(sa))) {
        char errbuf[1024];
        sdb_log(SDB_LOG_ERR, "unixsock: Failed to connect to %s: %s",
                sa.sun_path, sdb_strerror(errno, errbuf, sizeof(errbuf)));
        close(fd);
        return -1;
    }

    client->fh = fdopen(fd, "r+");
    if (! client->fh) {
        char errbuf[1024];
        sdb_log(SDB_LOG_ERR, "unixsock: Failed to open I/O "
                "stream for %s: %s", sa.sun_path,
                sdb_strerror(errno, errbuf, sizeof(errbuf)));
        close(fd);
        return -1;
    }

    /* enable line-buffering */
    setvbuf(client->fh, NULL, _IOLBF, 0);

    client->shutdown = 0;
    return 0;
} /* sdb_unixsock_client_connect */
Beispiel #4
0
static sdb_timeseries_info_t *
mock_describe_ts(const char *id, sdb_object_t *user_data)
{
	if (*id != '/') {
		sdb_log(SDB_LOG_ERR, "mock::timeseries: Invalid time-series %s", id);
		exit(1);
	}

	if (SDB_OBJ_WRAPPER(user_data)->data != MAGIC_DATA) {
		sdb_log(SDB_LOG_ERR, "mock::timeseries: Invalid user data %p "
				"passed to collect", SDB_OBJ_WRAPPER(user_data)->data);
		exit(1);
	}

	return sdb_timeseries_info_create(SDB_STATIC_ARRAY_LEN(names), names);
} /* mock_describe_ts */
Beispiel #5
0
static void
connection_destroy(sdb_object_t *obj)
{
	sdb_conn_t *conn;
	size_t len;

	assert(obj);
	conn = CONN(obj);

	conn->ready = 0;

	if (conn->finish)
		conn->finish(conn);
	conn->finish = NULL;

	if (conn->buf) {
		len = sdb_strbuf_len(conn->buf);
		if (len)
			sdb_log(SDB_LOG_INFO, "frontend: Discarding incomplete command "
					"(%zu byte%s left in buffer)", len, len == 1 ? "" : "s");
	}

	if (conn->client_addr.ss_family == AF_UNIX) {
		sdb_log(SDB_LOG_DEBUG, "frontend: Closing connection %s from peer %s",
				obj->name, conn->username);
	}
	else {
		char host[1024] = "<unknown>", port[32] = "";
		getnameinfo((struct sockaddr *)&conn->client_addr,
				conn->client_addr_len, host, sizeof(host), port, sizeof(port),
				NI_NUMERICHOST | NI_NUMERICSERV);
		sdb_log(SDB_LOG_DEBUG, "frontend: Closing connection %s from peer %s "
				"at %s:%s", obj->name, conn->username, host, port);
	}
	sdb_connection_close(conn);

	if (conn->username)
		free(conn->username);
	conn->username = NULL;

	sdb_strbuf_destroy(conn->buf);
	conn->buf = NULL;
	sdb_strbuf_destroy(conn->errbuf);
	conn->errbuf = NULL;
} /* connection_destroy */
Beispiel #6
0
static bool
rrdcached_connect(char *addr)
{
#ifdef HAVE_RRD_CLIENT_H
	rrd_clear_error();
	if (! rrdc_is_connected(addr)) {
		if (rrdc_connect(addr)) {
			sdb_log(SDB_LOG_ERR, "Failed to connectd to RRDCacheD at %s: %s",
					addr, rrd_get_error());
			return 0;
		}
	}
#else
	sdb_log(SDB_LOG_ERR, "Callback called with RRDCacheD address "
			"but your build of SysDB does not support that");
	return 0;
#endif
	return 1;
} /* rrdcached_connect */
Beispiel #7
0
static int
mock_shutdown(sdb_object_t *user_data)
{
	if (SDB_OBJ_WRAPPER(user_data)->data != MAGIC_DATA) {
		sdb_log(SDB_LOG_ERR, "mock::plugin: Invalid user data %p "
				"passed to shutdown", SDB_OBJ_WRAPPER(user_data)->data);
		exit(1);
	}
	return 0;
} /* mock_shutdown */
Beispiel #8
0
static int
sdb_unixsock_client_process_one_line(sdb_unixsock_client_t *client,
                                     char *line, sdb_unixsock_client_data_cb callback,
                                     sdb_object_t *user_data, const char *delim,
                                     int column_count, int *types)
{
    sdb_data_t data[column_count];
    char *orig_line = line;

    int i;

    assert(column_count > 0);

    for (i = 0; i < column_count; ++i) {
        char *next;

        if (! line) { /* this must not happen */
            sdb_log(SDB_LOG_ERR, "unixsock: Unexpected EOL while "
                    "parsing line (expected %i columns delimited by '%s'; "
                    "got %i): %s", column_count, delim,
                    /* last line number */ i, orig_line);
            return -1;
        }

        if ((delim[0] == '\0') || (delim[1] == '\0'))
            next = strchr(line, (int)delim[0]);
        else
            next = strpbrk(line, delim);

        if (next) {
            *next = '\0';
            ++next;
        }

        if (types && (types[i] != SDB_TYPE_STRING)) {
            if (sdb_data_parse(line, types[i], &data[i]))
                return -1;
        }
        else {
            /* Avoid a copy in common cases */
            data[i].type = SDB_TYPE_STRING;
            data[i].data.string = line;
        }

        line = next;
    }

    if (callback(client, (size_t)column_count, data, user_data))
        return -1;

    for (i = 0; i < column_count; ++i)
        if (types && (types[i] != SDB_TYPE_STRING))
            sdb_data_free_datum(&data[i]);
    return 0;
} /* sdb_unixsock_client_process_one_line */
Beispiel #9
0
static int
copy_data(sdb_timeseries_t *ts, rrd_value_t *data, time_t step,
		size_t ds_cnt, char **ds_names)
{
	time_t start = SDB_TIME_TO_SECS(ts->start);
	time_t end = SDB_TIME_TO_SECS(ts->end);
	time_t t;

	ssize_t ds_target[ds_cnt];
	size_t i, j;

	/* determine the target index of each data-source,
	 * or -1 if the data-source isn't wanted */
	for (i = 0; i < ds_cnt; i++) {
		ds_target[i] = -1;
		for (j = 0; j < ts->data_names_len; j++) {
			if (!strcmp(ds_names[i], ts->data_names[j])) {
				ds_target[i] = j;
				break;
			}
		}
	}

	/* check if any wanted data-source is missing */
	for (i = 0; i < ts->data_names_len; i++) {
		bool found = false;

		for (j = 0; j < ds_cnt; j++) {
			if (!strcmp(ts->data_names[i], ds_names[j])) {
				found = true;
				break;
			}
		}
		if (!found) {
			sdb_log(SDB_LOG_ERR, "Requested data-source '%s' not found",
					ts->data_names[i]);
			return -1;
		}
	}

	for (t = start; t <= end; t += step) {
		i = (size_t)(t - start) / (size_t)step;

		for (j = 0; j < ds_cnt; ++j) {
			if (ds_target[j] >= 0) {
				size_t x = (size_t)ds_target[j];
				ts->data[x][i].timestamp = SECS_TO_SDB_TIME(t);
				ts->data[x][i].value = *data;
			}
			++data;
		}
	}
	return 0;
}
Beispiel #10
0
int
sdb_module_init(sdb_plugin_info_t *info)
{
	sdb_object_t *user_data;

	sdb_plugin_set_info(info, SDB_PLUGIN_INFO_DESC, "a mock timeseries fetcher");
	sdb_plugin_set_info(info, SDB_PLUGIN_INFO_COPYRIGHT,
			"Copyright (C) 2012 Sebastian 'tokkee' Harl <*****@*****.**>");
	sdb_plugin_set_info(info, SDB_PLUGIN_INFO_LICENSE, "BSD");
	sdb_plugin_set_info(info, SDB_PLUGIN_INFO_VERSION, SDB_VERSION);
	sdb_plugin_set_info(info, SDB_PLUGIN_INFO_PLUGIN_VERSION, SDB_VERSION);

	user_data = sdb_object_create_wrapper("mock_data", MAGIC_DATA, NULL);
	if (! user_data) {
		sdb_log(SDB_LOG_ERR, "mock::plugin: Failed to allocate user data");
		exit(1);
	}
	sdb_plugin_register_timeseries_fetcher("mock", &mock_fetcher, user_data);
	sdb_object_deref(user_data);
	return 0;
} /* sdb_module_init */
Beispiel #11
0
int
sdb_unixsock_client_send(sdb_unixsock_client_t *client,
                         const char *msg)
{
    int status;

    if ((! client) || (! client->fh))
        return -1;

    if (client->shutdown & SDB_SHUT_WR) /* reconnect */
        sdb_unixsock_client_connect(client);

    status = fprintf(client->fh, "%s\r\n", msg);
    if (status < 0) {
        char errbuf[1024];
        sdb_log(SDB_LOG_ERR, "unixsock: Failed to write to "
                "socket (%s): %s", client->path,
                sdb_strerror(errno, errbuf, sizeof(errbuf)));
        return status;
    }
    return status;
} /* sdb_unixsock_client_send */
Beispiel #12
0
char *
sdb_unixsock_client_recv(sdb_unixsock_client_t *client,
                         char *buffer, size_t buflen)
{
    char *tmp;

    if ((! client) || (! client->fh) || (! buffer))
        return NULL;

    if (client->shutdown & SDB_SHUT_RD) /* reconnect */
        sdb_unixsock_client_connect(client);

    tmp = NULL;
    while (tmp == NULL) {
        errno = 0;
        tmp = fgets(buffer, (int)buflen - 1, client->fh);
        if (! tmp) {
            if ((errno == EAGAIN) || (errno == EINTR))
                continue;

            if (! feof(client->fh)) {
                char errbuf[1024];
                sdb_log(SDB_LOG_ERR, "unixsock: Failed to read "
                        "from socket (%s): %s", client->path,
                        sdb_strerror(errno, errbuf, sizeof(errbuf)));
            }
            return NULL;
        }
    }
    buffer[buflen - 1] = '\0';

    buflen = strlen(buffer);
    while (buflen && ((buffer[buflen - 1] == '\n') || (buffer[buflen - 1] == '\r'))) {
        buffer[buflen - 1] = '\0';
        --buflen;
    }
    return buffer;
} /* sdb_unixsock_client_recv */
Beispiel #13
0
int
sdb_unixsock_client_process_lines(sdb_unixsock_client_t *client,
                                  sdb_unixsock_client_data_cb callback, sdb_object_t *user_data,
                                  long int max_lines, const char *delim, int n_cols, ...)
{
    int *types = NULL;
    int success = 0;

    if ((! client) || (! client->fh) || (! callback))
        return -1;

    if (n_cols > 0) {
        va_list ap;
        int i;

        types = calloc((size_t)n_cols, sizeof(*types));
        if (! types)
            return -1;

        va_start(ap, n_cols);

        for (i = 0; i < n_cols; ++i) {
            types[i] = va_arg(ap, int);

            if ((types[i] < 1) || (types[i] > SDB_TYPE_BINARY)) {
                sdb_log(SDB_LOG_ERR, "unixsock: Unknown column "
                        "type %i while processing response from the "
                        "UNIX socket @ %s.", types[i], client->path);
                va_end(ap);
                free(types);
                return -1;
            }
        }

        va_end(ap);
    }
Beispiel #14
0
int
main(int argc, char **argv)
{
	const char *host = NULL;

	char *homedir;
	char hist_file[1024] = "";

	sdb_input_t input = SDB_INPUT_INIT;
	sdb_llist_t *commands = NULL;

	while (42) {
		int opt = getopt(argc, argv, "H:U:c:C:K:A:hV");

		if (-1 == opt)
			break;

		switch (opt) {
			case 'H':
				host = optarg;
				break;
			case 'U':
				input.user = optarg;
				break;

			case 'c':
				{
					sdb_object_t *obj;

					if (! commands)
						commands = sdb_llist_create();
					if (! commands) {
						sdb_log(SDB_LOG_ERR, "Failed to create list object");
						exit(1);
					}

					if (! (obj = sdb_object_create_T(optarg, sdb_object_t))) {
						sdb_log(SDB_LOG_ERR, "Failed to create object");
						exit(1);
					}
					if (sdb_llist_append(commands, obj)) {
						sdb_log(SDB_LOG_ERR, "Failed to append command to list");
						sdb_object_deref(obj);
						exit(1);
					}
					sdb_object_deref(obj);
				}
				break;

			case 'C':
				ssl_options.cert_file = optarg;
				break;
			case 'K':
				ssl_options.key_file = optarg;
				break;
			case 'A':
				ssl_options.ca_file = optarg;
				break;

			case 'h':
				exit_usage(argv[0], 0);
				break;
			case 'V':
				exit_version();
				break;
			default:
				exit_usage(argv[0], 1);
		}
	}

	if (optind < argc)
		exit_usage(argv[0], 1);

	if (! host)
		host = DEFAULT_SOCKET;
	if (! input.user)
		input.user = sdb_get_current_user();
	else
		input.user = strdup(input.user);
	if (! input.user)
		exit(1);

	if (sdb_ssl_init())
		exit(1);

	input.client = sdb_client_create(host);
	if (! input.client) {
		sdb_log(SDB_LOG_ERR, "Failed to create client object");
		sdb_input_reset(&input);
		exit(1);
	}
	canonicalize_ssl_options();
	if (sdb_client_set_ssl_options(input.client, &ssl_options)) {
		sdb_log(SDB_LOG_ERR, "Failed to apply SSL options");
		sdb_input_reset(&input);
		sdb_ssl_free_options(&ssl_options);
		exit(1);
	}
	sdb_ssl_free_options(&ssl_options);
	if (sdb_client_connect(input.client, input.user)) {
		sdb_log(SDB_LOG_ERR, "Failed to connect to SysDBd");
		sdb_input_reset(&input);
		exit(1);
	}

	if (commands) {
		int status = execute_commands(input.client, commands);
		sdb_llist_destroy(commands);
		sdb_input_reset(&input);
		if ((status != SDB_CONNECTION_OK) && (status != SDB_CONNECTION_DATA))
			exit(1);
		exit(0);
	}

	sdb_log(SDB_LOG_INFO, "SysDB client "SDB_CLIENT_VERSION_STRING
			SDB_CLIENT_VERSION_EXTRA" (libsysdbclient %s%s)",
			sdb_client_version_string(), sdb_client_version_extra());
	sdb_command_print_server_version(&input);
	printf("\n");

	using_history();

	if ((homedir = sdb_get_homedir())) {
		snprintf(hist_file, sizeof(hist_file) - 1,
				"%s/.sysdb_history", homedir);
		hist_file[sizeof(hist_file) - 1] = '\0';
		free(homedir);
		homedir = NULL;

		errno = 0;
		if (read_history(hist_file) && (errno != ENOENT)) {
			char errbuf[1024];
			sdb_log(SDB_LOG_WARNING, "Failed to load history (%s): %s",
					hist_file, sdb_strerror(errno, errbuf, sizeof(errbuf)));
		}
	}

	input.input = sdb_strbuf_create(2048);
	sdb_input_init(&input);
	sdb_input_mainloop();

	sdb_client_shutdown(input.client, SHUT_WR);
	while (! sdb_client_eof(input.client)) {
		/* wait for remaining data to arrive */
		sdb_command_print_reply(input.client);
	}

	if (hist_file[0] != '\0') {
		errno = 0;
		if (write_history(hist_file)) {
			char errbuf[1024];
			sdb_log(SDB_LOG_WARNING, "Failed to store history (%s): %s",
					hist_file, sdb_strerror(errno, errbuf, sizeof(errbuf)));
		}
	}

	sdb_input_reset(&input);
	sdb_ssl_shutdown();
	return 0;
} /* main */
Beispiel #15
0
static int
connection_init(sdb_object_t *obj, va_list ap)
{
	sdb_conn_t *conn;
	int sock_fd;
	int sock_fl;

	assert(obj);
	conn = CONN(obj);

	sock_fd = va_arg(ap, int);

	conn->buf = sdb_strbuf_create(/* size = */ 128);
	if (! conn->buf) {
		sdb_log(SDB_LOG_ERR, "frontend: Failed to allocate a read buffer "
				"for a new connection");
		return -1;
	}
	conn->errbuf = sdb_strbuf_create(0);
	if (! conn->errbuf) {
		sdb_log(SDB_LOG_ERR, "frontend: Failed to allocate an error buffer "
				"for a new connection");
		return -1;
	}

	conn->client_addr_len = sizeof(conn->client_addr);
	conn->fd = accept(sock_fd, (struct sockaddr *)&conn->client_addr,
			&conn->client_addr_len);

	if (conn->fd < 0) {
		char buf[1024];
		sdb_log(SDB_LOG_ERR, "frontend: Failed to accept remote "
				"connection: %s", sdb_strerror(errno,
					buf, sizeof(buf)));
		return -1;
	}

	/* update the object name */
	snprintf(obj->name + strlen(CONN_FD_PREFIX),
			strlen(CONN_FD_PLACEHOLDER), "%i", conn->fd);

	/* defaults */
	conn->read = conn_read;
	conn->write = conn_write;
	conn->finish = NULL;
	conn->ssl_session = NULL;

	sock_fl = fcntl(conn->fd, F_GETFL);
	if (fcntl(conn->fd, F_SETFL, sock_fl | O_NONBLOCK)) {
		char buf[1024];
		sdb_log(SDB_LOG_ERR, "frontend: Failed to switch connection conn#%i "
				"to non-blocking mode: %s", conn->fd,
				sdb_strerror(errno, buf, sizeof(buf)));
		return -1;
	}

	conn->username = NULL;
	conn->ready = 0;

	sdb_log(SDB_LOG_DEBUG, "frontend: Accepted connection on fd=%i",
			conn->fd);

	conn->cmd = SDB_CONNECTION_IDLE;
	conn->cmd_len = 0;
	conn->skip_len = 0;
	return 0;
} /* connection_init */
Beispiel #16
0
static sdb_timeseries_info_t *
sdb_rrd_describe(const char *id, sdb_object_t *user_data)
{
	rrd_info_t *info, *iter;
	char filename[strlen(id) + 1];
	sdb_timeseries_info_t *ts_info;

	strncpy(filename, id, sizeof(filename));

	if (user_data) {
		/* -> use RRDCacheD */
		char *addr = SDB_OBJ_WRAPPER(user_data)->data;

		if (! rrdcached_connect(addr))
			return NULL;

#ifdef HAVE_RRD_CLIENT_H
		/* TODO: detect and use rrdc_info if possible */
		sdb_log(SDB_LOG_ERR, "DESCRIBE not yet supported via RRDCacheD");
		return NULL;
#endif
	}
	else {
		rrd_clear_error();
		info = rrd_info_r(filename);
	}
	if (! info) {
		sdb_log(SDB_LOG_ERR, "Failed to extract header information from '%s': %s",
				filename, rrd_get_error());
		return NULL;
	}

	ts_info = calloc(1, sizeof(*ts_info));
	if (! ts_info) {
		sdb_log(SDB_LOG_ERR, "Failed to allocate memory");
		rrd_info_free(info);
		return NULL;
	}

	for (iter = info; iter != NULL; iter = iter->next) {
		size_t len, n, m;
		char *ds_name;
		char **tmp;

		/* Parse the DS name. The raw value is not exposed via the rrd_info
		 * interface. */
		n = strlen("ds[");
		if (strncmp(iter->key, "ds[", n))
			continue;

		len = strlen(iter->key);
		m = strlen("].index");
		if ((len < m) || strcmp(iter->key + len - m, "].index"))
			continue;

		ds_name = iter->key + n;
		len -= n;
		ds_name[len - m] = '\0';

		/* Append the new datum. */
		tmp = realloc(ts_info->data_names,
				(ts_info->data_names_len + 1) * sizeof(*ts_info->data_names));
		if (! tmp) {
			sdb_log(SDB_LOG_ERR, "Failed to allocate memory");
			sdb_timeseries_info_destroy(ts_info);
			rrd_info_free(info);
			return NULL;
		}

		ts_info->data_names = tmp;
		ts_info->data_names[ts_info->data_names_len] = strdup(ds_name);
		if (! ts_info->data_names[ts_info->data_names_len]) {
			sdb_log(SDB_LOG_ERR, "Failed to allocate memory");
			sdb_timeseries_info_destroy(ts_info);
			rrd_info_free(info);
			return NULL;
		}
		ts_info->data_names_len++;
	}
	rrd_info_free(info);

	return ts_info;
} /* sdb_rrd_describe */
Beispiel #17
0
static sdb_timeseries_t *
sdb_rrd_fetch(const char *id, sdb_timeseries_opts_t *opts,
		sdb_object_t *user_data)
{
	sdb_timeseries_t *ts;

	time_t start = (time_t)SDB_TIME_TO_SECS(opts->start);
	time_t end = (time_t)SDB_TIME_TO_SECS(opts->end);

	unsigned long step = 0;
	unsigned long ds_cnt = 0;
	unsigned long val_cnt = 0;
	char **ds_namv = NULL;
	rrd_value_t *data = NULL;

	if (user_data) {
		/* -> use RRDCacheD */
		char *addr = SDB_OBJ_WRAPPER(user_data)->data;

		if (! rrdcached_connect(addr))
			return NULL;

#ifdef HAVE_RRD_CLIENT_H
		if (rrdc_flush(id)) {
			sdb_log(SDB_LOG_ERR, "Failed to flush '%s' through RRDCacheD: %s",
					id, rrd_get_error());
			return NULL;
		}
#endif
	}

#define FREE_RRD_DATA() \
	do { \
		size_t i; \
		for (i = 0; i < ds_cnt; ++i) \
			rrd_freemem(ds_namv[i]); \
		rrd_freemem(ds_namv); \
		rrd_freemem(data); \
	} while (0)

	/* limit to about 1000 data-points for now
	 * TODO: make this configurable */
	step = (end - start) / 1000;

	if (rrd_fetch_r(id, "AVERAGE", &start, &end, &step,
				&ds_cnt, &ds_namv, &data)) {
		char errbuf[1024];
		sdb_strerror(errno, errbuf, sizeof(errbuf));
		sdb_log(SDB_LOG_ERR, "Failed to fetch data from %s: %s", id, errbuf);
		return NULL;
	}

	val_cnt = (unsigned long)(end - start) / step;

	/* RRDtool does not support fetching specific data-sources, so we'll have
	 * to filter the requested ones after fetching them all */
	if (opts->data_names && opts->data_names_len)
		ts = sdb_timeseries_create(opts->data_names_len,
				(const char * const *)opts->data_names, val_cnt);
	else
		ts = sdb_timeseries_create(ds_cnt, (const char * const *)ds_namv, val_cnt);
	if (! ts) {
		char errbuf[1024];
		sdb_strerror(errno, errbuf, sizeof(errbuf));
		sdb_log(SDB_LOG_ERR, "Failed to allocate time-series object: %s", errbuf);
		FREE_RRD_DATA();
		return NULL;
	}

	ts->start = SECS_TO_SDB_TIME(start + (time_t)step);
	ts->end = SECS_TO_SDB_TIME(end);

	if (copy_data(ts, data, (time_t)step, (size_t)ds_cnt, ds_namv) < 0) {
		FREE_RRD_DATA();
		sdb_timeseries_destroy(ts);
		return NULL;
	}

	FREE_RRD_DATA();
	return ts;
} /* sdb_rrd_fetch */