From: Sebastien Dugue Date: Tue, 28 Oct 2014 14:37:03 +0000 (+0100) Subject: rrd_daemon: Add LIST command to list the rrd databases X-Git-Tag: v1.7.0~36^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c3ca1affea38fe98c099f3a12c2c5e5bc8ad41a1;p=thirdparty%2Frrdtool-1.x.git rrd_daemon: Add LIST command to list the rrd databases --- diff --git a/src/Makefile.am b/src/Makefile.am index 9c60f42f..100eed73 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,7 +51,9 @@ RRD_C_FILES = \ rrd_fetch.c \ rrd_fetch_cb.c \ rrd_resize.c \ - rrd_tune.c + rrd_tune.c \ + rrd_list.c + if BUILD_RRDGRAPH RRD_C_FILES += rrd_graph.c \ diff --git a/src/librrd.sym b/src/librrd.sym index 3f304b7c..17bb693c 100644 --- a/src/librrd.sym +++ b/src/librrd.sym @@ -36,6 +36,8 @@ rrd_last rrd_last_r rrd_lastupdate rrd_lastupdate_r +rrd_list +rrd_list_r rrd_lock rrd_mkdir_p rrd_new_context diff --git a/src/rrd.h b/src/rrd.h index 6ea20099..1b543770 100644 --- a/src/rrd.h +++ b/src/rrd.h @@ -165,6 +165,7 @@ struct rrd_t; rrd_info_t * data); void rrd_info_free( rrd_info_t *); + char *rrd_list(int, char **); int rrd_update( int, char **); diff --git a/src/rrd_client.c b/src/rrd_client.c index b6ee4b11..9cf82d65 100644 --- a/src/rrd_client.c +++ b/src/rrd_client.c @@ -1015,6 +1015,44 @@ int rrdc_forget (const char *filename) { return rrdc_filebased_command("forget", filename); } +int rrdc_flushall (void) /* {{{ */ +{ + char buffer[RRD_CMD_MAX]; + char *buffer_ptr; + size_t buffer_free; + size_t buffer_size; + rrdc_response_t *res; + int status; + + memset (buffer, 0, sizeof (buffer)); + buffer_ptr = &buffer[0]; + buffer_free = sizeof (buffer); + + status = buffer_add_string ("flushall", &buffer_ptr, &buffer_free); + + if (status != 0) + return (ENOBUFS); + + pthread_mutex_lock (&lock); + + assert (buffer_free < sizeof (buffer)); + buffer_size = sizeof (buffer) - buffer_free; + assert (buffer[buffer_size - 1] == ' '); + buffer[buffer_size - 1] = '\n'; + + res = NULL; + status = request (buffer, buffer_size, &res); + pthread_mutex_unlock (&lock); + + if (status != 0) + return (status); + + status = res->status; + response_free (res); + + return (status); +} /* }}} int rrdc_flushall */ + rrd_info_t * rrdc_info (const char *filename) /* {{{ */ { char buffer[RRD_CMD_MAX]; @@ -1126,6 +1164,106 @@ rrd_info_t * rrdc_info (const char *filename) /* {{{ */ return (data); } /* }}} int rrdc_info */ +char *rrdc_list(const char *dirname) +{ + char buffer[RRD_CMD_MAX]; + char *buffer_ptr; + size_t buffer_free; + size_t buffer_size; + rrdc_response_t *res; + int status; + unsigned int i; + char *list = NULL; + int list_len = 0; + + if (dirname == NULL) { + rrd_set_error ("rrdc_info: no directory name"); + return (NULL); + } + + memset (buffer, 0, sizeof (buffer)); + buffer_ptr = &buffer[0]; + buffer_free = sizeof (buffer); + + status = buffer_add_string ("list", &buffer_ptr, &buffer_free); + + if (status != 0) { + rrd_set_error ("rrdc_list: out of memory"); + return NULL; + } + + status = buffer_add_string (dirname, &buffer_ptr, &buffer_free); + if (status != 0) + { + rrd_set_error ("rrdc_list: out of memory"); + return (NULL); + } + + + assert (buffer_free < sizeof (buffer)); + buffer_size = sizeof (buffer) - buffer_free; + assert (buffer[buffer_size - 1] == ' '); + buffer[buffer_size - 1] = '\n'; + + res = NULL; + + pthread_mutex_lock (&lock); + status = request (buffer, buffer_size, &res); + pthread_mutex_unlock (&lock); + + if (status != 0) { + rrd_set_error ("rrdcached: %s", res->message); + goto out_free_res; + } + + /* Handle the case where the list is empty, allocate + * a single byte zeroed string. + */ + if (res->lines_num == 0) { + list = calloc(1, 1); + + if (!list) { + rrd_set_error ("rrdc_list: out of memory"); + goto out_free_res; + } + + goto out_free_res; + } + + for (i = 0; i < res->lines_num ; i++ ) { + int len; + char *buf; + + len = strlen(res->lines[i]); + buf = realloc(list, list_len + len + 2); + + if (!buf) { + rrd_set_error ("rrdc_list: out of memory"); + + if (list) { + free(list); + list = NULL; + } + + goto out_free_res; + } + + if (!list) + buf[0] = '\0'; + + strcat(buf, res->lines[i]); + strcat(buf, "\n"); + + list = buf; + list_len += (len + 1); + } + +out_free_res: + response_free (res); + + return list; +} + time_t rrdc_last (const char *filename) /* {{{ */ { char buffer[RRD_CMD_MAX]; @@ -1633,6 +1771,36 @@ int rrdc_flush_if_daemon (const char *opt_daemon, const char *filename) /* {{{ * return status; } /* }}} int rrdc_flush_if_daemon */ +/* convenience function; if there is a daemon specified, or if we can + * detect one from the environment, then flush the file. Otherwise, no-op + */ +int rrdc_flushall_if_daemon (const char *opt_daemon) /* {{{ */ +{ + int status = 0; + + rrdc_connect(opt_daemon); + + if (rrdc_is_connected(opt_daemon)) + { + rrd_clear_error(); + status = rrdc_flushall (); + + if (status != 0 && !rrd_test_error()) + { + if (status > 0) + { + rrd_set_error("rrdc_flushall failed: %s", rrd_strerror(status)); + } + else if (status < 0) + { + rrd_set_error("rrdc_flushall failed with status %i.", status); + } + } + } /* if (rrdc_is_connected(..)) */ + + return status; +} /* }}} int rrdc_flush_if_daemon */ + int rrdc_stats_get (rrdc_stats_t **ret_stats) /* {{{ */ { diff --git a/src/rrd_client.h b/src/rrd_client.h index 28ab5841..470a7e79 100644 --- a/src/rrd_client.h +++ b/src/rrd_client.h @@ -51,6 +51,7 @@ int rrdc_update (const char *filename, int values_num, const char * const *values); rrd_info_t * rrdc_info (const char *filename); +char *rrdc_list(const char *dirname); time_t rrdc_last (const char *filename); time_t rrdc_first (const char *filename, int rraindex); int rrdc_create (const char *filename, @@ -72,6 +73,8 @@ int rrdc_create_r2 (const char *filename, int rrdc_flush (const char *filename); int rrdc_forget (const char *filename); int rrdc_flush_if_daemon (const char *opt_daemon, const char *filename); +int rrdc_flushall (void); +int rrdc_flushall_if_daemon (const char *opt_daemon); int rrdc_fetch (const char *filename, const char *cf, diff --git a/src/rrd_daemon.c b/src/rrd_daemon.c index 4ceddfcc..3bae3fba 100644 --- a/src/rrd_daemon.c +++ b/src/rrd_daemon.c @@ -105,6 +105,7 @@ #include #include #include +#include #ifdef HAVE_LIBWRAP #include @@ -1229,7 +1230,7 @@ static int check_file_access (const char *file, listen_socket_t *sock) /* {{{ */ if (*file != '/') return 1; /* file must be of the format base + "/" + <1+ char filename> */ - if (strlen(file) < _config_base_dir_len + 2) return 0; + if (strlen(file) < _config_base_dir_len + 1) return 0; if (strncmp(file, config_base_dir, _config_base_dir_len) != 0) return 0; if (*(file + _config_base_dir_len) != '/') return 0; @@ -2385,6 +2386,103 @@ done: return rc; } /* }}} static int handle_request_create */ +static int handle_request_list (HANDLER_PROTO) /* {{{ */ +{ + char *filename; + char fullpath[PATH_MAX]; + int status; + DIR *dir; + struct dirent *entry; + + if (config_base_dir == NULL) { + return send_response(sock, RESP_ERR, "No base directory defined\n"); + } + + /* Get pathname */ + status = buffer_get_field(&buffer, &buffer_size, &filename); + + if (status != 0) + return syntax_error(sock,cmd); + + /* get full pathname */ + snprintf(fullpath, PATH_MAX, "%s%s%s", + config_base_dir, (filename[0] == '/') ? "" : "/", filename); + + /* Prevent moving up the directory tree */ + if (strstr(fullpath, "..")) + send_response(sock, RESP_ERR, "%s\n", rrd_strerror(EACCES)); + + /* if filename contains wildcards, then use glob() */ + if (strchr(fullpath, '*') || strchr(fullpath, '?')) { + glob_t buf; + unsigned int i; + + if (glob(fullpath, 0, NULL, &buf)) { + globfree(&buf); + goto out_send_response; + } + + for (i = 0; i < buf.gl_pathc; i++) { + char *ptr; + + if (!check_file_access(buf.gl_pathv[i], sock)) + return send_response(sock, RESP_ERR, + "Cannot read: %s\n", buf.gl_pathv[i]); + + ptr = strrchr(buf.gl_pathv[i], '/'); + + if (ptr != NULL) + add_response_info(sock, "%s\n", ptr + 1); + } + + globfree(&buf); + goto out_send_response; + } + + if (!check_file_access(fullpath, sock)) + return send_response(sock, RESP_ERR, "Cannot read: %s\n", fullpath); + + /* If filename matches an RRD file, then return it */ + if (strstr(fullpath, ".rrd")) { + struct stat st; + char *ptr; + + if (!stat(fullpath, &st) && S_ISREG(st.st_mode)) { + ptr = strrchr(fullpath, '/'); + + if (ptr) + add_response_info(sock, "%s\n", ptr + 1); + } + + goto out_send_response; + } + + dir = opendir(fullpath); + + if (dir == NULL) { + return send_response(sock, RESP_ERR, + "Failed to open directory %s : %s\n", + fullpath, strerror(errno)); + } + + while ((entry = readdir(dir)) != NULL) { + + if ((strcmp(entry->d_name, ".") == 0) || + (strcmp(entry->d_name, "..") == 0)) { + continue; + } + + add_response_info(sock, "%s\n", entry->d_name); + } + closedir(dir); + +out_send_response: + send_response(sock, RESP_OK, "RRDs\n"); + + return (0); +} /* }}} int handle_request_list */ + + /* start "BATCH" processing */ static int batch_start (HANDLER_PROTO) /* {{{ */ { @@ -2594,6 +2692,16 @@ static command_t list_of_commands[] = { /* {{{ */ "not acceptable) and the step is in seconds (default is 300).\n" "The DS and RRA definitions are as for the 'rrdtool create' command.\n" }, + { + "LIST", + handle_request_list, + CMD_CONTEXT_CLIENT, + "LIST\n", + "This command lists the RRD files in the storage base directory.\n" + "Note that this is the list of RRD files on storage as of the last update.\n" + "There may be pending updates in the queue, so a FLUSH may have to be run\n" + "beforehand.\n" + }, { "QUIT", handle_request_quit, diff --git a/src/rrd_list.c b/src/rrd_list.c new file mode 100644 index 00000000..bb583655 --- /dev/null +++ b/src/rrd_list.c @@ -0,0 +1,120 @@ + +#include + +#include "rrd_tool.h" +#include "rrd_client.h" + +char *rrd_list_r(char *dirname); +char *rrd_list(int argc, char **argv); + +char *rrd_list_r(char *dirname) +{ + printf("rrd_list_r not implemented yet\n"); + return NULL; +} + +char *rrd_list(int argc, char **argv) +{ + char *opt_daemon = NULL; + int status; + int flushfirst = 1; + char *list; + + static struct optparse_long long_options[] = { + {"daemon", 'd', OPTPARSE_REQUIRED}, + {"noflush", 'F', OPTPARSE_NONE}, + {0}, + }; + struct optparse options; + int opt; + char *err = NULL; + + optparse_init(&options, argc, argv); + + while ((opt = optparse_long(&options, long_options, NULL)) != -1) { + + switch (opt) { + case 'd': + if (opt_daemon != NULL) { + free (opt_daemon); + } + opt_daemon = strdup (optarg); + if (opt_daemon == NULL) + { + rrd_set_error ("strdup failed."); + return NULL; + } + break; + + case 'F': + flushfirst = 0; + break; + + + case '?': + if (opt_daemon) + free(opt_daemon); + rrd_set_error("%s", options.errmsg); + return NULL; + break; + + default: + rrd_set_error ("Usage: rrdtool %s [--daemon [--noflush]] ", + argv[0]); + if (opt_daemon != NULL) { + free (opt_daemon); + } + + return NULL; + break; + } + + } + + if ((argc - optind) != 1) { + rrd_set_error ("Usage: rrdtool %s [--daemon [--noflush]] ", + argv[0]); + + if (opt_daemon != NULL) { + free (opt_daemon); + } + + return NULL; + } + + if( flushfirst ) { + status = rrdc_flushall_if_daemon(opt_daemon); + + if (status) { + if (opt_daemon != NULL) { + free (opt_daemon); + } + + return NULL; + } + } + + rrdc_connect (opt_daemon); + + if (rrdc_is_connected (opt_daemon)) { + list = rrdc_list(argv[optind]); + rrdc_disconnect(); + + } else { + if (opt_daemon) { + fprintf(stderr, "Error connecting to rrdcached"); + err = rrd_get_error(); + + if (err) + fprintf(stderr, ": %s", err); + fprintf(stderr, "\n"); + } + list = rrd_list_r(argv[optind]); + } + + if (opt_daemon != NULL) { + free(opt_daemon); + } + + return list; +} diff --git a/src/rrd_tool.c b/src/rrd_tool.c index 432db7b4..63855a10 100644 --- a/src/rrd_tool.c +++ b/src/rrd_tool.c @@ -53,7 +53,7 @@ void PrintUsage( const char *help_list = N_ ("Valid commands: create, update, updatev, graph, graphv, dump, restore,\n" - "\t\tlast, lastupdate, first, info, fetch, tune\n" + "\t\tlast, lastupdate, first, info, list, fetch, tune,\n" "\t\tresize, xport, flushcached\n"); const char *help_listremote = @@ -82,6 +82,10 @@ void PrintUsage( N_("* info - returns the configuration and status of the RRD\n\n" "\trrdtool info [--daemon|-d [--noflush|-F]] filename.rrd\n"); + const char *help_listrrds = + N_("* list - returns the list of RRDs\n\n" + "\trrdtool list [--daemon
] [--noflush] \n"); + const char *help_restore = N_("* restore - restore an RRD file from its XML form\n\n" "\trrdtool restore [--range-check|-r] [--force-overwrite|-f] filename.xml filename.rrd\n"); @@ -252,7 +256,7 @@ void PrintUsage( N_("RRDtool is distributed under the Terms of the GNU General\n" "Public License Version 2. (www.gnu.org/copyleft/gpl.html)\n\n" "For more information read the RRD manpages\n"); - enum { C_NONE, C_CREATE, C_DUMP, C_INFO, C_RESTORE, C_LAST, + enum { C_NONE, C_CREATE, C_DUMP, C_INFO, C_LIST, C_RESTORE, C_LAST, C_LASTUPDATE, C_FIRST, C_UPDATE, C_FETCH, C_GRAPH, C_GRAPHV, C_TUNE, C_RESIZE, C_XPORT, C_QUIT, C_LS, C_CD, C_MKDIR, C_PWD, @@ -267,6 +271,8 @@ void PrintUsage( help_cmd = C_DUMP; else if (!strcmp(cmd, "info")) help_cmd = C_INFO; + else if (!strcmp(cmd, "list")) + help_cmd = C_LIST; else if (!strcmp(cmd, "restore")) help_cmd = C_RESTORE; else if (!strcmp(cmd, "last")) @@ -322,6 +328,9 @@ void PrintUsage( case C_INFO: puts(_(help_info)); break; + case C_LIST: + puts(_(help_listrrds)); + break; case C_RESTORE: puts(_(help_restore)); break; @@ -682,7 +691,15 @@ int HandleInputLine( rrd_info_print(data); rrd_info_free(data); } - + else if (strcmp("list", argv[1]) == 0) { + char *list; + list = rrd_list(argc - 1, &argv[1]); + + if (list) { + printf("%s", list); + free(list); + } + } else if (strcmp("--version", argv[1]) == 0 || strcmp("version", argv[1]) == 0 || strcmp("v", argv[1]) == 0 || diff --git a/win32/librrd-4.def b/win32/librrd-4.def index 94392e6d..5856f2a1 100644 --- a/win32/librrd-4.def +++ b/win32/librrd-4.def @@ -16,6 +16,7 @@ rrd_first rrd_first_r rrd_flush rrd_flushcached +rrd_flushall rrd_free rrd_free_context rrd_freemem @@ -29,6 +30,8 @@ rrd_info_free rrd_info_print rrd_info_push rrd_info_r +rrd_list +rrd_list_r rrd_init rrd_last rrd_last_r @@ -63,6 +66,7 @@ rrdc_connect rrdc_is_connected rrdc_disconnect rrdc_flush +rrdc_flushall rrdc_stats_free rrdc_stats_get rrdc_update