strongswan/src/libstrongswan/utils/utils/strerror.c

148 lines
3.1 KiB
C

/*
* Copyright (C) 2012-2014 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program 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 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program 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.
*/
#include <stdlib.h>
#include <string.h>
#include <library.h>
#include <threading/thread_value.h>
#include <threading/spinlock.h>
#include "strerror.h"
/**
* The size of the thread-specific error buffer
*/
#define STRERROR_BUF_LEN 256
/**
* Thread specific strerror buffer, as char*
*/
static thread_value_t *strerror_buf;
#ifndef HAVE_STRERROR_R
/**
* Lock to access strerror() safely
*/
static spinlock_t *strerror_lock;
#endif /* HAVE_STRERROR_R */
/**
* Retrieve the error buffer assigned to the current thread (or create it)
*/
static inline char *get_strerror_buf()
{
char *buf;
bool old = FALSE;
if (!strerror_buf)
{
return NULL;
}
buf = strerror_buf->get(strerror_buf);
if (!buf)
{
if (lib->leak_detective)
{
old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
}
buf = malloc(STRERROR_BUF_LEN);
strerror_buf->set(strerror_buf, buf);
if (lib->leak_detective)
{
lib->leak_detective->set_state(lib->leak_detective, old);
}
}
return buf;
}
/**
* Use real strerror() below
*/
#undef strerror
/*
* Described in header.
*/
const char *strerror_safe(int errnum)
{
char *buf, *msg;
buf = get_strerror_buf();
if (!buf)
{
/* library not initialized? fallback */
return strerror(errnum);
}
#ifdef HAVE_STRERROR_R
# ifdef STRERROR_R_CHAR_P
/* char* version which may or may not return the original buffer */
msg = strerror_r(errnum, buf, STRERROR_BUF_LEN);
# else
/* int version returns 0 on success */
msg = strerror_r(errnum, buf, STRERROR_BUF_LEN) ? "Unknown error" : buf;
# endif
#else /* HAVE_STRERROR_R */
/* use a lock to ensure calling strerror(3) is thread-safe */
strerror_lock->lock(strerror_lock);
msg = strncpy(buf, strerror(errnum), STRERROR_BUF_LEN);
strerror_lock->unlock(strerror_lock);
buf[STRERROR_BUF_LEN - 1] = '\0';
#endif /* HAVE_STRERROR_R */
return msg;
}
/**
* free() with disabled leak detective
*/
static void free_no_ld(void *buf)
{
bool old = FALSE;
if (lib->leak_detective)
{
old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
}
free(buf);
if (lib->leak_detective)
{
lib->leak_detective->set_state(lib->leak_detective, old);
}
}
/**
* See header
*/
void strerror_init()
{
strerror_buf = thread_value_create(free_no_ld);
#ifndef HAVE_STRERROR_R
strerror_lock = spinlock_create();
#endif
}
/**
* See header
*/
void strerror_deinit()
{
strerror_buf->destroy(strerror_buf);
strerror_buf = NULL;
#ifndef HAVE_STRERROR_R
strerror_lock->destroy(strerror_lock);
#endif
}