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 #ifdef __GNU__ 117 #define SYS_ERR_BASE 0x40000000 118 #else 119 #define SYS_ERR_BASE 0 120 #endif 121 122 const char * error_message (errcode_t code) 123 { 124 int offset; 125 struct et_list *et; 126 errcode_t table_num; 127 int started = 0; 128 char *cp; 129 130 offset = (int) (code & ((1<<ERRCODE_RANGE)-1)); 131 table_num = code - offset; 132 if (table_num == SYS_ERR_BASE) { 133 #ifdef HAS_SYS_ERRLIST 134 if (code < sys_nerr) 135 return(sys_errlist[code]); 136 else 137 goto oops; 138 #else 139 cp = strerror(code); 140 if (cp) 141 return(cp); 142 else 143 goto oops; 144 #endif 145 } 146 et_list_lock(); 147 for (et = _et_list; et; et = et->next) { 148 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { 149 /* This is the right table */ 150 if (et->table->n_msgs <= offset) { 151 break; 152 } else { 153 const char *msg = et->table->msgs[offset]; 154 et_list_unlock(); 155 if (com_err_gettext) 156 return (*com_err_gettext)(msg); 157 else 158 return msg; 159 } 160 } 161 } 162 for (et = _et_dynamic_list; et; et = et->next) { 163 if ((et->table->base & 0xffffffL) == (table_num & 0xffffffL)) { 164 /* This is the right table */ 165 if (et->table->n_msgs <= offset) { 166 break; 167 } else { 168 const char *msg = et->table->msgs[offset]; 169 et_list_unlock(); 170 if (com_err_gettext) 171 return (*com_err_gettext)(msg); 172 else 173 return msg; 174 } 175 } 176 } 177 et_list_unlock(); 178 oops: 179 strcpy (buffer, "Unknown code "); 180 if (table_num) { 181 strcat (buffer, error_table_name (table_num)); 182 strcat (buffer, " "); 183 } 184 for (cp = buffer; *cp; cp++) 185 ; 186 if (offset >= 100) { 187 *cp++ = '0' + offset / 100; 188 offset %= 100; 189 started++; 190 } 191 if (started || offset >= 10) { 192 *cp++ = '0' + offset / 10; 193 offset %= 10; 194 } 195 *cp++ = '0' + offset; 196 *cp = '\0'; 197 return(buffer); 198 } 199 200 /* 201 * This routine will only return a value if the we are not running as 202 * a privileged process. 203 */ 204 static char *safe_getenv(const char *arg) 205 { 206 #if !defined(_WIN32) 207 if ((getuid() != geteuid()) || (getgid() != getegid())) 208 return NULL; 209 #endif 210 #if HAVE_PRCTL 211 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) 212 return NULL; 213 #else 214 #if (defined(linux) && defined(SYS_prctl)) 215 if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) 216 return NULL; 217 #endif 218 #endif 219 220 #if defined(HAVE_SECURE_GETENV) 221 return secure_getenv(arg); 222 #elif defined(HAVE___SECURE_GETENV) 223 return __secure_getenv(arg); 224 #else 225 return getenv(arg); 226 #endif 227 } 228 229 #define DEBUG_INIT 0x8000 230 #define DEBUG_ADDREMOVE 0x0001 231 232 static int debug_mask = 0; 233 static FILE *debug_f = 0; 234 235 static void init_debug(void) 236 { 237 char *dstr, *fn, *tmp; 238 int fd, flags; 239 240 if (debug_mask & DEBUG_INIT) 241 return; 242 243 dstr = getenv("COMERR_DEBUG"); 244 if (dstr) { 245 debug_mask = strtoul(dstr, &tmp, 0); 246 if (*tmp || errno) 247 debug_mask = 0; 248 } 249 250 debug_mask |= DEBUG_INIT; 251 if (debug_mask == DEBUG_INIT) 252 return; 253 254 fn = safe_getenv("COMERR_DEBUG_FILE"); 255 if (fn) 256 debug_f = fopen(fn, "a"); 257 if (!debug_f) 258 debug_f = fopen("/dev/tty", "a"); 259 if (debug_f) { 260 fd = fileno(debug_f); 261 #if defined(HAVE_FCNTL) 262 if (fd >= 0) { 263 flags = fcntl(fd, F_GETFD); 264 if (flags >= 0) 265 flags = fcntl(fd, F_SETFD, flags | FD_CLOEXEC); 266 if (flags < 0) { 267 fprintf(debug_f, "Couldn't set FD_CLOEXEC " 268 "on debug FILE: %s\n", strerror(errno)); 269 fclose(debug_f); 270 debug_f = NULL; 271 debug_mask = DEBUG_INIT; 272 } 273 } 274 #endif 275 } else 276 debug_mask = DEBUG_INIT; 277 278 } 279 280 /* 281 * New interface provided by krb5's com_err library 282 */ 283 errcode_t add_error_table(const struct error_table * et) 284 { 285 struct et_list *el; 286 287 if (!(el = (struct et_list *) malloc(sizeof(struct et_list)))) 288 return ENOMEM; 289 290 if (et_list_lock() != 0) { 291 free(el); 292 return errno; 293 } 294 295 el->table = et; 296 el->next = _et_dynamic_list; 297 _et_dynamic_list = el; 298 299 init_debug(); 300 if (debug_mask & DEBUG_ADDREMOVE) 301 fprintf(debug_f, "add_error_table: %s (0x%p)\n", 302 error_table_name(et->base), 303 (const void *) et); 304 305 et_list_unlock(); 306 return 0; 307 } 308 309 /* 310 * New interface provided by krb5's com_err library 311 */ 312 errcode_t remove_error_table(const struct error_table * et) 313 { 314 struct et_list *el; 315 struct et_list *el2 = 0; 316 317 if (et_list_lock() != 0) 318 return ENOENT; 319 320 el = _et_dynamic_list; 321 init_debug(); 322 while (el) { 323 if (el->table->base == et->base) { 324 if (el2) /* Not the beginning of the list */ 325 el2->next = el->next; 326 else 327 _et_dynamic_list = el->next; 328 (void) free(el); 329 if (debug_mask & DEBUG_ADDREMOVE) 330 fprintf(debug_f, 331 "remove_error_table: %s (0x%p)\n", 332 error_table_name(et->base), 333 (const void *) et); 334 et_list_unlock(); 335 return 0; 336 } 337 el2 = el; 338 el = el->next; 339 } 340 if (debug_mask & DEBUG_ADDREMOVE) 341 fprintf(debug_f, "remove_error_table FAILED: %s (0x%p)\n", 342 error_table_name(et->base), 343 (const void *) et); 344 et_list_unlock(); 345 return ENOENT; 346 } 347 348 /* 349 * Variant of the interface provided by Heimdal's com_err library 350 */ 351 void 352 add_to_error_table(struct et_list *new_table) 353 { 354 add_error_table(new_table->table); 355 }