New function ovs_strerror() as a thread-safe replacement for strerror().
[sliver-openvswitch.git] / lib / util.c
index 83d3ff8..1bb5096 100644 (file)
@@ -18,6 +18,7 @@
 #include "util.h"
 #include <errno.h>
 #include <limits.h>
+#include <pthread.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -27,7 +28,7 @@
 #include <unistd.h>
 #include "byte-order.h"
 #include "coverage.h"
-#include "openvswitch/types.h"
+#include "ovs-thread.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(util);
@@ -44,6 +45,9 @@ const char *subprogram_name = "";
 /* --version option output. */
 static char *program_version;
 
+/* Buffer used by ovs_strerror(). */
+DEFINE_PER_THREAD_DATA(struct { char s[128]; }, strerror_buffer, { "" });
+
 void
 ovs_assert_failure(const char *where, const char *function,
                    const char *condition)
@@ -306,19 +310,41 @@ ovs_error_valist(int err_no, const char *format, va_list args)
 const char *
 ovs_retval_to_string(int retval)
 {
-    static char unknown[48];
+    return (!retval ? ""
+            : retval == EOF ? "End of file"
+            : ovs_strerror(retval));
+}
 
-    if (!retval) {
-        return "";
-    }
-    if (retval > 0) {
-        return strerror(retval);
-    }
-    if (retval == EOF) {
-        return "End of file";
+const char *
+ovs_strerror(int error)
+{
+    enum { BUFSIZE = sizeof strerror_buffer_get()->s };
+    int save_errno;
+    char *buffer;
+    char *s;
+
+    save_errno = errno;
+    buffer = strerror_buffer_get()->s;
+
+#if STRERROR_R_CHAR_P
+    /* GNU style strerror_r() might return an immutable static string, or it
+     * might write and return 'buffer', but in either case we can pass the
+     * returned string directly to the caller. */
+    s = strerror_r(error, buffer, BUFSIZE);
+#else  /* strerror_r() returns an int. */
+    s = buffer;
+    if (strerror_r(error, buffer, BUFSIZE)) {
+        /* strerror_r() is only allowed to fail on ERANGE (because the buffer
+         * is too short).  We don't check the actual failure reason because
+         * POSIX requires strerror_r() to return the error but old glibc
+         * (before 2.13) returns -1 and sets errno. */
+        snprintf(buffer, BUFSIZE, "Unknown error %d", error);
     }
-    snprintf(unknown, sizeof unknown, "***unknown return value: %d***", retval);
-    return unknown;
+#endif
+
+    errno = save_errno;
+
+    return s;
 }
 
 /* Sets global "program_name" and "program_version" variables.  Should
@@ -340,6 +366,9 @@ set_program_name__(const char *argv0, const char *version, const char *date,
                    const char *time)
 {
     const char *slash = strrchr(argv0, '/');
+
+    assert_single_threaded();
+
     program_name = slash ? slash + 1 : argv0;
 
     free(program_version);
@@ -810,7 +839,7 @@ log_2_floor(uint32_t n)
 int
 log_2_ceil(uint32_t n)
 {
-    return log_2_floor(n) + !IS_POW2(n);
+    return log_2_floor(n) + !is_pow2(n);
 }
 
 /* Returns the number of trailing 0-bits in 'n'.  Undefined if 'n' == 0. */
@@ -843,7 +872,7 @@ raw_ctz(uint32_t n)
 #endif
 
 /* Returns the number of 1-bits in 'x', between 0 and 32 inclusive. */
-int
+unsigned int
 popcount(uint32_t x)
 {
     /* In my testing, this implementation is over twice as fast as any other