>From 1c4bcda3d1d468f8ad47341f6562f31bb8f51a9d Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Thu, 21 Nov 2013 11:59:40 -0500
Subject: [PATCH 1/2] Add Thread-safe implementation of strerror()

Unfortunately strerror() is not thread safe so we have to juggle with
strerror_r() which is a can of worms as 2 incompatible implementations
are available depending on what is defined at compile time.

Try to do something sane.
---
 proxy/src/gp_common.h |  3 +++
 proxy/src/gp_util.c   | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 62 insertions(+)

diff --git a/proxy/src/gp_common.h b/proxy/src/gp_common.h
index b5c525f37cd55460cba1deee866f1b236b6bc8e1..f2b8c3e99212c68bbe92f0b188b3b10f99bba903 100644
--- a/proxy/src/gp_common.h
+++ b/proxy/src/gp_common.h
@@ -69,6 +69,9 @@ bool gp_same(const char *a, const char *b);
 bool gp_boolean_is_true(const char *s);
 char *gp_getenv(const char *name);
 
+/* NOTE: read the note in gp_util.c before using gp_strerror() */
+char *gp_strerror(int errnum);
+
 #include "rpcgen/gss_proxy.h"
 
 union gp_rpc_arg {
diff --git a/proxy/src/gp_util.c b/proxy/src/gp_util.c
index a6c870ffd71564be81c4d05e9d1890b4e054812e..4fbac4efbc06663f27a3dee18f2b704e110c36ba 100644
--- a/proxy/src/gp_util.c
+++ b/proxy/src/gp_util.c
@@ -27,6 +27,8 @@
 #include <stdbool.h>
 #include <string.h>
 #include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
 
 bool gp_same(const char *a, const char *b)
 {
@@ -66,3 +68,60 @@ char *gp_getenv(const char *name)
     return NULL;
 #endif
 }
+
+/* NOTE: because strerror_r() is such a mess with glibc, we need to do some
+ * magic checking to find out what function prototype is being used of the
+ * two incompatible ones, and pray it doesn't change in the future.
+ * On top of that to avoid impacting the current code too much we've got to use
+ * thread-local storage to hold a buffer.
+ * gp_strerror() is basically a thread-safe version of strerror() that can
+ * never fail.
+ */
+const char gp_internal_err[] = "Internal strerror_r() error.";
+#define MAX_GP_STRERROR 1024
+char *gp_strerror(int errnum)
+{
+    static __thread char buf[MAX_GP_STRERROR];
+    int saved_errno = errno;
+
+#if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE)
+    /* XSI version */
+    int ret;
+
+    ret = strerror_r(errnum, buf, MAX_GP_STRERROR);
+    if (ret == -1) ret = errno;
+    switch (ret) {
+    case 0:
+        break;
+    case EINVAL:
+        ret = snprintf(buf, MAX_GP_STRERROR,
+                       "Unknown error code: %d", errnum);
+        if (ret > 0) break;
+        /* fallthrough */
+    default:
+        ret = snprintf(buf, MAX_GP_STRERROR,
+                       "Internal error describing error code: %d", errnum);
+        if (ret > 0) break;
+        memset(buf, 0, MAX_GP_STRERROR);
+        strncpy(buf, gp_internal_err, MAX_GP_STRERROR);
+        buf[MAX_GP_STRERROR -1] = '\0';
+    }
+#else
+    /* GNU-specific version */
+    char *ret;
+
+    ret = strerror_r(errnum, buf, MAX_GP_STRERROR);
+    if (ret == NULL) {
+        memset(buf, 0, MAX_GP_STRERROR);
+        strncpy(buf, gp_internal_err, MAX_GP_STRERROR);
+        buf[MAX_GP_STRERROR -1] = '\0';
+    } else if (ret != buf) {
+        memset(buf, 0, MAX_GP_STRERROR);
+        strncpy(buf, ret, MAX_GP_STRERROR);
+        buf[MAX_GP_STRERROR -1] = '\0';
+    }
+#endif
+
+    errno = saved_errno;
+    return buf;
+}
-- 
1.8.4.2

