diff options
author | Simo Sorce <simo@redhat.com> | 2013-11-21 11:59:40 -0500 |
---|---|---|
committer | Günther Deschner <gdeschner@redhat.com> | 2013-11-22 14:40:42 +0100 |
commit | 27ae6c5b8b37a8086800cd1a4edbb01a7fddfad6 (patch) | |
tree | 39eeca6536e6d5e7331782102905a3128a0a7583 /proxy/src | |
parent | 23f4ee4359d10f66e1938ce6b1d92d3cc77865ff (diff) | |
download | gss-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>
Diffstat (limited to 'proxy/src')
-rw-r--r-- | proxy/src/gp_common.h | 3 | ||||
-rw-r--r-- | proxy/src/gp_util.c | 59 |
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; +} |