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