/* * Copyright 1997,2000,2001,2004 by Massachusetts Institute of Technology * * Copyright 1987, 1988 by MIT Student Information Processing Board * * Permission to use, copy, modify, and distribute this software * and its documentation for any purpose and without fee is * hereby granted, provided that the above copyright notice * appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, * and that the names of M.I.T. and the M.I.T. S.I.P.B. not be * used in advertising or publicity pertaining to distribution * of the software without specific, written prior permission. * Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. and the M.I.T. S.I.P.B. make no representations about * the suitability of this software for any purpose. It is * provided "as is" without express or implied warranty. */ #include "autoconf.h" #include #ifdef HAVE_STDLIB_H #include #endif #include #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 /*@null@*/ static struct et_list * _et_list = (struct et_list *) NULL; /*@null@*//*@only@*/static struct dynamic_et_list * et_list_dynamic; static k5_mutex_t et_list_lock = K5_MUTEX_PARTIAL_INITIALIZER; static int terminated = 0; /* for debugging shlib fini sequence errors */ 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 const char * KRB5_CALLCONV error_message(long code) /*@modifies internalState@*/ { 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< 0 && code <= 1600) goto system_error_code; #endif 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")); /* This is the right table */ /* This could trip if int is 16 bits. */ 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) /* * WinSock errors exist in the 10000 and 11000 ranges * but might not appear if WinSock is not initialized */ 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 /* lpSource */, (DWORD) code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &msgbuf, (DWORD) 0 /*sizeof(buffer)*/, NULL /* va_list */ )) { /* * WinSock errors exist in the 10000 and 11000 ranges * but might not appear if WinSock is not initialized */ if (code >= WSABASEERR && code < WSABASEERR + 1100) { table_num = 0; offset = code; divisor = 10000; } goto oops; } else { char *buffer; cp = k5_getspecific(K5_KEY_COM_ERR); if (cp == NULL) { cp = malloc(ET_EBUFSIZ); if (cp == NULL) { win32_no_specific: return "Unknown error code"; } if (k5_setspecific(K5_KEY_COM_ERR, cp) != 0) { free(cp); goto win32_no_specific; } } 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 { /* ComErr doesn't know about this error, ask the system */ /* Of course there's no way to tell if it knew what error it got */ return (strerror (code)); } #endif cp = k5_getspecific(K5_KEY_COM_ERR); if (cp == NULL) { cp = malloc(ET_EBUFSIZ); if (cp == NULL) { no_specific: return "Unknown error code"; } if (k5_setspecific(K5_KEY_COM_ERR, cp) != 0) { free(cp); goto no_specific; } } 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); } /*@-incondefs@*/ /* _et_list is global on unix but not in header annotations */ errcode_t KRB5_CALLCONV add_error_table(/*@dependent@*/ const struct error_table * et) /*@modifies _et_list,et_list_dynamic@*/ /*@=incondefs@*/ { 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); } /*@-incondefs@*/ /* _et_list is global on unix but not in header annotations */ errcode_t KRB5_CALLCONV remove_error_table(const struct error_table * et) /*@modifies _et_list,et_list_dynamic@*/ /*@=incondefs@*/ { 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; /* Remove the first occurrance we can find. Prefer dynamic entries, but if there are none, check for a static one too. */ for (del = &et_list_dynamic; *del; del = &(*del)->next) if ((*del)->table->base == et->base) { /*@only@*/ 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); }