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.c516
1 files changed, 516 insertions, 0 deletions
diff --git a/lib/pthread_safe.c b/lib/pthread_safe.c
new file mode 100644
index 00000000..c4ab9679
--- /dev/null
+++ b/lib/pthread_safe.c
@@ -0,0 +1,516 @@
+/* Quagga Pthreads support -- thread safe versions of standard functions
+ * Copyright (C) 2009 Chris Hall (GMCH), Highwayman
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GNU Zebra; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/* We use thread specific storeage to provide buufers for the _r versions
+ * of standard functions, so that the callers don't need to provide
+ * their own. The contents of a buffer will remain intact until another
+ * safe_ function is called on the same thread
+ */
+
+#include "pthread_safe.h"
+#include "qpthreads.h"
+#include "memory.h"
+
+#include <pthread.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+
+#include "qfstring.h"
+#include "errno_names.h"
+
+/* prototypes */
+static void destructor(void* data);
+static char * thread_buff(void);
+
+static pthread_key_t tsd_key;
+static const int buff_size = 1024;
+
+/* Module initialization, before any threads have been created */
+void
+safe_init_r(void)
+{
+ if (qpthreads_enabled)
+ {
+ int status;
+ status = pthread_key_create(&tsd_key, destructor);
+ if (status != 0)
+ zabort("Can't create thread specific data key");
+ }
+}
+
+/* Clean up */
+void
+safe_finish(void)
+{
+ if (qpthreads_enabled)
+ pthread_key_delete(tsd_key);
+}
+
+/* called when thread terminates, clean up */
+static void
+destructor(void* data)
+{
+ XFREE(MTYPE_TSD, data);
+}
+
+/* Thread safe version of strerror. Never returns NULL.
+ * Contents of result remains intact until another call of
+ * a safe_ function.
+ */
+const char *
+safe_strerror(int errnum)
+{
+ static const char * unknown = "Unknown error";
+ if (qpthreads_enabled)
+ {
+ char * buff = thread_buff();
+ int ret = strerror_r(errnum, buff, buff_size);
+
+ return (ret >= 0)
+ ? buff
+ : unknown;
+ }
+ else
+ {
+ const char *s = strerror(errnum);
+ return (s != NULL)
+ ? s
+ : unknown;
+ }
+}
+
+/*==============================================================================
+ * Alternative error number handling.
+ *
+ * The descriptive strings for error numbers are all very well, but for some
+ * purposes knowing the name for the error is more useful -- the name, not the
+ * number, as the number is system dependent.
+ *
+ * The following provides:
+ *
+ * * errtoa() -- which maps error number to: ENAME '<strerror>'
+ *
+ * * errtoname() -- which maps error number to: ENAME
+ *
+ * * errtostr() -- which maps error number to: <strerror>
+ *
+ * where:
+ *
+ * * if name is not known gives: ERRNO=999
+ *
+ * * if strerror rejects the number gives: *unknown error*
+ *
+ * * err == 0 gives: EOK -- for the name
+ * 'no error' -- for the <strerror>
+ *
+ * These functions take a 'len' argument, and truncates the result to the given
+ * len (0 => no truncation) -- silently imposing the maximum length allowed by
+ * the strerror_t.
+ *
+ * If has to truncate the <strerror>, places "..." at the end of the message
+ * to show this has happened.
+ */
+
+static void errtox(strerror_t* st, int err, int len, int want) ;
+
+/*------------------------------------------------------------------------------
+ * Construct string to describe the given error of the form:
+ *
+ * ENAME '<strerror>'
+ *
+ * Thread safe extension to 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
+ *
+ * Thread-safe. Never returns NULL.
+ */
+extern strerror_t
+errtoname(int err, int len)
+{
+ strerror_t st ;
+
+ errtox(&st, err, len, 1) ; /* name */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Alternative thread-safe safe_strerror()
+ *
+ * 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 ;
+} ;
+
+/*-----------------------------------------------------------------------------
+ * Common code for errto<x> above.
+ *
+ * want == 1 -- return just name
+ * want == 2 -- return just the strerror()
+ * want == 3 -- return both, with the strerror() in single quotes.
+ */
+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 ;
+} ;
+
+/*==============================================================================
+ * getaddrinfo() and getnameinfo() "EAI_XXXX" error number handling.
+ *
+ * This is similar to the above for errno.
+ *
+ * The following provides:
+ *
+ * * eaitoa() -- which maps error number to: EAI_XXX '<gai_strerror>'
+ * or: as errtoa()
+ *
+ * * eaitoname() -- which maps error number to: EAI_XXX
+ * or: as errtoname()
+ *
+ * * eaitostr() -- which maps error number to: <gai_strerror>
+ * or: as errtostr()
+ *
+ * where:
+ *
+ * * if given EAI_SYSTEM, and given a non-zero errno type error number,
+ * produce the errno string.
+ *
+ * * if name is not known gives: EAI=999
+ *
+ * * gai_strerror returns a string saying the error is not known if that is
+ * the case.
+ *
+ * * eai == 0 gives: EAI_OK -- for the name
+ * 'no error' -- for the <sgai_strerror>
+ *
+ * NB: EAI_SYSTEM is an invitation to look at errno to discover the true
+ * error.
+ */
+
+static void eaitox(strerror_t* st, int eai, int err, int len, int want) ;
+
+/*------------------------------------------------------------------------------
+ * Construct string to describe the given EAI_XXX error of the form:
+ *
+ * EAI_XXX '<gai_strerror>'
+ * or: ENAME '<strerror>' -- if EAI_SYSTEM and err != 0
+ *
+ * Thread safe. Never returns NULL.
+ */
+extern strerror_t
+eaitoa(int eai, int err, int len)
+{
+ strerror_t st ;
+
+ eaitox(&st, eai, err, len, 3) ; /* name and message */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Convert EAI_XXX error number to its name...
+ *
+ * ...or, if EAI_SYSTEM and err != 0, convert err to its name.
+ *
+ * Thread-safe. Never returns NULL.
+ */
+extern strerror_t
+eaitoname(int eai, int err, int len)
+{
+ strerror_t st ;
+
+ eaitox(&st, eai, err, len, 1) ; /* name */
+
+ return st ;
+} ;
+
+/*------------------------------------------------------------------------------
+ * Alternative to gai_strerror()...
+ *
+ * ...or, if EAI_SYSTEM and err != 0, do strerror(err) or strerror_r(err).
+ *
+ * Thread-safe. Never returns NULL.
+ */
+extern strerror_t
+eaitostr(int eai, int err, int len)
+{
+ strerror_t st ;
+
+ eaitox(&st, eai, err, len, 2) ; /* message */
+
+ return st ;
+} ;
+
+/*-----------------------------------------------------------------------------
+ * Common code for eaito<x> above.
+ *
+ * want == 1 -- return just name
+ * want == 2 -- return just the gai_strerror()
+ * want == 3 -- return both, with the gai_strerror() in single quotes.
+ *
+ * err != 0 => if EAI_SYSTEM, return result for errno == err instead.
+ */
+static void
+eaitox(strerror_t* st, int eai, int err, int len, int want)
+{
+ qf_str_t qfs ;
+
+ const char* q ;
+ int ql ;
+
+ /* Look out for mapping EAI_SYSTEM */
+ if ((eai == EAI_SYSTEM) && (err != 0))
+ return errtox(st, err, len, want) ;
+
+ /* 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 = eaino_name_lookup(eai) ;
+
+ if (name != NULL)
+ qfs_append(&qfs, name) ;
+ else
+ qfs_printf(&qfs, "EAI=%d", eai) ;
+ } ;
+
+ /* name and string ? */
+ if (want == 3)
+ {
+ qfs_append(&qfs, " ") ;
+ q = "'" ;
+ ql = 2 ;
+ } ;
+
+ /* If want the error string, do that now */
+ if (want & 2)
+ {
+ const char* eaim ;
+
+ if (eai == 0)
+ eaim = "no error" ;
+ else
+ eaim = gai_strerror(eai) ;
+
+ /* Add strerror to the result... looking out for overflow. */
+ len = strlen(eaim) ;
+
+ if ((len + ql) <= qfs_left(&qfs)) /* accounting for "quotes" */
+ qfs_printf(&qfs, "%s%s%s", q, eaim, q) ;
+ else
+ qfs_printf(&qfs, "%s%.*s...%s", q, qfs_left(&qfs) - ql - 3, eaim, q) ;
+ /* -ve precision is ignored ! */
+ } ;
+
+ /* Put back errno */
+ errno = errno_saved ;
+} ;
+
+/*============================================================================*/
+
+/* Thread safe version of inet_ntoa. Never returns NULL.
+ * Contents of result remains intact until another call of
+ * a safe_ function.
+ */
+const char *
+safe_inet_ntoa (struct in_addr in)
+{
+ static const char * unknown = "Unknown address";
+ const char * buff;
+
+ buff = (qpthreads_enabled)
+ ? inet_ntop(AF_INET, &in, thread_buff(), buff_size)
+ : inet_ntoa(in);
+
+ return buff != NULL
+ ? buff
+ : unknown;
+}
+
+/* Return the thread's buffer, create it if necessary.
+ * (pthread Thread Specific Data)
+ */
+static char *
+thread_buff(void)
+{
+ int ret;
+ char * buff = pthread_getspecific(tsd_key);
+ if (buff == NULL)
+ {
+ buff = XMALLOC(MTYPE_TSD, buff_size);
+ ret = pthread_setspecific(tsd_key, buff);
+ if (ret != 0)
+ zabort("Can't set thread specific data");
+ }
+
+ return buff;
+}
+
+
+
+
+
+
+