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
--- /dev/null
+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)
+
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, ,
$(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)
* 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:
*
*****************************************************************************
* $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 <peter@stamfest.at>
+ *
* Revision 1.2 2002/05/07 21:58:32 oetiker
* new command rrdtool xport integrated
* -- Wolfgang Schrimm <Wolfgang.Schrimm@urz.uni-heidelberg.de>
#define _RRDLIB_H
#include <time.h>
+#include <stdio.h> /* for FILE */
/* Transplanted from rrd_format.h */
typedef double rrd_value_t; /* the data storage type is
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,
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 */
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 */
rrd_clear_error();
return err;
}
- tm_last = *localtime(&last);
+ localtime_r(&last, &tm_last);
strftime(buf,254,args[1],&tm_last);
return buf;
}
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;
}
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);
}
#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[] =
{
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);
}
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;
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 '?':
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;i<argc;i++){
- int ii;
+ hashed_name = FnvHash(filename);
+ for(i=0;i<argc;i++){
+ unsigned int ii;
if (strncmp(argv[i],"DS:",3)==0){
size_t old_size = sizeof(ds_def_t)*(rrd.stat_head->ds_cnt);
if((rrd.ds_def = rrd_realloc(rrd.ds_def,
}
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)
}
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)
{
rrd_free(&rrd);
return (-1);
}
- token = strtok(NULL,":");
+ token = strtok_r(NULL,":", &tokptr);
token_idx++;
} /* end while */
#ifdef DEBUG
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)
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);
*****************************************************************************
* $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 <peter@stamfest.at>
+ *
* Revision 1.4 2002/02/01 20:34:49 oetiker
* fixed version number and date/time
*
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);
}
printf("\t<version> %s </version>\n",RRD_VERSION);
printf("\t<step> %lu </step> <!-- Seconds -->\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
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<history> ");
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
*****************************************************************************
* $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 <peter@stamfest.at>
+ *
* Revision 1.2 2002/02/01 20:34:49 oetiker
* fixed version number and date/time
*
*************************************************************************** */
#include "rrd_tool.h"
-#define MAXLEN 4096
-static char rrd_error[MAXLEN] = "\0";
#include <stdarg.h>
-
+#define MAXLEN 4096
+#define ERRBUFLEN 256
+#define CTX (rrd_get_context())
void
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
*****************************************************************************
* $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 <peter@stamfest.at>
+ *
* Revision 1.3 2002/02/01 20:34:49 oetiker
* fixed version number and date/time
*
long
ds_match(rrd_t *rrd,char *ds_nam){
- long i;
+ unsigned long i;
for(i=0;i<rrd->stat_head->ds_cnt;i++)
if ((strcmp(ds_nam,rrd->ds_def[i].ds_nam))==0)
return i;
relevant rrds ... */
int
-data_fetch( image_desc_t *im )
+data_fetch(image_desc_t *im )
{
- int i,ii;
+ unsigned int i,ii;
int skip;
/* pull the data from the log files ... */
)
{
struct tm tm;
- tm = *localtime(&start);
+ localtime_r(&start, &tm);
switch(baseint){
case TMT_SECOND:
tm.tm_sec -= tm.tm_sec % basestep; break;
{
struct tm tm;
time_t madetime;
- tm = *localtime(¤t);
+ localtime_r(¤t, &tm);
do {
switch(baseint){
case TMT_SECOND:
} /* prepare printval */
if (!strcmp(im->gdes[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 {
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 */
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
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;
} 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);
}
}
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;
void
rrd_graph_init(image_desc_t *im)
{
- int i;
+ unsigned int i;
#ifdef HAVE_TZSET
tzset();
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);
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 *
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;
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;
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;
--- /dev/null
+/*****************************************************************************
+ * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002
+ * This file: Copyright 2003 Peter Stamfest <peter@stamfest.at>
+ * & 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 */
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;
fclose(in_file);
return(lastup);
}
-
-
--- /dev/null
+/*****************************************************************************
+ * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002
+ * This file: Copyright 2003 Peter Stamfest <peter@stamfest.at>
+ * & 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 <stdarg.h>
+
+struct rrd_context *rrd_get_context() {
+ return &global_ctx;
+}
+
+const char *rrd_strerror(int err) {
+ return strerror(err);
+}
*****************************************************************************
* $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 <peter@stamfest.at>
+ *
* Revision 1.5 2002/06/20 00:21:03 jake
* More Win32 build changes; thanks to Kerry Calvert.
*
}
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);
}
/*
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
void rrd_freemem(void *mem)
{
- free(mem);
+ if (mem) free(mem);
}
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);
}
}
} 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);
{
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);
*/
long lookup_DS(void *rrd_vptr,char *ds_name)
{
- int i;
+ unsigned int i;
rrd_t *rrd;
rrd = (rrd_t *) rrd_vptr;
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;
printf("\t<step> %lu </step> <!-- Seconds -->\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
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
--- /dev/null
+/*****************************************************************************
+ * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002
+ * This file: Copyright 2003 Peter Stamfest <peter@stamfest.at>
+ * & Tobias Oetiker
+ * Distributed under the GPL
+ *****************************************************************************
+ * rrd_thread_safe.c Contains routines used when thread safety is required
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+
+#include <pthread.h>
+#include <string.h>
+#include <error.h>
+#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
+
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)
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;
for (i = 0; i<ds_cnt;i++)
printf("%14s",ds_namv[i]);
printf ("\n\n");
- for (i = start+step; i <= end; i += step){
- printf("%10lu:", i);
+ for (ti = start+step; ti <= end; ti += step){
+ printf("%10lu:", ti);
for (ii = 0; ii < ds_cnt; ii++)
printf(" %0.10e", *(datai++));
printf("\n");
}
} else if (strcmp("xport", argv[1]) == 0) {
int xxsize;
- unsigned long int i = 0, j = 0;
- time_t start,end;
+ unsigned long int j = 0;
+ time_t start,end, ti;
unsigned long step, col_cnt,row_cnt;
rrd_value_t *data,*ptr;
char **legend_v;
printf(" </%s>\n", LEGEND_TAG);
printf(" </%s>\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</%s>", COL_TIME_TAG, i, COL_TIME_TAG);
+ printf ("<%s>%lu</%s>", COL_TIME_TAG, ti, COL_TIME_TAG);
for (j = 0; j < col_cnt; j++) {
rrd_value_t newval = DNAN;
newval = *ptr;
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
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
*****************************************************************************
* $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 <peter@stamfest.at>
+ *
* Revision 1.6 2002/02/01 20:34:49 oetiker
* fixed version number and date/time
*
#include "rrd_tool.h"
#include <sys/types.h>
#include <fcntl.h>
-#include "rrd_hw.h"
-#include "rrd_rpncalc.h"
#ifdef WIN32
#include <sys/locking.h>
#include <io.h>
#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));
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
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;
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);
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);
}
/* loop through the arguments. */
- for(arg_i=optind+1; arg_i<argc;arg_i++) {
+ for(arg_i=0; arg_i<argc;arg_i++) {
char *stepper = malloc((strlen(argv[arg_i])+1)*sizeof(char));
char *step_start = stepper;
char *p;
if (ds_tv.type == RELATIVE_TO_END_TIME ||
ds_tv.type == RELATIVE_TO_START_TIME) {
rrd_set_error("specifying time relative to the 'start' "
- "or 'end' makes no sense here: %s",
+ "or 'end' makes no sense here: %s",
updvals[0]);
free(step_start);
break;
if (schedule_smooth)
{
#ifndef WIN32
- rrd_file = fopen(argv[optind],"r+");
+ rrd_file = fopen(filename,"r+");
#else
- rrd_file = fopen(argv[optind],"rb+");
+ rrd_file = fopen(filename,"rb+");
#endif
rra_start = rra_begin;
for (i = 0; i < rrd.stat_head -> rra_cnt; ++i)
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;