Find attached the patch I promised to send to you. Please note that there
authoroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Thu, 13 Feb 2003 07:05:59 +0000 (07:05 +0000)
committeroetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa>
Thu, 13 Feb 2003 07:05:59 +0000 (07:05 +0000)
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>

git-svn-id: svn://svn.oetiker.ch/rrdtool/trunk/program@180 a5681a0c-68f1-0310-ab6d-d61299d08faa

25 files changed:
Makefile.am
THREADS [new file with mode: 0644]
configure.ac
src/Makefile.am
src/parsetime.c
src/rrd.h
src/rrd_cgi.c
src/rrd_create.c
src/rrd_dump.c
src/rrd_error.c
src/rrd_format.c
src/rrd_graph.c
src/rrd_graph_helper.c
src/rrd_info.c
src/rrd_is_thread_safe.h [new file with mode: 0644]
src/rrd_last.c
src/rrd_not_thread_safe.c [new file with mode: 0644]
src/rrd_open.c
src/rrd_restore.c
src/rrd_rpncalc.c
src/rrd_stat.c
src/rrd_thread_safe.c [new file with mode: 0644]
src/rrd_tool.c
src/rrd_tool.h
src/rrd_update.c

index 4bba56a..ea6447a 100644 (file)
@@ -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 (file)
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)
+  
index 7b7155e..afe8e4d 100644 (file)
@@ -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, ,
index 3aca332..f7965b1 100644 (file)
@@ -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)
index a081d58..9ad9c55 100644 (file)
@@ -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:
  *                                                               
index 8172c7f..bdcc720 100644 (file)
--- 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 <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>
@@ -21,6 +34,7 @@ extern "C" {
 #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
@@ -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 */
index 83a481f..53c104f 100644 (file)
@@ -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);
 }
index 57ac76b..8aa46a7 100644 (file)
@@ -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;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,
@@ -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);
index f9abbfb..0fcab17 100644 (file)
@@ -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 <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);
     }
 
@@ -49,8 +77,9 @@ rrd_dump(int argc, char **argv)
     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
@@ -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<history> ");
@@ -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
index bbc66b9..33d7c2f 100644 (file)
@@ -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 <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, ...)
@@ -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
index 0e0deaa..042495d 100644 (file)
@@ -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 <peter@stamfest.at>
+ *
  * 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;i<rrd->stat_head->ds_cnt;i++)
        if ((strcmp(ds_nam,rrd->ds_def[i].ds_nam))==0)
            return i;
index e1160f2..451e891 100644 (file)
@@ -677,9 +677,9 @@ for (col=0;col<row_cnt;col++) {
    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 ... */
@@ -1096,7 +1096,7 @@ find_first_time(
     )
 {
     struct tm tm;
-    tm = *localtime(&start);
+    localtime_r(&start, &tm);
     switch(baseint){
     case TMT_SECOND:
        tm.tm_sec -= tm.tm_sec % basestep; break;
@@ -1149,7 +1149,7 @@ find_next_time(
 {
     struct tm tm;
     time_t madetime;
-    tm = *localtime(&current);
+    localtime_r(&current, &tm);
     do {
        switch(baseint){
        case TMT_SECOND:
@@ -1254,14 +1254,15 @@ print_calc(image_desc_t *im, char ***prdata)
            } /* 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 {
@@ -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();
index bea9d7b..a33a8ef 100644 (file)
@@ -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);
index d236460..1e76941 100644 (file)
@@ -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 (file)
index 0000000..eb5ccf5
--- /dev/null
@@ -0,0 +1,26 @@
+/*****************************************************************************
+ * 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 */
index b9caa64..78ba5a3 100644 (file)
 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 (file)
index 0000000..17ab404
--- /dev/null
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * 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);
+}
index c0e6545..b85ceef 100644 (file)
@@ -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 <peter@stamfest.at>
+ *
  * 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);
       }
     }
index 6d7fdf8..ccac072 100644 (file)
@@ -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);
index 8e64d8b..9bb35d2 100644 (file)
@@ -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;
index a7b1f33..bd48ec6 100644 (file)
@@ -29,7 +29,7 @@ rrd_stat(int argc, char **argv)
     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
@@ -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 (file)
index 0000000..223b586
--- /dev/null
@@ -0,0 +1,67 @@
+/*****************************************************************************
+ * 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
+
index c424355..5fcb440 100644 (file)
@@ -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<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");
@@ -528,8 +530,8 @@ int HandleInputLine(int argc, char **argv, FILE* out)
        }
     } 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;
@@ -555,9 +557,9 @@ int HandleInputLine(int argc, char **argv, FILE* out)
          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;
index f21a17a..6d7652b 100644 (file)
@@ -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
index e4f9712..a6f93a8 100644 (file)
@@ -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 <peter@stamfest.at>
+ *
  * 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 <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));
 
 
@@ -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<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;
@@ -355,7 +390,7 @@ rrd_update(int argc, char **argv)
            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;
@@ -1149,9 +1184,9 @@ rrd_update(int argc, char **argv)
        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)
@@ -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;