From 83447a051fbcc88b33fcea6670520687668d3ba1 Mon Sep 17 00:00:00 2001 From: Chris Hall Date: Thu, 8 Apr 2010 19:51:10 +0100 Subject: New functions for error numbers and addresses in messages. Implemented less onerous ways of including descriptions of errors and IP addresses in logging and other messages. Implemented mapping of error numbers to error names, which is generally more meaningful. --- lib/pthread_safe.c | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) (limited to 'lib/pthread_safe.c') 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 #include #include +#include +#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 '' + * + * where 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 '' + * + * where 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 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; } + + + + + + + -- cgit v1.2.3