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 #if HAVE_FCNTL 39 #include <fcntl.h> 40 #endif 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 54 THREAD_LOCAL char buffer[25]; 55 56 struct et_list * _et_list = (struct et_list *) NULL; 57 struct 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 66 static sem_t _et_lock; 67 static int _et_lock_initialized; 68 69 static void COMERR_ATTR((constructor)) setup_et_lock(void) 70 { 71 sem_init(&_et_lock, 0, 1); 72 _et_lock_initialized = 1; 73 } 74 75 static void COMERR_ATTR((destructor)) fini_et_lock(void) 76 { 77 sem_destroy(&_et_lock); 78 _et_lock_initialized = 0; 79 } 80 #endif 81 82 83 int 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 94 int 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 103 typedef char *(*gettextf) (const char *); 104 105 static gettextf com_err_gettext = NULL; 106 107 gettextf 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 117 const 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(); 173 oops: 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 */ 199 static char *safe_getenv(const char *arg) 200 { 201 #if !defined(_WIN32) 202 if ((getuid() != geteuid()) || (getgid() != getegid())) 203 return NULL; 204 #endif 205 #if HAVE_PRCTL 206 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) 207 return NULL; 208 #else 209 #if (defined(linux) && defined(SYS_prctl)) 210 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) 211 return NULL; 212 #endif 213 #endif 214 215 #if defined(HAVE_SECURE_GETENV) 216 return secure_getenv(arg); 217 #elif defined(HAVE___SECURE_GETENV) 218 return __secure_getenv(arg); 219 #else 220 return getenv(arg); 221 #endif 222 } 223 224 #define DEBUG_INIT 0x8000 225 #define DEBUG_ADDREMOVE 0x0001 226 227 static int debug_mask = 0; 228 static FILE *debug_f = 0; 229 230 static void init_debug(void) 231 { 232 char *dstr, *fn, *tmp; 233 int fd, flags; 234 235 if (debug_mask & DEBUG_INIT) 236 return; 237 238 dstr = getenv("COMERR_DEBUG"); 239 if (dstr) { 240 debug_mask = strtoul(dstr, &tmp, 0); 241 if (*tmp || errno) 242 debug_mask = 0; 243 } 244 245 debug_mask |= DEBUG_INIT; 246 if (debug_mask == DEBUG_INIT) 247 return; 248 249 fn = safe_getenv("COMERR_DEBUG_FILE"); 250 if (fn) 251 debug_f = fopen(fn, "a"); 252 if (!debug_f) 253 debug_f = fopen("/dev/tty", "a"); 254 if (debug_f) { 255 fd = fileno(debug_f); 256 #if defined(HAVE_FCNTL) 257 if (fd >= 0) { 258 flags = fcntl(fd, F_GETFD); 259 if (flags >= 0) 260 fcntl(fd, F_SETFD, flags | FD_CLOEXEC); 261 } 262 #endif 263 } else 264 debug_mask = DEBUG_INIT; 265 266 } 267 268 /* 269 * New interface provided by krb5's com_err library 270 */ 271 errcode_t add_error_table(const struct error_table * et) 272 { 273 struct et_list *el; 274 275 if (!(el = (struct et_list *) malloc(sizeof(struct et_list)))) 276 return ENOMEM; 277 278 if (et_list_lock() != 0) { 279 free(el); 280 return errno; 281 } 282 283 el->table = et; 284 el->next = _et_dynamic_list; 285 _et_dynamic_list = el; 286 287 init_debug(); 288 if (debug_mask & DEBUG_ADDREMOVE) 289 fprintf(debug_f, "add_error_table: %s (0x%p)\n", 290 error_table_name(et->base), 291 (const void *) et); 292 293 et_list_unlock(); 294 return 0; 295 } 296 297 /* 298 * New interface provided by krb5's com_err library 299 */ 300 errcode_t remove_error_table(const struct error_table * et) 301 { 302 struct et_list *el; 303 struct et_list *el2 = 0; 304 305 if (et_list_lock() != 0) 306 return ENOENT; 307 308 el = _et_dynamic_list; 309 init_debug(); 310 while (el) { 311 if (el->table->base == et->base) { 312 if (el2) /* Not the beginning of the list */ 313 el2->next = el->next; 314 else 315 _et_dynamic_list = el->next; 316 (void) free(el); 317 if (debug_mask & DEBUG_ADDREMOVE) 318 fprintf(debug_f, 319 "remove_error_table: %s (0x%p)\n", 320 error_table_name(et->base), 321 (const void *) et); 322 et_list_unlock(); 323 return 0; 324 } 325 el2 = el; 326 el = el->next; 327 } 328 if (debug_mask & DEBUG_ADDREMOVE) 329 fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n", 330 error_table_name(et->base), 331 (const void *) et); 332 et_list_unlock(); 333 return ENOENT; 334 } 335 336 /* 337 * Variant of the interface provided by Heimdal's com_err library 338 */ 339 void 340 add_to_error_table(struct et_list *new_table) 341 { 342 add_error_table(new_table->table); 343 } 344