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