From 63fb8cad9ad2301d5c4da505b19528c3c8bf53a5 Mon Sep 17 00:00:00 2001 From: oetiker Date: Thu, 23 Sep 2010 06:08:09 +0000 Subject: [PATCH] Summary of changes: 1. Add INFO , FIRST , LAST and CREATE [-b start][-s step][-O] to the rrdcached functions. 2. Add rrd_create_set_no_overwrite as a function in rrd_create.c to allow the overwrite flag to be set from rrd_daemon.c 3. Add rrdc_info, rrdc_first, rrdc_last, rrdc_create functions in rrd_client.c 4. Add rrdcached support to rrd_info(), rrd_first(), rrd_last() and rrd_create() 5. Add --noflush (-F) option to rrd_info(), rrd_last() to prevent cache flushing before function call if required for efficiency 6. Add --daemon option to rrd_info(), rrd_first() for rrdcached support 7. Add buffer_add_ulong function to rrd_client.c (used by new rrdc_* functions) 8. Add rrd_clear_error() call to rrd_create_r () to clean up bug if create called more than once 9. Add -O option to rrdcached to force no-overwrite mode in CREATE function 10. All relevant documentation updated -- Steve Shipway steve steveshipway.org git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk@2130 a5681a0c-68f1-0310-ab6d-d61299d08faa --- program/NEWS | 1 + program/doc/rrdcached.pod | 43 +++++ program/doc/rrdcreate.pod | 8 + program/doc/rrdfirst.pod | 11 +- program/doc/rrdinfo.pod | 6 + program/doc/rrdlast.pod | 9 +- program/src/librrd.sym.in.in | 1 + program/src/rrd.h | 2 + program/src/rrd_client.c | 324 +++++++++++++++++++++++++++++++++++ program/src/rrd_client.h | 11 ++ program/src/rrd_create.c | 37 +++- program/src/rrd_daemon.c | 227 +++++++++++++++++++++++- program/src/rrd_first.c | 25 ++- program/src/rrd_info.c | 19 +- program/src/rrd_last.c | 25 ++- 15 files changed, 729 insertions(+), 20 deletions(-) diff --git a/program/NEWS b/program/NEWS index e89dcc4f..0c593af4 100644 --- a/program/NEWS +++ b/program/NEWS @@ -21,6 +21,7 @@ RRDcached * New FETCH command allowing rrd_fetch to operate through the daemon and thus work remotely. By Florian Forster +* New rrd_client commands INFO, FIRST, LAST, CREATE by Steve Shipway API --- diff --git a/program/doc/rrdcached.pod b/program/doc/rrdcached.pod index 9aebfe73..57b54b2c 100644 --- a/program/doc/rrdcached.pod +++ b/program/doc/rrdcached.pod @@ -20,6 +20,7 @@ B [-g] [B<-b>EIE[B<-B>]] [B<-a>EI] +[-O] =head1 DESCRIPTION @@ -245,6 +246,11 @@ exchange for slightly higher memory utilization. The default isE1. Do not set this more than the B<-w> value divided by your average RRD step size. +=item B<-O> + +Preven the CREATE command from overwriting existing files, even when it is +instructed to do so. This is for added security. + =back =head1 AFFECTED RRDTOOL COMMANDS @@ -280,6 +286,10 @@ info =item * +first + +=item * + last =item * @@ -294,6 +304,10 @@ update xport +=item * + +create + =back The B command can send values to the daemon instead of writing them to @@ -592,6 +606,34 @@ written out to disk. It is used during journal replay to determine which updates have already been applied. It is I valid in the journal; it is not accepted from the other command channels. +=item B I [I] + +Return the timestamp for the first CDP in the specified RRA. Default is to +use RRA zero if none is specified. + +=item B I + +Return the timestamp for the last update to the specified RRD. Note that the +cache is I flushed before checking, as the client is expected to request +this separately if it is required. + +=item B I + +Return the configuration information for the specified RRD. Note that the +cache is I flushed before checking, as the client is expected to request +this separately if it is required. + +The information is returned, one item per line, with the format: + + I I I + +=item B I [-s I] [-b I] [-O] I ... I ... + +This will create the RRD file according to the supplied parameters, provided +the parameters are valid, and (if the -O option is given or if the rrdcached +was started with the -O flag) the specified I does not already +exist. + =item B This command initiates the bulk load of multiple commands. This is @@ -713,6 +755,7 @@ Both B and this manual page have been written by Florian. =head1 CONTRIBUTORS kevin brintnall Ekbrint@rufus.netE +Steve Shipway Esteve@steveshipway.orgE =cut diff --git a/program/doc/rrdcreate.pod b/program/doc/rrdcreate.pod index b321de63..2167b7ac 100644 --- a/program/doc/rrdcreate.pod +++ b/program/doc/rrdcreate.pod @@ -8,6 +8,7 @@ B B I S<[B<--start>|B<-b> I]> S<[B<--step>|B<-s> I]> S<[B<--no-overwrite>]> +S<[B<--daemon> I
]> S<[BIB<:>IB<:>I]> S<[BIB<:>I]> @@ -41,6 +42,13 @@ into the B. Do not clobber an existing file of the same name. +=item B<--daemon> I
+ +Address of the L daemon. For a list of accepted formats, see +the B<-l> option in the L manual. + + rrdtool create --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd I + =head2 BIB<:>IB<:>I A single B can accept input from several data sources (B), diff --git a/program/doc/rrdfirst.pod b/program/doc/rrdfirst.pod index 9a3c6908..eca21e04 100644 --- a/program/doc/rrdfirst.pod +++ b/program/doc/rrdfirst.pod @@ -4,7 +4,7 @@ rrdfirst - Return the date of the first data sample in an RRA within an RRD =head1 SYNOPSIS -B B I [I<--rraindex number>] +B B I [I<--rraindex number>] [B<--daemon> I
] =head1 DESCRIPTION @@ -23,10 +23,17 @@ The index number of the B that is to be examined. If not specified, the index defaults to zero. B index numbers can be determined through B. +=item B<--daemon> I
+ +Address of the L daemon. For a list of accepted formats, see +the B<-l> option in the L manual. + + rrdtool first --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd + =back =head1 AUTHOR Burton Strauss - +Daemon support added by Steve Shipway diff --git a/program/doc/rrdinfo.pod b/program/doc/rrdinfo.pod index d147756c..1752b693 100644 --- a/program/doc/rrdinfo.pod +++ b/program/doc/rrdinfo.pod @@ -30,6 +30,12 @@ For a list of accepted formats, see the B<-l> option in the L manual. rrdtool info --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd +=item B<--noflush> + +Omit the C command usually sent to the daemon prior to retrieving the +data. If all you are interested in the the RRD Structure, and not the last update +time or current values, then this will improve efficiency. + =back =head1 EXAMPLE diff --git a/program/doc/rrdlast.pod b/program/doc/rrdlast.pod index a8bf7574..f0eaad0c 100644 --- a/program/doc/rrdlast.pod +++ b/program/doc/rrdlast.pod @@ -27,6 +27,13 @@ For a list of accepted formats, see the B<-l> option in the L manual. rrdtool last --daemon unix:/var/run/rrdcached.sock /var/lib/rrd/foo.rrd +=item B<--noflush> + +If the L daemon is being used, then omit the flush normally +send before returning the last update. If you add this option, you get the +last time the file on disk was updated; if you have relatively low cache times +and are calling this frequently it will preserve the caching benefit + =back =head1 ENVIRONMENT VARIABLES @@ -47,6 +54,6 @@ line argument takes precedence. =head1 AUTHOR Russ Wright - +Daemon support added by Steve Shipway diff --git a/program/src/librrd.sym.in.in b/program/src/librrd.sym.in.in index 9a8f7f95..458601f7 100644 --- a/program/src/librrd.sym.in.in +++ b/program/src/librrd.sym.in.in @@ -6,6 +6,7 @@ rrd_clear_error rrd_close rrd_create rrd_create_r +rrd_create_set_no_overwrite rrd_dontneed rrd_dump rrd_dump_r diff --git a/program/src/rrd.h b/program/src/rrd.h index fa02952d..331339db 100644 --- a/program/src/rrd.h +++ b/program/src/rrd.h @@ -143,6 +143,8 @@ extern "C" { int rrd_create( int, char **); + void rrd_create_set_no_overwrite( + int); rrd_info_t *rrd_info( int, char **); diff --git a/program/src/rrd_client.c b/program/src/rrd_client.c index 0d212838..043a144a 100644 --- a/program/src/rrd_client.c +++ b/program/src/rrd_client.c @@ -316,6 +316,16 @@ static int buffer_add_value (const char *value, /* {{{ */ return (buffer_add_string (temp, buffer_ret, buffer_size_ret)); } /* }}} int buffer_add_value */ +static int buffer_add_ulong (const unsigned long value, /* {{{ */ + char **buffer_ret, size_t *buffer_size_ret) +{ + char temp[4096]; + + snprintf (temp, sizeof (temp), "%lu", value); + temp[sizeof (temp) - 1] = 0; + return (buffer_add_string (temp, buffer_ret, buffer_size_ret)); +} /* }}} int buffer_add_ulong */ + /* Remove trailing newline (NL) and carriage return (CR) characters. Similar to * the Perl function `chomp'. Returns the number of characters that have been * removed. */ @@ -817,6 +827,320 @@ int rrdc_flush (const char *filename) /* {{{ */ return (status); } /* }}} int rrdc_flush */ +rrd_info_t * rrdc_info (const char *filename) /* {{{ */ +{ + char buffer[4096]; + char *buffer_ptr; + size_t buffer_free; + size_t buffer_size; + rrdc_response_t *res; + int status; + char file_path[PATH_MAX]; + rrd_info_t *data = NULL, *cd; + rrd_infoval_t info; + unsigned int l; + rrd_info_type_t itype; + char *k, *s; + + if (filename == NULL) { + rrd_set_error ("rrdc_info: no filename"); + return (NULL); + } + + memset (buffer, 0, sizeof (buffer)); + buffer_ptr = &buffer[0]; + buffer_free = sizeof (buffer); + + status = buffer_add_string ("info", &buffer_ptr, &buffer_free); + if (status != 0) { + rrd_set_error ("rrdc_info: out of memory"); + return (NULL); + } + + pthread_mutex_lock (&lock); + filename = get_path (filename, file_path); + if (filename == NULL) + { + pthread_mutex_unlock (&lock); + return (NULL); + } + + status = buffer_add_string (filename, &buffer_ptr, &buffer_free); + if (status != 0) + { + pthread_mutex_unlock (&lock); + rrd_set_error ("rrdc_info: 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; + status = request (buffer, buffer_size, &res); + pthread_mutex_unlock (&lock); + + if (status != 0) { + rrd_set_error ("rrdcached: %s", res->message); + return (NULL); + } + data = cd = NULL; + for( l=0 ; l < res->lines_num ; l++ ) { + /* first extract the keyword */ + for(k = s = res->lines[l];s && *s;s++) { + if(*s == ' ') { *s = 0; s++; break; } + } + if(!s || !*s) break; + itype = atoi(s); /* extract type code */ + for(;*s;s++) { if(*s == ' ') { *s = 0; s++; break; } } + if(!*s) break; + /* finally, we're pointing to the value */ + switch(itype) { + case RD_I_VAL: + if(*s == 'N') { info.u_val = DNAN; } else { info.u_val = atof(s); } + break; + case RD_I_CNT: + info.u_cnt = atol(s); + break; + case RD_I_INT: + info.u_int = atoi(s); + break; + case RD_I_STR: + chomp(s); + info.u_str = (char*)malloc(sizeof(char) * (strlen(s) + 1)); + strcpy(info.u_str,s); + break; + case RD_I_BLO: + rrd_set_error ("rrdc_info: BLOB objects are not supported"); + return (NULL); + default: + rrd_set_error ("rrdc_info: Unsupported info type %d",itype); + return (NULL); + } + + cd = rrd_info_push(cd, sprintf_alloc("%s",k), itype, info); + if(!data) data = cd; + } + response_free (res); + + return (data); +} /* }}} int rrdc_info */ + +time_t rrdc_last (const char *filename) /* {{{ */ +{ + char buffer[4096]; + char *buffer_ptr; + size_t buffer_free; + size_t buffer_size; + rrdc_response_t *res; + int status; + char file_path[PATH_MAX]; + time_t lastup; + + if (filename == NULL) { + rrd_set_error ("rrdc_last: no filename"); + return (-1); + } + + memset (buffer, 0, sizeof (buffer)); + buffer_ptr = &buffer[0]; + buffer_free = sizeof (buffer); + + status = buffer_add_string ("last", &buffer_ptr, &buffer_free); + if (status != 0) { + rrd_set_error ("rrdc_last: out of memory"); + return (-1); + } + + pthread_mutex_lock (&lock); + filename = get_path (filename, file_path); + if (filename == NULL) + { + pthread_mutex_unlock (&lock); + return (-1); + } + + status = buffer_add_string (filename, &buffer_ptr, &buffer_free); + if (status != 0) + { + pthread_mutex_unlock (&lock); + rrd_set_error ("rrdc_last: out of memory"); + return (-1); + } + + 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) { + rrd_set_error ("rrdcached: %s", res->message); + return (-1); + } + lastup = atol(res->message); + response_free (res); + + return (lastup); +} /* }}} int rrdc_last */ + +time_t rrdc_first (const char *filename, int rraindex) /* {{{ */ +{ + char buffer[4096]; + char *buffer_ptr; + size_t buffer_free; + size_t buffer_size; + rrdc_response_t *res; + int status; + char file_path[PATH_MAX]; + time_t firstup; + + if (filename == NULL) { + rrd_set_error ("rrdc_first: no filename specified"); + return (-1); + } + + memset (buffer, 0, sizeof (buffer)); + buffer_ptr = &buffer[0]; + buffer_free = sizeof (buffer); + + status = buffer_add_string ("first", &buffer_ptr, &buffer_free); + if (status != 0) { + rrd_set_error ("rrdc_first: out of memory"); + return (-1); + } + + pthread_mutex_lock (&lock); + filename = get_path (filename, file_path); + if (filename == NULL) + { + pthread_mutex_unlock (&lock); + return (-1); + } + + status = buffer_add_string (filename, &buffer_ptr, &buffer_free); + if (status != 0) + { + pthread_mutex_unlock (&lock); + rrd_set_error ("rrdc_first: out of memory"); + return (-1); + } + status = buffer_add_ulong (rraindex, &buffer_ptr, &buffer_free); + if (status != 0) + { + pthread_mutex_unlock (&lock); + rrd_set_error ("rrdc_first: out of memory"); + return (-1); + } + + 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) { + rrd_set_error ("rrdcached: %s", res->message); + return (-1); + } + firstup = atol(res->message); + response_free (res); + + return (firstup); +} /* }}} int rrdc_first */ + +int rrdc_create (const char *filename, /* {{{ */ + unsigned long pdp_step, + time_t last_up, + int no_overwrite, + int argc, + const char **argv) +{ + char buffer[4096]; + char *buffer_ptr; + size_t buffer_free; + size_t buffer_size; + rrdc_response_t *res; + int status; + char file_path[PATH_MAX]; + int i; + + if (filename == NULL) { + rrd_set_error ("rrdc_create: no filename specified"); + return (-1); + } + + memset (buffer, 0, sizeof (buffer)); + buffer_ptr = &buffer[0]; + buffer_free = sizeof (buffer); + + status = buffer_add_string ("create", &buffer_ptr, &buffer_free); + if (status != 0) { + rrd_set_error ("rrdc_create: out of memory"); + return (-1); + } + + pthread_mutex_lock (&lock); + filename = get_path (filename, file_path); + if (filename == NULL) + { + pthread_mutex_unlock (&lock); + return (-1); + } + + status = buffer_add_string (filename, &buffer_ptr, &buffer_free); + status = buffer_add_string ("-b", &buffer_ptr, &buffer_free); + status = buffer_add_ulong (last_up, &buffer_ptr, &buffer_free); + status = buffer_add_string ("-s", &buffer_ptr, &buffer_free); + status = buffer_add_ulong (pdp_step, &buffer_ptr, &buffer_free); + if(no_overwrite) { + status = buffer_add_string ("-O", &buffer_ptr, &buffer_free); + } + if (status != 0) + { + pthread_mutex_unlock (&lock); + rrd_set_error ("rrdc_create: out of memory"); + return (-1); + } + + for( i=0; imessage); + return (-1); + } + response_free (res); + return(0); +} /* }}} int rrdc_create */ + int rrdc_fetch (const char *filename, /* {{{ */ const char *cf, time_t *ret_start, time_t *ret_end, diff --git a/program/src/rrd_client.h b/program/src/rrd_client.h index 31f0e95e..e203d7ca 100644 --- a/program/src/rrd_client.h +++ b/program/src/rrd_client.h @@ -68,6 +68,17 @@ int rrdc_disconnect (void); int rrdc_update (const char *filename, int values_num, const char * const *values); +rrd_info_t * rrdc_info (const char *filename); +time_t rrdc_last (const char *filename); +time_t rrdc_first (const char *filename, int rraindex); +int rrdc_create (const char *filename, + unsigned long pdp_step, + time_t last_up, + int no_overwrite, + int argc, + const char **argv); + + int rrdc_flush (const char *filename); int rrdc_flush_if_daemon (const char *opt_daemon, const char *filename); diff --git a/program/src/rrd_create.c b/program/src/rrd_create.c index c8996ce8..707e6013 100644 --- a/program/src/rrd_create.c +++ b/program/src/rrd_create.c @@ -11,6 +11,7 @@ #include "rrd_tool.h" #include "rrd_rpncalc.h" #include "rrd_hw.h" +#include "rrd_client.h" #include "rrd_is_thread_safe.h" static int opt_no_overwrite = 0; @@ -33,6 +34,12 @@ void parseGENERIC_DS( static void rrd_free2( rrd_t *rrd); /* our onwn copy, immmune to mmap */ +void rrd_create_set_no_overwrite( + int opt ) +{ + opt_no_overwrite = (opt?1:0); +} + int rrd_create( int argc, char **argv) @@ -40,6 +47,7 @@ int rrd_create( struct option long_options[] = { {"start", required_argument, 0, 'b'}, {"step", required_argument, 0, 's'}, + {"daemon", required_argument, 0, 'd'}, {"no-overwrite", no_argument, 0, 'O'}, {0, 0, 0, 0} }; @@ -51,17 +59,29 @@ int rrd_create( char *parsetime_error = NULL; long long_tmp; int rc; + char * opt_daemon = NULL; optind = 0; opterr = 0; /* initialize getopt */ while (1) { - opt = getopt_long(argc, argv, "Ob:s:", long_options, &option_index); + opt = getopt_long(argc, argv, "Ob:s:d:", long_options, &option_index); if (opt == EOF) break; 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 (-1); + } + break; + case 'b': if ((parsetime_error = rrd_parsetime(optarg, &last_up_tv))) { rrd_set_error("start time: %s", parsetime_error); @@ -108,9 +128,17 @@ int rrd_create( rrd_set_error("need name of an rrd file to create"); return -1; } + + rrdc_connect (opt_daemon); + if (rrdc_is_connected (opt_daemon)) { + rc = rrdc_create (argv[optind], + pdp_step, last_up, opt_no_overwrite, + argc - optind - 1, (const char **) (argv + optind + 1)); + } else { rc = rrd_create_r(argv[optind], pdp_step, last_up, argc - optind - 1, (const char **) (argv + optind + 1)); + } return rc; } @@ -131,6 +159,9 @@ int rrd_create_r( unsigned short token_idx, error_flag, period = 0; unsigned long hashed_name; + /* clear any previous errors */ + rrd_clear_error(); + /* init rrd clean */ rrd_init(&rrd); /* static header */ @@ -189,11 +220,11 @@ int rrd_create_r( dummychar2, &offset)) { case 0: case 1: - rrd_set_error("Invalid DS name"); + rrd_set_error("Invalid DS name in [%s]",&argv[i][3]); break; case 2: case 3: - rrd_set_error("Invalid DS type"); + rrd_set_error("Invalid DS type in [%s]",&argv[i][3]); break; case 4: /* (%n may or may not be counted) */ case 5: /* check for duplicate datasource names */ diff --git a/program/src/rrd_daemon.c b/program/src/rrd_daemon.c index 22093205..bfcf6a3c 100644 --- a/program/src/rrd_daemon.c +++ b/program/src/rrd_daemon.c @@ -275,6 +275,8 @@ static uint64_t stats_journal_bytes = 0; static uint64_t stats_journal_rotate = 0; static pthread_mutex_t stats_lock = PTHREAD_MUTEX_INITIALIZER; +static int opt_no_overwrite = 0; + /* Journaled updates */ #define JOURNAL_REPLAY(s) ((s) == NULL) #define JOURNAL_BASE "rrd.journal" @@ -1695,6 +1697,182 @@ static int handle_request_wrote (HANDLER_PROTO) /* {{{ */ return (0); } /* }}} int handle_request_wrote */ +static int handle_request_info (HANDLER_PROTO) /* {{{ */ +{ + char *file, file_tmp[PATH_MAX]; + int status; + rrd_info_t *data; + + /* obtain filename */ + status = buffer_get_field(&buffer, &buffer_size, &file); + if (status != 0) + return syntax_error(sock,cmd); + /* get full pathname */ + get_abs_path(&file, file_tmp); + if (!check_file_access(file, sock)) { + return send_response(sock, RESP_ERR, "Cannot read: %s\n", file); + } + /* get data */ + rrd_clear_error (); + data = rrd_info_r(file); + if(!data) { + return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error()); + } + while (data) { + switch (data->type) { + case RD_I_VAL: + if (isnan(data->value.u_val)) + add_response_info(sock,"%s %d NaN\n",data->key, data->type); + else + add_response_info(sock,"%s %d %0.10e\n", data->key, data->type, data->value.u_val); + break; + case RD_I_CNT: + add_response_info(sock,"%s %d %lu\n", data->key, data->type, data->value.u_cnt); + break; + case RD_I_INT: + add_response_info(sock,"%s %d %d\n", data->key, data->type, data->value.u_int); + break; + case RD_I_STR: + add_response_info(sock,"%s %d %s\n", data->key, data->type, data->value.u_str); + break; + case RD_I_BLO: + add_response_info(sock,"%s %d %lu\n", data->key, data->type, data->value.u_blo.size); + break; + } + data = data->next; + } + return send_response(sock, RESP_OK, "Info for %s follows\n",file); +} /* }}} static int handle_request_info */ + +static int handle_request_first (HANDLER_PROTO) /* {{{ */ +{ + char *i, *file, file_tmp[PATH_MAX]; + int status; + int idx; + time_t t; + + /* obtain filename */ + status = buffer_get_field(&buffer, &buffer_size, &file); + if (status != 0) + return syntax_error(sock,cmd); + /* get full pathname */ + get_abs_path(&file, file_tmp); + if (!check_file_access(file, sock)) { + return send_response(sock, RESP_ERR, "Cannot read: %s\n", file); + } + + status = buffer_get_field(&buffer, &buffer_size, &i); + if (status != 0) + return syntax_error(sock,cmd); + idx = atoi(i); + if(idx<0) { + return send_response(sock, RESP_ERR, "Invalid index specified (%d)\n", idx); + } + + /* get data */ + rrd_clear_error (); + t = rrd_first_r(file,idx); + if(t<1) { + return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error()); + } + return send_response(sock, RESP_OK, "%lu\n",(unsigned)t); +} /* }}} static int handle_request_last */ + +static int handle_request_last (HANDLER_PROTO) /* {{{ */ +{ + char *file, file_tmp[PATH_MAX]; + int status; + time_t t; + + /* obtain filename */ + status = buffer_get_field(&buffer, &buffer_size, &file); + if (status != 0) + return syntax_error(sock,cmd); + /* get full pathname */ + get_abs_path(&file, file_tmp); + if (!check_file_access(file, sock)) { + return send_response(sock, RESP_ERR, "Cannot read: %s\n", file); + } + /* get data */ + rrd_clear_error (); + t = rrd_last_r(file); + if(t<1) { + return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error()); + } + return send_response(sock, RESP_OK, "%lu\n",(unsigned)t); +} /* }}} static int handle_request_last */ + +static int handle_request_create (HANDLER_PROTO) /* {{{ */ +{ + char *file, file_tmp[PATH_MAX]; + char *tok; + int ac = 0; + char *av[128]; + int status; + ulong step = 300; + time_t last_up = time(NULL)-10; + rrd_time_value_t last_up_tv; + char *parsetime_error = NULL; + int no_overwrite = opt_no_overwrite; + + + /* obtain filename */ + status = buffer_get_field(&buffer, &buffer_size, &file); + if (status != 0) + return syntax_error(sock,cmd); + /* get full pathname */ + get_abs_path(&file, file_tmp); + if (!check_file_access(file, sock)) { + return send_response(sock, RESP_ERR, "Cannot read: %s\n", file); + } + RRDD_LOG(LOG_INFO, "rrdcreate request for %s",file); + + status = buffer_get_field(&buffer, &buffer_size, &tok ); + for(;(tok && !status);status = buffer_get_field(&buffer, &buffer_size, &tok )) { + if( ! strncmp(tok,"-b",2) ) { + status = buffer_get_field(&buffer, &buffer_size, &tok ); + if (status != 0) return syntax_error(sock,cmd); + if ((parsetime_error = rrd_parsetime(tok, &last_up_tv))) + return send_response(sock, RESP_ERR, "start time: %s\n", parsetime_error); + if (last_up_tv.type == RELATIVE_TO_END_TIME || + last_up_tv.type == RELATIVE_TO_START_TIME) { + return send_response(sock, RESP_ERR, "Cannot specify time relative to start or end here.\n"); + } + last_up = mktime(&last_up_tv.tm) +last_up_tv.offset; + + continue; + } + if( ! strncmp(tok,"-s",2) ) { + status = buffer_get_field(&buffer, &buffer_size, &tok ); + if (status != 0) return syntax_error(sock,cmd); + step = atol(tok); + continue; + } + if( ! strncmp(tok,"-O",2) ) { + no_overwrite = 1; + continue; + } + if( ! strncmp(tok,"DS:",3) ) { av[ac++]=tok; continue; } + if( ! strncmp(tok,"RRA:",4) ) { av[ac++]=tok; continue; } + return syntax_error(sock,cmd); + } + if(step<1) { + return send_response(sock, RESP_ERR, "The step size cannot be less than 1 second.\n"); + } + if (last_up < 3600 * 24 * 365 * 10) { + return send_response(sock, RESP_ERR, "The first entry must be after 1980.\n"); + } + + rrd_create_set_no_overwrite(no_overwrite); + rrd_clear_error (); + status = rrd_create_r(file,step,last_up,ac,(const char **)av); + + if(!status) { + return send_response(sock, RESP_OK, "RRD created OK\n"); + } + return send_response(sock, RESP_ERR, "RRD Error: %s\n", rrd_get_error()); +} /* }}} static int handle_request_create */ + /* start "BATCH" processing */ static int batch_start (HANDLER_PROTO) /* {{{ */ { @@ -1847,6 +2025,45 @@ static command_t list_of_commands[] = { /* {{{ */ , "The 'FETCH' can be used by the client to retrieve values from an RRD file.\n" }, + { + "INFO", + handle_request_info, + CMD_CONTEXT_CLIENT, + "INFO \n", + "The INFO command retrieves information about a specified RRD file.\n" + "This is returned in standard rrdinfo format, a sequence of lines\n" + "with the format = \n" + "Note that this is the data as of the last update of the RRD file itself,\n" + "not the last time data was received via rrdcached, so there may be pending\n" + "updates in the queue. If this bothers you, then first run a FLUSH.\n" + }, + { + "FIRST", + handle_request_first, + CMD_CONTEXT_CLIENT, + "FIRST \n", + "The FIRST command retrieves the first data time for a specified RRA in\n" + "an RRD file.\n" + }, + { + "LAST", + handle_request_last, + CMD_CONTEXT_CLIENT, + "LAST \n", + "The LAST command retrieves the last update time for a specified RRD file.\n" + "Note that this is the time of the last update of the RRD file itself, not\n" + "the last time data was received via rrdcached, so there may be pending\n" + "updates in the queue. If this bothers you, then first run a FLUSH.\n" + }, + { + "CREATE", + handle_request_create, + CMD_CONTEXT_CLIENT | CMD_CONTEXT_BATCH, + "CREATE [-b start] [-s step] [-O] \n", + "The CREATE command will create an RRD file, overwriting any existing file\n" + "unless the -O option is given or rrdcached was started with the -O option.\n" + "The DS and RRA definitions are as for the 'rrdtool create' command.\n" + }, { "QUIT", handle_request_quit, @@ -2993,10 +3210,14 @@ static int read_options (int argc, char **argv) /* {{{ */ default_socket.socket_group = (gid_t)-1; default_socket.socket_permissions = (mode_t)-1; - while ((option = getopt(argc, argv, "gl:s:m:P:f:w:z:t:Bb:p:Fj:a:h?")) != -1) + while ((option = getopt(argc, argv, "Ogl:s:m:P:f:w:z:t:Bb:p:Fj:a:h?")) != -1) { switch (option) { + case 'O': + opt_no_overwrite = 1; + break; + case 'g': stay_foreground=1; break; @@ -3338,7 +3559,9 @@ static int read_options (int argc, char **argv) /* {{{ */ "for that group)\n" " -m File permissions (octal) of all following UNIX " "sockets\n" - " -a Memory allocation chunk size. Default is 1." + " -a Memory allocation chunk size. Default is 1.\n" + " -O Do not allow CREATE commands to overwrite existing\n" + " files, even if asked to.\n" "\n" "For more information and a detailed description of all options " "please refer\n" diff --git a/program/src/rrd_first.c b/program/src/rrd_first.c index faae5774..80b9e17d 100644 --- a/program/src/rrd_first.c +++ b/program/src/rrd_first.c @@ -8,6 +8,7 @@ #include #include "rrd_tool.h" +#include "rrd_client.h" time_t rrd_first( @@ -16,8 +17,10 @@ time_t rrd_first( { int target_rraindex = 0; char *endptr; + char *opt_daemon = NULL; struct option long_options[] = { {"rraindex", required_argument, 0, 129}, + {"daemon", required_argument, 0, 'd'}, {0, 0, 0, 0} }; @@ -28,7 +31,7 @@ time_t rrd_first( int option_index = 0; int opt; - opt = getopt_long(argc, argv, "", long_options, &option_index); + opt = getopt_long(argc, argv, "d:F", long_options, &option_index); if (opt == EOF) break; @@ -41,19 +44,35 @@ time_t rrd_first( return (-1); } break; + case 'd': + if (opt_daemon != NULL) + free (opt_daemon); + opt_daemon = strdup (optarg); + if (opt_daemon == NULL) + { + rrd_set_error ("strdup failed."); + return (-1); + } + break; default: - rrd_set_error("usage rrdtool %s [--rraindex number] file.rrd", + rrd_set_error("usage rrdtool %s [--rraindex number] [--daemon ] file.rrd", argv[0]); return (-1); } } if (optind >= argc) { - rrd_set_error("not enough arguments"); + rrd_set_error("usage rrdtool %s [--rraindex number] [--daemon ] file.rrd", + argv[0]); return -1; } + rrdc_connect (opt_daemon); + if (rrdc_is_connected (opt_daemon)) { + return (rrdc_first (argv[optind], target_rraindex)); + } else { return (rrd_first_r(argv[optind], target_rraindex)); + } } diff --git a/program/src/rrd_info.c b/program/src/rrd_info.c index 4f8b5dd4..d86a9074 100644 --- a/program/src/rrd_info.c +++ b/program/src/rrd_info.c @@ -92,6 +92,7 @@ rrd_info_t *rrd_info( rrd_info_t *info; char *opt_daemon = NULL; int status; + int flushfirst = 1; optind = 0; opterr = 0; /* initialize getopt */ @@ -101,10 +102,11 @@ rrd_info_t *rrd_info( int option_index = 0; static struct option long_options[] = { {"daemon", required_argument, 0, 'd'}, + {"noflush", no_argument, 0, 'F'}, {0, 0, 0, 0} }; - opt = getopt_long(argc, argv, "d:", long_options, &option_index); + opt = getopt_long(argc, argv, "d:F", long_options, &option_index); if (opt == EOF) break; @@ -121,8 +123,12 @@ rrd_info_t *rrd_info( } break; + case 'F': + flushfirst = 0; + break; + default: - rrd_set_error ("Usage: rrdtool %s [--daemon ] ", + rrd_set_error ("Usage: rrdtool %s [--daemon [--noflush]] ", argv[0]); return (NULL); break; @@ -130,15 +136,20 @@ rrd_info_t *rrd_info( } /* while (42) */ if ((argc - optind) != 1) { - rrd_set_error ("Usage: rrdtool %s [--daemon ] ", + rrd_set_error ("Usage: rrdtool %s [--daemon [--noflush]] ", argv[0]); return (NULL); } + if( flushfirst ) { status = rrdc_flush_if_daemon(opt_daemon, argv[optind]); - if (opt_daemon) free (opt_daemon); if (status) return (NULL); + } + rrdc_connect (opt_daemon); + if (rrdc_is_connected (opt_daemon)) + info = rrdc_info (argv[optind]); + else info = rrd_info_r(argv[optind]); return (info); diff --git a/program/src/rrd_last.c b/program/src/rrd_last.c index 8555fbc0..7fb3cb88 100644 --- a/program/src/rrd_last.c +++ b/program/src/rrd_last.c @@ -15,6 +15,8 @@ time_t rrd_last( { char *opt_daemon = NULL; int status; + time_t lastupdate; + int flushfirst = 1; optind = 0; opterr = 0; /* initialize getopt */ @@ -24,10 +26,11 @@ time_t rrd_last( int option_index = 0; static struct option long_options[] = { {"daemon", required_argument, 0, 'd'}, + {"noflush", no_argument, 0, 'F'}, {0, 0, 0, 0} }; - opt = getopt_long(argc, argv, "d:", long_options, &option_index); + opt = getopt_long(argc, argv, "d:F", long_options, &option_index); if (opt == EOF) break; @@ -44,8 +47,12 @@ time_t rrd_last( } break; + case 'F': + flushfirst = 0; + break; + default: - rrd_set_error ("Usage: rrdtool %s [--daemon ] ", + rrd_set_error ("Usage: rrdtool %s [--daemon [--noflush]] ", argv[0]); return (-1); break; @@ -53,16 +60,24 @@ time_t rrd_last( } /* while (42) */ if ((argc - optind) != 1) { - rrd_set_error ("Usage: rrdtool %s [--daemon ] ", + rrd_set_error ("Usage: rrdtool %s [--daemon [--noflush]] ", argv[0]); return (-1); } + if(flushfirst) { status = rrdc_flush_if_daemon(opt_daemon, argv[optind]); - if (opt_daemon) free(opt_daemon); if (status) return (-1); + } + + rrdc_connect (opt_daemon); + if (rrdc_is_connected (opt_daemon)) + lastupdate = rrdc_last (argv[optind]); + + else + lastupdate = rrd_last_r(argv[optind]); - return (rrd_last_r (argv[optind])); + return (lastupdate); } time_t rrd_last_r( -- 2.30.2