summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2013-11-21 11:59:40 -0500
committerGünther Deschner <gdeschner@redhat.com>2013-11-22 14:40:42 +0100
commit27ae6c5b8b37a8086800cd1a4edbb01a7fddfad6 (patch)
tree39eeca6536e6d5e7331782102905a3128a0a7583
parent23f4ee4359d10f66e1938ce6b1d92d3cc77865ff (diff)
downloadgss-proxy-27ae6c5b8b37a8086800cd1a4edbb01a7fddfad6.tar.gz
gss-proxy-27ae6c5b8b37a8086800cd1a4edbb01a7fddfad6.tar.xz
gss-proxy-27ae6c5b8b37a8086800cd1a4edbb01a7fddfad6.zip
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. https://fedorahosted.org/gss-proxy/ticket/111 Reviewed-by: Günther Deschner <gdeschner@redhat.com>
-rw-r--r--proxy/src/gp_common.h3
-rw-r--r--proxy/src/gp_util.c59
2 files changed, 62 insertions, 0 deletions
diff --git a/proxy/src/gp_common.h b/proxy/src/gp_common.h
index b5c525f..f2b8c3e 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 a6c870f..4fbac4e 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;
+}