summaryrefslogtreecommitdiffstats
path: root/lib/pthread_safe.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pthread_safe.c')
-rw-r--r--lib/pthread_safe.c230
1 files changed, 230 insertions, 0 deletions
diff --git a/lib/pthread_safe.c b/lib/pthread_safe.c
index f67e6b2a..b6c429e4 100644
--- a/lib/pthread_safe.c
+++ b/lib/pthread_safe.c
@@ -35,7 +35,10 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <errno.h>
+#include "qfstring.h"
+#include "errno_names.h"
/* prototypes */
static void destructor(void* data);
@@ -98,6 +101,226 @@ safe_strerror(int errnum)
}
}
+/*------------------------------------------------------------------------------
+ * Construct string to describe the given error of the form:
+ *
+ * ENAME '<strerror>'
+ *
+ * where <strerror> is given by strerror() or, if pthreads_enabled, strerror_r()
+ *
+ * Truncates the result to the given len (0 => no truncation). But silently
+ * imposes the maximum length allowed by the strerror_t.
+ *
+ * If has to truncate, places "..." at the end of the message to show this has
+ * happened.
+ *
+ * If a name is not known then (assuming strerror() won't know it either):
+ *
+ * ERRNO=999 *unknown error*
+ *
+ * For err==0 returns:
+ *
+ * EOK 'no error'
+ *
+ * Thread safe replacement for strerror. Never returns NULL.
+ */
+static void
+errtox(strerror_t* st, int err, int len, int want)
+{
+ qf_str_t qfs ;
+
+ const char* q ;
+ int ql ;
+
+ /* Prepare. */
+ int errno_saved = errno ;
+
+ if ((len <= 0) || (len >= (int)sizeof(st->str)))
+ len = sizeof(st->str) - 1 ;
+ qfs_init(&qfs, st->str, len + 1) ;
+
+ q = "" ;
+ ql = 0 ;
+
+ /* If want the error name, do that now. */
+ if (want & 1)
+ {
+ const char* name = errno_name_lookup(err) ;
+
+ if (name != NULL)
+ qfs_append(&qfs, name) ;
+ else
+ qfs_printf(&qfs, "ERRNO=%d", err) ;
+ } ;
+
+ /* name and string ? */
+ if (want == 3)
+ {
+ qfs_append(&qfs, " ") ;
+ q = "'" ;
+ ql = 2 ;
+ } ;
+
+ /* If want the error string, do that now */
+ if (want & 2)
+ {
+ char buf[400] ; /* impossibly vast */
+ int ret ;
+ const char* errm ;
+
+ if (err == 0)
+ errm = "no error" ;
+ else
+ {
+ if (qpthreads_enabled)
+ {
+ /* POSIX is not explicit about what happens if the buffer is not
+ * big enough to accommodate the message, except that an ERANGE
+ * error may be raised.
+ *
+ * By experiment: glibc-2.10.2-1.x86_64 returns -1, with errno
+ * set to ERANGE and no string at all if the buffer is too small.
+ *
+ * A huge buffer is used to get the message, and that is later
+ * truncated, if necessary, to fit in the strerror_t structure.
+ */
+
+ buf[0] = '\0' ; /* make sure starts empty */
+ ret = strerror_r(err, buf, sizeof(buf)) ;
+ errm = buf ;
+ if (ret != 0)
+ ret = errno ;
+ }
+ else
+ {
+ /* POSIX says that strerror *will* return something, but it is
+ * known that it may return NULL if the error number is not
+ * recognised.
+ */
+ errno = 0 ;
+ errm = strerror(err) ;
+ ret = errno ;
+ if ((ret == 0) && ((errm == NULL) || (*errm == '\0')))
+ ret = EINVAL ;
+ } ;
+
+ /* Deal with errors, however exotic. */
+ if (ret != 0)
+ {
+ q = "*" ;
+ ql = 2 ; /* force "*" "quotes" */
+ if (ret == EINVAL)
+ errm = "unknown error" ;
+ else if (ret == ERANGE)
+ {
+ if (*errm == '\0')
+ errm = "vast error message" ;
+ }
+ else
+ {
+ qf_str_t qfs_b ;
+ qfs_init(&qfs_b, buf, sizeof(buf)) ;
+ qfs_printf(&qfs_b, "strerror%s(%d) returned error %d",
+ qpthreads_enabled ? "_r" : "", err, ret) ;
+ errm = buf ;
+ } ;
+ } ;
+ } ;
+
+ /* Add strerror to the result... looking out for overflow. */
+ len = strlen(errm) ;
+
+ if ((len + ql) <= qfs_left(&qfs)) /* accounting for "quotes" */
+ qfs_printf(&qfs, "%s%s%s", q, errm, q) ;
+ else
+ qfs_printf(&qfs, "%s%.*s...%s", q, qfs_left(&qfs) - ql - 3, errm, q) ;
+ /* -ve precision is ignored ! */
+ } ;
+
+ /* Put back errno */
+ errno = errno_saved ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Construct string to describe the given error of the form:
+ *
+ * ENAME '<strerror>'
+ *
+ * where <strerror> is given by strerror() or, if pthreads_enabled, strerror_r()
+ *
+ * Truncates the result to the given len (0 => no truncation). But silently
+ * imposes the maximum length allowed by the strerror_t.
+ *
+ * If has to truncate, places "..." at the end of the message to show this has
+ * happened.
+ *
+ * If a name is not known then (assuming strerror() won't know it either):
+ *
+ * ERRNO=999 *unknown error*
+ *
+ * For err==0 returns:
+ *
+ * EOK 'no error'
+ *
+ * Thread safe replacement for strerror. Never returns NULL.
+ */
+extern strerror_t
+errtoa(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 3) ; /* name and message */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Convert error number to its name
+
+ * If a name is not known then:
+ *
+ * ERRNO=999
+ *
+ * For err==0 returns:
+ *
+ * EOK
+ *
+ * Truncates the result to the given len (0 => no truncation). But silently
+ * imposes the maximum length allowed by the strerror_t.
+ *
+ * This is thread-safe.
+ */
+extern strerror_t
+errtoname(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 1) ; /* name */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Alternative thread-safe safe_strerror()
+ *
+ * Truncates the result to the given len (0 => no truncation). But silently
+ * imposes the maximum length allowed by the strerror_t.
+ *
+ * If the <strerror> will not fit in the strerror_t, it is truncated and
+ * '...' placed at the end to show this has happened.
+ *
+ * Thread safe replacement for strerror. Never returns NULL.
+ */
+extern strerror_t
+errtostr(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 2) ; /* message */
+
+ return st ;
+} ;
+
/* Thread safe version of inet_ntoa. Never returns NULL.
* Contents of result remains intact until another call of
* a safe_ function.
@@ -135,3 +358,10 @@ thread_buff(void)
return buff;
}
+
+
+
+
+
+
+