| /* |
| * $Header$ |
| * $Source$ |
| * $Locker$ |
| * |
| * Copyright 1987 by the Student Information Processing Board |
| * of the Massachusetts Institute of Technology |
| * |
| * Permission to use, copy, modify, and distribute this software and |
| * its documentation for any purpose is hereby granted, provided 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. 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 <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <errno.h> |
| #ifdef HAVE_SYS_PRCTL_H |
| #include <sys/prctl.h> |
| #else |
| #define PR_GET_DUMPABLE 3 |
| #endif |
| #if (!defined(HAVE_PRCTL) && defined(linux)) |
| #include <sys/syscall.h> |
| #endif |
| #ifdef HAVE_SEMAPHORE_H |
| #include <semaphore.h> |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <fcntl.h> |
| #if HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| #include "com_err.h" |
| #include "error_table.h" |
| #include "internal.h" |
| |
| #ifdef TLS |
| #define THREAD_LOCAL static TLS |
| #else |
| #define THREAD_LOCAL static |
| #endif |
| |
| THREAD_LOCAL char buffer[25]; |
| |
| struct et_list * _et_list = (struct et_list *) NULL; |
| struct et_list * _et_dynamic_list = (struct et_list *) NULL; |
| |
| #ifdef __GNUC__ |
| #define COMERR_ATTR(x) __attribute__(x) |
| #else |
| #define COMERR_ATTR(x) |
| #endif |
| |
| #ifdef HAVE_SEM_INIT |
| static sem_t _et_lock; |
| static int _et_lock_initialized; |
| |
| static void COMERR_ATTR((constructor)) setup_et_lock(void) |
| { |
| sem_init(&_et_lock, 0, 1); |
| _et_lock_initialized = 1; |
| } |
| |
| static void COMERR_ATTR((destructor)) fini_et_lock(void) |
| { |
| sem_destroy(&_et_lock); |
| _et_lock_initialized = 0; |
| } |
| #endif |
| |
| |
| int et_list_lock(void) |
| { |
| #ifdef HAVE_SEM_INIT |
| if (!_et_lock_initialized) |
| setup_et_lock(); |
| return sem_wait(&_et_lock); |
| #else |
| return 0; |
| #endif |
| } |
| |
| int et_list_unlock(void) |
| { |
| #ifdef HAVE_SEM_INIT |
| if (_et_lock_initialized) |
| return sem_post(&_et_lock); |
| #endif |
| return 0; |
| } |
| |
| const char * error_message (errcode_t code) |
| { |
| int offset; |
| struct et_list *et; |
| errcode_t table_num; |
| int started = 0; |
| char *cp; |
| |
| offset = (int) (code & ((1<<ERRCODE_RANGE)-1)); |
| table_num = code - offset; |
| if (!table_num) { |
| #ifdef HAS_SYS_ERRLIST |
| if (offset < sys_nerr) |
| return(sys_errlist[offset]); |
| else |
| goto oops; |
| #else |
| cp = strerror(offset); |
| if (cp) |
| return(cp); |
| else |
| goto oops; |
| #endif |
| } |
| et_list_lock(); |
| for (et = _et_list; et; et = et->next) { |
| if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { |
| /* This is the right table */ |
| if (et->table->n_msgs <= offset) { |
| break; |
| } else { |
| const char *msg = et->table->msgs[offset]; |
| et_list_unlock(); |
| return msg; |
| } |
| } |
| } |
| for (et = _et_dynamic_list; et; et = et->next) { |
| if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { |
| /* This is the right table */ |
| if (et->table->n_msgs <= offset) { |
| break; |
| } else { |
| const char *msg = et->table->msgs[offset]; |
| et_list_unlock(); |
| return msg; |
| } |
| } |
| } |
| et_list_unlock(); |
| oops: |
| strcpy (buffer, "Unknown code "); |
| if (table_num) { |
| strcat (buffer, error_table_name (table_num)); |
| strcat (buffer, " "); |
| } |
| for (cp = buffer; *cp; cp++) |
| ; |
| if (offset >= 100) { |
| *cp++ = '0' + offset / 100; |
| offset %= 100; |
| started++; |
| } |
| if (started || offset >= 10) { |
| *cp++ = '0' + offset / 10; |
| offset %= 10; |
| } |
| *cp++ = '0' + offset; |
| *cp = '\0'; |
| return(buffer); |
| } |
| |
| /* |
| * This routine will only return a value if the we are not running as |
| * a privileged process. |
| */ |
| static char *safe_getenv(const char *arg) |
| { |
| if ((getuid() != geteuid()) || (getgid() != getegid())) |
| return NULL; |
| #if HAVE_PRCTL |
| if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) |
| return NULL; |
| #else |
| #if (defined(linux) && defined(SYS_prctl)) |
| if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) |
| return NULL; |
| #endif |
| #endif |
| |
| #ifdef HAVE___SECURE_GETENV |
| return __secure_getenv(arg); |
| #else |
| return getenv(arg); |
| #endif |
| } |
| |
| #define DEBUG_INIT 0x8000 |
| #define DEBUG_ADDREMOVE 0x0001 |
| |
| static int debug_mask = 0; |
| static FILE *debug_f = 0; |
| |
| static void init_debug(void) |
| { |
| char *dstr, *fn, *tmp; |
| int fd, flags; |
| |
| if (debug_mask & DEBUG_INIT) |
| return; |
| |
| dstr = getenv("COMERR_DEBUG"); |
| if (dstr) { |
| debug_mask = strtoul(dstr, &tmp, 0); |
| if (*tmp || errno) |
| debug_mask = 0; |
| } |
| |
| debug_mask |= DEBUG_INIT; |
| if (debug_mask == DEBUG_INIT) |
| return; |
| |
| fn = safe_getenv("COMERR_DEBUG_FILE"); |
| if (fn) |
| debug_f = fopen(fn, "a"); |
| if (!debug_f) |
| debug_f = fopen("/dev/tty", "a"); |
| if (debug_f) { |
| fd = fileno(debug_f); |
| if (fd >= 0) { |
| flags = fcntl(fd, F_GETFD); |
| if (flags >= 0) |
| fcntl(fd, F_SETFD, flags | FD_CLOEXEC); |
| } |
| } else |
| debug_mask = DEBUG_INIT; |
| |
| } |
| |
| /* |
| * New interface provided by krb5's com_err library |
| */ |
| errcode_t add_error_table(const struct error_table * et) |
| { |
| struct et_list *el; |
| |
| if (!(el = (struct et_list *) malloc(sizeof(struct et_list)))) |
| return ENOMEM; |
| |
| if (et_list_lock() != 0) { |
| free(el); |
| return errno; |
| } |
| |
| el->table = et; |
| el->next = _et_dynamic_list; |
| _et_dynamic_list = el; |
| |
| init_debug(); |
| if (debug_mask & DEBUG_ADDREMOVE) |
| fprintf(debug_f, "add_error_table: %s (0x%p)\n", |
| error_table_name(et->base), |
| (const void *) et); |
| |
| et_list_unlock(); |
| return 0; |
| } |
| |
| /* |
| * New interface provided by krb5's com_err library |
| */ |
| errcode_t remove_error_table(const struct error_table * et) |
| { |
| struct et_list *el; |
| struct et_list *el2 = 0; |
| |
| if (et_list_lock() != 0) |
| return ENOENT; |
| |
| el = _et_dynamic_list; |
| init_debug(); |
| while (el) { |
| if (el->table->base == et->base) { |
| if (el2) /* Not the beginning of the list */ |
| el2->next = el->next; |
| else |
| _et_dynamic_list = el->next; |
| (void) free(el); |
| if (debug_mask & DEBUG_ADDREMOVE) |
| fprintf(debug_f, |
| "remove_error_table: %s (0x%p)\n", |
| error_table_name(et->base), |
| (const void *) et); |
| et_list_unlock(); |
| return 0; |
| } |
| el2 = el; |
| el = el->next; |
| } |
| if (debug_mask & DEBUG_ADDREMOVE) |
| fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n", |
| error_table_name(et->base), |
| (const void *) et); |
| et_list_unlock(); |
| return ENOENT; |
| } |
| |
| /* |
| * Variant of the interface provided by Heimdal's com_err library |
| */ |
| void |
| add_to_error_table(struct et_list *new_table) |
| { |
| add_error_table(new_table->table); |
| } |