error_message.c revision f404167dda29a59d2be2882328aeb074b9899669
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 "config.h" 20#if HAVE_SECURE_GETENV 21#define _GNU_SOURCE 22#endif 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <errno.h> 27#ifdef HAVE_SYS_PRCTL_H 28#include <sys/prctl.h> 29#else 30#define PR_GET_DUMPABLE 3 31#endif 32#if (!defined(HAVE_PRCTL) && defined(linux)) 33#include <sys/syscall.h> 34#endif 35#ifdef HAVE_SEMAPHORE_H 36#include <semaphore.h> 37#endif 38#if HAVE_UNISTD_H 39#include <unistd.h> 40#endif 41#include <fcntl.h> 42#if HAVE_SYS_TYPES_H 43#include <sys/types.h> 44#endif 45#include "com_err.h" 46#include "error_table.h" 47#include "internal.h" 48 49#ifdef TLS 50#define THREAD_LOCAL static TLS 51#else 52#define THREAD_LOCAL static 53#endif 54 55THREAD_LOCAL char buffer[25]; 56 57struct et_list * _et_list = (struct et_list *) NULL; 58struct et_list * _et_dynamic_list = (struct et_list *) NULL; 59 60#ifdef __GNUC__ 61#define COMERR_ATTR(x) __attribute__(x) 62#else 63#define COMERR_ATTR(x) 64#endif 65 66#ifdef HAVE_SEM_INIT 67static sem_t _et_lock; 68static int _et_lock_initialized; 69 70static void COMERR_ATTR((constructor)) setup_et_lock(void) 71{ 72 sem_init(&_et_lock, 0, 1); 73 _et_lock_initialized = 1; 74} 75 76static void COMERR_ATTR((destructor)) fini_et_lock(void) 77{ 78 sem_destroy(&_et_lock); 79 _et_lock_initialized = 0; 80} 81#endif 82 83 84int et_list_lock(void) 85{ 86#ifdef HAVE_SEM_INIT 87 if (!_et_lock_initialized) 88 setup_et_lock(); 89 return sem_wait(&_et_lock); 90#else 91 return 0; 92#endif 93} 94 95int et_list_unlock(void) 96{ 97#ifdef HAVE_SEM_INIT 98 if (_et_lock_initialized) 99 return sem_post(&_et_lock); 100#endif 101 return 0; 102} 103 104typedef char *(*gettextf) (const char *); 105 106static gettextf com_err_gettext = NULL; 107 108gettextf set_com_err_gettext(gettextf new_proc) 109{ 110 gettextf x = com_err_gettext; 111 112 com_err_gettext = new_proc; 113 114 return x; 115} 116 117 118const char * error_message (errcode_t code) 119{ 120 int offset; 121 struct et_list *et; 122 errcode_t table_num; 123 int started = 0; 124 char *cp; 125 126 offset = (int) (code & ((1<<ERRCODE_RANGE)-1)); 127 table_num = code - offset; 128 if (!table_num) { 129#ifdef HAS_SYS_ERRLIST 130 if (offset < sys_nerr) 131 return(sys_errlist[offset]); 132 else 133 goto oops; 134#else 135 cp = strerror(offset); 136 if (cp) 137 return(cp); 138 else 139 goto oops; 140#endif 141 } 142 et_list_lock(); 143 for (et = _et_list; et; et = et->next) { 144 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { 145 /* This is the right table */ 146 if (et->table->n_msgs <= offset) { 147 break; 148 } else { 149 const char *msg = et->table->msgs[offset]; 150 et_list_unlock(); 151 if (com_err_gettext) 152 return (*com_err_gettext)(msg); 153 else 154 return msg; 155 } 156 } 157 } 158 for (et = _et_dynamic_list; et; et = et->next) { 159 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { 160 /* This is the right table */ 161 if (et->table->n_msgs <= offset) { 162 break; 163 } else { 164 const char *msg = et->table->msgs[offset]; 165 et_list_unlock(); 166 if (com_err_gettext) 167 return (*com_err_gettext)(msg); 168 else 169 return msg; 170 } 171 } 172 } 173 et_list_unlock(); 174oops: 175 strcpy (buffer, "Unknown code "); 176 if (table_num) { 177 strcat (buffer, error_table_name (table_num)); 178 strcat (buffer, " "); 179 } 180 for (cp = buffer; *cp; cp++) 181 ; 182 if (offset >= 100) { 183 *cp++ = '0' + offset / 100; 184 offset %= 100; 185 started++; 186 } 187 if (started || offset >= 10) { 188 *cp++ = '0' + offset / 10; 189 offset %= 10; 190 } 191 *cp++ = '0' + offset; 192 *cp = '\0'; 193 return(buffer); 194} 195 196/* 197 * This routine will only return a value if the we are not running as 198 * a privileged process. 199 */ 200static char *safe_getenv(const char *arg) 201{ 202 if ((getuid() != geteuid()) || (getgid() != getegid())) 203 return NULL; 204#if HAVE_PRCTL 205 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) 206 return NULL; 207#else 208#if (defined(linux) && defined(SYS_prctl)) 209 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) 210 return NULL; 211#endif 212#endif 213 214#if defined(HAVE_SECURE_GETENV) 215 return secure_getenv(arg); 216#elif defined(HAVE___SECURE_GETENV) 217 return __secure_getenv(arg); 218#else 219 return getenv(arg); 220#endif 221} 222 223#define DEBUG_INIT 0x8000 224#define DEBUG_ADDREMOVE 0x0001 225 226static int debug_mask = 0; 227static FILE *debug_f = 0; 228 229static void init_debug(void) 230{ 231 char *dstr, *fn, *tmp; 232 int fd, flags; 233 234 if (debug_mask & DEBUG_INIT) 235 return; 236 237 dstr = getenv("COMERR_DEBUG"); 238 if (dstr) { 239 debug_mask = strtoul(dstr, &tmp, 0); 240 if (*tmp || errno) 241 debug_mask = 0; 242 } 243 244 debug_mask |= DEBUG_INIT; 245 if (debug_mask == DEBUG_INIT) 246 return; 247 248 fn = safe_getenv("COMERR_DEBUG_FILE"); 249 if (fn) 250 debug_f = fopen(fn, "a"); 251 if (!debug_f) 252 debug_f = fopen("/dev/tty", "a"); 253 if (debug_f) { 254 fd = fileno(debug_f); 255 if (fd >= 0) { 256 flags = fcntl(fd, F_GETFD); 257 if (flags >= 0) 258 fcntl(fd, F_SETFD, flags | FD_CLOEXEC); 259 } 260 } else 261 debug_mask = DEBUG_INIT; 262 263} 264 265/* 266 * New interface provided by krb5's com_err library 267 */ 268errcode_t add_error_table(const struct error_table * et) 269{ 270 struct et_list *el; 271 272 if (!(el = (struct et_list *) malloc(sizeof(struct et_list)))) 273 return ENOMEM; 274 275 if (et_list_lock() != 0) { 276 free(el); 277 return errno; 278 } 279 280 el->table = et; 281 el->next = _et_dynamic_list; 282 _et_dynamic_list = el; 283 284 init_debug(); 285 if (debug_mask & DEBUG_ADDREMOVE) 286 fprintf(debug_f, "add_error_table: %s (0x%p)\n", 287 error_table_name(et->base), 288 (const void *) et); 289 290 et_list_unlock(); 291 return 0; 292} 293 294/* 295 * New interface provided by krb5's com_err library 296 */ 297errcode_t remove_error_table(const struct error_table * et) 298{ 299 struct et_list *el; 300 struct et_list *el2 = 0; 301 302 if (et_list_lock() != 0) 303 return ENOENT; 304 305 el = _et_dynamic_list; 306 init_debug(); 307 while (el) { 308 if (el->table->base == et->base) { 309 if (el2) /* Not the beginning of the list */ 310 el2->next = el->next; 311 else 312 _et_dynamic_list = el->next; 313 (void) free(el); 314 if (debug_mask & DEBUG_ADDREMOVE) 315 fprintf(debug_f, 316 "remove_error_table: %s (0x%p)\n", 317 error_table_name(et->base), 318 (const void *) et); 319 et_list_unlock(); 320 return 0; 321 } 322 el2 = el; 323 el = el->next; 324 } 325 if (debug_mask & DEBUG_ADDREMOVE) 326 fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n", 327 error_table_name(et->base), 328 (const void *) et); 329 et_list_unlock(); 330 return ENOENT; 331} 332 333/* 334 * Variant of the interface provided by Heimdal's com_err library 335 */ 336void 337add_to_error_table(struct et_list *new_table) 338{ 339 add_error_table(new_table->table); 340} 341