error_message.c revision d7f45af802330a0e1450afa05185d3722e77a76c
1/* 2 * $Header$ 3 * $Source$ 4 * $Locker$ 5 * 6 * Copyright 1987 by the Student Information Processing Board 7 * of the Massachusetts Institute of Technology 8 * 9 * Permission to use, copy, modify, and distribute this software and 10 * its documentation for any purpose is hereby granted, provided that 11 * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in 12 * advertising or publicity pertaining to distribution of the software 13 * without specific, written prior permission. M.I.T. and the 14 * M.I.T. S.I.P.B. make no representations about the suitability of 15 * this software for any purpose. It is provided "as is" without 16 * express or implied warranty. 17 */ 18 19#include <stdio.h> 20#include <stdlib.h> 21#include <string.h> 22#include <errno.h> 23#ifdef HAVE_SYS_PRCTL_H 24#include <sys/prctl.h> 25#else 26#define PR_GET_DUMPABLE 3 27#endif 28#if (!defined(HAVE_PRCTL) && defined(linux)) 29#include <sys/syscall.h> 30#endif 31#ifdef HAVE_SEMAPHORE_H 32#include <semaphore.h> 33#endif 34#if HAVE_UNISTD_H 35#include <unistd.h> 36#endif 37#if HAVE_SYS_TYPES_H 38#include <sys/types.h> 39#endif 40#include "com_err.h" 41#include "error_table.h" 42#include "internal.h" 43 44#ifdef TLS 45#define THREAD_LOCAL static TLS 46#else 47#define THREAD_LOCAL static 48#endif 49 50THREAD_LOCAL char buffer[25]; 51 52struct et_list * _et_list = (struct et_list *) NULL; 53struct et_list * _et_dynamic_list = (struct et_list *) NULL; 54 55#ifdef __GNUC__ 56#define COMERR_ATTR(x) __attribute__(x) 57#else 58#define COMERR_ATTR(x) 59#endif 60 61#ifdef HAVE_SEM_INIT 62static sem_t _et_lock; 63static _et_lock_initialized; 64 65static COMERR_ATTR((constructor)) setup_et_lock() 66{ 67 sem_init(&_et_lock, 0, 1); 68 _et_lock_initialized = 1; 69} 70 71static COMERR_ATTR((destructor)) fini_et_lock() 72{ 73 sem_destroy(&_et_lock); 74 _et_lock_initialized = 0; 75} 76#endif 77 78 79int et_list_lock() 80{ 81#ifdef HAVE_SEM_INIT 82 if (!_et_lock_initialized) 83 setup_et_lock(); 84 return sem_wait(&_et_lock); 85#else 86 return 0; 87#endif 88} 89 90int et_list_unlock() 91{ 92#ifdef HAVE_SEM_INIT 93 if (_et_lock_initialized) 94 return sem_post(&_et_lock); 95#endif 96 return 0; 97} 98 99const char * error_message (errcode_t code) 100{ 101 int offset; 102 struct et_list *et; 103 errcode_t table_num; 104 int started = 0; 105 char *cp; 106 107 offset = (int) (code & ((1<<ERRCODE_RANGE)-1)); 108 table_num = code - offset; 109 if (!table_num) { 110#ifdef HAS_SYS_ERRLIST 111 if (offset < sys_nerr) 112 return(sys_errlist[offset]); 113 else 114 goto oops; 115#else 116 cp = strerror(offset); 117 if (cp) 118 return(cp); 119 else 120 goto oops; 121#endif 122 } 123 et_list_lock(); 124 for (et = _et_list; et; et = et->next) { 125 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { 126 /* This is the right table */ 127 if (et->table->n_msgs <= offset) { 128 break; 129 } else { 130 const char *msg = et->table->msgs[offset]; 131 et_list_unlock(); 132 return msg; 133 } 134 } 135 } 136 for (et = _et_dynamic_list; et; et = et->next) { 137 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { 138 /* This is the right table */ 139 if (et->table->n_msgs <= offset) { 140 break; 141 } else { 142 const char *msg = et->table->msgs[offset]; 143 et_list_unlock(); 144 return msg; 145 } 146 } 147 } 148 et_list_unlock(); 149oops: 150 strcpy (buffer, "Unknown code "); 151 if (table_num) { 152 strcat (buffer, error_table_name (table_num)); 153 strcat (buffer, " "); 154 } 155 for (cp = buffer; *cp; cp++) 156 ; 157 if (offset >= 100) { 158 *cp++ = '0' + offset / 100; 159 offset %= 100; 160 started++; 161 } 162 if (started || offset >= 10) { 163 *cp++ = '0' + offset / 10; 164 offset %= 10; 165 } 166 *cp++ = '0' + offset; 167 *cp = '\0'; 168 return(buffer); 169} 170 171/* 172 * This routine will only return a value if the we are not running as 173 * a privileged process. 174 */ 175static char *safe_getenv(const char *arg) 176{ 177 if ((getuid() != geteuid()) || (getgid() != getegid())) 178 return NULL; 179#if HAVE_PRCTL 180 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) 181 return NULL; 182#else 183#if (defined(linux) && defined(SYS_prctl)) 184 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) 185 return NULL; 186#endif 187#endif 188 189#ifdef HAVE___SECURE_GETENV 190 return __secure_getenv(arg); 191#else 192 return getenv(arg); 193#endif 194} 195 196#define DEBUG_INIT 0x8000 197#define DEBUG_ADDREMOVE 0x0001 198 199static int debug_mask = 0; 200static FILE *debug_f = 0; 201 202static void init_debug(void) 203{ 204 char *dstr; 205 char *fn; 206 207 if (debug_mask & DEBUG_INIT) 208 return; 209 210 dstr = getenv("COMERR_DEBUG"); 211 if (dstr) 212 debug_mask = strtoul(dstr, 0, 0); 213 214 fn = safe_getenv("COMERR_DEBUG_FILE"); 215 if (fn) 216 debug_f = fopen(fn, "a"); 217 if (!debug_f) 218 debug_f = fopen("/dev/tty", "a"); 219 if (!debug_f) 220 debug_mask = 0; 221 222 debug_mask |= DEBUG_INIT; 223} 224 225/* 226 * New interface provided by krb5's com_err library 227 */ 228errcode_t add_error_table(const struct error_table * et) 229{ 230 struct et_list *el; 231 232 if (!(el = (struct et_list *) malloc(sizeof(struct et_list)))) 233 return ENOMEM; 234 235 if (et_list_lock() != 0) { 236 free(el); 237 return errno; 238 } 239 240 el->table = et; 241 el->next = _et_dynamic_list; 242 _et_dynamic_list = el; 243 244 init_debug(); 245 if (debug_mask & DEBUG_ADDREMOVE) 246 fprintf(debug_f, "add_error_table: %s (0x%p)\n", 247 error_table_name(et->base), 248 (const void *) et); 249 250 et_list_unlock(); 251 return 0; 252} 253 254/* 255 * New interface provided by krb5's com_err library 256 */ 257errcode_t remove_error_table(const struct error_table * et) 258{ 259 struct et_list *el; 260 struct et_list *el2 = 0; 261 262 if (et_list_lock() != 0) 263 return ENOENT; 264 265 el = _et_dynamic_list; 266 init_debug(); 267 while (el) { 268 if (el->table->base == et->base) { 269 if (el2) /* Not the beginning of the list */ 270 el2->next = el->next; 271 else 272 _et_dynamic_list = el->next; 273 (void) free(el); 274 if (debug_mask & DEBUG_ADDREMOVE) 275 fprintf(debug_f, 276 "remove_error_table: %s (0x%p)\n", 277 error_table_name(et->base), 278 (const void *) et); 279 et_list_unlock(); 280 return 0; 281 } 282 el2 = el; 283 el = el->next; 284 } 285 if (debug_mask & DEBUG_ADDREMOVE) 286 fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n", 287 error_table_name(et->base), 288 (const void *) et); 289 et_list_unlock(); 290 return ENOENT; 291} 292 293/* 294 * Variant of the interface provided by Heimdal's com_err library 295 */ 296void 297add_to_error_table(struct et_list *new_table) 298{ 299 add_error_table(new_table->table); 300} 301