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 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 ((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 225 static int debug_mask = 0; 226 static FILE *debug_f = 0; 227 228 static 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 */ 267 errcode_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 */ 296 errcode_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 */ 335 void 336 add_to_error_table(struct et_list *new_table) 337 { 338 add_error_table(new_table->table); 339 } 340