#include "autoconf.h"
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <string.h>
#include "com_err.h"
#include "error_table.h"
#include "k5-platform.h"
#if !defined(HAVE_STRERROR) && !defined(SYS_ERRLIST_DECLARED)
extern char const * const sys_errlist[];
extern const int sys_nerr;
#endif
static struct et_list * _et_list = (struct et_list *) NULL;
static struct dynamic_et_list * et_list_dynamic;
static k5_mutex_t et_list_lock = K5_MUTEX_PARTIAL_INITIALIZER;
static int terminated = 0;
MAKE_INIT_FUNCTION(com_err_initialize);
MAKE_FINI_FUNCTION(com_err_terminate);
int com_err_initialize(void)
{
int err;
#ifdef SHOW_INITFINI_FUNCS
printf("com_err_initialize\n");
#endif
terminated = 0;
err = k5_mutex_finish_init(&et_list_lock);
if (err)
return err;
err = k5_mutex_finish_init(&com_err_hook_lock);
if (err)
return err;
err = k5_key_register(K5_KEY_COM_ERR, free);
if (err)
return err;
return 0;
}
void com_err_terminate(void)
{
struct dynamic_et_list *e, *enext;
if (! INITIALIZER_RAN(com_err_initialize) || PROGRAM_EXITING()) {
#ifdef SHOW_INITFINI_FUNCS
printf("com_err_terminate: skipping\n");
#endif
return;
}
#ifdef SHOW_INITFINI_FUNCS
printf("com_err_terminate\n");
#endif
k5_key_delete(K5_KEY_COM_ERR);
k5_mutex_destroy(&com_err_hook_lock);
k5_mutex_lock(&et_list_lock);
for (e = et_list_dynamic; e; e = enext) {
enext = e->next;
free(e);
}
k5_mutex_unlock(&et_list_lock);
k5_mutex_destroy(&et_list_lock);
terminated = 1;
}
#ifndef DEBUG_TABLE_LIST
#define dprintf(X)
#else
#define dprintf(X) printf X
#endif
static char *
get_thread_buffer ()
{
char *cp;
cp = k5_getspecific(K5_KEY_COM_ERR);
if (cp == NULL) {
cp = malloc(ET_EBUFSIZ);
if (cp == NULL) {
return NULL;
}
if (k5_setspecific(K5_KEY_COM_ERR, cp) != 0) {
free(cp);
return NULL;
}
}
return cp;
}
const char * KRB5_CALLCONV
error_message(long code)
{
unsigned long offset;
unsigned long l_offset;
struct et_list *et;
struct dynamic_et_list *det;
unsigned long table_num;
int started = 0;
unsigned int divisor = 100;
char *cp, *cp1;
const struct error_table *table;
int merr;
l_offset = (unsigned long)code & ((1<<ERRCODE_RANGE)-1);
offset = l_offset;
table_num = ((unsigned long)code - l_offset) & ERRCODE_MAX;
if (table_num == 0
#ifdef __sgi
|| (code > 0 && code <= 1600)
#endif
) {
if (code == 0)
goto oops;
if ((unsigned long)(int)code != code)
abort ();
#ifdef HAVE_STRERROR_R
cp = get_thread_buffer();
if (cp && strerror_r((int) code, cp, ET_EBUFSIZ) == 0)
return cp;
#endif
#ifdef HAVE_STRERROR
cp = strerror((int) code);
if (cp)
return cp;
#elif defined HAVE_SYS_ERRLIST
if (offset < sys_nerr)
return(sys_errlist[offset]);
#endif
goto oops;
}
if (CALL_INIT_FUNCTION(com_err_initialize))
return 0;
merr = k5_mutex_lock(&et_list_lock);
if (merr)
goto oops;
dprintf (("scanning static list for %x\n", table_num));
for (et = _et_list; et != NULL; et = et->next) {
if (et->table == NULL)
continue;
dprintf (("\t%x = %s\n", et->table->base & ERRCODE_MAX,
et->table->msgs[0]));
if ((et->table->base & ERRCODE_MAX) == table_num) {
table = et->table;
goto found;
}
}
dprintf (("scanning dynamic list for %x\n", table_num));
for (det = et_list_dynamic; det != NULL; det = det->next) {
dprintf (("\t%x = %s\n", det->table->base & ERRCODE_MAX,
det->table->msgs[0]));
if ((det->table->base & ERRCODE_MAX) == table_num) {
table = det->table;
goto found;
}
}
goto no_table_found;
found:
k5_mutex_unlock(&et_list_lock);
dprintf (("found it!\n"));
if ((unsigned long)(unsigned int)offset != offset)
goto no_table_found;
if (table->n_msgs <= (unsigned int) offset)
goto no_table_found;
return table->msgs[offset];
no_table_found:
k5_mutex_unlock(&et_list_lock);
#if defined(_WIN32)
if (code >= WSABASEERR && code < WSABASEERR + 1100) {
table_num = 0;
offset = code;
divisor = WSABASEERR;
}
#endif
#ifdef _WIN32
{
LPVOID msgbuf;
if (! FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL ,
(DWORD) code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &msgbuf,
(DWORD) 0 ,
NULL )) {
if (code >= WSABASEERR && code < WSABASEERR + 1100) {
table_num = 0;
offset = code;
divisor = 10000;
}
goto oops;
} else {
char *buffer;
cp = get_thread_buffer();
if (cp == NULL)
return "Unknown error code";
buffer = cp;
strncpy(buffer, msgbuf, ET_EBUFSIZ);
buffer[ET_EBUFSIZ-1] = '\0';
cp = buffer + strlen(buffer) - 1;
if (*cp == '\n') *cp-- = '\0';
if (*cp == '\r') *cp-- = '\0';
if (*cp == '.') *cp-- = '\0';
LocalFree(msgbuf);
return buffer;
}
}
#endif
oops:
#if TARGET_OS_MAC
{
return (strerror (code));
}
#endif
cp = get_thread_buffer();
if (cp == NULL)
return "Unknown error code";
cp1 = cp;
strcpy(cp, "Unknown code ");
cp += sizeof("Unknown code ") - 1;
if (table_num != 0L) {
(void) error_table_name_r(table_num, cp);
while (*cp != '\0')
cp++;
*cp++ = ' ';
}
while (divisor > 1) {
if (started != 0 || offset >= divisor) {
*cp++ = '0' + offset / divisor;
offset %= divisor;
started++;
}
divisor /= 10;
}
*cp++ = '0' + offset;
*cp = '\0';
return(cp1);
}
errcode_t KRB5_CALLCONV
add_error_table( const struct error_table * et)
{
struct dynamic_et_list *del;
int merr;
if (CALL_INIT_FUNCTION(com_err_initialize))
return 0;
del = (struct dynamic_et_list *)malloc(sizeof(struct dynamic_et_list));
if (del == NULL)
return errno;
del->table = et;
merr = k5_mutex_lock(&et_list_lock);
if (merr) {
free(del);
return merr;
}
del->next = et_list_dynamic;
et_list_dynamic = del;
return k5_mutex_unlock(&et_list_lock);
}
errcode_t KRB5_CALLCONV
remove_error_table(const struct error_table * et)
{
struct dynamic_et_list **del;
struct et_list **el;
int merr;
if (CALL_INIT_FUNCTION(com_err_initialize))
return 0;
#if !defined(ENABLE_THREADS) && defined(DEBUG_THREADS)
if (et_list_lock.os.initialized == 0 && terminated != 0) {
fprintf(stderr, "\n\n *** Function remove_error_table called after com_err library termination. ***\n *** Shared library termination code executed in incorrect order? ***\n\n");
abort();
}
#endif
merr = k5_mutex_lock(&et_list_lock);
if (merr)
return merr;
for (del = &et_list_dynamic; *del; del = &(*del)->next)
if ((*del)->table->base == et->base) {
struct dynamic_et_list *old = *del;
*del = old->next;
free (old);
return k5_mutex_unlock(&et_list_lock);
}
for (el = &_et_list; *el; el = &(*el)->next)
if ((*el)->table != NULL && (*el)->table->base == et->base) {
struct et_list *old = *el;
*el = old->next;
old->next = NULL;
old->table = NULL;
return k5_mutex_unlock(&et_list_lock);
}
k5_mutex_unlock(&et_list_lock);
return ENOENT;
}
int com_err_finish_init()
{
return CALL_INIT_FUNCTION(com_err_initialize);
}