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