From 27ae6c5b8b37a8086800cd1a4edbb01a7fddfad6 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Thu, 21 Nov 2013 11:59:40 -0500 Subject: Add Thread-safe implementation of strerror() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- 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 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 #include #include +#include +#include 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; +} -- cgit