From 298b318acaf1147474ab9e97cac37c3608660efd Mon Sep 17 00:00:00 2001 From: oetiker Date: Thu, 13 Feb 2003 07:05:59 +0000 Subject: [PATCH] Find attached the patch I promised to send to you. Please note that there are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This library is identical to librrd, but it contains support code for per-thread global variables currently used for error information only. This is similar to how errno per-thread variables are implemented. librrd_th must be linked alongside of libpthred There is also a new file "THREADS", holding some documentation. -- Peter Stamfest git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@180 a5681a0c-68f1-0310-ab6d-d61299d08faa --- Makefile.am | 3 +- THREADS | 57 ++++++++++++++++++++++ configure.ac | 2 +- src/Makefile.am | 9 +++- src/parsetime.c | 3 ++ src/rrd.h | 45 +++++++++++++++++ src/rrd_cgi.c | 10 ++-- src/rrd_create.c | 114 ++++++++++++++++++++++++------------------- src/rrd_dump.c | 48 ++++++++++++++---- src/rrd_error.c | 103 ++++++++++++++++++++++++++++++++++----- src/rrd_format.c | 15 +++++- src/rrd_graph.c | 30 ++++++------ src/rrd_graph_helper.c | 4 +- src/rrd_info.c | 27 +++++++++-- src/rrd_is_thread_safe.h | 26 ++++++++++ src/rrd_last.c | 22 ++++++--- src/rrd_not_thread_safe.c | 35 ++++++++++++++ src/rrd_open.c | 35 +++++++++----- src/rrd_restore.c | 4 +- src/rrd_rpncalc.c | 28 +++++------ src/rrd_stat.c | 4 +- src/rrd_thread_safe.c | 67 +++++++++++++++++++++++++ src/rrd_tool.c | 16 +++--- src/rrd_tool.h | 10 ++++ src/rrd_update.c | 121 ++++++++++++++++++++++++++++++---------------- 25 files changed, 651 insertions(+), 187 deletions(-) create mode 100644 THREADS create mode 100644 src/rrd_is_thread_safe.h create mode 100644 src/rrd_not_thread_safe.c create mode 100644 src/rrd_thread_safe.c diff --git a/Makefile.am b/Makefile.am index 4bba56a..ea6447a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,7 +5,8 @@ RSYNC = rsync --rsh=ssh SUBDIRS = libraries src bindings doc examples # the following files are not mentioned in any other Makefile -EXTRA_DIST = COPYRIGHT CHANGES NT-BUILD-TIPS.txt TODO CONTRIBUTORS rrdtool.spec +EXTRA_DIST = COPYRIGHT CHANGES NT-BUILD-TIPS.txt TODO CONTRIBUTORS THREADS \ + rrdtool.spec CLEANFILES = config.cache diff --git a/THREADS b/THREADS new file mode 100644 index 0000000..0dc5cec --- /dev/null +++ b/THREADS @@ -0,0 +1,57 @@ +In order to use the librrd in multi-threaded programs you must: + + * Link with librrd_th instead of with librrd + * Use the *_r function instead or the *-functions + * Never use non *_r functions unless it is explicitly documented that the + function is tread-safe + +Every thread SHOULD call rrd_get_context() before the first call to +any librrd function in order to set up thread specific data. This is +not strictly required, but it is the only way to test if memory +allocation can be done by this function. Otherwise the program may die +with a SIGSEGV in a low-memory situation. + + +IMPORTANT NOTE FOR RRD CONTRIBUTORS: + +Some precautions must be followed when developing rrd from now on: + +* Only use thread-safe functions in library code. Many often used libc + functions aren't thread-safe. Take care if you want to use any of + the following functions: + + + direct strerror calls must be avoided: use rrd_strerror instead, + it provides a per-thread error message + + the getpw*, getgr*, gethost* function families (and some more get* + functions): use the *_r variants + + Time functions: asctime, ctime, gmtime, localtime: use *_r variants + + strtok: use strtok_r + + tmpnam: use tmpnam_r + + many other (lookup documentation) + +As an aide(?) a header file named "rrd_is_thread_safe.h" is provided +that works with the GNU C-preprocessor to "poison" some of the most +common non-thread-safe functions using the "#pragma GCC poison" +directive. Just include this header in source files you want to keep +thread-safe. + +* Do not introduce global variables! + + If you really, really have to use a global variable you may add a + new field to the rrd_context structure and modify rrd_error.c, + rrd_thread_safe.c and rrd_non_thread_safe.c + +* Do not use "getopt" or "getopt_long" in *_r (directly or indirectly) + + getopt uses global variables and behaves badly in a multithreaded + application when called concurrently. Instead provide a *_r function + taking all options as function parameters. You may provide argc and + **argv arguments for variable lenght argument lists. See + rrd_update_r as an example. + +* Do not use the parsetime function! + + It uses lots of global vars. You may use it in functions not + designed to be thread-safe like functions wrapping the _r version of some + operation (eg. rrd_create, but not in rrd_create_r) + diff --git a/configure.ac b/configure.ac index 7b7155e..afe8e4d 100644 --- a/configure.ac +++ b/configure.ac @@ -279,7 +279,7 @@ AC_FUNC_VPRINTF dnl for each function found we get a definition in config.h dnl of the form HAVE_FUNCTION -AC_CHECK_FUNCS(tzset opendir readdir chdir chroot getuid setlocale strerror snprintf vsnprintf fpclass class fp_class isnan memmove strchr mktime getrusage gettimeofday) +AC_CHECK_FUNCS(tzset opendir readdir chdir chroot getuid setlocale strerror strerror_r snprintf vsnprintf fpclass class fp_class isnan memmove strchr mktime getrusage gettimeofday) dnl HP-UX 11.00 does not have finite but does have isfinite as a macro AC_CHECK_FUNCS(fpclassify, , diff --git a/src/Makefile.am b/src/Makefile.am index 3aca332..f7965b1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -84,15 +84,20 @@ RRD_LIBS = \ $(PNG_LIB) \ $(ZLIB_LIB) -lib_LTLIBRARIES = librrd.la +lib_LTLIBRARIES = librrd.la librrd_th.la noinst_LTLIBRARIES = librrd_private.la -librrd_la_SOURCES = $(RRD_C_FILES) +librrd_la_SOURCES = $(RRD_C_FILES) rrd_not_thread_safe.c librrd_private_la_SOURCES = $(RRD_C_FILES) librrd_la_LIBADD = $(RRD_LIBS) librrd_la_LDFLAGS = -version-info 1:2:0 +librrd_th_la_SOURCES = $(RRD_C_FILES) rrd_thread_safe.c +librrd_th_la_LIBADD = $(RRD_LIBS) -lpthread +librrd_th_la_LDFLAGS = -version-info 1:2:0 + + include_HEADERS = rrd.h librrd_private_la_LIBADD = $(RRD_LIBS) diff --git a/src/parsetime.c b/src/parsetime.c index a081d58..9ad9c55 100644 --- a/src/parsetime.c +++ b/src/parsetime.c @@ -32,6 +32,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* NOTE: nothing in here is thread-safe!!!! Not even the localtime + calls ... */ + /* * The BNF-like specification of the time syntax parsed is below: * diff --git a/src/rrd.h b/src/rrd.h index 8172c7f..bdcc720 100644 --- a/src/rrd.h +++ b/src/rrd.h @@ -5,6 +5,19 @@ ***************************************************************************** * $Id$ * $Log$ + * Revision 1.3 2003/02/13 07:05:27 oetiker + * Find attached the patch I promised to send to you. Please note that there + * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c + * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This + * library is identical to librrd, but it contains support code for per-thread + * global variables currently used for error information only. This is similar + * to how errno per-thread variables are implemented. librrd_th must be linked + * alongside of libpthred + * + * There is also a new file "THREADS", holding some documentation. + * + * -- Peter Stamfest + * * Revision 1.2 2002/05/07 21:58:32 oetiker * new command rrdtool xport integrated * -- Wolfgang Schrimm @@ -21,6 +34,7 @@ extern "C" { #define _RRDLIB_H #include +#include /* for FILE */ /* Transplanted from rrd_format.h */ typedef double rrd_value_t; /* the data storage type is @@ -42,6 +56,17 @@ int rrd_xport(int, char **, int *, time_t *, time_t *, unsigned long *, unsigned long *, char ***, rrd_value_t **); +/* thread-safe (hopefully) */ +int rrd_create_r(char *filename, + unsigned long pdp_step, time_t last_up, + int argc, char **argv); +/* NOTE: rrd_update_r is only thread-safe if no at-style time + specifications get used!!! */ +int rrd_update_r(char *filename, char *template, + int argc, char **argv); +int rrd_dump_r(char *filename); +time_t rrd_last_r(char *filename); + /* Transplanted from parsetime.h */ typedef enum { ABSOLUTE_TIME, @@ -57,6 +82,16 @@ struct time_value { struct tm tm; }; +struct rrd_context { + int len; + int errlen; + char *lib_errstr; + char *rrd_error; +}; + +/* returns the current per-thread rrd_context */ +struct rrd_context *rrd_get_context(void); + char *parsetime(char *spec, struct time_value *ptv); /* END parsetime.h */ @@ -67,6 +102,16 @@ void rrd_set_error(char *,...); void rrd_clear_error(void); int rrd_test_error(void); char *rrd_get_error(void); + +/** MULTITHREADED HELPER FUNCTIONS */ +struct rrd_context *rrd_new_context(void); +void rrd_free_context (struct rrd_context *buf); + +/* void rrd_set_error_r (struct rrd_context *, char *, ...); */ +/* void rrd_clear_error_r(struct rrd_context *); */ +/* int rrd_test_error_r (struct rrd_context *); */ +/* char *rrd_get_error_r (struct rrd_context *); */ + int LockRRD(FILE *); #endif /* _RRDLIB_H */ diff --git a/src/rrd_cgi.c b/src/rrd_cgi.c index 83a481f..53c104f 100644 --- a/src/rrd_cgi.c +++ b/src/rrd_cgi.c @@ -413,7 +413,7 @@ char* printtimelast(long argc, char **args) { rrd_clear_error(); return err; } - tm_last = *localtime(&last); + localtime_r(&last, &tm_last); strftime(buf,254,args[1],&tm_last); return buf; } @@ -432,7 +432,7 @@ char* printtimenow(long argc, char **args) { if (buf == NULL){ return stralloc("[ERROR: allocating strftime buffer]"); }; - tm_now = *localtime(&now); + localtime_r(&now, &tm_now); strftime(buf,254,args[0],&tm_now); return buf; } @@ -578,10 +578,10 @@ int parse(char **buf, long i, char *tag, char * http_time(time_t *now) { - struct tm *tmptime; + struct tm tmptime; static char buf[60]; - tmptime=gmtime(now); - strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime); + gmtime_r(now, &tmptime); + strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT", &tmptime); return(buf); } diff --git a/src/rrd_create.c b/src/rrd_create.c index 57ac76b..8aa46a7 100644 --- a/src/rrd_create.c +++ b/src/rrd_create.c @@ -8,52 +8,22 @@ #include "rrd_rpncalc.h" #include "rrd_hw.h" +#include "rrd_is_thread_safe.h" + unsigned long FnvHash(char *str); int create_hw_contingent_rras(rrd_t *rrd, unsigned short period, unsigned long hashed_name); void parseGENERIC_DS(char *def,rrd_t *rrd, int ds_idx); -/* #define DEBUG */ int rrd_create(int argc, char **argv) { - rrd_t rrd; - long i,long_tmp; - int offset; - time_t last_up; + time_t last_up = time(NULL)-10; + unsigned long pdp_step = 300; struct time_value last_up_tv; char *parsetime_error = NULL; - char *token; - unsigned short token_idx, error_flag, period=0; - unsigned long hashed_name; - /* init last_up */ - last_up = time(NULL)-10; - /* init rrd clean */ - rrd_init(&rrd); - /* static header */ - if((rrd.stat_head = calloc(1,sizeof(stat_head_t)))==NULL){ - rrd_set_error("allocating rrd.stat_head"); - return(-1); - } - - /* live header */ - if((rrd.live_head = calloc(1,sizeof(live_head_t)))==NULL){ - rrd_set_error("allocating rrd.live_head"); - return(-1); - } + long long_tmp; + int rc; - /* set some defaults */ - strcpy(rrd.stat_head->cookie,RRD_COOKIE); - /* assume the will be version 1 compatible */ - strcpy(rrd.stat_head->version,"0001"); - rrd.stat_head->float_cookie = FLOAT_COOKIE; - rrd.stat_head->ds_cnt = 0; /* this will be adjusted later */ - rrd.stat_head->rra_cnt = 0; /* ditto */ - rrd.stat_head->pdp_step = 300; /* 5 minute default */ - - /* a default value */ - rrd.ds_def = NULL; - rrd.rra_def = NULL; - while (1){ static struct option long_options[] = { @@ -67,20 +37,18 @@ rrd_create(int argc, char **argv) long_options, &option_index); if (opt == EOF) - break; + break; switch(opt) { case 'b': if ((parsetime_error = parsetime(optarg, &last_up_tv))) { rrd_set_error("start time: %s", parsetime_error ); - rrd_free(&rrd); return(-1); } if (last_up_tv.type == RELATIVE_TO_END_TIME || last_up_tv.type == RELATIVE_TO_START_TIME) { rrd_set_error("specifying time relative to the 'start' " "or 'end' makes no sense here"); - rrd_free(&rrd); return(-1); } @@ -88,7 +56,6 @@ rrd_create(int argc, char **argv) if (last_up < 3600*24*365*10){ rrd_set_error("the first entry to the RRD should be after 1980"); - rrd_free(&rrd); return(-1); } break; @@ -97,10 +64,9 @@ rrd_create(int argc, char **argv) long_tmp = atol(optarg); if (long_tmp < 1){ rrd_set_error("step size should be no less than one second"); - rrd_free(&rrd); return(-1); } - rrd.stat_head->pdp_step = long_tmp; + pdp_step = long_tmp; break; case '?': @@ -108,19 +74,66 @@ rrd_create(int argc, char **argv) rrd_set_error("unknown option '%c'", optopt); else rrd_set_error("unknown option '%s'",argv[optind-1]); - rrd_free(&rrd); return(-1); } } + + rc = rrd_create_r(argv[optind], + pdp_step, last_up, + argc - optind - 1, argv + optind + 1); + + return rc; +} + +/* #define DEBUG */ +int +rrd_create_r(char *filename, + unsigned long pdp_step, time_t last_up, + int argc, char **argv) +{ + rrd_t rrd; + long i; + int offset; + char *token; + unsigned short token_idx, error_flag, period=0; + unsigned long hashed_name; + + /* init rrd clean */ + rrd_init(&rrd); + /* static header */ + if((rrd.stat_head = calloc(1,sizeof(stat_head_t)))==NULL){ + rrd_set_error("allocating rrd.stat_head"); + return(-1); + } + + /* live header */ + if((rrd.live_head = calloc(1,sizeof(live_head_t)))==NULL){ + rrd_set_error("allocating rrd.live_head"); + return(-1); + } + + /* set some defaults */ + strcpy(rrd.stat_head->cookie,RRD_COOKIE); + /* assume the will be version 1 compatible */ + strcpy(rrd.stat_head->version,"0001"); + rrd.stat_head->float_cookie = FLOAT_COOKIE; + rrd.stat_head->ds_cnt = 0; /* this will be adjusted later */ + rrd.stat_head->rra_cnt = 0; /* ditto */ + rrd.stat_head->pdp_step = pdp_step; /* 5 minute default */ + + /* a default value */ + rrd.ds_def = NULL; + rrd.rra_def = NULL; + rrd.live_head->last_up = last_up; /* optind points to the first non-option command line arg, * in this case, the file name. */ /* Compute the FNV hash value (used by SEASONAL and DEVSEASONAL * arrays. */ - hashed_name = FnvHash(argv[optind]); - for(i=optind+1;ids_cnt); if((rrd.ds_def = rrd_realloc(rrd.ds_def, @@ -169,6 +182,7 @@ rrd_create(int argc, char **argv) } rrd.stat_head -> ds_cnt++; } else if (strncmp(argv[i],"RRA:",3)==0){ + char *tokptr; size_t old_size = sizeof(rra_def_t)*(rrd.stat_head->rra_cnt); if((rrd.rra_def = rrd_realloc(rrd.rra_def, old_size+sizeof(rra_def_t)))==NULL) @@ -179,7 +193,7 @@ rrd_create(int argc, char **argv) } memset(&rrd.rra_def[rrd.stat_head->rra_cnt], 0, sizeof(rra_def_t)); - token = strtok(&argv[i][4],":"); + token = strtok_r(&argv[i][4],":", &tokptr); token_idx = error_flag = 0; while (token != NULL) { @@ -359,7 +373,7 @@ rrd_create(int argc, char **argv) rrd_free(&rrd); return (-1); } - token = strtok(NULL,":"); + token = strtok_r(NULL,":", &tokptr); token_idx++; } /* end while */ #ifdef DEBUG @@ -401,7 +415,7 @@ rrd_create(int argc, char **argv) rrd_free(&rrd); return(-1); } - return rrd_create_fn(argv[optind],&rrd); + return rrd_create_fn(filename, &rrd); } void parseGENERIC_DS(char *def,rrd_t *rrd, int ds_idx) @@ -519,7 +533,7 @@ rrd_create_fn(char *file_name, rrd_t *rrd) rrd_value_t unknown = DNAN ; if ((rrd_file = fopen(file_name,"wb")) == NULL ) { - rrd_set_error("creating '%s': %s",file_name,strerror(errno)); + rrd_set_error("creating '%s': %s",file_name, rrd_strerror(errno)); free(rrd->stat_head); free(rrd->ds_def); free(rrd->rra_def); diff --git a/src/rrd_dump.c b/src/rrd_dump.c index f9abbfb..0fcab17 100644 --- a/src/rrd_dump.c +++ b/src/rrd_dump.c @@ -5,6 +5,19 @@ ***************************************************************************** * $Id$ * $Log$ + * Revision 1.5 2003/02/13 07:05:27 oetiker + * Find attached the patch I promised to send to you. Please note that there + * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c + * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This + * library is identical to librrd, but it contains support code for per-thread + * global variables currently used for error information only. This is similar + * to how errno per-thread variables are implemented. librrd_th must be linked + * alongside of libpthred + * + * There is also a new file "THREADS", holding some documentation. + * + * -- Peter Stamfest + * * Revision 1.4 2002/02/01 20:34:49 oetiker * fixed version number and date/time * @@ -29,18 +42,33 @@ extern char *tzname[2]; int -rrd_dump(int argc, char **argv) +rrd_dump(int argc, char **argv) +{ + int rc; + + if (argc < 2) { + rrd_set_error("Not enough arguments"); + return -1; + } + + rc = rrd_dump_r(argv[1]); + + return rc; +} + +int +rrd_dump_r(char *filename) { - int i,ii,ix,iii=0; + unsigned int i,ii,ix,iii=0; time_t now; char somestring[255]; rrd_value_t my_cdp; long rra_base, rra_start, rra_next; - FILE *in_file; - rrd_t rrd; + FILE *in_file; + rrd_t rrd; rrd_value_t value; - - if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){ + struct tm tm; + if(rrd_open(filename, &in_file,&rrd, RRD_READONLY)==-1){ return(-1); } @@ -49,8 +77,9 @@ rrd_dump(int argc, char **argv) printf("\t %s \n",RRD_VERSION); printf("\t %lu \n",rrd.stat_head->pdp_step); #if HAVE_STRFTIME + localtime_r(&rrd.live_head->last_up, &tm); strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", - localtime(&rrd.live_head->last_up)); + &tm); #else # error "Need strftime" #endif @@ -225,7 +254,7 @@ rrd_dump(int argc, char **argv) break; case CF_FAILURES: { - short vidx; + unsigned short vidx; char *violations_array = (char *) ((void*) rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch); printf("\t\t\t "); @@ -275,7 +304,8 @@ rrd_dump(int argc, char **argv) timer++; #if HAVE_STRFTIME - strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", localtime(&now)); + localtime_r(&now, &tm); + strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", &tm); #else # error "Need strftime" #endif diff --git a/src/rrd_error.c b/src/rrd_error.c index bbc66b9..33d7c2f 100644 --- a/src/rrd_error.c +++ b/src/rrd_error.c @@ -5,6 +5,19 @@ ***************************************************************************** * $Id$ * $Log$ + * Revision 1.3 2003/02/13 07:05:27 oetiker + * Find attached the patch I promised to send to you. Please note that there + * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c + * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This + * library is identical to librrd, but it contains support code for per-thread + * global variables currently used for error information only. This is similar + * to how errno per-thread variables are implemented. librrd_th must be linked + * alongside of libpthred + * + * There is also a new file "THREADS", holding some documentation. + * + * -- Peter Stamfest + * * Revision 1.2 2002/02/01 20:34:49 oetiker * fixed version number and date/time * @@ -14,11 +27,11 @@ *************************************************************************** */ #include "rrd_tool.h" -#define MAXLEN 4096 -static char rrd_error[MAXLEN] = "\0"; #include - +#define MAXLEN 4096 +#define ERRBUFLEN 256 +#define CTX (rrd_get_context()) void rrd_set_error(char *fmt, ...) @@ -27,35 +40,101 @@ rrd_set_error(char *fmt, ...) rrd_clear_error(); va_start(argp, fmt); #ifdef HAVE_VSNPRINTF - vsnprintf((char *)rrd_error, MAXLEN-1, fmt, argp); + vsnprintf(CTX->rrd_error, CTX->len, fmt, argp); #else - vsprintf((char *)rrd_error, fmt, argp); + vsprintf(CTX->rrd_error, fmt, argp); #endif va_end(argp); } int rrd_test_error(void) { - return rrd_error[0] != '\0'; + return CTX->rrd_error[0] != '\0'; } void rrd_clear_error(void){ - rrd_error[0] = '\0'; + CTX->rrd_error[0] = '\0'; } char * rrd_get_error(void){ - return (char *)rrd_error; + return CTX->rrd_error; } +#if 0 +/* PS: Keep this stuff around, maybe we want it again if we use + rrd_contexts to really associate them with single RRD files and + operations on them... Then a single thread may use more than one + context. Using these functions would require to change each and + every function containing any of the non _r versions... */ +void +rrd_set_error_r(struct rrd_context *rrd_ctx, char *fmt, ...) +{ + va_list argp; + rrd_clear_error_r(rrd_ctx); + va_start(argp, fmt); +#ifdef HAVE_VSNPRINTF + vsnprintf((char *)rrd_ctx->rrd_error, rrd_ctx->len, fmt, argp); +#else + vsprintf((char *)rrd_ctx->rrd_error, fmt, argp); +#endif + va_end(argp); +} +int +rrd_test_error_r(struct rrd_context *rrd_ctx) { + return rrd_ctx->rrd_error[0] != '\0'; +} +void +rrd_clear_error_r(struct rrd_context *rrd_ctx) { + rrd_ctx->rrd_error[0] = '\0'; +} +char * +rrd_get_error_r(struct rrd_context *rrd_ctx) { + return (char *)rrd_ctx->rrd_error; +} +#endif +/* PS: Should we move this to some other file? It is not really error + related. */ +struct rrd_context * +rrd_new_context(void) { + struct rrd_context *rrd_ctx = + (struct rrd_context *) malloc(sizeof(struct rrd_context)); + + if (rrd_ctx) { + rrd_ctx->len = 0; + rrd_ctx->rrd_error = malloc(MAXLEN); + rrd_ctx->lib_errstr = malloc(ERRBUFLEN); + if (rrd_ctx->rrd_error && rrd_ctx->lib_errstr) { + *rrd_ctx->rrd_error = 0; + *rrd_ctx->lib_errstr = 0; + rrd_ctx->len = MAXLEN; + rrd_ctx->errlen = ERRBUFLEN; + return rrd_ctx; + } + if (rrd_ctx->rrd_error) free(rrd_ctx->rrd_error); + if (rrd_ctx->lib_errstr) free(rrd_ctx->lib_errstr); + free(rrd_ctx); + } + return NULL; +} +void +rrd_free_context(struct rrd_context *rrd_ctx) { + if (rrd_ctx) { + if (rrd_ctx->rrd_error) free(rrd_ctx->rrd_error); + free(rrd_ctx); + } +} - - - - +#if 0 +void rrd_globalize_error(struct rrd_context *rrd_ctx) { + if (rrd_ctx) { + rrd_set_error(rrd_ctx->rrd_error); + } +} +#endif diff --git a/src/rrd_format.c b/src/rrd_format.c index 0e0deaa..042495d 100644 --- a/src/rrd_format.c +++ b/src/rrd_format.c @@ -5,6 +5,19 @@ ***************************************************************************** * $Id$ * $Log$ + * Revision 1.4 2003/02/13 07:05:27 oetiker + * Find attached the patch I promised to send to you. Please note that there + * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c + * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This + * library is identical to librrd, but it contains support code for per-thread + * global variables currently used for error information only. This is similar + * to how errno per-thread variables are implemented. librrd_th must be linked + * alongside of libpthred + * + * There is also a new file "THREADS", holding some documentation. + * + * -- Peter Stamfest + * * Revision 1.3 2002/02/01 20:34:49 oetiker * fixed version number and date/time * @@ -69,7 +82,7 @@ enum cf_en cf_conv(char *string) long ds_match(rrd_t *rrd,char *ds_nam){ - long i; + unsigned long i; for(i=0;istat_head->ds_cnt;i++) if ((strcmp(ds_nam,rrd->ds_def[i].ds_nam))==0) return i; diff --git a/src/rrd_graph.c b/src/rrd_graph.c index e1160f2..451e891 100644 --- a/src/rrd_graph.c +++ b/src/rrd_graph.c @@ -677,9 +677,9 @@ for (col=0;colgdes[i].format,"%c")) { /* VDEF time print */ + char ctime_buf[128]; /* PS: for ctime_r, must be >= 26 chars */ if (im->gdes[i].gf == GF_PRINT){ (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char)); sprintf((*prdata)[prlines-2],"%s (%lu)", - ctime(&printtime),printtime); + ctime_r(&printtime,ctime_buf),printtime); (*prdata)[prlines-1] = NULL; } else { sprintf(im->gdes[i].legend,"%s (%lu)", - ctime(&printtime),printtime); + ctime_r(&printtime,ctime_buf),printtime); graphelement = 1; } } else { @@ -1694,7 +1695,7 @@ vertical_grid( long factor; char graph_label[100]; double X0,Y0,Y1; /* points for filled graph and more*/ - + struct tm tm; /* the type of time grid is determined by finding the number of seconds per pixel in the graph */ @@ -1774,7 +1775,8 @@ vertical_grid( if (ti < im->start || ti > im->end) continue; #if HAVE_STRFTIME - strftime(graph_label,99,im->xlab_user.stst,localtime(&tilab)); + localtime_r(&tilab, &tm); + strftime(graph_label,99,im->xlab_user.stst, &tm); #else # error "your libc has no strftime I guess we'll abort the exercise here." #endif @@ -2195,15 +2197,15 @@ graph_paint(image_desc_t *im, char ***calcpr) double areazero = 0.0; enum gf_en stack_gf = GF_PRINT; graph_desc_t *lastgdes = NULL; - + /* if we are lazy and there is nothing to PRINT ... quit now */ if (lazy && im->prt_c==0) return 0; - + /* pull the data from the rrd files ... */ if(data_fetch(im)==-1) return -1; - + /* evaluate VDEF and CDEF operations ... */ if(data_calc(im)==-1) return -1; @@ -2470,7 +2472,7 @@ graph_paint(image_desc_t *im, char ***calcpr) } else { if ((fo = fopen(im->graphfile,"wb")) == NULL) { rrd_set_error("Opening '%s' for write: %s",im->graphfile, - strerror(errno)); + rrd_strerror(errno)); return (-1); } } @@ -2488,7 +2490,7 @@ graph_paint(image_desc_t *im, char ***calcpr) int gdes_alloc(image_desc_t *im){ - long def_step = (im->end-im->start)/im->xsize; + unsigned long def_step = (im->end-im->start)/im->xsize; if (im->step > def_step) /* step can be increassed ... no decreassed */ def_step = im->step; @@ -2623,7 +2625,7 @@ rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize) void rrd_graph_init(image_desc_t *im) { - int i; + unsigned int i; #ifdef HAVE_TZSET tzset(); diff --git a/src/rrd_graph_helper.c b/src/rrd_graph_helper.c index bea9d7b..a33a8ef 100644 --- a/src/rrd_graph_helper.c +++ b/src/rrd_graph_helper.c @@ -293,8 +293,8 @@ rrd_parse_def(char *line, unsigned int *eaten, graph_desc_t *gdp, image_desc_t * start_tv.type = end_tv.type=ABSOLUTE_TIME; start_tv.offset = end_tv.offset=0; - memcpy(&start_tv.tm, localtime(&gdp->start) , sizeof(struct tm) ); - memcpy(&end_tv.tm, localtime(&gdp->end) , sizeof(struct tm) ); + localtime_r(&gdp->start, &start_tv.tm); + localtime_r(&gdp->end, &end_tv.tm); dprintf("- parsing '%s'\n",&line[*eaten]); dprintf("- from line '%s'\n",line); diff --git a/src/rrd_info.c b/src/rrd_info.c index d236460..1e76941 100644 --- a/src/rrd_info.c +++ b/src/rrd_info.c @@ -12,6 +12,7 @@ static char * sprintf_alloc(char *, ...); static info_t *push(info_t *, char *, enum info_type, infoval); info_t *rrd_info(int, char **); +info_t *rrd_info_r(char *filename); /* allocate memory for string */ static char * @@ -60,10 +61,26 @@ static info_t return(next); } - + info_t * rrd_info(int argc, char **argv) { - int i,ii=0; + info_t *info; + + if(argc < 2){ + rrd_set_error("please specify an rrd"); + return NULL; + } + + info = rrd_info_r(argv[1]); + + return(info); +} + + + +info_t * +rrd_info_r(char *filename) { + unsigned int i,ii=0; FILE *in_file; rrd_t rrd; info_t *data,*cd; @@ -71,12 +88,12 @@ rrd_info(int argc, char **argv) { enum cf_en current_cf; enum dst_en current_ds; - if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){ + if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1){ return(NULL); } fclose(in_file); - info.u_str=argv[1]; + info.u_str=filename; cd=push(NULL,sprintf_alloc("filename"), RD_I_STR, info); data=cd; @@ -193,7 +210,7 @@ rrd_info(int argc, char **argv) { break; case CF_FAILURES: { - short j; + unsigned short j; char *violations_array; char history[MAX_FAILURES_WINDOW_LEN+1]; violations_array = (char*) rrd.cdp_prep[i*rrd.stat_head->ds_cnt +ii].scratch; diff --git a/src/rrd_is_thread_safe.h b/src/rrd_is_thread_safe.h new file mode 100644 index 0000000..eb5ccf5 --- /dev/null +++ b/src/rrd_is_thread_safe.h @@ -0,0 +1,26 @@ +/***************************************************************************** + * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002 + * This file: Copyright 2003 Peter Stamfest + * & Tobias Oetiker + * Distributed under the GPL + ***************************************************************************** + * rrd_is_thread_safe.c Poisons some nasty function calls using GNU cpp + ***************************************************************************** + * $Id$ + *************************************************************************** */ + +#ifndef _RRD_IS_THREAD_SAFE_H +#define _RRD_IS_THREAD_SAFE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#undef strerror +#pragma GCC poison strtok asctime ctime gmtime localtime tmpnam strerror + +#ifdef __cplusplus +} +#endif + +#endif /*_RRD_IS_THREAD_SAFE_H */ diff --git a/src/rrd_last.c b/src/rrd_last.c index b9caa64..78ba5a3 100644 --- a/src/rrd_last.c +++ b/src/rrd_last.c @@ -11,16 +11,28 @@ time_t rrd_last(int argc, char **argv) { - FILE *in_file; time_t lastup; - rrd_t rrd; - if(argc < 2){ rrd_set_error("please specify an rrd"); return(-1); } - if(rrd_open(argv[1], &in_file, &rrd, RRD_READONLY)==-1){ + + lastup = rrd_last_r(argv[1]); + + return(lastup); +} + + +time_t +rrd_last_r(char *filename) +{ + FILE *in_file; + time_t lastup; + + rrd_t rrd; + + if(rrd_open(filename, &in_file, &rrd, RRD_READONLY)==-1){ return(-1); } lastup = rrd.live_head->last_up; @@ -28,7 +40,5 @@ rrd_last(int argc, char **argv) fclose(in_file); return(lastup); } - - diff --git a/src/rrd_not_thread_safe.c b/src/rrd_not_thread_safe.c new file mode 100644 index 0000000..17ab404 --- /dev/null +++ b/src/rrd_not_thread_safe.c @@ -0,0 +1,35 @@ +/***************************************************************************** + * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002 + * This file: Copyright 2003 Peter Stamfest + * & Tobias Oetiker + * Distributed under the GPL + ***************************************************************************** + * rrd_not_thread_safe.c Contains routines used when thread safety is not + * an issue + ***************************************************************************** + * $Id$ + *************************************************************************** */ +#include "rrd.h" +#include "rrd_tool.h" +#define MAXLEN 4096 +#define ERRBUFLEN 256 +static char rrd_error[MAXLEN] = "\0"; +static char rrd_liberror[ERRBUFLEN] = "\0"; +/* The global context is very useful in the transition period to even + more thread-safe stuff, it can be used whereever we need a context + and do not need to worry about concurrency. */ +static struct rrd_context global_ctx = { + sizeof(rrd_error), + sizeof(rrd_liberror), + rrd_error, + rrd_liberror +}; +#include + +struct rrd_context *rrd_get_context() { + return &global_ctx; +} + +const char *rrd_strerror(int err) { + return strerror(err); +} diff --git a/src/rrd_open.c b/src/rrd_open.c index c0e6545..b85ceef 100644 --- a/src/rrd_open.c +++ b/src/rrd_open.c @@ -5,6 +5,19 @@ ***************************************************************************** * $Id$ * $Log$ + * Revision 1.6 2003/02/13 07:05:27 oetiker + * Find attached the patch I promised to send to you. Please note that there + * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c + * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This + * library is identical to librrd, but it contains support code for per-thread + * global variables currently used for error information only. This is similar + * to how errno per-thread variables are implemented. librrd_th must be linked + * alongside of libpthred + * + * There is also a new file "THREADS", holding some documentation. + * + * -- Peter Stamfest + * * Revision 1.5 2002/06/20 00:21:03 jake * More Win32 build changes; thanks to Kerry Calvert. * @@ -55,7 +68,7 @@ rrd_open(char *file_name, FILE **in_file, rrd_t *rrd, int rdwr) } if (((*in_file) = fopen(file_name,mode)) == NULL ){ - rrd_set_error("opening '%s': %s",file_name, strerror(errno)); + rrd_set_error("opening '%s': %s",file_name, rrd_strerror(errno)); return (-1); } /* @@ -124,14 +137,14 @@ void rrd_init(rrd_t *rrd) void rrd_free(rrd_t *rrd) { - free(rrd->stat_head); - free(rrd->ds_def); - free(rrd->rra_def); - free(rrd->live_head); - free(rrd->rra_ptr); - free(rrd->pdp_prep); - free(rrd->cdp_prep); - free(rrd->rrd_value); + if (rrd->stat_head) free(rrd->stat_head); + if (rrd->ds_def) free(rrd->ds_def); + if (rrd->rra_def) free(rrd->rra_def); + if (rrd->live_head) free(rrd->live_head); + if (rrd->rra_ptr) free(rrd->rra_ptr); + if (rrd->pdp_prep) free(rrd->pdp_prep); + if (rrd->cdp_prep) free(rrd->cdp_prep); + if (rrd->rrd_value) free(rrd->rrd_value); } /* routine used by external libraries to free memory allocated by @@ -139,7 +152,7 @@ void rrd_free(rrd_t *rrd) void rrd_freemem(void *mem) { - free(mem); + if (mem) free(mem); } int readfile(char *file_name, char **buffer, int skipfirst){ @@ -149,7 +162,7 @@ int readfile(char *file_name, char **buffer, int skipfirst){ if ((strcmp("-",file_name) == 0)) { input = stdin; } else { if ((input = fopen(file_name,"rb")) == NULL ){ - rrd_set_error("opening '%s': %s",file_name,strerror(errno)); + rrd_set_error("opening '%s': %s",file_name,rrd_strerror(errno)); return (-1); } } diff --git a/src/rrd_restore.c b/src/rrd_restore.c index 6d7fdf8..ccac072 100644 --- a/src/rrd_restore.c +++ b/src/rrd_restore.c @@ -409,7 +409,7 @@ rrd_write(char *file_name, rrd_t *rrd) } else { int fd = open(file_name,O_RDWR|O_CREAT|O_EXCL,0666); if (fd == -1 || (rrd_file = fdopen(fd,"wb")) == NULL) { - rrd_set_error("creating '%s': %s",file_name,strerror(errno)); + rrd_set_error("creating '%s': %s",file_name,rrd_strerror(errno)); if (fd != -1) close(fd); return(-1); @@ -557,7 +557,7 @@ parse_FAILURES_history(char **buf, rrd_t *rrd, int rra_index, int ds_index) { char history[MAX_FAILURES_WINDOW_LEN + 1]; char *violations_array; - short i; + unsigned short i; /* 28 = MAX_FAILURES_WINDOW_LEN */ read_tag(buf, "history", "%28[0-1]", history); diff --git a/src/rrd_rpncalc.c b/src/rrd_rpncalc.c index 8e64d8b..9bb35d2 100644 --- a/src/rrd_rpncalc.c +++ b/src/rrd_rpncalc.c @@ -222,7 +222,7 @@ void parseCDEF_DS(char *def,rrd_t *rrd, int ds_idx) */ long lookup_DS(void *rrd_vptr,char *ds_name) { - int i; + unsigned int i; rrd_t *rrd; rrd = (rrd_t *) rrd_vptr; @@ -670,20 +670,20 @@ int tzoffset( time_t now ){ int gm_sec, gm_min, gm_hour, gm_yday, gm_year, l_sec, l_min, l_hour, l_yday, l_year; - struct tm *t; + struct tm t; int off; - t = gmtime(&now); - gm_sec = t->tm_sec; - gm_min = t->tm_min; - gm_hour = t->tm_hour; - gm_yday = t->tm_yday; - gm_year = t->tm_year; - t = localtime(&now); - l_sec = t->tm_sec; - l_min = t->tm_min; - l_hour = t->tm_hour; - l_yday = t->tm_yday; - l_year = t->tm_year; + gmtime_r(&now, &t); + gm_sec = t.tm_sec; + gm_min = t.tm_min; + gm_hour = t.tm_hour; + gm_yday = t.tm_yday; + gm_year = t.tm_year; + localtime_r(&now, &t); + l_sec = t.tm_sec; + l_min = t.tm_min; + l_hour = t.tm_hour; + l_yday = t.tm_yday; + l_year = t.tm_year; off = (l_sec-gm_sec)+(l_min-gm_min)*60+(l_hour-gm_hour)*3600; if ( l_yday > gm_yday || l_year > gm_year){ off += 24*3600; diff --git a/src/rrd_stat.c b/src/rrd_stat.c index a7b1f33..bd48ec6 100644 --- a/src/rrd_stat.c +++ b/src/rrd_stat.c @@ -29,7 +29,7 @@ rrd_stat(int argc, char **argv) printf("\t %lu \n",rrd.stat_head->pdp_step); #if HAVE_STRFTIME strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", - localtime(&rrd.live_head->last_up)); + localtime_r(&rrd.live_head->last_up, &tm)); #else # error "Need strftime" #endif @@ -116,7 +116,7 @@ rrd_stat(int argc, char **argv) timer++; #if HAVE_STRFTIME - strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", localtime(&now)); + strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", localtime_r(&now, &tm)); #else # error "Need strftime" #endif diff --git a/src/rrd_thread_safe.c b/src/rrd_thread_safe.c new file mode 100644 index 0000000..223b586 --- /dev/null +++ b/src/rrd_thread_safe.c @@ -0,0 +1,67 @@ +/***************************************************************************** + * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002 + * This file: Copyright 2003 Peter Stamfest + * & Tobias Oetiker + * Distributed under the GPL + ***************************************************************************** + * rrd_thread_safe.c Contains routines used when thread safety is required + ***************************************************************************** + * $Id$ + *************************************************************************** */ + +#include +#include +#include +#include "rrd.h" +#include "rrd_tool.h" + +/* Key for the thread-specific rrd_context */ +static pthread_key_t context_key; + +/* Once-only initialisation of the key */ +static pthread_once_t context_key_once = PTHREAD_ONCE_INIT; + +/* Free the thread-specific rrd_context - we might actually use + rrd_free_context instead... + */ +static void context_destroy_context(void *ctx_) +{ + struct rrd_context *ctx = ctx_; + if (ctx) rrd_free_context(ctx); +} + +/* Allocate the key */ +static void context_get_key() +{ + pthread_key_create(&context_key, context_destroy_context); +} + +struct rrd_context *rrd_get_context(void) { + struct rrd_context *ctx; + + pthread_once(&context_key_once, context_get_key); + ctx = pthread_getspecific(context_key); + if (!ctx) { + ctx = rrd_new_context(); + pthread_setspecific(context_key, ctx); + } + return ctx; +} + +#ifdef HAVE_STRERROR_R +const char *rrd_strerror(int err) { + struct rrd_context *ctx = rrd_get_context(); + return strerror_r(err, ctx->lib_errstr, ctx->errlen); +} +#else +const char *rrd_strerror(int err) { + static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; + struct rrd_context *ctx; + ctx = rrd_get_context(); + pthread_mutex_lock(&mtx); + strncpy(ctx->lib_errstr, strerror(err), ctx->errlen); + pthread_mutex_unlock(&mtx); + return ctx->lib_errstr; +} +#endif + diff --git a/src/rrd_tool.c b/src/rrd_tool.c index c424355..5fcb440 100644 --- a/src/rrd_tool.c +++ b/src/rrd_tool.c @@ -360,6 +360,8 @@ int main(int argc, char *argv[]) return 0; } +/* HandleInputLine is NOT thread safe - due to readdir issues, + resolving them portably is not really simple. */ int HandleInputLine(int argc, char **argv, FILE* out) { #if defined(HAVE_OPENDIR) && defined (HAVE_READDIR) @@ -505,7 +507,7 @@ int HandleInputLine(int argc, char **argv, FILE* out) else if (strcmp("update", argv[1]) == 0) rrd_update(argc-1, &argv[1]); else if (strcmp("fetch", argv[1]) == 0) { - time_t start,end; + time_t start,end, ti; unsigned long step, ds_cnt,i,ii; rrd_value_t *data,*datai; char **ds_namv; @@ -515,8 +517,8 @@ int HandleInputLine(int argc, char **argv, FILE* out) for (i = 0; i\n", LEGEND_TAG); printf(" \n", META_TAG); printf(" <%s>\n", DATA_TAG); - for (i = start+step; i <= end; i += step) { + for (ti = start+step; ti <= end; ti += step) { printf (" <%s>", DATA_ROW_TAG); - printf ("<%s>%lu", COL_TIME_TAG, i, COL_TIME_TAG); + printf ("<%s>%lu", COL_TIME_TAG, ti, COL_TIME_TAG); for (j = 0; j < col_cnt; j++) { rrd_value_t newval = DNAN; newval = *ptr; diff --git a/src/rrd_tool.h b/src/rrd_tool.h index f21a17a..6d7652b 100644 --- a/src/rrd_tool.h +++ b/src/rrd_tool.h @@ -164,7 +164,9 @@ void rrd_freemem(void *mem); void rrd_init(rrd_t *rrd); int rrd_open(char *file_name, FILE **in_file, rrd_t *rrd, int rdwr); +int rrd_open_r(char *file_name, FILE **in_file, rrd_t *rrd, int rdwr); int readfile(char *file, char **buffer, int skipfirst); +int readfile_r(char *file, char **buffer, int skipfirst); #define RRD_READONLY 0 #define RRD_READWRITE 1 @@ -174,6 +176,14 @@ enum dst_en dst_conv(char *string); long ds_match(rrd_t *rrd,char *ds_nam); double rrd_diff(char *a, char *b); + /* rrd_strerror is thread safe, but still it uses a global buffer + (but one per thread), thus subsequent calls within a single + thread overwrite the same buffer */ +const char *rrd_strerror(int err); + +/* just a defensive work-around... */ +#define strerror(x) rrd_strerror(x) + #endif #ifdef __cplusplus diff --git a/src/rrd_update.c b/src/rrd_update.c index e4f9712..a6f93a8 100644 --- a/src/rrd_update.c +++ b/src/rrd_update.c @@ -5,6 +5,19 @@ ***************************************************************************** * $Id$ * $Log$ + * Revision 1.7 2003/02/13 07:05:27 oetiker + * Find attached the patch I promised to send to you. Please note that there + * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c + * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This + * library is identical to librrd, but it contains support code for per-thread + * global variables currently used for error information only. This is similar + * to how errno per-thread variables are implemented. librrd_th must be linked + * alongside of libpthred + * + * There is also a new file "THREADS", holding some documentation. + * + * -- Peter Stamfest + * * Revision 1.6 2002/02/01 20:34:49 oetiker * fixed version number and date/time * @@ -41,8 +54,6 @@ #include "rrd_tool.h" #include #include -#include "rrd_hw.h" -#include "rrd_rpncalc.h" #ifdef WIN32 #include @@ -50,11 +61,18 @@ #include #endif +#include "rrd_hw.h" +#include "rrd_rpncalc.h" + +#include "rrd_is_thread_safe.h" + /* Local prototypes */ int LockRRD(FILE *rrd_file); -void write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current, - unsigned short CDP_scratch_idx, FILE *rrd_file); - +void write_RRA_row (rrd_t *rrd, unsigned long rra_idx, + unsigned long *rra_current, + unsigned short CDP_scratch_idx, FILE *rrd_file); +int rrd_update_r(char *filename, char *template, int argc, char **argv); + #define IFDNAN(X,Y) (isnan(X) ? (Y) : (X)); @@ -81,10 +99,54 @@ main(int argc, char **argv){ int rrd_update(int argc, char **argv) { + char *template = NULL; + int rc; + + while (1) { + static struct option long_options[] = + { + {"template", required_argument, 0, 't'}, + {0,0,0,0} + }; + int option_index = 0; + int opt; + opt = getopt_long(argc, argv, "t:", + long_options, &option_index); + + if (opt == EOF) + break; + + switch(opt) { + case 't': + template = optarg; + break; + + case '?': + rrd_set_error("unknown option '%s'",argv[optind-1]); + /* rrd_free(&rrd); */ + return(-1); + } + } + + /* need at least 2 arguments: filename, data. */ + if (argc-optind < 2) { + rrd_set_error("Not enough arguments"); + + return -1; + } + + rc = rrd_update_r(argv[optind], template, + argc - optind - 1, argv + optind + 1); + return rc; +} + +int +rrd_update_r(char *filename, char *template, int argc, char **argv) +{ int arg_i = 2; short j; - long i,ii,iii=1; + unsigned long i,ii,iii=1; unsigned long rra_begin; /* byte pointer to the rra * area in the rrd file. this @@ -118,14 +180,13 @@ rrd_update(int argc, char **argv) long *tmpl_idx; /* index representing the settings transported by the template index */ - long tmpl_cnt = 2; /* time and data */ + unsigned long tmpl_cnt = 2; /* time and data */ FILE *rrd_file; rrd_t rrd; time_t current_time = time(NULL); char **updvals; int schedule_smooth = 0; - char *template = NULL; rrd_value_t *seasonal_coef = NULL, *last_seasonal_coef = NULL; /* a vector of future Holt-Winters seasonal coefs */ unsigned long elapsed_pdp_st; @@ -145,39 +206,13 @@ rrd_update(int argc, char **argv) rpnstack_init(&rpnstack); - while (1) { - static struct option long_options[] = - { - {"template", required_argument, 0, 't'}, - {0,0,0,0} - }; - int option_index = 0; - int opt; - opt = getopt_long(argc, argv, "t:", - long_options, &option_index); - - if (opt == EOF) - break; - - switch(opt) { - case 't': - template = optarg; - break; - - case '?': - rrd_set_error("unknown option '%s'",argv[optind-1]); - rrd_free(&rrd); - return(-1); - } - } - - /* need at least 2 arguments: filename, data. */ - if (argc-optind < 2) { + /* need at least 1 arguments: data. */ + if (argc < 1) { rrd_set_error("Not enough arguments"); return -1; } - if(rrd_open(argv[optind],&rrd_file,&rrd, RRD_READWRITE)==-1){ + if(rrd_open(filename,&rrd_file,&rrd, RRD_READWRITE)==-1){ return -1; } rra_current = rra_start = rra_begin = ftell(rrd_file); @@ -244,7 +279,7 @@ rrd_update(int argc, char **argv) if (template) { char *dsname; - int tmpl_len; + unsigned int tmpl_len; dsname = template; tmpl_cnt = 1; /* the first entry is the time */ tmpl_len = strlen(template); @@ -288,7 +323,7 @@ rrd_update(int argc, char **argv) } /* loop through the arguments. */ - for(arg_i=optind+1; arg_i rra_cnt; ++i) @@ -1219,7 +1254,7 @@ LockRRD(FILE *rrdfile) void write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current, - unsigned short CDP_scratch_idx, FILE *rrd_file) + unsigned short CDP_scratch_idx, FILE *rrd_file) { unsigned long ds_idx, cdp_idx; -- 2.11.0